]> git.ipfire.org Git - thirdparty/util-linux.git/blobdiff - misc-utils/logger.c
findmnt: (verify) check mnt_table_next_fs() return code [coverity scan]
[thirdparty/util-linux.git] / misc-utils / logger.c
index 6662a27384f85bc363b3963c443b45a24247b8bd..a99a0c96cdf5e93d2a36c94361c75deee0ce88aa 100644 (file)
@@ -51,7 +51,6 @@
 #include <netdb.h>
 #include <getopt.h>
 #include <pwd.h>
-#include <sys/types.h>
 #include <signal.h>
 #include <sys/uio.h>
 
@@ -69,6 +68,7 @@
 #include <syslog.h>
 
 #ifdef HAVE_LIBSYSTEMD
+# define SD_JOURNAL_SUPPRESS_LOCATION
 # include <systemd/sd-daemon.h>
 # include <systemd/sd-journal.h>
 #endif
@@ -116,7 +116,7 @@ struct logger_ctl {
        int pri;
        pid_t pid;                      /* zero when unwanted */
        char *hdr;                      /* the syslog header (based on protocol) */
-       char *tag;
+       char const *tag;
        char *msgid;
        char *unix_socket;              /* -u <path> or default to _PATH_DEVLOG */
        char *server;
@@ -131,7 +131,7 @@ struct logger_ctl {
        unsigned int
                        unix_socket_errors:1,   /* whether to report or not errors */
                        noact:1,                /* do not write to sockets */
-                       prio_prefix:1,          /* read priority from intput */
+                       prio_prefix:1,          /* read priority from input */
                        stderr_printout:1,      /* output message to stderr */
                        rfc5424_time:1,         /* include time stamp */
                        rfc5424_tq:1,           /* include time quality markup */
@@ -140,6 +140,9 @@ struct logger_ctl {
                        octet_count:1;          /* use RFC6587 octet counting */
 };
 
+#define is_connected(_ctl)     ((_ctl)->fd >= 0)
+static void logger_reopen(struct logger_ctl *ctl);
+
 /*
  * For tests we want to be able to control datetime outputs
  */
@@ -234,7 +237,7 @@ static int pencode(char *s)
 
 static int unix_socket(struct logger_ctl *ctl, const char *path, int *socket_type)
 {
-       int fd, i, type = -1;
+       int fd = -1, i, type = -1;
        static struct sockaddr_un s_addr;       /* AF_UNIX address of local logger */
 
        if (strlen(path) >= sizeof(s_addr.sun_path))
@@ -267,9 +270,7 @@ static int unix_socket(struct logger_ctl *ctl, const char *path, int *socket_typ
                if (ctl->unix_socket_errors)
                        err(EXIT_FAILURE, _("socket %s"), path);
 
-               /* openlog(3) compatibility, socket errors are
-                * not reported, but ignored silently */
-               ctl->noact = 1;
+               /* write_output() will try to reconnect */
                return -1;
        }
 
@@ -335,19 +336,37 @@ static int journald_entry(struct logger_ctl *ctl, FILE *fp)
        struct iovec *iovec;
        char *buf = NULL;
        ssize_t sz;
-       int n, lines, vectors = 8, ret = 0;
+       int n, lines = 0, vectors = 8, ret = 0, msgline = -1;
        size_t dummy = 0;
 
        iovec = xmalloc(vectors * sizeof(struct iovec));
-       for (lines = 0; /* nothing */ ; lines++) {
+       while (1) {
                buf = NULL;
                sz = getline(&buf, &dummy, fp);
-               if (sz == -1)
+               if (sz == -1 ||
+                  (sz = rtrim_whitespace((unsigned char *) buf)) == 0) {
+                       free(buf);
                        break;
-               if (0 < sz && buf[sz - 1] == '\n') {
-                       sz--;
-                       buf[sz] = '\0';
                }
+
+               if (strncmp(buf, "MESSAGE=", 8) == 0) {
+                       if (msgline == -1)
+                               msgline = lines;        /* remember the first message */
+                       else {
+                               char *p = xrealloc(iovec[msgline].iov_base,
+                                                  iovec[msgline].iov_len + sz - 8 + 2);
+
+                               iovec[msgline].iov_base = p;
+                               p += iovec[msgline].iov_len;
+                               *p++ = '\n';
+                               memcpy(p, buf + 8, sz - 8);
+                               free(buf);
+
+                               iovec[msgline].iov_len += sz - 8 + 1;
+                               continue;
+                       }
+               }
+
                if (lines == vectors) {
                        vectors *= 2;
                        if (IOV_MAX < vectors)
@@ -356,6 +375,7 @@ static int journald_entry(struct logger_ctl *ctl, FILE *fp)
                }
                iovec[lines].iov_base = buf;
                iovec[lines].iov_len = sz;
+               ++lines;
        }
 
        if (!ctl->noact)
@@ -371,9 +391,9 @@ static int journald_entry(struct logger_ctl *ctl, FILE *fp)
 }
 #endif
 
-static char *xgetlogin(void)
+static char const *xgetlogin(void)
 {
-       char *cp;
+       char const *cp;
        struct passwd *pw;
 
        if (!(cp = getlogin()) || !*cp)
@@ -387,12 +407,12 @@ static char *xgetlogin(void)
  * of a leading 0). The function uses a static buffer which is
  * overwritten on the next call (just like ctime() does).
  */
-static const char *rfc3164_current_time(void)
+static char const *rfc3164_current_time(void)
 {
        static char time[32];
        struct timeval tv;
        struct tm *tm;
-       static char *monthnames[] = {
+       static char const * const monthnames[] = {
                "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug",
                "Sep", "Oct", "Nov", "Dec"
        };
@@ -427,12 +447,16 @@ static const char *rfc3164_current_time(void)
  * it is too much for the logger utility. If octet-counting is
  * selected, we use that.
  */
-static void write_output(const struct logger_ctl *ctl, const char *const msg)
+static void write_output(struct logger_ctl *ctl, const char *const msg)
 {
        struct iovec iov[4];
        int iovlen = 0;
        char *octet = NULL;
 
+       /* initial connect failed? */
+       if (!ctl->noact && !is_connected(ctl))
+               logger_reopen(ctl);
+
        /* 1) octen count */
        if (ctl->octet_count) {
                size_t len = xasprintf(&octet, "%zu ", strlen(ctl->hdr) + strlen(msg));
@@ -445,7 +469,7 @@ static void write_output(const struct logger_ctl *ctl, const char *const msg)
        /* 3) message */
        iovec_add_string(iov, iovlen, msg, 0);
 
-       if (!ctl->noact) {
+       if (!ctl->noact && is_connected(ctl)) {
                struct msghdr message = { 0 };
 #ifdef SCM_CREDENTIALS
                struct cmsghdr *cmhp;
@@ -484,9 +508,23 @@ static void write_output(const struct logger_ctl *ctl, const char *const msg)
                        cred->pid = ctl->pid;
                }
 #endif
-
-               if (sendmsg(ctl->fd, &message, 0) < 0)
-                       warn(_("send message failed"));
+               /* Note that logger(1) maybe executed for long time (as pipe
+                * reader) and connection endpoint (syslogd) may be restarted.
+                *
+                * The libc syslog() function reconnects on failed send().
+                * Let's do the same to be robust.    [kzak -- Oct 2017]
+                *
+                * MSG_NOSIGNAL is POSIX.1-2008 compatible, but it for example
+                * not supported by apple-darwin15.6.0.
+                */
+#ifndef MSG_NOSIGNAL
+# define MSG_NOSIGNAL 0
+#endif
+               if (sendmsg(ctl->fd, &message, MSG_NOSIGNAL) < 0) {
+                       logger_reopen(ctl);
+                       if (sendmsg(ctl->fd, &message, MSG_NOSIGNAL) < 0)
+                               warn(_("send message failed"));
+               }
        }
 
        if (ctl->stderr_printout) {
@@ -506,8 +544,6 @@ static void syslog_rfc3164_header(struct logger_ctl *const ctl)
        char pid[30], *hostname;
 
        *pid = '\0';
-       if (ctl->fd < 0)
-               return;
        if (ctl->pid)
                snprintf(pid, sizeof(pid), "[%d]", ctl->pid);
 
@@ -571,7 +607,7 @@ static void add_structured_data_param(struct list_head *ls, const char *param)
        struct structured_data *sd;
 
        if (list_empty(ls))
-               errx(EXIT_FAILURE, _("--sd-id no specified for --sd-param %s"), param);
+               errx(EXIT_FAILURE, _("--sd-id was not specified for --sd-param %s"), param);
 
        assert(param);
 
@@ -680,14 +716,25 @@ static int valid_structured_data_id(const char *str)
 
        if (!at || at == str || !*(at + 1))
                return 0;
-       if (!isdigit_string(at + 1))
-               return 0;
+
+       /* <digits> or <digits>.<digits>[...] */
+       for (p = at + 1; p && *p; p++) {
+               const char *end;
+
+               if (isdigit_strend(p, &end))
+                       break;  /* only digits in the string */
+
+               if (end == NULL || end == p ||
+                   *end != '.' || *(end + 1) == '\0')
+                       return 0;
+               p = end;
+       }
 
        /* check for forbidden chars in the <name> */
        for (p = str; p < at; p++) {
                if (*p == '[' || *p == '=' || *p == '"' || *p == '@')
                        return 0;
-               if (isblank((unsigned int) *p) || iscntrl((unsigned int) *p))
+               if (isblank((unsigned char) *p) || iscntrl((unsigned char) *p))
                        return 0;
        }
        return 1;
@@ -718,15 +765,12 @@ static void syslog_rfc5424_header(struct logger_ctl *const ctl)
 {
        char *time;
        char *hostname;
-       char *const app_name = ctl->tag;
+       char const *app_name = ctl->tag;
        char *procid;
        char *const msgid = xstrdup(ctl->msgid ? ctl->msgid : NILVALUE);
        char *structured = NULL;
        struct list_head *sd;
 
-       if (ctl->fd < 0)
-               return;
-
        if (ctl->rfc5424_time) {
                struct timeval tv;
                struct tm *tm;
@@ -767,7 +811,7 @@ static void syslog_rfc5424_header(struct logger_ctl *const ctl)
 
        sd = get_reserved_structured_data(ctl);
 
-       /* time quality structured data (maybe overwriten by --sd-id timeQuality) */
+       /* time quality structured data (maybe overwritten by --sd-id timeQuality) */
        if (ctl->rfc5424_tq && !has_structured_data_id(sd, "timeQuality")) {
 
                add_structured_data_id(sd, "timeQuality");
@@ -806,11 +850,11 @@ static void syslog_rfc5424_header(struct logger_ctl *const ctl)
        free(structured);
 }
 
-static void parse_rfc5424_flags(struct logger_ctl *ctl, char *optarg)
+static void parse_rfc5424_flags(struct logger_ctl *ctl, char *s)
 {
        char *in, *tok;
 
-       in = optarg;
+       in = s;
        while ((tok = strtok(in, ","))) {
                in = NULL;
                if (!strcmp(tok, "notime")) {
@@ -825,15 +869,15 @@ static void parse_rfc5424_flags(struct logger_ctl *ctl, char *optarg)
        }
 }
 
-static int parse_unix_socket_errors_flags(char *optarg)
+static int parse_unix_socket_errors_flags(char *s)
 {
-       if (!strcmp(optarg, "off"))
+       if (!strcmp(s, "off"))
                return AF_UNIX_ERRORS_OFF;
-       if (!strcmp(optarg, "on"))
+       if (!strcmp(s, "on"))
                return AF_UNIX_ERRORS_ON;
-       if (!strcmp(optarg, "auto"))
+       if (!strcmp(s, "auto"))
                return AF_UNIX_ERRORS_AUTO;
-       warnx(_("invalid argument: %s: using automatic errors"), optarg);
+       warnx(_("invalid argument: %s: using automatic errors"), s);
        return AF_UNIX_ERRORS_AUTO;
 }
 
@@ -853,29 +897,48 @@ static void syslog_local_header(struct logger_ctl *const ctl)
 static void generate_syslog_header(struct logger_ctl *const ctl)
 {
        free(ctl->hdr);
+       ctl->hdr = NULL;
        ctl->syslogfp(ctl);
 }
 
-static void logger_open(struct logger_ctl *ctl)
+/* just open, nothing else */
+static void __logger_open(struct logger_ctl *ctl)
 {
        if (ctl->server) {
                ctl->fd = inet_socket(ctl->server, ctl->port, &ctl->socket_type);
-               if (!ctl->syslogfp)
-                       ctl->syslogfp = syslog_rfc5424_header;
        } else {
                if (!ctl->unix_socket)
                        ctl->unix_socket = _PATH_DEVLOG;
 
                ctl->fd = unix_socket(ctl, ctl->unix_socket, &ctl->socket_type);
-               if (!ctl->syslogfp)
-                       ctl->syslogfp = syslog_local_header;
        }
+}
+
+/* open and initialize relevant @ctl tuff */
+static void logger_open(struct logger_ctl *ctl)
+{
+       __logger_open(ctl);
+
+       if (!ctl->syslogfp)
+               ctl->syslogfp = ctl->server ? syslog_rfc5424_header :
+                                             syslog_local_header;
        if (!ctl->tag)
                ctl->tag = xgetlogin();
+
        generate_syslog_header(ctl);
 }
 
-static void logger_command_line(const struct logger_ctl *ctl, char **argv)
+/* re-open; usually after failed connection */
+static void logger_reopen(struct logger_ctl *ctl)
+{
+       if (ctl->fd != -1)
+               close(ctl->fd);
+       ctl->fd = -1;
+
+       __logger_open(ctl);
+}
+
+static void logger_command_line(struct logger_ctl *ctl, char **argv)
 {
        /* note: we never re-generate the syslog header here, even if we
         * generate multiple messages. If so, we think it is the right thing
@@ -910,7 +973,7 @@ static void logger_command_line(const struct logger_ctl *ctl, char **argv)
 
 static void logger_stdin(struct logger_ctl *ctl)
 {
-       /* note: we re-generate the the syslog header for each log message to
+       /* note: we re-generate the syslog header for each log message to
         * update header timestamps and to reflect possible priority changes.
         * The initial header is generated by logger_open().
         */
@@ -926,32 +989,32 @@ static void logger_stdin(struct logger_ctl *ctl)
        c = getchar();
        while (c != EOF) {
                i = 0;
-               if (ctl->prio_prefix) {
-                       if (c == '<') {
-                               pri = 0;
+               if (ctl->prio_prefix && c == '<') {
+                       pri = 0;
+                       buf[i++] = c;
+                       while (isdigit(c = getchar()) && pri <= 191) {
+                               buf[i++] = c;
+                               pri = pri * 10 + c - '0';
+                       }
+                       if (c != EOF && c != '\n')
                                buf[i++] = c;
-                               while (isdigit(c = getchar()) && pri <= 191) {
-                                       buf[i++] = c;
-                                       pri = pri * 10 + c - '0';
-                               }
-                               if (c != EOF && c != '\n')
-                                       buf[i++] = c;
-                               if (c == '>' && 0 <= pri && pri <= 191) { /* valid RFC PRI values */
-                                       i = 0;
-                                       if (pri < 8)
-                                               pri |= 8; /* kern facility is forbidden */
-                                       ctl->pri = pri;
-                               } else
-                                       ctl->pri = default_priority;
-
-                               if (ctl->pri != last_pri) {
-                                       has_header = 0;
-                                       max_usrmsg_size = ctl->max_message_size - strlen(ctl->hdr);
-                                       last_pri = ctl->pri;
-                               }
-                               if (c != EOF && c != '\n')
-                                       c = getchar();
+                       if (c == '>' && 0 <= pri && pri <= 191) {
+                               /* valid RFC PRI values */
+                               i = 0;
+                               if (pri < 8)    /* kern facility is forbidden */
+                                       pri |= 8;
+                               ctl->pri = pri;
+                       } else
+                               ctl->pri = default_priority;
+
+                       if (ctl->pri != last_pri) {
+                               has_header = 0;
+                               max_usrmsg_size =
+                                   ctl->max_message_size - strlen(ctl->hdr);
+                               last_pri = ctl->pri;
                        }
+                       if (c != EOF && c != '\n')
+                               c = getchar();
                }
 
                while (c != EOF && c != '\n' && i < max_usrmsg_size) {
@@ -970,6 +1033,8 @@ static void logger_stdin(struct logger_ctl *ctl)
                if (c == '\n')  /* discard line terminator */
                        c = getchar();
        }
+
+       free(buf);
 }
 
 static void logger_close(const struct logger_ctl *ctl)
@@ -979,8 +1044,9 @@ static void logger_close(const struct logger_ctl *ctl)
        free(ctl->hdr);
 }
 
-static void __attribute__ ((__noreturn__)) usage(FILE *out)
+static void __attribute__((__noreturn__)) usage(void)
 {
+       FILE *out = stdout;
        fputs(USAGE_HEADER, out);
        fprintf(out, _(" %s [options] [<message>]\n"), program_invocation_short_name);
 
@@ -1000,7 +1066,7 @@ static void __attribute__ ((__noreturn__)) usage(FILE *out)
        fputs(_(" -S, --size <size>        maximum size for a single message\n"), out);
        fputs(_(" -t, --tag <tag>          mark every line with this tag\n"), out);
        fputs(_(" -n, --server <name>      write to this remote syslog server\n"), out);
-       fputs(_(" -P, --port <number>      use this UDP port\n"), out);
+       fputs(_(" -P, --port <port>        use this port for UDP or TCP connection\n"), out);
        fputs(_(" -T, --tcp                use TCP only\n"), out);
        fputs(_(" -d, --udp                use UDP only\n"), out);
        fputs(_("     --rfc3164            use the obsolete BSD syslog protocol\n"), out);
@@ -1017,11 +1083,10 @@ static void __attribute__ ((__noreturn__)) usage(FILE *out)
 #endif
 
        fputs(USAGE_SEPARATOR, out);
-       fputs(USAGE_HELP, out);
-       fputs(USAGE_VERSION, out);
-       fprintf(out, USAGE_MAN_TAIL("logger(1)"));
+       printf(USAGE_HELP_OPTIONS(26));
+       printf(USAGE_MAN_TAIL("logger(1)"));
 
-       exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
+       exit(EXIT_SUCCESS);
 }
 
 /*
@@ -1090,7 +1155,7 @@ int main(int argc, char **argv)
        setlocale(LC_ALL, "");
        bindtextdomain(PACKAGE, LOCALEDIR);
        textdomain(PACKAGE);
-       atexit(close_stdout);
+       close_stdout_atexit();
 
        INIT_LIST_HEAD(&ctl.user_sds);
        INIT_LIST_HEAD(&ctl.reserved_sds);
@@ -1147,11 +1212,6 @@ int main(int argc, char **argv)
                case 'P':
                        ctl.port = optarg;
                        break;
-               case 'V':
-                       printf(UTIL_LINUX_VERSION);
-                       exit(EXIT_SUCCESS);
-               case 'h':
-                       usage(stdout);
                case OPT_OCTET_COUNT:
                        ctl.octet_count = 1;
                        break;
@@ -1198,9 +1258,13 @@ int main(int argc, char **argv)
                                errx(EXIT_FAILURE, _("invalid structured data parameter: '%s'"), optarg);
                        add_structured_data_param(get_user_structured_data(&ctl), optarg);
                        break;
-               case '?':
+
+               case 'V':
+                       print_version(EXIT_SUCCESS);
+               case 'h':
+                       usage();
                default:
-                       usage(stderr);
+                       errtryhelp(EXIT_FAILURE);
                }
        }
        argc -= optind;
@@ -1218,7 +1282,7 @@ int main(int argc, char **argv)
        }
 #endif
 
-       /* user overwrites build-in SD-ELEMENT */
+       /* user overwrites built-in SD-ELEMENT */
        if (has_structured_data_id(get_user_structured_data(&ctl), "timeQuality"))
                ctl.rfc5424_tq = 0;