Bump version to 1.32.0
[oweals/busybox.git] / sysklogd / syslogd.c
index 0799038e94439f4f03bedd17ba17c694d1e36cb3..ab50f4a28e701daba3e85aed3165781ca4d66b19 100644 (file)
  *
  * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
+//config:config SYSLOGD
+//config:      bool "syslogd (13 kb)"
+//config:      default y
+//config:      help
+//config:      The syslogd utility is used to record logs of all the
+//config:      significant events that occur on a system. Every
+//config:      message that is logged records the date and time of the
+//config:      event, and will generally also record the name of the
+//config:      application that generated the message. When used in
+//config:      conjunction with klogd, messages from the Linux kernel
+//config:      can also be recorded. This is terribly useful,
+//config:      especially for finding what happened when something goes
+//config:      wrong. And something almost always will go wrong if
+//config:      you wait long enough....
+//config:
+//config:config FEATURE_ROTATE_LOGFILE
+//config:      bool "Rotate message files"
+//config:      default y
+//config:      depends on SYSLOGD
+//config:      help
+//config:      This enables syslogd to rotate the message files
+//config:      on his own. No need to use an external rotate script.
+//config:
+//config:config FEATURE_REMOTE_LOG
+//config:      bool "Remote Log support"
+//config:      default y
+//config:      depends on SYSLOGD
+//config:      help
+//config:      When you enable this feature, the syslogd utility can
+//config:      be used to send system log messages to another system
+//config:      connected via a network. This allows the remote
+//config:      machine to log all the system messages, which can be
+//config:      terribly useful for reducing the number of serial
+//config:      cables you use. It can also be a very good security
+//config:      measure to prevent system logs from being tampered with
+//config:      by an intruder.
+//config:
+//config:config FEATURE_SYSLOGD_DUP
+//config:      bool "Support -D (drop dups) option"
+//config:      default y
+//config:      depends on SYSLOGD
+//config:      help
+//config:      Option -D instructs syslogd to drop consecutive messages
+//config:      which are totally the same.
+//config:
+//config:config FEATURE_SYSLOGD_CFG
+//config:      bool "Support syslog.conf"
+//config:      default y
+//config:      depends on SYSLOGD
+//config:      help
+//config:      Supports restricted syslogd config. See docs/syslog.conf.txt
+//config:
+//config:config FEATURE_SYSLOGD_PRECISE_TIMESTAMPS
+//config:      bool "Include milliseconds in timestamps"
+//config:      default n
+//config:      depends on SYSLOGD
+//config:      help
+//config:      Includes milliseconds (HH:MM:SS.mmm) in timestamp when
+//config:      timestamps are added.
+//config:
+//config:config FEATURE_SYSLOGD_READ_BUFFER_SIZE
+//config:      int "Read buffer size in bytes"
+//config:      default 256
+//config:      range 256 20000
+//config:      depends on SYSLOGD
+//config:      help
+//config:      This option sets the size of the syslog read buffer.
+//config:      Actual memory usage increases around five times the
+//config:      change done here.
+//config:
+//config:config FEATURE_IPC_SYSLOG
+//config:      bool "Circular Buffer support"
+//config:      default y
+//config:      depends on SYSLOGD
+//config:      help
+//config:      When you enable this feature, the syslogd utility will
+//config:      use a circular buffer to record system log messages.
+//config:      When the buffer is filled it will continue to overwrite
+//config:      the oldest messages. This can be very useful for
+//config:      systems with little or no permanent storage, since
+//config:      otherwise system logs can eventually fill up your
+//config:      entire filesystem, which may cause your system to
+//config:      break badly.
+//config:
+//config:config FEATURE_IPC_SYSLOG_BUFFER_SIZE
+//config:      int "Circular buffer size in Kbytes (minimum 4KB)"
+//config:      default 16
+//config:      range 4 2147483647
+//config:      depends on FEATURE_IPC_SYSLOG
+//config:      help
+//config:      This option sets the size of the circular buffer
+//config:      used to record system log messages.
+//config:
+//config:config FEATURE_KMSG_SYSLOG
+//config:      bool "Linux kernel printk buffer support"
+//config:      default y
+//config:      depends on SYSLOGD
+//config:      select PLATFORM_LINUX
+//config:      help
+//config:      When you enable this feature, the syslogd utility will
+//config:      write system log message to the Linux kernel's printk buffer.
+//config:      This can be used as a smaller alternative to the syslogd IPC
+//config:      support, as klogd and logread aren't needed.
+//config:
+//config:      NOTICE: Syslog facilities in log entries needs kernel 3.5+.
+
+//applet:IF_SYSLOGD(APPLET(syslogd, BB_DIR_SBIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_SYSLOGD) += syslogd_and_logger.o
 
 //usage:#define syslogd_trivial_usage
 //usage:       "[OPTIONS]"
 //usage:#define syslogd_full_usage "\n\n"
