static bool show_color = false;
static bool show_location = false;
+static bool upgrade_syslog_to_journal = false;
+
/* Akin to glibc's __abort_msg; which is private and we hence cannot
* use here. */
static char *log_abort_msg = NULL;
if (getpid() == 1) {
if (console_fd >= 3)
- close_nointr_nofail(console_fd);
+ safe_close(console_fd);
console_fd = -1;
}
}
void log_close_kmsg(void) {
-
- if (kmsg_fd < 0)
- return;
-
- close_nointr_nofail(kmsg_fd);
- kmsg_fd = -1;
+ kmsg_fd = safe_close(kmsg_fd);
}
static int log_open_kmsg(void) {
}
void log_close_syslog(void) {
-
- if (syslog_fd < 0)
- return;
-
- close_nointr_nofail(syslog_fd);
- syslog_fd = -1;
+ syslog_fd = safe_close(syslog_fd);
}
static int create_log_socket(int type) {
- int fd;
struct timeval tv;
+ int fd;
fd = socket(AF_UNIX, type|SOCK_CLOEXEC, 0);
if (fd < 0)
/* We need a blocking fd here since we'd otherwise lose
messages way too early. However, let's not hang forever in the
unlikely case of a deadlock. */
- timeval_store(&tv, 1*USEC_PER_MINUTE);
- setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
+ if (getpid() == 1)
+ timeval_store(&tv, 10 * USEC_PER_MSEC);
+ else
+ timeval_store(&tv, 10 * USEC_PER_SEC);
+ (void) setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
return fd;
}
static int log_open_syslog(void) {
- int r;
- union sockaddr_union sa = {
+
+ static const union sockaddr_union sa = {
.un.sun_family = AF_UNIX,
.un.sun_path = "/dev/log",
};
+ int r;
+
if (syslog_fd >= 0)
return 0;
}
if (connect(syslog_fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path)) < 0) {
- close_nointr_nofail(syslog_fd);
+ safe_close(syslog_fd);
/* Some legacy syslog systems still use stream
* sockets. They really shouldn't. But what can we
}
void log_close_journal(void) {
-
- if (journal_fd < 0)
- return;
-
- close_nointr_nofail(journal_fd);
- journal_fd = -1;
+ journal_fd = safe_close(journal_fd);
}
static int log_open_journal(void) {
- union sockaddr_union sa = {
+
+ static const union sockaddr_union sa = {
.un.sun_family = AF_UNIX,
.un.sun_path = "/run/systemd/journal/socket",
};
+
int r;
if (journal_fd >= 0)
assert(target >= 0);
assert(target < _LOG_TARGET_MAX);
+ if (upgrade_syslog_to_journal) {
+ if (target == LOG_TARGET_SYSLOG)
+ target = LOG_TARGET_JOURNAL;
+ else if (target == LOG_TARGET_SYSLOG_OR_KMSG)
+ target = LOG_TARGET_JOURNAL_OR_KMSG;
+ }
+
log_target = target;
}
static int write_to_console(
int level,
- const char*file,
+ int error,
+ const char *file,
int line,
const char *func,
- const char *object_name,
+ const char *object_field,
const char *object,
const char *buffer) {
- char location[64];
- struct iovec iovec[5] = {};
+ char location[64], prefix[1 + DECIMAL_STR_MAX(int) + 2];
+ struct iovec iovec[6] = {};
unsigned n = 0;
bool highlight;
if (console_fd < 0)
return 0;
+ if (log_target == LOG_TARGET_CONSOLE_PREFIXED) {
+ sprintf(prefix, "<%i>", level);
+ IOVEC_SET_STRING(iovec[n++], prefix);
+ }
+
highlight = LOG_PRI(level) <= LOG_ERR && show_color;
if (show_location) {
- snprintf(location, sizeof(location), "(%s:%u) ", file, line);
- char_array_0(location);
+ snprintf(location, sizeof(location), "(%s:%i) ", file, line);
IOVEC_SET_STRING(iovec[n++], location);
}
}
static int write_to_syslog(
- int level,
- const char*file,
- int line,
- const char *func,
- const char *object_name,
- const char *object,
- const char *buffer) {
+ int level,
+ int error,
+ const char *file,
+ int line,
+ const char *func,
+ const char *object_field,
+ const char *object,
+ const char *buffer) {
- char header_priority[16], header_time[64], header_pid[16];
+ char header_priority[2 + DECIMAL_STR_MAX(int) + 1],
+ header_time[64],
+ header_pid[4 + DECIMAL_STR_MAX(pid_t) + 1];
struct iovec iovec[5] = {};
struct msghdr msghdr = {
.msg_iov = iovec,
if (syslog_fd < 0)
return 0;
- snprintf(header_priority, sizeof(header_priority), "<%i>", level);
- char_array_0(header_priority);
+ xsprintf(header_priority, "<%i>", level);
t = (time_t) (now(CLOCK_REALTIME) / USEC_PER_SEC);
tm = localtime(&t);
if (strftime(header_time, sizeof(header_time), "%h %e %T ", tm) <= 0)
return -EINVAL;
- snprintf(header_pid, sizeof(header_pid), "[%lu]: ", (unsigned long) getpid());
- char_array_0(header_pid);
+ xsprintf(header_pid, "["PID_FMT"]: ", getpid());
IOVEC_SET_STRING(iovec[0], header_priority);
IOVEC_SET_STRING(iovec[1], header_time);
}
static int write_to_kmsg(
- int level,
- const char*file,
- int line,
- const char *func,
- const char *object_name,
- const char *object,
- const char *buffer) {
+ int level,
+ int error,
+ const char*file,
+ int line,
+ const char *func,
+ const char *object_field,
+ const char *object,
+ const char *buffer) {
- char header_priority[16], header_pid[16];
+ char header_priority[2 + DECIMAL_STR_MAX(int) + 1],
+ header_pid[4 + DECIMAL_STR_MAX(pid_t) + 1];
struct iovec iovec[5] = {};
if (kmsg_fd < 0)
return 0;
- snprintf(header_priority, sizeof(header_priority), "<%i>", level);
- char_array_0(header_priority);
-
- snprintf(header_pid, sizeof(header_pid), "[%lu]: ", (unsigned long) getpid());
- char_array_0(header_pid);
+ xsprintf(header_priority, "<%i>", level);
+ xsprintf(header_pid, "["PID_FMT"]: ", getpid());
IOVEC_SET_STRING(iovec[0], header_priority);
IOVEC_SET_STRING(iovec[1], program_invocation_short_name);
return 1;
}
-static int log_do_header(char *header, size_t size,
- int level,
- const char *file, int line, const char *func,
- const char *object_name, const char *object) {
+static int log_do_header(
+ char *header,
+ size_t size,
+ int level,
+ int error,
+ const char *file, int line, const char *func,
+ const char *object_field, const char *object) {
+
snprintf(header, size,
"PRIORITY=%i\n"
"SYSLOG_FACILITY=%i\n"
- "%s%.*s%s"
+ "%s%s%s"
+ "%s%.*i%s"
+ "%s%s%s"
"%s%.*i%s"
- "%s%.*s%s"
- "%s%.*s%s"
+ "%s%s%s"
"SYSLOG_IDENTIFIER=%s\n",
LOG_PRI(level),
LOG_FAC(level),
- file ? "CODE_FILE=" : "",
- file ? LINE_MAX : 0, file, /* %.0s means no output */
- file ? "\n" : "",
+ isempty(file) ? "" : "CODE_FILE=",
+ isempty(file) ? "" : file,
+ isempty(file) ? "" : "\n",
line ? "CODE_LINE=" : "",
line ? 1 : 0, line, /* %.0d means no output too, special case for 0 */
line ? "\n" : "",
- func ? "CODE_FUNCTION=" : "",
- func ? LINE_MAX : 0, func,
- func ? "\n" : "",
- object ? object_name : "",
- object ? LINE_MAX : 0, object, /* %.0s means no output */
- object ? "\n" : "",
+ isempty(func) ? "" : "CODE_FUNCTION=",
+ isempty(func) ? "" : func,
+ isempty(func) ? "" : "\n",
+ error ? "ERRNO=" : "",
+ error ? 1 : 0, error,
+ error ? "\n" : "",
+ isempty(object) ? "" : object_field,
+ isempty(object) ? "" : object,
+ isempty(object) ? "" : "\n",
program_invocation_short_name);
- header[size - 1] = '\0';
+
return 0;
}
static int write_to_journal(
- int level,
- const char*file,
- int line,
- const char *func,
- const char *object_name,
- const char *object,
- const char *buffer) {
+ int level,
+ int error,
+ const char*file,
+ int line,
+ const char *func,
+ const char *object_field,
+ const char *object,
+ const char *buffer) {
char header[LINE_MAX];
struct iovec iovec[4] = {};
if (journal_fd < 0)
return 0;
- log_do_header(header, sizeof(header), level,
- file, line, func, object_name, object);
+ log_do_header(header, sizeof(header), level, error, file, line, func, object_field, object);
IOVEC_SET_STRING(iovec[0], header);
IOVEC_SET_STRING(iovec[1], "MESSAGE=");
}
static int log_dispatch(
- int level,
- const char*file,
- int line,
- const char *func,
- const char *object_name,
- const char *object,
- char *buffer) {
+ int level,
+ int error,
+ const char *file,
+ int line,
+ const char *func,
+ const char *object_field,
+ const char *object,
+ char *buffer) {
- int r = 0;
+ assert(buffer);
if (log_target == LOG_TARGET_NULL)
- return 0;
+ return -error;
/* Patch in LOG_DAEMON facility if necessary */
if ((level & LOG_FACMASK) == 0)
level = log_facility | LOG_PRI(level);
+ if (error < 0)
+ error = -error;
+
do {
char *e;
int k = 0;
log_target == LOG_TARGET_JOURNAL_OR_KMSG ||
log_target == LOG_TARGET_JOURNAL) {
- k = write_to_journal(level, file, line, func,
- object_name, object, buffer);
+ k = write_to_journal(level, error, file, line, func, object_field, object, buffer);
if (k < 0) {
if (k != -EAGAIN)
log_close_journal();
log_open_kmsg();
- } else if (k > 0)
- r++;
+ }
}
if (log_target == LOG_TARGET_SYSLOG_OR_KMSG ||
log_target == LOG_TARGET_SYSLOG) {
- k = write_to_syslog(level, file, line, func,
- object_name, object, buffer);
+ k = write_to_syslog(level, error, file, line, func, object_field, object, buffer);
if (k < 0) {
if (k != -EAGAIN)
log_close_syslog();
log_open_kmsg();
- } else if (k > 0)
- r++;
+ }
}
if (k <= 0 &&
log_target == LOG_TARGET_JOURNAL_OR_KMSG ||
log_target == LOG_TARGET_KMSG)) {
- k = write_to_kmsg(level, file, line, func,
- object_name, object, buffer);
+ k = write_to_kmsg(level, error, file, line, func, object_field, object, buffer);
if (k < 0) {
log_close_kmsg();
log_open_console();
- } else if (k > 0)
- r++;
+ }
}
- if (k <= 0) {
- k = write_to_console(level, file, line, func,
- object_name, object, buffer);
- if (k < 0)
- return k;
- }
+ if (k <= 0)
+ (void) write_to_console(level, error, file, line, func, object_field, object, buffer);
buffer = e;
} while (buffer);
- return r;
+ return -error;
}
int log_dump_internal(
int level,
- const char*file,
+ int error,
+ const char *file,
int line,
const char *func,
char *buffer) {
/* This modifies the buffer... */
+ if (error < 0)
+ error = -error;
+
if (_likely_(LOG_PRI(level) > log_max_level))
- return 0;
+ return -error;
- return log_dispatch(level, file, line, func, NULL, NULL, buffer);
+ return log_dispatch(level, error, file, line, func, NULL, NULL, buffer);
}
-int log_metav(
- int level,
- const char*file,
- int line,
- const char *func,
- const char *format,
- va_list ap) {
+int log_internalv(
+ int level,
+ int error,
+ const char*file,
+ int line,
+ const char *func,
+ const char *format,
+ va_list ap) {
PROTECT_ERRNO;
char buffer[LINE_MAX];
+ if (error < 0)
+ error = -error;
+
if (_likely_(LOG_PRI(level) > log_max_level))
- return 0;
+ return -error;
+
+ /* Make sure that %m maps to the specified error */
+ if (error != 0)
+ errno = error;
vsnprintf(buffer, sizeof(buffer), format, ap);
- char_array_0(buffer);
- return log_dispatch(level, file, line, func, NULL, NULL, buffer);
+ return log_dispatch(level, error, file, line, func, NULL, NULL, buffer);
}
-int log_meta(
- int level,
- const char*file,
- int line,
- const char *func,
- const char *format, ...) {
+int log_internal(
+ int level,
+ int error,
+ const char*file,
+ int line,
+ const char *func,
+ const char *format, ...) {
- int r;
va_list ap;
+ int r;
va_start(ap, format);
- r = log_metav(level, file, line, func, format, ap);
+ r = log_internalv(level, error, file, line, func, format, ap);
va_end(ap);
return r;
}
-int log_metav_object(
- int level,
- const char*file,
- int line,
- const char *func,
- const char *object_name,
- const char *object,
- const char *format,
- va_list ap) {
+int log_object_internalv(
+ int level,
+ int error,
+ const char*file,
+ int line,
+ const char *func,
+ const char *object_field,
+ const char *object,
+ const char *format,
+ va_list ap) {
PROTECT_ERRNO;
char buffer[LINE_MAX];
+ if (error < 0)
+ error = -error;
+
if (_likely_(LOG_PRI(level) > log_max_level))
- return 0;
+ return -error;
+
+ /* Make sure that %m maps to the specified error */
+ if (error != 0)
+ errno = error;
vsnprintf(buffer, sizeof(buffer), format, ap);
- char_array_0(buffer);
- return log_dispatch(level, file, line, func,
- object_name, object, buffer);
+ return log_dispatch(level, error, file, line, func, object_field, object, buffer);
}
-int log_meta_object(
- int level,
- const char*file,
- int line,
- const char *func,
- const char *object_name,
- const char *object,
- const char *format, ...) {
+int log_object_internal(
+ int level,
+ int error,
+ const char*file,
+ int line,
+ const char *func,
+ const char *object_field,
+ const char *object,
+ const char *format, ...) {
- int r;
va_list ap;
+ int r;
va_start(ap, format);
- r = log_metav_object(level, file, line, func,
- object_name, object, format, ap);
+ r = log_object_internalv(level, error, file, line, func, object_field, object, format, ap);
va_end(ap);
return r;
}
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wformat-nonliteral"
-static void log_assert(int level, const char *text, const char *file, int line, const char *func, const char *format) {
+static void log_assert(
+ int level,
+ const char *text,
+ const char *file,
+ int line,
+ const char *func,
+ const char *format) {
+
static char buffer[LINE_MAX];
if (_likely_(LOG_PRI(level) > log_max_level))
return;
+ DISABLE_WARNING_FORMAT_NONLITERAL;
snprintf(buffer, sizeof(buffer), format, text, file, line, func);
+ REENABLE_WARNING;
- char_array_0(buffer);
log_abort_msg = buffer;
- log_dispatch(level, file, line, func, NULL, NULL, buffer);
+ log_dispatch(level, 0, file, line, func, NULL, NULL, buffer);
}
-#pragma GCC diagnostic pop
noreturn void log_assert_failed(const char *text, const char *file, int line, const char *func) {
log_assert(LOG_CRIT, text, file, line, func, "Assertion '%s' failed 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.");
+ log_internal(LOG_ERR, ENOMEM, file, line, func, "Out of memory.");
return -ENOMEM;
}
int log_struct_internal(
int level,
+ int error,
const char *file,
int line,
const char *func,
const char *format, ...) {
+ char buf[LINE_MAX];
+ bool found = false;
PROTECT_ERRNO;
va_list ap;
- int r;
+
+ if (error < 0)
+ error = -error;
if (_likely_(LOG_PRI(level) > log_max_level))
- return 0;
+ return -error;
if (log_target == LOG_TARGET_NULL)
- return 0;
+ return -error;
if ((level & LOG_FACMASK) == 0)
level = log_facility | LOG_PRI(level);
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;
.msg_iov = iovec,
};
static const char nl = '\n';
+ bool fallback = false;
/* If the journal is available do structured logging */
- log_do_header(header, sizeof(header), level,
- file, line, func, NULL, NULL);
+ log_do_header(header, sizeof(header), level, error, file, line, func, NULL, NULL);
IOVEC_SET_STRING(iovec[n++], header);
va_start(ap, format);
while (format && n + 1 < ELEMENTSOF(iovec)) {
- char *buf;
va_list aq;
+ char *m;
/* We need to copy the va_list structure,
* since vasprintf() leaves it afterwards at
* an undefined location */
+ if (error != 0)
+ errno = error;
+
va_copy(aq, ap);
- if (vasprintf(&buf, format, aq) < 0) {
+ if (vasprintf(&m, format, aq) < 0) {
va_end(aq);
- r = -ENOMEM;
+ fallback = true;
goto finish;
}
va_end(aq);
* the next format string */
VA_FORMAT_ADVANCE(format, ap);
- IOVEC_SET_STRING(iovec[n++], buf);
+ IOVEC_SET_STRING(iovec[n++], m);
iovec[n].iov_base = (char*) &nl;
iovec[n].iov_len = 1;
mh.msg_iovlen = n;
- if (sendmsg(journal_fd, &mh, MSG_NOSIGNAL) < 0)
- r = -errno;
- else
- r = 1;
+ (void) sendmsg(journal_fd, &mh, MSG_NOSIGNAL);
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 */
+ if (!fallback)
+ return -error;
+ }
- va_start(ap, format);
- while (format) {
- va_list aq;
+ /* Fallback if journal logging is not available or didn't work. */
- va_copy(aq, ap);
- vsnprintf(buf, sizeof(buf), format, aq);
- va_end(aq);
- char_array_0(buf);
+ va_start(ap, format);
+ while (format) {
+ va_list aq;
- if (startswith(buf, "MESSAGE=")) {
- found = true;
- break;
- }
+ if (error != 0)
+ errno = error;
- VA_FORMAT_ADVANCE(format, ap);
+ va_copy(aq, ap);
+ vsnprintf(buf, sizeof(buf), format, aq);
+ va_end(aq);
- format = va_arg(ap, char *);
+ if (startswith(buf, "MESSAGE=")) {
+ found = true;
+ break;
}
- va_end(ap);
- if (found)
- r = log_dispatch(level, file, line, func,
- NULL, NULL, buf + 8);
- else
- r = -EINVAL;
+ VA_FORMAT_ADVANCE(format, ap);
+
+ format = va_arg(ap, char *);
}
+ va_end(ap);
- return r;
+ if (!found)
+ return -error;
+
+ return log_dispatch(level, error, file, line, func, NULL, NULL, buf + 8);
}
int log_set_target_from_string(const char *e) {
return 0;
}
+static int parse_proc_cmdline_item(const char *key, const char *value) {
+
+ /*
+ * The systemd.log_xyz= settings are parsed by all tools, and
+ * so is "debug".
+ *
+ * However, "quiet" is only parsed by PID 1, and only turns of
+ * status output to /dev/console, but does not alter the log
+ * level.
+ */
+
+ if (streq(key, "debug") && !value)
+ log_set_max_level(LOG_DEBUG);
+
+ else if (streq(key, "systemd.log_target") && value) {
+
+ if (log_set_target_from_string(value) < 0)
+ log_warning("Failed to parse log target '%s'. Ignoring.", value);
+
+ } else if (streq(key, "systemd.log_level") && value) {
+
+ if (log_set_max_level_from_string(value) < 0)
+ log_warning("Failed to parse log level '%s'. Ignoring.", value);
+
+ } else if (streq(key, "systemd.log_color") && value) {
+
+ if (log_show_color_from_string(value) < 0)
+ log_warning("Failed to parse log color setting '%s'. Ignoring.", value);
+
+ } else if (streq(key, "systemd.log_location") && value) {
+
+ if (log_show_location_from_string(value) < 0)
+ log_warning("Failed to parse log location setting '%s'. Ignoring.", value);
+ }
+
+ return 0;
+}
+
void log_parse_environment(void) {
const char *e;
+ if (get_ctty_devnr(0, NULL) < 0)
+ /* Only try to read the command line in daemons.
+ We assume that anything that has a controlling
+ tty is user stuff. */
+ (void) parse_proc_cmdline(parse_proc_cmdline_item);
+
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);
+ log_warning("Failed to parse log target '%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);
+ log_warning("Failed to parse log level '%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);
+ 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);
+ log_warning("Failed to parse bool '%s'. Ignoring.", e);
}
LogTarget log_get_target(void) {
show_color = b;
}
+bool log_get_show_color(void) {
+ return show_color;
+}
+
void log_show_location(bool b) {
show_location = b;
}
+bool log_get_show_location(void) {
+ return show_location;
+}
+
int log_show_color_from_string(const char *e) {
int t;
}
bool log_on_console(void) {
- if (log_target == LOG_TARGET_CONSOLE)
+ if (log_target == LOG_TARGET_CONSOLE ||
+ log_target == LOG_TARGET_CONSOLE_PREFIXED)
return true;
return syslog_fd < 0 && kmsg_fd < 0 && journal_fd < 0;
}
-static const char *const log_target_table[] = {
+static const char *const log_target_table[_LOG_TARGET_MAX] = {
[LOG_TARGET_CONSOLE] = "console",
+ [LOG_TARGET_CONSOLE_PREFIXED] = "console-prefixed",
[LOG_TARGET_KMSG] = "kmsg",
[LOG_TARGET_JOURNAL] = "journal",
[LOG_TARGET_JOURNAL_OR_KMSG] = "journal-or-kmsg",
};
DEFINE_STRING_TABLE_LOOKUP(log_target, LogTarget);
+
+void log_received_signal(int level, const struct signalfd_siginfo *si) {
+ if (si->ssi_pid > 0) {
+ _cleanup_free_ char *p = NULL;
+
+ get_process_comm(si->ssi_pid, &p);
+
+ log_full(level,
+ "Received SIG%s from PID %"PRIu32" (%s).",
+ signal_to_string(si->ssi_signo),
+ si->ssi_pid, strna(p));
+ } else
+ log_full(level,
+ "Received SIG%s.",
+ signal_to_string(si->ssi_signo));
+
+}
+
+void log_set_upgrade_syslog_to_journal(bool b) {
+ upgrade_syslog_to_journal = b;
+}