diff --git a/include/windows/syslog.h b/include/windows/syslog.h index 41267da1c..3925ed641 100644 --- a/include/windows/syslog.h +++ b/include/windows/syslog.h @@ -28,6 +28,19 @@ #define LOG_NDELAY 8 /* don't delay open */ #define LOG_DAEMON 24 /* system daemons */ +#define LOG_KERN (0<<3) /* kernel messages */ +#define LOG_USER (1<<3) /* user-level messages */ +#define LOG_MAIL (2<<3) /* mail system */ +#define LOG_DAEMON (3<<3) /* system daemons */ +#define LOG_AUTH (4<<3) /* security/authorization messages */ +#define LOG_SYSLOG (5<<3) /* messages generated internally by syslogd */ +#define LOG_LPR (6<<3) /* line printer subsystem */ +#define LOG_NEWS (7<<3) /* network news subsystem */ +#define LOG_UUCP (8<<3) /* UUCP subsystem */ +#define LOG_CRON (9<<3) /* clock daemon */ +#define LOG_AUTHPRIV (10<<3) /* security/authorization messages */ +#define LOG_FTP (11<<3) /* FTP daemon */ + #define LOG_LOCAL0 (16<<3) /* reserved for local use */ #define LOG_LOCAL1 (17<<3) /* reserved for local use */ #define LOG_LOCAL2 (18<<3) /* reserved for local use */ diff --git a/lib/vlog.c b/lib/vlog.c index c68002772..da7a307ea 100644 --- a/lib/vlog.c +++ b/lib/vlog.c @@ -110,6 +110,41 @@ static bool log_async OVS_GUARDED_BY(log_file_mutex); /* Syslog export configuration. */ static int syslog_fd OVS_GUARDED_BY(pattern_rwlock) = -1; +/* Log facility configuration. */ +static atomic_int log_facility = ATOMIC_VAR_INIT(0); + +/* Facility name and its value. */ +struct vlog_facility { + char *name; /* Name. */ + unsigned int value; /* Facility associated with 'name'. */ +}; +static struct vlog_facility vlog_facilities[] = { + {"kern", LOG_KERN}, + {"user", LOG_USER}, + {"mail", LOG_MAIL}, + {"daemon", LOG_DAEMON}, + {"auth", LOG_AUTH}, + {"syslog", LOG_SYSLOG}, + {"lpr", LOG_LPR}, + {"news", LOG_NEWS}, + {"uucp", LOG_UUCP}, + {"clock", LOG_CRON}, + {"ftp", LOG_FTP}, + {"ntp", 12<<3}, + {"audit", 13<<3}, + {"alert", 14<<3}, + {"clock2", 15<<3}, + {"local0", LOG_LOCAL0}, + {"local1", LOG_LOCAL1}, + {"local2", LOG_LOCAL2}, + {"local3", LOG_LOCAL3}, + {"local4", LOG_LOCAL4}, + {"local5", LOG_LOCAL5}, + {"local6", LOG_LOCAL6}, + {"local7", LOG_LOCAL7} +}; +static bool vlog_facility_exists(const char* facility, int *value); + static void format_log_message(const struct vlog_module *, enum vlog_level, const char *pattern, const char *message, va_list, struct ds *) @@ -419,6 +454,14 @@ vlog_set_levels_from_string(const char *s_) goto exit; } vlog_set_pattern(destination, save_ptr); + } else if (word && !strcasecmp(word, "FACILITY")) { + int value; + + if (!vlog_facility_exists(save_ptr, &value)) { + msg = xstrdup("invalid facility"); + goto exit; + } + atomic_store_explicit(&log_facility, value, memory_order_relaxed); } else { struct vlog_module *module = NULL; enum vlog_level level = VLL_N_LEVELS; @@ -507,6 +550,22 @@ vlog_set_syslog_target(const char *target) ovs_rwlock_unlock(&pattern_rwlock); } +/* Returns 'false' if 'facility' is not a valid string. If 'facility' + * is a valid string, sets 'value' with the integer value of 'facility' + * and returns 'true'. */ +static bool +vlog_facility_exists(const char* facility, int *value) +{ + size_t i; + for (i = 0; i < ARRAY_SIZE(vlog_facilities); i++) { + if (!strcasecmp(vlog_facilities[i].name, facility)) { + *value = vlog_facilities[i].value; + return true; + } + } + return false; +} + static void vlog_unixctl_set(struct unixctl_conn *conn, int argc, const char *argv[], void *aux OVS_UNUSED) @@ -614,6 +673,7 @@ vlog_init(void) if (ovsthread_once_start(&once)) { static char *program_name_copy; long long int now; + int facility; /* Do initialization work that needs to be done before any logging * occurs. We want to keep this really minimal because any attempt to @@ -625,7 +685,9 @@ vlog_init(void) * 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; - openlog(program_name_copy, LOG_NDELAY, LOG_DAEMON); + atomic_read_explicit(&log_facility, &facility, memory_order_relaxed); + openlog(program_name_copy, LOG_NDELAY, + facility ? facility : LOG_DAEMON); ovsthread_once_done(&once); /* Now do anything that we want to happen only once but doesn't have to @@ -739,6 +801,7 @@ format_log_message(const struct vlog_module *module, enum vlog_level level, char tmp[128]; va_list args; const char *p; + int facility; ds_clear(s); for (p = pattern; *p != '\0'; ) { @@ -773,7 +836,10 @@ format_log_message(const struct vlog_module *module, enum vlog_level level, ds_put_cstr(s, program_name); break; case 'B': - ds_put_format(s, "%d", LOG_LOCAL0 + syslog_levels[level]); + atomic_read_explicit(&log_facility, &facility, + memory_order_relaxed); + facility = facility ? facility : LOG_LOCAL0; + ds_put_format(s, "%d", facility + syslog_levels[level]); break; case 'c': p = fetch_braces(p, "", tmp, sizeof tmp); @@ -897,12 +963,15 @@ vlog_valist(const struct vlog_module *module, enum vlog_level level, int syslog_level = syslog_levels[level]; char *save_ptr = NULL; char *line; + int facility; format_log_message(module, level, destinations[VLF_SYSLOG].pattern, message, args, &s); for (line = strtok_r(s.string, "\n", &save_ptr); line; line = strtok_r(NULL, "\n", &save_ptr)) { - syslog(syslog_level, "%s", line); + atomic_read_explicit(&log_facility, &facility, + memory_order_relaxed); + syslog(syslog_level|facility, "%s", line); } if (syslog_fd >= 0) { diff --git a/lib/vlog.man b/lib/vlog.man index ddf14d82b..5ee34f894 100644 --- a/lib/vlog.man +++ b/lib/vlog.man @@ -54,6 +54,17 @@ Sets the maximum logging verbosity level, equivalent to Sets the log pattern for \fIdestination\fR to \fIpattern\fR. Refer to \fBovs\-appctl\fR(8) for a description of the valid syntax for \fIpattern\fR. . +.IP "\fB\-vFACILITY:\fIfacility\fR" +.IQ "\fB\-\-verbose=FACILITY:\fIfacility\fR" +Sets the RFC5424 facility of the log message. \fIfacility\fR can be one of +\fBkern\fR, \fBuser\fR, \fBmail\fR, \fBdaemon\fR, \fBauth\fR, \fBsyslog\fR, +\fBlpr\fR, \fBnews\fR, \fBuucp\fR, \fBclock\fR, \fBftp\fR, \fBntp\fR, +\fBaudit\fR, \fBalert\fR, \fBclock2\fR, \fBlocal0\fR, \fBlocal1\fR, +\fBlocal2\fR, \fBlocal3\fR, \fBlocal4\fR, \fBlocal5\fR, \fBlocal6\fR or +\fBlocal7\fR. If this option is not specified, \fBdaemon\fR is used as +the default for the local system syslog and \fBlocal0\fR is used while sending +a message to the target provided via the \fB\-\-syslog\-target\fR option. +. .TP \fB\-\-log\-file\fR[\fB=\fIfile\fR] Enables logging to a file. If \fIfile\fR is specified, then it is diff --git a/python/ovs/vlog.py b/python/ovs/vlog.py index 105d126fb..5690924c6 100644 --- a/python/ovs/vlog.py +++ b/python/ovs/vlog.py @@ -40,6 +40,11 @@ LEVELS = { "emer": logging.CRITICAL, "off": logging.CRITICAL } +FACILITIES = ['auth', 'authpriv', 'cron', 'daemon', 'ftp', 'kern', 'lpr', + 'mail', 'news', 'syslog', 'user', 'uucp', 'local0', 'local1', + 'local2', 'local3', 'local4', 'local5', 'local6', 'local7'] +syslog_facility = "daemon" +syslog_handler = '' def get_level(level_str): @@ -224,9 +229,7 @@ class Vlog: if f == "console": logger.addHandler(logging.StreamHandler(sys.stderr)) elif f == "syslog": - logger.addHandler(logging.handlers.SysLogHandler( - address="/dev/log", - facility=logging.handlers.SysLogHandler.LOG_DAEMON)) + Vlog.add_syslog_handler() elif f == "file" and Vlog.__log_file: Vlog.__file_handler = logging.FileHandler(Vlog.__log_file) logger.addHandler(Vlog.__file_handler) @@ -280,6 +283,26 @@ class Vlog: destination = destination.lower() Vlog.__log_patterns[destination] = pattern + @staticmethod + def add_syslog_handler(facility=None): + global syslog_facility, syslog_handler + + # If handler is already added and there is no change in 'facility', + # there is nothing to do. + if (not facility or facility == syslog_facility) and syslog_handler: + return + + if facility: + syslog_facility = facility + + logger = logging.getLogger('syslog') + if syslog_handler: + logger.removeHandler(syslog_handler) + syslog_handler = logging.handlers.SysLogHandler(address="/dev/log", + facility=syslog_facility) + logger.addHandler(syslog_handler) + return + @staticmethod def set_levels_from_string(s): module = None @@ -298,6 +321,12 @@ class Vlog: return "Destination %s does not exist" % words[1] except IndexError: return "Please supply a valid pattern and destination" + elif words[0] == "FACILITY": + if words[1] in FACILITIES: + Vlog.add_syslog_handler(words[1]) + return + else: + return "Facility %s is invalid" % words[1] for word in [w.lower() for w in words]: if word == "any": diff --git a/tests/vlog.at b/tests/vlog.at index 4a143cdeb..fdd6732b5 100644 --- a/tests/vlog.at +++ b/tests/vlog.at @@ -234,3 +234,66 @@ AT_CHECK([APPCTL -t test-unixctl.py vlog/set pattern:file:'I<3OVS|%m']) AT_CHECK([APPCTL -t test-unixctl.py log patterntest]) AT_CHECK([grep -q 'I<3OVS' log]) AT_CLEANUP + +AT_SETUP([vlog - RFC5424 facility]) +OVS_RUNDIR=`pwd`; export OVS_RUNDIR +OVS_LOGDIR=`pwd`; export OVS_LOGDIR +OVS_DBDIR=`pwd`; export OVS_DBDIR +OVS_SYSCONFDIR=`pwd`; export OVS_SYSCONFDIR +ON_EXIT([kill `cat ovsdb-server.pid`]) + +dnl Create database. +touch .conf.db.~lock~ +AT_CHECK([ovsdb-tool create conf.db $abs_top_srcdir/vswitchd/vswitch.ovsschema]) + +AT_CHECK([ovsdb-server --detach --no-chdir --pidfile \ + --remote=punix:$OVS_RUNDIR/db.sock -vPATTERN:file:"<%B>1 %A %m" \ + --log-file], [0], [], [stderr]) +AT_CHECK([ovs-appctl -t ovsdb-server exit]) + +# A default facility of LOG_LOCAL0 while writing to file. +AT_CHECK([cat ovsdb-server.log | head -1 | awk '{print $1}'], [0], [<133>1 +]) +rm ovsdb-server.log + +AT_CHECK([ovsdb-server --detach --no-chdir --pidfile \ + --remote=punix:$OVS_RUNDIR/db.sock -vPATTERN:file:"<%B>1 %A %m" \ + -vFACILITY:daemon --log-file], [0], [], [stderr]) + +AT_CHECK([cat ovsdb-server.log | head -1 | awk '{print $1}'], [0], [<29>1 +]) + +AT_CHECK([ovs-appctl -t ovsdb-server vlog/set FACILITY:invalid], [2], [], +[invalid facility +ovs-appctl: ovsdb-server: server returned an error +]) + +AT_CHECK([ovs-appctl -t ovsdb-server vlog/set FACILITY:local7]) +AT_CHECK([ovs-appctl -t ovsdb-server vlog/set ANY:file:DBG]) +AT_CHECK([ovs-appctl -t ovsdb-server exit]) + +AT_CHECK([cat ovsdb-server.log | tail -1 | awk '{print $1}'], [0], [<191>1 +]) +AT_CLEANUP + +AT_SETUP([vlog - RFC5424 facility - Python]) +AT_SKIP_IF([test $HAVE_PYTHON = no]) +OVS_RUNDIR=`pwd`; export OVS_RUNDIR +OVS_LOGDIR=`pwd`; export OVS_LOGDIR +OVS_DBDIR=`pwd`; export OVS_DBDIR +OVS_SYSCONFDIR=`pwd`; export OVS_SYSCONFDIR +ON_EXIT([kill `cat test-unixctl.py.pid`]) + +AT_CHECK([$PYTHON $srcdir/test-unixctl.py --log-file=`pwd`/log --pidfile \ +-vFACILITY:invalid --detach], [1], [], [test-unixctl.py: processing "FACILITY:invalid": Facility invalid is invalid +]) + +AT_CHECK([$PYTHON $srcdir/test-unixctl.py --log-file=`pwd`/log --pidfile \ +-vFACILITY:daemon --detach]) + +AT_CHECK([ovs-appctl -t test-unixctl.py vlog/set FACILITY:invalid], [0], +[Facility invalid is invalid +]) + +AT_CHECK([ovs-appctl -t test-unixctl.py vlog/set FACILITY:local0]) +AT_CLEANUP diff --git a/utilities/ovs-appctl.8.in b/utilities/ovs-appctl.8.in index 238c74ba7..9e33f612d 100644 --- a/utilities/ovs-appctl.8.in +++ b/utilities/ovs-appctl.8.in @@ -254,6 +254,14 @@ The default pattern for console and file output is \fB%D{%Y-%m-%dT Daemons written in Python (e.g. \fBovs\-xapi\-sync\fR, \fBovs\-monitor\-ipsec) do not allow control over the log pattern. . +.IP "\fBvlog/set\fR FACILITY:\fIfacility\fR" +Sets the RFC5424 facility of the log message. \fIfacility\fR can be one of +\fBkern\fR, \fBuser\fR, \fBmail\fR, \fBdaemon\fR, \fBauth\fR, \fBsyslog\fR, +\fBlpr\fR, \fBnews\fR, \fBuucp\fR, \fBclock\fR, \fBftp\fR, \fBntp\fR, +\fBaudit\fR, \fBalert\fR, \fBclock2\fR, \fBlocal0\fR, \fBlocal1\fR, +\fBlocal2\fR, \fBlocal3\fR, \fBlocal4\fR, \fBlocal5\fR, \fBlocal6\fR or +\fBlocal7\fR. +. .IP "\fBvlog/reopen\fR" Causes the daemon to close and reopen its log file. (This is useful after rotating log files, to cause a new log file to be