INSTALL.md: Fix shell command line formatting.
[cascardo/ovs.git] / lib / vlog.c
index da7a307..bbc6eb6 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2015, 2016 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -37,6 +37,9 @@
 #include "sat-math.h"
 #include "socket-util.h"
 #include "svec.h"
+#include "syslog-direct.h"
+#include "syslog-libc.h"
+#include "syslog-provider.h"
 #include "timeval.h"
 #include "unixctl.h"
 #include "util.h"
@@ -74,9 +77,6 @@ VLOG_LEVELS
  * used for LOG_LOCAL0. */
 BUILD_ASSERT_DECL(LOG_LOCAL0 == (16 << 3));
 
-/* The log modules. */
-struct ovs_list vlog_modules = OVS_LIST_INITIALIZER(&vlog_modules);
-
 /* Protects the 'pattern' in all "struct destination"s, so that a race between
  * changing and reading the pattern does not cause an access to freed
  * memory. */
@@ -101,11 +101,17 @@ DEFINE_STATIC_PER_THREAD_DATA(unsigned int, msg_num, 0);
  *
  * All of the following is protected by 'log_file_mutex', which nests inside
  * pattern_rwlock. */
-static struct ovs_mutex log_file_mutex = OVS_MUTEX_INITIALIZER;
-static char *log_file_name OVS_GUARDED_BY(log_file_mutex);
+static struct ovs_mutex log_file_mutex OVS_ACQ_AFTER(pattern_rwlock)
+    = OVS_MUTEX_INITIALIZER;
+static char *log_file_name OVS_GUARDED_BY(log_file_mutex) = NULL;
 static int log_fd OVS_GUARDED_BY(log_file_mutex) = -1;
 static struct async_append *log_writer OVS_GUARDED_BY(log_file_mutex);
 static bool log_async OVS_GUARDED_BY(log_file_mutex);
+static struct syslogger *syslogger = NULL;
+
+/* The log modules. */
+static struct ovs_list vlog_modules OVS_GUARDED_BY(log_file_mutex)
+    = OVS_LIST_INITIALIZER(&vlog_modules);
 
 /* Syslog export configuration. */
 static int syslog_fd OVS_GUARDED_BY(pattern_rwlock) = -1;
@@ -205,9 +211,12 @@ vlog_get_destination_val(const char *name)
     return i;
 }
 
-void vlog_insert_module(struct ovs_list *vlog)
+void
+vlog_insert_module(struct ovs_list *vlog)
 {
+    ovs_mutex_lock(&log_file_mutex);
     list_insert(&vlog_modules, vlog);
+    ovs_mutex_unlock(&log_file_mutex);
 }
 
 /* Returns the name for logging module 'module'. */
@@ -224,11 +233,14 @@ vlog_module_from_name(const char *name)
 {
     struct vlog_module *mp;
 
+    ovs_mutex_lock(&log_file_mutex);
     LIST_FOR_EACH (mp, list, &vlog_modules) {
         if (!strcasecmp(name, mp->name)) {
+            ovs_mutex_unlock(&log_file_mutex);
             return mp;
         }
     }
+    ovs_mutex_unlock(&log_file_mutex);
 
     return NULL;
 }
@@ -426,6 +438,35 @@ vlog_reopen_log_file(void)
     }
 }
 
+#ifndef _WIN32
+/* In case a log file exists, change its owner to new 'user' and 'group'.
+ *
+ * This is useful for handling cases where the --log-file option is
+ * specified ahead of the --user option.  */
+void
+vlog_change_owner_unix(uid_t user, gid_t group)
+{
+    struct ds err = DS_EMPTY_INITIALIZER;
+    int error;
+
+    ovs_mutex_lock(&log_file_mutex);
+    error = log_file_name ? chown(log_file_name, user, group) : 0;
+    if (error) {
+        /* Build the error message. We can not call VLOG_FATAL directly
+         * here because VLOG_FATAL() will try again to to acquire
+         * 'log_file_mutex' lock, causing deadlock.
+         */
+        ds_put_format(&err, "Failed to change %s ownership: %s.",
+                      log_file_name, ovs_strerror(errno));
+    }
+    ovs_mutex_unlock(&log_file_mutex);
+
+    if (error) {
+        VLOG_FATAL("%s", ds_steal_cstr(&err));
+    }
+}
+#endif
+
 /* Set debugging levels.  Returns null if successful, otherwise an error
  * message that the caller must free(). */
 char *
