diff --git a/src/basic/log.c b/src/basic/log.c index 4f0fe54579..81e106803f 100644 --- a/src/basic/log.c +++ b/src/basic/log.c @@ -847,8 +847,8 @@ int log_oom_internal(LogRealm realm, const char *file, int line, const char *fun int log_format_iovec( struct iovec *iovec, - unsigned iovec_len, - unsigned *n, + size_t iovec_len, + size_t *n, bool newline_separator, int error, const char *format, @@ -928,7 +928,7 @@ int log_struct_internal( if (journal_fd >= 0) { char header[LINE_MAX]; struct iovec iovec[17] = {}; - unsigned n = 0, i; + size_t n = 0, i; int r; struct msghdr mh = { .msg_iov = iovec, diff --git a/src/basic/log.h b/src/basic/log.h index 10a6032788..e2608b2a92 100644 --- a/src/basic/log.h +++ b/src/basic/log.h @@ -180,8 +180,8 @@ int log_oom_internal( int log_format_iovec( struct iovec *iovec, - unsigned iovec_len, - unsigned *n, + size_t iovec_len, + size_t *n, bool newline_separator, int error, const char *format, diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c index 30861617bd..eeb4ac9a01 100644 --- a/src/core/dbus-execute.c +++ b/src/core/dbus-execute.c @@ -26,15 +26,17 @@ #include "af-list.h" #include "alloc-util.h" #include "bus-util.h" -#include "capability-util.h" #include "cap-list.h" +#include "capability-util.h" #include "dbus-execute.h" #include "env-util.h" #include "errno-list.h" #include "execute.h" #include "fd-util.h" #include "fileio.h" +#include "io-util.h" #include "ioprio.h" +#include "journal-util.h" #include "missing.h" #include "mount-util.h" #include "namespace.h" @@ -771,6 +773,37 @@ static int property_get_bind_paths( return sd_bus_message_close_container(reply); } +static int property_get_log_extra_fields( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + ExecContext *c = userdata; + size_t i; + int r; + + assert(bus); + assert(c); + assert(property); + assert(reply); + + r = sd_bus_message_open_container(reply, 'a', "ay"); + if (r < 0) + return r; + + for (i = 0; i < c->n_log_extra_fields; i++) { + r = sd_bus_message_append_array(reply, 'y', c->log_extra_fields[i].iov_base, c->log_extra_fields[i].iov_len); + if (r < 0) + return r; + } + + return sd_bus_message_close_container(reply); +} + const sd_bus_vtable bus_exec_vtable[] = { SD_BUS_VTABLE_START(0), SD_BUS_PROPERTY("Environment", "as", NULL, offsetof(ExecContext, environment), SD_BUS_VTABLE_PROPERTY_CONST), @@ -838,6 +871,8 @@ const sd_bus_vtable bus_exec_vtable[] = { SD_BUS_PROPERTY("SyslogLevelPrefix", "b", bus_property_get_bool, offsetof(ExecContext, syslog_level_prefix), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("SyslogLevel", "i", property_get_syslog_level, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("SyslogFacility", "i", property_get_syslog_facility, 0, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("LogLevelMax", "i", bus_property_get_int, offsetof(ExecContext, log_level_max), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("LogExtraFields", "aay", property_get_log_extra_fields, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("SecureBits", "i", bus_property_get_int, offsetof(ExecContext, secure_bits), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("CapabilityBoundingSet", "t", property_get_capability_bounding_set, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("AmbientCapabilities", "t", property_get_ambient_capabilities, 0, SD_BUS_VTABLE_PROPERTY_CONST), @@ -1137,6 +1172,98 @@ int bus_exec_context_set_transient_property( } return 1; + + } else if (streq(name, "LogLevelMax")) { + int32_t level; + + r = sd_bus_message_read(message, "i", &level); + if (r < 0) + return r; + + if (!log_level_is_valid(level)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Maximum log level value out of range"); + + if (mode != UNIT_CHECK) { + c->log_level_max = level; + unit_write_drop_in_private_format(u, mode, name, "LogLevelMax=%i", level); + } + + return 1; + + } else if (streq(name, "LogExtraFields")) { + size_t n = 0; + + r = sd_bus_message_enter_container(message, 'a', "ay"); + if (r < 0) + return r; + + for (;;) { + _cleanup_free_ void *copy = NULL; + struct iovec *t; + const char *eq; + const void *p; + size_t sz; + + /* Note that we expect a byte array for each field, instead of a string. That's because on the + * lower-level journal fields can actually contain binary data and are not restricted to text, + * and we should not "lose precision" in our types on the way. That said, I am pretty sure + * actually encoding binary data as unit metadata is not a good idea. Hence we actually refuse + * any actual binary data, and only accept UTF-8. This allows us to eventually lift this + * limitation, should a good, valid usecase arise. */ + + r = sd_bus_message_read_array(message, 'y', &p, &sz); + if (r < 0) + return r; + if (r == 0) + break; + + if (memchr(p, 0, sz)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Journal field contains zero byte"); + + eq = memchr(p, '=', sz); + if (!eq) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Journal field contains no '=' character"); + if (!journal_field_valid(p, eq - (const char*) p, false)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Journal field invalid"); + + if (mode != UNIT_CHECK) { + t = realloc_multiply(c->log_extra_fields, sizeof(struct iovec), c->n_log_extra_fields+1); + if (!t) + return -ENOMEM; + c->log_extra_fields = t; + } + + copy = malloc(sz + 1); + if (!copy) + return -ENOMEM; + + memcpy(copy, p, sz); + ((uint8_t*) copy)[sz] = 0; + + if (!utf8_is_valid(copy)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Journal field is not valid UTF-8"); + + if (mode != UNIT_CHECK) { + c->log_extra_fields[c->n_log_extra_fields++] = IOVEC_MAKE(copy, sz); + unit_write_drop_in_private_format(u, mode, name, "LogExtraFields=%s", (char*) copy); + + copy = NULL; + } + + n++; + } + + r = sd_bus_message_exit_container(message); + if (r < 0) + return r; + + if (mode != UNIT_CHECK && n == 0) { + exec_context_free_log_extra_fields(c); + unit_write_drop_in_private(u, mode, name, "LogExtraFields="); + } + + return 1; + } else if (streq(name, "SecureBits")) { int n; diff --git a/src/core/execute.c b/src/core/execute.c index 9f7181549e..45df0318e0 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -3483,11 +3483,12 @@ void exec_context_init(ExecContext *c) { c->directories[i].mode = 0755; c->capability_bounding_set = CAP_ALL; c->restrict_namespaces = NAMESPACE_FLAGS_ALL; + c->log_level_max = -1; } void exec_context_done(ExecContext *c) { - unsigned l; ExecDirectoryType i; + size_t l; assert(c); @@ -3534,6 +3535,10 @@ void exec_context_done(ExecContext *c) { for (i = 0; i < _EXEC_DIRECTORY_TYPE_MAX; i++) c->directories[i].paths = strv_free(c->directories[i].paths); + + c->log_level_max = -1; + + exec_context_free_log_extra_fields(c); } int exec_context_destroy_runtime_directory(ExecContext *c, const char *runtime_prefix) { @@ -3796,9 +3801,9 @@ static void strv_fprintf(FILE *f, char **l) { } void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) { + ExecDirectoryType dt; char **e, **d; unsigned i; - ExecDirectoryType dt; int r; assert(c); @@ -3966,6 +3971,26 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) { fprintf(f, "%sSyslogLevel: %s\n", prefix, lvl_str); } + if (c->log_level_max >= 0) { + _cleanup_free_ char *t = NULL; + + (void) log_level_to_string_alloc(c->log_level_max, &t); + + fprintf(f, "%sLogLevelMax: %s\n", prefix, strna(t)); + } + + if (c->n_log_extra_fields > 0) { + size_t j; + + for (j = 0; j < c->n_log_extra_fields; j++) { + fprintf(f, "%sLogExtraFields: ", prefix); + fwrite(c->log_extra_fields[j].iov_base, + 1, c->log_extra_fields[j].iov_len, + f); + fputc('\n', f); + } + } + if (c->secure_bits) { _cleanup_free_ char *str = NULL; @@ -4177,6 +4202,17 @@ int exec_context_get_effective_ioprio(ExecContext *c) { return p; } +void exec_context_free_log_extra_fields(ExecContext *c) { + size_t l; + + assert(c); + + for (l = 0; l < c->n_log_extra_fields; l++) + free(c->log_extra_fields[l].iov_base); + c->log_extra_fields = mfree(c->log_extra_fields); + c->n_log_extra_fields = 0; +} + void exec_status_start(ExecStatus *s, pid_t pid) { assert(s); diff --git a/src/core/execute.h b/src/core/execute.h index 23abdd4516..ab9d0dbe2d 100644 --- a/src/core/execute.h +++ b/src/core/execute.h @@ -212,6 +212,11 @@ struct ExecContext { char *syslog_identifier; bool syslog_level_prefix; + int log_level_max; + + struct iovec* log_extra_fields; + size_t n_log_extra_fields; + bool cpu_sched_reset_on_fork; bool non_blocking; bool private_tmp; @@ -353,6 +358,8 @@ bool exec_context_maintains_privileges(ExecContext *c); int exec_context_get_effective_ioprio(ExecContext *c); +void exec_context_free_log_extra_fields(ExecContext *c); + void exec_status_start(ExecStatus *s, pid_t pid); void exec_status_exit(ExecStatus *s, ExecContext *context, pid_t pid, int code, int status); void exec_status_dump(ExecStatus *s, FILE *f, const char *prefix); diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4 index 42c2dbb9e4..5b73f9aa9b 100644 --- a/src/core/load-fragment-gperf.gperf.m4 +++ b/src/core/load-fragment-gperf.gperf.m4 @@ -49,6 +49,8 @@ $1.SyslogIdentifier, config_parse_unit_string_printf, 0, $1.SyslogFacility, config_parse_log_facility, 0, offsetof($1, exec_context.syslog_priority) $1.SyslogLevel, config_parse_log_level, 0, offsetof($1, exec_context.syslog_priority) $1.SyslogLevelPrefix, config_parse_bool, 0, offsetof($1, exec_context.syslog_level_prefix) +$1.LogLevelMax, config_parse_log_level, 0, offsetof($1, exec_context.log_level_max) +$1.LogExtraFields, config_parse_log_extra_fields, 0, offsetof($1, exec_context) $1.Capabilities, config_parse_warn_compat, DISABLED_LEGACY, offsetof($1, exec_context) $1.SecureBits, config_parse_exec_secure_bits, 0, offsetof($1, exec_context) $1.CapabilityBoundingSet, config_parse_capability_set, 0, offsetof($1, exec_context.capability_bounding_set) diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c index 02d507f7b2..34fc04b65a 100644 --- a/src/core/load-fragment.c +++ b/src/core/load-fragment.c @@ -45,7 +45,9 @@ #include "escape.h" #include "fd-util.h" #include "fs-util.h" +#include "io-util.h" #include "ioprio.h" +#include "journal-util.h" #include "load-fragment.h" #include "log.h" #include "missing.h" @@ -2331,6 +2333,78 @@ int config_parse_unset_environ( return 0; } +int config_parse_log_extra_fields( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + ExecContext *c = data; + Unit *u = userdata; + const char *p; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(c); + + if (isempty(rvalue)) { + exec_context_free_log_extra_fields(c); + return 0; + } + + for (p = rvalue;; ) { + _cleanup_free_ char *word = NULL, *k = NULL; + struct iovec *t; + const char *eq; + + r = extract_first_word(&p, &word, NULL, EXTRACT_CUNESCAPE|EXTRACT_QUOTES); + if (r == 0) + break; + if (r == -ENOMEM) + return log_oom(); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid syntax, ignoring: %s", rvalue); + return 0; + } + + r = unit_full_printf(u, word, &k); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers on %s, ignoring field: %m", word); + continue; + } + + eq = strchr(k, '='); + if (!eq) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Log field lacks '=' character, ignoring field: %s", k); + continue; + } + + if (!journal_field_valid(k, eq-k, false)) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Log field name is invalid, ignoring field: %s", k); + continue; + } + + t = realloc_multiply(c->log_extra_fields, sizeof(struct iovec), c->n_log_extra_fields+1); + if (!t) + return log_oom(); + + c->log_extra_fields = t; + c->log_extra_fields[c->n_log_extra_fields++] = IOVEC_MAKE_STRING(k); + + k = NULL; + } + + return 0; +} + int config_parse_ip_tos(const char *unit, const char *filename, unsigned line, diff --git a/src/core/load-fragment.h b/src/core/load-fragment.h index 0bd6ec15d6..b0a3ce2c67 100644 --- a/src/core/load-fragment.h +++ b/src/core/load-fragment.h @@ -121,6 +121,7 @@ int config_parse_bind_paths(const char *unit, const char *filename, unsigned lin int config_parse_exec_keyring_mode(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_job_timeout_sec(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_job_running_timeout_sec(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_log_extra_fields(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); /* gperf prototypes */ const struct ConfigPerfItem* load_fragment_gperf_lookup(const char *key, GPERF_LEN_TYPE length); diff --git a/src/core/manager.c b/src/core/manager.c index 9978be7a48..ecb87167fa 100644 --- a/src/core/manager.c +++ b/src/core/manager.c @@ -53,14 +53,15 @@ #include "dirent-util.h" #include "env-util.h" #include "escape.h" -#include "execute.h" #include "exec-util.h" +#include "execute.h" #include "exit-status.h" #include "fd-util.h" #include "fileio.h" #include "fs-util.h" #include "hashmap.h" #include "io-util.h" +#include "label.h" #include "locale-setup.h" #include "log.h" #include "macro.h" @@ -714,6 +715,12 @@ int manager_new(UnitFileScope scope, unsigned test_run_flags, Manager **_m) { goto fail; } + if (MANAGER_IS_SYSTEM(m)) { + r = mkdir_label("/run/systemd/units", 0755); + if (r < 0 && r != -EEXIST) + goto fail; + } + /* Note that we do not set up the notify fd here. We do that after deserialization, * since they might have gotten serialized across the reexec. */ diff --git a/src/core/mount.c b/src/core/mount.c index e2c480a51d..6a89a2d539 100644 --- a/src/core/mount.c +++ b/src/core/mount.c @@ -769,6 +769,8 @@ static int mount_spawn(Mount *m, ExecCommand *c, pid_t *_pid) { m->reset_accounting = false; } + unit_export_state_files(UNIT(m)); + r = unit_setup_exec_runtime(UNIT(m)); if (r < 0) return r; diff --git a/src/core/scope.c b/src/core/scope.c index 073b216ea2..98be2191df 100644 --- a/src/core/scope.c +++ b/src/core/scope.c @@ -339,6 +339,8 @@ static int scope_start(Unit *u) { (void) unit_reset_cpu_accounting(u); (void) unit_reset_ip_accounting(u); + unit_export_state_files(UNIT(s)); + r = unit_attach_pids_to_cgroup(u); if (r < 0) { log_unit_warning_errno(UNIT(s), r, "Failed to add PIDs to scope's control group: %m"); diff --git a/src/core/service.c b/src/core/service.c index f42f1effb7..ac3f14665d 100644 --- a/src/core/service.c +++ b/src/core/service.c @@ -1251,6 +1251,8 @@ static int service_spawn( s->reset_accounting = false; } + unit_export_state_files(UNIT(s)); + r = unit_setup_exec_runtime(UNIT(s)); if (r < 0) return r; diff --git a/src/core/socket.c b/src/core/socket.c index 0686de1d1b..40b2502a40 100644 --- a/src/core/socket.c +++ b/src/core/socket.c @@ -1884,6 +1884,8 @@ static int socket_spawn(Socket *s, ExecCommand *c, pid_t *_pid) { s->reset_accounting = false; } + unit_export_state_files(UNIT(s)); + r = unit_setup_exec_runtime(UNIT(s)); if (r < 0) return r; diff --git a/src/core/swap.c b/src/core/swap.c index 42d91ba3f7..fca8c8dd0a 100644 --- a/src/core/swap.c +++ b/src/core/swap.c @@ -619,6 +619,8 @@ static int swap_spawn(Swap *s, ExecCommand *c, pid_t *_pid) { s->reset_accounting = false; } + unit_export_state_files(UNIT(s)); + r = unit_setup_exec_runtime(UNIT(s)); if (r < 0) goto fail; diff --git a/src/core/unit.c b/src/core/unit.c index e2446804e7..785bd5dc44 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -38,6 +38,7 @@ #include "fd-util.h" #include "fileio-label.h" #include "format-util.h" +#include "fs-util.h" #include "id128-util.h" #include "io-util.h" #include "load-dropin.h" @@ -51,6 +52,7 @@ #include "process-util.h" #include "set.h" #include "signal-util.h" +#include "sparse-endian.h" #include "special.h" #include "stat-util.h" #include "stdio-util.h" @@ -597,6 +599,9 @@ void unit_free(Unit *u) { unit_release_cgroup(u); + if (!MANAGER_IS_RELOADING(u->manager)) + unit_unlink_state_files(u); + unit_unref_uid_gid(u, false); (void) manager_update_failed_units(u->manager, u, false); @@ -2296,9 +2301,11 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su /* Keep track of failed units */ (void) manager_update_failed_units(u->manager, u, ns == UNIT_FAILED); - /* Make sure the cgroup is always removed when we become inactive */ - if (UNIT_IS_INACTIVE_OR_FAILED(ns)) + /* Make sure the cgroup and state files are always removed when we become inactive */ + if (UNIT_IS_INACTIVE_OR_FAILED(ns)) { unit_prune_cgroup(u); + unit_unlink_state_files(u); + } /* Note that this doesn't apply to RemainAfterExit services exiting * successfully, since there's no change of state in that case. Which is @@ -3102,6 +3109,10 @@ int unit_serialize(Unit *u, FILE *f, FDSet *fds, bool serialize_jobs) { unit_serialize_item(u, f, "transient", yes_no(u->transient)); + unit_serialize_item(u, f, "exported-invocation-id", yes_no(u->exported_invocation_id)); + unit_serialize_item(u, f, "exported-log-level-max", yes_no(u->exported_log_level_max)); + unit_serialize_item(u, f, "exported-log-extra-fields", yes_no(u->exported_log_extra_fields)); + unit_serialize_item_format(u, f, "cpu-usage-base", "%" PRIu64, u->cpu_usage_base); if (u->cpu_usage_last != NSEC_INFINITY) unit_serialize_item_format(u, f, "cpu-usage-last", "%" PRIu64, u->cpu_usage_last); @@ -3342,6 +3353,36 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) { continue; + } else if (streq(l, "exported-invocation-id")) { + + r = parse_boolean(v); + if (r < 0) + log_unit_debug(u, "Failed to parse exported invocation ID bool %s, ignoring.", v); + else + u->exported_invocation_id = r; + + continue; + + } else if (streq(l, "exported-log-level-max")) { + + r = parse_boolean(v); + if (r < 0) + log_unit_debug(u, "Failed to parse exported log level max bool %s, ignoring.", v); + else + u->exported_log_level_max = r; + + continue; + + } else if (streq(l, "exported-log-extra-fields")) { + + r = parse_boolean(v); + if (r < 0) + log_unit_debug(u, "Failed to parse exported log extra fields bool %s, ignoring.", v); + else + u->exported_log_extra_fields = r; + + continue; + } else if (STR_IN_SET(l, "cpu-usage-base", "cpuacct-usage-base")) { r = safe_atou64(v, &u->cpu_usage_base); @@ -4913,3 +4954,174 @@ void unit_remove_dependencies(Unit *u, UnitDependencyMask mask) { } while (!done); } } + +static int unit_export_invocation_id(Unit *u) { + const char *p; + int r; + + assert(u); + + if (u->exported_invocation_id) + return 0; + + if (sd_id128_is_null(u->invocation_id)) + return 0; + + p = strjoina("/run/systemd/units/invocation:", u->id); + r = symlink_atomic(u->invocation_id_string, p); + if (r < 0) + return log_unit_debug_errno(u, r, "Failed to create invocation ID symlink %s: %m", p); + + u->exported_invocation_id = true; + return 0; +} + +static int unit_export_log_level_max(Unit *u, const ExecContext *c) { + const char *p; + char buf[2]; + int r; + + assert(u); + assert(c); + + if (u->exported_log_level_max) + return 0; + + if (c->log_level_max < 0) + return 0; + + assert(c->log_level_max <= 7); + + buf[0] = '0' + c->log_level_max; + buf[1] = 0; + + p = strjoina("/run/systemd/units/log-level-max:", u->id); + r = symlink_atomic(buf, p); + if (r < 0) + return log_unit_debug_errno(u, r, "Failed to create maximum log level symlink %s: %m", p); + + u->exported_log_level_max = true; + return 0; +} + +static int unit_export_log_extra_fields(Unit *u, const ExecContext *c) { + _cleanup_close_ int fd = -1; + struct iovec *iovec; + const char *p; + char *pattern; + le64_t *sizes; + ssize_t n; + size_t i; + int r; + + if (u->exported_log_extra_fields) + return 0; + + if (c->n_log_extra_fields <= 0) + return 0; + + sizes = newa(le64_t, c->n_log_extra_fields); + iovec = newa(struct iovec, c->n_log_extra_fields * 2); + + for (i = 0; i < c->n_log_extra_fields; i++) { + sizes[i] = htole64(c->log_extra_fields[i].iov_len); + + iovec[i*2] = IOVEC_MAKE(sizes + i, sizeof(le64_t)); + iovec[i*2+1] = c->log_extra_fields[i]; + } + + p = strjoina("/run/systemd/units/log-extra-fields:", u->id); + pattern = strjoina(p, ".XXXXXX"); + + fd = mkostemp_safe(pattern); + if (fd < 0) + return log_unit_debug_errno(u, fd, "Failed to create extra fields file %s: %m", p); + + n = writev(fd, iovec, c->n_log_extra_fields*2); + if (n < 0) { + r = log_unit_debug_errno(u, errno, "Failed to write extra fields: %m"); + goto fail; + } + + (void) fchmod(fd, 0644); + + if (rename(pattern, p) < 0) { + r = log_unit_debug_errno(u, errno, "Failed to rename extra fields file: %m"); + goto fail; + } + + u->exported_log_extra_fields = true; + return 0; + +fail: + (void) unlink(pattern); + return r; +} + +void unit_export_state_files(Unit *u) { + const ExecContext *c; + + assert(u); + + if (!u->id) + return; + + if (!MANAGER_IS_SYSTEM(u->manager)) + return; + + /* Exports a couple of unit properties to /run/systemd/units/, so that journald can quickly query this data + * from there. Ideally, journald would use IPC to query this, like everybody else, but that's hard, as long as + * the IPC system itself and PID 1 also log to the journal. + * + * Note that these files really shouldn't be considered API for anyone else, as use a runtime file system as + * IPC replacement is not compatible with today's world of file system namespaces. However, this doesn't really + * apply to communication between the journal and systemd, as we assume that these two daemons live in the same + * namespace at least. + * + * Note that some of the "files" exported here are actually symlinks and not regular files. Symlinks work + * better for storing small bits of data, in particular as we can write them with two system calls, and read + * them with one. */ + + (void) unit_export_invocation_id(u); + + c = unit_get_exec_context(u); + if (c) { + (void) unit_export_log_level_max(u, c); + (void) unit_export_log_extra_fields(u, c); + } +} + +void unit_unlink_state_files(Unit *u) { + const char *p; + + assert(u); + + if (!u->id) + return; + + if (!MANAGER_IS_SYSTEM(u->manager)) + return; + + /* Undoes the effect of unit_export_state() */ + + if (u->exported_invocation_id) { + p = strjoina("/run/systemd/units/invocation:", u->id); + (void) unlink(p); + + u->exported_invocation_id = false; + } + + if (u->exported_log_level_max) { + p = strjoina("/run/systemd/units/log-level-max:", u->id); + (void) unlink(p); + + u->exported_log_level_max = false; + } + + if (u->exported_log_extra_fields) { + p = strjoina("/run/systemd/units/extra-fields:", u->id); + (void) unlink(p); + + u->exported_log_extra_fields = false; + } +} diff --git a/src/core/unit.h b/src/core/unit.h index 5e5e791bc4..adab6eb80f 100644 --- a/src/core/unit.h +++ b/src/core/unit.h @@ -338,6 +338,11 @@ struct Unit { /* For transient units: whether to add a bus track reference after creating the unit */ bool bus_track_add:1; + + /* Remember which unit state files we created */ + bool exported_invocation_id:1; + bool exported_log_level_max:1; + bool exported_log_extra_fields:1; }; struct UnitStatusMessageFormats { @@ -742,6 +747,9 @@ int unit_fork_helper_process(Unit *u, pid_t *ret); void unit_remove_dependencies(Unit *u, UnitDependencyMask mask); +void unit_export_state_files(Unit *u); +void unit_unlink_state_files(Unit *u); + /* Macros which append UNIT= or USER_UNIT= to the message */ #define log_unit_full(unit, level, error, ...) \ diff --git a/src/journal/journald-audit.c b/src/journal/journald-audit.c index 86ca56af94..2db923fb49 100644 --- a/src/journal/journald-audit.c +++ b/src/journal/journald-audit.c @@ -29,10 +29,10 @@ typedef struct MapField { const char *audit_field; const char *journal_field; - int (*map)(const char *field, const char **p, struct iovec **iov, size_t *n_iov_allocated, unsigned *n_iov); + int (*map)(const char *field, const char **p, struct iovec **iov, size_t *n_iov_allocated, size_t *n_iov); } MapField; -static int map_simple_field(const char *field, const char **p, struct iovec **iov, size_t *n_iov_allocated, unsigned *n_iov) { +static int map_simple_field(const char *field, const char **p, struct iovec **iov, size_t *n_iov_allocated, size_t *n_iov) { _cleanup_free_ char *c = NULL; size_t l = 0, allocated = 0; const char *e; @@ -69,7 +69,7 @@ static int map_simple_field(const char *field, const char **p, struct iovec **io return 1; } -static int map_string_field_internal(const char *field, const char **p, struct iovec **iov, size_t *n_iov_allocated, unsigned *n_iov, bool filter_printable) { +static int map_string_field_internal(const char *field, const char **p, struct iovec **iov, size_t *n_iov_allocated, size_t *n_iov, bool filter_printable) { _cleanup_free_ char *c = NULL; const char *s, *e; size_t l; @@ -146,15 +146,15 @@ static int map_string_field_internal(const char *field, const char **p, struct i return 1; } -static int map_string_field(const char *field, const char **p, struct iovec **iov, size_t *n_iov_allocated, unsigned *n_iov) { +static int map_string_field(const char *field, const char **p, struct iovec **iov, size_t *n_iov_allocated, size_t *n_iov) { return map_string_field_internal(field, p, iov, n_iov_allocated, n_iov, false); } -static int map_string_field_printable(const char *field, const char **p, struct iovec **iov, size_t *n_iov_allocated, unsigned *n_iov) { +static int map_string_field_printable(const char *field, const char **p, struct iovec **iov, size_t *n_iov_allocated, size_t *n_iov) { return map_string_field_internal(field, p, iov, n_iov_allocated, n_iov, true); } -static int map_generic_field(const char *prefix, const char **p, struct iovec **iov, size_t *n_iov_allocated, unsigned *n_iov) { +static int map_generic_field(const char *prefix, const char **p, struct iovec **iov, size_t *n_iov_allocated, size_t *n_iov) { const char *e, *f; char *c, *t; int r; @@ -259,7 +259,7 @@ static int map_all_fields( bool handle_msg, struct iovec **iov, size_t *n_iov_allocated, - unsigned *n_iov) { + size_t *n_iov) { int r; @@ -331,16 +331,15 @@ static int map_all_fields( } static void process_audit_string(Server *s, int type, const char *data, size_t size) { + size_t n_iov_allocated = 0, n_iov = 0, z; _cleanup_free_ struct iovec *iov = NULL; - size_t n_iov_allocated = 0; - unsigned n_iov = 0, k; uint64_t seconds, msec, id; const char *p, *type_name; - unsigned z; char id_field[sizeof("_AUDIT_ID=") + DECIMAL_STR_MAX(uint64_t)], type_field[sizeof("_AUDIT_TYPE=") + DECIMAL_STR_MAX(int)], source_time_field[sizeof("_SOURCE_REALTIME_TIMESTAMP=") + DECIMAL_STR_MAX(usec_t)]; char *m; + int k; assert(s); diff --git a/src/journal/journald-context.c b/src/journal/journald-context.c index eaa7f2544f..3a5a97b496 100644 --- a/src/journal/journald-context.c +++ b/src/journal/journald-context.c @@ -24,9 +24,16 @@ #include "alloc-util.h" #include "audit-util.h" #include "cgroup-util.h" +#include "fd-util.h" +#include "fileio.h" +#include "fs-util.h" +#include "io-util.h" +#include "journal-util.h" #include "journald-context.h" #include "process-util.h" #include "string-util.h" +#include "syslog-util.h" +#include "unaligned.h" #include "user-util.h" /* This implements a metadata cache for clients, which are identified by their PID. Requesting metadata through /proc @@ -115,6 +122,8 @@ static int client_context_new(Server *s, pid_t pid, ClientContext **ret) { c->owner_uid = UID_INVALID; c->lru_index = PRIOQ_IDX_NULL; c->timestamp = USEC_INFINITY; + c->extra_fields_mtime = NSEC_INFINITY; + c->log_level_max = -1; r = hashmap_put(s->client_contexts, PID_TO_PTR(pid), c); if (r < 0) { @@ -154,6 +163,13 @@ static void client_context_reset(ClientContext *c) { c->label = mfree(c->label); c->label_size = 0; + + c->extra_fields_iovec = mfree(c->extra_fields_iovec); + c->extra_fields_n_iovec = 0; + c->extra_fields_data = mfree(c->extra_fields_data); + c->extra_fields_mtime = NSEC_INFINITY; + + c->log_level_max = -1; } static ClientContext* client_context_free(Server *s, ClientContext *c) { @@ -296,40 +312,141 @@ static int client_context_read_invocation_id( Server *s, ClientContext *c) { - _cleanup_free_ char *escaped = NULL, *slice_path = NULL; - char ids[SD_ID128_STRING_MAX]; + _cleanup_free_ char *value = NULL; const char *p; int r; assert(s); assert(c); - /* Read the invocation ID of a unit off a unit. It's stored in the "trusted.invocation_id" extended attribute - * on the cgroup path. */ + /* Read the invocation ID of a unit off a unit. PID 1 stores it in a per-unit symlink in /run/systemd/units/ */ - if (!c->unit || !c->slice) + if (!c->unit) return 0; - r = cg_slice_to_path(c->slice, &slice_path); + p = strjoina("/run/systemd/units/invocation:", c->unit); + r = readlink_malloc(p, &value); if (r < 0) return r; - escaped = cg_escape(c->unit); - if (!escaped) - return -ENOMEM; + return sd_id128_from_string(value, &c->invocation_id); +} - p = strjoina(s->cgroup_root, "/", slice_path, "/", escaped); - if (!p) - return -ENOMEM; +static int client_context_read_log_level_max( + Server *s, + ClientContext *c) { - r = cg_get_xattr(SYSTEMD_CGROUP_CONTROLLER, p, "trusted.invocation_id", ids, 32); + _cleanup_free_ char *value = NULL; + const char *p; + int r, ll; + + if (!c->unit) + return 0; + + p = strjoina("/run/systemd/units/log-level-max:", c->unit); + r = readlink_malloc(p, &value); if (r < 0) return r; - if (r != 32) + + ll = log_level_from_string(value); + if (ll < 0) return -EINVAL; - ids[32] = 0; - return sd_id128_from_string(ids, &c->invocation_id); + c->log_level_max = ll; + return 0; +} + +static int client_context_read_extra_fields( + Server *s, + ClientContext *c) { + + size_t size = 0, n_iovec = 0, n_allocated = 0, left; + _cleanup_free_ struct iovec *iovec = NULL; + _cleanup_free_ void *data = NULL; + _cleanup_fclose_ FILE *f = NULL; + struct stat st; + const char *p; + uint8_t *q; + int r; + + if (!c->unit) + return 0; + + p = strjoina("/run/systemd/units/log-extra-fields:", c->unit); + + if (c->extra_fields_mtime != NSEC_INFINITY) { + if (stat(p, &st) < 0) { + if (errno == ENOENT) + return 0; + + return -errno; + } + + if (timespec_load_nsec(&st.st_mtim) == c->extra_fields_mtime) + return 0; + } + + f = fopen(p, "re"); + if (!f) { + if (errno == ENOENT) + return 0; + + return -errno; + } + + if (fstat(fileno(f), &st) < 0) /* The file might have been replaced since the stat() above, let's get a new + * one, that matches the stuff we are reading */ + return -errno; + + r = read_full_stream(f, (char**) &data, &size); + if (r < 0) + return r; + + q = data, left = size; + while (left > 0) { + uint8_t *field, *eq; + uint64_t v, n; + + if (left < sizeof(uint64_t)) + return -EBADMSG; + + v = unaligned_read_le64(q); + if (v < 2) + return -EBADMSG; + + n = sizeof(uint64_t) + v; + if (left < n) + return -EBADMSG; + + field = q + sizeof(uint64_t); + + eq = memchr(field, '=', v); + if (!eq) + return -EBADMSG; + + if (!journal_field_valid((const char *) field, eq - field, false)) + return -EBADMSG; + + if (!GREEDY_REALLOC(iovec, n_allocated, n_iovec+1)) + return -ENOMEM; + + iovec[n_iovec++] = IOVEC_MAKE(field, v); + + left -= n, q += n; + } + + free(c->extra_fields_iovec); + free(c->extra_fields_data); + + c->extra_fields_iovec = iovec; + c->extra_fields_n_iovec = n_iovec; + c->extra_fields_data = data; + c->extra_fields_mtime = timespec_load_nsec(&st.st_mtim); + + iovec = NULL; + data = NULL; + + return 0; } static void client_context_really_refresh( @@ -356,6 +473,8 @@ static void client_context_really_refresh( (void) client_context_read_cgroup(s, c, unit_id); (void) client_context_read_invocation_id(s, c); + (void) client_context_read_log_level_max(s, c); + (void) client_context_read_extra_fields(s, c); c->timestamp = timestamp; diff --git a/src/journal/journald-context.h b/src/journal/journald-context.h index eb1e21926a..d2a3772f08 100644 --- a/src/journal/journald-context.h +++ b/src/journal/journald-context.h @@ -60,6 +60,13 @@ struct ClientContext { char *label; size_t label_size; + + int log_level_max; + + struct iovec *extra_fields_iovec; + size_t extra_fields_n_iovec; + void *extra_fields_data; + nsec_t extra_fields_mtime; }; int client_context_get( @@ -90,3 +97,17 @@ void client_context_maybe_refresh( void client_context_acquire_default(Server *s); void client_context_flush_all(Server *s); + +static inline size_t client_context_extra_fields_n_iovec(const ClientContext *c) { + return c ? c->extra_fields_n_iovec : 0; +} + +static inline bool client_context_test_priority(const ClientContext *c, int priority) { + if (!c) + return true; + + if (c->log_level_max < 0) + return true; + + return LOG_PRI(priority) <= c->log_level_max; +} diff --git a/src/journal/journald-kmsg.c b/src/journal/journald-kmsg.c index a53075ac00..b4d3121a0d 100644 --- a/src/journal/journald-kmsg.c +++ b/src/journal/journald-kmsg.c @@ -109,15 +109,16 @@ static bool is_us(const char *pid) { } static void dev_kmsg_record(Server *s, const char *p, size_t l) { - struct iovec iovec[N_IOVEC_META_FIELDS + 7 + N_IOVEC_KERNEL_FIELDS + 2 + N_IOVEC_UDEV_FIELDS]; + _cleanup_free_ char *message = NULL, *syslog_priority = NULL, *syslog_pid = NULL, *syslog_facility = NULL, *syslog_identifier = NULL, *source_time = NULL, *identifier = NULL, *pid = NULL; - int priority, r; - unsigned n = 0, z = 0, j; + struct iovec iovec[N_IOVEC_META_FIELDS + 7 + N_IOVEC_KERNEL_FIELDS + 2 + N_IOVEC_UDEV_FIELDS]; + char *kernel_device = NULL; unsigned long long usec; + size_t n = 0, z = 0, j; + int priority, r; char *e, *f, *k; uint64_t serial; size_t pl; - char *kernel_device = NULL; assert(s); assert(p); diff --git a/src/journal/journald-native.c b/src/journal/journald-native.c index d1fdfccd46..5452b88ae2 100644 --- a/src/journal/journald-native.c +++ b/src/journal/journald-native.c @@ -114,19 +114,17 @@ static int server_process_entry( const struct timeval *tv, const char *label, size_t label_len) { - /* Process a single entry from a native message. - * Returns 0 if nothing special happened and the message processing should continue, - * and a negative or positive value otherwise. + /* Process a single entry from a native message. Returns 0 if nothing special happened and the message + * processing should continue, and a negative or positive value otherwise. * * Note that *remaining is altered on both success and failure. */ - struct iovec *iovec = NULL; - unsigned n = 0, j, tn = (unsigned) -1; - const char *p; - size_t m = 0, entry_size = 0; - int priority = LOG_INFO; + size_t n = 0, j, tn = (size_t) -1, m = 0, entry_size = 0; char *identifier = NULL, *message = NULL; + struct iovec *iovec = NULL; + int priority = LOG_INFO; pid_t object_pid = 0; + const char *p; int r = 0; p = buffer; @@ -160,7 +158,10 @@ static int server_process_entry( /* A property follows */ /* n existing properties, 1 new, +1 for _TRANSPORT */ - if (!GREEDY_REALLOC(iovec, m, n + 2 + N_IOVEC_META_FIELDS + N_IOVEC_OBJECT_FIELDS)) { + if (!GREEDY_REALLOC(iovec, m, + n + 2 + + N_IOVEC_META_FIELDS + N_IOVEC_OBJECT_FIELDS + + client_context_extra_fields_n_iovec(context))) { r = log_oom(); break; } @@ -243,13 +244,17 @@ static int server_process_entry( goto finish; } + if (!client_context_test_priority(context, priority)) { + r = 0; + goto finish; + } + tn = n++; iovec[tn] = IOVEC_MAKE_STRING("_TRANSPORT=journal"); entry_size += strlen("_TRANSPORT=journal"); if (entry_size + n + 1 > ENTRY_SIZE_MAX) { /* data + separators + trailer */ - log_debug("Entry is too big with %u properties and %zu bytes, ignoring.", - n, entry_size); + log_debug("Entry is too big with %zu properties and %zu bytes, ignoring.", n, entry_size); goto finish; } diff --git a/src/journal/journald-server.c b/src/journal/journald-server.c index e85e4e0f10..033f0b1a8b 100644 --- a/src/journal/journald-server.c +++ b/src/journal/journald-server.c @@ -753,7 +753,7 @@ static void write_to_journal(Server *s, uid_t uid, struct iovec *iovec, unsigned static void dispatch_message_real( Server *s, - struct iovec *iovec, unsigned n, unsigned m, + struct iovec *iovec, size_t n, size_t m, const ClientContext *c, const struct timeval *tv, int priority, @@ -766,7 +766,10 @@ static void dispatch_message_real( assert(s); assert(iovec); assert(n > 0); - assert(n + N_IOVEC_META_FIELDS + (pid_is_valid(object_pid) ? N_IOVEC_OBJECT_FIELDS : 0) <= m); + assert(n + + N_IOVEC_META_FIELDS + + (pid_is_valid(object_pid) ? N_IOVEC_OBJECT_FIELDS : 0) + + client_context_extra_fields_n_iovec(c) <= m); if (c) { IOVEC_ADD_NUMERIC_FIELD(iovec, n, c->pid, pid_t, pid_is_valid, PID_FMT, "_PID"); @@ -792,6 +795,11 @@ static void dispatch_message_real( IOVEC_ADD_STRING_FIELD(iovec, n, c->user_slice, "_SYSTEMD_USER_SLICE"); IOVEC_ADD_ID128_FIELD(iovec, n, c->invocation_id, "_SYSTEMD_INVOCATION_ID"); + + if (c->extra_fields_n_iovec > 0) { + memcpy(iovec + n, c->extra_fields_iovec, c->extra_fields_n_iovec * sizeof(struct iovec)); + n += c->extra_fields_n_iovec; + } } assert(n <= m); @@ -862,14 +870,17 @@ static void dispatch_message_real( void server_driver_message(Server *s, pid_t object_pid, const char *message_id, const char *format, ...) { - struct iovec iovec[N_IOVEC_META_FIELDS + 5 + N_IOVEC_PAYLOAD_FIELDS]; - unsigned n = 0, m; + struct iovec *iovec; + size_t n = 0, k, m; va_list ap; int r; assert(s); assert(format); + m = N_IOVEC_META_FIELDS + 5 + N_IOVEC_PAYLOAD_FIELDS + client_context_extra_fields_n_iovec(s->my_context); + iovec = newa(struct iovec, m); + assert_cc(3 == LOG_FAC(LOG_DAEMON)); iovec[n++] = IOVEC_MAKE_STRING("SYSLOG_FACILITY=3"); iovec[n++] = IOVEC_MAKE_STRING("SYSLOG_IDENTIFIER=systemd-journald"); @@ -880,18 +891,18 @@ void server_driver_message(Server *s, pid_t object_pid, const char *message_id, if (message_id) iovec[n++] = IOVEC_MAKE_STRING(message_id); - m = n; + k = n; va_start(ap, format); - r = log_format_iovec(iovec, ELEMENTSOF(iovec), &n, false, 0, format, ap); + r = log_format_iovec(iovec, m, &n, false, 0, format, ap); /* Error handling below */ va_end(ap); if (r >= 0) - dispatch_message_real(s, iovec, n, ELEMENTSOF(iovec), s->my_context, NULL, LOG_INFO, object_pid); + dispatch_message_real(s, iovec, n, m, s->my_context, NULL, LOG_INFO, object_pid); - while (m < n) - free(iovec[m++].iov_base); + while (k < n) + free(iovec[k++].iov_base); if (r < 0) { /* We failed to format the message. Emit a warning instead. */ @@ -902,13 +913,13 @@ void server_driver_message(Server *s, pid_t object_pid, const char *message_id, n = 3; iovec[n++] = IOVEC_MAKE_STRING("PRIORITY=4"); iovec[n++] = IOVEC_MAKE_STRING(buf); - dispatch_message_real(s, iovec, n, ELEMENTSOF(iovec), s->my_context, NULL, LOG_INFO, object_pid); + dispatch_message_real(s, iovec, n, m, s->my_context, NULL, LOG_INFO, object_pid); } } void server_dispatch_message( Server *s, - struct iovec *iovec, unsigned n, unsigned m, + struct iovec *iovec, size_t n, size_t m, ClientContext *c, const struct timeval *tv, int priority, diff --git a/src/journal/journald-server.h b/src/journal/journald-server.h index 5efc5fe119..f0076793e1 100644 --- a/src/journal/journald-server.h +++ b/src/journal/journald-server.h @@ -187,7 +187,7 @@ struct Server { #define N_IOVEC_OBJECT_FIELDS 14 #define N_IOVEC_PAYLOAD_FIELDS 15 -void server_dispatch_message(Server *s, struct iovec *iovec, unsigned n, unsigned m, ClientContext *c, const struct timeval *tv, int priority, pid_t object_pid); +void server_dispatch_message(Server *s, struct iovec *iovec, size_t n, size_t m, ClientContext *c, const struct timeval *tv, int priority, pid_t object_pid); void server_driver_message(Server *s, pid_t object_pid, const char *message_id, const char *format, ...) _sentinel_; /* gperf lookup function */ diff --git a/src/journal/journald-stream.c b/src/journal/journald-stream.c index 54dd096e45..95272db863 100644 --- a/src/journal/journald-stream.c +++ b/src/journal/journald-stream.c @@ -251,22 +251,33 @@ fail: } static int stdout_stream_log(StdoutStream *s, const char *p, LineBreak line_break) { - struct iovec iovec[N_IOVEC_META_FIELDS + 7]; + struct iovec *iovec; int priority; char syslog_priority[] = "PRIORITY=\0"; char syslog_facility[sizeof("SYSLOG_FACILITY=")-1 + DECIMAL_STR_MAX(int) + 1]; _cleanup_free_ char *message = NULL, *syslog_identifier = NULL; - unsigned n = 0; + size_t n = 0, m; int r; assert(s); assert(p); + if (s->context) + (void) client_context_maybe_refresh(s->server, s->context, NULL, NULL, 0, NULL, USEC_INFINITY); + else if (pid_is_valid(s->ucred.pid)) { + r = client_context_acquire(s->server, s->ucred.pid, &s->ucred, s->label, strlen_ptr(s->label), s->unit_id, &s->context); + if (r < 0) + log_warning_errno(r, "Failed to acquire client context, ignoring: %m"); + } + priority = s->priority; if (s->level_prefix) syslog_parse_priority(&p, &priority, false); + if (!client_context_test_priority(s->context, priority)) + return 0; + if (isempty(p)) return 0; @@ -282,6 +293,9 @@ static int stdout_stream_log(StdoutStream *s, const char *p, LineBreak line_brea if (s->server->forward_to_wall) server_forward_wall(s->server, priority, s->identifier, p, &s->ucred); + m = N_IOVEC_META_FIELDS + 7 + client_context_extra_fields_n_iovec(s->context); + iovec = newa(struct iovec, m); + iovec[n++] = IOVEC_MAKE_STRING("_TRANSPORT=stdout"); iovec[n++] = IOVEC_MAKE_STRING(s->id_field); @@ -315,15 +329,7 @@ static int stdout_stream_log(StdoutStream *s, const char *p, LineBreak line_brea if (message) iovec[n++] = IOVEC_MAKE_STRING(message); - if (s->context) - (void) client_context_maybe_refresh(s->server, s->context, NULL, NULL, 0, NULL, USEC_INFINITY); - else if (pid_is_valid(s->ucred.pid)) { - r = client_context_acquire(s->server, s->ucred.pid, &s->ucred, s->label, strlen_ptr(s->label), s->unit_id, &s->context); - if (r < 0) - log_warning_errno(r, "Failed to acquire client context, ignoring: %m"); - } - - server_dispatch_message(s->server, iovec, n, ELEMENTSOF(iovec), s->context, NULL, priority, 0); + server_dispatch_message(s->server, iovec, n, m, s->context, NULL, priority, 0); return 0; } diff --git a/src/journal/journald-syslog.c b/src/journal/journald-syslog.c index b3db310fbf..07f9dae67e 100644 --- a/src/journal/journald-syslog.c +++ b/src/journal/journald-syslog.c @@ -324,18 +324,27 @@ void server_process_syslog_message( syslog_facility[sizeof("SYSLOG_FACILITY=") + DECIMAL_STR_MAX(int)]; const char *message = NULL, *syslog_identifier = NULL, *syslog_pid = NULL; _cleanup_free_ char *identifier = NULL, *pid = NULL; - struct iovec iovec[N_IOVEC_META_FIELDS + 6]; int priority = LOG_USER | LOG_INFO, r; ClientContext *context = NULL; + struct iovec *iovec; const char *orig; - unsigned n = 0; + size_t n = 0, m; assert(s); assert(buf); + if (ucred && pid_is_valid(ucred->pid)) { + r = client_context_get(s, ucred->pid, ucred, label, label_len, NULL, &context); + if (r < 0) + log_warning_errno(r, "Failed to retrieve credentials for PID " PID_FMT ", ignoring: %m", ucred->pid); + } + orig = buf; syslog_parse_priority(&buf, &priority, true); + if (!client_context_test_priority(context, priority)) + return; + if (s->forward_to_syslog) forward_syslog_raw(s, priority, orig, ucred, tv); @@ -351,6 +360,9 @@ void server_process_syslog_message( if (s->forward_to_wall) server_forward_wall(s, priority, identifier, buf, ucred); + m = N_IOVEC_META_FIELDS + 6 + client_context_extra_fields_n_iovec(context); + iovec = newa(struct iovec, m); + iovec[n++] = IOVEC_MAKE_STRING("_TRANSPORT=syslog"); xsprintf(syslog_priority, "PRIORITY=%i", priority & LOG_PRIMASK); @@ -375,13 +387,7 @@ void server_process_syslog_message( if (message) iovec[n++] = IOVEC_MAKE_STRING(message); - if (ucred && pid_is_valid(ucred->pid)) { - r = client_context_get(s, ucred->pid, ucred, label, label_len, NULL, &context); - if (r < 0) - log_warning_errno(r, "Failed to retrieve credentials for PID " PID_FMT ", ignoring: %m", ucred->pid); - } - - server_dispatch_message(s, iovec, n, ELEMENTSOF(iovec), context, tv, priority, 0); + server_dispatch_message(s, iovec, n, m, context, tv, priority, 0); } int server_open_syslog_socket(Server *s) { diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c index 529bc62886..e24c0d4e1c 100644 --- a/src/shared/bus-unit-util.c +++ b/src/shared/bus-unit-util.c @@ -156,6 +156,31 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen r = sd_bus_message_append(m, "sv", n, "t", t); goto finish; + } else if (streq(field, "LogExtraFields")) { + + r = sd_bus_message_append(m, "s", "LogExtraFields"); + if (r < 0) + goto finish; + + r = sd_bus_message_open_container(m, 'v', "aay"); + if (r < 0) + goto finish; + + r = sd_bus_message_open_container(m, 'a', "ay"); + if (r < 0) + goto finish; + + r = sd_bus_message_append_array(m, 'y', eq, strlen(eq)); + if (r < 0) + goto finish; + + r = sd_bus_message_close_container(m); + if (r < 0) + goto finish; + + r = sd_bus_message_close_container(m); + goto finish; + } else if (STR_IN_SET(field, "MemoryLow", "MemoryHigh", "MemoryMax", "MemoryLimit")) { uint64_t bytes; @@ -363,7 +388,7 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen r = sd_bus_message_append(m, "v", "(bs)", ignore, s); - } else if (streq(field, "SyslogLevel")) { + } else if (STR_IN_SET(field, "SyslogLevel", "LogLevelMax")) { int level; level = log_level_from_string(eq); diff --git a/src/shared/conf-parser.c b/src/shared/conf-parser.c index 10a26d45aa..9ca9750750 100644 --- a/src/shared/conf-parser.c +++ b/src/shared/conf-parser.c @@ -834,7 +834,6 @@ int config_parse_log_facility( void *data, void *userdata) { - int *o = data, x; assert(filename); @@ -865,7 +864,6 @@ int config_parse_log_level( void *data, void *userdata) { - int *o = data, x; assert(filename); @@ -879,7 +877,11 @@ int config_parse_log_level( return 0; } - *o = (*o & LOG_FACMASK) | x; + if (*o < 0) /* if it wasn't initialized so far, assume zero facility */ + *o = x; + else + *o = (*o & LOG_FACMASK) | x; + return 0; }