{
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,
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
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);
#define DAEMON_H 1
#include <stdbool.h>
+#include <sys/types.h>
#define DAEMON_LONG_OPTIONS \
{"detach", no_argument, 0, 'D'}, \
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 */
VLOG_MODULE(vconn_unix)
VLOG_MODULE(vconn)
VLOG_MODULE(vlog)
+VLOG_MODULE(vlog_socket)
#ifdef HAVE_EXT
#include "ext/vlogext-modules.def"
/* 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
/* 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);
#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 { \
} while (0)
extern enum vlog_level min_vlog_levels[VLM_N_MODULES];
+
#endif /* vlog.h */
" -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;
+}
#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 {
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");
}
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. */
{
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'. */
#include <syslog.h>
#include <time.h>
#include <unistd.h>
+#include "dirs.h"
#include "dynamic-string.h"
#include "sat-math.h"
#include "timeval.h"
/* 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
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;
}
}
}
+/* 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]] ...
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);
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;
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;
}
}
+ 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;
}
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);
+}
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},
{"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},
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
" --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);
" -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);
" 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);
}
"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);
" --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);
}
#include "daemon.h"
#include "timeval.h"
#include "util.h"
+#include "vlog.h"
/* -s, --signal: signal to send. */
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);
set_program_name(argv[0]);
time_init();
+ vlog_init();
parse_options(argc, argv);
argc -= optind;
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);
}
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[])
{
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);
/* 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;
}
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;