/* SPDX-License-Identifier: LGPL-2.1+ */
-/***
- This file is part of systemd.
-
- Copyright 2010 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- 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
- Lesser General Public License for more details.
-
- 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 <errno.h>
#include <fcntl.h>
#include "syslog-util.h"
#include "terminal-util.h"
#include "time-util.h"
+#include "utf8.h"
#include "util.h"
#define SNDBUF_SIZE (8*1024*1024)
#endif // 0
static bool always_reopen_console = false;
static bool open_when_needed = false;
+static bool prohibit_ipc = false;
/* Akin to glibc's __abort_msg; which is private and we hence cannot
* use here. */
static char *log_abort_msg = NULL;
-void log_close_console(void) {
-
- if (console_fd < 0)
- return;
-
- if (getpid_cached() == 1) {
- if (console_fd >= 3)
- safe_close(console_fd);
-
- console_fd = -1;
- }
+/* An assert to use in logging functions that does not call recursively
+ * into our logging functions (since that might lead to a loop). */
+#define assert_raw(expr) \
+ do { \
+ if (_unlikely_(!(expr))) { \
+ fputs(#expr "\n", stderr); \
+ abort(); \
+ } \
+ } while (false)
+
+static void log_close_console(void) {
+ console_fd = safe_close_above_stdio(console_fd);
}
static int log_open_console(void) {
- if (console_fd >= 0)
+ if (!always_reopen_console) {
+ console_fd = STDERR_FILENO;
return 0;
+ }
- if (always_reopen_console) {
+ if (console_fd < 3) {
console_fd = open_terminal("/dev/console", O_WRONLY|O_NOCTTY|O_CLOEXEC);
if (console_fd < 0)
return console_fd;
- } else
- console_fd = STDERR_FILENO;
+
+ console_fd = fd_move_above_stdio(console_fd);
+ }
return 0;
}
-void log_close_kmsg(void) {
+static void log_close_kmsg(void) {
kmsg_fd = safe_close(kmsg_fd);
}
if (kmsg_fd < 0)
return -errno;
+ kmsg_fd = fd_move_above_stdio(kmsg_fd);
return 0;
}
-void log_close_syslog(void) {
+static void log_close_syslog(void) {
syslog_fd = safe_close(syslog_fd);
}
if (fd < 0)
return -errno;
+ fd = fd_move_above_stdio(fd);
(void) fd_inc_sndbuf(fd, SNDBUF_SIZE);
- /* 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. */
+ /* 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. */
if (getpid_cached() == 1)
timeval_store(&tv, 10 * USEC_PER_MSEC);
else
return r;
}
-void log_close_journal(void) {
+static void log_close_journal(void) {
#if 0 /// elogind does not support journald
journal_fd = safe_close(journal_fd);
#endif // 0
/* If we don't use the console we close it here, to not get
* killed by SAK. If we don't use syslog we close it here so
* that we are not confused by somebody deleting the socket in
- * the fs. If we don't use /dev/kmsg we still keep it open,
+ * the fs, and to make sure we don't use it if prohibit_ipc is
+ * set. If we don't use /dev/kmsg we still keep it open,
* because there is no reason to close it. */
if (log_target == LOG_TARGET_NULL) {
return 0;
}
- if (!IN_SET(log_target, LOG_TARGET_AUTO, LOG_TARGET_SAFE) ||
+ if (log_target != LOG_TARGET_AUTO ||
getpid_cached() == 1 ||
isatty(STDERR_FILENO) <= 0) {
#if 0 /// elogind does not support logging to systemd-journald
- if (IN_SET(log_target, LOG_TARGET_AUTO,
+ if (!prohibit_ipc &&
+ IN_SET(log_target, LOG_TARGET_AUTO,
LOG_TARGET_JOURNAL_OR_KMSG,
LOG_TARGET_JOURNAL)) {
r = log_open_journal();
}
#endif // 0
- if (IN_SET(log_target, LOG_TARGET_SYSLOG_OR_KMSG,
+ if (!prohibit_ipc &&
+ IN_SET(log_target, LOG_TARGET_SYSLOG_OR_KMSG,
LOG_TARGET_SYSLOG)) {
r = log_open_syslog();
if (r >= 0) {
}
if (IN_SET(log_target, LOG_TARGET_AUTO,
- LOG_TARGET_SAFE,
LOG_TARGET_JOURNAL_OR_KMSG,
LOG_TARGET_SYSLOG_OR_KMSG,
LOG_TARGET_KMSG)) {
char location[256], prefix[1 + DECIMAL_STR_MAX(int) + 2];
struct iovec iovec[6] = {};
- unsigned n = 0;
bool highlight;
+ size_t n = 0;
if (console_fd < 0)
return 0;
highlight = LOG_PRI(level) <= LOG_ERR && show_color;
if (show_location) {
- xsprintf(location, "(%s:%i) ", file, line);
+ (void) snprintf(location, sizeof location, "(%s:%i) ", file, line);
iovec[n++] = IOVEC_MAKE_STRING(location);
}
const char *file, int line, const char *func,
const char *object_field, const char *object,
const char *extra_field, const char *extra) {
+ int r;
- snprintf(header, size,
- "PRIORITY=%i\n"
- "SYSLOG_FACILITY=%i\n"
- "%s%s%s"
- "%s%.*i%s"
- "%s%s%s"
- "%s%.*i%s"
- "%s%s%s"
- "%s%s%s"
- "SYSLOG_IDENTIFIER=%s\n",
- LOG_PRI(level),
- LOG_FAC(level),
- 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" : "",
- isempty(func) ? "" : "CODE_FUNC=",
- isempty(func) ? "" : func,
- isempty(func) ? "" : "\n",
- error ? "ERRNO=" : "",
- error ? 1 : 0, error,
- error ? "\n" : "",
- isempty(object) ? "" : object_field,
- isempty(object) ? "" : object,
- isempty(object) ? "" : "\n",
- isempty(extra) ? "" : extra_field,
- isempty(extra) ? "" : extra,
- isempty(extra) ? "" : "\n",
- program_invocation_short_name);
+ r = snprintf(header, size,
+ "PRIORITY=%i\n"
+ "SYSLOG_FACILITY=%i\n"
+ "%s%.256s%s" /* CODE_FILE */
+ "%s%.*i%s" /* CODE_LINE */
+ "%s%.256s%s" /* CODE_FUNC */
+ "%s%.*i%s" /* ERRNO */
+ "%s%.256s%s" /* object */
+ "%s%.256s%s" /* extra */
+ "SYSLOG_IDENTIFIER=%.256s\n",
+ LOG_PRI(level),
+ LOG_FAC(level),
+ 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" : "",
+ isempty(func) ? "" : "CODE_FUNC=",
+ isempty(func) ? "" : func,
+ isempty(func) ? "" : "\n",
+ error ? "ERRNO=" : "",
+ error ? 1 : 0, error,
+ error ? "\n" : "",
+ isempty(object) ? "" : object_field,
+ isempty(object) ? "" : object,
+ isempty(object) ? "" : "\n",
+ isempty(extra) ? "" : extra_field,
+ isempty(extra) ? "" : extra,
+ isempty(extra) ? "" : "\n",
+ program_invocation_short_name);
+ assert_raw((size_t) r < size);
return 0;
}
const char *func,
const char *object_field,
const char *object,
- const char *extra,
const char *extra_field,
+ const char *extra,
char *buffer) {
- assert(buffer);
+ assert_raw(buffer);
if (error < 0)
error = -error;
LOG_TARGET_JOURNAL)) {
k = write_to_journal(level, error, file, line, func, object_field, object, extra_field, extra, buffer);
- if (k < 0) {
- if (k != -EAGAIN)
- log_close_journal();
- log_open_kmsg();
- }
+ if (k < 0 && k != -EAGAIN)
+ log_close_journal();
}
#endif // 0
LOG_TARGET_SYSLOG)) {
k = write_to_syslog(level, error, file, line, func, buffer);
- if (k < 0) {
- if (k != -EAGAIN)
- log_close_syslog();
- log_open_kmsg();
- }
+ if (k < 0 && k != -EAGAIN)
+ log_close_syslog();
}
if (k <= 0 &&
IN_SET(log_target, LOG_TARGET_AUTO,
- LOG_TARGET_SAFE,
LOG_TARGET_SYSLOG_OR_KMSG,
LOG_TARGET_JOURNAL_OR_KMSG,
LOG_TARGET_KMSG)) {
+ if (k < 0)
+ log_open_kmsg();
+
k = write_to_kmsg(level, error, file, line, func, buffer);
if (k < 0) {
log_close_kmsg();
if (_likely_(LOG_PRI(level) > log_max_level[realm]))
return -error;
- /* Make sure that %m maps to the specified error */
- if (error != 0)
- errno = error;
+ /* Make sure that %m maps to the specified error (or "Success"). */
+ errno = error;
- vsnprintf(buffer, sizeof(buffer), format, ap);
+ (void) vsnprintf(buffer, sizeof buffer, format, ap);
return log_dispatch_internal(level, error, file, line, func, NULL, NULL, NULL, NULL, buffer);
}
return r;
}
-int log_object_internalv(
+_printf_(10,0)
+static int log_object_internalv(
int level,
int error,
const char *file,
if (_likely_(LOG_PRI(level) > log_max_level[LOG_REALM_SYSTEMD]))
return -error;
- /* Make sure that %m maps to the specified error */
- if (error != 0)
- errno = error;
+ /* Make sure that %m maps to the specified error (or "Success"). */
+ errno = error;
/* Prepend the object name before the message */
if (object) {
} else
b = buffer = newa(char, LINE_MAX);
- vsnprintf(b, LINE_MAX, format, ap);
+ (void) vsnprintf(b, LINE_MAX, format, ap);
return log_dispatch_internal(level, error, file, line, func,
object_field, object, extra_field, extra, buffer);
return;
DISABLE_WARNING_FORMAT_NONLITERAL;
- xsprintf(buffer, format, text, file, line, func);
+ (void) snprintf(buffer, sizeof buffer, format, text, file, line, func);
REENABLE_WARNING;
log_abort_msg = buffer;
log_dispatch_internal(level, 0, file, line, func, NULL, NULL, NULL, NULL, buffer);
}
-noreturn void log_assert_failed_realm(
+_noreturn_ void log_assert_failed_realm(
LogRealm realm,
const char *text,
const char *file,
abort();
}
-noreturn void log_assert_failed_unreachable_realm(
+_noreturn_ void log_assert_failed_unreachable_realm(
LogRealm realm,
const char *text,
const char *file,
* since vasprintf() leaves it afterwards at
* an undefined location */
- if (error != 0)
- errno = error;
+ errno = error;
va_copy(aq, ap);
r = vasprintf(&m, format, aq);
while (format) {
va_list aq;
- if (error != 0)
- errno = error;
+ errno = error;
va_copy(aq, ap);
- vsnprintf(buf, sizeof(buf), format, aq);
+ (void) vsnprintf(buf, sizeof buf, format, aq);
va_end(aq);
if (startswith(buf, "MESSAGE=")) {
return -error;
}
- for (i = 0; i < n_input_iovec; i++) {
- if (input_iovec[i].iov_len < STRLEN("MESSAGE="))
- continue;
-
- if (memcmp(input_iovec[i].iov_base, "MESSAGE=", STRLEN("MESSAGE=")) == 0)
+ for (i = 0; i < n_input_iovec; i++)
+ if (memory_startswith(input_iovec[i].iov_base, input_iovec[i].iov_len, "MESSAGE="))
break;
- }
if (_unlikely_(i >= n_input_iovec)) /* Couldn't find MESSAGE=? */
return -error;
[LOG_TARGET_SYSLOG] = "syslog",
[LOG_TARGET_SYSLOG_OR_KMSG] = "syslog-or-kmsg",
[LOG_TARGET_AUTO] = "auto",
- [LOG_TARGET_SAFE] = "safe",
- [LOG_TARGET_NULL] = "null"
+ [LOG_TARGET_NULL] = "null",
};
DEFINE_STRING_TABLE_LOOKUP(log_target, LogTarget);
#if 0 /// UNNEEDED by elogind
void log_received_signal(int level, const struct signalfd_siginfo *si) {
- if (si->ssi_pid > 0) {
+ assert(si);
+
+ if (pid_is_valid(si->ssi_pid)) {
_cleanup_free_ char *p = NULL;
- get_process_comm(si->ssi_pid, &p);
+ (void) get_process_comm(si->ssi_pid, &p);
log_full(level,
"Received SIG%s from PID %"PRIu32" (%s).",
log_full(level,
"Received SIG%s.",
signal_to_string(si->ssi_signo));
-
}
#endif // 0
if (log_target == LOG_TARGET_NULL)
return -error;
- if (error != 0)
- errno = error;
+ errno = error;
va_start(ap, format);
- vsnprintf(buffer, sizeof(buffer), format, ap);
+ (void) vsnprintf(buffer, sizeof buffer, format, ap);
va_end(ap);
if (unit)
NULL);
}
+int log_syntax_invalid_utf8_internal(
+ const char *unit,
+ int level,
+ const char *config_file,
+ unsigned config_line,
+ const char *file,
+ int line,
+ const char *func,
+ const char *rvalue) {
+
+ _cleanup_free_ char *p = NULL;
+
+ if (rvalue)
+ p = utf8_escape_invalid(rvalue);
+
+ log_syntax_internal(unit, level, config_file, config_line, 0, file, line, func,
+ "String is not UTF-8 clean, ignoring assignment: %s", strna(p));
+
+ return -EINVAL;
+}
+
#if 0 /// UNNEEDED by elogind
void log_set_upgrade_syslog_to_journal(bool b) {
upgrade_syslog_to_journal = b;
+
+ /* Make the change effective immediately */
+ if (b) {
+ if (log_target == LOG_TARGET_SYSLOG)
+ log_target = LOG_TARGET_JOURNAL;
+ else if (log_target == LOG_TARGET_SYSLOG_OR_KMSG)
+ log_target = LOG_TARGET_JOURNAL_OR_KMSG;
+ }
}
void log_set_always_reopen_console(bool b) {
void log_set_open_when_needed(bool b) {
open_when_needed = b;
}
+
+void log_set_prohibit_ipc(bool b) {
+ prohibit_ipc = b;
+}
+
+int log_emergency_level(void) {
+ /* Returns the log level to use for log_emergency() logging. We use LOG_EMERG only when we are PID 1, as only
+ * then the system of the whole system is obviously affected. */
+
+ return getpid_cached() == 1 ? LOG_EMERG : LOG_ERR;
+}
+
+int log_dup_console(void) {
+ int copy;
+
+ /* Duplicate the fd we use for fd logging if it's < 3 and use the copy from now on. This call is useful
+ * whenever we want to continue logging through the original fd, but want to rearrange stderr. */
+
+ if (console_fd >= 3)
+ return 0;
+
+ copy = fcntl(console_fd, F_DUPFD_CLOEXEC, 3);
+ if (copy < 0)
+ return -errno;
+
+ console_fd = copy;
+ return 0;
+}