chiark / gitweb /
log: fix repeated invocation of vsnprintf()/vaprintf() in log_struct()
[elogind.git] / src / shared / log.c
index 9fffc1dbc00ba2123cae35afbf3d4eb0156865eb..63578683ab6575b73df32ddd9164893471d5f186 100644 (file)
@@ -6,16 +6,16 @@
   Copyright 2010 Lennart Poettering
 
   systemd is free software; you can redistribute it and/or modify it
-  under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 2 of the License, or
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
   (at your option) any later version.
 
   systemd is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
+  Lesser General Public License for more details.
 
-  You should have received a copy of the GNU General Public License
+  You should have received a copy of the GNU Lesser General Public License
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
 #include <sys/socket.h>
 #include <sys/un.h>
 #include <stddef.h>
+#include <printf.h>
 
 #include "log.h"
 #include "util.h"
+#include "missing.h"
 #include "macro.h"
 #include "socket-util.h"
 
@@ -72,14 +74,9 @@ static int log_open_console(void) {
                 return 0;
 
         if (getpid() == 1) {
-
                 console_fd = open_terminal("/dev/console", O_WRONLY|O_NOCTTY|O_CLOEXEC);
-                if (console_fd < 0) {
-                        log_error("Failed to open /dev/console for logging: %s", strerror(-console_fd));
+                if (console_fd < 0)
                         return console_fd;
-                }
-
-                log_debug("Successfully opened /dev/console for logging.");
         } else
                 console_fd = STDERR_FILENO;
 
@@ -101,12 +98,8 @@ static int log_open_kmsg(void) {
                 return 0;
 
         kmsg_fd = open("/dev/kmsg", O_WRONLY|O_NOCTTY|O_CLOEXEC);
-        if (kmsg_fd < 0) {
-                log_error("Failed to open /dev/kmsg for logging: %s", strerror(errno));
+        if (kmsg_fd < 0)
                 return -errno;
-        }
-
-        log_debug("Successfully opened /dev/kmsg for logging.");
 
         return 0;
 }
@@ -173,13 +166,10 @@ static int log_open_syslog(void) {
         } else
                 syslog_is_stream = false;
 
-        log_debug("Successfully opened syslog for logging.");
-
         return 0;
 
 fail:
         log_close_syslog();
-        log_debug("Failed to open syslog for logging: %s", strerror(-r));
         return r;
 }
 
@@ -214,13 +204,10 @@ static int log_open_journal(void) {
                 goto fail;
         }
 
-        log_debug("Successfully opened journal for logging.");
-
         return 0;
 
 fail:
         log_close_journal();
-        log_debug("Failed to open journal for logging: %s", strerror(-r));
         return r;
 }
 
@@ -240,7 +227,7 @@ int log_open(void) {
                 return 0;
         }
 
