vlog: Add ability to log to an arbitrary file, and following related changes:
authorBen Pfaff <blp@nicira.com>
Thu, 23 Oct 2008 17:45:39 +0000 (10:45 -0700)
committerBen Pfaff <blp@nicira.com>
Thu, 23 Oct 2008 21:07:25 +0000 (14:07 -0700)
* New command-line options for configuring log files, hence:
  - Centralized vlog usage messages.
* New vlogconf action for reopening log files.
* New vlogconf support for specifying a target by pidfile.

14 files changed:
controller/controller.c
include/daemon.h
include/vlog-modules.def
include/vlog.h
lib/daemon.c
lib/vlog-socket.c
lib/vlog.c
secchan/secchan.c
switch/switch.c
tests/test-dhcp-client.c
utilities/dpctl.c
utilities/ofp-discover.c
utilities/ofp-kill.c
utilities/vlogconf.c

index df8ca16..9a33418 100644 (file)
@@ -239,13 +239,13 @@ parse_options(int argc, char *argv[])
 {
     enum {
         OPT_MAX_IDLE = UCHAR_MAX + 1,
-        OPT_PEER_CA_CERT
+        OPT_PEER_CA_CERT,
+        VLOG_OPTION_ENUMS
     };
     static struct option long_options[] = {
         {"hub",         no_argument, 0, 'H'},
         {"noflow",      no_argument, 0, 'n'},
         {"max-idle",    required_argument, 0, OPT_MAX_IDLE},
-        {"verbose",     optional_argument, 0, 'v'},
         {"help",        no_argument, 0, 'h'},
         {"version",     no_argument, 0, 'V'},
         DAEMON_LONG_OPTIONS,
@@ -294,10 +294,7 @@ parse_options(int argc, char *argv[])
             printf("%s "VERSION" compiled "__DATE__" "__TIME__"\n", argv[0]);
             exit(EXIT_SUCCESS);
 
-        case 'v':
-            vlog_set_verbosity(optarg);
-            break;
-
+        VLOG_OPTION_HANDLERS
         DAEMON_OPTION_HANDLERS
 
 #ifdef HAVE_OPENSSL
@@ -327,12 +324,11 @@ usage(void)
            program_name, program_name);
     vconn_usage(true, true, false);
     daemon_usage();
+    vlog_usage();
     printf("\nOther options:\n"
            "  -H, --hub               act as hub instead of learning switch\n"
            "  -n, --noflow            pass traffic, but don't add flows\n"
            "  --max-idle=SECS         max idle time for new flows\n"
-           "  -v, --verbose=MODULE[:FACILITY[:LEVEL]]  set logging levels\n"
-           "  -v, --verbose           set maximum verbosity level\n"
            "  -h, --help              display this help message\n"
            "  -V, --version           display version information\n");
     exit(EXIT_SUCCESS);
index 94d369a..821465f 100644 (file)
@@ -35,6 +35,7 @@
 #define DAEMON_H 1
 
 #include <stdbool.h>
+#include <sys/types.h>
 
 #define DAEMON_LONG_OPTIONS                         \
         {"detach",      no_argument, 0, 'D'},       \
@@ -62,5 +63,6 @@ void daemonize(void);
 void die_if_already_running(void);
 void ignore_existing_pidfile(void);
 void daemon_usage(void);
+pid_t read_pidfile(const char *name);
 
 #endif /* daemon.h */
index 9edb8a8..3b805c7 100644 (file)
@@ -29,6 +29,7 @@ VLOG_MODULE(vconn_stream)
 VLOG_MODULE(vconn_unix)
 VLOG_MODULE(vconn)
 VLOG_MODULE(vlog)
+VLOG_MODULE(vlog_socket)
 
 #ifdef HAVE_EXT
 #include "ext/vlogext-modules.def"
index 6530de6..350475b 100644 (file)
@@ -59,7 +59,8 @@ enum vlog_level vlog_get_level_val(const char *name);
 /* Facilities that we can log to. */
 #define VLOG_FACILITIES                                         \
     VLOG_FACILITY(SYSLOG, "%05N|%c|%p|%m")                      \
-    VLOG_FACILITY(CONSOLE, "%d{%b %d %H:%M:%S}|%05N|%c|%p|%m")
+    VLOG_FACILITY(CONSOLE, "%d{%b %d %H:%M:%S}|%05N|%c|%p|%m")  \
+    VLOG_FACILITY(FILE, "%d{%b %d %H:%M:%S}|%05N|%c|%p|%m")
 enum vlog_facility {
 #define VLOG_FACILITY(NAME, PATTERN) VLF_##NAME,
     VLOG_FACILITIES
@@ -115,12 +116,17 @@ struct vlog_rate_limit {
 /* Configuring how each module logs messages. */
 enum vlog_level vlog_get_level(enum vlog_module, enum vlog_facility);
 void vlog_set_levels(enum vlog_module, enum vlog_facility, enum vlog_level);
-void vlog_set_pattern(enum vlog_facility, const char *pattern);
 char *vlog_set_levels_from_string(const char *);
 char *vlog_get_levels(void);
 bool vlog_is_enabled(enum vlog_module, enum vlog_level);
 void vlog_set_verbosity(const char *arg);
 
+/* Configuring log facilities. */
+void vlog_set_pattern(enum vlog_facility, const char *pattern);
+const char *vlog_get_log_file(void);
+int vlog_set_log_file(const char *file_name);
+int vlog_reopen_log_file(void);
+
 /* Function for actual logging. */
 void vlog_init(void);
 void vlog_exit(void);
@@ -164,6 +170,20 @@ void vlog_rate_limit(enum vlog_module, enum vlog_level,
 #define VLOG_DBG_RL(RL, ...) \
         vlog_rate_limit(THIS_MODULE, VLL_DBG, RL, __VA_ARGS__)
 
+/* Command line processing. */
+#define VLOG_OPTION_ENUMS OPT_LOG_FILE
+#define VLOG_LONG_OPTIONS                                   \
+        {"verbose",     optional_argument, 0, 'v'},         \
+        {"log-file",    optional_argument, 0, OPT_LOG_FILE}
+#define VLOG_OPTION_HANDLERS                    \
+        case 'v':                               \
+            vlog_set_verbosity(optarg);         \
+            break;                              \
+        case OPT_LOG_FILE:                      \
+            vlog_set_log_file(optarg);          \
+            break;
+void vlog_usage(void);
+
 /* Implementation details. */
 #define VLOG(LEVEL, ...)                                \
     do {                                                \
@@ -173,4 +193,5 @@ void vlog_rate_limit(enum vlog_module, enum vlog_level,
     } while (0)
 extern enum vlog_level min_vlog_levels[VLM_N_MODULES];
 
+
 #endif /* vlog.h */
index 8800d0d..b8fc5af 100644 (file)
@@ -246,3 +246,51 @@ daemon_usage(void)
         "  -f, --force             with -P, start even if already running\n",
         ofp_rundir, program_name);
 }
+
+/* Opens and reads a PID from 'pidfile'.  Returns the nonnegative PID if
+ * successful, otherwise a negative errno value. */
+pid_t
+read_pidfile(const char *pidfile)
+{
+    char line[128];
+    struct flock lck;
+    FILE *file;
+    int error;
+
+    file = fopen(pidfile, "r");
+    if (!file) {
+        error = errno;
+        VLOG_WARN("%s: open: %s", pidfile, strerror(error));
+        goto error;
+    }
+
+    lck.l_type = F_WRLCK;
+    lck.l_whence = SEEK_SET;
+    lck.l_start = 0;
+    lck.l_len = 0;
+    if (fcntl(fileno(file), F_GETLK, &lck)) {
+        error = errno;
+        VLOG_WARN("%s: fcntl: %s", pidfile, strerror(error));
+        goto error;
+    }
+
+    if (!fgets(line, sizeof line, file)) {
+        error = errno;
+        VLOG_WARN("%s: read: %s", pidfile, strerror(error));
+        goto error;
+    }
+
+    if (lck.l_pid != strtoul(line, NULL, 10)) {
+        error = ESRCH;
+        VLOG_WARN("l_pid (%ld) != %s pid (%s)",
+                   (long int) lck.l_pid, pidfile, line);
+        goto error;
+    }
+
+    fclose(file);
+    return lck.l_pid;
+
+error:
+    fclose(file);
+    return -error;
+}
index 7fffc13..7cfc5d7 100644 (file)
@@ -33,6 +33,7 @@
 
 #include <config.h>
 #include "vlog-socket.h"
+#include <ctype.h>
 #include <errno.h>
 #include <sys/un.h>
 #include <fcntl.h>
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <unistd.h>
+#include "daemon.h"
 #include "fatal-signal.h"
 #include "poll-loop.h"
 #include "socket-util.h"
 #include "timeval.h"
 #include "util.h"
-#include "vlog.h"
 
 #ifndef SCM_CREDENTIALS
 #include <time.h>
 #endif
+
+#define THIS_MODULE VLM_vlog_socket
+#include "vlog.h"
 \f
 /* Server for Vlog control connection. */
 struct vlog_server {
@@ -256,6 +260,12 @@ poll_server(int fd UNUSED, short int events, void *server_)
             reply = msg ? msg : xstrdup("ack");
         } else if (!strcmp(cmd_buf, "list")) {
             reply = vlog_get_levels();
+        } else if (!strcmp(cmd_buf, "reopen")) {
+            int error = vlog_reopen_log_file();
+            reply = (error
+                     ? xasprintf("could not reopen log file \"%s\": %s",
+                                 vlog_get_log_file(), strerror(error))
+                     : xstrdup("ack"));
         } else {
             reply = xstrdup("nak");
         }
@@ -274,11 +284,18 @@ struct vlog_client {
     int fd;
 };
 
-/* Connects to a Vlog server socket.  If 'path' does not start with '/', then
- * it start with a PID as a string.  If a non-null, non-absolute name was
- * passed to Vlog_server_socket::listen(), then it must follow the PID in
- * 'path'.  If 'path' starts with '/', then it must be an absolute path that
- * gives the exact name of the Unix domain socket to connect to.
+/* Connects to a Vlog server socket.  'path' may be:
+ *
+ *      - A string that starts with a PID.  If a non-null, non-absolute name
+ *        was passed to Vlog_server_socket::listen(), then it must follow the
+ *        PID in 'path'.
+ *
+ *      - An absolute path (starting with '/') to a Vlog server socket or a
+ *        pidfile.  If it is a pidfile, the pidfile will be read and translated
+ *        into a Vlog server socket file name.
+ *
+ *      - A relative path, which is translated into a pidfile name and then
+ *        treated as above.
  *
  * Returns 0 if successful, otherwise a positive errno value.  If successful,
  * sets '*clientp' to the new vlog_client, otherwise to NULL. */
@@ -287,29 +304,52 @@ vlog_client_connect(const char *path, struct vlog_client **clientp)
 {
     static int counter;
     struct vlog_client *client;
-    int fd;
+    struct stat s;
+    int error;
 
     client = xmalloc(sizeof *client);
-    client->connect_path = (path[0] == '/'
-                            ? xstrdup(path)
-                            : xasprintf("/tmp/vlogs.%s", path));
-
-    client->bind_path = xasprintf("/tmp/vlog.%ld.%d",
-                                  (long int) getpid(), counter++);
-    fd = make_unix_socket(SOCK_DGRAM, false, false,
-                          client->bind_path, client->connect_path);
-
-    if (fd >= 0) {
-        client->fd = fd;
-        *clientp = client;
-        return 0;
+    if (path[0] == '/') {
+        client->connect_path = xstrdup(path);
+    } else if (isdigit((unsigned char) path[0])) {
+        client->connect_path = xasprintf("/tmp/vlogs.%s", path);
     } else {
+        client->connect_path = make_pidfile_name(path);
+    }
+    client->bind_path = NULL;
+
+    if (stat(client->connect_path, &s)) {
+        error = errno;
+        VLOG_WARN("could not stat \"%s\": %s",
+                  client->connect_path, strerror(error));
+        goto error;
+    } else if (S_ISREG(s.st_mode)) {
+        pid_t pid = read_pidfile(client->connect_path);
+        if (pid < 0) {
+            error = -pid;
+            VLOG_WARN("could not read pidfile \"%s\": %s",
+                      client->connect_path, strerror(error));
+            goto error;
+        }
         free(client->connect_path);
-        free(client->bind_path);
-        free(client);
-        *clientp = NULL;
-        return errno;
+        client->connect_path = xasprintf("/tmp/vlogs.%ld", (long int) pid);
     }
+    client->bind_path = xasprintf("/tmp/vlog.%ld.%d",
+                                  (long int) getpid(), counter++);
+    client->fd = make_unix_socket(SOCK_DGRAM, false, false,
+                                  client->bind_path, client->connect_path);
+    if (client->fd < 0) {
+        error = -client->fd;
+        goto error;
+    }
+    *clientp = client;
+    return 0;
+
+error:
+    free(client->connect_path);
+    free(client->bind_path);
+    free(client);
+    *clientp = NULL;
+    return error;
 }
 
 /* Destroys 'client'. */
index 756433c..ddfab76 100644 (file)
@@ -43,6 +43,7 @@
 #include <syslog.h>
 #include <time.h>
 #include <unistd.h>
+#include "dirs.h"
 #include "dynamic-string.h"
 #include "sat-math.h"
 #include "timeval.h"
@@ -93,6 +94,10 @@ enum vlog_level min_vlog_levels[VLM_N_MODULES];
 /* Time at which vlog was initialized, in milliseconds. */
 static long long int boot_time;
 
+/* VLF_FILE configuration. */
+static char *log_file_name;
+static FILE *log_file;
+
 /* Searches the 'n_names' in 'names'.  Returns the index of a match for
  * 'target', or 'n_names' if no name matches. */
 static size_t
@@ -179,7 +184,9 @@ update_min_level(enum vlog_module module)
     enum vlog_facility facility;
 
     for (facility = 0; facility < VLF_N_FACILITIES; facility++) {
-        min_level = MAX(min_level, levels[module][facility]);
+        if (log_file || facility != VLF_FILE) {
+            min_level = MAX(min_level, levels[module][facility]); 
+        }
     }
     min_vlog_levels[module] = min_level;
 }
@@ -243,6 +250,71 @@ vlog_set_pattern(enum vlog_facility facility, const char *pattern)
     }
 }
 
+/* Returns the name of the log file used by VLF_FILE, or a null pointer if no
+ * log file has been set.  (A non-null return value does not assert that the
+ * named log file is in use: if vlog_set_log_file() or vlog_reopen_log_file()
+ * fails, it still sets the log file name.) */
+const char *
+vlog_get_log_file(void)
+{
+    return log_file_name;
+}
+
+/* Sets the name of the log file used by VLF_FILE to 'file_name', or to the
+ * default file name if 'file_name' is null.  Returns 0 if successful,
+ * otherwise a positive errno value. */
+int
+vlog_set_log_file(const char *file_name)
+{
+    char *old_log_file_name;
+    enum vlog_module module;
+    int error;
+
+    /* Close old log file. */
+    if (log_file) {
+        VLOG_WARN("closing log file");
+        fclose(log_file);
+        log_file = NULL;
+    }
+
+    /* Update log file name and free old name.  The ordering is important
+     * because 'file_name' might be 'log_file_name' or some suffix of it. */
+    old_log_file_name = log_file_name;
+    log_file_name = (file_name
+                     ? xstrdup(file_name)
+                     : xasprintf("%s/%s.log", ofp_logdir, program_name));
+    free(old_log_file_name);
+    file_name = NULL;           /* Might have been freed. */
+
+    /* Open new log file and update min_levels[] to reflect whether we actually
+     * have a log_file. */
+    log_file = fopen(log_file_name, "a");
+    for (module = 0; module < VLM_N_MODULES; module++) {
+        update_min_level(module);
+    }
+
+    /* Log success or failure. */
+    if (!log_file) {
+        VLOG_WARN("failed to open %s for logging: %s",
+                  log_file_name, strerror(errno));
+        error = errno;
+    } else {
+        VLOG_WARN("opened log file %s", log_file_name);
+        error = 0;
+    }
+
+    return error;
+}
+
+/* Closes and then attempts to re-open the current log file.  (This is useful
+ * just after log rotation, to ensure that the new log file starts being used.)
+ * Returns 0 if successful, otherwise a positive errno value. */
+int
+vlog_reopen_log_file(void)
+{
+    return vlog_set_log_file(log_file_name);
+}
+
 /* Set debugging levels:
  *
  *  mod[:facility[:level]] mod2[:facility[:level]] ...
@@ -358,14 +430,15 @@ vlog_get_levels(void)
     struct ds s = DS_EMPTY_INITIALIZER;
     enum vlog_module module;
 
-    ds_put_format(&s, "                 console    syslog\n");
-    ds_put_format(&s, "                 -------    ------\n");
+    ds_put_format(&s, "                 console    syslog    file\n");
+    ds_put_format(&s, "                 -------    ------    ------\n");
 
     for (module = 0; module < VLM_N_MODULES; module++) {
-        ds_put_format(&s, "%-16s  %4s       %4s\n",
+        ds_put_format(&s, "%-16s  %4s       %4s       %4s\n",
            vlog_get_module_name(module),
            vlog_get_level_name(vlog_get_level(module, VLF_CONSOLE)),
-           vlog_get_level_name(vlog_get_level(module, VLF_SYSLOG)));
+           vlog_get_level_name(vlog_get_level(module, VLF_SYSLOG)),
+           vlog_get_level_name(vlog_get_level(module, VLF_FILE)));
     }
 
     return ds_cstr(&s);
@@ -489,9 +562,10 @@ void
 vlog_valist(enum vlog_module module, enum vlog_level level,
             const char *message, va_list args)
 {
-    bool log_console = levels[module][VLF_CONSOLE] >= level;
-    bool log_syslog = levels[module][VLF_SYSLOG] >= level;
-    if (log_console || log_syslog) {
+    bool log_to_console = levels[module][VLF_CONSOLE] >= level;
+    bool log_to_syslog = levels[module][VLF_SYSLOG] >= level;
+    bool log_to_file = levels[module][VLF_FILE] >= level && log_file;
+    if (log_to_console || log_to_syslog || log_to_file) {
         int save_errno = errno;
         static unsigned int msg_num;
         struct ds s;
@@ -500,14 +574,14 @@ vlog_valist(enum vlog_module module, enum vlog_level level,
         ds_reserve(&s, 1024);
         msg_num++;
 
-        if (log_console) {
+        if (log_to_console) {
             format_log_message(module, level, VLF_CONSOLE, msg_num,
                                message, args, &s);
             ds_put_char(&s, '\n');
             fputs(ds_cstr(&s), stderr);
         }
 
-        if (log_syslog) {
+        if (log_to_syslog) {
             int syslog_level = syslog_levels[level];
             char *save_ptr = NULL;
             char *line;
@@ -520,6 +594,14 @@ vlog_valist(enum vlog_module module, enum vlog_level level,
             }
         }
 
+        if (log_to_file) {
+            format_log_message(module, level, VLF_FILE, msg_num,
+                               message, args, &s);
+            ds_put_char(&s, '\n');
+            fputs(ds_cstr(&s), log_file);
+            fflush(log_file);
+        }
+
         ds_destroy(&s);
         errno = save_errno;
     }
@@ -578,3 +660,14 @@ vlog_rate_limit(enum vlog_module module, enum vlog_level level,
         rl->n_dropped = 0;
     }
 }
+
+void
+vlog_usage(void) 
+{
+    printf("\nLogging options:\n"
+           "  -v, --verbose=MODULE[:FACILITY[:LEVEL]]  set logging levels\n"
+           "  -v, --verbose           set maximum verbosity level\n"
+           "  --log-file[=FILE]       enable logging to specified FILE\n"
+           "                          (default: %s/%s.log)\n",
+           ofp_logdir, program_name);
+}
index 8311d13..37f109a 100644 (file)
@@ -2514,7 +2514,8 @@ parse_options(int argc, char *argv[], struct settings *s)
         OPT_STP,
         OPT_NO_STP,
         OPT_OUT_OF_BAND,
-        OPT_IN_BAND
+        OPT_IN_BAND,
+        VLOG_OPTION_ENUMS
     };
     static struct option long_options[] = {
         {"accept-vconn", required_argument, 0, OPT_ACCEPT_VCONN},
@@ -2535,6 +2536,7 @@ parse_options(int argc, char *argv[], struct settings *s)
         {"help",        no_argument, 0, 'h'},
         {"version",     no_argument, 0, 'V'},
         DAEMON_LONG_OPTIONS,
+        VLOG_LONG_OPTIONS,
 #ifdef HAVE_OPENSSL
         VCONN_SSL_LONG_OPTIONS
         {"bootstrap-ca-cert", required_argument, 0, OPT_BOOTSTRAP_CA_CERT},
@@ -2670,12 +2672,10 @@ parse_options(int argc, char *argv[], struct settings *s)
             printf("%s "VERSION" compiled "__DATE__" "__TIME__"\n", argv[0]);
             exit(EXIT_SUCCESS);
 
-        case 'v':
-            vlog_set_verbosity(optarg);
-            break;
-
         DAEMON_OPTION_HANDLERS
 
+        VLOG_OPTION_HANDLERS
+
 #ifdef HAVE_OPENSSL
         VCONN_SSL_OPTION_HANDLERS
 
@@ -2770,9 +2770,8 @@ usage(void)
            "  --rate-limit[=PACKETS]  max rate, in packets/s (default: 1000)\n"
            "  --burst-limit=BURST     limit on packet credit for idle time\n");
     daemon_usage();
+    vlog_usage();
     printf("\nOther options:\n"
-           "  -v, --verbose=MODULE[:FACILITY[:LEVEL]]  set logging levels\n"
-           "  -v, --verbose           set maximum verbosity level\n"
            "  -h, --help              display this help message\n"
            "  -V, --version           display version information\n");
     exit(EXIT_SUCCESS);
index e24ea67..84ed8de 100644 (file)
@@ -299,9 +299,8 @@ usage(void)
            "  -l, --listen=METHOD     allow management connections on METHOD\n"
            "                          (a passive OpenFlow connection method)\n");
     daemon_usage();
+    vlog_usage();
     printf("\nOther options:\n"
-           "  -v, --verbose=MODULE[:FACILITY[:LEVEL]]  set logging levels\n"
-           "  -v, --verbose           set maximum verbosity level\n"
            "  -h, --help              display this help message\n"
            "  -V, --version           display version information\n");
     exit(EXIT_SUCCESS);
index 0a7c1a1..039ffed 100644 (file)
@@ -194,13 +194,12 @@ usage(void)
            "                          do not request a specific IP)\n"
            "  --vendor-class=STRING   use STRING as vendor class (default:\n"
            "                          none); use OpenFlow to imitate secchan\n"
-           "  --no-resolv-conf        do not update /etc/resolv.conf\n"
-           "\nOther options:\n"
-           "  -v, --verbose=MODULE[:FACILITY[:LEVEL]]  set logging levels\n"
-           "  -v, --verbose           set maximum verbosity level\n"
-           "  -h, --help              display this help message\n"
-           "  -V, --version           display version information\n",
+           "  --no-resolv-conf        do not update /etc/resolv.conf\n",
            program_name, program_name);
+    vlog_usage();
+    printf("\nOther options:\n"
+           "  -h, --help              display this help message\n"
+           "  -V, --version           display version information\n");
     exit(EXIT_SUCCESS);
 }
 
index 40f985a..23b9db4 100644 (file)
@@ -238,11 +238,10 @@ usage(void)
            "where each SWITCH is an active OpenFlow connection method.\n",
            program_name, program_name);
     vconn_usage(true, false, false);
-    printf("\nOptions:\n"
+    vlog_usage();
+    printf("\nOther options:\n"
            "  --strict                    use strict match for flow commands\n"
            "  -t, --timeout=SECS          give up after SECS seconds\n"
-           "  -v, --verbose=MODULE[:FACILITY[:LEVEL]]  set logging levels\n"
-           "  -v, --verbose               set maximum verbosity level\n"
            "  -h, --help                  display this help message\n"
            "  -V, --version               display version information\n");
     exit(EXIT_SUCCESS);
index d48f61a..402d589 100644 (file)
@@ -405,15 +405,15 @@ usage(void)
            "  --accept-vconn=REGEX    accept matching discovered controllers\n"
            "  --exit-without-bind     exit after discovery, without binding\n"
            "  --exit-after-bind       exit after discovery, after binding\n"
-           "  --no-detach             do not detach after discovery\n"
-           "\nOther options:\n"
+           "  --no-detach             do not detach after discovery\n",
+           program_name, program_name);
+    vlog_usage();
+    printf("\nOther options:\n"
            "  -t, --timeout=SECS      give up discovery after SECS seconds\n"
            "  -P, --pidfile[=FILE]    create pidfile (default: %s/%s.pid)\n"
            "  -f, --force             with -P, start even if already running\n"
-           "  -v, --verbose=MODULE[:FACILITY[:LEVEL]]  set logging levels\n"
-           "  -v, --verbose           set maximum verbosity level\n"
            "  -h, --help              display this help message\n"
            "  -V, --version           display version information\n",
-           program_name, program_name, ofp_rundir, program_name);
+           ofp_rundir, program_name);
     exit(EXIT_SUCCESS);
 }
index 4518ad7..a1c1e84 100644 (file)
@@ -43,6 +43,7 @@
 #include "daemon.h"
 #include "timeval.h"
 #include "util.h"
+#include "vlog.h"
 
 /* -s, --signal: signal to send. */
 static int sig_nr = SIGTERM;
@@ -51,7 +52,6 @@ static int sig_nr = SIGTERM;
 static bool force;
 
 static void cond_error(int err_no, const char *, ...) PRINTF_FORMAT(2, 3);
-static bool kill_pidfile(const char *pidfile, FILE *);
 
 static void parse_options(int argc, char *argv[]);
 static void usage(void);
@@ -64,6 +64,7 @@ main(int argc, char *argv[])
 
     set_program_name(argv[0]);
     time_init();
+    vlog_init();
     parse_options(argc, argv);
 
     argc -= optind;
@@ -77,16 +78,16 @@ main(int argc, char *argv[])
 
     for (i = 0; i < argc; i++) {
         char *pidfile;
-        FILE *file;
+        pid_t pid;
 
         pidfile = make_pidfile_name(argv[i]);
-        file = fopen(pidfile, "r");
-        if (!file) {
-            ok = false;
-            cond_error(errno, "%s: open", pidfile);
+        pid = read_pidfile(pidfile);
+        if (pid >= 0) {
+            if (kill(pid, sig_nr) < 0) {
+                cond_error(errno, "%s: kill(%ld)", pidfile, (long int) pid);
+            }
         } else {
-            ok = kill_pidfile(argv[i], file) && ok;
-            fclose(file);
+            cond_error(-pid, "could not read %s", pidfile);
         }
         free(pidfile);
     }
@@ -94,40 +95,6 @@ main(int argc, char *argv[])
     return ok || force ? EXIT_SUCCESS : EXIT_FAILURE;
 }
 
-static bool
-kill_pidfile(const char *pidfile, FILE *file)
-{
-    char line[128];
-    struct flock lck;
-
-    lck.l_type = F_WRLCK;
-    lck.l_whence = SEEK_SET;
-    lck.l_start = 0;
-    lck.l_len = 0;
-    if (fcntl(fileno(file), F_GETLK, &lck)) {
-        cond_error(errno, "%s: fcntl", pidfile); 
-        return false;
-    }
-
-    if (!fgets(line, sizeof line, file)) {
-        cond_error(errno, "%s: read", pidfile);
-        return false;
-    }
-
-    if (lck.l_pid != strtoul(line, NULL, 10)) {
-        cond_error(errno, "l_pid (%ld) != %s pid (%s)",
-                   (long int) lck.l_pid, pidfile, line);
-        return false;
-    }
-
-    if (kill(lck.l_pid, sig_nr) < 0) {
-        cond_error(errno, "%s: kill(%ld)", pidfile, (long int) lck.l_pid);
-        return false;
-    }
-
-    return true;
-}
-
 static void
 parse_options(int argc, char *argv[])
 {
index 006344b..6da0c17 100644 (file)
@@ -53,15 +53,17 @@ usage(char *prog_name, int exit_code)
     printf("Usage: %s [TARGET] [ACTION...]\n"
            "Targets:\n"
            "  -a, --all            Apply to all targets (default)\n"
-           "  -t, --target=TARGET  Specify target program, as a pid or an\n"
-           "                       absolute path to a Unix domain socket\n"
+           "  -t, --target=TARGET  Specify target program, as a pid, a\n"
+           "                       pidfile, or an absolute path to a Unix\n"
+           "                       domain socket\n"
            "Actions:\n"
            "  -l, --list         List current settings\n"
            "  -s, --set=MODULE[:FACILITY[:LEVEL]]\n"
            "        Set MODULE and FACILITY log level to LEVEL\n"
            "        MODULE may be any valid module name or 'ANY'\n"
-           "        FACILITY may be 'syslog' or 'console' or 'ANY' (default)\n"
-           "        LEVEL may be 'emer', 'err', 'warn', or 'dbg (default)'\n"
+           "        FACILITY may be 'syslog', 'console', 'file', or 'ANY' (default)\n"
+           "        LEVEL may be 'emer', 'err', 'warn', or 'dbg' (default)\n"
+           "  -r, --reopen       Make the program reopen its log file\n"
            "  -h, --help         Print this helpful information\n",
            prog_name);
     exit(exit_code);
@@ -147,6 +149,7 @@ int main(int argc, char *argv[])
         /* Action options come afterward. */
         {"list", no_argument, NULL, 'l'},
         {"set", required_argument, NULL, 's'},
+        {"reopen", no_argument, NULL, 'r'},
         {0, 0, 0, 0},
     };
     char *short_options;
@@ -204,6 +207,15 @@ int main(int argc, char *argv[])
             }
             break;
 
+        case 'r':
+            for (i = 0; i < n_clients; i++) {
+                struct vlog_client *client = clients[i];
+                char *request = xasprintf("reopen");
+                transact_ack(client, request, &ok);
+                free(request);
+            }
+            break;
+
         case 'h':
             usage(argv[0], EXIT_SUCCESS);
             break;