diff options
Diffstat (limited to 'src/core')
65 files changed, 4418 insertions, 2518 deletions
diff --git a/src/core/audit-fd.c b/src/core/audit-fd.c new file mode 100644 index 0000000000..5955bd846e --- /dev/null +++ b/src/core/audit-fd.c @@ -0,0 +1,73 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2012 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 "audit-fd.h" + +#ifdef HAVE_AUDIT + +#include <stdbool.h> +#include <libaudit.h> + +#include "log.h" +#include "util.h" + +static bool initialized = false; +static int audit_fd; + +int get_audit_fd(void) { + + if (!initialized) { + audit_fd = audit_open(); + + if (audit_fd < 0) { + if (errno != EAFNOSUPPORT && errno != EPROTONOSUPPORT) + log_error("Failed to connect to audit log: %m"); + + audit_fd = errno ? -errno : -EINVAL; + } + + initialized = true; + } + + return audit_fd; +} + +void close_audit_fd(void) { + + if (initialized && audit_fd >= 0) + close_nointr_nofail(audit_fd); + + initialized = true; + audit_fd = -ECONNRESET; +} + +#else + +int get_audit_fd(void) { + return -EAFNOSUPPORT; +} + +void close_audit_fd(void) { +} + +#endif diff --git a/src/core/audit-fd.h b/src/core/audit-fd.h new file mode 100644 index 0000000000..8b58289dc5 --- /dev/null +++ b/src/core/audit-fd.h @@ -0,0 +1,25 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#pragma once + +/*** + This file is part of systemd. + + Copyright 2012 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/>. +***/ + +int get_audit_fd(void); +void close_audit_fd(void); diff --git a/src/core/automount.c b/src/core/automount.c index 91162c2a58..4a98540d82 100644 --- a/src/core/automount.c +++ b/src/core/automount.c @@ -31,6 +31,7 @@ #include "unit.h" #include "automount.h" +#include "mount.h" #include "load-fragment.h" #include "load-dropin.h" #include "unit-name.h" @@ -40,6 +41,7 @@ #include "label.h" #include "mkdir.h" #include "path-util.h" +#include "dbus-common.h" static const UnitActiveState state_translation_table[_AUTOMOUNT_STATE_MAX] = { [AUTOMOUNT_DEAD] = UNIT_INACTIVE, @@ -132,7 +134,8 @@ int automount_add_one_mount_link(Automount *a, Mount *m) { if (path_equal(a->where, m->where)) return 0; - if ((r = unit_add_two_dependencies(UNIT(a), UNIT_AFTER, UNIT_REQUIRES, UNIT(m), true)) < 0) + r = unit_add_two_dependencies(UNIT(a), UNIT_AFTER, UNIT_REQUIRES, UNIT(m), true); + if (r < 0) return r; return 0; @@ -144,9 +147,11 @@ static int automount_add_mount_links(Automount *a) { assert(a); - LIST_FOREACH(units_by_type, other, UNIT(a)->manager->units_by_type[UNIT_MOUNT]) - if ((r = automount_add_one_mount_link(a, MOUNT(other))) < 0) + LIST_FOREACH(units_by_type, other, UNIT(a)->manager->units_by_type[UNIT_MOUNT]) { + r = automount_add_one_mount_link(a, MOUNT(other)); + if (r < 0) return r; + } return 0; } @@ -156,7 +161,7 @@ static int automount_add_default_dependencies(Automount *a) { assert(a); - if (UNIT(a)->manager->running_as != MANAGER_SYSTEM) + if (UNIT(a)->manager->running_as != SYSTEMD_SYSTEM) return 0; r = unit_add_two_dependencies_by_name(UNIT(a), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_UMOUNT_TARGET, NULL, true); @@ -175,18 +180,19 @@ static int automount_verify(Automount *a) { return 0; if (path_equal(a->where, "/")) { - log_error("Cannot have an automount unit for the root directory. Refusing."); + log_error_unit(UNIT(a)->id, "Cannot have an automount unit for the root directory. Refusing."); return -EINVAL; } - if (!(e = unit_name_from_path(a->where, ".automount"))) + e = unit_name_from_path(a->where, ".automount"); + if (!e) return -ENOMEM; b = unit_has_name(UNIT(a), e); free(e); if (!b) { - log_error("%s's Where setting doesn't match unit name. Refusing.", UNIT(a)->id); + log_error_unit(UNIT(a)->id, "%s's Where setting doesn't match unit name. Refusing.", UNIT(a)->id); return -EINVAL; } @@ -201,19 +207,23 @@ static int automount_load(Unit *u) { assert(u->load_state == UNIT_STUB); /* Load a .automount file */ - if ((r = unit_load_fragment_and_dropin_optional(u)) < 0) + r = unit_load_fragment_and_dropin_optional(u); + if (r < 0) return r; if (u->load_state == UNIT_LOADED) { Unit *x; - if (!a->where) - if (!(a->where = unit_name_to_path(u->id))) + if (!a->where) { + a->where = unit_name_to_path(u->id); + if (!a->where) return -ENOMEM; + } path_kill_slashes(a->where); - if ((r = automount_add_mount_links(a)) < 0) + r = automount_add_mount_links(a); + if (r < 0) return r; r = unit_load_related_unit(u, ".mount", &x); @@ -226,9 +236,11 @@ static int automount_load(Unit *u) { if (r < 0) return r; - if (UNIT(a)->default_dependencies) - if ((r = automount_add_default_dependencies(a)) < 0) + if (UNIT(a)->default_dependencies) { + r = automount_add_default_dependencies(a); + if (r < 0) return r; + } } return automount_verify(a); @@ -246,10 +258,11 @@ static void automount_set_state(Automount *a, AutomountState state) { unmount_autofs(a); if (state != old_state) - log_debug("%s changed %s -> %s", - UNIT(a)->id, - automount_state_to_string(old_state), - automount_state_to_string(state)); + log_debug_unit(UNIT(a)->id, + "%s changed %s -> %s", + UNIT(a)->id, + automount_state_to_string(old_state), + automount_state_to_string(state)); unit_notify(UNIT(a), state_translation_table[old_state], state_translation_table[state], true); } @@ -263,7 +276,8 @@ static int automount_coldplug(Unit *u) { if (a->deserialized_state != a->state) { - if ((r = open_dev_autofs(u->manager)) < 0) + r = open_dev_autofs(u->manager); + if (r < 0) return r; if (a->deserialized_state == AUTOMOUNT_WAITING || @@ -271,7 +285,8 @@ static int automount_coldplug(Unit *u) { assert(a->pipe_fd >= 0); - if ((r = unit_watch_fd(UNIT(a), a->pipe_fd, EPOLLIN, &a->pipe_watch)) < 0) + r = unit_watch_fd(UNIT(a), a->pipe_fd, EPOLLIN, &a->pipe_watch); + if (r < 0) return r; } @@ -316,7 +331,8 @@ static int open_dev_autofs(Manager *m) { label_fix("/dev/autofs", false, false); - if ((m->dev_autofs_fd = open("/dev/autofs", O_CLOEXEC|O_RDONLY)) < 0) { + m->dev_autofs_fd = open("/dev/autofs", O_CLOEXEC|O_RDONLY); + if (m->dev_autofs_fd < 0) { log_error("Failed to open /dev/autofs: %s", strerror(errno)); return -errno; } @@ -336,15 +352,12 @@ static int open_dev_autofs(Manager *m) { static int open_ioctl_fd(int dev_autofs_fd, const char *where, dev_t devid) { struct autofs_dev_ioctl *param; size_t l; - int r; assert(dev_autofs_fd >= 0); assert(where); l = sizeof(struct autofs_dev_ioctl) + strlen(where) + 1; - - if (!(param = malloc(l))) - return -ENOMEM; + param = alloca(l); init_autofs_dev_ioctl(param); param->size = l; @@ -352,22 +365,14 @@ static int open_ioctl_fd(int dev_autofs_fd, const char *where, dev_t devid) { param->openmount.devid = devid; strcpy(param->path, where); - if (ioctl(dev_autofs_fd, AUTOFS_DEV_IOCTL_OPENMOUNT, param) < 0) { - r = -errno; - goto finish; - } + if (ioctl(dev_autofs_fd, AUTOFS_DEV_IOCTL_OPENMOUNT, param) < 0) + return -errno; - if (param->ioctlfd < 0) { - r = -EIO; - goto finish; - } + if (param->ioctlfd < 0) + return -EIO; fd_cloexec(param->ioctlfd, true); - r = param->ioctlfd; - -finish: - free(param); - return r; + return param->ioctlfd; } static int autofs_protocol(int dev_autofs_fd, int ioctl_fd) { @@ -444,15 +449,16 @@ int automount_send_ready(Automount *a, int status) { if (set_isempty(a->tokens)) return 0; - if ((ioctl_fd = open_ioctl_fd(UNIT(a)->manager->dev_autofs_fd, a->where, a->dev_id)) < 0) { + ioctl_fd = open_ioctl_fd(UNIT(a)->manager->dev_autofs_fd, a->where, a->dev_id); + if (ioctl_fd < 0) { r = ioctl_fd; goto fail; } if (status) - log_debug("Sending failure: %s", strerror(-status)); + log_debug_unit(UNIT(a)->id, "Sending failure: %s", strerror(-status)); else - log_debug("Sending success."); + log_debug_unit(UNIT(a)->id, "Sending success."); r = 0; @@ -465,10 +471,11 @@ int automount_send_ready(Automount *a, int status) { * if you pass a positive status code here, the kernel will * freeze! Yay! */ - if ((k = autofs_send_ready(UNIT(a)->manager->dev_autofs_fd, - ioctl_fd, - token, - status)) < 0) + k = autofs_send_ready(UNIT(a)->manager->dev_autofs_fd, + ioctl_fd, + token, + status); + if (k < 0) r = k; } @@ -493,7 +500,8 @@ static void automount_enter_waiting(Automount *a) { if (a->tokens) set_clear(a->tokens); - if ((dev_autofs_fd = open_dev_autofs(UNIT(a)->manager)) < 0) { + dev_autofs_fd = open_dev_autofs(UNIT(a)->manager); + if (dev_autofs_fd < 0) { r = dev_autofs_fd; goto fail; } @@ -501,6 +509,8 @@ static void automount_enter_waiting(Automount *a) { /* We knowingly ignore the results of this call */ mkdir_p_label(a->where, 0555); + warn_if_dir_nonempty(a->meta.id, a->where); + if (pipe2(p, O_NONBLOCK|O_CLOEXEC) < 0) { r = -errno; goto fail; @@ -527,15 +537,18 @@ static void automount_enter_waiting(Automount *a) { goto fail; } - if ((ioctl_fd = open_ioctl_fd(dev_autofs_fd, a->where, st.st_dev)) < 0) { + ioctl_fd = open_ioctl_fd(dev_autofs_fd, a->where, st.st_dev); + if (ioctl_fd < 0) { r = ioctl_fd; goto fail; } - if ((r = autofs_protocol(dev_autofs_fd, ioctl_fd)) < 0) + r = autofs_protocol(dev_autofs_fd, ioctl_fd); + if (r < 0) goto fail; - if ((r = autofs_set_timeout(dev_autofs_fd, ioctl_fd, 300)) < 0) + r = autofs_set_timeout(dev_autofs_fd, ioctl_fd, 300); + if (r < 0) goto fail; /* Autofs fun fact: @@ -547,7 +560,8 @@ static void automount_enter_waiting(Automount *a) { close_nointr_nofail(ioctl_fd); ioctl_fd = -1; - if ((r = unit_watch_fd(UNIT(a), p[0], EPOLLIN, &a->pipe_watch)) < 0) + r = unit_watch_fd(UNIT(a), p[0], EPOLLIN, &a->pipe_watch); + if (r < 0) goto fail; a->pipe_fd = p[0]; @@ -566,14 +580,15 @@ fail: if (mounted) repeat_unmout(a->where); - log_error("Failed to initialize automounter: %s", strerror(-r)); + log_error_unit(UNIT(a)->id, + "Failed to initialize automounter: %s", strerror(-r)); automount_enter_dead(a, AUTOMOUNT_FAILURE_RESOURCES); } static void automount_enter_runnning(Automount *a) { int r; struct stat st; - DBusError error; + _cleanup_dbus_error_free_ DBusError error; assert(a); assert(UNIT_DEREF(a->mount)); @@ -583,7 +598,8 @@ static void automount_enter_runnning(Automount *a) { /* We don't take mount requests anymore if we are supposed to * shut down anyway */ if (unit_pending_inactive(UNIT(a))) { - log_debug("Suppressing automount request on %s since unit stop is scheduled.", UNIT(a)->id); + log_debug_unit(UNIT(a)->id, + "Suppressing automount request on %s since unit stop is scheduled.", UNIT(a)->id); automount_send_ready(a, -EHOSTDOWN); return; } @@ -592,14 +608,18 @@ static void automount_enter_runnning(Automount *a) { /* Before we do anything, let's see if somebody is playing games with us? */ if (lstat(a->where, &st) < 0) { - log_warning("%s failed to stat automount point: %m", UNIT(a)->id); + log_warning_unit(UNIT(a)->id, + "%s failed to stat automount point: %m", UNIT(a)->id); goto fail; } if (!S_ISDIR(st.st_mode) || st.st_dev != a->dev_id) - log_info("%s's automount point already active?", UNIT(a)->id); + log_info_unit(UNIT(a)->id, + "%s's automount point already active?", UNIT(a)->id); else if ((r = manager_add_job(UNIT(a)->manager, JOB_START, UNIT_DEREF(a->mount), JOB_REPLACE, true, &error, NULL)) < 0) { - log_warning("%s failed to queue mount startup job: %s", UNIT(a)->id, bus_error(&error, r)); + log_warning_unit(UNIT(a)->id, + "%s failed to queue mount startup job: %s", + UNIT(a)->id, bus_error(&error, r)); goto fail; } @@ -608,18 +628,18 @@ static void automount_enter_runnning(Automount *a) { fail: automount_enter_dead(a, AUTOMOUNT_FAILURE_RESOURCES); - dbus_error_free(&error); } static int automount_start(Unit *u) { Automount *a = AUTOMOUNT(u); assert(a); - assert(a->state == AUTOMOUNT_DEAD || a->state == AUTOMOUNT_FAILED); if (path_is_mount_point(a->where, false)) { - log_error("Path %s is already a mount point, refusing start for %s", a->where, u->id); + log_error_unit(u->id, + "Path %s is already a mount point, refusing start for %s", + a->where, u->id); return -EEXIST; } @@ -635,7 +655,6 @@ static int automount_stop(Unit *u) { Automount *a = AUTOMOUNT(u); assert(a); - assert(a->state == AUTOMOUNT_WAITING || a->state == AUTOMOUNT_RUNNING); automount_enter_dead(a, AUTOMOUNT_SUCCESS); @@ -661,7 +680,8 @@ static int automount_serialize(Unit *u, FILE *f, FDSet *fds) { if (a->pipe_fd >= 0) { int copy; - if ((copy = fdset_put_dup(fds, a->pipe_fd)) < 0) + copy = fdset_put_dup(fds, a->pipe_fd); + if (copy < 0) return copy; unit_serialize_item_format(u, f, "pipe-fd", "%i", copy); @@ -680,8 +700,9 @@ static int automount_deserialize_item(Unit *u, const char *key, const char *valu if (streq(key, "state")) { AutomountState state; - if ((state = automount_state_from_string(value)) < 0) - log_debug("Failed to parse state value %s", value); + state = automount_state_from_string(value); + if (state < 0) + log_debug_unit(u->id, "Failed to parse state value %s", value); else a->deserialized_state = state; } else if (streq(key, "result")) { @@ -689,7 +710,7 @@ static int automount_deserialize_item(Unit *u, const char *key, const char *valu f = automount_result_from_string(value); if (f < 0) - log_debug("Failed to parse result value %s", value); + log_debug_unit(u->id, "Failed to parse result value %s", value); else if (f != AUTOMOUNT_SUCCESS) a->result = f; @@ -697,27 +718,28 @@ static int automount_deserialize_item(Unit *u, const char *key, const char *valu unsigned d; if (safe_atou(value, &d) < 0) - log_debug("Failed to parse dev-id value %s", value); + log_debug_unit(u->id, "Failed to parse dev-id value %s", value); else a->dev_id = (unsigned) d; } else if (streq(key, "token")) { unsigned token; if (safe_atou(value, &token) < 0) - log_debug("Failed to parse token value %s", value); + log_debug_unit(u->id, "Failed to parse token value %s", value); else { if (!a->tokens) if (!(a->tokens = set_new(trivial_hash_func, trivial_compare_func))) return -ENOMEM; - if ((r = set_put(a->tokens, UINT_TO_PTR(token))) < 0) + r = set_put(a->tokens, UINT_TO_PTR(token)); + if (r < 0) return r; } } else if (streq(key, "pipe-fd")) { int fd; if (safe_atoi(value, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd)) - log_debug("Failed to parse pipe-fd value %s", value); + log_debug_unit(u->id, "Failed to parse pipe-fd value %s", value); else { if (a->pipe_fd >= 0) close_nointr_nofail(a->pipe_fd); @@ -725,7 +747,7 @@ static int automount_deserialize_item(Unit *u, const char *key, const char *valu a->pipe_fd = fdset_remove(fds, fd); } } else - log_debug("Unknown serialization key '%s'", key); + log_debug_unit(u->id, "Unknown serialization key '%s'", key); return 0; } @@ -763,12 +785,13 @@ static void automount_fd_event(Unit *u, int fd, uint32_t events, Watch *w) { assert(fd == a->pipe_fd); if (events != EPOLLIN) { - log_error("Got invalid poll event on pipe."); + log_error_unit(u->id, "Got invalid poll event on pipe."); goto fail; } - if ((l = loop_read(a->pipe_fd, &packet, sizeof(packet), true)) != sizeof(packet)) { - log_error("Invalid read from pipe: %s", l < 0 ? strerror(-l) : "short read"); + l = loop_read(a->pipe_fd, &packet, sizeof(packet), true); + if (l != sizeof(packet)) { + log_error_unit(u->id, "Invalid read from pipe: %s", l < 0 ? strerror(-l) : "short read"); goto fail; } @@ -777,23 +800,24 @@ static void automount_fd_event(Unit *u, int fd, uint32_t events, Watch *w) { case autofs_ptype_missing_direct: if (packet.v5_packet.pid > 0) { - char *p = NULL; + _cleanup_free_ char *p = NULL; get_process_comm(packet.v5_packet.pid, &p); - log_debug("Got direct mount request for %s, triggered by %lu (%s)", packet.v5_packet.name, (unsigned long) packet.v5_packet.pid, strna(p)); - free(p); - + log_debug_unit(u->id, + "Got direct mount request on %s, triggered by %lu (%s)", + a->where, (unsigned long) packet.v5_packet.pid, strna(p)); } else - log_debug("Got direct mount request for %s", packet.v5_packet.name); + log_debug_unit(u->id, "Got direct mount request on %s", a->where); - if (!a->tokens) - if (!(a->tokens = set_new(trivial_hash_func, trivial_compare_func))) { - log_error("Failed to allocate token set."); - goto fail; - } + r = set_ensure_allocated(&a->tokens, trivial_hash_func, trivial_compare_func); + if (r < 0) { + log_error_unit(u->id, "Failed to allocate token set."); + goto fail; + } - if ((r = set_put(a->tokens, UINT_TO_PTR(packet.v5_packet.wait_queue_token))) < 0) { - log_error("Failed to remember token: %s", strerror(-r)); + r = set_put(a->tokens, UINT_TO_PTR(packet.v5_packet.wait_queue_token)); + if (r < 0) { + log_error_unit(u->id, "Failed to remember token: %s", strerror(-r)); goto fail; } @@ -801,7 +825,7 @@ static void automount_fd_event(Unit *u, int fd, uint32_t events, Watch *w) { break; default: - log_error("Received unknown automount request %i", packet.hdr.type); + log_error_unit(u->id, "Received unknown automount request %i", packet.hdr.type); break; } diff --git a/src/core/build.h b/src/core/build.h index 0b38050bd4..4513a0bad7 100644 --- a/src/core/build.h +++ b/src/core/build.h @@ -63,4 +63,22 @@ #define _LIBCRYPTSETUP_FEATURE_ "-LIBCRYPTSETUP" #endif -#define SYSTEMD_FEATURES _PAM_FEATURE_ " " _LIBWRAP_FEATURE_ " " _AUDIT_FEATURE_ " " _SELINUX_FEATURE_ " " _IMA_FEATURE_ " " _SYSVINIT_FEATURE_ " " _LIBCRYPTSETUP_FEATURE_ +#ifdef HAVE_GCRYPT +#define _GCRYPT_FEATURE_ "+GCRYPT" +#else +#define _GCRYPT_FEATURE_ "-GCRYPT" +#endif + +#ifdef HAVE_ACL +#define _ACL_FEATURE_ "+ACL" +#else +#define _ACL_FEATURE_ "-ACL" +#endif + +#ifdef HAVE_XZ +#define _XZ_FEATURE_ "+XZ" +#else +#define _XZ_FEATURE_ "-XZ" +#endif + +#define SYSTEMD_FEATURES _PAM_FEATURE_ " " _LIBWRAP_FEATURE_ " " _AUDIT_FEATURE_ " " _SELINUX_FEATURE_ " " _IMA_FEATURE_ " " _SYSVINIT_FEATURE_ " " _LIBCRYPTSETUP_FEATURE_ " " _GCRYPT_FEATURE_ " " _ACL_FEATURE_ " " _XZ_FEATURE_ diff --git a/src/core/cgroup.c b/src/core/cgroup.c index 8ddb1118ed..8fc1731485 100644 --- a/src/core/cgroup.c +++ b/src/core/cgroup.c @@ -313,7 +313,7 @@ int manager_setup_cgroup(Manager *m) { goto finish; } - if (m->running_as == MANAGER_SYSTEM) + if (m->running_as == SYSTEMD_SYSTEM) strcpy(suffix, "/system"); else { snprintf(suffix, sizeof(suffix), "/systemd-%lu", (unsigned long) getpid()); diff --git a/src/core/condition.c b/src/core/condition.c index e4080d569d..b3184922b8 100644 --- a/src/core/condition.c +++ b/src/core/condition.c @@ -25,11 +25,13 @@ #include <unistd.h> #include <sys/capability.h> #include <sys/statvfs.h> +#include <fnmatch.h> #ifdef HAVE_SELINUX #include <selinux/selinux.h> #endif +#include <systemd/sd-id128.h> #include "util.h" #include "condition.h" #include "virt.h" @@ -194,6 +196,41 @@ static bool test_capability(const char *parameter) { return !!(capabilities & (1ULL << value)); } +static bool test_host(const char *parameter) { + sd_id128_t x, y; + char *h; + int r; + bool b; + + if (sd_id128_from_string(parameter, &x) >= 0) { + + r = sd_id128_get_machine(&y); + if (r < 0) + return false; + + return sd_id128_equal(x, y); + } + + h = gethostname_malloc(); + if (!h) + return false; + + b = fnmatch(parameter, h, FNM_CASEFOLD) == 0; + free(h); + + return b; +} + +static bool test_ac_power(const char *parameter) { + int r; + + r = parse_boolean(parameter); + if (r < 0) + return true; + + return (on_ac_power() != 0) == !!r; +} + bool condition_test(Condition *c) { assert(c); @@ -234,6 +271,15 @@ bool condition_test(Condition *c) { return !(k == -ENOENT || k > 0) == !c->negate; } + case CONDITION_FILE_NOT_EMPTY: { + struct stat st; + + if (stat(c->parameter, &st) < 0) + return c->negate; + + return (S_ISREG(st.st_mode) && st.st_size > 0) == !c->negate; + } + case CONDITION_FILE_IS_EXECUTABLE: { struct stat st; @@ -255,6 +301,12 @@ bool condition_test(Condition *c) { case CONDITION_CAPABILITY: return test_capability(c->parameter) == !c->negate; + case CONDITION_HOST: + return test_host(c->parameter) == !c->negate; + + case CONDITION_AC_POWER: + return test_ac_power(c->parameter) == !c->negate; + case CONDITION_NULL: return !c->negate; @@ -320,9 +372,12 @@ static const char* const condition_type_table[_CONDITION_TYPE_MAX] = { [CONDITION_PATH_IS_MOUNT_POINT] = "ConditionPathIsMountPoint", [CONDITION_PATH_IS_READ_WRITE] = "ConditionPathIsReadWrite", [CONDITION_DIRECTORY_NOT_EMPTY] = "ConditionDirectoryNotEmpty", + [CONDITION_FILE_NOT_EMPTY] = "ConditionFileNotEmpty", [CONDITION_KERNEL_COMMAND_LINE] = "ConditionKernelCommandLine", [CONDITION_VIRTUALIZATION] = "ConditionVirtualization", [CONDITION_SECURITY] = "ConditionSecurity", + [CONDITION_HOST] = "ConditionHost", + [CONDITION_AC_POWER] = "ConditionACPower", [CONDITION_NULL] = "ConditionNull" }; diff --git a/src/core/condition.h b/src/core/condition.h index 3dca432f77..1797385d26 100644 --- a/src/core/condition.h +++ b/src/core/condition.h @@ -33,11 +33,14 @@ typedef enum ConditionType { CONDITION_PATH_IS_MOUNT_POINT, CONDITION_PATH_IS_READ_WRITE, CONDITION_DIRECTORY_NOT_EMPTY, + CONDITION_FILE_NOT_EMPTY, CONDITION_FILE_IS_EXECUTABLE, CONDITION_KERNEL_COMMAND_LINE, CONDITION_VIRTUALIZATION, CONDITION_SECURITY, CONDITION_CAPABILITY, + CONDITION_HOST, + CONDITION_AC_POWER, CONDITION_NULL, _CONDITION_TYPE_MAX, _CONDITION_TYPE_INVALID = -1 diff --git a/src/core/dbus-automount.c b/src/core/dbus-automount.c index b93e3ea35f..060cbf7707 100644 --- a/src/core/dbus-automount.c +++ b/src/core/dbus-automount.c @@ -24,6 +24,7 @@ #include "dbus-unit.h" #include "dbus-automount.h" #include "dbus-common.h" +#include "selinux-access.h" #define BUS_AUTOMOUNT_INTERFACE \ " <interface name=\"org.freedesktop.systemd1.Automount\">\n" \ @@ -68,5 +69,7 @@ DBusHandlerResult bus_automount_message_handler(Unit *u, DBusConnection *c, DBus { NULL, } }; + SELINUX_UNIT_ACCESS_CHECK(u, c, message, "status"); + return bus_default_message_handler(c, message, INTROSPECTION, INTERFACES_LIST, bps); } diff --git a/src/core/dbus-device.c b/src/core/dbus-device.c index dfbbafb66d..dbd91fe3db 100644 --- a/src/core/dbus-device.c +++ b/src/core/dbus-device.c @@ -22,6 +22,7 @@ #include "dbus-unit.h" #include "dbus-device.h" #include "dbus-common.h" +#include "selinux-access.h" #define BUS_DEVICE_INTERFACE \ " <interface name=\"org.freedesktop.systemd1.Device\">\n" \ @@ -61,5 +62,7 @@ DBusHandlerResult bus_device_message_handler(Unit *u, DBusConnection *c, DBusMes { NULL, } }; + SELINUX_UNIT_ACCESS_CHECK(u, c, message, "status"); + return bus_default_message_handler(c, message, INTROSPECTION, INTERFACES_LIST, bps); } diff --git a/src/core/dbus-job.c b/src/core/dbus-job.c index 1b01ead2da..fdc1dce177 100644 --- a/src/core/dbus-job.c +++ b/src/core/dbus-job.c @@ -25,6 +25,7 @@ #include "log.h" #include "dbus-job.h" #include "dbus-common.h" +#include "selinux-access.h" #define BUS_JOB_INTERFACE \ " <interface name=\"org.freedesktop.systemd1.Job\">\n" \ @@ -68,7 +69,8 @@ static int bus_job_append_unit(DBusMessageIter *i, const char *property, void *d if (!dbus_message_iter_open_container(i, DBUS_TYPE_STRUCT, NULL, &sub)) return -ENOMEM; - if (!(p = unit_dbus_path(j->unit))) + p = unit_dbus_path(j->unit); + if (!p) return -ENOMEM; if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &j->unit->id) || @@ -94,43 +96,39 @@ static const BusProperty bus_job_properties[] = { }; static DBusHandlerResult bus_job_message_dispatch(Job *j, DBusConnection *connection, DBusMessage *message) { - DBusMessage *reply = NULL; + _cleanup_dbus_message_unref_ DBusMessage *reply = NULL; if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Job", "Cancel")) { - if (!(reply = dbus_message_new_method_return(message))) - goto oom; - job_finish_and_invalidate(j, JOB_CANCELED, true); + SELINUX_UNIT_ACCESS_CHECK(j->unit, connection, message, "stop"); + reply = dbus_message_new_method_return(message); + if (!reply) + return DBUS_HANDLER_RESULT_NEED_MEMORY; + + job_finish_and_invalidate(j, JOB_CANCELED, true); } else { const BusBoundProperties bps[] = { { "org.freedesktop.systemd1.Job", bus_job_properties, j }, { NULL, } }; - return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, bps); - } - if (reply) { - if (!dbus_connection_send(connection, reply, NULL)) - goto oom; + SELINUX_UNIT_ACCESS_CHECK(j->unit, connection, message, "status"); - dbus_message_unref(reply); + return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, bps); } - return DBUS_HANDLER_RESULT_HANDLED; - -oom: - if (reply) - dbus_message_unref(reply); + if (!dbus_connection_send(connection, reply, NULL)) + return DBUS_HANDLER_RESULT_NEED_MEMORY; - return DBUS_HANDLER_RESULT_NEED_MEMORY; + return DBUS_HANDLER_RESULT_HANDLED; } static DBusHandlerResult bus_job_message_handler(DBusConnection *connection, DBusMessage *message, void *data) { Manager *m = data; Job *j; int r; - DBusMessage *reply; + _cleanup_dbus_message_unref_ DBusMessage *reply = NULL; assert(connection); assert(message); @@ -145,7 +143,10 @@ static DBusHandlerResult bus_job_message_handler(DBusConnection *connection, DBu Iterator i; size_t size; - if (!(reply = dbus_message_new_method_return(message))) + SELINUX_ACCESS_CHECK(connection, message, "status"); + + reply = dbus_message_new_method_return(message); + if (!reply) goto oom; /* We roll our own introspection code here, instead of @@ -153,7 +154,8 @@ static DBusHandlerResult bus_job_message_handler(DBusConnection *connection, DBu * need to generate our introspection string * dynamically. */ - if (!(f = open_memstream(&introspection, &size))) + f = open_memstream(&introspection, &size); + if (!f) goto oom; fputs(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE @@ -188,36 +190,28 @@ static DBusHandlerResult bus_job_message_handler(DBusConnection *connection, DBu if (!dbus_connection_send(connection, reply, NULL)) goto oom; - dbus_message_unref(reply); - return DBUS_HANDLER_RESULT_HANDLED; } return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } - if ((r = manager_get_job_from_dbus_path(m, dbus_message_get_path(message), &j)) < 0) { - - if (r == -ENOMEM) - return DBUS_HANDLER_RESULT_NEED_MEMORY; - - if (r == -ENOENT) { - DBusError e; - - dbus_error_init(&e); - dbus_set_error_const(&e, DBUS_ERROR_UNKNOWN_OBJECT, "Unknown job"); - return bus_send_error_reply(connection, message, &e, r); - } + r = manager_get_job_from_dbus_path(m, dbus_message_get_path(message), &j); + if (r == -ENOMEM) + goto oom; + if (r == -ENOENT) { + DBusError e; - return bus_send_error_reply(connection, message, NULL, r); + dbus_error_init(&e); + dbus_set_error_const(&e, DBUS_ERROR_UNKNOWN_OBJECT, "Unknown job"); + return bus_send_error_reply(connection, message, &e, r); } + if (r < 0) + return bus_send_error_reply(connection, message, NULL, r); return bus_job_message_dispatch(j, connection, message); oom: - if (reply) - dbus_message_unref(reply); - return DBUS_HANDLER_RESULT_NEED_MEMORY; } diff --git a/src/core/dbus-manager.c b/src/core/dbus-manager.c index c341d36a6b..859fa1a906 100644 --- a/src/core/dbus-manager.c +++ b/src/core/dbus-manager.c @@ -30,9 +30,12 @@ #include "build.h" #include "dbus-common.h" #include "install.h" +#include "selinux-access.h" #include "watchdog.h" #include "hwclock.h" #include "path-util.h" +#include "dbus-unit.h" +#include "virt.h" #define BUS_MANAGER_INTERFACE_BEGIN \ " <interface name=\"org.freedesktop.systemd1.Manager\">\n" @@ -214,6 +217,8 @@ " <arg name=\"result\" type=\"s\"/>\n" \ " </signal>" \ " <signal name=\"StartupFinished\">\n" \ + " <arg name=\"firmware\" type=\"t\"/>\n" \ + " <arg name=\"loader\" type=\"t\"/>\n" \ " <arg name=\"kernel\" type=\"t\"/>\n" \ " <arg name=\"initrd\" type=\"t\"/>\n" \ " <arg name=\"userspace\" type=\"t\"/>\n" \ @@ -223,13 +228,18 @@ #define BUS_MANAGER_INTERFACE_PROPERTIES_GENERAL \ " <property name=\"Version\" type=\"s\" access=\"read\"/>\n" \ - " <property name=\"Distribution\" type=\"s\" access=\"read\"/>\n" \ " <property name=\"Features\" type=\"s\" access=\"read\"/>\n" \ " <property name=\"Tainted\" type=\"s\" access=\"read\"/>\n" \ + " <property name=\"FirmwareTimestamp\" type=\"t\" access=\"read\"/>\n" \ + " <property name=\"FirmwareTimestampMonotonic\" type=\"t\" access=\"read\"/>\n" \ + " <property name=\"LoaderTimestamp\" type=\"t\" access=\"read\"/>\n" \ + " <property name=\"LoaderTimestampMonotonic\" type=\"t\" access=\"read\"/>\n" \ + " <property name=\"KernelTimestamp\" type=\"t\" access=\"read\"/>\n" \ + " <property name=\"KernelTimestampMonotonic\" type=\"t\" access=\"read\"/>\n" \ " <property name=\"InitRDTimestamp\" type=\"t\" access=\"read\"/>\n" \ " <property name=\"InitRDTimestampMonotonic\" type=\"t\" access=\"read\"/>\n" \ - " <property name=\"StartupTimestamp\" type=\"t\" access=\"read\"/>\n" \ - " <property name=\"StartupTimestampMonotonic\" type=\"t\" access=\"read\"/>\n" \ + " <property name=\"UserspaceTimestamp\" type=\"t\" access=\"read\"/>\n" \ + " <property name=\"UserspaceTimestampMonotonic\" type=\"t\" access=\"read\"/>\n" \ " <property name=\"FinishTimestamp\" type=\"t\" access=\"read\"/>\n" \ " <property name=\"FinishTimestampMonotonic\" type=\"t\" access=\"read\"/>\n" \ " <property name=\"LogLevel\" type=\"s\" access=\"readwrite\"/>\n" \ @@ -248,7 +258,8 @@ " <property name=\"DefaultStandardOutput\" type=\"s\" access=\"read\"/>\n" \ " <property name=\"DefaultStandardError\" type=\"s\" access=\"read\"/>\n" \ " <property name=\"RuntimeWatchdogUSec\" type=\"s\" access=\"readwrite\"/>\n" \ - " <property name=\"ShutdownWatchdogUSec\" type=\"s\" access=\"readwrite\"/>\n" + " <property name=\"ShutdownWatchdogUSec\" type=\"s\" access=\"readwrite\"/>\n" \ + " <property name=\"Virtualization\" type=\"s\" access=\"read\"/>\n" #define BUS_MANAGER_INTERFACE_END \ " </interface>\n" @@ -340,17 +351,21 @@ static int bus_manager_set_log_target(DBusMessageIter *i, const char *property, } static int bus_manager_append_log_level(DBusMessageIter *i, const char *property, void *data) { - const char *t; + char *t; + int r; assert(i); assert(property); - t = log_level_to_string(log_get_max_level()); + r = log_level_to_string_alloc(log_get_max_level(), &t); + if (r < 0) + return r; if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &t)) - return -ENOMEM; + r = -ENOMEM; - return 0; + free(t); + return r; } static int bus_manager_set_log_level(DBusMessageIter *i, const char *property, void *data) { @@ -415,18 +430,20 @@ static int bus_manager_append_progress(DBusMessageIter *i, const char *property, return 0; } -static const char *message_get_sender_with_fallback(DBusMessage *m) { - const char *s; +static int bus_manager_append_virt(DBusMessageIter *i, const char *property, void *data) { + Manager *m = data; + const char *id = ""; + assert(i); + assert(property); assert(m); - if ((s = dbus_message_get_sender(m))) - return s; + detect_virtualization(&id); - /* When the message came in from a direct connection the - * message will have no sender. We fix that here. */ + if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &id)) + return -ENOMEM; - return ":no-sender"; + return 0; } static DBusMessage *message_from_file_changes( @@ -508,51 +525,55 @@ static int bus_manager_set_runtime_watchdog_usec(DBusMessageIter *i, const char static const char systemd_property_string[] = PACKAGE_STRING "\0" - DISTRIBUTION "\0" SYSTEMD_FEATURES; static const BusProperty bus_systemd_properties[] = { - { "Version", bus_property_append_string, "s", 0 }, - { "Distribution", bus_property_append_string, "s", sizeof(PACKAGE_STRING) }, - { "Features", bus_property_append_string, "s", sizeof(PACKAGE_STRING) + sizeof(DISTRIBUTION) }, + { "Version", bus_property_append_string, "s", 0 }, + { "Features", bus_property_append_string, "s", sizeof(PACKAGE_STRING) }, { NULL, } }; static const BusProperty bus_manager_properties[] = { - { "Tainted", bus_manager_append_tainted, "s", 0 }, - { "InitRDTimestamp", bus_property_append_uint64, "t", offsetof(Manager, initrd_timestamp.realtime) }, - { "InitRDTimestampMonotonic", bus_property_append_uint64, "t", offsetof(Manager, initrd_timestamp.monotonic) }, - { "StartupTimestamp", bus_property_append_uint64, "t", offsetof(Manager, startup_timestamp.realtime) }, - { "StartupTimestampMonotonic", bus_property_append_uint64, "t", offsetof(Manager, startup_timestamp.monotonic) }, - { "FinishTimestamp", bus_property_append_uint64, "t", offsetof(Manager, finish_timestamp.realtime) }, - { "FinishTimestampMonotonic", bus_property_append_uint64, "t", offsetof(Manager, finish_timestamp.monotonic) }, - { "LogLevel", bus_manager_append_log_level, "s", 0, false, bus_manager_set_log_level }, - { "LogTarget", bus_manager_append_log_target, "s", 0, false, bus_manager_set_log_target }, - { "NNames", bus_manager_append_n_names, "u", 0 }, - { "NJobs", bus_manager_append_n_jobs, "u", 0 }, - { "NInstalledJobs",bus_property_append_uint32, "u", offsetof(Manager, n_installed_jobs) }, - { "NFailedJobs", bus_property_append_uint32, "u", offsetof(Manager, n_failed_jobs) }, - { "Progress", bus_manager_append_progress, "d", 0 }, - { "Environment", bus_property_append_strv, "as", offsetof(Manager, environment), true }, - { "ConfirmSpawn", bus_property_append_bool, "b", offsetof(Manager, confirm_spawn) }, - { "ShowStatus", bus_property_append_bool, "b", offsetof(Manager, show_status) }, - { "UnitPath", bus_property_append_strv, "as", offsetof(Manager, lookup_paths.unit_path), true }, - { "ControlGroupHierarchy", bus_property_append_string, "s", offsetof(Manager, cgroup_hierarchy), true }, - { "DefaultControllers", bus_property_append_strv, "as", offsetof(Manager, default_controllers), true }, - { "DefaultStandardOutput", bus_manager_append_exec_output, "s", offsetof(Manager, default_std_output) }, - { "DefaultStandardError", bus_manager_append_exec_output, "s", offsetof(Manager, default_std_error) }, - { "RuntimeWatchdogUSec", bus_property_append_usec, "t", offsetof(Manager, runtime_watchdog), false, bus_manager_set_runtime_watchdog_usec }, - { "ShutdownWatchdogUSec", bus_property_append_usec, "t", offsetof(Manager, shutdown_watchdog), false, bus_property_set_usec }, + { "Tainted", bus_manager_append_tainted, "s", 0 }, + { "FirmwareTimestamp", bus_property_append_uint64, "t", offsetof(Manager, firmware_timestamp.realtime) }, + { "FirmwareTimestampMonotonic", bus_property_append_uint64, "t", offsetof(Manager, firmware_timestamp.monotonic) }, + { "LoaderTimestamp", bus_property_append_uint64, "t", offsetof(Manager, loader_timestamp.realtime) }, + { "LoaderTimestampMonotonic", bus_property_append_uint64, "t", offsetof(Manager, loader_timestamp.monotonic) }, + { "KernelTimestamp", bus_property_append_uint64, "t", offsetof(Manager, kernel_timestamp.realtime) }, + { "KernelTimestampMonotonic", bus_property_append_uint64, "t", offsetof(Manager, kernel_timestamp.monotonic) }, + { "InitRDTimestamp", bus_property_append_uint64, "t", offsetof(Manager, initrd_timestamp.realtime) }, + { "InitRDTimestampMonotonic", bus_property_append_uint64, "t", offsetof(Manager, initrd_timestamp.monotonic) }, + { "UserspaceTimestamp", bus_property_append_uint64, "t", offsetof(Manager, userspace_timestamp.realtime) }, + { "UserspaceTimestampMonotonic", bus_property_append_uint64, "t", offsetof(Manager, userspace_timestamp.monotonic) }, + { "FinishTimestamp", bus_property_append_uint64, "t", offsetof(Manager, finish_timestamp.realtime) }, + { "FinishTimestampMonotonic", bus_property_append_uint64, "t", offsetof(Manager, finish_timestamp.monotonic) }, + { "LogLevel", bus_manager_append_log_level, "s", 0, false, bus_manager_set_log_level }, + { "LogTarget", bus_manager_append_log_target, "s", 0, false, bus_manager_set_log_target }, + { "NNames", bus_manager_append_n_names, "u", 0 }, + { "NJobs", bus_manager_append_n_jobs, "u", 0 }, + { "NInstalledJobs", bus_property_append_uint32, "u", offsetof(Manager, n_installed_jobs) }, + { "NFailedJobs", bus_property_append_uint32, "u", offsetof(Manager, n_failed_jobs) }, + { "Progress", bus_manager_append_progress, "d", 0 }, + { "Environment", bus_property_append_strv, "as", offsetof(Manager, environment), true }, + { "ConfirmSpawn", bus_property_append_bool, "b", offsetof(Manager, confirm_spawn) }, + { "ShowStatus", bus_property_append_bool, "b", offsetof(Manager, show_status) }, + { "UnitPath", bus_property_append_strv, "as", offsetof(Manager, lookup_paths.unit_path), true }, + { "ControlGroupHierarchy", bus_property_append_string, "s", offsetof(Manager, cgroup_hierarchy), true }, + { "DefaultControllers", bus_property_append_strv, "as", offsetof(Manager, default_controllers), true }, + { "DefaultStandardOutput", bus_manager_append_exec_output, "s", offsetof(Manager, default_std_output) }, + { "DefaultStandardError", bus_manager_append_exec_output, "s", offsetof(Manager, default_std_error) }, + { "RuntimeWatchdogUSec", bus_property_append_usec, "t", offsetof(Manager, runtime_watchdog), false, bus_manager_set_runtime_watchdog_usec }, + { "ShutdownWatchdogUSec", bus_property_append_usec, "t", offsetof(Manager, shutdown_watchdog), false, bus_property_set_usec }, + { "Virtualization", bus_manager_append_virt, "s", 0, }, { NULL, } }; static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection, DBusMessage *message, void *data) { + _cleanup_dbus_message_unref_ DBusMessage *reply = NULL; + _cleanup_free_ char * path = NULL; Manager *m = data; - int r; DBusError error; - DBusMessage *reply = NULL; - char * path = NULL; JobType job_type = _JOB_TYPE_INVALID; bool reload_if_possible = false; const char *member; @@ -576,15 +597,20 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection, DBUS_TYPE_INVALID)) return bus_send_error_reply(connection, message, &error, -EINVAL); - if (!(u = manager_get_unit(m, name))) { + u = manager_get_unit(m, name); + if (!u) { dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s is not loaded.", name); return bus_send_error_reply(connection, message, &error, -ENOENT); } - if (!(reply = dbus_message_new_method_return(message))) + SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "status"); + + reply = dbus_message_new_method_return(message); + if (!reply) goto oom; - if (!(path = unit_dbus_path(u))) + path = unit_dbus_path(u); + if (!path) goto oom; if (!dbus_message_append_args( @@ -603,15 +629,20 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection, DBUS_TYPE_INVALID)) return bus_send_error_reply(connection, message, &error, -EINVAL); - if (!(u = cgroup_unit_by_pid(m, (pid_t) pid))) { + u = cgroup_unit_by_pid(m, (pid_t) pid); + if (!u) { dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "No unit for PID %lu is loaded.", (unsigned long) pid); return bus_send_error_reply(connection, message, &error, -ENOENT); } - if (!(reply = dbus_message_new_method_return(message))) + SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "status"); + + reply = dbus_message_new_method_return(message); + if (!reply) goto oom; - if (!(path = unit_dbus_path(u))) + path = unit_dbus_path(u); + if (!path) goto oom; if (!dbus_message_append_args( @@ -630,13 +661,18 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection, DBUS_TYPE_INVALID)) return bus_send_error_reply(connection, message, &error, -EINVAL); - if ((r = manager_load_unit(m, name, NULL, &error, &u)) < 0) + r = manager_load_unit(m, name, NULL, &error, &u); + if (r < 0) return bus_send_error_reply(connection, message, &error, r); - if (!(reply = dbus_message_new_method_return(message))) + SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "status"); + + reply = dbus_message_new_method_return(message); + if (!reply) goto oom; - if (!(path = unit_dbus_path(u))) + path = unit_dbus_path(u); + if (!path) goto oom; if (!dbus_message_append_args( @@ -695,11 +731,14 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection, return bus_send_error_reply(connection, message, &error, -ENOENT); } + SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "stop"); + r = unit_kill(u, who, signo, &error); if (r < 0) return bus_send_error_reply(connection, message, &error, r); - if (!(reply = dbus_message_new_method_return(message))) + reply = dbus_message_new_method_return(message); + if (!reply) goto oom; } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "GetJob")) { @@ -713,15 +752,20 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection, DBUS_TYPE_INVALID)) return bus_send_error_reply(connection, message, &error, -EINVAL); - if (!(j = manager_get_job(m, id))) { + j = manager_get_job(m, id); + if (!j) { dbus_set_error(&error, BUS_ERROR_NO_SUCH_JOB, "Job %u does not exist.", (unsigned) id); return bus_send_error_reply(connection, message, &error, -ENOENT); } - if (!(reply = dbus_message_new_method_return(message))) + SELINUX_UNIT_ACCESS_CHECK(j->unit, connection, message, "status"); + + reply = dbus_message_new_method_return(message); + if (!reply) goto oom; - if (!(path = job_dbus_path(j))) + path = job_dbus_path(j); + if (!path) goto oom; if (!dbus_message_append_args( @@ -732,16 +776,22 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection, } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ClearJobs")) { + SELINUX_ACCESS_CHECK(connection, message, "reboot"); + manager_clear_jobs(m); - if (!(reply = dbus_message_new_method_return(message))) + reply = dbus_message_new_method_return(message); + if (!reply) goto oom; } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ResetFailed")) { + SELINUX_ACCESS_CHECK(connection, message, "reload"); + manager_reset_failed(m); - if (!(reply = dbus_message_new_method_return(message))) + reply = dbus_message_new_method_return(message); + if (!reply) goto oom; } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ResetFailedUnit")) { @@ -755,14 +805,18 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection, DBUS_TYPE_INVALID)) return bus_send_error_reply(connection, message, &error, -EINVAL); - if (!(u = manager_get_unit(m, name))) { + u = manager_get_unit(m, name); + if (!u) { dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s is not loaded.", name); return bus_send_error_reply(connection, message, &error, -ENOENT); } + SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "reload"); + unit_reset_failed(u); - if (!(reply = dbus_message_new_method_return(message))) + reply = dbus_message_new_method_return(message); + if (!reply) goto oom; } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ListUnits")) { @@ -771,7 +825,10 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection, Unit *u; const char *k; - if (!(reply = dbus_message_new_method_return(message))) + SELINUX_ACCESS_CHECK(connection, message, "status"); + + reply = dbus_message_new_method_return(message); + if (!reply) goto oom; dbus_message_iter_init_append(reply, &iter); @@ -800,7 +857,8 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection, f = unit_following(u); following = f ? f->id : ""; - if (!(u_path = unit_dbus_path(u))) + u_path = unit_dbus_path(u); + if (!u_path) goto oom; if (u->job) { @@ -850,7 +908,10 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection, Iterator i; Job *j; - if (!(reply = dbus_message_new_method_return(message))) + SELINUX_ACCESS_CHECK(connection, message, "status"); + + reply = dbus_message_new_method_return(message); + if (!reply) goto oom; dbus_message_iter_init_append(reply, &iter); @@ -871,10 +932,12 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection, state = job_state_to_string(j->state); type = job_type_to_string(j->type); - if (!(j_path = job_dbus_path(j))) + j_path = job_dbus_path(j); + if (!j_path) goto oom; - if (!(u_path = unit_dbus_path(j->unit))) { + u_path = unit_dbus_path(j->unit); + if (!u_path) { free(j_path); goto oom; } @@ -904,38 +967,49 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection, char *client; Set *s; - if (!(s = BUS_CONNECTION_SUBSCRIBED(m, connection))) { - if (!(s = set_new(string_hash_func, string_compare_func))) + SELINUX_ACCESS_CHECK(connection, message, "status"); + + s = BUS_CONNECTION_SUBSCRIBED(m, connection); + if (!s) { + s = set_new(string_hash_func, string_compare_func); + if (!s) goto oom; - if (!(dbus_connection_set_data(connection, m->subscribed_data_slot, s, NULL))) { + if (!dbus_connection_set_data(connection, m->subscribed_data_slot, s, NULL)) { set_free(s); goto oom; } } - if (!(client = strdup(message_get_sender_with_fallback(message)))) + client = strdup(bus_message_get_sender_with_fallback(message)); + if (!client) goto oom; - if ((r = set_put(s, client)) < 0) { + r = set_put(s, client); + if (r < 0) { free(client); return bus_send_error_reply(connection, message, NULL, r); } - if (!(reply = dbus_message_new_method_return(message))) + reply = dbus_message_new_method_return(message); + if (!reply) goto oom; } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Unsubscribe")) { char *client; - if (!(client = set_remove(BUS_CONNECTION_SUBSCRIBED(m, connection), (char*) message_get_sender_with_fallback(message)))) { + SELINUX_ACCESS_CHECK(connection, message, "status"); + + client = set_remove(BUS_CONNECTION_SUBSCRIBED(m, connection), (char*) bus_message_get_sender_with_fallback(message)); + if (!client) { dbus_set_error(&error, BUS_ERROR_NOT_SUBSCRIBED, "Client is not subscribed."); return bus_send_error_reply(connection, message, &error, -ENOENT); } free(client); - if (!(reply = dbus_message_new_method_return(message))) + reply = dbus_message_new_method_return(message); + if (!reply) goto oom; } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Dump")) { @@ -943,10 +1017,14 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection, char *dump = NULL; size_t size; - if (!(reply = dbus_message_new_method_return(message))) + SELINUX_ACCESS_CHECK(connection, message, "status"); + + reply = dbus_message_new_method_return(message); + if (!reply) goto oom; - if (!(f = open_memstream(&dump, &size))) + f = open_memstream(&dump, &size); + if (!f) goto oom; manager_dump_units(m, f, NULL); @@ -971,6 +1049,8 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection, dbus_bool_t cleanup; Snapshot *s; + SELINUX_ACCESS_CHECK(connection, message, "start"); + if (!dbus_message_get_args( message, &error, @@ -979,16 +1059,19 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection, DBUS_TYPE_INVALID)) return bus_send_error_reply(connection, message, &error, -EINVAL); - if (name && name[0] == 0) + if (isempty(name)) name = NULL; - if ((r = snapshot_create(m, name, cleanup, &error, &s)) < 0) + r = snapshot_create(m, name, cleanup, &error, &s); + if (r < 0) return bus_send_error_reply(connection, message, &error, r); - if (!(reply = dbus_message_new_method_return(message))) + reply = dbus_message_new_method_return(message); + if (!reply) goto oom; - if (!(path = unit_dbus_path(UNIT(s)))) + path = unit_dbus_path(UNIT(s)); + if (!path) goto oom; if (!dbus_message_append_args( @@ -1006,7 +1089,10 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection, const char *k; size_t size; - if (!(reply = dbus_message_new_method_return(message))) + SELINUX_ACCESS_CHECK(connection, message, "status"); + + reply = dbus_message_new_method_return(message); + if (!reply) goto oom; /* We roll our own introspection code here, instead of @@ -1014,7 +1100,8 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection, * need to generate our introspection string * dynamically. */ - if (!(f = open_memstream(&introspection, &size))) + f = open_memstream(&introspection, &size); + if (!f) goto oom; fputs(INTROSPECTION_BEGIN, f); @@ -1025,7 +1112,8 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection, if (k != u->id) continue; - if (!(p = bus_path_escape(k))) { + p = bus_path_escape(k); + if (!p) { fclose(f); free(introspection); goto oom; @@ -1060,6 +1148,8 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection, } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Reload")) { + SELINUX_ACCESS_CHECK(connection, message, "reload"); + assert(!m->queued_message); /* Instead of sending the reply back right away, we @@ -1067,7 +1157,8 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection, * after the reload is finished. That way the caller * knows when the reload finished. */ - if (!(m->queued_message = dbus_message_new_method_return(message))) + m->queued_message = dbus_message_new_method_return(message); + if (!m->queued_message) goto oom; m->queued_message_connection = connection; @@ -1075,6 +1166,8 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection, } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Reexecute")) { + SELINUX_ACCESS_CHECK(connection, message, "reload"); + /* We don't send a reply back here, the client should * just wait for us disconnecting. */ @@ -1082,60 +1175,75 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection, } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Exit")) { - if (m->running_as == MANAGER_SYSTEM) { + SELINUX_ACCESS_CHECK(connection, message, "halt"); + + if (m->running_as == SYSTEMD_SYSTEM) { dbus_set_error(&error, BUS_ERROR_NOT_SUPPORTED, "Exit is only supported for user service managers."); return bus_send_error_reply(connection, message, &error, -ENOTSUP); } - if (!(reply = dbus_message_new_method_return(message))) + reply = dbus_message_new_method_return(message); + if (!reply) goto oom; m->exit_code = MANAGER_EXIT; } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Reboot")) { - if (m->running_as != MANAGER_SYSTEM) { + SELINUX_ACCESS_CHECK(connection, message, "reboot"); + + if (m->running_as != SYSTEMD_SYSTEM) { dbus_set_error(&error, BUS_ERROR_NOT_SUPPORTED, "Reboot is only supported for system managers."); return bus_send_error_reply(connection, message, &error, -ENOTSUP); } - if (!(reply = dbus_message_new_method_return(message))) + reply = dbus_message_new_method_return(message); + if (!reply) goto oom; m->exit_code = MANAGER_REBOOT; } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "PowerOff")) { - if (m->running_as != MANAGER_SYSTEM) { + SELINUX_ACCESS_CHECK(connection, message, "halt"); + + if (m->running_as != SYSTEMD_SYSTEM) { dbus_set_error(&error, BUS_ERROR_NOT_SUPPORTED, "Powering off is only supported for system managers."); return bus_send_error_reply(connection, message, &error, -ENOTSUP); } - if (!(reply = dbus_message_new_method_return(message))) + reply = dbus_message_new_method_return(message); + if (!reply) goto oom; m->exit_code = MANAGER_POWEROFF; } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Halt")) { - if (m->running_as != MANAGER_SYSTEM) { + SELINUX_ACCESS_CHECK(connection, message, "halt"); + + if (m->running_as != SYSTEMD_SYSTEM) { dbus_set_error(&error, BUS_ERROR_NOT_SUPPORTED, "Halting is only supported for system managers."); return bus_send_error_reply(connection, message, &error, -ENOTSUP); } - if (!(reply = dbus_message_new_method_return(message))) + reply = dbus_message_new_method_return(message); + if (!reply) goto oom; m->exit_code = MANAGER_HALT; } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "KExec")) { - if (m->running_as != MANAGER_SYSTEM) { + SELINUX_ACCESS_CHECK(connection, message, "reboot"); + + if (m->running_as != SYSTEMD_SYSTEM) { dbus_set_error(&error, BUS_ERROR_NOT_SUPPORTED, "kexec is only supported for system managers."); return bus_send_error_reply(connection, message, &error, -ENOTSUP); } - if (!(reply = dbus_message_new_method_return(message))) + reply = dbus_message_new_method_return(message); + if (!reply) goto oom; m->exit_code = MANAGER_KEXEC; @@ -1145,6 +1253,8 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection, char *u, *v; int k; + SELINUX_ACCESS_CHECK(connection, message, "reboot"); + if (!dbus_message_get_args( message, &error, @@ -1159,7 +1269,7 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection, if (!isempty(switch_root_init) && !path_is_absolute(switch_root_init)) return bus_send_error_reply(connection, message, NULL, -EINVAL); - if (m->running_as != MANAGER_SYSTEM) { + if (m->running_as != SYSTEMD_SYSTEM) { dbus_set_error(&error, BUS_ERROR_NOT_SUPPORTED, "Switching root is only supported for system managers."); return bus_send_error_reply(connection, message, &error, -ENOTSUP); } @@ -1207,20 +1317,21 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection, } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "SetEnvironment")) { char **l = NULL, **e = NULL; - if ((r = bus_parse_strv(message, &l)) < 0) { - if (r == -ENOMEM) - goto oom; + SELINUX_ACCESS_CHECK(connection, message, "reboot"); + r = bus_parse_strv(message, &l); + if (r == -ENOMEM) + goto oom; + if (r < 0) return bus_send_error_reply(connection, message, NULL, r); - } e = strv_env_merge(2, m->environment, l); strv_free(l); - if (!e) goto oom; - if (!(reply = dbus_message_new_method_return(message))) { + reply = dbus_message_new_method_return(message); + if (!reply) { strv_free(e); goto oom; } @@ -1231,12 +1342,13 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection, } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "UnsetEnvironment")) { char **l = NULL, **e = NULL; - if ((r = bus_parse_strv(message, &l)) < 0) { - if (r == -ENOMEM) - goto oom; + SELINUX_ACCESS_CHECK(connection, message, "reboot"); + r = bus_parse_strv(message, &l); + if (r == -ENOMEM) + goto oom; + if (r < 0) return bus_send_error_reply(connection, message, NULL, r); - } e = strv_env_delete(m->environment, 1, l); strv_free(l); @@ -1244,7 +1356,8 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection, if (!e) goto oom; - if (!(reply = dbus_message_new_method_return(message))) { + reply = dbus_message_new_method_return(message); + if (!reply) { strv_free(e); goto oom; } @@ -1256,16 +1369,16 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection, char **l_set = NULL, **l_unset = NULL, **e = NULL, **f = NULL; DBusMessageIter iter; + SELINUX_ACCESS_CHECK(connection, message, "reboot"); + if (!dbus_message_iter_init(message, &iter)) goto oom; r = bus_parse_strv_iter(&iter, &l_unset); - if (r < 0) { - if (r == -ENOMEM) - goto oom; - + if (r == -ENOMEM) + goto oom; + if (r < 0) return bus_send_error_reply(connection, message, NULL, r); - } if (!dbus_message_iter_next(&iter)) { strv_free(l_unset); @@ -1296,7 +1409,8 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection, if (!f) goto oom; - if (!(reply = dbus_message_new_method_return(message))) { + reply = dbus_message_new_method_return(message); + if (!reply) { strv_free(f); goto oom; } @@ -1309,6 +1423,8 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection, Iterator i; UnitFileList *item; + SELINUX_ACCESS_CHECK(connection, message, "status"); + reply = dbus_message_new_method_return(message); if (!reply) goto oom; @@ -1317,10 +1433,9 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection, if (!h) goto oom; - r = unit_file_get_list(m->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER, NULL, h); + r = unit_file_get_list(m->running_as == SYSTEMD_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER, NULL, h); if (r < 0) { unit_file_list_free(h); - dbus_message_unref(reply); return bus_send_error_reply(connection, message, NULL, r); } @@ -1356,6 +1471,8 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection, UnitFileState state; const char *s; + SELINUX_ACCESS_CHECK(connection, message, "status"); + if (!dbus_message_get_args( message, &error, @@ -1363,7 +1480,7 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection, DBUS_TYPE_INVALID)) return bus_send_error_reply(connection, message, &error, -EINVAL); - state = unit_file_get_state(m->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER, NULL, name); + state = unit_file_get_state(m->running_as == SYSTEMD_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER, NULL, name); if (state < 0) return bus_send_error_reply(connection, message, NULL, state); @@ -1387,12 +1504,14 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection, char **l = NULL; DBusMessageIter iter; - UnitFileScope scope = m->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER; + UnitFileScope scope = m->running_as == SYSTEMD_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER; UnitFileChange *changes = NULL; unsigned n_changes = 0; dbus_bool_t runtime, force; int carries_install_info = -1; + SELINUX_ACCESS_CHECK(connection, message, streq(member, "MaskUnitFiles") ? "disable" : "enable"); + if (!dbus_message_iter_init(message, &iter)) goto oom; @@ -1446,11 +1565,13 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection, char **l = NULL; DBusMessageIter iter; - UnitFileScope scope = m->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER; + UnitFileScope scope = m->running_as == SYSTEMD_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER; UnitFileChange *changes = NULL; unsigned n_changes = 0; dbus_bool_t runtime; + SELINUX_ACCESS_CHECK(connection, message, streq(member, "UnmaskUnitFiles") ? "enable" : "disable"); + if (!dbus_message_iter_init(message, &iter)) goto oom; @@ -1495,16 +1616,17 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection, { "org.freedesktop.systemd1.Manager", bus_manager_properties, m }, { NULL, } }; + + SELINUX_ACCESS_CHECK(connection, message, "status"); + return bus_default_message_handler(connection, message, NULL, INTERFACES_LIST, bps); } if (job_type != _JOB_TYPE_INVALID) { const char *name, *smode, *old_name = NULL; JobMode mode; - Job *j; - JobBusClient *cl; Unit *u; - bool b; + dbus_bool_t b; if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "StartUnitReplace")) b = dbus_message_get_args( @@ -1521,86 +1643,37 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection, DBUS_TYPE_STRING, &name, DBUS_TYPE_STRING, &smode, DBUS_TYPE_INVALID); - if (!b) return bus_send_error_reply(connection, message, &error, -EINVAL); - if (old_name) - if (!(u = manager_get_unit(m, old_name)) || - !u->job || - u->job->type != JOB_START) { + if (old_name) { + u = manager_get_unit(m, old_name); + if (!u || !u->job || u->job->type != JOB_START) { dbus_set_error(&error, BUS_ERROR_NO_SUCH_JOB, "No job queued for unit %s", old_name); return bus_send_error_reply(connection, message, &error, -ENOENT); } + } - - if ((mode = job_mode_from_string(smode)) == _JOB_MODE_INVALID) { + mode = job_mode_from_string(smode); + if (mode < 0) { dbus_set_error(&error, BUS_ERROR_INVALID_JOB_MODE, "Job mode %s is invalid.", smode); return bus_send_error_reply(connection, message, &error, -EINVAL); } - if ((r = manager_load_unit(m, name, NULL, &error, &u)) < 0) - return bus_send_error_reply(connection, message, &error, r); - - if (reload_if_possible && unit_can_reload(u)) { - if (job_type == JOB_RESTART) - job_type = JOB_RELOAD_OR_START; - else if (job_type == JOB_TRY_RESTART) - job_type = JOB_RELOAD; - } - - if (job_type == JOB_STOP && u->load_state == UNIT_ERROR && unit_active_state(u) == UNIT_INACTIVE) { - dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s not loaded.", name); - return bus_send_error_reply(connection, message, &error, -EPERM); - } - - if ((job_type == JOB_START && u->refuse_manual_start) || - (job_type == JOB_STOP && u->refuse_manual_stop) || - ((job_type == JOB_RESTART || job_type == JOB_TRY_RESTART) && - (u->refuse_manual_start || u->refuse_manual_stop))) { - dbus_set_error(&error, BUS_ERROR_ONLY_BY_DEPENDENCY, "Operation refused, may be requested by dependency only."); - return bus_send_error_reply(connection, message, &error, -EPERM); - } - - if ((r = manager_add_job(m, job_type, u, mode, true, &error, &j)) < 0) + r = manager_load_unit(m, name, NULL, &error, &u); + if (r < 0) return bus_send_error_reply(connection, message, &error, r); - cl = job_bus_client_new(connection, message_get_sender_with_fallback(message)); - if (!cl) - goto oom; - - LIST_PREPEND(JobBusClient, client, j->bus_client_list, cl); - - if (!(reply = dbus_message_new_method_return(message))) - goto oom; - - if (!(path = job_dbus_path(j))) - goto oom; - - if (!dbus_message_append_args( - reply, - DBUS_TYPE_OBJECT_PATH, &path, - DBUS_TYPE_INVALID)) - goto oom; + return bus_unit_queue_job(connection, message, u, job_type, mode, reload_if_possible); } - if (reply) { + if (reply) if (!dbus_connection_send(connection, reply, NULL)) goto oom; - dbus_message_unref(reply); - } - - free(path); - return DBUS_HANDLER_RESULT_HANDLED; oom: - free(path); - - if (reply) - dbus_message_unref(reply); - dbus_error_free(&error); return DBUS_HANDLER_RESULT_NEED_MEMORY; diff --git a/src/core/dbus-mount.c b/src/core/dbus-mount.c index 93bfa4c35d..d81edeb807 100644 --- a/src/core/dbus-mount.c +++ b/src/core/dbus-mount.c @@ -26,6 +26,7 @@ #include "dbus-kill.h" #include "dbus-execute.h" #include "dbus-common.h" +#include "selinux-access.h" #define BUS_MOUNT_INTERFACE \ " <interface name=\"org.freedesktop.systemd1.Mount\">\n" \ @@ -161,5 +162,7 @@ DBusHandlerResult bus_mount_message_handler(Unit *u, DBusConnection *c, DBusMess { NULL, } }; + SELINUX_UNIT_ACCESS_CHECK(u, c, message, "status"); + return bus_default_message_handler(c, message, INTROSPECTION, INTERFACES_LIST, bps ); } diff --git a/src/core/dbus-path.c b/src/core/dbus-path.c index b77b5191c9..f7fed1754d 100644 --- a/src/core/dbus-path.c +++ b/src/core/dbus-path.c @@ -25,6 +25,7 @@ #include "dbus-path.h" #include "dbus-execute.h" #include "dbus-common.h" +#include "selinux-access.h" #define BUS_PATH_INTERFACE \ " <interface name=\"org.freedesktop.systemd1.Path\">\n" \ @@ -115,5 +116,7 @@ DBusHandlerResult bus_path_message_handler(Unit *u, DBusConnection *c, DBusMessa { NULL, } }; + SELINUX_UNIT_ACCESS_CHECK(u, c, message, "status"); + return bus_default_message_handler(c, message, INTROSPECTION, INTERFACES_LIST, bps); } diff --git a/src/core/dbus-service.c b/src/core/dbus-service.c index c0fac16d2b..d99058dd46 100644 --- a/src/core/dbus-service.c +++ b/src/core/dbus-service.c @@ -26,6 +26,7 @@ #include "dbus-kill.h" #include "dbus-service.h" #include "dbus-common.h" +#include "selinux-access.h" #define BUS_SERVICE_INTERFACE \ " <interface name=\"org.freedesktop.systemd1.Service\">\n" \ @@ -115,7 +116,9 @@ static const BusProperty bus_service_properties[] = { { "PIDFile", bus_property_append_string, "s", offsetof(Service, pid_file), true }, { "NotifyAccess", bus_service_append_notify_access, "s", offsetof(Service, notify_access) }, { "RestartUSec", bus_property_append_usec, "t", offsetof(Service, restart_usec) }, - { "TimeoutUSec", bus_property_append_usec, "t", offsetof(Service, timeout_usec) }, + { "TimeoutUSec", bus_property_append_usec, "t", offsetof(Service, timeout_start_usec) }, + { "TimeoutStartUSec", bus_property_append_usec, "t", offsetof(Service, timeout_start_usec) }, + { "TimeoutStopUSec", bus_property_append_usec, "t", offsetof(Service, timeout_stop_usec) }, { "WatchdogUSec", bus_property_append_usec, "t", offsetof(Service, watchdog_usec) }, { "WatchdogTimestamp", bus_property_append_usec, "t", offsetof(Service, watchdog_timestamp.realtime) }, { "WatchdogTimestampMonotonic",bus_property_append_usec, "t", offsetof(Service, watchdog_timestamp.monotonic) }, @@ -152,5 +155,7 @@ DBusHandlerResult bus_service_message_handler(Unit *u, DBusConnection *connectio { NULL, } }; + SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "status"); + return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, bps); } diff --git a/src/core/dbus-snapshot.c b/src/core/dbus-snapshot.c index 7ff0bca2c8..435c6df39c 100644 --- a/src/core/dbus-snapshot.c +++ b/src/core/dbus-snapshot.c @@ -22,6 +22,7 @@ #include "dbus-unit.h" #include "dbus-snapshot.h" #include "dbus-common.h" +#include "selinux-access.h" #define BUS_SNAPSHOT_INTERFACE \ " <interface name=\"org.freedesktop.systemd1.Snapshot\">\n" \ @@ -52,17 +53,19 @@ static const BusProperty bus_snapshot_properties[] = { DBusHandlerResult bus_snapshot_message_handler(Unit *u, DBusConnection *c, DBusMessage *message) { Snapshot *s = SNAPSHOT(u); - - DBusMessage *reply = NULL; + _cleanup_dbus_message_unref_ DBusMessage *reply = NULL; DBusError error; dbus_error_init(&error); if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Snapshot", "Remove")) { + SELINUX_UNIT_ACCESS_CHECK(u, c, message, "stop"); + snapshot_remove(SNAPSHOT(u)); - if (!(reply = dbus_message_new_method_return(message))) + reply = dbus_message_new_method_return(message); + if (!reply) goto oom; } else { @@ -71,22 +74,20 @@ DBusHandlerResult bus_snapshot_message_handler(Unit *u, DBusConnection *c, DBusM { "org.freedesktop.systemd1.Snapshot", bus_snapshot_properties, s }, { NULL, } }; + + SELINUX_UNIT_ACCESS_CHECK(u, c, message, "status"); + return bus_default_message_handler(c, message, INTROSPECTION, INTERFACES_LIST, bps); } if (reply) { if (!dbus_connection_send(c, reply, NULL)) goto oom; - - dbus_message_unref(reply); } return DBUS_HANDLER_RESULT_HANDLED; oom: - if (reply) - dbus_message_unref(reply); - dbus_error_free(&error); return DBUS_HANDLER_RESULT_NEED_MEMORY; diff --git a/src/core/dbus-socket.c b/src/core/dbus-socket.c index b2045225d7..095a031612 100644 --- a/src/core/dbus-socket.c +++ b/src/core/dbus-socket.c @@ -26,6 +26,7 @@ #include "dbus-execute.h" #include "dbus-kill.h" #include "dbus-common.h" +#include "selinux-access.h" #define BUS_SOCKET_INTERFACE \ " <interface name=\"org.freedesktop.systemd1.Socket\">\n" \ @@ -62,6 +63,9 @@ " <property name=\"MessageQueueMaxMessages\" type=\"x\" access=\"read\"/>\n" \ " <property name=\"MessageQueueMessageSize\" type=\"x\" access=\"read\"/>\n" \ " <property name=\"Result\" type=\"s\" access=\"read\"/>\n" \ + " <property name=\"SmackLabel\" type=\"s\" access=\"read\"/>\n" \ + " <property name=\"SmackLabelIPIn\" type=\"s\" access=\"read\"/>\n" \ + " <property name=\"SmackLabelIPOut\" type=\"s\" access=\"read\"/>\n" \ " </interface>\n" \ #define INTROSPECTION \ @@ -125,6 +129,9 @@ static const BusProperty bus_socket_properties[] = { { "MessageQueueMaxMessages", bus_property_append_long, "x", offsetof(Socket, mq_maxmsg) }, { "MessageQueueMessageSize", bus_property_append_long, "x", offsetof(Socket, mq_msgsize) }, { "Result", bus_socket_append_socket_result, "s", offsetof(Socket, result) }, + { "SmackLabel", bus_property_append_string, "s", offsetof(Socket, smack), true }, + { "SmackLabelIPIn", bus_property_append_string, "s", offsetof(Socket, smack_ip_in), true }, + { "SmackLabelIPOut",bus_property_append_string, "s", offsetof(Socket, smack_ip_out), true }, { NULL, } }; @@ -138,5 +145,7 @@ DBusHandlerResult bus_socket_message_handler(Unit *u, DBusConnection *c, DBusMes { NULL, } }; + SELINUX_UNIT_ACCESS_CHECK(u, c, message, "status"); + return bus_default_message_handler(c, message, INTROSPECTION, INTERFACES_LIST, bps); } diff --git a/src/core/dbus-swap.c b/src/core/dbus-swap.c index cad6ec1aaa..67ea0f24fe 100644 --- a/src/core/dbus-swap.c +++ b/src/core/dbus-swap.c @@ -27,6 +27,7 @@ #include "dbus-execute.h" #include "dbus-kill.h" #include "dbus-common.h" +#include "selinux-access.h" #define BUS_SWAP_INTERFACE \ " <interface name=\"org.freedesktop.systemd1.Swap\">\n" \ @@ -108,5 +109,7 @@ DBusHandlerResult bus_swap_message_handler(Unit *u, DBusConnection *c, DBusMessa { NULL, } }; + SELINUX_UNIT_ACCESS_CHECK(u, c, message, "status"); + return bus_default_message_handler(c, message, INTROSPECTION, INTERFACES_LIST, bps); } diff --git a/src/core/dbus-target.c b/src/core/dbus-target.c index 67ffff870f..6a775506cc 100644 --- a/src/core/dbus-target.c +++ b/src/core/dbus-target.c @@ -24,6 +24,7 @@ #include "dbus-unit.h" #include "dbus-target.h" #include "dbus-common.h" +#include "selinux-access.h" #define BUS_TARGET_INTERFACE \ " <interface name=\"org.freedesktop.systemd1.Target\">\n" \ @@ -51,5 +52,7 @@ DBusHandlerResult bus_target_message_handler(Unit *u, DBusConnection *c, DBusMes { NULL, } }; + SELINUX_UNIT_ACCESS_CHECK(u, c, message, "status"); + return bus_default_message_handler(c, message, INTROSPECTION, INTERFACES_LIST, bps); } diff --git a/src/core/dbus-timer.c b/src/core/dbus-timer.c index c76c2e0500..11d18cbd83 100644 --- a/src/core/dbus-timer.c +++ b/src/core/dbus-timer.c @@ -25,6 +25,7 @@ #include "dbus-timer.h" #include "dbus-execute.h" #include "dbus-common.h" +#include "selinux-access.h" #define BUS_TIMER_INTERFACE \ " <interface name=\"org.freedesktop.systemd1.Timer\">\n" \ @@ -78,7 +79,8 @@ static int bus_timer_append_timers(DBusMessageIter *i, const char *property, voi /* s/Sec/USec/ */ l = strlen(t); - if (!(buf = new(char, l+2))) + buf = new(char, l+2); + if (!buf) return -ENOMEM; memcpy(buf, t, l-3); @@ -120,7 +122,8 @@ static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_timer_append_timer_result, timer_resu static const BusProperty bus_timer_properties[] = { { "Unit", bus_timer_append_unit, "s", 0 }, { "Timers", bus_timer_append_timers, "a(stt)", 0 }, - { "NextElapseUSec", bus_property_append_usec, "t", offsetof(Timer, next_elapse) }, + { "NextElapseUSec", bus_property_append_usec, "t", offsetof(Timer, next_elapse_monotonic) }, + { "NextElapseUSecRealtime", bus_property_append_usec, "t", offsetof(Timer, next_elapse_realtime) }, { "Result", bus_timer_append_timer_result,"s", offsetof(Timer, result) }, { NULL, } }; @@ -133,5 +136,7 @@ DBusHandlerResult bus_timer_message_handler(Unit *u, DBusConnection *c, DBusMess { NULL, } }; + SELINUX_UNIT_ACCESS_CHECK(u, c, message, "status"); + return bus_default_message_handler(c, message, INTROSPECTION, INTERFACES_LIST, bps); } diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c index ad817d7ee8..8433a720b2 100644 --- a/src/core/dbus-unit.c +++ b/src/core/dbus-unit.c @@ -26,6 +26,7 @@ #include "dbus-unit.h" #include "bus-errors.h" #include "dbus-common.h" +#include "selinux-access.h" const char bus_unit_interface[] _introspect_("Unit") = BUS_UNIT_INTERFACE; @@ -234,7 +235,7 @@ static int bus_unit_append_can_isolate(DBusMessageIter *i, const char *property, static int bus_unit_append_job(DBusMessageIter *i, const char *property, void *data) { Unit *u = data; DBusMessageIter sub; - char *p; + _cleanup_free_ char *p = NULL; assert(i); assert(property); @@ -245,14 +246,13 @@ static int bus_unit_append_job(DBusMessageIter *i, const char *property, void *d if (u->job) { - if (!(p = job_dbus_path(u->job))) + p = job_dbus_path(u->job); + if (!p) return -ENOMEM; if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT32, &u->job->id) || - !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &p)) { - free(p); + !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &p)) return -ENOMEM; - } } else { uint32_t id = 0; @@ -260,18 +260,15 @@ static int bus_unit_append_job(DBusMessageIter *i, const char *property, void *d * data. Since we need to fill in a valid path we * simple point to ourselves. */ - if (!(p = unit_dbus_path(u))) + p = unit_dbus_path(u); + if (!p) return -ENOMEM; if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT32, &id) || - !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &p)) { - free(p); + !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &p)) return -ENOMEM; - } } - free(p); - if (!dbus_message_iter_close_container(i, &sub)) return -ENOMEM; @@ -288,8 +285,10 @@ static int bus_unit_append_default_cgroup(DBusMessageIter *i, const char *proper assert(property); assert(u); - if ((cgb = unit_get_default_cgroup(u))) { - if (!(t = cgroup_bonding_to_string(cgb))) + cgb = unit_get_default_cgroup(u); + if (cgb) { + t = cgroup_bonding_to_string(cgb); + if (!t) return -ENOMEM; } else t = (char*) ""; @@ -311,15 +310,14 @@ static int bus_unit_append_cgroups(DBusMessageIter *i, const char *property, voi return -ENOMEM; LIST_FOREACH(by_unit, cgb, u->cgroup_bondings) { - char *t; + char _cleanup_free_ *t = NULL; bool success; - if (!(t = cgroup_bonding_to_string(cgb))) + t = cgroup_bonding_to_string(cgb); + if (!t) return -ENOMEM; success = dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &t); - free(t); - if (!success) return -ENOMEM; } @@ -339,7 +337,7 @@ static int bus_unit_append_cgroup_attrs(DBusMessageIter *i, const char *property return -ENOMEM; LIST_FOREACH(by_unit, a, u->cgroup_attributes) { - char *v = NULL; + char _cleanup_free_ *v = NULL; bool success; if (a->map_callback) @@ -351,9 +349,6 @@ static int bus_unit_append_cgroup_attrs(DBusMessageIter *i, const char *property dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &a->name) && dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, v ? &v : &a->value) && dbus_message_iter_close_container(&sub, &sub2); - - free(v); - if (!success) return -ENOMEM; } @@ -405,12 +400,11 @@ static int bus_unit_append_load_error(DBusMessageIter *i, const char *property, } static DBusHandlerResult bus_unit_message_dispatch(Unit *u, DBusConnection *connection, DBusMessage *message) { - DBusMessage *reply = NULL; - Manager *m = u->manager; + _cleanup_dbus_message_unref_ DBusMessage *reply = NULL; DBusError error; JobType job_type = _JOB_TYPE_INVALID; - char *path = NULL; bool reload_if_possible = false; + int r; dbus_error_init(&error); @@ -434,7 +428,6 @@ static DBusHandlerResult bus_unit_message_dispatch(Unit *u, DBusConnection *conn const char *swho; int32_t signo; KillWho who; - int r; if (!dbus_message_get_args( message, @@ -455,6 +448,8 @@ static DBusHandlerResult bus_unit_message_dispatch(Unit *u, DBusConnection *conn if (signo <= 0 || signo >= _NSIG) return bus_send_error_reply(connection, message, &error, -EINVAL); + SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "stop"); + r = unit_kill(u, who, signo, &error); if (r < 0) return bus_send_error_reply(connection, message, &error, r); @@ -465,9 +460,12 @@ static DBusHandlerResult bus_unit_message_dispatch(Unit *u, DBusConnection *conn } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "ResetFailed")) { + SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "reload"); + unit_reset_failed(u); - if (!(reply = dbus_message_new_method_return(message))) + reply = dbus_message_new_method_return(message); + if (!reply) goto oom; } else if (UNIT_VTABLE(u)->bus_message_handler) @@ -478,16 +476,6 @@ static DBusHandlerResult bus_unit_message_dispatch(Unit *u, DBusConnection *conn if (job_type != _JOB_TYPE_INVALID) { const char *smode; JobMode mode; - Job *j; - int r; - - if ((job_type == JOB_START && u->refuse_manual_start) || - (job_type == JOB_STOP && u->refuse_manual_stop) || - ((job_type == JOB_RESTART || job_type == JOB_TRY_RESTART) && - (u->refuse_manual_start || u->refuse_manual_stop))) { - dbus_set_error(&error, BUS_ERROR_ONLY_BY_DEPENDENCY, "Operation refused, may be requested by dependency only."); - return bus_send_error_reply(connection, message, &error, -EPERM); - } if (!dbus_message_get_args( message, @@ -496,53 +484,23 @@ static DBusHandlerResult bus_unit_message_dispatch(Unit *u, DBusConnection *conn DBUS_TYPE_INVALID)) return bus_send_error_reply(connection, message, &error, -EINVAL); - if (reload_if_possible && unit_can_reload(u)) { - if (job_type == JOB_RESTART) - job_type = JOB_RELOAD_OR_START; - else if (job_type == JOB_TRY_RESTART) - job_type = JOB_RELOAD; - } - - if ((mode = job_mode_from_string(smode)) == _JOB_MODE_INVALID) { + mode = job_mode_from_string(smode); + if (mode < 0) { dbus_set_error(&error, BUS_ERROR_INVALID_JOB_MODE, "Job mode %s is invalid.", smode); return bus_send_error_reply(connection, message, &error, -EINVAL); } - if ((r = manager_add_job(m, job_type, u, mode, true, &error, &j)) < 0) - return bus_send_error_reply(connection, message, &error, r); - - if (!(reply = dbus_message_new_method_return(message))) - goto oom; - - if (!(path = job_dbus_path(j))) - goto oom; - - if (!dbus_message_append_args( - reply, - DBUS_TYPE_OBJECT_PATH, &path, - DBUS_TYPE_INVALID)) - goto oom; + return bus_unit_queue_job(connection, message, u, job_type, mode, reload_if_possible); } - if (reply) { + if (reply) if (!dbus_connection_send(connection, reply, NULL)) goto oom; - dbus_message_unref(reply); - } - - free(path); - return DBUS_HANDLER_RESULT_HANDLED; oom: - free(path); - - if (reply) - dbus_message_unref(reply); - dbus_error_free(&error); - return DBUS_HANDLER_RESULT_NEED_MEMORY; } @@ -550,7 +508,7 @@ static DBusHandlerResult bus_unit_message_handler(DBusConnection *connection, DB Manager *m = data; Unit *u; int r; - DBusMessage *reply = NULL; + _cleanup_dbus_message_unref_ DBusMessage *reply = NULL; DBusError error; assert(connection); @@ -562,6 +520,8 @@ static DBusHandlerResult bus_unit_message_handler(DBusConnection *connection, DB if (streq(dbus_message_get_path(message), "/org/freedesktop/systemd1/unit")) { /* Be nice to gdbus and return introspection data for our mid-level paths */ + SELINUX_ACCESS_CHECK(connection, message, "status"); + if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect")) { char *introspection = NULL; FILE *f; @@ -569,7 +529,8 @@ static DBusHandlerResult bus_unit_message_handler(DBusConnection *connection, DB const char *k; size_t size; - if (!(reply = dbus_message_new_method_return(message))) + reply = dbus_message_new_method_return(message); + if (!reply) goto oom; /* We roll our own introspection code here, instead of @@ -577,7 +538,8 @@ static DBusHandlerResult bus_unit_message_handler(DBusConnection *connection, DB * need to generate our introspection string * dynamically. */ - if (!(f = open_memstream(&introspection, &size))) + f = open_memstream(&introspection, &size); + if (!f) goto oom; fputs(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE @@ -592,7 +554,8 @@ static DBusHandlerResult bus_unit_message_handler(DBusConnection *connection, DB if (k != u->id) continue; - if (!(p = bus_path_escape(k))) { + p = bus_path_escape(k); + if (!p) { fclose(f); free(introspection); goto oom; @@ -625,8 +588,6 @@ static DBusHandlerResult bus_unit_message_handler(DBusConnection *connection, DB if (!dbus_connection_send(connection, reply, NULL)) goto oom; - dbus_message_unref(reply); - return DBUS_HANDLER_RESULT_HANDLED; } @@ -634,19 +595,14 @@ static DBusHandlerResult bus_unit_message_handler(DBusConnection *connection, DB } r = manager_load_unit_from_dbus_path(m, dbus_message_get_path(message), &error, &u); - if (r < 0) { - if (r == -ENOMEM) - goto oom; - + if (r == -ENOMEM) + goto oom; + if (r < 0) return bus_send_error_reply(connection, message, &error, r); - } return bus_unit_message_dispatch(u, connection, message); oom: - if (reply) - dbus_message_unref(reply); - dbus_error_free(&error); return DBUS_HANDLER_RESULT_NEED_MEMORY; @@ -657,8 +613,8 @@ const DBusObjectPathVTable bus_unit_vtable = { }; void bus_unit_send_change_signal(Unit *u) { - char *p = NULL; - DBusMessage *m = NULL; + _cleanup_free_ char *p = NULL; + _cleanup_dbus_message_unref_ DBusMessage *m = NULL; assert(u); @@ -675,20 +631,22 @@ void bus_unit_send_change_signal(Unit *u) { return; } - if (!(p = unit_dbus_path(u))) + p = unit_dbus_path(u); + if (!p) goto oom; if (u->sent_dbus_new_signal) { /* Send a properties changed signal. First for the * specific type, then for the generic unit. The * clients may rely on this order to get atomic - * behaviour if needed. */ + * behavior if needed. */ if (UNIT_VTABLE(u)->bus_invalidating_properties) { - if (!(m = bus_properties_changed_new(p, - UNIT_VTABLE(u)->bus_interface, - UNIT_VTABLE(u)->bus_invalidating_properties))) + m = bus_properties_changed_new(p, + UNIT_VTABLE(u)->bus_interface, + UNIT_VTABLE(u)->bus_invalidating_properties); + if (!m) goto oom; if (bus_broadcast(u->manager, m) < 0) @@ -697,13 +655,18 @@ void bus_unit_send_change_signal(Unit *u) { dbus_message_unref(m); } - if (!(m = bus_properties_changed_new(p, "org.freedesktop.systemd1.Unit", INVALIDATING_PROPERTIES))) + m = bus_properties_changed_new(p, "org.freedesktop.systemd1.Unit", + INVALIDATING_PROPERTIES); + if (!m) goto oom; } else { /* Send a new signal */ - if (!(m = dbus_message_new_signal("/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "UnitNew"))) + m = dbus_message_new_signal("/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "UnitNew"); + if (!m) goto oom; if (!dbus_message_append_args(m, @@ -716,25 +679,17 @@ void bus_unit_send_change_signal(Unit *u) { if (bus_broadcast(u->manager, m) < 0) goto oom; - free(p); - dbus_message_unref(m); - u->sent_dbus_new_signal = true; return; oom: - free(p); - - if (m) - dbus_message_unref(m); - - log_error("Failed to allocate unit change/new signal."); + log_oom(); } void bus_unit_send_removed_signal(Unit *u) { - char *p = NULL; - DBusMessage *m = NULL; + _cleanup_free_ char *p = NULL; + _cleanup_dbus_message_unref_ DBusMessage *m = NULL; assert(u); @@ -747,10 +702,14 @@ void bus_unit_send_removed_signal(Unit *u) { if (!u->id) return; - if (!(p = unit_dbus_path(u))) + p = unit_dbus_path(u); + if (!p) goto oom; - if (!(m = dbus_message_new_signal("/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "UnitRemoved"))) + m = dbus_message_new_signal("/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "UnitRemoved"); + if (!m) goto oom; if (!dbus_message_append_args(m, @@ -762,18 +721,92 @@ void bus_unit_send_removed_signal(Unit *u) { if (bus_broadcast(u->manager, m) < 0) goto oom; - free(p); - dbus_message_unref(m); - return; oom: - free(p); + log_oom(); +} + +DBusHandlerResult bus_unit_queue_job( + DBusConnection *connection, + DBusMessage *message, + Unit *u, + JobType type, + JobMode mode, + bool reload_if_possible) { + + _cleanup_dbus_message_unref_ DBusMessage *reply = NULL; + _cleanup_free_ char *path = NULL; + Job *j; + JobBusClient *cl; + DBusError error; + int r; + + assert(connection); + assert(message); + assert(u); + assert(type >= 0 && type < _JOB_TYPE_MAX); + assert(mode >= 0 && mode < _JOB_MODE_MAX); + + dbus_error_init(&error); + + if (reload_if_possible && unit_can_reload(u)) { + if (type == JOB_RESTART) + type = JOB_RELOAD_OR_START; + else if (type == JOB_TRY_RESTART) + type = JOB_RELOAD; + } + + SELINUX_UNIT_ACCESS_CHECK(u, connection, message, + (type == JOB_START || type == JOB_RESTART || type == JOB_TRY_RESTART) ? "start" : + type == JOB_STOP ? "stop" : "reload"); - if (m) - dbus_message_unref(m); + if (type == JOB_STOP && u->load_state == UNIT_ERROR && unit_active_state(u) == UNIT_INACTIVE) { + dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s not loaded.", u->id); + return bus_send_error_reply(connection, message, &error, -EPERM); + } + + if ((type == JOB_START && u->refuse_manual_start) || + (type == JOB_STOP && u->refuse_manual_stop) || + ((type == JOB_RESTART || type == JOB_TRY_RESTART) && (u->refuse_manual_start || u->refuse_manual_stop))) { + dbus_set_error(&error, BUS_ERROR_ONLY_BY_DEPENDENCY, + "Operation refused, unit %s may be requested by dependency only.", u->id); + return bus_send_error_reply(connection, message, &error, -EPERM); + } - log_error("Failed to allocate unit remove signal."); + r = manager_add_job(u->manager, type, u, mode, true, &error, &j); + if (r < 0) + return bus_send_error_reply(connection, message, &error, r); + + cl = job_bus_client_new(connection, bus_message_get_sender_with_fallback(message)); + if (!cl) + goto oom; + + LIST_PREPEND(JobBusClient, client, j->bus_client_list, cl); + + reply = dbus_message_new_method_return(message); + if (!reply) + goto oom; + + path = job_dbus_path(j); + if (!path) + goto oom; + + if (!dbus_message_append_args( + reply, + DBUS_TYPE_OBJECT_PATH, &path, + DBUS_TYPE_INVALID)) + goto oom; + + if (!dbus_connection_send(connection, reply, NULL)) + goto oom; + + return DBUS_HANDLER_RESULT_HANDLED; + +oom: + dbus_error_free(&error); + + return DBUS_HANDLER_RESULT_NEED_MEMORY; } const BusProperty bus_unit_properties[] = { diff --git a/src/core/dbus-unit.h b/src/core/dbus-unit.h index 7ab355c279..ac6785a949 100644 --- a/src/core/dbus-unit.h +++ b/src/core/dbus-unit.h @@ -133,6 +133,15 @@ extern const BusProperty bus_unit_properties[]; void bus_unit_send_change_signal(Unit *u); void bus_unit_send_removed_signal(Unit *u); + +DBusHandlerResult bus_unit_queue_job( + DBusConnection *connection, + DBusMessage *message, + Unit *u, + JobType type, + JobMode mode, + bool reload_if_possible); + extern const DBusObjectPathVTable bus_unit_vtable; extern const char bus_unit_interface[]; diff --git a/src/core/dbus.c b/src/core/dbus.c index 9db172b6e6..2a1c66054a 100644 --- a/src/core/dbus.c +++ b/src/core/dbus.c @@ -30,6 +30,7 @@ #include "strv.h" #include "cgroup.h" #include "mkdir.h" +#include "missing.h" #include "dbus-unit.h" #include "dbus-job.h" #include "dbus-manager.h" @@ -444,7 +445,7 @@ static DBusHandlerResult system_bus_message_filter(DBusConnection *connection, D log_debug("System D-Bus connection terminated."); bus_done_system(m); - } else if (m->running_as != MANAGER_SYSTEM && + } else if (m->running_as != SYSTEMD_SYSTEM && dbus_message_is_signal(message, "org.freedesktop.systemd1.Agent", "Released")) { const char *cgroup; @@ -480,7 +481,7 @@ static DBusHandlerResult private_bus_message_filter(DBusConnection *connection, if (dbus_message_is_signal(message, DBUS_INTERFACE_LOCAL, "Disconnected")) shutdown_connection(m, connection); - else if (m->running_as == MANAGER_SYSTEM && + else if (m->running_as == SYSTEMD_SYSTEM && dbus_message_is_signal(message, "org.freedesktop.systemd1.Agent", "Released")) { const char *cgroup; @@ -775,7 +776,7 @@ static int init_registered_system_bus(Manager *m) { if (!dbus_connection_add_filter(m->system_bus, system_bus_message_filter, m, NULL)) return log_oom(); - if (m->running_as != MANAGER_SYSTEM) { + if (m->running_as != SYSTEMD_SYSTEM) { DBusError error; dbus_error_init(&error); @@ -837,7 +838,7 @@ static int init_registered_api_bus(Manager *m) { if (r < 0) return r; - if (m->running_as == MANAGER_USER) { + if (m->running_as == SYSTEMD_USER) { char *id; log_debug("Successfully connected to API D-Bus bus %s as %s", strnull((id = dbus_connection_get_server_id(m->api_bus))), @@ -888,7 +889,7 @@ static void bus_register_cb(DBusPendingCall *pending, void *userdata) { if (conn == &m->system_bus) { r = init_registered_system_bus(m); - if (r == 0 && m->running_as == MANAGER_SYSTEM) + if (r == 0 && m->running_as == SYSTEMD_SYSTEM) r = init_registered_api_bus(m); } else r = init_registered_api_bus(m); @@ -955,12 +956,12 @@ static DBusConnection* manager_bus_connect_private(Manager *m, DBusBusType type) switch (type) { case DBUS_BUS_SYSTEM: - address = getenv("DBUS_SYSTEM_BUS_ADDRESS"); + address = secure_getenv("DBUS_SYSTEM_BUS_ADDRESS"); if (!address || !address[0]) address = DBUS_SYSTEM_BUS_DEFAULT_ADDRESS; break; case DBUS_BUS_SESSION: - address = getenv("DBUS_SESSION_BUS_ADDRESS"); + address = secure_getenv("DBUS_SESSION_BUS_ADDRESS"); if (!address || !address[0]) address = DBUS_SESSION_BUS_DEFAULT_ADDRESS; break; @@ -1018,7 +1019,7 @@ static int bus_init_api(Manager *m) { if (m->api_bus) return 0; - if (m->running_as == MANAGER_SYSTEM) { + if (m->running_as == SYSTEMD_SYSTEM) { m->api_bus = m->system_bus; /* In this mode there is no distinct connection to the API bus, * the API is published on the system bus. @@ -1065,7 +1066,7 @@ static int bus_init_private(Manager *m) { if (m->private_bus) return 0; - if (m->running_as == MANAGER_SYSTEM) { + if (m->running_as == SYSTEMD_SYSTEM) { /* We want the private bus only when running as init */ if (getpid() != 1) @@ -1077,7 +1078,7 @@ static int bus_init_private(Manager *m) { const char *e; char *p; - e = getenv("XDG_RUNTIME_DIR"); + e = secure_getenv("XDG_RUNTIME_DIR"); if (!e) return 0; @@ -1189,7 +1190,7 @@ static void shutdown_connection(Manager *m, DBusConnection *c) { dbus_connection_set_dispatch_status_function(c, NULL, NULL, NULL); /* system manager cannot afford to block on DBus */ - if (m->running_as != MANAGER_SYSTEM) + if (m->running_as != SYSTEMD_SYSTEM) dbus_connection_flush(c); dbus_connection_close(c); dbus_connection_unref(c); @@ -1199,7 +1200,7 @@ static void bus_done_api(Manager *m) { if (!m->api_bus) return; - if (m->running_as == MANAGER_USER) + if (m->running_as == SYSTEMD_USER) shutdown_connection(m, m->api_bus); m->api_bus = NULL; @@ -1214,7 +1215,7 @@ static void bus_done_system(Manager *m) { if (!m->system_bus) return; - if (m->running_as == MANAGER_SYSTEM) + if (m->running_as == SYSTEMD_SYSTEM) bus_done_api(m); shutdown_connection(m, m->system_bus); @@ -1361,11 +1362,11 @@ int bus_broadcast(Manager *m, DBusMessage *message) { assert(message); SET_FOREACH(c, m->bus_connections_for_dispatch, i) - if (c != m->system_bus || m->running_as == MANAGER_SYSTEM) + if (c != m->system_bus || m->running_as == SYSTEMD_SYSTEM) oom = !dbus_connection_send(c, message, NULL); SET_FOREACH(c, m->bus_connections, i) - if (c != m->system_bus || m->running_as == MANAGER_SYSTEM) + if (c != m->system_bus || m->running_as == SYSTEMD_SYSTEM) oom = !dbus_connection_send(c, message, NULL); return oom ? -ENOMEM : 0; @@ -1436,6 +1437,8 @@ int bus_fdset_add_all(Manager *m, FDSet *fds) { void bus_broadcast_finished( Manager *m, + usec_t firmware_usec, + usec_t loader_usec, usec_t kernel_usec, usec_t initrd_usec, usec_t userspace_usec, @@ -1453,6 +1456,8 @@ void bus_broadcast_finished( assert_cc(sizeof(usec_t) == sizeof(uint64_t)); if (!dbus_message_append_args(message, + DBUS_TYPE_UINT64, &firmware_usec, + DBUS_TYPE_UINT64, &loader_usec, DBUS_TYPE_UINT64, &kernel_usec, DBUS_TYPE_UINT64, &initrd_usec, DBUS_TYPE_UINT64, &userspace_usec, diff --git a/src/core/dbus.h b/src/core/dbus.h index 0502d7c251..c7a058e198 100644 --- a/src/core/dbus.h +++ b/src/core/dbus.h @@ -42,7 +42,7 @@ bool bus_connection_has_subscriber(Manager *m, DBusConnection *c); int bus_fdset_add_all(Manager *m, FDSet *fds); -void bus_broadcast_finished(Manager *m, usec_t kernel_usec, usec_t initrd_usec, usec_t userspace_usec, usec_t total_usec); +void bus_broadcast_finished(Manager *m, usec_t firmware_usec, usec_t loader_usec, usec_t kernel_usec, usec_t initrd_usec, usec_t userspace_usec, usec_t total_usec); #define BUS_CONNECTION_SUBSCRIBED(m, c) dbus_connection_get_data((c), (m)->subscribed_data_slot) #define BUS_PENDING_CALL_NAME(m, p) dbus_pending_call_get_data((p), (m)->name_data_slot) diff --git a/src/core/device.c b/src/core/device.c index 5307341d70..0b01718ad4 100644 --- a/src/core/device.c +++ b/src/core/device.c @@ -92,10 +92,10 @@ static void device_set_state(Device *d, DeviceState state) { d->state = state; if (state != old_state) - log_debug("%s changed %s -> %s", - UNIT(d)->id, - device_state_to_string(old_state), - device_state_to_string(state)); + log_debug_unit(UNIT(d)->id, + "%s changed %s -> %s", UNIT(d)->id, + device_state_to_string(old_state), + device_state_to_string(state)); unit_notify(UNIT(d), state_translation_table[old_state], state_translation_table[state], true); } @@ -251,30 +251,46 @@ static int device_update_unit(Manager *m, struct udev_device *dev, const char *p * interpret for the main object */ const char *wants, *alias; - if ((alias = udev_device_get_property_value(dev, "SYSTEMD_ALIAS"))) { - if (!is_path(alias)) - log_warning("SYSTEMD_ALIAS for %s is not a path, ignoring: %s", sysfs, alias); - else { - if ((r = device_add_escaped_name(u, alias)) < 0) + alias = udev_device_get_property_value(dev, "SYSTEMD_ALIAS"); + if (alias) { + char *state, *w; + size_t l; + + FOREACH_WORD_QUOTED(w, l, alias, state) { + char *e; + + e = strndup(w, l); + if (!e) { + r = -ENOMEM; goto fail; + } + + if (!is_path(e)) { + log_warning("SYSTEMD_ALIAS for %s is not a path, ignoring: %s", sysfs, e); + free(e); + } else { + device_update_unit(m, dev, e, false); + free(e); + } } } - if ((wants = udev_device_get_property_value(dev, "SYSTEMD_WANTS"))) { + wants = udev_device_get_property_value(dev, "SYSTEMD_WANTS"); + if (wants) { char *state, *w; size_t l; FOREACH_WORD_QUOTED(w, l, wants, state) { char *e; - if (!(e = strndup(w, l))) { + e = strndup(w, l); + if (!e) { r = -ENOMEM; goto fail; } r = unit_add_dependency_by_name(u, UNIT_WANTS, e, NULL, true); free(e); - if (r < 0) goto fail; } diff --git a/src/core/execute.c b/src/core/execute.c index fc0edc6cfd..7dc15044b4 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -39,6 +39,7 @@ #include <linux/oom.h> #include <sys/poll.h> #include <linux/seccomp-bpf.h> +#include <glob.h> #ifdef HAVE_PAM #include <security/pam_appl.h> @@ -50,6 +51,7 @@ #include "capability.h" #include "util.h" #include "log.h" +#include "sd-messages.h" #include "ioprio.h" #include "securebits.h" #include "cgroup.h" @@ -999,7 +1001,7 @@ int exec_spawn(ExecCommand *command, int r; char *line; int socket_fd; - char **files_env = NULL; + char _cleanup_strv_free_ **files_env = NULL; assert(command); assert(context); @@ -1020,8 +1022,13 @@ int exec_spawn(ExecCommand *command, } else socket_fd = -1; - if ((r = exec_context_load_environment(context, &files_env)) < 0) { - log_error("Failed to load environment files: %s", strerror(-r)); + r = exec_context_load_environment(context, &files_env); + if (r < 0) { + log_struct(LOG_ERR, + "UNIT=%s", unit_id, + "MESSAGE=Failed to load environment files: %s", strerror(-r), + "ERRNO=%d", -r, + NULL); return r; } @@ -1029,24 +1036,24 @@ int exec_spawn(ExecCommand *command, argv = command->argv; line = exec_command_line(argv); - if (!line) { - r = -ENOMEM; - goto fail_parent; - } + if (!line) + return log_oom(); - log_debug("About to execute: %s", line); + log_struct(LOG_DEBUG, + "UNIT=%s", unit_id, + "MESSAGE=About to execute %s", line, + NULL); free(line); r = cgroup_bonding_realize_list(cgroup_bondings); if (r < 0) - goto fail_parent; + return r; cgroup_attribute_apply_list(cgroup_attributes, cgroup_bondings); - if ((pid = fork()) < 0) { - r = -errno; - goto fail_parent; - } + pid = fork(); + if (pid < 0) + return -errno; if (pid == 0) { int i, err; @@ -1054,7 +1061,8 @@ int exec_spawn(ExecCommand *command, const char *username = NULL, *home = NULL; uid_t uid = (uid_t) -1; gid_t gid = (gid_t) -1; - char **our_env = NULL, **pam_env = NULL, **final_env = NULL, **final_argv = NULL; + char _cleanup_strv_free_ **our_env = NULL, **pam_env = NULL, + **final_env = NULL, **final_argv = NULL; unsigned n_env = 0; bool set_access = false; @@ -1283,7 +1291,7 @@ int exec_spawn(ExecCommand *command, umask(context->umask); #ifdef HAVE_PAM - if (context->pam_name && username) { + if (apply_permissions && context->pam_name && username) { err = setup_pam(context->pam_name, username, uid, context->tty_path, &pam_env, fds, n_fds); if (err < 0) { r = EXIT_PAM; @@ -1304,7 +1312,7 @@ int exec_spawn(ExecCommand *command, if (strv_length(context->read_write_dirs) > 0 || strv_length(context->read_only_dirs) > 0 || strv_length(context->inaccessible_dirs) > 0 || - context->mount_flags != MS_SHARED || + context->mount_flags != 0 || context->private_tmp) { err = setup_namespace(context->read_write_dirs, context->read_only_dirs, @@ -1331,8 +1339,7 @@ int exec_spawn(ExecCommand *command, goto fail_child; } } else { - - char *d; + char _cleanup_free_ *d = NULL; if (asprintf(&d, "%s/%s", context->root_directory ? context->root_directory : "", @@ -1344,12 +1351,9 @@ int exec_spawn(ExecCommand *command, if (chdir(d) < 0) { err = -errno; - free(d); r = EXIT_CHDIR; goto fail_child; } - - free(d); } /* We repeat the fd closing here, to make sure that @@ -1495,21 +1499,24 @@ int exec_spawn(ExecCommand *command, fail_child: if (r != 0) { log_open(); - log_warning("Failed at step %s spawning %s: %s", - exit_status_to_string(r, EXIT_STATUS_SYSTEMD), - command->path, strerror(-err)); + log_struct(LOG_ERR, MESSAGE_ID(SD_MESSAGE_SPAWN_FAILED), + "EXECUTABLE=%s", command->path, + "MESSAGE=Failed at step %s spawning %s: %s", + exit_status_to_string(r, EXIT_STATUS_SYSTEMD), + command->path, strerror(-err), + "ERRNO=%d", -err, + NULL); + log_close(); } - strv_free(our_env); - strv_free(final_env); - strv_free(pam_env); - strv_free(files_env); - strv_free(final_argv); - _exit(r); } - strv_free(files_env); + log_struct(LOG_DEBUG, + "UNIT=%s", unit_id, + "MESSAGE=Forked %s as %lu", + command->path, (unsigned long) pid, + NULL); /* We add the new process to the cgroup both in the child (so * that we can be sure that no user code is ever executed @@ -1519,17 +1526,10 @@ int exec_spawn(ExecCommand *command, if (cgroup_bondings) cgroup_bonding_install_list(cgroup_bondings, pid, cgroup_suffix); - log_debug("Forked %s as %lu", command->path, (unsigned long) pid); - exec_status_start(&command->exec_status, pid); *ret = pid; return 0; - -fail_parent: - strv_free(files_env); - - return r; } void exec_context_init(ExecContext *c) { @@ -1540,7 +1540,6 @@ void exec_context_init(ExecContext *c) { c->cpu_sched_policy = SCHED_OTHER; c->syslog_priority = LOG_DAEMON|LOG_INFO; c->syslog_level_prefix = true; - c->mount_flags = MS_SHARED; c->control_group_persistent = -1; c->ignore_sigpipe = true; c->timer_slack_nsec = (nsec_t) -1; @@ -1659,6 +1658,8 @@ int exec_context_load_environment(const ExecContext *c, char ***l) { int k; bool ignore = false; char **p; + glob_t pglob; + int count, n; fn = *i; @@ -1676,29 +1677,55 @@ int exec_context_load_environment(const ExecContext *c, char ***l) { return -EINVAL; } - if ((k = load_env_file(fn, &p)) < 0) { + /* Filename supports globbing, take all matching files */ + zero(pglob); + errno = 0; + if (glob(fn, 0, NULL, &pglob) != 0) { + globfree(&pglob); + if (ignore) + continue; + strv_free(r); + return errno ? -errno : -EINVAL; + } + count = pglob.gl_pathc; + if (count == 0) { + globfree(&pglob); if (ignore) continue; strv_free(r); - return k; + return -EINVAL; } + for (n = 0; n < count; n++) { + k = load_env_file(pglob.gl_pathv[n], &p); + if (k < 0) { + if (ignore) + continue; - if (r == NULL) - r = p; - else { - char **m; + strv_free(r); + globfree(&pglob); + return k; + } - m = strv_env_merge(2, r, p); - strv_free(r); - strv_free(p); + if (r == NULL) + r = p; + else { + char **m; + + m = strv_env_merge(2, r, p); + strv_free(r); + strv_free(p); - if (!m) - return -ENOMEM; + if (!m) { + globfree(&pglob); + return -ENOMEM; + } - r = m; + r = m; + } } + globfree(&pglob); } *l = r; @@ -1770,21 +1797,37 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) { if (c->rlimit[i]) fprintf(f, "%s%s: %llu\n", prefix, rlimit_to_string(i), (unsigned long long) c->rlimit[i]->rlim_max); - if (c->ioprio_set) + if (c->ioprio_set) { + char *class_str; + int r; + + r = ioprio_class_to_string_alloc(IOPRIO_PRIO_CLASS(c->ioprio), &class_str); + if (r < 0) + class_str = NULL; fprintf(f, "%sIOSchedulingClass: %s\n" "%sIOPriority: %i\n", - prefix, ioprio_class_to_string(IOPRIO_PRIO_CLASS(c->ioprio)), + prefix, strna(class_str), prefix, (int) IOPRIO_PRIO_DATA(c->ioprio)); + free(class_str); + } - if (c->cpu_sched_set) + if (c->cpu_sched_set) { + char *policy_str; + int r; + + r = sched_policy_to_string_alloc(c->cpu_sched_policy, &policy_str); + if (r < 0) + policy_str = NULL; fprintf(f, "%sCPUSchedulingPolicy: %s\n" "%sCPUSchedulingPriority: %i\n" "%sCPUSchedulingResetOnFork: %s\n", - prefix, sched_policy_to_string(c->cpu_sched_policy), + prefix, strna(policy_str), prefix, c->cpu_sched_priority, prefix, yes_no(c->cpu_sched_reset_on_fork)); + free(policy_str); + } if (c->cpuset) { fprintf(f, "%sCPUAffinity:", prefix); @@ -1819,12 +1862,26 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) { if (c->std_output == EXEC_OUTPUT_SYSLOG || c->std_output == EXEC_OUTPUT_KMSG || c->std_output == EXEC_OUTPUT_JOURNAL || c->std_output == EXEC_OUTPUT_SYSLOG_AND_CONSOLE || c->std_output == EXEC_OUTPUT_KMSG_AND_CONSOLE || c->std_output == EXEC_OUTPUT_JOURNAL_AND_CONSOLE || c->std_error == EXEC_OUTPUT_SYSLOG || c->std_error == EXEC_OUTPUT_KMSG || c->std_error == EXEC_OUTPUT_JOURNAL || - c->std_error == EXEC_OUTPUT_SYSLOG_AND_CONSOLE || c->std_error == EXEC_OUTPUT_KMSG_AND_CONSOLE || c->std_error == EXEC_OUTPUT_JOURNAL_AND_CONSOLE) + c->std_error == EXEC_OUTPUT_SYSLOG_AND_CONSOLE || c->std_error == EXEC_OUTPUT_KMSG_AND_CONSOLE || c->std_error == EXEC_OUTPUT_JOURNAL_AND_CONSOLE) { + char *fac_str, *lvl_str; + int r; + + r = log_facility_unshifted_to_string_alloc(c->syslog_priority >> 3, &fac_str); + if (r < 0) + fac_str = NULL; + + r = log_level_to_string_alloc(LOG_PRI(c->syslog_priority), &lvl_str); + if (r < 0) + lvl_str = NULL; + fprintf(f, "%sSyslogFacility: %s\n" "%sSyslogLevel: %s\n", - prefix, log_facility_unshifted_to_string(c->syslog_priority >> 3), - prefix, log_level_to_string(LOG_PRI(c->syslog_priority))); + prefix, strna(fac_str), + prefix, strna(lvl_str)); + free(lvl_str); + free(fac_str); + } if (c->capabilities) { char *t; diff --git a/src/core/fdset.c b/src/core/fdset.c deleted file mode 100644 index fe918cd8cc..0000000000 --- a/src/core/fdset.c +++ /dev/null @@ -1,167 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -/*** - 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 <dirent.h> -#include <fcntl.h> -#include <unistd.h> - -#include "set.h" -#include "util.h" -#include "macro.h" -#include "fdset.h" - -#define MAKE_SET(s) ((Set*) s) -#define MAKE_FDSET(s) ((FDSet*) s) - -/* Make sure we can distuingish fd 0 and NULL */ -#define FD_TO_PTR(fd) INT_TO_PTR((fd)+1) -#define PTR_TO_FD(p) (PTR_TO_INT(p)-1) - -FDSet *fdset_new(void) { - return MAKE_FDSET(set_new(trivial_hash_func, trivial_compare_func)); -} - -void fdset_free(FDSet *s) { - void *p; - - while ((p = set_steal_first(MAKE_SET(s)))) { - /* Valgrind's fd might have ended up in this set here, - * due to fdset_new_fill(). We'll ignore all failures - * here, so that the EBADFD that valgrind will return - * us on close() doesn't influence us */ - - /* When reloading duplicates of the private bus - * connection fds and suchlike are closed here, which - * has no effect at all, since they are only - * duplicates. So don't be surprised about these log - * messages. */ - - log_debug("Closing left-over fd %i", PTR_TO_FD(p)); - close_nointr(PTR_TO_FD(p)); - } - - set_free(MAKE_SET(s)); -} - -int fdset_put(FDSet *s, int fd) { - assert(s); - assert(fd >= 0); - - return set_put(MAKE_SET(s), FD_TO_PTR(fd)); -} - -int fdset_put_dup(FDSet *s, int fd) { - int copy, r; - - assert(s); - assert(fd >= 0); - - if ((copy = fcntl(fd, F_DUPFD_CLOEXEC, 3)) < 0) - return -errno; - - if ((r = fdset_put(s, copy)) < 0) { - close_nointr_nofail(copy); - return r; - } - - return copy; -} - -bool fdset_contains(FDSet *s, int fd) { - assert(s); - assert(fd >= 0); - - return !!set_get(MAKE_SET(s), FD_TO_PTR(fd)); -} - -int fdset_remove(FDSet *s, int fd) { - assert(s); - assert(fd >= 0); - - return set_remove(MAKE_SET(s), FD_TO_PTR(fd)) ? fd : -ENOENT; -} - -int fdset_new_fill(FDSet **_s) { - DIR *d; - struct dirent *de; - int r = 0; - FDSet *s; - - assert(_s); - - /* Creates an fdsets and fills in all currently open file - * descriptors. */ - - if (!(d = opendir("/proc/self/fd"))) - return -errno; - - if (!(s = fdset_new())) { - r = -ENOMEM; - goto finish; - } - - while ((de = readdir(d))) { - int fd = -1; - - if (ignore_file(de->d_name)) - continue; - - if ((r = safe_atoi(de->d_name, &fd)) < 0) - goto finish; - - if (fd < 3) - continue; - - if (fd == dirfd(d)) - continue; - - if ((r = fdset_put(s, fd)) < 0) - goto finish; - } - - r = 0; - *_s = s; - s = NULL; - -finish: - closedir(d); - - /* We won't close the fds here! */ - if (s) - set_free(MAKE_SET(s)); - - return r; -} - -int fdset_cloexec(FDSet *fds, bool b) { - Iterator i; - void *p; - int r; - - assert(fds); - - SET_FOREACH(p, MAKE_SET(fds), i) - if ((r = fd_cloexec(PTR_TO_FD(p), b)) < 0) - return r; - - return 0; -} diff --git a/src/core/hostname-setup.c b/src/core/hostname-setup.c index 25ea09c733..7894f8a5f2 100644 --- a/src/core/hostname-setup.c +++ b/src/core/hostname-setup.c @@ -30,16 +30,6 @@ #include "util.h" #include "log.h" -#if defined(TARGET_FEDORA) || defined(TARGET_ALTLINUX) || defined(TARGET_MANDRIVA) || defined(TARGET_MAGEIA) -#define FILENAME "/etc/sysconfig/network" -#elif defined(TARGET_SUSE) || defined(TARGET_SLACKWARE) -#define FILENAME "/etc/HOSTNAME" -#elif defined(TARGET_ARCH) -#define FILENAME "/etc/rc.conf" -#elif defined(TARGET_GENTOO) -#define FILENAME "/etc/conf.d/hostname" -#endif - static int read_and_strip_hostname(const char *path, char **hn) { char *s; int r; @@ -59,104 +49,23 @@ static int read_and_strip_hostname(const char *path, char **hn) { } *hn = s; - - return 0; -} - -static int read_distro_hostname(char **hn) { - -#if defined(TARGET_FEDORA) || defined(TARGET_ARCH) || defined(TARGET_GENTOO) || defined(TARGET_ALTLINUX) || defined(TARGET_MANDRIVA) || defined(TARGET_MAGEIA) - int r; - FILE *f; - - assert(hn); - - f = fopen(FILENAME, "re"); - if (!f) - return -errno; - - for (;;) { - char line[LINE_MAX]; - char *s, *k; - - if (!fgets(line, sizeof(line), f)) { - if (feof(f)) - break; - - r = -errno; - goto finish; - } - - s = strstrip(line); - - if (!startswith_no_case(s, "HOSTNAME=")) - continue; - - k = strdup(s+9); - if (!k) { - r = -ENOMEM; - goto finish; - } - - hostname_cleanup(k); - - if (isempty(k)) { - free(k); - r = -ENOENT; - goto finish; - } - - *hn = k; - r = 0; - goto finish; - } - - r = -ENOENT; - -finish: - fclose(f); - return r; - -#elif defined(TARGET_SUSE) || defined(TARGET_SLACKWARE) - return read_and_strip_hostname(FILENAME, hn); -#else - return -ENOENT; -#endif -} - -static int read_hostname(char **hn) { - int r; - - assert(hn); - - /* First, try to load the generic hostname configuration file, - * that we support on all distributions */ - - r = read_and_strip_hostname("/etc/hostname", hn); - if (r < 0) { - if (r == -ENOENT) - return read_distro_hostname(hn); - - return r; - } - return 0; } int hostname_setup(void) { int r; - char *b = NULL; - const char *hn = NULL; + _cleanup_free_ char *b = NULL; + const char *hn; bool enoent = false; - r = read_hostname(&b); + r = read_and_strip_hostname("/etc/hostname", &b); if (r < 0) { - hn = NULL; - if (r == -ENOENT) enoent = true; else log_warning("Failed to read configured hostname: %s", strerror(-r)); + + hn = NULL; } else hn = b; @@ -164,7 +73,7 @@ int hostname_setup(void) { /* Don't override the hostname if it is already set * and not explicitly configured */ if (hostname_is_set()) - goto finish; + return 0; if (enoent) log_info("No hostname configured."); @@ -174,12 +83,9 @@ int hostname_setup(void) { if (sethostname(hn, strlen(hn)) < 0) { log_warning("Failed to set hostname to <%s>: %m", hn); - r = -errno; - } else - log_info("Set hostname to <%s>.", hn); - -finish: - free(b); + return -errno; + } - return r; + log_info("Set hostname to <%s>.", hn); + return 0; } diff --git a/src/core/job.c b/src/core/job.c index 8d51aa3fdd..5ff95f5c90 100644 --- a/src/core/job.c +++ b/src/core/job.c @@ -24,6 +24,8 @@ #include <sys/timerfd.h> #include <sys/epoll.h> +#include "systemd/sd-id128.h" +#include "systemd/sd-messages.h" #include "set.h" #include "unit.h" #include "macro.h" @@ -178,15 +180,16 @@ Job* job_install(Job *j) { if (uj) { if (j->type != JOB_NOP && job_type_is_conflicting(uj->type, j->type)) - job_finish_and_invalidate(uj, JOB_CANCELED, true); + job_finish_and_invalidate(uj, JOB_CANCELED, false); else { /* not conflicting, i.e. mergeable */ if (j->type == JOB_NOP || uj->state == JOB_WAITING || (job_type_allows_late_merge(j->type) && job_type_is_superset(uj->type, j->type))) { job_merge_into_installed(uj, j); - log_debug("Merged into installed job %s/%s as %u", - uj->unit->id, job_type_to_string(uj->type), (unsigned) uj->id); + log_debug_unit(uj->unit->id, + "Merged into installed job %s/%s as %u", + uj->unit->id, job_type_to_string(uj->type), (unsigned) uj->id); return uj; } else { /* already running and not safe to merge into */ @@ -194,8 +197,9 @@ Job* job_install(Job *j) { /* XXX It should be safer to queue j to run after uj finishes, but it is * not currently possible to have more than one installed job per unit. */ job_merge_into_installed(uj, j); - log_debug("Merged into running job, re-running: %s/%s as %u", - uj->unit->id, job_type_to_string(uj->type), (unsigned) uj->id); + log_debug_unit(uj->unit->id, + "Merged into running job, re-running: %s/%s as %u", + uj->unit->id, job_type_to_string(uj->type), (unsigned) uj->id); uj->state = JOB_WAITING; return uj; } @@ -206,7 +210,9 @@ Job* job_install(Job *j) { *pj = j; j->installed = true; j->manager->n_installed_jobs ++; - log_debug("Installed new job %s/%s as %u", j->unit->id, job_type_to_string(j->type), (unsigned) j->id); + log_debug_unit(j->unit->id, + "Installed new job %s/%s as %u", + j->unit->id, job_type_to_string(j->type), (unsigned) j->id); return j; } @@ -223,12 +229,16 @@ int job_install_deserialized(Job *j) { pj = (j->type == JOB_NOP) ? &j->unit->nop_job : &j->unit->job; if (*pj) { - log_debug("Unit %s already has a job installed. Not installing deserialized job.", j->unit->id); + log_debug_unit(j->unit->id, + "Unit %s already has a job installed. Not installing deserialized job.", + j->unit->id); return -EEXIST; } *pj = j; j->installed = true; - log_debug("Reinstalled deserialized job %s/%s as %u", j->unit->id, job_type_to_string(j->type), (unsigned) j->id); + log_debug_unit(j->unit->id, + "Reinstalled deserialized job %s/%s as %u", + j->unit->id, job_type_to_string(j->type), (unsigned) j->id); return 0; } @@ -455,9 +465,10 @@ bool job_is_runnable(Job *j) { } static void job_change_type(Job *j, JobType newtype) { - log_debug("Converting job %s/%s -> %s/%s", - j->unit->id, job_type_to_string(j->type), - j->unit->id, job_type_to_string(newtype)); + log_debug_unit(j->unit->id, + "Converting job %s/%s -> %s/%s", + j->unit->id, job_type_to_string(j->type), + j->unit->id, job_type_to_string(newtype)); j->type = newtype; } @@ -549,17 +560,74 @@ int job_run_and_invalidate(Job *j) { return r; } -static void job_print_status_message(Unit *u, JobType t, JobResult result) { +static const char *job_get_status_message_format(Unit *u, JobType t, JobResult result) { const UnitStatusMessageFormats *format_table; - const char *format; + + assert(u); + assert(t >= 0); + assert(t < _JOB_TYPE_MAX); format_table = &UNIT_VTABLE(u)->status_message_formats; if (!format_table) - return; + return NULL; + if (t == JOB_START) + return format_table->finished_start_job[result]; + else if (t == JOB_STOP || t == JOB_RESTART) + return format_table->finished_stop_job[result]; + + return NULL; +} + +static const char *job_get_status_message_format_try_harder(Unit *u, JobType t, JobResult result) { + const char *format; + + assert(u); + assert(t >= 0); + assert(t < _JOB_TYPE_MAX); + + format = job_get_status_message_format(u, t, result); + if (format) + return format; + + /* Return generic strings */ if (t == JOB_START) { + if (result == JOB_DONE) + return "Started %s."; + else if (result == JOB_FAILED) + return "Failed to start %s."; + else if (result == JOB_DEPENDENCY) + return "Dependency failed for %s."; + else if (result == JOB_TIMEOUT) + return "Timed out starting %s."; + } else if (t == JOB_STOP || t == JOB_RESTART) { + if (result == JOB_DONE) + return "Stopped %s."; + else if (result == JOB_FAILED) + return "Stopped (with error) %s."; + else if (result == JOB_TIMEOUT) + return "Timed out stoppping %s."; + } else if (t == JOB_RELOAD) { + if (result == JOB_DONE) + return "Reloaded %s."; + else if (result == JOB_FAILED) + return "Reload failed for %s."; + else if (result == JOB_TIMEOUT) + return "Timed out reloading %s."; + } + + return NULL; +} - format = format_table->finished_start_job[result]; +static void job_print_status_message(Unit *u, JobType t, JobResult result) { + const char *format; + + assert(u); + assert(t >= 0); + assert(t < _JOB_TYPE_MAX); + + if (t == JOB_START) { + format = job_get_status_message_format(u, t, result); if (!format) return; @@ -572,7 +640,7 @@ static void job_print_status_message(Unit *u, JobType t, JobResult result) { case JOB_FAILED: unit_status_printf(u, ANSI_HIGHLIGHT_RED_ON "FAILED" ANSI_HIGHLIGHT_OFF, format, unit_description(u)); - unit_status_printf(u, "", "See 'systemctl status %s' for details.", u->id); + unit_status_printf(u, NULL, "See 'systemctl status %s' for details.", u->id); break; case JOB_DEPENDENCY: @@ -589,7 +657,7 @@ static void job_print_status_message(Unit *u, JobType t, JobResult result) { } else if (t == JOB_STOP || t == JOB_RESTART) { - format = format_table->finished_stop_job[result]; + format = job_get_status_message_format(u, t, result); if (!format) return; @@ -618,6 +686,58 @@ static void job_print_status_message(Unit *u, JobType t, JobResult result) { } } +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +static void job_log_status_message(Unit *u, JobType t, JobResult result) { + const char *format; + char buf[LINE_MAX]; + + assert(u); + assert(t >= 0); + assert(t < _JOB_TYPE_MAX); + + /* Skip this if it goes to the console. since we already print + * to the console anyway... */ + + if (log_on_console()) + return; + + format = job_get_status_message_format_try_harder(u, t, result); + if (!format) + return; + + snprintf(buf, sizeof(buf), format, unit_description(u)); + char_array_0(buf); + + if (t == JOB_START) { + sd_id128_t mid; + + mid = result == JOB_DONE ? SD_MESSAGE_UNIT_STARTED : SD_MESSAGE_UNIT_FAILED; + log_struct(result == JOB_DONE ? LOG_INFO : LOG_ERR, + MESSAGE_ID(mid), + "UNIT=%s", u->id, + "RESULT=%s", job_result_to_string(result), + "MESSAGE=%s", buf, + NULL); + + } else if (t == JOB_STOP) + log_struct(result == JOB_DONE ? LOG_INFO : LOG_ERR, + MESSAGE_ID(SD_MESSAGE_UNIT_STOPPED), + "UNIT=%s", u->id, + "RESULT=%s", job_result_to_string(result), + "MESSAGE=%s", buf, + NULL); + + else if (t == JOB_RELOAD) + log_struct(result == JOB_DONE ? LOG_INFO : LOG_ERR, + MESSAGE_ID(SD_MESSAGE_UNIT_RELOADED), + "UNIT=%s", u->id, + "RESULT=%s", job_result_to_string(result), + "MESSAGE=%s", buf, + NULL); +} +#pragma GCC diagnostic pop + int job_finish_and_invalidate(Job *j, JobResult result, bool recursive) { Unit *u; Unit *other; @@ -633,9 +753,11 @@ int job_finish_and_invalidate(Job *j, JobResult result, bool recursive) { j->result = result; - log_debug("Job %s/%s finished, result=%s", u->id, job_type_to_string(t), job_result_to_string(result)); + log_debug_unit(u->id, "Job %s/%s finished, result=%s", + u->id, job_type_to_string(t), job_result_to_string(result)); job_print_status_message(u, t, result); + job_log_status_message(u, t, result); job_add_to_dbus_queue(j); @@ -692,14 +814,19 @@ int job_finish_and_invalidate(Job *j, JobResult result, bool recursive) { } /* Trigger OnFailure dependencies that are not generated by - * the unit itself. We don't tread JOB_CANCELED as failure in + * the unit itself. We don't treat JOB_CANCELED as failure in * this context. And JOB_FAILURE is already handled by the * unit itself. */ if (result == JOB_TIMEOUT || result == JOB_DEPENDENCY) { - log_notice("Job %s/%s failed with result '%s'.", + log_struct(LOG_NOTICE, + "UNIT=%s", u->id, + "JOB_TYPE=%s", job_type_to_string(t), + "JOB_RESULT=%s", job_result_to_string(t), + "Job %s/%s failed with result '%s'.", u->id, job_type_to_string(t), - job_result_to_string(result)); + job_result_to_string(result), + NULL); unit_trigger_on_failure(u); } @@ -806,7 +933,8 @@ void job_timer_event(Job *j, uint64_t n_elapsed, Watch *w) { assert(j); assert(w == &j->timer_watch); - log_warning("Job %s/%s timed out.", j->unit->id, job_type_to_string(j->type)); + log_warning_unit(j->unit->id, "Job %s/%s timed out.", + j->unit->id, job_type_to_string(j->type)); job_finish_and_invalidate(j, JOB_TIMEOUT, true); } diff --git a/src/core/job.h b/src/core/job.h index 349fb687cf..3aa49d4b46 100644 --- a/src/core/job.h +++ b/src/core/job.h @@ -91,12 +91,12 @@ enum JobMode { }; enum JobResult { - JOB_DONE, - JOB_CANCELED, - JOB_TIMEOUT, - JOB_FAILED, - JOB_DEPENDENCY, - JOB_SKIPPED, + JOB_DONE, /* Job completed successfully */ + JOB_CANCELED, /* Job canceled by a conflicting job installation or by explicit cancel request */ + JOB_TIMEOUT, /* JobTimeout elapsed */ + JOB_FAILED, /* Job failed */ + JOB_DEPENDENCY, /* A required dependency job did not result in JOB_DONE */ + JOB_SKIPPED, /* JOB_RELOAD of inactive unit; negative result of JOB_VERIFY_ACTIVE */ _JOB_RESULT_MAX, _JOB_RESULT_INVALID = -1 }; diff --git a/src/core/kmod-setup.c b/src/core/kmod-setup.c index cc2a2d94b2..20ab232374 100644 --- a/src/core/kmod-setup.c +++ b/src/core/kmod-setup.c @@ -30,10 +30,16 @@ #include "kmod-setup.h" -static const char * const kmod_table[] = { - "autofs4", "/sys/class/misc/autofs", - "ipv6", "/sys/module/ipv6", - "unix", "/proc/net/unix" +typedef struct Kmodule { + const char *name; + const char *directory; + bool (*condition_fn)(void); +} KModule; + +static const KModule kmod_table[] = { + { "autofs4", "/sys/class/misc/autofs", NULL } , + { "ipv6", "/sys/module/ipv6", NULL }, + { "unix", "/proc/net/unix", NULL } , }; #pragma GCC diagnostic push @@ -41,7 +47,8 @@ static const char * const kmod_table[] = { static void systemd_kmod_log(void *data, int priority, const char *file, int line, const char *fn, const char *format, va_list args) { - log_metav(priority, file, line, fn, format, args); + /* library logging is enabled at debug only */ + log_metav(LOG_DEBUG, file, line, fn, format, args); } #pragma GCC diagnostic pop @@ -52,13 +59,15 @@ int kmod_setup(void) { int err; for (i = 0; i < ELEMENTSOF(kmod_table); i += 2) { + if (kmod_table[i].condition_fn && !kmod_table[i].condition_fn()) + continue; - if (access(kmod_table[i+1], F_OK) >= 0) + if (access(kmod_table[i].directory, F_OK) >= 0) continue; log_debug("Your kernel apparently lacks built-in %s support. Might be a good idea to compile it in. " "We'll now try to work around this by loading the module...", - kmod_table[i]); + kmod_table[i].name); if (!ctx) { ctx = kmod_new(NULL, NULL); @@ -68,13 +77,12 @@ int kmod_setup(void) { } kmod_set_log_fn(ctx, systemd_kmod_log, NULL); - kmod_load_resources(ctx); } - err = kmod_module_new_from_name(ctx, kmod_table[i], &mod); + err = kmod_module_new_from_name(ctx, kmod_table[i].name, &mod); if (err < 0) { - log_error("Failed to load module '%s'", kmod_table[i]); + log_error("Failed to lookup module '%s'", kmod_table[i].name); continue; } @@ -84,7 +92,7 @@ int kmod_setup(void) { else if (err == KMOD_PROBE_APPLY_BLACKLIST) log_info("Module '%s' is blacklisted", kmod_module_get_name(mod)); else - log_error("Failed to insert '%s'", kmod_module_get_name(mod)); + log_error("Failed to insert module '%s'", kmod_module_get_name(mod)); kmod_module_unref(mod); } diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4 index 2b1cfa073c..7fba0cfb77 100644 --- a/src/core/load-fragment-gperf.gperf.m4 +++ b/src/core/load-fragment-gperf.gperf.m4 @@ -130,11 +130,14 @@ Unit.ConditionPathIsSymbolicLink,config_parse_unit_condition_path, CONDITION_P Unit.ConditionPathIsMountPoint, config_parse_unit_condition_path, CONDITION_PATH_IS_MOUNT_POINT, 0 Unit.ConditionPathIsReadWrite, config_parse_unit_condition_path, CONDITION_PATH_IS_READ_WRITE, 0 Unit.ConditionDirectoryNotEmpty, config_parse_unit_condition_path, CONDITION_DIRECTORY_NOT_EMPTY, 0 +Unit.ConditionFileNotEmpty, config_parse_unit_condition_path, CONDITION_FILE_NOT_EMPTY, 0 Unit.ConditionFileIsExecutable, config_parse_unit_condition_path, CONDITION_FILE_IS_EXECUTABLE, 0 Unit.ConditionKernelCommandLine, config_parse_unit_condition_string, CONDITION_KERNEL_COMMAND_LINE, 0 Unit.ConditionVirtualization, config_parse_unit_condition_string, CONDITION_VIRTUALIZATION, 0 Unit.ConditionSecurity, config_parse_unit_condition_string, CONDITION_SECURITY, 0 Unit.ConditionCapability, config_parse_unit_condition_string, CONDITION_CAPABILITY, 0 +Unit.ConditionHost, config_parse_unit_condition_string, CONDITION_HOST, 0 +Unit.ConditionACPower, config_parse_unit_condition_string, CONDITION_AC_POWER, 0 Unit.ConditionNull, config_parse_unit_condition_null, 0, 0 m4_dnl Service.PIDFile, config_parse_unit_path_printf, 0, offsetof(Service, pid_file) @@ -145,7 +148,9 @@ Service.ExecReload, config_parse_exec, SERVICE_EXE Service.ExecStop, config_parse_exec, SERVICE_EXEC_STOP, offsetof(Service, exec_command) Service.ExecStopPost, config_parse_exec, SERVICE_EXEC_STOP_POST, offsetof(Service, exec_command) Service.RestartSec, config_parse_usec, 0, offsetof(Service, restart_usec) -Service.TimeoutSec, config_parse_service_timeout, 0, offsetof(Service, timeout_usec) +Service.TimeoutSec, config_parse_service_timeout, 0, offsetof(Service, timeout_start_usec) +Service.TimeoutStartSec, config_parse_service_timeout, 0, offsetof(Service, timeout_start_usec) +Service.TimeoutStopSec, config_parse_service_timeout, 0, offsetof(Service, timeout_stop_usec) Service.WatchdogSec, config_parse_usec, 0, offsetof(Service, watchdog_usec) Service.StartLimitInterval, config_parse_usec, 0, offsetof(Service, start_limit.interval) Service.StartLimitBurst, config_parse_unsigned, 0, offsetof(Service, start_limit.burst) @@ -156,6 +161,8 @@ Service.PermissionsStartOnly, config_parse_bool, 0, Service.RootDirectoryStartOnly, config_parse_bool, 0, offsetof(Service, root_directory_start_only) Service.RemainAfterExit, config_parse_bool, 0, offsetof(Service, remain_after_exit) Service.GuessMainPID, config_parse_bool, 0, offsetof(Service, guess_main_pid) +Service.RestartPreventExitStatus, config_parse_set_status, 0, offsetof(Service, restart_ignore_status) +Service.SuccessExitStatus, config_parse_set_status, 0, offsetof(Service, success_status) m4_ifdef(`HAVE_SYSV_COMPAT', `Service.SysVStartPriority, config_parse_sysv_priority, 0, offsetof(Service, sysv_start_priority)', `Service.SysVStartPriority, config_parse_warn_compat, 0, 0') @@ -203,6 +210,9 @@ Socket.TCPCongestion, config_parse_string, 0, Socket.MessageQueueMaxMessages, config_parse_long, 0, offsetof(Socket, mq_maxmsg) Socket.MessageQueueMessageSize, config_parse_long, 0, offsetof(Socket, mq_msgsize) Socket.Service, config_parse_socket_service, 0, 0 +Socket.SmackLabel, config_parse_string, 0, offsetof(Socket, smack) +Socket.SmackLabelIPIn, config_parse_string, 0, offsetof(Socket, smack_ip_in) +Socket.SmackLabelIPOut, config_parse_string, 0, offsetof(Socket, smack_ip_out) EXEC_CONTEXT_CONFIG_ITEMS(Socket)m4_dnl KILL_CONTEXT_CONFIG_ITEMS(Socket)m4_dnl m4_dnl @@ -225,6 +235,7 @@ Swap.TimeoutSec, config_parse_usec, 0, EXEC_CONTEXT_CONFIG_ITEMS(Swap)m4_dnl KILL_CONTEXT_CONFIG_ITEMS(Swap)m4_dnl m4_dnl +Timer.OnCalendar, config_parse_timer, 0, 0 Timer.OnActiveSec, config_parse_timer, 0, 0 Timer.OnBootSec, config_parse_timer, 0, 0 Timer.OnStartupSec, config_parse_timer, 0, 0 diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c index bbd82b9d24..e35fdbc5ec 100644 --- a/src/core/load-fragment.c +++ b/src/core/load-fragment.c @@ -4,6 +4,7 @@ This file is part of systemd. Copyright 2010 Lennart Poettering + Copyright 2012 Holger Hans Peter Freyther 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 @@ -42,6 +43,7 @@ #include "securebits.h" #include "missing.h" #include "unit-name.h" +#include "unit-printf.h" #include "bus-errors.h" #include "utf8.h" #include "path-util.h" @@ -84,7 +86,7 @@ int config_parse_unit_deps( assert(rvalue); FOREACH_WORD_QUOTED(w, l, rvalue, state) { - char *t, *k; + char _cleanup_free_ *t = NULL, *k = NULL; int r; t = strndup(w, l); @@ -92,15 +94,13 @@ int config_parse_unit_deps( return -ENOMEM; k = unit_name_printf(u, t); - free(t); if (!k) return -ENOMEM; r = unit_add_dependency_by_name(u, d, k, NULL, true); if (r < 0) - log_error("[%s:%u] Failed to add dependency on %s, ignoring: %s", filename, line, k, strerror(-r)); - - free(k); + log_error("[%s:%u] Failed to add dependency on %s, ignoring: %s", + filename, line, k, strerror(-r)); } return 0; @@ -185,7 +185,7 @@ int config_parse_unit_path_printf( k = unit_full_printf(u, rvalue); if (!k) - return -ENOMEM; + return log_oom(); r = config_parse_path(filename, line, section, lvalue, ltype, k, data, userdata); free(k); @@ -325,10 +325,12 @@ int config_parse_socket_bind( s = SOCKET(data); - if ((b = socket_address_bind_ipv6_only_from_string(rvalue)) < 0) { + b = socket_address_bind_ipv6_only_from_string(rvalue); + if (b < 0) { int r; - if ((r = parse_boolean(rvalue)) < 0) { + r = parse_boolean(rvalue); + if (r < 0) { log_error("[%s:%u] Failed to parse bind IPv6 only value, ignoring: %s", filename, line, rvalue); return 0; } @@ -435,6 +437,7 @@ int config_parse_exec( e += ltype; for (;;) { + int i; char *w; size_t l; char *state; @@ -449,18 +452,21 @@ int config_parse_exec( if (rvalue[0] == 0) break; - if (rvalue[0] == '-') { - ignore = true; - rvalue ++; - } + for (i = 0; i < 2; i++) { + if (rvalue[0] == '-' && !ignore) { + ignore = true; + rvalue ++; + } - if (rvalue[0] == '@') { - honour_argv0 = true; - rvalue ++; + if (rvalue[0] == '@' && !honour_argv0) { + honour_argv0 = true; + rvalue ++; + } } if (*rvalue != '/') { - log_error("[%s:%u] Invalid executable path in command line, ignoring: %s", filename, line, rvalue); + log_error("[%s:%u] Executable path is not absolute, ignoring: %s", + filename, line, rvalue); return 0; } @@ -480,6 +486,8 @@ int config_parse_exec( FOREACH_WORD_QUOTED(w, l, rvalue, state) { if (strncmp(w, ";", MAX(l, 1U)) == 0) break; + else if (strncmp(w, "\\;", MAX(l, 1U)) == 0) + w ++; if (honour_argv0 && w == rvalue) { assert(!path); @@ -613,7 +621,8 @@ int config_parse_exec_io_class( assert(rvalue); assert(data); - if ((x = ioprio_class_from_string(rvalue)) < 0) { + x = ioprio_class_from_string(rvalue); + if (x < 0) { log_error("[%s:%u] Failed to parse IO scheduling class, ignoring: %s", filename, line, rvalue); return 0; } @@ -672,12 +681,15 @@ int config_parse_exec_cpu_sched_policy( assert(rvalue); assert(data); - if ((x = sched_policy_from_string(rvalue)) < 0) { + x = sched_policy_from_string(rvalue); + if (x < 0) { log_error("[%s:%u] Failed to parse CPU scheduling policy, ignoring: %s", filename, line, rvalue); return 0; } c->cpu_sched_policy = x; + /* Moving to or from real-time policy? We need to adjust the priority */ + c->cpu_sched_priority = CLAMP(c->cpu_sched_priority, sched_get_priority_min(x), sched_get_priority_max(x)); c->cpu_sched_set = true; return 0; @@ -694,19 +706,28 @@ int config_parse_exec_cpu_sched_prio( void *userdata) { ExecContext *c = data; - int i; + int i, min, max; assert(filename); assert(lvalue); assert(rvalue); assert(data); - /* On Linux RR/FIFO have the same range */ - if (safe_atoi(rvalue, &i) < 0 || i < sched_get_priority_min(SCHED_RR) || i > sched_get_priority_max(SCHED_RR)) { + if (safe_atoi(rvalue, &i) < 0) { log_error("[%s:%u] Failed to parse CPU scheduling priority, ignoring: %s", filename, line, rvalue); return 0; } + + /* On Linux RR/FIFO range from 1 to 99 and OTHER/BATCH may only be 0 */ + min = sched_get_priority_min(c->cpu_sched_policy); + max = sched_get_priority_max(c->cpu_sched_policy); + + if (i < min || i > max) { + log_error("[%s:%u] CPU scheduling priority is out of range, ignoring: %s", filename, line, rvalue); + return 0; + } + c->cpu_sched_priority = i; c->cpu_sched_set = true; @@ -734,22 +755,25 @@ int config_parse_exec_cpu_affinity( assert(data); FOREACH_WORD_QUOTED(w, l, rvalue, state) { - char *t; + char _cleanup_free_ *t = NULL; int r; unsigned cpu; - if (!(t = strndup(w, l))) + t = strndup(w, l); + if (!t) return -ENOMEM; r = safe_atou(t, &cpu); - free(t); - if (!(c->cpuset)) - if (!(c->cpuset = cpu_set_malloc(&c->cpuset_ncpus))) + if (!c->cpuset) { + c->cpuset = cpu_set_malloc(&c->cpuset_ncpus); + if (!c->cpuset) return -ENOMEM; + } if (r < 0 || cpu >= c->cpuset_ncpus) { - log_error("[%s:%u] Failed to parse CPU affinity, ignoring: %s", filename, line, rvalue); + log_error("[%s:%u] Failed to parse CPU affinity %s, ignoring: %s", + filename, line, t, rvalue); return 0; } @@ -826,7 +850,8 @@ int config_parse_exec_secure_bits( else if (first_word(w, "noroot-locked")) c->secure_bits |= SECURE_NOROOT_LOCKED; else { - log_error("[%s:%u] Failed to parse secure bits, ignoring: %s", filename, line, rvalue); + log_error("[%s:%u] Failed to parse secure bits, ignoring: %s", + filename, line, rvalue); return 0; } } @@ -867,7 +892,7 @@ int config_parse_bounding_set( * interface. */ FOREACH_WORD_QUOTED(w, l, rvalue, state) { - char *t; + char _cleanup_free_ *t = NULL; int r; cap_value_t cap; @@ -876,10 +901,9 @@ int config_parse_bounding_set( return -ENOMEM; r = cap_from_name(t, &cap); - free(t); - if (r < 0) { - log_error("[%s:%u] Failed to parse capability bounding set, ignoring: %s", filename, line, rvalue); + log_error("[%s:%u] Failed to parse capability in bounding set, ignoring: %s", + filename, line, t); continue; } @@ -945,7 +969,7 @@ int config_parse_unit_cgroup( char *state; FOREACH_WORD_QUOTED(w, l, rvalue, state) { - char *t, *k; + char _cleanup_free_ *t = NULL, *k = NULL, *ku = NULL; int r; t = strndup(w, l); @@ -953,22 +977,17 @@ int config_parse_unit_cgroup( return -ENOMEM; k = unit_full_printf(u, t); - free(t); - if (!k) return -ENOMEM; - t = cunescape(k); - free(k); - - if (!t) + ku = cunescape(k); + if (!ku) return -ENOMEM; - r = unit_add_cgroup_from_text(u, t); - free(t); - + r = unit_add_cgroup_from_text(u, ku); if (r < 0) { - log_error("[%s:%u] Failed to parse cgroup value, ignoring: %s", filename, line, rvalue); + log_error("[%s:%u] Failed to parse cgroup value %s, ignoring: %s", + filename, line, k, rvalue); return 0; } } @@ -1082,15 +1101,22 @@ int config_parse_exec_mount_flags( assert(rvalue); assert(data); - FOREACH_WORD_QUOTED(w, l, rvalue, state) { - if (strncmp(w, "shared", MAX(l, 6U)) == 0) + FOREACH_WORD_SEPARATOR(w, l, rvalue, ", ", state) { + char _cleanup_free_ *t; + + t = strndup(w, l); + if (!t) + return -ENOMEM; + + if (streq(t, "shared")) flags |= MS_SHARED; - else if (strncmp(w, "slave", MAX(l, 5U)) == 0) + else if (streq(t, "slave")) flags |= MS_SLAVE; - else if (strncmp(w, "private", MAX(l, 7U)) == 0) + else if (streq(w, "private")) flags |= MS_PRIVATE; else { - log_error("[%s:%u] Failed to parse mount flags, ignoring: %s", filename, line, rvalue); + log_error("[%s:%u] Failed to parse mount flag %s, ignoring: %s", + filename, line, t, rvalue); return 0; } } @@ -1110,30 +1136,47 @@ int config_parse_timer( void *userdata) { Timer *t = data; - usec_t u; + usec_t u = 0; TimerValue *v; TimerBase b; + CalendarSpec *c = NULL; + clockid_t id; assert(filename); assert(lvalue); assert(rvalue); assert(data); - if ((b = timer_base_from_string(lvalue)) < 0) { + b = timer_base_from_string(lvalue); + if (b < 0) { log_error("[%s:%u] Failed to parse timer base, ignoring: %s", filename, line, lvalue); return 0; } - if (parse_usec(rvalue, &u) < 0) { - log_error("[%s:%u] Failed to parse timer value, ignoring: %s", filename, line, rvalue); - return 0; + if (b == TIMER_CALENDAR) { + if (calendar_spec_from_string(rvalue, &c) < 0) { + log_error("[%s:%u] Failed to parse calendar specification, ignoring: %s", filename, line, rvalue); + return 0; + } + + id = CLOCK_REALTIME; + } else { + if (parse_usec(rvalue, &u) < 0) { + log_error("[%s:%u] Failed to parse timer value, ignoring: %s", filename, line, rvalue); + return 0; + } + + id = CLOCK_MONOTONIC; } - if (!(v = new0(TimerValue, 1))) + v = new0(TimerValue, 1); + if (!v) return -ENOMEM; v->base = b; + v->clock_id = id; v->value = u; + v->calendar_spec = c; LIST_PREPEND(TimerValue, value, t->values, v); @@ -1192,32 +1235,36 @@ int config_parse_path_spec( Path *p = data; PathSpec *s; PathType b; + char *k; assert(filename); assert(lvalue); assert(rvalue); assert(data); - if ((b = path_type_from_string(lvalue)) < 0) { + b = path_type_from_string(lvalue); + if (b < 0) { log_error("[%s:%u] Failed to parse path type, ignoring: %s", filename, line, lvalue); return 0; } - if (!path_is_absolute(rvalue)) { - log_error("[%s:%u] Path is not absolute, ignoring: %s", filename, line, rvalue); + k = unit_full_printf(UNIT(p), rvalue); + if (!k) + return log_oom(); + + if (!path_is_absolute(k)) { + log_error("[%s:%u] Path is not absolute, ignoring: %s", filename, line, k); + free(k); return 0; } - if (!(s = new0(PathSpec, 1))) - return -ENOMEM; - - if (!(s->path = strdup(rvalue))) { - free(s); - return -ENOMEM; + s = new0(PathSpec, 1); + if (!s) { + free(k); + return log_oom(); } - path_kill_slashes(s->path); - + s->path = path_kill_slashes(k); s->type = b; s->inotify_fd = -1; @@ -1324,33 +1371,30 @@ int config_parse_service_sockets( assert(data); FOREACH_WORD_QUOTED(w, l, rvalue, state) { - char *t, *k; + char _cleanup_free_ *t = NULL, *k = NULL; t = strndup(w, l); if (!t) return -ENOMEM; k = unit_name_printf(UNIT(s), t); - free(t); - if (!k) return -ENOMEM; if (!endswith(k, ".socket")) { - log_error("[%s:%u] Unit must be of type socket, ignoring: %s", filename, line, rvalue); - free(k); + log_error("[%s:%u] Unit must be of type socket, ignoring: %s", + filename, line, k); continue; } r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_WANTS, UNIT_AFTER, k, NULL, true); if (r < 0) - log_error("[%s:%u] Failed to add dependency on %s, ignoring: %s", filename, line, k, strerror(-r)); + log_error("[%s:%u] Failed to add dependency on %s, ignoring: %s", + filename, line, k, strerror(-r)); r = unit_add_dependency_by_name(UNIT(s), UNIT_TRIGGERED_BY, k, NULL, true); if (r < 0) return r; - - free(k); } return 0; @@ -1376,10 +1420,16 @@ int config_parse_service_timeout( r = config_parse_usec(filename, line, section, lvalue, ltype, rvalue, data, userdata); - if (!r) - s->timeout_defined = true; + if (r) + return r; - return r; + if (streq(lvalue, "TimeoutSec")) { + s->start_timeout_defined = true; + s->timeout_stop_usec = s->timeout_start_usec; + } else if (streq(lvalue, "TimeoutStartSec")) + s->start_timeout_defined = true; + + return 0; } int config_parse_unit_env_file( @@ -1439,11 +1489,11 @@ int config_parse_ip_tos( assert(rvalue); assert(data); - if ((x = ip_tos_from_string(rvalue)) < 0) - if (safe_atoi(rvalue, &x) < 0) { - log_error("[%s:%u] Failed to parse IP TOS value, ignoring: %s", filename, line, rvalue); - return 0; - } + x = ip_tos_from_string(rvalue); + if (x < 0) { + log_error("[%s:%u] Failed to parse IP TOS value, ignoring: %s", filename, line, rvalue); + return 0; + } *ip_tos = x; return 0; @@ -1463,6 +1513,7 @@ int config_parse_unit_condition_path( Unit *u = data; bool trigger, negate; Condition *c; + _cleanup_free_ char *p = NULL; assert(filename); assert(lvalue); @@ -1477,12 +1528,16 @@ int config_parse_unit_condition_path( if (negate) rvalue++; - if (!path_is_absolute(rvalue)) { - log_error("[%s:%u] Path in condition not absolute, ignoring: %s", filename, line, rvalue); + p = unit_full_printf(u, rvalue); + if (!p) + return -ENOMEM; + + if (!path_is_absolute(p)) { + log_error("[%s:%u] Path in condition not absolute, ignoring: %s", filename, line, p); return 0; } - c = condition_new(cond, rvalue, trigger, negate); + c = condition_new(cond, p, trigger, negate); if (!c) return -ENOMEM; @@ -1504,21 +1559,29 @@ int config_parse_unit_condition_string( Unit *u = data; bool trigger, negate; Condition *c; + _cleanup_free_ char *s = NULL; assert(filename); assert(lvalue); assert(rvalue); assert(data); - if ((trigger = rvalue[0] == '|')) + trigger = rvalue[0] == '|'; + if (trigger) rvalue++; - if ((negate = rvalue[0] == '!')) + negate = rvalue[0] == '!'; + if (negate) rvalue++; - if (!(c = condition_new(cond, rvalue, trigger, negate))) + s = unit_full_printf(u, rvalue); + if (!s) return -ENOMEM; + c = condition_new(cond, s, trigger, negate); + if (!c) + return log_oom(); + LIST_PREPEND(Condition, conditions, u->conditions, c); return 0; } @@ -2022,7 +2085,7 @@ int config_parse_syscall_filter( ExecContext *c = data; Unit *u = userdata; - bool invert; + bool invert = false; char *w; size_t l; char *state; @@ -2059,17 +2122,17 @@ int config_parse_syscall_filter( FOREACH_WORD_QUOTED(w, l, rvalue, state) { int id; - char *t; + char _cleanup_free_ *t = NULL; t = strndup(w, l); if (!t) return -ENOMEM; id = syscall_from_name(t); - free(t); if (id < 0) { - log_error("[%s:%u] Failed to parse syscall, ignoring: %s", filename, line, rvalue); + log_error("[%s:%u] Failed to parse syscall, ignoring: %s", + filename, line, t); continue; } diff --git a/src/core/locale-setup.c b/src/core/locale-setup.c index 25eb13474a..48b59bf448 100644 --- a/src/core/locale-setup.c +++ b/src/core/locale-setup.c @@ -72,166 +72,61 @@ int locale_setup(void) { zero(variables); - if (detect_container(NULL) <= 0) - if ((r = parse_env_file("/proc/cmdline", WHITESPACE, -#if defined(TARGET_FEDORA) - "LANG", &variables[VARIABLE_LANG], -#endif - "locale.LANG", &variables[VARIABLE_LANG], - "locale.LANGUAGE", &variables[VARIABLE_LANGUAGE], - "locale.LC_CTYPE", &variables[VARIABLE_LC_CTYPE], - "locale.LC_NUMERIC", &variables[VARIABLE_LC_NUMERIC], - "locale.LC_TIME", &variables[VARIABLE_LC_TIME], - "locale.LC_COLLATE", &variables[VARIABLE_LC_COLLATE], - "locale.LC_MONETARY", &variables[VARIABLE_LC_MONETARY], - "locale.LC_MESSAGES", &variables[VARIABLE_LC_MESSAGES], - "locale.LC_PAPER", &variables[VARIABLE_LC_PAPER], - "locale.LC_NAME", &variables[VARIABLE_LC_NAME], - "locale.LC_ADDRESS", &variables[VARIABLE_LC_ADDRESS], - "locale.LC_TELEPHONE", &variables[VARIABLE_LC_TELEPHONE], - "locale.LC_MEASUREMENT", &variables[VARIABLE_LC_MEASUREMENT], - "locale.LC_IDENTIFICATION", &variables[VARIABLE_LC_IDENTIFICATION], - NULL)) < 0) { - - if (r != -ENOENT) - log_warning("Failed to read /proc/cmdline: %s", strerror(-r)); - } + if (detect_container(NULL) <= 0) { + r = parse_env_file("/proc/cmdline", WHITESPACE, + "locale.LANG", &variables[VARIABLE_LANG], + "locale.LANGUAGE", &variables[VARIABLE_LANGUAGE], + "locale.LC_CTYPE", &variables[VARIABLE_LC_CTYPE], + "locale.LC_NUMERIC", &variables[VARIABLE_LC_NUMERIC], + "locale.LC_TIME", &variables[VARIABLE_LC_TIME], + "locale.LC_COLLATE", &variables[VARIABLE_LC_COLLATE], + "locale.LC_MONETARY", &variables[VARIABLE_LC_MONETARY], + "locale.LC_MESSAGES", &variables[VARIABLE_LC_MESSAGES], + "locale.LC_PAPER", &variables[VARIABLE_LC_PAPER], + "locale.LC_NAME", &variables[VARIABLE_LC_NAME], + "locale.LC_ADDRESS", &variables[VARIABLE_LC_ADDRESS], + "locale.LC_TELEPHONE", &variables[VARIABLE_LC_TELEPHONE], + "locale.LC_MEASUREMENT", &variables[VARIABLE_LC_MEASUREMENT], + "locale.LC_IDENTIFICATION", &variables[VARIABLE_LC_IDENTIFICATION], + NULL); + + if (r < 0 && r != -ENOENT) + log_warning("Failed to read /proc/cmdline: %s", strerror(-r)); + } /* Hmm, nothing set on the kernel cmd line? Then let's * try /etc/locale.conf */ - if (r <= 0 && - (r = parse_env_file("/etc/locale.conf", NEWLINE, - "LANG", &variables[VARIABLE_LANG], - "LANGUAGE", &variables[VARIABLE_LANGUAGE], - "LC_CTYPE", &variables[VARIABLE_LC_CTYPE], - "LC_NUMERIC", &variables[VARIABLE_LC_NUMERIC], - "LC_TIME", &variables[VARIABLE_LC_TIME], - "LC_COLLATE", &variables[VARIABLE_LC_COLLATE], - "LC_MONETARY", &variables[VARIABLE_LC_MONETARY], - "LC_MESSAGES", &variables[VARIABLE_LC_MESSAGES], - "LC_PAPER", &variables[VARIABLE_LC_PAPER], - "LC_NAME", &variables[VARIABLE_LC_NAME], - "LC_ADDRESS", &variables[VARIABLE_LC_ADDRESS], - "LC_TELEPHONE", &variables[VARIABLE_LC_TELEPHONE], - "LC_MEASUREMENT", &variables[VARIABLE_LC_MEASUREMENT], - "LC_IDENTIFICATION", &variables[VARIABLE_LC_IDENTIFICATION], - NULL)) < 0) { - - if (r != -ENOENT) + if (r <= 0) { + r = parse_env_file("/etc/locale.conf", NEWLINE, + "LANG", &variables[VARIABLE_LANG], + "LANGUAGE", &variables[VARIABLE_LANGUAGE], + "LC_CTYPE", &variables[VARIABLE_LC_CTYPE], + "LC_NUMERIC", &variables[VARIABLE_LC_NUMERIC], + "LC_TIME", &variables[VARIABLE_LC_TIME], + "LC_COLLATE", &variables[VARIABLE_LC_COLLATE], + "LC_MONETARY", &variables[VARIABLE_LC_MONETARY], + "LC_MESSAGES", &variables[VARIABLE_LC_MESSAGES], + "LC_PAPER", &variables[VARIABLE_LC_PAPER], + "LC_NAME", &variables[VARIABLE_LC_NAME], + "LC_ADDRESS", &variables[VARIABLE_LC_ADDRESS], + "LC_TELEPHONE", &variables[VARIABLE_LC_TELEPHONE], + "LC_MEASUREMENT", &variables[VARIABLE_LC_MEASUREMENT], + "LC_IDENTIFICATION", &variables[VARIABLE_LC_IDENTIFICATION], + NULL); + + if (r < 0 && r != -ENOENT) log_warning("Failed to read /etc/locale.conf: %s", strerror(-r)); } -#if defined(TARGET_FEDORA) || defined(TARGET_ALTLINUX) - if (r <= 0 && - (r = parse_env_file("/etc/sysconfig/i18n", NEWLINE, - "LANG", &variables[VARIABLE_LANG], - NULL)) < 0) { - - if (r != -ENOENT) - log_warning("Failed to read /etc/sysconfig/i18n: %s", strerror(-r)); - } - -#elif defined(TARGET_SUSE) - if (r <= 0 && - (r = parse_env_file("/etc/sysconfig/language", NEWLINE, - "RC_LANG", &variables[VARIABLE_LANG], - NULL)) < 0) { - - if (r != -ENOENT) - log_warning("Failed to read /etc/sysconfig/language: %s", strerror(-r)); - } - -#elif defined(TARGET_DEBIAN) || defined(TARGET_UBUNTU) || defined(TARGET_ANGSTROM) - if (r <= 0 && - (r = parse_env_file("/etc/default/locale", NEWLINE, - "LANG", &variables[VARIABLE_LANG], - "LC_CTYPE", &variables[VARIABLE_LC_CTYPE], - "LC_NUMERIC", &variables[VARIABLE_LC_NUMERIC], - "LC_TIME", &variables[VARIABLE_LC_TIME], - "LC_COLLATE", &variables[VARIABLE_LC_COLLATE], - "LC_MONETARY", &variables[VARIABLE_LC_MONETARY], - "LC_MESSAGES", &variables[VARIABLE_LC_MESSAGES], - "LC_PAPER", &variables[VARIABLE_LC_PAPER], - "LC_NAME", &variables[VARIABLE_LC_NAME], - "LC_ADDRESS", &variables[VARIABLE_LC_ADDRESS], - "LC_TELEPHONE", &variables[VARIABLE_LC_TELEPHONE], - "LC_MEASUREMENT", &variables[VARIABLE_LC_MEASUREMENT], - "LC_IDENTIFICATION", &variables[VARIABLE_LC_IDENTIFICATION], - NULL)) < 0) { - - if (r != -ENOENT) - log_warning("Failed to read /etc/default/locale: %s", strerror(-r)); - } - -#elif defined(TARGET_ARCH) - if (r <= 0 && - (r = parse_env_file("/etc/rc.conf", NEWLINE, - "LOCALE", &variables[VARIABLE_LANG], - NULL)) < 0) { - - if (r != -ENOENT) - log_warning("Failed to read /etc/rc.conf: %s", strerror(-r)); - } - -#elif defined(TARGET_GENTOO) - /* Gentoo's openrc expects locale variables in /etc/env.d/ - * These files are later compiled by env-update into shell - * export commands at /etc/profile.env, with variables being - * exported by openrc's runscript (so /etc/init.d/) - */ - if (r <= 0 && - (r = parse_env_file("/etc/profile.env", NEWLINE, - "export LANG", &variables[VARIABLE_LANG], - "export LC_CTYPE", &variables[VARIABLE_LC_CTYPE], - "export LC_NUMERIC", &variables[VARIABLE_LC_NUMERIC], - "export LC_TIME", &variables[VARIABLE_LC_TIME], - "export LC_COLLATE", &variables[VARIABLE_LC_COLLATE], - "export LC_MONETARY", &variables[VARIABLE_LC_MONETARY], - "export LC_MESSAGES", &variables[VARIABLE_LC_MESSAGES], - "export LC_PAPER", &variables[VARIABLE_LC_PAPER], - "export LC_NAME", &variables[VARIABLE_LC_NAME], - "export LC_ADDRESS", &variables[VARIABLE_LC_ADDRESS], - "export LC_TELEPHONE", &variables[VARIABLE_LC_TELEPHONE], - "export LC_MEASUREMENT", &variables[VARIABLE_LC_MEASUREMENT], - "export LC_IDENTIFICATION", &variables[VARIABLE_LC_IDENTIFICATION], - NULL)) < 0) { - - if (r != -ENOENT) - log_warning("Failed to read /etc/profile.env: %s", strerror(-r)); - } -#elif defined(TARGET_MANDRIVA) || defined(TARGET_MAGEIA ) - if (r <= 0 && - (r = parse_env_file("/etc/sysconfig/i18n", NEWLINE, - "LANG", &variables[VARIABLE_LANG], - "LC_CTYPE", &variables[VARIABLE_LC_CTYPE], - "LC_NUMERIC", &variables[VARIABLE_LC_NUMERIC], - "LC_TIME", &variables[VARIABLE_LC_TIME], - "LC_COLLATE", &variables[VARIABLE_LC_COLLATE], - "LC_MONETARY", &variables[VARIABLE_LC_MONETARY], - "LC_MESSAGES", &variables[VARIABLE_LC_MESSAGES], - "LC_PAPER", &variables[VARIABLE_LC_PAPER], - "LC_NAME", &variables[VARIABLE_LC_NAME], - "LC_ADDRESS", &variables[VARIABLE_LC_ADDRESS], - "LC_TELEPHONE", &variables[VARIABLE_LC_TELEPHONE], - "LC_MEASUREMENT", &variables[VARIABLE_LC_MEASUREMENT], - "LC_IDENTIFICATION", &variables[VARIABLE_LC_IDENTIFICATION], - NULL)) < 0) { - - if (r != -ENOENT) - log_warning("Failed to read /etc/sysconfig/i18n: %s", strerror(-r)); - } - -#endif - if (!variables[VARIABLE_LANG]) { - if (!(variables[VARIABLE_LANG] = strdup("C"))) { + variables[VARIABLE_LANG] = strdup("C"); + if (!variables[VARIABLE_LANG]) { r = -ENOMEM; goto finish; } } for (i = 0; i < _VARIABLE_MAX; i++) { - if (variables[i]) { if (setenv(variable_names[i], variables[i], 1) < 0) { r = -errno; diff --git a/src/core/machine-id-setup.c b/src/core/machine-id-setup.c index c6fd77ac8d..7f4c23b130 100644 --- a/src/core/machine-id-setup.c +++ b/src/core/machine-id-setup.c @@ -226,13 +226,17 @@ int machine_id_setup(void) { } /* And now, let's mount it over */ - r = mount("/run/machine-id", "/etc/machine-id", "bind", MS_BIND|MS_RDONLY, NULL) < 0 ? -errno : 0; + r = mount("/run/machine-id", "/etc/machine-id", NULL, MS_BIND, NULL) < 0 ? -errno : 0; if (r < 0) { unlink("/run/machine-id"); log_error("Failed to mount /etc/machine-id: %s", strerror(-r)); - } else + } else { log_info("Installed transient /etc/machine-id file."); + /* Mark the mount read-only */ + mount(NULL, "/etc/machine-id", NULL, MS_BIND|MS_RDONLY|MS_REMOUNT, NULL); + } + finish: if (fd >= 0) diff --git a/src/core/macros.systemd.in b/src/core/macros.systemd.in index 86e869307c..647cce6913 100644 --- a/src/core/macros.systemd.in +++ b/src/core/macros.systemd.in @@ -20,6 +20,12 @@ # RPM macros for packages installing systemd unit files %_unitdir @systemunitdir@ +%_presetdir @systempresetdir@ +%_udevhwdbdir @udevhwdbdir@ +%_udevrulesdir @udevrulesdir@ +%_journalcatalogdir @catalogdir@ +%_tmpfilesdir @tmpfilesdir@ +%_sysctldir @sysctldir@ %systemd_requires \ Requires(post): systemd \ @@ -53,3 +59,15 @@ if [ $1 -ge 1 ] ; then \ @rootbindir@/systemctl try-restart %{?*} >/dev/null 2>&1 || : \ fi \ %{nil} + +%udev_hwdb_update() \ +@bindir@/udevadm hwdb --update >/dev/null 2>&1 || : \ +%{nil} + +%udev_rules_update() \ +@bindir@/udevadm control --reload >/dev/null 2>&1 || : \ +%{nil} + +%journal_catalog_update() \ +@rootbindir@/journalctl --update-catalog >/dev/null 2>&1 || : \ +%{nil} diff --git a/src/core/main.c b/src/core/main.c index 1326b94f68..1ee3c9c0e8 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -55,13 +55,16 @@ #include "mount-setup.h" #include "loopback-setup.h" +#ifdef HAVE_KMOD #include "kmod-setup.h" +#endif #include "hostname-setup.h" #include "machine-id-setup.h" #include "locale-setup.h" #include "hwclock.h" #include "selinux-setup.h" #include "ima-setup.h" +#include "sd-daemon.h" static enum { ACTION_RUN, @@ -73,7 +76,7 @@ static enum { } arg_action = ACTION_RUN; static char *arg_default_unit = NULL; -static ManagerRunningAs arg_running_as = _MANAGER_RUNNING_AS_INVALID; +static SystemdRunningAs arg_running_as = _SYSTEMD_RUNNING_AS_INVALID; static bool arg_dump_core = true; static bool arg_crash_shell = false; @@ -168,12 +171,12 @@ _noreturn_ static void crash(int sig) { pid = fork(); if (pid < 0) - log_error("Failed to fork off crash shell: %s", strerror(errno)); + log_error("Failed to fork off crash shell: %m"); else if (pid == 0) { make_console_stdio(); execl("/bin/sh", "/bin/sh", NULL); - log_error("execl() failed: %s", strerror(errno)); + log_error("execl() failed: %m"); _exit(1); } @@ -350,12 +353,12 @@ static int parse_proc_cmdline_word(const char *word) { if (!eq) { r = unsetenv(cenv); if (r < 0) - log_warning("unsetenv failed %s. Ignoring.", strerror(errno)); + log_warning("unsetenv failed %m. Ignoring."); } else { *eq = 0; r = setenv(cenv, eq + 1, 1); if (r < 0) - log_warning("setenv failed %s. Ignoring.", strerror(errno)); + log_warning("setenv failed %m. Ignoring."); } free(cenv); @@ -495,14 +498,14 @@ static int config_parse_cpu_affinity2( unsigned cpu; if (!(t = strndup(w, l))) - return -ENOMEM; + return log_oom(); r = safe_atou(t, &cpu); free(t); if (!c) if (!(c = cpu_set_malloc(&ncpus))) - return -ENOMEM; + return log_oom(); if (r < 0 || cpu >= ncpus) { log_error("[%s:%u] Failed to parse CPU affinity: %s", filename, line, rvalue); @@ -568,7 +571,7 @@ static int config_parse_join_controllers( s = strndup(w, length); if (!s) - return -ENOMEM; + return log_oom(); l = strv_split(s, ","); free(s); @@ -584,7 +587,7 @@ static int config_parse_join_controllers( arg_join_controllers = new(char**, 2); if (!arg_join_controllers) { strv_free(l); - return -ENOMEM; + return log_oom(); } arg_join_controllers[0] = l; @@ -598,7 +601,7 @@ static int config_parse_join_controllers( t = new0(char**, n+2); if (!t) { strv_free(l); - return -ENOMEM; + return log_oom(); } n = 0; @@ -612,7 +615,7 @@ static int config_parse_join_controllers( if (!c) { strv_free(l); strv_free_free(t); - return -ENOMEM; + return log_oom(); } strv_free(l); @@ -624,7 +627,7 @@ static int config_parse_join_controllers( if (!c) { strv_free(l); strv_free_free(t); - return -ENOMEM; + return log_oom(); } t[n++] = c; @@ -684,7 +687,7 @@ static int parse_config_file(void) { const char *fn; int r; - fn = arg_running_as == MANAGER_SYSTEM ? SYSTEM_CONFIG_FILE : USER_CONFIG_FILE; + fn = arg_running_as == SYSTEMD_SYSTEM ? SYSTEM_CONFIG_FILE : USER_CONFIG_FILE; f = fopen(fn, "re"); if (!f) { if (errno == ENOENT) @@ -727,10 +730,13 @@ static int parse_proc_cmdline(void) { } r = parse_proc_cmdline_word(word); - free(word); - - if (r < 0) + if (r < 0) { + log_error("Failed on cmdline argument %s: %s", word, strerror(-r)); + free(word); goto finish; + } + + free(word); } r = 0; @@ -869,11 +875,11 @@ static int parse_argv(int argc, char *argv[]) { break; case ARG_SYSTEM: - arg_running_as = MANAGER_SYSTEM; + arg_running_as = SYSTEMD_SYSTEM; break; case ARG_USER: - arg_running_as = MANAGER_USER; + arg_running_as = SYSTEMD_USER; break; case ARG_TEST: @@ -928,14 +934,18 @@ static int parse_argv(int argc, char *argv[]) { int fd; FILE *f; - if ((r = safe_atoi(optarg, &fd)) < 0 || fd < 0) { + r = safe_atoi(optarg, &fd); + if (r < 0 || fd < 0) { log_error("Failed to parse deserialize option %s.", optarg); - return r; + return r < 0 ? r : -EINVAL; } - if (!(f = fdopen(fd, "r"))) { + fd_cloexec(fd, true); + + f = fdopen(fd, "r"); + if (!f) { log_error("Failed to open serialization fd: %m"); - return r; + return -errno; } if (serialization) @@ -1017,8 +1027,10 @@ static int parse_argv(int argc, char *argv[]) { * instead. */ for (a = argv; a < argv + argc; a++) - if ((r = parse_proc_cmdline_word(*a)) < 0) + if ((r = parse_proc_cmdline_word(*a)) < 0) { + log_error("Failed on cmdline argument %s: %s", *a, strerror(-r)); return r; + } } return 0; @@ -1052,7 +1064,6 @@ static int help(void) { static int version(void) { puts(PACKAGE_STRING); - puts(DISTRIBUTION); puts(SYSTEMD_FEATURES); return 0; @@ -1120,6 +1131,42 @@ fail: return r; } +static int bump_rlimit_nofile(struct rlimit *saved_rlimit) { + struct rlimit nl; + int r; + + assert(saved_rlimit); + + /* Save the original RLIMIT_NOFILE so that we can reset it + * later when transitioning from the initrd to the main + * systemd or suchlike. */ + if (getrlimit(RLIMIT_NOFILE, saved_rlimit) < 0) { + log_error("Reading RLIMIT_NOFILE failed: %m"); + return -errno; + } + + /* Make sure forked processes get the default kernel setting */ + if (!arg_default_rlimit[RLIMIT_NOFILE]) { + struct rlimit *rl; + + rl = newdup(struct rlimit, saved_rlimit, 1); + if (!rl) + return log_oom(); + + arg_default_rlimit[RLIMIT_NOFILE] = rl; + } + + /* Bump up the resource limit for ourselves substantially */ + nl.rlim_cur = nl.rlim_max = 64*1024; + r = setrlimit_closest(RLIMIT_NOFILE, &nl); + if (r < 0) { + log_error("Setting RLIMIT_NOFILE failed: %s", strerror(-r)); + return r; + } + + return 0; +} + static struct dual_timestamp* parse_initrd_timestamp(struct dual_timestamp *t) { const char *e; unsigned long long a, b; @@ -1186,6 +1233,28 @@ static void test_cgroups(void) { sleep(10); } +static int initialize_join_controllers(void) { + /* By default, mount "cpu" + "cpuacct" together, and "net_cls" + * + "net_prio". We'd like to add "cpuset" to the mix, but + * "cpuset" does't really work for groups with no initialized + * attributes. */ + + arg_join_controllers = new(char**, 3); + if (!arg_join_controllers) + return -ENOMEM; + + arg_join_controllers[0] = strv_new("cpu", "cpuacct", NULL); + if (!arg_join_controllers[0]) + return -ENOMEM; + + arg_join_controllers[1] = strv_new("net_cls", "net_prio", NULL); + if (!arg_join_controllers[1]) + return -ENOMEM; + + arg_join_controllers[2] = NULL; + return 0; +} + int main(int argc, char *argv[]) { Manager *m = NULL; int r, retval = EXIT_FAILURE; @@ -1202,6 +1271,7 @@ int main(int argc, char *argv[]) { bool arm_reboot_watchdog = false; bool queue_default_job = false; char *switch_root_dir = NULL, *switch_root_init = NULL; + static struct rlimit saved_rlimit_nofile = { 0, 0 }; #ifdef HAVE_SYSV_COMPAT if (getpid() != 1 && strstr(program_invocation_short_name, "init")) { @@ -1243,10 +1313,15 @@ int main(int argc, char *argv[]) { saved_argc = argc; log_show_color(isatty(STDERR_FILENO) > 0); - log_show_location(false); - log_set_max_level(LOG_INFO); - if (getpid() == 1) { + if (getpid() == 1 && detect_container(NULL) <= 0) { + + /* Running outside of a container as PID 1 */ + arg_running_as = SYSTEMD_SYSTEM; + make_null_stdio(); + log_set_target(LOG_TARGET_KMSG); + log_open(); + if (in_initrd()) { char *rd_timestamp = NULL; @@ -1260,9 +1335,6 @@ int main(int argc, char *argv[]) { } } - arg_running_as = MANAGER_SYSTEM; - log_set_target(detect_container(NULL) > 0 ? LOG_TARGET_JOURNAL : LOG_TARGET_JOURNAL_OR_KMSG); - if (!skip_setup) { if (selinux_setup(&loaded_policy) < 0) goto finish; @@ -1270,41 +1342,72 @@ int main(int argc, char *argv[]) { goto finish; } - log_open(); - if (label_init(NULL) < 0) goto finish; - if (!skip_setup) + if (!skip_setup) { if (hwclock_is_localtime() > 0) { int min; - r = hwclock_apply_localtime_delta(&min); + /* The first-time call to settimeofday() does a time warp in the kernel */ + r = hwclock_set_timezone(&min); if (r < 0) log_error("Failed to apply local time delta, ignoring: %s", strerror(-r)); else log_info("RTC configured in localtime, applying delta of %i minutes to system time.", min); + } else if (!in_initrd()) { + /* + * Do dummy first-time call to seal the kernel's time warp magic + * + * Do not call this this from inside the initrd. The initrd might not + * carry /etc/adjtime with LOCAL, but the real system could be set up + * that way. In such case, we need to delay the time-warp or the sealing + * until we reach the real system. + */ + hwclock_reset_timezone(); + + /* Tell the kernel our time zone */ + r = hwclock_set_timezone(NULL); + if (r < 0) + log_error("Failed to set the kernel's time zone, ignoring: %s", strerror(-r)); } + } + + /* Set the default for later on, but don't actually + * open the logs like this for now. Note that if we + * are transitioning from the initrd there might still + * be journal fd open, and we shouldn't attempt + * opening that before we parsed /proc/cmdline which + * might redirect output elsewhere. */ + log_set_target(LOG_TARGET_JOURNAL_OR_KMSG); + + } else if (getpid() == 1) { + + /* Running inside a container, as PID 1 */ + arg_running_as = SYSTEMD_SYSTEM; + log_set_target(LOG_TARGET_CONSOLE); + log_open(); + + /* For the later on, see above... */ + log_set_target(LOG_TARGET_JOURNAL); } else { - arg_running_as = MANAGER_USER; + + /* Running as user instance */ + arg_running_as = SYSTEMD_USER; log_set_target(LOG_TARGET_AUTO); log_open(); } /* Initialize default unit */ - if (set_default_unit(SPECIAL_DEFAULT_TARGET) < 0) - goto finish; - - /* By default, mount "cpu" and "cpuacct" together */ - arg_join_controllers = new(char**, 2); - if (!arg_join_controllers) + r = set_default_unit(SPECIAL_DEFAULT_TARGET); + if (r < 0) { + log_error("Failed to set default unit %s: %s", SPECIAL_DEFAULT_TARGET, strerror(-r)); goto finish; + } - arg_join_controllers[0] = strv_new("cpu", "cpuacct", NULL); - arg_join_controllers[1] = NULL; - - if (!arg_join_controllers[0]) + r = initialize_join_controllers(); + if (r < 0) goto finish; /* Mount /proc, /sys and friends, so that /proc/cmdline and @@ -1324,7 +1427,7 @@ int main(int argc, char *argv[]) { if (parse_config_file() < 0) goto finish; - if (arg_running_as == MANAGER_SYSTEM) + if (arg_running_as == SYSTEMD_SYSTEM) if (parse_proc_cmdline() < 0) goto finish; @@ -1333,12 +1436,20 @@ int main(int argc, char *argv[]) { if (parse_argv(argc, argv) < 0) goto finish; - if (arg_action == ACTION_TEST && geteuid() == 0) { + if (arg_action == ACTION_TEST && + geteuid() == 0) { log_error("Don't run test mode as root."); goto finish; } - if (arg_running_as == MANAGER_SYSTEM && + if (arg_running_as == SYSTEMD_USER && + arg_action == ACTION_RUN && + sd_booted() <= 0) { + log_error("Trying to run as user instance, but the system has not been booted with systemd."); + goto finish; + } + + if (arg_running_as == SYSTEMD_SYSTEM && arg_action == ACTION_RUN && running_in_chroot() > 0) { log_error("Cannot be run in a chroot() environment."); @@ -1366,16 +1477,15 @@ int main(int argc, char *argv[]) { log_close(); /* Remember open file descriptors for later deserialization */ - if (serialization) { - r = fdset_new_fill(&fds); - if (r < 0) { - log_error("Failed to allocate fd set: %s", strerror(-r)); - goto finish; - } + r = fdset_new_fill(&fds); + if (r < 0) { + log_error("Failed to allocate fd set: %s", strerror(-r)); + goto finish; + } else + fdset_cloexec(fds, true); + if (serialization) assert_se(fdset_remove(fds, fileno(serialization)) >= 0); - } else - close_all_fds(NULL, 0); /* Set up PATH unless it is already set */ setenv("PATH", @@ -1384,9 +1494,9 @@ int main(int argc, char *argv[]) { #else "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin", #endif - arg_running_as == MANAGER_SYSTEM); + arg_running_as == SYSTEMD_SYSTEM); - if (arg_running_as == MANAGER_SYSTEM) { + if (arg_running_as == SYSTEMD_SYSTEM) { /* Parse the data passed to us. We leave this * variables set, but the manager later on will not * pass them on to our children. */ @@ -1410,6 +1520,12 @@ int main(int argc, char *argv[]) { unsetenv("USER"); unsetenv("LOGNAME"); + /* We suppress the socket activation env vars, as + * we'll try to match *any* open fd to units if + * possible. */ + unsetenv("LISTEN_FDS"); + unsetenv("LISTEN_PID"); + /* All other variables are left as is, so that clients * can still read them via /proc/1/environ */ } @@ -1417,7 +1533,7 @@ int main(int argc, char *argv[]) { /* Move out of the way, so that we won't block unmounts */ assert_se(chdir("/") == 0); - if (arg_running_as == MANAGER_SYSTEM) { + if (arg_running_as == SYSTEMD_SYSTEM) { /* Become a session leader if we aren't one yet. */ setsid(); @@ -1430,10 +1546,8 @@ int main(int argc, char *argv[]) { /* Reset the console, but only if this is really init and we * are freshly booted */ - if (arg_running_as == MANAGER_SYSTEM && arg_action == ACTION_RUN) { + if (arg_running_as == SYSTEMD_SYSTEM && arg_action == ACTION_RUN) console_setup(getpid() == 1 && !skip_setup); - make_null_stdio(); - } /* Open the logging devices, if possible and necessary */ log_open(); @@ -1449,10 +1563,10 @@ int main(int argc, char *argv[]) { goto finish; } - if (arg_running_as == MANAGER_SYSTEM) { + if (arg_running_as == SYSTEMD_SYSTEM) { const char *virtualization = NULL; - log_info(PACKAGE_STRING " running in system mode. (" SYSTEMD_FEATURES "; " DISTRIBUTION ")"); + log_info(PACKAGE_STRING " running in system mode. (" SYSTEMD_FEATURES ")"); detect_virtualization(&virtualization); if (virtualization) @@ -1462,15 +1576,17 @@ int main(int argc, char *argv[]) { log_info("Running in initial RAM disk."); } else - log_debug(PACKAGE_STRING " running in user mode. (" SYSTEMD_FEATURES "; " DISTRIBUTION ")"); + log_debug(PACKAGE_STRING " running in user mode. (" SYSTEMD_FEATURES ")"); - if (arg_running_as == MANAGER_SYSTEM && !skip_setup) { + if (arg_running_as == SYSTEMD_SYSTEM && !skip_setup) { locale_setup(); if (arg_show_status || plymouth_running()) status_welcome(); +#ifdef HAVE_KMOD kmod_setup(); +#endif hostname_setup(); machine_id_setup(); loopback_setup(); @@ -1480,7 +1596,7 @@ int main(int argc, char *argv[]) { test_cgroups(); } - if (arg_running_as == MANAGER_SYSTEM && arg_runtime_watchdog > 0) + if (arg_running_as == SYSTEMD_SYSTEM && arg_runtime_watchdog > 0) watchdog_set_timeout(&arg_runtime_watchdog); if (arg_timer_slack_nsec != (nsec_t) -1) @@ -1500,15 +1616,18 @@ int main(int argc, char *argv[]) { } } - if (arg_running_as == MANAGER_USER) { + if (arg_running_as == SYSTEMD_USER) { /* Become reaper of our children */ - r = prctl(PR_SET_CHILD_SUBREAPER, 1); - if (r < 0) - log_error("Failed to prctl(PR_SET_CHILD_SUBREAPER): %s", strerror(-r)); - if (r == -EINVAL) - log_error("Perhaps the kernel version is too old (< 3.4?)"); + if (prctl(PR_SET_CHILD_SUBREAPER, 1) < 0) { + log_warning("Failed to make us a subreaper: %m"); + if (errno == EINVAL) + log_info("Perhaps the kernel version is too old (< 3.4?)"); + } } + if (arg_running_as == SYSTEMD_SYSTEM) + bump_rlimit_nofile(&saved_rlimit_nofile); + r = manager_new(arg_running_as, &m); if (r < 0) { log_error("Failed to allocate manager object: %s", strerror(-r)); @@ -1542,10 +1661,7 @@ int main(int argc, char *argv[]) { /* This will close all file descriptors that were opened, but * not claimed by any unit. */ - if (fds) { - fdset_free(fds); - fds = NULL; - } + fdset_free(fds); if (serialization) { fclose(serialization); @@ -1687,7 +1803,7 @@ finish: manager_free(m); for (j = 0; j < RLIMIT_NLIMITS; j++) - free (arg_default_rlimit[j]); + free(arg_default_rlimit[j]); free(arg_default_unit); strv_free(arg_default_controllers); @@ -1705,6 +1821,12 @@ finish: * rebooted while we do that */ watchdog_close(true); + /* Reset the RLIMIT_NOFILE to the kernel default, so + * that the new systemd can pass the kernel default to + * its child processes */ + if (saved_rlimit_nofile.rlim_cur > 0) + setrlimit(RLIMIT_NOFILE, &saved_rlimit_nofile); + if (switch_root_dir) { /* Kill all remaining processes from the * initrd, but don't wait for them, so that we @@ -1739,7 +1861,7 @@ finish: args[i++] = SYSTEMD_BINARY_PATH; if (switch_root_dir) args[i++] = "--switched-root"; - args[i++] = arg_running_as == MANAGER_SYSTEM ? "--system" : "--user"; + args[i++] = arg_running_as == SYSTEMD_SYSTEM ? "--system" : "--user"; args[i++] = "--deserialize"; args[i++] = sfd; args[i++] = NULL; diff --git a/src/core/manager.c b/src/core/manager.c index bcaf913b5a..df0fd63e87 100644 --- a/src/core/manager.c +++ b/src/core/manager.c @@ -36,12 +36,15 @@ #include <sys/types.h> #include <sys/stat.h> #include <dirent.h> +#include <sys/timerfd.h> #ifdef HAVE_AUDIT #include <libaudit.h> #endif -#include <systemd/sd-daemon.h> +#include "systemd/sd-daemon.h" +#include "systemd/sd-id128.h" +#include "systemd/sd-messages.h" #include "manager.h" #include "transaction.h" @@ -66,6 +69,7 @@ #include "watchdog.h" #include "cgroup-util.h" #include "path-util.h" +#include "audit-fd.h" /* As soon as 16 units are in our GC queue, make sure to run a gc sweep */ #define GC_QUEUE_ENTRIES_MAX 16 @@ -117,27 +121,75 @@ static int manager_setup_notify(Manager *m) { ev.events = EPOLLIN; ev.data.ptr = &m->notify_watch; - if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->notify_watch.fd, &ev) < 0) + if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->notify_watch.fd, &ev) < 0) { + log_error("Failed to add timer change fd to epoll: %m"); return -errno; + } sa.un.sun_path[0] = '@'; m->notify_socket = strdup(sa.un.sun_path); if (!m->notify_socket) - return -ENOMEM; + return log_oom(); log_debug("Using notification socket %s", m->notify_socket); return 0; } +static int manager_setup_time_change(Manager *m) { + struct epoll_event ev; + struct itimerspec its; + + assert(m); + assert(m->time_change_watch.type == WATCH_INVALID); + + /* Uses TFD_TIMER_CANCEL_ON_SET to get notifications whenever + * CLOCK_REALTIME makes a jump relative to CLOCK_MONOTONIC */ + + m->time_change_watch.type = WATCH_TIME_CHANGE; + m->time_change_watch.fd = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK|TFD_CLOEXEC); + if (m->time_change_watch.fd < 0) { + log_error("Failed to create timerfd: %m"); + return -errno; + } + + zero(its); + + /* We only care for the cancellation event, hence we set the + * timeout to the latest possible value. */ + assert_cc(sizeof(time_t) == sizeof(long)); + its.it_value.tv_sec = LONG_MAX; + + if (timerfd_settime(m->time_change_watch.fd, TFD_TIMER_ABSTIME|TFD_TIMER_CANCEL_ON_SET, &its, NULL) < 0) { + log_debug("Failed to set up TFD_TIMER_CANCEL_ON_SET, ignoring: %m"); + close_nointr_nofail(m->time_change_watch.fd); + watch_init(&m->time_change_watch); + return 0; + } + + zero(ev); + ev.events = EPOLLIN; + ev.data.ptr = &m->time_change_watch; + + if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->time_change_watch.fd, &ev) < 0) { + log_error("Failed to add timer change fd to epoll: %m"); + return -errno; + } + + log_debug("Set up TFD_TIMER_CANCEL_ON_SET timerfd."); + + return 0; +} + static int enable_special_signals(Manager *m) { int fd; assert(m); /* Enable that we get SIGINT on control-alt-del. In containers - * this will fail with EPERM, so ignore that. */ - if (reboot(RB_DISABLE_CAD) < 0 && errno != EPERM) + * this will fail with EPERM (older) or EINVAL (newer), so + * ignore that. */ + if (reboot(RB_DISABLE_CAD) < 0 && errno != EPERM && errno != EINVAL) log_warning("Failed to enable ctrl-alt-del handling: %m"); fd = open_terminal("/dev/tty0", O_RDWR|O_NOCTTY|O_CLOEXEC); @@ -195,6 +247,7 @@ static int manager_setup_signals(Manager *m) { SIGRTMIN+21, /* systemd: disable status messages */ SIGRTMIN+22, /* systemd: set log level to LOG_DEBUG */ SIGRTMIN+23, /* systemd: set log level to LOG_INFO */ + SIGRTMIN+24, /* systemd: Immediate exit (--user only) */ SIGRTMIN+26, /* systemd: set log target to journal-or-kmsg */ SIGRTMIN+27, /* systemd: set log target to console */ SIGRTMIN+28, /* systemd: set log target to kmsg */ @@ -203,7 +256,8 @@ static int manager_setup_signals(Manager *m) { assert_se(sigprocmask(SIG_SETMASK, &mask, NULL) == 0); m->signal_watch.type = WATCH_SIGNAL; - if ((m->signal_watch.fd = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC)) < 0) + m->signal_watch.fd = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC); + if (m->signal_watch.fd < 0) return -errno; zero(ev); @@ -213,7 +267,7 @@ static int manager_setup_signals(Manager *m) { if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->signal_watch.fd, &ev) < 0) return -errno; - if (m->running_as == MANAGER_SYSTEM) + if (m->running_as == SYSTEMD_SYSTEM) return enable_special_signals(m); return 0; @@ -234,18 +288,20 @@ static void manager_strip_environment(Manager *m) { strv_remove_prefix(m->environment, "RD_"); } -int manager_new(ManagerRunningAs running_as, Manager **_m) { +int manager_new(SystemdRunningAs running_as, Manager **_m) { Manager *m; int r = -ENOMEM; assert(_m); assert(running_as >= 0); - assert(running_as < _MANAGER_RUNNING_AS_MAX); + assert(running_as < _SYSTEMD_RUNNING_AS_MAX); - if (!(m = new0(Manager, 1))) + m = new0(Manager, 1); + if (!m) return -ENOMEM; - dual_timestamp_get(&m->startup_timestamp); + dual_timestamp_get(&m->userspace_timestamp); + dual_timestamp_from_monotonic(&m->kernel_timestamp, 0); m->running_as = running_as; m->name_data_slot = m->conn_data_slot = m->subscribed_data_slot = -1; @@ -253,11 +309,13 @@ int manager_new(ManagerRunningAs running_as, Manager **_m) { m->pin_cgroupfs_fd = -1; m->idle_pipe[0] = m->idle_pipe[1] = -1; -#ifdef HAVE_AUDIT - m->audit_fd = -1; -#endif + watch_init(&m->signal_watch); + watch_init(&m->mount_watch); + watch_init(&m->swap_watch); + watch_init(&m->udev_watch); + watch_init(&m->time_change_watch); - m->signal_watch.fd = m->mount_watch.fd = m->udev_watch.fd = m->epoll_fd = m->dev_autofs_fd = m->swap_watch.fd = -1; + m->epoll_fd = m->dev_autofs_fd = -1; m->current_job_id = 1; /* start as id #1, so that we can leave #0 around as "null-like" value */ m->environment = strv_copy(environ); @@ -266,7 +324,7 @@ int manager_new(ManagerRunningAs running_as, Manager **_m) { manager_strip_environment(m); - if (running_as == MANAGER_SYSTEM) { + if (running_as == SYSTEMD_SYSTEM) { m->default_controllers = strv_new("cpu", NULL); if (!m->default_controllers) goto fail; @@ -287,29 +345,30 @@ int manager_new(ManagerRunningAs running_as, Manager **_m) { if (!(m->watch_bus = hashmap_new(string_hash_func, string_compare_func))) goto fail; - if ((m->epoll_fd = epoll_create1(EPOLL_CLOEXEC)) < 0) + m->epoll_fd = epoll_create1(EPOLL_CLOEXEC); + if (m->epoll_fd < 0) goto fail; - if ((r = manager_setup_signals(m)) < 0) + r = manager_setup_signals(m); + if (r < 0) goto fail; - if ((r = manager_setup_cgroup(m)) < 0) + r = manager_setup_cgroup(m); + if (r < 0) goto fail; - if ((r = manager_setup_notify(m)) < 0) + r = manager_setup_notify(m); + if (r < 0) goto fail; - /* Try to connect to the busses, if possible. */ - if ((r = bus_init(m, running_as != MANAGER_SYSTEM)) < 0) + r = manager_setup_time_change(m); + if (r < 0) goto fail; -#ifdef HAVE_AUDIT - if ((m->audit_fd = audit_open()) < 0 && - /* If the kernel lacks netlink or audit support, - * don't worry about it. */ - errno != EAFNOSUPPORT && errno != EPROTONOSUPPORT) - log_error("Failed to connect to audit log: %m"); -#endif + /* Try to connect to the busses, if possible. */ + r = bus_init(m, running_as != SYSTEMD_SYSTEM); + if (r < 0) + goto fail; m->taint_usr = dir_is_empty("/usr") > 0; @@ -429,7 +488,7 @@ static unsigned manager_dispatch_gc_queue(Manager *m) { if (u->gc_marker == gc_marker + GC_OFFSET_BAD || u->gc_marker == gc_marker + GC_OFFSET_UNSURE) { - log_debug("Collecting %s", u->id); + log_debug_unit(u->id, "Collecting %s", u->id); u->gc_marker = gc_marker + GC_OFFSET_BAD; unit_add_to_cleanup_queue(u); } @@ -493,11 +552,8 @@ void manager_free(Manager *m) { close_nointr_nofail(m->signal_watch.fd); if (m->notify_watch.fd >= 0) close_nointr_nofail(m->notify_watch.fd); - -#ifdef HAVE_AUDIT - if (m->audit_fd >= 0) - audit_close(m->audit_fd); -#endif + if (m->time_change_watch.fd >= 0) + close_nointr_nofail(m->time_change_watch.fd); free(m->notify_socket); @@ -579,7 +635,8 @@ static void manager_build_unit_path_cache(Manager *m) { STRV_FOREACH(i, m->lookup_paths.unit_path) { struct dirent *de; - if (!(d = opendir(*i))) { + d = opendir(*i); + if (!d) { log_error("Failed to open directory: %m"); continue; } @@ -651,6 +708,16 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) { r = q; } + /* Any fds left? Find some unit which wants them. This is + * useful to allow container managers to pass some file + * descriptors to us pre-initialized. This enables + * socket-based activation of entire containers. */ + if (fdset_size(fds) > 0) { + q = manager_distribute_fds(m, fds); + if (q < 0) + r = q; + } + /* Third, fire things up! */ q = manager_coldplug(m); if (q < 0) @@ -683,7 +750,9 @@ int manager_add_job(Manager *m, JobType type, Unit *unit, JobMode mode, bool ove return -EPERM; } - log_debug("Trying to enqueue job %s/%s/%s", unit->id, job_type_to_string(type), job_mode_to_string(mode)); + log_debug_unit(unit->id, + "Trying to enqueue job %s/%s/%s", unit->id, + job_type_to_string(type), job_mode_to_string(mode)); job_type_collapse(&type, unit); @@ -707,7 +776,9 @@ int manager_add_job(Manager *m, JobType type, Unit *unit, JobMode mode, bool ove if (r < 0) goto tr_abort; - log_debug("Enqueued job %s/%s as %u", unit->id, job_type_to_string(type), (unsigned) tr->anchor_job->id); + log_debug_unit(unit->id, + "Enqueued job %s/%s as %u", unit->id, + job_type_to_string(type), (unsigned) tr->anchor_job->id); if (_ret) *_ret = tr->anchor_job; @@ -730,7 +801,8 @@ int manager_add_job_by_name(Manager *m, JobType type, const char *name, JobMode assert(name); assert(mode < _JOB_MODE_MAX); - if ((r = manager_load_unit(m, name, NULL, NULL, &unit)) < 0) + r = manager_load_unit(m, name, NULL, NULL, &unit); + if (r < 0) return r; return manager_add_job(m, type, unit, mode, override, e, _ret); @@ -842,7 +914,8 @@ int manager_load_unit(Manager *m, const char *name, const char *path, DBusError /* This will load the service information files, but not actually * start any services or anything. */ - if ((r = manager_load_unit_prepare(m, name, path, e, _ret)) != 0) + r = manager_load_unit_prepare(m, name, path, e, _ret); + if (r != 0) return r; manager_dispatch_load_queue(m); @@ -966,7 +1039,8 @@ static int manager_process_notify_fd(Manager *m) { msghdr.msg_control = &control; msghdr.msg_controllen = sizeof(control); - if ((n = recvmsg(m->notify_watch.fd, &msghdr, MSG_DONTWAIT)) <= 0) { + n = recvmsg(m->notify_watch.fd, &msghdr, MSG_DONTWAIT); + if (n <= 0) { if (n >= 0) return -EIO; @@ -986,18 +1060,22 @@ static int manager_process_notify_fd(Manager *m) { ucred = (struct ucred*) CMSG_DATA(&control.cmsghdr); - if (!(u = hashmap_get(m->watch_pids, LONG_TO_PTR(ucred->pid)))) - if (!(u = cgroup_unit_by_pid(m, ucred->pid))) { + u = hashmap_get(m->watch_pids, LONG_TO_PTR(ucred->pid)); + if (!u) { + u = cgroup_unit_by_pid(m, ucred->pid); + if (!u) { log_warning("Cannot find unit for notify message of PID %lu.", (unsigned long) ucred->pid); continue; } + } assert((size_t) n < sizeof(buf)); buf[n] = 0; - if (!(tags = strv_split(buf, "\n\r"))) - return -ENOMEM; + tags = strv_split(buf, "\n\r"); + if (!tags) + return log_oom(); - log_debug("Got notification message for unit %s", u->id); + log_debug_unit(u->id, "Got notification message for unit %s", u->id); if (UNIT_VTABLE(u)->notify_message) UNIT_VTABLE(u)->notify_message(u, ucred->pid, tags); @@ -1036,22 +1114,23 @@ static int manager_dispatch_sigchld(Manager *m) { break; if (si.si_code == CLD_EXITED || si.si_code == CLD_KILLED || si.si_code == CLD_DUMPED) { - char *name = NULL; + char _cleanup_free_ *name = NULL; get_process_comm(si.si_pid, &name); log_debug("Got SIGCHLD for process %lu (%s)", (unsigned long) si.si_pid, strna(name)); - free(name); } /* Let's flush any message the dying child might still * have queued for us. This ensures that the process * still exists in /proc so that we can figure out * which cgroup and hence unit it belongs to. */ - if ((r = manager_process_notify_fd(m)) < 0) + r = manager_process_notify_fd(m); + if (r < 0) return r; /* And now figure out the unit this belongs to */ - if (!(u = hashmap_get(m->watch_pids, LONG_TO_PTR(si.si_pid)))) + u = hashmap_get(m->watch_pids, LONG_TO_PTR(si.si_pid)); + if (!u) u = cgroup_unit_by_pid(m, si.si_pid); /* And now, we actually reap the zombie. */ @@ -1076,7 +1155,8 @@ static int manager_dispatch_sigchld(Manager *m) { if (!u) continue; - log_debug("Child %lu belongs to %s", (long unsigned) si.si_pid, u->id); + log_debug_unit(u->id, + "Child %lu belongs to %s", (long unsigned) si.si_pid, u->id); hashmap_remove(m->watch_pids, LONG_TO_PTR(si.si_pid)); UNIT_VTABLE(u)->sigchld_event(u, si.si_pid, si.si_code, si.si_status); @@ -1091,10 +1171,12 @@ static int manager_start_target(Manager *m, const char *name, JobMode mode) { dbus_error_init(&error); - log_debug("Activating special unit %s", name); + log_debug_unit(name, "Activating special unit %s", name); - if ((r = manager_add_job_by_name(m, JOB_START, name, mode, true, &error, NULL)) < 0) - log_error("Failed to enqueue %s job: %s", name, bus_error(&error, r)); + r = manager_add_job_by_name(m, JOB_START, name, mode, true, &error, NULL); + if (r < 0) + log_error_unit(name, + "Failed to enqueue %s job: %s", name, bus_error(&error, r)); dbus_error_free(&error); @@ -1109,7 +1191,8 @@ static int manager_process_signal_fd(Manager *m) { assert(m); for (;;) { - if ((n = read(m->signal_watch.fd, &sfsi, sizeof(sfsi))) != sizeof(sfsi)) { + n = read(m->signal_watch.fd, &sfsi, sizeof(sfsi)); + if (n != sizeof(sfsi)) { if (n >= 0) return -EIO; @@ -1139,7 +1222,7 @@ static int manager_process_signal_fd(Manager *m) { break; case SIGTERM: - if (m->running_as == MANAGER_SYSTEM) { + if (m->running_as == SYSTEMD_SYSTEM) { /* This is for compatibility with the * original sysvinit */ m->exit_code = MANAGER_REEXECUTE; @@ -1149,7 +1232,7 @@ static int manager_process_signal_fd(Manager *m) { /* Fall through */ case SIGINT: - if (m->running_as == MANAGER_SYSTEM) { + if (m->running_as == SYSTEMD_SYSTEM) { manager_start_target(m, SPECIAL_CTRL_ALT_DEL_TARGET, JOB_REPLACE); break; } @@ -1163,14 +1246,14 @@ static int manager_process_signal_fd(Manager *m) { break; case SIGWINCH: - if (m->running_as == MANAGER_SYSTEM) + if (m->running_as == SYSTEMD_SYSTEM) manager_start_target(m, SPECIAL_KBREQUEST_TARGET, JOB_REPLACE); /* This is a nop on non-init */ break; case SIGPWR: - if (m->running_as == MANAGER_SYSTEM) + if (m->running_as == SYSTEMD_SYSTEM) manager_start_target(m, SPECIAL_SIGPWR_TARGET, JOB_REPLACE); /* This is a nop on non-init */ @@ -1282,6 +1365,15 @@ static int manager_process_signal_fd(Manager *m) { log_notice("Setting log level to info."); break; + case 24: + if (m->running_as == SYSTEMD_USER) { + m->exit_code = MANAGER_EXIT; + return 0; + } + + /* This is a nop on init */ + break; + case 26: log_set_target(LOG_TARGET_JOURNAL_OR_KMSG); log_notice("Setting log target to journal-or-kmsg."); @@ -1363,11 +1455,13 @@ static int process_event(Manager *m, struct epoll_event *ev) { ssize_t k; /* Some timer event, to be dispatched to the units */ - if ((k = read(w->fd, &v, sizeof(v))) != sizeof(v)) { + k = read(w->fd, &v, sizeof(v)); + if (k != sizeof(v)) { if (k < 0 && (errno == EINTR || errno == EAGAIN)) break; + log_error("Failed to read timer event counter: %s", k < 0 ? strerror(-k) : "Short read"); return k < 0 ? -errno : -EIO; } @@ -1401,6 +1495,28 @@ static int process_event(Manager *m, struct epoll_event *ev) { bus_timeout_event(m, w, ev->events); break; + case WATCH_TIME_CHANGE: { + Unit *u; + Iterator i; + + log_struct(LOG_INFO, + MESSAGE_ID(SD_MESSAGE_TIME_CHANGE), + "MESSAGE=Time has been changed", + NULL); + + /* Restart the watch */ + close_nointr_nofail(m->time_change_watch.fd); + watch_init(&m->time_change_watch); + manager_setup_time_change(m); + + HASHMAP_FOREACH(u, m->units, i) { + if (UNIT_VTABLE(u)->time_change) + UNIT_VTABLE(u)->time_change(u); + } + + break; + } + default: log_error("event type=%i", w->type); assert_not_reached("Unknown epoll event type."); @@ -1434,7 +1550,7 @@ int manager_loop(Manager *m) { int n; int wait_msec = -1; - if (m->runtime_watchdog > 0 && m->running_as == MANAGER_SYSTEM) + if (m->runtime_watchdog > 0 && m->running_as == SYSTEMD_SYSTEM) watchdog_ping(); if (!ratelimit_test(&rl)) { @@ -1466,7 +1582,7 @@ int manager_loop(Manager *m) { continue; /* Sleep for half the watchdog time */ - if (m->runtime_watchdog > 0 && m->running_as == MANAGER_SYSTEM) { + if (m->runtime_watchdog > 0 && m->running_as == SYSTEMD_SYSTEM) { wait_msec = (int) (m->runtime_watchdog / 2 / USEC_PER_MSEC); if (wait_msec <= 0) wait_msec = 1; @@ -1532,10 +1648,12 @@ int manager_get_job_from_dbus_path(Manager *m, const char *s, Job **_j) { if (!startswith(s, "/org/freedesktop/systemd1/job/")) return -EINVAL; - if ((r = safe_atou(s + 30, &id)) < 0) + r = safe_atou(s + 30, &id); + if (r < 0) return r; - if (!(j = manager_get_job(m, id))) + j = manager_get_job(m, id); + if (!j) return -ENOENT; *_j = j; @@ -1547,8 +1665,10 @@ void manager_send_unit_audit(Manager *m, Unit *u, int type, bool success) { #ifdef HAVE_AUDIT char *p; + int audit_fd; - if (m->audit_fd < 0) + audit_fd = get_audit_fd(); + if (audit_fd < 0) return; /* Don't generate audit events if the service was already @@ -1556,23 +1676,24 @@ void manager_send_unit_audit(Manager *m, Unit *u, int type, bool success) { if (m->n_reloading > 0) return; - if (m->running_as != MANAGER_SYSTEM) + if (m->running_as != SYSTEMD_SYSTEM) return; if (u->type != UNIT_SERVICE) return; - if (!(p = unit_name_to_prefix_and_instance(u->id))) { - log_error("Failed to allocate unit name for audit message: %s", strerror(ENOMEM)); + p = unit_name_to_prefix_and_instance(u->id); + if (!p) { + log_error_unit(u->id, + "Failed to allocate unit name for audit message: %s", strerror(ENOMEM)); return; } - if (audit_log_user_comm_message(m->audit_fd, type, "", p, NULL, NULL, NULL, success) < 0) { + if (audit_log_user_comm_message(audit_fd, type, "", p, NULL, NULL, NULL, success) < 0) { if (errno == EPERM) { /* We aren't allowed to send audit messages? * Then let's not retry again. */ - audit_close(m->audit_fd); - m->audit_fd = -1; + close_audit_fd(); } else log_warning("Failed to send audit message: %m"); } @@ -1593,7 +1714,7 @@ void manager_send_unit_plymouth(Manager *m, Unit *u) { if (m->n_reloading > 0) return; - if (m->running_as != MANAGER_SYSTEM) + if (m->running_as != SYSTEMD_SYSTEM) return; if (u->type != UNIT_SERVICE && @@ -1692,7 +1813,7 @@ int manager_open_serialization(Manager *m, FILE **_f) { assert(_f); - if (m->running_as == MANAGER_SYSTEM) + if (m->running_as == SYSTEMD_SYSTEM) asprintf(&path, "/run/systemd/dump-%lu-XXXXXX", (unsigned long) getpid()); else asprintf(&path, "/tmp/systemd-dump-%lu-XXXXXX", (unsigned long) getpid()); @@ -1714,7 +1835,8 @@ int manager_open_serialization(Manager *m, FILE **_f) { log_debug("Serializing state to %s", path); free(path); - if (!(f = fdopen(fd, "w+"))) + f = fdopen(fd, "w+"); + if (!f) return -errno; *_f = f; @@ -1739,10 +1861,13 @@ int manager_serialize(Manager *m, FILE *f, FDSet *fds, bool serialize_jobs) { fprintf(f, "n-installed-jobs=%u\n", m->n_installed_jobs); fprintf(f, "n-failed-jobs=%u\n", m->n_failed_jobs); + dual_timestamp_serialize(f, "firmware-timestamp", &m->firmware_timestamp); + dual_timestamp_serialize(f, "kernel-timestamp", &m->kernel_timestamp); + dual_timestamp_serialize(f, "loader-timestamp", &m->loader_timestamp); dual_timestamp_serialize(f, "initrd-timestamp", &m->initrd_timestamp); if (!in_initrd()) { - dual_timestamp_serialize(f, "startup-timestamp", &m->startup_timestamp); + dual_timestamp_serialize(f, "userspace-timestamp", &m->userspace_timestamp); dual_timestamp_serialize(f, "finish-timestamp", &m->finish_timestamp); } @@ -1834,10 +1959,16 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) { log_debug("Failed to parse taint /usr flag %s", l+10); else m->taint_usr = m->taint_usr || b; - } else if (startswith(l, "initrd-timestamp=")) + } else if (startswith(l, "firmware-timestamp=")) + dual_timestamp_deserialize(l+19, &m->firmware_timestamp); + else if (startswith(l, "loader-timestamp=")) + dual_timestamp_deserialize(l+17, &m->loader_timestamp); + else if (startswith(l, "kernel-timestamp=")) + dual_timestamp_deserialize(l+17, &m->kernel_timestamp); + else if (startswith(l, "initrd-timestamp=")) dual_timestamp_deserialize(l+17, &m->initrd_timestamp); - else if (startswith(l, "startup-timestamp=")) - dual_timestamp_deserialize(l+18, &m->startup_timestamp); + else if (startswith(l, "userspace-timestamp=")) + dual_timestamp_deserialize(l+20, &m->userspace_timestamp); else if (startswith(l, "finish-timestamp=")) dual_timestamp_deserialize(l+17, &m->finish_timestamp); else @@ -1860,10 +1991,12 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) { char_array_0(name); - if ((r = manager_load_unit(m, strstrip(name), NULL, NULL, &u)) < 0) + r = manager_load_unit(m, strstrip(name), NULL, NULL, &u); + if (r < 0) goto finish; - if ((r = unit_deserialize(u, f, fds)) < 0) + r = unit_deserialize(u, f, fds); + if (r < 0) goto finish; } @@ -1879,6 +2012,28 @@ finish: return r; } +int manager_distribute_fds(Manager *m, FDSet *fds) { + Unit *u; + Iterator i; + int r; + + assert(m); + + HASHMAP_FOREACH(u, m->units, i) { + + if (fdset_size(fds) <= 0) + break; + + if (UNIT_VTABLE(u)->distribute_fds) { + r = UNIT_VTABLE(u)->distribute_fds(u, fds); + if (r < 0) + return r; + } + } + + return 0; +} + int manager_reload(Manager *m) { int r, q; FILE *f; @@ -1994,7 +2149,8 @@ bool manager_unit_pending_inactive(Manager *m, const char *name) { assert(name); /* Returns true if the unit is inactive or going down */ - if (!(u = manager_get_unit(m, name))) + u = manager_get_unit(m, name); + if (!u) return true; return unit_pending_inactive(u); @@ -2002,7 +2158,7 @@ bool manager_unit_pending_inactive(Manager *m, const char *name) { void manager_check_finished(Manager *m) { char userspace[FORMAT_TIMESPAN_MAX], initrd[FORMAT_TIMESPAN_MAX], kernel[FORMAT_TIMESPAN_MAX], sum[FORMAT_TIMESPAN_MAX]; - usec_t kernel_usec, initrd_usec, userspace_usec, total_usec; + usec_t firmware_usec, loader_usec, kernel_usec, initrd_usec, userspace_usec, total_usec; assert(m); @@ -2020,39 +2176,64 @@ void manager_check_finished(Manager *m) { dual_timestamp_get(&m->finish_timestamp); - if (m->running_as == MANAGER_SYSTEM && detect_container(NULL) <= 0) { + if (m->running_as == SYSTEMD_SYSTEM && detect_container(NULL) <= 0) { - userspace_usec = m->finish_timestamp.monotonic - m->startup_timestamp.monotonic; - total_usec = m->finish_timestamp.monotonic; + /* Note that m->kernel_usec.monotonic is always at 0, + * and m->firmware_usec.monotonic and + * m->loader_usec.monotonic should be considered + * negative values. */ - if (dual_timestamp_is_set(&m->initrd_timestamp)) { + firmware_usec = m->firmware_timestamp.monotonic - m->loader_timestamp.monotonic; + loader_usec = m->loader_timestamp.monotonic - m->kernel_timestamp.monotonic; + userspace_usec = m->finish_timestamp.monotonic - m->userspace_timestamp.monotonic; + total_usec = m->firmware_timestamp.monotonic + m->finish_timestamp.monotonic; - kernel_usec = m->initrd_timestamp.monotonic; - initrd_usec = m->startup_timestamp.monotonic - m->initrd_timestamp.monotonic; + if (dual_timestamp_is_set(&m->initrd_timestamp)) { - log_info("Startup finished in %s (kernel) + %s (initrd) + %s (userspace) = %s.", - format_timespan(kernel, sizeof(kernel), kernel_usec), - format_timespan(initrd, sizeof(initrd), initrd_usec), - format_timespan(userspace, sizeof(userspace), userspace_usec), - format_timespan(sum, sizeof(sum), total_usec)); + kernel_usec = m->initrd_timestamp.monotonic - m->kernel_timestamp.monotonic; + initrd_usec = m->userspace_timestamp.monotonic - m->initrd_timestamp.monotonic; + + if (!log_on_console()) + log_struct(LOG_INFO, + MESSAGE_ID(SD_MESSAGE_STARTUP_FINISHED), + "KERNEL_USEC=%llu", (unsigned long long) kernel_usec, + "INITRD_USEC=%llu", (unsigned long long) initrd_usec, + "USERSPACE_USEC=%llu", (unsigned long long) userspace_usec, + "MESSAGE=Startup finished in %s (kernel) + %s (initrd) + %s (userspace) = %s.", + format_timespan(kernel, sizeof(kernel), kernel_usec), + format_timespan(initrd, sizeof(initrd), initrd_usec), + format_timespan(userspace, sizeof(userspace), userspace_usec), + format_timespan(sum, sizeof(sum), total_usec), + NULL); } else { - kernel_usec = m->startup_timestamp.monotonic; + kernel_usec = m->userspace_timestamp.monotonic - m->kernel_timestamp.monotonic; initrd_usec = 0; - log_info("Startup finished in %s (kernel) + %s (userspace) = %s.", - format_timespan(kernel, sizeof(kernel), kernel_usec), - format_timespan(userspace, sizeof(userspace), userspace_usec), - format_timespan(sum, sizeof(sum), total_usec)); + if (!log_on_console()) + log_struct(LOG_INFO, + MESSAGE_ID(SD_MESSAGE_STARTUP_FINISHED), + "KERNEL_USEC=%llu", (unsigned long long) kernel_usec, + "USERSPACE_USEC=%llu", (unsigned long long) userspace_usec, + "MESSAGE=Startup finished in %s (kernel) + %s (userspace) = %s.", + format_timespan(kernel, sizeof(kernel), kernel_usec), + format_timespan(userspace, sizeof(userspace), userspace_usec), + format_timespan(sum, sizeof(sum), total_usec), + NULL); } } else { - userspace_usec = initrd_usec = kernel_usec = 0; - total_usec = m->finish_timestamp.monotonic - m->startup_timestamp.monotonic; - - log_debug("Startup finished in %s.", - format_timespan(sum, sizeof(sum), total_usec)); + firmware_usec = loader_usec = initrd_usec = kernel_usec = 0; + total_usec = userspace_usec = m->finish_timestamp.monotonic - m->userspace_timestamp.monotonic; + + if (!log_on_console()) + log_struct(LOG_INFO, + MESSAGE_ID(SD_MESSAGE_STARTUP_FINISHED), + "USERSPACE_USEC=%llu", (unsigned long long) userspace_usec, + "MESSAGE=Startup finished in %s.", + format_timespan(sum, sizeof(sum), total_usec), + NULL); } - bus_broadcast_finished(m, kernel_usec, initrd_usec, userspace_usec, total_usec); + bus_broadcast_finished(m, firmware_usec, loader_usec, kernel_usec, initrd_usec, userspace_usec, total_usec); sd_notifyf(false, "READY=1\nSTATUS=Startup finished in %s.", @@ -2070,7 +2251,7 @@ static int create_generator_dir(Manager *m, char **generator, const char *name) if (*generator) return 0; - if (m->running_as == MANAGER_SYSTEM && getpid() == 1) { + if (m->running_as == SYSTEMD_SYSTEM && getpid() == 1) { p = strappend("/run/systemd/", name); if (!p) @@ -2122,7 +2303,7 @@ void manager_run_generators(Manager *m) { assert(m); - generator_path = m->running_as == MANAGER_SYSTEM ? SYSTEM_GENERATOR_PATH : USER_GENERATOR_PATH; + generator_path = m->running_as == SYSTEMD_SYSTEM ? SYSTEM_GENERATOR_PATH : USER_GENERATOR_PATH; d = opendir(generator_path); if (!d) { if (errno == ENOENT) @@ -2224,7 +2405,7 @@ void manager_recheck_journal(Manager *m) { assert(m); - if (m->running_as != MANAGER_SYSTEM) + if (m->running_as != SYSTEMD_SYSTEM) return; u = manager_get_unit(m, SPECIAL_JOURNALD_SOCKET); @@ -2247,7 +2428,7 @@ void manager_recheck_journal(Manager *m) { void manager_set_show_status(Manager *m, bool b) { assert(m); - if (m->running_as != MANAGER_SYSTEM) + if (m->running_as != SYSTEMD_SYSTEM) return; m->show_status = b; @@ -2261,7 +2442,7 @@ void manager_set_show_status(Manager *m, bool b) { bool manager_get_show_status(Manager *m) { assert(m); - if (m->running_as != MANAGER_SYSTEM) + if (m->running_as != SYSTEMD_SYSTEM) return false; if (m->show_status) @@ -2273,9 +2454,9 @@ bool manager_get_show_status(Manager *m) { return plymouth_running(); } -static const char* const manager_running_as_table[_MANAGER_RUNNING_AS_MAX] = { - [MANAGER_SYSTEM] = "system", - [MANAGER_USER] = "user" -}; +void watch_init(Watch *w) { + assert(w); -DEFINE_STRING_TABLE_LOOKUP(manager_running_as, ManagerRunningAs); + w->type = WATCH_INVALID; + w->fd = -1; +} diff --git a/src/core/manager.h b/src/core/manager.h index 603c2dc318..cc4edf8f1e 100644 --- a/src/core/manager.h +++ b/src/core/manager.h @@ -49,13 +49,6 @@ typedef enum ManagerExitCode { _MANAGER_EXIT_CODE_INVALID = -1 } ManagerExitCode; -typedef enum ManagerRunningAs { - MANAGER_SYSTEM, - MANAGER_USER, - _MANAGER_RUNNING_AS_MAX, - _MANAGER_RUNNING_AS_INVALID = -1 -} ManagerRunningAs; - enum WatchType { WATCH_INVALID, WATCH_SIGNAL, @@ -67,7 +60,8 @@ enum WatchType { WATCH_SWAP, WATCH_UDEV, WATCH_DBUS_WATCH, - WATCH_DBUS_TIMEOUT + WATCH_DBUS_TIMEOUT, + WATCH_TIME_CHANGE }; struct Watch { @@ -132,6 +126,7 @@ struct Manager { Watch notify_watch; Watch signal_watch; + Watch time_change_watch; int epoll_fd; @@ -146,8 +141,11 @@ struct Manager { usec_t runtime_watchdog; usec_t shutdown_watchdog; + dual_timestamp firmware_timestamp; + dual_timestamp loader_timestamp; + dual_timestamp kernel_timestamp; dual_timestamp initrd_timestamp; - dual_timestamp startup_timestamp; + dual_timestamp userspace_timestamp; dual_timestamp finish_timestamp; char *generator_unit_path; @@ -204,13 +202,8 @@ struct Manager { * file system */ int pin_cgroupfs_fd; - /* Audit fd */ -#ifdef HAVE_AUDIT - int audit_fd; -#endif - /* Flags */ - ManagerRunningAs running_as; + SystemdRunningAs running_as; ManagerExitCode exit_code:5; bool dispatching_load_queue:1; @@ -239,7 +232,7 @@ struct Manager { char *switch_root_init; }; -int manager_new(ManagerRunningAs running_as, Manager **m); +int manager_new(SystemdRunningAs running_as, Manager **m); void manager_free(Manager *m); int manager_enumerate(Manager *m); @@ -279,6 +272,7 @@ int manager_open_serialization(Manager *m, FILE **_f); int manager_serialize(Manager *m, FILE *f, FDSet *fds, bool serialize_jobs); int manager_deserialize(Manager *m, FILE *f, FDSet *fds); +int manager_distribute_fds(Manager *m, FDSet *fds); int manager_reload(Manager *m); @@ -301,5 +295,4 @@ void manager_recheck_journal(Manager *m); void manager_set_show_status(Manager *m, bool b); bool manager_get_show_status(Manager *m); -const char *manager_running_as_to_string(ManagerRunningAs i); -ManagerRunningAs manager_running_as_from_string(const char *s); +void watch_init(Watch *w); diff --git a/src/core/mount-setup.c b/src/core/mount-setup.c index 07794df049..98614d0c3e 100644 --- a/src/core/mount-setup.c +++ b/src/core/mount-setup.c @@ -40,18 +40,26 @@ #include "mkdir.h" #include "path-util.h" #include "missing.h" +#include "virt.h" #ifndef TTY_GID #define TTY_GID 5 #endif +typedef enum MountMode { + MNT_NONE = 0, + MNT_FATAL = 1 << 0, + MNT_IN_CONTAINER = 1 << 1, +} MountMode; + typedef struct MountPoint { const char *what; const char *where; const char *type; const char *options; unsigned long flags; - bool fatal; + bool (*condition_fn)(void); + MountMode mode; } MountPoint; /* The first three entries we might need before SELinux is up. The @@ -60,15 +68,26 @@ typedef struct MountPoint { #define N_EARLY_MOUNT 4 static const MountPoint mount_table[] = { - { "proc", "/proc", "proc", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, true }, - { "sysfs", "/sys", "sysfs", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, true }, - { "devtmpfs", "/dev", "devtmpfs", "mode=755", MS_NOSUID|MS_STRICTATIME, true }, - { "securityfs", "/sys/kernel/security", "securityfs", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, false }, - { "tmpfs", "/dev/shm", "tmpfs", "mode=1777", MS_NOSUID|MS_NODEV|MS_STRICTATIME, true }, - { "devpts", "/dev/pts", "devpts", "mode=620,gid=" STRINGIFY(TTY_GID), MS_NOSUID|MS_NOEXEC, false }, - { "tmpfs", "/run", "tmpfs", "mode=755", MS_NOSUID|MS_NODEV|MS_STRICTATIME, true }, - { "tmpfs", "/sys/fs/cgroup", "tmpfs", "mode=755", MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME, false }, - { "cgroup", "/sys/fs/cgroup/systemd", "cgroup", "none,name=systemd", MS_NOSUID|MS_NOEXEC|MS_NODEV, false }, + { "proc", "/proc", "proc", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, + NULL, MNT_FATAL|MNT_IN_CONTAINER }, + { "sysfs", "/sys", "sysfs", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, + NULL, MNT_FATAL|MNT_IN_CONTAINER }, + { "devtmpfs", "/dev", "devtmpfs", "mode=755", MS_NOSUID|MS_STRICTATIME, + NULL, MNT_FATAL|MNT_IN_CONTAINER }, + { "securityfs", "/sys/kernel/security", "securityfs", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, + NULL, MNT_NONE }, + { "efivarfs", "/sys/firmware/efi/efivars", "efivarfs", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, + is_efiboot, MNT_NONE }, + { "tmpfs", "/dev/shm", "tmpfs", "mode=1777", MS_NOSUID|MS_NODEV|MS_STRICTATIME, + NULL, MNT_FATAL|MNT_IN_CONTAINER }, + { "devpts", "/dev/pts", "devpts", "mode=620,gid=" STRINGIFY(TTY_GID), MS_NOSUID|MS_NOEXEC, + NULL, MNT_IN_CONTAINER }, + { "tmpfs", "/run", "tmpfs", "mode=755", MS_NOSUID|MS_NODEV|MS_STRICTATIME, + NULL, MNT_FATAL|MNT_IN_CONTAINER }, + { "tmpfs", "/sys/fs/cgroup", "tmpfs", "mode=755", MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME, + NULL, MNT_IN_CONTAINER }, + { "cgroup", "/sys/fs/cgroup/systemd", "cgroup", "none,name=systemd", MS_NOSUID|MS_NOEXEC|MS_NODEV, + NULL, MNT_IN_CONTAINER }, }; /* These are API file systems that might be mounted by other software, @@ -86,10 +105,7 @@ static const char ignore_paths[] = /* Container bind mounts */ "/proc/sys\0" "/dev/console\0" - "/proc/kmsg\0" - "/etc/localtime\0" - "/etc/timezone\0" - "/etc/machine-id\0"; + "/proc/kmsg\0"; bool mount_point_is_api(const char *path) { unsigned i; @@ -119,16 +135,24 @@ static int mount_one(const MountPoint *p, bool relabel) { assert(p); + if (p->condition_fn && !p->condition_fn()) + return 0; + /* Relabel first, just in case */ if (relabel) label_fix(p->where, true, true); - if ((r = path_is_mount_point(p->where, true)) < 0) + r = path_is_mount_point(p->where, true); + if (r < 0) return r; if (r > 0) return 0; + /* Skip securityfs in a container */ + if (!(p->mode & MNT_IN_CONTAINER) && detect_container(NULL) > 0) + return 0; + /* The access mode here doesn't really matter too much, since * the mounted file system will take precedence anyway. */ mkdir_p_label(p->where, 0755); @@ -144,8 +168,8 @@ static int mount_one(const MountPoint *p, bool relabel) { p->type, p->flags, p->options) < 0) { - log_full(p->fatal ? LOG_ERR : LOG_DEBUG, "Failed to mount %s: %s", p->where, strerror(errno)); - return p->fatal ? -errno : 0; + log_full((p->mode & MNT_FATAL) ? LOG_ERR : LOG_DEBUG, "Failed to mount %s: %s", p->where, strerror(errno)); + return (p->mode & MNT_FATAL) ? -errno : 0; } /* Relabel again, since we now mounted something fresh here */ @@ -190,8 +214,7 @@ int mount_cgroup_controllers(char ***join_controllers) { controllers = set_new(string_hash_func, string_compare_func); if (!controllers) { - r = -ENOMEM; - log_error("Failed to allocate controller set."); + r = log_oom(); goto finish; } @@ -262,9 +285,8 @@ int mount_cgroup_controllers(char ***join_controllers) { options = strv_join(*k, ","); if (!options) { - log_error("Failed to join options"); free(controller); - r = -ENOMEM; + r = log_oom(); goto finish; } @@ -275,9 +297,8 @@ int mount_cgroup_controllers(char ***join_controllers) { where = strappend("/sys/fs/cgroup/", options); if (!where) { - log_error("Failed to build path"); free(options); - r = -ENOMEM; + r = log_oom(); goto finish; } @@ -287,7 +308,6 @@ int mount_cgroup_controllers(char ***join_controllers) { p.type = "cgroup"; p.options = options; p.flags = MS_NOSUID|MS_NOEXEC|MS_NODEV; - p.fatal = false; r = mount_one(&p, true); free(controller); @@ -306,8 +326,7 @@ int mount_cgroup_controllers(char ***join_controllers) { t = strappend("/sys/fs/cgroup/", *i); if (!t) { - log_error("Failed to build path"); - r = -ENOMEM; + r = log_oom(); free(options); goto finish; } @@ -402,7 +421,17 @@ int mount_setup(bool loaded_policy) { /* Create a few default symlinks, which are normally created * by udevd, but some scripts might need them before we start * udevd. */ - dev_setup(); + dev_setup(NULL); + + /* Mark the root directory as shared in regards to mount + * propagation. The kernel defaults to "private", but we think + * it makes more sense to have a default of "shared" so that + * nspawn and the container tools work out of the box. If + * specific setups need other settings they can reset the + * propagation mode to private if needed. */ + if (detect_container(NULL) <= 0) + if (mount(NULL, "/", NULL, MS_REC|MS_SHARED, NULL) < 0) + log_warning("Failed to set up the root directory for shared mount propagation: %m"); /* Create a few directories we always want around */ mkdir_label("/run/systemd", 0755); diff --git a/src/core/mount.c b/src/core/mount.c index 83e51a7aba..5d2b010013 100644 --- a/src/core/mount.c +++ b/src/core/mount.c @@ -30,6 +30,7 @@ #include "load-fragment.h" #include "load-dropin.h" #include "log.h" +#include "sd-messages.h" #include "strv.h" #include "mkdir.h" #include "path-util.h" @@ -221,9 +222,11 @@ static int mount_add_swap_links(Mount *m) { assert(m); - LIST_FOREACH(units_by_type, other, UNIT(m)->manager->units_by_type[UNIT_SWAP]) - if ((r = swap_add_one_mount_link(SWAP(other), m)) < 0) + LIST_FOREACH(units_by_type, other, UNIT(m)->manager->units_by_type[UNIT_SWAP]) { + r = swap_add_one_mount_link(SWAP(other), m); + if (r < 0) return r; + } return 0; } @@ -234,9 +237,11 @@ static int mount_add_path_links(Mount *m) { assert(m); - LIST_FOREACH(units_by_type, other, UNIT(m)->manager->units_by_type[UNIT_PATH]) - if ((r = path_add_one_mount_link(PATH(other), m)) < 0) + LIST_FOREACH(units_by_type, other, UNIT(m)->manager->units_by_type[UNIT_PATH]) { + r = path_add_one_mount_link(PATH(other), m); + if (r < 0) return r; + } return 0; } @@ -247,9 +252,11 @@ static int mount_add_automount_links(Mount *m) { assert(m); - LIST_FOREACH(units_by_type, other, UNIT(m)->manager->units_by_type[UNIT_AUTOMOUNT]) - if ((r = automount_add_one_mount_link(AUTOMOUNT(other), m)) < 0) + LIST_FOREACH(units_by_type, other, UNIT(m)->manager->units_by_type[UNIT_AUTOMOUNT]) { + r = automount_add_one_mount_link(AUTOMOUNT(other), m); + if (r < 0) return r; + } return 0; } @@ -260,9 +267,11 @@ static int mount_add_socket_links(Mount *m) { assert(m); - LIST_FOREACH(units_by_type, other, UNIT(m)->manager->units_by_type[UNIT_SOCKET]) - if ((r = socket_add_one_mount_link(SOCKET(other), m)) < 0) + LIST_FOREACH(units_by_type, other, UNIT(m)->manager->units_by_type[UNIT_SOCKET]) { + r = socket_add_one_mount_link(SOCKET(other), m); + if (r < 0) return r; + } return 0; } @@ -352,17 +361,21 @@ static int mount_add_device_links(Mount *m) { if (!p->what) return 0; - if (!mount_is_bind(p) && - !path_equal(m->where, "/")) { - r = unit_add_node_link(UNIT(m), p->what, false); - if (r < 0) - return r; - } + if (mount_is_bind(p)) + return 0; + + if (!is_device_path(p->what)) + return 0; + + if (path_equal(m->where, "/")) + return 0; + + r = unit_add_node_link(UNIT(m), p->what, false); + if (r < 0) + return r; if (p->passno > 0 && - !mount_is_bind(p) && - !path_equal(m->where, "/") && - UNIT(m)->manager->running_as == MANAGER_SYSTEM) { + UNIT(m)->manager->running_as == SYSTEMD_SYSTEM) { char *name; Unit *fsck; /* Let's add in the fsck service */ @@ -374,7 +387,8 @@ static int mount_add_device_links(Mount *m) { r = manager_load_unit_prepare(UNIT(m)->manager, name, NULL, NULL, &fsck); if (r < 0) { - log_warning("Failed to prepare unit %s: %s", name, strerror(-r)); + log_warning_unit(name, + "Failed to prepare unit %s: %s", name, strerror(-r)); free(name); return r; } @@ -396,7 +410,7 @@ static int mount_add_quota_links(Mount *m) { assert(m); - if (UNIT(m)->manager->running_as != MANAGER_SYSTEM) + if (UNIT(m)->manager->running_as != SYSTEMD_SYSTEM) return 0; p = get_mount_parameters_fragment(m); @@ -424,7 +438,7 @@ static int mount_add_default_dependencies(Mount *m) { assert(m); - if (UNIT(m)->manager->running_as != MANAGER_SYSTEM) + if (UNIT(m)->manager->running_as != SYSTEMD_SYSTEM) return 0; p = get_mount_parameters_fragment(m); @@ -485,7 +499,9 @@ static int mount_fix_timeouts(Mount *m) { free(t); if (r < 0) { - log_warning("Failed to parse timeout for %s, ignoring: %s", m->where, timeout); + log_warning_unit(UNIT(m)->id, + "Failed to parse timeout for %s, ignoring: %s", + m->where, timeout); return r; } @@ -517,22 +533,29 @@ static int mount_verify(Mount *m) { free(e); if (!b) { - log_error("%s's Where setting doesn't match unit name. Refusing.", UNIT(m)->id); + log_error_unit(UNIT(m)->id, + "%s's Where setting doesn't match unit name. Refusing.", + UNIT(m)->id); return -EINVAL; } if (mount_point_is_api(m->where) || mount_point_ignore(m->where)) { - log_error("Cannot create mount unit for API file system %s. Refusing.", m->where); + log_error_unit(UNIT(m)->id, + "Cannot create mount unit for API file system %s. Refusing.", + m->where); return -EINVAL; } if (UNIT(m)->fragment_path && !m->parameters_fragment.what) { - log_error("%s's What setting is missing. Refusing.", UNIT(m)->id); + log_error_unit(UNIT(m)->id, + "%s's What setting is missing. Refusing.", UNIT(m)->id); return -EBADMSG; } if (m->exec_context.pam_name && m->kill_context.kill_mode != KILL_CONTROL_GROUP) { - log_error("%s has PAM enabled. Kill mode must be set to 'control-group'. Refusing.", UNIT(m)->id); + log_error_unit(UNIT(m)->id, + "%s has PAM enabled. Kill mode must be set to control-group'. Refusing.", + UNIT(m)->id); return -EINVAL; } @@ -543,10 +566,6 @@ static int mount_add_extras(Mount *m) { Unit *u = UNIT(m); int r; - r = unit_add_exec_dependencies(u, &m->exec_context); - if (r < 0) - return r; - if (UNIT(m)->fragment_path) m->from_fragment = true; @@ -558,6 +577,10 @@ static int mount_add_extras(Mount *m) { path_kill_slashes(m->where); + r = unit_add_exec_dependencies(u, &m->exec_context); + if (r < 0) + return r; + if (!UNIT(m)->description) { r = unit_set_description(u, m->where); if (r < 0) @@ -699,10 +722,11 @@ static void mount_set_state(Mount *m, MountState state) { } if (state != old_state) - log_debug("%s changed %s -> %s", - UNIT(m)->id, - mount_state_to_string(old_state), - mount_state_to_string(state)); + log_debug_unit(UNIT(m)->id, + "%s changed %s -> %s", + UNIT(m)->id, + mount_state_to_string(old_state), + mount_state_to_string(state)); unit_notify(UNIT(m), state_translation_table[old_state], state_translation_table[state], m->reload_result == MOUNT_SUCCESS); m->reload_result = MOUNT_SUCCESS; @@ -737,10 +761,12 @@ static int mount_coldplug(Unit *u) { if (m->control_pid <= 0) return -EBADMSG; - if ((r = unit_watch_pid(UNIT(m), m->control_pid)) < 0) + r = unit_watch_pid(UNIT(m), m->control_pid); + if (r < 0) return r; - if ((r = unit_watch_timer(UNIT(m), m->timeout_usec, &m->timer_watch)) < 0) + r = unit_watch_timer(UNIT(m), CLOCK_MONOTONIC, true, m->timeout_usec, &m->timer_watch); + if (r < 0) return r; } @@ -796,7 +822,8 @@ static int mount_spawn(Mount *m, ExecCommand *c, pid_t *_pid) { assert(c); assert(_pid); - if ((r = unit_watch_timer(UNIT(m), m->timeout_usec, &m->timer_watch)) < 0) + r = unit_watch_timer(UNIT(m), CLOCK_MONOTONIC, true, m->timeout_usec, &m->timer_watch); + if (r < 0) goto fail; if ((r = exec_spawn(c, @@ -866,7 +893,9 @@ static void mount_enter_signal(Mount *m, MountState state, MountResult f) { if (m->control_pid > 0) { if (kill_and_sigcont(m->control_pid, sig) < 0 && errno != ESRCH) - log_warning("Failed to kill control process %li: %m", (long) m->control_pid); + log_warning_unit(UNIT(m)->id, + "Failed to kill control process %li: %m", + (long) m->control_pid); else wait_for_exit = true; } @@ -886,7 +915,9 @@ static void mount_enter_signal(Mount *m, MountState state, MountResult f) { r = cgroup_bonding_kill_list(UNIT(m)->cgroup_bondings, sig, true, false, pid_set, NULL); if (r < 0) { if (r != -EAGAIN && r != -ESRCH && r != -ENOENT) - log_warning("Failed to kill control group: %s", strerror(-r)); + log_warning_unit(UNIT(m)->id, + "Failed to kill control group: %s", + strerror(-r)); } else if (r > 0) wait_for_exit = true; @@ -896,7 +927,8 @@ static void mount_enter_signal(Mount *m, MountState state, MountResult f) { } if (wait_for_exit) { - if ((r = unit_watch_timer(UNIT(m), m->timeout_usec, &m->timer_watch)) < 0) + r = unit_watch_timer(UNIT(m), CLOCK_MONOTONIC, true, m->timeout_usec, &m->timer_watch); + if (r < 0) goto fail; mount_set_state(m, state); @@ -908,7 +940,8 @@ static void mount_enter_signal(Mount *m, MountState state, MountResult f) { return; fail: - log_warning("%s failed to kill processes: %s", UNIT(m)->id, strerror(-r)); + log_warning_unit(UNIT(m)->id, + "%s failed to kill processes: %s", UNIT(m)->id, strerror(-r)); if (state == MOUNT_REMOUNTING_SIGTERM || state == MOUNT_REMOUNTING_SIGKILL) mount_enter_mounted(m, MOUNT_FAILURE_RESOURCES); @@ -919,6 +952,18 @@ fail: set_free(pid_set); } +void warn_if_dir_nonempty(const char *unit, const char* where) { + if (dir_is_empty(where) > 0) + return; + log_struct(LOG_NOTICE, + "MESSAGE=%s: Directory %s to mount over is not empty, mounting anyway.", + unit, where, + "WHERE=%s", where, + "_SYSTEMD_UNIT=%s", unit, + MESSAGE_ID(SD_MESSAGE_OVERMOUNTING), + NULL); +} + static void mount_enter_unmounting(Mount *m) { int r; @@ -944,7 +989,9 @@ static void mount_enter_unmounting(Mount *m) { return; fail: - log_warning("%s failed to run 'umount' task: %s", UNIT(m)->id, strerror(-r)); + log_warning_unit(UNIT(m)->id, + "%s failed to run 'umount' task: %s", + UNIT(m)->id, strerror(-r)); mount_enter_mounted(m, MOUNT_FAILURE_RESOURCES); } @@ -959,6 +1006,8 @@ static void mount_enter_mounting(Mount *m) { mkdir_p_label(m->where, m->directory_mode); + warn_if_dir_nonempty(m->meta.id, m->where); + /* Create the source directory for bind-mounts if needed */ p = get_mount_parameters_fragment(m); if (p && mount_is_bind(p)) @@ -981,7 +1030,8 @@ static void mount_enter_mounting(Mount *m) { mount_unwatch_control_pid(m); - if ((r = mount_spawn(m, m->control_command, &m->control_pid)) < 0) + r = mount_spawn(m, m->control_command, &m->control_pid); + if (r < 0) goto fail; mount_set_state(m, MOUNT_MOUNTING); @@ -989,7 +1039,9 @@ static void mount_enter_mounting(Mount *m) { return; fail: - log_warning("%s failed to run 'mount' task: %s", UNIT(m)->id, strerror(-r)); + log_warning_unit(UNIT(m)->id, + "%s failed to run 'mount' task: %s", + UNIT(m)->id, strerror(-r)); mount_enter_dead(m, MOUNT_FAILURE_RESOURCES); } @@ -1047,7 +1099,9 @@ static void mount_enter_remounting(Mount *m) { return; fail: - log_warning("%s failed to run 'remount' task: %s", UNIT(m)->id, strerror(-r)); + log_warning_unit(UNIT(m)->id, + "%s failed to run 'remount' task: %s", + UNIT(m)->id, strerror(-r)); m->reload_result = MOUNT_FAILURE_RESOURCES; mount_enter_mounted(m, MOUNT_SUCCESS); } @@ -1149,7 +1203,7 @@ static int mount_deserialize_item(Unit *u, const char *key, const char *value, F MountState state; if ((state = mount_state_from_string(value)) < 0) - log_debug("Failed to parse state value %s", value); + log_debug_unit(u->id, "Failed to parse state value %s", value); else m->deserialized_state = state; } else if (streq(key, "result")) { @@ -1157,7 +1211,8 @@ static int mount_deserialize_item(Unit *u, const char *key, const char *value, F f = mount_result_from_string(value); if (f < 0) - log_debug("Failed to parse result value %s", value); + log_debug_unit(UNIT(m)->id, + "Failed to parse result value %s", value); else if (f != MOUNT_SUCCESS) m->result = f; @@ -1166,7 +1221,8 @@ static int mount_deserialize_item(Unit *u, const char *key, const char *value, F f = mount_result_from_string(value); if (f < 0) - log_debug("Failed to parse reload result value %s", value); + log_debug_unit(UNIT(m)->id, + "Failed to parse reload result value %s", value); else if (f != MOUNT_SUCCESS) m->reload_result = f; @@ -1174,21 +1230,24 @@ static int mount_deserialize_item(Unit *u, const char *key, const char *value, F pid_t pid; if (parse_pid(value, &pid) < 0) - log_debug("Failed to parse control-pid value %s", value); + log_debug_unit(UNIT(m)->id, + "Failed to parse control-pid value %s", value); else m->control_pid = pid; } else if (streq(key, "control-command")) { MountExecCommand id; if ((id = mount_exec_command_from_string(value)) < 0) - log_debug("Failed to parse exec-command value %s", value); + log_debug_unit(UNIT(m)->id, + "Failed to parse exec-command value %s", value); else { m->control_command_id = id; m->control_command = m->exec_command + id; } } else - log_debug("Unknown serialization key '%s'", key); + log_debug_unit(UNIT(m)->id, + "Unknown serialization key '%s'", key); return 0; } @@ -1225,7 +1284,7 @@ static void mount_sigchld_event(Unit *u, pid_t pid, int code, int status) { m->control_pid = 0; - if (is_clean_exit(code, status)) + if (is_clean_exit(code, status, NULL)) f = MOUNT_SUCCESS; else if (code == CLD_EXITED) f = MOUNT_FAILURE_EXIT_CODE; @@ -1246,8 +1305,9 @@ static void mount_sigchld_event(Unit *u, pid_t pid, int code, int status) { m->control_command_id = _MOUNT_EXEC_COMMAND_INVALID; } - log_full(f == MOUNT_SUCCESS ? LOG_DEBUG : LOG_NOTICE, - "%s mount process exited, code=%s status=%i", u->id, sigchld_code_to_string(code), status); + log_full_unit(f == MOUNT_SUCCESS ? LOG_DEBUG : LOG_NOTICE, u->id, + "%s mount process exited, code=%s status=%i", + u->id, sigchld_code_to_string(code), status); /* Note that mount(8) returning and the kernel sending us a * mount table change event might happen out-of-order. If an @@ -1314,27 +1374,33 @@ static void mount_timer_event(Unit *u, uint64_t elapsed, Watch *w) { case MOUNT_MOUNTING: case MOUNT_MOUNTING_DONE: - log_warning("%s mounting timed out. Stopping.", u->id); + log_warning_unit(u->id, + "%s mounting timed out. Stopping.", u->id); mount_enter_signal(m, MOUNT_MOUNTING_SIGTERM, MOUNT_FAILURE_TIMEOUT); break; case MOUNT_REMOUNTING: - log_warning("%s remounting timed out. Stopping.", u->id); + log_warning_unit(u->id, + "%s remounting timed out. Stopping.", u->id); m->reload_result = MOUNT_FAILURE_TIMEOUT; mount_enter_mounted(m, MOUNT_SUCCESS); break; case MOUNT_UNMOUNTING: - log_warning("%s unmounting timed out. Stopping.", u->id); + log_warning_unit(u->id, + "%s unmounting timed out. Stopping.", u->id); mount_enter_signal(m, MOUNT_UNMOUNTING_SIGTERM, MOUNT_FAILURE_TIMEOUT); break; case MOUNT_MOUNTING_SIGTERM: if (m->kill_context.send_sigkill) { - log_warning("%s mounting timed out. Killing.", u->id); + log_warning_unit(u->id, + "%s mounting timed out. Killing.", u->id); mount_enter_signal(m, MOUNT_MOUNTING_SIGKILL, MOUNT_FAILURE_TIMEOUT); } else { - log_warning("%s mounting timed out. Skipping SIGKILL. Ignoring.", u->id); + log_warning_unit(u->id, + "%s mounting timed out. Skipping SIGKILL. Ignoring.", + u->id); if (m->from_proc_self_mountinfo) mount_enter_mounted(m, MOUNT_FAILURE_TIMEOUT); @@ -1345,10 +1411,13 @@ static void mount_timer_event(Unit *u, uint64_t elapsed, Watch *w) { case MOUNT_REMOUNTING_SIGTERM: if (m->kill_context.send_sigkill) { - log_warning("%s remounting timed out. Killing.", u->id); + log_warning_unit(u->id, + "%s remounting timed out. Killing.", u->id); mount_enter_signal(m, MOUNT_REMOUNTING_SIGKILL, MOUNT_FAILURE_TIMEOUT); } else { - log_warning("%s remounting timed out. Skipping SIGKILL. Ignoring.", u->id); + log_warning_unit(u->id, + "%s remounting timed out. Skipping SIGKILL. Ignoring.", + u->id); if (m->from_proc_self_mountinfo) mount_enter_mounted(m, MOUNT_FAILURE_TIMEOUT); @@ -1359,10 +1428,13 @@ static void mount_timer_event(Unit *u, uint64_t elapsed, Watch *w) { case MOUNT_UNMOUNTING_SIGTERM: if (m->kill_context.send_sigkill) { - log_warning("%s unmounting timed out. Killing.", u->id); + log_warning_unit(u->id, + "%s unmounting timed out. Killing.", u->id); mount_enter_signal(m, MOUNT_UNMOUNTING_SIGKILL, MOUNT_FAILURE_TIMEOUT); } else { - log_warning("%s unmounting timed out. Skipping SIGKILL. Ignoring.", u->id); + log_warning_unit(u->id, + "%s unmounting timed out. Skipping SIGKILL. Ignoring.", + u->id); if (m->from_proc_self_mountinfo) mount_enter_mounted(m, MOUNT_FAILURE_TIMEOUT); @@ -1374,7 +1446,9 @@ static void mount_timer_event(Unit *u, uint64_t elapsed, Watch *w) { case MOUNT_MOUNTING_SIGKILL: case MOUNT_REMOUNTING_SIGKILL: case MOUNT_UNMOUNTING_SIGKILL: - log_warning("%s mount process still around after SIGKILL. Ignoring.", u->id); + log_warning_unit(u->id, + "%s mount process still around after SIGKILL. Ignoring.", + u->id); if (m->from_proc_self_mountinfo) mount_enter_mounted(m, MOUNT_FAILURE_TIMEOUT); @@ -1400,6 +1474,7 @@ static int mount_add_one( bool delete; char *e, *w = NULL, *o = NULL, *f = NULL; MountParameters *p; + bool load_extras = false; assert(m); assert(what); @@ -1450,12 +1525,21 @@ static int mount_add_one( delete = false; free(e); + if (!MOUNT(u)->where) { + MOUNT(u)->where = strdup(where); + if (!MOUNT(u)->where) { + r = -ENOMEM; + goto fail; + } + } + if (u->load_state == UNIT_ERROR) { u->load_state = UNIT_LOADED; u->load_error = 0; - r = mount_add_extras(MOUNT(u)); - if (r < 0) - goto fail; + + /* Load in the extras later on, after we + * finished initialization of the unit */ + load_extras = true; } } @@ -1486,6 +1570,12 @@ static int mount_add_one( p->passno = passno; + if (load_extras) { + r = mount_add_extras(MOUNT(u)); + if (r < 0) + goto fail; + } + unit_add_to_dbus_queue(u); return 0; @@ -1630,7 +1720,8 @@ void mount_fd_event(Manager *m, int events) { * /proc/self/mountinfo file, which informs us about mounting * table changes */ - if ((r = mount_load_proc_self_mountinfo(m, true)) < 0) { + r = mount_load_proc_self_mountinfo(m, true); + if (r < 0) { log_error("Failed to reread /proc/self/mountinfo: %s", strerror(-r)); /* Reset flags, just in case, for later calls */ @@ -1797,6 +1888,8 @@ DEFINE_STRING_TABLE_LOOKUP(mount_result, MountResult); const UnitVTable mount_vtable = { .object_size = sizeof(Mount), + .exec_context_offset = offsetof(Mount, exec_context), + .sections = "Unit\0" "Mount\0" diff --git a/src/core/mount.h b/src/core/mount.h index 67d6132a5d..30c6d9b249 100644 --- a/src/core/mount.h +++ b/src/core/mount.h @@ -119,3 +119,5 @@ MountExecCommand mount_exec_command_from_string(const char *s); const char* mount_result_to_string(MountResult i); MountResult mount_result_from_string(const char *s); + +void warn_if_dir_nonempty(const char *unit, const char* where); diff --git a/src/core/namespace.c b/src/core/namespace.c index 4bef15fdf5..ba18ddc5b0 100644 --- a/src/core/namespace.c +++ b/src/core/namespace.c @@ -41,13 +41,15 @@ typedef enum PathMode { /* This is ordered by priority! */ INACCESSIBLE, READONLY, - PRIVATE, + PRIVATE_TMP, + PRIVATE_VAR_TMP, READWRITE } PathMode; typedef struct Path { const char *path; PathMode mode; + bool done; } Path; static int append_paths(Path **p, char **strv, PathMode mode) { @@ -91,25 +93,22 @@ static int path_compare(const void *a, const void *b) { return 0; } -static void drop_duplicates(Path *p, unsigned *n, bool *need_inaccessible, bool *need_private) { +static void drop_duplicates(Path *p, unsigned *n, bool *need_inaccessible) { Path *f, *t, *previous; assert(p); assert(n); assert(need_inaccessible); - assert(need_private); for (f = p, t = p, previous = NULL; f < p+*n; f++) { + /* The first one wins */ if (previous && path_equal(f->path, previous->path)) continue; t->path = f->path; t->mode = f->mode; - if (t->mode == PRIVATE) - *need_private = true; - if (t->mode == INACCESSIBLE) *need_inaccessible = true; @@ -121,65 +120,62 @@ static void drop_duplicates(Path *p, unsigned *n, bool *need_inaccessible, bool *n = t - p; } -static int apply_mount(Path *p, const char *root_dir, const char *inaccessible_dir, const char *private_dir, unsigned long flags) { +static int apply_mount( + Path *p, + const char *tmp_dir, + const char *var_tmp_dir, + const char *inaccessible_dir) { + const char *what; - char *where; int r; assert(p); - assert(root_dir); - assert(inaccessible_dir); - assert(private_dir); - - where = strappend(root_dir, p->path); - if (!where) - return -ENOMEM; switch (p->mode) { case INACCESSIBLE: what = inaccessible_dir; - flags |= MS_RDONLY; break; case READONLY: - flags |= MS_RDONLY; - /* Fall through */ - case READWRITE: what = p->path; break; - case PRIVATE: - what = private_dir; + case PRIVATE_TMP: + what = tmp_dir; + break; + + case PRIVATE_VAR_TMP: + what = var_tmp_dir; break; default: assert_not_reached("Unknown mode"); } - r = mount(what, where, NULL, MS_BIND|MS_REC, NULL); - if (r >= 0) { - log_debug("Successfully mounted %s to %s", what, where); + assert(what); - /* The bind mount will always inherit the original - * flags. If we want to set any flag we need - * to do so in a second independent step. */ - if (flags) - r = mount(NULL, where, NULL, MS_REMOUNT|MS_BIND|MS_REC|flags, NULL); + r = mount(what, p->path, NULL, MS_BIND|MS_REC, NULL); + if (r >= 0) + log_debug("Successfully mounted %s to %s", what, p->path); - /* Avoid exponential growth of trees */ - if (r >= 0 && path_equal(p->path, "/")) - r = mount(NULL, where, NULL, MS_REMOUNT|MS_BIND|flags, NULL); + return r; +} - if (r < 0) { - r = -errno; - umount2(where, MNT_DETACH); - } - } +static int make_read_only(Path *p) { + int r; - free(where); - return r; + assert(p); + + if (p->mode != INACCESSIBLE && p->mode != READONLY) + return 0; + + r = mount(NULL, p->path, NULL, MS_BIND|MS_REMOUNT|MS_RDONLY|MS_REC, NULL); + if (r < 0) + return -errno; + + return 0; } int setup_namespace( @@ -190,30 +186,26 @@ int setup_namespace( unsigned long flags) { char - tmp_dir[] = "/tmp/systemd-namespace-XXXXXX", - root_dir[] = "/tmp/systemd-namespace-XXXXXX/root", - old_root_dir[] = "/tmp/systemd-namespace-XXXXXX/root/tmp/old-root-XXXXXX", - inaccessible_dir[] = "/tmp/systemd-namespace-XXXXXX/inaccessible", - private_dir[] = "/tmp/systemd-namespace-XXXXXX/private"; + tmp_dir[] = "/tmp/systemd-private-XXXXXX", + var_tmp_dir[] = "/var/tmp/systemd-private-XXXXXX", + inaccessible_dir[] = "/tmp/systemd-inaccessible-XXXXXX"; Path *paths, *p; unsigned n; - bool need_private = false, need_inaccessible = false; - bool remove_tmp = false, remove_root = false, remove_old_root = false, remove_inaccessible = false, remove_private = false; + bool need_inaccessible = false; + bool remove_tmp = false, remove_var_tmp = false, remove_inaccessible = false; int r; - const char *t; + + if (!flags) + flags = MS_SHARED; n = strv_length(writable) + strv_length(readable) + strv_length(inaccessible) + - (private_tmp ? 3 : 1); + (private_tmp ? 2 : 0); - paths = new(Path, n); - if (!paths) - return -ENOMEM; - - p = paths; + p = paths = alloca(sizeof(Path) * n); if ((r = append_paths(&p, writable, READWRITE)) < 0 || (r = append_paths(&p, readable, READONLY)) < 0 || (r = append_paths(&p, inaccessible, INACCESSIBLE)) < 0) @@ -221,60 +213,70 @@ int setup_namespace( if (private_tmp) { p->path = "/tmp"; - p->mode = PRIVATE; + p->mode = PRIVATE_TMP; p++; p->path = "/var/tmp"; - p->mode = PRIVATE; + p->mode = PRIVATE_VAR_TMP; p++; } - p->path = "/"; - p->mode = READWRITE; - p++; - assert(paths + n == p); qsort(paths, n, sizeof(Path), path_compare); - drop_duplicates(paths, &n, &need_inaccessible, &need_private); + drop_duplicates(paths, &n, &need_inaccessible); - if (!mkdtemp(tmp_dir)) { - r = -errno; - goto fail; - } - remove_tmp = true; + if (need_inaccessible) { + mode_t u; + char *d; - memcpy(root_dir, tmp_dir, sizeof(tmp_dir)-1); - if (mkdir(root_dir, 0777) < 0) { - r = -errno; - goto fail; - } - remove_root = true; + u = umask(0777); + d = mkdtemp(inaccessible_dir); + umask(u); - if (need_inaccessible) { - memcpy(inaccessible_dir, tmp_dir, sizeof(tmp_dir)-1); - if (mkdir(inaccessible_dir, 0) < 0) { + if (!d) { r = -errno; goto fail; } + remove_inaccessible = true; } - if (need_private) { + if (private_tmp) { mode_t u; - - memcpy(private_dir, tmp_dir, sizeof(tmp_dir)-1); + char *d; u = umask(0000); - if (mkdir(private_dir, 0777 + S_ISVTX) < 0) { - umask(u); + d = mkdtemp(tmp_dir); + umask(u); + if (!d) { r = -errno; goto fail; } + remove_tmp = true; + + u = umask(0000); + d = mkdtemp(var_tmp_dir); umask(u); - remove_private = true; + + if (!d) { + r = -errno; + goto fail; + } + + remove_var_tmp = true; + + if (chmod(tmp_dir, 0777 + S_ISVTX) < 0) { + r = -errno; + goto fail; + } + + if (chmod(var_tmp_dir, 0777 + S_ISVTX) < 0) { + r = -errno; + goto fail; + } } if (unshare(CLONE_NEWNS) < 0) { @@ -282,7 +284,7 @@ int setup_namespace( goto fail; } - /* Remount / as SLAVE so that nothing mounted in the namespace + /* Remount / as SLAVE so that nothing now mounted in the namespace shows up in the parent */ if (mount(NULL, "/", NULL, MS_SLAVE|MS_REC, NULL) < 0) { r = -errno; @@ -290,67 +292,39 @@ int setup_namespace( } for (p = paths; p < paths + n; p++) { - r = apply_mount(p, root_dir, inaccessible_dir, private_dir, flags); + r = apply_mount(p, tmp_dir, var_tmp_dir, inaccessible_dir); if (r < 0) goto undo_mounts; } - memcpy(old_root_dir, tmp_dir, sizeof(tmp_dir)-1); - if (!mkdtemp(old_root_dir)) { - r = -errno; - goto undo_mounts; - } - remove_old_root = true; - - if (chdir(root_dir) < 0) { - r = -errno; - goto undo_mounts; + for (p = paths; p < paths + n; p++) { + r = make_read_only(p); + if (r < 0) + goto undo_mounts; } - if (pivot_root(root_dir, old_root_dir) < 0) { + /* Remount / as the desired mode */ + if (mount(NULL, "/", NULL, flags|MS_REC, NULL) < 0) { r = -errno; goto undo_mounts; } - t = old_root_dir + sizeof(root_dir) - 1; - if (umount2(t, MNT_DETACH) < 0) - /* At this point it's too late to turn anything back, - * since we are already in the new root. */ - return -errno; - - if (rmdir(t) < 0) - return -errno; - return 0; undo_mounts: - - for (p--; p >= paths; p--) { - char full_path[PATH_MAX]; - - snprintf(full_path, sizeof(full_path), "%s%s", root_dir, p->path); - char_array_0(full_path); - - umount2(full_path, MNT_DETACH); - } + for (p = paths; p < paths + n; p++) + if (p->done) + umount2(p->path, MNT_DETACH); fail: - if (remove_old_root) - rmdir(old_root_dir); - if (remove_inaccessible) rmdir(inaccessible_dir); - if (remove_private) - rmdir(private_dir); - - if (remove_root) - rmdir(root_dir); - if (remove_tmp) rmdir(tmp_dir); - free(paths); + if (remove_var_tmp) + rmdir(var_tmp_dir); return r; } diff --git a/src/core/path.c b/src/core/path.c index 42dd5da8e1..767620ba75 100644 --- a/src/core/path.c +++ b/src/core/path.c @@ -73,14 +73,16 @@ int path_spec_watch(PathSpec *s, Unit *u) { goto fail; } - if ((s->primary_wd = inotify_add_watch(s->inotify_fd, k, flags_table[s->type])) >= 0) + s->primary_wd = inotify_add_watch(s->inotify_fd, k, flags_table[s->type]); + if (s->primary_wd >= 0) exists = true; do { int flags; /* This assumes the path was passed through path_kill_slashes()! */ - if (!(slash = strrchr(k, '/'))) + slash = strrchr(k, '/'); + if (!slash) break; /* Trim the path at the last slash. Keep the slash if it's the root dir. */ @@ -122,7 +124,7 @@ int path_spec_fd_event(PathSpec *s, uint32_t events) { int r = 0; if (events != EPOLLIN) { - log_error("Got Invalid poll event on inotify."); + log_error("Got invalid poll event on inotify."); r = -EINVAL; goto out; } @@ -135,13 +137,15 @@ int path_spec_fd_event(PathSpec *s, uint32_t events) { assert(l > 0); - if (!(buf = malloc(l))) { + buf = malloc(l); + if (!buf) { log_error("Failed to allocate buffer: %m"); r = -errno; goto out; } - if ((k = read(s->inotify_fd, buf, l)) < 0) { + k = read(s->inotify_fd, buf, l); + if (k < 0) { log_error("Failed to read inotify event: %m"); r = -errno; goto out; @@ -215,7 +219,8 @@ static void path_spec_mkdir(PathSpec *s, mode_t mode) { if (s->type == PATH_EXISTS || s->type == PATH_EXISTS_GLOB) return; - if ((r = mkdir_p_label(s->path, mode)) < 0) + r = mkdir_p_label(s->path, mode); + if (r < 0) log_warning("mkdir(%s) failed: %s", s->path, strerror(-r)); } @@ -302,7 +307,8 @@ static int path_verify(Path *p) { return 0; if (!p->specs) { - log_error("%s lacks path setting. Refusing.", UNIT(p)->id); + log_error_unit(UNIT(p)->id, + "%s lacks path setting. Refusing.", UNIT(p)->id); return -EINVAL; } @@ -314,7 +320,7 @@ static int path_add_default_dependencies(Path *p) { assert(p); - if (UNIT(p)->manager->running_as == MANAGER_SYSTEM) { + if (UNIT(p)->manager->running_as == SYSTEMD_SYSTEM) { if ((r = unit_add_dependency_by_name(UNIT(p), UNIT_BEFORE, SPECIAL_BASIC_TARGET, NULL, true)) < 0) return r; diff --git a/src/core/selinux-access.c b/src/core/selinux-access.c new file mode 100644 index 0000000000..6dfe8b45f3 --- /dev/null +++ b/src/core/selinux-access.c @@ -0,0 +1,441 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2012 Dan Walsh + + 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 + (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. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include "selinux-access.h" + +#ifdef HAVE_SELINUX + +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <limits.h> +#include <selinux/selinux.h> +#include <selinux/avc.h> +#ifdef HAVE_AUDIT +#include <libaudit.h> +#endif +#include <dbus.h> + +#include "util.h" +#include "log.h" +#include "bus-errors.h" +#include "dbus-common.h" +#include "audit.h" +#include "selinux-util.h" +#include "audit-fd.h" + +static bool initialized = false; + +struct auditstruct { + const char *path; + char *cmdline; + uid_t loginuid; + uid_t uid; + gid_t gid; +}; + +static int bus_get_selinux_security_context( + DBusConnection *connection, + const char *name, + char **scon, + DBusError *error) { + + _cleanup_dbus_message_unref_ DBusMessage *m = NULL, *reply = NULL; + DBusMessageIter iter, sub; + const char *bytes; + char *b; + int nbytes; + + m = dbus_message_new_method_call( + DBUS_SERVICE_DBUS, + DBUS_PATH_DBUS, + DBUS_INTERFACE_DBUS, + "GetConnectionSELinuxSecurityContext"); + if (!m) { + dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, NULL); + return -ENOMEM; + } + + if (!dbus_message_append_args( + m, + DBUS_TYPE_STRING, &name, + DBUS_TYPE_INVALID)) { + dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, NULL); + return -ENOMEM; + } + + reply = dbus_connection_send_with_reply_and_block(connection, m, -1, error); + if (!reply) + return -EIO; + + if (dbus_set_error_from_message(error, reply)) + return -EIO; + + if (!dbus_message_iter_init(reply, &iter)) + return -EIO; + + if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) + return -EIO; + + dbus_message_iter_recurse(&iter, &sub); + dbus_message_iter_get_fixed_array(&sub, &bytes, &nbytes); + + b = strndup(bytes, nbytes); + if (!b) + return -ENOMEM; + + *scon = b; + + log_debug("GetConnectionSELinuxSecurityContext %s (pid %ld)", *scon, (long) bus_get_unix_process_id(connection, name, error)); + + return 0; +} + +static int bus_get_audit_data( + DBusConnection *connection, + const char *name, + struct auditstruct *audit, + DBusError *error) { + + pid_t pid; + int r; + + pid = bus_get_unix_process_id(connection, name, error); + if (pid <= 0) + return -EIO; + + r = audit_loginuid_from_pid(pid, &audit->loginuid); + if (r < 0) + return r; + + r = get_process_uid(pid, &audit->uid); + if (r < 0) + return r; + + r = get_process_gid(pid, &audit->gid); + if (r < 0) + return r; + + r = get_process_cmdline(pid, LINE_MAX, true, &audit->cmdline); + if (r < 0) + return r; + + return 0; +} + +/* + Any time an access gets denied this callback will be called + with the aduit data. We then need to just copy the audit data into the msgbuf. +*/ +static int audit_callback( + void *auditdata, + security_class_t cls, + char *msgbuf, + size_t msgbufsize) { + + struct auditstruct *audit = (struct auditstruct *) auditdata; + + snprintf(msgbuf, msgbufsize, + "auid=%d uid=%d gid=%d%s%s%s%s%s%s", + audit->loginuid, + audit->uid, + audit->gid, + (audit->path ? " path=\"" : ""), + strempty(audit->path), + (audit->path ? "\"" : ""), + (audit->cmdline ? " cmdline=\"" : ""), + strempty(audit->cmdline), + (audit->cmdline ? "\"" : "")); + + msgbuf[msgbufsize-1] = 0; + + return 0; +} + +/* + Any time an access gets denied this callback will be called + code copied from dbus. If audit is turned on the messages will go as + user_avc's into the /var/log/audit/audit.log, otherwise they will be + sent to syslog. +*/ +static int log_callback(int type, const char *fmt, ...) { + va_list ap; + + va_start(ap, fmt); + +#ifdef HAVE_AUDIT + if (get_audit_fd() >= 0) { + char buf[LINE_MAX]; + + vsnprintf(buf, sizeof(buf), fmt, ap); + audit_log_user_avc_message(get_audit_fd(), AUDIT_USER_AVC, buf, NULL, NULL, NULL, 0); + va_end(ap); + + return 0; + } +#endif + log_metav(LOG_USER | LOG_INFO, __FILE__, __LINE__, __FUNCTION__, fmt, ap); + va_end(ap); + + return 0; +} + +/* + Function must be called once to initialize the SELinux AVC environment. + Sets up callbacks. + If you want to cleanup memory you should need to call selinux_access_finish. +*/ +static int access_init(void) { + int r; + + if (avc_open(NULL, 0)) { + log_error("avc_open() failed: %m"); + return -errno; + } + + selinux_set_callback(SELINUX_CB_AUDIT, (union selinux_callback) audit_callback); + selinux_set_callback(SELINUX_CB_LOG, (union selinux_callback) log_callback); + + if (security_getenforce() >= 0) + return 0; + + r = -errno; + avc_destroy(); + + return r; +} + +static int selinux_access_init(DBusError *error) { + int r; + + if (initialized) + return 0; + + if (use_selinux()) { + r = access_init(); + if (r < 0) { + dbus_set_error(error, DBUS_ERROR_ACCESS_DENIED, "Failed to initialize SELinux."); + return r; + } + } + + initialized = true; + return 0; +} + +void selinux_access_free(void) { + if (!initialized) + return; + + avc_destroy(); + initialized = false; +} + +static int get_audit_data( + DBusConnection *connection, + DBusMessage *message, + struct auditstruct *audit, + DBusError *error) { + + const char *sender; + int r, fd; + struct ucred ucred; + socklen_t len; + + sender = dbus_message_get_sender(message); + if (sender) + return bus_get_audit_data(connection, sender, audit, error); + + if (!dbus_connection_get_unix_fd(connection, &fd)) + return -EINVAL; + + r = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &len); + if (r < 0) { + log_error("Failed to determine peer credentials: %m"); + return -errno; + } + + audit->uid = ucred.uid; + audit->gid = ucred.gid; + + r = audit_loginuid_from_pid(ucred.pid, &audit->loginuid); + if (r < 0) + return r; + + r = get_process_cmdline(ucred.pid, LINE_MAX, true, &audit->cmdline); + if (r < 0) + return r; + + return 0; +} + +/* + This function returns the security context of the remote end of the dbus + connections. Whether it is on the bus or a local connection. +*/ +static int get_calling_context( + DBusConnection *connection, + DBusMessage *message, + security_context_t *scon, + DBusError *error) { + + const char *sender; + int r; + int fd; + + /* + If sender exists then + if sender is NULL this indicates a local connection. Grab the fd + from dbus and do an getpeercon to peers process context + */ + sender = dbus_message_get_sender(message); + if (sender) { + log_error("SELinux Got Sender %s", sender); + + r = bus_get_selinux_security_context(connection, sender, scon, error); + if (r >= 0) + return r; + + log_error("bus_get_selinux_security_context failed: %m"); + return r; + } + + log_debug("SELinux No Sender"); + if (!dbus_connection_get_unix_fd(connection, &fd)) { + log_error("bus_connection_get_unix_fd failed %m"); + return -EINVAL; + } + + r = getpeercon(fd, scon); + if (r < 0) { + log_error("getpeercon failed %m"); + return -errno; + } + + return 0; +} + +/* + This function communicates with the kernel to check whether or not it should + allow the access. + If the machine is in permissive mode it will return ok. Audit messages will + still be generated if the access would be denied in enforcing mode. +*/ +int selinux_access_check( + DBusConnection *connection, + DBusMessage *message, + const char *path, + const char *permission, + DBusError *error) { + + security_context_t scon = NULL, fcon = NULL; + int r = 0; + const char *tclass = NULL; + struct auditstruct audit; + + assert(connection); + assert(message); + assert(permission); + assert(error); + + if (!use_selinux()) + return 0; + + r = selinux_access_init(error); + if (r < 0) + return r; + + log_debug("SELinux access check for path=%s permission=%s", strna(path), permission); + + audit.uid = audit.loginuid = (uid_t) -1; + audit.gid = (gid_t) -1; + audit.cmdline = NULL; + audit.path = path; + + r = get_calling_context(connection, message, &scon, error); + if (r < 0) { + log_error("Failed to get caller's security context on: %m"); + goto finish; + } + + if (path) { + tclass = "service"; + /* get the file context of the unit file */ + r = getfilecon(path, &fcon); + if (r < 0) { + dbus_set_error(error, DBUS_ERROR_ACCESS_DENIED, "Failed to get file context on %s.", path); + r = -errno; + log_error("Failed to get security context on %s: %m",path); + goto finish; + } + + } else { + tclass = "system"; + r = getcon(&fcon); + if (r < 0) { + dbus_set_error(error, DBUS_ERROR_ACCESS_DENIED, "Failed to get current context."); + r = -errno; + log_error("Failed to get current process context on: %m"); + goto finish; + } + } + + (void) get_audit_data(connection, message, &audit, error); + + errno = 0; + r = selinux_check_access(scon, fcon, tclass, permission, &audit); + if (r < 0) { + dbus_set_error(error, DBUS_ERROR_ACCESS_DENIED, "SELinux policy denies access."); + r = -errno; + log_error("SELinux policy denies access."); + } + + log_debug("SELinux access check scon=%s tcon=%s tclass=%s perm=%s path=%s cmdline=%s: %i", scon, fcon, tclass, permission, path, audit.cmdline, r); + +finish: + free(audit.cmdline); + freecon(scon); + freecon(fcon); + + if (r && security_getenforce() != 1) { + dbus_error_init(error); + r = 0; + } + + return r; +} + +#else + +int selinux_access_check( + DBusConnection *connection, + DBusMessage *message, + const char *path, + const char *permission, + DBusError *error) { + + return 0; +} + +void selinux_access_free(void) { +} + +#endif diff --git a/src/core/selinux-access.h b/src/core/selinux-access.h new file mode 100644 index 0000000000..9183cbc9a6 --- /dev/null +++ b/src/core/selinux-access.h @@ -0,0 +1,62 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#pragma once + +/*** + This file is part of systemd. + + Copyright 2012 Dan Walsh + + 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 + (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. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <dbus.h> + +void selinux_access_free(void); + +int selinux_access_check(DBusConnection *connection, DBusMessage *message, const char *path, const char *permission, DBusError *error); + +#ifdef HAVE_SELINUX + +#define SELINUX_ACCESS_CHECK(connection, message, permission) \ + do { \ + DBusError _error; \ + int _r; \ + DBusConnection *_c = (connection); \ + DBusMessage *_m = (message); \ + dbus_error_init(&_error); \ + _r = selinux_access_check(_c, _m, NULL, (permission), &_error); \ + if (_r < 0) \ + return bus_send_error_reply(_c, _m, &_error, _r); \ + } while (false) + +#define SELINUX_UNIT_ACCESS_CHECK(unit, connection, message, permission) \ + do { \ + DBusError _error; \ + int _r; \ + DBusConnection *_c = (connection); \ + DBusMessage *_m = (message); \ + Unit *_u = (unit); \ + dbus_error_init(&_error); \ + _r = selinux_access_check(_c, _m, _u->source_path ?: _u->fragment_path, (permission), &_error); \ + if (_r < 0) \ + return bus_send_error_reply(_c, _m, &_error, _r); \ + } while (false) + +#else + +#define SELINUX_ACCESS_CHECK(connection, message, permission) do { } while (false) +#define SELINUX_UNIT_ACCESS_CHECK(unit, connection, message, permission) do { } while (false) + +#endif diff --git a/src/core/selinux-setup.c b/src/core/selinux-setup.c index fa025b7268..e9c0de92f1 100644 --- a/src/core/selinux-setup.c +++ b/src/core/selinux-setup.c @@ -30,11 +30,12 @@ #endif #include "selinux-setup.h" +#include "selinux-util.h" +#include "label.h" #include "mount-setup.h" #include "macro.h" #include "util.h" #include "log.h" -#include "label.h" #ifdef HAVE_SELINUX static int null_log(int type, const char *fmt, ...) { @@ -79,12 +80,11 @@ int selinux_setup(bool *loaded_policy) { /* Now load the policy */ before_load = now(CLOCK_MONOTONIC); r = selinux_init_load_policy(&enforce); - if (r == 0) { char timespan[FORMAT_TIMESPAN_MAX]; char *label; - label_retest_selinux(); + retest_selinux(); /* Transition to the new context */ r = label_get_create_label_from_exe(SYSTEMD_BINARY_PATH, &label); diff --git a/src/core/service.c b/src/core/service.c index 1c127bdbcb..2a4e691e78 100644 --- a/src/core/service.c +++ b/src/core/service.c @@ -33,6 +33,7 @@ #include "log.h" #include "strv.h" #include "unit-name.h" +#include "unit-printf.h" #include "dbus-service.h" #include "special.h" #include "bus-errors.h" @@ -48,8 +49,7 @@ typedef enum RunlevelType { RUNLEVEL_UP, - RUNLEVEL_DOWN, - RUNLEVEL_SYSINIT + RUNLEVEL_DOWN } RunlevelType; static const struct { @@ -64,16 +64,6 @@ static const struct { { "rc4.d", SPECIAL_RUNLEVEL4_TARGET, RUNLEVEL_UP }, { "rc5.d", SPECIAL_RUNLEVEL5_TARGET, RUNLEVEL_UP }, -#ifdef TARGET_SUSE - /* SUSE style boot.d */ - { "boot.d", SPECIAL_SYSINIT_TARGET, RUNLEVEL_SYSINIT }, -#endif - -#if defined(TARGET_DEBIAN) || defined(TARGET_UBUNTU) || defined(TARGET_ANGSTROM) - /* Debian style rcS.d */ - { "rcS.d", SPECIAL_SYSINIT_TARGET, RUNLEVEL_SYSINIT }, -#endif - /* Standard SysV runlevels for shutdown */ { "rc0.d", SPECIAL_POWEROFF_TARGET, RUNLEVEL_DOWN }, { "rc6.d", SPECIAL_REBOOT_TARGET, RUNLEVEL_DOWN } @@ -82,12 +72,10 @@ static const struct { directories in this order, and we want to make sure that sysv_start_priority is known when we first load the unit. And that value we only know from S links. Hence - UP/SYSINIT must be read before DOWN */ + UP must be read before DOWN */ }; #define RUNLEVELS_UP "12345" -/* #define RUNLEVELS_DOWN "06" */ -#define RUNLEVELS_BOOT "bBsS" #endif static const UnitActiveState state_translation_table[_SERVICE_STATE_MAX] = { @@ -134,13 +122,14 @@ static void service_init(Unit *u) { assert(u); assert(u->load_state == UNIT_STUB); - s->timeout_usec = DEFAULT_TIMEOUT_USEC; + s->timeout_start_usec = DEFAULT_TIMEOUT_USEC; + s->timeout_stop_usec = DEFAULT_TIMEOUT_USEC; s->restart_usec = DEFAULT_RESTART_USEC; s->type = _SERVICE_TYPE_INVALID; - s->watchdog_watch.type = WATCH_INVALID; + watch_init(&s->watchdog_watch); + watch_init(&s->timer_watch); - s->timer_watch.type = WATCH_INVALID; #ifdef HAVE_SYSV_COMPAT s->sysv_start_priority = -1; s->sysv_start_priority_from_rcnd = -1; @@ -180,7 +169,8 @@ static void service_unwatch_pid_file(Service *s) { if (!s->pid_file_pathspec) return; - log_debug("Stopping watch for %s's PID file %s", UNIT(s)->id, s->pid_file_pathspec->path); + log_debug_unit(UNIT(s)->id, "Stopping watch for %s's PID file %s", + UNIT(s)->id, s->pid_file_pathspec->path); path_spec_unwatch(s->pid_file_pathspec, UNIT(s)); path_spec_done(s->pid_file_pathspec); free(s->pid_file_pathspec); @@ -202,8 +192,9 @@ static int service_set_main_pid(Service *s, pid_t pid) { s->main_pid_known = true; if (get_parent_of_pid(pid, &ppid) >= 0 && ppid != getpid()) { - log_warning("%s: Supervising process %lu which is not our child. We'll most likely not notice when it exits.", - UNIT(s)->id, (unsigned long) pid); + log_warning_unit(UNIT(s)->id, + "%s: Supervising process %lu which is not our child. We'll most likely not notice when it exits.", + UNIT(s)->id, (unsigned long) pid); s->main_pid_alien = true; } else @@ -255,14 +246,16 @@ static void service_handle_watchdog(Service *s) { offset = now(CLOCK_MONOTONIC) - s->watchdog_timestamp.monotonic; if (offset >= s->watchdog_usec) { - log_error("%s watchdog timeout!", UNIT(s)->id); + log_error_unit(UNIT(s)->id, "%s watchdog timeout!", UNIT(s)->id); service_enter_dead(s, SERVICE_FAILURE_WATCHDOG, true); return; } - r = unit_watch_timer(UNIT(s), s->watchdog_usec - offset, &s->watchdog_watch); + r = unit_watch_timer(UNIT(s), CLOCK_MONOTONIC, true, s->watchdog_usec - offset, &s->watchdog_watch); if (r < 0) - log_warning("%s failed to install watchdog timer: %s", UNIT(s)->id, strerror(-r)); + log_warning_unit(UNIT(s)->id, + "%s failed to install watchdog timer: %s", + UNIT(s)->id, strerror(-r)); } static void service_reset_watchdog(Service *s) { @@ -293,6 +286,16 @@ static void service_done(Unit *u) { s->control_command = NULL; s->main_command = NULL; + set_free(s->restart_ignore_status.code); + s->restart_ignore_status.code = NULL; + set_free(s->restart_ignore_status.signal); + s->restart_ignore_status.signal = NULL; + + set_free(s->success_status.code); + s->success_status.code = NULL; + set_free(s->success_status.signal); + s->success_status.signal = NULL; + /* This will leak a process, but at least no memory or any of * our resources */ service_unwatch_main_pid(s); @@ -322,21 +325,12 @@ static char *sysv_translate_name(const char *name) { if (!(r = new(char, strlen(name) + sizeof(".service")))) return NULL; -#if defined(TARGET_DEBIAN) || defined(TARGET_UBUNTU) || defined(TARGET_ANGSTROM) if (endswith(name, ".sh")) /* Drop Debian-style .sh suffix */ strcpy(stpcpy(r, name) - 3, ".service"); -#endif -#ifdef TARGET_SUSE - if (startswith(name, "boot.")) - /* Drop SuSE-style boot. prefix */ - strcpy(stpcpy(r, name + 5), ".service"); -#endif -#ifdef TARGET_FRUGALWARE if (startswith(name, "rc.")) /* Drop Frugalware-style rc. prefix */ strcpy(stpcpy(r, name + 3), ".service"); -#endif else /* Normal init scripts */ strcpy(stpcpy(r, name), ".service"); @@ -355,14 +349,11 @@ static int sysv_translate_facility(const char *name, const char *filename, char static const char * const table[] = { /* LSB defined facilities */ "local_fs", SPECIAL_LOCAL_FS_TARGET, -#if defined(TARGET_MANDRIVA) || defined(TARGET_MAGEIA) -#else /* Due to unfortunate name selection in Mandriva, * $network is provided by network-up which is ordered * after network which actually starts interfaces. * To break the loop, just ignore it */ "network", SPECIAL_NETWORK_TARGET, -#endif "named", SPECIAL_NSS_LOOKUP_TARGET, "portmap", SPECIAL_RPCBIND_TARGET, "remote_fs", SPECIAL_REMOTE_FS_TARGET, @@ -373,20 +364,8 @@ static int sysv_translate_facility(const char *name, const char *filename, char "mail-transfer-agent", SPECIAL_MAIL_TRANSFER_AGENT_TARGET, "x-display-manager", SPECIAL_DISPLAY_MANAGER_SERVICE, "null", NULL, - -#if defined(TARGET_DEBIAN) || defined(TARGET_UBUNTU) || defined(TARGET_ANGSTROM) "mail-transport-agent", SPECIAL_MAIL_TRANSFER_AGENT_TARGET, -#endif - -#ifdef TARGET_FEDORA - "MTA", SPECIAL_MAIL_TRANSFER_AGENT_TARGET, - "smtpdaemon", SPECIAL_MAIL_TRANSFER_AGENT_TARGET, - "httpd", SPECIAL_HTTP_DAEMON_TARGET, -#endif - -#ifdef TARGET_SUSE "smtp", SPECIAL_MAIL_TRANSFER_AGENT_TARGET, -#endif }; unsigned i; @@ -514,7 +493,7 @@ static ExecCommand *exec_command_new(const char *path, const char *arg1) { return c; } -static int sysv_exec_commands(Service *s) { +static int sysv_exec_commands(Service *s, const bool supports_reload) { ExecCommand *c; assert(s); @@ -531,14 +510,25 @@ static int sysv_exec_commands(Service *s) { return -ENOMEM; exec_command_append_list(s->exec_command+SERVICE_EXEC_STOP, c); - c = exec_command_new(UNIT(s)->source_path, "reload"); - if (!c) - return -ENOMEM; - exec_command_append_list(s->exec_command+SERVICE_EXEC_RELOAD, c); + if (supports_reload) { + c = exec_command_new(UNIT(s)->source_path, "reload"); + if (!c) + return -ENOMEM; + exec_command_append_list(s->exec_command+SERVICE_EXEC_RELOAD, c); + } return 0; } +static bool usage_contains_reload(const char *line) { + return (strcasestr(line, "{reload|") || + strcasestr(line, "{reload}") || + strcasestr(line, "{reload\"") || + strcasestr(line, "|reload|") || + strcasestr(line, "|reload}") || + strcasestr(line, "|reload\"")); +} + static int service_load_sysv_path(Service *s, const char *path) { FILE *f; Unit *u; @@ -548,10 +538,12 @@ static int service_load_sysv_path(Service *s, const char *path) { NORMAL, DESCRIPTION, LSB, - LSB_DESCRIPTION + LSB_DESCRIPTION, + USAGE_CONTINUATION } state = NORMAL; char *short_description = NULL, *long_description = NULL, *chkconfig_description = NULL, *description; struct stat st; + bool supports_reload = false; assert(s); assert(path); @@ -593,15 +585,32 @@ static int service_load_sysv_path(Service *s, const char *path) { break; r = -errno; - log_error("Failed to read configuration file '%s': %s", path, strerror(-r)); + log_error_unit(u->id, + "Failed to read configuration file '%s': %s", + path, strerror(-r)); goto finish; } line++; t = strstrip(l); - if (*t != '#') + if (*t != '#') { + /* Try to figure out whether this init script supports + * the reload operation. This heuristic looks for + * "Usage" lines which include the reload option. */ + if ( state == USAGE_CONTINUATION || + (state == NORMAL && strcasestr(t, "usage"))) { + if (usage_contains_reload(t)) { + supports_reload = true; + state = NORMAL; + } else if (t[strlen(t)-1] == '\\') + state = USAGE_CONTINUATION; + else + state = NORMAL; + } + continue; + } if (state == NORMAL && streq(t, "### BEGIN INIT INFO")) { state = LSB; @@ -631,7 +640,9 @@ static int service_load_sysv_path(Service *s, const char *path) { runlevels, &start_priority) != 2) { - log_warning("[%s:%u] Failed to parse chkconfig line. Ignoring.", path, line); + log_warning_unit(u->id, + "[%s:%u] Failed to parse chkconfig line. Ignoring.", + path, line); continue; } @@ -639,7 +650,9 @@ static int service_load_sysv_path(Service *s, const char *path) { * symlink farms is preferred over the * data from the LSB header. */ if (start_priority < 0 || start_priority > 99) - log_warning("[%s:%u] Start priority out of range. Ignoring.", path, line); + log_warning_unit(u->id, + "[%s:%u] Start priority out of range. Ignoring.", + path, line); else s->sysv_start_priority = start_priority; @@ -688,7 +701,9 @@ static int service_load_sysv_path(Service *s, const char *path) { fn = strstrip(t+8); if (!path_is_absolute(fn)) { - log_warning("[%s:%u] PID file not absolute. Ignoring.", path, line); + log_warning_unit(u->id, + "[%s:%u] PID file not absolute. Ignoring.", + path, line); continue; } @@ -775,7 +790,9 @@ static int service_load_sysv_path(Service *s, const char *path) { r = unit_add_two_dependencies_by_name(u, UNIT_BEFORE, UNIT_WANTS, m, NULL, true); if (r < 0) - log_error("[%s:%u] Failed to add LSB Provides name %s, ignoring: %s", path, line, m, strerror(-r)); + log_error_unit(u->id, + "[%s:%u] Failed to add LSB Provides name %s, ignoring: %s", + path, line, m, strerror(-r)); free(m); } @@ -800,7 +817,9 @@ static int service_load_sysv_path(Service *s, const char *path) { r = sysv_translate_facility(n, path_get_file_name(path), &m); if (r < 0) { - log_error("[%s:%u] Failed to translate LSB dependency %s, ignoring: %s", path, line, n, strerror(-r)); + log_error_unit(u->id, + "[%s:%u] Failed to translate LSB dependency %s, ignoring: %s", + path, line, n, strerror(-r)); free(n); continue; } @@ -813,7 +832,8 @@ static int service_load_sysv_path(Service *s, const char *path) { r = unit_add_dependency_by_name(u, startswith_no_case(t, "X-Start-Before:") ? UNIT_BEFORE : UNIT_AFTER, m, NULL, true); if (r < 0) - log_error("[%s:%u] Failed to add dependency on %s, ignoring: %s", path, line, m, strerror(-r)); + log_error_unit(u->id, "[%s:%u] Failed to add dependency on %s, ignoring: %s", + path, line, m, strerror(-r)); free(m); } @@ -894,15 +914,8 @@ static int service_load_sysv_path(Service *s, const char *path) { } } - if ((r = sysv_exec_commands(s)) < 0) + if ((r = sysv_exec_commands(s, supports_reload)) < 0) goto finish; - if (s->sysv_runlevels && - chars_intersect(RUNLEVELS_BOOT, s->sysv_runlevels) && - chars_intersect(RUNLEVELS_UP, s->sysv_runlevels)) { - /* Service has both boot and "up" runlevels - configured. Kill the "up" ones. */ - delete_chars(s->sysv_runlevels, RUNLEVELS_UP); - } if (s->sysv_runlevels && !chars_intersect(RUNLEVELS_UP, s->sysv_runlevels)) { /* If there a runlevels configured for this service @@ -914,9 +927,12 @@ static int service_load_sysv_path(Service *s, const char *path) { UNIT(s)->default_dependencies = false; /* Don't timeout special services during boot (like fsck) */ - s->timeout_usec = 0; - } else - s->timeout_usec = DEFAULT_SYSV_TIMEOUT_USEC; + s->timeout_start_usec = 0; + s->timeout_stop_usec = 0; + } else { + s->timeout_start_usec = DEFAULT_SYSV_TIMEOUT_USEC; + s->timeout_stop_usec = DEFAULT_SYSV_TIMEOUT_USEC; + } /* Special setting for all SysV services */ s->type = SERVICE_FORKING; @@ -976,22 +992,11 @@ static int service_load_sysv_name(Service *s, const char *name) { assert(s); assert(name); - /* For SysV services we strip the boot.*, rc.* and *.sh + /* For SysV services we strip the rc.* and *.sh * prefixes/suffixes. */ -#if defined(TARGET_DEBIAN) || defined(TARGET_UBUNTU) || defined(TARGET_ANGSTROM) - if (endswith(name, ".sh.service")) + if (startswith(name, "rc.") || + endswith(name, ".sh.service")) return -ENOENT; -#endif - -#ifdef TARGET_SUSE - if (startswith(name, "boot.")) - return -ENOENT; -#endif - -#ifdef TARGET_FRUGALWARE - if (startswith(name, "rc.")) - return -ENOENT; -#endif STRV_FOREACH(p, UNIT(s)->manager->lookup_paths.sysvinit_path) { char *path; @@ -1006,31 +1011,13 @@ static int service_load_sysv_name(Service *s, const char *name) { r = service_load_sysv_path(s, path); -#if defined(TARGET_DEBIAN) || defined(TARGET_UBUNTU) || defined(TARGET_ANGSTROM) if (r >= 0 && UNIT(s)->load_state == UNIT_STUB) { /* Try Debian style *.sh source'able init scripts */ strcat(path, ".sh"); r = service_load_sysv_path(s, path); } -#endif free(path); -#ifdef TARGET_SUSE - if (r >= 0 && UNIT(s)->load_state == UNIT_STUB) { - /* Try SUSE style boot.* init scripts */ - - path = strjoin(*p, "/boot.", name, NULL); - if (!path) - return -ENOMEM; - - /* Drop .service suffix */ - path[strlen(path)-8] = 0; - r = service_load_sysv_path(s, path); - free(path); - } -#endif - -#ifdef TARGET_FRUGALWARE if (r >= 0 && UNIT(s)->load_state == UNIT_STUB) { /* Try Frugalware style rc.* init scripts */ @@ -1043,12 +1030,11 @@ static int service_load_sysv_name(Service *s, const char *name) { r = service_load_sysv_path(s, path); free(path); } -#endif if (r < 0) return r; - if ((UNIT(s)->load_state != UNIT_STUB)) + if (UNIT(s)->load_state != UNIT_STUB) break; } @@ -1136,32 +1122,31 @@ static int service_verify(Service *s) { return 0; if (!s->exec_command[SERVICE_EXEC_START]) { - log_error("%s lacks ExecStart setting. Refusing.", UNIT(s)->id); + log_error_unit(UNIT(s)->id, + "%s lacks ExecStart setting. Refusing.", UNIT(s)->id); return -EINVAL; } if (s->type != SERVICE_ONESHOT && s->exec_command[SERVICE_EXEC_START]->command_next) { - log_error("%s has more than one ExecStart setting, which is only allowed for Type=oneshot services. Refusing.", UNIT(s)->id); - return -EINVAL; - } - - if (s->type == SERVICE_ONESHOT && - s->exec_command[SERVICE_EXEC_RELOAD]) { - log_error("%s has an ExecReload setting, which is not allowed for Type=oneshot services. Refusing.", UNIT(s)->id); + log_error_unit(UNIT(s)->id, + "%s has more than one ExecStart setting, which is only allowed for Type=oneshot services. Refusing.", UNIT(s)->id); return -EINVAL; } if (s->type == SERVICE_DBUS && !s->bus_name) { - log_error("%s is of type D-Bus but no D-Bus service name has been specified. Refusing.", UNIT(s)->id); + log_error_unit(UNIT(s)->id, + "%s is of type D-Bus but no D-Bus service name has been specified. Refusing.", UNIT(s)->id); return -EINVAL; } if (s->bus_name && s->type != SERVICE_DBUS) - log_warning("%s has a D-Bus service name specified, but is not of type dbus. Ignoring.", UNIT(s)->id); + log_warning_unit(UNIT(s)->id, + "%s has a D-Bus service name specified, but is not of type dbus. Ignoring.", UNIT(s)->id); if (s->exec_context.pam_name && s->kill_context.kill_mode != KILL_CONTROL_GROUP) { - log_error("%s has PAM enabled. Kill mode must be set to 'control-group'. Refusing.", UNIT(s)->id); + log_error_unit(UNIT(s)->id, + "%s has PAM enabled. Kill mode must be set to 'control-group'. Refusing.", UNIT(s)->id); return -EINVAL; } @@ -1177,12 +1162,12 @@ static int service_add_default_dependencies(Service *s) { * majority of services. */ /* First, pull in base system */ - if (UNIT(s)->manager->running_as == MANAGER_SYSTEM) { + if (UNIT(s)->manager->running_as == SYSTEMD_SYSTEM) { if ((r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_BASIC_TARGET, NULL, true)) < 0) return r; - } else if (UNIT(s)->manager->running_as == MANAGER_USER) { + } else if (UNIT(s)->manager->running_as == SYSTEMD_USER) { if ((r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SOCKETS_TARGET, NULL, true)) < 0) return r; @@ -1241,9 +1226,9 @@ static int service_load(Unit *u) { if (s->type == _SERVICE_TYPE_INVALID) s->type = s->bus_name ? SERVICE_DBUS : SERVICE_SIMPLE; - /* Oneshot services have disabled timeout by default */ - if (s->type == SERVICE_ONESHOT && !s->timeout_defined) - s->timeout_usec = 0; + /* Oneshot services have disabled start timeout by default */ + if (s->type == SERVICE_ONESHOT && !s->start_timeout_defined) + s->timeout_start_usec = 0; service_fix_output(s); @@ -1403,8 +1388,9 @@ static int service_load_pid_file(Service *s, bool may_warn) { if ((r = read_one_line_file(s->pid_file, &k)) < 0) { if (may_warn) - log_info("PID file %s not readable (yet?) after %s.", - s->pid_file, service_state_to_string(s->state)); + log_info_unit(UNIT(s)->id, + "PID file %s not readable (yet?) after %s.", + s->pid_file, service_state_to_string(s->state)); return r; } @@ -1416,8 +1402,9 @@ static int service_load_pid_file(Service *s, bool may_warn) { if (kill(pid, 0) < 0 && errno != EPERM) { if (may_warn) - log_info("PID %lu read from file %s does not exist.", - (unsigned long) pid, s->pid_file); + log_info_unit(UNIT(s)->id, + "PID %lu read from file %s does not exist.", + (unsigned long) pid, s->pid_file); return -ESRCH; } @@ -1425,12 +1412,14 @@ static int service_load_pid_file(Service *s, bool may_warn) { if (pid == s->main_pid) return 0; - log_debug("Main PID changing: %lu -> %lu", - (unsigned long) s->main_pid, (unsigned long) pid); + log_debug_unit(UNIT(s)->id, + "Main PID changing: %lu -> %lu", + (unsigned long) s->main_pid, (unsigned long) pid); service_unwatch_main_pid(s); s->main_pid_known = false; } else - log_debug("Main PID loaded: %lu", (unsigned long) pid); + log_debug_unit(UNIT(s)->id, + "Main PID loaded: %lu", (unsigned long) pid); if ((r = service_set_main_pid(s, pid)) < 0) return r; @@ -1461,7 +1450,8 @@ static int service_search_main_pid(Service *s) { if ((pid = cgroup_bonding_search_main_pid_list(UNIT(s)->cgroup_bondings)) <= 0) return -ENOENT; - log_debug("Main PID guessed: %lu", (unsigned long) pid); + log_debug_unit(UNIT(s)->id, + "Main PID guessed: %lu", (unsigned long) pid); if ((r = service_set_main_pid(s, pid)) < 0) return r; @@ -1577,7 +1567,10 @@ static void service_set_state(Service *s, ServiceState state) { cgroup_bonding_trim_list(UNIT(s)->cgroup_bondings, true); if (old_state != state) - log_debug("%s changed %s -> %s", UNIT(s)->id, service_state_to_string(old_state), service_state_to_string(state)); + log_debug_unit(UNIT(s)->id, + "%s changed %s -> %s", UNIT(s)->id, + service_state_to_string(old_state), + service_state_to_string(state)); unit_notify(UNIT(s), table[old_state], table[state], s->reload_result == SERVICE_SUCCESS); s->reload_result = SERVICE_SUCCESS; @@ -1603,13 +1596,13 @@ static int service_coldplug(Unit *u) { s->deserialized_state == SERVICE_FINAL_SIGTERM || s->deserialized_state == SERVICE_FINAL_SIGKILL || s->deserialized_state == SERVICE_AUTO_RESTART) { - - if (s->deserialized_state == SERVICE_AUTO_RESTART || s->timeout_usec > 0) { + if (s->deserialized_state == SERVICE_AUTO_RESTART || s->timeout_start_usec > 0) { usec_t k; - k = s->deserialized_state == SERVICE_AUTO_RESTART ? s->restart_usec : s->timeout_usec; + k = s->deserialized_state == SERVICE_AUTO_RESTART ? s->restart_usec : s->timeout_start_usec; - if ((r = unit_watch_timer(UNIT(s), k, &s->timer_watch)) < 0) + r = unit_watch_timer(UNIT(s), CLOCK_MONOTONIC, true, k, &s->timer_watch); + if (r < 0) return r; } } @@ -1753,8 +1746,9 @@ static int service_spawn( } } - if (timeout && s->timeout_usec) { - if ((r = unit_watch_timer(UNIT(s), s->timeout_usec, &s->timer_watch)) < 0) + if (timeout && s->timeout_start_usec) { + r = unit_watch_timer(UNIT(s), CLOCK_MONOTONIC, true, s->timeout_start_usec, &s->timer_watch); + if (r < 0) goto fail; } else unit_unwatch_timer(UNIT(s), &s->timer_watch); @@ -1764,7 +1758,8 @@ static int service_spawn( goto fail; } - if (!(our_env = new0(char*, 4))) { + our_env = new0(char*, 5); + if (!our_env) { r = -ENOMEM; goto fail; } @@ -1787,10 +1782,14 @@ static int service_spawn( goto fail; } - if (!(final_env = strv_env_merge(2, - UNIT(s)->manager->environment, - our_env, - NULL))) { + if (s->meta.manager->running_as != SYSTEMD_SYSTEM) + if (asprintf(our_env + n_env++, "MANAGERPID=%lu", (unsigned long) getpid()) < 0) { + r = -ENOMEM; + goto fail; + } + + final_env = strv_env_merge(2, UNIT(s)->manager->environment, our_env, NULL); + if (!final_env) { r = -ENOMEM; goto fail; } @@ -1814,7 +1813,6 @@ static int service_spawn( if (r < 0) goto fail; - if ((r = unit_watch_pid(UNIT(s), pid)) < 0) /* FIXME: we need to do something here */ goto fail; @@ -1897,9 +1895,14 @@ static void service_enter_dead(Service *s, ServiceResult f, bool allow_restart) (s->restart == SERVICE_RESTART_ON_SUCCESS && s->result == SERVICE_SUCCESS) || (s->restart == SERVICE_RESTART_ON_FAILURE && s->result != SERVICE_SUCCESS) || (s->restart == SERVICE_RESTART_ON_ABORT && (s->result == SERVICE_FAILURE_SIGNAL || - s->result == SERVICE_FAILURE_CORE_DUMP)))) { - - r = unit_watch_timer(UNIT(s), s->restart_usec, &s->timer_watch); + s->result == SERVICE_FAILURE_CORE_DUMP))) && + (s->result != SERVICE_FAILURE_EXIT_CODE || + !set_contains(s->restart_ignore_status.code, INT_TO_PTR(s->main_exec_status.status))) && + (s->result != SERVICE_FAILURE_SIGNAL || + !set_contains(s->restart_ignore_status.signal, INT_TO_PTR(s->main_exec_status.status))) + ) { + + r = unit_watch_timer(UNIT(s), CLOCK_MONOTONIC, true, s->restart_usec, &s->timer_watch); if (r < 0) goto fail; @@ -1911,7 +1914,9 @@ static void service_enter_dead(Service *s, ServiceResult f, bool allow_restart) return; fail: - log_warning("%s failed to run install restart timer: %s", UNIT(s)->id, strerror(-r)); + log_warning_unit(UNIT(s)->id, + "%s failed to run install restart timer: %s", + UNIT(s)->id, strerror(-r)); service_enter_dead(s, SERVICE_FAILURE_RESOURCES, false); } @@ -1950,7 +1955,9 @@ static void service_enter_stop_post(Service *s, ServiceResult f) { return; fail: - log_warning("%s failed to run 'stop-post' task: %s", UNIT(s)->id, strerror(-r)); + log_warning_unit(UNIT(s)->id, + "%s failed to run 'stop-post' task: %s", + UNIT(s)->id, strerror(-r)); service_enter_signal(s, SERVICE_FINAL_SIGTERM, SERVICE_FAILURE_RESOURCES); } @@ -1969,14 +1976,16 @@ static void service_enter_signal(Service *s, ServiceState state, ServiceResult f if (s->main_pid > 0) { if (kill_and_sigcont(s->main_pid, sig) < 0 && errno != ESRCH) - log_warning("Failed to kill main process %li: %m", (long) s->main_pid); + log_warning_unit(UNIT(s)->id, + "Failed to kill main process %li: %m", (long) s->main_pid); else wait_for_exit = !s->main_pid_alien; } if (s->control_pid > 0) { if (kill_and_sigcont(s->control_pid, sig) < 0 && errno != ESRCH) - log_warning("Failed to kill control process %li: %m", (long) s->control_pid); + log_warning_unit(UNIT(s)->id, + "Failed to kill control process %li: %m", (long) s->control_pid); else wait_for_exit = true; } @@ -2001,7 +2010,8 @@ static void service_enter_signal(Service *s, ServiceState state, ServiceResult f r = cgroup_bonding_kill_list(UNIT(s)->cgroup_bondings, sig, true, false, pid_set, NULL); if (r < 0) { if (r != -EAGAIN && r != -ESRCH && r != -ENOENT) - log_warning("Failed to kill control group: %s", strerror(-r)); + log_warning_unit(UNIT(s)->id, + "Failed to kill control group: %s", strerror(-r)); } else if (r > 0) wait_for_exit = true; @@ -2011,9 +2021,11 @@ static void service_enter_signal(Service *s, ServiceState state, ServiceResult f } if (wait_for_exit) { - if (s->timeout_usec > 0) - if ((r = unit_watch_timer(UNIT(s), s->timeout_usec, &s->timer_watch)) < 0) + if (s->timeout_stop_usec > 0) { + r = unit_watch_timer(UNIT(s), CLOCK_MONOTONIC, true, s->timeout_stop_usec, &s->timer_watch); + if (r < 0) goto fail; + } service_set_state(s, state); } else if (state == SERVICE_STOP_SIGTERM || state == SERVICE_STOP_SIGKILL) @@ -2024,7 +2036,8 @@ static void service_enter_signal(Service *s, ServiceState state, ServiceResult f return; fail: - log_warning("%s failed to kill processes: %s", UNIT(s)->id, strerror(-r)); + log_warning_unit(UNIT(s)->id, + "%s failed to kill processes: %s", UNIT(s)->id, strerror(-r)); if (state == SERVICE_STOP_SIGTERM || state == SERVICE_STOP_SIGKILL) service_enter_stop_post(s, SERVICE_FAILURE_RESOURCES); @@ -2068,7 +2081,8 @@ static void service_enter_stop(Service *s, ServiceResult f) { return; fail: - log_warning("%s failed to run 'stop' task: %s", UNIT(s)->id, strerror(-r)); + log_warning_unit(UNIT(s)->id, + "%s failed to run 'stop' task: %s", UNIT(s)->id, strerror(-r)); service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_FAILURE_RESOURCES); } @@ -2123,7 +2137,8 @@ static void service_enter_start_post(Service *s) { return; fail: - log_warning("%s failed to run 'start-post' task: %s", UNIT(s)->id, strerror(-r)); + log_warning_unit(UNIT(s)->id, + "%s failed to run 'start-post' task: %s", UNIT(s)->id, strerror(-r)); service_enter_stop(s, SERVICE_FAILURE_RESOURCES); } @@ -2206,7 +2221,8 @@ static void service_enter_start(Service *s) { return; fail: - log_warning("%s failed to run 'start' task: %s", UNIT(s)->id, strerror(-r)); + log_warning_unit(UNIT(s)->id, + "%s failed to run 'start' task: %s", UNIT(s)->id, strerror(-r)); service_enter_signal(s, SERVICE_FINAL_SIGTERM, SERVICE_FAILURE_RESOURCES); } @@ -2245,7 +2261,8 @@ static void service_enter_start_pre(Service *s) { return; fail: - log_warning("%s failed to run 'start-pre' task: %s", UNIT(s)->id, strerror(-r)); + log_warning_unit(UNIT(s)->id, + "%s failed to run 'start-pre' task: %s", UNIT(s)->id, strerror(-r)); service_enter_dead(s, SERVICE_FAILURE_RESOURCES, true); } @@ -2258,9 +2275,10 @@ static void service_enter_restart(Service *s) { if (UNIT(s)->job && UNIT(s)->job->type == JOB_STOP) { /* Don't restart things if we are going down anyway */ - log_info("Stop job pending for unit, delaying automatic restart."); + log_info_unit(UNIT(s)->id, + "Stop job pending for unit, delaying automatic restart."); - r = unit_watch_timer(UNIT(s), s->restart_usec, &s->timer_watch); + r = unit_watch_timer(UNIT(s), CLOCK_MONOTONIC, true, s->restart_usec, &s->timer_watch); if (r < 0) goto fail; @@ -2279,11 +2297,14 @@ static void service_enter_restart(Service *s) { * it will be canceled as part of the service_stop() call that * is executed as part of JOB_RESTART. */ - log_debug("%s scheduled restart job.", UNIT(s)->id); + log_debug_unit(UNIT(s)->id, + "%s scheduled restart job.", UNIT(s)->id); return; fail: - log_warning("%s failed to schedule restart job: %s", UNIT(s)->id, bus_error(&error, -r)); + log_warning_unit(UNIT(s)->id, + "%s failed to schedule restart job: %s", + UNIT(s)->id, bus_error(&error, -r)); service_enter_dead(s, SERVICE_FAILURE_RESOURCES, false); dbus_error_free(&error); @@ -2319,7 +2340,9 @@ static void service_enter_reload(Service *s) { return; fail: - log_warning("%s failed to run 'reload' task: %s", UNIT(s)->id, strerror(-r)); + log_warning_unit(UNIT(s)->id, + "%s failed to run 'reload' task: %s", + UNIT(s)->id, strerror(-r)); s->reload_result = SERVICE_FAILURE_RESOURCES; service_enter_running(s, SERVICE_SUCCESS); } @@ -2353,7 +2376,9 @@ static void service_run_next_control(Service *s) { return; fail: - log_warning("%s failed to run next control task: %s", UNIT(s)->id, strerror(-r)); + log_warning_unit(UNIT(s)->id, + "%s failed to run next control task: %s", + UNIT(s)->id, strerror(-r)); if (s->state == SERVICE_START_PRE) service_enter_signal(s, SERVICE_FINAL_SIGTERM, SERVICE_FAILURE_RESOURCES); @@ -2398,7 +2423,8 @@ static void service_run_next_main(Service *s) { return; fail: - log_warning("%s failed to run next main task: %s", UNIT(s)->id, strerror(-r)); + log_warning_unit(UNIT(s)->id, + "%s failed to run next main task: %s", UNIT(s)->id, strerror(-r)); service_enter_stop(s, SERVICE_FAILURE_RESOURCES); } @@ -2411,7 +2437,9 @@ static int service_start_limit_test(Service *s) { switch (s->start_limit_action) { case SERVICE_START_LIMIT_NONE: - log_warning("%s start request repeated too quickly, refusing to start.", UNIT(s)->id); + log_warning_unit(UNIT(s)->id, + "%s start request repeated too quickly, refusing to start.", + UNIT(s)->id); break; case SERVICE_START_LIMIT_REBOOT: { @@ -2420,11 +2448,13 @@ static int service_start_limit_test(Service *s) { dbus_error_init(&error); - log_warning("%s start request repeated too quickly, rebooting.", UNIT(s)->id); + log_warning_unit(UNIT(s)->id, + "%s start request repeated too quickly, rebooting.", UNIT(s)->id); r = manager_add_job_by_name(UNIT(s)->manager, JOB_START, SPECIAL_REBOOT_TARGET, JOB_REPLACE, true, &error, NULL); if (r < 0) { - log_error("Failed to reboot: %s.", bus_error(&error, r)); + log_error_unit(UNIT(s)->id, + "Failed to reboot: %s.", bus_error(&error, r)); dbus_error_free(&error); } @@ -2432,17 +2462,21 @@ static int service_start_limit_test(Service *s) { } case SERVICE_START_LIMIT_REBOOT_FORCE: - log_warning("%s start request repeated too quickly, forcibly rebooting.", UNIT(s)->id); + log_warning_unit(UNIT(s)->id, + "%s start request repeated too quickly, forcibly rebooting.", UNIT(s)->id); UNIT(s)->manager->exit_code = MANAGER_REBOOT; break; case SERVICE_START_LIMIT_REBOOT_IMMEDIATE: - log_warning("%s start request repeated too quickly, rebooting immediately.", UNIT(s)->id); + log_warning_unit(UNIT(s)->id, + "%s start request repeated too quickly, rebooting immediately.", UNIT(s)->id); + sync(); reboot(RB_AUTOBOOT); break; default: - log_error("start limit action=%i", s->start_limit_action); + log_error_unit(UNIT(s)->id, + "start limit action=%i", s->start_limit_action); assert_not_reached("Unknown StartLimitAction."); } @@ -2486,7 +2520,7 @@ static int service_start(Unit *u) { /* Make sure we don't enter a busy loop of some kind. */ r = service_start_limit_test(s); if (r < 0) { - service_notify_sockets_dead(s, true); + service_enter_dead(s, SERVICE_FAILURE_START_LIMIT, false); return r; } @@ -2624,7 +2658,7 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value, ServiceState state; if ((state = service_state_from_string(value)) < 0) - log_debug("Failed to parse state value %s", value); + log_debug_unit(u->id, "Failed to parse state value %s", value); else s->deserialized_state = state; } else if (streq(key, "result")) { @@ -2632,7 +2666,7 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value, f = service_result_from_string(value); if (f < 0) - log_debug("Failed to parse result value %s", value); + log_debug_unit(u->id, "Failed to parse result value %s", value); else if (f != SERVICE_SUCCESS) s->result = f; @@ -2641,7 +2675,7 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value, f = service_result_from_string(value); if (f < 0) - log_debug("Failed to parse reload result value %s", value); + log_debug_unit(u->id, "Failed to parse reload result value %s", value); else if (f != SERVICE_SUCCESS) s->reload_result = f; @@ -2649,21 +2683,21 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value, pid_t pid; if (parse_pid(value, &pid) < 0) - log_debug("Failed to parse control-pid value %s", value); + log_debug_unit(u->id, "Failed to parse control-pid value %s", value); else s->control_pid = pid; } else if (streq(key, "main-pid")) { pid_t pid; if (parse_pid(value, &pid) < 0) - log_debug("Failed to parse main-pid value %s", value); + log_debug_unit(u->id, "Failed to parse main-pid value %s", value); else service_set_main_pid(s, (pid_t) pid); } else if (streq(key, "main-pid-known")) { int b; if ((b = parse_boolean(value)) < 0) - log_debug("Failed to parse main-pid-known value %s", value); + log_debug_unit(u->id, "Failed to parse main-pid-known value %s", value); else s->main_pid_known = b; } else if (streq(key, "status-text")) { @@ -2678,7 +2712,7 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value, ServiceExecCommand id; if ((id = service_exec_command_from_string(value)) < 0) - log_debug("Failed to parse exec-command value %s", value); + log_debug_unit(u->id, "Failed to parse exec-command value %s", value); else { s->control_command_id = id; s->control_command = s->exec_command[id]; @@ -2687,7 +2721,7 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value, int fd; if (safe_atoi(value, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd)) - log_debug("Failed to parse socket-fd value %s", value); + log_debug_unit(u->id, "Failed to parse socket-fd value %s", value); else { if (s->socket_fd >= 0) @@ -2698,21 +2732,21 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value, pid_t pid; if (parse_pid(value, &pid) < 0) - log_debug("Failed to parse main-exec-status-pid value %s", value); + log_debug_unit(u->id, "Failed to parse main-exec-status-pid value %s", value); else s->main_exec_status.pid = pid; } else if (streq(key, "main-exec-status-code")) { int i; if (safe_atoi(value, &i) < 0) - log_debug("Failed to parse main-exec-status-code value %s", value); + log_debug_unit(u->id, "Failed to parse main-exec-status-code value %s", value); else s->main_exec_status.code = i; } else if (streq(key, "main-exec-status-status")) { int i; if (safe_atoi(value, &i) < 0) - log_debug("Failed to parse main-exec-status-status value %s", value); + log_debug_unit(u->id, "Failed to parse main-exec-status-status value %s", value); else s->main_exec_status.status = i; } else if (streq(key, "main-exec-status-start")) @@ -2722,7 +2756,7 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value, else if (streq(key, "watchdog-timestamp")) dual_timestamp_deserialize(value, &s->watchdog_timestamp); else - log_debug("Unknown serialization key '%s'", key); + log_debug_unit(u->id, "Unknown serialization key '%s'", key); return 0; } @@ -2790,7 +2824,9 @@ static int service_retry_pid_file(Service *s) { static int service_watch_pid_file(Service *s) { int r; - log_debug("Setting watch for %s's PID file %s", UNIT(s)->id, s->pid_file_pathspec->path); + log_debug_unit(UNIT(s)->id, + "Setting watch for %s's PID file %s", + UNIT(s)->id, s->pid_file_pathspec->path); r = path_spec_watch(s->pid_file_pathspec, UNIT(s)); if (r < 0) goto fail; @@ -2800,8 +2836,9 @@ static int service_watch_pid_file(Service *s) { return 0; fail: - log_error("Failed to set a watch for %s's PID file %s: %s", - UNIT(s)->id, s->pid_file_pathspec->path, strerror(-r)); + log_error_unit(UNIT(s)->id, + "Failed to set a watch for %s's PID file %s: %s", + UNIT(s)->id, s->pid_file_pathspec->path, strerror(-r)); service_unwatch_pid_file(s); return r; } @@ -2843,7 +2880,7 @@ static void service_fd_event(Unit *u, int fd, uint32_t events, Watch *w) { assert(s->pid_file_pathspec); assert(path_spec_owns_inotify_fd(s->pid_file_pathspec, fd)); - log_debug("inotify event for %s", u->id); + log_debug_unit(u->id, "inotify event for %s", u->id); if (path_spec_fd_event(s->pid_file_pathspec, events) < 0) goto fail; @@ -2867,7 +2904,8 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { assert(s); assert(pid >= 0); - if (UNIT(s)->fragment_path ? is_clean_exit(code, status) : is_clean_exit_lsb(code, status)) + if (UNIT(s)->fragment_path ? is_clean_exit(code, status, &s->success_status) : + is_clean_exit_lsb(code, status, &s->success_status)) f = SERVICE_SUCCESS; else if (code == CLD_EXITED) f = SERVICE_FAILURE_EXIT_CODE; @@ -2899,8 +2937,16 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { f = SERVICE_SUCCESS; } - log_full(f == SERVICE_SUCCESS ? LOG_DEBUG : LOG_NOTICE, - "%s: main process exited, code=%s, status=%i", u->id, sigchld_code_to_string(code), status); + log_struct(f == SERVICE_SUCCESS ? LOG_DEBUG : LOG_NOTICE, + "MESSAGE=%s: main process exited, code=%s, status=%i/%s", + u->id, sigchld_code_to_string(code), status, + strna(code == CLD_EXITED + ? exit_status_to_string(status, EXIT_STATUS_FULL) + : signal_to_string(status)), + "UNIT=%s", u->id, + "EXIT_CODE=%s", sigchld_code_to_string(code), + "EXIT_STATUS=%i", status, + NULL); if (f != SERVICE_SUCCESS) s->result = f; @@ -2912,7 +2958,9 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { /* There is another command to * * execute, so let's do that. */ - log_debug("%s running next main command for state %s", u->id, service_state_to_string(s->state)); + log_debug_unit(u->id, + "%s running next main command for state %s", + u->id, service_state_to_string(s->state)); service_run_next_main(s); } else { @@ -2938,12 +2986,10 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { else service_enter_signal(s, SERVICE_FINAL_SIGTERM, f); break; - } else { - assert(s->type == SERVICE_DBUS || s->type == SERVICE_NOTIFY); - - /* Fall through */ } + /* Fall through */ + case SERVICE_RUNNING: service_enter_running(s, f); break; @@ -2973,8 +3019,9 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { f = SERVICE_SUCCESS; } - log_full(f == SERVICE_SUCCESS ? LOG_DEBUG : LOG_NOTICE, - "%s: control process exited, code=%s status=%i", u->id, sigchld_code_to_string(code), status); + log_full_unit(f == SERVICE_SUCCESS ? LOG_DEBUG : LOG_NOTICE, u->id, + "%s: control process exited, code=%s status=%i", + u->id, sigchld_code_to_string(code), status); if (f != SERVICE_SUCCESS) s->result = f; @@ -2991,7 +3038,9 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { /* There is another command to * * execute, so let's do that. */ - log_debug("%s running next control command for state %s", u->id, service_state_to_string(s->state)); + log_debug_unit(u->id, + "%s running next control command for state %s", + u->id, service_state_to_string(s->state)); service_run_next_control(s); } else { @@ -3001,7 +3050,9 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { s->control_command = NULL; s->control_command_id = _SERVICE_EXEC_COMMAND_INVALID; - log_debug("%s got final SIGCHLD for state %s", u->id, service_state_to_string(s->state)); + log_debug_unit(u->id, + "%s got final SIGCHLD for state %s", + u->id, service_state_to_string(s->state)); switch (s->state) { @@ -3013,7 +3064,9 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { break; case SERVICE_START: - assert(s->type == SERVICE_FORKING); + if (s->type != SERVICE_FORKING) + /* Maybe spurious event due to a reload that changed the type? */ + break; if (f != SERVICE_SUCCESS) { service_enter_signal(s, SERVICE_FINAL_SIGTERM, f); @@ -3121,32 +3174,38 @@ static void service_timer_event(Unit *u, uint64_t elapsed, Watch* w) { case SERVICE_START_PRE: case SERVICE_START: - log_warning("%s operation timed out. Terminating.", u->id); + log_warning_unit(u->id, + "%s operation timed out. Terminating.", u->id); service_enter_signal(s, SERVICE_FINAL_SIGTERM, SERVICE_FAILURE_TIMEOUT); break; case SERVICE_START_POST: - log_warning("%s operation timed out. Stopping.", u->id); + log_warning_unit(u->id, + "%s operation timed out. Stopping.", u->id); service_enter_stop(s, SERVICE_FAILURE_TIMEOUT); break; case SERVICE_RELOAD: - log_warning("%s operation timed out. Stopping.", u->id); + log_warning_unit(u->id, + "%s operation timed out. Stopping.", u->id); s->reload_result = SERVICE_FAILURE_TIMEOUT; service_enter_running(s, SERVICE_SUCCESS); break; case SERVICE_STOP: - log_warning("%s stopping timed out. Terminating.", u->id); + log_warning_unit(u->id, + "%s stopping timed out. Terminating.", u->id); service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_FAILURE_TIMEOUT); break; case SERVICE_STOP_SIGTERM: if (s->kill_context.send_sigkill) { - log_warning("%s stopping timed out. Killing.", u->id); + log_warning_unit(u->id, + "%s stopping timed out. Killing.", u->id); service_enter_signal(s, SERVICE_STOP_SIGKILL, SERVICE_FAILURE_TIMEOUT); } else { - log_warning("%s stopping timed out. Skipping SIGKILL.", u->id); + log_warning_unit(u->id, + "%s stopping timed out. Skipping SIGKILL.", u->id); service_enter_stop_post(s, SERVICE_FAILURE_TIMEOUT); } @@ -3157,33 +3216,40 @@ static void service_timer_event(Unit *u, uint64_t elapsed, Watch* w) { * Must be something we cannot kill, so let's just be * weirded out and continue */ - log_warning("%s still around after SIGKILL. Ignoring.", u->id); + log_warning_unit(u->id, + "%s still around after SIGKILL. Ignoring.", u->id); service_enter_stop_post(s, SERVICE_FAILURE_TIMEOUT); break; case SERVICE_STOP_POST: - log_warning("%s stopping timed out (2). Terminating.", u->id); + log_warning_unit(u->id, + "%s stopping timed out (2). Terminating.", u->id); service_enter_signal(s, SERVICE_FINAL_SIGTERM, SERVICE_FAILURE_TIMEOUT); break; case SERVICE_FINAL_SIGTERM: if (s->kill_context.send_sigkill) { - log_warning("%s stopping timed out (2). Killing.", u->id); + log_warning_unit(u->id, + "%s stopping timed out (2). Killing.", u->id); service_enter_signal(s, SERVICE_FINAL_SIGKILL, SERVICE_FAILURE_TIMEOUT); } else { - log_warning("%s stopping timed out (2). Skipping SIGKILL. Entering failed mode.", u->id); + log_warning_unit(u->id, + "%s stopping timed out (2). Skipping SIGKILL. Entering failed mode.", + u->id); service_enter_dead(s, SERVICE_FAILURE_TIMEOUT, false); } break; case SERVICE_FINAL_SIGKILL: - log_warning("%s still around after SIGKILL (2). Entering failed mode.", u->id); + log_warning_unit(u->id, + "%s still around after SIGKILL (2). Entering failed mode.", u->id); service_enter_dead(s, SERVICE_FAILURE_TIMEOUT, true); break; case SERVICE_AUTO_RESTART: - log_info("%s holdoff time over, scheduling restart.", u->id); + log_info_unit(u->id, + "%s holdoff time over, scheduling restart.", u->id); service_enter_restart(s); break; @@ -3197,7 +3263,8 @@ static void service_cgroup_notify_event(Unit *u) { assert(u); - log_debug("%s: cgroup is empty", u->id); + log_debug_unit(u->id, + "%s: cgroup is empty", u->id); switch (s->state) { @@ -3212,7 +3279,8 @@ static void service_cgroup_notify_event(Unit *u) { /* If we were hoping for the daemon to write its PID file, * we can give up now. */ if (s->pid_file_pathspec) { - log_warning("%s never wrote its PID file. Failing.", UNIT(s)->id); + log_warning_unit(u->id, + "%s never wrote its PID file. Failing.", UNIT(s)->id); service_unwatch_pid_file(s); if (s->state == SERVICE_START) service_enter_signal(s, SERVICE_FINAL_SIGTERM, SERVICE_FAILURE_RESOURCES); @@ -3253,18 +3321,21 @@ static void service_notify_message(Unit *u, pid_t pid, char **tags) { assert(u); if (s->notify_access == NOTIFY_NONE) { - log_warning("%s: Got notification message from PID %lu, but reception is disabled.", - u->id, (unsigned long) pid); + log_warning_unit(u->id, + "%s: Got notification message from PID %lu, but reception is disabled.", + u->id, (unsigned long) pid); return; } if (s->notify_access == NOTIFY_MAIN && pid != s->main_pid) { - log_warning("%s: Got notification message from PID %lu, but reception only permitted for PID %lu", - u->id, (unsigned long) pid, (unsigned long) s->main_pid); + log_warning_unit(u->id, + "%s: Got notification message from PID %lu, but reception only permitted for PID %lu", + u->id, (unsigned long) pid, (unsigned long) s->main_pid); return; } - log_debug("%s: Got message", u->id); + log_debug_unit(u->id, + "%s: Got message", u->id); /* Interpret MAINPID= */ if ((e = strv_find_prefix(tags, "MAINPID=")) && @@ -3274,9 +3345,11 @@ static void service_notify_message(Unit *u, pid_t pid, char **tags) { s->state == SERVICE_RELOAD)) { if (parse_pid(e + 8, &pid) < 0) - log_warning("Failed to parse notification message %s", e); + log_warning_unit(u->id, + "Failed to parse notification message %s", e); else { - log_debug("%s: got %s", u->id, e); + log_debug_unit(u->id, + "%s: got %s", u->id, e); service_set_main_pid(s, pid); } } @@ -3285,7 +3358,8 @@ static void service_notify_message(Unit *u, pid_t pid, char **tags) { if (s->type == SERVICE_NOTIFY && s->state == SERVICE_START && strv_find(tags, "READY=1")) { - log_debug("%s: got READY=1", u->id); + log_debug_unit(u->id, + "%s: got READY=1", u->id); service_enter_start_post(s); } @@ -3298,17 +3372,20 @@ static void service_notify_message(Unit *u, pid_t pid, char **tags) { if (e[7]) { if (!utf8_is_valid(e+7)) { - log_warning("Status message in notification is not UTF-8 clean."); + log_warning_unit(u->id, + "Status message in notification is not UTF-8 clean."); return; } t = strdup(e+7); if (!t) { - log_error("Failed to allocate string."); + log_error_unit(u->id, + "Failed to allocate string."); return; } - log_debug("%s: got %s", u->id, e); + log_debug_unit(u->id, + "%s: got %s", u->id, e); free(s->status_text); s->status_text = t; @@ -3319,7 +3396,8 @@ static void service_notify_message(Unit *u, pid_t pid, char **tags) { } if (strv_find(tags, "WATCHDOG=1")) { - log_debug("%s: got WATCHDOG=1", u->id); + log_debug_unit(u->id, + "%s: got WATCHDOG=1", u->id); service_reset_watchdog(s); } @@ -3329,71 +3407,6 @@ static void service_notify_message(Unit *u, pid_t pid, char **tags) { #ifdef HAVE_SYSV_COMPAT -#ifdef TARGET_SUSE -static void sysv_facility_in_insserv_conf(Manager *mgr) { - FILE *f=NULL; - int r; - - if (!(f = fopen("/etc/insserv.conf", "re"))) { - r = errno == ENOENT ? 0 : -errno; - goto finish; - } - - while (!feof(f)) { - char l[LINE_MAX], *t; - char **parsed = NULL; - - if (!fgets(l, sizeof(l), f)) { - if (feof(f)) - break; - - r = -errno; - log_error("Failed to read configuration file '/etc/insserv.conf': %s", strerror(-r)); - goto finish; - } - - t = strstrip(l); - if (*t != '$' && *t != '<') - continue; - - parsed = strv_split(t,WHITESPACE); - /* we ignore <interactive>, not used, equivalent to X-Interactive */ - if (parsed && !startswith_no_case (parsed[0], "<interactive>")) { - char *facility; - Unit *u; - if (sysv_translate_facility(parsed[0], NULL, &facility) < 0) - continue; - if ((u = manager_get_unit(mgr, facility)) && (u->type == UNIT_TARGET)) { - UnitDependency e; - char *dep = NULL, *name, **j; - - STRV_FOREACH (j, parsed+1) { - if (*j[0]=='+') { - e = UNIT_WANTS; - name = *j+1; - } - else { - e = UNIT_REQUIRES; - name = *j; - } - if (sysv_translate_facility(name, NULL, &dep) < 0) - continue; - - r = unit_add_two_dependencies_by_name(u, UNIT_BEFORE, e, dep, NULL, true); - free(dep); - } - } - free(facility); - } - strv_free(parsed); - } -finish: - if (f) - fclose(f); - -} -#endif - static int service_enumerate(Manager *m) { char **p; unsigned i; @@ -3406,7 +3419,7 @@ static int service_enumerate(Manager *m) { assert(m); - if (m->running_as != MANAGER_SYSTEM) + if (m->running_as != SYSTEMD_SYSTEM) return 0; zero(runlevel_services); @@ -3471,14 +3484,15 @@ static int service_enumerate(Manager *m) { goto finish; } - if ((r = manager_load_unit_prepare(m, name, NULL, NULL, &service)) < 0) { + r = manager_load_unit_prepare(m, name, NULL, NULL, &service); + if (r < 0) { log_warning("Failed to prepare unit %s: %s", name, strerror(-r)); continue; } if (de->d_name[0] == 'S') { - if (rcnd_table[i].type == RUNLEVEL_UP || rcnd_table[i].type == RUNLEVEL_SYSINIT) { + if (rcnd_table[i].type == RUNLEVEL_UP) { SERVICE(service)->sysv_start_priority_from_rcnd = MAX(a*10 + b, SERVICE(service)->sysv_start_priority_from_rcnd); @@ -3492,8 +3506,7 @@ static int service_enumerate(Manager *m) { goto finish; } else if (de->d_name[0] == 'K' && - (rcnd_table[i].type == RUNLEVEL_DOWN || - rcnd_table[i].type == RUNLEVEL_SYSINIT)) { + (rcnd_table[i].type == RUNLEVEL_DOWN)) { if ((r = set_ensure_allocated(&shutdown_services, trivial_hash_func, trivial_compare_func)) < 0) goto finish; @@ -3527,9 +3540,7 @@ static int service_enumerate(Manager *m) { * runlevels we assume the stop jobs will be implicitly added * by the core logic. Also, we don't really distinguish here * between the runlevels 0 and 6 and just add them to the - * special shutdown target. On SUSE the boot.d/ runlevel is - * also used for shutdown, so we add links for that too to the - * shutdown target.*/ + * special shutdown target. */ SET_FOREACH(service, shutdown_services, j) { service = unit_follow_merge(service); @@ -3542,10 +3553,6 @@ static int service_enumerate(Manager *m) { r = 0; -#ifdef TARGET_SUSE - sysv_facility_in_insserv_conf (m); -#endif - finish: free(path); free(fpath); @@ -3577,11 +3584,17 @@ static void service_bus_name_owner_change( assert(old_owner || new_owner); if (old_owner && new_owner) - log_debug("%s's D-Bus name %s changed owner from %s to %s", u->id, name, old_owner, new_owner); + log_debug_unit(u->id, + "%s's D-Bus name %s changed owner from %s to %s", + u->id, name, old_owner, new_owner); else if (old_owner) - log_debug("%s's D-Bus name %s no longer registered by %s", u->id, name, old_owner); + log_debug_unit(u->id, + "%s's D-Bus name %s no longer registered by %s", + u->id, name, old_owner); else - log_debug("%s's D-Bus name %s now registered by %s", u->id, name, new_owner); + log_debug_unit(u->id, + "%s's D-Bus name %s now registered by %s", + u->id, name, new_owner); s->bus_name_good = !!new_owner; @@ -3602,7 +3615,8 @@ static void service_bus_name_owner_change( s->state == SERVICE_RELOAD)) { /* Try to acquire PID from bus service */ - log_debug("Trying to acquire PID from D-Bus name..."); + log_debug_unit(u->id, + "Trying to acquire PID from D-Bus name..."); bus_query_pid(u->manager, name); } @@ -3618,7 +3632,9 @@ static void service_bus_query_pid_done( assert(s); assert(name); - log_debug("%s's D-Bus name %s is now owned by process %u", u->id, name, (unsigned) pid); + log_debug_unit(u->id, + "%s's D-Bus name %s is now owned by process %u", + u->id, name, (unsigned) pid); if (s->main_pid <= 0 && (s->state == SERVICE_START || @@ -3798,7 +3814,8 @@ static const char* const service_result_table[_SERVICE_RESULT_MAX] = { [SERVICE_FAILURE_EXIT_CODE] = "exit-code", [SERVICE_FAILURE_SIGNAL] = "signal", [SERVICE_FAILURE_CORE_DUMP] = "core-dump", - [SERVICE_FAILURE_WATCHDOG] = "watchdog" + [SERVICE_FAILURE_WATCHDOG] = "watchdog", + [SERVICE_FAILURE_START_LIMIT] = "start-limit" }; DEFINE_STRING_TABLE_LOOKUP(service_result, ServiceResult); @@ -3813,6 +3830,8 @@ DEFINE_STRING_TABLE_LOOKUP(start_limit_action, StartLimitAction); const UnitVTable service_vtable = { .object_size = sizeof(Service), + .exec_context_offset = offsetof(Service, exec_context), + .sections = "Unit\0" "Service\0" diff --git a/src/core/service.h b/src/core/service.h index cc63347c76..d1e53bf727 100644 --- a/src/core/service.h +++ b/src/core/service.h @@ -28,6 +28,7 @@ typedef struct Service Service; #include "ratelimit.h" #include "service.h" #include "kill.h" +#include "exit-status.h" typedef enum ServiceState { SERVICE_DEAD, @@ -97,6 +98,7 @@ typedef enum ServiceResult { SERVICE_FAILURE_SIGNAL, SERVICE_FAILURE_CORE_DUMP, SERVICE_FAILURE_WATCHDOG, + SERVICE_FAILURE_START_LIMIT, _SERVICE_RESULT_MAX, _SERVICE_RESULT_INVALID = -1 } ServiceResult; @@ -115,12 +117,15 @@ struct Service { ServiceType type; ServiceRestart restart; + ExitStatusSet restart_ignore_status; + ExitStatusSet success_status; /* If set we'll read the main daemon PID from this file */ char *pid_file; usec_t restart_usec; - usec_t timeout_usec; + usec_t timeout_start_usec; + usec_t timeout_stop_usec; dual_timestamp watchdog_timestamp; usec_t watchdog_usec; @@ -166,7 +171,7 @@ struct Service { bool bus_name_good:1; bool forbid_restart:1; bool got_socket_fd:1; - bool timeout_defined:1; + bool start_timeout_defined:1; #ifdef HAVE_SYSV_COMPAT bool is_sysv:1; bool sysv_has_lsb:1; diff --git a/src/core/shutdown.c b/src/core/shutdown.c index 105a604542..0b0e0c3d47 100644 --- a/src/core/shutdown.c +++ b/src/core/shutdown.c @@ -104,15 +104,14 @@ static int pivot_to_new_root(void) { return -errno; } - /* - In case some evil process made "/" MS_SHARED - It works for pivot_root, but the ref count for the root device - is not decreasing :-/ - */ - if (mount(NULL, "/", NULL, MS_PRIVATE, NULL) < 0) { - log_error("Failed to make \"/\" private mount %m"); - return -errno; - } + /* Work-around for a kernel bug: for some reason the kernel + * refuses switching root if any file systems are mounted + * MS_SHARED. Hence remount them MS_PRIVATE here as a + * work-around. + * + * https://bugzilla.redhat.com/show_bug.cgi?id=847418 */ + if (mount(NULL, "/", NULL, MS_REC|MS_PRIVATE, NULL) < 0) + log_warning("Failed to make \"/\" private mount: %m"); if (pivot_root(".", "oldroot") < 0) { log_error("pivot failed: %m"); @@ -198,31 +197,34 @@ int main(int argc, char *argv[]) { if (need_umount) { log_info("Unmounting file systems."); r = umount_all(&changed); - if (r == 0) + if (r == 0) { need_umount = false; - else if (r > 0) + log_info("All filesystems unmounted."); + } else if (r > 0) log_info("Not all file systems unmounted, %d left.", r); else log_error("Failed to unmount file systems: %s", strerror(-r)); } if (need_swapoff) { - log_info("Disabling swaps."); + log_info("Deactivating swaps."); r = swapoff_all(&changed); - if (r == 0) + if (r == 0) { need_swapoff = false; - else if (r > 0) - log_info("Not all swaps are turned off, %d left.", r); + log_info("All swaps deactivated."); + } else if (r > 0) + log_info("Not all swaps deactivated, %d left.", r); else - log_error("Failed to turn off swaps: %s", strerror(-r)); + log_error("Failed to deactivate swaps: %s", strerror(-r)); } if (need_loop_detach) { log_info("Detaching loop devices."); r = loopback_detach_all(&changed); - if (r == 0) + if (r == 0) { need_loop_detach = false; - else if (r > 0) + log_info("All loop devices detached."); + } else if (r > 0) log_info("Not all loop devices detached, %d left.", r); else log_error("Failed to detach loop devices: %s", strerror(-r)); @@ -231,10 +233,11 @@ int main(int argc, char *argv[]) { if (need_dm_detach) { log_info("Detaching DM devices."); r = dm_detach_all(&changed); - if (r == 0) + if (r == 0) { need_dm_detach = false; - else if (r > 0) - log_warning("Not all DM devices detached, %d left.", r); + log_info("All DM devices detached."); + } else if (r > 0) + log_info("Not all DM devices detached, %d left.", r); else log_error("Failed to detach DM devices: %s", strerror(-r)); } @@ -264,14 +267,8 @@ int main(int argc, char *argv[]) { arguments[2] = NULL; execute_directory(SYSTEM_SHUTDOWN_PATH, NULL, arguments); - /* If we are in a container, just exit, this will kill our - * container for good. */ - if (in_container) { - log_error("Exiting container."); - exit(0); - } - - if (access("/run/initramfs/shutdown", X_OK) == 0) { + if (!in_container && !in_initrd() && + access("/run/initramfs/shutdown", X_OK) == 0) { if (prepare_new_root() >= 0 && pivot_to_new_root() >= 0) { @@ -280,28 +277,45 @@ int main(int argc, char *argv[]) { } } - sync(); + /* The kernel will automaticall flush ATA disks and suchlike + * on reboot(), but the file systems need to be synce'd + * explicitly in advance. So let's do this here, but not + * needlessly slow down containers. */ + if (!in_container) + sync(); if (cmd == LINUX_REBOOT_CMD_KEXEC) { - /* We cheat and exec kexec to avoid doing all its work */ - pid_t pid = fork(); - - if (pid < 0) - log_error("Could not fork: %m. Falling back to normal reboot."); - else if (pid > 0) { - wait_for_terminate_and_warn("kexec", pid); - log_warning("kexec failed. Falling back to normal reboot."); - } else { - /* Child */ - const char *args[3] = { "/sbin/kexec", "-e", NULL }; - execv(args[0], (char * const *) args); - return EXIT_FAILURE; + + if (!in_container) { + /* We cheat and exec kexec to avoid doing all its work */ + pid_t pid = fork(); + + if (pid < 0) + log_error("Could not fork: %m. Falling back to normal reboot."); + else if (pid > 0) { + wait_for_terminate_and_warn("kexec", pid); + log_warning("kexec failed. Falling back to normal reboot."); + } else { + /* Child */ + const char *args[3] = { "/sbin/kexec", "-e", NULL }; + execv(args[0], (char * const *) args); + return EXIT_FAILURE; + } } cmd = RB_AUTOBOOT; } reboot(cmd); + + if (errno == EPERM && in_container) { + /* If we are in a container, and we lacked + * CAP_SYS_BOOT just exit, this will kill our + * container for good. */ + log_error("Exiting container."); + exit(0); + } + log_error("Failed to invoke reboot(): %m"); r = -errno; diff --git a/src/core/socket.c b/src/core/socket.c index 837b166e3b..fcbcdbe192 100644 --- a/src/core/socket.c +++ b/src/core/socket.c @@ -28,6 +28,9 @@ #include <signal.h> #include <arpa/inet.h> #include <mqueue.h> +#ifdef HAVE_ATTR_XATTR_H +#include <attr/xattr.h> +#endif #include "unit.h" #include "socket.h" @@ -39,6 +42,7 @@ #include "mkdir.h" #include "path-util.h" #include "unit-name.h" +#include "unit-printf.h" #include "dbus-socket.h" #include "missing.h" #include "special.h" @@ -130,6 +134,10 @@ static void socket_done(Unit *u) { free(s->bind_to_device); s->bind_to_device = NULL; + free(s->smack); + free(s->smack_ip_in); + free(s->smack_ip_out); + unit_unwatch_timer(u, &s->timer_watch); } @@ -205,27 +213,35 @@ static int socket_verify(Socket *s) { return 0; if (!s->ports) { - log_error("%s lacks Listen setting. Refusing.", UNIT(s)->id); + log_error_unit(UNIT(s)->id, + "%s lacks Listen setting. Refusing.", UNIT(s)->id); return -EINVAL; } if (s->accept && have_non_accept_socket(s)) { - log_error("%s configured for accepting sockets, but sockets are non-accepting. Refusing.", UNIT(s)->id); + log_error_unit(UNIT(s)->id, + "%s configured for accepting sockets, but sockets are non-accepting. Refusing.", + UNIT(s)->id); return -EINVAL; } if (s->accept && s->max_connections <= 0) { - log_error("%s's MaxConnection setting too small. Refusing.", UNIT(s)->id); + log_error_unit(UNIT(s)->id, + "%s's MaxConnection setting too small. Refusing.", UNIT(s)->id); return -EINVAL; } if (s->accept && UNIT_DEREF(s->service)) { - log_error("Explicit service configuration for accepting sockets not supported on %s. Refusing.", UNIT(s)->id); + log_error_unit(UNIT(s)->id, + "Explicit service configuration for accepting sockets not supported on %s. Refusing.", + UNIT(s)->id); return -EINVAL; } if (s->exec_context.pam_name && s->kill_context.kill_mode != KILL_CONTROL_GROUP) { - log_error("%s has PAM enabled. Kill mode must be set to 'control-group'. Refusing.", UNIT(s)->id); + log_error_unit(UNIT(s)->id, + "%s has PAM enabled. Kill mode must be set to 'control-group'. Refusing.", + UNIT(s)->id); return -EINVAL; } @@ -264,7 +280,8 @@ int socket_add_one_mount_link(Socket *s, Mount *m) { if (!socket_needs_mount(s, m->where)) return 0; - if ((r = unit_add_two_dependencies(UNIT(s), UNIT_AFTER, UNIT_REQUIRES, UNIT(m), true)) < 0) + r = unit_add_two_dependencies(UNIT(s), UNIT_AFTER, UNIT_REQUIRES, UNIT(m), true); + if (r < 0) return r; return 0; @@ -276,9 +293,11 @@ static int socket_add_mount_links(Socket *s) { assert(s); - LIST_FOREACH(units_by_type, other, UNIT(s)->manager->units_by_type[UNIT_MOUNT]) - if ((r = socket_add_one_mount_link(s, MOUNT(other))) < 0) + LIST_FOREACH(units_by_type, other, UNIT(s)->manager->units_by_type[UNIT_MOUNT]) { + r = socket_add_one_mount_link(s, MOUNT(other)); + if (r < 0) return r; + } return 0; } @@ -305,7 +324,7 @@ static int socket_add_default_dependencies(Socket *s) { int r; assert(s); - if (UNIT(s)->manager->running_as == MANAGER_SYSTEM) { + if (UNIT(s)->manager->running_as == SYSTEMD_SYSTEM) { if ((r = unit_add_dependency_by_name(UNIT(s), UNIT_BEFORE, SPECIAL_SOCKETS_TARGET, NULL, true)) < 0) return r; @@ -504,6 +523,21 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) { "%sMessageQueueMessageSize: %li\n", prefix, s->mq_msgsize); + if (s->smack) + fprintf(f, + "%sSmackLabel: %s\n", + prefix, s->smack); + + if (s->smack_ip_in) + fprintf(f, + "%sSmackLabelIPIn: %s\n", + prefix, s->smack_ip_in); + + if (s->smack_ip_out) + fprintf(f, + "%sSmackLabelIPOut: %s\n", + prefix, s->smack_ip_out); + LIST_FOREACH(port, p, s->ports) { if (p->type == SOCKET_SOCKET) { @@ -584,7 +618,7 @@ static int instance_from_socket(int fd, unsigned nr, char **instance) { } case AF_INET6: { - static const char ipv4_prefix[] = { + static const unsigned char ipv4_prefix[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF }; @@ -674,30 +708,30 @@ static void socket_apply_socket_options(Socket *s, int fd) { if (s->keep_alive) { int b = s->keep_alive; if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &b, sizeof(b)) < 0) - log_warning("SO_KEEPALIVE failed: %m"); + log_warning_unit(UNIT(s)->id, "SO_KEEPALIVE failed: %m"); } if (s->broadcast) { int one = 1; if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &one, sizeof(one)) < 0) - log_warning("SO_BROADCAST failed: %m"); + log_warning_unit(UNIT(s)->id, "SO_BROADCAST failed: %m"); } if (s->pass_cred) { int one = 1; if (setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)) < 0) - log_warning("SO_PASSCRED failed: %m"); + log_warning_unit(UNIT(s)->id, "SO_PASSCRED failed: %m"); } if (s->pass_sec) { int one = 1; if (setsockopt(fd, SOL_SOCKET, SO_PASSSEC, &one, sizeof(one)) < 0) - log_warning("SO_PASSSEC failed: %m"); + log_warning_unit(UNIT(s)->id, "SO_PASSSEC failed: %m"); } if (s->priority >= 0) if (setsockopt(fd, SOL_SOCKET, SO_PRIORITY, &s->priority, sizeof(s->priority)) < 0) - log_warning("SO_PRIORITY failed: %m"); + log_warning_unit(UNIT(s)->id, "SO_PRIORITY failed: %m"); if (s->receive_buffer > 0) { int value = (int) s->receive_buffer; @@ -706,23 +740,23 @@ static void socket_apply_socket_options(Socket *s, int fd) { if (setsockopt(fd, SOL_SOCKET, SO_RCVBUFFORCE, &value, sizeof(value)) < 0) if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &value, sizeof(value)) < 0) - log_warning("SO_RCVBUF failed: %m"); + log_warning_unit(UNIT(s)->id, "SO_RCVBUF failed: %m"); } if (s->send_buffer > 0) { int value = (int) s->send_buffer; if (setsockopt(fd, SOL_SOCKET, SO_SNDBUFFORCE, &value, sizeof(value)) < 0) if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &value, sizeof(value)) < 0) - log_warning("SO_SNDBUF failed: %m"); + log_warning_unit(UNIT(s)->id, "SO_SNDBUF failed: %m"); } if (s->mark >= 0) if (setsockopt(fd, SOL_SOCKET, SO_MARK, &s->mark, sizeof(s->mark)) < 0) - log_warning("SO_MARK failed: %m"); + log_warning_unit(UNIT(s)->id, "SO_MARK failed: %m"); if (s->ip_tos >= 0) if (setsockopt(fd, IPPROTO_IP, IP_TOS, &s->ip_tos, sizeof(s->ip_tos)) < 0) - log_warning("IP_TOS failed: %m"); + log_warning_unit(UNIT(s)->id, "IP_TOS failed: %m"); if (s->ip_ttl >= 0) { int r, x; @@ -737,12 +771,25 @@ static void socket_apply_socket_options(Socket *s, int fd) { } if (r < 0 && x < 0) - log_warning("IP_TTL/IPV6_UNICAST_HOPS failed: %m"); + log_warning_unit(UNIT(s)->id, + "IP_TTL/IPV6_UNICAST_HOPS failed: %m"); } if (s->tcp_congestion) if (setsockopt(fd, SOL_TCP, TCP_CONGESTION, s->tcp_congestion, strlen(s->tcp_congestion)+1) < 0) - log_warning("TCP_CONGESTION failed: %m"); + log_warning_unit(UNIT(s)->id, "TCP_CONGESTION failed: %m"); + +#ifdef HAVE_ATTR_XATTR_H + if (s->smack_ip_in) + if (fsetxattr(fd, "security.SMACK64IPIN", s->smack_ip_in, strlen(s->smack_ip_in), 0) < 0) + log_error_unit(UNIT(s)->id, + "fsetxattr(\"security.SMACK64IPIN\"): %m"); + + if (s->smack_ip_out) + if (fsetxattr(fd, "security.SMACK64IPOUT", s->smack_ip_out, strlen(s->smack_ip_out), 0) < 0) + log_error_unit(UNIT(s)->id, + "fsetxattr(\"security.SMACK64IPOUT\"): %m"); +#endif } static void socket_apply_fifo_options(Socket *s, int fd) { @@ -751,7 +798,15 @@ static void socket_apply_fifo_options(Socket *s, int fd) { if (s->pipe_size > 0) if (fcntl(fd, F_SETPIPE_SZ, s->pipe_size) < 0) - log_warning("F_SETPIPE_SZ: %m"); + log_warning_unit(UNIT(s)->id, + "F_SETPIPE_SZ: %m"); + +#ifdef HAVE_ATTR_XATTR_H + if (s->smack) + if (fsetxattr(fd, "security.SMACK64", s->smack, strlen(s->smack), 0) < 0) + log_error_unit(UNIT(s)->id, + "fsetxattr(\"security.SMACK64\"): %m"); +#endif } static int fifo_address_create( @@ -1075,10 +1130,10 @@ static void socket_set_state(Socket *s, SocketState state) { socket_close_fds(s); if (state != old_state) - log_debug("%s changed %s -> %s", - UNIT(s)->id, - socket_state_to_string(old_state), - socket_state_to_string(state)); + log_debug_unit(UNIT(s)->id, + "%s changed %s -> %s", UNIT(s)->id, + socket_state_to_string(old_state), + socket_state_to_string(state)); unit_notify(UNIT(s), state_translation_table[old_state], state_translation_table[state], true); } @@ -1104,10 +1159,12 @@ static int socket_coldplug(Unit *u) { if (s->control_pid <= 0) return -EBADMSG; - if ((r = unit_watch_pid(UNIT(s), s->control_pid)) < 0) + r = unit_watch_pid(UNIT(s), s->control_pid); + if (r < 0) return r; - if ((r = unit_watch_timer(UNIT(s), s->timeout_usec, &s->timer_watch)) < 0) + r = unit_watch_timer(UNIT(s), CLOCK_MONOTONIC, true, s->timeout_usec, &s->timer_watch); + if (r < 0) return r; } @@ -1139,10 +1196,12 @@ static int socket_spawn(Socket *s, ExecCommand *c, pid_t *_pid) { assert(c); assert(_pid); - if ((r = unit_watch_timer(UNIT(s), s->timeout_usec, &s->timer_watch)) < 0) + r = unit_watch_timer(UNIT(s), CLOCK_MONOTONIC, true, s->timeout_usec, &s->timer_watch); + if (r < 0) goto fail; - if (!(argv = unit_full_printf_strv(UNIT(s), c->argv))) { + argv = unit_full_printf_strv(UNIT(s), c->argv); + if (!argv) { r = -ENOMEM; goto fail; } @@ -1214,7 +1273,9 @@ static void socket_enter_stop_post(Socket *s, SocketResult f) { return; fail: - log_warning("%s failed to run 'stop-post' task: %s", UNIT(s)->id, strerror(-r)); + log_warning_unit(UNIT(s)->id, + "%s failed to run 'stop-post' task: %s", + UNIT(s)->id, strerror(-r)); socket_enter_signal(s, SOCKET_FINAL_SIGTERM, SOCKET_FAILURE_RESOURCES); } @@ -1234,7 +1295,9 @@ static void socket_enter_signal(Socket *s, SocketState state, SocketResult f) { if (s->control_pid > 0) { if (kill_and_sigcont(s->control_pid, sig) < 0 && errno != ESRCH) - log_warning("Failed to kill control process %li: %m", (long) s->control_pid); + log_warning_unit(UNIT(s)->id, + "Failed to kill control process %li: %m", + (long) s->control_pid); else wait_for_exit = true; } @@ -1254,7 +1317,9 @@ static void socket_enter_signal(Socket *s, SocketState state, SocketResult f) { r = cgroup_bonding_kill_list(UNIT(s)->cgroup_bondings, sig, true, false, pid_set, NULL); if (r < 0) { if (r != -EAGAIN && r != -ESRCH && r != -ENOENT) - log_warning("Failed to kill control group: %s", strerror(-r)); + log_warning_unit(UNIT(s)->id, + "Failed to kill control group: %s", + strerror(-r)); } else if (r > 0) wait_for_exit = true; @@ -1264,7 +1329,8 @@ static void socket_enter_signal(Socket *s, SocketState state, SocketResult f) { } if (wait_for_exit) { - if ((r = unit_watch_timer(UNIT(s), s->timeout_usec, &s->timer_watch)) < 0) + r = unit_watch_timer(UNIT(s), CLOCK_MONOTONIC, true, s->timeout_usec, &s->timer_watch); + if (r < 0) goto fail; socket_set_state(s, state); @@ -1276,7 +1342,9 @@ static void socket_enter_signal(Socket *s, SocketState state, SocketResult f) { return; fail: - log_warning("%s failed to kill processes: %s", UNIT(s)->id, strerror(-r)); + log_warning_unit(UNIT(s)->id, + "%s failed to kill processes: %s", + UNIT(s)->id, strerror(-r)); if (state == SOCKET_STOP_PRE_SIGTERM || state == SOCKET_STOP_PRE_SIGKILL) socket_enter_stop_post(s, SOCKET_FAILURE_RESOURCES); @@ -1309,7 +1377,9 @@ static void socket_enter_stop_pre(Socket *s, SocketResult f) { return; fail: - log_warning("%s failed to run 'stop-pre' task: %s", UNIT(s)->id, strerror(-r)); + log_warning_unit(UNIT(s)->id, + "%s failed to run 'stop-pre' task: %s", + UNIT(s)->id, strerror(-r)); socket_enter_stop_post(s, SOCKET_FAILURE_RESOURCES); } @@ -1319,7 +1389,9 @@ static void socket_enter_listening(Socket *s) { r = socket_watch_fds(s); if (r < 0) { - log_warning("%s failed to watch sockets: %s", UNIT(s)->id, strerror(-r)); + log_warning_unit(UNIT(s)->id, + "%s failed to watch sockets: %s", + UNIT(s)->id, strerror(-r)); goto fail; } @@ -1336,7 +1408,9 @@ static void socket_enter_start_post(Socket *s) { r = socket_open_fds(s); if (r < 0) { - log_warning("%s failed to listen on sockets: %s", UNIT(s)->id, strerror(-r)); + log_warning_unit(UNIT(s)->id, + "%s failed to listen on sockets: %s", + UNIT(s)->id, strerror(-r)); goto fail; } @@ -1347,7 +1421,9 @@ static void socket_enter_start_post(Socket *s) { if ((s->control_command = s->exec_command[SOCKET_EXEC_START_POST])) { r = socket_spawn(s, s->control_command, &s->control_pid); if (r < 0) { - log_warning("%s failed to run 'start-post' task: %s", UNIT(s)->id, strerror(-r)); + log_warning_unit(UNIT(s)->id, + "%s failed to run 'start-post' task: %s", + UNIT(s)->id, strerror(-r)); goto fail; } @@ -1380,7 +1456,9 @@ static void socket_enter_start_pre(Socket *s) { return; fail: - log_warning("%s failed to run 'start-pre' task: %s", UNIT(s)->id, strerror(-r)); + log_warning_unit(UNIT(s)->id, + "%s failed to run 'start-pre' task: %s", + UNIT(s)->id, strerror(-r)); socket_enter_dead(s, SOCKET_FAILURE_RESOURCES); } @@ -1394,7 +1472,9 @@ static void socket_enter_running(Socket *s, int cfd) { /* We don't take connections anymore if we are supposed to * shut down anyway */ if (unit_pending_inactive(UNIT(s))) { - log_debug("Suppressing connection request on %s since unit stop is scheduled.", UNIT(s)->id); + log_debug_unit(UNIT(s)->id, + "Suppressing connection request on %s since unit stop is scheduled.", + UNIT(s)->id); if (cfd >= 0) close_nointr_nofail(cfd); @@ -1404,7 +1484,9 @@ static void socket_enter_running(Socket *s, int cfd) { r = socket_watch_fds(s); if (r < 0) { - log_warning("%s failed to watch sockets: %s", UNIT(s)->id, strerror(-r)); + log_warning_unit(UNIT(s)->id, + "%s failed to watch sockets: %s", + UNIT(s)->id, strerror(-r)); socket_enter_stop_pre(s, SOCKET_FAILURE_RESOURCES); } } @@ -1437,7 +1519,9 @@ static void socket_enter_running(Socket *s, int cfd) { Service *service; if (s->n_connections >= s->max_connections) { - log_warning("Too many incoming connections (%u)", s->n_connections); + log_warning_unit(UNIT(s)->id, + "%s: Too many incoming connections (%u)", + UNIT(s)->id, s->n_connections); close_nointr_nofail(cfd); return; } @@ -1506,7 +1590,11 @@ static void socket_enter_running(Socket *s, int cfd) { return; fail: - log_warning("%s failed to queue socket startup job: %s", UNIT(s)->id, bus_error(&error, r)); + log_warning_unit(UNIT(s)->id, + "%s failed to queue service startup job (Maybe the service file is missing or not a %s unit?): %s", + UNIT(s)->id, + cfd >= 0 ? "template" : "non-template", + bus_error(&error, r)); socket_enter_stop_pre(s, SOCKET_FAILURE_RESOURCES); if (cfd >= 0) @@ -1532,7 +1620,9 @@ static void socket_run_next(Socket *s) { return; fail: - log_warning("%s failed to run next task: %s", UNIT(s)->id, strerror(-r)); + log_warning_unit(UNIT(s)->id, + "%s failed to run next task: %s", + UNIT(s)->id, strerror(-r)); if (s->state == SOCKET_START_POST) socket_enter_stop_pre(s, SOCKET_FAILURE_RESOURCES); @@ -1568,7 +1658,9 @@ static int socket_start(Unit *u) { service = SERVICE(UNIT_DEREF(s->service)); if (UNIT(service)->load_state != UNIT_LOADED) { - log_error("Socket service %s not loaded, refusing.", UNIT(service)->id); + log_error_unit(UNIT(service)->id, + "Socket service %s not loaded, refusing.", + UNIT(service)->id); return -ENOENT; } @@ -1577,13 +1669,16 @@ static int socket_start(Unit *u) { if (service->state != SERVICE_DEAD && service->state != SERVICE_FAILED && service->state != SERVICE_AUTO_RESTART) { - log_error("Socket service %s already active, refusing.", UNIT(service)->id); + log_error_unit(UNIT(service)->id, + "Socket service %s already active, refusing.", + UNIT(service)->id); return -EBUSY; } #ifdef HAVE_SYSV_COMPAT if (service->is_sysv) { - log_error("Using SysV services for socket activation is not supported. Refusing."); + log_error_unit(UNIT(s)->id, + "Using SysV services for socket activation is not supported. Refusing."); return -ENOENT; } #endif @@ -1655,7 +1750,8 @@ static int socket_serialize(Unit *u, FILE *f, FDSet *fds) { if (p->type == SOCKET_SOCKET) { char *t; - if ((r = socket_address_print(&p->address, &t)) < 0) + r = socket_address_print(&p->address, &t); + if (r < 0) return r; if (socket_address_family(&p->address) == AF_NETLINK) @@ -1665,6 +1761,8 @@ static int socket_serialize(Unit *u, FILE *f, FDSet *fds) { free(t); } else if (p->type == SOCKET_SPECIAL) unit_serialize_item_format(u, f, "special", "%i %s", copy, p->path); + else if (p->type == SOCKET_MQUEUE) + unit_serialize_item_format(u, f, "mqueue", "%i %s", copy, p->path); else { assert(p->type == SOCKET_FIFO); unit_serialize_item_format(u, f, "fifo", "%i %s", copy, p->path); @@ -1685,8 +1783,10 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value, if (streq(key, "state")) { SocketState state; - if ((state = socket_state_from_string(value)) < 0) - log_debug("Failed to parse state value %s", value); + state = socket_state_from_string(value); + if (state < 0) + log_debug_unit(u->id, + "Failed to parse state value %s", value); else s->deserialized_state = state; } else if (streq(key, "result")) { @@ -1694,7 +1794,8 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value, f = socket_result_from_string(value); if (f < 0) - log_debug("Failed to parse result value %s", value); + log_debug_unit(u->id, + "Failed to parse result value %s", value); else if (f != SOCKET_SUCCESS) s->result = f; @@ -1702,21 +1803,25 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value, unsigned k; if (safe_atou(value, &k) < 0) - log_debug("Failed to parse n-accepted value %s", value); + log_debug_unit(u->id, + "Failed to parse n-accepted value %s", value); else s->n_accepted += k; } else if (streq(key, "control-pid")) { pid_t pid; if (parse_pid(value, &pid) < 0) - log_debug("Failed to parse control-pid value %s", value); + log_debug_unit(u->id, + "Failed to parse control-pid value %s", value); else s->control_pid = pid; } else if (streq(key, "control-command")) { SocketExecCommand id; - if ((id = socket_exec_command_from_string(value)) < 0) - log_debug("Failed to parse exec-command value %s", value); + id = socket_exec_command_from_string(value); + if (id < 0) + log_debug_unit(u->id, + "Failed to parse exec-command value %s", value); else { s->control_command_id = id; s->control_command = s->exec_command[id]; @@ -1726,7 +1831,8 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value, SocketPort *p; if (sscanf(value, "%i %n", &fd, &skip) < 1 || fd < 0 || !fdset_contains(fds, fd)) - log_debug("Failed to parse fifo value %s", value); + log_debug_unit(u->id, + "Failed to parse fifo value %s", value); else { LIST_FOREACH(port, p, s->ports) @@ -1746,7 +1852,8 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value, SocketPort *p; if (sscanf(value, "%i %n", &fd, &skip) < 1 || fd < 0 || !fdset_contains(fds, fd)) - log_debug("Failed to parse special value %s", value); + log_debug_unit(u->id, + "Failed to parse special value %s", value); else { LIST_FOREACH(port, p, s->ports) @@ -1761,12 +1868,34 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value, } } + } else if (streq(key, "mqueue")) { + int fd, skip = 0; + SocketPort *p; + + if (sscanf(value, "%i %n", &fd, &skip) < 1 || fd < 0 || !fdset_contains(fds, fd)) + log_debug_unit(u->id, + "Failed to parse mqueue value %s", value); + else { + + LIST_FOREACH(port, p, s->ports) + if (p->type == SOCKET_MQUEUE && + streq_ptr(p->path, value+skip)) + break; + + if (p) { + if (p->fd >= 0) + close_nointr_nofail(p->fd); + p->fd = fdset_remove(fds, fd); + } + } + } else if (streq(key, "socket")) { int fd, type, skip = 0; SocketPort *p; if (sscanf(value, "%i %i %n", &fd, &type, &skip) < 2 || fd < 0 || type < 0 || !fdset_contains(fds, fd)) - log_debug("Failed to parse socket value %s", value); + log_debug_unit(u->id, + "Failed to parse socket value %s", value); else { LIST_FOREACH(port, p, s->ports) @@ -1785,7 +1914,8 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value, SocketPort *p; if (sscanf(value, "%i %n", &fd, &skip) < 1 || fd < 0 || !fdset_contains(fds, fd)) - log_debug("Failed to parse socket value %s", value); + log_debug_unit(u->id, + "Failed to parse socket value %s", value); else { LIST_FOREACH(port, p, s->ports) @@ -1800,7 +1930,36 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value, } } else - log_debug("Unknown serialization key '%s'", key); + log_debug_unit(UNIT(s)->id, + "Unknown serialization key '%s'", key); + + return 0; +} + +static int socket_distribute_fds(Unit *u, FDSet *fds) { + Socket *s = SOCKET(u); + SocketPort *p; + + assert(u); + + LIST_FOREACH(port, p, s->ports) { + Iterator i; + int fd; + + if (p->type != SOCKET_SOCKET) + continue; + + if (p->fd >= 0) + continue; + + FDSET_FOREACH(fd, fds, i) { + if (socket_address_matches_fd(&p->address, fd)) { + p->fd = fdset_remove(fds, fd); + s->deserialized_state = SOCKET_LISTENING; + break; + } + } + } return 0; } @@ -1835,14 +1994,18 @@ static void socket_fd_event(Unit *u, int fd, uint32_t events, Watch *w) { if (s->state != SOCKET_LISTENING) return; - log_debug("Incoming traffic on %s", u->id); + log_debug_unit(u->id, "Incoming traffic on %s", u->id); if (events != EPOLLIN) { if (events & EPOLLHUP) - log_error("%s: Got POLLHUP on a listening socket. The service probably invoked shutdown() on it, and should better not do that.", u->id); + log_error_unit(u->id, + "%s: Got POLLHUP on a listening socket. The service probably invoked shutdown() on it, and should better not do that.", + u->id); else - log_error("%s: Got unexpected poll event (0x%x) on socket.", u->id, events); + log_error_unit(u->id, + "%s: Got unexpected poll event (0x%x) on socket.", + u->id, events); goto fail; } @@ -1850,12 +2013,14 @@ static void socket_fd_event(Unit *u, int fd, uint32_t events, Watch *w) { if (w->socket_accept) { for (;;) { - if ((cfd = accept4(fd, NULL, NULL, SOCK_NONBLOCK)) < 0) { + cfd = accept4(fd, NULL, NULL, SOCK_NONBLOCK); + if (cfd < 0) { if (errno == EINTR) continue; - log_error("Failed to accept socket: %m"); + log_error_unit(u->id, + "Failed to accept socket: %m"); goto fail; } @@ -1884,7 +2049,7 @@ static void socket_sigchld_event(Unit *u, pid_t pid, int code, int status) { s->control_pid = 0; - if (is_clean_exit(code, status)) + if (is_clean_exit(code, status, NULL)) f = SOCKET_SUCCESS; else if (code == CLD_EXITED) f = SOCKET_FAILURE_EXIT_CODE; @@ -1902,8 +2067,10 @@ static void socket_sigchld_event(Unit *u, pid_t pid, int code, int status) { f = SOCKET_SUCCESS; } - log_full(f == SOCKET_SUCCESS ? LOG_DEBUG : LOG_NOTICE, - "%s control process exited, code=%s status=%i", u->id, sigchld_code_to_string(code), status); + log_full_unit(f == SOCKET_SUCCESS ? LOG_DEBUG : LOG_NOTICE, + u->id, + "%s control process exited, code=%s status=%i", + u->id, sigchld_code_to_string(code), status); if (f != SOCKET_SUCCESS) s->result = f; @@ -1912,7 +2079,9 @@ static void socket_sigchld_event(Unit *u, pid_t pid, int code, int status) { s->control_command->command_next && f == SOCKET_SUCCESS) { - log_debug("%s running next command for state %s", u->id, socket_state_to_string(s->state)); + log_debug_unit(u->id, + "%s running next command for state %s", + u->id, socket_state_to_string(s->state)); socket_run_next(s); } else { s->control_command = NULL; @@ -1921,7 +2090,9 @@ static void socket_sigchld_event(Unit *u, pid_t pid, int code, int status) { /* No further commands for this step, so let's figure * out what to do next */ - log_debug("%s got final SIGCHLD for state %s", u->id, socket_state_to_string(s->state)); + log_debug_unit(u->id, + "%s got final SIGCHLD for state %s", + u->id, socket_state_to_string(s->state)); switch (s->state) { @@ -1970,52 +2141,65 @@ static void socket_timer_event(Unit *u, uint64_t elapsed, Watch *w) { switch (s->state) { case SOCKET_START_PRE: - log_warning("%s starting timed out. Terminating.", u->id); + log_warning_unit(u->id, + "%s starting timed out. Terminating.", u->id); socket_enter_signal(s, SOCKET_FINAL_SIGTERM, SOCKET_FAILURE_TIMEOUT); break; case SOCKET_START_POST: - log_warning("%s starting timed out. Stopping.", u->id); + log_warning_unit(u->id, + "%s starting timed out. Stopping.", u->id); socket_enter_stop_pre(s, SOCKET_FAILURE_TIMEOUT); break; case SOCKET_STOP_PRE: - log_warning("%s stopping timed out. Terminating.", u->id); + log_warning_unit(u->id, + "%s stopping timed out. Terminating.", u->id); socket_enter_signal(s, SOCKET_STOP_PRE_SIGTERM, SOCKET_FAILURE_TIMEOUT); break; case SOCKET_STOP_PRE_SIGTERM: if (s->kill_context.send_sigkill) { - log_warning("%s stopping timed out. Killing.", u->id); + log_warning_unit(u->id, + "%s stopping timed out. Killing.", u->id); socket_enter_signal(s, SOCKET_STOP_PRE_SIGKILL, SOCKET_FAILURE_TIMEOUT); } else { - log_warning("%s stopping timed out. Skipping SIGKILL. Ignoring.", u->id); + log_warning_unit(u->id, + "%s stopping timed out. Skipping SIGKILL. Ignoring.", + u->id); socket_enter_stop_post(s, SOCKET_FAILURE_TIMEOUT); } break; case SOCKET_STOP_PRE_SIGKILL: - log_warning("%s still around after SIGKILL. Ignoring.", u->id); + log_warning_unit(u->id, + "%s still around after SIGKILL. Ignoring.", u->id); socket_enter_stop_post(s, SOCKET_FAILURE_TIMEOUT); break; case SOCKET_STOP_POST: - log_warning("%s stopping timed out (2). Terminating.", u->id); + log_warning_unit(u->id, + "%s stopping timed out (2). Terminating.", u->id); socket_enter_signal(s, SOCKET_FINAL_SIGTERM, SOCKET_FAILURE_TIMEOUT); break; case SOCKET_FINAL_SIGTERM: if (s->kill_context.send_sigkill) { - log_warning("%s stopping timed out (2). Killing.", u->id); + log_warning_unit(u->id, + "%s stopping timed out (2). Killing.", u->id); socket_enter_signal(s, SOCKET_FINAL_SIGKILL, SOCKET_FAILURE_TIMEOUT); } else { - log_warning("%s stopping timed out (2). Skipping SIGKILL. Ignoring.", u->id); + log_warning_unit(u->id, + "%s stopping timed out (2). Skipping SIGKILL. Ignoring.", + u->id); socket_enter_dead(s, SOCKET_FAILURE_TIMEOUT); } break; case SOCKET_FINAL_SIGKILL: - log_warning("%s still around after SIGKILL (2). Entering failed mode.", u->id); + log_warning_unit(u->id, + "%s still around after SIGKILL (2). Entering failed mode.", + u->id); socket_enter_dead(s, SOCKET_FAILURE_TIMEOUT); break; @@ -2071,7 +2255,9 @@ void socket_notify_service_dead(Socket *s, bool failed_permanent) { * services. */ if (s->state == SOCKET_RUNNING) { - log_debug("%s got notified about service death (failed permanently: %s)", UNIT(s)->id, yes_no(failed_permanent)); + log_debug_unit(UNIT(s)->id, + "%s got notified about service death (failed permanently: %s)", + UNIT(s)->id, yes_no(failed_permanent)); if (failed_permanent) socket_enter_stop_pre(s, SOCKET_FAILURE_SERVICE_FAILED_PERMANENT); else @@ -2090,7 +2276,8 @@ void socket_connection_unref(Socket *s) { assert(s->n_connections > 0); s->n_connections--; - log_debug("%s: One connection closed, %u left.", UNIT(s)->id, s->n_connections); + log_debug_unit(UNIT(s)->id, + "%s: One connection closed, %u left.", UNIT(s)->id, s->n_connections); } static void socket_reset_failed(Unit *u) { @@ -2194,6 +2381,8 @@ DEFINE_STRING_TABLE_LOOKUP(socket_result, SocketResult); const UnitVTable socket_vtable = { .object_size = sizeof(Socket), + .exec_context_offset = offsetof(Socket, exec_context), + .sections = "Unit\0" "Socket\0" @@ -2214,6 +2403,7 @@ const UnitVTable socket_vtable = { .serialize = socket_serialize, .deserialize_item = socket_deserialize_item, + .distribute_fds = socket_distribute_fds, .active_state = socket_active_state, .sub_state_to_string = socket_sub_state_to_string, diff --git a/src/core/socket.h b/src/core/socket.h index a06b3eae94..f099520dce 100644 --- a/src/core/socket.h +++ b/src/core/socket.h @@ -144,6 +144,10 @@ struct Socket { /* Only for INET6 sockets: issue IPV6_V6ONLY sockopt */ SocketAddressBindIPv6Only bind_ipv6_only; + + char *smack; + char *smack_ip_in; + char *smack_ip_out; }; /* Called from the service code when collecting fds */ diff --git a/src/core/special.h b/src/core/special.h index e3004a518d..ef72260ecd 100644 --- a/src/core/special.h +++ b/src/core/special.h @@ -37,6 +37,7 @@ #define SPECIAL_EXIT_TARGET "exit.target" #define SPECIAL_SUSPEND_TARGET "suspend.target" #define SPECIAL_HIBERNATE_TARGET "hibernate.target" +#define SPECIAL_HYBRID_SLEEP_TARGET "hybrid-sleep.target" /* Special boot targets */ #define SPECIAL_RESCUE_TARGET "rescue.target" @@ -58,9 +59,34 @@ #define SPECIAL_RPCBIND_TARGET "rpcbind.target" /* LSB's $portmap */ #define SPECIAL_SYSLOG_TARGET "syslog.target" /* LSB's $syslog */ #define SPECIAL_TIME_SYNC_TARGET "time-sync.target" /* LSB's $time */ -#define SPECIAL_DISPLAY_MANAGER_SERVICE "display-manager.service" /* Debian's $x-display-manager */ -#define SPECIAL_MAIL_TRANSFER_AGENT_TARGET "mail-transfer-agent.target" /* Debian's $mail-{transport|transfer-agent */ -#define SPECIAL_HTTP_DAEMON_TARGET "http-daemon.target" +#define SPECIAL_DISPLAY_MANAGER_SERVICE "display-manager.service" /* Common extension of LSB */ +#define SPECIAL_MAIL_TRANSFER_AGENT_TARGET "mail-transfer-agent.target" /* Common extension of LSB */ + +/* + * Rules regarding adding further high level targets like the above: + * + * - Be conservative, only add more of these when we really need + * them. We need strong usecases for further additions. + * + * - When there can be multiple implementations running side-by-side, + * it needs to be a .target unit which can pull in all + * implementations. + * + * - If something can be implemented with socket activation, and + * without, it needs to be a .target unit, so that it can pull in + * the appropriate unit. + * + * - Otherwise, it should be a .service unit. + * + * - In some cases it is OK to have both a .service and a .target + * unit, i.e. if there can be multiple parallel implementations, but + * only one is the "system" one. Example: syslog. + * + * Or to put this in other words: .service symlinks can be used to + * arbitrate between multiple implementations if there can be only one + * of a kind. .target units can be used to support multiple + * implementations that can run side-by-side. + */ /* Magic early boot services */ #define SPECIAL_FSCK_SERVICE "systemd-fsck@.service" diff --git a/src/core/swap.c b/src/core/swap.c index 458e00efe5..c8e25d0665 100644 --- a/src/core/swap.c +++ b/src/core/swap.c @@ -39,6 +39,7 @@ #include "exit-status.h" #include "def.h" #include "path-util.h" +#include "virt.h" static const UnitActiveState state_translation_table[_SWAP_STATE_MAX] = { [SWAP_DEAD] = UNIT_INACTIVE, @@ -54,6 +55,7 @@ static const UnitActiveState state_translation_table[_SWAP_STATE_MAX] = { static void swap_unset_proc_swaps(Swap *s) { Swap *first; + Hashmap *swaps; assert(s); @@ -62,14 +64,17 @@ static void swap_unset_proc_swaps(Swap *s) { /* Remove this unit from the chain of swaps which share the * same kernel swap device. */ - - first = hashmap_get(UNIT(s)->manager->swaps_by_proc_swaps, s->parameters_proc_swaps.what); + swaps = UNIT(s)->manager->swaps_by_proc_swaps; + first = hashmap_get(swaps, s->parameters_proc_swaps.what); LIST_REMOVE(Swap, same_proc_swaps, first, s); if (first) - hashmap_remove_and_replace(UNIT(s)->manager->swaps_by_proc_swaps, s->parameters_proc_swaps.what, first->parameters_proc_swaps.what, first); + hashmap_remove_and_replace(swaps, + s->parameters_proc_swaps.what, + first->parameters_proc_swaps.what, + first); else - hashmap_remove(UNIT(s)->manager->swaps_by_proc_swaps, s->parameters_proc_swaps.what); + hashmap_remove(swaps, s->parameters_proc_swaps.what); free(s->parameters_proc_swaps.what); s->parameters_proc_swaps.what = NULL; @@ -145,7 +150,8 @@ int swap_add_one_mount_link(Swap *s, Mount *m) { if (!path_startswith(s->what, m->where)) return 0; - if ((r = unit_add_two_dependencies(UNIT(s), UNIT_AFTER, UNIT_REQUIRES, UNIT(m), true)) < 0) + r = unit_add_two_dependencies(UNIT(s), UNIT_AFTER, UNIT_REQUIRES, UNIT(m), true); + if (r < 0) return r; return 0; @@ -164,21 +170,6 @@ static int swap_add_mount_links(Swap *s) { return 0; } -static int swap_add_target_links(Swap *s) { - Unit *tu; - int r; - - assert(s); - - if (!s->from_fragment) - return 0; - - if ((r = manager_load_unit(UNIT(s)->manager, SPECIAL_SWAP_TARGET, NULL, NULL, &tu)) < 0) - return r; - - return unit_add_dependency(UNIT(s), UNIT_BEFORE, tu, true); -} - static int swap_add_device_links(Swap *s) { SwapParameters *p; @@ -195,7 +186,7 @@ static int swap_add_device_links(Swap *s) { if (is_device_path(s->what)) return unit_add_node_link(UNIT(s), s->what, !p->noauto && p->nofail && - UNIT(s)->manager->running_as == MANAGER_SYSTEM); + UNIT(s)->manager->running_as == SYSTEMD_SYSTEM); else /* File based swap devices need to be ordered after * systemd-remount-fs.service, since they might need a @@ -208,7 +199,10 @@ static int swap_add_default_dependencies(Swap *s) { assert(s); - if (UNIT(s)->manager->running_as != MANAGER_SYSTEM) + if (UNIT(s)->manager->running_as != SYSTEMD_SYSTEM) + return 0; + + if (detect_container(NULL) > 0) return 0; r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_UMOUNT_TARGET, NULL, true); @@ -220,24 +214,27 @@ static int swap_add_default_dependencies(Swap *s) { static int swap_verify(Swap *s) { bool b; - char *e; + char _cleanup_free_ *e = NULL; if (UNIT(s)->load_state != UNIT_LOADED) return 0; - if (!(e = unit_name_from_path(s->what, ".swap"))) - return -ENOMEM; + e = unit_name_from_path(s->what, ".swap"); + if (e == NULL) + return log_oom(); b = unit_has_name(UNIT(s), e); - free(e); - if (!b) { - log_error("%s: Value of \"What\" and unit name do not match, not loading.\n", UNIT(s)->id); + log_error_unit(UNIT(s)->id, + "%s: Value of \"What\" and unit name do not match, not loading.", + UNIT(s)->id); return -EINVAL; } if (s->exec_context.pam_name && s->kill_context.kill_mode != KILL_CONTROL_GROUP) { - log_error("%s has PAM enabled. Kill mode must be set to 'control-group'. Refusing.", UNIT(s)->id); + log_error_unit(UNIT(s)->id, + "%s has PAM enabled. Kill mode must be set to 'control-group'. Refusing to load.", + UNIT(s)->id); return -EINVAL; } @@ -252,11 +249,13 @@ static int swap_load(Unit *u) { assert(u->load_state == UNIT_STUB); /* Load a .swap file */ - if ((r = unit_load_fragment_and_dropin_optional(u)) < 0) + r = unit_load_fragment_and_dropin_optional(u); + if (r < 0) return r; if (u->load_state == UNIT_LOADED) { - if ((r = unit_add_exec_dependencies(u, &s->exec_context)) < 0) + r = unit_add_exec_dependencies(u, &s->exec_context); + if (r < 0) return r; if (UNIT(s)->fragment_path) @@ -280,21 +279,23 @@ static int swap_load(Unit *u) { if ((r = unit_set_description(u, s->what)) < 0) return r; - if ((r = swap_add_device_links(s)) < 0) - return r; - - if ((r = swap_add_mount_links(s)) < 0) + r = swap_add_device_links(s); + if (r < 0) return r; - if ((r = swap_add_target_links(s)) < 0) + r = swap_add_mount_links(s); + if (r < 0) return r; - if ((r = unit_add_default_cgroups(u)) < 0) + r = unit_add_default_cgroups(u); + if (r < 0) return r; - if (UNIT(s)->default_dependencies) - if ((r = swap_add_default_dependencies(s)) < 0) + if (UNIT(s)->default_dependencies) { + r = swap_add_default_dependencies(s); + if (r < 0) return r; + } r = unit_exec_context_defaults(u, &s->exec_context); if (r < 0) @@ -314,7 +315,8 @@ static int swap_add_one( bool set_flags) { Unit *u = NULL; - char *e = NULL, *wp = NULL; + char _cleanup_free_ *e = NULL; + char *wp = NULL; bool delete = false; int r; SwapParameters *p; @@ -326,7 +328,7 @@ static int swap_add_one( e = unit_name_from_path(what, ".swap"); if (!e) - return -ENOMEM; + return log_oom(); u = manager_get_unit(m, e); @@ -339,10 +341,8 @@ static int swap_add_one( delete = true; u = unit_new(m, sizeof(Swap)); - if (!u) { - free(e); - return -ENOMEM; - } + if (!u) + return log_oom(); r = unit_add_name(u, e); if (r < 0) @@ -350,7 +350,7 @@ static int swap_add_one( SWAP(u)->what = strdup(what); if (!SWAP(u)->what) { - r = -ENOMEM; + r = log_oom(); goto fail; } @@ -361,16 +361,19 @@ static int swap_add_one( p = &SWAP(u)->parameters_proc_swaps; if (!p->what) { - if (!(wp = strdup(what_proc_swaps))) { - r = -ENOMEM; + wp = strdup(what_proc_swaps); + if (!wp) { + r = log_oom(); goto fail; } - if (!m->swaps_by_proc_swaps) - if (!(m->swaps_by_proc_swaps = hashmap_new(string_hash_func, string_compare_func))) { - r = -ENOMEM; + if (!m->swaps_by_proc_swaps) { + m->swaps_by_proc_swaps = hashmap_new(string_hash_func, string_compare_func); + if (!m->swaps_by_proc_swaps) { + r = log_oom(); goto fail; } + } free(p->what); p->what = wp; @@ -378,7 +381,8 @@ static int swap_add_one( first = hashmap_get(m->swaps_by_proc_swaps, wp); LIST_PREPEND(Swap, same_proc_swaps, first, SWAP(u)); - if ((r = hashmap_replace(m->swaps_by_proc_swaps, wp, first)) < 0) + r = hashmap_replace(m->swaps_by_proc_swaps, wp, first); + if (r < 0) goto fail; } @@ -395,15 +399,12 @@ static int swap_add_one( unit_add_to_dbus_queue(u); - free(e); - return 0; fail: - log_warning("Failed to load swap unit: %s", strerror(-r)); + log_warning_unit(e, "Failed to load swap unit: %s", strerror(-r)); free(wp); - free(e); if (delete && u) unit_free(u); @@ -425,11 +426,13 @@ static int swap_process_new_swap(Manager *m, const char *device, int prio, bool /* So this is a proper swap device. Create swap units * for all names this swap device is known under */ - if (!(d = udev_device_new_from_devnum(m->udev, 'b', st.st_rdev))) - return -ENOMEM; + d = udev_device_new_from_devnum(m->udev, 'b', st.st_rdev); + if (!d) + return log_oom(); dn = udev_device_get_devnode(d); - if (dn) + /* Skip dn==device, since that case will be handled below */ + if (dn && !streq(dn, device)) r = swap_add_one(m, dn, device, prio, false, false, set_flags); /* Add additional units for all symlinks */ @@ -444,7 +447,8 @@ static int swap_process_new_swap(Manager *m, const char *device, int prio, bool continue; if (stat(p, &st) >= 0) - if ((!S_ISBLK(st.st_mode)) || st.st_rdev != udev_device_get_devnum(d)) + if ((!S_ISBLK(st.st_mode)) || + st.st_rdev != udev_device_get_devnum(d)) continue; k = swap_add_one(m, p, device, prio, false, false, set_flags); @@ -483,12 +487,14 @@ static void swap_set_state(Swap *s, SwapState state) { } if (state != old_state) - log_debug("%s changed %s -> %s", - UNIT(s)->id, - swap_state_to_string(old_state), - swap_state_to_string(state)); - - unit_notify(UNIT(s), state_translation_table[old_state], state_translation_table[state], true); + log_debug_unit(UNIT(s)->id, + "%s changed %s -> %s", + UNIT(s)->id, + swap_state_to_string(old_state), + swap_state_to_string(state)); + + unit_notify(UNIT(s), state_translation_table[old_state], + state_translation_table[state], true); } static int swap_coldplug(Unit *u) { @@ -516,10 +522,12 @@ static int swap_coldplug(Unit *u) { if (s->control_pid <= 0) return -EBADMSG; - if ((r = unit_watch_pid(UNIT(s), s->control_pid)) < 0) + r = unit_watch_pid(UNIT(s), s->control_pid); + if (r < 0) return r; - if ((r = unit_watch_timer(UNIT(s), s->timeout_usec, &s->timer_watch)) < 0) + r = unit_watch_timer(UNIT(s), CLOCK_MONOTONIC, true, s->timeout_usec, &s->timer_watch); + if (r < 0) return r; } @@ -581,27 +589,30 @@ static int swap_spawn(Swap *s, ExecCommand *c, pid_t *_pid) { assert(c); assert(_pid); - if ((r = unit_watch_timer(UNIT(s), s->timeout_usec, &s->timer_watch)) < 0) + r = unit_watch_timer(UNIT(s), CLOCK_MONOTONIC, true, s->timeout_usec, &s->timer_watch); + if (r < 0) goto fail; - if ((r = exec_spawn(c, - NULL, - &s->exec_context, - NULL, 0, - UNIT(s)->manager->environment, - true, - true, - true, - UNIT(s)->manager->confirm_spawn, - UNIT(s)->cgroup_bondings, - UNIT(s)->cgroup_attributes, - NULL, - UNIT(s)->id, - NULL, - &pid)) < 0) + r = exec_spawn(c, + NULL, + &s->exec_context, + NULL, 0, + UNIT(s)->manager->environment, + true, + true, + true, + UNIT(s)->manager->confirm_spawn, + UNIT(s)->cgroup_bondings, + UNIT(s)->cgroup_attributes, + NULL, + UNIT(s)->id, + NULL, + &pid); + if (r < 0) goto fail; - if ((r = unit_watch_pid(UNIT(s), pid)) < 0) + r = unit_watch_pid(UNIT(s), pid); + if (r < 0) /* FIXME: we need to do something here */ goto fail; @@ -650,27 +661,33 @@ static void swap_enter_signal(Swap *s, SwapState state, SwapResult f) { if (s->control_pid > 0) { if (kill_and_sigcont(s->control_pid, sig) < 0 && errno != ESRCH) - log_warning("Failed to kill control process %li: %m", (long) s->control_pid); + log_warning_unit(UNIT(s)->id, + "Failed to kill control process %li: %m", + (long) s->control_pid); else wait_for_exit = true; } if (s->kill_context.kill_mode == KILL_CONTROL_GROUP) { - if (!(pid_set = set_new(trivial_hash_func, trivial_compare_func))) { - r = -ENOMEM; + pid_set = set_new(trivial_hash_func, trivial_compare_func); + if (!pid_set) { + r = log_oom(); goto fail; } /* Exclude the control pid from being killed via the cgroup */ - if (s->control_pid > 0) - if ((r = set_put(pid_set, LONG_TO_PTR(s->control_pid))) < 0) + if (s->control_pid > 0) { + r = set_put(pid_set, LONG_TO_PTR(s->control_pid)); + if (r < 0) goto fail; + } r = cgroup_bonding_kill_list(UNIT(s)->cgroup_bondings, sig, true, false, pid_set, NULL); if (r < 0) { if (r != -EAGAIN && r != -ESRCH && r != -ENOENT) - log_warning("Failed to kill control group: %s", strerror(-r)); + log_warning_unit(UNIT(s)->id, + "Failed to kill control group: %s", strerror(-r)); } else if (r > 0) wait_for_exit = true; @@ -680,7 +697,8 @@ static void swap_enter_signal(Swap *s, SwapState state, SwapResult f) { } if (wait_for_exit) { - if ((r = unit_watch_timer(UNIT(s), s->timeout_usec, &s->timer_watch)) < 0) + r = unit_watch_timer(UNIT(s), CLOCK_MONOTONIC, true, s->timeout_usec, &s->timer_watch); + if (r < 0) goto fail; swap_set_state(s, state); @@ -690,7 +708,8 @@ static void swap_enter_signal(Swap *s, SwapState state, SwapResult f) { return; fail: - log_warning("%s failed to kill processes: %s", UNIT(s)->id, strerror(-r)); + log_warning_unit(UNIT(s)->id, + "%s failed to kill processes: %s", UNIT(s)->id, strerror(-r)); swap_enter_dead(s, SWAP_FAILURE_RESOURCES); @@ -736,7 +755,8 @@ static void swap_enter_activating(Swap *s) { swap_unwatch_control_pid(s); - if ((r = swap_spawn(s, s->control_command, &s->control_pid)) < 0) + r = swap_spawn(s, s->control_command, &s->control_pid); + if (r < 0) goto fail; swap_set_state(s, SWAP_ACTIVATING); @@ -744,7 +764,9 @@ static void swap_enter_activating(Swap *s) { return; fail: - log_warning("%s failed to run 'swapon' task: %s", UNIT(s)->id, strerror(-r)); + log_warning_unit(UNIT(s)->id, + "%s failed to run 'swapon' task: %s", + UNIT(s)->id, strerror(-r)); swap_enter_dead(s, SWAP_FAILURE_RESOURCES); } @@ -756,16 +778,17 @@ static void swap_enter_deactivating(Swap *s) { s->control_command_id = SWAP_EXEC_DEACTIVATE; s->control_command = s->exec_command + SWAP_EXEC_DEACTIVATE; - if ((r = exec_command_set( - s->control_command, + r = exec_command_set(s->control_command, "/sbin/swapoff", s->what, - NULL)) < 0) + NULL); + if (r < 0) goto fail; swap_unwatch_control_pid(s); - if ((r = swap_spawn(s, s->control_command, &s->control_pid)) < 0) + r = swap_spawn(s, s->control_command, &s->control_pid); + if (r < 0) goto fail; swap_set_state(s, SWAP_DEACTIVATING); @@ -773,7 +796,9 @@ static void swap_enter_deactivating(Swap *s) { return; fail: - log_warning("%s failed to run 'swapoff' task: %s", UNIT(s)->id, strerror(-r)); + log_warning_unit(UNIT(s)->id, + "%s failed to run 'swapoff' task: %s", + UNIT(s)->id, strerror(-r)); swap_enter_active(s, SWAP_FAILURE_RESOURCES); } @@ -797,6 +822,9 @@ static int swap_start(Unit *u) { assert(s->state == SWAP_DEAD || s->state == SWAP_FAILED); + if (detect_container(NULL) > 0) + return -EPERM; + s->result = SWAP_SUCCESS; swap_enter_activating(s); return 0; @@ -817,6 +845,9 @@ static int swap_stop(Unit *u) { assert(s->state == SWAP_ACTIVATING || s->state == SWAP_ACTIVE); + if (detect_container(NULL) > 0) + return -EPERM; + swap_enter_deactivating(s); return 0; } @@ -849,8 +880,9 @@ static int swap_deserialize_item(Unit *u, const char *key, const char *value, FD if (streq(key, "state")) { SwapState state; - if ((state = swap_state_from_string(value)) < 0) - log_debug("Failed to parse state value %s", value); + state = swap_state_from_string(value); + if (state < 0) + log_debug_unit(u->id, "Failed to parse state value %s", value); else s->deserialized_state = state; } else if (streq(key, "result")) { @@ -858,29 +890,30 @@ static int swap_deserialize_item(Unit *u, const char *key, const char *value, FD f = swap_result_from_string(value); if (f < 0) - log_debug("Failed to parse result value %s", value); + log_debug_unit(u->id, "Failed to parse result value %s", value); else if (f != SWAP_SUCCESS) s->result = f; } else if (streq(key, "control-pid")) { pid_t pid; if (parse_pid(value, &pid) < 0) - log_debug("Failed to parse control-pid value %s", value); + log_debug_unit(u->id, "Failed to parse control-pid value %s", value); else s->control_pid = pid; } else if (streq(key, "control-command")) { SwapExecCommand id; - if ((id = swap_exec_command_from_string(value)) < 0) - log_debug("Failed to parse exec-command value %s", value); + id = swap_exec_command_from_string(value); + if (id < 0) + log_debug_unit(u->id, "Failed to parse exec-command value %s", value); else { s->control_command_id = id; s->control_command = s->exec_command + id; } } else - log_debug("Unknown serialization key '%s'", key); + log_debug_unit(u->id, "Unknown serialization key '%s'", key); return 0; } @@ -917,7 +950,7 @@ static void swap_sigchld_event(Unit *u, pid_t pid, int code, int status) { s->control_pid = 0; - if (is_clean_exit(code, status)) + if (is_clean_exit(code, status, NULL)) f = SWAP_SUCCESS; else if (code == CLD_EXITED) f = SWAP_FAILURE_EXIT_CODE; @@ -938,8 +971,10 @@ static void swap_sigchld_event(Unit *u, pid_t pid, int code, int status) { s->control_command_id = _SWAP_EXEC_COMMAND_INVALID; } - log_full(f == SWAP_SUCCESS ? LOG_DEBUG : LOG_NOTICE, - "%s swap process exited, code=%s status=%i", u->id, sigchld_code_to_string(code), status); + log_full_unit(f == SWAP_SUCCESS ? LOG_DEBUG : LOG_NOTICE, + u->id, + "%s swap process exited, code=%s status=%i", + u->id, sigchld_code_to_string(code), status); switch (s->state) { @@ -985,38 +1020,38 @@ static void swap_timer_event(Unit *u, uint64_t elapsed, Watch *w) { switch (s->state) { case SWAP_ACTIVATING: - log_warning("%s activation timed out. Stopping.", u->id); + log_warning_unit(u->id, "%s activation timed out. Stopping.", u->id); swap_enter_signal(s, SWAP_ACTIVATING_SIGTERM, SWAP_FAILURE_TIMEOUT); break; case SWAP_DEACTIVATING: - log_warning("%s deactivation timed out. Stopping.", u->id); + log_warning_unit(u->id, "%s deactivation timed out. Stopping.", u->id); swap_enter_signal(s, SWAP_DEACTIVATING_SIGTERM, SWAP_FAILURE_TIMEOUT); break; case SWAP_ACTIVATING_SIGTERM: if (s->kill_context.send_sigkill) { - log_warning("%s activation timed out. Killing.", u->id); + log_warning_unit(u->id, "%s activation timed out. Killing.", u->id); swap_enter_signal(s, SWAP_ACTIVATING_SIGKILL, SWAP_FAILURE_TIMEOUT); } else { - log_warning("%s activation timed out. Skipping SIGKILL. Ignoring.", u->id); + log_warning_unit(u->id, "%s activation timed out. Skipping SIGKILL. Ignoring.", u->id); swap_enter_dead(s, SWAP_FAILURE_TIMEOUT); } break; case SWAP_DEACTIVATING_SIGTERM: if (s->kill_context.send_sigkill) { - log_warning("%s deactivation timed out. Killing.", u->id); + log_warning_unit(u->id, "%s deactivation timed out. Killing.", u->id); swap_enter_signal(s, SWAP_DEACTIVATING_SIGKILL, SWAP_FAILURE_TIMEOUT); } else { - log_warning("%s deactivation timed out. Skipping SIGKILL. Ignoring.", u->id); + log_warning_unit(u->id, "%s deactivation timed out. Skipping SIGKILL. Ignoring.", u->id); swap_enter_dead(s, SWAP_FAILURE_TIMEOUT); } break; case SWAP_ACTIVATING_SIGKILL: case SWAP_DEACTIVATING_SIGKILL: - log_warning("%s swap process still around after SIGKILL. Ignoring.", u->id); + log_warning_unit(u->id, "%s swap process still around after SIGKILL. Ignoring.", u->id); swap_enter_dead(s, SWAP_FAILURE_TIMEOUT); break; @@ -1039,18 +1074,18 @@ static int swap_load_proc_swaps(Manager *m, bool set_flags) { char *dev = NULL, *d; int prio = 0, k; - if ((k = fscanf(m->proc_swaps, - "%ms " /* device/file */ - "%*s " /* type of swap */ - "%*s " /* swap size */ - "%*s " /* used */ - "%i\n", /* priority */ - &dev, &prio)) != 2) { - + k = fscanf(m->proc_swaps, + "%ms " /* device/file */ + "%*s " /* type of swap */ + "%*s " /* swap size */ + "%*s " /* used */ + "%i\n", /* priority */ + &dev, &prio); + if (k != 2) { if (k == EOF) break; - log_warning("Failed to parse /proc/swaps:%u.", i); + log_warning("Failed to parse /proc/swaps:%u", i); free(dev); continue; } @@ -1089,7 +1124,8 @@ int swap_fd_event(Manager *m, int events) { assert(m); assert(events & EPOLLPRI); - if ((r = swap_load_proc_swaps(m, true)) < 0) { + r = swap_load_proc_swaps(m, true); + if (r < 0) { log_error("Failed to reread /proc/swaps: %s", strerror(-r)); /* Reset flags, just in case, for late calls */ @@ -1229,7 +1265,8 @@ static int swap_enumerate(Manager *m) { assert(m); if (!m->proc_swaps) { - if (!(m->proc_swaps = fopen("/proc/swaps", "re"))) + m->proc_swaps = fopen("/proc/swaps", "re"); + if (!m->proc_swaps) return (errno == ENOENT) ? 0 : -errno; m->swap_watch.type = WATCH_SWAP; @@ -1243,7 +1280,8 @@ static int swap_enumerate(Manager *m) { return -errno; } - if ((r = swap_load_proc_swaps(m, false)) < 0) + r = swap_load_proc_swaps(m, false); + if (r < 0) swap_shutdown(m); return r; @@ -1344,6 +1382,8 @@ DEFINE_STRING_TABLE_LOOKUP(swap_result, SwapResult); const UnitVTable swap_vtable = { .object_size = sizeof(Swap), + .exec_context_offset = offsetof(Swap, exec_context), + .sections = "Unit\0" "Swap\0" diff --git a/src/core/switch-root.c b/src/core/switch-root.c index 9832a520e4..ce0e41d510 100644 --- a/src/core/switch-root.c +++ b/src/core/switch-root.c @@ -30,6 +30,7 @@ #include "util.h" #include "path-util.h" #include "switch-root.h" +#include "missing.h" int switch_root(const char *new_root) { @@ -44,10 +45,21 @@ int switch_root(const char *new_root) { struct stat new_root_stat; bool old_root_remove; const char *i; + _cleanup_free_ char *temporary_old_root = NULL; if (path_equal(new_root, "/")) return 0; + /* When using pivot_root() we assume that /mnt exists as place + * we can temporarily move the old root to. As we immediately + * unmount it from there it doesn't matter much which + * directory we choose for this, but it should be more likely + * than not that /mnt exists and is suitable as mount point + * and is on the same fs as the old root dir */ + temporary_old_root = strappend(new_root, "/mnt"); + if (!temporary_old_root) + return -ENOMEM; + old_root_remove = in_initrd(); if (stat(new_root, &new_root_stat) < 0) { @@ -56,6 +68,15 @@ int switch_root(const char *new_root) { goto fail; } + /* Work-around for a kernel bug: for some reason the kernel + * refuses switching root if any file systems are mounted + * MS_SHARED. Hence remount them MS_PRIVATE here as a + * work-around. + * + * https://bugzilla.redhat.com/show_bug.cgi?id=847418 */ + if (mount(NULL, "/", NULL, MS_REC|MS_PRIVATE, NULL) < 0) + log_warning("Failed to make \"/\" private mount: %m"); + NULSTR_FOREACH(i, move_mounts) { char new_mount[PATH_MAX]; struct stat sb; @@ -94,7 +115,20 @@ int switch_root(const char *new_root) { log_warning("Failed to open root directory: %m"); } - if (mount(new_root, "/", NULL, MS_MOVE, NULL) < 0) { + /* We first try a pivot_root() so that we can umount the old + * root dir. In many cases (i.e. where rootfs is /), that's + * not possible however, and hence we simply overmount root */ + if (pivot_root(new_root, temporary_old_root) >= 0) { + + /* Immediately get rid of the old root. Since we are + * running off it we need to do this lazily. */ + if (umount2(temporary_old_root, MNT_DETACH) < 0) { + r = -errno; + log_error("Failed to umount old root dir %s: %m", temporary_old_root); + goto fail; + } + + } else if (mount(new_root, "/", NULL, MS_MOVE, NULL) < 0) { r = -errno; log_error("Failed to mount moving %s to /: %m", new_root); goto fail; @@ -106,6 +140,12 @@ int switch_root(const char *new_root) { goto fail; } + if (chdir("/") < 0) { + r = -errno; + log_error("Failed to change directory: %m"); + goto fail; + } + if (old_root_fd >= 0) { struct stat rb; diff --git a/src/core/system.conf b/src/core/system.conf index d5208186a0..68076d9735 100644 --- a/src/core/system.conf +++ b/src/core/system.conf @@ -20,7 +20,7 @@ #DefaultControllers=cpu #DefaultStandardOutput=journal #DefaultStandardError=inherit -#JoinControllers=cpu,cpuacct +#JoinControllers=cpu,cpuacct,cpuset net_cls,net_prio #RuntimeWatchdogSec=0 #ShutdownWatchdogSec=10min #CapabilityBoundingSet= diff --git a/src/core/systemd.pc.in b/src/core/systemd.pc.in index 49f65b2a65..2f49d5df52 100644 --- a/src/core/systemd.pc.in +++ b/src/core/systemd.pc.in @@ -9,7 +9,9 @@ prefix=@prefix@ exec_prefix=@exec_prefix@ systemdutildir=@rootlibexecdir@ systemdsystemunitdir=@systemunitdir@ +systemdsystempresetdir=@systempresetdir@ systemduserunitdir=@userunitdir@ +systemduserpresetdir=@userpresetdir@ systemdsystemconfdir=@pkgsysconfdir@/system systemduserconfdir=@pkgsysconfdir@/user systemdsystemunitpath=${systemdsystemconfdir}:/etc/systemd/system:/run/systemd/system:/usr/local/lib/systemd/system:${systemdsystemunitdir}:/usr/lib/systemd/system:/lib/systemd/system diff --git a/src/core/target.c b/src/core/target.c index 55f9fc6fc4..981424132b 100644 --- a/src/core/target.c +++ b/src/core/target.c @@ -53,13 +53,15 @@ static void target_set_state(Target *t, TargetState state) { } static int target_add_default_dependencies(Target *t) { + static const UnitDependency deps[] = { UNIT_REQUIRES, UNIT_REQUIRES_OVERRIDABLE, UNIT_REQUISITE, UNIT_REQUISITE_OVERRIDABLE, UNIT_WANTS, - UNIT_BINDS_TO + UNIT_BINDS_TO, + UNIT_PART_OF }; Iterator i; @@ -75,9 +77,11 @@ static int target_add_default_dependencies(Target *t) { * sure we don't create a loop. */ for (k = 0; k < ELEMENTSOF(deps); k++) - SET_FOREACH(other, UNIT(t)->dependencies[deps[k]], i) - if ((r = unit_add_default_target_dependency(other, UNIT(t))) < 0) + SET_FOREACH(other, UNIT(t)->dependencies[deps[k]], i) { + r = unit_add_default_target_dependency(other, UNIT(t)); + if (r < 0) return r; + } /* Make sure targets are unloaded on shutdown */ return unit_add_dependency_by_name(UNIT(t), UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, NULL, true); @@ -89,14 +93,15 @@ static int target_load(Unit *u) { assert(t); - if ((r = unit_load_fragment_and_dropin(u)) < 0) + r = unit_load_fragment_and_dropin(u); + if (r < 0) return r; /* This is a new unit? Then let's add in some extras */ - if (u->load_state == UNIT_LOADED) { - if (u->default_dependencies) - if ((r = target_add_default_dependencies(t)) < 0) - return r; + if (u->load_state == UNIT_LOADED && u->default_dependencies) { + r = target_add_default_dependencies(t); + if (r < 0) + return r; } return 0; @@ -167,7 +172,8 @@ static int target_deserialize_item(Unit *u, const char *key, const char *value, if (streq(key, "state")) { TargetState state; - if ((state = target_state_from_string(value)) < 0) + state = target_state_from_string(value); + if (state < 0) log_debug("Failed to parse state value %s", value); else s->deserialized_state = state; diff --git a/src/core/timer.c b/src/core/timer.c index 0b3c5ce077..31ef176e7e 100644 --- a/src/core/timer.c +++ b/src/core/timer.c @@ -42,7 +42,10 @@ static void timer_init(Unit *u) { assert(u); assert(u->load_state == UNIT_STUB); - t->next_elapse = (usec_t) -1; + t->next_elapse_monotonic = (usec_t) -1; + t->next_elapse_realtime = (usec_t) -1; + watch_init(&t->monotonic_watch); + watch_init(&t->realtime_watch); } static void timer_done(Unit *u) { @@ -53,10 +56,15 @@ static void timer_done(Unit *u) { while ((v = t->values)) { LIST_REMOVE(TimerValue, value, t->values, v); + + if (v->calendar_spec) + calendar_spec_free(v->calendar_spec); + free(v); } - unit_unwatch_timer(u, &t->timer_watch); + unit_unwatch_timer(u, &t->monotonic_watch); + unit_unwatch_timer(u, &t->realtime_watch); unit_ref_unset(&t->unit); } @@ -68,7 +76,8 @@ static int timer_verify(Timer *t) { return 0; if (!t->values) { - log_error("%s lacks value setting. Refusing.", UNIT(t)->id); + log_error_unit(UNIT(t)->id, + "%s lacks value setting. Refusing.", UNIT(t)->id); return -EINVAL; } @@ -80,11 +89,13 @@ static int timer_add_default_dependencies(Timer *t) { assert(t); - if (UNIT(t)->manager->running_as == MANAGER_SYSTEM) { - if ((r = unit_add_dependency_by_name(UNIT(t), UNIT_BEFORE, SPECIAL_BASIC_TARGET, NULL, true)) < 0) + if (UNIT(t)->manager->running_as == SYSTEMD_SYSTEM) { + r = unit_add_dependency_by_name(UNIT(t), UNIT_BEFORE, SPECIAL_BASIC_TARGET, NULL, true); + if (r < 0) return r; - if ((r = unit_add_two_dependencies_by_name(UNIT(t), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, NULL, true)) < 0) + r = unit_add_two_dependencies_by_name(UNIT(t), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, NULL, true); + if (r < 0) return r; } @@ -98,7 +109,8 @@ static int timer_load(Unit *u) { assert(u); assert(u->load_state == UNIT_STUB); - if ((r = unit_load_fragment_and_dropin(u)) < 0) + r = unit_load_fragment_and_dropin(u); + if (r < 0) return r; if (u->load_state == UNIT_LOADED) { @@ -117,9 +129,11 @@ static int timer_load(Unit *u) { if (r < 0) return r; - if (UNIT(t)->default_dependencies) - if ((r = timer_add_default_dependencies(t)) < 0) + if (UNIT(t)->default_dependencies) { + r = timer_add_default_dependencies(t); + if (r < 0) return r; + } } return timer_verify(t); @@ -128,8 +142,6 @@ static int timer_load(Unit *u) { static void timer_dump(Unit *u, FILE *f, const char *prefix) { Timer *t = TIMER(u); TimerValue *v; - char - timespan1[FORMAT_TIMESPAN_MAX]; fprintf(f, "%sTimer State: %s\n" @@ -139,12 +151,28 @@ static void timer_dump(Unit *u, FILE *f, const char *prefix) { prefix, timer_result_to_string(t->result), prefix, UNIT_DEREF(t->unit)->id); - LIST_FOREACH(value, v, t->values) - fprintf(f, - "%s%s: %s\n", - prefix, - timer_base_to_string(v->base), - strna(format_timespan(timespan1, sizeof(timespan1), v->value))); + LIST_FOREACH(value, v, t->values) { + + if (v->base == TIMER_CALENDAR) { + _cleanup_free_ char *p = NULL; + + calendar_spec_to_string(v->calendar_spec, &p); + + fprintf(f, + "%s%s: %s\n", + prefix, + timer_base_to_string(v->base), + strna(p)); + } else { + char timespan1[FORMAT_TIMESPAN_MAX]; + + fprintf(f, + "%s%s: %s\n", + prefix, + timer_base_to_string(v->base), + strna(format_timespan(timespan1, sizeof(timespan1), v->value))); + } + } } static void timer_set_state(Timer *t, TimerState state) { @@ -154,14 +182,16 @@ static void timer_set_state(Timer *t, TimerState state) { old_state = t->state; t->state = state; - if (state != TIMER_WAITING) - unit_unwatch_timer(UNIT(t), &t->timer_watch); + if (state != TIMER_WAITING) { + unit_unwatch_timer(UNIT(t), &t->monotonic_watch); + unit_unwatch_timer(UNIT(t), &t->realtime_watch); + } if (state != old_state) - log_debug("%s changed %s -> %s", - UNIT(t)->id, - timer_state_to_string(old_state), - timer_state_to_string(state)); + log_debug_unit(UNIT(t)->id, + "%s changed %s -> %s", UNIT(t)->id, + timer_state_to_string(old_state), + timer_state_to_string(state)); unit_notify(UNIT(t), state_translation_table[old_state], state_translation_table[state], true); } @@ -196,85 +226,131 @@ static void timer_enter_dead(Timer *t, TimerResult f) { static void timer_enter_waiting(Timer *t, bool initial) { TimerValue *v; - usec_t base = 0, delay, n; - bool found = false; + usec_t base = 0; + dual_timestamp ts; + bool found_monotonic = false, found_realtime = false; int r; - n = now(CLOCK_MONOTONIC); + dual_timestamp_get(&ts); + t->next_elapse_monotonic = t->next_elapse_realtime = 0; LIST_FOREACH(value, v, t->values) { if (v->disabled) continue; - switch (v->base) { + if (v->base == TIMER_CALENDAR) { - case TIMER_ACTIVE: - if (state_translation_table[t->state] == UNIT_ACTIVE) - base = UNIT(t)->inactive_exit_timestamp.monotonic; + r = calendar_spec_next_usec(v->calendar_spec, ts.realtime, &v->next_elapse); + if (r < 0) + continue; + + if (!initial && v->next_elapse < ts.realtime) { + v->disabled = true; + continue; + } + + if (!found_realtime) + t->next_elapse_realtime = v->next_elapse; else - base = n; - break; + t->next_elapse_realtime = MIN(t->next_elapse_realtime, v->next_elapse); - case TIMER_BOOT: - /* CLOCK_MONOTONIC equals the uptime on Linux */ - base = 0; - break; + found_realtime = true; - case TIMER_STARTUP: - base = UNIT(t)->manager->startup_timestamp.monotonic; - break; + } else { + switch (v->base) { - case TIMER_UNIT_ACTIVE: + case TIMER_ACTIVE: + if (state_translation_table[t->state] == UNIT_ACTIVE) + base = UNIT(t)->inactive_exit_timestamp.monotonic; + else + base = ts.monotonic; + break; - if (UNIT_DEREF(t->unit)->inactive_exit_timestamp.monotonic <= 0) - continue; + case TIMER_BOOT: + /* CLOCK_MONOTONIC equals the uptime on Linux */ + base = 0; + break; - base = UNIT_DEREF(t->unit)->inactive_exit_timestamp.monotonic; - break; + case TIMER_STARTUP: + base = UNIT(t)->manager->userspace_timestamp.monotonic; + break; - case TIMER_UNIT_INACTIVE: + case TIMER_UNIT_ACTIVE: - if (UNIT_DEREF(t->unit)->inactive_enter_timestamp.monotonic <= 0) - continue; + if (UNIT_DEREF(t->unit)->inactive_exit_timestamp.monotonic <= 0) + continue; - base = UNIT_DEREF(t->unit)->inactive_enter_timestamp.monotonic; - break; + base = UNIT_DEREF(t->unit)->inactive_exit_timestamp.monotonic; + break; - default: - assert_not_reached("Unknown timer base"); - } + case TIMER_UNIT_INACTIVE: - v->next_elapse = base + v->value; + if (UNIT_DEREF(t->unit)->inactive_enter_timestamp.monotonic <= 0) + continue; - if (!initial && v->next_elapse < n) { - v->disabled = true; - continue; - } + base = UNIT_DEREF(t->unit)->inactive_enter_timestamp.monotonic; + break; - if (!found) - t->next_elapse = v->next_elapse; - else - t->next_elapse = MIN(t->next_elapse, v->next_elapse); + default: + assert_not_reached("Unknown timer base"); + } + + v->next_elapse = base + v->value; + + if (!initial && v->next_elapse < ts.monotonic) { + v->disabled = true; + continue; + } + + if (!found_monotonic) + t->next_elapse_monotonic = v->next_elapse; + else + t->next_elapse_monotonic = MIN(t->next_elapse_monotonic, v->next_elapse); - found = true; + found_monotonic = true; + } } - if (!found) { + if (!found_monotonic && !found_realtime) { + log_debug_unit(UNIT(t)->id, "%s: Timer is elapsed.", UNIT(t)->id); timer_set_state(t, TIMER_ELAPSED); return; } - delay = n < t->next_elapse ? t->next_elapse - n : 0; + if (found_monotonic) { + char buf[FORMAT_TIMESPAN_MAX]; + log_debug_unit(UNIT(t)->id, + "%s: Monotonic timer elapses in %s the next time.", + UNIT(t)->id, + format_timespan(buf, sizeof(buf), t->next_elapse_monotonic - ts.monotonic)); - if ((r = unit_watch_timer(UNIT(t), delay, &t->timer_watch)) < 0) - goto fail; + r = unit_watch_timer(UNIT(t), CLOCK_MONOTONIC, false, t->next_elapse_monotonic, &t->monotonic_watch); + if (r < 0) + goto fail; + } else + unit_unwatch_timer(UNIT(t), &t->monotonic_watch); + + if (found_realtime) { + char buf[FORMAT_TIMESTAMP_MAX]; + log_debug_unit(UNIT(t)->id, + "%s: Realtime timer elapses at %s the next time.", + UNIT(t)->id, + format_timestamp(buf, sizeof(buf), t->next_elapse_realtime)); + + r = unit_watch_timer(UNIT(t), CLOCK_REALTIME, false, t->next_elapse_realtime, &t->realtime_watch); + if (r < 0) + goto fail; + } else + unit_unwatch_timer(UNIT(t), &t->realtime_watch); timer_set_state(t, TIMER_WAITING); return; fail: - log_warning("%s failed to enter waiting state: %s", UNIT(t)->id, strerror(-r)); + log_warning_unit(UNIT(t)->id, + "%s failed to enter waiting state: %s", + UNIT(t)->id, strerror(-r)); timer_enter_dead(t, TIMER_FAILURE_RESOURCES); } @@ -289,14 +365,17 @@ static void timer_enter_running(Timer *t) { if (UNIT(t)->job && UNIT(t)->job->type == JOB_STOP) return; - if ((r = manager_add_job(UNIT(t)->manager, JOB_START, UNIT_DEREF(t->unit), JOB_REPLACE, true, &error, NULL)) < 0) + r = manager_add_job(UNIT(t)->manager, JOB_START, UNIT_DEREF(t->unit), JOB_REPLACE, true, &error, NULL); + if (r < 0) goto fail; timer_set_state(t, TIMER_RUNNING); return; fail: - log_warning("%s failed to queue unit startup job: %s", UNIT(t)->id, bus_error(&error, r)); + log_warning_unit(UNIT(t)->id, + "%s failed to queue unit startup job: %s", + UNIT(t)->id, bus_error(&error, r)); timer_enter_dead(t, TIMER_FAILURE_RESOURCES); dbus_error_free(&error); @@ -350,8 +429,9 @@ static int timer_deserialize_item(Unit *u, const char *key, const char *value, F if (streq(key, "state")) { TimerState state; - if ((state = timer_state_from_string(value)) < 0) - log_debug("Failed to parse state value %s", value); + state = timer_state_from_string(value); + if (state < 0) + log_debug_unit(u->id, "Failed to parse state value %s", value); else t->deserialized_state = state; } else if (streq(key, "result")) { @@ -359,12 +439,12 @@ static int timer_deserialize_item(Unit *u, const char *key, const char *value, F f = timer_result_from_string(value); if (f < 0) - log_debug("Failed to parse result value %s", value); + log_debug_unit(u->id, "Failed to parse result value %s", value); else if (f != TIMER_SUCCESS) t->result = f; } else - log_debug("Unknown serialization key '%s'", key); + log_debug_unit(u->id, "Unknown serialization key '%s'", key); return 0; } @@ -390,7 +470,7 @@ static void timer_timer_event(Unit *u, uint64_t elapsed, Watch *w) { if (t->state != TIMER_WAITING) return; - log_debug("Timer elapsed on %s", u->id); + log_debug_unit(u->id, "Timer elapsed on %s", u->id); timer_enter_running(t); } @@ -431,7 +511,9 @@ void timer_unit_notify(Unit *u, UnitActiveState new_state) { case TIMER_RUNNING: if (UNIT_IS_INACTIVE_OR_FAILED(new_state)) { - log_debug("%s got notified about unit deactivation.", UNIT(t)->id); + log_debug_unit(UNIT(t)->id, + "%s got notified about unit deactivation.", + UNIT(t)->id); timer_enter_waiting(t, false); } @@ -458,6 +540,19 @@ static void timer_reset_failed(Unit *u) { t->result = TIMER_SUCCESS; } +static void timer_time_change(Unit *u) { + Timer *t = TIMER(u); + + assert(u); + + if (t->state != TIMER_WAITING) + return; + + log_info_unit(u->id, + "%s: time change, recalculating next elapse.", u->id); + timer_enter_waiting(t, false); +} + static const char* const timer_state_table[_TIMER_STATE_MAX] = { [TIMER_DEAD] = "dead", [TIMER_WAITING] = "waiting", @@ -473,7 +568,8 @@ static const char* const timer_base_table[_TIMER_BASE_MAX] = { [TIMER_BOOT] = "OnBootSec", [TIMER_STARTUP] = "OnStartupSec", [TIMER_UNIT_ACTIVE] = "OnUnitActiveSec", - [TIMER_UNIT_INACTIVE] = "OnUnitInactiveSec" + [TIMER_UNIT_INACTIVE] = "OnUnitInactiveSec", + [TIMER_CALENDAR] = "OnCalendar" }; DEFINE_STRING_TABLE_LOOKUP(timer_base, TimerBase); @@ -512,6 +608,7 @@ const UnitVTable timer_vtable = { .timer_event = timer_timer_event, .reset_failed = timer_reset_failed, + .time_change = timer_time_change, .bus_interface = "org.freedesktop.systemd1.Timer", .bus_message_handler = bus_timer_message_handler, diff --git a/src/core/timer.h b/src/core/timer.h index c6d1d42e44..57a514a68c 100644 --- a/src/core/timer.h +++ b/src/core/timer.h @@ -24,6 +24,7 @@ typedef struct Timer Timer; #include "unit.h" +#include "calendarspec.h" typedef enum TimerState { TIMER_DEAD, @@ -41,18 +42,21 @@ typedef enum TimerBase { TIMER_STARTUP, TIMER_UNIT_ACTIVE, TIMER_UNIT_INACTIVE, + TIMER_CALENDAR, _TIMER_BASE_MAX, _TIMER_BASE_INVALID = -1 } TimerBase; typedef struct TimerValue { + TimerBase base; + bool disabled; + clockid_t clock_id; + usec_t value; + CalendarSpec *calendar_spec; usec_t next_elapse; LIST_FIELDS(struct TimerValue, value); - - TimerBase base; - bool disabled; } TimerValue; typedef enum TimerResult { @@ -66,12 +70,14 @@ struct Timer { Unit meta; LIST_HEAD(TimerValue, values); - usec_t next_elapse; + usec_t next_elapse_monotonic; + usec_t next_elapse_realtime; TimerState state, deserialized_state; UnitRef unit; - Watch timer_watch; + Watch monotonic_watch; + Watch realtime_watch; TimerResult result; }; diff --git a/src/core/transaction.c b/src/core/transaction.c index 1f8d803aeb..1854047afd 100644 --- a/src/core/transaction.c +++ b/src/core/transaction.c @@ -186,8 +186,14 @@ static int delete_one_unmergeable_job(Transaction *tr, Job *j) { * another unit in which case we * rather remove the start. */ - log_debug("Looking at job %s/%s conflicted_by=%s", j->unit->id, job_type_to_string(j->type), yes_no(j->type == JOB_STOP && job_is_conflicted_by(j))); - log_debug("Looking at job %s/%s conflicted_by=%s", k->unit->id, job_type_to_string(k->type), yes_no(k->type == JOB_STOP && job_is_conflicted_by(k))); + log_debug_unit(j->unit->id, + "Looking at job %s/%s conflicted_by=%s", + j->unit->id, job_type_to_string(j->type), + yes_no(j->type == JOB_STOP && job_is_conflicted_by(j))); + log_debug_unit(k->unit->id, + "Looking at job %s/%s conflicted_by=%s", + k->unit->id, job_type_to_string(k->type), + yes_no(k->type == JOB_STOP && job_is_conflicted_by(k))); if (j->type == JOB_STOP) { @@ -213,7 +219,9 @@ static int delete_one_unmergeable_job(Transaction *tr, Job *j) { return -ENOEXEC; /* Ok, we can drop one, so let's do so. */ - log_debug("Fixing conflicting jobs by deleting job %s/%s", d->unit->id, job_type_to_string(d->type)); + log_debug_unit(d->unit->id, + "Fixing conflicting jobs by deleting job %s/%s", + d->unit->id, job_type_to_string(d->type)); transaction_delete_job(tr, d, true); return 0; } @@ -352,12 +360,17 @@ static int transaction_verify_order_one(Transaction *tr, Job *j, Job *from, unsi * job to remove. We use the marker to find our way * back, since smart how we are we stored our way back * in there. */ - log_warning("Found ordering cycle on %s/%s", j->unit->id, job_type_to_string(j->type)); + log_warning_unit(j->unit->id, + "Found ordering cycle on %s/%s", + j->unit->id, job_type_to_string(j->type)); delete = NULL; for (k = from; k; k = ((k->generation == generation && k->marker != k) ? k->marker : NULL)) { - log_info("Walked on cycle path to %s/%s", k->unit->id, job_type_to_string(k->type)); + /* logging for j not k here here to provide consistent narrative */ + log_info_unit(j->unit->id, + "Walked on cycle path to %s/%s", + k->unit->id, job_type_to_string(k->type)); if (!delete && !unit_matters_to_anchor(k->unit, k)) { @@ -374,14 +387,24 @@ static int transaction_verify_order_one(Transaction *tr, Job *j, Job *from, unsi if (delete) { - log_warning("Breaking ordering cycle by deleting job %s/%s", delete->unit->id, job_type_to_string(delete->type)); + /* logging for j not k here here to provide consistent narrative */ + log_warning_unit(j->unit->id, + "Breaking ordering cycle by deleting job %s/%s", + delete->unit->id, job_type_to_string(delete->type)); + log_error_unit(delete->unit->id, + "Job %s/%s deleted to break ordering cycle starting with %s/%s", + delete->unit->id, job_type_to_string(delete->type), + j->unit->id, job_type_to_string(j->type)); + status_printf(ANSI_HIGHLIGHT_RED_ON " SKIP " ANSI_HIGHLIGHT_OFF, true, + "Ordering cycle found, skipping %s", unit_description(delete->unit)); transaction_delete_unit(tr, delete->unit); return -EAGAIN; } log_error("Unable to break cycle"); - dbus_set_error(e, BUS_ERROR_TRANSACTION_ORDER_IS_CYCLIC, "Transaction order is cyclic. See system logs for details."); + dbus_set_error(e, BUS_ERROR_TRANSACTION_ORDER_IS_CYCLIC, + "Transaction order is cyclic. See system logs for details."); return -ENOEXEC; } @@ -392,7 +415,7 @@ static int transaction_verify_order_one(Transaction *tr, Job *j, Job *from, unsi j->marker = from ? from : j; j->generation = generation; - /* We assume that the the dependencies are bidirectional, and + /* We assume that the dependencies are bidirectional, and * hence can ignore UNIT_AFTER */ SET_FOREACH(u, j->unit->dependencies[UNIT_BEFORE], i) { Job *o; @@ -524,13 +547,19 @@ rescan: continue; if (stops_running_service) - log_debug("%s/%s would stop a running service.", j->unit->id, job_type_to_string(j->type)); + log_debug_unit(j->unit->id, + "%s/%s would stop a running service.", + j->unit->id, job_type_to_string(j->type)); if (changes_existing_job) - log_debug("%s/%s would change existing job.", j->unit->id, job_type_to_string(j->type)); + log_debug_unit(j->unit->id, + "%s/%s would change existing job.", + j->unit->id, job_type_to_string(j->type)); /* Ok, let's get rid of this */ - log_debug("Deleting %s/%s to minimize impact.", j->unit->id, job_type_to_string(j->type)); + log_debug_unit(j->unit->id, + "Deleting %s/%s to minimize impact.", + j->unit->id, job_type_to_string(j->type)); transaction_delete_job(tr, j, true); goto rescan; @@ -782,9 +811,10 @@ static void transaction_unlink_job(Transaction *tr, Job *j, bool delete_dependen job_dependency_free(j->object_list); if (other && delete_dependencies) { - log_debug("Deleting job %s/%s as dependency of job %s/%s", - other->unit->id, job_type_to_string(other->type), - j->unit->id, job_type_to_string(j->type)); + log_debug_unit(other->unit->id, + "Deleting job %s/%s as dependency of job %s/%s", + other->unit->id, job_type_to_string(other->type), + j->unit->id, job_type_to_string(j->type)); transaction_delete_job(tr, other, delete_dependencies); } } @@ -870,7 +900,9 @@ int transaction_add_job_and_dependencies( SET_FOREACH(dep, following, i) { r = transaction_add_job_and_dependencies(tr, type, dep, ret, false, override, false, false, ignore_order, e); if (r < 0) { - log_warning("Cannot add dependency job for unit %s, ignoring: %s", dep->id, bus_error(e, r)); + log_warning_unit(dep->id, + "Cannot add dependency job for unit %s, ignoring: %s", + dep->id, bus_error(e, r)); if (e) dbus_error_free(e); @@ -907,8 +939,9 @@ int transaction_add_job_and_dependencies( SET_FOREACH(dep, ret->unit->dependencies[UNIT_REQUIRES_OVERRIDABLE], i) { r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, !override, override, false, false, ignore_order, e); if (r < 0) { - log_full(r == -EADDRNOTAVAIL ? LOG_DEBUG : LOG_WARNING, - "Cannot add dependency job for unit %s, ignoring: %s", dep->id, bus_error(e, r)); + log_full_unit(r == -EADDRNOTAVAIL ? LOG_DEBUG : LOG_WARNING, dep->id, + "Cannot add dependency job for unit %s, ignoring: %s", + dep->id, bus_error(e, r)); if (e) dbus_error_free(e); @@ -918,8 +951,9 @@ int transaction_add_job_and_dependencies( SET_FOREACH(dep, ret->unit->dependencies[UNIT_WANTS], i) { r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, false, false, false, false, ignore_order, e); if (r < 0) { - log_full(r == -EADDRNOTAVAIL ? LOG_DEBUG : LOG_WARNING, - "Cannot add dependency job for unit %s, ignoring: %s", dep->id, bus_error(e, r)); + log_full_unit(r == -EADDRNOTAVAIL ? LOG_DEBUG : LOG_WARNING, dep->id, + "Cannot add dependency job for unit %s, ignoring: %s", + dep->id, bus_error(e, r)); if (e) dbus_error_free(e); @@ -940,8 +974,9 @@ int transaction_add_job_and_dependencies( SET_FOREACH(dep, ret->unit->dependencies[UNIT_REQUISITE_OVERRIDABLE], i) { r = transaction_add_job_and_dependencies(tr, JOB_VERIFY_ACTIVE, dep, ret, !override, override, false, false, ignore_order, e); if (r < 0) { - log_full(r == -EADDRNOTAVAIL ? LOG_DEBUG : LOG_WARNING, - "Cannot add dependency job for unit %s, ignoring: %s", dep->id, bus_error(e, r)); + log_full_unit(r == -EADDRNOTAVAIL ? LOG_DEBUG : LOG_WARNING, dep->id, + "Cannot add dependency job for unit %s, ignoring: %s", + dep->id, bus_error(e, r)); if (e) dbus_error_free(e); @@ -962,7 +997,9 @@ int transaction_add_job_and_dependencies( SET_FOREACH(dep, ret->unit->dependencies[UNIT_CONFLICTED_BY], i) { r = transaction_add_job_and_dependencies(tr, JOB_STOP, dep, ret, false, override, false, false, ignore_order, e); if (r < 0) { - log_warning("Cannot add dependency job for unit %s, ignoring: %s", dep->id, bus_error(e, r)); + log_warning_unit(dep->id, + "Cannot add dependency job for unit %s, ignoring: %s", + dep->id, bus_error(e, r)); if (e) dbus_error_free(e); @@ -1013,7 +1050,9 @@ int transaction_add_job_and_dependencies( SET_FOREACH(dep, ret->unit->dependencies[UNIT_PROPAGATES_RELOAD_TO], i) { r = transaction_add_job_and_dependencies(tr, JOB_RELOAD, dep, ret, false, override, false, false, ignore_order, e); if (r < 0) { - log_warning("Cannot add dependency reload job for unit %s, ignoring: %s", dep->id, bus_error(e, r)); + log_warning_unit(dep->id, + "Cannot add dependency reload job for unit %s, ignoring: %s", + dep->id, bus_error(e, r)); if (e) dbus_error_free(e); @@ -1058,7 +1097,9 @@ int transaction_add_isolate_jobs(Transaction *tr, Manager *m) { r = transaction_add_job_and_dependencies(tr, JOB_STOP, u, tr->anchor_job, true, false, false, false, false, NULL); if (r < 0) - log_warning("Cannot add isolate job for unit %s, ignoring: %s", u->id, strerror(-r)); + log_warning_unit(u->id, + "Cannot add isolate job for unit %s, ignoring: %s", + u->id, strerror(-r)); } return 0; diff --git a/src/core/umount.c b/src/core/umount.c index a5a215b2b5..96232d38db 100644 --- a/src/core/umount.c +++ b/src/core/umount.c @@ -34,6 +34,7 @@ #include "umount.h" #include "path-util.h" #include "util.h" +#include "virt.h" typedef struct MountPoint { char *path; @@ -406,18 +407,44 @@ static int mount_points_list_umount(MountPoint **head, bool *changed, bool log_e assert(head); LIST_FOREACH_SAFE(mount_point, m, n, *head) { + + /* If we are in a container, don't attempt to + read-only mount anything as that brings no real + benefits, but might confuse the host, as we remount + the superblock here, not the bind mound. */ + if (detect_container(NULL) <= 0) { + /* We always try to remount directories + * read-only first, before we go on and umount + * them. + * + * Mount points can be stacked. If a mount + * point is stacked below / or /usr, we + * cannnot umount or remount it directly, + * since there is no way to refer to the + * underlying mount. There's nothing we can do + * about it for the general case, but we can + * do something about it if it is aliased + * somehwere else via a bind mount. If we + * explicitly remount the super block of that + * alias read-only we hence should be + * relatively safe regarding keeping the fs we + * can otherwise not see dirty. */ + mount(NULL, m->path, NULL, MS_REMOUNT|MS_RDONLY, NULL); + } + + /* Skip / and /usr since we cannot unmount that + * anyway, since we are running from it. They have + * already been remounted ro. */ if (path_equal(m->path, "/") #ifndef HAVE_SPLIT_USR || path_equal(m->path, "/usr") #endif - ) { - n_failed++; + ) continue; - } /* Trying to umount. Forcing to umount if busy (only for NFS mounts) */ + log_info("Unmounting %s.", m->path); if (umount2(m->path, MNT_FORCE) == 0) { - log_info("Unmounted %s.", m->path); if (changed) *changed = true; @@ -431,29 +458,6 @@ static int mount_points_list_umount(MountPoint **head, bool *changed, bool log_e return n_failed; } -static int mount_points_list_remount_read_only(MountPoint **head, bool *changed) { - MountPoint *m, *n; - int n_failed = 0; - - assert(head); - - LIST_FOREACH_SAFE(mount_point, m, n, *head) { - - /* Trying to remount read-only */ - if (mount(NULL, m->path, NULL, MS_MGC_VAL|MS_REMOUNT|MS_RDONLY, NULL) == 0) { - if (changed) - *changed = true; - - mount_point_free(head, m); - } else { - log_warning("Could not remount as read-only %s: %m", m->path); - n_failed++; - } - } - - return n_failed; -} - static int swap_points_list_off(MountPoint **head, bool *changed) { MountPoint *m, *n; int n_failed = 0; @@ -461,6 +465,7 @@ static int swap_points_list_off(MountPoint **head, bool *changed) { assert(head); LIST_FOREACH_SAFE(mount_point, m, n, *head) { + log_info("Deactivating swap %s.", m->path); if (swapoff(m->path) == 0) { if (changed) *changed = true; @@ -496,14 +501,15 @@ static int loopback_points_list_detach(MountPoint **head, bool *changed) { continue; } - if ((r = delete_loopback(m->path)) >= 0) { - + log_info("Detaching loopback %s.", m->path); + r = delete_loopback(m->path); + if (r >= 0) { if (r > 0 && changed) *changed = true; mount_point_free(head, m); } else { - log_warning("Could not delete loopback %s: %m", m->path); + log_warning("Could not detach loopback %s: %m", m->path); n_failed++; } } @@ -530,14 +536,15 @@ static int dm_points_list_detach(MountPoint **head, bool *changed) { continue; } - if ((r = delete_dm(m->devnum)) >= 0) { - - if (r > 0 && changed) + log_info("Detaching DM %u:%u.", major(m->devnum), minor(m->devnum)); + r = delete_dm(m->devnum); + if (r >= 0) { + if (changed) *changed = true; mount_point_free(head, m); } else { - log_warning("Could not delete dm %s: %m", m->path); + log_warning("Could not detach DM %s: %m", m->path); n_failed++; } } @@ -548,11 +555,9 @@ static int dm_points_list_detach(MountPoint **head, bool *changed) { int umount_all(bool *changed) { int r; bool umount_changed; - LIST_HEAD(MountPoint, mp_list_head); LIST_HEAD_INIT(MountPoint, mp_list_head); - r = mount_points_list_get(&mp_list_head); if (r < 0) goto end; @@ -572,8 +577,6 @@ int umount_all(bool *changed) { if (r <= 0) goto end; - r = mount_points_list_remount_read_only(&mp_list_head, changed); - end: mount_points_list_free(&mp_list_head); diff --git a/src/core/unit-printf.c b/src/core/unit-printf.c new file mode 100644 index 0000000000..a58c96c238 --- /dev/null +++ b/src/core/unit-printf.c @@ -0,0 +1,353 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + 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 "systemd/sd-id128.h" +#include "unit.h" +#include "specifier.h" +#include "path-util.h" +#include "strv.h" +#include "unit-name.h" +#include "unit-printf.h" + +static char *specifier_prefix_and_instance(char specifier, void *data, void *userdata) { + Unit *u = userdata; + assert(u); + + return unit_name_to_prefix_and_instance(u->id); +} + +static char *specifier_prefix(char specifier, void *data, void *userdata) { + Unit *u = userdata; + assert(u); + + return unit_name_to_prefix(u->id); +} + +static char *specifier_prefix_unescaped(char specifier, void *data, void *userdata) { + Unit *u = userdata; + char *p, *r; + + assert(u); + + p = unit_name_to_prefix(u->id); + if (!p) + return NULL; + + r = unit_name_unescape(p); + free(p); + + return r; +} + +static char *specifier_instance_unescaped(char specifier, void *data, void *userdata) { + Unit *u = userdata; + assert(u); + + if (u->instance) + return unit_name_unescape(u->instance); + + return strdup(""); +} + +static char *specifier_filename(char specifier, void *data, void *userdata) { + Unit *u = userdata; + assert(u); + + if (u->instance) + return unit_name_path_unescape(u->instance); + + return unit_name_to_path(u->id); +} + +static char *specifier_cgroup(char specifier, void *data, void *userdata) { + Unit *u = userdata; + assert(u); + + return unit_default_cgroup_path(u); +} + +static char *specifier_cgroup_root(char specifier, void *data, void *userdata) { + Unit *u = userdata; + char *p; + assert(u); + + if (specifier == 'r') + return strdup(u->manager->cgroup_hierarchy); + + if (path_get_parent(u->manager->cgroup_hierarchy, &p) < 0) + return strdup(""); + + if (streq(p, "/")) { + free(p); + return strdup(""); + } + + return p; +} + +static char *specifier_runtime(char specifier, void *data, void *userdata) { + Unit *u = userdata; + assert(u); + + if (u->manager->running_as == SYSTEMD_USER) { + const char *e; + + e = getenv("XDG_RUNTIME_DIR"); + if (e) + return strdup(e); + } + + return strdup("/run"); +} + +static char *specifier_user_name(char specifier, void *data, void *userdata) { + Unit *u = userdata; + ExecContext *c; + int r; + const char *username; + uid_t uid; + char *printed = NULL; + + assert(u); + + c = unit_get_exec_context(u); + + /* get USER env from our own env if set */ + if (!c || !c->user) + return getusername_malloc(); + + /* fish username from passwd */ + username = c->user; + r = get_user_creds(&username, &uid, NULL, NULL, NULL); + if (r < 0) + return NULL; + + switch (specifier) { + case 'U': + if (asprintf(&printed, "%d", uid) < 0) + return NULL; + break; + case 'u': + printed = strdup(username); + break; + } + + return printed; +} + +static char *specifier_user_home(char specifier, void *data, void *userdata) { + Unit *u = userdata; + ExecContext *c; + int r; + const char *username, *home; + + assert(u); + + c = unit_get_exec_context(u); + + /* return HOME if set, otherwise from passwd */ + if (!c || !c->user) { + char *h; + + r = get_home_dir(&h); + if (r < 0) + return NULL; + + return h; + } + + username = c->user; + r = get_user_creds(&username, NULL, NULL, &home, NULL); + if (r < 0) + return NULL; + + return strdup(home); +} + +static char *specifier_user_shell(char specifier, void *data, void *userdata) { + Unit *u = userdata; + ExecContext *c; + int r; + const char *username, *shell; + + assert(u); + + c = unit_get_exec_context(u); + + /* return HOME if set, otherwise from passwd */ + if (!c || !c->user) { + char *sh; + + r = get_shell(&sh); + if (r < 0) + return strdup("/bin/sh"); + + return sh; + } + + username = c->user; + r = get_user_creds(&username, NULL, NULL, NULL, &shell); + if (r < 0) + return strdup("/bin/sh"); + + return strdup(shell); +} + +static char *specifier_machine_id(char specifier, void *data, void *userdata) { + sd_id128_t id; + char *buf; + int r; + + r = sd_id128_get_machine(&id); + if (r < 0) + return NULL; + + buf = new(char, 33); + if (!buf) + return NULL; + + return sd_id128_to_string(id, buf); +} + +static char *specifier_boot_id(char specifier, void *data, void *userdata) { + sd_id128_t id; + char *buf; + int r; + + r = sd_id128_get_boot(&id); + if (r < 0) + return NULL; + + buf = new(char, 33); + if (!buf) + return NULL; + + return sd_id128_to_string(id, buf); +} + +static char *specifier_host_name(char specifier, void *data, void *userdata) { + return gethostname_malloc(); +} + +char *unit_name_printf(Unit *u, const char* format) { + + /* + * This will use the passed string as format string and + * replace the following specifiers: + * + * %n: the full id of the unit (foo@bar.waldo) + * %N: the id of the unit without the suffix (foo@bar) + * %p: the prefix (foo) + * %i: the instance (bar) + */ + + const Specifier table[] = { + { 'n', specifier_string, u->id }, + { 'N', specifier_prefix_and_instance, NULL }, + { 'p', specifier_prefix, NULL }, + { 'i', specifier_string, u->instance }, + { 0, NULL, NULL } + }; + + assert(u); + assert(format); + + return specifier_printf(format, table, u); +} + +char *unit_full_printf(Unit *u, const char *format) { + + /* This is similar to unit_name_printf() but also supports + * unescaping. Also, adds a couple of additional codes: + * + * %f the the instance if set, otherwise the id + * %c cgroup path of unit + * %r root cgroup path of this systemd instance (e.g. "/user/lennart/shared/systemd-4711") + * %R parent of root cgroup path (e.g. "/usr/lennart/shared") + * %t the runtime directory to place sockets in (e.g. "/run" or $XDG_RUNTIME_DIR) + * %u the username of the configured user or running user + * %h the homedir of the configured user or running user + * %s the shell of the configured user or running user + * %m the machine ID of the running system + * %b the boot ID of the running system + * %H the host name of the running system + */ + + const Specifier table[] = { + { 'n', specifier_string, u->id }, + { 'N', specifier_prefix_and_instance, NULL }, + { 'p', specifier_prefix, NULL }, + { 'P', specifier_prefix_unescaped, NULL }, + { 'i', specifier_string, u->instance }, + { 'I', specifier_instance_unescaped, NULL }, + + { 'f', specifier_filename, NULL }, + { 'c', specifier_cgroup, NULL }, + { 'r', specifier_cgroup_root, NULL }, + { 'R', specifier_cgroup_root, NULL }, + { 't', specifier_runtime, NULL }, + { 'U', specifier_user_name, NULL }, + { 'u', specifier_user_name, NULL }, + { 'h', specifier_user_home, NULL }, + { 's', specifier_user_shell, NULL }, + + { 'm', specifier_machine_id, NULL }, + { 'H', specifier_host_name, NULL }, + { 'b', specifier_boot_id, NULL }, + { 0, NULL, NULL } + }; + + assert(u); + assert(format); + + return specifier_printf(format, table, u); +} + +char **unit_full_printf_strv(Unit *u, char **l) { + size_t n; + char **r, **i, **j; + + /* Applies unit_full_printf to every entry in l */ + + assert(u); + + n = strv_length(l); + r = new(char*, n+1); + if (!r) + return NULL; + + for (i = l, j = r; *i; i++, j++) { + *j = unit_full_printf(u, *i); + if (!*j) + goto fail; + } + + *j = NULL; + return r; + +fail: + for (j--; j >= r; j--) + free(*j); + + free(r); + + return NULL; +} diff --git a/src/core/fdset.h b/src/core/unit-printf.h index c3e408c8ad..d2f4ccd178 100644 --- a/src/core/fdset.h +++ b/src/core/unit-printf.h @@ -21,17 +21,8 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -typedef struct FDSet FDSet; +#include "unit.h" -FDSet* fdset_new(void); -void fdset_free(FDSet *s); - -int fdset_put(FDSet *s, int fd); -int fdset_put_dup(FDSet *s, int fd); - -bool fdset_contains(FDSet *s, int fd); -int fdset_remove(FDSet *s, int fd); - -int fdset_new_fill(FDSet **_s); - -int fdset_cloexec(FDSet *fds, bool b); +char *unit_name_printf(Unit *u, const char* text); +char *unit_full_printf(Unit *u, const char *text); +char **unit_full_printf_strv(Unit *u, char **l); diff --git a/src/core/unit.c b/src/core/unit.c index 0d5d15ef90..45453dce64 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -29,6 +29,8 @@ #include <unistd.h> #include <sys/stat.h> +#include "systemd/sd-id128.h" +#include "systemd/sd-messages.h" #include "set.h" #include "unit.h" #include "macro.h" @@ -38,7 +40,6 @@ #include "load-dropin.h" #include "log.h" #include "unit-name.h" -#include "specifier.h" #include "dbus-unit.h" #include "special.h" #include "cgroup-util.h" @@ -256,6 +257,9 @@ bool unit_check_gc(Unit *u) { if (unit_active_state(u) != UNIT_INACTIVE) return true; + if (u->refs) + return true; + if (UNIT_VTABLE(u)->check_gc) if (UNIT_VTABLE(u)->check_gc(u)) return true; @@ -609,7 +613,7 @@ int unit_add_exec_dependencies(Unit *u, ExecContext *c) { /* If syslog or kernel logging is requested, make sure our own * logging daemon is run first. */ - if (u->manager->running_as == MANAGER_SYSTEM) + if (u->manager->running_as == SYSTEMD_SYSTEM) if ((r = unit_add_two_dependencies_by_name(u, UNIT_REQUIRES, UNIT_AFTER, SPECIAL_JOURNALD_SOCKET, NULL, true)) < 0) return r; @@ -936,21 +940,96 @@ bool unit_condition_test(Unit *u) { return u->condition_result; } -static void unit_status_print_starting_stopping(Unit *u, bool stopping) { +static const char* unit_get_status_message_format(Unit *u, JobType t) { const UnitStatusMessageFormats *format_table; - const char *format; + + assert(u); + assert(t >= 0); + assert(t < _JOB_TYPE_MAX); + + if (t != JOB_START && t != JOB_STOP) + return NULL; format_table = &UNIT_VTABLE(u)->status_message_formats; if (!format_table) - return; + return NULL; + + return format_table->starting_stopping[t == JOB_STOP]; +} - format = format_table->starting_stopping[stopping]; +static const char *unit_get_status_message_format_try_harder(Unit *u, JobType t) { + const char *format; + + assert(u); + assert(t >= 0); + assert(t < _JOB_TYPE_MAX); + + format = unit_get_status_message_format(u, t); + if (format) + return format; + + /* Return generic strings */ + if (t == JOB_START) + return "Starting %s."; + else if (t == JOB_STOP) + return "Stopping %s."; + else if (t == JOB_RELOAD) + return "Reloading %s."; + + return NULL; +} + +static void unit_status_print_starting_stopping(Unit *u, JobType t) { + const char *format; + + assert(u); + + /* We only print status messages for selected units on + * selected operations. */ + + format = unit_get_status_message_format(u, t); if (!format) return; unit_status_printf(u, "", format, unit_description(u)); } +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +static void unit_status_log_starting_stopping_reloading(Unit *u, JobType t) { + const char *format; + char buf[LINE_MAX]; + sd_id128_t mid; + + assert(u); + + if (t != JOB_START && t != JOB_STOP && t != JOB_RELOAD) + return; + + if (log_on_console()) + return; + + /* We log status messages for all units and all operations. */ + + format = unit_get_status_message_format_try_harder(u, t); + if (!format) + return; + + snprintf(buf, sizeof(buf), format, unit_description(u)); + char_array_0(buf); + + mid = t == JOB_START ? SD_MESSAGE_UNIT_STARTING : + t == JOB_STOP ? SD_MESSAGE_UNIT_STOPPING : + SD_MESSAGE_UNIT_RELOADING; + + log_struct(LOG_INFO, + MESSAGE_ID(mid), + "UNIT=%s", u->id, + "MESSAGE=%s", buf, + NULL); +} +#pragma GCC diagnostic pop + /* Errors: * -EBADR: This unit type does not support starting. * -EALREADY: Unit is already started. @@ -990,7 +1069,8 @@ int unit_start(Unit *u) { return unit_start(following); } - unit_status_print_starting_stopping(u, false); + unit_status_log_starting_stopping_reloading(u, JOB_START); + unit_status_print_starting_stopping(u, JOB_START); /* If it is stopped, but we cannot start it, then fail */ if (!UNIT_VTABLE(u)->start) @@ -1040,7 +1120,8 @@ int unit_stop(Unit *u) { return unit_stop(following); } - unit_status_print_starting_stopping(u, true); + unit_status_log_starting_stopping_reloading(u, JOB_STOP); + unit_status_print_starting_stopping(u, JOB_STOP); if (!UNIT_VTABLE(u)->stop) return -EBADR; @@ -1079,6 +1160,8 @@ int unit_reload(Unit *u) { return unit_reload(following); } + unit_status_log_starting_stopping_reloading(u, JOB_RELOAD); + unit_add_to_dbus_queue(u); return UNIT_VTABLE(u)->reload(u); } @@ -1243,7 +1326,7 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su /* Note that this is called for all low-level state changes, * even if they might map to the same high-level * UnitActiveState! That means that ns == os is OK an expected - * behaviour here. For example: if a mount point is remounted + * behavior here. For example: if a mount point is remounted * this function will be called too! */ if (u->manager->n_reloading <= 0) { @@ -1355,7 +1438,10 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su check_unneeded_dependencies(u); if (ns != os && ns == UNIT_FAILED) { - log_notice("Unit %s entered failed state.", u->id); + log_struct(LOG_NOTICE, + "MESSAGE=Unit %s entered failed state", u->id, + "UNIT=%s", u->id, + NULL); unit_trigger_on_failure(u); } } @@ -1474,7 +1560,7 @@ void unit_unwatch_pid(Unit *u, pid_t pid) { hashmap_remove_value(u->manager->watch_pids, LONG_TO_PTR(pid), u); } -int unit_watch_timer(Unit *u, usec_t delay, Watch *w) { +int unit_watch_timer(Unit *u, clockid_t clock_id, bool relative, usec_t usec, Watch *w) { struct itimerspec its; int flags, fd; bool ours; @@ -1494,14 +1580,15 @@ int unit_watch_timer(Unit *u, usec_t delay, Watch *w) { } else if (w->type == WATCH_INVALID) { ours = true; - if ((fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK|TFD_CLOEXEC)) < 0) + fd = timerfd_create(clock_id, TFD_NONBLOCK|TFD_CLOEXEC); + if (fd < 0) return -errno; } else assert_not_reached("Invalid watch type"); zero(its); - if (delay <= 0) { + if (usec <= 0) { /* Set absolute time in the past, but not 0, since we * don't want to disarm the timer */ its.it_value.tv_sec = 0; @@ -1509,8 +1596,8 @@ int unit_watch_timer(Unit *u, usec_t delay, Watch *w) { flags = TFD_TIMER_ABSTIME; } else { - timespec_store(&its.it_value, delay); - flags = 0; + timespec_store(&its.it_value, usec); + flags = relative ? 0 : TFD_TIMER_ABSTIME; } /* This will also flush the elapse counter */ @@ -1881,7 +1968,7 @@ int unit_add_cgroup(Unit *u, CGroupBonding *b) { return 0; } -static char *default_cgroup_path(Unit *u) { +char *unit_default_cgroup_path(Unit *u) { char *p; assert(u); @@ -1914,7 +2001,7 @@ int unit_add_cgroup_from_text(Unit *u, const char *name) { return r; if (!path) { - path = default_cgroup_path(u); + path = unit_default_cgroup_path(u); ours = true; } @@ -1976,7 +2063,8 @@ static int unit_add_one_default_cgroup(Unit *u, const char *controller) { if (!(b->controller = strdup(controller))) goto fail; - if (!(b->path = default_cgroup_path(u))) + b->path = unit_default_cgroup_path(u); + if (!b->path) goto fail; b->ours = true; @@ -2133,253 +2221,6 @@ int unit_get_related_unit(Unit *u, const char *type, Unit **_found) { return 0; } -static char *specifier_prefix_and_instance(char specifier, void *data, void *userdata) { - Unit *u = userdata; - assert(u); - - return unit_name_to_prefix_and_instance(u->id); -} - -static char *specifier_prefix(char specifier, void *data, void *userdata) { - Unit *u = userdata; - assert(u); - - return unit_name_to_prefix(u->id); -} - -static char *specifier_prefix_unescaped(char specifier, void *data, void *userdata) { - Unit *u = userdata; - char *p, *r; - - assert(u); - - if (!(p = unit_name_to_prefix(u->id))) - return NULL; - - r = unit_name_unescape(p); - free(p); - - return r; -} - -static char *specifier_instance_unescaped(char specifier, void *data, void *userdata) { - Unit *u = userdata; - assert(u); - - if (u->instance) - return unit_name_unescape(u->instance); - - return strdup(""); -} - -static char *specifier_filename(char specifier, void *data, void *userdata) { - Unit *u = userdata; - assert(u); - - if (u->instance) - return unit_name_path_unescape(u->instance); - - return unit_name_to_path(u->instance); -} - -static char *specifier_cgroup(char specifier, void *data, void *userdata) { - Unit *u = userdata; - assert(u); - - return default_cgroup_path(u); -} - -static char *specifier_cgroup_root(char specifier, void *data, void *userdata) { - Unit *u = userdata; - char *p; - assert(u); - - if (specifier == 'r') - return strdup(u->manager->cgroup_hierarchy); - - if (path_get_parent(u->manager->cgroup_hierarchy, &p) < 0) - return strdup(""); - - if (streq(p, "/")) { - free(p); - return strdup(""); - } - - return p; -} - -static char *specifier_runtime(char specifier, void *data, void *userdata) { - Unit *u = userdata; - assert(u); - - if (u->manager->running_as == MANAGER_USER) { - const char *e; - - e = getenv("XDG_RUNTIME_DIR"); - if (e) - return strdup(e); - } - - return strdup("/run"); -} - -static char *specifier_user_name(char specifier, void *data, void *userdata) { - Service *s = userdata; - int r; - const char *username; - - /* get USER env from our own env if set */ - if (!s->exec_context.user) - return getusername_malloc(); - - /* fish username from passwd */ - username = s->exec_context.user; - r = get_user_creds(&username, NULL, NULL, NULL, NULL); - if (r < 0) - return NULL; - - return strdup(username); -} - -static char *specifier_user_home(char specifier, void *data, void *userdata) { - Service *s = userdata; - int r; - const char *username, *home; - - /* return HOME if set, otherwise from passwd */ - if (!s->exec_context.user) { - char *h; - - r = get_home_dir(&h); - if (r < 0) - return NULL; - - return h; - } - - username = s->exec_context.user; - r = get_user_creds(&username, NULL, NULL, &home, NULL); - if (r < 0) - return NULL; - - return strdup(home); -} - -static char *specifier_user_shell(char specifier, void *data, void *userdata) { - Service *s = userdata; - int r; - const char *username, *shell; - - /* return HOME if set, otherwise from passwd */ - if (!s->exec_context.user) { - char *sh; - - r = get_shell(&sh); - if (r < 0) - return strdup("/bin/sh"); - - return sh; - } - - username = s->exec_context.user; - r = get_user_creds(&username, NULL, NULL, NULL, &shell); - if (r < 0) - return strdup("/bin/sh"); - - return strdup(shell); -} - -char *unit_name_printf(Unit *u, const char* format) { - - /* - * This will use the passed string as format string and - * replace the following specifiers: - * - * %n: the full id of the unit (foo@bar.waldo) - * %N: the id of the unit without the suffix (foo@bar) - * %p: the prefix (foo) - * %i: the instance (bar) - */ - - const Specifier table[] = { - { 'n', specifier_string, u->id }, - { 'N', specifier_prefix_and_instance, NULL }, - { 'p', specifier_prefix, NULL }, - { 'i', specifier_string, u->instance }, - { 0, NULL, NULL } - }; - - assert(u); - assert(format); - - return specifier_printf(format, table, u); -} - -char *unit_full_printf(Unit *u, const char *format) { - - /* This is similar to unit_name_printf() but also supports - * unescaping. Also, adds a couple of additional codes: - * - * %c cgroup path of unit - * %r root cgroup path of this systemd instance (e.g. "/user/lennart/shared/systemd-4711") - * %R parent of root cgroup path (e.g. "/usr/lennart/shared") - * %t the runtime directory to place sockets in (e.g. "/run" or $XDG_RUNTIME_DIR) - * %u the username of the configured User or running user - * %h the homedir of the configured User or running user - */ - - const Specifier table[] = { - { 'n', specifier_string, u->id }, - { 'N', specifier_prefix_and_instance, NULL }, - { 'p', specifier_prefix, NULL }, - { 'P', specifier_prefix_unescaped, NULL }, - { 'i', specifier_string, u->instance }, - { 'I', specifier_instance_unescaped, NULL }, - { 'f', specifier_filename, NULL }, - { 'c', specifier_cgroup, NULL }, - { 'r', specifier_cgroup_root, NULL }, - { 'R', specifier_cgroup_root, NULL }, - { 't', specifier_runtime, NULL }, - { 'u', specifier_user_name, NULL }, - { 'h', specifier_user_home, NULL }, - { 's', specifier_user_shell, NULL }, - { 0, NULL, NULL } - }; - - assert(u); - assert(format); - - return specifier_printf(format, table, u); -} - -char **unit_full_printf_strv(Unit *u, char **l) { - size_t n; - char **r, **i, **j; - - /* Applies unit_full_printf to every entry in l */ - - assert(u); - - n = strv_length(l); - if (!(r = new(char*, n+1))) - return NULL; - - for (i = l, j = r; *i; i++, j++) - if (!(*j = unit_full_printf(u, *i))) - goto fail; - - *j = NULL; - return r; - -fail: - for (j--; j >= r; j--) - free(*j); - - free(r); - - return NULL; -} - int unit_watch_bus_name(Unit *u, const char *name) { assert(u); assert(name); @@ -2591,16 +2432,18 @@ int unit_add_node_link(Unit *u, const char *what, bool wants) { r = manager_load_unit(u->manager, e, NULL, NULL, &device); free(e); - if (r < 0) return r; - if ((r = unit_add_two_dependencies(u, UNIT_AFTER, UNIT_BINDS_TO, device, true)) < 0) + r = unit_add_two_dependencies(u, UNIT_AFTER, UNIT_BINDS_TO, device, true); + if (r < 0) return r; - if (wants) - if ((r = unit_add_dependency(device, UNIT_WANTS, u, false)) < 0) + if (wants) { + r = unit_add_dependency(device, UNIT_WANTS, u, false); + if (r < 0) return r; + } return 0; } @@ -2751,7 +2594,7 @@ UnitFileState unit_get_unit_file_state(Unit *u) { if (u->unit_file_state < 0 && u->fragment_path) u->unit_file_state = unit_file_get_state( - u->manager->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER, + u->manager->running_as == SYSTEMD_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER, NULL, path_get_file_name(u->fragment_path)); return u->unit_file_state; @@ -2834,7 +2677,7 @@ int unit_exec_context_defaults(Unit *u, ExecContext *c) { return -ENOMEM; } - if (u->manager->running_as == MANAGER_USER && + if (u->manager->running_as == SYSTEMD_USER && !c->working_directory) { r = get_home_dir(&c->working_directory); @@ -2845,6 +2688,17 @@ int unit_exec_context_defaults(Unit *u, ExecContext *c) { return 0; } +ExecContext *unit_get_exec_context(Unit *u) { + size_t offset; + assert(u); + + offset = UNIT_VTABLE(u)->exec_context_offset; + if (offset <= 0) + return NULL; + + return (ExecContext*) ((uint8_t*) u + offset); +} + static const char* const unit_active_state_table[_UNIT_ACTIVE_STATE_MAX] = { [UNIT_ACTIVE] = "active", [UNIT_RELOADING] = "reloading", diff --git a/src/core/unit.h b/src/core/unit.h index 89bd61c2d8..702bfeece6 100644 --- a/src/core/unit.h +++ b/src/core/unit.h @@ -266,6 +266,10 @@ struct UnitVTable { /* How much memory does an object of this unit type need */ size_t object_size; + /* If greater than 0, the offset into the object where + * ExecContext is found, if the unit type has that */ + size_t exec_context_offset; + /* Config file sections this unit type understands, separated * by NUL chars */ const char *sections; @@ -285,7 +289,7 @@ struct UnitVTable { * UNIT_STUB if no configuration could be found. */ int (*load)(Unit *u); - /* If a a lot of units got created via enumerate(), this is + /* If a lot of units got created via enumerate(), this is * where to actually set the state and call unit_notify(). */ int (*coldplug)(Unit *u); @@ -306,6 +310,9 @@ struct UnitVTable { /* Restore one item from the serialization */ int (*deserialize_item)(Unit *u, const char *key, const char *data, FDSet *fds); + /* Try to match up fds with what we need for this unit */ + int (*distribute_fds)(Unit *u, FDSet *fds); + /* Boils down the more complex internal state of this unit to * a simpler one that the engine can understand */ UnitActiveState (*active_state)(Unit *u); @@ -354,6 +361,9 @@ struct UnitVTable { /* Return the set of units that are following each other */ int (*following_set)(Unit *u, Set **s); + /* Called whenever CLOCK_REALTIME made a jump */ + void (*time_change)(Unit *u); + /* This is called for each unit type and should be used to * enumerate existing devices and load them. However, * everything that is loaded here should still stay in @@ -480,7 +490,7 @@ void unit_unwatch_fd(Unit *u, Watch *w); int unit_watch_pid(Unit *u, pid_t pid); void unit_unwatch_pid(Unit *u, pid_t pid); -int unit_watch_timer(Unit *u, usec_t delay, Watch *w); +int unit_watch_timer(Unit *u, clockid_t, bool relative, usec_t usec, Watch *w); void unit_unwatch_timer(Unit *u, Watch *w); int unit_watch_bus_name(Unit *u, const char *name); @@ -495,10 +505,6 @@ char *unit_dbus_path(Unit *u); int unit_load_related_unit(Unit *u, const char *type, Unit **_found); int unit_get_related_unit(Unit *u, const char *type, Unit **_found); -char *unit_name_printf(Unit *u, const char* text); -char *unit_full_printf(Unit *u, const char *text); -char **unit_full_printf_strv(Unit *u, char **l); - bool unit_can_serialize(Unit *u); int unit_serialize(Unit *u, FILE *f, FDSet *fds, bool serialize_jobs); void unit_serialize_item_format(Unit *u, FILE *f, const char *key, const char *value, ...) _printf_attr_(4,5); @@ -522,6 +528,8 @@ bool unit_pending_active(Unit *u); int unit_add_default_target_dependency(Unit *u, Unit *target); +char *unit_default_cgroup_path(Unit *u); + int unit_following_set(Unit *u, Set **s); void unit_trigger_on_failure(Unit *u); @@ -540,8 +548,17 @@ int unit_add_mount_links(Unit *u); int unit_exec_context_defaults(Unit *u, ExecContext *c); +ExecContext *unit_get_exec_context(Unit *u); + const char *unit_active_state_to_string(UnitActiveState i); UnitActiveState unit_active_state_from_string(const char *s); const char *unit_dependency_to_string(UnitDependency i); UnitDependency unit_dependency_from_string(const char *s); + +#define log_full_unit(level, unit, ...) log_meta_object(level, __FILE__, __LINE__, __func__, "UNIT=", unit, __VA_ARGS__) +#define log_debug_unit(unit, ...) log_full_unit(LOG_DEBUG, unit, __VA_ARGS__) +#define log_info_unit(unit, ...) log_full_unit(LOG_INFO, unit, __VA_ARGS__) +#define log_notice_unit(unit, ...) log_full_unit(LOG_NOTICE, unit, __VA_ARGS__) +#define log_warning_unit(unit, ...) log_full_unit(LOG_WARNING, unit, __VA_ARGS__) +#define log_error_unit(unit, ...) log_full_unit(LOG_ERR, unit, __VA_ARGS__) |