-//usage:       "System logging utility.\n"
-//usage:       "This version of syslogd ignores /etc/syslog.conf\n"
-//usage:     "\nOptions:"
+//usage:       "System logging utility\n"
+//usage:       IF_NOT_FEATURE_SYSLOGD_CFG(
+//usage:       "(this version of syslogd ignores /etc/syslog.conf)\n"
+//usage:       )
 //usage:     "\n       -n              Run in foreground"
-//usage:     "\n       -O FILE         Log to given file (default:/var/log/messages)"
-//usage:     "\n       -l N            Set local log level"
-//usage:     "\n       -S              Smaller logging output"
-//usage:       IF_FEATURE_ROTATE_LOGFILE(
-//usage:     "\n       -s SIZE         Max size (KB) before rotate (default:200KB, 0=off)"
-//usage:     "\n       -b N            N rotated logs to keep (default:1, max=99, 0=purge)")
 //usage:       IF_FEATURE_REMOTE_LOG(
-//usage:     "\n       -R HOST[:PORT]  Log to IP or hostname on PORT (default PORT=514/UDP)"
-//usage:     "\n       -L              Log locally and via network (default is network only if -R)")
-//usage:       IF_FEATURE_SYSLOGD_DUP(
-//usage:     "\n       -D              Drop duplicates")
+//usage:     "\n       -R HOST[:PORT]  Log to HOST:PORT (default PORT:514)"
+//usage:     "\n       -L              Log locally and via network (default is network only if -R)"
+//usage:       )
 //usage:       IF_FEATURE_IPC_SYSLOG(
-//usage:     "\n       -C[size(KiB)]   Log to shared mem buffer (read it using logread)")
 /* NB: -Csize shouldn't have space (because size is optional) */
-/* //usage:  "\n       -m MIN          Minutes between MARK lines (default:20, 0=off)" */
+//usage:     "\n       -C[size_kb]     Log to shared mem buffer (use logread to read it)"
+//usage:       )
+//usage:       IF_FEATURE_KMSG_SYSLOG(
+//usage:     "\n       -K              Log to kernel printk buffer (use dmesg to read it)"
+//usage:       )
+//usage:     "\n       -O FILE         Log to FILE (default: /var/log/messages, stdout if -)"
+//usage:       IF_FEATURE_ROTATE_LOGFILE(
+//usage:     "\n       -s SIZE         Max size (KB) before rotation (default 200KB, 0=off)"
+//usage:     "\n       -b N            N rotated logs to keep (default 1, max 99, 0=purge)"
+//usage:       )
+//usage:     "\n       -l N            Log only messages more urgent than prio N (1-8)"
+//usage:     "\n       -S              Smaller output"
+//usage:     "\n       -t              Strip client-generated timestamps"
+//usage:       IF_FEATURE_SYSLOGD_DUP(
+//usage:     "\n       -D              Drop duplicates"
+//usage:       )
+//usage:       IF_FEATURE_SYSLOGD_CFG(
+//usage:     "\n       -f FILE         Use FILE as config (default:/etc/syslog.conf)"
+//usage:       )
+/* //usage:  "\n       -m MIN          Minutes between MARK lines (default 20, 0=off)" */
 //usage:
 //usage:#define syslogd_example_usage
 //usage:       "$ syslogd -R masterlog:514\n"
 #define SYSLOG_NAMES_CONST
 #include <syslog.h>
 */
