summaryrefslogtreecommitdiffstats
path: root/base/safe_strerror_posix.cc
diff options
context:
space:
mode:
authortschmelcher@chromium.org <tschmelcher@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-10-09 23:52:20 +0000
committertschmelcher@chromium.org <tschmelcher@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-10-09 23:52:20 +0000
commitd8617a6ad0d531e8ad63298f7cd4a091b78aa43e (patch)
treee60489d36fcbdf6f580e7c4cd715f2a085d5b56b /base/safe_strerror_posix.cc
parenta36804546751df937333345b7a27b4ef8d60b67d (diff)
downloadchromium_src-d8617a6ad0d531e8ad63298f7cd4a091b78aa43e.zip
chromium_src-d8617a6ad0d531e8ad63298f7cd4a091b78aa43e.tar.gz
chromium_src-d8617a6ad0d531e8ad63298f7cd4a091b78aa43e.tar.bz2
Add logging macros that automatically append the last system error in string form.
Also add thread-safe, portable variants for strerror() and strerror_r() on POSIX so that existing error logging code that calls strerror() for something other than LOG, LOG_IF, or CHECK can be changed to use safe versions too. After this CL I will eliminate all unsafe uses of strerror() in our code. TEST=Linux: tested PLOG and DPLOG with both a valid error and invalid error on a dbg build with both the default strerror_r implementation (GNU) and the other one (POSIX) via some throw-away macro evilness, and also tested the default strerror_r again on an opt build to verify DPLOG is a no-op; Windows: tested PLOG and DPLOG with both a valid error and invalid error on a dbg build; also tested LOG_GETLASTERROR_MODULE with winhttp and ERROR_WINHTTP_CANNOT_CONNECT and verified that it prints the correct system message and that it doesn't with PLOG; also tested LOG_GETLASTERROR_MODULE with a bogus module name and verified that it prints an error that it can't find the module, and the original error; Mac: none (implicitly tested via the Linux POSIX tests); trybots for Win, Mac, and Linux 32-bit; built locally for Linux 32-bit and 64-bit and tested base_unittests and also running Chromium itself; wrote the upcoming CL that switches strerror() calls to use PLOG and verified that it builds and works for both Linux 32-bit and Linux 64-bit; lint BUG=none Review URL: http://codereview.chromium.org/265052 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@28632 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'base/safe_strerror_posix.cc')
-rw-r--r--base/safe_strerror_posix.cc107
1 files changed, 107 insertions, 0 deletions
diff --git a/base/safe_strerror_posix.cc b/base/safe_strerror_posix.cc
new file mode 100644
index 0000000..008b785
--- /dev/null
+++ b/base/safe_strerror_posix.cc
@@ -0,0 +1,107 @@
+// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/safe_strerror_posix.h"
+
+#include <errno.h>
+#include <string.h>
+
+#if defined(__GLIBC__) && defined(__GNUC__)
+// GCC will complain about the unused second wrap function unless we tell it
+// that we meant for them to be potentially unused, which is exactly what this
+// attribute is for.
+#define POSSIBLY_UNUSED __attribute__((unused))
+#else
+#define POSSIBLY_UNUSED
+#endif
+
+#if defined(__GLIBC__)
+// glibc has two strerror_r functions: a historical GNU-specific one that
+// returns type char *, and a POSIX.1-2001 compliant one available since 2.3.4
+// that returns int. This wraps the GNU-specific one.
+static void POSSIBLY_UNUSED wrap_posix_strerror_r(
+ char *(*strerror_r_ptr)(int, char *, size_t),
+ int err,
+ char *buf,
+ size_t len) {
+ // GNU version.
+ char *rc = (*strerror_r_ptr)(err, buf, len);
+ if (rc != buf) {
+ // glibc did not use buf and returned a static string instead. Copy it
+ // into buf.
+ buf[0] = '\0';
+ strncat(buf, rc, len - 1);
+ }
+ // The GNU version never fails. Unknown errors get an "unknown error" message.
+ // The result is always null terminated.
+}
+#endif // __GLIBC__
+
+// Wrapper for strerror_r functions that implement the POSIX interface. POSIX
+// does not define the behaviour for some of the edge cases, so we wrap it to
+// guarantee that they are handled. This is compiled on all POSIX platforms, but
+// it will only be used on Linux if the POSIX strerror_r implementation is
+// being used (see below).
+static void POSSIBLY_UNUSED wrap_posix_strerror_r(
+ int (*strerror_r_ptr)(int, char *, size_t),
+ int err,
+ char *buf,
+ size_t len) {
+ int old_errno = errno;
+ // Have to cast since otherwise we get an error if this is the GNU version
+ // (but in such a scenario this function is never called). Sadly we can't use
+ // C++-style casts because the appropriate one is reinterpret_cast but it's
+ // considered illegal to reinterpret_cast a type to itself, so we get an
+ // error in the opposite case.
+ int result = (*strerror_r_ptr)(err, buf, len);
+ if (result == 0) {
+ // POSIX is vague about whether the string will be terminated, although
+ // it indirectly implies that typically ERANGE will be returned, instead
+ // of truncating the string. We play it safe by always terminating the
+ // string explicitly.
+ buf[len - 1] = '\0';
+ } else {
+ // Error. POSIX is vague about whether the return value is itself a system
+ // error code or something else. On Linux currently it is -1 and errno is
+ // set. On BSD-derived systems it is a system error and errno is unchanged.
+ // We try and detect which case it is so as to put as much useful info as
+ // we can into our message.
+ int strerror_error; // The error encountered in strerror
+ int new_errno = errno;
+ if (new_errno != old_errno) {
+ // errno was changed, so probably the return value is just -1 or something
+ // else that doesn't provide any info, and errno is the error.
+ strerror_error = new_errno;
+ } else {
+ // Either the error from strerror_r was the same as the previous value, or
+ // errno wasn't used. Assume the latter.
+ strerror_error = result;
+ }
+ // snprintf truncates and always null-terminates.
+ snprintf(buf,
+ len,
+ "Error %d while retrieving error %d",
+ strerror_error,
+ err);
+ }
+ errno = old_errno;
+}
+
+void safe_strerror_r(int err, char *buf, size_t len) {
+ if (buf == NULL || len <= 0) {
+ return;
+ }
+ // If using glibc (i.e., Linux), the compiler will automatically select the
+ // appropriate overloaded function based on the function type of strerror_r.
+ // The other one will be elided from the translation unit since both are
+ // static.
+ wrap_posix_strerror_r(&strerror_r, err, buf, len);
+}
+
+std::string safe_strerror(int err) {
+ const int buffer_size = 256;
+ char buf[buffer_size];
+ safe_strerror_r(err, buf, sizeof(buf));
+ return std::string(buf);
+}