@@ -534,6 +575,24 @@ vlog_set_verbosity(const char *arg)
     }
 }
 
+void
+vlog_set_syslog_method(const char *method)
+{
+    if (syslogger) {
+        /* Set syslogger only, if one is not already set.  This effectively
+         * means that only the first --syslog-method argument is honored. */
+        return;
+    }
+
+    if (!strcmp(method, "libc")) {
+        syslogger = syslog_libc_create();
+    } else if (!strncmp(method, "udp:", 4) || !strncmp(method, "unix:", 5)) {
+        syslogger = syslog_direct_create(method);
+    } else {
+        ovs_fatal(0, "unsupported syslog method '%s'", method);
+    }
+}
+
 /* Set the vlog udp syslog target. */
 void
 vlog_set_syslog_target(const char *target)
@@ -592,6 +651,17 @@ vlog_unixctl_list(struct unixctl_conn *conn, int argc OVS_UNUSED,
     free(msg);
 }
 
+static void
+vlog_unixctl_list_pattern(struct unixctl_conn *conn, int argc OVS_UNUSED,
+                          const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED)
+{
+    char *msg;
+
+    msg = vlog_get_patterns();
+    unixctl_command_reply(conn, msg);
+    free(msg);
+}
+
 static void
 vlog_unixctl_reopen(struct unixctl_conn *conn, int argc OVS_UNUSED,
                     const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED)
@@ -614,14 +684,38 @@ vlog_unixctl_reopen(struct unixctl_conn *conn, int argc OVS_UNUSED,
     }
 }
 
+static void
+vlog_unixctl_close(struct unixctl_conn *conn, int argc OVS_UNUSED,
+                   const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED)
+{
+    ovs_mutex_lock(&log_file_mutex);
+    if (log_fd >= 0) {
+        close(log_fd);
+        log_fd = -1;
+
+        async_append_destroy(log_writer);
+        log_writer = NULL;
+
+        struct vlog_module *mp;
+        LIST_FOR_EACH (mp, list, &vlog_modules) {
+            update_min_level(mp);
+        }
+    }
+    ovs_mutex_unlock(&log_file_mutex);
+
+    unixctl_command_reply(conn, NULL);
+}
+
 static void
 set_all_rate_limits(bool enable)
 {
     struct vlog_module *mp;
 
+    ovs_mutex_lock(&log_file_mutex);
     LIST_FOR_EACH (mp, list, &vlog_modules) {
         mp->honor_rate_limits = enable;
     }
+    ovs_mutex_unlock(&log_file_mutex);
 }
 
 static void