+#ifndef _PATH_LOG
+#define _PATH_LOG      "/dev/log"
+#endif
 
 #include <sys/un.h>
 #include <sys/uio.h>
@@ -96,6 +220,7 @@ typedef struct {
 typedef struct logFile_t {
        const char *path;
        int fd;
+       time_t last_log_time;
 #if ENABLE_FEATURE_ROTATE_LOGFILE
        unsigned size;
        uint8_t isRegular;
@@ -132,6 +257,10 @@ IF_FEATURE_IPC_SYSLOG( \
 ) \
 IF_FEATURE_SYSLOGD_CFG( \
        logRule_t *log_rules; \
+) \
+IF_FEATURE_KMSG_SYSLOG( \
+       int kmsgfd; \
+       int primask; \
 )
 
 struct init_globals {
@@ -147,7 +276,6 @@ struct globals {
 #if ENABLE_FEATURE_IPC_SYSLOG
        struct shbuf_ds *shbuf;
 #endif
-       time_t last_log_time;
        /* localhost's name. We print only first 64 chars */
        char *hostname;
 
@@ -156,7 +284,7 @@ struct globals {
        /* ...then copy to parsebuf, escaping control chars */
        /* (can grow x2 max) */
        char parsebuf[MAX_READ*2];
-       /* ...then sprintf into printbuf, adding timestamp (15 chars),
+       /* ...then sprintf into printbuf, adding timestamp (15 or 19 chars),
         * host (64), fac.prio (20) to the message */
        /* (growth by: 15 + 64 + 20 + delims = ~110) */
        char printbuf[MAX_READ*2 + 128];
@@ -197,6 +325,7 @@ enum {
        OPTBIT_outfile, // -O
        OPTBIT_loglevel, // -l
        OPTBIT_small, // -S
+       OPTBIT_timestamp, // -t
        IF_FEATURE_ROTATE_LOGFILE(OPTBIT_filesize   ,)  // -s
        IF_FEATURE_ROTATE_LOGFILE(OPTBIT_rotatecnt  ,)  // -b
        IF_FEATURE_REMOTE_LOG(    OPTBIT_remotelog  ,)  // -R
@@ -204,12 +333,14 @@ enum {
        IF_FEATURE_IPC_SYSLOG(    OPTBIT_circularlog,)  // -C
        IF_FEATURE_SYSLOGD_DUP(   OPTBIT_dup        ,)  // -D
        IF_FEATURE_SYSLOGD_CFG(   OPTBIT_cfg        ,)  // -f
+       IF_FEATURE_KMSG_SYSLOG(   OPTBIT_kmsg       ,)  // -K
 
        OPT_mark        = 1 << OPTBIT_mark    ,
        OPT_nofork      = 1 << OPTBIT_nofork  ,
        OPT_outfile     = 1 << OPTBIT_outfile ,
        OPT_loglevel    = 1 << OPTBIT_loglevel,
        OPT_small       = 1 << OPTBIT_small   ,
+       OPT_timestamp   = 1 << OPTBIT_timestamp,
        OPT_filesize    = IF_FEATURE_ROTATE_LOGFILE((1 << OPTBIT_filesize   )) + 0,
        OPT_rotatecnt   = IF_FEATURE_ROTATE_LOGFILE((1 << OPTBIT_rotatecnt  )) + 0,
        OPT_remotelog   = IF_FEATURE_REMOTE_LOG(    (1 << OPTBIT_remotelog  )) + 0,
@@ -217,15 +348,17 @@ enum {
        OPT_circularlog = IF_FEATURE_IPC_SYSLOG(    (1 << OPTBIT_circularlog)) + 0,
        OPT_dup         = IF_FEATURE_SYSLOGD_DUP(   (1 << OPTBIT_dup        )) + 0,
        OPT_cfg         = IF_FEATURE_SYSLOGD_CFG(   (1 << OPTBIT_cfg        )) + 0,
+       OPT_kmsg        = IF_FEATURE_KMSG_SYSLOG(   (1 << OPTBIT_kmsg       )) + 0,
 };
-#define OPTION_STR "m:nO:l:S" \
+#define OPTION_STR "m:nO:l:St" \
        IF_FEATURE_ROTATE_LOGFILE("s:" ) \
        IF_FEATURE_ROTATE_LOGFILE("b:" ) \
-       IF_FEATURE_REMOTE_LOG(    "R:) \
+       IF_FEATURE_REMOTE_LOG(    "R:*") \
        IF_FEATURE_REMOTE_LOG(    "L"  ) \
        IF_FEATURE_IPC_SYSLOG(    "C::") \
        IF_FEATURE_SYSLOGD_DUP(   "D"  ) \
-       IF_FEATURE_SYSLOGD_CFG(   "f:"  )
+       IF_FEATURE_SYSLOGD_CFG(   "f:" ) \
+       IF_FEATURE_KMSG_SYSLOG(   "K"  )
 #define OPTION_DECL *opt_m, *opt_l \
        IF_FEATURE_ROTATE_LOGFILE(,*opt_s) \
        IF_FEATURE_ROTATE_LOGFILE(,*opt_b) \
@@ -234,7 +367,7 @@ enum {
 #define OPTION_PARAM &opt_m, &(G.logFile.path), &opt_l \
        IF_FEATURE_ROTATE_LOGFILE(,&opt_s) \
        IF_FEATURE_ROTATE_LOGFILE(,&opt_b) \
-       IF_FEATURE_REMOTE_LOG(    ,&remoteAddrList) \
+       IF_FEATURE_REMOTE_LOG(    ,&remoteAddrList) \
        IF_FEATURE_IPC_SYSLOG(    ,&opt_C) \
        IF_FEATURE_SYSLOGD_CFG(   ,&opt_f)
 
@@ -270,7 +403,7 @@ static void parse_syslogdcfg(const char *file)
        parser_t *parser;
 
        parser = config_open2(file ? file : "/etc/syslog.conf",
-                               file ? xfopen_for_read : fopen_or_warn_stdin);
+                               file ? xfopen_for_read : fopen_for_read);
        if (!parser)
                /* didn't find default /etc/syslog.conf */
                /* proceed as if we built busybox without config support */
@@ -284,10 +417,8 @@ static void parse_syslogdcfg(const char *file)
                logRule_t *cur_rule;
 
                /* unexpected trailing token? */
-               if (tok[2]) {
-                       t = tok[2];
+               if (tok[2])
                        goto cfgerr;
-               }
 
                cur_rule = *pp_rule = xzalloc(sizeof(*cur_rule));
 
@@ -307,10 +438,8 @@ static void parse_syslogdcfg(const char *file)
                                *next_selector++ = '\0';
 
                        t = strchr(cur_selector, '.');
-                       if (!t) {
-                               t = cur_selector;
+                       if (!t)
                                goto cfgerr;
-                       }
                        *t++ = '\0'; /* separate facility from priority */
 
                        negated_prio = 0;
@@ -329,7 +458,7 @@ static void parse_syslogdcfg(const char *file)
                                primap = 0xff; /* all 8 log levels enabled */
                        else {
                                uint8_t priority;
-                               code = find_by_name(t, prioritynames);
+                               code = find_by_name(t, bb_prioritynames);
                                if (!code)
                                        goto cfgerr;
                                primap = 0;
@@ -362,7 +491,7 @@ static void parse_syslogdcfg(const char *file)
                                        next_facility = strchr(t, ',');
                                        if (next_facility)
                                                *next_facility++ = '\0';
-                                       code = find_by_name(t, facilitynames);
+                                       code = find_by_name(t, bb_facilitynames);
                                        if (!code)
                                                goto cfgerr;
                                        /* "mark" is not a real facility, skip it */
@@ -414,7 +543,9 @@ static void parse_syslogdcfg(const char *file)
        return;
 
  cfgerr:
-       bb_error_msg_and_die("bad line %d: wrong token '%s'", parser->lineno, t);
+       bb_error_msg_and_die("error in '%s' at line %d",
+                       file ? file : "/etc/syslog.conf",
+                       parser->lineno);
 }
 #endif
 
@@ -449,12 +580,12 @@ static void ipcsyslog_init(void)
 
        G.shmid = shmget(KEY_ID, G.shm_size, IPC_CREAT | 0644);
        if (G.shmid == -1) {
-               bb_perror_msg_and_die("shmget");
+               bb_simple_perror_msg_and_die("shmget");
        }
 
        G.shbuf = shmat(G.shmid, NULL, 0);
        if (G.shbuf == (void*) -1L) { /* shmat has bizarre error return */
-               bb_perror_msg_and_die("shmat");
+               bb_simple_perror_msg_and_die("shmat");
        }
 
        memset(G.shbuf, 0, G.shm_size);
@@ -469,7 +600,7 @@ static void ipcsyslog_init(void)
                        if (G.s_semid != -1)
                                return;
                }
-               bb_perror_msg_and_die("semget");
+               bb_simple_perror_msg_and_die("semget");
        }
 }
 
@@ -480,7 +611,7 @@ static void log_to_shmem(const char *msg)
        int len;
 
        if (semop(G.s_semid, G.SMwdn, 3) == -1) {
-               bb_perror_msg_and_die("SMwdn");
+               bb_simple_perror_msg_and_die("SMwdn");
        }
 
        /* Circular Buffer Algorithm:
@@ -508,17 +639,55 @@ static void log_to_shmem(const char *msg)
                goto again;
        }
        if (semop(G.s_semid, G.SMwup, 1) == -1) {
-               bb_perror_msg_and_die("SMwup");
+               bb_simple_perror_msg_and_die("SMwup");
        }
        if (DEBUG)
                printf("tail:%d\n", G.shbuf->tail);
 }
 #else
-void ipcsyslog_cleanup(void);
-void ipcsyslog_init(void);
+static void ipcsyslog_cleanup(void) {}
+static void ipcsyslog_init(void) {}
 void log_to_shmem(const char *msg);
 #endif /* FEATURE_IPC_SYSLOG */
 
+#if ENABLE_FEATURE_KMSG_SYSLOG
+static void kmsg_init(void)
+{
+       G.kmsgfd = xopen("/dev/kmsg", O_WRONLY);
+
+       /*
+        * kernel < 3.5 expects single char printk KERN_* priority prefix,
+        * from 3.5 onwards the full syslog facility/priority format is supported
+        */
+       if (get_linux_version_code() < KERNEL_VERSION(3,5,0))
+               G.primask = LOG_PRIMASK;
+       else
+               G.primask = -1;
+}
+
+static void kmsg_cleanup(void)
+{
+       if (ENABLE_FEATURE_CLEAN_UP)
+               close(G.kmsgfd);
+}
+
+/* Write message to /dev/kmsg */
+static void log_to_kmsg(int pri, const char *msg)
+{
+       /*
+        * kernel < 3.5 expects single char printk KERN_* priority prefix,
+        * from 3.5 onwards the full syslog facility/priority format is supported
+        */
+       pri &= G.primask;
+
+       full_write(G.kmsgfd, G.printbuf, sprintf(G.printbuf, "<%d>%s\n", pri, msg));
+}
+#else
+static void kmsg_init(void) {}
+static void kmsg_cleanup(void) {}
+static void log_to_kmsg(int pri UNUSED_PARAM, const char *msg UNUSED_PARAM) {}
+#endif /* FEATURE_KMSG_SYSLOG */
+
 /* Print a message to the log file. */
 static void log_locally(time_t now, char *msg, logFile_t *log_file)
 {
@@ -527,42 +696,54 @@ static void log_locally(time_t now, char *msg, logFile_t *log_file)
 #endif
        int len = strlen(msg);
 
-       if (log_file->fd >= 0) {
-               /* Reopen log file every second. This allows admin
-                * to delete the file and not worry about restarting us.
+       /* fd can't be 0 (we connect fd 0 to /dev/log socket) */
+       /* fd is 1 if "-O -" is in use */
+       if (log_file->fd > 1) {
+               /* Reopen log files every second. This allows admin
+                * to delete the files and not worry about restarting us.
                 * This costs almost nothing since it happens
-                * _at most_ once a second.
+                * _at most_ once a second for each file, and happens
+                * only when each file is actually written.
                 */
                if (!now)
                        now = time(NULL);
-               if (G.last_log_time != now) {
-                       G.last_log_time = now;
+               if (log_file->last_log_time != now) {
+                       log_file->last_log_time = now;
                        close(log_file->fd);
                        goto reopen;
                }
-       } else {
+       }
+       else if (log_file->fd == 1) {
+               /* We are logging to stdout: do nothing */
+       }
+       else {
+               if (LONE_DASH(log_file->path)) {
+                       log_file->fd = 1;
+                       /* log_file->isRegular = 0; - already is */
+               } else {
  reopen:
-               log_file->fd = open(log_file->path, O_WRONLY | O_CREAT
+                       log_file->fd = open(log_file->path, O_WRONLY | O_CREAT
                                        | O_NOCTTY | O_APPEND | O_NONBLOCK,
                                        0666);
-               if (log_file->fd < 0) {
-                       /* cannot open logfile? - print to /dev/console then */
-                       int fd = device_open(DEV_CONSOLE, O_WRONLY | O_NOCTTY | O_NONBLOCK);
-                       if (fd < 0)
-                               fd = 2; /* then stderr, dammit */
-                       full_write(fd, msg, len);
-                       if (fd != 2)
-                               close(fd);
-                       return;
-               }
+                       if (log_file->fd < 0) {
+                               /* cannot open logfile? - print to /dev/console then */
+                               int fd = device_open(DEV_CONSOLE, O_WRONLY | O_NOCTTY | O_NONBLOCK);
+                               if (fd < 0)
+                                       fd = 2; /* then stderr, dammit */
+                               full_write(fd, msg, len);
+                               if (fd != 2)
+                                       close(fd);
+                               return;
+                       }
 #if ENABLE_FEATURE_ROTATE_LOGFILE
-               {
-                       struct stat statf;
-                       log_file->isRegular = (fstat(log_file->fd, &statf) == 0 && S_ISREG(statf.st_mode));
-                       /* bug (mostly harmless): can wrap around if file > 4gb */
-                       log_file->size = statf.st_size;
-               }
+                       {
+                               struct stat statf;
+                               log_file->isRegular = (fstat(log_file->fd, &statf) == 0 && S_ISREG(statf.st_mode));
+                               /* bug (mostly harmless): can wrap around if file > 4gb */
+                               log_file->size = statf.st_size;
+                       }
 #endif
+               }
        }
 
 #ifdef SYSLOGD_WRLOCK
@@ -590,18 +771,33 @@ static void log_locally(time_t now, char *msg, logFile_t *log_file)
                        }
                        /* newFile == "f.0" now */
                        rename(log_file->path, newFile);
+               }
+
+               /* We may or may not have just renamed the file away;
+                * if we didn't rename because we aren't keeping any backlog,
+                * then it's time to clobber the file. If we did rename it...,
+                * incredibly, if F and F.0 are hardlinks, POSIX _demands_
+                * that rename returns 0 but does not remove F!!!
+                * (hardlinked F/F.0 pair was observed after
+                * power failure during rename()).
+                * So ensure old file is gone in any case:
+                */
+               unlink(log_file->path);
 #ifdef SYSLOGD_WRLOCK
-                       fl.l_type = F_UNLCK;
-                       fcntl(log_file->fd, F_SETLKW, &fl);
+               fl.l_type = F_UNLCK;
+               fcntl(log_file->fd, F_SETLKW, &fl);
 #endif
-                       close(log_file->fd);
-                       goto reopen;
-               }
-               ftruncate(log_file->fd, 0);
+               close(log_file->fd);
+               goto reopen;
        }
-       log_file->size +=
+/* TODO: what to do on write errors ("disk full")? */
+       len = full_write(log_file->fd, msg, len);
+       if (len > 0)
+               log_file->size += len;
+#else
+       full_write(log_file->fd, msg, len);
 #endif
-                       full_write(log_file->fd, msg, len);
+
 #ifdef SYSLOGD_WRLOCK
        fl.l_type = F_UNLCK;
        fcntl(log_file->fd, F_SETLKW, &fl);
@@ -612,9 +808,9 @@ static void parse_fac_prio_20(int pri, char *res20)
 {
        const CODE *c_pri, *c_fac;
 
-       c_fac = find_by_val(LOG_FAC(pri) << 3, facilitynames);
+       c_fac = find_by_val(LOG_FAC(pri) << 3, bb_facilitynames);
        if (c_fac) {
-               c_pri = find_by_val(LOG_PRI(pri), prioritynames);
+               c_pri = find_by_val(LOG_PRI(pri), bb_prioritynames);
                if (c_pri) {
                        snprintf(res20, 20, "%s.%s", c_fac->c_name, c_pri->c_name);
                        return;
@@ -628,22 +824,45 @@ static void parse_fac_prio_20(int pri, char *res20)
  * that there is no timestamp, short-circuiting the test. */
 static void timestamp_and_log(int pri, char *msg, int len)
 {
-       char *timestamp;
+       char *timestamp = NULL;
        time_t now;
 
        /* Jan 18 00:11:22 msg... */
        /* 01234567890123456 */
-       if (len < 16 || msg[3] != ' ' || msg[6] != ' '
-        || msg[9] != ':' || msg[12] != ':' || msg[15] != ' '
+       if (len >= 16 && msg[3] == ' ' && msg[6] == ' '
+        && msg[9] == ':' && msg[12] == ':' && msg[15] == ' '
        ) {
-               time(&now);
+               if (!(option_mask32 & OPT_timestamp)) {
+                       /* use message timestamp */
+                       timestamp = msg;
+                       now = 0;
+               }
+               msg += 16;
+       }
+
+#if ENABLE_FEATURE_SYSLOGD_PRECISE_TIMESTAMPS
+       if (!timestamp) {
+               struct timeval tv;
+               gettimeofday(&tv, NULL);
+               now = tv.tv_sec;
                timestamp = ctime(&now) + 4; /* skip day of week */
+               /* overwrite year by milliseconds, zero terminate */
+               sprintf(timestamp + 15, ".%03u", (unsigned)tv.tv_usec / 1000u);
        } else {
-               now = 0;
-               timestamp = msg;
-               msg += 16;
+               timestamp[15] = '\0';
+       }
+#else
+       if (!timestamp) {
+               time(&now);
+               timestamp = ctime(&now) + 4; /* skip day of week */
        }
        timestamp[15] = '\0';
+#endif
+
+       if (option_mask32 & OPT_kmsg) {
+               log_to_kmsg(pri, msg);
+               return;
+       }
 
        if (option_mask32 & OPT_small)
                sprintf(G.printbuf, "%s %s\n", timestamp, msg);
@@ -674,7 +893,7 @@ static void timestamp_and_log(int pri, char *msg, int len)
        if (LOG_PRI(pri) < G.logLevel) {
 #if ENABLE_FEATURE_IPC_SYSLOG
                if ((option_mask32 & OPT_circularlog) && G.shbuf) {
-                       log_to_shmem(msg);
+                       log_to_shmem(G.printbuf);
                        return;
                }
 #endif
@@ -746,18 +965,13 @@ static NOINLINE int create_socket(void)
        int sock_fd;
        char *dev_log_name;
 
-#if ENABLE_FEATURE_SYSTEMD
-       if (sd_listen_fds() == 1)
-               return SD_LISTEN_FDS_START;
-#endif
-
        memset(&sunx, 0, sizeof(sunx));
        sunx.sun_family = AF_UNIX;
 
        /* Unlink old /dev/log or object it points to. */
        /* (if it exists, bind will fail) */
-       strcpy(sunx.sun_path, "/dev/log");
-       dev_log_name = xmalloc_follow_symlinks("/dev/log");
+       strcpy(sunx.sun_path, _PATH_LOG);
+       dev_log_name = xmalloc_follow_symlinks(_PATH_LOG);
        if (dev_log_name) {
                safe_strncpy(sunx.sun_path, dev_log_name, sizeof(sunx.sun_path));
                free(dev_log_name);
@@ -766,7 +980,7 @@ static NOINLINE int create_socket(void)
 
        sock_fd = xsocket(AF_UNIX, SOCK_DGRAM, 0);
        xbind(sock_fd, (struct sockaddr *) &sunx, sizeof(sunx));
-       chmod("/dev/log", 0666);
+       chmod(_PATH_LOG, 0666);
 
        return sock_fd;
 }
@@ -792,7 +1006,6 @@ static int try_to_resolve_remote(remoteHost_t *rh)
 static void do_syslogd(void) NORETURN;
 static void do_syslogd(void)
 {
-       int sock_fd;
 #if ENABLE_FEATURE_REMOTE_LOG
        llist_t *item;
 #endif
@@ -813,11 +1026,13 @@ static void do_syslogd(void)
        signal(SIGALRM, do_mark);
        alarm(G.markInterval);
 #endif
-       sock_fd = create_socket();
+       xmove_fd(create_socket(), STDIN_FILENO);
 
-       if (ENABLE_FEATURE_IPC_SYSLOG && (option_mask32 & OPT_circularlog)) {
+       if (option_mask32 & OPT_circularlog)
                ipcsyslog_init();
-       }
+
+       if (option_mask32 & OPT_kmsg)
+               kmsg_init();
 
        timestamp_and_log_internal("syslogd started: BusyBox v" BB_VER);
 
@@ -832,10 +1047,10 @@ static void do_syslogd(void)
                        recvbuf = G.recvbuf;
 #endif
  read_again:
-               sz = read(sock_fd, recvbuf, MAX_READ - 1);
+               sz = read(STDIN_FILENO, recvbuf, MAX_READ - 1);
                if (sz < 0) {
                        if (!bb_got_signal)
-                               bb_perror_msg("read from /dev/log");
+                               bb_perror_msg("read from %s", _PATH_LOG);
                        break;
                }
 
@@ -903,9 +1118,10 @@ static void do_syslogd(void)
        } /* while (!bb_got_signal) */
 
        timestamp_and_log_internal("syslogd exiting");
-       puts("syslogd exiting");
-       if (ENABLE_FEATURE_IPC_SYSLOG)
-               ipcsyslog_cleanup();
+       remove_pidfile_std_path_and_ext("syslogd");
+       ipcsyslog_cleanup();
+       if (option_mask32 & OPT_kmsg)
+               kmsg_cleanup();
        kill_myself_with_sig(bb_got_signal);
 #undef recvbuf
 }
@@ -921,9 +1137,8 @@ int syslogd_main(int argc UNUSED_PARAM, char **argv)
 
        INIT_G();
 
-       /* No non-option params, -R can occur multiple times */
-       opt_complementary = "=0" IF_FEATURE_REMOTE_LOG(":R::");
-       opts = getopt32(argv, OPTION_STR, OPTION_PARAM);
+       /* No non-option params */
+       opts = getopt32(argv, "^"OPTION_STR"\0""=0", OPTION_PARAM);
 #if ENABLE_FEATURE_REMOTE_LOG
        while (remoteAddrList) {
                remoteHost_t *rh = xzalloc(sizeof(*rh));
@@ -967,8 +1182,10 @@ int syslogd_main(int argc UNUSED_PARAM, char **argv)
        if (!(opts & OPT_nofork)) {
                bb_daemonize_or_rexec(DAEMON_CHDIR_ROOT, argv);
        }
+
        //umask(0); - why??
-       write_pidfile("/var/run/syslogd.pid");
+       write_pidfile_std_path_and_ext("syslogd");
+
        do_syslogd();
        /* return EXIT_SUCCESS; */
 }