fixed lots of not-thread-safe things

Most of the things I fixed here were using libc functions that are
fundamentally not thread-safe.
diff --git a/aos/common/libc/README b/aos/common/libc/README
new file mode 100644
index 0000000..c4ee817
--- /dev/null
+++ b/aos/common/libc/README
@@ -0,0 +1,18 @@
+This directory has replacements for some libc functions that we don't want to
+use because they're not thread-safe and/or they allocate memory.
+
+Some of them are implemented as C++ functions because it makes fixing the
+memory management issues that cause the standard versions to be not thread-safe
+possible and some of them are still callable from C when it makes sense.
+
+<http://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html#tag_15_09_01>
+has a list of the not-thread-safe POSIX functions. We've gotten rid of all of
+those except for the following:
+  getenv(3): <http://austingroupbugs.net/view.php?id=188> proposes changing
+    POSIX to require it to return a pointer into environ, making it thread-safe,
+    which glibc already does.
+  inet_ntoa(3): Glibc uses a thread-local buffer, which makes it thread-safe,
+    and uses of this function should go away soon.
+  readdir(3): Glibc already guarantees that only invocations on the same
+    directory stream aren't thread-safe, and there's talk of changing POSIX to
+    say the same thing.
diff --git a/aos/common/libc/aos_strerror.cc b/aos/common/libc/aos_strerror.cc
new file mode 100644
index 0000000..d5fc2b4
--- /dev/null
+++ b/aos/common/libc/aos_strerror.cc
@@ -0,0 +1,42 @@
+#include "aos/common/libc/aos_strerror.h"
+
+#include <assert.h>
+#include <sys/types.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "aos/linux_code/thread_local.h"
+
+// This code uses an overloaded function to handle the result from either
+// version of strerror_r correctly without needing a way to get the choice out
+// of the compiler/glibc/whatever explicitly.
+
+namespace {
+
+const size_t kBufferSize = 128;
+
+// Handle the result from the GNU version of strerror_r. It never fails, so
+// that's pretty easy...
+__attribute__((unused))
+char *aos_strerror_handle_result(int /*error*/, char *ret, char * /*buffer*/) {
+  return ret;
+}
+
+// Handle the result from the POSIX version of strerror_r.
+__attribute__((unused))
+char *aos_strerror_handle_result(int error, int ret, char *buffer) {
+  if (ret != 0) {
+    assert(snprintf(buffer, kBufferSize, "Unknown error %d", error) > 0);
+  }
+  return buffer;
+}
+
+}  // namespace
+
+const char *aos_strerror(int error) {
+  static AOS_THREAD_LOCAL char buffer[kBufferSize];
+
+  // Call the overload for whichever version we're using.
+  return aos_strerror_handle_result(
+      error, strerror_r(error, buffer, sizeof(buffer)), buffer);
+}
diff --git a/aos/common/libc/aos_strerror.h b/aos/common/libc/aos_strerror.h
new file mode 100644
index 0000000..acce427
--- /dev/null
+++ b/aos/common/libc/aos_strerror.h
@@ -0,0 +1,23 @@
+#ifndef AOS_COMMON_LIBC_AOS_STRERROR_H_
+#define AOS_COMMON_LIBC_AOS_STRERROR_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Thread-safe version of strerror(3) (except it may change errno).
+//
+// Returns a pointer to static data or a thread-local buffer.
+//
+// Necessary because strerror_r(3) is such a mess (which version you get is
+// determined at compile time by black magic related to feature macro
+// definitions, compiler flags, glibc version, and even whether you're using g++
+// or clang++) and strerror_l(3) might not work if you end up with the magic
+// LC_GLOBAL_LOCALE locale.
+const char *aos_strerror(int error);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // AOS_COMMON_LIBC_AOS_STRERROR_H_
diff --git a/aos/common/libc/aos_strerror_test.cc b/aos/common/libc/aos_strerror_test.cc
new file mode 100644
index 0000000..c4574fe
--- /dev/null
+++ b/aos/common/libc/aos_strerror_test.cc
@@ -0,0 +1,29 @@
+#include "aos/common/libc/aos_strerror.h"
+
+#include <errno.h>
+
+#include "gtest/gtest.h"
+
+namespace aos {
+namespace libc {
+namespace testing {
+
+// Tries a couple of easy ones.
+TEST(StrerrorTest, Basic) {
+  EXPECT_STREQ("Argument list too long", aos_strerror(E2BIG));
+  EXPECT_STREQ("Bad file descriptor", aos_strerror(EBADF));
+  EXPECT_STREQ("Unknown error 4021", aos_strerror(4021));
+}
+
+// Runs through all errno values and makes sure it gives the same result as
+// strerror(3).
+TEST(StrerrorTest, All) {
+  for (int i = 0; i < 4095; ++i) {
+    SCOPED_TRACE("iteration " + ::std::to_string(i));
+    EXPECT_STREQ(strerror(i), aos_strerror(i));
+  }
+}
+
+}  // namespace testing
+}  // namespace libc
+}  // namespace aos
diff --git a/aos/common/libc/aos_strsignal.cc b/aos/common/libc/aos_strsignal.cc
new file mode 100644
index 0000000..5fae327
--- /dev/null
+++ b/aos/common/libc/aos_strsignal.cc
@@ -0,0 +1,23 @@
+#include "aos/common/libc/aos_strsignal.h"
+
+#include <signal.h>
+
+#include "aos/linux_code/thread_local.h"
+#include "aos/common/logging/logging.h"
+
+const char *aos_strsignal(int signal) {
+  static AOS_THREAD_LOCAL char buffer[512];
+
+  if (signal >= SIGRTMIN && signal <= SIGRTMAX) {
+    CHECK(snprintf(buffer, sizeof(buffer), "Real-time signal %d",
+                   signal - SIGRTMIN) > 0);
+    return buffer;
+  }
+
+  if (signal > 0 && signal < NSIG && sys_siglist[signal] != nullptr) {
+    return sys_siglist[signal];
+  }
+
+  CHECK(snprintf(buffer, sizeof(buffer), "Unknown signal %d", signal) > 0);
+  return buffer;
+}
diff --git a/aos/common/libc/aos_strsignal.h b/aos/common/libc/aos_strsignal.h
new file mode 100644
index 0000000..ef6795c
--- /dev/null
+++ b/aos/common/libc/aos_strsignal.h
@@ -0,0 +1,17 @@
+#ifndef AOS_COMMON_LIBC_AOS_STRSIGNAL_H_
+#define AOS_COMMON_LIBC_AOS_STRSIGNAL_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Thread-safe version of strsignal(3) (except it will never return NULL).
+//
+// Returns a pointer to static data or a thread-local buffer.
+const char *aos_strsignal(int signal);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // AOS_COMMON_LIBC_AOS_STRSIGNAL_H_
diff --git a/aos/common/libc/aos_strsignal_test.cc b/aos/common/libc/aos_strsignal_test.cc
new file mode 100644
index 0000000..50cf635
--- /dev/null
+++ b/aos/common/libc/aos_strsignal_test.cc
@@ -0,0 +1,28 @@
+#include "aos/common/libc/aos_strsignal.h"
+
+#include <signal.h>
+
+#include "gtest/gtest.h"
+
+namespace aos {
+namespace libc {
+namespace testing {
+
+// Tries a couple of easy ones.
+TEST(StrsignalTest, Basic) {
+  EXPECT_STREQ("Hangup", aos_strsignal(SIGHUP));
+  EXPECT_STREQ("Broken pipe", aos_strsignal(SIGPIPE));
+  EXPECT_STREQ("Real-time signal 2", aos_strsignal(SIGRTMIN + 2));
+  EXPECT_STREQ("Unknown signal 155", aos_strsignal(155));
+}
+
+// Tests that all the signals give the same result as strsignal(3).
+TEST(StrsignalTest, All) {
+  for (int i = 0; i < SIGRTMAX + 5; ++i) {
+    EXPECT_STREQ(strsignal(i), aos_strsignal(i));
+  }
+}
+
+}  // namespace testing
+}  // namespace libc
+}  // namespace aos
diff --git a/aos/common/libc/dirname.cc b/aos/common/libc/dirname.cc
new file mode 100644
index 0000000..c843898
--- /dev/null
+++ b/aos/common/libc/dirname.cc
@@ -0,0 +1,39 @@
+#include "aos/common/libc/dirname.h"
+
+namespace aos {
+namespace libc {
+namespace {
+
+::std::string DoDirname(const ::std::string &path, size_t last_slash) {
+  // If there aren't any other '/'s in it.
+  if (last_slash == ::std::string::npos) return ".";
+
+  // Back up as long as we see '/'s.
+  do {
+    // If we get all the way to the beginning.
+    if (last_slash == 0) return "/";
+    --last_slash;
+  } while (path[last_slash] == '/');
+
+  return path.substr(0, last_slash + 1);
+}
+
+}  // namespace
+
+::std::string Dirname(const ::std::string &path) {
+  // Without this, we end up with integer underflows below, which is technically
+  // undefined.
+  if (path.size() == 0) return ".";
+
+  size_t last_slash = path.rfind('/');
+
+  // If the path ends with a '/'.
+  if (last_slash == path.size() - 1) {
+    last_slash = DoDirname(path, last_slash).rfind('/');
+  }
+
+  return DoDirname(path, last_slash);
+}
+
+}  // namespace libc
+}  // namespace aos
diff --git a/aos/common/libc/dirname.h b/aos/common/libc/dirname.h
new file mode 100644
index 0000000..f05f8f1
--- /dev/null
+++ b/aos/common/libc/dirname.h
@@ -0,0 +1,15 @@
+#ifndef AOS_COMMON_LIBC_DIRNAME_H_
+#define AOS_COMMON_LIBC_DIRNAME_H_
+
+#include <string>
+
+namespace aos {
+namespace libc {
+
+// Thread-safe version of dirname(3).
+::std::string Dirname(const ::std::string &path);
+
+}  // namespace libc
+}  // namespace aos
+
+#endif  // AOS_COMMON_LIBC_DIRNAME_H_
diff --git a/aos/common/libc/dirname_test.cc b/aos/common/libc/dirname_test.cc
new file mode 100644
index 0000000..0207961
--- /dev/null
+++ b/aos/common/libc/dirname_test.cc
@@ -0,0 +1,79 @@
+#include "aos/common/libc/dirname.h"
+
+#include <libgen.h>
+
+#include "gtest/gtest.h"
+
+namespace aos {
+namespace libc {
+namespace testing {
+
+// Tests the examples from the Linux man-pages release 3.44 dirname(3).
+TEST(DirnameTest, ManPageExamples) {
+  EXPECT_EQ("/usr", Dirname("/usr/lib"));
+  EXPECT_EQ("/", Dirname("/usr/"));
+  EXPECT_EQ(".", Dirname("usr"));
+  EXPECT_EQ("/", Dirname("/"));
+  EXPECT_EQ(".", Dirname("."));
+  EXPECT_EQ(".", Dirname(".."));
+}
+
+// Tests that it handles multiple '/'s in a row correctly.
+TEST(DirnameTest, MultipleSlashes) {
+  EXPECT_EQ("//usr", Dirname("//usr//lib"));
+  EXPECT_EQ("//usr/lib", Dirname("//usr/lib//bla"));
+  EXPECT_EQ("/", Dirname("//usr//"));
+  EXPECT_EQ(".", Dirname("usr//"));
+  EXPECT_EQ("/", Dirname("//"));
+  EXPECT_EQ(".", Dirname(".//"));
+  EXPECT_EQ(".", Dirname("..//"));
+}
+
+TEST(DirnameTest, WeirdInputs) {
+  EXPECT_EQ(".", Dirname(""));
+  EXPECT_EQ(".", Dirname("..."));
+}
+
+// Runs through a bunch of randomly constructed pathnames and makes sure it
+// gives the same result as dirname(3).
+TEST(DirnameTest, Random) {
+  static const char kTestBytes[] = "a0//.. ";
+  static const size_t kTestBytesSize = sizeof(kTestBytes) - 1;
+  static const size_t kTestPathSize = 6;
+
+  ::std::string test_string(kTestPathSize, '\0');
+  char test_path[kTestPathSize + 1];
+  for (size_t i0 = 0; i0 < kTestBytesSize; ++i0) {
+    test_string[0] = kTestBytes[i0];
+    for (size_t i1 = 0; i1 < kTestBytesSize; ++i1) {
+      // dirname(3) returns "//" in this case which is weird and our Dirname
+      // doesn't.
+      if (test_string[0] == '/' && kTestBytes[i1] == '/') continue;
+
+      test_string[1] = kTestBytes[i1];
+      for (size_t i2 = 0; i2 < kTestBytesSize; ++i2) {
+        test_string[2] = kTestBytes[i2];
+        for (size_t i3 = 0; i3 < kTestBytesSize; ++i3) {
+          test_string[3] = kTestBytes[i3];
+          for (size_t i4 = 0; i4 < kTestBytesSize; ++i4) {
+            test_string[4] = kTestBytes[i4];
+            for (size_t i5 = 0; i5 < kTestBytesSize; ++i5) {
+              test_string[5] = kTestBytes[i5];
+
+              memcpy(test_path, test_string.c_str(), kTestPathSize);
+              test_path[kTestPathSize] = '\0';
+
+              SCOPED_TRACE("path is '" + test_string + "'");
+              EXPECT_EQ(::std::string(dirname(test_path)),
+                        Dirname(test_string));
+            }
+          }
+        }
+      }
+    }
+  }
+}
+
+}  // namespace testing
+}  // namespace libc
+}  // namespace aos
diff --git a/aos/common/libc/libc.gyp b/aos/common/libc/libc.gyp
new file mode 100644
index 0000000..d7fc357
--- /dev/null
+++ b/aos/common/libc/libc.gyp
@@ -0,0 +1,62 @@
+{
+  'targets': [
+    {
+      'target_name': 'aos_strsignal',
+      'type': 'static_library',
+      'sources': [
+        'aos_strsignal.cc',
+      ],
+      'dependencies': [
+        '<(AOS)/build/aos.gyp:logging_interface',
+      ],
+    },
+    {
+      'target_name': 'aos_strsignal_test',
+      'type': 'executable',
+      'sources': [
+        'aos_strsignal_test.cc',
+      ],
+      'dependencies': [
+        'aos_strsignal',
+        '<(EXTERNALS):gtest',
+        '<(AOS)/build/aos.gyp:logging',
+      ],
+    },
+    {
+      'target_name': 'dirname',
+      'type': 'static_library',
+      'sources': [
+        'dirname.cc',
+      ],
+    },
+    {
+      'target_name': 'dirname_test',
+      'type': 'executable',
+      'sources': [
+        'dirname_test.cc',
+      ],
+      'dependencies': [
+        'dirname',
+        '<(EXTERNALS):gtest',
+      ],
+    },
+    {
+      'target_name': 'aos_strerror',
+      'type': 'static_library',
+      'sources': [
+        'aos_strerror.cc',
+      ],
+    },
+    {
+      'target_name': 'aos_strerror_test',
+      'type': 'executable',
+      'sources': [
+        'aos_strerror_test.cc',
+      ],
+      'dependencies': [
+        'aos_strerror',
+        '<(EXTERNALS):gtest',
+      ],
+    },
+  ],
+}