@@ -671,23 +765,18 @@ vlog_init(void)
     static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER;
 
     if (ovsthread_once_start(&once)) {
-        static char *program_name_copy;
         long long int now;
         int facility;
+        bool print_syslog_target_deprecation;
 
         /* Do initialization work that needs to be done before any logging
          * occurs.  We want to keep this really minimal because any attempt to
          * log anything before calling ovsthread_once_done() will deadlock. */
-
-        /* openlog() is allowed to keep the pointer passed in, without making a
-         * copy.  The daemonize code sometimes frees and replaces
-         * 'program_name', so make a private copy just for openlog().  (We keep
-         * a pointer to the private copy to suppress memory leak warnings in
-         * case openlog() does make its own copy.) */
-        program_name_copy = program_name ? xstrdup(program_name) : NULL;
         atomic_read_explicit(&log_facility, &facility, memory_order_relaxed);
-        openlog(program_name_copy, LOG_NDELAY,
-                facility ? facility : LOG_DAEMON);
+        if (!syslogger) {
+            syslogger = syslog_libc_create();
+        }
+        syslogger->class->openlog(syslogger, facility ? facility : LOG_DAEMON);
         ovsthread_once_done(&once);
 
         /* Now do anything that we want to happen only once but doesn't have to
@@ -705,12 +794,25 @@ vlog_init(void)
             1, INT_MAX, vlog_unixctl_set, NULL);
         unixctl_command_register("vlog/list", "", 0, 0, vlog_unixctl_list,
                                  NULL);
+        unixctl_command_register("vlog/list-pattern", "", 0, 0,
+                                 vlog_unixctl_list_pattern, NULL);
         unixctl_command_register("vlog/enable-rate-limit", "[module]...",
                                  0, INT_MAX, vlog_enable_rate_limit, NULL);
         unixctl_command_register("vlog/disable-rate-limit", "[module]...",
                                  0, INT_MAX, vlog_disable_rate_limit, NULL);
         unixctl_command_register("vlog/reopen", "", 0, 0,
                                  vlog_unixctl_reopen, NULL);
+        unixctl_command_register("vlog/close", "", 0, 0,
+                                 vlog_unixctl_close, NULL);
+
+        ovs_rwlock_rdlock(&pattern_rwlock);
+        print_syslog_target_deprecation = syslog_fd >= 0;
+        ovs_rwlock_unlock(&pattern_rwlock);
+
+        if (print_syslog_target_deprecation) {
+            VLOG_WARN("--syslog-target flag is deprecated, use "
+                      "--syslog-method instead");
+        }
     }
 }
 
@@ -743,6 +845,7 @@ vlog_get_levels(void)
     ds_put_format(&s, "                 console    syslog    file\n");
     ds_put_format(&s, "                 -------    ------    ------\n");
 
+    ovs_mutex_lock(&log_file_mutex);
     LIST_FOR_EACH (mp, list, &vlog_modules) {
         struct ds line;
 
@@ -759,6 +862,7 @@ vlog_get_levels(void)
 
         svec_add_nocopy(&lines, ds_steal_cstr(&line));
     }
+    ovs_mutex_unlock(&log_file_mutex);
 
     svec_sort(&lines);
     SVEC_FOR_EACH (i, line, &lines) {
@@ -769,6 +873,32 @@ vlog_get_levels(void)
     return ds_cstr(&s);
 }
 
+/* Returns as a string current logging patterns for each destination.
+ * This string must be released by caller. */
+char *
+vlog_get_patterns(void)
+{
+    struct ds ds = DS_EMPTY_INITIALIZER;
+    enum vlog_destination destination;
+
+    ovs_rwlock_rdlock(&pattern_rwlock);
+    ds_put_format(&ds, "         prefix                            format\n");
+    ds_put_format(&ds, "         ------                            ------\n");
+
+    for (destination = 0; destination < VLF_N_DESTINATIONS; destination++) {
+        struct destination *f = &destinations[destination];
+        const char *prefix = "none";
+
+        if (destination == VLF_SYSLOG && syslogger) {
+            prefix = syslog_get_prefix(syslogger);
+        }
+        ds_put_format(&ds, "%-7s  %-32s  %s\n", f->name, prefix, f->pattern);
+    }
+    ovs_rwlock_unlock(&pattern_rwlock);
+
+    return ds_cstr(&ds);
+}
+
 /* Returns true if a log message emitted for the given 'module' and 'level'
  * would cause some log output, false if that module and level are completely
  * disabled. */
@@ -971,7 +1101,7 @@ vlog_valist(const struct vlog_module *module, enum vlog_level level,
                  line = strtok_r(NULL, "\n", &save_ptr)) {
                 atomic_read_explicit(&log_facility, &facility,
                                      memory_order_relaxed);
-                syslog(syslog_level|facility, "%s", line);
+                syslogger->class->syslog(syslogger, syslog_level|facility, line);
             }
 
             if (syslog_fd >= 0) {
@@ -1154,6 +1284,8 @@ Logging options:\n\
   -v, --verbose            set maximum verbosity level\n\
   --log-file[=FILE]        enable logging to specified FILE\n\
                            (default: %s/%s.log)\n\
+  --syslog-method=(libc|unix:file|udp:ip:port)\n\
+                           specify how to send messages to syslog daemon\n\
   --syslog-target=HOST:PORT  also send syslog msgs to HOST:PORT via UDP\n",
            ovs_logdir(), program_name);
 }