Replace all uses of strerror() by ovs_strerror(), for thread safety.
[sliver-openvswitch.git] / lib / vlog.c
index 31ba96e..f87f48f 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -28,6 +28,7 @@
 #include <syslog.h>
 #include <time.h>
 #include <unistd.h>
+#include "coverage.h"
 #include "dirs.h"
 #include "dynamic-string.h"
 #include "ofpbuf.h"
 
 VLOG_DEFINE_THIS_MODULE(vlog);
 
+COVERAGE_DEFINE(vlog_recursive);
+
+/* ovs_assert() logs the assertion message, so using ovs_assert() in this
+ * source file could cause recursion. */
+#undef ovs_assert
+#define ovs_assert use_assert_instead_of_ovs_assert_in_this_module
+
 /* Name for each logging level. */
-static const char *level_names[VLL_N_LEVELS] = {
+static const char *const level_names[VLL_N_LEVELS] = {
 #define VLOG_LEVEL(NAME, SYSLOG_LEVEL) #NAME,
     VLOG_LEVELS
 #undef VLOG_LEVEL
 };
 
 /* Syslog value for each logging level. */
-static int syslog_levels[VLL_N_LEVELS] = {
+static const int syslog_levels[VLL_N_LEVELS] = {
 #define VLOG_LEVEL(NAME, SYSLOG_LEVEL) SYSLOG_LEVEL,
     VLOG_LEVELS
 #undef VLOG_LEVEL
@@ -102,7 +110,7 @@ static void vlog_update_async_log_fd(void);
 /* Searches the 'n_names' in 'names'.  Returns the index of a match for
  * 'target', or 'n_names' if no name matches. */
 static size_t
-search_name_array(const char *target, const char **names, size_t n_names)
+search_name_array(const char *target, const char *const *names, size_t n_names)
 {
     size_t i;
 
@@ -312,7 +320,7 @@ vlog_set_log_file(const char *file_name)
     /* Log success or failure. */
     if (log_fd < 0) {
         VLOG_WARN("failed to open %s for logging: %s",
-                  log_file_name, strerror(errno));
+                  log_file_name, ovs_strerror(errno));
         error = errno;
     } else {
         VLOG_INFO("opened log file %s", log_file_name);
@@ -423,6 +431,16 @@ exit:
     return msg;
 }
 
+/* Set debugging levels.  Abort with an error message if 's' is invalid. */
+void
+vlog_set_levels_from_string_assert(const char *s)
+{
+    char *error = vlog_set_levels_from_string(s);
+    if (error) {
+        ovs_fatal(0, "%s", error);
+    }
+}
+
 /* If 'arg' is null, configure maximum verbosity.  Otherwise, sets
  * configuration according to 'arg' (see vlog_set_levels_from_string()). */
 void
@@ -471,7 +489,7 @@ vlog_unixctl_reopen(struct unixctl_conn *conn, int argc OVS_UNUSED,
     if (log_file_name) {
         int error = vlog_reopen_log_file();
         if (error) {
-            unixctl_command_reply_error(conn, strerror(errno));
+            unixctl_command_reply_error(conn, ovs_strerror(errno));
         } else {
             unixctl_command_reply(conn, NULL);
         }
@@ -552,12 +570,9 @@ vlog_init(void)
 
     now = time_wall();
     if (now < 0) {
-        struct tm tm;
-        char s[128];
-
-        gmtime_r(&now, &tm);
-        strftime(s, sizeof s, "%a, %d %b %Y %H:%M:%S", &tm);
+        char *s = xastrftime("%a, %d %b %Y %H:%M:%S", now, true);
         VLOG_ERR("current time is negative: %s (%ld)", s, (long int) now);
+        free(s);
     }
 
     unixctl_command_register(
@@ -691,11 +706,11 @@ format_log_message(const struct vlog_module *module, enum vlog_level level,
             break;
         case 'd':
             p = fetch_braces(p, "%Y-%m-%d %H:%M:%S", tmp, sizeof tmp);
-            ds_put_strftime(s, tmp, false);
+            ds_put_strftime(s, tmp, time_wall(), false);
             break;
         case 'D':
             p = fetch_braces(p, "%Y-%m-%d %H:%M:%S", tmp, sizeof tmp);
-            ds_put_strftime(s, tmp, true);
+            ds_put_strftime(s, tmp, time_wall(), true);
             break;
         case 'm':
             /* Format user-supplied log message and trim trailing new-lines. */
@@ -952,13 +967,26 @@ static void
 vlog_write_file(struct ds *s)
 {
     if (worker_is_running()) {
-        worker_request(s->string, s->length,
-                       &log_fd, vlog_async_inited ? 0 : 1,
-                       vlog_async_write_request_cb, NULL, NULL);
-        vlog_async_inited = true;
-    } else {
-        ignore(write(log_fd, s->string, s->length));
+        static bool in_worker_request = false;
+        if (!in_worker_request) {
+            in_worker_request = true;
+
+            worker_request(s->string, s->length,
+                           &log_fd, vlog_async_inited ? 0 : 1,
+                           vlog_async_write_request_cb, NULL, NULL);
+            vlog_async_inited = true;
+
+            in_worker_request = false;
+            return;
+        } else {
+            /* We've been entered recursively.  This can happen if
+             * worker_request(), or a function that it calls, tries to log
+             * something.  We can't call worker_request() recursively, so fall
+             * back to writing the log file directly. */
+            COVERAGE_INC(vlog_recursive);
+        }
     }
+    ignore(write(log_fd, s->string, s->length));
 }
 
 static void