inotify: add x, o, and u events
[oweals/busybox.git] / sysklogd / syslogd.c
index 345bf0e87940ca8f1d3d7b7bb68076b5196b5b35..38ea3d7ff2e4177907cc94b0c8b2dca0d167468e 100644 (file)
  * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
  */
 
+/*
+ * Done in syslogd_and_logger.c:
 #include "libbb.h"
+#define SYSLOG_NAMES
+#define SYSLOG_NAMES_CONST
+#include <syslog.h>
+*/
+
 #include <paths.h>
 #include <sys/un.h>
-
-/* SYSLOG_NAMES defined to pull prioritynames[] and facilitynames[]
- * from syslog.h. Grrrr - glibc puts those in _rwdata_! :( */
-#define SYSLOG_NAMES
-#define SYSLOG_NAMES_CONST /* uclibc is saner :) */
-#include <sys/syslog.h>
 #include <sys/uio.h>
 
 #if ENABLE_FEATURE_REMOTE_LOG
@@ -99,11 +100,11 @@ struct globals {
        struct shbuf_ds *shbuf;
 #endif
        time_t last_log_time;
-       /* localhost's name */
-       char localHostName[64];
+       /* localhost's name. We print only first 64 chars */
+       char *hostname;
 
        /* We recv into recvbuf... */
-       char recvbuf[MAX_READ];
+       char recvbuf[MAX_READ * (1 + ENABLE_FEATURE_SYSLOGD_DUP)];
        /* ...then copy to parsebuf, escaping control chars */
        /* (can grow x2 max) */
        char parsebuf[MAX_READ*2];
@@ -138,7 +139,7 @@ static const struct init_globals init_data = {
 
 #define G (*ptr_to_globals)
 #define INIT_G() do { \
-       PTR_TO_GLOBALS = memcpy(xzalloc(sizeof(G)), &init_data, sizeof(init_data)); \
+       SET_PTR_TO_GLOBALS(memcpy(xzalloc(sizeof(G)), &init_data, sizeof(init_data))); \
 } while (0)
 
 
@@ -154,6 +155,7 @@ enum {
        USE_FEATURE_REMOTE_LOG(    OPTBIT_remote     ,) // -R
        USE_FEATURE_REMOTE_LOG(    OPTBIT_locallog   ,) // -L
        USE_FEATURE_IPC_SYSLOG(    OPTBIT_circularlog,) // -C
+       USE_FEATURE_SYSLOGD_DUP(   OPTBIT_dup        ,) // -D
 
        OPT_mark        = 1 << OPTBIT_mark    ,
        OPT_nofork      = 1 << OPTBIT_nofork  ,
@@ -165,13 +167,15 @@ enum {
        OPT_remotelog   = USE_FEATURE_REMOTE_LOG(    (1 << OPTBIT_remote     )) + 0,
        OPT_locallog    = USE_FEATURE_REMOTE_LOG(    (1 << OPTBIT_locallog   )) + 0,
        OPT_circularlog = USE_FEATURE_IPC_SYSLOG(    (1 << OPTBIT_circularlog)) + 0,
+       OPT_dup         = USE_FEATURE_SYSLOGD_DUP(   (1 << OPTBIT_dup        )) + 0,
 };
 #define OPTION_STR "m:nO:l:S" \
        USE_FEATURE_ROTATE_LOGFILE("s:" ) \
        USE_FEATURE_ROTATE_LOGFILE("b:" ) \
        USE_FEATURE_REMOTE_LOG(    "R:" ) \
        USE_FEATURE_REMOTE_LOG(    "L"  ) \
-       USE_FEATURE_IPC_SYSLOG(    "C::")
+       USE_FEATURE_IPC_SYSLOG(    "C::") \
+       USE_FEATURE_SYSLOGD_DUP(   "D"  )
 #define OPTION_DECL *opt_m, *opt_l \
        USE_FEATURE_ROTATE_LOGFILE(,*opt_s) \
        USE_FEATURE_ROTATE_LOGFILE(,*opt_b) \
@@ -191,8 +195,8 @@ enum {
 #error Please check CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE
 #endif
 
-/* our shared key */
-#define KEY_ID ((long)0x414e4547) /* "GENA" */
+/* our shared key (syslogd.c and logread.c must be in sync) */
+enum { KEY_ID = 0x414e4547 }; /* "GENA" */
 
 static void ipcsyslog_cleanup(void)
 {
@@ -210,7 +214,7 @@ static void ipcsyslog_cleanup(void)
 static void ipcsyslog_init(void)
 {
        if (DEBUG)
-               printf("shmget(%lx, %d,...)\n", KEY_ID, G.shm_size);
+               printf("shmget(%x, %d,...)\n", (int)KEY_ID, G.shm_size);
 
        G.shmid = shmget(KEY_ID, G.shm_size, IPC_CREAT | 0644);
        if (G.shmid == -1) {
@@ -346,6 +350,7 @@ static void log_locally(time_t now, char *msg)
                                sprintf(newFile, "%s.%d", G.logFilePath, i);
                                if (i == 0) break;
                                sprintf(oldFile, "%s.%d", G.logFilePath, --i);
+                               /* ignore errors - file might be missing */
                                rename(oldFile, newFile);
                        }
                        /* newFile == "f.0" now */
@@ -359,7 +364,7 @@ static void log_locally(time_t now, char *msg)
        }
        G.curFileSize +=
 #endif
-                       full_write(G.logFD, msg, len);
+                       full_write(G.logFD, msg, len);
        fl.l_type = F_UNLCK;
        fcntl(G.logFD, F_SETLKW, &fl);
 }
@@ -372,13 +377,15 @@ static void parse_fac_prio_20(int pri, char *res20)
                c_fac = facilitynames;
                while (c_fac->c_name) {
                        if (c_fac->c_val != (LOG_FAC(pri) << 3)) {
-                               c_fac++; continue;
+                               c_fac++;
+                               continue;
                        }
                        /* facility is found, look for prio */
                        c_pri = prioritynames;
                        while (c_pri->c_name) {
                                if (c_pri->c_val != LOG_PRI(pri)) {
-                                       c_pri++; continue;
+                                       c_pri++;
+                                       continue;
                                }
                                snprintf(res20, 20, "%s.%s",
                                                c_fac->c_name, c_pri->c_name);
@@ -416,7 +423,7 @@ static void timestamp_and_log(int pri, char *msg, int len)
        else {
                char res[20];
                parse_fac_prio_20(pri, res);
-               sprintf(G.printbuf, "%s %s %s %s\n", timestamp, G.localHostName, res, msg);
+               sprintf(G.printbuf, "%s %.64s %s %s\n", timestamp, G.hostname, res, msg);
        }
 
        /* Log message locally (to file or shared mem) */
@@ -430,6 +437,9 @@ static void timestamp_and_log_internal(const char *msg)
        timestamp_and_log(LOG_SYSLOG | LOG_INFO, (char*)msg, 0);
 }
 
+/* tmpbuf[len] is a NUL byte (set by caller), but there can be other,
+ * embedded NULs. Split messages on each of these NULs, parse prio,
+ * escape control chars and log each locally. */
 static void split_escape_and_log(char *tmpbuf, int len)
 {
        char *p = tmpbuf;
@@ -472,7 +482,7 @@ static void quit_signal(int sig)
        puts("syslogd exiting");
        if (ENABLE_FEATURE_IPC_SYSLOG)
                ipcsyslog_cleanup();
-       exit(1);
+       kill_myself_with_sig(sig);
 }
 
 #ifdef SYSLOGD_MARK
@@ -527,19 +537,28 @@ static int try_to_resolve_remote(void)
                if (!G.remoteAddr)
                        return -1;
        }
-       return socket(G.remoteAddr->sa.sa_family, SOCK_DGRAM, 0);
+       return socket(G.remoteAddr->u.sa.sa_family, SOCK_DGRAM, 0);
 }
 #endif
 
-static void do_syslogd(void) ATTRIBUTE_NORETURN;
+static void do_syslogd(void) NORETURN;
 static void do_syslogd(void)
 {
        int sock_fd;
+#if ENABLE_FEATURE_SYSLOGD_DUP
+       int last_sz = -1;
+       char *last_buf;
+       char *recvbuf = G.recvbuf;
+#else
+#define recvbuf (G.recvbuf)
+#endif
 
        /* Set up signal handlers */
-       signal(SIGINT, quit_signal);
-       signal(SIGTERM, quit_signal);
-       signal(SIGQUIT, quit_signal);
+       bb_signals(0
+               + (1 << SIGINT)
+               + (1 << SIGTERM)
+               + (1 << SIGQUIT)
+               , quit_signal);
        signal(SIGHUP, SIG_IGN);
        /* signal(SIGCHLD, SIG_IGN); - why? */
 #ifdef SYSLOGD_MARK
@@ -555,34 +574,41 @@ static void do_syslogd(void)
        timestamp_and_log_internal("syslogd started: BusyBox v" BB_VER);
 
        for (;;) {
-               size_t sz;
+               ssize_t sz;
+
+#if ENABLE_FEATURE_SYSLOGD_DUP
+               last_buf = recvbuf;
+               if (recvbuf == G.recvbuf)
+                       recvbuf = G.recvbuf + MAX_READ;
+               else
+                       recvbuf = G.recvbuf;
+#endif
  read_again:
-               sz = safe_read(sock_fd, G.recvbuf, MAX_READ - 1);
-               if (sz < 0) {
+               sz = safe_read(sock_fd, recvbuf, MAX_READ - 1);
+               if (sz < 0)
                        bb_perror_msg_and_die("read from /dev/log");
-               }
 
-               /* Drop trailing NULs (typically there is one NUL) */
+               /* Drop trailing '\n' and NULs (typically there is one NUL) */
                while (1) {
                        if (sz == 0)
                                goto read_again;
                        /* man 3 syslog says: "A trailing newline is added when needed".
                         * However, neither glibc nor uclibc do this:
                         * syslog(prio, "test")   sends "test\0" to /dev/log,
-                        * syslog(prio, "test\n") sends "test\n\0",
+                        * syslog(prio, "test\n") sends "test\n\0".
                         * IOW: newline is passed verbatim!
                         * I take it to mean that it's syslogd's job
-                        * to make those look identical in the log files */
-                       if (G.recvbuf[sz-1] && G.recvbuf[sz-1] != '\n')
+                        * to make those look identical in the log files. */
+                       if (recvbuf[sz-1] != '\0' && recvbuf[sz-1] != '\n')
                                break;
                        sz--;
                }
-               /* Maybe we need to add '\n' here, not later?
-                * It looks like stock syslogd does send '\n' over network,
-                * but we do not (see sendto below) */
-               G.recvbuf[sz] = '\0'; /* make sure it *is* NUL terminated */
-
-               /* TODO: maybe suppress duplicates? */
+#if ENABLE_FEATURE_SYSLOGD_DUP
+               if ((option_mask32 & OPT_dup) && (sz == last_sz))
+                       if (memcmp(last_buf, recvbuf, sz) == 0)
+                               continue;
+               last_sz = sz;
+#endif
 #if ENABLE_FEATURE_REMOTE_LOG
                /* We are not modifying log messages in any way before send */
                /* Remote site cannot trust _us_ anyway and need to do validation again */
@@ -592,22 +618,29 @@ static void do_syslogd(void)
                                if (-1 == G.remoteFD)
                                        goto no_luck;
                        }
+                       /* Stock syslogd sends it '\n'-terminated
+                        * over network, mimic that */
+                       recvbuf[sz] = '\n';
                        /* send message to remote logger, ignore possible error */
-                       sendto(G.remoteFD, G.recvbuf, sz, MSG_DONTWAIT,
-                                   &G.remoteAddr->sa, G.remoteAddr->len);
+                       /* TODO: on some errors, close and set G.remoteFD to -1
+                        * so that DNS resolution and connect is retried? */
+                       sendto(G.remoteFD, recvbuf, sz+1, MSG_DONTWAIT,
+                                   &G.remoteAddr->u.sa, G.remoteAddr->len);
  no_luck: ;
                }
 #endif
-               if (!ENABLE_FEATURE_REMOTE_LOG || (option_mask32 & OPT_locallog))
-                       split_escape_and_log(G.recvbuf, sz);
+               if (!ENABLE_FEATURE_REMOTE_LOG || (option_mask32 & OPT_locallog)) {
+                       recvbuf[sz] = '\0'; /* ensure it *is* NUL terminated */
+                       split_escape_and_log(recvbuf, sz);
+               }
        } /* for (;;) */
+#undef recvbuf
 }
 
 int syslogd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
-int syslogd_main(int argc, char **argv)
+int syslogd_main(int argc UNUSED_PARAM, char **argv)
 {
        char OPTION_DECL;
-       char *p;
 
        INIT_G();
 #if ENABLE_FEATURE_REMOTE_LOG
@@ -642,11 +675,8 @@ int syslogd_main(int argc, char **argv)
                option_mask32 |= OPT_locallog;
 
        /* Store away localhost's name before the fork */
-       gethostname(G.localHostName, sizeof(G.localHostName));
-       p = strchr(G.localHostName, '.');
-       if (p) {
-               *p = '\0';
-       }
+       G.hostname = safe_gethostname();
+       *strchrnul(G.hostname, '.') = '\0';
 
        if (!(option_mask32 & OPT_nofork)) {
                bb_daemonize_or_rexec(DAEMON_CHDIR_ROOT, argv);
@@ -656,3 +686,11 @@ int syslogd_main(int argc, char **argv)
        do_syslogd();
        /* return EXIT_SUCCESS; */
 }
+
+/* Clean up. Needed because we are included from syslogd_and_logger.c */
+#undef G
+#undef GLOBALS
+#undef INIT_G
+#undef OPTION_STR
+#undef OPTION_DECL
+#undef OPTION_PARAM