-        if (log_target != LOG_TARGET_AUTO ||
+        if ((log_target != LOG_TARGET_AUTO && log_target != LOG_TARGET_SAFE) ||
             getpid() == 1 ||
             isatty(STDERR_FILENO) <= 0) {
 
@@ -266,6 +253,7 @@ int log_open(void) {
                 }
 
                 if (log_target == LOG_TARGET_AUTO ||
+                    log_target == LOG_TARGET_SAFE ||
                     log_target == LOG_TARGET_JOURNAL_OR_KMSG ||
                     log_target == LOG_TARGET_SYSLOG_OR_KMSG ||
                     log_target == LOG_TARGET_KMSG) {
@@ -466,12 +454,14 @@ static int write_to_journal(
                  "CODE_FILE=%s\n"
                  "CODE_LINE=%i\n"
                  "CODE_FUNCTION=%s\n"
+                 "SYSLOG_IDENTIFIER=%s\n"
                  "MESSAGE=",
                  LOG_PRI(level),
                  LOG_FAC(level),
                  file,
                  line,
-                 func);
+                 func,
+                 program_invocation_short_name);
 
         char_array_0(header);
 
@@ -545,7 +535,9 @@ static int log_dispatch(
 
                 if (k <= 0 &&
                     (log_target == LOG_TARGET_AUTO ||
+                     log_target == LOG_TARGET_SAFE ||
                      log_target == LOG_TARGET_SYSLOG_OR_KMSG ||
+                     log_target == LOG_TARGET_JOURNAL_OR_KMSG ||
                      log_target == LOG_TARGET_KMSG)) {
 
                         k = write_to_kmsg(level, file, line, func, buffer);
@@ -653,6 +645,144 @@ _noreturn_ void log_assert_failed_unreachable(const char *text, const char *file
         log_assert(text, file, line, func, "Code should not be reached '%s' at %s:%u, function %s(). Aborting.");
 }
 
+int log_oom_internal(const char *file, int line, const char *func) {
+        log_meta(LOG_ERR, file, line, func, "Out of memory.");
+        return -ENOMEM;
+}
+
+int log_struct_internal(
+                int level,
+                const char *file,
+                int line,
+                const char *func,
+                const char *format, ...) {
+
+        int saved_errno;
+        va_list ap;
+        int r;
+
+        if (_likely_(LOG_PRI(level) > log_max_level))
+                return 0;
+
+        if (log_target == LOG_TARGET_NULL)
+                return 0;
+
+        if ((level & LOG_FACMASK) == 0)
+                level = log_facility | LOG_PRI(level);
+
+        saved_errno = errno;
+
+        if ((log_target == LOG_TARGET_AUTO ||
+             log_target == LOG_TARGET_JOURNAL_OR_KMSG ||
+             log_target == LOG_TARGET_JOURNAL) &&
+            journal_fd >= 0) {
+
+                char header[LINE_MAX];
+                struct iovec iovec[17];
+                unsigned n = 0, i;
+                struct msghdr mh;
+                const char nl = '\n';
+
+                /* If the journal is available do structured logging */
+
+                snprintf(header, sizeof(header),
+                        "PRIORITY=%i\n"
+                        "SYSLOG_FACILITY=%i\n"
+                        "CODE_FILE=%s\n"
+                        "CODE_LINE=%i\n"
+                        "CODE_FUNCTION=%s\n"
+                        "SYSLOG_IDENTIFIER=%s\n",
+                        LOG_PRI(level),
+                        LOG_FAC(level),
+                        file,
+                        line,
+                        func,
+                        program_invocation_short_name);
+                char_array_0(header);
+
+                zero(iovec);
+                IOVEC_SET_STRING(iovec[n++], header);
+
+                va_start(ap, format);
+                while (format && n + 1 < ELEMENTSOF(iovec)) {
+                        char *buf;
+                        va_list aq;
+
+                        /* We need to copy the va_list structure,
+                         * since vasprintf() leaves it afterwards at
+                         * an undefined location */
+
+                        va_copy(aq, ap);
+                        if (vasprintf(&buf, format, aq) < 0) {
+                                va_end(aq);
+                                r = -ENOMEM;
+                                goto finish;
+                        }
+                        va_end(aq);
+
+                        /* Now, jump enough ahead, so that we point to
+                         * the next format string */
+                        VA_FORMAT_ADVANCE(format, ap);
+
+                        IOVEC_SET_STRING(iovec[n++], buf);
+
+                        iovec[n].iov_base = (char*) &nl;
+                        iovec[n].iov_len = 1;
+                        n++;
+
+                        format = va_arg(ap, char *);
+                }
+
+                zero(mh);
+                mh.msg_iov = iovec;
+                mh.msg_iovlen = n;
+
+                if (sendmsg(journal_fd, &mh, MSG_NOSIGNAL) < 0)
+                        r = -errno;
+                else
+                        r = 1;
+
+        finish:
+                va_end(ap);
+                for (i = 1; i < n; i += 2)
+                        free(iovec[i].iov_base);
+
+        } else {
+                char buf[LINE_MAX];
+                bool found = false;
+
+                /* Fallback if journal logging is not available */
+
+                va_start(ap, format);
+                while (format) {
+                        va_list aq;
+
+                        va_copy(aq, ap);
+                        vsnprintf(buf, sizeof(buf), format, aq);
+                        va_end(aq);
+                        char_array_0(buf);
+
+                        if (startswith(buf, "MESSAGE=")) {
+                                found = true;
+                                break;
+                        }
+
+                        VA_FORMAT_ADVANCE(format, ap);
+
+                        format = va_arg(ap, char *);
+                }
+                va_end(ap);
+
+                if (found)
+                        r = log_dispatch(level, file, line, func, buf + 8);
+                else
+                        r = -EINVAL;
+        }
+
+        errno = saved_errno;
+        return r;
+}
+
 int log_set_target_from_string(const char *e) {
         LogTarget t;
 
@@ -678,21 +808,21 @@ int log_set_max_level_from_string(const char *e) {
 void log_parse_environment(void) {
         const char *e;
 
-        if ((e = getenv("SYSTEMD_LOG_TARGET")))
-                if (log_set_target_from_string(e) < 0)
-                        log_warning("Failed to parse log target %s. Ignoring.", e);
+        e = secure_getenv("SYSTEMD_LOG_TARGET");
+        if (e && log_set_target_from_string(e) < 0)
+                log_warning("Failed to parse log target %s. Ignoring.", e);
 
-        if ((e = getenv("SYSTEMD_LOG_LEVEL")))
-                if (log_set_max_level_from_string(e) < 0)
-                        log_warning("Failed to parse log level %s. Ignoring.", e);
+        e = secure_getenv("SYSTEMD_LOG_LEVEL");
+        if (e && log_set_max_level_from_string(e) < 0)
+                log_warning("Failed to parse log level %s. Ignoring.", e);
 
-        if ((e = getenv("SYSTEMD_LOG_COLOR")))
-                if (log_show_color_from_string(e) < 0)
-                        log_warning("Failed to parse bool %s. Ignoring.", e);
+        e = secure_getenv("SYSTEMD_LOG_COLOR");
+        if (e && log_show_color_from_string(e) < 0)
+                log_warning("Failed to parse bool %s. Ignoring.", e);
 
-        if ((e = getenv("SYSTEMD_LOG_LOCATION")))
-                if (log_show_location_from_string(e) < 0)
-                        log_warning("Failed to parse bool %s. Ignoring.", e);
+        e = secure_getenv("SYSTEMD_LOG_LOCATION");
+        if (e && log_show_location_from_string(e) < 0)
+                log_warning("Failed to parse bool %s. Ignoring.", e);
 }
 
 LogTarget log_get_target(void) {
@@ -733,6 +863,13 @@ int log_show_location_from_string(const char *e) {
         return 0;
 }
 
+bool log_on_console(void) {
+        if (log_target == LOG_TARGET_CONSOLE)
+                return true;
+
+        return syslog_fd < 0 && kmsg_fd < 0 && journal_fd < 0;
+}
+
 static const char *const log_target_table[] = {
         [LOG_TARGET_CONSOLE] = "console",
         [LOG_TARGET_KMSG] = "kmsg",
@@ -741,6 +878,7 @@ static const char *const log_target_table[] = {
         [LOG_TARGET_SYSLOG] = "syslog",
         [LOG_TARGET_SYSLOG_OR_KMSG] = "syslog-or-kmsg",
         [LOG_TARGET_AUTO] = "auto",
+        [LOG_TARGET_SAFE] = "safe",
         [LOG_TARGET_NULL] = "null"
 };