diff options
Diffstat (limited to 'src/core')
128 files changed, 0 insertions, 43625 deletions
diff --git a/src/core/.gitignore b/src/core/.gitignore deleted file mode 100644 index a763f72507..0000000000 --- a/src/core/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -/syscall-from-name.gperf -/syscall-from-name.h -/syscall-list.txt -/syscall-to-name.h -/macros.systemd -/systemd.pc diff --git a/src/core/Makefile b/src/core/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/core/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile
\ No newline at end of file diff --git a/src/core/audit-fd.c b/src/core/audit-fd.c deleted file mode 100644 index 5955bd846e..0000000000 --- a/src/core/audit-fd.c +++ /dev/null @@ -1,73 +0,0 @@ -/*-*- 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 deleted file mode 100644 index 8b58289dc5..0000000000 --- a/src/core/audit-fd.h +++ /dev/null @@ -1,25 +0,0 @@ -/*-*- 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 deleted file mode 100644 index b1619a64e6..0000000000 --- a/src/core/automount.c +++ /dev/null @@ -1,902 +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 <limits.h> -#include <sys/mount.h> -#include <unistd.h> -#include <fcntl.h> -#include <sys/epoll.h> -#include <sys/stat.h> -#include <linux/auto_fs4.h> -#include <linux/auto_dev-ioctl.h> - -#include "unit.h" -#include "automount.h" -#include "load-fragment.h" -#include "load-dropin.h" -#include "unit-name.h" -#include "dbus-automount.h" -#include "bus-errors.h" -#include "special.h" -#include "label.h" -#include "mkdir.h" -#include "path-util.h" - -static const UnitActiveState state_translation_table[_AUTOMOUNT_STATE_MAX] = { - [AUTOMOUNT_DEAD] = UNIT_INACTIVE, - [AUTOMOUNT_WAITING] = UNIT_ACTIVE, - [AUTOMOUNT_RUNNING] = UNIT_ACTIVE, - [AUTOMOUNT_FAILED] = UNIT_FAILED -}; - -static int open_dev_autofs(Manager *m); - -static void automount_init(Unit *u) { - Automount *a = AUTOMOUNT(u); - - assert(u); - assert(u->load_state == UNIT_STUB); - - a->pipe_watch.fd = a->pipe_fd = -1; - a->pipe_watch.type = WATCH_INVALID; - - a->directory_mode = 0755; - - UNIT(a)->ignore_on_isolate = true; -} - -static void repeat_unmout(const char *path) { - assert(path); - - for (;;) { - /* If there are multiple mounts on a mount point, this - * removes them all */ - - if (umount2(path, MNT_DETACH) >= 0) - continue; - - if (errno != EINVAL) - log_error("Failed to unmount: %m"); - - break; - } -} - -static void unmount_autofs(Automount *a) { - assert(a); - - if (a->pipe_fd < 0) - return; - - automount_send_ready(a, -EHOSTDOWN); - - unit_unwatch_fd(UNIT(a), &a->pipe_watch); - close_nointr_nofail(a->pipe_fd); - a->pipe_fd = -1; - - /* If we reload/reexecute things we keep the mount point - * around */ - if (a->where && - (UNIT(a)->manager->exit_code != MANAGER_RELOAD && - UNIT(a)->manager->exit_code != MANAGER_REEXECUTE)) - repeat_unmout(a->where); -} - -static void automount_done(Unit *u) { - Automount *a = AUTOMOUNT(u); - - assert(a); - - unmount_autofs(a); - unit_ref_unset(&a->mount); - - free(a->where); - a->where = NULL; - - set_free(a->tokens); - a->tokens = NULL; -} - -int automount_add_one_mount_link(Automount *a, Mount *m) { - int r; - - assert(a); - assert(m); - - if (UNIT(a)->load_state != UNIT_LOADED || - UNIT(m)->load_state != UNIT_LOADED) - return 0; - - if (!path_startswith(a->where, m->where)) - return 0; - - 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) - return r; - - return 0; -} - -static int automount_add_mount_links(Automount *a) { - Unit *other; - int r; - - 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) - return r; - - return 0; -} - -static int automount_add_default_dependencies(Automount *a) { - int r; - - assert(a); - - 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); - if (r < 0) - return r; - - return 0; -} - -static int automount_verify(Automount *a) { - bool b; - char *e; - assert(a); - - if (UNIT(a)->load_state != UNIT_LOADED) - return 0; - - if (path_equal(a->where, "/")) { - log_error("Cannot have an automount unit for the root directory. Refusing."); - return -EINVAL; - } - - if (!(e = unit_name_from_path(a->where, ".automount"))) - 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); - return -EINVAL; - } - - return 0; -} - -static int automount_load(Unit *u) { - int r; - Automount *a = AUTOMOUNT(u); - - assert(u); - assert(u->load_state == UNIT_STUB); - - /* Load a .automount file */ - if ((r = unit_load_fragment_and_dropin_optional(u)) < 0) - return r; - - if (u->load_state == UNIT_LOADED) { - Unit *x; - - if (!a->where) - if (!(a->where = unit_name_to_path(u->id))) - return -ENOMEM; - - path_kill_slashes(a->where); - - if ((r = automount_add_mount_links(a)) < 0) - return r; - - r = unit_load_related_unit(u, ".mount", &x); - if (r < 0) - return r; - - unit_ref_set(&a->mount, x); - - r = unit_add_two_dependencies(u, UNIT_BEFORE, UNIT_TRIGGERS, UNIT_DEREF(a->mount), true); - if (r < 0) - return r; - - if (UNIT(a)->default_dependencies) - if ((r = automount_add_default_dependencies(a)) < 0) - return r; - } - - return automount_verify(a); -} - -static void automount_set_state(Automount *a, AutomountState state) { - AutomountState old_state; - assert(a); - - old_state = a->state; - a->state = state; - - if (state != AUTOMOUNT_WAITING && - state != AUTOMOUNT_RUNNING) - 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)); - - unit_notify(UNIT(a), state_translation_table[old_state], state_translation_table[state], true); -} - -static int automount_coldplug(Unit *u) { - Automount *a = AUTOMOUNT(u); - int r; - - assert(a); - assert(a->state == AUTOMOUNT_DEAD); - - if (a->deserialized_state != a->state) { - - if ((r = open_dev_autofs(u->manager)) < 0) - return r; - - if (a->deserialized_state == AUTOMOUNT_WAITING || - a->deserialized_state == AUTOMOUNT_RUNNING) { - - assert(a->pipe_fd >= 0); - - if ((r = unit_watch_fd(UNIT(a), a->pipe_fd, EPOLLIN, &a->pipe_watch)) < 0) - return r; - } - - automount_set_state(a, a->deserialized_state); - } - - return 0; -} - -static void automount_dump(Unit *u, FILE *f, const char *prefix) { - Automount *a = AUTOMOUNT(u); - - assert(a); - - fprintf(f, - "%sAutomount State: %s\n" - "%sResult: %s\n" - "%sWhere: %s\n" - "%sDirectoryMode: %04o\n", - prefix, automount_state_to_string(a->state), - prefix, automount_result_to_string(a->result), - prefix, a->where, - prefix, a->directory_mode); -} - -static void automount_enter_dead(Automount *a, AutomountResult f) { - assert(a); - - if (f != AUTOMOUNT_SUCCESS) - a->result = f; - - automount_set_state(a, a->result != AUTOMOUNT_SUCCESS ? AUTOMOUNT_FAILED : AUTOMOUNT_DEAD); -} - -static int open_dev_autofs(Manager *m) { - struct autofs_dev_ioctl param; - - assert(m); - - if (m->dev_autofs_fd >= 0) - return m->dev_autofs_fd; - - label_fix("/dev/autofs", false, false); - - if ((m->dev_autofs_fd = open("/dev/autofs", O_CLOEXEC|O_RDONLY)) < 0) { - log_error("Failed to open /dev/autofs: %s", strerror(errno)); - return -errno; - } - - init_autofs_dev_ioctl(¶m); - if (ioctl(m->dev_autofs_fd, AUTOFS_DEV_IOCTL_VERSION, ¶m) < 0) { - close_nointr_nofail(m->dev_autofs_fd); - m->dev_autofs_fd = -1; - return -errno; - } - - log_debug("Autofs kernel version %i.%i", param.ver_major, param.ver_minor); - - return m->dev_autofs_fd; -} - -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; - - init_autofs_dev_ioctl(param); - param->size = l; - param->ioctlfd = -1; - param->openmount.devid = devid; - strcpy(param->path, where); - - if (ioctl(dev_autofs_fd, AUTOFS_DEV_IOCTL_OPENMOUNT, param) < 0) { - r = -errno; - goto finish; - } - - if (param->ioctlfd < 0) { - r = -EIO; - goto finish; - } - - fd_cloexec(param->ioctlfd, true); - r = param->ioctlfd; - -finish: - free(param); - return r; -} - -static int autofs_protocol(int dev_autofs_fd, int ioctl_fd) { - uint32_t major, minor; - struct autofs_dev_ioctl param; - - assert(dev_autofs_fd >= 0); - assert(ioctl_fd >= 0); - - init_autofs_dev_ioctl(¶m); - param.ioctlfd = ioctl_fd; - - if (ioctl(dev_autofs_fd, AUTOFS_DEV_IOCTL_PROTOVER, ¶m) < 0) - return -errno; - - major = param.protover.version; - - init_autofs_dev_ioctl(¶m); - param.ioctlfd = ioctl_fd; - - if (ioctl(dev_autofs_fd, AUTOFS_DEV_IOCTL_PROTOSUBVER, ¶m) < 0) - return -errno; - - minor = param.protosubver.sub_version; - - log_debug("Autofs protocol version %i.%i", major, minor); - return 0; -} - -static int autofs_set_timeout(int dev_autofs_fd, int ioctl_fd, time_t sec) { - struct autofs_dev_ioctl param; - - assert(dev_autofs_fd >= 0); - assert(ioctl_fd >= 0); - - init_autofs_dev_ioctl(¶m); - param.ioctlfd = ioctl_fd; - param.timeout.timeout = sec; - - if (ioctl(dev_autofs_fd, AUTOFS_DEV_IOCTL_TIMEOUT, ¶m) < 0) - return -errno; - - return 0; -} - -static int autofs_send_ready(int dev_autofs_fd, int ioctl_fd, uint32_t token, int status) { - struct autofs_dev_ioctl param; - - assert(dev_autofs_fd >= 0); - assert(ioctl_fd >= 0); - - init_autofs_dev_ioctl(¶m); - param.ioctlfd = ioctl_fd; - - if (status) { - param.fail.token = token; - param.fail.status = status; - } else - param.ready.token = token; - - if (ioctl(dev_autofs_fd, status ? AUTOFS_DEV_IOCTL_FAIL : AUTOFS_DEV_IOCTL_READY, ¶m) < 0) - return -errno; - - return 0; -} - -int automount_send_ready(Automount *a, int status) { - int ioctl_fd, r; - unsigned token; - - assert(a); - assert(status <= 0); - - 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) { - r = ioctl_fd; - goto fail; - } - - if (status) - log_debug("Sending failure: %s", strerror(-status)); - else - log_debug("Sending success."); - - r = 0; - - /* Autofs thankfully does not hand out 0 as a token */ - while ((token = PTR_TO_UINT(set_steal_first(a->tokens)))) { - int k; - - /* Autofs fun fact II: - * - * 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) - r = k; - } - -fail: - if (ioctl_fd >= 0) - close_nointr_nofail(ioctl_fd); - - return r; -} - -static void automount_enter_waiting(Automount *a) { - int p[2] = { -1, -1 }; - char name[32], options[128]; - bool mounted = false; - int r, ioctl_fd = -1, dev_autofs_fd; - struct stat st; - - assert(a); - assert(a->pipe_fd < 0); - assert(a->where); - - if (a->tokens) - set_clear(a->tokens); - - if ((dev_autofs_fd = open_dev_autofs(UNIT(a)->manager)) < 0) { - r = dev_autofs_fd; - goto fail; - } - - /* We knowingly ignore the results of this call */ - mkdir_p_label(a->where, 0555); - - if (dir_is_empty(a->where) <= 0) - log_notice("%s: Directory %s to mount over is not empty, mounting anyway. (To see the over-mounted files, please manually mount the underlying file system to a secondary location.)", a->meta.id, a->where); - - if (pipe2(p, O_NONBLOCK|O_CLOEXEC) < 0) { - r = -errno; - goto fail; - } - - snprintf(options, sizeof(options), "fd=%i,pgrp=%u,minproto=5,maxproto=5,direct", p[1], (unsigned) getpgrp()); - char_array_0(options); - - snprintf(name, sizeof(name), "systemd-%u", (unsigned) getpid()); - char_array_0(name); - - if (mount(name, a->where, "autofs", 0, options) < 0) { - r = -errno; - goto fail; - } - - mounted = true; - - close_nointr_nofail(p[1]); - p[1] = -1; - - if (stat(a->where, &st) < 0) { - r = -errno; - goto fail; - } - - if ((ioctl_fd = open_ioctl_fd(dev_autofs_fd, a->where, st.st_dev)) < 0) { - r = ioctl_fd; - goto fail; - } - - if ((r = autofs_protocol(dev_autofs_fd, ioctl_fd)) < 0) - goto fail; - - if ((r = autofs_set_timeout(dev_autofs_fd, ioctl_fd, 300)) < 0) - goto fail; - - /* Autofs fun fact: - * - * Unless we close the ioctl fd here, for some weird reason - * the direct mount will not receive events from the - * kernel. */ - - close_nointr_nofail(ioctl_fd); - ioctl_fd = -1; - - if ((r = unit_watch_fd(UNIT(a), p[0], EPOLLIN, &a->pipe_watch)) < 0) - goto fail; - - a->pipe_fd = p[0]; - a->dev_id = st.st_dev; - - automount_set_state(a, AUTOMOUNT_WAITING); - - return; - -fail: - assert_se(close_pipe(p) == 0); - - if (ioctl_fd >= 0) - close_nointr_nofail(ioctl_fd); - - if (mounted) - repeat_unmout(a->where); - - log_error("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; - - assert(a); - assert(UNIT_DEREF(a->mount)); - - dbus_error_init(&error); - - /* 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); - automount_send_ready(a, -EHOSTDOWN); - return; - } - - mkdir_p_label(a->where, a->directory_mode); - - /* 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); - 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); - 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)); - goto fail; - } - - automount_set_state(a, AUTOMOUNT_RUNNING); - return; - -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); - return -EEXIST; - } - - if (UNIT_DEREF(a->mount)->load_state != UNIT_LOADED) - return -ENOENT; - - a->result = AUTOMOUNT_SUCCESS; - automount_enter_waiting(a); - return 0; -} - -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); - return 0; -} - -static int automount_serialize(Unit *u, FILE *f, FDSet *fds) { - Automount *a = AUTOMOUNT(u); - void *p; - Iterator i; - - assert(a); - assert(f); - assert(fds); - - unit_serialize_item(u, f, "state", automount_state_to_string(a->state)); - unit_serialize_item(u, f, "result", automount_result_to_string(a->result)); - unit_serialize_item_format(u, f, "dev-id", "%u", (unsigned) a->dev_id); - - SET_FOREACH(p, a->tokens, i) - unit_serialize_item_format(u, f, "token", "%u", PTR_TO_UINT(p)); - - if (a->pipe_fd >= 0) { - int copy; - - if ((copy = fdset_put_dup(fds, a->pipe_fd)) < 0) - return copy; - - unit_serialize_item_format(u, f, "pipe-fd", "%i", copy); - } - - return 0; -} - -static int automount_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) { - Automount *a = AUTOMOUNT(u); - int r; - - assert(a); - assert(fds); - - if (streq(key, "state")) { - AutomountState state; - - if ((state = automount_state_from_string(value)) < 0) - log_debug("Failed to parse state value %s", value); - else - a->deserialized_state = state; - } else if (streq(key, "result")) { - AutomountResult f; - - f = automount_result_from_string(value); - if (f < 0) - log_debug("Failed to parse result value %s", value); - else if (f != AUTOMOUNT_SUCCESS) - a->result = f; - - } else if (streq(key, "dev-id")) { - unsigned d; - - if (safe_atou(value, &d) < 0) - log_debug("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); - 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) - 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); - else { - if (a->pipe_fd >= 0) - close_nointr_nofail(a->pipe_fd); - - a->pipe_fd = fdset_remove(fds, fd); - } - } else - log_debug("Unknown serialization key '%s'", key); - - return 0; -} - -static UnitActiveState automount_active_state(Unit *u) { - assert(u); - - return state_translation_table[AUTOMOUNT(u)->state]; -} - -static const char *automount_sub_state_to_string(Unit *u) { - assert(u); - - return automount_state_to_string(AUTOMOUNT(u)->state); -} - -static bool automount_check_gc(Unit *u) { - Automount *a = AUTOMOUNT(u); - - assert(a); - - if (!UNIT_DEREF(a->mount)) - return false; - - return UNIT_VTABLE(UNIT_DEREF(a->mount))->check_gc(UNIT_DEREF(a->mount)); -} - -static void automount_fd_event(Unit *u, int fd, uint32_t events, Watch *w) { - Automount *a = AUTOMOUNT(u); - union autofs_v5_packet_union packet; - ssize_t l; - int r; - - assert(a); - assert(fd == a->pipe_fd); - - if (events != EPOLLIN) { - log_error("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"); - goto fail; - } - - switch (packet.hdr.type) { - - case autofs_ptype_missing_direct: - - if (packet.v5_packet.pid > 0) { - char *p = NULL; - - get_process_comm(packet.v5_packet.pid, &p); - log_debug("Got direct mount request on %s, triggered by %lu (%s)", - a->where, (unsigned long) packet.v5_packet.pid, strna(p)); - free(p); - - } else - log_debug("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; - } - - 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)); - goto fail; - } - - automount_enter_runnning(a); - break; - - default: - log_error("Received unknown automount request %i", packet.hdr.type); - break; - } - - return; - -fail: - automount_enter_dead(a, AUTOMOUNT_FAILURE_RESOURCES); -} - -static void automount_shutdown(Manager *m) { - assert(m); - - if (m->dev_autofs_fd >= 0) - close_nointr_nofail(m->dev_autofs_fd); -} - -static void automount_reset_failed(Unit *u) { - Automount *a = AUTOMOUNT(u); - - assert(a); - - if (a->state == AUTOMOUNT_FAILED) - automount_set_state(a, AUTOMOUNT_DEAD); - - a->result = AUTOMOUNT_SUCCESS; -} - -static const char* const automount_state_table[_AUTOMOUNT_STATE_MAX] = { - [AUTOMOUNT_DEAD] = "dead", - [AUTOMOUNT_WAITING] = "waiting", - [AUTOMOUNT_RUNNING] = "running", - [AUTOMOUNT_FAILED] = "failed" -}; - -DEFINE_STRING_TABLE_LOOKUP(automount_state, AutomountState); - -static const char* const automount_result_table[_AUTOMOUNT_RESULT_MAX] = { - [AUTOMOUNT_SUCCESS] = "success", - [AUTOMOUNT_FAILURE_RESOURCES] = "resources" -}; - -DEFINE_STRING_TABLE_LOOKUP(automount_result, AutomountResult); - -const UnitVTable automount_vtable = { - .object_size = sizeof(Automount), - .sections = - "Unit\0" - "Automount\0" - "Install\0", - - .no_alias = true, - .no_instances = true, - - .init = automount_init, - .load = automount_load, - .done = automount_done, - - .coldplug = automount_coldplug, - - .dump = automount_dump, - - .start = automount_start, - .stop = automount_stop, - - .serialize = automount_serialize, - .deserialize_item = automount_deserialize_item, - - .active_state = automount_active_state, - .sub_state_to_string = automount_sub_state_to_string, - - .check_gc = automount_check_gc, - - .fd_event = automount_fd_event, - - .reset_failed = automount_reset_failed, - - .bus_interface = "org.freedesktop.systemd1.Automount", - .bus_message_handler = bus_automount_message_handler, - .bus_invalidating_properties = bus_automount_invalidating_properties, - - .shutdown = automount_shutdown, - - .status_message_formats = { - .finished_start_job = { - [JOB_DONE] = "Set up automount %s.", - [JOB_FAILED] = "Failed to set up automount %s.", - [JOB_DEPENDENCY] = "Dependency failed for %s.", - }, - .finished_stop_job = { - [JOB_DONE] = "Unset automount %s.", - [JOB_FAILED] = "Failed to unset automount %s.", - }, - }, -}; diff --git a/src/core/automount.h b/src/core/automount.h deleted file mode 100644 index 3d5736d1cb..0000000000 --- a/src/core/automount.h +++ /dev/null @@ -1,73 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -#pragma once - -/*** - 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/>. -***/ - -typedef struct Automount Automount; - -#include "unit.h" - -typedef enum AutomountState { - AUTOMOUNT_DEAD, - AUTOMOUNT_WAITING, - AUTOMOUNT_RUNNING, - AUTOMOUNT_FAILED, - _AUTOMOUNT_STATE_MAX, - _AUTOMOUNT_STATE_INVALID = -1 -} AutomountState; - -typedef enum AutomountResult { - AUTOMOUNT_SUCCESS, - AUTOMOUNT_FAILURE_RESOURCES, - _AUTOMOUNT_RESULT_MAX, - _AUTOMOUNT_RESULT_INVALID = -1 -} AutomountResult; - -struct Automount { - Unit meta; - - AutomountState state, deserialized_state; - - char *where; - - UnitRef mount; - - int pipe_fd; - mode_t directory_mode; - Watch pipe_watch; - dev_t dev_id; - - Set *tokens; - - AutomountResult result; -}; - -extern const UnitVTable automount_vtable; - -int automount_send_ready(Automount *a, int status); - -int automount_add_one_mount_link(Automount *a, Mount *m); - -const char* automount_state_to_string(AutomountState i); -AutomountState automount_state_from_string(const char *s); - -const char* automount_result_to_string(AutomountResult i); -AutomountResult automount_result_from_string(const char *s); diff --git a/src/core/build.h b/src/core/build.h deleted file mode 100644 index 4513a0bad7..0000000000 --- a/src/core/build.h +++ /dev/null @@ -1,84 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -#pragma once - -/*** - 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/>. -***/ - -#ifdef HAVE_PAM -#define _PAM_FEATURE_ "+PAM" -#else -#define _PAM_FEATURE_ "-PAM" -#endif - -#ifdef HAVE_LIBWRAP -#define _LIBWRAP_FEATURE_ "+LIBWRAP" -#else -#define _LIBWRAP_FEATURE_ "-LIBWRAP" -#endif - -#ifdef HAVE_AUDIT -#define _AUDIT_FEATURE_ "+AUDIT" -#else -#define _AUDIT_FEATURE_ "-AUDIT" -#endif - -#ifdef HAVE_SELINUX -#define _SELINUX_FEATURE_ "+SELINUX" -#else -#define _SELINUX_FEATURE_ "-SELINUX" -#endif - -#ifdef HAVE_IMA -#define _IMA_FEATURE_ "+IMA" -#else -#define _IMA_FEATURE_ "-IMA" -#endif - -#ifdef HAVE_SYSV_COMPAT -#define _SYSVINIT_FEATURE_ "+SYSVINIT" -#else -#define _SYSVINIT_FEATURE_ "-SYSVINIT" -#endif - -#ifdef HAVE_LIBCRYPTSETUP -#define _LIBCRYPTSETUP_FEATURE_ "+LIBCRYPTSETUP" -#else -#define _LIBCRYPTSETUP_FEATURE_ "-LIBCRYPTSETUP" -#endif - -#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/bus-errors.h b/src/core/bus-errors.h deleted file mode 100644 index 04c1b2849d..0000000000 --- a/src/core/bus-errors.h +++ /dev/null @@ -1,55 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -#pragma once - -/*** - 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 <string.h> -#include <dbus/dbus.h> - -#define BUS_ERROR_NO_SUCH_UNIT "org.freedesktop.systemd1.NoSuchUnit" -#define BUS_ERROR_NO_SUCH_JOB "org.freedesktop.systemd1.NoSuchJob" -#define BUS_ERROR_NOT_SUBSCRIBED "org.freedesktop.systemd1.NotSubscribed" -#define BUS_ERROR_INVALID_PATH "org.freedesktop.systemd1.InvalidPath" -#define BUS_ERROR_INVALID_NAME "org.freedesktop.systemd1.InvalidName" -#define BUS_ERROR_UNIT_TYPE_MISMATCH "org.freedesktop.systemd1.UnitTypeMismatch" -#define BUS_ERROR_UNIT_EXISTS "org.freedesktop.systemd1.UnitExists" -#define BUS_ERROR_NOT_SUPPORTED "org.freedesktop.systemd1.NotSupported" -#define BUS_ERROR_INVALID_JOB_MODE "org.freedesktop.systemd1.InvalidJobMode" -#define BUS_ERROR_ONLY_BY_DEPENDENCY "org.freedesktop.systemd1.OnlyByDependency" -#define BUS_ERROR_NO_ISOLATION "org.freedesktop.systemd1.NoIsolation" -#define BUS_ERROR_LOAD_FAILED "org.freedesktop.systemd1.LoadFailed" -#define BUS_ERROR_MASKED "org.freedesktop.systemd1.Masked" -#define BUS_ERROR_JOB_TYPE_NOT_APPLICABLE "org.freedesktop.systemd1.JobTypeNotApplicable" -#define BUS_ERROR_TRANSACTION_IS_DESTRUCTIVE "org.freedesktop.systemd1.TransactionIsDestructive" -#define BUS_ERROR_TRANSACTION_JOBS_CONFLICTING "org.freedesktop.systemd1.TransactionJobsConflicting" -#define BUS_ERROR_TRANSACTION_ORDER_IS_CYCLIC "org.freedesktop.systemd1.TransactionOrderIsCyclic" -#define BUS_ERROR_SHUTTING_DOWN "org.freedesktop.systemd1.ShuttingDown" -#define BUS_ERROR_NO_SUCH_PROCESS "org.freedesktop.systemd1.NoSuchProcess" - -static inline const char *bus_error(const DBusError *e, int r) { - if (e && e->message) - return e->message; - - if (r >= 0) - return strerror(r); - - return strerror(-r); -} diff --git a/src/core/cgroup-attr.c b/src/core/cgroup-attr.c deleted file mode 100644 index 71af09cf87..0000000000 --- a/src/core/cgroup-attr.c +++ /dev/null @@ -1,102 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -/*** - This file is part of systemd. - - Copyright 2011 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 "cgroup-attr.h" -#include "cgroup-util.h" -#include "list.h" - -int cgroup_attribute_apply(CGroupAttribute *a, CGroupBonding *b) { - int r; - char *path = NULL; - char *v = NULL; - - assert(a); - - b = cgroup_bonding_find_list(b, a->controller); - if (!b) - return 0; - - if (a->map_callback) { - r = a->map_callback(a->controller, a->name, a->value, &v); - if (r < 0) - return r; - } - - r = cg_get_path(a->controller, b->path, a->name, &path); - if (r < 0) { - free(v); - return r; - } - - r = write_one_line_file(path, v ? v : a->value); - if (r < 0) - log_warning("Failed to write '%s' to %s: %s", v ? v : a->value, path, strerror(-r)); - - free(path); - free(v); - - return r; -} - -int cgroup_attribute_apply_list(CGroupAttribute *first, CGroupBonding *b) { - CGroupAttribute *a; - int r = 0; - - LIST_FOREACH(by_unit, a, first) { - int k; - - k = cgroup_attribute_apply(a, b); - if (r == 0) - r = k; - } - - return r; -} - -CGroupAttribute *cgroup_attribute_find_list(CGroupAttribute *first, const char *controller, const char *name) { - CGroupAttribute *a; - - assert(controller); - assert(name); - - LIST_FOREACH(by_unit, a, first) - if (streq(a->controller, controller) && - streq(a->name, name)) - return a; - - return NULL; -} - -static void cgroup_attribute_free(CGroupAttribute *a) { - assert(a); - - free(a->controller); - free(a->name); - free(a->value); - free(a); -} - -void cgroup_attribute_free_list(CGroupAttribute *first) { - CGroupAttribute *a, *n; - - LIST_FOREACH_SAFE(by_unit, a, n, first) - cgroup_attribute_free(a); -} diff --git a/src/core/cgroup-attr.h b/src/core/cgroup-attr.h deleted file mode 100644 index 2b754eac40..0000000000 --- a/src/core/cgroup-attr.h +++ /dev/null @@ -1,46 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -#pragma once - -/*** - This file is part of systemd. - - Copyright 2011 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/>. -***/ - -typedef struct CGroupAttribute CGroupAttribute; - -typedef int (*CGroupAttributeMapCallback)(const char *controller, const char*name, const char *value, char **ret); - -#include "unit.h" -#include "cgroup.h" - -struct CGroupAttribute { - char *controller; - char *name; - char *value; - - CGroupAttributeMapCallback map_callback; - - LIST_FIELDS(CGroupAttribute, by_unit); -}; - -int cgroup_attribute_apply(CGroupAttribute *a, CGroupBonding *b); -int cgroup_attribute_apply_list(CGroupAttribute *first, CGroupBonding *b); - -CGroupAttribute *cgroup_attribute_find_list(CGroupAttribute *first, const char *controller, const char *name); - -void cgroup_attribute_free_list(CGroupAttribute *first); diff --git a/src/core/cgroup.c b/src/core/cgroup.c deleted file mode 100644 index 8fc1731485..0000000000 --- a/src/core/cgroup.c +++ /dev/null @@ -1,598 +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 <assert.h> -#include <unistd.h> -#include <sys/types.h> -#include <signal.h> -#include <sys/mount.h> -#include <fcntl.h> - -#include "cgroup.h" -#include "cgroup-util.h" -#include "log.h" -#include "strv.h" -#include "path-util.h" - -int cgroup_bonding_realize(CGroupBonding *b) { - int r; - - assert(b); - assert(b->path); - assert(b->controller); - - r = cg_create(b->controller, b->path); - if (r < 0) { - log_warning("Failed to create cgroup %s:%s: %s", b->controller, b->path, strerror(-r)); - return r; - } - - b->realized = true; - - return 0; -} - -int cgroup_bonding_realize_list(CGroupBonding *first) { - CGroupBonding *b; - int r; - - LIST_FOREACH(by_unit, b, first) - if ((r = cgroup_bonding_realize(b)) < 0 && b->essential) - return r; - - return 0; -} - -void cgroup_bonding_free(CGroupBonding *b, bool trim) { - assert(b); - - if (b->unit) { - CGroupBonding *f; - - LIST_REMOVE(CGroupBonding, by_unit, b->unit->cgroup_bondings, b); - - if (streq(b->controller, SYSTEMD_CGROUP_CONTROLLER)) { - assert_se(f = hashmap_get(b->unit->manager->cgroup_bondings, b->path)); - LIST_REMOVE(CGroupBonding, by_path, f, b); - - if (f) - hashmap_replace(b->unit->manager->cgroup_bondings, b->path, f); - else - hashmap_remove(b->unit->manager->cgroup_bondings, b->path); - } - } - - if (b->realized && b->ours && trim) - cg_trim(b->controller, b->path, false); - - free(b->controller); - free(b->path); - free(b); -} - -void cgroup_bonding_free_list(CGroupBonding *first, bool remove_or_trim) { - CGroupBonding *b, *n; - - LIST_FOREACH_SAFE(by_unit, b, n, first) - cgroup_bonding_free(b, remove_or_trim); -} - -void cgroup_bonding_trim(CGroupBonding *b, bool delete_root) { - assert(b); - - if (b->realized && b->ours) - cg_trim(b->controller, b->path, delete_root); -} - -void cgroup_bonding_trim_list(CGroupBonding *first, bool delete_root) { - CGroupBonding *b; - - LIST_FOREACH(by_unit, b, first) - cgroup_bonding_trim(b, delete_root); -} - - -int cgroup_bonding_install(CGroupBonding *b, pid_t pid, const char *cgroup_suffix) { - char *p = NULL; - const char *path; - int r; - - assert(b); - assert(pid >= 0); - - if (cgroup_suffix) { - p = strjoin(b->path, "/", cgroup_suffix, NULL); - if (!p) - return -ENOMEM; - - path = p; - } else - path = b->path; - - r = cg_create_and_attach(b->controller, path, pid); - free(p); - - if (r < 0) - return r; - - b->realized = true; - return 0; -} - -int cgroup_bonding_install_list(CGroupBonding *first, pid_t pid, const char *cgroup_suffix) { - CGroupBonding *b; - int r; - - LIST_FOREACH(by_unit, b, first) { - r = cgroup_bonding_install(b, pid, cgroup_suffix); - if (r < 0 && b->essential) - return r; - } - - return 0; -} - -int cgroup_bonding_set_group_access(CGroupBonding *b, mode_t mode, uid_t uid, gid_t gid) { - assert(b); - - if (!b->realized) - return -EINVAL; - - return cg_set_group_access(b->controller, b->path, mode, uid, gid); -} - -int cgroup_bonding_set_group_access_list(CGroupBonding *first, mode_t mode, uid_t uid, gid_t gid) { - CGroupBonding *b; - int r; - - LIST_FOREACH(by_unit, b, first) { - r = cgroup_bonding_set_group_access(b, mode, uid, gid); - if (r < 0) - return r; - } - - return 0; -} - -int cgroup_bonding_set_task_access(CGroupBonding *b, mode_t mode, uid_t uid, gid_t gid, int sticky) { - assert(b); - - if (!b->realized) - return -EINVAL; - - return cg_set_task_access(b->controller, b->path, mode, uid, gid, sticky); -} - -int cgroup_bonding_set_task_access_list(CGroupBonding *first, mode_t mode, uid_t uid, gid_t gid, int sticky) { - CGroupBonding *b; - int r; - - LIST_FOREACH(by_unit, b, first) { - r = cgroup_bonding_set_task_access(b, mode, uid, gid, sticky); - if (r < 0) - return r; - } - - return 0; -} - -int cgroup_bonding_kill(CGroupBonding *b, int sig, bool sigcont, bool rem, Set *s, const char *cgroup_suffix) { - char *p = NULL; - const char *path; - int r; - - assert(b); - assert(sig >= 0); - - /* Don't kill cgroups that aren't ours */ - if (!b->ours) - return 0; - - if (cgroup_suffix) { - p = strjoin(b->path, "/", cgroup_suffix, NULL); - if (!p) - return -ENOMEM; - - path = p; - } else - path = b->path; - - r = cg_kill_recursive(b->controller, path, sig, sigcont, true, rem, s); - free(p); - - return r; -} - -int cgroup_bonding_kill_list(CGroupBonding *first, int sig, bool sigcont, bool rem, Set *s, const char *cgroup_suffix) { - CGroupBonding *b; - Set *allocated_set = NULL; - int ret = -EAGAIN, r; - - if (!first) - return 0; - - if (!s) - if (!(s = allocated_set = set_new(trivial_hash_func, trivial_compare_func))) - return -ENOMEM; - - LIST_FOREACH(by_unit, b, first) { - r = cgroup_bonding_kill(b, sig, sigcont, rem, s, cgroup_suffix); - if (r < 0) { - if (r == -EAGAIN || r == -ESRCH) - continue; - - ret = r; - goto finish; - } - - if (ret < 0 || r > 0) - ret = r; - } - -finish: - if (allocated_set) - set_free(allocated_set); - - return ret; -} - -/* Returns 1 if the group is empty, 0 if it is not, -EAGAIN if we - * cannot know */ -int cgroup_bonding_is_empty(CGroupBonding *b) { - int r; - - assert(b); - - if ((r = cg_is_empty_recursive(b->controller, b->path, true)) < 0) - return r; - - /* If it is empty it is empty */ - if (r > 0) - return 1; - - /* It's not only us using this cgroup, so we just don't know */ - return b->ours ? 0 : -EAGAIN; -} - -int cgroup_bonding_is_empty_list(CGroupBonding *first) { - CGroupBonding *b; - - LIST_FOREACH(by_unit, b, first) { - int r; - - if ((r = cgroup_bonding_is_empty(b)) < 0) { - /* If this returned -EAGAIN, then we don't know if the - * group is empty, so let's see if another group can - * tell us */ - - if (r != -EAGAIN) - return r; - } else - return r; - } - - return -EAGAIN; -} - -int manager_setup_cgroup(Manager *m) { - char *current = NULL, *path = NULL; - int r; - char suffix[32]; - - assert(m); - - /* 0. Be nice to Ingo Molnar #628004 */ - if (path_is_mount_point("/sys/fs/cgroup/systemd", false) <= 0) { - log_warning("No control group support available, not creating root group."); - return 0; - } - - /* 1. Determine hierarchy */ - r = cg_get_by_pid(SYSTEMD_CGROUP_CONTROLLER, 0, ¤t); - if (r < 0) { - log_error("Cannot determine cgroup we are running in: %s", strerror(-r)); - goto finish; - } - - if (m->running_as == SYSTEMD_SYSTEM) - strcpy(suffix, "/system"); - else { - snprintf(suffix, sizeof(suffix), "/systemd-%lu", (unsigned long) getpid()); - char_array_0(suffix); - } - - free(m->cgroup_hierarchy); - if (endswith(current, suffix)) { - /* We probably got reexecuted and can continue to use our root cgroup */ - m->cgroup_hierarchy = current; - current = NULL; - - } else { - /* We need a new root cgroup */ - m->cgroup_hierarchy = NULL; - if (asprintf(&m->cgroup_hierarchy, "%s%s", streq(current, "/") ? "" : current, suffix) < 0) { - r = log_oom(); - goto finish; - } - } - - /* 2. Show data */ - r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_hierarchy, NULL, &path); - if (r < 0) { - log_error("Cannot find cgroup mount point: %s", strerror(-r)); - goto finish; - } - - log_debug("Using cgroup controller " SYSTEMD_CGROUP_CONTROLLER ". File system hierarchy is at %s.", path); - - /* 3. Install agent */ - r = cg_install_release_agent(SYSTEMD_CGROUP_CONTROLLER, SYSTEMD_CGROUP_AGENT_PATH); - if (r < 0) - log_warning("Failed to install release agent, ignoring: %s", strerror(-r)); - else if (r > 0) - log_debug("Installed release agent."); - else - log_debug("Release agent already installed."); - - /* 4. Realize the group */ - r = cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_hierarchy, 0); - if (r < 0) { - log_error("Failed to create root cgroup hierarchy: %s", strerror(-r)); - goto finish; - } - - /* 5. And pin it, so that it cannot be unmounted */ - if (m->pin_cgroupfs_fd >= 0) - close_nointr_nofail(m->pin_cgroupfs_fd); - - m->pin_cgroupfs_fd = open(path, O_RDONLY|O_CLOEXEC|O_DIRECTORY|O_NOCTTY|O_NONBLOCK); - if (r < 0) { - log_error("Failed to open pin file: %m"); - r = -errno; - goto finish; - } - - log_debug("Created root group."); - - cg_shorten_controllers(m->default_controllers); - -finish: - free(current); - free(path); - - return r; -} - -void manager_shutdown_cgroup(Manager *m, bool delete) { - assert(m); - - if (delete && m->cgroup_hierarchy) - cg_delete(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_hierarchy); - - if (m->pin_cgroupfs_fd >= 0) { - close_nointr_nofail(m->pin_cgroupfs_fd); - m->pin_cgroupfs_fd = -1; - } - - free(m->cgroup_hierarchy); - m->cgroup_hierarchy = NULL; -} - -int cgroup_bonding_get(Manager *m, const char *cgroup, CGroupBonding **bonding) { - CGroupBonding *b; - char *p; - - assert(m); - assert(cgroup); - assert(bonding); - - b = hashmap_get(m->cgroup_bondings, cgroup); - if (b) { - *bonding = b; - return 1; - } - - p = strdup(cgroup); - if (!p) - return -ENOMEM; - - for (;;) { - char *e; - - e = strrchr(p, '/'); - if (!e || e == p) { - free(p); - *bonding = NULL; - return 0; - } - - *e = 0; - - b = hashmap_get(m->cgroup_bondings, p); - if (b) { - free(p); - *bonding = b; - return 1; - } - } -} - -int cgroup_notify_empty(Manager *m, const char *group) { - CGroupBonding *l, *b; - int r; - - assert(m); - assert(group); - - r = cgroup_bonding_get(m, group, &l); - if (r <= 0) - return r; - - LIST_FOREACH(by_path, b, l) { - int t; - - if (!b->unit) - continue; - - t = cgroup_bonding_is_empty_list(b); - if (t < 0) { - - /* If we don't know, we don't know */ - if (t != -EAGAIN) - log_warning("Failed to check whether cgroup is empty: %s", strerror(errno)); - - continue; - } - - if (t > 0) { - /* If it is empty, let's delete it */ - cgroup_bonding_trim_list(b->unit->cgroup_bondings, true); - - if (UNIT_VTABLE(b->unit)->cgroup_notify_empty) - UNIT_VTABLE(b->unit)->cgroup_notify_empty(b->unit); - } - } - - return 0; -} - -Unit* cgroup_unit_by_pid(Manager *m, pid_t pid) { - CGroupBonding *l, *b; - char *group = NULL; - - assert(m); - - if (pid <= 1) - return NULL; - - if (cg_get_by_pid(SYSTEMD_CGROUP_CONTROLLER, pid, &group) < 0) - return NULL; - - l = hashmap_get(m->cgroup_bondings, group); - - if (!l) { - char *slash; - - while ((slash = strrchr(group, '/'))) { - if (slash == group) - break; - - *slash = 0; - - if ((l = hashmap_get(m->cgroup_bondings, group))) - break; - } - } - - free(group); - - LIST_FOREACH(by_path, b, l) { - - if (!b->unit) - continue; - - if (b->ours) - return b->unit; - } - - return NULL; -} - -CGroupBonding *cgroup_bonding_find_list(CGroupBonding *first, const char *controller) { - CGroupBonding *b; - - assert(controller); - - LIST_FOREACH(by_unit, b, first) - if (streq(b->controller, controller)) - return b; - - return NULL; -} - -char *cgroup_bonding_to_string(CGroupBonding *b) { - char *r; - - assert(b); - - if (asprintf(&r, "%s:%s", b->controller, b->path) < 0) - return NULL; - - return r; -} - -pid_t cgroup_bonding_search_main_pid(CGroupBonding *b) { - FILE *f; - pid_t pid = 0, npid, mypid; - - assert(b); - - if (!b->ours) - return 0; - - if (cg_enumerate_processes(b->controller, b->path, &f) < 0) - return 0; - - mypid = getpid(); - - while (cg_read_pid(f, &npid) > 0) { - pid_t ppid; - - if (npid == pid) - continue; - - /* Ignore processes that aren't our kids */ - if (get_parent_of_pid(npid, &ppid) >= 0 && ppid != mypid) - continue; - - if (pid != 0) { - /* Dang, there's more than one daemonized PID - in this group, so we don't know what process - is the main process. */ - pid = 0; - break; - } - - pid = npid; - } - - fclose(f); - - return pid; -} - -pid_t cgroup_bonding_search_main_pid_list(CGroupBonding *first) { - CGroupBonding *b; - pid_t pid; - - /* Try to find a main pid from this cgroup, but checking if - * there's only one PID in the cgroup and returning it. Later - * on we might want to add additional, smarter heuristics - * here. */ - - LIST_FOREACH(by_unit, b, first) - if ((pid = cgroup_bonding_search_main_pid(b)) != 0) - return pid; - - return 0; - -} diff --git a/src/core/cgroup.h b/src/core/cgroup.h deleted file mode 100644 index 229da52ba4..0000000000 --- a/src/core/cgroup.h +++ /dev/null @@ -1,91 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -#pragma once - -/*** - 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/>. -***/ - -typedef struct CGroupBonding CGroupBonding; - -#include "unit.h" - -/* Binds a cgroup to a name */ -struct CGroupBonding { - char *controller; - char *path; - - Unit *unit; - - /* For the Unit::cgroup_bondings list */ - LIST_FIELDS(CGroupBonding, by_unit); - - /* For the Manager::cgroup_bondings hashmap */ - LIST_FIELDS(CGroupBonding, by_path); - - /* When shutting down, remove cgroup? Are our own tasks the - * only ones in this group?*/ - bool ours:1; - - /* If we cannot create this group, or add a process to it, is this fatal? */ - bool essential:1; - - /* This cgroup is realized */ - bool realized:1; -}; - -int cgroup_bonding_realize(CGroupBonding *b); -int cgroup_bonding_realize_list(CGroupBonding *first); - -void cgroup_bonding_free(CGroupBonding *b, bool trim); -void cgroup_bonding_free_list(CGroupBonding *first, bool trim); - -int cgroup_bonding_install(CGroupBonding *b, pid_t pid, const char *suffix); -int cgroup_bonding_install_list(CGroupBonding *first, pid_t pid, const char *suffix); - -int cgroup_bonding_set_group_access(CGroupBonding *b, mode_t mode, uid_t uid, gid_t gid); -int cgroup_bonding_set_group_access_list(CGroupBonding *b, mode_t mode, uid_t uid, gid_t gid); - -int cgroup_bonding_set_task_access(CGroupBonding *b, mode_t mode, uid_t uid, gid_t gid, int sticky); -int cgroup_bonding_set_task_access_list(CGroupBonding *b, mode_t mode, uid_t uid, gid_t gid, int sticky); - -int cgroup_bonding_kill(CGroupBonding *b, int sig, bool sigcont, bool rem, Set *s, const char *suffix); -int cgroup_bonding_kill_list(CGroupBonding *first, int sig, bool sigcont, bool rem, Set *s, const char *suffix); - -void cgroup_bonding_trim(CGroupBonding *first, bool delete_root); -void cgroup_bonding_trim_list(CGroupBonding *first, bool delete_root); - -int cgroup_bonding_is_empty(CGroupBonding *b); -int cgroup_bonding_is_empty_list(CGroupBonding *first); - -CGroupBonding *cgroup_bonding_find_list(CGroupBonding *first, const char *controller); - -char *cgroup_bonding_to_string(CGroupBonding *b); - -pid_t cgroup_bonding_search_main_pid(CGroupBonding *b); -pid_t cgroup_bonding_search_main_pid_list(CGroupBonding *b); - -#include "manager.h" - -int manager_setup_cgroup(Manager *m); -void manager_shutdown_cgroup(Manager *m, bool delete); - -int cgroup_bonding_get(Manager *m, const char *cgroup, CGroupBonding **bonding); -int cgroup_notify_empty(Manager *m, const char *group); - -Unit* cgroup_unit_by_pid(Manager *m, pid_t pid); diff --git a/src/core/condition.c b/src/core/condition.c deleted file mode 100644 index 32a37ccad6..0000000000 --- a/src/core/condition.c +++ /dev/null @@ -1,370 +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 <stdlib.h> -#include <errno.h> -#include <string.h> -#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" -#include "path-util.h" - -Condition* condition_new(ConditionType type, const char *parameter, bool trigger, bool negate) { - Condition *c; - - assert(type < _CONDITION_TYPE_MAX); - - c = new0(Condition, 1); - if (!c) - return NULL; - - c->type = type; - c->trigger = trigger; - c->negate = negate; - - if (parameter) { - c->parameter = strdup(parameter); - if (!c->parameter) { - free(c); - return NULL; - } - } - - return c; -} - -void condition_free(Condition *c) { - assert(c); - - free(c->parameter); - free(c); -} - -void condition_free_list(Condition *first) { - Condition *c, *n; - - LIST_FOREACH_SAFE(conditions, c, n, first) - condition_free(c); -} - -static bool test_kernel_command_line(const char *parameter) { - char *line, *w, *state, *word = NULL; - bool equal; - int r; - size_t l, pl; - bool found = false; - - assert(parameter); - - if (detect_container(NULL) > 0) - return false; - - r = read_one_line_file("/proc/cmdline", &line); - if (r < 0) { - log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r)); - return false; - } - - equal = !!strchr(parameter, '='); - pl = strlen(parameter); - - FOREACH_WORD_QUOTED(w, l, line, state) { - - free(word); - word = strndup(w, l); - if (!word) - break; - - if (equal) { - if (streq(word, parameter)) { - found = true; - break; - } - } else { - if (startswith(word, parameter) && (word[pl] == '=' || word[pl] == 0)) { - found = true; - break; - } - } - - } - - free(word); - free(line); - - return found; -} - -static bool test_virtualization(const char *parameter) { - int b; - Virtualization v; - const char *id; - - assert(parameter); - - v = detect_virtualization(&id); - if (v < 0) { - log_warning("Failed to detect virtualization, ignoring: %s", strerror(-v)); - return false; - } - - /* First, compare with yes/no */ - b = parse_boolean(parameter); - - if (v > 0 && b > 0) - return true; - - if (v == 0 && b == 0) - return true; - - /* Then, compare categorization */ - if (v == VIRTUALIZATION_VM && streq(parameter, "vm")) - return true; - - if (v == VIRTUALIZATION_CONTAINER && streq(parameter, "container")) - return true; - - /* Finally compare id */ - return v > 0 && streq(parameter, id); -} - -static bool test_security(const char *parameter) { -#ifdef HAVE_SELINUX - if (streq(parameter, "selinux")) - return is_selinux_enabled() > 0; -#endif - return false; -} - -static bool test_capability(const char *parameter) { - cap_value_t value; - FILE *f; - char line[LINE_MAX]; - unsigned long long capabilities = (unsigned long long) -1; - - /* If it's an invalid capability, we don't have it */ - - if (cap_from_name(parameter, &value) < 0) - return false; - - /* If it's a valid capability we default to assume - * that we have it */ - - f = fopen("/proc/self/status", "re"); - if (!f) - return true; - - while (fgets(line, sizeof(line), f)) { - truncate_nl(line); - - if (startswith(line, "CapBnd:")) { - (void) sscanf(line+7, "%llx", &capabilities); - break; - } - } - - fclose(f); - - 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; -} - -bool condition_test(Condition *c) { - assert(c); - - switch(c->type) { - - case CONDITION_PATH_EXISTS: - return (access(c->parameter, F_OK) >= 0) == !c->negate; - - case CONDITION_PATH_EXISTS_GLOB: - return (glob_exists(c->parameter) > 0) == !c->negate; - - case CONDITION_PATH_IS_DIRECTORY: { - struct stat st; - - if (stat(c->parameter, &st) < 0) - return c->negate; - return S_ISDIR(st.st_mode) == !c->negate; - } - - case CONDITION_PATH_IS_SYMBOLIC_LINK: { - struct stat st; - - if (lstat(c->parameter, &st) < 0) - return c->negate; - return S_ISLNK(st.st_mode) == !c->negate; - } - - case CONDITION_PATH_IS_MOUNT_POINT: - return (path_is_mount_point(c->parameter, true) > 0) == !c->negate; - - case CONDITION_PATH_IS_READ_WRITE: - return (path_is_read_only_fs(c->parameter) > 0) == c->negate; - - case CONDITION_DIRECTORY_NOT_EMPTY: { - int k; - - k = dir_is_empty(c->parameter); - 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; - - if (stat(c->parameter, &st) < 0) - return c->negate; - - return (S_ISREG(st.st_mode) && (st.st_mode & 0111)) == !c->negate; - } - - case CONDITION_KERNEL_COMMAND_LINE: - return test_kernel_command_line(c->parameter) == !c->negate; - - case CONDITION_VIRTUALIZATION: - return test_virtualization(c->parameter) == !c->negate; - - case CONDITION_SECURITY: - return test_security(c->parameter) == !c->negate; - - case CONDITION_CAPABILITY: - return test_capability(c->parameter) == !c->negate; - - case CONDITION_HOST: - return test_host(c->parameter) == !c->negate; - - case CONDITION_NULL: - return !c->negate; - - default: - assert_not_reached("Invalid condition type."); - } -} - -bool condition_test_list(Condition *first) { - Condition *c; - int triggered = -1; - - /* If the condition list is empty, then it is true */ - if (!first) - return true; - - /* Otherwise, if all of the non-trigger conditions apply and - * if any of the trigger conditions apply (unless there are - * none) we return true */ - LIST_FOREACH(conditions, c, first) { - bool b; - - b = condition_test(c); - - if (!c->trigger && !b) - return false; - - if (c->trigger && triggered <= 0) - triggered = b; - } - - return triggered != 0; -} - -void condition_dump(Condition *c, FILE *f, const char *prefix) { - assert(c); - assert(f); - - if (!prefix) - prefix = ""; - - fprintf(f, - "%s\t%s: %s%s%s\n", - prefix, - condition_type_to_string(c->type), - c->trigger ? "|" : "", - c->negate ? "!" : "", - c->parameter); -} - -void condition_dump_list(Condition *first, FILE *f, const char *prefix) { - Condition *c; - - LIST_FOREACH(conditions, c, first) - condition_dump(c, f, prefix); -} - -static const char* const condition_type_table[_CONDITION_TYPE_MAX] = { - [CONDITION_PATH_EXISTS] = "ConditionPathExists", - [CONDITION_PATH_EXISTS_GLOB] = "ConditionPathExistsGlob", - [CONDITION_PATH_IS_DIRECTORY] = "ConditionPathIsDirectory", - [CONDITION_PATH_IS_SYMBOLIC_LINK] = "ConditionPathIsSymbolicLink", - [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_NULL] = "ConditionNull" -}; - -DEFINE_STRING_TABLE_LOOKUP(condition_type, ConditionType); diff --git a/src/core/condition.h b/src/core/condition.h deleted file mode 100644 index 03954e40b3..0000000000 --- a/src/core/condition.h +++ /dev/null @@ -1,69 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -#pragma once - -/*** - 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 <stdbool.h> - -#include "list.h" - -typedef enum ConditionType { - CONDITION_PATH_EXISTS, - CONDITION_PATH_EXISTS_GLOB, - CONDITION_PATH_IS_DIRECTORY, - CONDITION_PATH_IS_SYMBOLIC_LINK, - 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_NULL, - _CONDITION_TYPE_MAX, - _CONDITION_TYPE_INVALID = -1 -} ConditionType; - -typedef struct Condition { - ConditionType type; - char *parameter; - - bool trigger:1; - bool negate:1; - - LIST_FIELDS(struct Condition, conditions); -} Condition; - -Condition* condition_new(ConditionType type, const char *parameter, bool trigger, bool negate); -void condition_free(Condition *c); -void condition_free_list(Condition *c); - -bool condition_test(Condition *c); -bool condition_test_list(Condition *c); - -void condition_dump(Condition *c, FILE *f, const char *prefix); -void condition_dump_list(Condition *c, FILE *f, const char *prefix); - -const char* condition_type_to_string(ConditionType t); -int condition_type_from_string(const char *s); diff --git a/src/core/dbus-automount.c b/src/core/dbus-automount.c deleted file mode 100644 index 060cbf7707..0000000000 --- a/src/core/dbus-automount.c +++ /dev/null @@ -1,75 +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 "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" \ - " <property name=\"Where\" type=\"s\" access=\"read\"/>\n" \ - " <property name=\"DirectoryMode\" type=\"u\" access=\"read\"/>\n" \ - " <property name=\"Result\" type=\"s\" access=\"read\"/>\n" \ - " </interface>\n" - -#define INTROSPECTION \ - DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \ - "<node>\n" \ - BUS_UNIT_INTERFACE \ - BUS_AUTOMOUNT_INTERFACE \ - BUS_PROPERTIES_INTERFACE \ - BUS_PEER_INTERFACE \ - BUS_INTROSPECTABLE_INTERFACE \ - "</node>\n" - -#define INTERFACES_LIST \ - BUS_UNIT_INTERFACES_LIST \ - "org.freedesktop.systemd1.Automount\0" - -const char bus_automount_interface[] _introspect_("Automount") = BUS_AUTOMOUNT_INTERFACE; - -const char bus_automount_invalidating_properties[] = - "Result\0"; - -static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_automount_append_automount_result, automount_result, AutomountResult); - -static const BusProperty bus_automount_properties[] = { - { "Where", bus_property_append_string, "s", offsetof(Automount, where), true }, - { "DirectoryMode", bus_property_append_mode, "u", offsetof(Automount, directory_mode) }, - { "Result", bus_automount_append_automount_result, "s", offsetof(Automount, result) }, - { NULL, } -}; - -DBusHandlerResult bus_automount_message_handler(Unit *u, DBusConnection *c, DBusMessage *message) { - Automount *am = AUTOMOUNT(u); - const BusBoundProperties bps[] = { - { "org.freedesktop.systemd1.Unit", bus_unit_properties, u }, - { "org.freedesktop.systemd1.Automount", bus_automount_properties, am }, - { 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-automount.h b/src/core/dbus-automount.h deleted file mode 100644 index b338e25fc1..0000000000 --- a/src/core/dbus-automount.h +++ /dev/null @@ -1,31 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -#pragma once - -/*** - 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 <dbus/dbus.h> - -#include "unit.h" - -DBusHandlerResult bus_automount_message_handler(Unit *u, DBusConnection *c, DBusMessage *message); - -extern const char bus_automount_interface[]; -extern const char bus_automount_invalidating_properties[]; diff --git a/src/core/dbus-device.c b/src/core/dbus-device.c deleted file mode 100644 index dbd91fe3db..0000000000 --- a/src/core/dbus-device.c +++ /dev/null @@ -1,68 +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 "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" \ - " <property name=\"SysFSPath\" type=\"s\" access=\"read\"/>\n" \ - " </interface>\n" - -#define INTROSPECTION \ - DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \ - "<node>\n" \ - BUS_UNIT_INTERFACE \ - BUS_DEVICE_INTERFACE \ - BUS_PROPERTIES_INTERFACE \ - BUS_PEER_INTERFACE \ - BUS_INTROSPECTABLE_INTERFACE \ - "</node>\n" - -#define INTERFACES_LIST \ - BUS_UNIT_INTERFACES_LIST \ - "org.freedesktop.systemd1.Device\0" - -const char bus_device_interface[] _introspect_("Device") = BUS_DEVICE_INTERFACE; - -const char bus_device_invalidating_properties[] = - "SysFSPath\0"; - -static const BusProperty bus_device_properties[] = { - { "SysFSPath", bus_property_append_string, "s", offsetof(Device, sysfs), true }, - { NULL, } -}; - - -DBusHandlerResult bus_device_message_handler(Unit *u, DBusConnection *c, DBusMessage *message) { - Device *d = DEVICE(u); - const BusBoundProperties bps[] = { - { "org.freedesktop.systemd1.Unit", bus_unit_properties, u }, - { "org.freedesktop.systemd1.Device", bus_device_properties, d }, - { 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.h b/src/core/dbus-device.h deleted file mode 100644 index 311e0685d1..0000000000 --- a/src/core/dbus-device.h +++ /dev/null @@ -1,31 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -#pragma once - -/*** - 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 <dbus/dbus.h> - -#include "unit.h" - -DBusHandlerResult bus_device_message_handler(Unit *u, DBusConnection *c, DBusMessage *message); - -extern const char bus_device_interface[]; -extern const char bus_device_invalidating_properties[]; diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c deleted file mode 100644 index e815cb58e4..0000000000 --- a/src/core/dbus-execute.c +++ /dev/null @@ -1,439 +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 <dbus/dbus.h> -#include <sys/prctl.h> - -#include "dbus-execute.h" -#include "missing.h" -#include "ioprio.h" -#include "strv.h" -#include "dbus-common.h" -#include "syscall-list.h" - -DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_execute_append_input, exec_input, ExecInput); -DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_execute_append_output, exec_output, ExecOutput); - -int bus_execute_append_env_files(DBusMessageIter *i, const char *property, void *data) { - char **env_files = data, **j; - DBusMessageIter sub, sub2; - - assert(i); - assert(property); - - if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "(sb)", &sub)) - return -ENOMEM; - - STRV_FOREACH(j, env_files) { - dbus_bool_t b = false; - char *fn = *j; - - if (fn[0] == '-') { - b = true; - fn++; - } - - if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) || - !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &fn) || - !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_BOOLEAN, &b) || - !dbus_message_iter_close_container(&sub, &sub2)) - return -ENOMEM; - } - - if (!dbus_message_iter_close_container(i, &sub)) - return -ENOMEM; - - return 0; -} - -int bus_execute_append_oom_score_adjust(DBusMessageIter *i, const char *property, void *data) { - ExecContext *c = data; - int32_t n; - - assert(i); - assert(property); - assert(c); - - if (c->oom_score_adjust_set) - n = c->oom_score_adjust; - else { - char *t; - - n = 0; - if (read_one_line_file("/proc/self/oom_score_adj", &t) >= 0) { - safe_atoi(t, &n); - free(t); - } - } - - if (!dbus_message_iter_append_basic(i, DBUS_TYPE_INT32, &n)) - return -ENOMEM; - - return 0; -} - -int bus_execute_append_nice(DBusMessageIter *i, const char *property, void *data) { - ExecContext *c = data; - int32_t n; - - assert(i); - assert(property); - assert(c); - - if (c->nice_set) - n = c->nice; - else - n = getpriority(PRIO_PROCESS, 0); - - if (!dbus_message_iter_append_basic(i, DBUS_TYPE_INT32, &n)) - return -ENOMEM; - - return 0; -} - -int bus_execute_append_ioprio(DBusMessageIter *i, const char *property, void *data) { - ExecContext *c = data; - int32_t n; - - assert(i); - assert(property); - assert(c); - - if (c->ioprio_set) - n = c->ioprio; - else - n = ioprio_get(IOPRIO_WHO_PROCESS, 0); - - if (!dbus_message_iter_append_basic(i, DBUS_TYPE_INT32, &n)) - return -ENOMEM; - - return 0; -} - -int bus_execute_append_cpu_sched_policy(DBusMessageIter *i, const char *property, void *data) { - ExecContext *c = data; - int32_t n; - - assert(i); - assert(property); - assert(c); - - if (c->cpu_sched_set) - n = c->cpu_sched_policy; - else - n = sched_getscheduler(0); - - if (!dbus_message_iter_append_basic(i, DBUS_TYPE_INT32, &n)) - return -ENOMEM; - - return 0; -} - -int bus_execute_append_cpu_sched_priority(DBusMessageIter *i, const char *property, void *data) { - ExecContext *c = data; - int32_t n; - - assert(i); - assert(property); - assert(c); - - if (c->cpu_sched_set) - n = c->cpu_sched_priority; - else { - struct sched_param p; - n = 0; - - zero(p); - if (sched_getparam(0, &p) >= 0) - n = p.sched_priority; - } - - if (!dbus_message_iter_append_basic(i, DBUS_TYPE_INT32, &n)) - return -ENOMEM; - - return 0; -} - -int bus_execute_append_affinity(DBusMessageIter *i, const char *property, void *data) { - ExecContext *c = data; - dbus_bool_t b; - DBusMessageIter sub; - - assert(i); - assert(property); - assert(c); - - if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "y", &sub)) - return -ENOMEM; - - if (c->cpuset) - b = dbus_message_iter_append_fixed_array(&sub, DBUS_TYPE_BYTE, &c->cpuset, CPU_ALLOC_SIZE(c->cpuset_ncpus)); - else - b = dbus_message_iter_append_fixed_array(&sub, DBUS_TYPE_BYTE, &c->cpuset, 0); - - if (!b) - return -ENOMEM; - - if (!dbus_message_iter_close_container(i, &sub)) - return -ENOMEM; - - return 0; -} - -int bus_execute_append_timer_slack_nsec(DBusMessageIter *i, const char *property, void *data) { - ExecContext *c = data; - uint64_t u; - - assert(i); - assert(property); - assert(c); - - if (c->timer_slack_nsec != (nsec_t) -1) - u = (uint64_t) c->timer_slack_nsec; - else - u = (uint64_t) prctl(PR_GET_TIMERSLACK); - - if (!dbus_message_iter_append_basic(i, DBUS_TYPE_UINT64, &u)) - return -ENOMEM; - - return 0; -} - -int bus_execute_append_capability_bs(DBusMessageIter *i, const char *property, void *data) { - ExecContext *c = data; - uint64_t normal, inverted; - - assert(i); - assert(property); - assert(c); - - /* We store this negated internally, to match the kernel, but - * we expose it normalized. */ - - normal = *(uint64_t*) data; - inverted = ~normal; - - return bus_property_append_uint64(i, property, &inverted); -} - -int bus_execute_append_capabilities(DBusMessageIter *i, const char *property, void *data) { - ExecContext *c = data; - char *t = NULL; - const char *s; - dbus_bool_t b; - - assert(i); - assert(property); - assert(c); - - if (c->capabilities) - s = t = cap_to_text(c->capabilities, NULL); - else - s = ""; - - if (!s) - return -ENOMEM; - - b = dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &s); - - if (t) - cap_free(t); - - if (!b) - return -ENOMEM; - - return 0; -} - -int bus_execute_append_rlimits(DBusMessageIter *i, const char *property, void *data) { - ExecContext *c = data; - int r; - uint64_t u; - - assert(i); - assert(property); - assert(c); - - assert_se((r = rlimit_from_string(property)) >= 0); - - if (c->rlimit[r]) - u = (uint64_t) c->rlimit[r]->rlim_max; - else { - struct rlimit rl; - - zero(rl); - getrlimit(r, &rl); - - u = (uint64_t) rl.rlim_max; - } - - if (!dbus_message_iter_append_basic(i, DBUS_TYPE_UINT64, &u)) - return -ENOMEM; - - return 0; -} - -int bus_execute_append_command(DBusMessageIter *i, const char *property, void *data) { - ExecCommand *c = data; - DBusMessageIter sub, sub2, sub3; - - assert(i); - assert(property); - - if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "(sasbttttuii)", &sub)) - return -ENOMEM; - - LIST_FOREACH(command, c, c) { - char **l; - uint32_t pid; - int32_t code, status; - dbus_bool_t b; - - if (!c->path) - continue; - - if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) || - !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &c->path) || - !dbus_message_iter_open_container(&sub2, DBUS_TYPE_ARRAY, "s", &sub3)) - return -ENOMEM; - - STRV_FOREACH(l, c->argv) - if (!dbus_message_iter_append_basic(&sub3, DBUS_TYPE_STRING, l)) - return -ENOMEM; - - pid = (uint32_t) c->exec_status.pid; - code = (int32_t) c->exec_status.code; - status = (int32_t) c->exec_status.status; - - b = !!c->ignore; - - if (!dbus_message_iter_close_container(&sub2, &sub3) || - !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_BOOLEAN, &b) || - !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_UINT64, &c->exec_status.start_timestamp.realtime) || - !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_UINT64, &c->exec_status.start_timestamp.monotonic) || - !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_UINT64, &c->exec_status.exit_timestamp.realtime) || - !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_UINT64, &c->exec_status.exit_timestamp.monotonic) || - !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_UINT32, &pid) || - !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_INT32, &code) || - !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_INT32, &status)) - return -ENOMEM; - - if (!dbus_message_iter_close_container(&sub, &sub2)) - return -ENOMEM; - } - - if (!dbus_message_iter_close_container(i, &sub)) - return -ENOMEM; - - return 0; -} - -int bus_execute_append_syscall_filter(DBusMessageIter *i, const char *property, void *data) { - ExecContext *c = data; - dbus_bool_t b; - DBusMessageIter sub; - - assert(i); - assert(property); - assert(c); - - if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "u", &sub)) - return -ENOMEM; - - if (c->syscall_filter) - b = dbus_message_iter_append_fixed_array(&sub, DBUS_TYPE_UINT32, &c->syscall_filter, (syscall_max() + 31) >> 4); - else - b = dbus_message_iter_append_fixed_array(&sub, DBUS_TYPE_UINT32, &c->syscall_filter, 0); - - if (!b) - return -ENOMEM; - - if (!dbus_message_iter_close_container(i, &sub)) - return -ENOMEM; - - return 0; -} - -const BusProperty bus_exec_context_properties[] = { - { "Environment", bus_property_append_strv, "as", offsetof(ExecContext, environment), true }, - { "EnvironmentFiles", bus_execute_append_env_files, "a(sb)", offsetof(ExecContext, environment_files), true }, - { "UMask", bus_property_append_mode, "u", offsetof(ExecContext, umask) }, - { "LimitCPU", bus_execute_append_rlimits, "t", 0 }, - { "LimitFSIZE", bus_execute_append_rlimits, "t", 0 }, - { "LimitDATA", bus_execute_append_rlimits, "t", 0 }, - { "LimitSTACK", bus_execute_append_rlimits, "t", 0 }, - { "LimitCORE", bus_execute_append_rlimits, "t", 0 }, - { "LimitRSS", bus_execute_append_rlimits, "t", 0 }, - { "LimitNOFILE", bus_execute_append_rlimits, "t", 0 }, - { "LimitAS", bus_execute_append_rlimits, "t", 0 }, - { "LimitNPROC", bus_execute_append_rlimits, "t", 0 }, - { "LimitMEMLOCK", bus_execute_append_rlimits, "t", 0 }, - { "LimitLOCKS", bus_execute_append_rlimits, "t", 0 }, - { "LimitSIGPENDING", bus_execute_append_rlimits, "t", 0 }, - { "LimitMSGQUEUE", bus_execute_append_rlimits, "t", 0 }, - { "LimitNICE", bus_execute_append_rlimits, "t", 0 }, - { "LimitRTPRIO", bus_execute_append_rlimits, "t", 0 }, - { "LimitRTTIME", bus_execute_append_rlimits, "t", 0 }, - { "WorkingDirectory", bus_property_append_string, "s", offsetof(ExecContext, working_directory), true }, - { "RootDirectory", bus_property_append_string, "s", offsetof(ExecContext, root_directory), true }, - { "OOMScoreAdjust", bus_execute_append_oom_score_adjust, "i", 0 }, - { "Nice", bus_execute_append_nice, "i", 0 }, - { "IOScheduling", bus_execute_append_ioprio, "i", 0 }, - { "CPUSchedulingPolicy", bus_execute_append_cpu_sched_policy, "i", 0 }, - { "CPUSchedulingPriority", bus_execute_append_cpu_sched_priority, "i", 0 }, - { "CPUAffinity", bus_execute_append_affinity, "ay", 0 }, - { "TimerSlackNSec", bus_execute_append_timer_slack_nsec, "t", 0 }, - { "CPUSchedulingResetOnFork", bus_property_append_bool, "b", offsetof(ExecContext, cpu_sched_reset_on_fork) }, - { "NonBlocking", bus_property_append_bool, "b", offsetof(ExecContext, non_blocking) }, - { "StandardInput", bus_execute_append_input, "s", offsetof(ExecContext, std_input) }, - { "StandardOutput", bus_execute_append_output, "s", offsetof(ExecContext, std_output) }, - { "StandardError", bus_execute_append_output, "s", offsetof(ExecContext, std_error) }, - { "TTYPath", bus_property_append_string, "s", offsetof(ExecContext, tty_path), true }, - { "TTYReset", bus_property_append_bool, "b", offsetof(ExecContext, tty_reset) }, - { "TTYVHangup", bus_property_append_bool, "b", offsetof(ExecContext, tty_vhangup) }, - { "TTYVTDisallocate", bus_property_append_bool, "b", offsetof(ExecContext, tty_vt_disallocate) }, - { "SyslogPriority", bus_property_append_int, "i", offsetof(ExecContext, syslog_priority) }, - { "SyslogIdentifier", bus_property_append_string, "s", offsetof(ExecContext, syslog_identifier), true }, - { "SyslogLevelPrefix", bus_property_append_bool, "b", offsetof(ExecContext, syslog_level_prefix) }, - { "Capabilities", bus_execute_append_capabilities, "s", 0 }, - { "SecureBits", bus_property_append_int, "i", offsetof(ExecContext, secure_bits) }, - { "CapabilityBoundingSet", bus_execute_append_capability_bs, "t", offsetof(ExecContext, capability_bounding_set_drop) }, - { "User", bus_property_append_string, "s", offsetof(ExecContext, user), true }, - { "Group", bus_property_append_string, "s", offsetof(ExecContext, group), true }, - { "SupplementaryGroups", bus_property_append_strv, "as", offsetof(ExecContext, supplementary_groups), true }, - { "TCPWrapName", bus_property_append_string, "s", offsetof(ExecContext, tcpwrap_name), true }, - { "PAMName", bus_property_append_string, "s", offsetof(ExecContext, pam_name), true }, - { "ReadWriteDirectories", bus_property_append_strv, "as", offsetof(ExecContext, read_write_dirs), true }, - { "ReadOnlyDirectories", bus_property_append_strv, "as", offsetof(ExecContext, read_only_dirs), true }, - { "InaccessibleDirectories", bus_property_append_strv, "as", offsetof(ExecContext, inaccessible_dirs), true }, - { "MountFlags", bus_property_append_ul, "t", offsetof(ExecContext, mount_flags) }, - { "PrivateTmp", bus_property_append_bool, "b", offsetof(ExecContext, private_tmp) }, - { "PrivateNetwork", bus_property_append_bool, "b", offsetof(ExecContext, private_network) }, - { "SameProcessGroup", bus_property_append_bool, "b", offsetof(ExecContext, same_pgrp) }, - { "UtmpIdentifier", bus_property_append_string, "s", offsetof(ExecContext, utmp_id), true }, - { "ControlGroupModify", bus_property_append_bool, "b", offsetof(ExecContext, control_group_modify) }, - { "ControlGroupPersistent", bus_property_append_tristate_false, "b", offsetof(ExecContext, control_group_persistent) }, - { "IgnoreSIGPIPE", bus_property_append_bool, "b", offsetof(ExecContext, ignore_sigpipe) }, - { "NoNewPrivileges", bus_property_append_bool, "b", offsetof(ExecContext, no_new_privileges) }, - { "SystemCallFilter", bus_execute_append_syscall_filter, "au", 0 }, - { NULL, } -}; diff --git a/src/core/dbus-execute.h b/src/core/dbus-execute.h deleted file mode 100644 index eaa1b73e69..0000000000 --- a/src/core/dbus-execute.h +++ /dev/null @@ -1,124 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -#pragma once - -/*** - 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 <dbus/dbus.h> - -#include "manager.h" -#include "dbus-common.h" - -#define BUS_EXEC_STATUS_INTERFACE(prefix) \ - " <property name=\"" prefix "StartTimestamp\" type=\"t\" access=\"read\"/>\n" \ - " <property name=\"" prefix "StartTimestampMonotonic\" type=\"t\" access=\"read\"/>\n" \ - " <property name=\"" prefix "ExitTimestamp\" type=\"t\" access=\"read\"/>\n" \ - " <property name=\"" prefix "ExitTimestampMonotonic\" type=\"t\" access=\"read\"/>\n" \ - " <property name=\"" prefix "PID\" type=\"u\" access=\"read\"/>\n" \ - " <property name=\"" prefix "Code\" type=\"i\" access=\"read\"/>\n" \ - " <property name=\"" prefix "Status\" type=\"i\" access=\"read\"/>\n" - -#define BUS_EXEC_CONTEXT_INTERFACE \ - " <property name=\"Environment\" type=\"as\" access=\"read\"/>\n" \ - " <property name=\"UMask\" type=\"u\" access=\"read\"/>\n" \ - " <property name=\"LimitCPU\" type=\"t\" access=\"read\"/>\n" \ - " <property name=\"LimitFSIZE\" type=\"t\" access=\"read\"/>\n" \ - " <property name=\"LimitDATA\" type=\"t\" access=\"read\"/>\n" \ - " <property name=\"LimitSTACK\" type=\"t\" access=\"read\"/>\n" \ - " <property name=\"LimitCORE\" type=\"t\" access=\"read\"/>\n" \ - " <property name=\"LimitRSS\" type=\"t\" access=\"read\"/>\n" \ - " <property name=\"LimitNOFILE\" type=\"t\" access=\"read\"/>\n" \ - " <property name=\"LimitAS\" type=\"t\" access=\"read\"/>\n" \ - " <property name=\"LimitNPROC\" type=\"t\" access=\"read\"/>\n" \ - " <property name=\"LimitMEMLOCK\" type=\"t\" access=\"read\"/>\n" \ - " <property name=\"LimitLOCKS\" type=\"t\" access=\"read\"/>\n" \ - " <property name=\"LimitSIGPENDING\" type=\"t\" access=\"read\"/>\n" \ - " <property name=\"LimitMSGQUEUE\" type=\"t\" access=\"read\"/>\n" \ - " <property name=\"LimitNICE\" type=\"t\" access=\"read\"/>\n" \ - " <property name=\"LimitRTPRIO\" type=\"t\" access=\"read\"/>\n" \ - " <property name=\"LimitRTTIME\" type=\"t\" access=\"read\"/>\n" \ - " <property name=\"WorkingDirectory\" type=\"s\" access=\"read\"/>\n" \ - " <property name=\"RootDirectory\" type=\"s\" access=\"read\"/>\n" \ - " <property name=\"OOMScoreAdjust\" type=\"i\" access=\"read\"/>\n" \ - " <property name=\"Nice\" type=\"i\" access=\"read\"/>\n" \ - " <property name=\"IOScheduling\" type=\"i\" access=\"read\"/>\n" \ - " <property name=\"CPUSchedulingPolicy\" type=\"i\" access=\"read\"/>\n" \ - " <property name=\"CPUSchedulingPriority\" type=\"i\" access=\"read\"/>\n" \ - " <property name=\"CPUAffinity\" type=\"ay\" access=\"read\"/>\n" \ - " <property name=\"TimerSlackNS\" type=\"t\" access=\"read\"/>\n" \ - " <property name=\"CPUSchedulingResetOnFork\" type=\"b\" access=\"read\"/>\n" \ - " <property name=\"NonBlocking\" type=\"b\" access=\"read\"/>\n" \ - " <property name=\"StandardInput\" type=\"s\" access=\"read\"/>\n" \ - " <property name=\"StandardOutput\" type=\"s\" access=\"read\"/>\n" \ - " <property name=\"StandardError\" type=\"s\" access=\"read\"/>\n" \ - " <property name=\"TTYPath\" type=\"s\" access=\"read\"/>\n" \ - " <property name=\"TTYReset\" type=\"b\" access=\"read\"/>\n" \ - " <property name=\"TTYVHangup\" type=\"b\" access=\"read\"/>\n" \ - " <property name=\"TTYVTDisallocate\" type=\"b\" access=\"read\"/>\n" \ - " <property name=\"SyslogPriority\" type=\"i\" access=\"read\"/>\n" \ - " <property name=\"SyslogIdentifier\" type=\"s\" access=\"read\"/>\n" \ - " <property name=\"SyslogLevelPrefix\" type=\"b\" access=\"read\"/>\n" \ - " <property name=\"Capabilities\" type=\"s\" access=\"read\"/>\n" \ - " <property name=\"SecureBits\" type=\"i\" access=\"read\"/>\n" \ - " <property name=\"CapabilityBoundingSet\" type=\"t\" access=\"read\"/>\n" \ - " <property name=\"User\" type=\"s\" access=\"read\"/>\n" \ - " <property name=\"Group\" type=\"s\" access=\"read\"/>\n" \ - " <property name=\"SupplementaryGroups\" type=\"as\" access=\"read\"/>\n" \ - " <property name=\"TCPWrapName\" type=\"s\" access=\"read\"/>\n" \ - " <property name=\"PAMName\" type=\"s\" access=\"read\"/>\n" \ - " <property name=\"ReadWriteDirectories\" type=\"as\" access=\"read\"/>\n" \ - " <property name=\"ReadOnlyDirectories\" type=\"as\" access=\"read\"/>\n" \ - " <property name=\"InaccessibleDirectories\" type=\"as\" access=\"read\"/>\n" \ - " <property name=\"MountFlags\" type=\"t\" access=\"read\"/>\n" \ - " <property name=\"PrivateTmp\" type=\"b\" access=\"read\"/>\n" \ - " <property name=\"SameProcessGroup\" type=\"b\" access=\"read\"/>\n" \ - " <property name=\"KillMode\" type=\"s\" access=\"read\"/>\n" \ - " <property name=\"KillSignal\" type=\"i\" access=\"read\"/>\n" \ - " <property name=\"UtmpIdentifier\" type=\"s\" access=\"read\"/>\n" \ - " <property name=\"ControlGroupModify\" type=\"b\" access=\"read\"/>\n" \ - " <property name=\"ControlGroupPersistent\" type=\"b\" access=\"read\"/>\n" \ - " <property name=\"PrivateNetwork\" type=\"b\" access=\"read\"/>\n" \ - " <property name=\"IgnoreSIGPIPE\" type=\"b\" access=\"read\"/>\n" \ - " <property name=\"NoNewPrivileges\" type=\"b\" access=\"read\"/>\n" \ - " <property name=\"SystemCallFilter\" type=\"au\" access=\"read\"/>\n" - -#define BUS_EXEC_COMMAND_INTERFACE(name) \ - " <property name=\"" name "\" type=\"a(sasbttuii)\" access=\"read\"/>\n" - -extern const BusProperty bus_exec_context_properties[]; - -#define BUS_EXEC_COMMAND_PROPERTY(name, command, indirect) \ - { name, bus_execute_append_command, "a(sasbttttuii)", (command), (indirect), NULL } - -int bus_execute_append_output(DBusMessageIter *i, const char *property, void *data); -int bus_execute_append_input(DBusMessageIter *i, const char *property, void *data); -int bus_execute_append_oom_score_adjust(DBusMessageIter *i, const char *property, void *data); -int bus_execute_append_nice(DBusMessageIter *i, const char *property, void *data); -int bus_execute_append_ioprio(DBusMessageIter *i, const char *property, void *data); -int bus_execute_append_cpu_sched_policy(DBusMessageIter *i, const char *property, void *data); -int bus_execute_append_cpu_sched_priority(DBusMessageIter *i, const char *property, void *data); -int bus_execute_append_affinity(DBusMessageIter *i, const char *property, void *data); -int bus_execute_append_timer_slack_nsec(DBusMessageIter *i, const char *property, void *data); -int bus_execute_append_capabilities(DBusMessageIter *i, const char *property, void *data); -int bus_execute_append_capability_bs(DBusMessageIter *i, const char *property, void *data); -int bus_execute_append_rlimits(DBusMessageIter *i, const char *property, void *data); -int bus_execute_append_command(DBusMessageIter *u, const char *property, void *data); -int bus_execute_append_env_files(DBusMessageIter *i, const char *property, void *data); -int bus_execute_append_syscall_filter(DBusMessageIter *i, const char *property, void *data); diff --git a/src/core/dbus-job.c b/src/core/dbus-job.c deleted file mode 100644 index fdc1dce177..0000000000 --- a/src/core/dbus-job.c +++ /dev/null @@ -1,378 +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 "dbus.h" -#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" \ - " <method name=\"Cancel\"/>\n" \ - " <property name=\"Id\" type=\"u\" access=\"read\"/>\n" \ - " <property name=\"Unit\" type=\"(so)\" access=\"read\"/>\n" \ - " <property name=\"JobType\" type=\"s\" access=\"read\"/>\n" \ - " <property name=\"State\" type=\"s\" access=\"read\"/>\n" \ - " </interface>\n" - -#define INTROSPECTION \ - DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \ - "<node>\n" \ - BUS_JOB_INTERFACE \ - BUS_PROPERTIES_INTERFACE \ - BUS_PEER_INTERFACE \ - BUS_INTROSPECTABLE_INTERFACE \ - "</node>\n" - -const char bus_job_interface[] _introspect_("Job") = BUS_JOB_INTERFACE; - -#define INTERFACES_LIST \ - BUS_GENERIC_INTERFACES_LIST \ - "org.freedesktop.systemd1.Job\0" - -#define INVALIDATING_PROPERTIES \ - "State\0" - -static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_job_append_state, job_state, JobState); -static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_job_append_type, job_type, JobType); - -static int bus_job_append_unit(DBusMessageIter *i, const char *property, void *data) { - Job *j = data; - DBusMessageIter sub; - char *p; - - assert(i); - assert(property); - assert(j); - - if (!dbus_message_iter_open_container(i, DBUS_TYPE_STRUCT, NULL, &sub)) - return -ENOMEM; - - p = unit_dbus_path(j->unit); - if (!p) - return -ENOMEM; - - if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &j->unit->id) || - !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &p)) { - free(p); - return -ENOMEM; - } - - free(p); - - if (!dbus_message_iter_close_container(i, &sub)) - return -ENOMEM; - - return 0; -} - -static const BusProperty bus_job_properties[] = { - { "Id", bus_property_append_uint32, "u", offsetof(Job, id) }, - { "State", bus_job_append_state, "s", offsetof(Job, state) }, - { "JobType", bus_job_append_type, "s", offsetof(Job, type) }, - { "Unit", bus_job_append_unit, "(so)", 0 }, - { NULL, } -}; - -static DBusHandlerResult bus_job_message_dispatch(Job *j, DBusConnection *connection, DBusMessage *message) { - _cleanup_dbus_message_unref_ DBusMessage *reply = NULL; - - if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Job", "Cancel")) { - - 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, } - }; - - SELINUX_UNIT_ACCESS_CHECK(j->unit, connection, message, "status"); - - return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, bps); - } - - if (!dbus_connection_send(connection, reply, NULL)) - 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; - _cleanup_dbus_message_unref_ DBusMessage *reply = NULL; - - assert(connection); - assert(message); - assert(m); - - if (streq(dbus_message_get_path(message), "/org/freedesktop/systemd1/job")) { - /* Be nice to gdbus and return introspection data for our mid-level paths */ - - if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect")) { - char *introspection = NULL; - FILE *f; - Iterator i; - size_t size; - - 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 - * relying on bus_default_message_handler() because we - * need to generate our introspection string - * dynamically. */ - - f = open_memstream(&introspection, &size); - if (!f) - goto oom; - - fputs(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE - "<node>\n", f); - - fputs(BUS_INTROSPECTABLE_INTERFACE, f); - fputs(BUS_PEER_INTERFACE, f); - - HASHMAP_FOREACH(j, m->jobs, i) - fprintf(f, "<node name=\"job/%lu\"/>", (unsigned long) j->id); - - fputs("</node>\n", f); - - if (ferror(f)) { - fclose(f); - free(introspection); - goto oom; - } - - fclose(f); - - if (!introspection) - goto oom; - - if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &introspection, DBUS_TYPE_INVALID)) { - free(introspection); - goto oom; - } - - free(introspection); - - if (!dbus_connection_send(connection, reply, NULL)) - goto oom; - - return DBUS_HANDLER_RESULT_HANDLED; - } - - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - } - - r = manager_get_job_from_dbus_path(m, dbus_message_get_path(message), &j); - if (r == -ENOMEM) - goto oom; - 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); - } - if (r < 0) - return bus_send_error_reply(connection, message, NULL, r); - - return bus_job_message_dispatch(j, connection, message); - -oom: - return DBUS_HANDLER_RESULT_NEED_MEMORY; -} - -const DBusObjectPathVTable bus_job_vtable = { - .message_function = bus_job_message_handler -}; - -static int job_send_message(Job *j, DBusMessage* (*new_message)(Job *j)) { - DBusMessage *m = NULL; - int r; - - assert(j); - assert(new_message); - - if (bus_has_subscriber(j->manager) || j->forgot_bus_clients) { - m = new_message(j); - if (!m) - goto oom; - r = bus_broadcast(j->manager, m); - dbus_message_unref(m); - if (r < 0) - return r; - - } else { - /* If nobody is subscribed, we just send the message - * to the client(s) which created the job */ - JobBusClient *cl; - assert(j->bus_client_list); - LIST_FOREACH(client, cl, j->bus_client_list) { - assert(cl->bus); - - m = new_message(j); - if (!m) - goto oom; - - if (!dbus_message_set_destination(m, cl->name)) - goto oom; - - if (!dbus_connection_send(cl->bus, m, NULL)) - goto oom; - - dbus_message_unref(m); - m = NULL; - } - } - - return 0; -oom: - if (m) - dbus_message_unref(m); - return -ENOMEM; -} - -static DBusMessage* new_change_signal_message(Job *j) { - DBusMessage *m = NULL; - char *p = NULL; - - p = job_dbus_path(j); - if (!p) - goto oom; - - if (j->sent_dbus_new_signal) { - /* Send a properties changed signal */ - m = bus_properties_changed_new(p, "org.freedesktop.systemd1.Job", INVALIDATING_PROPERTIES); - if (!m) - goto oom; - - } else { - /* Send a new signal */ - - m = dbus_message_new_signal("/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "JobNew"); - if (!m) - goto oom; - - if (!dbus_message_append_args(m, - DBUS_TYPE_UINT32, &j->id, - DBUS_TYPE_OBJECT_PATH, &p, - DBUS_TYPE_STRING, &j->unit->id, - DBUS_TYPE_INVALID)) - goto oom; - } - - return m; - -oom: - if (m) - dbus_message_unref(m); - free(p); - return NULL; -} - -static DBusMessage* new_removed_signal_message(Job *j) { - DBusMessage *m = NULL; - char *p = NULL; - const char *r; - - p = job_dbus_path(j); - if (!p) - goto oom; - - m = dbus_message_new_signal("/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "JobRemoved"); - if (!m) - goto oom; - - r = job_result_to_string(j->result); - - if (!dbus_message_append_args(m, - DBUS_TYPE_UINT32, &j->id, - DBUS_TYPE_OBJECT_PATH, &p, - DBUS_TYPE_STRING, &j->unit->id, - DBUS_TYPE_STRING, &r, - DBUS_TYPE_INVALID)) - goto oom; - - return m; - -oom: - if (m) - dbus_message_unref(m); - free(p); - return NULL; -} - -void bus_job_send_change_signal(Job *j) { - assert(j); - - if (j->in_dbus_queue) { - LIST_REMOVE(Job, dbus_queue, j->manager->dbus_job_queue, j); - j->in_dbus_queue = false; - } - - if (!bus_has_subscriber(j->manager) && !j->bus_client_list && !j->forgot_bus_clients) { - j->sent_dbus_new_signal = true; - return; - } - - if (job_send_message(j, new_change_signal_message) < 0) - goto oom; - - j->sent_dbus_new_signal = true; - - return; - -oom: - log_error("Failed to allocate job change signal."); -} - -void bus_job_send_removed_signal(Job *j) { - assert(j); - - if (!bus_has_subscriber(j->manager) && !j->bus_client_list && !j->forgot_bus_clients) - return; - - if (!j->sent_dbus_new_signal) - bus_job_send_change_signal(j); - - if (job_send_message(j, new_removed_signal_message) < 0) - goto oom; - - return; - -oom: - log_error("Failed to allocate job remove signal."); -} diff --git a/src/core/dbus-job.h b/src/core/dbus-job.h deleted file mode 100644 index a1b928fb16..0000000000 --- a/src/core/dbus-job.h +++ /dev/null @@ -1,33 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -#pragma once - -/*** - 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 <dbus/dbus.h> - -#include "job.h" - -void bus_job_send_change_signal(Job *j); -void bus_job_send_removed_signal(Job *j); - -extern const DBusObjectPathVTable bus_job_vtable; - -extern const char bus_job_interface[]; diff --git a/src/core/dbus-kill.c b/src/core/dbus-kill.c deleted file mode 100644 index 165f63074b..0000000000 --- a/src/core/dbus-kill.c +++ /dev/null @@ -1,35 +0,0 @@ -/*-*- 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 <dbus/dbus.h> - -#include "dbus-kill.h" -#include "dbus-common.h" - -DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_kill_append_mode, kill_mode, KillMode); - -const BusProperty bus_kill_context_properties[] = { - { "KillMode", bus_kill_append_mode, "s", offsetof(KillContext, kill_mode) }, - { "KillSignal", bus_property_append_int, "i", offsetof(KillContext, kill_signal) }, - { "SendSIGKILL", bus_property_append_bool, "b", offsetof(KillContext, send_sigkill) }, - { NULL, } -}; diff --git a/src/core/dbus-kill.h b/src/core/dbus-kill.h deleted file mode 100644 index 238fbd36d6..0000000000 --- a/src/core/dbus-kill.h +++ /dev/null @@ -1,39 +0,0 @@ -/*-*- 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/>. -***/ - -#include <dbus/dbus.h> - -#include "manager.h" -#include "dbus-common.h" - -#define BUS_KILL_CONTEXT_INTERFACE \ - " <property name=\"KillMode\" type=\"s\" access=\"read\"/>\n" \ - " <property name=\"KillSignal\" type=\"i\" access=\"read\"/>\n" \ - " <property name=\"SendSIGKILL\" type=\"b\" access=\"read\"/>\n" - -#define BUS_KILL_COMMAND_INTERFACE(name) \ - " <property name=\"" name "\" type=\"a(sasbttuii)\" access=\"read\"/>\n" - -extern const BusProperty bus_kill_context_properties[]; - -int bus_kill_append_mode(DBusMessageIter *i, const char *property, void *data); diff --git a/src/core/dbus-manager.c b/src/core/dbus-manager.c deleted file mode 100644 index 2010241e6a..0000000000 --- a/src/core/dbus-manager.c +++ /dev/null @@ -1,1685 +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 <unistd.h> - -#include "dbus.h" -#include "log.h" -#include "dbus-manager.h" -#include "strv.h" -#include "bus-errors.h" -#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" - -#define BUS_MANAGER_INTERFACE_METHODS \ - " <method name=\"GetUnit\">\n" \ - " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \ - " <arg name=\"unit\" type=\"o\" direction=\"out\"/>\n" \ - " </method>\n" \ - " <method name=\"GetUnitByPID\">\n" \ - " <arg name=\"pid\" type=\"u\" direction=\"in\"/>\n" \ - " <arg name=\"unit\" type=\"o\" direction=\"out\"/>\n" \ - " </method>\n" \ - " <method name=\"LoadUnit\">\n" \ - " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \ - " <arg name=\"unit\" type=\"o\" direction=\"out\"/>\n" \ - " </method>\n" \ - " <method name=\"StartUnit\">\n" \ - " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \ - " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \ - " <arg name=\"job\" type=\"o\" direction=\"out\"/>\n" \ - " </method>\n" \ - " <method name=\"StartUnitReplace\">\n" \ - " <arg name=\"old_unit\" type=\"s\" direction=\"in\"/>\n" \ - " <arg name=\"new_unit\" type=\"s\" direction=\"in\"/>\n" \ - " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \ - " <arg name=\"job\" type=\"o\" direction=\"out\"/>\n" \ - " </method>\n" \ - " <method name=\"StopUnit\">\n" \ - " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \ - " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \ - " <arg name=\"job\" type=\"o\" direction=\"out\"/>\n" \ - " </method>\n" \ - " <method name=\"ReloadUnit\">\n" \ - " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \ - " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \ - " <arg name=\"job\" type=\"o\" direction=\"out\"/>\n" \ - " </method>\n" \ - " <method name=\"RestartUnit\">\n" \ - " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \ - " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \ - " <arg name=\"job\" type=\"o\" direction=\"out\"/>\n" \ - " </method>\n" \ - " <method name=\"TryRestartUnit\">\n" \ - " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \ - " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \ - " <arg name=\"job\" type=\"o\" direction=\"out\"/>\n" \ - " </method>\n" \ - " <method name=\"ReloadOrRestartUnit\">\n" \ - " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \ - " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \ - " <arg name=\"job\" type=\"o\" direction=\"out\"/>\n" \ - " </method>\n" \ - " <method name=\"ReloadOrTryRestartUnit\">\n" \ - " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \ - " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \ - " <arg name=\"job\" type=\"o\" direction=\"out\"/>\n" \ - " </method>\n" \ - " <method name=\"KillUnit\">\n" \ - " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \ - " <arg name=\"who\" type=\"s\" direction=\"in\"/>\n" \ - " <arg name=\"signal\" type=\"i\" direction=\"in\"/>\n" \ - " </method>\n" \ - " <method name=\"ResetFailedUnit\">\n" \ - " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \ - " </method>\n" \ - " <method name=\"GetJob\">\n" \ - " <arg name=\"id\" type=\"u\" direction=\"in\"/>\n" \ - " <arg name=\"job\" type=\"o\" direction=\"out\"/>\n" \ - " </method>\n" \ - " <method name=\"ClearJobs\"/>\n" \ - " <method name=\"ResetFailed\"/>\n" \ - " <method name=\"ListUnits\">\n" \ - " <arg name=\"units\" type=\"a(ssssssouso)\" direction=\"out\"/>\n" \ - " </method>\n" \ - " <method name=\"ListJobs\">\n" \ - " <arg name=\"jobs\" type=\"a(usssoo)\" direction=\"out\"/>\n" \ - " </method>\n" \ - " <method name=\"Subscribe\"/>\n" \ - " <method name=\"Unsubscribe\"/>\n" \ - " <method name=\"Dump\">\n" \ - " <arg name=\"dump\" type=\"s\" direction=\"out\"/>\n" \ - " </method>\n" \ - " <method name=\"CreateSnapshot\">\n" \ - " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \ - " <arg name=\"cleanup\" type=\"b\" direction=\"in\"/>\n" \ - " <arg name=\"unit\" type=\"o\" direction=\"out\"/>\n" \ - " </method>\n" \ - " <method name=\"Reload\"/>\n" \ - " <method name=\"Reexecute\"/>\n" \ - " <method name=\"Exit\"/>\n" \ - " <method name=\"Reboot\"/>\n" \ - " <method name=\"PowerOff\"/>\n" \ - " <method name=\"Halt\"/>\n" \ - " <method name=\"KExec\"/>\n" \ - " <method name=\"SwitchRoot\">\n" \ - " <arg name=\"new_root\" type=\"s\" direction=\"in\"/>\n" \ - " <arg name=\"init\" type=\"s\" direction=\"in\"/>\n" \ - " </method>\n" \ - " <method name=\"SetEnvironment\">\n" \ - " <arg name=\"names\" type=\"as\" direction=\"in\"/>\n" \ - " </method>\n" \ - " <method name=\"UnsetEnvironment\">\n" \ - " <arg name=\"names\" type=\"as\" direction=\"in\"/>\n" \ - " </method>\n" \ - " <method name=\"UnsetAndSetEnvironment\">\n" \ - " <arg name=\"unset\" type=\"as\" direction=\"in\"/>\n" \ - " <arg name=\"set\" type=\"as\" direction=\"in\"/>\n" \ - " </method>\n" \ - " <method name=\"ListUnitFiles\">\n" \ - " <arg name=\"files\" type=\"a(ss)\" direction=\"out\"/>\n" \ - " </method>\n" \ - " <method name=\"GetUnitFileState\">\n" \ - " <arg name=\"file\" type=\"s\" direction=\"in\"/>\n" \ - " <arg name=\"state\" type=\"s\" direction=\"out\"/>\n" \ - " </method>\n" \ - " <method name=\"EnableUnitFiles\">\n" \ - " <arg name=\"files\" type=\"as\" direction=\"in\"/>\n" \ - " <arg name=\"runtime\" type=\"b\" direction=\"in\"/>\n" \ - " <arg name=\"force\" type=\"b\" direction=\"in\"/>\n" \ - " <arg name=\"carries_install_info\" type=\"b\" direction=\"out\"/>\n" \ - " <arg name=\"changes\" type=\"a(sss)\" direction=\"out\"/>\n" \ - " </method>\n" \ - " <method name=\"DisableUnitFiles\">\n" \ - " <arg name=\"files\" type=\"as\" direction=\"in\"/>\n" \ - " <arg name=\"runtime\" type=\"b\" direction=\"in\"/>\n" \ - " <arg name=\"changes\" type=\"a(sss)\" direction=\"out\"/>\n" \ - " </method>\n" \ - " <method name=\"ReenableUnitFiles\">\n" \ - " <arg name=\"files\" type=\"as\" direction=\"in\"/>\n" \ - " <arg name=\"runtime\" type=\"b\" direction=\"in\"/>\n" \ - " <arg name=\"force\" type=\"b\" direction=\"in\"/>\n" \ - " <arg name=\"carries_install_info\" type=\"b\" direction=\"out\"/>\n" \ - " <arg name=\"changes\" type=\"a(sss)\" direction=\"out\"/>\n" \ - " </method>\n" \ - " <method name=\"LinkUnitFiles\">\n" \ - " <arg name=\"files\" type=\"as\" direction=\"in\"/>\n" \ - " <arg name=\"runtime\" type=\"b\" direction=\"in\"/>\n" \ - " <arg name=\"force\" type=\"b\" direction=\"in\"/>\n" \ - " <arg name=\"changes\" type=\"a(sss)\" direction=\"out\"/>\n" \ - " </method>\n" \ - " <method name=\"PresetUnitFiles\">\n" \ - " <arg name=\"files\" type=\"as\" direction=\"in\"/>\n" \ - " <arg name=\"runtime\" type=\"b\" direction=\"in\"/>\n" \ - " <arg name=\"force\" type=\"b\" direction=\"in\"/>\n" \ - " <arg name=\"carries_install_info\" type=\"b\" direction=\"out\"/>\n" \ - " <arg name=\"changes\" type=\"a(sss)\" direction=\"out\"/>\n" \ - " </method>\n" \ - " <method name=\"MaskUnitFiles\">\n" \ - " <arg name=\"files\" type=\"as\" direction=\"in\"/>\n" \ - " <arg name=\"runtime\" type=\"b\" direction=\"in\"/>\n" \ - " <arg name=\"force\" type=\"b\" direction=\"in\"/>\n" \ - " <arg name=\"changes\" type=\"a(sss)\" direction=\"out\"/>\n" \ - " </method>\n" \ - " <method name=\"UnmaskUnitFiles\">\n" \ - " <arg name=\"files\" type=\"as\" direction=\"in\"/>\n" \ - " <arg name=\"runtime\" type=\"b\" direction=\"in\"/>\n" \ - " <arg name=\"changes\" type=\"a(sss)\" direction=\"out\"/>\n" \ - " </method>\n" - -#define BUS_MANAGER_INTERFACE_SIGNALS \ - " <signal name=\"UnitNew\">\n" \ - " <arg name=\"id\" type=\"s\"/>\n" \ - " <arg name=\"unit\" type=\"o\"/>\n" \ - " </signal>\n" \ - " <signal name=\"UnitRemoved\">\n" \ - " <arg name=\"id\" type=\"s\"/>\n" \ - " <arg name=\"unit\" type=\"o\"/>\n" \ - " </signal>\n" \ - " <signal name=\"JobNew\">\n" \ - " <arg name=\"id\" type=\"u\"/>\n" \ - " <arg name=\"job\" type=\"o\"/>\n" \ - " <arg name=\"unit\" type=\"s\"/>\n" \ - " </signal>\n" \ - " <signal name=\"JobRemoved\">\n" \ - " <arg name=\"id\" type=\"u\"/>\n" \ - " <arg name=\"job\" type=\"o\"/>\n" \ - " <arg name=\"unit\" type=\"s\"/>\n" \ - " <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" \ - " <arg name=\"total\" type=\"t\"/>\n" \ - " </signal>" \ - " <signal name=\"UnitFilesChanged\"/>\n" - -#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=\"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" \ - " <property name=\"LogTarget\" type=\"s\" access=\"readwrite\"/>\n" \ - " <property name=\"NNames\" type=\"u\" access=\"read\"/>\n" \ - " <property name=\"NJobs\" type=\"u\" access=\"read\"/>\n" \ - " <property name=\"NInstalledJobs\" type=\"u\" access=\"read\"/>\n" \ - " <property name=\"NFailedJobs\" type=\"u\" access=\"read\"/>\n" \ - " <property name=\"Progress\" type=\"d\" access=\"read\"/>\n" \ - " <property name=\"Environment\" type=\"as\" access=\"read\"/>\n" \ - " <property name=\"ConfirmSpawn\" type=\"b\" access=\"read\"/>\n" \ - " <property name=\"ShowStatus\" type=\"b\" access=\"read\"/>\n" \ - " <property name=\"UnitPath\" type=\"as\" access=\"read\"/>\n" \ - " <property name=\"ControlGroupHierarchy\" type=\"s\" access=\"read\"/>\n" \ - " <property name=\"DefaultControllers\" type=\"as\" access=\"read\"/>\n" \ - " <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=\"Virtualization\" type=\"s\" access=\"read\"/>\n" - -#define BUS_MANAGER_INTERFACE_END \ - " </interface>\n" - -#define BUS_MANAGER_INTERFACE \ - BUS_MANAGER_INTERFACE_BEGIN \ - BUS_MANAGER_INTERFACE_METHODS \ - BUS_MANAGER_INTERFACE_SIGNALS \ - BUS_MANAGER_INTERFACE_PROPERTIES_GENERAL \ - BUS_MANAGER_INTERFACE_END - -#define INTROSPECTION_BEGIN \ - DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \ - "<node>\n" \ - BUS_MANAGER_INTERFACE \ - BUS_PROPERTIES_INTERFACE \ - BUS_PEER_INTERFACE \ - BUS_INTROSPECTABLE_INTERFACE - -#define INTROSPECTION_END \ - "</node>\n" - -#define INTERFACES_LIST \ - BUS_GENERIC_INTERFACES_LIST \ - "org.freedesktop.systemd1.Manager\0" - -const char bus_manager_interface[] _introspect_("Manager") = BUS_MANAGER_INTERFACE; - -static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_manager_append_exec_output, exec_output, ExecOutput); - -static int bus_manager_append_tainted(DBusMessageIter *i, const char *property, void *data) { - const char *t; - Manager *m = data; - char buf[LINE_MAX] = "", *e = buf, *p = NULL; - - assert(i); - assert(property); - assert(m); - - if (m->taint_usr) - e = stpcpy(e, "split-usr:"); - - if (readlink_malloc("/etc/mtab", &p) < 0) - e = stpcpy(e, "mtab-not-symlink:"); - else - free(p); - - if (access("/proc/cgroups", F_OK) < 0) - e = stpcpy(e, "cgroups-missing:"); - - if (hwclock_is_localtime() > 0) - e = stpcpy(e, "local-hwclock:"); - - /* remove the last ':' */ - if (e != buf) - e[-1] = 0; - - t = buf; - - if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &t)) - return -ENOMEM; - - return 0; -} - -static int bus_manager_append_log_target(DBusMessageIter *i, const char *property, void *data) { - const char *t; - - assert(i); - assert(property); - - t = log_target_to_string(log_get_target()); - - if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &t)) - return -ENOMEM; - - return 0; -} - -static int bus_manager_set_log_target(DBusMessageIter *i, const char *property, void *data) { - const char *t; - - assert(i); - assert(property); - - dbus_message_iter_get_basic(i, &t); - - return log_set_target_from_string(t); -} - -static int bus_manager_append_log_level(DBusMessageIter *i, const char *property, void *data) { - char *t; - int r; - - assert(i); - assert(property); - - 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)) - r = -ENOMEM; - - free(t); - return r; -} - -static int bus_manager_set_log_level(DBusMessageIter *i, const char *property, void *data) { - const char *t; - - assert(i); - assert(property); - - dbus_message_iter_get_basic(i, &t); - - return log_set_max_level_from_string(t); -} - -static int bus_manager_append_n_names(DBusMessageIter *i, const char *property, void *data) { - Manager *m = data; - uint32_t u; - - assert(i); - assert(property); - assert(m); - - u = hashmap_size(m->units); - - if (!dbus_message_iter_append_basic(i, DBUS_TYPE_UINT32, &u)) - return -ENOMEM; - - return 0; -} - -static int bus_manager_append_n_jobs(DBusMessageIter *i, const char *property, void *data) { - Manager *m = data; - uint32_t u; - - assert(i); - assert(property); - assert(m); - - u = hashmap_size(m->jobs); - - if (!dbus_message_iter_append_basic(i, DBUS_TYPE_UINT32, &u)) - return -ENOMEM; - - return 0; -} - -static int bus_manager_append_progress(DBusMessageIter *i, const char *property, void *data) { - double d; - Manager *m = data; - - assert(i); - assert(property); - assert(m); - - if (dual_timestamp_is_set(&m->finish_timestamp)) - d = 1.0; - else - d = 1.0 - ((double) hashmap_size(m->jobs) / (double) m->n_installed_jobs); - - if (!dbus_message_iter_append_basic(i, DBUS_TYPE_DOUBLE, &d)) - return -ENOMEM; - - return 0; -} - -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); - - detect_virtualization(&id); - - if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &id)) - return -ENOMEM; - - return 0; -} - -static DBusMessage *message_from_file_changes( - DBusMessage *m, - UnitFileChange *changes, - unsigned n_changes, - int carries_install_info) { - - DBusMessageIter iter, sub, sub2; - DBusMessage *reply; - unsigned i; - - reply = dbus_message_new_method_return(m); - if (!reply) - return NULL; - - dbus_message_iter_init_append(reply, &iter); - - if (carries_install_info >= 0) { - dbus_bool_t b; - - b = !!carries_install_info; - if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_BOOLEAN, &b)) - goto oom; - } - - if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(sss)", &sub)) - goto oom; - - for (i = 0; i < n_changes; i++) { - const char *type, *path, *source; - - type = unit_file_change_type_to_string(changes[i].type); - path = strempty(changes[i].path); - source = strempty(changes[i].source); - - if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) || - !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &type) || - !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &path) || - !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &source) || - !dbus_message_iter_close_container(&sub, &sub2)) - goto oom; - } - - if (!dbus_message_iter_close_container(&iter, &sub)) - goto oom; - - return reply; - -oom: - dbus_message_unref(reply); - return NULL; -} - -static int bus_manager_send_unit_files_changed(Manager *m) { - DBusMessage *s; - int r; - - s = dbus_message_new_signal("/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "UnitFilesChanged"); - if (!s) - return -ENOMEM; - - r = bus_broadcast(m, s); - dbus_message_unref(s); - - return r; -} - -static int bus_manager_set_runtime_watchdog_usec(DBusMessageIter *i, const char *property, void *data) { - uint64_t *t = data; - - assert(i); - assert(property); - - dbus_message_iter_get_basic(i, t); - - return watchdog_set_timeout(t); -} - -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) }, - { NULL, } -}; - -static const BusProperty bus_manager_properties[] = { - { "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; - JobType job_type = _JOB_TYPE_INVALID; - bool reload_if_possible = false; - const char *member; - - assert(connection); - assert(message); - assert(m); - - dbus_error_init(&error); - - member = dbus_message_get_member(message); - - if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "GetUnit")) { - const char *name; - Unit *u; - - if (!dbus_message_get_args( - message, - &error, - DBUS_TYPE_STRING, &name, - DBUS_TYPE_INVALID)) - return bus_send_error_reply(connection, message, &error, -EINVAL); - - 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, "status"); - - reply = dbus_message_new_method_return(message); - if (!reply) - goto oom; - - path = unit_dbus_path(u); - if (!path) - goto oom; - - if (!dbus_message_append_args( - reply, - DBUS_TYPE_OBJECT_PATH, &path, - DBUS_TYPE_INVALID)) - goto oom; - } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "GetUnitByPID")) { - Unit *u; - uint32_t pid; - - if (!dbus_message_get_args( - message, - &error, - DBUS_TYPE_UINT32, &pid, - DBUS_TYPE_INVALID)) - return bus_send_error_reply(connection, message, &error, -EINVAL); - - 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); - } - - SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "status"); - - reply = dbus_message_new_method_return(message); - if (!reply) - goto oom; - - path = unit_dbus_path(u); - if (!path) - goto oom; - - if (!dbus_message_append_args( - reply, - DBUS_TYPE_OBJECT_PATH, &path, - DBUS_TYPE_INVALID)) - goto oom; - } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "LoadUnit")) { - const char *name; - Unit *u; - - if (!dbus_message_get_args( - message, - &error, - DBUS_TYPE_STRING, &name, - DBUS_TYPE_INVALID)) - return bus_send_error_reply(connection, message, &error, -EINVAL); - - r = manager_load_unit(m, name, NULL, &error, &u); - if (r < 0) - return bus_send_error_reply(connection, message, &error, r); - - SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "status"); - - reply = dbus_message_new_method_return(message); - if (!reply) - goto oom; - - path = unit_dbus_path(u); - if (!path) - goto oom; - - if (!dbus_message_append_args( - reply, - DBUS_TYPE_OBJECT_PATH, &path, - DBUS_TYPE_INVALID)) - goto oom; - - } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "StartUnit")) - job_type = JOB_START; - else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "StartUnitReplace")) - job_type = JOB_START; - else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "StopUnit")) - job_type = JOB_STOP; - else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ReloadUnit")) - job_type = JOB_RELOAD; - else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "RestartUnit")) - job_type = JOB_RESTART; - else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "TryRestartUnit")) - job_type = JOB_TRY_RESTART; - else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ReloadOrRestartUnit")) { - reload_if_possible = true; - job_type = JOB_RESTART; - } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ReloadOrTryRestartUnit")) { - reload_if_possible = true; - job_type = JOB_TRY_RESTART; - } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "KillUnit")) { - const char *name, *swho; - int32_t signo; - Unit *u; - KillWho who; - - if (!dbus_message_get_args( - message, - &error, - DBUS_TYPE_STRING, &name, - DBUS_TYPE_STRING, &swho, - DBUS_TYPE_INT32, &signo, - DBUS_TYPE_INVALID)) - return bus_send_error_reply(connection, message, &error, -EINVAL); - - if (isempty(swho)) - who = KILL_ALL; - else { - who = kill_who_from_string(swho); - if (who < 0) - return bus_send_error_reply(connection, message, &error, -EINVAL); - } - - if (signo <= 0 || signo >= _NSIG) - return bus_send_error_reply(connection, message, &error, -EINVAL); - - 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, "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))) - goto oom; - - } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "GetJob")) { - uint32_t id; - Job *j; - - if (!dbus_message_get_args( - message, - &error, - DBUS_TYPE_UINT32, &id, - DBUS_TYPE_INVALID)) - return bus_send_error_reply(connection, message, &error, -EINVAL); - - 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); - } - - SELINUX_UNIT_ACCESS_CHECK(j->unit, connection, message, "status"); - - 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; - - } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ClearJobs")) { - - SELINUX_ACCESS_CHECK(connection, message, "reboot"); - - manager_clear_jobs(m); - - 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); - - reply = dbus_message_new_method_return(message); - if (!reply) - goto oom; - - } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ResetFailedUnit")) { - const char *name; - Unit *u; - - if (!dbus_message_get_args( - message, - &error, - DBUS_TYPE_STRING, &name, - DBUS_TYPE_INVALID)) - return bus_send_error_reply(connection, message, &error, -EINVAL); - - 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); - - reply = dbus_message_new_method_return(message); - if (!reply) - goto oom; - - } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ListUnits")) { - DBusMessageIter iter, sub; - Iterator i; - Unit *u; - const char *k; - - SELINUX_ACCESS_CHECK(connection, message, "status"); - - reply = dbus_message_new_method_return(message); - if (!reply) - goto oom; - - dbus_message_iter_init_append(reply, &iter); - - if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(ssssssouso)", &sub)) - goto oom; - - HASHMAP_FOREACH_KEY(u, k, m->units, i) { - char *u_path, *j_path; - const char *description, *load_state, *active_state, *sub_state, *sjob_type, *following; - DBusMessageIter sub2; - uint32_t job_id; - Unit *f; - - if (k != u->id) - continue; - - if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2)) - goto oom; - - description = unit_description(u); - load_state = unit_load_state_to_string(u->load_state); - active_state = unit_active_state_to_string(unit_active_state(u)); - sub_state = unit_sub_state_to_string(u); - - f = unit_following(u); - following = f ? f->id : ""; - - u_path = unit_dbus_path(u); - if (!u_path) - goto oom; - - if (u->job) { - job_id = (uint32_t) u->job->id; - - if (!(j_path = job_dbus_path(u->job))) { - free(u_path); - goto oom; - } - - sjob_type = job_type_to_string(u->job->type); - } else { - job_id = 0; - j_path = u_path; - sjob_type = ""; - } - - if (!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &u->id) || - !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &description) || - !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &load_state) || - !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &active_state) || - !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &sub_state) || - !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &following) || - !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &u_path) || - !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_UINT32, &job_id) || - !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &sjob_type) || - !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &j_path)) { - free(u_path); - if (u->job) - free(j_path); - goto oom; - } - - free(u_path); - if (u->job) - free(j_path); - - if (!dbus_message_iter_close_container(&sub, &sub2)) - goto oom; - } - - if (!dbus_message_iter_close_container(&iter, &sub)) - goto oom; - - } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ListJobs")) { - DBusMessageIter iter, sub; - Iterator i; - Job *j; - - SELINUX_ACCESS_CHECK(connection, message, "status"); - - reply = dbus_message_new_method_return(message); - if (!reply) - goto oom; - - dbus_message_iter_init_append(reply, &iter); - - if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(usssoo)", &sub)) - goto oom; - - HASHMAP_FOREACH(j, m->jobs, i) { - char *u_path, *j_path; - const char *state, *type; - uint32_t id; - DBusMessageIter sub2; - - if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2)) - goto oom; - - id = (uint32_t) j->id; - state = job_state_to_string(j->state); - type = job_type_to_string(j->type); - - j_path = job_dbus_path(j); - if (!j_path) - goto oom; - - u_path = unit_dbus_path(j->unit); - if (!u_path) { - free(j_path); - goto oom; - } - - if (!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_UINT32, &id) || - !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &j->unit->id) || - !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &type) || - !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &state) || - !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &j_path) || - !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &u_path)) { - free(j_path); - free(u_path); - goto oom; - } - - free(j_path); - free(u_path); - - if (!dbus_message_iter_close_container(&sub, &sub2)) - goto oom; - } - - if (!dbus_message_iter_close_container(&iter, &sub)) - goto oom; - - } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Subscribe")) { - char *client; - Set *s; - - 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)) { - set_free(s); - goto oom; - } - } - - client = strdup(bus_message_get_sender_with_fallback(message)); - if (!client) - goto oom; - - r = set_put(s, client); - if (r < 0) { - free(client); - return bus_send_error_reply(connection, message, NULL, r); - } - - 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; - - 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); - - reply = dbus_message_new_method_return(message); - if (!reply) - goto oom; - - } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Dump")) { - FILE *f; - char *dump = NULL; - size_t size; - - SELINUX_ACCESS_CHECK(connection, message, "status"); - - reply = dbus_message_new_method_return(message); - if (!reply) - goto oom; - - f = open_memstream(&dump, &size); - if (!f) - goto oom; - - manager_dump_units(m, f, NULL); - manager_dump_jobs(m, f, NULL); - - if (ferror(f)) { - fclose(f); - free(dump); - goto oom; - } - - fclose(f); - - if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &dump, DBUS_TYPE_INVALID)) { - free(dump); - goto oom; - } - - free(dump); - } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "CreateSnapshot")) { - const char *name; - dbus_bool_t cleanup; - Snapshot *s; - - SELINUX_ACCESS_CHECK(connection, message, "start"); - - if (!dbus_message_get_args( - message, - &error, - DBUS_TYPE_STRING, &name, - DBUS_TYPE_BOOLEAN, &cleanup, - DBUS_TYPE_INVALID)) - return bus_send_error_reply(connection, message, &error, -EINVAL); - - if (isempty(name)) - name = NULL; - - r = snapshot_create(m, name, cleanup, &error, &s); - if (r < 0) - return bus_send_error_reply(connection, message, &error, r); - - reply = dbus_message_new_method_return(message); - if (!reply) - goto oom; - - path = unit_dbus_path(UNIT(s)); - if (!path) - goto oom; - - if (!dbus_message_append_args( - reply, - DBUS_TYPE_OBJECT_PATH, &path, - DBUS_TYPE_INVALID)) - goto oom; - - } else if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect")) { - char *introspection = NULL; - FILE *f; - Iterator i; - Unit *u; - Job *j; - const char *k; - size_t size; - - 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 - * relying on bus_default_message_handler() because we - * need to generate our introspection string - * dynamically. */ - - f = open_memstream(&introspection, &size); - if (!f) - goto oom; - - fputs(INTROSPECTION_BEGIN, f); - - HASHMAP_FOREACH_KEY(u, k, m->units, i) { - char *p; - - if (k != u->id) - continue; - - p = bus_path_escape(k); - if (!p) { - fclose(f); - free(introspection); - goto oom; - } - - fprintf(f, "<node name=\"unit/%s\"/>", p); - free(p); - } - - HASHMAP_FOREACH(j, m->jobs, i) - fprintf(f, "<node name=\"job/%lu\"/>", (unsigned long) j->id); - - fputs(INTROSPECTION_END, f); - - if (ferror(f)) { - fclose(f); - free(introspection); - goto oom; - } - - fclose(f); - - if (!introspection) - goto oom; - - if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &introspection, DBUS_TYPE_INVALID)) { - free(introspection); - goto oom; - } - - free(introspection); - - } 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 - * just remember that we need to and then send it - * after the reload is finished. That way the caller - * knows when the reload finished. */ - - m->queued_message = dbus_message_new_method_return(message); - if (!m->queued_message) - goto oom; - - m->queued_message_connection = connection; - m->exit_code = MANAGER_RELOAD; - - } 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. */ - - m->exit_code = MANAGER_REEXECUTE; - - } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Exit")) { - - 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); - } - - 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")) { - - 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); - } - - 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")) { - - 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); - } - - 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")) { - - 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); - } - - 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")) { - - 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); - } - - reply = dbus_message_new_method_return(message); - if (!reply) - goto oom; - - m->exit_code = MANAGER_KEXEC; - - } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "SwitchRoot")) { - const char *switch_root, *switch_root_init; - char *u, *v; - int k; - - SELINUX_ACCESS_CHECK(connection, message, "reboot"); - - if (!dbus_message_get_args( - message, - &error, - DBUS_TYPE_STRING, &switch_root, - DBUS_TYPE_STRING, &switch_root_init, - DBUS_TYPE_INVALID)) - return bus_send_error_reply(connection, message, &error, -EINVAL); - - if (path_equal(switch_root, "/") || !path_is_absolute(switch_root)) - return bus_send_error_reply(connection, message, NULL, -EINVAL); - - if (!isempty(switch_root_init) && !path_is_absolute(switch_root_init)) - return bus_send_error_reply(connection, message, NULL, -EINVAL); - - 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); - } - - /* Safety check */ - if (isempty(switch_root_init)) - k = access(switch_root, F_OK); - else { - char *p; - - p = strjoin(switch_root, "/", switch_root_init, NULL); - if (!p) - goto oom; - - k = access(p, X_OK); - free(p); - } - if (k < 0) - return bus_send_error_reply(connection, message, NULL, -errno); - - u = strdup(switch_root); - if (!u) - goto oom; - - if (!isempty(switch_root_init)) { - v = strdup(switch_root_init); - if (!v) { - free(u); - goto oom; - } - } else - v = NULL; - - free(m->switch_root); - free(m->switch_root_init); - m->switch_root = u; - m->switch_root_init = v; - - reply = dbus_message_new_method_return(message); - if (!reply) - goto oom; - - m->exit_code = MANAGER_SWITCH_ROOT; - - } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "SetEnvironment")) { - char **l = NULL, **e = NULL; - - 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; - - reply = dbus_message_new_method_return(message); - if (!reply) { - strv_free(e); - goto oom; - } - - strv_free(m->environment); - m->environment = e; - - } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "UnsetEnvironment")) { - char **l = NULL, **e = NULL; - - 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); - - if (!e) - goto oom; - - if (!(reply = dbus_message_new_method_return(message))) { - strv_free(e); - goto oom; - } - - strv_free(m->environment); - m->environment = e; - - } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "UnsetAndSetEnvironment")) { - 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 == -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); - return bus_send_error_reply(connection, message, NULL, -EINVAL); - } - - r = bus_parse_strv_iter(&iter, &l_set); - if (r < 0) { - strv_free(l_unset); - if (r == -ENOMEM) - goto oom; - - return bus_send_error_reply(connection, message, NULL, r); - } - - e = strv_env_delete(m->environment, 1, l_unset); - strv_free(l_unset); - - if (!e) { - strv_free(l_set); - goto oom; - } - - f = strv_env_merge(2, e, l_set); - strv_free(l_set); - strv_free(e); - - if (!f) - goto oom; - - if (!(reply = dbus_message_new_method_return(message))) { - strv_free(f); - goto oom; - } - - strv_free(m->environment); - m->environment = f; - } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ListUnitFiles")) { - DBusMessageIter iter, sub, sub2; - Hashmap *h; - Iterator i; - UnitFileList *item; - - SELINUX_ACCESS_CHECK(connection, message, "status"); - - reply = dbus_message_new_method_return(message); - if (!reply) - goto oom; - - h = hashmap_new(string_hash_func, string_compare_func); - if (!h) - goto oom; - - 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); - } - - dbus_message_iter_init_append(reply, &iter); - - if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(ss)", &sub)) { - unit_file_list_free(h); - goto oom; - } - - HASHMAP_FOREACH(item, h, i) { - const char *state; - - state = unit_file_state_to_string(item->state); - assert(state); - - if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) || - !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &item->path) || - !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &state) || - !dbus_message_iter_close_container(&sub, &sub2)) { - unit_file_list_free(h); - goto oom; - } - } - - unit_file_list_free(h); - - if (!dbus_message_iter_close_container(&iter, &sub)) - goto oom; - - } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "GetUnitFileState")) { - const char *name; - UnitFileState state; - const char *s; - - SELINUX_ACCESS_CHECK(connection, message, "status"); - - if (!dbus_message_get_args( - message, - &error, - DBUS_TYPE_STRING, &name, - DBUS_TYPE_INVALID)) - return bus_send_error_reply(connection, message, &error, -EINVAL); - - 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); - - s = unit_file_state_to_string(state); - assert(s); - - reply = dbus_message_new_method_return(message); - if (!reply) - goto oom; - - if (!dbus_message_append_args( - reply, - DBUS_TYPE_STRING, &s, - DBUS_TYPE_INVALID)) - goto oom; - } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "EnableUnitFiles") || - dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ReenableUnitFiles") || - dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "LinkUnitFiles") || - dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "PresetUnitFiles") || - dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "MaskUnitFiles")) { - - char **l = NULL; - DBusMessageIter iter; - 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; - - r = bus_parse_strv_iter(&iter, &l); - if (r < 0) { - if (r == -ENOMEM) - goto oom; - - return bus_send_error_reply(connection, message, NULL, r); - } - - if (!dbus_message_iter_next(&iter) || - bus_iter_get_basic_and_next(&iter, DBUS_TYPE_BOOLEAN, &runtime, true) < 0 || - bus_iter_get_basic_and_next(&iter, DBUS_TYPE_BOOLEAN, &force, false) < 0) { - strv_free(l); - return bus_send_error_reply(connection, message, NULL, -EIO); - } - - if (streq(member, "EnableUnitFiles")) { - r = unit_file_enable(scope, runtime, NULL, l, force, &changes, &n_changes); - carries_install_info = r; - } else if (streq(member, "ReenableUnitFiles")) { - r = unit_file_reenable(scope, runtime, NULL, l, force, &changes, &n_changes); - carries_install_info = r; - } else if (streq(member, "LinkUnitFiles")) - r = unit_file_link(scope, runtime, NULL, l, force, &changes, &n_changes); - else if (streq(member, "PresetUnitFiles")) { - r = unit_file_preset(scope, runtime, NULL, l, force, &changes, &n_changes); - carries_install_info = r; - } else if (streq(member, "MaskUnitFiles")) - r = unit_file_mask(scope, runtime, NULL, l, force, &changes, &n_changes); - else - assert_not_reached("Uh? Wrong method"); - - strv_free(l); - bus_manager_send_unit_files_changed(m); - - if (r < 0) { - unit_file_changes_free(changes, n_changes); - return bus_send_error_reply(connection, message, NULL, r); - } - - reply = message_from_file_changes(message, changes, n_changes, carries_install_info); - unit_file_changes_free(changes, n_changes); - - if (!reply) - goto oom; - - } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "DisableUnitFiles") || - dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "UnmaskUnitFiles")) { - - char **l = NULL; - DBusMessageIter iter; - 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; - - r = bus_parse_strv_iter(&iter, &l); - if (r < 0) { - if (r == -ENOMEM) - goto oom; - - return bus_send_error_reply(connection, message, NULL, r); - } - - if (!dbus_message_iter_next(&iter) || - bus_iter_get_basic_and_next(&iter, DBUS_TYPE_BOOLEAN, &runtime, false) < 0) { - strv_free(l); - return bus_send_error_reply(connection, message, NULL, -EIO); - } - - if (streq(member, "DisableUnitFiles")) - r = unit_file_disable(scope, runtime, NULL, l, &changes, &n_changes); - else if (streq(member, "UnmaskUnitFiles")) - r = unit_file_unmask(scope, runtime, NULL, l, &changes, &n_changes); - else - assert_not_reached("Uh? Wrong method"); - - strv_free(l); - bus_manager_send_unit_files_changed(m); - - if (r < 0) { - unit_file_changes_free(changes, n_changes); - return bus_send_error_reply(connection, message, NULL, r); - } - - reply = message_from_file_changes(message, changes, n_changes, -1); - unit_file_changes_free(changes, n_changes); - - if (!reply) - goto oom; - - } else { - const BusBoundProperties bps[] = { - { "org.freedesktop.systemd1.Manager", bus_systemd_properties, systemd_property_string }, - { "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; - Unit *u; - dbus_bool_t b; - - if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "StartUnitReplace")) - b = dbus_message_get_args( - message, - &error, - DBUS_TYPE_STRING, &old_name, - DBUS_TYPE_STRING, &name, - DBUS_TYPE_STRING, &smode, - DBUS_TYPE_INVALID); - else - b = dbus_message_get_args( - message, - &error, - 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) { - 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); - } - } - - 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); - } - - r = manager_load_unit(m, name, NULL, &error, &u); - if (r < 0) - return bus_send_error_reply(connection, message, &error, r); - - return bus_unit_queue_job(connection, message, u, job_type, mode, reload_if_possible); - } - - if (reply) - 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 DBusObjectPathVTable bus_manager_vtable = { - .message_function = bus_manager_message_handler -}; diff --git a/src/core/dbus-manager.h b/src/core/dbus-manager.h deleted file mode 100644 index f0dce5a2e9..0000000000 --- a/src/core/dbus-manager.h +++ /dev/null @@ -1,28 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -#pragma once - -/*** - 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 <dbus/dbus.h> - -extern const DBusObjectPathVTable bus_manager_vtable; - -extern const char bus_manager_interface[]; diff --git a/src/core/dbus-mount.c b/src/core/dbus-mount.c deleted file mode 100644 index d81edeb807..0000000000 --- a/src/core/dbus-mount.c +++ /dev/null @@ -1,168 +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 "dbus-unit.h" -#include "dbus-mount.h" -#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" \ - " <property name=\"Where\" type=\"s\" access=\"read\"/>\n" \ - " <property name=\"What\" type=\"s\" access=\"read\"/>\n" \ - " <property name=\"Options\" type=\"s\" access=\"read\"/>\n" \ - " <property name=\"Type\" type=\"s\" access=\"read\"/>\n" \ - " <property name=\"TimeoutUSec\" type=\"t\" access=\"read\"/>\n" \ - BUS_EXEC_COMMAND_INTERFACE("ExecMount") \ - BUS_EXEC_COMMAND_INTERFACE("ExecUnmount") \ - BUS_EXEC_COMMAND_INTERFACE("ExecRemount") \ - BUS_EXEC_CONTEXT_INTERFACE \ - BUS_KILL_CONTEXT_INTERFACE \ - " <property name=\"ControlPID\" type=\"u\" access=\"read\"/>\n" \ - " <property name=\"DirectoryMode\" type=\"u\" access=\"read\"/>\n" \ - " <property name=\"Result\" type=\"s\" access=\"read\"/>\n" \ - " </interface>\n" - -#define INTROSPECTION \ - DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \ - "<node>\n" \ - BUS_UNIT_INTERFACE \ - BUS_MOUNT_INTERFACE \ - BUS_PROPERTIES_INTERFACE \ - BUS_PEER_INTERFACE \ - BUS_INTROSPECTABLE_INTERFACE \ - "</node>\n" - -#define INTERFACES_LIST \ - BUS_UNIT_INTERFACES_LIST \ - "org.freedesktop.systemd1.Mount\0" - -const char bus_mount_interface[] _introspect_("Mount") = BUS_MOUNT_INTERFACE; - -const char bus_mount_invalidating_properties[] = - "What\0" - "Options\0" - "Type\0" - "ExecMount\0" - "ExecUnmount\0" - "ExecRemount\0" - "ControlPID\0" - "Result\0"; - -static int bus_mount_append_what(DBusMessageIter *i, const char *property, void *data) { - Mount *m = data; - const char *d; - - assert(i); - assert(property); - assert(m); - - if (m->from_proc_self_mountinfo && m->parameters_proc_self_mountinfo.what) - d = m->parameters_proc_self_mountinfo.what; - else if (m->from_fragment && m->parameters_fragment.what) - d = m->parameters_fragment.what; - else - d = ""; - - if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &d)) - return -ENOMEM; - - return 0; -} - -static int bus_mount_append_options(DBusMessageIter *i, const char *property, void *data) { - Mount *m = data; - const char *d; - - assert(i); - assert(property); - assert(m); - - if (m->from_proc_self_mountinfo && m->parameters_proc_self_mountinfo.options) - d = m->parameters_proc_self_mountinfo.options; - else if (m->from_fragment && m->parameters_fragment.options) - d = m->parameters_fragment.options; - else - d = ""; - - if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &d)) - return -ENOMEM; - - return 0; -} - -static int bus_mount_append_type(DBusMessageIter *i, const char *property, void *data) { - Mount *m = data; - const char *d; - - assert(i); - assert(property); - assert(m); - - if (m->from_proc_self_mountinfo && m->parameters_proc_self_mountinfo.fstype) - d = m->parameters_proc_self_mountinfo.fstype; - else if (m->from_fragment && m->parameters_fragment.fstype) - d = m->parameters_fragment.fstype; - else - d = ""; - - if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &d)) - return -ENOMEM; - - return 0; -} - -static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_mount_append_mount_result, mount_result, MountResult); - -static const BusProperty bus_mount_properties[] = { - { "Where", bus_property_append_string, "s", offsetof(Mount, where), true }, - { "What", bus_mount_append_what, "s", 0 }, - { "Options", bus_mount_append_options, "s", 0 }, - { "Type", bus_mount_append_type, "s", 0 }, - { "TimeoutUSec", bus_property_append_usec, "t", offsetof(Mount, timeout_usec) }, - BUS_EXEC_COMMAND_PROPERTY("ExecMount", offsetof(Mount, exec_command[MOUNT_EXEC_MOUNT]), false), - BUS_EXEC_COMMAND_PROPERTY("ExecUnmount", offsetof(Mount, exec_command[MOUNT_EXEC_UNMOUNT]), false), - BUS_EXEC_COMMAND_PROPERTY("ExecRemount", offsetof(Mount, exec_command[MOUNT_EXEC_REMOUNT]), false), - { "ControlPID", bus_property_append_pid, "u", offsetof(Mount, control_pid) }, - { "DirectoryMode", bus_property_append_mode, "u", offsetof(Mount, directory_mode) }, - { "Result", bus_mount_append_mount_result, "s", offsetof(Mount, result) }, - { NULL, } -}; - -DBusHandlerResult bus_mount_message_handler(Unit *u, DBusConnection *c, DBusMessage *message) { - Mount *m = MOUNT(u); - - const BusBoundProperties bps[] = { - { "org.freedesktop.systemd1.Unit", bus_unit_properties, u }, - { "org.freedesktop.systemd1.Mount", bus_mount_properties, m }, - { "org.freedesktop.systemd1.Mount", bus_exec_context_properties, &m->exec_context }, - { "org.freedesktop.systemd1.Mount", bus_kill_context_properties, &m->kill_context }, - { 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-mount.h b/src/core/dbus-mount.h deleted file mode 100644 index 8597394373..0000000000 --- a/src/core/dbus-mount.h +++ /dev/null @@ -1,31 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -#pragma once - -/*** - 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 <dbus/dbus.h> - -#include "unit.h" - -DBusHandlerResult bus_mount_message_handler(Unit *u, DBusConnection *c, DBusMessage *message); - -extern const char bus_mount_interface[]; -extern const char bus_mount_invalidating_properties[]; diff --git a/src/core/dbus-path.c b/src/core/dbus-path.c deleted file mode 100644 index f7fed1754d..0000000000 --- a/src/core/dbus-path.c +++ /dev/null @@ -1,122 +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 "dbus-unit.h" -#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" \ - " <property name=\"Unit\" type=\"s\" access=\"read\"/>\n" \ - " <property name=\"Paths\" type=\"a(ss)\" access=\"read\"/>\n" \ - " <property name=\"MakeDirectory\" type=\"b\" access=\"read\"/>\n" \ - " <property name=\"DirectoryMode\" type=\"u\" access=\"read\"/>\n" \ - " <property name=\"Result\" type=\"s\" access=\"read\"/>\n" \ - " </interface>\n" - -#define INTROSPECTION \ - DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \ - "<node>\n" \ - BUS_UNIT_INTERFACE \ - BUS_PATH_INTERFACE \ - BUS_PROPERTIES_INTERFACE \ - BUS_PEER_INTERFACE \ - BUS_INTROSPECTABLE_INTERFACE \ - "</node>\n" - -#define INTERFACES_LIST \ - BUS_UNIT_INTERFACES_LIST \ - "org.freedesktop.systemd1.Path\0" - -const char bus_path_interface[] _introspect_("Path") = BUS_PATH_INTERFACE; - -const char bus_path_invalidating_properties[] = - "Result\0"; - -static int bus_path_append_paths(DBusMessageIter *i, const char *property, void *data) { - Path *p = data; - DBusMessageIter sub, sub2; - PathSpec *k; - - assert(i); - assert(property); - assert(p); - - if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "(ss)", &sub)) - return -ENOMEM; - - LIST_FOREACH(spec, k, p->specs) { - const char *t = path_type_to_string(k->type); - - if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) || - !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &t) || - !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &k->path) || - !dbus_message_iter_close_container(&sub, &sub2)) - return -ENOMEM; - } - - if (!dbus_message_iter_close_container(i, &sub)) - return -ENOMEM; - - return 0; -} - -static int bus_path_append_unit(DBusMessageIter *i, const char *property, void *data) { - Unit *u = data; - Path *p = PATH(u); - const char *t; - - assert(i); - assert(property); - assert(u); - - t = UNIT_DEREF(p->unit) ? UNIT_DEREF(p->unit)->id : ""; - - return dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &t) ? 0 : -ENOMEM; -} - -static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_path_append_path_result, path_result, PathResult); - -static const BusProperty bus_path_properties[] = { - { "Unit", bus_path_append_unit, "s", 0 }, - { "Paths", bus_path_append_paths, "a(ss)", 0 }, - { "MakeDirectory", bus_property_append_bool, "b", offsetof(Path, make_directory) }, - { "DirectoryMode", bus_property_append_mode, "u", offsetof(Path, directory_mode) }, - { "Result", bus_path_append_path_result, "s", offsetof(Path, result) }, - { NULL, } -}; - -DBusHandlerResult bus_path_message_handler(Unit *u, DBusConnection *c, DBusMessage *message) { - Path *p = PATH(u); - const BusBoundProperties bps[] = { - { "org.freedesktop.systemd1.Unit", bus_unit_properties, u }, - { "org.freedesktop.systemd1.Path", bus_path_properties, p }, - { 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.h b/src/core/dbus-path.h deleted file mode 100644 index c945f7d588..0000000000 --- a/src/core/dbus-path.h +++ /dev/null @@ -1,32 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -#pragma once - -/*** - 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 <dbus/dbus.h> - -#include "unit.h" - -DBusHandlerResult bus_path_message_handler(Unit *u, DBusConnection *c, DBusMessage *message); - -extern const char bus_path_interface[]; - -extern const char bus_path_invalidating_properties[]; diff --git a/src/core/dbus-service.c b/src/core/dbus-service.c deleted file mode 100644 index d99058dd46..0000000000 --- a/src/core/dbus-service.c +++ /dev/null @@ -1,161 +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 "dbus-unit.h" -#include "dbus-execute.h" -#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" \ - " <property name=\"Type\" type=\"s\" access=\"read\"/>\n" \ - " <property name=\"Restart\" type=\"s\" access=\"read\"/>\n" \ - " <property name=\"PIDFile\" type=\"s\" access=\"read\"/>\n" \ - " <property name=\"NotifyAccess\" type=\"s\" access=\"read\"/>\n" \ - " <property name=\"RestartUSec\" type=\"t\" access=\"read\"/>\n" \ - " <property name=\"TimeoutUSec\" type=\"t\" access=\"read\"/>\n" \ - " <property name=\"WatchdogUSec\" type=\"t\" access=\"read\"/>\n" \ - " <property name=\"WatchdogTimestamp\" type=\"t\" access=\"read\"/>\n" \ - " <property name=\"WatchdogTimestampMonotonic\" type=\"t\" access=\"read\"/>\n" \ - " <property name=\"StartLimitInterval\" type=\"t\" access=\"read\"/>\n" \ - " <property name=\"StartLimitBurst\" type=\"u\" access=\"read\"/>\n" \ - " <property name=\"StartLimitAction\" type=\"s\" access=\"readwrite\"/>\n" \ - BUS_EXEC_COMMAND_INTERFACE("ExecStartPre") \ - BUS_EXEC_COMMAND_INTERFACE("ExecStart") \ - BUS_EXEC_COMMAND_INTERFACE("ExecStartPost") \ - BUS_EXEC_COMMAND_INTERFACE("ExecReload") \ - BUS_EXEC_COMMAND_INTERFACE("ExecStop") \ - BUS_EXEC_COMMAND_INTERFACE("ExecStopPost") \ - BUS_EXEC_CONTEXT_INTERFACE \ - BUS_KILL_CONTEXT_INTERFACE \ - " <property name=\"PermissionsStartOnly\" type=\"b\" access=\"read\"/>\n" \ - " <property name=\"RootDirectoryStartOnly\" type=\"b\" access=\"read\"/>\n" \ - " <property name=\"RemainAfterExit\" type=\"b\" access=\"read\"/>\n" \ - BUS_EXEC_STATUS_INTERFACE("ExecMain") \ - " <property name=\"MainPID\" type=\"u\" access=\"read\"/>\n" \ - " <property name=\"ControlPID\" type=\"u\" access=\"read\"/>\n" \ - " <property name=\"BusName\" type=\"s\" access=\"read\"/>\n" \ - " <property name=\"StatusText\" type=\"s\" access=\"read\"/>\n" \ - " <property name=\"Result\" type=\"s\" access=\"read\"/>\n" \ - " </interface>\n" - -#define INTROSPECTION \ - DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \ - "<node>\n" \ - BUS_UNIT_INTERFACE \ - BUS_SERVICE_INTERFACE \ - BUS_PROPERTIES_INTERFACE \ - BUS_PEER_INTERFACE \ - BUS_INTROSPECTABLE_INTERFACE \ - "</node>\n" - -#define INTERFACES_LIST \ - BUS_UNIT_INTERFACES_LIST \ - "org.freedesktop.systemd1.Service\0" - -const char bus_service_interface[] _introspect_("Service") = BUS_SERVICE_INTERFACE; - -const char bus_service_invalidating_properties[] = - "ExecStartPre\0" - "ExecStart\0" - "ExecStartPost\0" - "ExecReload\0" - "ExecStop\0" - "ExecStopPost\0" - "ExecMain\0" - "WatchdogTimestamp\0" - "WatchdogTimestampMonotonic\0" - "MainPID\0" - "ControlPID\0" - "StatusText\0" - "Result\0"; - -static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_service_append_type, service_type, ServiceType); -static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_service_append_restart, service_restart, ServiceRestart); -static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_service_append_notify_access, notify_access, NotifyAccess); -static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_service_append_service_result, service_result, ServiceResult); -static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_service_append_start_limit_action, start_limit_action, StartLimitAction); -static DEFINE_BUS_PROPERTY_SET_ENUM(bus_service_set_start_limit_action, start_limit_action, StartLimitAction); - -static const BusProperty bus_exec_main_status_properties[] = { - { "ExecMainStartTimestamp", bus_property_append_usec, "t", offsetof(ExecStatus, start_timestamp.realtime) }, - { "ExecMainStartTimestampMonotonic",bus_property_append_usec, "t", offsetof(ExecStatus, start_timestamp.monotonic) }, - { "ExecMainExitTimestamp", bus_property_append_usec, "t", offsetof(ExecStatus, start_timestamp.realtime) }, - { "ExecMainExitTimestampMonotonic", bus_property_append_usec, "t", offsetof(ExecStatus, start_timestamp.monotonic) }, - { "ExecMainPID", bus_property_append_pid, "u", offsetof(ExecStatus, pid) }, - { "ExecMainCode", bus_property_append_int, "i", offsetof(ExecStatus, code) }, - { "ExecMainStatus", bus_property_append_int, "i", offsetof(ExecStatus, status) }, - { NULL, } -}; - -static const BusProperty bus_service_properties[] = { - { "Type", bus_service_append_type, "s", offsetof(Service, type) }, - { "Restart", bus_service_append_restart, "s", offsetof(Service, restart) }, - { "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_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) }, - { "StartLimitInterval", bus_property_append_usec, "t", offsetof(Service, start_limit.interval) }, - { "StartLimitBurst", bus_property_append_uint32, "u", offsetof(Service, start_limit.burst) }, - { "StartLimitAction", bus_service_append_start_limit_action,"s", offsetof(Service, start_limit_action), false, bus_service_set_start_limit_action}, - BUS_EXEC_COMMAND_PROPERTY("ExecStartPre", offsetof(Service, exec_command[SERVICE_EXEC_START_PRE]), true ), - BUS_EXEC_COMMAND_PROPERTY("ExecStart", offsetof(Service, exec_command[SERVICE_EXEC_START]), true ), - BUS_EXEC_COMMAND_PROPERTY("ExecStartPost", offsetof(Service, exec_command[SERVICE_EXEC_START_POST]), true ), - BUS_EXEC_COMMAND_PROPERTY("ExecReload", offsetof(Service, exec_command[SERVICE_EXEC_RELOAD]), true ), - BUS_EXEC_COMMAND_PROPERTY("ExecStop", offsetof(Service, exec_command[SERVICE_EXEC_STOP]), true ), - BUS_EXEC_COMMAND_PROPERTY("ExecStopPost", offsetof(Service, exec_command[SERVICE_EXEC_STOP_POST]), true ), - { "PermissionsStartOnly", bus_property_append_bool, "b", offsetof(Service, permissions_start_only) }, - { "RootDirectoryStartOnly", bus_property_append_bool, "b", offsetof(Service, root_directory_start_only) }, - { "RemainAfterExit", bus_property_append_bool, "b", offsetof(Service, remain_after_exit) }, - { "GuessMainPID", bus_property_append_bool, "b", offsetof(Service, guess_main_pid) }, - { "MainPID", bus_property_append_pid, "u", offsetof(Service, main_pid) }, - { "ControlPID", bus_property_append_pid, "u", offsetof(Service, control_pid) }, - { "BusName", bus_property_append_string, "s", offsetof(Service, bus_name), true }, - { "StatusText", bus_property_append_string, "s", offsetof(Service, status_text), true }, - { "Result", bus_service_append_service_result,"s", offsetof(Service, result) }, - { NULL, } -}; - -DBusHandlerResult bus_service_message_handler(Unit *u, DBusConnection *connection, DBusMessage *message) { - Service *s = SERVICE(u); - - const BusBoundProperties bps[] = { - { "org.freedesktop.systemd1.Unit", bus_unit_properties, u }, - { "org.freedesktop.systemd1.Service", bus_service_properties, s }, - { "org.freedesktop.systemd1.Service", bus_exec_context_properties, &s->exec_context }, - { "org.freedesktop.systemd1.Service", bus_kill_context_properties, &s->kill_context }, - { "org.freedesktop.systemd1.Service", bus_exec_main_status_properties, &s->main_exec_status }, - { 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-service.h b/src/core/dbus-service.h deleted file mode 100644 index 143aed7ae5..0000000000 --- a/src/core/dbus-service.h +++ /dev/null @@ -1,31 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -#pragma once - -/*** - 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 <dbus/dbus.h> - -#include "unit.h" - -DBusHandlerResult bus_service_message_handler(Unit *u, DBusConnection *c, DBusMessage *message); - -extern const char bus_service_interface[]; -extern const char bus_service_invalidating_properties[]; diff --git a/src/core/dbus-snapshot.c b/src/core/dbus-snapshot.c deleted file mode 100644 index 435c6df39c..0000000000 --- a/src/core/dbus-snapshot.c +++ /dev/null @@ -1,94 +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 "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" \ - " <method name=\"Remove\"/>\n" \ - " <property name=\"Cleanup\" type=\"b\" access=\"read\"/>\n" \ - " </interface>\n" - -#define INTROSPECTION \ - DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \ - "<node>\n" \ - BUS_UNIT_INTERFACE \ - BUS_SNAPSHOT_INTERFACE \ - BUS_PROPERTIES_INTERFACE \ - BUS_PEER_INTERFACE \ - BUS_INTROSPECTABLE_INTERFACE \ - "</node>\n" - -#define INTERFACES_LIST \ - BUS_UNIT_INTERFACES_LIST \ - "org.freedesktop.systemd1.Snapshot\0" - -const char bus_snapshot_interface[] _introspect_("Snapshot") = BUS_SNAPSHOT_INTERFACE; - -static const BusProperty bus_snapshot_properties[] = { - { "Cleanup", bus_property_append_bool, "b", offsetof(Snapshot, cleanup) }, - { NULL, } -}; - -DBusHandlerResult bus_snapshot_message_handler(Unit *u, DBusConnection *c, DBusMessage *message) { - Snapshot *s = SNAPSHOT(u); - _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)); - - reply = dbus_message_new_method_return(message); - if (!reply) - goto oom; - - } else { - const BusBoundProperties bps[] = { - { "org.freedesktop.systemd1.Unit", bus_unit_properties, u }, - { "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; - } - - return DBUS_HANDLER_RESULT_HANDLED; - -oom: - dbus_error_free(&error); - - return DBUS_HANDLER_RESULT_NEED_MEMORY; -} diff --git a/src/core/dbus-snapshot.h b/src/core/dbus-snapshot.h deleted file mode 100644 index 1208aafff6..0000000000 --- a/src/core/dbus-snapshot.h +++ /dev/null @@ -1,30 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -#pragma once - -/*** - 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 <dbus/dbus.h> - -#include "unit.h" - -DBusHandlerResult bus_snapshot_message_handler(Unit *u, DBusConnection *c, DBusMessage *message); - -extern const char bus_snapshot_interface[]; diff --git a/src/core/dbus-socket.c b/src/core/dbus-socket.c deleted file mode 100644 index 095a031612..0000000000 --- a/src/core/dbus-socket.c +++ /dev/null @@ -1,151 +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 "dbus-unit.h" -#include "dbus-socket.h" -#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" \ - " <property name=\"BindIPv6Only\" type=\"b\" access=\"read\"/>\n" \ - " <property name=\"Backlog\" type=\"u\" access=\"read\"/>\n" \ - " <property name=\"TimeoutUSec\" type=\"t\" access=\"read\"/>\n" \ - BUS_EXEC_COMMAND_INTERFACE("ExecStartPre") \ - BUS_EXEC_COMMAND_INTERFACE("ExecStartPost") \ - BUS_EXEC_COMMAND_INTERFACE("ExecStopPre") \ - BUS_EXEC_COMMAND_INTERFACE("ExecStopPost") \ - BUS_EXEC_CONTEXT_INTERFACE \ - BUS_KILL_CONTEXT_INTERFACE \ - " <property name=\"ControlPID\" type=\"u\" access=\"read\"/>\n" \ - " <property name=\"BindToDevice\" type=\"s\" access=\"read\"/>\n" \ - " <property name=\"DirectoryMode\" type=\"u\" access=\"read\"/>\n" \ - " <property name=\"SocketMode\" type=\"u\" access=\"read\"/>\n" \ - " <property name=\"Accept\" type=\"b\" access=\"read\"/>\n" \ - " <property name=\"KeepAlive\" type=\"b\" access=\"read\"/>\n" \ - " <property name=\"Priority\" type=\"i\" access=\"read\"/>\n" \ - " <property name=\"ReceiveBuffer\" type=\"t\" access=\"read\"/>\n" \ - " <property name=\"SendBuffer\" type=\"t\" access=\"read\"/>\n" \ - " <property name=\"IPTOS\" type=\"i\" access=\"read\"/>\n" \ - " <property name=\"IPTTL\" type=\"i\" access=\"read\"/>\n" \ - " <property name=\"PipeSize\" type=\"t\" access=\"read\"/>\n" \ - " <property name=\"FreeBind\" type=\"b\" access=\"read\"/>\n" \ - " <property name=\"Transparent\" type=\"b\" access=\"read\"/>\n" \ - " <property name=\"Broadcast\" type=\"b\" access=\"read\"/>\n" \ - " <property name=\"PassCredentials\" type=\"b\" access=\"read\"/>\n" \ - " <property name=\"PassSecurity\" type=\"b\" access=\"read\"/>\n" \ - " <property name=\"Mark\" type=\"i\" access=\"read\"/>\n" \ - " <property name=\"MaxConnections\" type=\"u\" access=\"read\"/>\n" \ - " <property name=\"NAccepted\" type=\"u\" access=\"read\"/>\n" \ - " <property name=\"NConnections\" type=\"u\" access=\"read\"/>\n" \ - " <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 \ - DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \ - "<node>\n" \ - BUS_UNIT_INTERFACE \ - BUS_SOCKET_INTERFACE \ - BUS_PROPERTIES_INTERFACE \ - BUS_PEER_INTERFACE \ - BUS_INTROSPECTABLE_INTERFACE \ - "</node>\n" - -#define INTERFACES_LIST \ - BUS_UNIT_INTERFACES_LIST \ - "org.freedesktop.systemd1.Socket\0" - -const char bus_socket_interface[] _introspect_("Socket") = BUS_SOCKET_INTERFACE; - -const char bus_socket_invalidating_properties[] = - "ExecStartPre\0" - "ExecStartPost\0" - "ExecStopPre\0" - "ExecStopPost\0" - "ControlPID\0" - "NAccepted\0" - "NConnections\0" - "Result\0"; - -static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_socket_append_bind_ipv6_only, socket_address_bind_ipv6_only, SocketAddressBindIPv6Only); -static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_socket_append_socket_result, socket_result, SocketResult); - -static const BusProperty bus_socket_properties[] = { - { "BindIPv6Only", bus_socket_append_bind_ipv6_only, "s", offsetof(Socket, bind_ipv6_only) }, - { "Backlog", bus_property_append_unsigned, "u", offsetof(Socket, backlog) }, - { "TimeoutUSec", bus_property_append_usec, "t", offsetof(Socket, timeout_usec) }, - BUS_EXEC_COMMAND_PROPERTY("ExecStartPre", offsetof(Socket, exec_command[SOCKET_EXEC_START_PRE]), true ), - BUS_EXEC_COMMAND_PROPERTY("ExecStartPost", offsetof(Socket, exec_command[SOCKET_EXEC_START_POST]), true ), - BUS_EXEC_COMMAND_PROPERTY("ExecStopPre", offsetof(Socket, exec_command[SOCKET_EXEC_STOP_PRE]), true ), - BUS_EXEC_COMMAND_PROPERTY("ExecStopPost", offsetof(Socket, exec_command[SOCKET_EXEC_STOP_POST]), true ), - { "ControlPID", bus_property_append_pid, "u", offsetof(Socket, control_pid) }, - { "BindToDevice", bus_property_append_string, "s", offsetof(Socket, bind_to_device), true }, - { "DirectoryMode", bus_property_append_mode, "u", offsetof(Socket, directory_mode) }, - { "SocketMode", bus_property_append_mode, "u", offsetof(Socket, socket_mode) }, - { "Accept", bus_property_append_bool, "b", offsetof(Socket, accept) }, - { "KeepAlive", bus_property_append_bool, "b", offsetof(Socket, keep_alive) }, - { "Priority", bus_property_append_int, "i", offsetof(Socket, priority) }, - { "ReceiveBuffer", bus_property_append_size, "t", offsetof(Socket, receive_buffer) }, - { "SendBuffer", bus_property_append_size, "t", offsetof(Socket, send_buffer) }, - { "IPTOS", bus_property_append_int, "i", offsetof(Socket, ip_tos) }, - { "IPTTL", bus_property_append_int, "i", offsetof(Socket, ip_ttl) }, - { "PipeSize", bus_property_append_size, "t", offsetof(Socket, pipe_size) }, - { "FreeBind", bus_property_append_bool, "b", offsetof(Socket, free_bind) }, - { "Transparent", bus_property_append_bool, "b", offsetof(Socket, transparent) }, - { "Broadcast", bus_property_append_bool, "b", offsetof(Socket, broadcast) }, - { "PassCredentials",bus_property_append_bool, "b", offsetof(Socket, pass_cred) }, - { "PassSecurity", bus_property_append_bool, "b", offsetof(Socket, pass_sec) }, - { "Mark", bus_property_append_int, "i", offsetof(Socket, mark) }, - { "MaxConnections", bus_property_append_unsigned, "u", offsetof(Socket, max_connections) }, - { "NConnections", bus_property_append_unsigned, "u", offsetof(Socket, n_connections) }, - { "NAccepted", bus_property_append_unsigned, "u", offsetof(Socket, n_accepted) }, - { "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, } -}; - -DBusHandlerResult bus_socket_message_handler(Unit *u, DBusConnection *c, DBusMessage *message) { - Socket *s = SOCKET(u); - const BusBoundProperties bps[] = { - { "org.freedesktop.systemd1.Unit", bus_unit_properties, u }, - { "org.freedesktop.systemd1.Socket", bus_socket_properties, s }, - { "org.freedesktop.systemd1.Socket", bus_exec_context_properties, &s->exec_context }, - { "org.freedesktop.systemd1.Socket", bus_kill_context_properties, &s->kill_context }, - { 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-socket.h b/src/core/dbus-socket.h deleted file mode 100644 index 5369b22e5e..0000000000 --- a/src/core/dbus-socket.h +++ /dev/null @@ -1,31 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -#pragma once - -/*** - 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 <dbus/dbus.h> - -#include "unit.h" - -DBusHandlerResult bus_socket_message_handler(Unit *u, DBusConnection *c, DBusMessage *message); - -extern const char bus_socket_interface[]; -extern const char bus_socket_invalidating_properties[]; diff --git a/src/core/dbus-swap.c b/src/core/dbus-swap.c deleted file mode 100644 index 67ea0f24fe..0000000000 --- a/src/core/dbus-swap.c +++ /dev/null @@ -1,115 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - Copyright 2010 Maarten Lankhorst - - 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 "dbus-unit.h" -#include "dbus-swap.h" -#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" \ - " <property name=\"What\" type=\"s\" access=\"read\"/>\n" \ - " <property name=\"Priority\" type=\"i\" access=\"read\"/>\n" \ - " <property name=\"TimeoutUSec\" type=\"t\" access=\"read\"/>\n" \ - BUS_EXEC_COMMAND_INTERFACE("ExecActivate") \ - BUS_EXEC_COMMAND_INTERFACE("ExecDeactivate") \ - BUS_EXEC_CONTEXT_INTERFACE \ - BUS_KILL_CONTEXT_INTERFACE \ - " <property name=\"ControlPID\" type=\"u\" access=\"read\"/>\n" \ - " <property name=\"Result\" type=\"s\" access=\"read\"/>\n" \ - " </interface>\n" - -#define INTROSPECTION \ - DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \ - "<node>\n" \ - BUS_UNIT_INTERFACE \ - BUS_SWAP_INTERFACE \ - BUS_PROPERTIES_INTERFACE \ - BUS_PEER_INTERFACE \ - BUS_INTROSPECTABLE_INTERFACE \ - "</node>\n" - -#define INTERFACES_LIST \ - BUS_UNIT_INTERFACES_LIST \ - "org.freedesktop.systemd1.Swap\0" - -const char bus_swap_interface[] _introspect_("Swap") = BUS_SWAP_INTERFACE; - -const char bus_swap_invalidating_properties[] = - "What\0" - "Priority\0" - "ExecActivate\0" - "ExecDeactivate\0" - "ControlPID\0" - "Result\0"; - -static int bus_swap_append_priority(DBusMessageIter *i, const char *property, void *data) { - Swap *s = data; - dbus_int32_t j; - - assert(i); - assert(property); - assert(s); - - if (s->from_proc_swaps) - j = s->parameters_proc_swaps.priority; - else if (s->from_fragment) - j = s->parameters_fragment.priority; - else - j = -1; - - if (!dbus_message_iter_append_basic(i, DBUS_TYPE_INT32, &j)) - return -ENOMEM; - - return 0; -} - -static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_swap_append_swap_result, swap_result, SwapResult); - -static const BusProperty bus_swap_properties[] = { - { "What", bus_property_append_string, "s", offsetof(Swap, what), true }, - { "Priority", bus_swap_append_priority, "i", 0 }, - BUS_EXEC_COMMAND_PROPERTY("ExecActivate", offsetof(Swap, exec_command[SWAP_EXEC_ACTIVATE]), false), - BUS_EXEC_COMMAND_PROPERTY("ExecDeactivate", offsetof(Swap, exec_command[SWAP_EXEC_DEACTIVATE]), false), - { "ControlPID", bus_property_append_pid, "u", offsetof(Swap, control_pid) }, - { "Result", bus_swap_append_swap_result,"s", offsetof(Swap, result) }, - { NULL, } -}; - -DBusHandlerResult bus_swap_message_handler(Unit *u, DBusConnection *c, DBusMessage *message) { - Swap *s = SWAP(u); - const BusBoundProperties bps[] = { - { "org.freedesktop.systemd1.Unit", bus_unit_properties, u }, - { "org.freedesktop.systemd1.Swap", bus_swap_properties, s }, - { "org.freedesktop.systemd1.Swap", bus_exec_context_properties, &s->exec_context }, - { "org.freedesktop.systemd1.Swap", bus_kill_context_properties, &s->kill_context }, - { 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.h b/src/core/dbus-swap.h deleted file mode 100644 index 41fe4447ff..0000000000 --- a/src/core/dbus-swap.h +++ /dev/null @@ -1,32 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -#pragma once - -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - Copyright 2010 Maarten Lankhorst - - 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 <dbus/dbus.h> - -#include "unit.h" - -DBusHandlerResult bus_swap_message_handler(Unit *u, DBusConnection *c, DBusMessage *message); - -extern const char bus_swap_interface[]; -extern const char bus_swap_invalidating_properties[]; diff --git a/src/core/dbus-target.c b/src/core/dbus-target.c deleted file mode 100644 index 6a775506cc..0000000000 --- a/src/core/dbus-target.c +++ /dev/null @@ -1,58 +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 "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" \ - " </interface>\n" - -#define INTROSPECTION \ - DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \ - "<node>\n" \ - BUS_UNIT_INTERFACE \ - BUS_TARGET_INTERFACE \ - BUS_PROPERTIES_INTERFACE \ - BUS_PEER_INTERFACE \ - BUS_INTROSPECTABLE_INTERFACE \ - "</node>\n" - -#define INTERFACES_LIST \ - BUS_UNIT_INTERFACES_LIST \ - "org.freedesktop.systemd1.Target\0" - -const char bus_target_interface[] _introspect_("Target") = BUS_TARGET_INTERFACE; - -DBusHandlerResult bus_target_message_handler(Unit *u, DBusConnection *c, DBusMessage *message) { - const BusBoundProperties bps[] = { - { "org.freedesktop.systemd1.Unit", bus_unit_properties, u }, - { 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.h b/src/core/dbus-target.h deleted file mode 100644 index a8a0304c75..0000000000 --- a/src/core/dbus-target.h +++ /dev/null @@ -1,30 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -#pragma once - -/*** - 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 <dbus/dbus.h> - -#include "unit.h" - -DBusHandlerResult bus_target_message_handler(Unit *u, DBusConnection *c, DBusMessage *message); - -extern const char bus_target_interface[]; diff --git a/src/core/dbus-timer.c b/src/core/dbus-timer.c deleted file mode 100644 index 84b823c9a4..0000000000 --- a/src/core/dbus-timer.c +++ /dev/null @@ -1,140 +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 "dbus-unit.h" -#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" \ - " <property name=\"Unit\" type=\"s\" access=\"read\"/>\n" \ - " <property name=\"Timers\" type=\"a(stt)\" access=\"read\"/>\n" \ - " <property name=\"NextElapseUSec\" type=\"t\" access=\"read\"/>\n" \ - " <property name=\"Result\" type=\"s\" access=\"read\"/>\n" \ - " </interface>\n" - -#define INTROSPECTION \ - DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \ - "<node>\n" \ - BUS_UNIT_INTERFACE \ - BUS_TIMER_INTERFACE \ - BUS_PROPERTIES_INTERFACE \ - BUS_PEER_INTERFACE \ - BUS_INTROSPECTABLE_INTERFACE \ - "</node>\n" - -#define INTERFACES_LIST \ - BUS_UNIT_INTERFACES_LIST \ - "org.freedesktop.systemd1.Timer\0" - -const char bus_timer_interface[] _introspect_("Timer") = BUS_TIMER_INTERFACE; - -const char bus_timer_invalidating_properties[] = - "Timers\0" - "NextElapseUSec\0" - "Result\0"; - -static int bus_timer_append_timers(DBusMessageIter *i, const char *property, void *data) { - Timer *p = data; - DBusMessageIter sub, sub2; - TimerValue *k; - - assert(i); - assert(property); - assert(p); - - if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "(stt)", &sub)) - return -ENOMEM; - - LIST_FOREACH(value, k, p->values) { - char *buf; - const char *t; - size_t l; - bool b; - - t = timer_base_to_string(k->base); - assert(endswith(t, "Sec")); - - /* s/Sec/USec/ */ - l = strlen(t); - if (!(buf = new(char, l+2))) - return -ENOMEM; - - memcpy(buf, t, l-3); - memcpy(buf+l-3, "USec", 5); - - b = dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) && - dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &buf) && - dbus_message_iter_append_basic(&sub2, DBUS_TYPE_UINT64, &k->value) && - dbus_message_iter_append_basic(&sub2, DBUS_TYPE_UINT64, &k->next_elapse) && - dbus_message_iter_close_container(&sub, &sub2); - - free(buf); - if (!b) - return -ENOMEM; - } - - if (!dbus_message_iter_close_container(i, &sub)) - return -ENOMEM; - - return 0; -} - -static int bus_timer_append_unit(DBusMessageIter *i, const char *property, void *data) { - Unit *u = data; - Timer *timer = TIMER(u); - const char *t; - - assert(i); - assert(property); - assert(u); - - t = UNIT_DEREF(timer->unit) ? UNIT_DEREF(timer->unit)->id : ""; - - return dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &t) ? 0 : -ENOMEM; -} - -static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_timer_append_timer_result, timer_result, TimerResult); - -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) }, - { "Result", bus_timer_append_timer_result,"s", offsetof(Timer, result) }, - { NULL, } -}; - -DBusHandlerResult bus_timer_message_handler(Unit *u, DBusConnection *c, DBusMessage *message) { - Timer *t = TIMER(u); - const BusBoundProperties bps[] = { - { "org.freedesktop.systemd1.Unit", bus_unit_properties, u }, - { "org.freedesktop.systemd1.Timer", bus_timer_properties, t }, - { 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.h b/src/core/dbus-timer.h deleted file mode 100644 index 9ac30501d1..0000000000 --- a/src/core/dbus-timer.h +++ /dev/null @@ -1,31 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -#pragma once - -/*** - 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 <dbus/dbus.h> - -#include "unit.h" - -DBusHandlerResult bus_timer_message_handler(Unit *u, DBusConnection *c, DBusMessage *message); - -extern const char bus_timer_interface[]; -extern const char bus_timer_invalidating_properties[]; diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c deleted file mode 100644 index 83ee018ff3..0000000000 --- a/src/core/dbus-unit.c +++ /dev/null @@ -1,887 +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 "dbus.h" -#include "log.h" -#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; - -#define INVALIDATING_PROPERTIES \ - "LoadState\0" \ - "ActiveState\0" \ - "SubState\0" \ - "InactiveExitTimestamp\0" \ - "ActiveEnterTimestamp\0" \ - "ActiveExitTimestamp\0" \ - "InactiveEnterTimestamp\0" \ - "Job\0" \ - "NeedDaemonReload\0" - -static int bus_unit_append_names(DBusMessageIter *i, const char *property, void *data) { - char *t; - Iterator j; - DBusMessageIter sub; - Unit *u = data; - - if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "s", &sub)) - return -ENOMEM; - - SET_FOREACH(t, u->names, j) - if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &t)) - return -ENOMEM; - - if (!dbus_message_iter_close_container(i, &sub)) - return -ENOMEM; - - return 0; -} - -static int bus_unit_append_following(DBusMessageIter *i, const char *property, void *data) { - Unit *u = data, *f; - const char *d; - - assert(i); - assert(property); - assert(u); - - f = unit_following(u); - d = f ? f->id : ""; - - if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &d)) - return -ENOMEM; - - return 0; -} - -static int bus_unit_append_dependencies(DBusMessageIter *i, const char *property, void *data) { - Unit *u; - Iterator j; - DBusMessageIter sub; - Set *s = data; - - if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "s", &sub)) - return -ENOMEM; - - SET_FOREACH(u, s, j) - if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &u->id)) - return -ENOMEM; - - if (!dbus_message_iter_close_container(i, &sub)) - return -ENOMEM; - - return 0; -} - -static int bus_unit_append_description(DBusMessageIter *i, const char *property, void *data) { - Unit *u = data; - const char *d; - - assert(i); - assert(property); - assert(u); - - d = unit_description(u); - - if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &d)) - return -ENOMEM; - - return 0; -} - -static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_unit_append_load_state, unit_load_state, UnitLoadState); - -static int bus_unit_append_active_state(DBusMessageIter *i, const char *property, void *data) { - Unit *u = data; - const char *state; - - assert(i); - assert(property); - assert(u); - - state = unit_active_state_to_string(unit_active_state(u)); - - if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &state)) - return -ENOMEM; - - return 0; -} - -static int bus_unit_append_sub_state(DBusMessageIter *i, const char *property, void *data) { - Unit *u = data; - const char *state; - - assert(i); - assert(property); - assert(u); - - state = unit_sub_state_to_string(u); - - if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &state)) - return -ENOMEM; - - return 0; -} - -static int bus_unit_append_file_state(DBusMessageIter *i, const char *property, void *data) { - Unit *u = data; - const char *state; - - assert(i); - assert(property); - assert(u); - - state = strempty(unit_file_state_to_string(unit_get_unit_file_state(u))); - - if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &state)) - return -ENOMEM; - - return 0; -} - -static int bus_unit_append_can_start(DBusMessageIter *i, const char *property, void *data) { - Unit *u = data; - dbus_bool_t b; - - assert(i); - assert(property); - assert(u); - - b = unit_can_start(u) && - !u->refuse_manual_start; - - if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b)) - return -ENOMEM; - - return 0; -} - -static int bus_unit_append_can_stop(DBusMessageIter *i, const char *property, void *data) { - Unit *u = data; - dbus_bool_t b; - - assert(i); - assert(property); - assert(u); - - /* On the lower levels we assume that every unit we can start - * we can also stop */ - - b = unit_can_start(u) && - !u->refuse_manual_stop; - - if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b)) - return -ENOMEM; - - return 0; -} - -static int bus_unit_append_can_reload(DBusMessageIter *i, const char *property, void *data) { - Unit *u = data; - dbus_bool_t b; - - assert(i); - assert(property); - assert(u); - - b = unit_can_reload(u); - - if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b)) - return -ENOMEM; - - return 0; -} - -static int bus_unit_append_can_isolate(DBusMessageIter *i, const char *property, void *data) { - Unit *u = data; - dbus_bool_t b; - - assert(i); - assert(property); - assert(u); - - b = unit_can_isolate(u) && - !u->refuse_manual_start; - - if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b)) - return -ENOMEM; - - return 0; -} - -static int bus_unit_append_job(DBusMessageIter *i, const char *property, void *data) { - Unit *u = data; - DBusMessageIter sub; - char *p; - - assert(i); - assert(property); - assert(u); - - if (!dbus_message_iter_open_container(i, DBUS_TYPE_STRUCT, NULL, &sub)) - return -ENOMEM; - - if (u->job) { - - if (!(p = job_dbus_path(u->job))) - 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); - return -ENOMEM; - } - } else { - uint32_t id = 0; - - /* No job, so let's fill in some placeholder - * data. Since we need to fill in a valid path we - * simple point to ourselves. */ - - if (!(p = unit_dbus_path(u))) - 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); - return -ENOMEM; - } - } - - free(p); - - if (!dbus_message_iter_close_container(i, &sub)) - return -ENOMEM; - - return 0; -} - -static int bus_unit_append_default_cgroup(DBusMessageIter *i, const char *property, void *data) { - Unit *u = data; - char *t; - CGroupBonding *cgb; - bool success; - - assert(i); - assert(property); - assert(u); - - if ((cgb = unit_get_default_cgroup(u))) { - if (!(t = cgroup_bonding_to_string(cgb))) - return -ENOMEM; - } else - t = (char*) ""; - - success = dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &t); - - if (cgb) - free(t); - - return success ? 0 : -ENOMEM; -} - -static int bus_unit_append_cgroups(DBusMessageIter *i, const char *property, void *data) { - Unit *u = data; - CGroupBonding *cgb; - DBusMessageIter sub; - - if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "s", &sub)) - return -ENOMEM; - - LIST_FOREACH(by_unit, cgb, u->cgroup_bondings) { - char *t; - bool success; - - if (!(t = cgroup_bonding_to_string(cgb))) - return -ENOMEM; - - success = dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &t); - free(t); - - if (!success) - return -ENOMEM; - } - - if (!dbus_message_iter_close_container(i, &sub)) - return -ENOMEM; - - return 0; -} - -static int bus_unit_append_cgroup_attrs(DBusMessageIter *i, const char *property, void *data) { - Unit *u = data; - CGroupAttribute *a; - DBusMessageIter sub, sub2; - - if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "(sss)", &sub)) - return -ENOMEM; - - LIST_FOREACH(by_unit, a, u->cgroup_attributes) { - char *v = NULL; - bool success; - - if (a->map_callback) - a->map_callback(a->controller, a->name, a->value, &v); - - success = - dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) && - dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &a->controller) && - 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; - } - - if (!dbus_message_iter_close_container(i, &sub)) - return -ENOMEM; - - return 0; -} - -static int bus_unit_append_need_daemon_reload(DBusMessageIter *i, const char *property, void *data) { - Unit *u = data; - dbus_bool_t b; - - assert(i); - assert(property); - assert(u); - - b = unit_need_daemon_reload(u); - - if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b)) - return -ENOMEM; - - return 0; -} - -static int bus_unit_append_load_error(DBusMessageIter *i, const char *property, void *data) { - Unit *u = data; - const char *name, *message; - DBusMessageIter sub; - - assert(i); - assert(property); - assert(u); - - if (u->load_error != 0) { - name = bus_errno_to_dbus(u->load_error); - message = strempty(strerror(-u->load_error)); - } else - name = message = ""; - - if (!dbus_message_iter_open_container(i, DBUS_TYPE_STRUCT, NULL, &sub) || - !dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &name) || - !dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &message) || - !dbus_message_iter_close_container(i, &sub)) - return -ENOMEM; - - return 0; -} - -static DBusHandlerResult bus_unit_message_dispatch(Unit *u, DBusConnection *connection, DBusMessage *message) { - _cleanup_dbus_message_unref_ DBusMessage *reply = NULL; - DBusError error; - JobType job_type = _JOB_TYPE_INVALID; - bool reload_if_possible = false; - int r; - - dbus_error_init(&error); - - if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Start")) - job_type = JOB_START; - else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Stop")) - job_type = JOB_STOP; - else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Reload")) - job_type = JOB_RELOAD; - else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Restart")) - job_type = JOB_RESTART; - else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "TryRestart")) - job_type = JOB_TRY_RESTART; - else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "ReloadOrRestart")) { - reload_if_possible = true; - job_type = JOB_RESTART; - } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "ReloadOrTryRestart")) { - reload_if_possible = true; - job_type = JOB_TRY_RESTART; - } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Kill")) { - const char *swho; - int32_t signo; - KillWho who; - - if (!dbus_message_get_args( - message, - &error, - DBUS_TYPE_STRING, &swho, - DBUS_TYPE_INT32, &signo, - DBUS_TYPE_INVALID)) - return bus_send_error_reply(connection, message, &error, -EINVAL); - - if (isempty(swho)) - who = KILL_ALL; - else { - who = kill_who_from_string(swho); - if (who < 0) - return bus_send_error_reply(connection, message, &error, -EINVAL); - } - - 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); - - reply = dbus_message_new_method_return(message); - if (!reply) - goto oom; - - } 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); - - reply = dbus_message_new_method_return(message); - if (!reply) - goto oom; - - } else if (UNIT_VTABLE(u)->bus_message_handler) - return UNIT_VTABLE(u)->bus_message_handler(u, connection, message); - else - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - - if (job_type != _JOB_TYPE_INVALID) { - const char *smode; - JobMode mode; - - if (!dbus_message_get_args( - message, - &error, - DBUS_TYPE_STRING, &smode, - DBUS_TYPE_INVALID)) - return bus_send_error_reply(connection, message, &error, -EINVAL); - - 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); - } - - return bus_unit_queue_job(connection, message, u, job_type, mode, reload_if_possible); - } - - if (reply) - 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; -} - -static DBusHandlerResult bus_unit_message_handler(DBusConnection *connection, DBusMessage *message, void *data) { - Manager *m = data; - Unit *u; - int r; - _cleanup_dbus_message_unref_ DBusMessage *reply = NULL; - DBusError error; - - assert(connection); - assert(message); - assert(m); - - dbus_error_init(&error); - - 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; - Iterator i; - const char *k; - size_t size; - - reply = dbus_message_new_method_return(message); - if (!reply) - goto oom; - - /* We roll our own introspection code here, instead of - * relying on bus_default_message_handler() because we - * need to generate our introspection string - * dynamically. */ - - f = open_memstream(&introspection, &size); - if (!f) - goto oom; - - fputs(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE - "<node>\n", f); - - fputs(BUS_INTROSPECTABLE_INTERFACE, f); - fputs(BUS_PEER_INTERFACE, f); - - HASHMAP_FOREACH_KEY(u, k, m->units, i) { - char *p; - - if (k != u->id) - continue; - - p = bus_path_escape(k); - if (!p) { - fclose(f); - free(introspection); - goto oom; - } - - fprintf(f, "<node name=\"%s\"/>", p); - free(p); - } - - fputs("</node>\n", f); - - if (ferror(f)) { - fclose(f); - free(introspection); - goto oom; - } - - fclose(f); - - if (!introspection) - goto oom; - - if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &introspection, DBUS_TYPE_INVALID)) { - free(introspection); - goto oom; - } - - free(introspection); - - if (!dbus_connection_send(connection, reply, NULL)) - goto oom; - - return DBUS_HANDLER_RESULT_HANDLED; - } - - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - } - - r = manager_load_unit_from_dbus_path(m, dbus_message_get_path(message), &error, &u); - 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: - dbus_error_free(&error); - - return DBUS_HANDLER_RESULT_NEED_MEMORY; -} - -const DBusObjectPathVTable bus_unit_vtable = { - .message_function = bus_unit_message_handler -}; - -void bus_unit_send_change_signal(Unit *u) { - char *p = NULL; - DBusMessage *m = NULL; - - assert(u); - - if (u->in_dbus_queue) { - LIST_REMOVE(Unit, dbus_queue, u->manager->dbus_unit_queue, u); - u->in_dbus_queue = false; - } - - if (!u->id) - return; - - if (!bus_has_subscriber(u->manager)) { - u->sent_dbus_new_signal = true; - return; - } - - if (!(p = unit_dbus_path(u))) - 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 - * 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))) - goto oom; - - if (bus_broadcast(u->manager, m) < 0) - goto oom; - - dbus_message_unref(m); - } - - if (!(m = bus_properties_changed_new(p, "org.freedesktop.systemd1.Unit", INVALIDATING_PROPERTIES))) - goto oom; - - } else { - /* Send a new signal */ - - if (!(m = dbus_message_new_signal("/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "UnitNew"))) - goto oom; - - if (!dbus_message_append_args(m, - DBUS_TYPE_STRING, &u->id, - DBUS_TYPE_OBJECT_PATH, &p, - DBUS_TYPE_INVALID)) - goto oom; - } - - 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."); -} - -void bus_unit_send_removed_signal(Unit *u) { - char *p = NULL; - DBusMessage *m = NULL; - - assert(u); - - if (!bus_has_subscriber(u->manager)) - return; - - if (!u->sent_dbus_new_signal) - bus_unit_send_change_signal(u); - - if (!u->id) - return; - - if (!(p = unit_dbus_path(u))) - goto oom; - - if (!(m = dbus_message_new_signal("/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "UnitRemoved"))) - goto oom; - - if (!dbus_message_append_args(m, - DBUS_TYPE_STRING, &u->id, - DBUS_TYPE_OBJECT_PATH, &p, - DBUS_TYPE_INVALID)) - goto oom; - - if (bus_broadcast(u->manager, m) < 0) - goto oom; - - free(p); - dbus_message_unref(m); - - return; - -oom: - free(p); - - if (m) - dbus_message_unref(m); - - log_error("Failed to allocate unit remove signal."); -} - -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 (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); - } - - 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[] = { - { "Id", bus_property_append_string, "s", offsetof(Unit, id), true }, - { "Names", bus_unit_append_names, "as", 0 }, - { "Following", bus_unit_append_following, "s", 0 }, - { "Requires", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUIRES]), true }, - { "RequiresOverridable", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUIRES_OVERRIDABLE]), true }, - { "Requisite", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUISITE]), true }, - { "RequisiteOverridable", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUISITE_OVERRIDABLE]), true }, - { "Wants", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_WANTS]), true }, - { "BindsTo", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_BINDS_TO]), true }, - { "PartOf", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_PART_OF]), true }, - { "RequiredBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUIRED_BY]), true }, - { "RequiredByOverridable",bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUIRED_BY_OVERRIDABLE]), true }, - { "WantedBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_WANTED_BY]), true }, - { "BoundBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_BOUND_BY]), true }, - { "ConsistsOf", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_CONSISTS_OF]), true }, - { "Conflicts", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_CONFLICTS]), true }, - { "ConflictedBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_CONFLICTED_BY]), true }, - { "Before", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_BEFORE]), true }, - { "After", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_AFTER]), true }, - { "OnFailure", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_ON_FAILURE]), true }, - { "Triggers", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_TRIGGERS]), true }, - { "TriggeredBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_TRIGGERED_BY]), true }, - { "PropagatesReloadTo", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_PROPAGATES_RELOAD_TO]), true }, - { "ReloadPropagatedFrom", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_RELOAD_PROPAGATED_FROM]), true }, - { "RequiresMountsFor", bus_property_append_strv, "as", offsetof(Unit, requires_mounts_for), true }, - { "Documentation", bus_property_append_strv, "as", offsetof(Unit, documentation), true }, - { "Description", bus_unit_append_description, "s", 0 }, - { "LoadState", bus_unit_append_load_state, "s", offsetof(Unit, load_state) }, - { "ActiveState", bus_unit_append_active_state, "s", 0 }, - { "SubState", bus_unit_append_sub_state, "s", 0 }, - { "FragmentPath", bus_property_append_string, "s", offsetof(Unit, fragment_path), true }, - { "SourcePath", bus_property_append_string, "s", offsetof(Unit, source_path), true }, - { "UnitFileState", bus_unit_append_file_state, "s", 0 }, - { "InactiveExitTimestamp",bus_property_append_usec, "t", offsetof(Unit, inactive_exit_timestamp.realtime) }, - { "InactiveExitTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, inactive_exit_timestamp.monotonic) }, - { "ActiveEnterTimestamp", bus_property_append_usec, "t", offsetof(Unit, active_enter_timestamp.realtime) }, - { "ActiveEnterTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, active_enter_timestamp.monotonic) }, - { "ActiveExitTimestamp", bus_property_append_usec, "t", offsetof(Unit, active_exit_timestamp.realtime) }, - { "ActiveExitTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, active_exit_timestamp.monotonic) }, - { "InactiveEnterTimestamp", bus_property_append_usec, "t", offsetof(Unit, inactive_enter_timestamp.realtime) }, - { "InactiveEnterTimestampMonotonic",bus_property_append_usec, "t", offsetof(Unit, inactive_enter_timestamp.monotonic) }, - { "CanStart", bus_unit_append_can_start, "b", 0 }, - { "CanStop", bus_unit_append_can_stop, "b", 0 }, - { "CanReload", bus_unit_append_can_reload, "b", 0 }, - { "CanIsolate", bus_unit_append_can_isolate, "b", 0 }, - { "Job", bus_unit_append_job, "(uo)", 0 }, - { "StopWhenUnneeded", bus_property_append_bool, "b", offsetof(Unit, stop_when_unneeded) }, - { "RefuseManualStart", bus_property_append_bool, "b", offsetof(Unit, refuse_manual_start) }, - { "RefuseManualStop", bus_property_append_bool, "b", offsetof(Unit, refuse_manual_stop) }, - { "AllowIsolate", bus_property_append_bool, "b", offsetof(Unit, allow_isolate) }, - { "DefaultDependencies", bus_property_append_bool, "b", offsetof(Unit, default_dependencies) }, - { "OnFailureIsolate", bus_property_append_bool, "b", offsetof(Unit, on_failure_isolate) }, - { "IgnoreOnIsolate", bus_property_append_bool, "b", offsetof(Unit, ignore_on_isolate) }, - { "IgnoreOnSnapshot", bus_property_append_bool, "b", offsetof(Unit, ignore_on_snapshot) }, - { "DefaultControlGroup", bus_unit_append_default_cgroup, "s", 0 }, - { "ControlGroup", bus_unit_append_cgroups, "as", 0 }, - { "ControlGroupAttributes", bus_unit_append_cgroup_attrs,"a(sss)", 0 }, - { "NeedDaemonReload", bus_unit_append_need_daemon_reload, "b", 0 }, - { "JobTimeoutUSec", bus_property_append_usec, "t", offsetof(Unit, job_timeout) }, - { "ConditionTimestamp", bus_property_append_usec, "t", offsetof(Unit, condition_timestamp.realtime) }, - { "ConditionTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, condition_timestamp.monotonic) }, - { "ConditionResult", bus_property_append_bool, "b", offsetof(Unit, condition_result) }, - { "LoadError", bus_unit_append_load_error, "(ss)", 0 }, - { NULL, } -}; diff --git a/src/core/dbus-unit.h b/src/core/dbus-unit.h deleted file mode 100644 index ac6785a949..0000000000 --- a/src/core/dbus-unit.h +++ /dev/null @@ -1,147 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -#pragma once - -/*** - 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 <dbus/dbus.h> - -#include "manager.h" -#include "dbus-common.h" - -#define BUS_UNIT_INTERFACE \ - " <interface name=\"org.freedesktop.systemd1.Unit\">\n" \ - " <method name=\"Start\">\n" \ - " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \ - " <arg name=\"job\" type=\"o\" direction=\"out\"/>\n" \ - " </method>\n" \ - " <method name=\"Stop\">\n" \ - " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \ - " <arg name=\"job\" type=\"o\" direction=\"out\"/>\n" \ - " </method>\n" \ - " <method name=\"Reload\">\n" \ - " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \ - " <arg name=\"job\" type=\"o\" direction=\"out\"/>\n" \ - " </method>\n" \ - " <method name=\"Restart\">\n" \ - " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \ - " <arg name=\"job\" type=\"o\" direction=\"out\"/>\n" \ - " </method>\n" \ - " <method name=\"TryRestart\">\n" \ - " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \ - " <arg name=\"job\" type=\"o\" direction=\"out\"/>\n" \ - " </method>\n" \ - " <method name=\"ReloadOrRestart\">\n" \ - " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \ - " <arg name=\"job\" type=\"o\" direction=\"out\"/>\n" \ - " </method>\n" \ - " <method name=\"ReloadOrTryRestart\">\n" \ - " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \ - " <arg name=\"job\" type=\"o\" direction=\"out\"/>\n" \ - " </method>\n" \ - " <method name=\"Kill\">\n" \ - " <arg name=\"who\" type=\"s\" direction=\"in\"/>\n" \ - " <arg name=\"signal\" type=\"i\" direction=\"in\"/>\n" \ - " </method>\n" \ - " <method name=\"ResetFailed\"/>\n" \ - " <property name=\"Id\" type=\"s\" access=\"read\"/>\n" \ - " <property name=\"Names\" type=\"as\" access=\"read\"/>\n" \ - " <property name=\"Following\" type=\"s\" access=\"read\"/>\n" \ - " <property name=\"Requires\" type=\"as\" access=\"read\"/>\n" \ - " <property name=\"RequiresOverridable\" type=\"as\" access=\"read\"/>\n" \ - " <property name=\"Requisite\" type=\"as\" access=\"read\"/>\n" \ - " <property name=\"RequisiteOverridable\" type=\"as\" access=\"read\"/>\n" \ - " <property name=\"Wants\" type=\"as\" access=\"read\"/>\n" \ - " <property name=\"BindsTo\" type=\"as\" access=\"read\"/>\n" \ - " <property name=\"RequiredBy\" type=\"as\" access=\"read\"/>\n" \ - " <property name=\"RequiredByOverridable\" type=\"as\" access=\"read\"/>\n" \ - " <property name=\"WantedBy\" type=\"as\" access=\"read\"/>\n" \ - " <property name=\"BoundBy\" type=\"as\" access=\"read\"/>\n" \ - " <property name=\"Conflicts\" type=\"as\" access=\"read\"/>\n" \ - " <property name=\"ConflictedBy\" type=\"as\" access=\"read\"/>\n" \ - " <property name=\"Before\" type=\"as\" access=\"read\"/>\n" \ - " <property name=\"After\" type=\"as\" access=\"read\"/>\n" \ - " <property name=\"OnFailure\" type=\"as\" access=\"read\"/>\n" \ - " <property name=\"Triggers\" type=\"as\" access=\"read\"/>\n" \ - " <property name=\"TriggeredBy\" type=\"as\" access=\"read\"/>\n" \ - " <property name=\"PropagatesReloadTo\" type=\"as\" access=\"read\"/>\n" \ - " <property name=\"ReloadPropagatedFrom\" type=\"as\" access=\"read\"/>\n" \ - " <property name=\"RequiresMountsFor\" type=\"as\" access=\"read\"/>\n" \ - " <property name=\"Description\" type=\"s\" access=\"read\"/>\n" \ - " <property name=\"SourcePath\" type=\"s\" access=\"read\"/>\n" \ - " <property name=\"Documentation\" type=\"as\" access=\"read\"/>\n" \ - " <property name=\"LoadState\" type=\"s\" access=\"read\"/>\n" \ - " <property name=\"ActiveState\" type=\"s\" access=\"read\"/>\n" \ - " <property name=\"SubState\" type=\"s\" access=\"read\"/>\n" \ - " <property name=\"FragmentPath\" type=\"s\" access=\"read\"/>\n" \ - " <property name=\"UnitFileState\" type=\"s\" access=\"read\"/>\n" \ - " <property name=\"InactiveExitTimestamp\" type=\"t\" access=\"read\"/>\n" \ - " <property name=\"InactiveExitTimestampMonotonic\" type=\"t\" access=\"read\"/>\n" \ - " <property name=\"ActiveEnterTimestamp\" type=\"t\" access=\"read\"/>\n" \ - " <property name=\"ActiveEnterTimestampMonotonic\" type=\"t\" access=\"read\"/>\n" \ - " <property name=\"ActiveExitTimestamp\" type=\"t\" access=\"read\"/>\n" \ - " <property name=\"ActiveExitTimestampMonotonic\" type=\"t\" access=\"read\"/>\n" \ - " <property name=\"InactiveEnterTimestamp\" type=\"t\" access=\"read\"/>\n" \ - " <property name=\"InactiveEnterTimestampMonotonic\" type=\"t\" access=\"read\"/>\n" \ - " <property name=\"CanStart\" type=\"b\" access=\"read\"/>\n" \ - " <property name=\"CanStop\" type=\"b\" access=\"read\"/>\n" \ - " <property name=\"CanReload\" type=\"b\" access=\"read\"/>\n" \ - " <property name=\"CanIsolate\" type=\"b\" access=\"read\"/>\n" \ - " <property name=\"Job\" type=\"(uo)\" access=\"read\"/>\n" \ - " <property name=\"StopWhenUnneeded\" type=\"b\" access=\"read\"/>\n" \ - " <property name=\"RefuseManualStart\" type=\"b\" access=\"read\"/>\n" \ - " <property name=\"RefuseManualStop\" type=\"b\" access=\"read\"/>\n" \ - " <property name=\"AllowIsolate\" type=\"b\" access=\"read\"/>\n" \ - " <property name=\"DefaultDependencies\" type=\"b\" access=\"read\"/>\n" \ - " <property name=\"OnFailureIsolate\" type=\"b\" access=\"read\"/>\n" \ - " <property name=\"IgnoreOnIsolate\" type=\"b\" access=\"read\"/>\n" \ - " <property name=\"IgnoreOnSnapshot\" type=\"b\" access=\"read\"/>\n" \ - " <property name=\"DefaultControlGroup\" type=\"s\" access=\"read\"/>\n" \ - " <property name=\"ControlGroup\" type=\"as\" access=\"read\"/>\n" \ - " <property name=\"ControlGroupAttributes\" type=\"a(sss)\" access=\"read\"/>\n" \ - " <property name=\"NeedDaemonReload\" type=\"b\" access=\"read\"/>\n" \ - " <property name=\"JobTimeoutUSec\" type=\"t\" access=\"read\"/>\n" \ - " <property name=\"ConditionTimestamp\" type=\"t\" access=\"read\"/>\n" \ - " <property name=\"ConditionTimestampMonotonic\" type=\"t\" access=\"read\"/>\n" \ - " <property name=\"ConditionResult\" type=\"b\" access=\"read\"/>\n" \ - " <property name=\"LoadError\" type=\"(ss)\" access=\"read\"/>\n" \ - " </interface>\n" - -#define BUS_UNIT_INTERFACES_LIST \ - BUS_GENERIC_INTERFACES_LIST \ - "org.freedesktop.systemd1.Unit\0" - -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 deleted file mode 100644 index 2a1c66054a..0000000000 --- a/src/core/dbus.c +++ /dev/null @@ -1,1479 +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 <sys/epoll.h> -#include <sys/timerfd.h> -#include <errno.h> -#include <unistd.h> -#include <dbus/dbus.h> - -#include "dbus.h" -#include "log.h" -#include "strv.h" -#include "cgroup.h" -#include "mkdir.h" -#include "missing.h" -#include "dbus-unit.h" -#include "dbus-job.h" -#include "dbus-manager.h" -#include "dbus-service.h" -#include "dbus-socket.h" -#include "dbus-target.h" -#include "dbus-device.h" -#include "dbus-mount.h" -#include "dbus-automount.h" -#include "dbus-snapshot.h" -#include "dbus-swap.h" -#include "dbus-timer.h" -#include "dbus-path.h" -#include "bus-errors.h" -#include "special.h" -#include "dbus-common.h" - -#define CONNECTIONS_MAX 52 - -/* Well-known address (http://dbus.freedesktop.org/doc/dbus-specification.html#message-bus-types) */ -#define DBUS_SYSTEM_BUS_DEFAULT_ADDRESS "unix:path=/var/run/dbus/system_bus_socket" -/* Only used as a fallback */ -#define DBUS_SESSION_BUS_DEFAULT_ADDRESS "autolaunch:" - -static const char bus_properties_interface[] = BUS_PROPERTIES_INTERFACE; -static const char bus_introspectable_interface[] = BUS_INTROSPECTABLE_INTERFACE; - -const char *const bus_interface_table[] = { - "org.freedesktop.DBus.Properties", bus_properties_interface, - "org.freedesktop.DBus.Introspectable", bus_introspectable_interface, - "org.freedesktop.systemd1.Manager", bus_manager_interface, - "org.freedesktop.systemd1.Job", bus_job_interface, - "org.freedesktop.systemd1.Unit", bus_unit_interface, - "org.freedesktop.systemd1.Service", bus_service_interface, - "org.freedesktop.systemd1.Socket", bus_socket_interface, - "org.freedesktop.systemd1.Target", bus_target_interface, - "org.freedesktop.systemd1.Device", bus_device_interface, - "org.freedesktop.systemd1.Mount", bus_mount_interface, - "org.freedesktop.systemd1.Automount", bus_automount_interface, - "org.freedesktop.systemd1.Snapshot", bus_snapshot_interface, - "org.freedesktop.systemd1.Swap", bus_swap_interface, - "org.freedesktop.systemd1.Timer", bus_timer_interface, - "org.freedesktop.systemd1.Path", bus_path_interface, - NULL -}; - -static void bus_done_api(Manager *m); -static void bus_done_system(Manager *m); -static void bus_done_private(Manager *m); -static void shutdown_connection(Manager *m, DBusConnection *c); - -static void bus_dispatch_status(DBusConnection *bus, DBusDispatchStatus status, void *data) { - Manager *m = data; - - assert(bus); - assert(m); - - /* We maintain two sets, one for those connections where we - * requested a dispatch, and another where we didn't. And then, - * we move the connections between the two sets. */ - - if (status == DBUS_DISPATCH_COMPLETE) - set_move_one(m->bus_connections, m->bus_connections_for_dispatch, bus); - else - set_move_one(m->bus_connections_for_dispatch, m->bus_connections, bus); -} - -void bus_watch_event(Manager *m, Watch *w, int events) { - assert(m); - assert(w); - - /* This is called by the event loop whenever there is - * something happening on D-Bus' file handles. */ - - if (!dbus_watch_get_enabled(w->data.bus_watch)) - return; - - dbus_watch_handle(w->data.bus_watch, bus_events_to_flags(events)); -} - -static dbus_bool_t bus_add_watch(DBusWatch *bus_watch, void *data) { - Manager *m = data; - Watch *w; - struct epoll_event ev; - - assert(bus_watch); - assert(m); - - if (!(w = new0(Watch, 1))) - return FALSE; - - w->fd = dbus_watch_get_unix_fd(bus_watch); - w->type = WATCH_DBUS_WATCH; - w->data.bus_watch = bus_watch; - - zero(ev); - ev.events = bus_flags_to_events(bus_watch); - ev.data.ptr = w; - - if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, w->fd, &ev) < 0) { - - if (errno != EEXIST) { - free(w); - return FALSE; - } - - /* Hmm, bloody D-Bus creates multiple watches on the - * same fd. epoll() does not like that. As a dirty - * hack we simply dup() the fd and hence get a second - * one we can safely add to the epoll(). */ - - if ((w->fd = dup(w->fd)) < 0) { - free(w); - return FALSE; - } - - if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, w->fd, &ev) < 0) { - close_nointr_nofail(w->fd); - free(w); - return FALSE; - } - - w->fd_is_dupped = true; - } - - dbus_watch_set_data(bus_watch, w, NULL); - - return TRUE; -} - -static void bus_remove_watch(DBusWatch *bus_watch, void *data) { - Manager *m = data; - Watch *w; - - assert(bus_watch); - assert(m); - - w = dbus_watch_get_data(bus_watch); - if (!w) - return; - - assert(w->type == WATCH_DBUS_WATCH); - assert_se(epoll_ctl(m->epoll_fd, EPOLL_CTL_DEL, w->fd, NULL) >= 0); - - if (w->fd_is_dupped) - close_nointr_nofail(w->fd); - - free(w); -} - -static void bus_toggle_watch(DBusWatch *bus_watch, void *data) { - Manager *m = data; - Watch *w; - struct epoll_event ev; - - assert(bus_watch); - assert(m); - - w = dbus_watch_get_data(bus_watch); - if (!w) - return; - - assert(w->type == WATCH_DBUS_WATCH); - - zero(ev); - ev.events = bus_flags_to_events(bus_watch); - ev.data.ptr = w; - - assert_se(epoll_ctl(m->epoll_fd, EPOLL_CTL_MOD, w->fd, &ev) == 0); -} - -static int bus_timeout_arm(Manager *m, Watch *w) { - struct itimerspec its; - - assert(m); - assert(w); - - zero(its); - - if (dbus_timeout_get_enabled(w->data.bus_timeout)) { - timespec_store(&its.it_value, dbus_timeout_get_interval(w->data.bus_timeout) * USEC_PER_MSEC); - its.it_interval = its.it_value; - } - - if (timerfd_settime(w->fd, 0, &its, NULL) < 0) - return -errno; - - return 0; -} - -void bus_timeout_event(Manager *m, Watch *w, int events) { - assert(m); - assert(w); - - /* This is called by the event loop whenever there is - * something happening on D-Bus' file handles. */ - - if (!(dbus_timeout_get_enabled(w->data.bus_timeout))) - return; - - dbus_timeout_handle(w->data.bus_timeout); -} - -static dbus_bool_t bus_add_timeout(DBusTimeout *timeout, void *data) { - Manager *m = data; - Watch *w; - struct epoll_event ev; - - assert(timeout); - assert(m); - - if (!(w = new0(Watch, 1))) - return FALSE; - - if ((w->fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK|TFD_CLOEXEC)) < 0) - goto fail; - - w->type = WATCH_DBUS_TIMEOUT; - w->data.bus_timeout = timeout; - - if (bus_timeout_arm(m, w) < 0) - goto fail; - - zero(ev); - ev.events = EPOLLIN; - ev.data.ptr = w; - - if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, w->fd, &ev) < 0) - goto fail; - - dbus_timeout_set_data(timeout, w, NULL); - - return TRUE; - -fail: - if (w->fd >= 0) - close_nointr_nofail(w->fd); - - free(w); - return FALSE; -} - -static void bus_remove_timeout(DBusTimeout *timeout, void *data) { - Manager *m = data; - Watch *w; - - assert(timeout); - assert(m); - - w = dbus_timeout_get_data(timeout); - if (!w) - return; - - assert(w->type == WATCH_DBUS_TIMEOUT); - - assert_se(epoll_ctl(m->epoll_fd, EPOLL_CTL_DEL, w->fd, NULL) >= 0); - close_nointr_nofail(w->fd); - free(w); -} - -static void bus_toggle_timeout(DBusTimeout *timeout, void *data) { - Manager *m = data; - Watch *w; - int r; - - assert(timeout); - assert(m); - - w = dbus_timeout_get_data(timeout); - if (!w) - return; - - assert(w->type == WATCH_DBUS_TIMEOUT); - - if ((r = bus_timeout_arm(m, w)) < 0) - log_error("Failed to rearm timer: %s", strerror(-r)); -} - -static DBusHandlerResult api_bus_message_filter(DBusConnection *connection, DBusMessage *message, void *data) { - Manager *m = data; - DBusError error; - DBusMessage *reply = NULL; - - assert(connection); - assert(message); - assert(m); - - dbus_error_init(&error); - - if (dbus_message_get_type(message) == DBUS_MESSAGE_TYPE_METHOD_CALL || - dbus_message_get_type(message) == DBUS_MESSAGE_TYPE_SIGNAL) - log_debug("Got D-Bus request: %s.%s() on %s", - dbus_message_get_interface(message), - dbus_message_get_member(message), - dbus_message_get_path(message)); - - if (dbus_message_is_signal(message, DBUS_INTERFACE_LOCAL, "Disconnected")) { - log_debug("API D-Bus connection terminated."); - bus_done_api(m); - - } else if (dbus_message_is_signal(message, DBUS_INTERFACE_DBUS, "NameOwnerChanged")) { - const char *name, *old_owner, *new_owner; - - if (!dbus_message_get_args(message, &error, - DBUS_TYPE_STRING, &name, - DBUS_TYPE_STRING, &old_owner, - DBUS_TYPE_STRING, &new_owner, - DBUS_TYPE_INVALID)) - log_error("Failed to parse NameOwnerChanged message: %s", bus_error_message(&error)); - else { - if (set_remove(BUS_CONNECTION_SUBSCRIBED(m, connection), (char*) name)) - log_debug("Subscription client vanished: %s (left: %u)", name, set_size(BUS_CONNECTION_SUBSCRIBED(m, connection))); - - if (old_owner[0] == 0) - old_owner = NULL; - - if (new_owner[0] == 0) - new_owner = NULL; - - manager_dispatch_bus_name_owner_changed(m, name, old_owner, new_owner); - } - } else if (dbus_message_is_signal(message, "org.freedesktop.systemd1.Activator", "ActivationRequest")) { - const char *name; - - if (!dbus_message_get_args(message, &error, - DBUS_TYPE_STRING, &name, - DBUS_TYPE_INVALID)) - log_error("Failed to parse ActivationRequest message: %s", bus_error_message(&error)); - else { - int r; - Unit *u; - - log_debug("Got D-Bus activation request for %s", name); - - if (manager_unit_pending_inactive(m, SPECIAL_DBUS_SERVICE) || - manager_unit_pending_inactive(m, SPECIAL_DBUS_SOCKET)) { - r = -EADDRNOTAVAIL; - dbus_set_error(&error, BUS_ERROR_SHUTTING_DOWN, "Refusing activation, D-Bus is shutting down."); - } else { - r = manager_load_unit(m, name, NULL, &error, &u); - - if (r >= 0 && u->refuse_manual_start) - r = -EPERM; - - if (r >= 0) - r = manager_add_job(m, JOB_START, u, JOB_REPLACE, true, &error, NULL); - } - - if (r < 0) { - const char *id, *text; - - log_debug("D-Bus activation failed for %s: %s", name, strerror(-r)); - - if (!(reply = dbus_message_new_signal("/org/freedesktop/systemd1", "org.freedesktop.systemd1.Activator", "ActivationFailure"))) - goto oom; - - id = error.name ? error.name : bus_errno_to_dbus(r); - text = bus_error(&error, r); - - if (!dbus_message_set_destination(reply, DBUS_SERVICE_DBUS) || - !dbus_message_append_args(reply, - DBUS_TYPE_STRING, &name, - DBUS_TYPE_STRING, &id, - DBUS_TYPE_STRING, &text, - DBUS_TYPE_INVALID)) - goto oom; - } - - /* On success we don't do anything, the service will be spawned now */ - } - } - - dbus_error_free(&error); - - if (reply) { - if (!dbus_connection_send(connection, reply, NULL)) - goto oom; - - dbus_message_unref(reply); - } - - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - -oom: - if (reply) - dbus_message_unref(reply); - - dbus_error_free(&error); - - return DBUS_HANDLER_RESULT_NEED_MEMORY; -} - -static DBusHandlerResult system_bus_message_filter(DBusConnection *connection, DBusMessage *message, void *data) { - Manager *m = data; - DBusError error; - - assert(connection); - assert(message); - assert(m); - - dbus_error_init(&error); - - if (m->api_bus != m->system_bus && - (dbus_message_get_type(message) == DBUS_MESSAGE_TYPE_METHOD_CALL || - dbus_message_get_type(message) == DBUS_MESSAGE_TYPE_SIGNAL)) - log_debug("Got D-Bus request on system bus: %s.%s() on %s", - dbus_message_get_interface(message), - dbus_message_get_member(message), - dbus_message_get_path(message)); - - if (dbus_message_is_signal(message, DBUS_INTERFACE_LOCAL, "Disconnected")) { - log_debug("System D-Bus connection terminated."); - bus_done_system(m); - - } else if (m->running_as != SYSTEMD_SYSTEM && - dbus_message_is_signal(message, "org.freedesktop.systemd1.Agent", "Released")) { - - const char *cgroup; - - if (!dbus_message_get_args(message, &error, - DBUS_TYPE_STRING, &cgroup, - DBUS_TYPE_INVALID)) - log_error("Failed to parse Released message: %s", bus_error_message(&error)); - else - cgroup_notify_empty(m, cgroup); - } - - dbus_error_free(&error); - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; -} - -static DBusHandlerResult private_bus_message_filter(DBusConnection *connection, DBusMessage *message, void *data) { - Manager *m = data; - DBusError error; - - assert(connection); - assert(message); - assert(m); - - dbus_error_init(&error); - - if (dbus_message_get_type(message) == DBUS_MESSAGE_TYPE_METHOD_CALL || - dbus_message_get_type(message) == DBUS_MESSAGE_TYPE_SIGNAL) - log_debug("Got D-Bus request: %s.%s() on %s", - dbus_message_get_interface(message), - dbus_message_get_member(message), - dbus_message_get_path(message)); - - if (dbus_message_is_signal(message, DBUS_INTERFACE_LOCAL, "Disconnected")) - shutdown_connection(m, connection); - else if (m->running_as == SYSTEMD_SYSTEM && - dbus_message_is_signal(message, "org.freedesktop.systemd1.Agent", "Released")) { - - const char *cgroup; - - if (!dbus_message_get_args(message, &error, - DBUS_TYPE_STRING, &cgroup, - DBUS_TYPE_INVALID)) - log_error("Failed to parse Released message: %s", bus_error_message(&error)); - else - cgroup_notify_empty(m, cgroup); - - /* Forward the message to the system bus, so that user - * instances are notified as well */ - - if (m->system_bus) - dbus_connection_send(m->system_bus, message, NULL); - } - - dbus_error_free(&error); - - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; -} - -unsigned bus_dispatch(Manager *m) { - DBusConnection *c; - - assert(m); - - if (m->queued_message) { - /* If we cannot get rid of this message we won't - * dispatch any D-Bus messages, so that we won't end - * up wanting to queue another message. */ - - if (m->queued_message_connection) - if (!dbus_connection_send(m->queued_message_connection, m->queued_message, NULL)) - return 0; - - dbus_message_unref(m->queued_message); - m->queued_message = NULL; - m->queued_message_connection = NULL; - } - - if ((c = set_first(m->bus_connections_for_dispatch))) { - if (dbus_connection_dispatch(c) == DBUS_DISPATCH_COMPLETE) - set_move_one(m->bus_connections, m->bus_connections_for_dispatch, c); - - return 1; - } - - return 0; -} - -static void request_name_pending_cb(DBusPendingCall *pending, void *userdata) { - DBusMessage *reply; - DBusError error; - - dbus_error_init(&error); - - assert_se(reply = dbus_pending_call_steal_reply(pending)); - - switch (dbus_message_get_type(reply)) { - - case DBUS_MESSAGE_TYPE_ERROR: - - assert_se(dbus_set_error_from_message(&error, reply)); - log_warning("RequestName() failed: %s", bus_error_message(&error)); - break; - - case DBUS_MESSAGE_TYPE_METHOD_RETURN: { - uint32_t r; - - if (!dbus_message_get_args(reply, - &error, - DBUS_TYPE_UINT32, &r, - DBUS_TYPE_INVALID)) { - log_error("Failed to parse RequestName() reply: %s", bus_error_message(&error)); - break; - } - - if (r == 1) - log_debug("Successfully acquired name."); - else - log_error("Name already owned."); - - break; - } - - default: - assert_not_reached("Invalid reply message"); - } - - dbus_message_unref(reply); - dbus_error_free(&error); -} - -static int request_name(Manager *m) { - const char *name = "org.freedesktop.systemd1"; - /* Allow replacing of our name, to ease implementation of - * reexecution, where we keep the old connection open until - * after the new connection is set up and the name installed - * to allow clients to synchronously wait for reexecution to - * finish */ - uint32_t flags = DBUS_NAME_FLAG_ALLOW_REPLACEMENT|DBUS_NAME_FLAG_REPLACE_EXISTING; - DBusMessage *message = NULL; - DBusPendingCall *pending = NULL; - - if (!(message = dbus_message_new_method_call( - DBUS_SERVICE_DBUS, - DBUS_PATH_DBUS, - DBUS_INTERFACE_DBUS, - "RequestName"))) - goto oom; - - if (!dbus_message_append_args( - message, - DBUS_TYPE_STRING, &name, - DBUS_TYPE_UINT32, &flags, - DBUS_TYPE_INVALID)) - goto oom; - - if (!dbus_connection_send_with_reply(m->api_bus, message, &pending, -1)) - goto oom; - - if (!dbus_pending_call_set_notify(pending, request_name_pending_cb, m, NULL)) - goto oom; - - dbus_message_unref(message); - dbus_pending_call_unref(pending); - - /* We simple ask for the name and don't wait for it. Sooner or - * later we'll have it. */ - - return 0; - -oom: - if (pending) { - dbus_pending_call_cancel(pending); - dbus_pending_call_unref(pending); - } - - if (message) - dbus_message_unref(message); - - return -ENOMEM; -} - -static void query_name_list_pending_cb(DBusPendingCall *pending, void *userdata) { - DBusMessage *reply; - DBusError error; - Manager *m = userdata; - - assert(m); - - dbus_error_init(&error); - - assert_se(reply = dbus_pending_call_steal_reply(pending)); - - switch (dbus_message_get_type(reply)) { - - case DBUS_MESSAGE_TYPE_ERROR: - - assert_se(dbus_set_error_from_message(&error, reply)); - log_warning("ListNames() failed: %s", bus_error_message(&error)); - break; - - case DBUS_MESSAGE_TYPE_METHOD_RETURN: { - int r; - char **l; - - if ((r = bus_parse_strv(reply, &l)) < 0) - log_warning("Failed to parse ListNames() reply: %s", strerror(-r)); - else { - char **t; - - STRV_FOREACH(t, l) - /* This is a bit hacky, we say the - * owner of the name is the name - * itself, because we don't want the - * extra traffic to figure out the - * real owner. */ - manager_dispatch_bus_name_owner_changed(m, *t, NULL, *t); - - strv_free(l); - } - - break; - } - - default: - assert_not_reached("Invalid reply message"); - } - - dbus_message_unref(reply); - dbus_error_free(&error); -} - -static int query_name_list(Manager *m) { - DBusMessage *message = NULL; - DBusPendingCall *pending = NULL; - - /* Asks for the currently installed bus names */ - - if (!(message = dbus_message_new_method_call( - DBUS_SERVICE_DBUS, - DBUS_PATH_DBUS, - DBUS_INTERFACE_DBUS, - "ListNames"))) - goto oom; - - if (!dbus_connection_send_with_reply(m->api_bus, message, &pending, -1)) - goto oom; - - if (!dbus_pending_call_set_notify(pending, query_name_list_pending_cb, m, NULL)) - goto oom; - - dbus_message_unref(message); - dbus_pending_call_unref(pending); - - /* We simple ask for the list and don't wait for it. Sooner or - * later we'll get it. */ - - return 0; - -oom: - if (pending) { - dbus_pending_call_cancel(pending); - dbus_pending_call_unref(pending); - } - - if (message) - dbus_message_unref(message); - - return -ENOMEM; -} - -static int bus_setup_loop(Manager *m, DBusConnection *bus) { - assert(m); - assert(bus); - - dbus_connection_set_exit_on_disconnect(bus, FALSE); - - if (!dbus_connection_set_watch_functions(bus, bus_add_watch, bus_remove_watch, bus_toggle_watch, m, NULL) || - !dbus_connection_set_timeout_functions(bus, bus_add_timeout, bus_remove_timeout, bus_toggle_timeout, m, NULL)) - return log_oom(); - - if (set_put(m->bus_connections_for_dispatch, bus) < 0) - return log_oom(); - - dbus_connection_set_dispatch_status_function(bus, bus_dispatch_status, m, NULL); - return 0; -} - -static dbus_bool_t allow_only_same_user(DBusConnection *connection, unsigned long uid, void *data) { - return uid == 0 || uid == geteuid(); -} - -static void bus_new_connection( - DBusServer *server, - DBusConnection *new_connection, - void *data) { - - Manager *m = data; - - assert(m); - - if (set_size(m->bus_connections) >= CONNECTIONS_MAX) { - log_error("Too many concurrent connections."); - return; - } - - dbus_connection_set_unix_user_function(new_connection, allow_only_same_user, NULL, NULL); - - if (bus_setup_loop(m, new_connection) < 0) - return; - - if (!dbus_connection_register_object_path(new_connection, "/org/freedesktop/systemd1", &bus_manager_vtable, m) || - !dbus_connection_register_fallback(new_connection, "/org/freedesktop/systemd1/unit", &bus_unit_vtable, m) || - !dbus_connection_register_fallback(new_connection, "/org/freedesktop/systemd1/job", &bus_job_vtable, m) || - !dbus_connection_add_filter(new_connection, private_bus_message_filter, m, NULL)) { - log_oom(); - return; - } - - log_debug("Accepted connection on private bus."); - - dbus_connection_ref(new_connection); -} - -static int init_registered_system_bus(Manager *m) { - char *id; - - if (!dbus_connection_add_filter(m->system_bus, system_bus_message_filter, m, NULL)) - return log_oom(); - - if (m->running_as != SYSTEMD_SYSTEM) { - DBusError error; - - dbus_error_init(&error); - - dbus_bus_add_match(m->system_bus, - "type='signal'," - "interface='org.freedesktop.systemd1.Agent'," - "member='Released'," - "path='/org/freedesktop/systemd1/agent'", - &error); - - if (dbus_error_is_set(&error)) { - log_error("Failed to register match: %s", bus_error_message(&error)); - dbus_error_free(&error); - return -1; - } - } - - log_debug("Successfully connected to system D-Bus bus %s as %s", - strnull((id = dbus_connection_get_server_id(m->system_bus))), - strnull(dbus_bus_get_unique_name(m->system_bus))); - dbus_free(id); - - return 0; -} - -static int init_registered_api_bus(Manager *m) { - int r; - - if (!dbus_connection_register_object_path(m->api_bus, "/org/freedesktop/systemd1", &bus_manager_vtable, m) || - !dbus_connection_register_fallback(m->api_bus, "/org/freedesktop/systemd1/unit", &bus_unit_vtable, m) || - !dbus_connection_register_fallback(m->api_bus, "/org/freedesktop/systemd1/job", &bus_job_vtable, m) || - !dbus_connection_add_filter(m->api_bus, api_bus_message_filter, m, NULL)) - return log_oom(); - - /* Get NameOwnerChange messages */ - dbus_bus_add_match(m->api_bus, - "type='signal'," - "sender='"DBUS_SERVICE_DBUS"'," - "interface='"DBUS_INTERFACE_DBUS"'," - "member='NameOwnerChanged'," - "path='"DBUS_PATH_DBUS"'", - NULL); - - /* Get activation requests */ - dbus_bus_add_match(m->api_bus, - "type='signal'," - "sender='"DBUS_SERVICE_DBUS"'," - "interface='org.freedesktop.systemd1.Activator'," - "member='ActivationRequest'," - "path='"DBUS_PATH_DBUS"'", - NULL); - - r = request_name(m); - if (r < 0) - return r; - - r = query_name_list(m); - if (r < 0) - return r; - - 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))), - strnull(dbus_bus_get_unique_name(m->api_bus))); - dbus_free(id); - } else - log_debug("Successfully initialized API on the system bus"); - - return 0; -} - -static void bus_register_cb(DBusPendingCall *pending, void *userdata) { - Manager *m = userdata; - DBusConnection **conn; - DBusMessage *reply; - DBusError error; - const char *name; - int r = 0; - - dbus_error_init(&error); - - conn = dbus_pending_call_get_data(pending, m->conn_data_slot); - assert(conn == &m->system_bus || conn == &m->api_bus); - - reply = dbus_pending_call_steal_reply(pending); - - switch (dbus_message_get_type(reply)) { - case DBUS_MESSAGE_TYPE_ERROR: - assert_se(dbus_set_error_from_message(&error, reply)); - log_warning("Failed to register to bus: %s", bus_error_message(&error)); - r = -1; - break; - case DBUS_MESSAGE_TYPE_METHOD_RETURN: - if (!dbus_message_get_args(reply, &error, - DBUS_TYPE_STRING, &name, - DBUS_TYPE_INVALID)) { - log_error("Failed to parse Hello reply: %s", bus_error_message(&error)); - r = -1; - break; - } - - log_debug("Received name %s in reply to Hello", name); - if (!dbus_bus_set_unique_name(*conn, name)) { - log_error("Failed to set unique name"); - r = -1; - break; - } - - if (conn == &m->system_bus) { - r = init_registered_system_bus(m); - if (r == 0 && m->running_as == SYSTEMD_SYSTEM) - r = init_registered_api_bus(m); - } else - r = init_registered_api_bus(m); - - break; - default: - assert_not_reached("Invalid reply message"); - } - - dbus_message_unref(reply); - dbus_error_free(&error); - - if (r < 0) { - if (conn == &m->system_bus) { - log_debug("Failed setting up the system bus"); - bus_done_system(m); - } else { - log_debug("Failed setting up the API bus"); - bus_done_api(m); - } - } -} - -static int manager_bus_async_register(Manager *m, DBusConnection **conn) { - DBusMessage *message = NULL; - DBusPendingCall *pending = NULL; - - message = dbus_message_new_method_call(DBUS_SERVICE_DBUS, - DBUS_PATH_DBUS, - DBUS_INTERFACE_DBUS, - "Hello"); - if (!message) - goto oom; - - if (!dbus_connection_send_with_reply(*conn, message, &pending, -1)) - goto oom; - - if (!dbus_pending_call_set_data(pending, m->conn_data_slot, conn, NULL)) - goto oom; - - if (!dbus_pending_call_set_notify(pending, bus_register_cb, m, NULL)) - goto oom; - - dbus_message_unref(message); - dbus_pending_call_unref(pending); - - return 0; -oom: - if (pending) { - dbus_pending_call_cancel(pending); - dbus_pending_call_unref(pending); - } - - if (message) - dbus_message_unref(message); - - return -ENOMEM; -} - -static DBusConnection* manager_bus_connect_private(Manager *m, DBusBusType type) { - const char *address; - DBusConnection *connection; - DBusError error; - - switch (type) { - case DBUS_BUS_SYSTEM: - address = secure_getenv("DBUS_SYSTEM_BUS_ADDRESS"); - if (!address || !address[0]) - address = DBUS_SYSTEM_BUS_DEFAULT_ADDRESS; - break; - case DBUS_BUS_SESSION: - address = secure_getenv("DBUS_SESSION_BUS_ADDRESS"); - if (!address || !address[0]) - address = DBUS_SESSION_BUS_DEFAULT_ADDRESS; - break; - default: - assert_not_reached("Invalid bus type"); - } - - dbus_error_init(&error); - - connection = dbus_connection_open_private(address, &error); - if (!connection) { - log_warning("Failed to open private bus connection: %s", bus_error_message(&error)); - goto fail; - } - - return connection; -fail: - if (connection) - dbus_connection_close(connection); - dbus_error_free(&error); - return NULL; -} - -static int bus_init_system(Manager *m) { - int r; - - if (m->system_bus) - return 0; - - m->system_bus = manager_bus_connect_private(m, DBUS_BUS_SYSTEM); - if (!m->system_bus) { - log_debug("Failed to connect to system D-Bus, retrying later"); - r = 0; - goto fail; - } - - r = bus_setup_loop(m, m->system_bus); - if (r < 0) - goto fail; - - r = manager_bus_async_register(m, &m->system_bus); - if (r < 0) - goto fail; - - return 0; -fail: - bus_done_system(m); - - return r; -} - -static int bus_init_api(Manager *m) { - int r; - - if (m->api_bus) - return 0; - - 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. - * bus_register_cb() is aware of that and will init the API - * when the system bus gets registered. - * No need to setup anything here. */ - return 0; - } - - m->api_bus = manager_bus_connect_private(m, DBUS_BUS_SESSION); - if (!m->api_bus) { - log_debug("Failed to connect to API D-Bus, retrying later"); - r = 0; - goto fail; - } - - r = bus_setup_loop(m, m->api_bus); - if (r < 0) - goto fail; - - r = manager_bus_async_register(m, &m->api_bus); - if (r < 0) - goto fail; - - return 0; -fail: - bus_done_api(m); - - return r; -} - -static int bus_init_private(Manager *m) { - DBusError error; - int r; - const char *const external_only[] = { - "EXTERNAL", - NULL - }; - - assert(m); - - dbus_error_init(&error); - - if (m->private_bus) - return 0; - - if (m->running_as == SYSTEMD_SYSTEM) { - - /* We want the private bus only when running as init */ - if (getpid() != 1) - return 0; - - unlink("/run/systemd/private"); - m->private_bus = dbus_server_listen("unix:path=/run/systemd/private", &error); - } else { - const char *e; - char *p; - - e = secure_getenv("XDG_RUNTIME_DIR"); - if (!e) - return 0; - - if (asprintf(&p, "unix:path=%s/systemd/private", e) < 0) { - r = log_oom(); - goto fail; - } - - mkdir_parents_label(p+10, 0755); - unlink(p+10); - m->private_bus = dbus_server_listen(p, &error); - free(p); - } - - if (!m->private_bus) { - log_error("Failed to create private D-Bus server: %s", bus_error_message(&error)); - r = -EIO; - goto fail; - } - - if (!dbus_server_set_auth_mechanisms(m->private_bus, (const char**) external_only) || - !dbus_server_set_watch_functions(m->private_bus, bus_add_watch, bus_remove_watch, bus_toggle_watch, m, NULL) || - !dbus_server_set_timeout_functions(m->private_bus, bus_add_timeout, bus_remove_timeout, bus_toggle_timeout, m, NULL)) { - r = log_oom(); - goto fail; - } - - dbus_server_set_new_connection_function(m->private_bus, bus_new_connection, m, NULL); - - log_debug("Successfully created private D-Bus server."); - - return 0; - -fail: - bus_done_private(m); - dbus_error_free(&error); - - return r; -} - -int bus_init(Manager *m, bool try_bus_connect) { - int r; - - if (set_ensure_allocated(&m->bus_connections, trivial_hash_func, trivial_compare_func) < 0 || - set_ensure_allocated(&m->bus_connections_for_dispatch, trivial_hash_func, trivial_compare_func) < 0) - goto oom; - - if (m->name_data_slot < 0) - if (!dbus_pending_call_allocate_data_slot(&m->name_data_slot)) - goto oom; - - if (m->conn_data_slot < 0) - if (!dbus_pending_call_allocate_data_slot(&m->conn_data_slot)) - goto oom; - - if (m->subscribed_data_slot < 0) - if (!dbus_connection_allocate_data_slot(&m->subscribed_data_slot)) - goto oom; - - if (try_bus_connect) { - if ((r = bus_init_system(m)) < 0 || - (r = bus_init_api(m)) < 0) - return r; - } - - if ((r = bus_init_private(m)) < 0) - return r; - - return 0; -oom: - return log_oom(); -} - -static void shutdown_connection(Manager *m, DBusConnection *c) { - Set *s; - Job *j; - Iterator i; - - HASHMAP_FOREACH(j, m->jobs, i) { - JobBusClient *cl, *nextcl; - LIST_FOREACH_SAFE(client, cl, nextcl, j->bus_client_list) { - if (cl->bus == c) { - LIST_REMOVE(JobBusClient, client, j->bus_client_list, cl); - free(cl); - } - } - } - - set_remove(m->bus_connections, c); - set_remove(m->bus_connections_for_dispatch, c); - - if ((s = BUS_CONNECTION_SUBSCRIBED(m, c))) { - char *t; - - while ((t = set_steal_first(s))) - free(t); - - set_free(s); - } - - if (m->queued_message_connection == c) { - m->queued_message_connection = NULL; - - if (m->queued_message) { - dbus_message_unref(m->queued_message); - m->queued_message = NULL; - } - } - - dbus_connection_set_dispatch_status_function(c, NULL, NULL, NULL); - /* system manager cannot afford to block on DBus */ - if (m->running_as != SYSTEMD_SYSTEM) - dbus_connection_flush(c); - dbus_connection_close(c); - dbus_connection_unref(c); -} - -static void bus_done_api(Manager *m) { - if (!m->api_bus) - return; - - if (m->running_as == SYSTEMD_USER) - shutdown_connection(m, m->api_bus); - - m->api_bus = NULL; - - if (m->queued_message) { - dbus_message_unref(m->queued_message); - m->queued_message = NULL; - } -} - -static void bus_done_system(Manager *m) { - if (!m->system_bus) - return; - - if (m->running_as == SYSTEMD_SYSTEM) - bus_done_api(m); - - shutdown_connection(m, m->system_bus); - m->system_bus = NULL; -} - -static void bus_done_private(Manager *m) { - if (!m->private_bus) - return; - - dbus_server_disconnect(m->private_bus); - dbus_server_unref(m->private_bus); - m->private_bus = NULL; -} - -void bus_done(Manager *m) { - DBusConnection *c; - - bus_done_api(m); - bus_done_system(m); - bus_done_private(m); - - while ((c = set_steal_first(m->bus_connections))) - shutdown_connection(m, c); - - while ((c = set_steal_first(m->bus_connections_for_dispatch))) - shutdown_connection(m, c); - - set_free(m->bus_connections); - set_free(m->bus_connections_for_dispatch); - - if (m->name_data_slot >= 0) - dbus_pending_call_free_data_slot(&m->name_data_slot); - - if (m->conn_data_slot >= 0) - dbus_pending_call_free_data_slot(&m->conn_data_slot); - - if (m->subscribed_data_slot >= 0) - dbus_connection_free_data_slot(&m->subscribed_data_slot); -} - -static void query_pid_pending_cb(DBusPendingCall *pending, void *userdata) { - Manager *m = userdata; - DBusMessage *reply; - DBusError error; - const char *name; - - dbus_error_init(&error); - - assert_se(name = BUS_PENDING_CALL_NAME(m, pending)); - assert_se(reply = dbus_pending_call_steal_reply(pending)); - - switch (dbus_message_get_type(reply)) { - - case DBUS_MESSAGE_TYPE_ERROR: - - assert_se(dbus_set_error_from_message(&error, reply)); - log_warning("GetConnectionUnixProcessID() failed: %s", bus_error_message(&error)); - break; - - case DBUS_MESSAGE_TYPE_METHOD_RETURN: { - uint32_t r; - - if (!dbus_message_get_args(reply, - &error, - DBUS_TYPE_UINT32, &r, - DBUS_TYPE_INVALID)) { - log_error("Failed to parse GetConnectionUnixProcessID() reply: %s", bus_error_message(&error)); - break; - } - - manager_dispatch_bus_query_pid_done(m, name, (pid_t) r); - break; - } - - default: - assert_not_reached("Invalid reply message"); - } - - dbus_message_unref(reply); - dbus_error_free(&error); -} - -int bus_query_pid(Manager *m, const char *name) { - DBusMessage *message = NULL; - DBusPendingCall *pending = NULL; - char *n = NULL; - - assert(m); - assert(name); - - if (!(message = dbus_message_new_method_call( - DBUS_SERVICE_DBUS, - DBUS_PATH_DBUS, - DBUS_INTERFACE_DBUS, - "GetConnectionUnixProcessID"))) - goto oom; - - if (!(dbus_message_append_args( - message, - DBUS_TYPE_STRING, &name, - DBUS_TYPE_INVALID))) - goto oom; - - if (!dbus_connection_send_with_reply(m->api_bus, message, &pending, -1)) - goto oom; - - if (!(n = strdup(name))) - goto oom; - - if (!dbus_pending_call_set_data(pending, m->name_data_slot, n, free)) - goto oom; - - n = NULL; - - if (!dbus_pending_call_set_notify(pending, query_pid_pending_cb, m, NULL)) - goto oom; - - dbus_message_unref(message); - dbus_pending_call_unref(pending); - - return 0; - -oom: - free(n); - - if (pending) { - dbus_pending_call_cancel(pending); - dbus_pending_call_unref(pending); - } - - if (message) - dbus_message_unref(message); - - return -ENOMEM; -} - -int bus_broadcast(Manager *m, DBusMessage *message) { - bool oom = false; - Iterator i; - DBusConnection *c; - - assert(m); - assert(message); - - SET_FOREACH(c, m->bus_connections_for_dispatch, i) - 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 == SYSTEMD_SYSTEM) - oom = !dbus_connection_send(c, message, NULL); - - return oom ? -ENOMEM : 0; -} - -bool bus_has_subscriber(Manager *m) { - Iterator i; - DBusConnection *c; - - assert(m); - - SET_FOREACH(c, m->bus_connections_for_dispatch, i) - if (bus_connection_has_subscriber(m, c)) - return true; - - SET_FOREACH(c, m->bus_connections, i) - if (bus_connection_has_subscriber(m, c)) - return true; - - return false; -} - -bool bus_connection_has_subscriber(Manager *m, DBusConnection *c) { - assert(m); - assert(c); - - return !set_isempty(BUS_CONNECTION_SUBSCRIBED(m, c)); -} - -int bus_fdset_add_all(Manager *m, FDSet *fds) { - Iterator i; - DBusConnection *c; - - assert(m); - assert(fds); - - /* When we are about to reexecute we add all D-Bus fds to the - * set to pass over to the newly executed systemd. They won't - * be used there however, except that they are closed at the - * very end of deserialization, those making it possible for - * clients to synchronously wait for systemd to reexec by - * simply waiting for disconnection */ - - SET_FOREACH(c, m->bus_connections_for_dispatch, i) { - int fd; - - if (dbus_connection_get_unix_fd(c, &fd)) { - fd = fdset_put_dup(fds, fd); - - if (fd < 0) - return fd; - } - } - - SET_FOREACH(c, m->bus_connections, i) { - int fd; - - if (dbus_connection_get_unix_fd(c, &fd)) { - fd = fdset_put_dup(fds, fd); - - if (fd < 0) - return fd; - } - } - - return 0; -} - -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) { - - DBusMessage *message; - - assert(m); - - message = dbus_message_new_signal("/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "StartupFinished"); - if (!message) { - log_oom(); - return; - } - - 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, - DBUS_TYPE_UINT64, &total_usec, - DBUS_TYPE_INVALID)) { - log_oom(); - goto finish; - } - - - if (bus_broadcast(m, message) < 0) { - log_oom(); - goto finish; - } - -finish: - if (message) - dbus_message_unref(message); -} diff --git a/src/core/dbus.h b/src/core/dbus.h deleted file mode 100644 index c7a058e198..0000000000 --- a/src/core/dbus.h +++ /dev/null @@ -1,50 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -#pragma once - -/*** - 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 <dbus/dbus.h> - -#include "manager.h" - -int bus_init(Manager *m, bool try_bus_connect); -void bus_done(Manager *m); - -unsigned bus_dispatch(Manager *m); - -void bus_watch_event(Manager *m, Watch *w, int events); -void bus_timeout_event(Manager *m, Watch *w, int events); - -int bus_query_pid(Manager *m, const char *name); - -int bus_broadcast(Manager *m, DBusMessage *message); - -bool bus_has_subscriber(Manager *m); -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 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) - -extern const char * const bus_interface_table[]; diff --git a/src/core/device.c b/src/core/device.c deleted file mode 100644 index be76cafaed..0000000000 --- a/src/core/device.c +++ /dev/null @@ -1,644 +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 <sys/epoll.h> -#include <libudev.h> - -#include "unit.h" -#include "device.h" -#include "strv.h" -#include "log.h" -#include "unit-name.h" -#include "dbus-device.h" -#include "def.h" -#include "path-util.h" - -static const UnitActiveState state_translation_table[_DEVICE_STATE_MAX] = { - [DEVICE_DEAD] = UNIT_INACTIVE, - [DEVICE_PLUGGED] = UNIT_ACTIVE -}; - -static void device_unset_sysfs(Device *d) { - Device *first; - - assert(d); - - if (!d->sysfs) - return; - - /* Remove this unit from the chain of devices which share the - * same sysfs path. */ - first = hashmap_get(UNIT(d)->manager->devices_by_sysfs, d->sysfs); - LIST_REMOVE(Device, same_sysfs, first, d); - - if (first) - hashmap_remove_and_replace(UNIT(d)->manager->devices_by_sysfs, d->sysfs, first->sysfs, first); - else - hashmap_remove(UNIT(d)->manager->devices_by_sysfs, d->sysfs); - - free(d->sysfs); - d->sysfs = NULL; -} - -static void device_init(Unit *u) { - Device *d = DEVICE(u); - - assert(d); - assert(UNIT(d)->load_state == UNIT_STUB); - - /* In contrast to all other unit types we timeout jobs waiting - * for devices by default. This is because they otherwise wait - * indefinitely for plugged in devices, something which cannot - * happen for the other units since their operations time out - * anyway. */ - UNIT(d)->job_timeout = DEFAULT_TIMEOUT_USEC; - - UNIT(d)->ignore_on_isolate = true; - UNIT(d)->ignore_on_snapshot = true; -} - -static void device_done(Unit *u) { - Device *d = DEVICE(u); - - assert(d); - - device_unset_sysfs(d); -} - -static void device_set_state(Device *d, DeviceState state) { - DeviceState old_state; - assert(d); - - old_state = d->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)); - - unit_notify(UNIT(d), state_translation_table[old_state], state_translation_table[state], true); -} - -static int device_coldplug(Unit *u) { - Device *d = DEVICE(u); - - assert(d); - assert(d->state == DEVICE_DEAD); - - if (d->sysfs) - device_set_state(d, DEVICE_PLUGGED); - - return 0; -} - -static void device_dump(Unit *u, FILE *f, const char *prefix) { - Device *d = DEVICE(u); - - assert(d); - - fprintf(f, - "%sDevice State: %s\n" - "%sSysfs Path: %s\n", - prefix, device_state_to_string(d->state), - prefix, strna(d->sysfs)); -} - -static UnitActiveState device_active_state(Unit *u) { - assert(u); - - return state_translation_table[DEVICE(u)->state]; -} - -static const char *device_sub_state_to_string(Unit *u) { - assert(u); - - return device_state_to_string(DEVICE(u)->state); -} - -static int device_add_escaped_name(Unit *u, const char *dn) { - char *e; - int r; - - assert(u); - assert(dn); - assert(dn[0] == '/'); - - e = unit_name_from_path(dn, ".device"); - if (!e) - return -ENOMEM; - - r = unit_add_name(u, e); - free(e); - - if (r < 0 && r != -EEXIST) - return r; - - return 0; -} - -static int device_find_escape_name(Manager *m, const char *dn, Unit **_u) { - char *e; - Unit *u; - - assert(m); - assert(dn); - assert(dn[0] == '/'); - assert(_u); - - e = unit_name_from_path(dn, ".device"); - if (!e) - return -ENOMEM; - - u = manager_get_unit(m, e); - free(e); - - if (u) { - *_u = u; - return 1; - } - - return 0; -} - -static int device_update_unit(Manager *m, struct udev_device *dev, const char *path, bool main) { - const char *sysfs, *model; - Unit *u = NULL; - int r; - bool delete; - - assert(m); - - if (!(sysfs = udev_device_get_syspath(dev))) - return -ENOMEM; - - if ((r = device_find_escape_name(m, path, &u)) < 0) - return r; - - if (u && DEVICE(u)->sysfs && !path_equal(DEVICE(u)->sysfs, sysfs)) - return -EEXIST; - - if (!u) { - delete = true; - - u = unit_new(m, sizeof(Device)); - if (!u) - return -ENOMEM; - - r = device_add_escaped_name(u, path); - if (r < 0) - goto fail; - - unit_add_to_load_queue(u); - } else - delete = false; - - /* If this was created via some dependency and has not - * actually been seen yet ->sysfs will not be - * initialized. Hence initialize it if necessary. */ - - if (!DEVICE(u)->sysfs) { - Device *first; - - if (!(DEVICE(u)->sysfs = strdup(sysfs))) { - r = -ENOMEM; - goto fail; - } - - if (!m->devices_by_sysfs) - if (!(m->devices_by_sysfs = hashmap_new(string_hash_func, string_compare_func))) { - r = -ENOMEM; - goto fail; - } - - first = hashmap_get(m->devices_by_sysfs, sysfs); - LIST_PREPEND(Device, same_sysfs, first, DEVICE(u)); - - if ((r = hashmap_replace(m->devices_by_sysfs, DEVICE(u)->sysfs, first)) < 0) - goto fail; - } - - if ((model = udev_device_get_property_value(dev, "ID_MODEL_FROM_DATABASE")) || - (model = udev_device_get_property_value(dev, "ID_MODEL"))) { - if ((r = unit_set_description(u, model)) < 0) - goto fail; - } else - if ((r = unit_set_description(u, path)) < 0) - goto fail; - - if (main) { - /* The additional systemd udev properties we only - * interpret for the main object */ - const char *wants, *alias; - - 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); - } - } - } - - 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; - - 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; - } - } - } - - unit_add_to_dbus_queue(u); - return 0; - -fail: - log_warning("Failed to load device unit: %s", strerror(-r)); - - if (delete && u) - unit_free(u); - - return r; -} - -static int device_process_new_device(Manager *m, struct udev_device *dev, bool update_state) { - const char *sysfs, *dn; - struct udev_list_entry *item = NULL, *first = NULL; - - assert(m); - - if (!(sysfs = udev_device_get_syspath(dev))) - return -ENOMEM; - - /* Add the main unit named after the sysfs path */ - device_update_unit(m, dev, sysfs, true); - - /* Add an additional unit for the device node */ - if ((dn = udev_device_get_devnode(dev))) - device_update_unit(m, dev, dn, false); - - /* Add additional units for all symlinks */ - first = udev_device_get_devlinks_list_entry(dev); - udev_list_entry_foreach(item, first) { - const char *p; - struct stat st; - - /* Don't bother with the /dev/block links */ - p = udev_list_entry_get_name(item); - - if (path_startswith(p, "/dev/block/") || - path_startswith(p, "/dev/char/")) - continue; - - /* Verify that the symlink in the FS actually belongs - * to this device. This is useful to deal with - * conflicting devices, e.g. when two disks want the - * same /dev/disk/by-label/xxx link because they have - * the same label. We want to make sure that the same - * device that won the symlink wins in systemd, so we - * check the device node major/minor*/ - if (stat(p, &st) >= 0) - if ((!S_ISBLK(st.st_mode) && !S_ISCHR(st.st_mode)) || - st.st_rdev != udev_device_get_devnum(dev)) - continue; - - device_update_unit(m, dev, p, false); - } - - if (update_state) { - Device *d, *l; - - manager_dispatch_load_queue(m); - - l = hashmap_get(m->devices_by_sysfs, sysfs); - LIST_FOREACH(same_sysfs, d, l) - device_set_state(d, DEVICE_PLUGGED); - } - - return 0; -} - -static int device_process_path(Manager *m, const char *path, bool update_state) { - int r; - struct udev_device *dev; - - assert(m); - assert(path); - - if (!(dev = udev_device_new_from_syspath(m->udev, path))) { - log_warning("Failed to get udev device object from udev for path %s.", path); - return -ENOMEM; - } - - r = device_process_new_device(m, dev, update_state); - udev_device_unref(dev); - return r; -} - -static int device_process_removed_device(Manager *m, struct udev_device *dev) { - const char *sysfs; - Device *d; - - assert(m); - assert(dev); - - if (!(sysfs = udev_device_get_syspath(dev))) - return -ENOMEM; - - /* Remove all units of this sysfs path */ - while ((d = hashmap_get(m->devices_by_sysfs, sysfs))) { - device_unset_sysfs(d); - device_set_state(d, DEVICE_DEAD); - } - - return 0; -} - -static Unit *device_following(Unit *u) { - Device *d = DEVICE(u); - Device *other, *first = NULL; - - assert(d); - - if (startswith(u->id, "sys-")) - return NULL; - - /* Make everybody follow the unit that's named after the sysfs path */ - for (other = d->same_sysfs_next; other; other = other->same_sysfs_next) - if (startswith(UNIT(other)->id, "sys-")) - return UNIT(other); - - for (other = d->same_sysfs_prev; other; other = other->same_sysfs_prev) { - if (startswith(UNIT(other)->id, "sys-")) - return UNIT(other); - - first = other; - } - - return UNIT(first); -} - -static int device_following_set(Unit *u, Set **_s) { - Device *d = DEVICE(u); - Device *other; - Set *s; - int r; - - assert(d); - assert(_s); - - if (!d->same_sysfs_prev && !d->same_sysfs_next) { - *_s = NULL; - return 0; - } - - if (!(s = set_new(NULL, NULL))) - return -ENOMEM; - - for (other = d->same_sysfs_next; other; other = other->same_sysfs_next) - if ((r = set_put(s, other)) < 0) - goto fail; - - for (other = d->same_sysfs_prev; other; other = other->same_sysfs_prev) - if ((r = set_put(s, other)) < 0) - goto fail; - - *_s = s; - return 1; - -fail: - set_free(s); - return r; -} - -static void device_shutdown(Manager *m) { - assert(m); - - if (m->udev_monitor) { - udev_monitor_unref(m->udev_monitor); - m->udev_monitor = NULL; - } - - if (m->udev) { - udev_unref(m->udev); - m->udev = NULL; - } - - hashmap_free(m->devices_by_sysfs); - m->devices_by_sysfs = NULL; -} - -static int device_enumerate(Manager *m) { - struct epoll_event ev; - int r; - struct udev_enumerate *e = NULL; - struct udev_list_entry *item = NULL, *first = NULL; - - assert(m); - - if (!m->udev) { - if (!(m->udev = udev_new())) - return -ENOMEM; - - if (!(m->udev_monitor = udev_monitor_new_from_netlink(m->udev, "udev"))) { - r = -ENOMEM; - goto fail; - } - - /* This will fail if we are unprivileged, but that - * should not matter much, as user instances won't run - * during boot. */ - udev_monitor_set_receive_buffer_size(m->udev_monitor, 128*1024*1024); - - if (udev_monitor_filter_add_match_tag(m->udev_monitor, "systemd") < 0) { - r = -ENOMEM; - goto fail; - } - - if (udev_monitor_enable_receiving(m->udev_monitor) < 0) { - r = -EIO; - goto fail; - } - - m->udev_watch.type = WATCH_UDEV; - m->udev_watch.fd = udev_monitor_get_fd(m->udev_monitor); - - zero(ev); - ev.events = EPOLLIN; - ev.data.ptr = &m->udev_watch; - - if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->udev_watch.fd, &ev) < 0) - return -errno; - } - - if (!(e = udev_enumerate_new(m->udev))) { - r = -ENOMEM; - goto fail; - } - if (udev_enumerate_add_match_tag(e, "systemd") < 0) { - r = -EIO; - goto fail; - } - - if (udev_enumerate_scan_devices(e) < 0) { - r = -EIO; - goto fail; - } - - first = udev_enumerate_get_list_entry(e); - udev_list_entry_foreach(item, first) - device_process_path(m, udev_list_entry_get_name(item), false); - - udev_enumerate_unref(e); - return 0; - -fail: - if (e) - udev_enumerate_unref(e); - - device_shutdown(m); - return r; -} - -void device_fd_event(Manager *m, int events) { - struct udev_device *dev; - int r; - const char *action, *ready; - - assert(m); - - if (events != EPOLLIN) { - static RATELIMIT_DEFINE(limit, 10*USEC_PER_SEC, 5); - - if (!ratelimit_test(&limit)) - log_error("Failed to get udev event: %m"); - if (!(events & EPOLLIN)) - return; - } - - if (!(dev = udev_monitor_receive_device(m->udev_monitor))) { - /* - * libudev might filter-out devices which pass the bloom filter, - * so getting NULL here is not necessarily an error - */ - return; - } - - if (!(action = udev_device_get_action(dev))) { - log_error("Failed to get udev action string."); - goto fail; - } - - ready = udev_device_get_property_value(dev, "SYSTEMD_READY"); - - if (streq(action, "remove") || (ready && parse_boolean(ready) == 0)) { - if ((r = device_process_removed_device(m, dev)) < 0) { - log_error("Failed to process udev device event: %s", strerror(-r)); - goto fail; - } - } else { - if ((r = device_process_new_device(m, dev, true)) < 0) { - log_error("Failed to process udev device event: %s", strerror(-r)); - goto fail; - } - } - -fail: - udev_device_unref(dev); -} - -static const char* const device_state_table[_DEVICE_STATE_MAX] = { - [DEVICE_DEAD] = "dead", - [DEVICE_PLUGGED] = "plugged" -}; - -DEFINE_STRING_TABLE_LOOKUP(device_state, DeviceState); - -const UnitVTable device_vtable = { - .object_size = sizeof(Device), - .sections = - "Unit\0" - "Device\0" - "Install\0", - - .no_instances = true, - - .init = device_init, - - .load = unit_load_fragment_and_dropin_optional, - .done = device_done, - .coldplug = device_coldplug, - - .dump = device_dump, - - .active_state = device_active_state, - .sub_state_to_string = device_sub_state_to_string, - - .bus_interface = "org.freedesktop.systemd1.Device", - .bus_message_handler = bus_device_message_handler, - .bus_invalidating_properties = bus_device_invalidating_properties, - - .following = device_following, - .following_set = device_following_set, - - .enumerate = device_enumerate, - .shutdown = device_shutdown, - - .status_message_formats = { - .starting_stopping = { - [0] = "Expecting device %s...", - }, - .finished_start_job = { - [JOB_DONE] = "Found device %s.", - [JOB_TIMEOUT] = "Timed out waiting for device %s.", - }, - }, -}; diff --git a/src/core/device.h b/src/core/device.h deleted file mode 100644 index 3c4604f60e..0000000000 --- a/src/core/device.h +++ /dev/null @@ -1,56 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -#pragma once - -/*** - 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/>. -***/ - -typedef struct Device Device; - -#include "unit.h" - -/* We simply watch devices, we cannot plug/unplug them. That - * simplifies the state engine greatly */ -typedef enum DeviceState { - DEVICE_DEAD, - DEVICE_PLUGGED, - _DEVICE_STATE_MAX, - _DEVICE_STATE_INVALID = -1 -} DeviceState; - -struct Device { - Unit meta; - - char *sysfs; - - /* In order to be able to distinguish dependencies on - different device nodes we might end up creating multiple - devices for the same sysfs path. We chain them up here. */ - - LIST_FIELDS(struct Device, same_sysfs); - - DeviceState state; -}; - -extern const UnitVTable device_vtable; - -void device_fd_event(Manager *m, int events); - -const char* device_state_to_string(DeviceState i); -DeviceState device_state_from_string(const char *s); diff --git a/src/core/execute.c b/src/core/execute.c deleted file mode 100644 index e236d38e0f..0000000000 --- a/src/core/execute.c +++ /dev/null @@ -1,2132 +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 <assert.h> -#include <dirent.h> -#include <errno.h> -#include <fcntl.h> -#include <unistd.h> -#include <string.h> -#include <signal.h> -#include <sys/socket.h> -#include <sys/un.h> -#include <sys/prctl.h> -#include <linux/sched.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <grp.h> -#include <pwd.h> -#include <sys/mount.h> -#include <linux/fs.h> -#include <linux/oom.h> -#include <sys/poll.h> -#include <linux/seccomp-bpf.h> - -#ifdef HAVE_PAM -#include <security/pam_appl.h> -#endif - -#include "execute.h" -#include "strv.h" -#include "macro.h" -#include "capability.h" -#include "util.h" -#include "log.h" -#include "ioprio.h" -#include "securebits.h" -#include "cgroup.h" -#include "namespace.h" -#include "tcpwrap.h" -#include "exit-status.h" -#include "missing.h" -#include "utmp-wtmp.h" -#include "def.h" -#include "loopback-setup.h" -#include "path-util.h" -#include "syscall-list.h" -#include "sd-id128.h" -#include "sd-messages.h" - -#define IDLE_TIMEOUT_USEC (5*USEC_PER_SEC) - -/* This assumes there is a 'tty' group */ -#define TTY_MODE 0620 - -static int shift_fds(int fds[], unsigned n_fds) { - int start, restart_from; - - if (n_fds <= 0) - return 0; - - /* Modifies the fds array! (sorts it) */ - - assert(fds); - - start = 0; - for (;;) { - int i; - - restart_from = -1; - - for (i = start; i < (int) n_fds; i++) { - int nfd; - - /* Already at right index? */ - if (fds[i] == i+3) - continue; - - if ((nfd = fcntl(fds[i], F_DUPFD, i+3)) < 0) - return -errno; - - close_nointr_nofail(fds[i]); - fds[i] = nfd; - - /* Hmm, the fd we wanted isn't free? Then - * let's remember that and try again from here*/ - if (nfd != i+3 && restart_from < 0) - restart_from = i; - } - - if (restart_from < 0) - break; - - start = restart_from; - } - - return 0; -} - -static int flags_fds(const int fds[], unsigned n_fds, bool nonblock) { - unsigned i; - int r; - - if (n_fds <= 0) - return 0; - - assert(fds); - - /* Drops/Sets O_NONBLOCK and FD_CLOEXEC from the file flags */ - - for (i = 0; i < n_fds; i++) { - - if ((r = fd_nonblock(fds[i], nonblock)) < 0) - return r; - - /* We unconditionally drop FD_CLOEXEC from the fds, - * since after all we want to pass these fds to our - * children */ - - if ((r = fd_cloexec(fds[i], false)) < 0) - return r; - } - - return 0; -} - -static const char *tty_path(const ExecContext *context) { - assert(context); - - if (context->tty_path) - return context->tty_path; - - return "/dev/console"; -} - -void exec_context_tty_reset(const ExecContext *context) { - assert(context); - - if (context->tty_vhangup) - terminal_vhangup(tty_path(context)); - - if (context->tty_reset) - reset_terminal(tty_path(context)); - - if (context->tty_vt_disallocate && context->tty_path) - vt_disallocate(context->tty_path); -} - -static int open_null_as(int flags, int nfd) { - int fd, r; - - assert(nfd >= 0); - - if ((fd = open("/dev/null", flags|O_NOCTTY)) < 0) - return -errno; - - if (fd != nfd) { - r = dup2(fd, nfd) < 0 ? -errno : nfd; - close_nointr_nofail(fd); - } else - r = nfd; - - return r; -} - -static int connect_logger_as(const ExecContext *context, ExecOutput output, const char *ident, const char *unit_id, int nfd) { - int fd, r; - union sockaddr_union sa; - - assert(context); - assert(output < _EXEC_OUTPUT_MAX); - assert(ident); - assert(nfd >= 0); - - fd = socket(AF_UNIX, SOCK_STREAM, 0); - if (fd < 0) - return -errno; - - zero(sa); - sa.un.sun_family = AF_UNIX; - strncpy(sa.un.sun_path, "/run/systemd/journal/stdout", sizeof(sa.un.sun_path)); - - r = connect(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path)); - if (r < 0) { - close_nointr_nofail(fd); - return -errno; - } - - if (shutdown(fd, SHUT_RD) < 0) { - close_nointr_nofail(fd); - return -errno; - } - - dprintf(fd, - "%s\n" - "%s\n" - "%i\n" - "%i\n" - "%i\n" - "%i\n" - "%i\n", - context->syslog_identifier ? context->syslog_identifier : ident, - unit_id, - context->syslog_priority, - !!context->syslog_level_prefix, - output == EXEC_OUTPUT_SYSLOG || output == EXEC_OUTPUT_SYSLOG_AND_CONSOLE, - output == EXEC_OUTPUT_KMSG || output == EXEC_OUTPUT_KMSG_AND_CONSOLE, - output == EXEC_OUTPUT_SYSLOG_AND_CONSOLE || output == EXEC_OUTPUT_KMSG_AND_CONSOLE || output == EXEC_OUTPUT_JOURNAL_AND_CONSOLE); - - if (fd != nfd) { - r = dup2(fd, nfd) < 0 ? -errno : nfd; - close_nointr_nofail(fd); - } else - r = nfd; - - return r; -} -static int open_terminal_as(const char *path, mode_t mode, int nfd) { - int fd, r; - - assert(path); - assert(nfd >= 0); - - if ((fd = open_terminal(path, mode | O_NOCTTY)) < 0) - return fd; - - if (fd != nfd) { - r = dup2(fd, nfd) < 0 ? -errno : nfd; - close_nointr_nofail(fd); - } else - r = nfd; - - return r; -} - -static bool is_terminal_input(ExecInput i) { - return - i == EXEC_INPUT_TTY || - i == EXEC_INPUT_TTY_FORCE || - i == EXEC_INPUT_TTY_FAIL; -} - -static int fixup_input(ExecInput std_input, int socket_fd, bool apply_tty_stdin) { - - if (is_terminal_input(std_input) && !apply_tty_stdin) - return EXEC_INPUT_NULL; - - if (std_input == EXEC_INPUT_SOCKET && socket_fd < 0) - return EXEC_INPUT_NULL; - - return std_input; -} - -static int fixup_output(ExecOutput std_output, int socket_fd) { - - if (std_output == EXEC_OUTPUT_SOCKET && socket_fd < 0) - return EXEC_OUTPUT_INHERIT; - - return std_output; -} - -static int setup_input(const ExecContext *context, int socket_fd, bool apply_tty_stdin) { - ExecInput i; - - assert(context); - - i = fixup_input(context->std_input, socket_fd, apply_tty_stdin); - - switch (i) { - - case EXEC_INPUT_NULL: - return open_null_as(O_RDONLY, STDIN_FILENO); - - case EXEC_INPUT_TTY: - case EXEC_INPUT_TTY_FORCE: - case EXEC_INPUT_TTY_FAIL: { - int fd, r; - - if ((fd = acquire_terminal( - tty_path(context), - i == EXEC_INPUT_TTY_FAIL, - i == EXEC_INPUT_TTY_FORCE, - false, - (usec_t) -1)) < 0) - return fd; - - if (fd != STDIN_FILENO) { - r = dup2(fd, STDIN_FILENO) < 0 ? -errno : STDIN_FILENO; - close_nointr_nofail(fd); - } else - r = STDIN_FILENO; - - return r; - } - - case EXEC_INPUT_SOCKET: - return dup2(socket_fd, STDIN_FILENO) < 0 ? -errno : STDIN_FILENO; - - default: - assert_not_reached("Unknown input type"); - } -} - -static int setup_output(const ExecContext *context, int socket_fd, const char *ident, const char *unit_id, bool apply_tty_stdin) { - ExecOutput o; - ExecInput i; - - assert(context); - assert(ident); - - i = fixup_input(context->std_input, socket_fd, apply_tty_stdin); - o = fixup_output(context->std_output, socket_fd); - - /* This expects the input is already set up */ - - switch (o) { - - case EXEC_OUTPUT_INHERIT: - - /* If input got downgraded, inherit the original value */ - if (i == EXEC_INPUT_NULL && is_terminal_input(context->std_input)) - return open_terminal_as(tty_path(context), O_WRONLY, STDOUT_FILENO); - - /* If the input is connected to anything that's not a /dev/null, inherit that... */ - if (i != EXEC_INPUT_NULL) - return dup2(STDIN_FILENO, STDOUT_FILENO) < 0 ? -errno : STDOUT_FILENO; - - /* If we are not started from PID 1 we just inherit STDOUT from our parent process. */ - if (getppid() != 1) - return STDOUT_FILENO; - - /* We need to open /dev/null here anew, to get the - * right access mode. So we fall through */ - - case EXEC_OUTPUT_NULL: - return open_null_as(O_WRONLY, STDOUT_FILENO); - - case EXEC_OUTPUT_TTY: - if (is_terminal_input(i)) - return dup2(STDIN_FILENO, STDOUT_FILENO) < 0 ? -errno : STDOUT_FILENO; - - /* We don't reset the terminal if this is just about output */ - return open_terminal_as(tty_path(context), O_WRONLY, STDOUT_FILENO); - - case EXEC_OUTPUT_SYSLOG: - case EXEC_OUTPUT_SYSLOG_AND_CONSOLE: - case EXEC_OUTPUT_KMSG: - case EXEC_OUTPUT_KMSG_AND_CONSOLE: - case EXEC_OUTPUT_JOURNAL: - case EXEC_OUTPUT_JOURNAL_AND_CONSOLE: - return connect_logger_as(context, o, ident, unit_id, STDOUT_FILENO); - - case EXEC_OUTPUT_SOCKET: - assert(socket_fd >= 0); - return dup2(socket_fd, STDOUT_FILENO) < 0 ? -errno : STDOUT_FILENO; - - default: - assert_not_reached("Unknown output type"); - } -} - -static int setup_error(const ExecContext *context, int socket_fd, const char *ident, const char *unit_id, bool apply_tty_stdin) { - ExecOutput o, e; - ExecInput i; - - assert(context); - assert(ident); - - i = fixup_input(context->std_input, socket_fd, apply_tty_stdin); - o = fixup_output(context->std_output, socket_fd); - e = fixup_output(context->std_error, socket_fd); - - /* This expects the input and output are already set up */ - - /* Don't change the stderr file descriptor if we inherit all - * the way and are not on a tty */ - if (e == EXEC_OUTPUT_INHERIT && - o == EXEC_OUTPUT_INHERIT && - i == EXEC_INPUT_NULL && - !is_terminal_input(context->std_input) && - getppid () != 1) - return STDERR_FILENO; - - /* Duplicate from stdout if possible */ - if (e == o || e == EXEC_OUTPUT_INHERIT) - return dup2(STDOUT_FILENO, STDERR_FILENO) < 0 ? -errno : STDERR_FILENO; - - switch (e) { - - case EXEC_OUTPUT_NULL: - return open_null_as(O_WRONLY, STDERR_FILENO); - - case EXEC_OUTPUT_TTY: - if (is_terminal_input(i)) - return dup2(STDIN_FILENO, STDERR_FILENO) < 0 ? -errno : STDERR_FILENO; - - /* We don't reset the terminal if this is just about output */ - return open_terminal_as(tty_path(context), O_WRONLY, STDERR_FILENO); - - case EXEC_OUTPUT_SYSLOG: - case EXEC_OUTPUT_SYSLOG_AND_CONSOLE: - case EXEC_OUTPUT_KMSG: - case EXEC_OUTPUT_KMSG_AND_CONSOLE: - case EXEC_OUTPUT_JOURNAL: - case EXEC_OUTPUT_JOURNAL_AND_CONSOLE: - return connect_logger_as(context, e, ident, unit_id, STDERR_FILENO); - - case EXEC_OUTPUT_SOCKET: - assert(socket_fd >= 0); - return dup2(socket_fd, STDERR_FILENO) < 0 ? -errno : STDERR_FILENO; - - default: - assert_not_reached("Unknown error type"); - } -} - -static int chown_terminal(int fd, uid_t uid) { - struct stat st; - - assert(fd >= 0); - - /* This might fail. What matters are the results. */ - (void) fchown(fd, uid, -1); - (void) fchmod(fd, TTY_MODE); - - if (fstat(fd, &st) < 0) - return -errno; - - if (st.st_uid != uid || (st.st_mode & 0777) != TTY_MODE) - return -EPERM; - - return 0; -} - -static int setup_confirm_stdio(int *_saved_stdin, - int *_saved_stdout) { - int fd = -1, saved_stdin, saved_stdout = -1, r; - - assert(_saved_stdin); - assert(_saved_stdout); - - saved_stdin = fcntl(STDIN_FILENO, F_DUPFD, 3); - if (saved_stdin < 0) - return -errno; - - saved_stdout = fcntl(STDOUT_FILENO, F_DUPFD, 3); - if (saved_stdout < 0) { - r = errno; - goto fail; - } - - fd = acquire_terminal( - "/dev/console", - false, - false, - false, - DEFAULT_CONFIRM_USEC); - if (fd < 0) { - r = fd; - goto fail; - } - - r = chown_terminal(fd, getuid()); - if (r < 0) - goto fail; - - if (dup2(fd, STDIN_FILENO) < 0) { - r = -errno; - goto fail; - } - - if (dup2(fd, STDOUT_FILENO) < 0) { - r = -errno; - goto fail; - } - - if (fd >= 2) - close_nointr_nofail(fd); - - *_saved_stdin = saved_stdin; - *_saved_stdout = saved_stdout; - - return 0; - -fail: - if (saved_stdout >= 0) - close_nointr_nofail(saved_stdout); - - if (saved_stdin >= 0) - close_nointr_nofail(saved_stdin); - - if (fd >= 0) - close_nointr_nofail(fd); - - return r; -} - -static int write_confirm_message(const char *format, ...) { - int fd; - va_list ap; - - assert(format); - - fd = open_terminal("/dev/console", O_WRONLY|O_NOCTTY|O_CLOEXEC); - if (fd < 0) - return fd; - - va_start(ap, format); - vdprintf(fd, format, ap); - va_end(ap); - - close_nointr_nofail(fd); - - return 0; -} - -static int restore_confirm_stdio(int *saved_stdin, - int *saved_stdout) { - - int r = 0; - - assert(saved_stdin); - assert(saved_stdout); - - release_terminal(); - - if (*saved_stdin >= 0) - if (dup2(*saved_stdin, STDIN_FILENO) < 0) - r = -errno; - - if (*saved_stdout >= 0) - if (dup2(*saved_stdout, STDOUT_FILENO) < 0) - r = -errno; - - if (*saved_stdin >= 0) - close_nointr_nofail(*saved_stdin); - - if (*saved_stdout >= 0) - close_nointr_nofail(*saved_stdout); - - return r; -} - -static int ask_for_confirmation(char *response, char **argv) { - int saved_stdout = -1, saved_stdin = -1, r; - char *line; - - r = setup_confirm_stdio(&saved_stdin, &saved_stdout); - if (r < 0) - return r; - - line = exec_command_line(argv); - if (!line) - return -ENOMEM; - - r = ask(response, "yns", "Execute %s? [Yes, No, Skip] ", line); - free(line); - - restore_confirm_stdio(&saved_stdin, &saved_stdout); - - return r; -} - -static int enforce_groups(const ExecContext *context, const char *username, gid_t gid) { - bool keep_groups = false; - int r; - - assert(context); - - /* Lookup and set GID and supplementary group list. Here too - * we avoid NSS lookups for gid=0. */ - - if (context->group || username) { - - if (context->group) { - const char *g = context->group; - - if ((r = get_group_creds(&g, &gid)) < 0) - return r; - } - - /* First step, initialize groups from /etc/groups */ - if (username && gid != 0) { - if (initgroups(username, gid) < 0) - return -errno; - - keep_groups = true; - } - - /* Second step, set our gids */ - if (setresgid(gid, gid, gid) < 0) - return -errno; - } - - if (context->supplementary_groups) { - int ngroups_max, k; - gid_t *gids; - char **i; - - /* Final step, initialize any manually set supplementary groups */ - assert_se((ngroups_max = (int) sysconf(_SC_NGROUPS_MAX)) > 0); - - if (!(gids = new(gid_t, ngroups_max))) - return -ENOMEM; - - if (keep_groups) { - if ((k = getgroups(ngroups_max, gids)) < 0) { - free(gids); - return -errno; - } - } else - k = 0; - - STRV_FOREACH(i, context->supplementary_groups) { - const char *g; - - if (k >= ngroups_max) { - free(gids); - return -E2BIG; - } - - g = *i; - r = get_group_creds(&g, gids+k); - if (r < 0) { - free(gids); - return r; - } - - k++; - } - - if (setgroups(k, gids) < 0) { - free(gids); - return -errno; - } - - free(gids); - } - - return 0; -} - -static int enforce_user(const ExecContext *context, uid_t uid) { - int r; - assert(context); - - /* Sets (but doesn't lookup) the uid and make sure we keep the - * capabilities while doing so. */ - - if (context->capabilities) { - cap_t d; - static const cap_value_t bits[] = { - CAP_SETUID, /* Necessary so that we can run setresuid() below */ - CAP_SETPCAP /* Necessary so that we can set PR_SET_SECUREBITS later on */ - }; - - /* First step: If we need to keep capabilities but - * drop privileges we need to make sure we keep our - * caps, whiel we drop privileges. */ - if (uid != 0) { - int sb = context->secure_bits|SECURE_KEEP_CAPS; - - if (prctl(PR_GET_SECUREBITS) != sb) - if (prctl(PR_SET_SECUREBITS, sb) < 0) - return -errno; - } - - /* Second step: set the capabilities. This will reduce - * the capabilities to the minimum we need. */ - - if (!(d = cap_dup(context->capabilities))) - return -errno; - - if (cap_set_flag(d, CAP_EFFECTIVE, ELEMENTSOF(bits), bits, CAP_SET) < 0 || - cap_set_flag(d, CAP_PERMITTED, ELEMENTSOF(bits), bits, CAP_SET) < 0) { - r = -errno; - cap_free(d); - return r; - } - - if (cap_set_proc(d) < 0) { - r = -errno; - cap_free(d); - return r; - } - - cap_free(d); - } - - /* Third step: actually set the uids */ - if (setresuid(uid, uid, uid) < 0) - return -errno; - - /* At this point we should have all necessary capabilities but - are otherwise a normal user. However, the caps might got - corrupted due to the setresuid() so we need clean them up - later. This is done outside of this call. */ - - return 0; -} - -#ifdef HAVE_PAM - -static int null_conv( - int num_msg, - const struct pam_message **msg, - struct pam_response **resp, - void *appdata_ptr) { - - /* We don't support conversations */ - - return PAM_CONV_ERR; -} - -static int setup_pam( - const char *name, - const char *user, - uid_t uid, - const char *tty, - char ***pam_env, - int fds[], unsigned n_fds) { - - static const struct pam_conv conv = { - .conv = null_conv, - .appdata_ptr = NULL - }; - - pam_handle_t *handle = NULL; - sigset_t ss, old_ss; - int pam_code = PAM_SUCCESS; - int err; - char **e = NULL; - bool close_session = false; - pid_t pam_pid = 0, parent_pid; - - assert(name); - assert(user); - assert(pam_env); - - /* We set up PAM in the parent process, then fork. The child - * will then stay around until killed via PR_GET_PDEATHSIG or - * systemd via the cgroup logic. It will then remove the PAM - * session again. The parent process will exec() the actual - * daemon. We do things this way to ensure that the main PID - * of the daemon is the one we initially fork()ed. */ - - if ((pam_code = pam_start(name, user, &conv, &handle)) != PAM_SUCCESS) { - handle = NULL; - goto fail; - } - - if (tty) - if ((pam_code = pam_set_item(handle, PAM_TTY, tty)) != PAM_SUCCESS) - goto fail; - - if ((pam_code = pam_acct_mgmt(handle, PAM_SILENT)) != PAM_SUCCESS) - goto fail; - - if ((pam_code = pam_open_session(handle, PAM_SILENT)) != PAM_SUCCESS) - goto fail; - - close_session = true; - - if ((!(e = pam_getenvlist(handle)))) { - pam_code = PAM_BUF_ERR; - goto fail; - } - - /* Block SIGTERM, so that we know that it won't get lost in - * the child */ - if (sigemptyset(&ss) < 0 || - sigaddset(&ss, SIGTERM) < 0 || - sigprocmask(SIG_BLOCK, &ss, &old_ss) < 0) - goto fail; - - parent_pid = getpid(); - - if ((pam_pid = fork()) < 0) - goto fail; - - if (pam_pid == 0) { - int sig; - int r = EXIT_PAM; - - /* The child's job is to reset the PAM session on - * termination */ - - /* This string must fit in 10 chars (i.e. the length - * of "/sbin/init"), to look pretty in /bin/ps */ - rename_process("(sd-pam)"); - - /* Make sure we don't keep open the passed fds in this - child. We assume that otherwise only those fds are - open here that have been opened by PAM. */ - close_many(fds, n_fds); - - /* Drop privileges - we don't need any to pam_close_session - * and this will make PR_SET_PDEATHSIG work in most cases. - * If this fails, ignore the error - but expect sd-pam threads - * to fail to exit normally */ - if (setresuid(uid, uid, uid) < 0) - log_error("Error: Failed to setresuid() in sd-pam: %s", strerror(-r)); - - /* Wait until our parent died. This will only work if - * the above setresuid() succeeds, otherwise the kernel - * will not allow unprivileged parents kill their privileged - * children this way. We rely on the control groups kill logic - * to do the rest for us. */ - if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0) - goto child_finish; - - /* Check if our parent process might already have - * died? */ - if (getppid() == parent_pid) { - for (;;) { - if (sigwait(&ss, &sig) < 0) { - if (errno == EINTR) - continue; - - goto child_finish; - } - - assert(sig == SIGTERM); - break; - } - } - - /* If our parent died we'll end the session */ - if (getppid() != parent_pid) - if ((pam_code = pam_close_session(handle, PAM_DATA_SILENT)) != PAM_SUCCESS) - goto child_finish; - - r = 0; - - child_finish: - pam_end(handle, pam_code | PAM_DATA_SILENT); - _exit(r); - } - - /* If the child was forked off successfully it will do all the - * cleanups, so forget about the handle here. */ - handle = NULL; - - /* Unblock SIGTERM again in the parent */ - if (sigprocmask(SIG_SETMASK, &old_ss, NULL) < 0) - goto fail; - - /* We close the log explicitly here, since the PAM modules - * might have opened it, but we don't want this fd around. */ - closelog(); - - *pam_env = e; - e = NULL; - - return 0; - -fail: - if (pam_code != PAM_SUCCESS) - err = -EPERM; /* PAM errors do not map to errno */ - else - err = -errno; - - if (handle) { - if (close_session) - pam_code = pam_close_session(handle, PAM_DATA_SILENT); - - pam_end(handle, pam_code | PAM_DATA_SILENT); - } - - strv_free(e); - - closelog(); - - if (pam_pid > 1) { - kill(pam_pid, SIGTERM); - kill(pam_pid, SIGCONT); - } - - return err; -} -#endif - -static void rename_process_from_path(const char *path) { - char process_name[11]; - const char *p; - size_t l; - - /* This resulting string must fit in 10 chars (i.e. the length - * of "/sbin/init") to look pretty in /bin/ps */ - - p = path_get_file_name(path); - if (isempty(p)) { - rename_process("(...)"); - return; - } - - l = strlen(p); - if (l > 8) { - /* The end of the process name is usually more - * interesting, since the first bit might just be - * "systemd-" */ - p = p + l - 8; - l = 8; - } - - process_name[0] = '('; - memcpy(process_name+1, p, l); - process_name[1+l] = ')'; - process_name[1+l+1] = 0; - - rename_process(process_name); -} - -static int apply_seccomp(uint32_t *syscall_filter) { - static const struct sock_filter header[] = { - VALIDATE_ARCHITECTURE, - EXAMINE_SYSCALL - }; - static const struct sock_filter footer[] = { - _KILL_PROCESS - }; - - int i; - unsigned n; - struct sock_filter *f; - struct sock_fprog prog; - - assert(syscall_filter); - - /* First: count the syscalls to check for */ - for (i = 0, n = 0; i < syscall_max(); i++) - if (syscall_filter[i >> 4] & (1 << (i & 31))) - n++; - - /* Second: build the filter program from a header the syscall - * matches and the footer */ - f = alloca(sizeof(struct sock_filter) * (ELEMENTSOF(header) + 2*n + ELEMENTSOF(footer))); - memcpy(f, header, sizeof(header)); - - for (i = 0, n = 0; i < syscall_max(); i++) - if (syscall_filter[i >> 4] & (1 << (i & 31))) { - struct sock_filter item[] = { - BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, i, 0, 1), - BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW) - }; - - assert_cc(ELEMENTSOF(item) == 2); - - f[ELEMENTSOF(header) + 2*n] = item[0]; - f[ELEMENTSOF(header) + 2*n+1] = item[1]; - - n++; - } - - memcpy(f + (ELEMENTSOF(header) + 2*n), footer, sizeof(footer)); - - /* Third: install the filter */ - zero(prog); - prog.len = ELEMENTSOF(header) + ELEMENTSOF(footer) + 2*n; - prog.filter = f; - if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog) < 0) - return -errno; - - return 0; -} - -int exec_spawn(ExecCommand *command, - char **argv, - const ExecContext *context, - int fds[], unsigned n_fds, - char **environment, - bool apply_permissions, - bool apply_chroot, - bool apply_tty_stdin, - bool confirm_spawn, - CGroupBonding *cgroup_bondings, - CGroupAttribute *cgroup_attributes, - const char *cgroup_suffix, - const char *unit_id, - int idle_pipe[2], - pid_t *ret) { - - pid_t pid; - int r; - char *line; - int socket_fd; - char _cleanup_strv_free_ **files_env = NULL; - - assert(command); - assert(context); - assert(ret); - assert(fds || n_fds <= 0); - - if (context->std_input == EXEC_INPUT_SOCKET || - context->std_output == EXEC_OUTPUT_SOCKET || - context->std_error == EXEC_OUTPUT_SOCKET) { - - if (n_fds != 1) - return -EINVAL; - - socket_fd = fds[0]; - - fds = NULL; - n_fds = 0; - } else - socket_fd = -1; - - 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; - } - - if (!argv) - argv = command->argv; - - line = exec_command_line(argv); - if (!line) - return log_oom(); - - 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) - return r; - - cgroup_attribute_apply_list(cgroup_attributes, cgroup_bondings); - - pid = fork(); - if (pid < 0) - return -errno; - - if (pid == 0) { - int i, err; - sigset_t ss; - const char *username = NULL, *home = NULL; - uid_t uid = (uid_t) -1; - gid_t gid = (gid_t) -1; - char _cleanup_strv_free_ **our_env = NULL, **pam_env = NULL, - **final_env = NULL, **final_argv = NULL; - unsigned n_env = 0; - bool set_access = false; - - /* child */ - - rename_process_from_path(command->path); - - /* We reset exactly these signals, since they are the - * only ones we set to SIG_IGN in the main daemon. All - * others we leave untouched because we set them to - * SIG_DFL or a valid handler initially, both of which - * will be demoted to SIG_DFL. */ - default_signals(SIGNALS_CRASH_HANDLER, - SIGNALS_IGNORE, -1); - - if (context->ignore_sigpipe) - ignore_signals(SIGPIPE, -1); - - assert_se(sigemptyset(&ss) == 0); - if (sigprocmask(SIG_SETMASK, &ss, NULL) < 0) { - err = -errno; - r = EXIT_SIGNAL_MASK; - goto fail_child; - } - - if (idle_pipe) { - if (idle_pipe[1] >= 0) - close_nointr_nofail(idle_pipe[1]); - if (idle_pipe[0] >= 0) { - fd_wait_for_event(idle_pipe[0], POLLHUP, IDLE_TIMEOUT_USEC); - close_nointr_nofail(idle_pipe[0]); - } - } - - /* Close sockets very early to make sure we don't - * block init reexecution because it cannot bind its - * sockets */ - log_forget_fds(); - err = close_all_fds(socket_fd >= 0 ? &socket_fd : fds, - socket_fd >= 0 ? 1 : n_fds); - if (err < 0) { - r = EXIT_FDS; - goto fail_child; - } - - if (!context->same_pgrp) - if (setsid() < 0) { - err = -errno; - r = EXIT_SETSID; - goto fail_child; - } - - if (context->tcpwrap_name) { - if (socket_fd >= 0) - if (!socket_tcpwrap(socket_fd, context->tcpwrap_name)) { - err = -EACCES; - r = EXIT_TCPWRAP; - goto fail_child; - } - - for (i = 0; i < (int) n_fds; i++) { - if (!socket_tcpwrap(fds[i], context->tcpwrap_name)) { - err = -EACCES; - r = EXIT_TCPWRAP; - goto fail_child; - } - } - } - - exec_context_tty_reset(context); - - if (confirm_spawn) { - char response; - - err = ask_for_confirmation(&response, argv); - if (err == -ETIMEDOUT) - write_confirm_message("Confirmation question timed out, assuming positive response.\n"); - else if (err < 0) - write_confirm_message("Couldn't ask confirmation question, assuming positive response: %s\n", strerror(-err)); - else if (response == 's') { - write_confirm_message("Skipping execution.\n"); - err = -ECANCELED; - r = EXIT_CONFIRM; - goto fail_child; - } else if (response == 'n') { - write_confirm_message("Failing execution.\n"); - err = r = 0; - goto fail_child; - } - } - - /* If a socket is connected to STDIN/STDOUT/STDERR, we - * must sure to drop O_NONBLOCK */ - if (socket_fd >= 0) - fd_nonblock(socket_fd, false); - - err = setup_input(context, socket_fd, apply_tty_stdin); - if (err < 0) { - r = EXIT_STDIN; - goto fail_child; - } - - err = setup_output(context, socket_fd, path_get_file_name(command->path), unit_id, apply_tty_stdin); - if (err < 0) { - r = EXIT_STDOUT; - goto fail_child; - } - - err = setup_error(context, socket_fd, path_get_file_name(command->path), unit_id, apply_tty_stdin); - if (err < 0) { - r = EXIT_STDERR; - goto fail_child; - } - - if (cgroup_bondings) { - err = cgroup_bonding_install_list(cgroup_bondings, 0, cgroup_suffix); - if (err < 0) { - r = EXIT_CGROUP; - goto fail_child; - } - } - - if (context->oom_score_adjust_set) { - char t[16]; - - snprintf(t, sizeof(t), "%i", context->oom_score_adjust); - char_array_0(t); - - if (write_one_line_file("/proc/self/oom_score_adj", t) < 0) { - err = -errno; - r = EXIT_OOM_ADJUST; - goto fail_child; - } - } - - if (context->nice_set) - if (setpriority(PRIO_PROCESS, 0, context->nice) < 0) { - err = -errno; - r = EXIT_NICE; - goto fail_child; - } - - if (context->cpu_sched_set) { - struct sched_param param; - - zero(param); - param.sched_priority = context->cpu_sched_priority; - - if (sched_setscheduler(0, context->cpu_sched_policy | - (context->cpu_sched_reset_on_fork ? SCHED_RESET_ON_FORK : 0), ¶m) < 0) { - err = -errno; - r = EXIT_SETSCHEDULER; - goto fail_child; - } - } - - if (context->cpuset) - if (sched_setaffinity(0, CPU_ALLOC_SIZE(context->cpuset_ncpus), context->cpuset) < 0) { - err = -errno; - r = EXIT_CPUAFFINITY; - goto fail_child; - } - - if (context->ioprio_set) - if (ioprio_set(IOPRIO_WHO_PROCESS, 0, context->ioprio) < 0) { - err = -errno; - r = EXIT_IOPRIO; - goto fail_child; - } - - if (context->timer_slack_nsec != (nsec_t) -1) - if (prctl(PR_SET_TIMERSLACK, context->timer_slack_nsec) < 0) { - err = -errno; - r = EXIT_TIMERSLACK; - goto fail_child; - } - - if (context->utmp_id) - utmp_put_init_process(context->utmp_id, getpid(), getsid(0), context->tty_path); - - if (context->user) { - username = context->user; - err = get_user_creds(&username, &uid, &gid, &home, NULL); - if (err < 0) { - r = EXIT_USER; - goto fail_child; - } - - if (is_terminal_input(context->std_input)) { - err = chown_terminal(STDIN_FILENO, uid); - if (err < 0) { - r = EXIT_STDIN; - goto fail_child; - } - } - - if (cgroup_bondings && context->control_group_modify) { - err = cgroup_bonding_set_group_access_list(cgroup_bondings, 0755, uid, gid); - if (err >= 0) - err = cgroup_bonding_set_task_access_list(cgroup_bondings, 0644, uid, gid, context->control_group_persistent); - if (err < 0) { - r = EXIT_CGROUP; - goto fail_child; - } - - set_access = true; - } - } - - if (cgroup_bondings && !set_access && context->control_group_persistent >= 0) { - err = cgroup_bonding_set_task_access_list(cgroup_bondings, (mode_t) -1, (uid_t) -1, (uid_t) -1, context->control_group_persistent); - if (err < 0) { - r = EXIT_CGROUP; - goto fail_child; - } - } - - if (apply_permissions) { - err = enforce_groups(context, username, gid); - if (err < 0) { - r = EXIT_GROUP; - goto fail_child; - } - } - - umask(context->umask); - -#ifdef HAVE_PAM - 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; - goto fail_child; - } - } -#endif - if (context->private_network) { - if (unshare(CLONE_NEWNET) < 0) { - err = -errno; - r = EXIT_NETWORK; - goto fail_child; - } - - loopback_setup(); - } - - if (strv_length(context->read_write_dirs) > 0 || - strv_length(context->read_only_dirs) > 0 || - strv_length(context->inaccessible_dirs) > 0 || - context->mount_flags != 0 || - context->private_tmp) { - err = setup_namespace(context->read_write_dirs, - context->read_only_dirs, - context->inaccessible_dirs, - context->private_tmp, - context->mount_flags); - if (err < 0) { - r = EXIT_NAMESPACE; - goto fail_child; - } - } - - if (apply_chroot) { - if (context->root_directory) - if (chroot(context->root_directory) < 0) { - err = -errno; - r = EXIT_CHROOT; - goto fail_child; - } - - if (chdir(context->working_directory ? context->working_directory : "/") < 0) { - err = -errno; - r = EXIT_CHDIR; - goto fail_child; - } - } else { - char _cleanup_free_ *d = NULL; - - if (asprintf(&d, "%s/%s", - context->root_directory ? context->root_directory : "", - context->working_directory ? context->working_directory : "") < 0) { - err = -ENOMEM; - r = EXIT_MEMORY; - goto fail_child; - } - - if (chdir(d) < 0) { - err = -errno; - r = EXIT_CHDIR; - goto fail_child; - } - } - - /* We repeat the fd closing here, to make sure that - * nothing is leaked from the PAM modules */ - err = close_all_fds(fds, n_fds); - if (err >= 0) - err = shift_fds(fds, n_fds); - if (err >= 0) - err = flags_fds(fds, n_fds, context->non_blocking); - if (err < 0) { - r = EXIT_FDS; - goto fail_child; - } - - if (apply_permissions) { - - for (i = 0; i < RLIMIT_NLIMITS; i++) { - if (!context->rlimit[i]) - continue; - - if (setrlimit_closest(i, context->rlimit[i]) < 0) { - err = -errno; - r = EXIT_LIMITS; - goto fail_child; - } - } - - if (context->capability_bounding_set_drop) { - err = capability_bounding_set_drop(context->capability_bounding_set_drop, false); - if (err < 0) { - r = EXIT_CAPABILITIES; - goto fail_child; - } - } - - if (context->user) { - err = enforce_user(context, uid); - if (err < 0) { - r = EXIT_USER; - goto fail_child; - } - } - - /* PR_GET_SECUREBITS is not privileged, while - * PR_SET_SECUREBITS is. So to suppress - * potential EPERMs we'll try not to call - * PR_SET_SECUREBITS unless necessary. */ - if (prctl(PR_GET_SECUREBITS) != context->secure_bits) - if (prctl(PR_SET_SECUREBITS, context->secure_bits) < 0) { - err = -errno; - r = EXIT_SECUREBITS; - goto fail_child; - } - - if (context->capabilities) - if (cap_set_proc(context->capabilities) < 0) { - err = -errno; - r = EXIT_CAPABILITIES; - goto fail_child; - } - - if (context->no_new_privileges) - if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) < 0) { - err = -errno; - r = EXIT_NO_NEW_PRIVILEGES; - goto fail_child; - } - - if (context->syscall_filter) { - err = apply_seccomp(context->syscall_filter); - if (err < 0) { - r = EXIT_SECCOMP; - goto fail_child; - } - } - } - - if (!(our_env = new0(char*, 7))) { - err = -ENOMEM; - r = EXIT_MEMORY; - goto fail_child; - } - - if (n_fds > 0) - if (asprintf(our_env + n_env++, "LISTEN_PID=%lu", (unsigned long) getpid()) < 0 || - asprintf(our_env + n_env++, "LISTEN_FDS=%u", n_fds) < 0) { - err = -ENOMEM; - r = EXIT_MEMORY; - goto fail_child; - } - - if (home) - if (asprintf(our_env + n_env++, "HOME=%s", home) < 0) { - err = -ENOMEM; - r = EXIT_MEMORY; - goto fail_child; - } - - if (username) - if (asprintf(our_env + n_env++, "LOGNAME=%s", username) < 0 || - asprintf(our_env + n_env++, "USER=%s", username) < 0) { - err = -ENOMEM; - r = EXIT_MEMORY; - goto fail_child; - } - - if (is_terminal_input(context->std_input) || - context->std_output == EXEC_OUTPUT_TTY || - context->std_error == EXEC_OUTPUT_TTY) - if (!(our_env[n_env++] = strdup(default_term_for_tty(tty_path(context))))) { - err = -ENOMEM; - r = EXIT_MEMORY; - goto fail_child; - } - - assert(n_env <= 7); - - if (!(final_env = strv_env_merge( - 5, - environment, - our_env, - context->environment, - files_env, - pam_env, - NULL))) { - err = -ENOMEM; - r = EXIT_MEMORY; - goto fail_child; - } - - if (!(final_argv = replace_env_argv(argv, final_env))) { - err = -ENOMEM; - r = EXIT_MEMORY; - goto fail_child; - } - - final_env = strv_env_clean(final_env); - - execve(command->path, final_argv, final_env); - err = -errno; - r = EXIT_EXEC; - - fail_child: - if (r != 0) { - log_open(); - 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(); - } - - _exit(r); - } - - 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 - * outside of the cgroup) and in the parent (so that we can be - * sure that when we kill the cgroup the process will be - * killed too). */ - if (cgroup_bondings) - cgroup_bonding_install_list(cgroup_bondings, pid, cgroup_suffix); - - exec_status_start(&command->exec_status, pid); - - *ret = pid; - return 0; -} - -void exec_context_init(ExecContext *c) { - assert(c); - - c->umask = 0022; - c->ioprio = IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE, 0); - c->cpu_sched_policy = SCHED_OTHER; - c->syslog_priority = LOG_DAEMON|LOG_INFO; - c->syslog_level_prefix = true; - c->control_group_persistent = -1; - c->ignore_sigpipe = true; - c->timer_slack_nsec = (nsec_t) -1; -} - -void exec_context_done(ExecContext *c) { - unsigned l; - - assert(c); - - strv_free(c->environment); - c->environment = NULL; - - strv_free(c->environment_files); - c->environment_files = NULL; - - for (l = 0; l < ELEMENTSOF(c->rlimit); l++) { - free(c->rlimit[l]); - c->rlimit[l] = NULL; - } - - free(c->working_directory); - c->working_directory = NULL; - free(c->root_directory); - c->root_directory = NULL; - - free(c->tty_path); - c->tty_path = NULL; - - free(c->tcpwrap_name); - c->tcpwrap_name = NULL; - - free(c->syslog_identifier); - c->syslog_identifier = NULL; - - free(c->user); - c->user = NULL; - - free(c->group); - c->group = NULL; - - strv_free(c->supplementary_groups); - c->supplementary_groups = NULL; - - free(c->pam_name); - c->pam_name = NULL; - - if (c->capabilities) { - cap_free(c->capabilities); - c->capabilities = NULL; - } - - strv_free(c->read_only_dirs); - c->read_only_dirs = NULL; - - strv_free(c->read_write_dirs); - c->read_write_dirs = NULL; - - strv_free(c->inaccessible_dirs); - c->inaccessible_dirs = NULL; - - if (c->cpuset) - CPU_FREE(c->cpuset); - - free(c->utmp_id); - c->utmp_id = NULL; - - free(c->syscall_filter); - c->syscall_filter = NULL; -} - -void exec_command_done(ExecCommand *c) { - assert(c); - - free(c->path); - c->path = NULL; - - strv_free(c->argv); - c->argv = NULL; -} - -void exec_command_done_array(ExecCommand *c, unsigned n) { - unsigned i; - - for (i = 0; i < n; i++) - exec_command_done(c+i); -} - -void exec_command_free_list(ExecCommand *c) { - ExecCommand *i; - - while ((i = c)) { - LIST_REMOVE(ExecCommand, command, c, i); - exec_command_done(i); - free(i); - } -} - -void exec_command_free_array(ExecCommand **c, unsigned n) { - unsigned i; - - for (i = 0; i < n; i++) { - exec_command_free_list(c[i]); - c[i] = NULL; - } -} - -int exec_context_load_environment(const ExecContext *c, char ***l) { - char **i, **r = NULL; - - assert(c); - assert(l); - - STRV_FOREACH(i, c->environment_files) { - char *fn; - int k; - bool ignore = false; - char **p; - - fn = *i; - - if (fn[0] == '-') { - ignore = true; - fn ++; - } - - if (!path_is_absolute(fn)) { - - if (ignore) - continue; - - strv_free(r); - return -EINVAL; - } - - if ((k = load_env_file(fn, &p)) < 0) { - - if (ignore) - continue; - - strv_free(r); - return k; - } - - 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; - - r = m; - } - } - - *l = r; - - return 0; -} - -static void strv_fprintf(FILE *f, char **l) { - char **g; - - assert(f); - - STRV_FOREACH(g, l) - fprintf(f, " %s", *g); -} - -void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) { - char ** e; - unsigned i; - - assert(c); - assert(f); - - if (!prefix) - prefix = ""; - - fprintf(f, - "%sUMask: %04o\n" - "%sWorkingDirectory: %s\n" - "%sRootDirectory: %s\n" - "%sNonBlocking: %s\n" - "%sPrivateTmp: %s\n" - "%sControlGroupModify: %s\n" - "%sControlGroupPersistent: %s\n" - "%sPrivateNetwork: %s\n" - "%sIgnoreSIGPIPE: %s\n", - prefix, c->umask, - prefix, c->working_directory ? c->working_directory : "/", - prefix, c->root_directory ? c->root_directory : "/", - prefix, yes_no(c->non_blocking), - prefix, yes_no(c->private_tmp), - prefix, yes_no(c->control_group_modify), - prefix, yes_no(c->control_group_persistent), - prefix, yes_no(c->private_network), - prefix, yes_no(c->ignore_sigpipe)); - - STRV_FOREACH(e, c->environment) - fprintf(f, "%sEnvironment: %s\n", prefix, *e); - - STRV_FOREACH(e, c->environment_files) - fprintf(f, "%sEnvironmentFile: %s\n", prefix, *e); - - if (c->tcpwrap_name) - fprintf(f, - "%sTCPWrapName: %s\n", - prefix, c->tcpwrap_name); - - if (c->nice_set) - fprintf(f, - "%sNice: %i\n", - prefix, c->nice); - - if (c->oom_score_adjust_set) - fprintf(f, - "%sOOMScoreAdjust: %i\n", - prefix, c->oom_score_adjust); - - for (i = 0; i < RLIM_NLIMITS; i++) - 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) { - 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, strna(class_str), - prefix, (int) IOPRIO_PRIO_DATA(c->ioprio)); - free(class_str); - } - - 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, 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); - for (i = 0; i < c->cpuset_ncpus; i++) - if (CPU_ISSET_S(i, CPU_ALLOC_SIZE(c->cpuset_ncpus), c->cpuset)) - fprintf(f, " %i", i); - fputs("\n", f); - } - - if (c->timer_slack_nsec != (nsec_t) -1) - fprintf(f, "%sTimerSlackNSec: %lu\n", prefix, (unsigned long)c->timer_slack_nsec); - - fprintf(f, - "%sStandardInput: %s\n" - "%sStandardOutput: %s\n" - "%sStandardError: %s\n", - prefix, exec_input_to_string(c->std_input), - prefix, exec_output_to_string(c->std_output), - prefix, exec_output_to_string(c->std_error)); - - if (c->tty_path) - fprintf(f, - "%sTTYPath: %s\n" - "%sTTYReset: %s\n" - "%sTTYVHangup: %s\n" - "%sTTYVTDisallocate: %s\n", - prefix, c->tty_path, - prefix, yes_no(c->tty_reset), - prefix, yes_no(c->tty_vhangup), - prefix, yes_no(c->tty_vt_disallocate)); - - 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) { - 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, strna(fac_str), - prefix, strna(lvl_str)); - free(lvl_str); - free(fac_str); - } - - if (c->capabilities) { - char *t; - if ((t = cap_to_text(c->capabilities, NULL))) { - fprintf(f, "%sCapabilities: %s\n", - prefix, t); - cap_free(t); - } - } - - if (c->secure_bits) - fprintf(f, "%sSecure Bits:%s%s%s%s%s%s\n", - prefix, - (c->secure_bits & SECURE_KEEP_CAPS) ? " keep-caps" : "", - (c->secure_bits & SECURE_KEEP_CAPS_LOCKED) ? " keep-caps-locked" : "", - (c->secure_bits & SECURE_NO_SETUID_FIXUP) ? " no-setuid-fixup" : "", - (c->secure_bits & SECURE_NO_SETUID_FIXUP_LOCKED) ? " no-setuid-fixup-locked" : "", - (c->secure_bits & SECURE_NOROOT) ? " noroot" : "", - (c->secure_bits & SECURE_NOROOT_LOCKED) ? "noroot-locked" : ""); - - if (c->capability_bounding_set_drop) { - unsigned long l; - fprintf(f, "%sCapabilityBoundingSet:", prefix); - - for (l = 0; l <= cap_last_cap(); l++) - if (!(c->capability_bounding_set_drop & ((uint64_t) 1ULL << (uint64_t) l))) { - char *t; - - if ((t = cap_to_name(l))) { - fprintf(f, " %s", t); - cap_free(t); - } - } - - fputs("\n", f); - } - - if (c->user) - fprintf(f, "%sUser: %s\n", prefix, c->user); - if (c->group) - fprintf(f, "%sGroup: %s\n", prefix, c->group); - - if (strv_length(c->supplementary_groups) > 0) { - fprintf(f, "%sSupplementaryGroups:", prefix); - strv_fprintf(f, c->supplementary_groups); - fputs("\n", f); - } - - if (c->pam_name) - fprintf(f, "%sPAMName: %s\n", prefix, c->pam_name); - - if (strv_length(c->read_write_dirs) > 0) { - fprintf(f, "%sReadWriteDirs:", prefix); - strv_fprintf(f, c->read_write_dirs); - fputs("\n", f); - } - - if (strv_length(c->read_only_dirs) > 0) { - fprintf(f, "%sReadOnlyDirs:", prefix); - strv_fprintf(f, c->read_only_dirs); - fputs("\n", f); - } - - if (strv_length(c->inaccessible_dirs) > 0) { - fprintf(f, "%sInaccessibleDirs:", prefix); - strv_fprintf(f, c->inaccessible_dirs); - fputs("\n", f); - } - - if (c->utmp_id) - fprintf(f, - "%sUtmpIdentifier: %s\n", - prefix, c->utmp_id); -} - -void exec_status_start(ExecStatus *s, pid_t pid) { - assert(s); - - zero(*s); - s->pid = pid; - dual_timestamp_get(&s->start_timestamp); -} - -void exec_status_exit(ExecStatus *s, ExecContext *context, pid_t pid, int code, int status) { - assert(s); - - if (s->pid && s->pid != pid) - zero(*s); - - s->pid = pid; - dual_timestamp_get(&s->exit_timestamp); - - s->code = code; - s->status = status; - - if (context) { - if (context->utmp_id) - utmp_put_dead_process(context->utmp_id, pid, code, status); - - exec_context_tty_reset(context); - } -} - -void exec_status_dump(ExecStatus *s, FILE *f, const char *prefix) { - char buf[FORMAT_TIMESTAMP_MAX]; - - assert(s); - assert(f); - - if (!prefix) - prefix = ""; - - if (s->pid <= 0) - return; - - fprintf(f, - "%sPID: %lu\n", - prefix, (unsigned long) s->pid); - - if (s->start_timestamp.realtime > 0) - fprintf(f, - "%sStart Timestamp: %s\n", - prefix, format_timestamp(buf, sizeof(buf), s->start_timestamp.realtime)); - - if (s->exit_timestamp.realtime > 0) - fprintf(f, - "%sExit Timestamp: %s\n" - "%sExit Code: %s\n" - "%sExit Status: %i\n", - prefix, format_timestamp(buf, sizeof(buf), s->exit_timestamp.realtime), - prefix, sigchld_code_to_string(s->code), - prefix, s->status); -} - -char *exec_command_line(char **argv) { - size_t k; - char *n, *p, **a; - bool first = true; - - assert(argv); - - k = 1; - STRV_FOREACH(a, argv) - k += strlen(*a)+3; - - if (!(n = new(char, k))) - return NULL; - - p = n; - STRV_FOREACH(a, argv) { - - if (!first) - *(p++) = ' '; - else - first = false; - - if (strpbrk(*a, WHITESPACE)) { - *(p++) = '\''; - p = stpcpy(p, *a); - *(p++) = '\''; - } else - p = stpcpy(p, *a); - - } - - *p = 0; - - /* FIXME: this doesn't really handle arguments that have - * spaces and ticks in them */ - - return n; -} - -void exec_command_dump(ExecCommand *c, FILE *f, const char *prefix) { - char *p2; - const char *prefix2; - - char *cmd; - - assert(c); - assert(f); - - if (!prefix) - prefix = ""; - p2 = strappend(prefix, "\t"); - prefix2 = p2 ? p2 : prefix; - - cmd = exec_command_line(c->argv); - - fprintf(f, - "%sCommand Line: %s\n", - prefix, cmd ? cmd : strerror(ENOMEM)); - - free(cmd); - - exec_status_dump(&c->exec_status, f, prefix2); - - free(p2); -} - -void exec_command_dump_list(ExecCommand *c, FILE *f, const char *prefix) { - assert(f); - - if (!prefix) - prefix = ""; - - LIST_FOREACH(command, c, c) - exec_command_dump(c, f, prefix); -} - -void exec_command_append_list(ExecCommand **l, ExecCommand *e) { - ExecCommand *end; - - assert(l); - assert(e); - - if (*l) { - /* It's kind of important, that we keep the order here */ - LIST_FIND_TAIL(ExecCommand, command, *l, end); - LIST_INSERT_AFTER(ExecCommand, command, *l, end, e); - } else - *l = e; -} - -int exec_command_set(ExecCommand *c, const char *path, ...) { - va_list ap; - char **l, *p; - - assert(c); - assert(path); - - va_start(ap, path); - l = strv_new_ap(path, ap); - va_end(ap); - - if (!l) - return -ENOMEM; - - if (!(p = strdup(path))) { - strv_free(l); - return -ENOMEM; - } - - free(c->path); - c->path = p; - - strv_free(c->argv); - c->argv = l; - - return 0; -} - -static const char* const exec_input_table[_EXEC_INPUT_MAX] = { - [EXEC_INPUT_NULL] = "null", - [EXEC_INPUT_TTY] = "tty", - [EXEC_INPUT_TTY_FORCE] = "tty-force", - [EXEC_INPUT_TTY_FAIL] = "tty-fail", - [EXEC_INPUT_SOCKET] = "socket" -}; - -DEFINE_STRING_TABLE_LOOKUP(exec_input, ExecInput); - -static const char* const exec_output_table[_EXEC_OUTPUT_MAX] = { - [EXEC_OUTPUT_INHERIT] = "inherit", - [EXEC_OUTPUT_NULL] = "null", - [EXEC_OUTPUT_TTY] = "tty", - [EXEC_OUTPUT_SYSLOG] = "syslog", - [EXEC_OUTPUT_SYSLOG_AND_CONSOLE] = "syslog+console", - [EXEC_OUTPUT_KMSG] = "kmsg", - [EXEC_OUTPUT_KMSG_AND_CONSOLE] = "kmsg+console", - [EXEC_OUTPUT_JOURNAL] = "journal", - [EXEC_OUTPUT_JOURNAL_AND_CONSOLE] = "journal+console", - [EXEC_OUTPUT_SOCKET] = "socket" -}; - -DEFINE_STRING_TABLE_LOOKUP(exec_output, ExecOutput); diff --git a/src/core/execute.h b/src/core/execute.h deleted file mode 100644 index 2bcd2e1e6c..0000000000 --- a/src/core/execute.h +++ /dev/null @@ -1,209 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -#pragma once - -/*** - 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/>. -***/ - -typedef struct ExecStatus ExecStatus; -typedef struct ExecCommand ExecCommand; -typedef struct ExecContext ExecContext; - -#include <linux/types.h> -#include <sys/time.h> -#include <sys/resource.h> -#include <sys/capability.h> -#include <stdbool.h> -#include <stdio.h> -#include <sched.h> - -struct CGroupBonding; -struct CGroupAttribute; - -#include "list.h" -#include "util.h" - -typedef enum ExecInput { - EXEC_INPUT_NULL, - EXEC_INPUT_TTY, - EXEC_INPUT_TTY_FORCE, - EXEC_INPUT_TTY_FAIL, - EXEC_INPUT_SOCKET, - _EXEC_INPUT_MAX, - _EXEC_INPUT_INVALID = -1 -} ExecInput; - -typedef enum ExecOutput { - EXEC_OUTPUT_INHERIT, - EXEC_OUTPUT_NULL, - EXEC_OUTPUT_TTY, - EXEC_OUTPUT_SYSLOG, - EXEC_OUTPUT_SYSLOG_AND_CONSOLE, - EXEC_OUTPUT_KMSG, - EXEC_OUTPUT_KMSG_AND_CONSOLE, - EXEC_OUTPUT_JOURNAL, - EXEC_OUTPUT_JOURNAL_AND_CONSOLE, - EXEC_OUTPUT_SOCKET, - _EXEC_OUTPUT_MAX, - _EXEC_OUTPUT_INVALID = -1 -} ExecOutput; - -struct ExecStatus { - dual_timestamp start_timestamp; - dual_timestamp exit_timestamp; - pid_t pid; - int code; /* as in siginfo_t::si_code */ - int status; /* as in sigingo_t::si_status */ -}; - -struct ExecCommand { - char *path; - char **argv; - ExecStatus exec_status; - LIST_FIELDS(ExecCommand, command); /* useful for chaining commands */ - bool ignore; -}; - -struct ExecContext { - char **environment; - char **environment_files; - - struct rlimit *rlimit[RLIMIT_NLIMITS]; - char *working_directory, *root_directory; - - mode_t umask; - int oom_score_adjust; - int nice; - int ioprio; - int cpu_sched_policy; - int cpu_sched_priority; - - cpu_set_t *cpuset; - unsigned cpuset_ncpus; - - ExecInput std_input; - ExecOutput std_output; - ExecOutput std_error; - - nsec_t timer_slack_nsec; - - char *tcpwrap_name; - - char *tty_path; - - bool tty_reset; - bool tty_vhangup; - bool tty_vt_disallocate; - - bool ignore_sigpipe; - - /* Since resolving these names might might involve socket - * connections and we don't want to deadlock ourselves these - * names are resolved on execution only and in the child - * process. */ - char *user; - char *group; - char **supplementary_groups; - - char *pam_name; - - char *utmp_id; - - char **read_write_dirs, **read_only_dirs, **inaccessible_dirs; - unsigned long mount_flags; - - uint64_t capability_bounding_set_drop; - - cap_t capabilities; - int secure_bits; - - int syslog_priority; - char *syslog_identifier; - bool syslog_level_prefix; - - bool cpu_sched_reset_on_fork; - bool non_blocking; - bool private_tmp; - bool private_network; - - bool no_new_privileges; - - bool control_group_modify; - int control_group_persistent; - - /* This is not exposed to the user but available - * internally. We need it to make sure that whenever we spawn - * /bin/mount it is run in the same process group as us so - * that the autofs logic detects that it belongs to us and we - * don't enter a trigger loop. */ - bool same_pgrp; - - uint32_t *syscall_filter; - - bool oom_score_adjust_set:1; - bool nice_set:1; - bool ioprio_set:1; - bool cpu_sched_set:1; -}; - -int exec_spawn(ExecCommand *command, - char **argv, - const ExecContext *context, - int fds[], unsigned n_fds, - char **environment, - bool apply_permissions, - bool apply_chroot, - bool apply_tty_stdin, - bool confirm_spawn, - struct CGroupBonding *cgroup_bondings, - struct CGroupAttribute *cgroup_attributes, - const char *cgroup_suffix, - const char *unit_id, - int pipe_fd[2], - pid_t *ret); - -void exec_command_done(ExecCommand *c); -void exec_command_done_array(ExecCommand *c, unsigned n); - -void exec_command_free_list(ExecCommand *c); -void exec_command_free_array(ExecCommand **c, unsigned n); - -char *exec_command_line(char **argv); - -void exec_command_dump(ExecCommand *c, FILE *f, const char *prefix); -void exec_command_dump_list(ExecCommand *c, FILE *f, const char *prefix); -void exec_command_append_list(ExecCommand **l, ExecCommand *e); -int exec_command_set(ExecCommand *c, const char *path, ...); - -void exec_context_init(ExecContext *c); -void exec_context_done(ExecContext *c); -void exec_context_dump(ExecContext *c, FILE* f, const char *prefix); -void exec_context_tty_reset(const ExecContext *context); - -int exec_context_load_environment(const ExecContext *c, char ***l); - -void exec_status_start(ExecStatus *s, pid_t pid); -void exec_status_exit(ExecStatus *s, ExecContext *context, pid_t pid, int code, int status); -void exec_status_dump(ExecStatus *s, FILE *f, const char *prefix); - -const char* exec_output_to_string(ExecOutput i); -ExecOutput exec_output_from_string(const char *s); - -const char* exec_input_to_string(ExecInput i); -ExecInput exec_input_from_string(const char *s); 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/fdset.h b/src/core/fdset.h deleted file mode 100644 index c3e408c8ad..0000000000 --- a/src/core/fdset.h +++ /dev/null @@ -1,37 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -#pragma once - -/*** - 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/>. -***/ - -typedef struct FDSet FDSet; - -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); diff --git a/src/core/hostname-setup.c b/src/core/hostname-setup.c deleted file mode 100644 index dbd2227e21..0000000000 --- a/src/core/hostname-setup.c +++ /dev/null @@ -1,182 +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 <unistd.h> -#include <stdio.h> -#include <errno.h> -#include <string.h> -#include <stdlib.h> - -#include "hostname-setup.h" -#include "macro.h" -#include "util.h" -#include "log.h" - -#if 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_GENTOO) -#define FILENAME "/etc/conf.d/hostname" -#endif - -static int read_and_strip_hostname(const char *path, char **hn) { - char *s; - int r; - - assert(path); - assert(hn); - - r = read_one_line_file(path, &s); - if (r < 0) - return r; - - hostname_cleanup(s); - - if (isempty(s)) { - free(s); - return -ENOENT; - } - - *hn = s; - - return 0; -} - -static int read_distro_hostname(char **hn) { - -#if defined(TARGET_GENTOO) || defined(TARGET_ALTLINUX) || defined(TARGET_MANDRIVA) || defined(TARGET_MAGEIA) - int r; - _cleanup_fclose_ FILE *f = NULL; - - 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: - 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; - bool enoent = false; - - r = read_hostname(&b); - if (r < 0) { - hn = NULL; - - if (r == -ENOENT) - enoent = true; - else - log_warning("Failed to read configured hostname: %s", strerror(-r)); - } else - hn = b; - - if (isempty(hn)) { - /* Don't override the hostname if it is already set - * and not explicitly configured */ - if (hostname_is_set()) - goto finish; - - if (enoent) - log_info("No hostname configured."); - - hn = "localhost"; - } - - 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 r; -} diff --git a/src/core/hostname-setup.h b/src/core/hostname-setup.h deleted file mode 100644 index 8dc3a9e1d8..0000000000 --- a/src/core/hostname-setup.h +++ /dev/null @@ -1,24 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -#pragma once - -/*** - 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/>. -***/ - -int hostname_setup(void); diff --git a/src/core/ima-setup.c b/src/core/ima-setup.c deleted file mode 100644 index e8cc1ba8b6..0000000000 --- a/src/core/ima-setup.c +++ /dev/null @@ -1,115 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - Copyright (C) 2012 Roberto Sassu - Politecnico di Torino, Italy - TORSEC group -- http://security.polito.it - - 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 <unistd.h> -#include <stdio.h> -#include <errno.h> -#include <string.h> -#include <stdlib.h> -#include <fcntl.h> -#include <sys/stat.h> -#include <sys/mman.h> - -#include "ima-setup.h" -#include "mount-setup.h" -#include "macro.h" -#include "util.h" -#include "log.h" -#include "label.h" - -#define IMA_SECFS_DIR "/sys/kernel/security/ima" -#define IMA_SECFS_POLICY IMA_SECFS_DIR "/policy" -#define IMA_POLICY_PATH "/etc/ima/ima-policy" - -int ima_setup(void) { - -#ifdef HAVE_IMA - struct stat st; - ssize_t policy_size = 0, written = 0; - char *policy; - int policyfd = -1, imafd = -1; - int result = 0; - -#ifndef HAVE_SELINUX - /* Mount the securityfs filesystem */ - mount_setup_early(); -#endif - - if (stat(IMA_POLICY_PATH, &st) < 0) - return 0; - - policy_size = st.st_size; - if (stat(IMA_SECFS_DIR, &st) < 0) { - log_debug("IMA support is disabled in the kernel, ignoring."); - return 0; - } - - if (stat(IMA_SECFS_POLICY, &st) < 0) { - log_error("Another IMA custom policy has already been loaded, " - "ignoring."); - return 0; - } - - policyfd = open(IMA_POLICY_PATH, O_RDONLY|O_CLOEXEC); - if (policyfd < 0) { - log_error("Failed to open the IMA custom policy file %s (%m), " - "ignoring.", IMA_POLICY_PATH); - return 0; - } - - imafd = open(IMA_SECFS_POLICY, O_WRONLY|O_CLOEXEC); - if (imafd < 0) { - log_error("Failed to open the IMA kernel interface %s (%m), " - "ignoring.", IMA_SECFS_POLICY); - goto out; - } - - policy = mmap(NULL, policy_size, PROT_READ, MAP_PRIVATE, policyfd, 0); - if (policy == MAP_FAILED) { - log_error("mmap() failed (%m), freezing"); - result = -errno; - goto out; - } - - written = loop_write(imafd, policy, (size_t)policy_size, false); - if (written != policy_size) { - log_error("Failed to load the IMA custom policy file %s (%m), " - "ignoring.", IMA_POLICY_PATH); - goto out_mmap; - } - - log_info("Successfully loaded the IMA custom policy %s.", - IMA_POLICY_PATH); -out_mmap: - munmap(policy, policy_size); -out: - if (policyfd >= 0) - close_nointr_nofail(policyfd); - if (imafd >= 0) - close_nointr_nofail(imafd); - if (result) - return result; -#endif /* HAVE_IMA */ - - return 0; -} diff --git a/src/core/ima-setup.h b/src/core/ima-setup.h deleted file mode 100644 index 14b56d1fc2..0000000000 --- a/src/core/ima-setup.h +++ /dev/null @@ -1,26 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -#pragma once - -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - Copyright (C) 2012 Roberto Sassu - Politecnico di Torino, Italy - TORSEC group -- http://security.polito.it - - 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 ima_setup(void); diff --git a/src/core/initreq.h b/src/core/initreq.h deleted file mode 100644 index 859042ce42..0000000000 --- a/src/core/initreq.h +++ /dev/null @@ -1,77 +0,0 @@ -/* - * initreq.h Interface to talk to init through /dev/initctl. - * - * Copyright (C) 1995-2004 Miquel van Smoorenburg - * - * This library 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 of the License, or (at your option) any later version. - * - * Version: @(#)initreq.h 1.28 31-Mar-2004 MvS - * - */ -#ifndef _INITREQ_H -#define _INITREQ_H - -#include <sys/param.h> - -#if defined(__FreeBSD_kernel__) -# define INIT_FIFO "/etc/.initctl" -#else -# define INIT_FIFO "/dev/initctl" -#endif - -#define INIT_MAGIC 0x03091969 -#define INIT_CMD_START 0 -#define INIT_CMD_RUNLVL 1 -#define INIT_CMD_POWERFAIL 2 -#define INIT_CMD_POWERFAILNOW 3 -#define INIT_CMD_POWEROK 4 -#define INIT_CMD_BSD 5 -#define INIT_CMD_SETENV 6 -#define INIT_CMD_UNSETENV 7 - -#define INIT_CMD_CHANGECONS 12345 - -#ifdef MAXHOSTNAMELEN -# define INITRQ_HLEN MAXHOSTNAMELEN -#else -# define INITRQ_HLEN 64 -#endif - -/* - * This is what BSD 4.4 uses when talking to init. - * Linux doesn't use this right now. - */ -struct init_request_bsd { - char gen_id[8]; /* Beats me.. telnetd uses "fe" */ - char tty_id[16]; /* Tty name minus /dev/tty */ - char host[INITRQ_HLEN]; /* Hostname */ - char term_type[16]; /* Terminal type */ - int signal; /* Signal to send */ - int pid; /* Process to send to */ - char exec_name[128]; /* Program to execute */ - char reserved[128]; /* For future expansion. */ -}; - - -/* - * Because of legacy interfaces, "runlevel" and "sleeptime" - * aren't in a separate struct in the union. - * - * The weird sizes are because init expects the whole - * struct to be 384 bytes. - */ -struct init_request { - int magic; /* Magic number */ - int cmd; /* What kind of request */ - int runlevel; /* Runlevel to change to */ - int sleeptime; /* Time between TERM and KILL */ - union { - struct init_request_bsd bsd; - char data[368]; - } i; -}; - -#endif diff --git a/src/core/job.c b/src/core/job.c deleted file mode 100644 index f08b8cbc7d..0000000000 --- a/src/core/job.c +++ /dev/null @@ -1,1086 +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 <assert.h> -#include <errno.h> -#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" -#include "strv.h" -#include "load-fragment.h" -#include "load-dropin.h" -#include "log.h" -#include "dbus-job.h" - -JobBusClient* job_bus_client_new(DBusConnection *connection, const char *name) { - JobBusClient *cl; - size_t name_len; - - name_len = strlen(name); - cl = malloc0(sizeof(JobBusClient) + name_len + 1); - if (!cl) - return NULL; - - cl->bus = connection; - memcpy(cl->name, name, name_len + 1); - return cl; -} - -Job* job_new_raw(Unit *unit) { - Job *j; - - /* used for deserialization */ - - assert(unit); - - j = new0(Job, 1); - if (!j) - return NULL; - - j->manager = unit->manager; - j->unit = unit; - j->type = _JOB_TYPE_INVALID; - j->timer_watch.type = WATCH_INVALID; - - return j; -} - -Job* job_new(Unit *unit, JobType type) { - Job *j; - - assert(type < _JOB_TYPE_MAX); - - j = job_new_raw(unit); - if (!j) - return NULL; - - j->id = j->manager->current_job_id++; - j->type = type; - - /* We don't link it here, that's what job_dependency() is for */ - - return j; -} - -void job_free(Job *j) { - JobBusClient *cl; - - assert(j); - assert(!j->installed); - assert(!j->transaction_prev); - assert(!j->transaction_next); - assert(!j->subject_list); - assert(!j->object_list); - - if (j->in_run_queue) - LIST_REMOVE(Job, run_queue, j->manager->run_queue, j); - - if (j->in_dbus_queue) - LIST_REMOVE(Job, dbus_queue, j->manager->dbus_job_queue, j); - - if (j->timer_watch.type != WATCH_INVALID) { - assert(j->timer_watch.type == WATCH_JOB_TIMER); - assert(j->timer_watch.data.job == j); - assert(j->timer_watch.fd >= 0); - - assert_se(epoll_ctl(j->manager->epoll_fd, EPOLL_CTL_DEL, j->timer_watch.fd, NULL) >= 0); - close_nointr_nofail(j->timer_watch.fd); - } - - while ((cl = j->bus_client_list)) { - LIST_REMOVE(JobBusClient, client, j->bus_client_list, cl); - free(cl); - } - free(j); -} - -void job_uninstall(Job *j) { - Job **pj; - - assert(j->installed); - - pj = (j->type == JOB_NOP) ? &j->unit->nop_job : &j->unit->job; - assert(*pj == j); - - /* Detach from next 'bigger' objects */ - - /* daemon-reload should be transparent to job observers */ - if (j->manager->n_reloading <= 0) - bus_job_send_removed_signal(j); - - *pj = NULL; - - unit_add_to_gc_queue(j->unit); - - hashmap_remove(j->manager->jobs, UINT32_TO_PTR(j->id)); - j->installed = false; -} - -static bool job_type_allows_late_merge(JobType t) { - /* Tells whether it is OK to merge a job of type 't' with an already - * running job. - * Reloads cannot be merged this way. Think of the sequence: - * 1. Reload of a daemon is in progress; the daemon has already loaded - * its config file, but hasn't completed the reload operation yet. - * 2. Edit foo's config file. - * 3. Trigger another reload to have the daemon use the new config. - * Should the second reload job be merged into the first one, the daemon - * would not know about the new config. - * JOB_RESTART jobs on the other hand can be merged, because they get - * patched into JOB_START after stopping the unit. So if we see a - * JOB_RESTART running, it means the unit hasn't stopped yet and at - * this time the merge is still allowed. */ - return t != JOB_RELOAD; -} - -static void job_merge_into_installed(Job *j, Job *other) { - assert(j->installed); - assert(j->unit == other->unit); - - if (j->type != JOB_NOP) - job_type_merge_and_collapse(&j->type, other->type, j->unit); - else - assert(other->type == JOB_NOP); - - j->override = j->override || other->override; -} - -Job* job_install(Job *j) { - Job **pj; - Job *uj; - - assert(!j->installed); - assert(j->type < _JOB_TYPE_MAX_IN_TRANSACTION); - - pj = (j->type == JOB_NOP) ? &j->unit->nop_job : &j->unit->job; - uj = *pj; - - if (uj) { - if (j->type != JOB_NOP && job_type_is_conflicting(uj->type, j->type)) - 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); - return uj; - } else { - /* already running and not safe to merge into */ - /* Patch uj to become a merged job and re-run it. */ - /* 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); - uj->state = JOB_WAITING; - return uj; - } - } - } - - /* Install the job */ - *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); - return j; -} - -int job_install_deserialized(Job *j) { - Job **pj; - - assert(!j->installed); - - if (j->type < 0 || j->type >= _JOB_TYPE_MAX_IN_TRANSACTION) { - log_debug("Invalid job type %s in deserialization.", strna(job_type_to_string(j->type))); - return -EINVAL; - } - - 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); - 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); - return 0; -} - -JobDependency* job_dependency_new(Job *subject, Job *object, bool matters, bool conflicts) { - JobDependency *l; - - assert(object); - - /* Adds a new job link, which encodes that the 'subject' job - * needs the 'object' job in some way. If 'subject' is NULL - * this means the 'anchor' job (i.e. the one the user - * explicitly asked for) is the requester. */ - - if (!(l = new0(JobDependency, 1))) - return NULL; - - l->subject = subject; - l->object = object; - l->matters = matters; - l->conflicts = conflicts; - - if (subject) - LIST_PREPEND(JobDependency, subject, subject->subject_list, l); - - LIST_PREPEND(JobDependency, object, object->object_list, l); - - return l; -} - -void job_dependency_free(JobDependency *l) { - assert(l); - - if (l->subject) - LIST_REMOVE(JobDependency, subject, l->subject->subject_list, l); - - LIST_REMOVE(JobDependency, object, l->object->object_list, l); - - free(l); -} - -void job_dump(Job *j, FILE*f, const char *prefix) { - assert(j); - assert(f); - - if (!prefix) - prefix = ""; - - fprintf(f, - "%s-> Job %u:\n" - "%s\tAction: %s -> %s\n" - "%s\tState: %s\n" - "%s\tForced: %s\n", - prefix, j->id, - prefix, j->unit->id, job_type_to_string(j->type), - prefix, job_state_to_string(j->state), - prefix, yes_no(j->override)); -} - -/* - * Merging is commutative, so imagine the matrix as symmetric. We store only - * its lower triangle to avoid duplication. We don't store the main diagonal, - * because A merged with A is simply A. - * - * If the resulting type is collapsed immediately afterwards (to get rid of - * the JOB_RELOAD_OR_START, which lies outside the lookup function's domain), - * the following properties hold: - * - * Merging is associative! A merged with B merged with C is the same as - * A merged with C merged with B. - * - * Mergeability is transitive! If A can be merged with B and B with C then - * A also with C. - * - * Also, if A merged with B cannot be merged with C, then either A or B cannot - * be merged with C either. - */ -static const JobType job_merging_table[] = { -/* What \ With * JOB_START JOB_VERIFY_ACTIVE JOB_STOP JOB_RELOAD */ -/*********************************************************************************/ -/*JOB_START */ -/*JOB_VERIFY_ACTIVE */ JOB_START, -/*JOB_STOP */ -1, -1, -/*JOB_RELOAD */ JOB_RELOAD_OR_START, JOB_RELOAD, -1, -/*JOB_RESTART */ JOB_RESTART, JOB_RESTART, -1, JOB_RESTART, -}; - -JobType job_type_lookup_merge(JobType a, JobType b) { - assert_cc(ELEMENTSOF(job_merging_table) == _JOB_TYPE_MAX_MERGING * (_JOB_TYPE_MAX_MERGING - 1) / 2); - assert(a >= 0 && a < _JOB_TYPE_MAX_MERGING); - assert(b >= 0 && b < _JOB_TYPE_MAX_MERGING); - - if (a == b) - return a; - - if (a < b) { - JobType tmp = a; - a = b; - b = tmp; - } - - return job_merging_table[(a - 1) * a / 2 + b]; -} - -bool job_type_is_redundant(JobType a, UnitActiveState b) { - switch (a) { - - case JOB_START: - return - b == UNIT_ACTIVE || - b == UNIT_RELOADING; - - case JOB_STOP: - return - b == UNIT_INACTIVE || - b == UNIT_FAILED; - - case JOB_VERIFY_ACTIVE: - return - b == UNIT_ACTIVE || - b == UNIT_RELOADING; - - case JOB_RELOAD: - return - b == UNIT_RELOADING; - - case JOB_RESTART: - return - b == UNIT_ACTIVATING; - - default: - assert_not_reached("Invalid job type"); - } -} - -void job_type_collapse(JobType *t, Unit *u) { - UnitActiveState s; - - switch (*t) { - - case JOB_TRY_RESTART: - s = unit_active_state(u); - if (UNIT_IS_INACTIVE_OR_DEACTIVATING(s)) - *t = JOB_NOP; - else - *t = JOB_RESTART; - break; - - case JOB_RELOAD_OR_START: - s = unit_active_state(u); - if (UNIT_IS_INACTIVE_OR_DEACTIVATING(s)) - *t = JOB_START; - else - *t = JOB_RELOAD; - break; - - default: - ; - } -} - -int job_type_merge_and_collapse(JobType *a, JobType b, Unit *u) { - JobType t = job_type_lookup_merge(*a, b); - if (t < 0) - return -EEXIST; - *a = t; - job_type_collapse(a, u); - return 0; -} - -bool job_is_runnable(Job *j) { - Iterator i; - Unit *other; - - assert(j); - assert(j->installed); - - /* Checks whether there is any job running for the units this - * job needs to be running after (in the case of a 'positive' - * job type) or before (in the case of a 'negative' job - * type. */ - - /* First check if there is an override */ - if (j->ignore_order) - return true; - - if (j->type == JOB_NOP) - return true; - - if (j->type == JOB_START || - j->type == JOB_VERIFY_ACTIVE || - j->type == JOB_RELOAD) { - - /* Immediate result is that the job is or might be - * started. In this case lets wait for the - * dependencies, regardless whether they are - * starting or stopping something. */ - - SET_FOREACH(other, j->unit->dependencies[UNIT_AFTER], i) - if (other->job) - return false; - } - - /* Also, if something else is being stopped and we should - * change state after it, then lets wait. */ - - SET_FOREACH(other, j->unit->dependencies[UNIT_BEFORE], i) - if (other->job && - (other->job->type == JOB_STOP || - other->job->type == JOB_RESTART)) - return false; - - /* This means that for a service a and a service b where b - * shall be started after a: - * - * start a + start b → 1st step start a, 2nd step start b - * start a + stop b → 1st step stop b, 2nd step start a - * stop a + start b → 1st step stop a, 2nd step start b - * stop a + stop b → 1st step stop b, 2nd step stop a - * - * This has the side effect that restarts are properly - * synchronized too. */ - - return true; -} - -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)); - - j->type = newtype; -} - -int job_run_and_invalidate(Job *j) { - int r; - uint32_t id; - Manager *m; - - assert(j); - assert(j->installed); - assert(j->type < _JOB_TYPE_MAX_IN_TRANSACTION); - assert(j->in_run_queue); - - LIST_REMOVE(Job, run_queue, j->manager->run_queue, j); - j->in_run_queue = false; - - if (j->state != JOB_WAITING) - return 0; - - if (!job_is_runnable(j)) - return -EAGAIN; - - j->state = JOB_RUNNING; - job_add_to_dbus_queue(j); - - /* While we execute this operation the job might go away (for - * example: because it is replaced by a new, conflicting - * job.) To make sure we don't access a freed job later on we - * store the id here, so that we can verify the job is still - * valid. */ - id = j->id; - m = j->manager; - - switch (j->type) { - - case JOB_START: - r = unit_start(j->unit); - - /* If this unit cannot be started, then simply wait */ - if (r == -EBADR) - r = 0; - break; - - case JOB_VERIFY_ACTIVE: { - UnitActiveState t = unit_active_state(j->unit); - if (UNIT_IS_ACTIVE_OR_RELOADING(t)) - r = -EALREADY; - else if (t == UNIT_ACTIVATING) - r = -EAGAIN; - else - r = -ENOEXEC; - break; - } - - case JOB_STOP: - case JOB_RESTART: - r = unit_stop(j->unit); - - /* If this unit cannot stopped, then simply wait. */ - if (r == -EBADR) - r = 0; - break; - - case JOB_RELOAD: - r = unit_reload(j->unit); - break; - - case JOB_NOP: - r = -EALREADY; - break; - - default: - assert_not_reached("Unknown job type"); - } - - j = manager_get_job(m, id); - if (j) { - if (r == -EALREADY) - r = job_finish_and_invalidate(j, JOB_DONE, true); - else if (r == -ENOEXEC) - r = job_finish_and_invalidate(j, JOB_SKIPPED, true); - else if (r == -EAGAIN) - j->state = JOB_WAITING; - else if (r < 0) - r = job_finish_and_invalidate(j, JOB_FAILED, true); - } - - return r; -} - -static const char *job_get_status_message_format(Unit *u, JobType t, JobResult result) { - const UnitStatusMessageFormats *format_table; - - assert(u); - assert(t >= 0); - assert(t < _JOB_TYPE_MAX); - - format_table = &UNIT_VTABLE(u)->status_message_formats; - if (!format_table) - 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; -} - -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; - - switch (result) { - - case JOB_DONE: - if (u->condition_result) - unit_status_printf(u, ANSI_HIGHLIGHT_GREEN_ON " OK " ANSI_HIGHLIGHT_OFF, format, unit_description(u)); - break; - - case JOB_FAILED: - unit_status_printf(u, ANSI_HIGHLIGHT_RED_ON "FAILED" ANSI_HIGHLIGHT_OFF, format, unit_description(u)); - unit_status_printf(u, NULL, "See 'systemctl status %s' for details.", u->id); - break; - - case JOB_DEPENDENCY: - unit_status_printf(u, ANSI_HIGHLIGHT_YELLOW_ON "DEPEND" ANSI_HIGHLIGHT_OFF, format, unit_description(u)); - break; - - case JOB_TIMEOUT: - unit_status_printf(u, ANSI_HIGHLIGHT_RED_ON " TIME " ANSI_HIGHLIGHT_OFF, format, unit_description(u)); - break; - - default: - ; - } - - } else if (t == JOB_STOP || t == JOB_RESTART) { - - format = job_get_status_message_format(u, t, result); - if (!format) - return; - - switch (result) { - - case JOB_TIMEOUT: - unit_status_printf(u, ANSI_HIGHLIGHT_RED_ON " TIME " ANSI_HIGHLIGHT_OFF, format, unit_description(u)); - break; - - case JOB_DONE: - case JOB_FAILED: - unit_status_printf(u, ANSI_HIGHLIGHT_GREEN_ON " OK " ANSI_HIGHLIGHT_OFF, format, unit_description(u)); - break; - - default: - ; - } - - } else if (t == JOB_VERIFY_ACTIVE) { - - /* When verify-active detects the unit is inactive, report it. - * Most likely a DEPEND warning from a requisiting unit will - * occur next and it's nice to see what was requisited. */ - if (result == JOB_SKIPPED) - unit_status_printf(u, ANSI_HIGHLIGHT_ON " INFO " ANSI_HIGHLIGHT_OFF, "%s is not active.", unit_description(u)); - } -} - -#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; - JobType t; - Iterator i; - - assert(j); - assert(j->installed); - assert(j->type < _JOB_TYPE_MAX_IN_TRANSACTION); - - u = j->unit; - t = j->type; - - j->result = result; - - log_debug("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); - - /* Patch restart jobs so that they become normal start jobs */ - if (result == JOB_DONE && t == JOB_RESTART) { - - job_change_type(j, JOB_START); - j->state = JOB_WAITING; - - job_add_to_run_queue(j); - - goto finish; - } - - if (result == JOB_FAILED) - j->manager->n_failed_jobs ++; - - job_uninstall(j); - job_free(j); - - /* Fail depending jobs on failure */ - if (result != JOB_DONE && recursive) { - - if (t == JOB_START || - t == JOB_VERIFY_ACTIVE) { - - SET_FOREACH(other, u->dependencies[UNIT_REQUIRED_BY], i) - if (other->job && - (other->job->type == JOB_START || - other->job->type == JOB_VERIFY_ACTIVE)) - job_finish_and_invalidate(other->job, JOB_DEPENDENCY, true); - - SET_FOREACH(other, u->dependencies[UNIT_BOUND_BY], i) - if (other->job && - (other->job->type == JOB_START || - other->job->type == JOB_VERIFY_ACTIVE)) - job_finish_and_invalidate(other->job, JOB_DEPENDENCY, true); - - SET_FOREACH(other, u->dependencies[UNIT_REQUIRED_BY_OVERRIDABLE], i) - if (other->job && - !other->job->override && - (other->job->type == JOB_START || - other->job->type == JOB_VERIFY_ACTIVE)) - job_finish_and_invalidate(other->job, JOB_DEPENDENCY, true); - - } else if (t == JOB_STOP) { - - SET_FOREACH(other, u->dependencies[UNIT_CONFLICTED_BY], i) - if (other->job && - (other->job->type == JOB_START || - other->job->type == JOB_VERIFY_ACTIVE)) - job_finish_and_invalidate(other->job, JOB_DEPENDENCY, true); - } - } - - /* Trigger OnFailure dependencies that are not generated by - * the unit itself. We don't tread 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'.", - u->id, - job_type_to_string(t), - job_result_to_string(result)); - - unit_trigger_on_failure(u); - } - -finish: - /* Try to start the next jobs that can be started */ - SET_FOREACH(other, u->dependencies[UNIT_AFTER], i) - if (other->job) - job_add_to_run_queue(other->job); - SET_FOREACH(other, u->dependencies[UNIT_BEFORE], i) - if (other->job) - job_add_to_run_queue(other->job); - - manager_check_finished(u->manager); - - return 0; -} - -int job_start_timer(Job *j) { - struct itimerspec its; - struct epoll_event ev; - int fd, r; - assert(j); - - if (j->unit->job_timeout <= 0 || - j->timer_watch.type == WATCH_JOB_TIMER) - return 0; - - assert(j->timer_watch.type == WATCH_INVALID); - - if ((fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK|TFD_CLOEXEC)) < 0) { - r = -errno; - goto fail; - } - - zero(its); - timespec_store(&its.it_value, j->unit->job_timeout); - - if (timerfd_settime(fd, 0, &its, NULL) < 0) { - r = -errno; - goto fail; - } - - zero(ev); - ev.data.ptr = &j->timer_watch; - ev.events = EPOLLIN; - - if (epoll_ctl(j->manager->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0) { - r = -errno; - goto fail; - } - - j->timer_watch.type = WATCH_JOB_TIMER; - j->timer_watch.fd = fd; - j->timer_watch.data.job = j; - - return 0; - -fail: - if (fd >= 0) - close_nointr_nofail(fd); - - return r; -} - -void job_add_to_run_queue(Job *j) { - assert(j); - assert(j->installed); - - if (j->in_run_queue) - return; - - LIST_PREPEND(Job, run_queue, j->manager->run_queue, j); - j->in_run_queue = true; -} - -void job_add_to_dbus_queue(Job *j) { - assert(j); - assert(j->installed); - - if (j->in_dbus_queue) - return; - - /* We don't check if anybody is subscribed here, since this - * job might just have been created and not yet assigned to a - * connection/client. */ - - LIST_PREPEND(Job, dbus_queue, j->manager->dbus_job_queue, j); - j->in_dbus_queue = true; -} - -char *job_dbus_path(Job *j) { - char *p; - - assert(j); - - if (asprintf(&p, "/org/freedesktop/systemd1/job/%lu", (unsigned long) j->id) < 0) - return NULL; - - return p; -} - -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)); - job_finish_and_invalidate(j, JOB_TIMEOUT, true); -} - -int job_serialize(Job *j, FILE *f, FDSet *fds) { - fprintf(f, "job-id=%u\n", j->id); - fprintf(f, "job-type=%s\n", job_type_to_string(j->type)); - fprintf(f, "job-state=%s\n", job_state_to_string(j->state)); - fprintf(f, "job-override=%s\n", yes_no(j->override)); - fprintf(f, "job-sent-dbus-new-signal=%s\n", yes_no(j->sent_dbus_new_signal)); - fprintf(f, "job-ignore-order=%s\n", yes_no(j->ignore_order)); - /* Cannot save bus clients. Just note the fact that we're losing - * them. job_send_message() will fallback to broadcasting. */ - fprintf(f, "job-forgot-bus-clients=%s\n", - yes_no(j->forgot_bus_clients || j->bus_client_list)); - if (j->timer_watch.type == WATCH_JOB_TIMER) { - int copy = fdset_put_dup(fds, j->timer_watch.fd); - if (copy < 0) - return copy; - fprintf(f, "job-timer-watch-fd=%d\n", copy); - } - - /* End marker */ - fputc('\n', f); - return 0; -} - -int job_deserialize(Job *j, FILE *f, FDSet *fds) { - for (;;) { - char line[LINE_MAX], *l, *v; - size_t k; - - if (!fgets(line, sizeof(line), f)) { - if (feof(f)) - return 0; - return -errno; - } - - char_array_0(line); - l = strstrip(line); - - /* End marker */ - if (l[0] == 0) - return 0; - - k = strcspn(l, "="); - - if (l[k] == '=') { - l[k] = 0; - v = l+k+1; - } else - v = l+k; - - if (streq(l, "job-id")) { - if (safe_atou32(v, &j->id) < 0) - log_debug("Failed to parse job id value %s", v); - } else if (streq(l, "job-type")) { - JobType t = job_type_from_string(v); - if (t < 0) - log_debug("Failed to parse job type %s", v); - else if (t >= _JOB_TYPE_MAX_IN_TRANSACTION) - log_debug("Cannot deserialize job of type %s", v); - else - j->type = t; - } else if (streq(l, "job-state")) { - JobState s = job_state_from_string(v); - if (s < 0) - log_debug("Failed to parse job state %s", v); - else - j->state = s; - } else if (streq(l, "job-override")) { - int b = parse_boolean(v); - if (b < 0) - log_debug("Failed to parse job override flag %s", v); - else - j->override = j->override || b; - } else if (streq(l, "job-sent-dbus-new-signal")) { - int b = parse_boolean(v); - if (b < 0) - log_debug("Failed to parse job sent_dbus_new_signal flag %s", v); - else - j->sent_dbus_new_signal = j->sent_dbus_new_signal || b; - } else if (streq(l, "job-ignore-order")) { - int b = parse_boolean(v); - if (b < 0) - log_debug("Failed to parse job ignore_order flag %s", v); - else - j->ignore_order = j->ignore_order || b; - } else if (streq(l, "job-forgot-bus-clients")) { - int b = parse_boolean(v); - if (b < 0) - log_debug("Failed to parse job forgot_bus_clients flag %s", v); - else - j->forgot_bus_clients = j->forgot_bus_clients || b; - } else if (streq(l, "job-timer-watch-fd")) { - int fd; - if (safe_atoi(v, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd)) - log_debug("Failed to parse job-timer-watch-fd value %s", v); - else { - if (j->timer_watch.type == WATCH_JOB_TIMER) - close_nointr_nofail(j->timer_watch.fd); - - j->timer_watch.type = WATCH_JOB_TIMER; - j->timer_watch.fd = fdset_remove(fds, fd); - j->timer_watch.data.job = j; - } - } - } -} - -int job_coldplug(Job *j) { - struct epoll_event ev; - - if (j->timer_watch.type != WATCH_JOB_TIMER) - return 0; - - zero(ev); - ev.data.ptr = &j->timer_watch; - ev.events = EPOLLIN; - - if (epoll_ctl(j->manager->epoll_fd, EPOLL_CTL_ADD, j->timer_watch.fd, &ev) < 0) - return -errno; - - return 0; -} - -static const char* const job_state_table[_JOB_STATE_MAX] = { - [JOB_WAITING] = "waiting", - [JOB_RUNNING] = "running" -}; - -DEFINE_STRING_TABLE_LOOKUP(job_state, JobState); - -static const char* const job_type_table[_JOB_TYPE_MAX] = { - [JOB_START] = "start", - [JOB_VERIFY_ACTIVE] = "verify-active", - [JOB_STOP] = "stop", - [JOB_RELOAD] = "reload", - [JOB_RELOAD_OR_START] = "reload-or-start", - [JOB_RESTART] = "restart", - [JOB_TRY_RESTART] = "try-restart", - [JOB_NOP] = "nop", -}; - -DEFINE_STRING_TABLE_LOOKUP(job_type, JobType); - -static const char* const job_mode_table[_JOB_MODE_MAX] = { - [JOB_FAIL] = "fail", - [JOB_REPLACE] = "replace", - [JOB_ISOLATE] = "isolate", - [JOB_IGNORE_DEPENDENCIES] = "ignore-dependencies", - [JOB_IGNORE_REQUIREMENTS] = "ignore-requirements" -}; - -DEFINE_STRING_TABLE_LOOKUP(job_mode, JobMode); - -static const char* const job_result_table[_JOB_RESULT_MAX] = { - [JOB_DONE] = "done", - [JOB_CANCELED] = "canceled", - [JOB_TIMEOUT] = "timeout", - [JOB_FAILED] = "failed", - [JOB_DEPENDENCY] = "dependency", - [JOB_SKIPPED] = "skipped" -}; - -DEFINE_STRING_TABLE_LOOKUP(job_result, JobResult); diff --git a/src/core/job.h b/src/core/job.h deleted file mode 100644 index 3aa49d4b46..0000000000 --- a/src/core/job.h +++ /dev/null @@ -1,230 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -#pragma once - -/*** - 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 <stdbool.h> -#include <inttypes.h> -#include <errno.h> - -typedef struct Job Job; -typedef struct JobDependency JobDependency; -typedef struct JobBusClient JobBusClient; -typedef enum JobType JobType; -typedef enum JobState JobState; -typedef enum JobMode JobMode; -typedef enum JobResult JobResult; - -/* Be careful when changing the job types! Adjust job_merging_table[] accordingly! */ -enum JobType { - JOB_START, /* if a unit does not support being started, we'll just wait until it becomes active */ - JOB_VERIFY_ACTIVE, - - JOB_STOP, - - JOB_RELOAD, /* if running, reload */ - - /* Note that restarts are first treated like JOB_STOP, but - * then instead of finishing are patched to become - * JOB_START. */ - JOB_RESTART, /* If running, stop. Then start unconditionally. */ - - _JOB_TYPE_MAX_MERGING, - - /* JOB_NOP can enter into a transaction, but as it won't pull in - * any dependencies, it won't have to merge with anything. - * job_install() avoids the problem of merging JOB_NOP too (it's - * special-cased, only merges with other JOB_NOPs). */ - JOB_NOP = _JOB_TYPE_MAX_MERGING, /* do nothing */ - - _JOB_TYPE_MAX_IN_TRANSACTION, - - /* JOB_TRY_RESTART can never appear in a transaction, because - * it always collapses into JOB_RESTART or JOB_NOP before entering. - * Thus we never need to merge it with anything. */ - JOB_TRY_RESTART = _JOB_TYPE_MAX_IN_TRANSACTION, /* if running, stop and then start */ - - /* JOB_RELOAD_OR_START won't enter into a transaction and cannot result - * from transaction merging (there's no way for JOB_RELOAD and - * JOB_START to meet in one transaction). It can result from a merge - * during job installation, but then it will immediately collapse into - * one of the two simpler types. */ - JOB_RELOAD_OR_START, /* if running, reload, otherwise start */ - - _JOB_TYPE_MAX, - _JOB_TYPE_INVALID = -1 -}; - -enum JobState { - JOB_WAITING, - JOB_RUNNING, - _JOB_STATE_MAX, - _JOB_STATE_INVALID = -1 -}; - -enum JobMode { - JOB_FAIL, /* Fail if a conflicting job is already queued */ - JOB_REPLACE, /* Replace an existing conflicting job */ - JOB_ISOLATE, /* Start a unit, and stop all others */ - JOB_IGNORE_DEPENDENCIES, /* Ignore both requirement and ordering dependencies */ - JOB_IGNORE_REQUIREMENTS, /* Ignore requirement dependencies */ - _JOB_MODE_MAX, - _JOB_MODE_INVALID = -1 -}; - -enum JobResult { - 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 -}; - -#include "manager.h" -#include "unit.h" -#include "hashmap.h" -#include "list.h" - -struct JobDependency { - /* Encodes that the 'subject' job needs the 'object' job in - * some way. This structure is used only while building a transaction. */ - Job *subject; - Job *object; - - LIST_FIELDS(JobDependency, subject); - LIST_FIELDS(JobDependency, object); - - bool matters; - bool conflicts; -}; - -struct JobBusClient { - LIST_FIELDS(JobBusClient, client); - /* Note that this bus object is not ref counted here. */ - DBusConnection *bus; - char name[0]; -}; - -struct Job { - Manager *manager; - Unit *unit; - - LIST_FIELDS(Job, transaction); - LIST_FIELDS(Job, run_queue); - LIST_FIELDS(Job, dbus_queue); - - LIST_HEAD(JobDependency, subject_list); - LIST_HEAD(JobDependency, object_list); - - /* Used for graph algs as a "I have been here" marker */ - Job* marker; - unsigned generation; - - uint32_t id; - - JobType type; - JobState state; - - Watch timer_watch; - - /* There can be more than one client, because of job merging. */ - LIST_HEAD(JobBusClient, bus_client_list); - - JobResult result; - - bool installed:1; - bool in_run_queue:1; - bool matters_to_anchor:1; - bool override:1; - bool in_dbus_queue:1; - bool sent_dbus_new_signal:1; - bool ignore_order:1; - bool forgot_bus_clients:1; -}; - -JobBusClient* job_bus_client_new(DBusConnection *connection, const char *name); - -Job* job_new(Unit *unit, JobType type); -Job* job_new_raw(Unit *unit); -void job_free(Job *job); -Job* job_install(Job *j); -int job_install_deserialized(Job *j); -void job_uninstall(Job *j); -void job_dump(Job *j, FILE*f, const char *prefix); -int job_serialize(Job *j, FILE *f, FDSet *fds); -int job_deserialize(Job *j, FILE *f, FDSet *fds); -int job_coldplug(Job *j); - -JobDependency* job_dependency_new(Job *subject, Job *object, bool matters, bool conflicts); -void job_dependency_free(JobDependency *l); - -int job_merge(Job *j, Job *other); - -JobType job_type_lookup_merge(JobType a, JobType b); - -static inline bool job_type_is_mergeable(JobType a, JobType b) { - return job_type_lookup_merge(a, b) >= 0; -} - -static inline bool job_type_is_conflicting(JobType a, JobType b) { - return !job_type_is_mergeable(a, b); -} - -static inline bool job_type_is_superset(JobType a, JobType b) { - /* Checks whether operation a is a "superset" of b in its actions */ - return a == job_type_lookup_merge(a, b); -} - -bool job_type_is_redundant(JobType a, UnitActiveState b); - -/* Collapses a state-dependent job type into a simpler type by observing - * the state of the unit which it is going to be applied to. */ -void job_type_collapse(JobType *t, Unit *u); - -int job_type_merge_and_collapse(JobType *a, JobType b, Unit *u); - -bool job_is_runnable(Job *j); - -void job_add_to_run_queue(Job *j); -void job_add_to_dbus_queue(Job *j); - -int job_start_timer(Job *j); -void job_timer_event(Job *j, uint64_t n_elapsed, Watch *w); - -int job_run_and_invalidate(Job *j); -int job_finish_and_invalidate(Job *j, JobResult result, bool recursive); - -char *job_dbus_path(Job *j); - -const char* job_type_to_string(JobType t); -JobType job_type_from_string(const char *s); - -const char* job_state_to_string(JobState t); -JobState job_state_from_string(const char *s); - -const char* job_mode_to_string(JobMode t); -JobMode job_mode_from_string(const char *s); - -const char* job_result_to_string(JobResult t); -JobResult job_result_from_string(const char *s); diff --git a/src/core/kill.c b/src/core/kill.c deleted file mode 100644 index 0775653f73..0000000000 --- a/src/core/kill.c +++ /dev/null @@ -1,63 +0,0 @@ -/*-*- 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 <string.h> - -#include "kill.h" -#include "util.h" - -void kill_context_init(KillContext *c) { - assert(c); - - c->kill_signal = SIGTERM; - c->send_sigkill = true; -} - -void kill_context_dump(KillContext *c, FILE *f, const char *prefix) { - assert(c); - - if (!prefix) - prefix = ""; - - fprintf(f, - "%sKillMode: %s\n" - "%sKillSignal: SIG%s\n" - "%sSendSIGKILL: %s\n", - prefix, kill_mode_to_string(c->kill_mode), - prefix, signal_to_string(c->kill_signal), - prefix, yes_no(c->send_sigkill)); -} - -static const char* const kill_mode_table[_KILL_MODE_MAX] = { - [KILL_CONTROL_GROUP] = "control-group", - [KILL_PROCESS] = "process", - [KILL_NONE] = "none" -}; - -DEFINE_STRING_TABLE_LOOKUP(kill_mode, KillMode); - -static const char* const kill_who_table[_KILL_WHO_MAX] = { - [KILL_MAIN] = "main", - [KILL_CONTROL] = "control", - [KILL_ALL] = "all" -}; - -DEFINE_STRING_TABLE_LOOKUP(kill_who, KillWho); diff --git a/src/core/kill.h b/src/core/kill.h deleted file mode 100644 index 3c9b0ab8db..0000000000 --- a/src/core/kill.h +++ /dev/null @@ -1,60 +0,0 @@ -/*-*- 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/>. -***/ - -typedef struct KillContext KillContext; - -#include <stdbool.h> -#include <stdio.h> - -typedef enum KillMode { - /* The kill mode is a property of a unit. */ - KILL_CONTROL_GROUP = 0, - KILL_PROCESS, - KILL_NONE, - _KILL_MODE_MAX, - _KILL_MODE_INVALID = -1 -} KillMode; - -struct KillContext { - KillMode kill_mode; - int kill_signal; - bool send_sigkill; -}; - -typedef enum KillWho { - /* Kill who is a property of an operation */ - KILL_MAIN, - KILL_CONTROL, - KILL_ALL, - _KILL_WHO_MAX, - _KILL_WHO_INVALID = -1 -} KillWho; - -void kill_context_init(KillContext *c); -void kill_context_dump(KillContext *c, FILE *f, const char *prefix); - -const char *kill_mode_to_string(KillMode k); -KillMode kill_mode_from_string(const char *s); - -const char *kill_who_to_string(KillWho k); -KillWho kill_who_from_string(const char *s); diff --git a/src/core/killall.c b/src/core/killall.c deleted file mode 100644 index 55200ffa48..0000000000 --- a/src/core/killall.c +++ /dev/null @@ -1,177 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -/*** - This file is part of systemd. - - Copyright 2010 ProFUSION embedded systems - - 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 <sys/wait.h> -#include <signal.h> -#include <errno.h> - -#include "util.h" -#include "def.h" -#include "killall.h" - -#define TIMEOUT_USEC (5 * USEC_PER_SEC) - -static bool ignore_proc(pid_t pid) { - char buf[PATH_MAX]; - FILE *f; - char c; - size_t count; - uid_t uid; - int r; - - /* We are PID 1, let's not commit suicide */ - if (pid == 1) - return true; - - r = get_process_uid(pid, &uid); - if (r < 0) - return true; /* not really, but better safe than sorry */ - - /* Non-root processes otherwise are always subject to be killed */ - if (uid != 0) - return false; - - snprintf(buf, sizeof(buf), "/proc/%lu/cmdline", (unsigned long) pid); - char_array_0(buf); - - f = fopen(buf, "re"); - if (!f) - return true; /* not really, but has the desired effect */ - - count = fread(&c, 1, 1, f); - fclose(f); - - /* Kernel threads have an empty cmdline */ - if (count <= 0) - return true; - - /* Processes with argv[0][0] = '@' we ignore from the killing - * spree. - * - * http://www.freedesktop.org/wiki/Software/systemd/RootStorageDaemons */ - if (count == 1 && c == '@') - return true; - - return false; -} - -static void wait_for_children(int n_processes, sigset_t *mask) { - usec_t until; - - assert(mask); - - until = now(CLOCK_MONOTONIC) + TIMEOUT_USEC; - for (;;) { - struct timespec ts; - int k; - usec_t n; - - for (;;) { - pid_t pid = waitpid(-1, NULL, WNOHANG); - - if (pid == 0) - break; - - if (pid < 0 && errno == ECHILD) - return; - - if (n_processes > 0) - if (--n_processes == 0) - return; - } - - n = now(CLOCK_MONOTONIC); - if (n >= until) - return; - - timespec_store(&ts, until - n); - - if ((k = sigtimedwait(mask, NULL, &ts)) != SIGCHLD) { - - if (k < 0 && errno != EAGAIN) { - log_error("sigtimedwait() failed: %m"); - return; - } - - if (k >= 0) - log_warning("sigtimedwait() returned unexpected signal."); - } - } -} - -static int killall(int sig) { - DIR *dir; - struct dirent *d; - unsigned int n_processes = 0; - - dir = opendir("/proc"); - if (!dir) - return -errno; - - while ((d = readdir(dir))) { - pid_t pid; - - if (d->d_type != DT_DIR && - d->d_type != DT_UNKNOWN) - continue; - - if (parse_pid(d->d_name, &pid) < 0) - continue; - - if (ignore_proc(pid)) - continue; - - if (kill(pid, sig) >= 0) - n_processes++; - else if (errno != ENOENT) - log_warning("Could not kill %d: %m", pid); - } - - closedir(dir); - - return n_processes; -} - -void broadcast_signal(int sig, bool wait_for_exit) { - sigset_t mask, oldmask; - int n_processes; - - assert_se(sigemptyset(&mask) == 0); - assert_se(sigaddset(&mask, SIGCHLD) == 0); - assert_se(sigprocmask(SIG_BLOCK, &mask, &oldmask) == 0); - - if (kill(-1, SIGSTOP) < 0 && errno != ESRCH) - log_warning("kill(-1, SIGSTOP) failed: %m"); - - n_processes = killall(sig); - - if (kill(-1, SIGCONT) < 0 && errno != ESRCH) - log_warning("kill(-1, SIGCONT) failed: %m"); - - if (n_processes <= 0) - goto finish; - - if (wait_for_exit) - wait_for_children(n_processes, &mask); - -finish: - sigprocmask(SIG_SETMASK, &oldmask, NULL); -} diff --git a/src/core/killall.h b/src/core/killall.h deleted file mode 100644 index d08ac142f0..0000000000 --- a/src/core/killall.h +++ /dev/null @@ -1,27 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -#ifndef fookillallhfoo -#define fookillallhfoo - -/*** - 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/>. -***/ - -void broadcast_signal(int sig, bool wait); - -#endif diff --git a/src/core/kmod-setup.c b/src/core/kmod-setup.c deleted file mode 100644 index 20ab232374..0000000000 --- a/src/core/kmod-setup.c +++ /dev/null @@ -1,104 +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 <sys/wait.h> -#include <unistd.h> -#include <string.h> -#include <errno.h> -#include <libkmod.h> - -#include "macro.h" -#include "execute.h" - -#include "kmod-setup.h" - -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 -#pragma GCC diagnostic ignored "-Wformat-nonliteral" -static void systemd_kmod_log(void *data, int priority, const char *file, int line, - const char *fn, const char *format, va_list args) -{ - /* library logging is enabled at debug only */ - log_metav(LOG_DEBUG, file, line, fn, format, args); -} -#pragma GCC diagnostic pop - -int kmod_setup(void) { - unsigned i; - struct kmod_ctx *ctx = NULL; - struct kmod_module *mod; - 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].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].name); - - if (!ctx) { - ctx = kmod_new(NULL, NULL); - if (!ctx) { - log_error("Failed to allocate memory for kmod"); - return -ENOMEM; - } - - kmod_set_log_fn(ctx, systemd_kmod_log, NULL); - kmod_load_resources(ctx); - } - - err = kmod_module_new_from_name(ctx, kmod_table[i].name, &mod); - if (err < 0) { - log_error("Failed to lookup module '%s'", kmod_table[i].name); - continue; - } - - err = kmod_module_probe_insert_module(mod, KMOD_PROBE_APPLY_BLACKLIST, NULL, NULL, NULL, NULL); - if (err == 0) - log_info("Inserted module '%s'", kmod_module_get_name(mod)); - else if (err == KMOD_PROBE_APPLY_BLACKLIST) - log_info("Module '%s' is blacklisted", kmod_module_get_name(mod)); - else - log_error("Failed to insert module '%s'", kmod_module_get_name(mod)); - - kmod_module_unref(mod); - } - - if (ctx) - kmod_unref(ctx); - - return 0; -} diff --git a/src/core/kmod-setup.h b/src/core/kmod-setup.h deleted file mode 100644 index 24dcdddfa4..0000000000 --- a/src/core/kmod-setup.h +++ /dev/null @@ -1,24 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -#pragma once - -/*** - 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/>. -***/ - -int kmod_setup(void); diff --git a/src/core/load-dropin.c b/src/core/load-dropin.c deleted file mode 100644 index 86f81c7484..0000000000 --- a/src/core/load-dropin.c +++ /dev/null @@ -1,150 +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 <dirent.h> -#include <errno.h> - -#include "unit.h" -#include "load-dropin.h" -#include "log.h" -#include "strv.h" -#include "unit-name.h" - -static int iterate_dir(Unit *u, const char *path, UnitDependency dependency) { - DIR *d; - struct dirent *de; - int r; - - assert(u); - assert(path); - - d = opendir(path); - if (!d) { - - if (errno == ENOENT) - return 0; - - return -errno; - } - - while ((de = readdir(d))) { - char *f; - - if (ignore_file(de->d_name)) - continue; - - f = strjoin(path, "/", de->d_name, NULL); - if (!f) { - r = -ENOMEM; - goto finish; - } - - r = unit_add_dependency_by_name(u, dependency, de->d_name, f, true); - free(f); - - if (r < 0) - log_error("Cannot add dependency %s to %s, ignoring: %s", de->d_name, u->id, strerror(-r)); - } - - r = 0; - -finish: - closedir(d); - return r; -} - -static int process_dir(Unit *u, const char *unit_path, const char *name, const char *suffix, UnitDependency dependency) { - int r; - char *path; - - assert(u); - assert(unit_path); - assert(name); - assert(suffix); - - path = strjoin(unit_path, "/", name, suffix, NULL); - if (!path) - return -ENOMEM; - - if (u->manager->unit_path_cache && - !set_get(u->manager->unit_path_cache, path)) - r = 0; - else - r = iterate_dir(u, path, dependency); - free(path); - - if (r < 0) - return r; - - if (u->instance) { - char *template; - /* Also try the template dir */ - - template = unit_name_template(name); - if (!template) - return -ENOMEM; - - path = strjoin(unit_path, "/", template, suffix, NULL); - free(template); - - if (!path) - return -ENOMEM; - - if (u->manager->unit_path_cache && - !set_get(u->manager->unit_path_cache, path)) - r = 0; - else - r = iterate_dir(u, path, dependency); - free(path); - - if (r < 0) - return r; - } - - return 0; -} - -int unit_load_dropin(Unit *u) { - Iterator i; - char *t; - - assert(u); - - /* Load dependencies from supplementary drop-in directories */ - - SET_FOREACH(t, u->names, i) { - char **p; - - STRV_FOREACH(p, u->manager->lookup_paths.unit_path) { - int r; - - r = process_dir(u, *p, t, ".wants", UNIT_WANTS); - if (r < 0) - return r; - - r = process_dir(u, *p, t, ".requires", UNIT_REQUIRES); - if (r < 0) - return r; - } - } - - return 0; -} diff --git a/src/core/load-dropin.h b/src/core/load-dropin.h deleted file mode 100644 index 1d2fafeee6..0000000000 --- a/src/core/load-dropin.h +++ /dev/null @@ -1,28 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -#pragma once - -/*** - 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 "unit.h" - -/* Read service data supplementary drop-in directories */ - -int unit_load_dropin(Unit *u); diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4 deleted file mode 100644 index 0c5ccebd73..0000000000 --- a/src/core/load-fragment-gperf.gperf.m4 +++ /dev/null @@ -1,255 +0,0 @@ -%{ -#include <stddef.h> -#include "conf-parser.h" -#include "load-fragment.h" -#include "missing.h" -%} -struct ConfigPerfItem; -%null_strings -%language=ANSI-C -%define slot-name section_and_lvalue -%define hash-function-name load_fragment_gperf_hash -%define lookup-function-name load_fragment_gperf_lookup -%readonly-tables -%omit-struct-type -%struct-type -%includes -%% -m4_dnl Define the context options only once -m4_define(`EXEC_CONTEXT_CONFIG_ITEMS', -`$1.WorkingDirectory, config_parse_unit_path_printf, 0, offsetof($1, exec_context.working_directory) -$1.RootDirectory, config_parse_unit_path_printf, 0, offsetof($1, exec_context.root_directory) -$1.User, config_parse_unit_string_printf, 0, offsetof($1, exec_context.user) -$1.Group, config_parse_unit_string_printf, 0, offsetof($1, exec_context.group) -$1.SupplementaryGroups, config_parse_strv, 0, offsetof($1, exec_context.supplementary_groups) -$1.Nice, config_parse_exec_nice, 0, offsetof($1, exec_context) -$1.OOMScoreAdjust, config_parse_exec_oom_score_adjust, 0, offsetof($1, exec_context) -$1.IOSchedulingClass, config_parse_exec_io_class, 0, offsetof($1, exec_context) -$1.IOSchedulingPriority, config_parse_exec_io_priority, 0, offsetof($1, exec_context) -$1.CPUSchedulingPolicy, config_parse_exec_cpu_sched_policy, 0, offsetof($1, exec_context) -$1.CPUSchedulingPriority, config_parse_exec_cpu_sched_prio, 0, offsetof($1, exec_context) -$1.CPUSchedulingResetOnFork, config_parse_bool, 0, offsetof($1, exec_context.cpu_sched_reset_on_fork) -$1.CPUAffinity, config_parse_exec_cpu_affinity, 0, offsetof($1, exec_context) -$1.UMask, config_parse_mode, 0, offsetof($1, exec_context.umask) -$1.Environment, config_parse_unit_strv_printf, 0, offsetof($1, exec_context.environment) -$1.EnvironmentFile, config_parse_unit_env_file, 0, offsetof($1, exec_context.environment_files) -$1.StandardInput, config_parse_input, 0, offsetof($1, exec_context.std_input) -$1.StandardOutput, config_parse_output, 0, offsetof($1, exec_context.std_output) -$1.StandardError, config_parse_output, 0, offsetof($1, exec_context.std_error) -$1.TTYPath, config_parse_unit_path_printf, 0, offsetof($1, exec_context.tty_path) -$1.TTYReset, config_parse_bool, 0, offsetof($1, exec_context.tty_reset) -$1.TTYVHangup, config_parse_bool, 0, offsetof($1, exec_context.tty_vhangup) -$1.TTYVTDisallocate, config_parse_bool, 0, offsetof($1, exec_context.tty_vt_disallocate) -$1.SyslogIdentifier, config_parse_unit_string_printf, 0, offsetof($1, exec_context.syslog_identifier) -$1.SyslogFacility, config_parse_facility, 0, offsetof($1, exec_context.syslog_priority) -$1.SyslogLevel, config_parse_level, 0, offsetof($1, exec_context.syslog_priority) -$1.SyslogLevelPrefix, config_parse_bool, 0, offsetof($1, exec_context.syslog_level_prefix) -$1.Capabilities, config_parse_exec_capabilities, 0, offsetof($1, exec_context) -$1.SecureBits, config_parse_exec_secure_bits, 0, offsetof($1, exec_context) -$1.CapabilityBoundingSet, config_parse_bounding_set, 0, offsetof($1, exec_context.capability_bounding_set_drop) -$1.TimerSlackNSec, config_parse_nsec, 0, offsetof($1, exec_context.timer_slack_nsec) -$1.NoNewPrivileges config_parse_bool, 0, offsetof($1, exec_context.no_new_privileges) -$1.SystemCallFilter, config_parse_syscall_filter, 0, offsetof($1, exec_context) -$1.LimitCPU, config_parse_limit, RLIMIT_CPU, offsetof($1, exec_context.rlimit) -$1.LimitFSIZE, config_parse_limit, RLIMIT_FSIZE, offsetof($1, exec_context.rlimit) -$1.LimitDATA, config_parse_limit, RLIMIT_DATA, offsetof($1, exec_context.rlimit) -$1.LimitSTACK, config_parse_limit, RLIMIT_STACK, offsetof($1, exec_context.rlimit) -$1.LimitCORE, config_parse_limit, RLIMIT_CORE, offsetof($1, exec_context.rlimit) -$1.LimitRSS, config_parse_limit, RLIMIT_RSS, offsetof($1, exec_context.rlimit) -$1.LimitNOFILE, config_parse_limit, RLIMIT_NOFILE, offsetof($1, exec_context.rlimit) -$1.LimitAS, config_parse_limit, RLIMIT_AS, offsetof($1, exec_context.rlimit) -$1.LimitNPROC, config_parse_limit, RLIMIT_NPROC, offsetof($1, exec_context.rlimit) -$1.LimitMEMLOCK, config_parse_limit, RLIMIT_MEMLOCK, offsetof($1, exec_context.rlimit) -$1.LimitLOCKS, config_parse_limit, RLIMIT_LOCKS, offsetof($1, exec_context.rlimit) -$1.LimitSIGPENDING, config_parse_limit, RLIMIT_SIGPENDING, offsetof($1, exec_context.rlimit) -$1.LimitMSGQUEUE, config_parse_limit, RLIMIT_MSGQUEUE, offsetof($1, exec_context.rlimit) -$1.LimitNICE, config_parse_limit, RLIMIT_NICE, offsetof($1, exec_context.rlimit) -$1.LimitRTPRIO, config_parse_limit, RLIMIT_RTPRIO, offsetof($1, exec_context.rlimit) -$1.LimitRTTIME, config_parse_limit, RLIMIT_RTTIME, offsetof($1, exec_context.rlimit) -$1.ControlGroup, config_parse_unit_cgroup, 0, 0 -$1.ControlGroupAttribute, config_parse_unit_cgroup_attr, 0, 0 -$1.CPUShares, config_parse_unit_cpu_shares, 0, 0 -$1.MemoryLimit, config_parse_unit_memory_limit, 0, 0 -$1.MemorySoftLimit, config_parse_unit_memory_limit, 0, 0 -$1.DeviceAllow, config_parse_unit_device_allow, 0, 0 -$1.DeviceDeny, config_parse_unit_device_allow, 0, 0 -$1.BlockIOWeight, config_parse_unit_blkio_weight, 0, 0 -$1.BlockIOReadBandwidth, config_parse_unit_blkio_bandwidth, 0, 0 -$1.BlockIOWriteBandwidth, config_parse_unit_blkio_bandwidth, 0, 0 -$1.ReadWriteDirectories, config_parse_path_strv, 0, offsetof($1, exec_context.read_write_dirs) -$1.ReadOnlyDirectories, config_parse_path_strv, 0, offsetof($1, exec_context.read_only_dirs) -$1.InaccessibleDirectories, config_parse_path_strv, 0, offsetof($1, exec_context.inaccessible_dirs) -$1.PrivateTmp, config_parse_bool, 0, offsetof($1, exec_context.private_tmp) -$1.PrivateNetwork, config_parse_bool, 0, offsetof($1, exec_context.private_network) -$1.MountFlags, config_parse_exec_mount_flags, 0, offsetof($1, exec_context) -$1.TCPWrapName, config_parse_unit_string_printf, 0, offsetof($1, exec_context.tcpwrap_name) -$1.PAMName, config_parse_unit_string_printf, 0, offsetof($1, exec_context.pam_name) -$1.IgnoreSIGPIPE, config_parse_bool, 0, offsetof($1, exec_context.ignore_sigpipe) -$1.UtmpIdentifier, config_parse_unit_string_printf, 0, offsetof($1, exec_context.utmp_id) -$1.ControlGroupModify, config_parse_bool, 0, offsetof($1, exec_context.control_group_modify) -$1.ControlGroupPersistent, config_parse_tristate, 0, offsetof($1, exec_context.control_group_persistent)' -)m4_dnl -m4_define(`KILL_CONTEXT_CONFIG_ITEMS', -`$1.SendSIGKILL, config_parse_bool, 0, offsetof($1, kill_context.send_sigkill) -$1.KillMode, config_parse_kill_mode, 0, offsetof($1, kill_context.kill_mode) -$1.KillSignal, config_parse_kill_signal, 0, offsetof($1, kill_context.kill_signal)' -)m4_dnl -Unit.Description, config_parse_unit_string_printf, 0, offsetof(Unit, description) -Unit.Documentation, config_parse_documentation, 0, offsetof(Unit, documentation) -Unit.SourcePath, config_parse_path, 0, offsetof(Unit, source_path) -Unit.Requires, config_parse_unit_deps, UNIT_REQUIRES, 0 -Unit.RequiresOverridable, config_parse_unit_deps, UNIT_REQUIRES_OVERRIDABLE, 0 -Unit.Requisite, config_parse_unit_deps, UNIT_REQUISITE, 0 -Unit.RequisiteOverridable, config_parse_unit_deps, UNIT_REQUISITE_OVERRIDABLE, 0 -Unit.Wants, config_parse_unit_deps, UNIT_WANTS, 0 -Unit.BindsTo, config_parse_unit_deps, UNIT_BINDS_TO, 0 -Unit.BindTo, config_parse_unit_deps, UNIT_BINDS_TO, 0 -Unit.Conflicts, config_parse_unit_deps, UNIT_CONFLICTS, 0 -Unit.Before, config_parse_unit_deps, UNIT_BEFORE, 0 -Unit.After, config_parse_unit_deps, UNIT_AFTER, 0 -Unit.OnFailure, config_parse_unit_deps, UNIT_ON_FAILURE, 0 -Unit.PropagatesReloadTo, config_parse_unit_deps, UNIT_PROPAGATES_RELOAD_TO, 0 -Unit.PropagateReloadTo, config_parse_unit_deps, UNIT_PROPAGATES_RELOAD_TO, 0 -Unit.ReloadPropagatedFrom, config_parse_unit_deps, UNIT_RELOAD_PROPAGATED_FROM, 0 -Unit.PropagateReloadFrom, config_parse_unit_deps, UNIT_RELOAD_PROPAGATED_FROM, 0 -Unit.PartOf, config_parse_unit_deps, UNIT_PART_OF, 0 -Unit.RequiresMountsFor, config_parse_unit_requires_mounts_for, 0, offsetof(Unit, requires_mounts_for) -Unit.StopWhenUnneeded, config_parse_bool, 0, offsetof(Unit, stop_when_unneeded) -Unit.RefuseManualStart, config_parse_bool, 0, offsetof(Unit, refuse_manual_start) -Unit.RefuseManualStop, config_parse_bool, 0, offsetof(Unit, refuse_manual_stop) -Unit.AllowIsolate, config_parse_bool, 0, offsetof(Unit, allow_isolate) -Unit.DefaultDependencies, config_parse_bool, 0, offsetof(Unit, default_dependencies) -Unit.OnFailureIsolate, config_parse_bool, 0, offsetof(Unit, on_failure_isolate) -Unit.IgnoreOnIsolate, config_parse_bool, 0, offsetof(Unit, ignore_on_isolate) -Unit.IgnoreOnSnapshot, config_parse_bool, 0, offsetof(Unit, ignore_on_snapshot) -Unit.JobTimeoutSec, config_parse_usec, 0, offsetof(Unit, job_timeout) -Unit.ConditionPathExists, config_parse_unit_condition_path, CONDITION_PATH_EXISTS, 0 -Unit.ConditionPathExistsGlob, config_parse_unit_condition_path, CONDITION_PATH_EXISTS_GLOB, 0 -Unit.ConditionPathIsDirectory, config_parse_unit_condition_path, CONDITION_PATH_IS_DIRECTORY, 0 -Unit.ConditionPathIsSymbolicLink,config_parse_unit_condition_path, CONDITION_PATH_IS_SYMBOLIC_LINK,0 -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.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.ConditionNull, config_parse_unit_condition_null, 0, 0 -m4_dnl -Service.PIDFile, config_parse_unit_path_printf, 0, offsetof(Service, pid_file) -Service.ExecStartPre, config_parse_exec, SERVICE_EXEC_START_PRE, offsetof(Service, exec_command) -Service.ExecStart, config_parse_exec, SERVICE_EXEC_START, offsetof(Service, exec_command) -Service.ExecStartPost, config_parse_exec, SERVICE_EXEC_START_POST, offsetof(Service, exec_command) -Service.ExecReload, config_parse_exec, SERVICE_EXEC_RELOAD, offsetof(Service, exec_command) -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_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) -Service.StartLimitAction, config_parse_start_limit_action, 0, offsetof(Service, start_limit_action) -Service.Type, config_parse_service_type, 0, offsetof(Service, type) -Service.Restart, config_parse_service_restart, 0, offsetof(Service, restart) -Service.PermissionsStartOnly, config_parse_bool, 0, offsetof(Service, permissions_start_only) -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') -Service.NonBlocking, config_parse_bool, 0, offsetof(Service, exec_context.non_blocking) -Service.BusName, config_parse_unit_string_printf, 0, offsetof(Service, bus_name) -Service.NotifyAccess, config_parse_notify_access, 0, offsetof(Service, notify_access) -Service.Sockets, config_parse_service_sockets, 0, 0 -Service.FsckPassNo, config_parse_fsck_passno, 0, offsetof(Service, fsck_passno) -EXEC_CONTEXT_CONFIG_ITEMS(Service)m4_dnl -KILL_CONTEXT_CONFIG_ITEMS(Service)m4_dnl -m4_dnl -Socket.ListenStream, config_parse_socket_listen, 0, 0 -Socket.ListenDatagram, config_parse_socket_listen, 0, 0 -Socket.ListenSequentialPacket, config_parse_socket_listen, 0, 0 -Socket.ListenFIFO, config_parse_socket_listen, 0, 0 -Socket.ListenNetlink, config_parse_socket_listen, 0, 0 -Socket.ListenSpecial, config_parse_socket_listen, 0, 0 -Socket.ListenMessageQueue, config_parse_socket_listen, 0, 0 -Socket.BindIPv6Only, config_parse_socket_bind, 0, 0, -Socket.Backlog, config_parse_unsigned, 0, offsetof(Socket, backlog) -Socket.BindToDevice, config_parse_socket_bindtodevice, 0, 0 -Socket.ExecStartPre, config_parse_exec, SOCKET_EXEC_START_PRE, offsetof(Socket, exec_command) -Socket.ExecStartPost, config_parse_exec, SOCKET_EXEC_START_POST, offsetof(Socket, exec_command) -Socket.ExecStopPre, config_parse_exec, SOCKET_EXEC_STOP_PRE, offsetof(Socket, exec_command) -Socket.ExecStopPost, config_parse_exec, SOCKET_EXEC_STOP_POST, offsetof(Socket, exec_command) -Socket.TimeoutSec, config_parse_usec, 0, offsetof(Socket, timeout_usec) -Socket.DirectoryMode, config_parse_mode, 0, offsetof(Socket, directory_mode) -Socket.SocketMode, config_parse_mode, 0, offsetof(Socket, socket_mode) -Socket.Accept, config_parse_bool, 0, offsetof(Socket, accept) -Socket.MaxConnections, config_parse_unsigned, 0, offsetof(Socket, max_connections) -Socket.KeepAlive, config_parse_bool, 0, offsetof(Socket, keep_alive) -Socket.Priority, config_parse_int, 0, offsetof(Socket, priority) -Socket.ReceiveBuffer, config_parse_bytes_size, 0, offsetof(Socket, receive_buffer) -Socket.SendBuffer, config_parse_bytes_size, 0, offsetof(Socket, send_buffer) -Socket.IPTOS, config_parse_ip_tos, 0, offsetof(Socket, ip_tos) -Socket.IPTTL, config_parse_int, 0, offsetof(Socket, ip_ttl) -Socket.Mark, config_parse_int, 0, offsetof(Socket, mark) -Socket.PipeSize, config_parse_bytes_size, 0, offsetof(Socket, pipe_size) -Socket.FreeBind, config_parse_bool, 0, offsetof(Socket, free_bind) -Socket.Transparent, config_parse_bool, 0, offsetof(Socket, transparent) -Socket.Broadcast, config_parse_bool, 0, offsetof(Socket, broadcast) -Socket.PassCredentials, config_parse_bool, 0, offsetof(Socket, pass_cred) -Socket.PassSecurity, config_parse_bool, 0, offsetof(Socket, pass_sec) -Socket.TCPCongestion, config_parse_string, 0, offsetof(Socket, tcp_congestion) -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 -Mount.What, config_parse_string, 0, offsetof(Mount, parameters_fragment.what) -Mount.Where, config_parse_path, 0, offsetof(Mount, where) -Mount.Options, config_parse_string, 0, offsetof(Mount, parameters_fragment.options) -Mount.Type, config_parse_string, 0, offsetof(Mount, parameters_fragment.fstype) -Mount.FsckPassNo, config_parse_fsck_passno, 0, offsetof(Mount, parameters_fragment.passno) -Mount.TimeoutSec, config_parse_usec, 0, offsetof(Mount, timeout_usec) -Mount.DirectoryMode, config_parse_mode, 0, offsetof(Mount, directory_mode) -EXEC_CONTEXT_CONFIG_ITEMS(Mount)m4_dnl -KILL_CONTEXT_CONFIG_ITEMS(Mount)m4_dnl -m4_dnl -Automount.Where, config_parse_path, 0, offsetof(Automount, where) -Automount.DirectoryMode, config_parse_mode, 0, offsetof(Automount, directory_mode) -m4_dnl -Swap.What, config_parse_path, 0, offsetof(Swap, parameters_fragment.what) -Swap.Priority, config_parse_int, 0, offsetof(Swap, parameters_fragment.priority) -Swap.TimeoutSec, config_parse_usec, 0, offsetof(Swap, timeout_usec) -EXEC_CONTEXT_CONFIG_ITEMS(Swap)m4_dnl -KILL_CONTEXT_CONFIG_ITEMS(Swap)m4_dnl -m4_dnl -Timer.OnActiveSec, config_parse_timer, 0, 0 -Timer.OnBootSec, config_parse_timer, 0, 0 -Timer.OnStartupSec, config_parse_timer, 0, 0 -Timer.OnUnitActiveSec, config_parse_timer, 0, 0 -Timer.OnUnitInactiveSec, config_parse_timer, 0, 0 -Timer.Unit, config_parse_timer_unit, 0, 0 -m4_dnl -Path.PathExists, config_parse_path_spec, 0, 0 -Path.PathExistsGlob, config_parse_path_spec, 0, 0 -Path.PathChanged, config_parse_path_spec, 0, 0 -Path.PathModified, config_parse_path_spec, 0, 0 -Path.DirectoryNotEmpty, config_parse_path_spec, 0, 0 -Path.Unit, config_parse_path_unit, 0, 0 -Path.MakeDirectory, config_parse_bool, 0, offsetof(Path, make_directory) -Path.DirectoryMode, config_parse_mode, 0, offsetof(Path, directory_mode) -m4_dnl The [Install] section is ignored here. -Install.Alias, NULL, 0, 0 -Install.WantedBy, NULL, 0, 0 -Install.RequiredBy, NULL, 0, 0 -Install.Also, NULL, 0, 0 diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c deleted file mode 100644 index 5803044178..0000000000 --- a/src/core/load-fragment.c +++ /dev/null @@ -1,2541 +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 <linux/oom.h> -#include <assert.h> -#include <errno.h> -#include <string.h> -#include <unistd.h> -#include <fcntl.h> -#include <sched.h> -#include <sys/prctl.h> -#include <sys/mount.h> -#include <linux/fs.h> -#include <sys/stat.h> -#include <sys/time.h> -#include <sys/resource.h> - -#include "unit.h" -#include "strv.h" -#include "conf-parser.h" -#include "load-fragment.h" -#include "log.h" -#include "ioprio.h" -#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" -#include "syscall-list.h" - -#ifndef HAVE_SYSV_COMPAT -int config_parse_warn_compat( - const char *filename, - unsigned line, - const char *section, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - log_debug("[%s:%u] Support for option %s= has been disabled at compile time and is ignored", filename, line, lvalue); - return 0; -} -#endif - -int config_parse_unit_deps( - const char *filename, - unsigned line, - const char *section, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - UnitDependency d = ltype; - Unit *u = userdata; - char *w; - size_t l; - char *state; - - assert(filename); - assert(lvalue); - assert(rvalue); - - FOREACH_WORD_QUOTED(w, l, rvalue, state) { - char *t, *k; - int r; - - t = strndup(w, l); - if (!t) - 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); - } - - return 0; -} - -int config_parse_unit_string_printf( - const char *filename, - unsigned line, - const char *section, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - Unit *u = userdata; - char *k; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(u); - - k = unit_full_printf(u, rvalue); - if (!k) - return -ENOMEM; - - r = config_parse_string(filename, line, section, lvalue, ltype, k, data, userdata); - free (k); - - return r; -} - -int config_parse_unit_strv_printf( - const char *filename, - unsigned line, - const char *section, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - Unit *u = userdata; - char *k; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(u); - - k = unit_full_printf(u, rvalue); - if (!k) - return -ENOMEM; - - r = config_parse_strv(filename, line, section, lvalue, ltype, k, data, userdata); - free(k); - - return r; -} - -int config_parse_unit_path_printf( - const char *filename, - unsigned line, - const char *section, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - Unit *u = userdata; - char *k; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(u); - - k = unit_full_printf(u, rvalue); - if (!k) - return log_oom(); - - r = config_parse_path(filename, line, section, lvalue, ltype, k, data, userdata); - free(k); - - return r; -} - -int config_parse_socket_listen( - const char *filename, - unsigned line, - const char *section, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - SocketPort *p, *tail; - Socket *s; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - s = SOCKET(data); - - p = new0(SocketPort, 1); - if (!p) - return -ENOMEM; - - if (streq(lvalue, "ListenFIFO")) { - p->type = SOCKET_FIFO; - - if (!(p->path = unit_full_printf(UNIT(s), rvalue))) { - free(p); - return -ENOMEM; - } - - path_kill_slashes(p->path); - - } else if (streq(lvalue, "ListenSpecial")) { - p->type = SOCKET_SPECIAL; - - if (!(p->path = unit_full_printf(UNIT(s), rvalue))) { - free(p); - return -ENOMEM; - } - - path_kill_slashes(p->path); - - } else if (streq(lvalue, "ListenMessageQueue")) { - - p->type = SOCKET_MQUEUE; - - if (!(p->path = unit_full_printf(UNIT(s), rvalue))) { - free(p); - return -ENOMEM; - } - - path_kill_slashes(p->path); - - } else if (streq(lvalue, "ListenNetlink")) { - char *k; - int r; - - p->type = SOCKET_SOCKET; - k = unit_full_printf(UNIT(s), rvalue); - r = socket_address_parse_netlink(&p->address, k); - free(k); - - if (r < 0) { - log_error("[%s:%u] Failed to parse address value, ignoring: %s", filename, line, rvalue); - free(p); - return 0; - } - - } else { - char *k; - int r; - - p->type = SOCKET_SOCKET; - k = unit_full_printf(UNIT(s), rvalue); - r = socket_address_parse(&p->address, k); - free(k); - - if (r < 0) { - log_error("[%s:%u] Failed to parse address value, ignoring: %s", filename, line, rvalue); - free(p); - return 0; - } - - if (streq(lvalue, "ListenStream")) - p->address.type = SOCK_STREAM; - else if (streq(lvalue, "ListenDatagram")) - p->address.type = SOCK_DGRAM; - else { - assert(streq(lvalue, "ListenSequentialPacket")); - p->address.type = SOCK_SEQPACKET; - } - - if (socket_address_family(&p->address) != AF_LOCAL && p->address.type == SOCK_SEQPACKET) { - log_error("[%s:%u] Address family not supported, ignoring: %s", filename, line, rvalue); - free(p); - return 0; - } - } - - p->fd = -1; - - if (s->ports) { - LIST_FIND_TAIL(SocketPort, port, s->ports, tail); - LIST_INSERT_AFTER(SocketPort, port, s->ports, tail, p); - } else - LIST_PREPEND(SocketPort, port, s->ports, p); - - return 0; -} - -int config_parse_socket_bind( - const char *filename, - unsigned line, - const char *section, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - Socket *s; - SocketAddressBindIPv6Only b; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - s = SOCKET(data); - - b = socket_address_bind_ipv6_only_from_string(rvalue); - if (b < 0) { - int r; - - 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; - } - - s->bind_ipv6_only = r ? SOCKET_ADDRESS_IPV6_ONLY : SOCKET_ADDRESS_BOTH; - } else - s->bind_ipv6_only = b; - - return 0; -} - -int config_parse_exec_nice( - const char *filename, - unsigned line, - const char *section, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - ExecContext *c = data; - int priority; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - if (safe_atoi(rvalue, &priority) < 0) { - log_error("[%s:%u] Failed to parse nice priority, ignoring: %s. ", filename, line, rvalue); - return 0; - } - - if (priority < PRIO_MIN || priority >= PRIO_MAX) { - log_error("[%s:%u] Nice priority out of range, ignoring: %s", filename, line, rvalue); - return 0; - } - - c->nice = priority; - c->nice_set = true; - - return 0; -} - -int config_parse_exec_oom_score_adjust( - const char *filename, - unsigned line, - const char *section, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - ExecContext *c = data; - int oa; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - if (safe_atoi(rvalue, &oa) < 0) { - log_error("[%s:%u] Failed to parse the OOM score adjust value, ignoring: %s", filename, line, rvalue); - return 0; - } - - if (oa < OOM_SCORE_ADJ_MIN || oa > OOM_SCORE_ADJ_MAX) { - log_error("[%s:%u] OOM score adjust value out of range, ignoring: %s", filename, line, rvalue); - return 0; - } - - c->oom_score_adjust = oa; - c->oom_score_adjust_set = true; - - return 0; -} - -int config_parse_exec( - const char *filename, - unsigned line, - const char *section, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - ExecCommand **e = data, *nce; - char *path, **n; - unsigned k; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(e); - - /* We accept an absolute path as first argument, or - * alternatively an absolute prefixed with @ to allow - * overriding of argv[0]. */ - - e += ltype; - - for (;;) { - char *w; - size_t l; - char *state; - bool honour_argv0 = false, ignore = false; - - path = NULL; - nce = NULL; - n = NULL; - - rvalue += strspn(rvalue, WHITESPACE); - - if (rvalue[0] == 0) - break; - - if (rvalue[0] == '-') { - ignore = true; - rvalue ++; - } - - if (rvalue[0] == '@') { - honour_argv0 = true; - rvalue ++; - } - - if (*rvalue != '/') { - log_error("[%s:%u] Invalid executable path in command line, ignoring: %s", filename, line, rvalue); - return 0; - } - - k = 0; - FOREACH_WORD_QUOTED(w, l, rvalue, state) { - if (strncmp(w, ";", MAX(l, 1U)) == 0) - break; - - k++; - } - - n = new(char*, k + !honour_argv0); - if (!n) - return -ENOMEM; - - k = 0; - FOREACH_WORD_QUOTED(w, l, rvalue, state) { - if (strncmp(w, ";", MAX(l, 1U)) == 0) - break; - - if (honour_argv0 && w == rvalue) { - assert(!path); - - path = strndup(w, l); - if (!path) { - r = -ENOMEM; - goto fail; - } - - if (!utf8_is_valid(path)) { - log_error("[%s:%u] Path is not UTF-8 clean, ignoring assignment: %s", filename, line, rvalue); - r = 0; - goto fail; - } - - } else { - char *c; - - c = n[k++] = cunescape_length(w, l); - if (!c) { - r = -ENOMEM; - goto fail; - } - - if (!utf8_is_valid(c)) { - log_error("[%s:%u] Path is not UTF-8 clean, ignoring assignment: %s", filename, line, rvalue); - r = 0; - goto fail; - } - } - } - - n[k] = NULL; - - if (!n[0]) { - log_error("[%s:%u] Invalid command line, ignoring: %s", filename, line, rvalue); - r = 0; - goto fail; - } - - if (!path) { - path = strdup(n[0]); - if (!path) { - r = -ENOMEM; - goto fail; - } - } - - assert(path_is_absolute(path)); - - nce = new0(ExecCommand, 1); - if (!nce) { - r = -ENOMEM; - goto fail; - } - - nce->argv = n; - nce->path = path; - nce->ignore = ignore; - - path_kill_slashes(nce->path); - - exec_command_append_list(e, nce); - - rvalue = state; - } - - return 0; - -fail: - n[k] = NULL; - strv_free(n); - free(path); - free(nce); - - return r; -} - -DEFINE_CONFIG_PARSE_ENUM(config_parse_service_type, service_type, ServiceType, "Failed to parse service type"); -DEFINE_CONFIG_PARSE_ENUM(config_parse_service_restart, service_restart, ServiceRestart, "Failed to parse service restart specifier"); - -int config_parse_socket_bindtodevice( - const char *filename, - unsigned line, - const char *section, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - Socket *s = data; - char *n; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - if (rvalue[0] && !streq(rvalue, "*")) { - if (!(n = strdup(rvalue))) - return -ENOMEM; - } else - n = NULL; - - free(s->bind_to_device); - s->bind_to_device = n; - - return 0; -} - -DEFINE_CONFIG_PARSE_ENUM(config_parse_output, exec_output, ExecOutput, "Failed to parse output specifier"); -DEFINE_CONFIG_PARSE_ENUM(config_parse_input, exec_input, ExecInput, "Failed to parse input specifier"); - -int config_parse_exec_io_class( - const char *filename, - unsigned line, - const char *section, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - ExecContext *c = data; - int x; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - 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; - } - - c->ioprio = IOPRIO_PRIO_VALUE(x, IOPRIO_PRIO_DATA(c->ioprio)); - c->ioprio_set = true; - - return 0; -} - -int config_parse_exec_io_priority( - const char *filename, - unsigned line, - const char *section, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - ExecContext *c = data; - int i; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - if (safe_atoi(rvalue, &i) < 0 || i < 0 || i >= IOPRIO_BE_NR) { - log_error("[%s:%u] Failed to parse io priority, ignoring: %s", filename, line, rvalue); - return 0; - } - - c->ioprio = IOPRIO_PRIO_VALUE(IOPRIO_PRIO_CLASS(c->ioprio), i); - c->ioprio_set = true; - - return 0; -} - -int config_parse_exec_cpu_sched_policy( - const char *filename, - unsigned line, - const char *section, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - - ExecContext *c = data; - int x; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - 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; - c->cpu_sched_set = true; - - return 0; -} - -int config_parse_exec_cpu_sched_prio( - const char *filename, - unsigned line, - const char *section, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - ExecContext *c = data; - int i; - - 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)) { - log_error("[%s:%u] Failed to parse CPU scheduling priority, ignoring: %s", filename, line, rvalue); - return 0; - } - - c->cpu_sched_priority = i; - c->cpu_sched_set = true; - - return 0; -} - -int config_parse_exec_cpu_affinity( - const char *filename, - unsigned line, - const char *section, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - ExecContext *c = data; - char *w; - size_t l; - char *state; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - FOREACH_WORD_QUOTED(w, l, rvalue, state) { - char *t; - int r; - unsigned cpu; - - if (!(t = strndup(w, l))) - return -ENOMEM; - - r = safe_atou(t, &cpu); - free(t); - - if (!(c->cpuset)) - if (!(c->cpuset = cpu_set_malloc(&c->cpuset_ncpus))) - return -ENOMEM; - - if (r < 0 || cpu >= c->cpuset_ncpus) { - log_error("[%s:%u] Failed to parse CPU affinity, ignoring: %s", filename, line, rvalue); - return 0; - } - - CPU_SET_S(cpu, CPU_ALLOC_SIZE(c->cpuset_ncpus), c->cpuset); - } - - return 0; -} - -int config_parse_exec_capabilities( - const char *filename, - unsigned line, - const char *section, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - ExecContext *c = data; - cap_t cap; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - if (!(cap = cap_from_text(rvalue))) { - if (errno == ENOMEM) - return -ENOMEM; - - log_error("[%s:%u] Failed to parse capabilities, ignoring: %s", filename, line, rvalue); - return 0; - } - - if (c->capabilities) - cap_free(c->capabilities); - c->capabilities = cap; - - return 0; -} - -int config_parse_exec_secure_bits( - const char *filename, - unsigned line, - const char *section, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - ExecContext *c = data; - char *w; - size_t l; - char *state; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - FOREACH_WORD_QUOTED(w, l, rvalue, state) { - if (first_word(w, "keep-caps")) - c->secure_bits |= SECURE_KEEP_CAPS; - else if (first_word(w, "keep-caps-locked")) - c->secure_bits |= SECURE_KEEP_CAPS_LOCKED; - else if (first_word(w, "no-setuid-fixup")) - c->secure_bits |= SECURE_NO_SETUID_FIXUP; - else if (first_word(w, "no-setuid-fixup-locked")) - c->secure_bits |= SECURE_NO_SETUID_FIXUP_LOCKED; - else if (first_word(w, "noroot")) - c->secure_bits |= SECURE_NOROOT; - 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); - return 0; - } - } - - return 0; -} - -int config_parse_bounding_set( - const char *filename, - unsigned line, - const char *section, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - uint64_t *capability_bounding_set_drop = data; - char *w; - size_t l; - char *state; - bool invert = false; - uint64_t sum = 0; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - if (rvalue[0] == '~') { - invert = true; - rvalue++; - } - - /* Note that we store this inverted internally, since the - * kernel wants it like this. But we actually expose it - * non-inverted everywhere to have a fully normalized - * interface. */ - - FOREACH_WORD_QUOTED(w, l, rvalue, state) { - char *t; - int r; - cap_value_t cap; - - t = strndup(w, l); - if (!t) - 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); - continue; - } - - sum |= ((uint64_t) 1ULL) << (uint64_t) cap; - } - - if (invert) - *capability_bounding_set_drop |= sum; - else - *capability_bounding_set_drop |= ~sum; - - return 0; -} - -int config_parse_limit( - const char *filename, - unsigned line, - const char *section, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - struct rlimit **rl = data; - unsigned long long u; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - rl += ltype; - - if (streq(rvalue, "infinity")) - u = (unsigned long long) RLIM_INFINITY; - else if (safe_atollu(rvalue, &u) < 0) { - log_error("[%s:%u] Failed to parse resource value, ignoring: %s", filename, line, rvalue); - return 0; - } - - if (!*rl) - if (!(*rl = new(struct rlimit, 1))) - return -ENOMEM; - - (*rl)->rlim_cur = (*rl)->rlim_max = (rlim_t) u; - return 0; -} - -int config_parse_unit_cgroup( - const char *filename, - unsigned line, - const char *section, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - Unit *u = userdata; - char *w; - size_t l; - char *state; - - FOREACH_WORD_QUOTED(w, l, rvalue, state) { - char *t, *k; - int r; - - t = strndup(w, l); - if (!t) - return -ENOMEM; - - k = unit_full_printf(u, t); - free(t); - - if (!k) - return -ENOMEM; - - t = cunescape(k); - free(k); - - if (!t) - return -ENOMEM; - - r = unit_add_cgroup_from_text(u, t); - free(t); - - if (r < 0) { - log_error("[%s:%u] Failed to parse cgroup value, ignoring: %s", filename, line, rvalue); - return 0; - } - } - - return 0; -} - -#ifdef HAVE_SYSV_COMPAT -int config_parse_sysv_priority( - const char *filename, - unsigned line, - const char *section, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - int *priority = data; - int i; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - if (safe_atoi(rvalue, &i) < 0 || i < 0) { - log_error("[%s:%u] Failed to parse SysV start priority, ignoring: %s", filename, line, rvalue); - return 0; - } - - *priority = (int) i; - return 0; -} -#endif - -int config_parse_fsck_passno( - const char *filename, - unsigned line, - const char *section, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - int *passno = data; - int i; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - if (safe_atoi(rvalue, &i) || i < 0) { - log_error("[%s:%u] Failed to parse fsck pass number, ignoring: %s", filename, line, rvalue); - return 0; - } - - *passno = (int) i; - return 0; -} - -DEFINE_CONFIG_PARSE_ENUM(config_parse_kill_mode, kill_mode, KillMode, "Failed to parse kill mode"); - -int config_parse_kill_signal( - const char *filename, - unsigned line, - const char *section, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - int *sig = data; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(sig); - - if ((r = signal_from_string_try_harder(rvalue)) <= 0) { - log_error("[%s:%u] Failed to parse kill signal, ignoring: %s", filename, line, rvalue); - return 0; - } - - *sig = r; - return 0; -} - -int config_parse_exec_mount_flags( - const char *filename, - unsigned line, - const char *section, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - ExecContext *c = data; - char *w; - size_t l; - char *state; - unsigned long flags = 0; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - FOREACH_WORD_QUOTED(w, l, rvalue, state) { - if (strncmp(w, "shared", MAX(l, 6U)) == 0) - flags |= MS_SHARED; - else if (strncmp(w, "slave", MAX(l, 5U)) == 0) - flags |= MS_SLAVE; - else if (strncmp(w, "private", MAX(l, 7U)) == 0) - flags |= MS_PRIVATE; - else { - log_error("[%s:%u] Failed to parse mount flags, ignoring: %s", filename, line, rvalue); - return 0; - } - } - - c->mount_flags = flags; - return 0; -} - -int config_parse_timer( - const char *filename, - unsigned line, - const char *section, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - Timer *t = data; - usec_t u; - TimerValue *v; - TimerBase b; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - if ((b = timer_base_from_string(lvalue)) < 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 (!(v = new0(TimerValue, 1))) - return -ENOMEM; - - v->base = b; - v->value = u; - - LIST_PREPEND(TimerValue, value, t->values, v); - - return 0; -} - -int config_parse_timer_unit( - const char *filename, - unsigned line, - const char *section, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - Timer *t = data; - int r; - DBusError error; - Unit *u; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - dbus_error_init(&error); - - if (endswith(rvalue, ".timer")) { - log_error("[%s:%u] Unit cannot be of type timer, ignoring: %s", filename, line, rvalue); - return 0; - } - - r = manager_load_unit(UNIT(t)->manager, rvalue, NULL, NULL, &u); - if (r < 0) { - log_error("[%s:%u] Failed to load unit %s, ignoring: %s", filename, line, rvalue, bus_error(&error, r)); - dbus_error_free(&error); - return 0; - } - - unit_ref_set(&t->unit, u); - - return 0; -} - -int config_parse_path_spec( - const char *filename, - unsigned line, - const char *section, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - Path *p = data; - PathSpec *s; - PathType b; - char *k; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - 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; - } - - 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; - } - - s = new0(PathSpec, 1); - if (!s) { - free(k); - return log_oom(); - } - - s->path = path_kill_slashes(k); - s->type = b; - s->inotify_fd = -1; - - LIST_PREPEND(PathSpec, spec, p->specs, s); - - return 0; -} - -int config_parse_path_unit( - const char *filename, - unsigned line, - const char *section, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - Path *t = data; - int r; - DBusError error; - Unit *u; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - dbus_error_init(&error); - - if (endswith(rvalue, ".path")) { - log_error("[%s:%u] Unit cannot be of type path, ignoring: %s", filename, line, rvalue); - return 0; - } - - if ((r = manager_load_unit(UNIT(t)->manager, rvalue, NULL, &error, &u)) < 0) { - log_error("[%s:%u] Failed to load unit %s, ignoring: %s", filename, line, rvalue, bus_error(&error, r)); - dbus_error_free(&error); - return 0; - } - - unit_ref_set(&t->unit, u); - - return 0; -} - -int config_parse_socket_service( - const char *filename, - unsigned line, - const char *section, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - Socket *s = data; - int r; - DBusError error; - Unit *x; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - dbus_error_init(&error); - - if (!endswith(rvalue, ".service")) { - log_error("[%s:%u] Unit must be of type service, ignoring: %s", filename, line, rvalue); - return 0; - } - - r = manager_load_unit(UNIT(s)->manager, rvalue, NULL, &error, &x); - if (r < 0) { - log_error("[%s:%u] Failed to load unit %s, ignoring: %s", filename, line, rvalue, bus_error(&error, r)); - dbus_error_free(&error); - return 0; - } - - unit_ref_set(&s->service, x); - - return 0; -} - -int config_parse_service_sockets( - const char *filename, - unsigned line, - const char *section, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - Service *s = data; - int r; - char *state, *w; - size_t l; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - FOREACH_WORD_QUOTED(w, l, rvalue, state) { - char *t, *k; - - 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); - 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)); - - r = unit_add_dependency_by_name(UNIT(s), UNIT_TRIGGERED_BY, k, NULL, true); - if (r < 0) - return r; - - free(k); - } - - return 0; -} - -int config_parse_service_timeout( - const char *filename, - unsigned line, - const char *section, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - Service *s = userdata; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(s); - - r = config_parse_usec(filename, line, section, lvalue, ltype, rvalue, data, userdata); - - if (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( - const char *filename, - unsigned line, - const char *section, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - char ***env = data, **k; - Unit *u = userdata; - char *s; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - s = unit_full_printf(u, rvalue); - if (!s) - return -ENOMEM; - - if (!path_is_absolute(s[0] == '-' ? s + 1 : s)) { - log_error("[%s:%u] Path '%s' is not absolute, ignoring.", filename, line, s); - free(s); - return 0; - } - - k = strv_append(*env, s); - free(s); - if (!k) - return -ENOMEM; - - strv_free(*env); - *env = k; - - return 0; -} - -int config_parse_ip_tos( - const char *filename, - unsigned line, - const char *section, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - int *ip_tos = data, x; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - 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; -} - -int config_parse_unit_condition_path( - const char *filename, - unsigned line, - const char *section, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - ConditionType cond = ltype; - Unit *u = data; - bool trigger, negate; - Condition *c; - _cleanup_free_ char *p = NULL; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - trigger = rvalue[0] == '|'; - if (trigger) - rvalue++; - - negate = rvalue[0] == '!'; - if (negate) - 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, p, trigger, negate); - if (!c) - return -ENOMEM; - - LIST_PREPEND(Condition, conditions, u->conditions, c); - return 0; -} - -int config_parse_unit_condition_string( - const char *filename, - unsigned line, - const char *section, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - ConditionType cond = ltype; - Unit *u = data; - bool trigger, negate; - Condition *c; - _cleanup_free_ char *s = NULL; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - trigger = rvalue[0] == '|'; - if (trigger) - rvalue++; - - negate = rvalue[0] == '!'; - if (negate) - rvalue++; - - 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; -} - -int config_parse_unit_condition_null( - const char *filename, - unsigned line, - const char *section, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - Unit *u = data; - Condition *c; - bool trigger, negate; - int b; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - if ((trigger = rvalue[0] == '|')) - rvalue++; - - if ((negate = rvalue[0] == '!')) - rvalue++; - - if ((b = parse_boolean(rvalue)) < 0) { - log_error("[%s:%u] Failed to parse boolean value in condition, ignoring: %s", filename, line, rvalue); - return 0; - } - - if (!b) - negate = !negate; - - if (!(c = condition_new(CONDITION_NULL, NULL, trigger, negate))) - return -ENOMEM; - - LIST_PREPEND(Condition, conditions, u->conditions, c); - return 0; -} - -DEFINE_CONFIG_PARSE_ENUM(config_parse_notify_access, notify_access, NotifyAccess, "Failed to parse notify access specifier"); -DEFINE_CONFIG_PARSE_ENUM(config_parse_start_limit_action, start_limit_action, StartLimitAction, "Failed to parse start limit action specifier"); - -int config_parse_unit_cgroup_attr( - const char *filename, - unsigned line, - const char *section, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - Unit *u = data; - char **l; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - l = strv_split_quoted(rvalue); - if (!l) - return -ENOMEM; - - if (strv_length(l) != 2) { - log_error("[%s:%u] Failed to parse cgroup attribute value, ignoring: %s", filename, line, rvalue); - strv_free(l); - return 0; - } - - r = unit_add_cgroup_attribute(u, NULL, l[0], l[1], NULL); - strv_free(l); - - if (r < 0) { - log_error("[%s:%u] Failed to add cgroup attribute value, ignoring: %s", filename, line, rvalue); - return 0; - } - - return 0; -} - -int config_parse_unit_cpu_shares(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata) { - Unit *u = data; - int r; - unsigned long ul; - char *t; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - if (safe_atolu(rvalue, &ul) < 0 || ul < 1) { - log_error("[%s:%u] Failed to parse CPU shares value, ignoring: %s", filename, line, rvalue); - return 0; - } - - if (asprintf(&t, "%lu", ul) < 0) - return -ENOMEM; - - r = unit_add_cgroup_attribute(u, "cpu", "cpu.shares", t, NULL); - free(t); - - if (r < 0) { - log_error("[%s:%u] Failed to add cgroup attribute value, ignoring: %s", filename, line, rvalue); - return 0; - } - - return 0; -} - -int config_parse_unit_memory_limit(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata) { - Unit *u = data; - int r; - off_t sz; - char *t; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - if (parse_bytes(rvalue, &sz) < 0 || sz <= 0) { - log_error("[%s:%u] Failed to parse memory limit value, ignoring: %s", filename, line, rvalue); - return 0; - } - - if (asprintf(&t, "%llu", (unsigned long long) sz) < 0) - return -ENOMEM; - - r = unit_add_cgroup_attribute(u, - "memory", - streq(lvalue, "MemorySoftLimit") ? "memory.soft_limit_in_bytes" : "memory.limit_in_bytes", - t, NULL); - free(t); - - if (r < 0) { - log_error("[%s:%u] Failed to add cgroup attribute value, ignoring: %s", filename, line, rvalue); - return 0; - } - - return 0; -} - -static int device_map(const char *controller, const char *name, const char *value, char **ret) { - char **l; - - assert(controller); - assert(name); - assert(value); - assert(ret); - - l = strv_split_quoted(value); - if (!l) - return -ENOMEM; - - assert(strv_length(l) >= 1); - - if (streq(l[0], "*")) { - - if (asprintf(ret, "a *:*%s%s", - isempty(l[1]) ? "" : " ", strempty(l[1])) < 0) { - strv_free(l); - return -ENOMEM; - } - - } else { - struct stat st; - - if (stat(l[0], &st) < 0) { - log_warning("Couldn't stat device %s", l[0]); - strv_free(l); - return -errno; - } - - if (!S_ISCHR(st.st_mode) && !S_ISBLK(st.st_mode)) { - log_warning("%s is not a device.", l[0]); - strv_free(l); - return -ENODEV; - } - - if (asprintf(ret, "%c %u:%u%s%s", - S_ISCHR(st.st_mode) ? 'c' : 'b', - major(st.st_rdev), minor(st.st_rdev), - isempty(l[1]) ? "" : " ", strempty(l[1])) < 0) { - - strv_free(l); - return -ENOMEM; - } - } - - strv_free(l); - return 0; -} - -int config_parse_unit_device_allow(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata) { - Unit *u = data; - char **l; - int r; - unsigned k; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - l = strv_split_quoted(rvalue); - if (!l) - return -ENOMEM; - - k = strv_length(l); - if (k < 1 || k > 2) { - log_error("[%s:%u] Failed to parse device value, ignoring: %s", filename, line, rvalue); - strv_free(l); - return 0; - } - - if (!streq(l[0], "*") && !path_startswith(l[0], "/dev")) { - log_error("[%s:%u] Device node path not absolute, ignoring: %s", filename, line, rvalue); - strv_free(l); - return 0; - } - - if (!isempty(l[1]) && !in_charset(l[1], "rwm")) { - log_error("[%s:%u] Device access string invalid, ignoring: %s", filename, line, rvalue); - strv_free(l); - return 0; - } - strv_free(l); - - r = unit_add_cgroup_attribute(u, "devices", - streq(lvalue, "DeviceAllow") ? "devices.allow" : "devices.deny", - rvalue, device_map); - - if (r < 0) { - log_error("[%s:%u] Failed to add cgroup attribute value, ignoring: %s", filename, line, rvalue); - return 0; - } - - return 0; -} - -static int blkio_map(const char *controller, const char *name, const char *value, char **ret) { - struct stat st; - char **l; - dev_t d; - - assert(controller); - assert(name); - assert(value); - assert(ret); - - l = strv_split_quoted(value); - if (!l) - return -ENOMEM; - - assert(strv_length(l) == 2); - - if (stat(l[0], &st) < 0) { - log_warning("Couldn't stat device %s", l[0]); - strv_free(l); - return -errno; - } - - if (S_ISBLK(st.st_mode)) - d = st.st_rdev; - else if (major(st.st_dev) != 0) { - /* If this is not a device node then find the block - * device this file is stored on */ - d = st.st_dev; - - /* If this is a partition, try to get the originating - * block device */ - block_get_whole_disk(d, &d); - } else { - log_warning("%s is not a block device and file system block device cannot be determined or is not local.", l[0]); - strv_free(l); - return -ENODEV; - } - - if (asprintf(ret, "%u:%u %s", major(d), minor(d), l[1]) < 0) { - strv_free(l); - return -ENOMEM; - } - - strv_free(l); - return 0; -} - -int config_parse_unit_blkio_weight(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata) { - Unit *u = data; - int r; - unsigned long ul; - const char *device = NULL, *weight; - unsigned k; - char *t, **l; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - l = strv_split_quoted(rvalue); - if (!l) - return -ENOMEM; - - k = strv_length(l); - if (k < 1 || k > 2) { - log_error("[%s:%u] Failed to parse weight value, ignoring: %s", filename, line, rvalue); - strv_free(l); - return 0; - } - - if (k == 1) - weight = l[0]; - else { - device = l[0]; - weight = l[1]; - } - - if (device && !path_is_absolute(device)) { - log_error("[%s:%u] Failed to parse block device node value, ignoring: %s", filename, line, rvalue); - strv_free(l); - return 0; - } - - if (safe_atolu(weight, &ul) < 0 || ul < 10 || ul > 1000) { - log_error("[%s:%u] Failed to parse block IO weight value, ignoring: %s", filename, line, rvalue); - strv_free(l); - return 0; - } - - if (device) - r = asprintf(&t, "%s %lu", device, ul); - else - r = asprintf(&t, "%lu", ul); - strv_free(l); - - if (r < 0) - return -ENOMEM; - - if (device) - r = unit_add_cgroup_attribute(u, "blkio", "blkio.weight_device", t, blkio_map); - else - r = unit_add_cgroup_attribute(u, "blkio", "blkio.weight", t, NULL); - free(t); - - if (r < 0) { - log_error("[%s:%u] Failed to add cgroup attribute value, ignoring: %s", filename, line, rvalue); - return 0; - } - - return 0; -} - -int config_parse_unit_blkio_bandwidth(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata) { - Unit *u = data; - int r; - off_t bytes; - unsigned k; - char *t, **l; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - l = strv_split_quoted(rvalue); - if (!l) - return -ENOMEM; - - k = strv_length(l); - if (k != 2) { - log_error("[%s:%u] Failed to parse bandwidth value, ignoring: %s", filename, line, rvalue); - strv_free(l); - return 0; - } - - if (!path_is_absolute(l[0])) { - log_error("[%s:%u] Failed to parse block device node value, ignoring: %s", filename, line, rvalue); - strv_free(l); - return 0; - } - - if (parse_bytes(l[1], &bytes) < 0 || bytes <= 0) { - log_error("[%s:%u] Failed to parse block IO bandwidth value, ignoring: %s", filename, line, rvalue); - strv_free(l); - return 0; - } - - r = asprintf(&t, "%s %llu", l[0], (unsigned long long) bytes); - strv_free(l); - - if (r < 0) - return -ENOMEM; - - r = unit_add_cgroup_attribute(u, "blkio", - streq(lvalue, "BlockIOReadBandwidth") ? "blkio.read_bps_device" : "blkio.write_bps_device", - t, blkio_map); - free(t); - - if (r < 0) { - log_error("[%s:%u] Failed to add cgroup attribute value, ignoring: %s", filename, line, rvalue); - return 0; - } - - return 0; -} - -int config_parse_unit_requires_mounts_for( - const char *filename, - unsigned line, - const char *section, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - Unit *u = userdata; - int r; - bool empty_before; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - empty_before = !u->requires_mounts_for; - - r = config_parse_path_strv(filename, line, section, lvalue, ltype, rvalue, data, userdata); - - /* Make it easy to find units with requires_mounts set */ - if (empty_before && u->requires_mounts_for) - LIST_PREPEND(Unit, has_requires_mounts_for, u->manager->has_requires_mounts_for, u); - - return r; -} - -int config_parse_documentation( - const char *filename, - unsigned line, - const char *section, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - Unit *u = userdata; - int r; - char **a, **b; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(u); - - r = config_parse_unit_strv_printf(filename, line, section, lvalue, ltype, rvalue, data, userdata); - if (r < 0) - return r; - - for (a = b = u->documentation; a && *a; a++) { - - if (is_valid_documentation_url(*a)) - *(b++) = *a; - else { - log_error("[%s:%u] Invalid URL, ignoring: %s", filename, line, *a); - free(*a); - } - } - *b = NULL; - - return r; -} - -static void syscall_set(uint32_t *p, int nr) { - p[nr >> 4] |= 1 << (nr & 31); -} - -static void syscall_unset(uint32_t *p, int nr) { - p[nr >> 4] &= ~(1 << (nr & 31)); -} - -int config_parse_syscall_filter( - const char *filename, - unsigned line, - const char *section, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - ExecContext *c = data; - Unit *u = userdata; - bool invert = false; - char *w; - size_t l; - char *state; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(u); - - if (rvalue[0] == '~') { - invert = true; - rvalue++; - } - - if (!c->syscall_filter) { - size_t n; - - n = (syscall_max() + 31) >> 4; - c->syscall_filter = new(uint32_t, n); - if (!c->syscall_filter) - return -ENOMEM; - - memset(c->syscall_filter, invert ? 0xFF : 0, n * sizeof(uint32_t)); - - /* Add these by default */ - syscall_set(c->syscall_filter, __NR_execve); - syscall_set(c->syscall_filter, __NR_rt_sigreturn); -#ifdef __NR_sigreturn - syscall_set(c->syscall_filter, __NR_sigreturn); -#endif - syscall_set(c->syscall_filter, __NR_exit_group); - syscall_set(c->syscall_filter, __NR_exit); - } - - FOREACH_WORD_QUOTED(w, l, rvalue, state) { - int id; - char *t; - - 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); - continue; - } - - if (invert) - syscall_unset(c->syscall_filter, id); - else - syscall_set(c->syscall_filter, id); - } - - c->no_new_privileges = true; - - return 0; -} - -#define FOLLOW_MAX 8 - -static int open_follow(char **filename, FILE **_f, Set *names, char **_final) { - unsigned c = 0; - int fd, r; - FILE *f; - char *id = NULL; - - assert(filename); - assert(*filename); - assert(_f); - assert(names); - - /* This will update the filename pointer if the loaded file is - * reached by a symlink. The old string will be freed. */ - - for (;;) { - char *target, *name; - - if (c++ >= FOLLOW_MAX) - return -ELOOP; - - path_kill_slashes(*filename); - - /* Add the file name we are currently looking at to - * the names of this unit, but only if it is a valid - * unit name. */ - name = path_get_file_name(*filename); - - if (unit_name_is_valid(name, true)) { - - id = set_get(names, name); - if (!id) { - id = strdup(name); - if (!id) - return -ENOMEM; - - r = set_put(names, id); - if (r < 0) { - free(id); - return r; - } - } - } - - /* Try to open the file name, but don't if its a symlink */ - fd = open(*filename, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW); - if (fd >= 0) - break; - - if (errno != ELOOP) - return -errno; - - /* Hmm, so this is a symlink. Let's read the name, and follow it manually */ - r = readlink_and_make_absolute(*filename, &target); - if (r < 0) - return r; - - free(*filename); - *filename = target; - } - - f = fdopen(fd, "re"); - if (!f) { - r = -errno; - close_nointr_nofail(fd); - return r; - } - - *_f = f; - *_final = id; - return 0; -} - -static int merge_by_names(Unit **u, Set *names, const char *id) { - char *k; - int r; - - assert(u); - assert(*u); - assert(names); - - /* Let's try to add in all symlink names we found */ - while ((k = set_steal_first(names))) { - - /* First try to merge in the other name into our - * unit */ - r = unit_merge_by_name(*u, k); - if (r < 0) { - Unit *other; - - /* Hmm, we couldn't merge the other unit into - * ours? Then let's try it the other way - * round */ - - other = manager_get_unit((*u)->manager, k); - free(k); - - if (other) { - r = unit_merge(other, *u); - if (r >= 0) { - *u = other; - return merge_by_names(u, names, NULL); - } - } - - return r; - } - - if (id == k) - unit_choose_id(*u, id); - - free(k); - } - - return 0; -} - -static int load_from_path(Unit *u, const char *path) { - int r; - Set *symlink_names; - FILE *f = NULL; - char *filename = NULL, *id = NULL; - Unit *merged; - struct stat st; - - assert(u); - assert(path); - - symlink_names = set_new(string_hash_func, string_compare_func); - if (!symlink_names) - return -ENOMEM; - - if (path_is_absolute(path)) { - - filename = strdup(path); - if (!filename) { - r = -ENOMEM; - goto finish; - } - - r = open_follow(&filename, &f, symlink_names, &id); - if (r < 0) { - free(filename); - filename = NULL; - - if (r != -ENOENT) - goto finish; - } - - } else { - char **p; - - STRV_FOREACH(p, u->manager->lookup_paths.unit_path) { - - /* Instead of opening the path right away, we manually - * follow all symlinks and add their name to our unit - * name set while doing so */ - filename = path_make_absolute(path, *p); - if (!filename) { - r = -ENOMEM; - goto finish; - } - - if (u->manager->unit_path_cache && - !set_get(u->manager->unit_path_cache, filename)) - r = -ENOENT; - else - r = open_follow(&filename, &f, symlink_names, &id); - - if (r < 0) { - free(filename); - filename = NULL; - - if (r != -ENOENT) - goto finish; - - /* Empty the symlink names for the next run */ - set_clear_free(symlink_names); - continue; - } - - break; - } - } - - if (!filename) { - /* Hmm, no suitable file found? */ - r = 0; - goto finish; - } - - merged = u; - r = merge_by_names(&merged, symlink_names, id); - if (r < 0) - goto finish; - - if (merged != u) { - u->load_state = UNIT_MERGED; - r = 0; - goto finish; - } - - if (fstat(fileno(f), &st) < 0) { - r = -errno; - goto finish; - } - - if (null_or_empty(&st)) - u->load_state = UNIT_MASKED; - else { - /* Now, parse the file contents */ - r = config_parse(filename, f, UNIT_VTABLE(u)->sections, config_item_perf_lookup, (void*) load_fragment_gperf_lookup, false, u); - if (r < 0) - goto finish; - - u->load_state = UNIT_LOADED; - } - - free(u->fragment_path); - u->fragment_path = filename; - filename = NULL; - - u->fragment_mtime = timespec_load(&st.st_mtim); - - if (u->source_path) { - if (stat(u->source_path, &st) >= 0) - u->source_mtime = timespec_load(&st.st_mtim); - else - u->source_mtime = 0; - } - - r = 0; - -finish: - set_free_free(symlink_names); - free(filename); - - if (f) - fclose(f); - - return r; -} - -int unit_load_fragment(Unit *u) { - int r; - Iterator i; - const char *t; - - assert(u); - assert(u->load_state == UNIT_STUB); - assert(u->id); - - /* First, try to find the unit under its id. We always look - * for unit files in the default directories, to make it easy - * to override things by placing things in /etc/systemd/system */ - r = load_from_path(u, u->id); - if (r < 0) - return r; - - /* Try to find an alias we can load this with */ - if (u->load_state == UNIT_STUB) - SET_FOREACH(t, u->names, i) { - - if (t == u->id) - continue; - - r = load_from_path(u, t); - if (r < 0) - return r; - - if (u->load_state != UNIT_STUB) - break; - } - - /* And now, try looking for it under the suggested (originally linked) path */ - if (u->load_state == UNIT_STUB && u->fragment_path) { - - r = load_from_path(u, u->fragment_path); - if (r < 0) - return r; - - if (u->load_state == UNIT_STUB) { - /* Hmm, this didn't work? Then let's get rid - * of the fragment path stored for us, so that - * we don't point to an invalid location. */ - free(u->fragment_path); - u->fragment_path = NULL; - } - } - - /* Look for a template */ - if (u->load_state == UNIT_STUB && u->instance) { - char *k; - - k = unit_name_template(u->id); - if (!k) - return -ENOMEM; - - r = load_from_path(u, k); - free(k); - - if (r < 0) - return r; - - if (u->load_state == UNIT_STUB) - SET_FOREACH(t, u->names, i) { - - if (t == u->id) - continue; - - k = unit_name_template(t); - if (!k) - return -ENOMEM; - - r = load_from_path(u, k); - free(k); - - if (r < 0) - return r; - - if (u->load_state != UNIT_STUB) - break; - } - } - - return 0; -} - -void unit_dump_config_items(FILE *f) { - static const struct { - const ConfigParserCallback callback; - const char *rvalue; - } table[] = { - { config_parse_int, "INTEGER" }, - { config_parse_unsigned, "UNSIGNED" }, - { config_parse_bytes_size, "SIZE" }, - { config_parse_bool, "BOOLEAN" }, - { config_parse_string, "STRING" }, - { config_parse_path, "PATH" }, - { config_parse_unit_path_printf, "PATH" }, - { config_parse_strv, "STRING [...]" }, - { config_parse_exec_nice, "NICE" }, - { config_parse_exec_oom_score_adjust, "OOMSCOREADJUST" }, - { config_parse_exec_io_class, "IOCLASS" }, - { config_parse_exec_io_priority, "IOPRIORITY" }, - { config_parse_exec_cpu_sched_policy, "CPUSCHEDPOLICY" }, - { config_parse_exec_cpu_sched_prio, "CPUSCHEDPRIO" }, - { config_parse_exec_cpu_affinity, "CPUAFFINITY" }, - { config_parse_mode, "MODE" }, - { config_parse_unit_env_file, "FILE" }, - { config_parse_output, "OUTPUT" }, - { config_parse_input, "INPUT" }, - { config_parse_facility, "FACILITY" }, - { config_parse_level, "LEVEL" }, - { config_parse_exec_capabilities, "CAPABILITIES" }, - { config_parse_exec_secure_bits, "SECUREBITS" }, - { config_parse_bounding_set, "BOUNDINGSET" }, - { config_parse_limit, "LIMIT" }, - { config_parse_unit_cgroup, "CGROUP [...]" }, - { config_parse_unit_deps, "UNIT [...]" }, - { config_parse_exec, "PATH [ARGUMENT [...]]" }, - { config_parse_service_type, "SERVICETYPE" }, - { config_parse_service_restart, "SERVICERESTART" }, -#ifdef HAVE_SYSV_COMPAT - { config_parse_sysv_priority, "SYSVPRIORITY" }, -#else - { config_parse_warn_compat, "NOTSUPPORTED" }, -#endif - { config_parse_kill_mode, "KILLMODE" }, - { config_parse_kill_signal, "SIGNAL" }, - { config_parse_socket_listen, "SOCKET [...]" }, - { config_parse_socket_bind, "SOCKETBIND" }, - { config_parse_socket_bindtodevice, "NETWORKINTERFACE" }, - { config_parse_usec, "SECONDS" }, - { config_parse_nsec, "NANOSECONDS" }, - { config_parse_path_strv, "PATH [...]" }, - { config_parse_unit_requires_mounts_for, "PATH [...]" }, - { config_parse_exec_mount_flags, "MOUNTFLAG [...]" }, - { config_parse_unit_string_printf, "STRING" }, - { config_parse_timer, "TIMER" }, - { config_parse_timer_unit, "NAME" }, - { config_parse_path_spec, "PATH" }, - { config_parse_path_unit, "UNIT" }, - { config_parse_notify_access, "ACCESS" }, - { config_parse_ip_tos, "TOS" }, - { config_parse_unit_condition_path, "CONDITION" }, - { config_parse_unit_condition_string, "CONDITION" }, - { config_parse_unit_condition_null, "CONDITION" }, - }; - - const char *prev = NULL; - const char *i; - - assert(f); - - NULSTR_FOREACH(i, load_fragment_gperf_nulstr) { - const char *rvalue = "OTHER", *lvalue; - unsigned j; - size_t prefix_len; - const char *dot; - const ConfigPerfItem *p; - - assert_se(p = load_fragment_gperf_lookup(i, strlen(i))); - - dot = strchr(i, '.'); - lvalue = dot ? dot + 1 : i; - prefix_len = dot-i; - - if (dot) - if (!prev || strncmp(prev, i, prefix_len+1) != 0) { - if (prev) - fputc('\n', f); - - fprintf(f, "[%.*s]\n", (int) prefix_len, i); - } - - for (j = 0; j < ELEMENTSOF(table); j++) - if (p->parse == table[j].callback) { - rvalue = table[j].rvalue; - break; - } - - fprintf(f, "%s=%s\n", lvalue, rvalue); - prev = i; - } -} diff --git a/src/core/load-fragment.h b/src/core/load-fragment.h deleted file mode 100644 index 24f738464c..0000000000 --- a/src/core/load-fragment.h +++ /dev/null @@ -1,88 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -#pragma once - -/*** - 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 "unit.h" - -/* Read service data from .desktop file style configuration fragments */ - -int unit_load_fragment(Unit *u); - -void unit_dump_config_items(FILE *f); - -int config_parse_warn_compat(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_unit_deps(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_unit_string_printf(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_unit_strv_printf(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_unit_path_printf(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_documentation(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_socket_listen(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_socket_bind(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_exec_nice(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_exec_oom_score_adjust(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_exec(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_service_timeout(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_service_type(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_service_restart(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_socket_bindtodevice(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_output(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_input(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_exec_io_class(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_exec_io_priority(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_exec_cpu_sched_policy(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_exec_cpu_sched_prio(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_exec_cpu_affinity(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_exec_capabilities(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_exec_secure_bits(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_bounding_set(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_limit(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_unit_cgroup(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_sysv_priority(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_fsck_passno(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_kill_signal(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_exec_mount_flags(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_timer(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_timer_unit(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_path_spec(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_path_unit(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_socket_service(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_service_sockets(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_unit_env_file(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_ip_tos(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_unit_condition_path(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_unit_condition_string(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_unit_condition_null(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_kill_mode(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_notify_access(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_start_limit_action(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_unit_cgroup_attr(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_unit_cpu_shares(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_unit_memory_limit(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_unit_device_allow(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_unit_blkio_weight(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_unit_blkio_bandwidth(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_unit_requires_mounts_for(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_syscall_filter(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); - -/* gperf prototypes */ -const struct ConfigPerfItem* load_fragment_gperf_lookup(const char *key, unsigned length); -extern const char load_fragment_gperf_nulstr[]; diff --git a/src/core/locale-setup.c b/src/core/locale-setup.c deleted file mode 100644 index b9bd3a0748..0000000000 --- a/src/core/locale-setup.c +++ /dev/null @@ -1,241 +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 <string.h> -#include <stdlib.h> -#include <errno.h> - -#include "locale-setup.h" -#include "util.h" -#include "macro.h" -#include "virt.h" - -enum { - /* We don't list LC_ALL here on purpose. People should be - * using LANG instead. */ - - VARIABLE_LANG, - VARIABLE_LANGUAGE, - VARIABLE_LC_CTYPE, - VARIABLE_LC_NUMERIC, - VARIABLE_LC_TIME, - VARIABLE_LC_COLLATE, - VARIABLE_LC_MONETARY, - VARIABLE_LC_MESSAGES, - VARIABLE_LC_PAPER, - VARIABLE_LC_NAME, - VARIABLE_LC_ADDRESS, - VARIABLE_LC_TELEPHONE, - VARIABLE_LC_MEASUREMENT, - VARIABLE_LC_IDENTIFICATION, - _VARIABLE_MAX -}; - -static const char * const variable_names[_VARIABLE_MAX] = { - [VARIABLE_LANG] = "LANG", - [VARIABLE_LANGUAGE] = "LANGUAGE", - [VARIABLE_LC_CTYPE] = "LC_CTYPE", - [VARIABLE_LC_NUMERIC] = "LC_NUMERIC", - [VARIABLE_LC_TIME] = "LC_TIME", - [VARIABLE_LC_COLLATE] = "LC_COLLATE", - [VARIABLE_LC_MONETARY] = "LC_MONETARY", - [VARIABLE_LC_MESSAGES] = "LC_MESSAGES", - [VARIABLE_LC_PAPER] = "LC_PAPER", - [VARIABLE_LC_NAME] = "LC_NAME", - [VARIABLE_LC_ADDRESS] = "LC_ADDRESS", - [VARIABLE_LC_TELEPHONE] = "LC_TELEPHONE", - [VARIABLE_LC_MEASUREMENT] = "LC_MEASUREMENT", - [VARIABLE_LC_IDENTIFICATION] = "LC_IDENTIFICATION" -}; - -int locale_setup(void) { - char *variables[_VARIABLE_MAX]; - int r = 0, i; - - 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)); - } - - /* 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) - log_warning("Failed to read /etc/locale.conf: %s", strerror(-r)); - } - -#if 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_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"))) { - 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; - goto finish; - } - } else - unsetenv(variable_names[i]); - } - - r = 0; - -finish: - for (i = 0; i < _VARIABLE_MAX; i++) - free(variables[i]); - - return r; -} diff --git a/src/core/locale-setup.h b/src/core/locale-setup.h deleted file mode 100644 index 5a0f2f7888..0000000000 --- a/src/core/locale-setup.h +++ /dev/null @@ -1,24 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -#pragma once - -/*** - 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/>. -***/ - -int locale_setup(void); diff --git a/src/core/loopback-setup.c b/src/core/loopback-setup.c deleted file mode 100644 index 065b75a6e3..0000000000 --- a/src/core/loopback-setup.c +++ /dev/null @@ -1,317 +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 <sys/socket.h> -#include <net/if.h> -#include <asm/types.h> -#include <netinet/in.h> -#include <string.h> -#include <stdlib.h> -#include <unistd.h> -#include <linux/netlink.h> -#include <linux/rtnetlink.h> - -#include "util.h" -#include "macro.h" -#include "loopback-setup.h" -#include "socket-util.h" - -#define NLMSG_TAIL(nmsg) \ - ((struct rtattr *) (((uint8_t*) (nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len))) - -static int add_rtattr(struct nlmsghdr *n, size_t max_length, int type, const void *data, size_t data_length) { - size_t length; - struct rtattr *rta; - - length = RTA_LENGTH(data_length); - - if (NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(length) > max_length) - return -E2BIG; - - rta = NLMSG_TAIL(n); - rta->rta_type = type; - rta->rta_len = length; - memcpy(RTA_DATA(rta), data, data_length); - n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(length); - - return 0; -} - -static ssize_t sendto_loop(int fd, const void *buf, size_t buf_len, int flags, const struct sockaddr *sa, socklen_t sa_len) { - - for (;;) { - ssize_t l; - - l = sendto(fd, buf, buf_len, flags, sa, sa_len); - if (l >= 0) - return l; - - if (errno != EINTR) - return -errno; - } -} - -static ssize_t recvfrom_loop(int fd, void *buf, size_t buf_len, int flags, struct sockaddr *sa, socklen_t *sa_len) { - - for (;;) { - ssize_t l; - - l = recvfrom(fd, buf, buf_len, flags, sa, sa_len); - if (l >= 0) - return l; - - if (errno != EINTR) - return -errno; - } -} - -static int add_adresses(int fd, int if_loopback, unsigned *requests) { - union { - struct sockaddr sa; - struct sockaddr_nl nl; - } sa; - union { - struct nlmsghdr header; - uint8_t buf[NLMSG_ALIGN(sizeof(struct nlmsghdr)) + - NLMSG_ALIGN(sizeof(struct ifaddrmsg)) + - RTA_LENGTH(sizeof(struct in6_addr))]; - } request; - - struct ifaddrmsg *ifaddrmsg; - uint32_t ipv4_address = htonl(INADDR_LOOPBACK); - int r; - - zero(request); - - request.header.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg)); - request.header.nlmsg_type = RTM_NEWADDR; - request.header.nlmsg_flags = NLM_F_REQUEST|NLM_F_CREATE|NLM_F_ACK; - request.header.nlmsg_seq = *requests + 1; - - ifaddrmsg = NLMSG_DATA(&request.header); - ifaddrmsg->ifa_family = AF_INET; - ifaddrmsg->ifa_prefixlen = 8; - ifaddrmsg->ifa_flags = IFA_F_PERMANENT; - ifaddrmsg->ifa_scope = RT_SCOPE_HOST; - ifaddrmsg->ifa_index = if_loopback; - - r = add_rtattr(&request.header, sizeof(request), IFA_LOCAL, &ipv4_address, sizeof(ipv4_address)); - if (r < 0) - return r; - - zero(sa); - sa.nl.nl_family = AF_NETLINK; - - if (sendto_loop(fd, &request, request.header.nlmsg_len, 0, &sa.sa, sizeof(sa)) < 0) - return -errno; - (*requests)++; - - if (!socket_ipv6_is_supported()) - return 0; - - request.header.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg)); - request.header.nlmsg_seq = *requests + 1; - - ifaddrmsg->ifa_family = AF_INET6; - ifaddrmsg->ifa_prefixlen = 128; - - r = add_rtattr(&request.header, sizeof(request), IFA_LOCAL, &in6addr_loopback, sizeof(in6addr_loopback)); - if (r < 0) - return r; - - if (sendto_loop(fd, &request, request.header.nlmsg_len, 0, &sa.sa, sizeof(sa)) < 0) - return -errno; - (*requests)++; - - return 0; -} - -static int start_interface(int fd, int if_loopback, unsigned *requests) { - union { - struct sockaddr sa; - struct sockaddr_nl nl; - } sa; - union { - struct nlmsghdr header; - uint8_t buf[NLMSG_ALIGN(sizeof(struct nlmsghdr)) + - NLMSG_ALIGN(sizeof(struct ifinfomsg))]; - } request; - - struct ifinfomsg *ifinfomsg; - - zero(request); - - request.header.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); - request.header.nlmsg_type = RTM_NEWLINK; - request.header.nlmsg_flags = NLM_F_REQUEST|NLM_F_ACK; - request.header.nlmsg_seq = *requests + 1; - - ifinfomsg = NLMSG_DATA(&request.header); - ifinfomsg->ifi_family = AF_UNSPEC; - ifinfomsg->ifi_index = if_loopback; - ifinfomsg->ifi_flags = IFF_UP; - ifinfomsg->ifi_change = IFF_UP; - - zero(sa); - sa.nl.nl_family = AF_NETLINK; - - if (sendto_loop(fd, &request, request.header.nlmsg_len, 0, &sa.sa, sizeof(sa)) < 0) - return -errno; - - (*requests)++; - - return 0; -} - -static int read_response(int fd, unsigned requests_max) { - union { - struct sockaddr sa; - struct sockaddr_nl nl; - } sa; - union { - struct nlmsghdr header; - uint8_t buf[NLMSG_ALIGN(sizeof(struct nlmsghdr)) + - NLMSG_ALIGN(sizeof(struct nlmsgerr))]; - } response; - - ssize_t l; - socklen_t sa_len = sizeof(sa); - struct nlmsgerr *nlmsgerr; - - l = recvfrom_loop(fd, &response, sizeof(response), 0, &sa.sa, &sa_len); - if (l < 0) - return -errno; - - if (sa_len != sizeof(sa.nl) || - sa.nl.nl_family != AF_NETLINK) - return -EIO; - - if (sa.nl.nl_pid != 0) - return 0; - - if ((size_t) l < sizeof(struct nlmsghdr)) - return -EIO; - - if (response.header.nlmsg_type != NLMSG_ERROR || - (pid_t) response.header.nlmsg_pid != getpid() || - response.header.nlmsg_seq >= requests_max) - return 0; - - if ((size_t) l < NLMSG_LENGTH(sizeof(struct nlmsgerr)) || - response.header.nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr))) - return -EIO; - - nlmsgerr = NLMSG_DATA(&response.header); - - if (nlmsgerr->error < 0 && nlmsgerr->error != -EEXIST) - return nlmsgerr->error; - - return response.header.nlmsg_seq; -} - -static int check_loopback(void) { - int r, fd; - union { - struct sockaddr sa; - struct sockaddr_in in; - } sa; - - /* If we failed to set up the loop back device, check whether - * it might already be set up */ - - fd = socket(AF_INET, SOCK_DGRAM|SOCK_NONBLOCK|SOCK_CLOEXEC, 0); - if (fd < 0) - return -errno; - - zero(sa); - sa.in.sin_family = AF_INET; - sa.in.sin_addr.s_addr = INADDR_LOOPBACK; - - if (bind(fd, &sa.sa, sizeof(sa.in)) >= 0) - r = 1; - else - r = errno == EADDRNOTAVAIL ? 0 : -errno; - - close_nointr_nofail(fd); - - return r; -} - -int loopback_setup(void) { - int r, if_loopback; - union { - struct sockaddr sa; - struct sockaddr_nl nl; - } sa; - unsigned requests = 0, i; - int fd; - bool eperm = false; - - errno = 0; - if_loopback = (int) if_nametoindex("lo"); - if (if_loopback <= 0) - return errno ? -errno : -ENODEV; - - fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); - if (fd < 0) - return -errno; - - zero(sa); - sa.nl.nl_family = AF_NETLINK; - if (bind(fd, &sa.sa, sizeof(sa)) < 0) { - r = -errno; - goto finish; - } - - r = add_adresses(fd, if_loopback, &requests); - if (r < 0) - goto finish; - - r = start_interface(fd, if_loopback, &requests); - if (r < 0) - goto finish; - - for (i = 0; i < requests; i++) { - r = read_response(fd, requests); - - if (r == -EPERM) - eperm = true; - else if (r < 0) - goto finish; - } - - if (eperm && check_loopback() < 0) { - r = -EPERM; - goto finish; - } - - r = 0; - -finish: - if (r < 0) - log_warning("Failed to configure loopback device: %s", strerror(-r)); - - if (fd >= 0) - close_nointr_nofail(fd); - - return r; -} diff --git a/src/core/loopback-setup.h b/src/core/loopback-setup.h deleted file mode 100644 index dd83cf13a7..0000000000 --- a/src/core/loopback-setup.h +++ /dev/null @@ -1,24 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -#pragma once - -/*** - 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/>. -***/ - -int loopback_setup(void); diff --git a/src/core/machine-id-setup.c b/src/core/machine-id-setup.c deleted file mode 100644 index 7f4c23b130..0000000000 --- a/src/core/machine-id-setup.c +++ /dev/null @@ -1,246 +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 <unistd.h> -#include <stdio.h> -#include <errno.h> -#include <string.h> -#include <stdlib.h> -#include <fcntl.h> -#include <sys/mount.h> - -#include <systemd/sd-id128.h> - -#include "machine-id-setup.h" -#include "macro.h" -#include "util.h" -#include "mkdir.h" -#include "log.h" -#include "virt.h" - -static int shorten_uuid(char destination[36], const char *source) { - unsigned i, j; - - for (i = 0, j = 0; i < 36 && j < 32; i++) { - int t; - - t = unhexchar(source[i]); - if (t < 0) - continue; - - destination[j++] = hexchar(t); - } - - if (i == 36 && j == 32) { - destination[32] = '\n'; - destination[33] = 0; - return 0; - } - - return -EINVAL; -} - -static int generate(char id[34]) { - int fd, r; - unsigned char *p; - sd_id128_t buf; - char *q; - ssize_t k; - const char *vm_id; - - assert(id); - - /* First, try reading the D-Bus machine id, unless it is a symlink */ - fd = open("/var/lib/dbus/machine-id", O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW); - if (fd >= 0) { - - k = loop_read(fd, id, 32, false); - close_nointr_nofail(fd); - - if (k >= 32) { - id[32] = '\n'; - id[33] = 0; - - log_info("Initializing machine ID from D-Bus machine ID."); - return 0; - } - } - - /* If that didn't work, see if we are running in qemu/kvm and a - * machine ID was passed in via -uuid on the qemu/kvm command - * line */ - - r = detect_vm(&vm_id); - if (r > 0 && streq(vm_id, "kvm")) { - char uuid[37]; - - fd = open("/sys/class/dmi/id/product_uuid", O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW); - if (fd >= 0) { - k = loop_read(fd, uuid, 36, false); - close_nointr_nofail(fd); - - if (k >= 36) { - r = shorten_uuid(id, uuid); - if (r >= 0) { - log_info("Initializing machine ID from KVM UUID."); - return 0; - } - } - } - } - - /* If that didn't work either, see if we are running in a - * container, and a machine ID was passed in via - * $container_uuid the way libvirt/LXC does it */ - r = detect_container(NULL); - if (r > 0) { - char *e; - - r = getenv_for_pid(1, "container_uuid", &e); - if (r > 0) { - if (strlen(e) >= 36) { - r = shorten_uuid(id, e); - if (r >= 0) { - log_info("Initializing machine ID from container UUID."); - free(e); - return 0; - } - } - - free(e); - } - } - - /* If that didn't work, generate a random machine id */ - r = sd_id128_randomize(&buf); - if (r < 0) { - log_error("Failed to open /dev/urandom: %s", strerror(-r)); - return r; - } - - for (p = buf.bytes, q = id; p < buf.bytes + sizeof(buf); p++, q += 2) { - q[0] = hexchar(*p >> 4); - q[1] = hexchar(*p & 15); - } - - id[32] = '\n'; - id[33] = 0; - - log_info("Initializing machine ID from random generator."); - - return 0; -} - -int machine_id_setup(void) { - int fd, r; - bool writable; - struct stat st; - char id[34]; /* 32 + \n + \0 */ - mode_t m; - - m = umask(0000); - - /* We create this 0444, to indicate that this isn't really - * something you should ever modify. Of course, since the file - * will be owned by root it doesn't matter much, but maybe - * people look. */ - - fd = open("/etc/machine-id", O_RDWR|O_CREAT|O_CLOEXEC|O_NOCTTY, 0444); - if (fd >= 0) - writable = true; - else { - fd = open("/etc/machine-id", O_RDONLY|O_CLOEXEC|O_NOCTTY); - if (fd < 0) { - umask(m); - log_error("Cannot open /etc/machine-id: %m"); - return -errno; - } - - writable = false; - } - - umask(m); - - if (fstat(fd, &st) < 0) { - log_error("fstat() failed: %m"); - r = -errno; - goto finish; - } - - if (S_ISREG(st.st_mode)) { - if (loop_read(fd, id, 32, false) >= 32) { - r = 0; - goto finish; - } - } - - /* Hmm, so, the id currently stored is not useful, then let's - * generate one */ - - r = generate(id); - if (r < 0) - goto finish; - - if (S_ISREG(st.st_mode) && writable) { - lseek(fd, 0, SEEK_SET); - - if (loop_write(fd, id, 33, false) == 33) { - r = 0; - goto finish; - } - } - - close_nointr_nofail(fd); - fd = -1; - - /* Hmm, we couldn't write it? So let's write it to - * /run/machine-id as a replacement */ - - m = umask(0022); - r = write_one_line_file("/run/machine-id", id); - umask(m); - - if (r < 0) { - log_error("Cannot write /run/machine-id: %s", strerror(-r)); - - unlink("/run/machine-id"); - goto finish; - } - - /* And now, let's mount it over */ - 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 { - 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) - close_nointr_nofail(fd); - - return r; -} diff --git a/src/core/machine-id-setup.h b/src/core/machine-id-setup.h deleted file mode 100644 index b9e6b4d674..0000000000 --- a/src/core/machine-id-setup.h +++ /dev/null @@ -1,24 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -#pragma once - -/*** - 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/>. -***/ - -int machine_id_setup(void); diff --git a/src/core/macros.systemd.in b/src/core/macros.systemd.in deleted file mode 100644 index e521df337d..0000000000 --- a/src/core/macros.systemd.in +++ /dev/null @@ -1,56 +0,0 @@ -# -*- Mode: makefile; indent-tabs-mode: t -*- */ -# -# 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/>. - -# RPM macros for packages installing systemd unit files - -%_unitdir @systemunitdir@ -%_presetdir @systempresetdir@ - -%systemd_requires \ -Requires(post): systemd \ -Requires(preun): systemd \ -Requires(postun): systemd \ -%{nil} - -%systemd_post() \ -if [ $1 -eq 1 ] ; then \ - # Initial installation \ - @rootbindir@/systemctl preset %{?*} >/dev/null 2>&1 || : \ -fi \ -%{nil} - -%systemd_preun() \ -if [ $1 -eq 0 ] ; then \ - # Package removal, not upgrade \ - @rootbindir@/systemctl --no-reload disable %{?*} > /dev/null 2>&1 || : \ - @rootbindir@/systemctl stop %{?*} > /dev/null 2>&1 || : \ -fi \ -%{nil} - -%systemd_postun() \ -@rootbindir@/systemctl daemon-reload >/dev/null 2>&1 || : \ -%{nil} - -%systemd_postun_with_restart() \ -@rootbindir@/systemctl daemon-reload >/dev/null 2>&1 || : \ -if [ $1 -ge 1 ] ; then \ - # Package upgrade, not uninstall \ - @rootbindir@/systemctl try-restart %{?*} >/dev/null 2>&1 || : \ -fi \ -%{nil} diff --git a/src/core/main.c b/src/core/main.c deleted file mode 100644 index 4da8ecb864..0000000000 --- a/src/core/main.c +++ /dev/null @@ -1,1950 +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 <dbus/dbus.h> - -#include <stdio.h> -#include <errno.h> -#include <string.h> -#include <unistd.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <getopt.h> -#include <signal.h> -#include <sys/wait.h> -#include <fcntl.h> -#include <sys/prctl.h> -#include <sys/mount.h> - -#include "manager.h" -#include "log.h" -#include "load-fragment.h" -#include "fdset.h" -#include "special.h" -#include "conf-parser.h" -#include "bus-errors.h" -#include "missing.h" -#include "label.h" -#include "build.h" -#include "strv.h" -#include "def.h" -#include "virt.h" -#include "watchdog.h" -#include "path-util.h" -#include "switch-root.h" -#include "capability.h" -#include "killall.h" - -#include "mount-setup.h" -#include "loopback-setup.h" -#include "kmod-setup.h" -#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, - ACTION_HELP, - ACTION_VERSION, - ACTION_TEST, - ACTION_DUMP_CONFIGURATION_ITEMS, - ACTION_DONE -} arg_action = ACTION_RUN; - -static char *arg_default_unit = NULL; -static SystemdRunningAs arg_running_as = _SYSTEMD_RUNNING_AS_INVALID; - -static bool arg_dump_core = true; -static bool arg_crash_shell = false; -static int arg_crash_chvt = -1; -static bool arg_confirm_spawn = false; -static bool arg_show_status = true; -static bool arg_switched_root = false; -static char **arg_default_controllers = NULL; -static char ***arg_join_controllers = NULL; -static ExecOutput arg_default_std_output = EXEC_OUTPUT_JOURNAL; -static ExecOutput arg_default_std_error = EXEC_OUTPUT_INHERIT; -static usec_t arg_runtime_watchdog = 0; -static usec_t arg_shutdown_watchdog = 10 * USEC_PER_MINUTE; -static struct rlimit *arg_default_rlimit[RLIMIT_NLIMITS] = {}; -static uint64_t arg_capability_bounding_set_drop = 0; -static nsec_t arg_timer_slack_nsec = (nsec_t) -1; - -static FILE* serialization = NULL; - -static void nop_handler(int sig) { -} - -_noreturn_ static void crash(int sig) { - - if (!arg_dump_core) - log_error("Caught <%s>, not dumping core.", signal_to_string(sig)); - else { - struct sigaction sa; - pid_t pid; - - /* We want to wait for the core process, hence let's enable SIGCHLD */ - zero(sa); - sa.sa_handler = nop_handler; - sa.sa_flags = SA_NOCLDSTOP|SA_RESTART; - assert_se(sigaction(SIGCHLD, &sa, NULL) == 0); - - if ((pid = fork()) < 0) - log_error("Caught <%s>, cannot fork for core dump: %s", signal_to_string(sig), strerror(errno)); - - else if (pid == 0) { - struct rlimit rl; - - /* Enable default signal handler for core dump */ - zero(sa); - sa.sa_handler = SIG_DFL; - assert_se(sigaction(sig, &sa, NULL) == 0); - - /* Don't limit the core dump size */ - zero(rl); - rl.rlim_cur = RLIM_INFINITY; - rl.rlim_max = RLIM_INFINITY; - setrlimit(RLIMIT_CORE, &rl); - - /* Just to be sure... */ - assert_se(chdir("/") == 0); - - /* Raise the signal again */ - raise(sig); - - assert_not_reached("We shouldn't be here..."); - _exit(1); - - } else { - siginfo_t status; - int r; - - /* Order things nicely. */ - if ((r = wait_for_terminate(pid, &status)) < 0) - log_error("Caught <%s>, waitpid() failed: %s", signal_to_string(sig), strerror(-r)); - else if (status.si_code != CLD_DUMPED) - log_error("Caught <%s>, core dump failed.", signal_to_string(sig)); - else - log_error("Caught <%s>, dumped core as pid %lu.", signal_to_string(sig), (unsigned long) pid); - } - } - - if (arg_crash_chvt) - chvt(arg_crash_chvt); - - if (arg_crash_shell) { - struct sigaction sa; - pid_t pid; - - log_info("Executing crash shell in 10s..."); - sleep(10); - - /* Let the kernel reap children for us */ - zero(sa); - sa.sa_handler = SIG_IGN; - sa.sa_flags = SA_NOCLDSTOP|SA_NOCLDWAIT|SA_RESTART; - assert_se(sigaction(SIGCHLD, &sa, NULL) == 0); - - pid = fork(); - if (pid < 0) - 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: %m"); - _exit(1); - } - - log_info("Successfully spawned crash shell as pid %lu.", (unsigned long) pid); - } - - log_info("Freezing execution."); - freeze(); -} - -static void install_crash_handler(void) { - struct sigaction sa; - - zero(sa); - - sa.sa_handler = crash; - sa.sa_flags = SA_NODEFER; - - sigaction_many(&sa, SIGNALS_CRASH_HANDLER, -1); -} - -static int console_setup(bool do_reset) { - int tty_fd, r; - - /* If we are init, we connect stdin/stdout/stderr to /dev/null - * and make sure we don't have a controlling tty. */ - - release_terminal(); - - if (!do_reset) - return 0; - - tty_fd = open_terminal("/dev/console", O_WRONLY|O_NOCTTY|O_CLOEXEC); - if (tty_fd < 0) { - log_error("Failed to open /dev/console: %s", strerror(-tty_fd)); - return -tty_fd; - } - - /* We don't want to force text mode. - * plymouth may be showing pictures already from initrd. */ - r = reset_terminal_fd(tty_fd, false); - if (r < 0) - log_error("Failed to reset /dev/console: %s", strerror(-r)); - - close_nointr_nofail(tty_fd); - return r; -} - -static int set_default_unit(const char *u) { - char *c; - - assert(u); - - c = strdup(u); - if (!c) - return -ENOMEM; - - free(arg_default_unit); - arg_default_unit = c; - - return 0; -} - -static int parse_proc_cmdline_word(const char *word) { - - static const char * const rlmap[] = { - "emergency", SPECIAL_EMERGENCY_TARGET, - "-b", SPECIAL_EMERGENCY_TARGET, - "single", SPECIAL_RESCUE_TARGET, - "-s", SPECIAL_RESCUE_TARGET, - "s", SPECIAL_RESCUE_TARGET, - "S", SPECIAL_RESCUE_TARGET, - "1", SPECIAL_RESCUE_TARGET, - "2", SPECIAL_RUNLEVEL2_TARGET, - "3", SPECIAL_RUNLEVEL3_TARGET, - "4", SPECIAL_RUNLEVEL4_TARGET, - "5", SPECIAL_RUNLEVEL5_TARGET, - }; - - assert(word); - - if (startswith(word, "systemd.unit=")) { - - if (!in_initrd()) - return set_default_unit(word + 13); - - } else if (startswith(word, "rd.systemd.unit=")) { - - if (in_initrd()) - return set_default_unit(word + 16); - - } else if (startswith(word, "systemd.log_target=")) { - - if (log_set_target_from_string(word + 19) < 0) - log_warning("Failed to parse log target %s. Ignoring.", word + 19); - - } else if (startswith(word, "systemd.log_level=")) { - - if (log_set_max_level_from_string(word + 18) < 0) - log_warning("Failed to parse log level %s. Ignoring.", word + 18); - - } else if (startswith(word, "systemd.log_color=")) { - - if (log_show_color_from_string(word + 18) < 0) - log_warning("Failed to parse log color setting %s. Ignoring.", word + 18); - - } else if (startswith(word, "systemd.log_location=")) { - - if (log_show_location_from_string(word + 21) < 0) - log_warning("Failed to parse log location setting %s. Ignoring.", word + 21); - - } else if (startswith(word, "systemd.dump_core=")) { - int r; - - if ((r = parse_boolean(word + 18)) < 0) - log_warning("Failed to parse dump core switch %s. Ignoring.", word + 18); - else - arg_dump_core = r; - - } else if (startswith(word, "systemd.crash_shell=")) { - int r; - - if ((r = parse_boolean(word + 20)) < 0) - log_warning("Failed to parse crash shell switch %s. Ignoring.", word + 20); - else - arg_crash_shell = r; - - } else if (startswith(word, "systemd.confirm_spawn=")) { - int r; - - if ((r = parse_boolean(word + 22)) < 0) - log_warning("Failed to parse confirm spawn switch %s. Ignoring.", word + 22); - else - arg_confirm_spawn = r; - - } else if (startswith(word, "systemd.crash_chvt=")) { - int k; - - if (safe_atoi(word + 19, &k) < 0) - log_warning("Failed to parse crash chvt switch %s. Ignoring.", word + 19); - else - arg_crash_chvt = k; - - } else if (startswith(word, "systemd.show_status=")) { - int r; - - if ((r = parse_boolean(word + 20)) < 0) - log_warning("Failed to parse show status switch %s. Ignoring.", word + 20); - else - arg_show_status = r; - } else if (startswith(word, "systemd.default_standard_output=")) { - int r; - - if ((r = exec_output_from_string(word + 32)) < 0) - log_warning("Failed to parse default standard output switch %s. Ignoring.", word + 32); - else - arg_default_std_output = r; - } else if (startswith(word, "systemd.default_standard_error=")) { - int r; - - if ((r = exec_output_from_string(word + 31)) < 0) - log_warning("Failed to parse default standard error switch %s. Ignoring.", word + 31); - else - arg_default_std_error = r; - } else if (startswith(word, "systemd.setenv=")) { - char *cenv, *eq; - int r; - - cenv = strdup(word + 15); - if (!cenv) - return -ENOMEM; - - eq = strchr(cenv, '='); - if (!eq) { - r = unsetenv(cenv); - if (r < 0) - log_warning("unsetenv failed %m. Ignoring."); - } else { - *eq = 0; - r = setenv(cenv, eq + 1, 1); - if (r < 0) - log_warning("setenv failed %m. Ignoring."); - } - free(cenv); - - } else if (startswith(word, "systemd.") || - (in_initrd() && startswith(word, "rd.systemd."))) { - - log_warning("Unknown kernel switch %s. Ignoring.", word); - - log_info("Supported kernel switches:\n" - "systemd.unit=UNIT Default unit to start\n" - "rd.systemd.unit=UNIT Default unit to start when run in initrd\n" - "systemd.dump_core=0|1 Dump core on crash\n" - "systemd.crash_shell=0|1 Run shell on crash\n" - "systemd.crash_chvt=N Change to VT #N on crash\n" - "systemd.confirm_spawn=0|1 Confirm every process spawn\n" - "systemd.show_status=0|1 Show status updates on the console during bootup\n" - "systemd.log_target=console|kmsg|journal|journal-or-kmsg|syslog|syslog-or-kmsg|null\n" - " Log target\n" - "systemd.log_level=LEVEL Log level\n" - "systemd.log_color=0|1 Highlight important log messages\n" - "systemd.log_location=0|1 Include code location in log messages\n" - "systemd.default_standard_output=null|tty|syslog|syslog+console|kmsg|kmsg+console|journal|journal+console\n" - " Set default log output for services\n" - "systemd.default_standard_error=null|tty|syslog|syslog+console|kmsg|kmsg+console|journal|journal+console\n" - " Set default log error output for services\n" - "systemd.setenv=ASSIGNMENT Set an environment variable for all spawned processes\n"); - - } else if (streq(word, "quiet")) - arg_show_status = false; - else if (!in_initrd()) { - unsigned i; - - /* SysV compatibility */ - for (i = 0; i < ELEMENTSOF(rlmap); i += 2) - if (streq(word, rlmap[i])) - return set_default_unit(rlmap[i+1]); - } - - return 0; -} - -static int config_parse_level2( - const char *filename, - unsigned line, - const char *section, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - assert(filename); - assert(lvalue); - assert(rvalue); - - log_set_max_level_from_string(rvalue); - return 0; -} - -static int config_parse_target( - const char *filename, - unsigned line, - const char *section, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - assert(filename); - assert(lvalue); - assert(rvalue); - - log_set_target_from_string(rvalue); - return 0; -} - -static int config_parse_color( - const char *filename, - unsigned line, - const char *section, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - assert(filename); - assert(lvalue); - assert(rvalue); - - log_show_color_from_string(rvalue); - return 0; -} - -static int config_parse_location( - const char *filename, - unsigned line, - const char *section, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - assert(filename); - assert(lvalue); - assert(rvalue); - - log_show_location_from_string(rvalue); - return 0; -} - -static int config_parse_cpu_affinity2( - const char *filename, - unsigned line, - const char *section, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - char *w; - size_t l; - char *state; - cpu_set_t *c = NULL; - unsigned ncpus = 0; - - assert(filename); - assert(lvalue); - assert(rvalue); - - FOREACH_WORD_QUOTED(w, l, rvalue, state) { - char *t; - int r; - unsigned cpu; - - if (!(t = strndup(w, l))) - return log_oom(); - - r = safe_atou(t, &cpu); - free(t); - - if (!c) - if (!(c = cpu_set_malloc(&ncpus))) - return log_oom(); - - if (r < 0 || cpu >= ncpus) { - log_error("[%s:%u] Failed to parse CPU affinity: %s", filename, line, rvalue); - CPU_FREE(c); - return -EBADMSG; - } - - CPU_SET_S(cpu, CPU_ALLOC_SIZE(ncpus), c); - } - - if (c) { - if (sched_setaffinity(0, CPU_ALLOC_SIZE(ncpus), c) < 0) - log_warning("Failed to set CPU affinity: %m"); - - CPU_FREE(c); - } - - return 0; -} - -static void strv_free_free(char ***l) { - char ***i; - - if (!l) - return; - - for (i = l; *i; i++) - strv_free(*i); - - free(l); -} - -static void free_join_controllers(void) { - if (!arg_join_controllers) - return; - - strv_free_free(arg_join_controllers); - arg_join_controllers = NULL; -} - -static int config_parse_join_controllers( - const char *filename, - unsigned line, - const char *section, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - unsigned n = 0; - char *state, *w; - size_t length; - - assert(filename); - assert(lvalue); - assert(rvalue); - - free_join_controllers(); - - FOREACH_WORD_QUOTED(w, length, rvalue, state) { - char *s, **l; - - s = strndup(w, length); - if (!s) - return log_oom(); - - l = strv_split(s, ","); - free(s); - - strv_uniq(l); - - if (strv_length(l) <= 1) { - strv_free(l); - continue; - } - - if (!arg_join_controllers) { - arg_join_controllers = new(char**, 2); - if (!arg_join_controllers) { - strv_free(l); - return log_oom(); - } - - arg_join_controllers[0] = l; - arg_join_controllers[1] = NULL; - - n = 1; - } else { - char ***a; - char ***t; - - t = new0(char**, n+2); - if (!t) { - strv_free(l); - return log_oom(); - } - - n = 0; - - for (a = arg_join_controllers; *a; a++) { - - if (strv_overlap(*a, l)) { - char **c; - - c = strv_merge(*a, l); - if (!c) { - strv_free(l); - strv_free_free(t); - return log_oom(); - } - - strv_free(l); - l = c; - } else { - char **c; - - c = strv_copy(*a); - if (!c) { - strv_free(l); - strv_free_free(t); - return log_oom(); - } - - t[n++] = c; - } - } - - t[n++] = strv_uniq(l); - - strv_free_free(arg_join_controllers); - arg_join_controllers = t; - } - } - - return 0; -} - -static int parse_config_file(void) { - - const ConfigTableItem items[] = { - { "Manager", "LogLevel", config_parse_level2, 0, NULL }, - { "Manager", "LogTarget", config_parse_target, 0, NULL }, - { "Manager", "LogColor", config_parse_color, 0, NULL }, - { "Manager", "LogLocation", config_parse_location, 0, NULL }, - { "Manager", "DumpCore", config_parse_bool, 0, &arg_dump_core }, - { "Manager", "CrashShell", config_parse_bool, 0, &arg_crash_shell }, - { "Manager", "ShowStatus", config_parse_bool, 0, &arg_show_status }, - { "Manager", "CrashChVT", config_parse_int, 0, &arg_crash_chvt }, - { "Manager", "CPUAffinity", config_parse_cpu_affinity2, 0, NULL }, - { "Manager", "DefaultControllers", config_parse_strv, 0, &arg_default_controllers }, - { "Manager", "DefaultStandardOutput", config_parse_output, 0, &arg_default_std_output }, - { "Manager", "DefaultStandardError", config_parse_output, 0, &arg_default_std_error }, - { "Manager", "JoinControllers", config_parse_join_controllers, 0, &arg_join_controllers }, - { "Manager", "RuntimeWatchdogSec", config_parse_usec, 0, &arg_runtime_watchdog }, - { "Manager", "ShutdownWatchdogSec", config_parse_usec, 0, &arg_shutdown_watchdog }, - { "Manager", "CapabilityBoundingSet", config_parse_bounding_set, 0, &arg_capability_bounding_set_drop }, - { "Manager", "TimerSlackNSec", config_parse_nsec, 0, &arg_timer_slack_nsec }, - { "Manager", "DefaultLimitCPU", config_parse_limit, 0, &arg_default_rlimit[RLIMIT_CPU]}, - { "Manager", "DefaultLimitFSIZE", config_parse_limit, 0, &arg_default_rlimit[RLIMIT_FSIZE]}, - { "Manager", "DefaultLimitDATA", config_parse_limit, 0, &arg_default_rlimit[RLIMIT_DATA]}, - { "Manager", "DefaultLimitSTACK", config_parse_limit, 0, &arg_default_rlimit[RLIMIT_STACK]}, - { "Manager", "DefaultLimitCORE", config_parse_limit, 0, &arg_default_rlimit[RLIMIT_CORE]}, - { "Manager", "DefaultLimitRSS", config_parse_limit, 0, &arg_default_rlimit[RLIMIT_RSS]}, - { "Manager", "DefaultLimitNOFILE", config_parse_limit, 0, &arg_default_rlimit[RLIMIT_NOFILE]}, - { "Manager", "DefaultLimitAS", config_parse_limit, 0, &arg_default_rlimit[RLIMIT_AS]}, - { "Manager", "DefaultLimitNPROC", config_parse_limit, 0, &arg_default_rlimit[RLIMIT_NPROC]}, - { "Manager", "DefaultLimitMEMLOCK", config_parse_limit, 0, &arg_default_rlimit[RLIMIT_MEMLOCK]}, - { "Manager", "DefaultLimitLOCKS", config_parse_limit, 0, &arg_default_rlimit[RLIMIT_LOCKS]}, - { "Manager", "DefaultLimitSIGPENDING",config_parse_limit, 0, &arg_default_rlimit[RLIMIT_SIGPENDING]}, - { "Manager", "DefaultLimitMSGQUEUE", config_parse_limit, 0, &arg_default_rlimit[RLIMIT_MSGQUEUE]}, - { "Manager", "DefaultLimitNICE", config_parse_limit, 0, &arg_default_rlimit[RLIMIT_NICE]}, - { "Manager", "DefaultLimitRTPRIO", config_parse_limit, 0, &arg_default_rlimit[RLIMIT_RTPRIO]}, - { "Manager", "DefaultLimitRTTIME", config_parse_limit, 0, &arg_default_rlimit[RLIMIT_RTTIME]}, - { NULL, NULL, NULL, 0, NULL } - }; - - FILE *f; - const char *fn; - int r; - - fn = arg_running_as == SYSTEMD_SYSTEM ? SYSTEM_CONFIG_FILE : USER_CONFIG_FILE; - f = fopen(fn, "re"); - if (!f) { - if (errno == ENOENT) - return 0; - - log_warning("Failed to open configuration file '%s': %m", fn); - return 0; - } - - r = config_parse(fn, f, "Manager\0", config_item_table_lookup, (void*) items, false, NULL); - if (r < 0) - log_warning("Failed to parse configuration file: %s", strerror(-r)); - - fclose(f); - - return 0; -} - -static int parse_proc_cmdline(void) { - char *line, *w, *state; - int r; - size_t l; - - /* Don't read /proc/cmdline if we are in a container, since - * that is only relevant for the host system */ - if (detect_container(NULL) > 0) - return 0; - - if ((r = read_one_line_file("/proc/cmdline", &line)) < 0) { - log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r)); - return 0; - } - - FOREACH_WORD_QUOTED(w, l, line, state) { - char *word; - - if (!(word = strndup(w, l))) { - r = -ENOMEM; - goto finish; - } - - r = parse_proc_cmdline_word(word); - if (r < 0) { - log_error("Failed on cmdline argument %s: %s", word, strerror(-r)); - free(word); - goto finish; - } - - free(word); - } - - r = 0; - -finish: - free(line); - return r; -} - -static int parse_argv(int argc, char *argv[]) { - - enum { - ARG_LOG_LEVEL = 0x100, - ARG_LOG_TARGET, - ARG_LOG_COLOR, - ARG_LOG_LOCATION, - ARG_UNIT, - ARG_SYSTEM, - ARG_USER, - ARG_TEST, - ARG_VERSION, - ARG_DUMP_CONFIGURATION_ITEMS, - ARG_DUMP_CORE, - ARG_CRASH_SHELL, - ARG_CONFIRM_SPAWN, - ARG_SHOW_STATUS, - ARG_DESERIALIZE, - ARG_SWITCHED_ROOT, - ARG_INTROSPECT, - ARG_DEFAULT_STD_OUTPUT, - ARG_DEFAULT_STD_ERROR - }; - - static const struct option options[] = { - { "log-level", required_argument, NULL, ARG_LOG_LEVEL }, - { "log-target", required_argument, NULL, ARG_LOG_TARGET }, - { "log-color", optional_argument, NULL, ARG_LOG_COLOR }, - { "log-location", optional_argument, NULL, ARG_LOG_LOCATION }, - { "unit", required_argument, NULL, ARG_UNIT }, - { "system", no_argument, NULL, ARG_SYSTEM }, - { "user", no_argument, NULL, ARG_USER }, - { "test", no_argument, NULL, ARG_TEST }, - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, ARG_VERSION }, - { "dump-configuration-items", no_argument, NULL, ARG_DUMP_CONFIGURATION_ITEMS }, - { "dump-core", optional_argument, NULL, ARG_DUMP_CORE }, - { "crash-shell", optional_argument, NULL, ARG_CRASH_SHELL }, - { "confirm-spawn", optional_argument, NULL, ARG_CONFIRM_SPAWN }, - { "show-status", optional_argument, NULL, ARG_SHOW_STATUS }, - { "deserialize", required_argument, NULL, ARG_DESERIALIZE }, - { "switched-root", no_argument, NULL, ARG_SWITCHED_ROOT }, - { "introspect", optional_argument, NULL, ARG_INTROSPECT }, - { "default-standard-output", required_argument, NULL, ARG_DEFAULT_STD_OUTPUT, }, - { "default-standard-error", required_argument, NULL, ARG_DEFAULT_STD_ERROR, }, - { NULL, 0, NULL, 0 } - }; - - int c, r; - - assert(argc >= 1); - assert(argv); - - if (getpid() == 1) - opterr = 0; - - while ((c = getopt_long(argc, argv, "hDbsz:", options, NULL)) >= 0) - - switch (c) { - - case ARG_LOG_LEVEL: - if ((r = log_set_max_level_from_string(optarg)) < 0) { - log_error("Failed to parse log level %s.", optarg); - return r; - } - - break; - - case ARG_LOG_TARGET: - - if ((r = log_set_target_from_string(optarg)) < 0) { - log_error("Failed to parse log target %s.", optarg); - return r; - } - - break; - - case ARG_LOG_COLOR: - - if (optarg) { - if ((r = log_show_color_from_string(optarg)) < 0) { - log_error("Failed to parse log color setting %s.", optarg); - return r; - } - } else - log_show_color(true); - - break; - - case ARG_LOG_LOCATION: - - if (optarg) { - if ((r = log_show_location_from_string(optarg)) < 0) { - log_error("Failed to parse log location setting %s.", optarg); - return r; - } - } else - log_show_location(true); - - break; - - case ARG_DEFAULT_STD_OUTPUT: - - if ((r = exec_output_from_string(optarg)) < 0) { - log_error("Failed to parse default standard output setting %s.", optarg); - return r; - } else - arg_default_std_output = r; - break; - - case ARG_DEFAULT_STD_ERROR: - - if ((r = exec_output_from_string(optarg)) < 0) { - log_error("Failed to parse default standard error output setting %s.", optarg); - return r; - } else - arg_default_std_error = r; - break; - - case ARG_UNIT: - - if ((r = set_default_unit(optarg)) < 0) { - log_error("Failed to set default unit %s: %s", optarg, strerror(-r)); - return r; - } - - break; - - case ARG_SYSTEM: - arg_running_as = SYSTEMD_SYSTEM; - break; - - case ARG_USER: - arg_running_as = SYSTEMD_USER; - break; - - case ARG_TEST: - arg_action = ACTION_TEST; - break; - - case ARG_VERSION: - arg_action = ACTION_VERSION; - break; - - case ARG_DUMP_CONFIGURATION_ITEMS: - arg_action = ACTION_DUMP_CONFIGURATION_ITEMS; - break; - - case ARG_DUMP_CORE: - r = optarg ? parse_boolean(optarg) : 1; - if (r < 0) { - log_error("Failed to parse dump core boolean %s.", optarg); - return r; - } - arg_dump_core = r; - break; - - case ARG_CRASH_SHELL: - r = optarg ? parse_boolean(optarg) : 1; - if (r < 0) { - log_error("Failed to parse crash shell boolean %s.", optarg); - return r; - } - arg_crash_shell = r; - break; - - case ARG_CONFIRM_SPAWN: - r = optarg ? parse_boolean(optarg) : 1; - if (r < 0) { - log_error("Failed to parse confirm spawn boolean %s.", optarg); - return r; - } - arg_confirm_spawn = r; - break; - - case ARG_SHOW_STATUS: - r = optarg ? parse_boolean(optarg) : 1; - if (r < 0) { - log_error("Failed to parse show status boolean %s.", optarg); - return r; - } - arg_show_status = r; - break; - - case ARG_DESERIALIZE: { - int fd; - FILE *f; - - if ((r = safe_atoi(optarg, &fd)) < 0 || fd < 0) { - log_error("Failed to parse deserialize option %s.", optarg); - return r; - } - - if (!(f = fdopen(fd, "r"))) { - log_error("Failed to open serialization fd: %m"); - return r; - } - - if (serialization) - fclose(serialization); - - serialization = f; - - break; - } - - case ARG_SWITCHED_ROOT: - arg_switched_root = true; - break; - - case ARG_INTROSPECT: { - const char * const * i = NULL; - - for (i = bus_interface_table; *i; i += 2) - if (!optarg || streq(i[0], optarg)) { - fputs(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE - "<node>\n", stdout); - fputs(i[1], stdout); - fputs("</node>\n", stdout); - - if (optarg) - break; - } - - if (!i[0] && optarg) - log_error("Unknown interface %s.", optarg); - - arg_action = ACTION_DONE; - break; - } - - case 'h': - arg_action = ACTION_HELP; - break; - - case 'D': - log_set_max_level(LOG_DEBUG); - break; - - case 'b': - case 's': - case 'z': - /* Just to eat away the sysvinit kernel - * cmdline args without getopt() error - * messages that we'll parse in - * parse_proc_cmdline_word() or ignore. */ - - case '?': - default: - if (getpid() != 1) { - log_error("Unknown option code %c", c); - return -EINVAL; - } - - break; - } - - if (optind < argc && getpid() != 1) { - /* Hmm, when we aren't run as init system - * let's complain about excess arguments */ - - log_error("Excess arguments."); - return -EINVAL; - } - - if (detect_container(NULL) > 0) { - char **a; - - /* All /proc/cmdline arguments the kernel didn't - * understand it passed to us. We're not really - * interested in that usually since /proc/cmdline is - * more interesting and complete. With one exception: - * if we are run in a container /proc/cmdline is not - * relevant for the container, hence we rely on argv[] - * instead. */ - - for (a = argv; a < argv + argc; a++) - if ((r = parse_proc_cmdline_word(*a)) < 0) { - log_error("Failed on cmdline argument %s: %s", *a, strerror(-r)); - return r; - } - } - - return 0; -} - -static int help(void) { - - printf("%s [OPTIONS...]\n\n" - "Starts up and maintains the system or user services.\n\n" - " -h --help Show this help\n" - " --test Determine startup sequence, dump it and exit\n" - " --dump-configuration-items Dump understood unit configuration items\n" - " --introspect[=INTERFACE] Extract D-Bus interface data\n" - " --unit=UNIT Set default unit\n" - " --system Run a system instance, even if PID != 1\n" - " --user Run a user instance\n" - " --dump-core[=0|1] Dump core on crash\n" - " --crash-shell[=0|1] Run shell on crash\n" - " --confirm-spawn[=0|1] Ask for confirmation when spawning processes\n" - " --show-status[=0|1] Show status updates on the console during bootup\n" - " --log-target=TARGET Set log target (console, journal, syslog, kmsg, journal-or-kmsg, syslog-or-kmsg, null)\n" - " --log-level=LEVEL Set log level (debug, info, notice, warning, err, crit, alert, emerg)\n" - " --log-color[=0|1] Highlight important log messages\n" - " --log-location[=0|1] Include code location in log messages\n" - " --default-standard-output= Set default standard output for services\n" - " --default-standard-error= Set default standard error output for services\n", - program_invocation_short_name); - - return 0; -} - -static int version(void) { - puts(PACKAGE_STRING); - puts(DISTRIBUTION); - puts(SYSTEMD_FEATURES); - - return 0; -} - -static int prepare_reexecute(Manager *m, FILE **_f, FDSet **_fds, bool serialize_jobs) { - FILE *f = NULL; - FDSet *fds = NULL; - int r; - - assert(m); - assert(_f); - assert(_fds); - - /* Make sure nothing is really destructed when we shut down */ - m->n_reloading ++; - - r = manager_open_serialization(m, &f); - if (r < 0) { - log_error("Failed to create serialization file: %s", strerror(-r)); - goto fail; - } - - fds = fdset_new(); - if (!fds) { - r = -ENOMEM; - log_error("Failed to allocate fd set: %s", strerror(-r)); - goto fail; - } - - r = manager_serialize(m, f, fds, serialize_jobs); - if (r < 0) { - log_error("Failed to serialize state: %s", strerror(-r)); - goto fail; - } - - if (fseeko(f, 0, SEEK_SET) < 0) { - log_error("Failed to rewind serialization fd: %m"); - goto fail; - } - - r = fd_cloexec(fileno(f), false); - if (r < 0) { - log_error("Failed to disable O_CLOEXEC for serialization: %s", strerror(-r)); - goto fail; - } - - r = fdset_cloexec(fds, false); - if (r < 0) { - log_error("Failed to disable O_CLOEXEC for serialization fds: %s", strerror(-r)); - goto fail; - } - - *_f = f; - *_fds = fds; - - return 0; - -fail: - fdset_free(fds); - - if (f) - fclose(f); - - 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; - - assert(t); - - e = getenv("RD_TIMESTAMP"); - if (!e) - return NULL; - - if (sscanf(e, "%llu %llu", &a, &b) != 2) - return NULL; - - t->realtime = (usec_t) a; - t->monotonic = (usec_t) b; - - return t; -} - -static void test_mtab(void) { - char *p; - - /* Check that /etc/mtab is a symlink */ - - if (readlink_malloc("/etc/mtab", &p) >= 0) { - bool b; - - b = streq(p, "/proc/self/mounts") || streq(p, "/proc/mounts"); - free(p); - - if (b) - return; - } - - log_warning("/etc/mtab is not a symlink or not pointing to /proc/self/mounts. " - "This is not supported anymore. " - "Please make sure to replace this file by a symlink to avoid incorrect or misleading mount(8) output."); -} - -static void test_usr(void) { - - /* Check that /usr is not a separate fs */ - - if (dir_is_empty("/usr") <= 0) - return; - - log_warning("/usr appears to be on its own filesytem and is not already mounted. This is not a supported setup. " - "Some things will probably break (sometimes even silently) in mysterious ways. " - "Consult http://freedesktop.org/wiki/Software/systemd/separate-usr-is-broken for more information."); -} - -static void test_cgroups(void) { - - if (access("/proc/cgroups", F_OK) >= 0) - return; - - log_warning("CONFIG_CGROUPS was not set when your kernel was compiled. " - "Systems without control groups are not supported. " - "We will now sleep for 10s, and then continue boot-up. " - "Expect breakage and please do not file bugs. " - "Instead fix your kernel and enable CONFIG_CGROUPS. " - "Consult http://0pointer.de/blog/projects/cgroups-vs-cgroups.html for more information."); - - 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; - usec_t before_startup, after_startup; - char timespan[FORMAT_TIMESPAN_MAX]; - FDSet *fds = NULL; - bool reexecute = false; - const char *shutdown_verb = NULL; - dual_timestamp initrd_timestamp = { 0ULL, 0ULL }; - static char systemd[] = "systemd"; - bool skip_setup = false; - int j; - bool loaded_policy = false; - 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")) { - /* This is compatibility support for SysV, where - * calling init as a user is identical to telinit. */ - - errno = -ENOENT; - execv(SYSTEMCTL_BINARY_PATH, argv); - log_error("Failed to exec " SYSTEMCTL_BINARY_PATH ": %m"); - return 1; - } -#endif - - /* Determine if this is a reexecution or normal bootup. We do - * the full command line parsing much later, so let's just - * have a quick peek here. */ - for (j = 1; j < argc; j++) - if (streq(argv[j], "--deserialize")) { - skip_setup = true; - break; - } - - /* If we have switched root, do all the special setup - * things */ - for (j = 1; j < argc; j++) - if (streq(argv[j], "--switched-root")) { - skip_setup = false; - break; - } - - /* If we get started via the /sbin/init symlink then we are - called 'init'. After a subsequent reexecution we are then - called 'systemd'. That is confusing, hence let's call us - systemd right-away. */ - program_invocation_short_name = systemd; - prctl(PR_SET_NAME, systemd); - - saved_argv = argv; - saved_argc = argc; - - log_show_color(isatty(STDERR_FILENO) > 0); - - 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; - - dual_timestamp_get(&initrd_timestamp); - asprintf(&rd_timestamp, "%llu %llu", - (unsigned long long) initrd_timestamp.realtime, - (unsigned long long) initrd_timestamp.monotonic); - if (rd_timestamp) { - setenv("RD_TIMESTAMP", rd_timestamp, 1); - free(rd_timestamp); - } - } - - if (!skip_setup) { - if (selinux_setup(&loaded_policy) < 0) - goto finish; - if (ima_setup() < 0) - goto finish; - } - - if (label_init(NULL) < 0) - goto finish; - - if (!skip_setup) { - if (hwclock_is_localtime() > 0) { - int 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 { - - /* Running as user instance */ - arg_running_as = SYSTEMD_USER; - log_set_target(LOG_TARGET_AUTO); - log_open(); - } - - /* Initialize default unit */ - 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; - } - - r = initialize_join_controllers(); - if (r < 0) - goto finish; - - /* Mount /proc, /sys and friends, so that /proc/cmdline and - * /proc/$PID/fd is available. */ - if (geteuid() == 0 && !getenv("SYSTEMD_SKIP_API_MOUNTS")) { - r = mount_setup(loaded_policy); - if (r < 0) - goto finish; - } - - /* Reset all signal handlers. */ - assert_se(reset_all_signal_handlers() == 0); - - /* If we are init, we can block sigkill. Yay. */ - ignore_signals(SIGNALS_IGNORE, -1); - - if (parse_config_file() < 0) - goto finish; - - if (arg_running_as == SYSTEMD_SYSTEM) - if (parse_proc_cmdline() < 0) - goto finish; - - log_parse_environment(); - - if (parse_argv(argc, argv) < 0) - goto finish; - - if (arg_action == ACTION_TEST && - geteuid() == 0) { - log_error("Don't run test mode as root."); - goto finish; - } - - 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."); - goto finish; - } - - if (arg_action == ACTION_HELP) { - retval = help(); - goto finish; - } else if (arg_action == ACTION_VERSION) { - retval = version(); - goto finish; - } else if (arg_action == ACTION_DUMP_CONFIGURATION_ITEMS) { - unit_dump_config_items(stdout); - retval = EXIT_SUCCESS; - goto finish; - } else if (arg_action == ACTION_DONE) { - retval = EXIT_SUCCESS; - goto finish; - } - - assert_se(arg_action == ACTION_RUN || arg_action == ACTION_TEST); - - /* Close logging fds, in order not to confuse fdset below */ - 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; - } - - assert_se(fdset_remove(fds, fileno(serialization)) >= 0); - } else - close_all_fds(NULL, 0); - - /* Set up PATH unless it is already set */ - setenv("PATH", -#ifdef HAVE_SPLIT_USR - "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", -#else - "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin", -#endif - arg_running_as == SYSTEMD_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. */ - if (!in_initrd()) - parse_initrd_timestamp(&initrd_timestamp); - - /* Unset some environment variables passed in from the - * kernel that don't really make sense for us. */ - unsetenv("HOME"); - unsetenv("TERM"); - - /* When we are invoked by a shell, these might be set, - * but make little sense to pass on */ - unsetenv("PWD"); - unsetenv("SHLVL"); - unsetenv("_"); - - /* When we are invoked by a chroot-like tool such as - * nspawn, these might be set, but make little sense - * to pass on */ - unsetenv("USER"); - unsetenv("LOGNAME"); - - /* All other variables are left as is, so that clients - * can still read them via /proc/1/environ */ - } - - /* Move out of the way, so that we won't block unmounts */ - assert_se(chdir("/") == 0); - - if (arg_running_as == SYSTEMD_SYSTEM) { - /* Become a session leader if we aren't one yet. */ - setsid(); - - /* Disable the umask logic */ - umask(0); - } - - /* Make sure D-Bus doesn't fiddle with the SIGPIPE handlers */ - dbus_connection_set_change_sigpipe(FALSE); - - /* Reset the console, but only if this is really init and we - * are freshly booted */ - if (arg_running_as == SYSTEMD_SYSTEM && arg_action == ACTION_RUN) - console_setup(getpid() == 1 && !skip_setup); - - /* Open the logging devices, if possible and necessary */ - log_open(); - - /* Make sure we leave a core dump without panicing the - * kernel. */ - if (getpid() == 1) - install_crash_handler(); - - if (geteuid() == 0 && !getenv("SYSTEMD_SKIP_API_MOUNTS")) { - r = mount_cgroup_controllers(arg_join_controllers); - if (r < 0) - goto finish; - } - - if (arg_running_as == SYSTEMD_SYSTEM) { - const char *virtualization = NULL; - - log_info(PACKAGE_STRING " running in system mode. (" SYSTEMD_FEATURES "; " DISTRIBUTION ")"); - - detect_virtualization(&virtualization); - if (virtualization) - log_info("Detected virtualization '%s'.", virtualization); - - if (in_initrd()) - log_info("Running in initial RAM disk."); - - } else - log_debug(PACKAGE_STRING " running in user mode. (" SYSTEMD_FEATURES "; " DISTRIBUTION ")"); - - if (arg_running_as == SYSTEMD_SYSTEM && !skip_setup) { - locale_setup(); - - if (arg_show_status || plymouth_running()) - status_welcome(); - - kmod_setup(); - hostname_setup(); - machine_id_setup(); - loopback_setup(); - - test_mtab(); - test_usr(); - test_cgroups(); - } - - if (arg_running_as == SYSTEMD_SYSTEM && arg_runtime_watchdog > 0) - watchdog_set_timeout(&arg_runtime_watchdog); - - if (arg_timer_slack_nsec != (nsec_t) -1) - if (prctl(PR_SET_TIMERSLACK, arg_timer_slack_nsec) < 0) - log_error("Failed to adjust timer slack: %m"); - - if (arg_capability_bounding_set_drop) { - r = capability_bounding_set_drop(arg_capability_bounding_set_drop, true); - if (r < 0) { - log_error("Failed to drop capability bounding set: %s", strerror(-r)); - goto finish; - } - r = capability_bounding_set_drop_usermode(arg_capability_bounding_set_drop); - if (r < 0) { - log_error("Failed to drop capability bounding set of usermode helpers: %s", strerror(-r)); - goto finish; - } - } - - if (arg_running_as == SYSTEMD_USER) { - /* Become reaper of our children */ - 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)); - goto finish; - } - - m->confirm_spawn = arg_confirm_spawn; - m->default_std_output = arg_default_std_output; - m->default_std_error = arg_default_std_error; - m->runtime_watchdog = arg_runtime_watchdog; - m->shutdown_watchdog = arg_shutdown_watchdog; - - manager_set_default_rlimits(m, arg_default_rlimit); - - if (dual_timestamp_is_set(&initrd_timestamp)) - m->initrd_timestamp = initrd_timestamp; - - if (arg_default_controllers) - manager_set_default_controllers(m, arg_default_controllers); - - manager_set_show_status(m, arg_show_status); - - /* Remember whether we should queue the default job */ - queue_default_job = !serialization || arg_switched_root; - - before_startup = now(CLOCK_MONOTONIC); - - r = manager_startup(m, serialization, fds); - if (r < 0) - log_error("Failed to fully start up daemon: %s", strerror(-r)); - - /* This will close all file descriptors that were opened, but - * not claimed by any unit. */ - if (fds) { - fdset_free(fds); - fds = NULL; - } - - if (serialization) { - fclose(serialization); - serialization = NULL; - } - - if (queue_default_job) { - DBusError error; - Unit *target = NULL; - Job *default_unit_job; - - dbus_error_init(&error); - - log_debug("Activating default unit: %s", arg_default_unit); - - r = manager_load_unit(m, arg_default_unit, NULL, &error, &target); - if (r < 0) { - log_error("Failed to load default target: %s", bus_error(&error, r)); - dbus_error_free(&error); - } else if (target->load_state == UNIT_ERROR) - log_error("Failed to load default target: %s", strerror(-target->load_error)); - else if (target->load_state == UNIT_MASKED) - log_error("Default target masked."); - - if (!target || target->load_state != UNIT_LOADED) { - log_info("Trying to load rescue target..."); - - r = manager_load_unit(m, SPECIAL_RESCUE_TARGET, NULL, &error, &target); - if (r < 0) { - log_error("Failed to load rescue target: %s", bus_error(&error, r)); - dbus_error_free(&error); - goto finish; - } else if (target->load_state == UNIT_ERROR) { - log_error("Failed to load rescue target: %s", strerror(-target->load_error)); - goto finish; - } else if (target->load_state == UNIT_MASKED) { - log_error("Rescue target masked."); - goto finish; - } - } - - assert(target->load_state == UNIT_LOADED); - - if (arg_action == ACTION_TEST) { - printf("-> By units:\n"); - manager_dump_units(m, stdout, "\t"); - } - - r = manager_add_job(m, JOB_START, target, JOB_REPLACE, false, &error, &default_unit_job); - if (r < 0) { - log_error("Failed to start default target: %s", bus_error(&error, r)); - dbus_error_free(&error); - goto finish; - } - m->default_unit_job_id = default_unit_job->id; - - after_startup = now(CLOCK_MONOTONIC); - log_full(arg_action == ACTION_TEST ? LOG_INFO : LOG_DEBUG, - "Loaded units and determined initial transaction in %s.", - format_timespan(timespan, sizeof(timespan), after_startup - before_startup)); - - if (arg_action == ACTION_TEST) { - printf("-> By jobs:\n"); - manager_dump_jobs(m, stdout, "\t"); - retval = EXIT_SUCCESS; - goto finish; - } - } - - for (;;) { - r = manager_loop(m); - if (r < 0) { - log_error("Failed to run mainloop: %s", strerror(-r)); - goto finish; - } - - switch (m->exit_code) { - - case MANAGER_EXIT: - retval = EXIT_SUCCESS; - log_debug("Exit."); - goto finish; - - case MANAGER_RELOAD: - log_info("Reloading."); - r = manager_reload(m); - if (r < 0) - log_error("Failed to reload: %s", strerror(-r)); - break; - - case MANAGER_REEXECUTE: - - if (prepare_reexecute(m, &serialization, &fds, true) < 0) - goto finish; - - reexecute = true; - log_notice("Reexecuting."); - goto finish; - - case MANAGER_SWITCH_ROOT: - /* Steal the switch root parameters */ - switch_root_dir = m->switch_root; - switch_root_init = m->switch_root_init; - m->switch_root = m->switch_root_init = NULL; - - if (!switch_root_init) - if (prepare_reexecute(m, &serialization, &fds, false) < 0) - goto finish; - - reexecute = true; - log_notice("Switching root."); - goto finish; - - case MANAGER_REBOOT: - case MANAGER_POWEROFF: - case MANAGER_HALT: - case MANAGER_KEXEC: { - static const char * const table[_MANAGER_EXIT_CODE_MAX] = { - [MANAGER_REBOOT] = "reboot", - [MANAGER_POWEROFF] = "poweroff", - [MANAGER_HALT] = "halt", - [MANAGER_KEXEC] = "kexec" - }; - - assert_se(shutdown_verb = table[m->exit_code]); - arm_reboot_watchdog = m->exit_code == MANAGER_REBOOT; - - log_notice("Shutting down."); - goto finish; - } - - default: - assert_not_reached("Unknown exit code."); - } - } - -finish: - if (m) - manager_free(m); - - for (j = 0; j < RLIMIT_NLIMITS; j++) - free(arg_default_rlimit[j]); - - free(arg_default_unit); - strv_free(arg_default_controllers); - free_join_controllers(); - - dbus_shutdown(); - label_finish(); - - if (reexecute) { - const char **args; - unsigned i, args_size; - - /* Close and disarm the watchdog, so that the new - * instance can reinitialize it, but doesn't get - * 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 - * can handle the SIGCHLD for them after - * deserializing. */ - broadcast_signal(SIGTERM, false); - - /* And switch root */ - r = switch_root(switch_root_dir); - if (r < 0) - log_error("Failed to switch root, ignoring: %s", strerror(-r)); - } - - args_size = MAX(6, argc+1); - args = newa(const char*, args_size); - - if (!switch_root_init) { - char sfd[16]; - - /* First try to spawn ourselves with the right - * path, and with full serialization. We do - * this only if the user didn't specify an - * explicit init to spawn. */ - - assert(serialization); - assert(fds); - - snprintf(sfd, sizeof(sfd), "%i", fileno(serialization)); - char_array_0(sfd); - - i = 0; - args[i++] = SYSTEMD_BINARY_PATH; - if (switch_root_dir) - args[i++] = "--switched-root"; - args[i++] = arg_running_as == SYSTEMD_SYSTEM ? "--system" : "--user"; - args[i++] = "--deserialize"; - args[i++] = sfd; - args[i++] = NULL; - - assert(i <= args_size); - execv(args[0], (char* const*) args); - } - - /* Try the fallback, if there is any, without any - * serialization. We pass the original argv[] and - * envp[]. (Well, modulo the ordering changes due to - * getopt() in argv[], and some cleanups in envp[], - * but let's hope that doesn't matter.) */ - - if (serialization) { - fclose(serialization); - serialization = NULL; - } - - if (fds) { - fdset_free(fds); - fds = NULL; - } - - /* Reopen the console */ - make_console_stdio(); - - for (j = 1, i = 1; j < argc; j++) - args[i++] = argv[j]; - args[i++] = NULL; - assert(i <= args_size); - - if (switch_root_init) { - args[0] = switch_root_init; - execv(args[0], (char* const*) args); - log_warning("Failed to execute configured init, trying fallback: %m"); - } - - args[0] = "/sbin/init"; - execv(args[0], (char* const*) args); - - if (errno == ENOENT) { - log_warning("No /sbin/init, trying fallback"); - - args[0] = "/bin/sh"; - args[1] = NULL; - execv(args[0], (char* const*) args); - log_error("Failed to execute /bin/sh, giving up: %m"); - } else - log_warning("Failed to execute /sbin/init, giving up: %m"); - } - - if (serialization) - fclose(serialization); - - if (fds) - fdset_free(fds); - - if (shutdown_verb) { - const char * command_line[] = { - SYSTEMD_SHUTDOWN_BINARY_PATH, - shutdown_verb, - NULL - }; - char **env_block; - - if (arm_reboot_watchdog && arg_shutdown_watchdog > 0) { - char e[32]; - - /* If we reboot let's set the shutdown - * watchdog and tell the shutdown binary to - * repeatedly ping it */ - watchdog_set_timeout(&arg_shutdown_watchdog); - watchdog_close(false); - - /* Tell the binary how often to ping */ - snprintf(e, sizeof(e), "WATCHDOG_USEC=%llu", (unsigned long long) arg_shutdown_watchdog); - char_array_0(e); - - env_block = strv_append(environ, e); - } else { - env_block = strv_copy(environ); - watchdog_close(true); - } - - execve(SYSTEMD_SHUTDOWN_BINARY_PATH, (char **) command_line, env_block); - free(env_block); - log_error("Failed to execute shutdown binary, freezing: %m"); - } - - if (getpid() == 1) - freeze(); - - return retval; -} diff --git a/src/core/manager.c b/src/core/manager.c deleted file mode 100644 index f932c79a1b..0000000000 --- a/src/core/manager.c +++ /dev/null @@ -1,2309 +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 <assert.h> -#include <errno.h> -#include <string.h> -#include <sys/epoll.h> -#include <signal.h> -#include <sys/signalfd.h> -#include <sys/wait.h> -#include <unistd.h> -#include <sys/poll.h> -#include <sys/reboot.h> -#include <sys/ioctl.h> -#include <linux/kd.h> -#include <termios.h> -#include <fcntl.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <dirent.h> - -#ifdef HAVE_AUDIT -#include <libaudit.h> -#endif - -#include "systemd/sd-daemon.h" -#include "systemd/sd-id128.h" -#include "systemd/sd-messages.h" - -#include "manager.h" -#include "transaction.h" -#include "hashmap.h" -#include "macro.h" -#include "strv.h" -#include "log.h" -#include "util.h" -#include "mkdir.h" -#include "ratelimit.h" -#include "cgroup.h" -#include "mount-setup.h" -#include "unit-name.h" -#include "dbus-unit.h" -#include "dbus-job.h" -#include "missing.h" -#include "path-lookup.h" -#include "special.h" -#include "bus-errors.h" -#include "exit-status.h" -#include "virt.h" -#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 - -/* As soon as 5s passed since a unit was added to our GC queue, make sure to run a gc sweep */ -#define GC_QUEUE_USEC_MAX (10*USEC_PER_SEC) - -/* Where clients shall send notification messages to */ -#define NOTIFY_SOCKET "@/org/freedesktop/systemd1/notify" - -static int manager_setup_notify(Manager *m) { - union { - struct sockaddr sa; - struct sockaddr_un un; - } sa; - struct epoll_event ev; - int one = 1; - - assert(m); - - m->notify_watch.type = WATCH_NOTIFY; - m->notify_watch.fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); - if (m->notify_watch.fd < 0) { - log_error("Failed to allocate notification socket: %m"); - return -errno; - } - - zero(sa); - sa.sa.sa_family = AF_UNIX; - - if (getpid() != 1 || detect_container(NULL) > 0) - snprintf(sa.un.sun_path, sizeof(sa.un.sun_path), NOTIFY_SOCKET "/%llu", random_ull()); - else - strncpy(sa.un.sun_path, NOTIFY_SOCKET, sizeof(sa.un.sun_path)); - - sa.un.sun_path[0] = 0; - - if (bind(m->notify_watch.fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + 1 + strlen(sa.un.sun_path+1)) < 0) { - log_error("bind() failed: %m"); - return -errno; - } - - if (setsockopt(m->notify_watch.fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)) < 0) { - log_error("SO_PASSCRED failed: %m"); - return -errno; - } - - zero(ev); - ev.events = EPOLLIN; - ev.data.ptr = &m->notify_watch; - - if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->notify_watch.fd, &ev) < 0) - return -errno; - - sa.un.sun_path[0] = '@'; - m->notify_socket = strdup(sa.un.sun_path); - if (!m->notify_socket) - return -ENOMEM; - - log_debug("Using notification socket %s", m->notify_socket); - - 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 (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); - if (fd < 0) { - /* Support systems without virtual console */ - if (fd != -ENOENT) - log_warning("Failed to open /dev/tty0: %m"); - } else { - /* Enable that we get SIGWINCH on kbrequest */ - if (ioctl(fd, KDSIGACCEPT, SIGWINCH) < 0) - log_warning("Failed to enable kbrequest handling: %s", strerror(errno)); - - close_nointr_nofail(fd); - } - - return 0; -} - -static int manager_setup_signals(Manager *m) { - sigset_t mask; - struct epoll_event ev; - struct sigaction sa; - - assert(m); - - /* We are not interested in SIGSTOP and friends. */ - zero(sa); - sa.sa_handler = SIG_DFL; - sa.sa_flags = SA_NOCLDSTOP|SA_RESTART; - assert_se(sigaction(SIGCHLD, &sa, NULL) == 0); - - assert_se(sigemptyset(&mask) == 0); - - sigset_add_many(&mask, - SIGCHLD, /* Child died */ - SIGTERM, /* Reexecute daemon */ - SIGHUP, /* Reload configuration */ - SIGUSR1, /* systemd/upstart: reconnect to D-Bus */ - SIGUSR2, /* systemd: dump status */ - SIGINT, /* Kernel sends us this on control-alt-del */ - SIGWINCH, /* Kernel sends us this on kbrequest (alt-arrowup) */ - SIGPWR, /* Some kernel drivers and upsd send us this on power failure */ - SIGRTMIN+0, /* systemd: start default.target */ - SIGRTMIN+1, /* systemd: isolate rescue.target */ - SIGRTMIN+2, /* systemd: isolate emergency.target */ - SIGRTMIN+3, /* systemd: start halt.target */ - SIGRTMIN+4, /* systemd: start poweroff.target */ - SIGRTMIN+5, /* systemd: start reboot.target */ - SIGRTMIN+6, /* systemd: start kexec.target */ - SIGRTMIN+13, /* systemd: Immediate halt */ - SIGRTMIN+14, /* systemd: Immediate poweroff */ - SIGRTMIN+15, /* systemd: Immediate reboot */ - SIGRTMIN+16, /* systemd: Immediate kexec */ - SIGRTMIN+20, /* systemd: enable status messages */ - 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 */ - SIGRTMIN+29, /* systemd: set log target to syslog-or-kmsg */ - -1); - 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) - return -errno; - - zero(ev); - ev.events = EPOLLIN; - ev.data.ptr = &m->signal_watch; - - if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->signal_watch.fd, &ev) < 0) - return -errno; - - if (m->running_as == SYSTEMD_SYSTEM) - return enable_special_signals(m); - - return 0; -} - -static void manager_strip_environment(Manager *m) { - assert(m); - - /* Remove variables from the inherited set that are part of - * the container interface: - * http://www.freedesktop.org/wiki/Software/systemd/ContainerInterface */ - strv_remove_prefix(m->environment, "container="); - strv_remove_prefix(m->environment, "container_"); - - /* Remove variables from the inherited set that are part of - * the initrd interface: - * http://www.freedesktop.org/wiki/Software/systemd/InitrdInterface */ - strv_remove_prefix(m->environment, "RD_"); -} - -int manager_new(SystemdRunningAs running_as, Manager **_m) { - Manager *m; - int r = -ENOMEM; - - assert(_m); - assert(running_as >= 0); - assert(running_as < _SYSTEMD_RUNNING_AS_MAX); - - m = new0(Manager, 1); - if (!m) - return -ENOMEM; - - dual_timestamp_get(&m->userspace_timestamp); - - m->running_as = running_as; - m->name_data_slot = m->conn_data_slot = m->subscribed_data_slot = -1; - m->exit_code = _MANAGER_EXIT_CODE_INVALID; - m->pin_cgroupfs_fd = -1; - m->idle_pipe[0] = m->idle_pipe[1] = -1; - - 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->current_job_id = 1; /* start as id #1, so that we can leave #0 around as "null-like" value */ - - m->environment = strv_copy(environ); - if (!m->environment) - goto fail; - - manager_strip_environment(m); - - if (running_as == SYSTEMD_SYSTEM) { - m->default_controllers = strv_new("cpu", NULL); - if (!m->default_controllers) - goto fail; - } - - if (!(m->units = hashmap_new(string_hash_func, string_compare_func))) - goto fail; - - if (!(m->jobs = hashmap_new(trivial_hash_func, trivial_compare_func))) - goto fail; - - if (!(m->watch_pids = hashmap_new(trivial_hash_func, trivial_compare_func))) - goto fail; - - if (!(m->cgroup_bondings = hashmap_new(string_hash_func, string_compare_func))) - goto fail; - - if (!(m->watch_bus = hashmap_new(string_hash_func, string_compare_func))) - goto fail; - - if ((m->epoll_fd = epoll_create1(EPOLL_CLOEXEC)) < 0) - goto fail; - - if ((r = manager_setup_signals(m)) < 0) - goto fail; - - if ((r = manager_setup_cgroup(m)) < 0) - goto fail; - - if ((r = manager_setup_notify(m)) < 0) - goto fail; - - /* Try to connect to the busses, if possible. */ - if ((r = bus_init(m, running_as != SYSTEMD_SYSTEM)) < 0) - goto fail; - - m->taint_usr = dir_is_empty("/usr") > 0; - - *_m = m; - return 0; - -fail: - manager_free(m); - return r; -} - -static unsigned manager_dispatch_cleanup_queue(Manager *m) { - Unit *u; - unsigned n = 0; - - assert(m); - - while ((u = m->cleanup_queue)) { - assert(u->in_cleanup_queue); - - unit_free(u); - n++; - } - - return n; -} - -enum { - GC_OFFSET_IN_PATH, /* This one is on the path we were traveling */ - GC_OFFSET_UNSURE, /* No clue */ - GC_OFFSET_GOOD, /* We still need this unit */ - GC_OFFSET_BAD, /* We don't need this unit anymore */ - _GC_OFFSET_MAX -}; - -static void unit_gc_sweep(Unit *u, unsigned gc_marker) { - Iterator i; - Unit *other; - bool is_bad; - - assert(u); - - if (u->gc_marker == gc_marker + GC_OFFSET_GOOD || - u->gc_marker == gc_marker + GC_OFFSET_BAD || - u->gc_marker == gc_marker + GC_OFFSET_IN_PATH) - return; - - if (u->in_cleanup_queue) - goto bad; - - if (unit_check_gc(u)) - goto good; - - u->gc_marker = gc_marker + GC_OFFSET_IN_PATH; - - is_bad = true; - - SET_FOREACH(other, u->dependencies[UNIT_REFERENCED_BY], i) { - unit_gc_sweep(other, gc_marker); - - if (other->gc_marker == gc_marker + GC_OFFSET_GOOD) - goto good; - - if (other->gc_marker != gc_marker + GC_OFFSET_BAD) - is_bad = false; - } - - if (is_bad) - goto bad; - - /* We were unable to find anything out about this entry, so - * let's investigate it later */ - u->gc_marker = gc_marker + GC_OFFSET_UNSURE; - unit_add_to_gc_queue(u); - return; - -bad: - /* We definitely know that this one is not useful anymore, so - * let's mark it for deletion */ - u->gc_marker = gc_marker + GC_OFFSET_BAD; - unit_add_to_cleanup_queue(u); - return; - -good: - u->gc_marker = gc_marker + GC_OFFSET_GOOD; -} - -static unsigned manager_dispatch_gc_queue(Manager *m) { - Unit *u; - unsigned n = 0; - unsigned gc_marker; - - assert(m); - - if ((m->n_in_gc_queue < GC_QUEUE_ENTRIES_MAX) && - (m->gc_queue_timestamp <= 0 || - (m->gc_queue_timestamp + GC_QUEUE_USEC_MAX) > now(CLOCK_MONOTONIC))) - return 0; - - log_debug("Running GC..."); - - m->gc_marker += _GC_OFFSET_MAX; - if (m->gc_marker + _GC_OFFSET_MAX <= _GC_OFFSET_MAX) - m->gc_marker = 1; - - gc_marker = m->gc_marker; - - while ((u = m->gc_queue)) { - assert(u->in_gc_queue); - - unit_gc_sweep(u, gc_marker); - - LIST_REMOVE(Unit, gc_queue, m->gc_queue, u); - u->in_gc_queue = false; - - n++; - - if (u->gc_marker == gc_marker + GC_OFFSET_BAD || - u->gc_marker == gc_marker + GC_OFFSET_UNSURE) { - log_debug("Collecting %s", u->id); - u->gc_marker = gc_marker + GC_OFFSET_BAD; - unit_add_to_cleanup_queue(u); - } - } - - m->n_in_gc_queue = 0; - m->gc_queue_timestamp = 0; - - return n; -} - -static void manager_clear_jobs_and_units(Manager *m) { - Unit *u; - - assert(m); - - while ((u = hashmap_first(m->units))) - unit_free(u); - - manager_dispatch_cleanup_queue(m); - - assert(!m->load_queue); - assert(!m->run_queue); - assert(!m->dbus_unit_queue); - assert(!m->dbus_job_queue); - assert(!m->cleanup_queue); - assert(!m->gc_queue); - - assert(hashmap_isempty(m->jobs)); - assert(hashmap_isempty(m->units)); -} - -void manager_free(Manager *m) { - UnitType c; - int i; - - assert(m); - - manager_clear_jobs_and_units(m); - - for (c = 0; c < _UNIT_TYPE_MAX; c++) - if (unit_vtable[c]->shutdown) - unit_vtable[c]->shutdown(m); - - /* If we reexecute ourselves, we keep the root cgroup - * around */ - manager_shutdown_cgroup(m, m->exit_code != MANAGER_REEXECUTE); - - manager_undo_generators(m); - - bus_done(m); - - hashmap_free(m->units); - hashmap_free(m->jobs); - hashmap_free(m->watch_pids); - hashmap_free(m->watch_bus); - - if (m->epoll_fd >= 0) - close_nointr_nofail(m->epoll_fd); - if (m->signal_watch.fd >= 0) - close_nointr_nofail(m->signal_watch.fd); - if (m->notify_watch.fd >= 0) - close_nointr_nofail(m->notify_watch.fd); - - free(m->notify_socket); - - lookup_paths_free(&m->lookup_paths); - strv_free(m->environment); - - strv_free(m->default_controllers); - - hashmap_free(m->cgroup_bondings); - set_free_free(m->unit_path_cache); - - close_pipe(m->idle_pipe); - - free(m->switch_root); - free(m->switch_root_init); - - for (i = 0; i < RLIMIT_NLIMITS; i++) - free(m->rlimit[i]); - - free(m); -} - -int manager_enumerate(Manager *m) { - int r = 0, q; - UnitType c; - - assert(m); - - /* Let's ask every type to load all units from disk/kernel - * that it might know */ - for (c = 0; c < _UNIT_TYPE_MAX; c++) - if (unit_vtable[c]->enumerate) - if ((q = unit_vtable[c]->enumerate(m)) < 0) - r = q; - - manager_dispatch_load_queue(m); - return r; -} - -int manager_coldplug(Manager *m) { - int r = 0, q; - Iterator i; - Unit *u; - char *k; - - assert(m); - - /* Then, let's set up their initial state. */ - HASHMAP_FOREACH_KEY(u, k, m->units, i) { - - /* ignore aliases */ - if (u->id != k) - continue; - - if ((q = unit_coldplug(u)) < 0) - r = q; - } - - return r; -} - -static void manager_build_unit_path_cache(Manager *m) { - char **i; - DIR *d = NULL; - int r; - - assert(m); - - set_free_free(m->unit_path_cache); - - if (!(m->unit_path_cache = set_new(string_hash_func, string_compare_func))) { - log_error("Failed to allocate unit path cache."); - return; - } - - /* This simply builds a list of files we know exist, so that - * we don't always have to go to disk */ - - STRV_FOREACH(i, m->lookup_paths.unit_path) { - struct dirent *de; - - if (!(d = opendir(*i))) { - log_error("Failed to open directory: %m"); - continue; - } - - while ((de = readdir(d))) { - char *p; - - if (ignore_file(de->d_name)) - continue; - - p = strjoin(streq(*i, "/") ? "" : *i, "/", de->d_name, NULL); - if (!p) { - r = -ENOMEM; - goto fail; - } - - if ((r = set_put(m->unit_path_cache, p)) < 0) { - free(p); - goto fail; - } - } - - closedir(d); - d = NULL; - } - - return; - -fail: - log_error("Failed to build unit path cache: %s", strerror(-r)); - - set_free_free(m->unit_path_cache); - m->unit_path_cache = NULL; - - if (d) - closedir(d); -} - -int manager_startup(Manager *m, FILE *serialization, FDSet *fds) { - int r, q; - - assert(m); - - manager_run_generators(m); - - r = lookup_paths_init( - &m->lookup_paths, m->running_as, true, - m->generator_unit_path, - m->generator_unit_path_early, - m->generator_unit_path_late); - if (r < 0) - return r; - - manager_build_unit_path_cache(m); - - /* If we will deserialize make sure that during enumeration - * this is already known, so we increase the counter here - * already */ - if (serialization) - m->n_reloading ++; - - /* First, enumerate what we can from all config files */ - r = manager_enumerate(m); - - /* Second, deserialize if there is something to deserialize */ - if (serialization) { - q = manager_deserialize(m, serialization, fds); - if (q < 0) - r = q; - } - - /* Third, fire things up! */ - q = manager_coldplug(m); - if (q < 0) - r = q; - - if (serialization) { - assert(m->n_reloading > 0); - m->n_reloading --; - } - - return r; -} - -int manager_add_job(Manager *m, JobType type, Unit *unit, JobMode mode, bool override, DBusError *e, Job **_ret) { - int r; - Transaction *tr; - - assert(m); - assert(type < _JOB_TYPE_MAX); - assert(unit); - assert(mode < _JOB_MODE_MAX); - - if (mode == JOB_ISOLATE && type != JOB_START) { - dbus_set_error(e, BUS_ERROR_INVALID_JOB_MODE, "Isolate is only valid for start."); - return -EINVAL; - } - - if (mode == JOB_ISOLATE && !unit->allow_isolate) { - dbus_set_error(e, BUS_ERROR_NO_ISOLATION, "Operation refused, unit may not be isolated."); - return -EPERM; - } - - log_debug("Trying to enqueue job %s/%s/%s", unit->id, job_type_to_string(type), job_mode_to_string(mode)); - - job_type_collapse(&type, unit); - - tr = transaction_new(); - if (!tr) - return -ENOMEM; - - r = transaction_add_job_and_dependencies(tr, type, unit, NULL, true, override, false, - mode == JOB_IGNORE_DEPENDENCIES || mode == JOB_IGNORE_REQUIREMENTS, - mode == JOB_IGNORE_DEPENDENCIES, e); - if (r < 0) - goto tr_abort; - - if (mode == JOB_ISOLATE) { - r = transaction_add_isolate_jobs(tr, m); - if (r < 0) - goto tr_abort; - } - - r = transaction_activate(tr, m, mode, e); - 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); - - if (_ret) - *_ret = tr->anchor_job; - - transaction_free(tr); - return 0; - -tr_abort: - transaction_abort(tr); - transaction_free(tr); - return r; -} - -int manager_add_job_by_name(Manager *m, JobType type, const char *name, JobMode mode, bool override, DBusError *e, Job **_ret) { - Unit *unit; - int r; - - assert(m); - assert(type < _JOB_TYPE_MAX); - assert(name); - assert(mode < _JOB_MODE_MAX); - - 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); -} - -Job *manager_get_job(Manager *m, uint32_t id) { - assert(m); - - return hashmap_get(m->jobs, UINT32_TO_PTR(id)); -} - -Unit *manager_get_unit(Manager *m, const char *name) { - assert(m); - assert(name); - - return hashmap_get(m->units, name); -} - -unsigned manager_dispatch_load_queue(Manager *m) { - Unit *u; - unsigned n = 0; - - assert(m); - - /* Make sure we are not run recursively */ - if (m->dispatching_load_queue) - return 0; - - m->dispatching_load_queue = true; - - /* Dispatches the load queue. Takes a unit from the queue and - * tries to load its data until the queue is empty */ - - while ((u = m->load_queue)) { - assert(u->in_load_queue); - - unit_load(u); - n++; - } - - m->dispatching_load_queue = false; - return n; -} - -int manager_load_unit_prepare(Manager *m, const char *name, const char *path, DBusError *e, Unit **_ret) { - Unit *ret; - UnitType t; - int r; - - assert(m); - assert(name || path); - - /* This will prepare the unit for loading, but not actually - * load anything from disk. */ - - if (path && !is_path(path)) { - dbus_set_error(e, BUS_ERROR_INVALID_PATH, "Path %s is not absolute.", path); - return -EINVAL; - } - - if (!name) - name = path_get_file_name(path); - - t = unit_name_to_type(name); - - if (t == _UNIT_TYPE_INVALID || !unit_name_is_valid(name, false)) { - dbus_set_error(e, BUS_ERROR_INVALID_NAME, "Unit name %s is not valid.", name); - return -EINVAL; - } - - ret = manager_get_unit(m, name); - if (ret) { - *_ret = ret; - return 1; - } - - ret = unit_new(m, unit_vtable[t]->object_size); - if (!ret) - return -ENOMEM; - - if (path) { - ret->fragment_path = strdup(path); - if (!ret->fragment_path) { - unit_free(ret); - return -ENOMEM; - } - } - - if ((r = unit_add_name(ret, name)) < 0) { - unit_free(ret); - return r; - } - - unit_add_to_load_queue(ret); - unit_add_to_dbus_queue(ret); - unit_add_to_gc_queue(ret); - - if (_ret) - *_ret = ret; - - return 0; -} - -int manager_load_unit(Manager *m, const char *name, const char *path, DBusError *e, Unit **_ret) { - int r; - - assert(m); - - /* This will load the service information files, but not actually - * start any services or anything. */ - - r = manager_load_unit_prepare(m, name, path, e, _ret); - if (r != 0) - return r; - - manager_dispatch_load_queue(m); - - if (_ret) - *_ret = unit_follow_merge(*_ret); - - return 0; -} - -void manager_dump_jobs(Manager *s, FILE *f, const char *prefix) { - Iterator i; - Job *j; - - assert(s); - assert(f); - - HASHMAP_FOREACH(j, s->jobs, i) - job_dump(j, f, prefix); -} - -void manager_dump_units(Manager *s, FILE *f, const char *prefix) { - Iterator i; - Unit *u; - const char *t; - - assert(s); - assert(f); - - HASHMAP_FOREACH_KEY(u, t, s->units, i) - if (u->id == t) - unit_dump(u, f, prefix); -} - -void manager_clear_jobs(Manager *m) { - Job *j; - - assert(m); - - while ((j = hashmap_first(m->jobs))) - /* No need to recurse. We're cancelling all jobs. */ - job_finish_and_invalidate(j, JOB_CANCELED, false); -} - -unsigned manager_dispatch_run_queue(Manager *m) { - Job *j; - unsigned n = 0; - - if (m->dispatching_run_queue) - return 0; - - m->dispatching_run_queue = true; - - while ((j = m->run_queue)) { - assert(j->installed); - assert(j->in_run_queue); - - job_run_and_invalidate(j); - n++; - } - - m->dispatching_run_queue = false; - return n; -} - -unsigned manager_dispatch_dbus_queue(Manager *m) { - Job *j; - Unit *u; - unsigned n = 0; - - assert(m); - - if (m->dispatching_dbus_queue) - return 0; - - m->dispatching_dbus_queue = true; - - while ((u = m->dbus_unit_queue)) { - assert(u->in_dbus_queue); - - bus_unit_send_change_signal(u); - n++; - } - - while ((j = m->dbus_job_queue)) { - assert(j->in_dbus_queue); - - bus_job_send_change_signal(j); - n++; - } - - m->dispatching_dbus_queue = false; - return n; -} - -static int manager_process_notify_fd(Manager *m) { - ssize_t n; - - assert(m); - - for (;;) { - char buf[4096]; - struct msghdr msghdr; - struct iovec iovec; - struct ucred *ucred; - union { - struct cmsghdr cmsghdr; - uint8_t buf[CMSG_SPACE(sizeof(struct ucred))]; - } control; - Unit *u; - char **tags; - - zero(iovec); - iovec.iov_base = buf; - iovec.iov_len = sizeof(buf)-1; - - zero(control); - zero(msghdr); - msghdr.msg_iov = &iovec; - msghdr.msg_iovlen = 1; - msghdr.msg_control = &control; - msghdr.msg_controllen = sizeof(control); - - if ((n = recvmsg(m->notify_watch.fd, &msghdr, MSG_DONTWAIT)) <= 0) { - if (n >= 0) - return -EIO; - - if (errno == EAGAIN || errno == EINTR) - break; - - return -errno; - } - - if (msghdr.msg_controllen < CMSG_LEN(sizeof(struct ucred)) || - control.cmsghdr.cmsg_level != SOL_SOCKET || - control.cmsghdr.cmsg_type != SCM_CREDENTIALS || - control.cmsghdr.cmsg_len != CMSG_LEN(sizeof(struct ucred))) { - log_warning("Received notify message without credentials. Ignoring."); - continue; - } - - 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))) { - 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; - - log_debug("Got notification message for unit %s", u->id); - - if (UNIT_VTABLE(u)->notify_message) - UNIT_VTABLE(u)->notify_message(u, ucred->pid, tags); - - strv_free(tags); - } - - return 0; -} - -static int manager_dispatch_sigchld(Manager *m) { - assert(m); - - for (;;) { - siginfo_t si; - Unit *u; - int r; - - zero(si); - - /* First we call waitd() for a PID and do not reap the - * zombie. That way we can still access /proc/$PID for - * it while it is a zombie. */ - if (waitid(P_ALL, 0, &si, WEXITED|WNOHANG|WNOWAIT) < 0) { - - if (errno == ECHILD) - break; - - if (errno == EINTR) - continue; - - return -errno; - } - - if (si.si_pid <= 0) - break; - - if (si.si_code == CLD_EXITED || si.si_code == CLD_KILLED || si.si_code == CLD_DUMPED) { - char *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) - 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 = cgroup_unit_by_pid(m, si.si_pid); - - /* And now, we actually reap the zombie. */ - if (waitid(P_PID, si.si_pid, &si, WEXITED) < 0) { - if (errno == EINTR) - continue; - - return -errno; - } - - if (si.si_code != CLD_EXITED && si.si_code != CLD_KILLED && si.si_code != CLD_DUMPED) - continue; - - log_debug("Child %lu died (code=%s, status=%i/%s)", - (long unsigned) si.si_pid, - sigchld_code_to_string(si.si_code), - si.si_status, - strna(si.si_code == CLD_EXITED - ? exit_status_to_string(si.si_status, EXIT_STATUS_FULL) - : signal_to_string(si.si_status))); - - if (!u) - continue; - - log_debug("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); - } - - return 0; -} - -static int manager_start_target(Manager *m, const char *name, JobMode mode) { - int r; - DBusError error; - - dbus_error_init(&error); - - log_debug("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)); - - dbus_error_free(&error); - - return r; -} - -static int manager_process_signal_fd(Manager *m) { - ssize_t n; - struct signalfd_siginfo sfsi; - bool sigchld = false; - - assert(m); - - for (;;) { - if ((n = read(m->signal_watch.fd, &sfsi, sizeof(sfsi))) != sizeof(sfsi)) { - - if (n >= 0) - return -EIO; - - if (errno == EINTR || errno == EAGAIN) - break; - - return -errno; - } - - if (sfsi.ssi_pid > 0) { - char *p = NULL; - - get_process_comm(sfsi.ssi_pid, &p); - - log_debug("Received SIG%s from PID %lu (%s).", - signal_to_string(sfsi.ssi_signo), - (unsigned long) sfsi.ssi_pid, strna(p)); - free(p); - } else - log_debug("Received SIG%s.", signal_to_string(sfsi.ssi_signo)); - - switch (sfsi.ssi_signo) { - - case SIGCHLD: - sigchld = true; - break; - - case SIGTERM: - if (m->running_as == SYSTEMD_SYSTEM) { - /* This is for compatibility with the - * original sysvinit */ - m->exit_code = MANAGER_REEXECUTE; - break; - } - - /* Fall through */ - - case SIGINT: - if (m->running_as == SYSTEMD_SYSTEM) { - manager_start_target(m, SPECIAL_CTRL_ALT_DEL_TARGET, JOB_REPLACE); - break; - } - - /* Run the exit target if there is one, if not, just exit. */ - if (manager_start_target(m, SPECIAL_EXIT_TARGET, JOB_REPLACE) < 0) { - m->exit_code = MANAGER_EXIT; - return 0; - } - - break; - - case SIGWINCH: - 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 == SYSTEMD_SYSTEM) - manager_start_target(m, SPECIAL_SIGPWR_TARGET, JOB_REPLACE); - - /* This is a nop on non-init */ - break; - - case SIGUSR1: { - Unit *u; - - u = manager_get_unit(m, SPECIAL_DBUS_SERVICE); - - if (!u || UNIT_IS_ACTIVE_OR_RELOADING(unit_active_state(u))) { - log_info("Trying to reconnect to bus..."); - bus_init(m, true); - } - - if (!u || !UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(u))) { - log_info("Loading D-Bus service..."); - manager_start_target(m, SPECIAL_DBUS_SERVICE, JOB_REPLACE); - } - - break; - } - - case SIGUSR2: { - FILE *f; - char *dump = NULL; - size_t size; - - if (!(f = open_memstream(&dump, &size))) { - log_warning("Failed to allocate memory stream."); - break; - } - - manager_dump_units(m, f, "\t"); - manager_dump_jobs(m, f, "\t"); - - if (ferror(f)) { - fclose(f); - free(dump); - log_warning("Failed to write status stream"); - break; - } - - fclose(f); - log_dump(LOG_INFO, dump); - free(dump); - - break; - } - - case SIGHUP: - m->exit_code = MANAGER_RELOAD; - break; - - default: { - - /* Starting SIGRTMIN+0 */ - static const char * const target_table[] = { - [0] = SPECIAL_DEFAULT_TARGET, - [1] = SPECIAL_RESCUE_TARGET, - [2] = SPECIAL_EMERGENCY_TARGET, - [3] = SPECIAL_HALT_TARGET, - [4] = SPECIAL_POWEROFF_TARGET, - [5] = SPECIAL_REBOOT_TARGET, - [6] = SPECIAL_KEXEC_TARGET - }; - - /* Starting SIGRTMIN+13, so that target halt and system halt are 10 apart */ - static const ManagerExitCode code_table[] = { - [0] = MANAGER_HALT, - [1] = MANAGER_POWEROFF, - [2] = MANAGER_REBOOT, - [3] = MANAGER_KEXEC - }; - - if ((int) sfsi.ssi_signo >= SIGRTMIN+0 && - (int) sfsi.ssi_signo < SIGRTMIN+(int) ELEMENTSOF(target_table)) { - int idx = (int) sfsi.ssi_signo - SIGRTMIN; - manager_start_target(m, target_table[idx], - (idx == 1 || idx == 2) ? JOB_ISOLATE : JOB_REPLACE); - break; - } - - if ((int) sfsi.ssi_signo >= SIGRTMIN+13 && - (int) sfsi.ssi_signo < SIGRTMIN+13+(int) ELEMENTSOF(code_table)) { - m->exit_code = code_table[sfsi.ssi_signo - SIGRTMIN - 13]; - break; - } - - switch (sfsi.ssi_signo - SIGRTMIN) { - - case 20: - log_debug("Enabling showing of status."); - manager_set_show_status(m, true); - break; - - case 21: - log_debug("Disabling showing of status."); - manager_set_show_status(m, false); - break; - - case 22: - log_set_max_level(LOG_DEBUG); - log_notice("Setting log level to debug."); - break; - - case 23: - log_set_max_level(LOG_INFO); - 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."); - break; - - case 27: - log_set_target(LOG_TARGET_CONSOLE); - log_notice("Setting log target to console."); - break; - - case 28: - log_set_target(LOG_TARGET_KMSG); - log_notice("Setting log target to kmsg."); - break; - - case 29: - log_set_target(LOG_TARGET_SYSLOG_OR_KMSG); - log_notice("Setting log target to syslog-or-kmsg."); - break; - - default: - log_warning("Got unhandled signal <%s>.", signal_to_string(sfsi.ssi_signo)); - } - } - } - } - - if (sigchld) - return manager_dispatch_sigchld(m); - - return 0; -} - -static int process_event(Manager *m, struct epoll_event *ev) { - int r; - Watch *w; - - assert(m); - assert(ev); - - assert_se(w = ev->data.ptr); - - if (w->type == WATCH_INVALID) - return 0; - - switch (w->type) { - - case WATCH_SIGNAL: - - /* An incoming signal? */ - if (ev->events != EPOLLIN) - return -EINVAL; - - if ((r = manager_process_signal_fd(m)) < 0) - return r; - - break; - - case WATCH_NOTIFY: - - /* An incoming daemon notification event? */ - if (ev->events != EPOLLIN) - return -EINVAL; - - if ((r = manager_process_notify_fd(m)) < 0) - return r; - - break; - - case WATCH_FD: - - /* Some fd event, to be dispatched to the units */ - UNIT_VTABLE(w->data.unit)->fd_event(w->data.unit, w->fd, ev->events, w); - break; - - case WATCH_UNIT_TIMER: - case WATCH_JOB_TIMER: { - uint64_t v; - ssize_t k; - - /* Some timer event, to be dispatched to the units */ - if ((k = read(w->fd, &v, sizeof(v))) != sizeof(v)) { - - if (k < 0 && (errno == EINTR || errno == EAGAIN)) - break; - - return k < 0 ? -errno : -EIO; - } - - if (w->type == WATCH_UNIT_TIMER) - UNIT_VTABLE(w->data.unit)->timer_event(w->data.unit, v, w); - else - job_timer_event(w->data.job, v, w); - break; - } - - case WATCH_MOUNT: - /* Some mount table change, intended for the mount subsystem */ - mount_fd_event(m, ev->events); - break; - - case WATCH_SWAP: - /* Some swap table change, intended for the swap subsystem */ - swap_fd_event(m, ev->events); - break; - - case WATCH_UDEV: - /* Some notification from udev, intended for the device subsystem */ - device_fd_event(m, ev->events); - break; - - case WATCH_DBUS_WATCH: - bus_watch_event(m, w, ev->events); - break; - - case WATCH_DBUS_TIMEOUT: - bus_timeout_event(m, w, ev->events); - break; - - default: - log_error("event type=%i", w->type); - assert_not_reached("Unknown epoll event type."); - } - - return 0; -} - -int manager_loop(Manager *m) { - int r; - - RATELIMIT_DEFINE(rl, 1*USEC_PER_SEC, 50000); - - assert(m); - m->exit_code = MANAGER_RUNNING; - - /* Release the path cache */ - set_free_free(m->unit_path_cache); - m->unit_path_cache = NULL; - - manager_check_finished(m); - - /* There might still be some zombies hanging around from - * before we were exec()'ed. Leat's reap them */ - r = manager_dispatch_sigchld(m); - if (r < 0) - return r; - - while (m->exit_code == MANAGER_RUNNING) { - struct epoll_event event; - int n; - int wait_msec = -1; - - if (m->runtime_watchdog > 0 && m->running_as == SYSTEMD_SYSTEM) - watchdog_ping(); - - if (!ratelimit_test(&rl)) { - /* Yay, something is going seriously wrong, pause a little */ - log_warning("Looping too fast. Throttling execution a little."); - sleep(1); - continue; - } - - if (manager_dispatch_load_queue(m) > 0) - continue; - - if (manager_dispatch_run_queue(m) > 0) - continue; - - if (bus_dispatch(m) > 0) - continue; - - if (manager_dispatch_cleanup_queue(m) > 0) - continue; - - if (manager_dispatch_gc_queue(m) > 0) - continue; - - if (manager_dispatch_dbus_queue(m) > 0) - continue; - - if (swap_dispatch_reload(m) > 0) - continue; - - /* Sleep for half the watchdog time */ - 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; - } else - wait_msec = -1; - - n = epoll_wait(m->epoll_fd, &event, 1, wait_msec); - if (n < 0) { - - if (errno == EINTR) - continue; - - return -errno; - } else if (n == 0) - continue; - - assert(n == 1); - - r = process_event(m, &event); - if (r < 0) - return r; - } - - return m->exit_code; -} - -int manager_load_unit_from_dbus_path(Manager *m, const char *s, DBusError *e, Unit **_u) { - char *n; - Unit *u; - int r; - - assert(m); - assert(s); - assert(_u); - - if (!startswith(s, "/org/freedesktop/systemd1/unit/")) - return -EINVAL; - - n = bus_path_unescape(s+31); - if (!n) - return -ENOMEM; - - r = manager_load_unit(m, n, NULL, e, &u); - free(n); - - if (r < 0) - return r; - - *_u = u; - - return 0; -} - -int manager_get_job_from_dbus_path(Manager *m, const char *s, Job **_j) { - Job *j; - unsigned id; - int r; - - assert(m); - assert(s); - assert(_j); - - if (!startswith(s, "/org/freedesktop/systemd1/job/")) - return -EINVAL; - - if ((r = safe_atou(s + 30, &id)) < 0) - return r; - - if (!(j = manager_get_job(m, id))) - return -ENOENT; - - *_j = j; - - return 0; -} - -void manager_send_unit_audit(Manager *m, Unit *u, int type, bool success) { - -#ifdef HAVE_AUDIT - char *p; - int audit_fd; - - audit_fd = get_audit_fd(); - if (audit_fd < 0) - return; - - /* Don't generate audit events if the service was already - * started and we're just deserializing */ - if (m->n_reloading > 0) - return; - - 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)); - return; - } - - 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. */ - close_audit_fd(); - } else - log_warning("Failed to send audit message: %m"); - } - - free(p); -#endif - -} - -void manager_send_unit_plymouth(Manager *m, Unit *u) { - int fd = -1; - union sockaddr_union sa; - int n = 0; - char *message = NULL; - - /* Don't generate plymouth events if the service was already - * started and we're just deserializing */ - if (m->n_reloading > 0) - return; - - if (m->running_as != SYSTEMD_SYSTEM) - return; - - if (u->type != UNIT_SERVICE && - u->type != UNIT_MOUNT && - u->type != UNIT_SWAP) - return; - - /* We set SOCK_NONBLOCK here so that we rather drop the - * message then wait for plymouth */ - if ((fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0)) < 0) { - log_error("socket() failed: %m"); - return; - } - - zero(sa); - sa.sa.sa_family = AF_UNIX; - strncpy(sa.un.sun_path+1, "/org/freedesktop/plymouthd", sizeof(sa.un.sun_path)-1); - if (connect(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + 1 + strlen(sa.un.sun_path+1)) < 0) { - - if (errno != EPIPE && - errno != EAGAIN && - errno != ENOENT && - errno != ECONNREFUSED && - errno != ECONNRESET && - errno != ECONNABORTED) - log_error("connect() failed: %m"); - - goto finish; - } - - if (asprintf(&message, "U\002%c%s%n", (int) (strlen(u->id) + 1), u->id, &n) < 0) { - log_oom(); - goto finish; - } - - errno = 0; - if (write(fd, message, n + 1) != n + 1) { - - if (errno != EPIPE && - errno != EAGAIN && - errno != ENOENT && - errno != ECONNREFUSED && - errno != ECONNRESET && - errno != ECONNABORTED) - log_error("Failed to write Plymouth message: %m"); - - goto finish; - } - -finish: - if (fd >= 0) - close_nointr_nofail(fd); - - free(message); -} - -void manager_dispatch_bus_name_owner_changed( - Manager *m, - const char *name, - const char* old_owner, - const char *new_owner) { - - Unit *u; - - assert(m); - assert(name); - - if (!(u = hashmap_get(m->watch_bus, name))) - return; - - UNIT_VTABLE(u)->bus_name_owner_change(u, name, old_owner, new_owner); -} - -void manager_dispatch_bus_query_pid_done( - Manager *m, - const char *name, - pid_t pid) { - - Unit *u; - - assert(m); - assert(name); - assert(pid >= 1); - - if (!(u = hashmap_get(m->watch_bus, name))) - return; - - UNIT_VTABLE(u)->bus_query_pid_done(u, name, pid); -} - -int manager_open_serialization(Manager *m, FILE **_f) { - char *path = NULL; - mode_t saved_umask; - int fd; - FILE *f; - - assert(_f); - - 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()); - - if (!path) - return -ENOMEM; - - saved_umask = umask(0077); - fd = mkostemp(path, O_RDWR|O_CLOEXEC); - umask(saved_umask); - - if (fd < 0) { - free(path); - return -errno; - } - - unlink(path); - - log_debug("Serializing state to %s", path); - free(path); - - if (!(f = fdopen(fd, "w+"))) - return -errno; - - *_f = f; - - return 0; -} - -int manager_serialize(Manager *m, FILE *f, FDSet *fds, bool serialize_jobs) { - Iterator i; - Unit *u; - const char *t; - int r; - - assert(m); - assert(f); - assert(fds); - - m->n_reloading ++; - - fprintf(f, "current-job-id=%i\n", m->current_job_id); - fprintf(f, "taint-usr=%s\n", yes_no(m->taint_usr)); - 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, "userspace-timestamp", &m->userspace_timestamp); - dual_timestamp_serialize(f, "finish-timestamp", &m->finish_timestamp); - } - - fputc('\n', f); - - HASHMAP_FOREACH_KEY(u, t, m->units, i) { - if (u->id != t) - continue; - - if (!unit_can_serialize(u)) - continue; - - /* Start marker */ - fputs(u->id, f); - fputc('\n', f); - - if ((r = unit_serialize(u, f, fds, serialize_jobs)) < 0) { - m->n_reloading --; - return r; - } - } - - assert(m->n_reloading > 0); - m->n_reloading --; - - if (ferror(f)) - return -EIO; - - r = bus_fdset_add_all(m, fds); - if (r < 0) - return r; - - return 0; -} - -int manager_deserialize(Manager *m, FILE *f, FDSet *fds) { - int r = 0; - - assert(m); - assert(f); - - log_debug("Deserializing state..."); - - m->n_reloading ++; - - for (;;) { - char line[LINE_MAX], *l; - - if (!fgets(line, sizeof(line), f)) { - if (feof(f)) - r = 0; - else - r = -errno; - - goto finish; - } - - char_array_0(line); - l = strstrip(line); - - if (l[0] == 0) - break; - - if (startswith(l, "current-job-id=")) { - uint32_t id; - - if (safe_atou32(l+15, &id) < 0) - log_debug("Failed to parse current job id value %s", l+15); - else - m->current_job_id = MAX(m->current_job_id, id); - } else if (startswith(l, "n-installed-jobs=")) { - uint32_t n; - - if (safe_atou32(l+17, &n) < 0) - log_debug("Failed to parse installed jobs counter %s", l+17); - else - m->n_installed_jobs += n; - } else if (startswith(l, "n-failed-jobs=")) { - uint32_t n; - - if (safe_atou32(l+14, &n) < 0) - log_debug("Failed to parse failed jobs counter %s", l+14); - else - m->n_failed_jobs += n; - } else if (startswith(l, "taint-usr=")) { - int b; - - if ((b = parse_boolean(l+10)) < 0) - log_debug("Failed to parse taint /usr flag %s", l+10); - else - m->taint_usr = m->taint_usr || b; - } 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, "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 - log_debug("Unknown serialization item '%s'", l); - } - - for (;;) { - Unit *u; - char name[UNIT_NAME_MAX+2]; - - /* Start marker */ - if (!fgets(name, sizeof(name), f)) { - if (feof(f)) - r = 0; - else - r = -errno; - - goto finish; - } - - char_array_0(name); - - if ((r = manager_load_unit(m, strstrip(name), NULL, NULL, &u)) < 0) - goto finish; - - if ((r = unit_deserialize(u, f, fds)) < 0) - goto finish; - } - -finish: - if (ferror(f)) { - r = -EIO; - goto finish; - } - - assert(m->n_reloading > 0); - m->n_reloading --; - - return r; -} - -int manager_reload(Manager *m) { - int r, q; - FILE *f; - FDSet *fds; - - assert(m); - - r = manager_open_serialization(m, &f); - if (r < 0) - return r; - - m->n_reloading ++; - - fds = fdset_new(); - if (!fds) { - m->n_reloading --; - r = -ENOMEM; - goto finish; - } - - r = manager_serialize(m, f, fds, true); - if (r < 0) { - m->n_reloading --; - goto finish; - } - - if (fseeko(f, 0, SEEK_SET) < 0) { - m->n_reloading --; - r = -errno; - goto finish; - } - - /* From here on there is no way back. */ - manager_clear_jobs_and_units(m); - manager_undo_generators(m); - lookup_paths_free(&m->lookup_paths); - - /* Find new unit paths */ - manager_run_generators(m); - - q = lookup_paths_init( - &m->lookup_paths, m->running_as, true, - m->generator_unit_path, - m->generator_unit_path_early, - m->generator_unit_path_late); - if (q < 0) - r = q; - - manager_build_unit_path_cache(m); - - /* First, enumerate what we can from all config files */ - q = manager_enumerate(m); - if (q < 0) - r = q; - - /* Second, deserialize our stored data */ - q = manager_deserialize(m, f, fds); - if (q < 0) - r = q; - - fclose(f); - f = NULL; - - /* Third, fire things up! */ - q = manager_coldplug(m); - if (q < 0) - r = q; - - assert(m->n_reloading > 0); - m->n_reloading--; - -finish: - if (f) - fclose(f); - - if (fds) - fdset_free(fds); - - return r; -} - -bool manager_is_booting_or_shutting_down(Manager *m) { - Unit *u; - - assert(m); - - /* Is the initial job still around? */ - if (manager_get_job(m, m->default_unit_job_id)) - return true; - - /* Is there a job for the shutdown target? */ - u = manager_get_unit(m, SPECIAL_SHUTDOWN_TARGET); - if (u) - return !!u->job; - - return false; -} - -void manager_reset_failed(Manager *m) { - Unit *u; - Iterator i; - - assert(m); - - HASHMAP_FOREACH(u, m->units, i) - unit_reset_failed(u); -} - -bool manager_unit_pending_inactive(Manager *m, const char *name) { - Unit *u; - - assert(m); - assert(name); - - /* Returns true if the unit is inactive or going down */ - if (!(u = manager_get_unit(m, name))) - return true; - - return unit_pending_inactive(u); -} - -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 firmware_usec, loader_usec, kernel_usec, initrd_usec, userspace_usec, total_usec; - - assert(m); - - if (hashmap_size(m->jobs) > 0) - return; - - /* Notify Type=idle units that we are done now */ - close_pipe(m->idle_pipe); - - /* Turn off confirm spawn now */ - m->confirm_spawn = false; - - if (dual_timestamp_is_set(&m->finish_timestamp)) - return; - - dual_timestamp_get(&m->finish_timestamp); - - if (m->running_as == SYSTEMD_SYSTEM && detect_container(NULL) <= 0) { - - /* 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. */ - - 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; - - if (dual_timestamp_is_set(&m->initrd_timestamp)) { - - 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->userspace_timestamp.monotonic - m->kernel_timestamp.monotonic; - initrd_usec = 0; - - 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 { - 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, firmware_usec, loader_usec, kernel_usec, initrd_usec, userspace_usec, total_usec); - - sd_notifyf(false, - "READY=1\nSTATUS=Startup finished in %s.", - format_timespan(sum, sizeof(sum), total_usec)); -} - -static int create_generator_dir(Manager *m, char **generator, const char *name) { - char *p; - int r; - - assert(m); - assert(generator); - assert(name); - - if (*generator) - return 0; - - if (m->running_as == SYSTEMD_SYSTEM && getpid() == 1) { - - p = strappend("/run/systemd/", name); - if (!p) - return log_oom(); - - r = mkdir_p_label(p, 0755); - if (r < 0) { - log_error("Failed to create generator directory: %s", strerror(-r)); - free(p); - return r; - } - } else { - p = strjoin("/tmp/systemd-", name, ".XXXXXX", NULL); - if (!p) - return log_oom(); - - if (!mkdtemp(p)) { - free(p); - log_error("Failed to create generator directory: %m"); - return -errno; - } - } - - *generator = p; - return 0; -} - -static void trim_generator_dir(Manager *m, char **generator) { - assert(m); - assert(generator); - - if (!*generator) - return; - - if (rmdir(*generator) >= 0) { - free(*generator); - *generator = NULL; - } - - return; -} - -void manager_run_generators(Manager *m) { - DIR *d = NULL; - const char *generator_path; - const char *argv[5]; - mode_t u; - int r; - - assert(m); - - generator_path = m->running_as == SYSTEMD_SYSTEM ? SYSTEM_GENERATOR_PATH : USER_GENERATOR_PATH; - d = opendir(generator_path); - if (!d) { - if (errno == ENOENT) - return; - - log_error("Failed to enumerate generator directory: %m"); - return; - } - - r = create_generator_dir(m, &m->generator_unit_path, "generator"); - if (r < 0) - goto finish; - - r = create_generator_dir(m, &m->generator_unit_path_early, "generator.early"); - if (r < 0) - goto finish; - - r = create_generator_dir(m, &m->generator_unit_path_late, "generator.late"); - if (r < 0) - goto finish; - - argv[0] = NULL; /* Leave this empty, execute_directory() will fill something in */ - argv[1] = m->generator_unit_path; - argv[2] = m->generator_unit_path_early; - argv[3] = m->generator_unit_path_late; - argv[4] = NULL; - - u = umask(0022); - execute_directory(generator_path, d, (char**) argv); - umask(u); - - trim_generator_dir(m, &m->generator_unit_path); - trim_generator_dir(m, &m->generator_unit_path_early); - trim_generator_dir(m, &m->generator_unit_path_late); - -finish: - if (d) - closedir(d); -} - -static void remove_generator_dir(Manager *m, char **generator) { - assert(m); - assert(generator); - - if (!*generator) - return; - - strv_remove(m->lookup_paths.unit_path, *generator); - rm_rf(*generator, false, true, false); - - free(*generator); - *generator = NULL; -} - -void manager_undo_generators(Manager *m) { - assert(m); - - remove_generator_dir(m, &m->generator_unit_path); - remove_generator_dir(m, &m->generator_unit_path_early); - remove_generator_dir(m, &m->generator_unit_path_late); -} - -int manager_set_default_controllers(Manager *m, char **controllers) { - char **l; - - assert(m); - - l = strv_copy(controllers); - if (!l) - return -ENOMEM; - - strv_free(m->default_controllers); - m->default_controllers = l; - - cg_shorten_controllers(m->default_controllers); - - return 0; -} - -int manager_set_default_rlimits(Manager *m, struct rlimit **default_rlimit) { - int i; - - assert(m); - - for (i = 0; i < RLIMIT_NLIMITS; i++) { - if (!default_rlimit[i]) - continue; - - m->rlimit[i] = newdup(struct rlimit, default_rlimit[i], 1); - if (!m->rlimit[i]) - return -ENOMEM; - } - - return 0; -} - -void manager_recheck_journal(Manager *m) { - Unit *u; - - assert(m); - - if (m->running_as != SYSTEMD_SYSTEM) - return; - - u = manager_get_unit(m, SPECIAL_JOURNALD_SOCKET); - if (u && SOCKET(u)->state != SOCKET_RUNNING) { - log_close_journal(); - return; - } - - u = manager_get_unit(m, SPECIAL_JOURNALD_SERVICE); - if (u && SERVICE(u)->state != SERVICE_RUNNING) { - log_close_journal(); - return; - } - - /* Hmm, OK, so the socket is fully up and the service is up - * too, then let's make use of the thing. */ - log_open(); -} - -void manager_set_show_status(Manager *m, bool b) { - assert(m); - - if (m->running_as != SYSTEMD_SYSTEM) - return; - - m->show_status = b; - - if (b) - touch("/run/systemd/show-status"); - else - unlink("/run/systemd/show-status"); -} - -bool manager_get_show_status(Manager *m) { - assert(m); - - if (m->running_as != SYSTEMD_SYSTEM) - return false; - - if (m->show_status) - return true; - - /* If Plymouth is running make sure we show the status, so - * that there's something nice to see when people press Esc */ - - return plymouth_running(); -} diff --git a/src/core/manager.h b/src/core/manager.h deleted file mode 100644 index 22145024f1..0000000000 --- a/src/core/manager.h +++ /dev/null @@ -1,293 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -#pragma once - -/*** - 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 <stdbool.h> -#include <inttypes.h> -#include <stdio.h> -#include <dbus/dbus.h> - -#include "fdset.h" - -/* Enforce upper limit how many names we allow */ -#define MANAGER_MAX_NAMES 131072 /* 128K */ - -typedef struct Manager Manager; -typedef enum WatchType WatchType; -typedef struct Watch Watch; - -typedef enum ManagerExitCode { - MANAGER_RUNNING, - MANAGER_EXIT, - MANAGER_RELOAD, - MANAGER_REEXECUTE, - MANAGER_REBOOT, - MANAGER_POWEROFF, - MANAGER_HALT, - MANAGER_KEXEC, - MANAGER_SWITCH_ROOT, - _MANAGER_EXIT_CODE_MAX, - _MANAGER_EXIT_CODE_INVALID = -1 -} ManagerExitCode; - -enum WatchType { - WATCH_INVALID, - WATCH_SIGNAL, - WATCH_NOTIFY, - WATCH_FD, - WATCH_UNIT_TIMER, - WATCH_JOB_TIMER, - WATCH_MOUNT, - WATCH_SWAP, - WATCH_UDEV, - WATCH_DBUS_WATCH, - WATCH_DBUS_TIMEOUT -}; - -struct Watch { - int fd; - WatchType type; - union { - struct Unit *unit; - struct Job *job; - DBusWatch *bus_watch; - DBusTimeout *bus_timeout; - } data; - bool fd_is_dupped:1; - bool socket_accept:1; -}; - -#include "unit.h" -#include "job.h" -#include "hashmap.h" -#include "list.h" -#include "set.h" -#include "dbus.h" -#include "path-lookup.h" - -struct Manager { - /* Note that the set of units we know of is allowed to be - * inconsistent. However the subset of it that is loaded may - * not, and the list of jobs may neither. */ - - /* Active jobs and units */ - Hashmap *units; /* name string => Unit object n:1 */ - Hashmap *jobs; /* job id => Job object 1:1 */ - - /* To make it easy to iterate through the units of a specific - * type we maintain a per type linked list */ - LIST_HEAD(Unit, units_by_type[_UNIT_TYPE_MAX]); - - /* To optimize iteration of units that have requires_mounts_for set */ - LIST_HEAD(Unit, has_requires_mounts_for); - - /* Units that need to be loaded */ - LIST_HEAD(Unit, load_queue); /* this is actually more a stack than a queue, but uh. */ - - /* Jobs that need to be run */ - LIST_HEAD(Job, run_queue); /* more a stack than a queue, too */ - - /* Units and jobs that have not yet been announced via - * D-Bus. When something about a job changes it is added here - * if it is not in there yet. This allows easy coalescing of - * D-Bus change signals. */ - LIST_HEAD(Unit, dbus_unit_queue); - LIST_HEAD(Job, dbus_job_queue); - - /* Units to remove */ - LIST_HEAD(Unit, cleanup_queue); - - /* Units to check when doing GC */ - LIST_HEAD(Unit, gc_queue); - - Hashmap *watch_pids; /* pid => Unit object n:1 */ - - char *notify_socket; - - Watch notify_watch; - Watch signal_watch; - - int epoll_fd; - - unsigned n_snapshots; - - LookupPaths lookup_paths; - Set *unit_path_cache; - - char **environment; - char **default_controllers; - - 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 userspace_timestamp; - dual_timestamp finish_timestamp; - - char *generator_unit_path; - char *generator_unit_path_early; - char *generator_unit_path_late; - - /* Data specific to the device subsystem */ - struct udev* udev; - struct udev_monitor* udev_monitor; - Watch udev_watch; - Hashmap *devices_by_sysfs; - - /* Data specific to the mount subsystem */ - FILE *proc_self_mountinfo; - Watch mount_watch; - - /* Data specific to the swap filesystem */ - FILE *proc_swaps; - Hashmap *swaps_by_proc_swaps; - bool request_reload; - Watch swap_watch; - - /* Data specific to the D-Bus subsystem */ - DBusConnection *api_bus, *system_bus; - DBusServer *private_bus; - Set *bus_connections, *bus_connections_for_dispatch; - - DBusMessage *queued_message; /* This is used during reloading: - * before the reload we queue the - * reply message here, and - * afterwards we send it */ - DBusConnection *queued_message_connection; /* The connection to send the queued message on */ - - Hashmap *watch_bus; /* D-Bus names => Unit object n:1 */ - int32_t name_data_slot; - int32_t conn_data_slot; - int32_t subscribed_data_slot; - - uint32_t current_job_id; - uint32_t default_unit_job_id; - - /* Data specific to the Automount subsystem */ - int dev_autofs_fd; - - /* Data specific to the cgroup subsystem */ - Hashmap *cgroup_bondings; /* path string => CGroupBonding object 1:n */ - char *cgroup_hierarchy; - - usec_t gc_queue_timestamp; - int gc_marker; - unsigned n_in_gc_queue; - - /* Make sure the user cannot accidentally unmount our cgroup - * file system */ - int pin_cgroupfs_fd; - - /* Flags */ - SystemdRunningAs running_as; - ManagerExitCode exit_code:5; - - bool dispatching_load_queue:1; - bool dispatching_run_queue:1; - bool dispatching_dbus_queue:1; - - bool taint_usr:1; - - bool show_status; - bool confirm_spawn; - - ExecOutput default_std_output, default_std_error; - - struct rlimit *rlimit[RLIMIT_NLIMITS]; - - /* non-zero if we are reloading or reexecuting, */ - int n_reloading; - - unsigned n_installed_jobs; - unsigned n_failed_jobs; - - /* Type=idle pipes */ - int idle_pipe[2]; - - char *switch_root; - char *switch_root_init; -}; - -int manager_new(SystemdRunningAs running_as, Manager **m); -void manager_free(Manager *m); - -int manager_enumerate(Manager *m); -int manager_coldplug(Manager *m); -int manager_startup(Manager *m, FILE *serialization, FDSet *fds); - -Job *manager_get_job(Manager *m, uint32_t id); -Unit *manager_get_unit(Manager *m, const char *name); - -int manager_get_job_from_dbus_path(Manager *m, const char *s, Job **_j); - -int manager_load_unit_prepare(Manager *m, const char *name, const char *path, DBusError *e, Unit **_ret); -int manager_load_unit(Manager *m, const char *name, const char *path, DBusError *e, Unit **_ret); -int manager_load_unit_from_dbus_path(Manager *m, const char *s, DBusError *e, Unit **_u); - -int manager_add_job(Manager *m, JobType type, Unit *unit, JobMode mode, bool force, DBusError *e, Job **_ret); -int manager_add_job_by_name(Manager *m, JobType type, const char *name, JobMode mode, bool force, DBusError *e, Job **_ret); - -void manager_dump_units(Manager *s, FILE *f, const char *prefix); -void manager_dump_jobs(Manager *s, FILE *f, const char *prefix); - -void manager_clear_jobs(Manager *m); - -unsigned manager_dispatch_load_queue(Manager *m); -unsigned manager_dispatch_run_queue(Manager *m); -unsigned manager_dispatch_dbus_queue(Manager *m); - -int manager_set_default_controllers(Manager *m, char **controllers); -int manager_set_default_rlimits(Manager *m, struct rlimit **default_rlimit); - -int manager_loop(Manager *m); - -void manager_dispatch_bus_name_owner_changed(Manager *m, const char *name, const char* old_owner, const char *new_owner); -void manager_dispatch_bus_query_pid_done(Manager *m, const char *name, pid_t pid); - -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_reload(Manager *m); - -bool manager_is_booting_or_shutting_down(Manager *m); - -void manager_reset_failed(Manager *m); - -void manager_send_unit_audit(Manager *m, Unit *u, int type, bool success); -void manager_send_unit_plymouth(Manager *m, Unit *u); - -bool manager_unit_pending_inactive(Manager *m, const char *name); - -void manager_check_finished(Manager *m); - -void manager_run_generators(Manager *m); -void manager_undo_generators(Manager *m); - -void manager_recheck_journal(Manager *m); - -void manager_set_show_status(Manager *m, bool b); -bool manager_get_show_status(Manager *m); diff --git a/src/core/mount-setup.c b/src/core/mount-setup.c deleted file mode 100644 index 98614d0c3e..0000000000 --- a/src/core/mount-setup.c +++ /dev/null @@ -1,441 +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 <sys/mount.h> -#include <errno.h> -#include <sys/stat.h> -#include <stdlib.h> -#include <string.h> -#include <libgen.h> -#include <assert.h> -#include <unistd.h> -#include <ftw.h> - -#include "mount-setup.h" -#include "dev-setup.h" -#include "log.h" -#include "macro.h" -#include "util.h" -#include "label.h" -#include "set.h" -#include "strv.h" -#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 (*condition_fn)(void); - MountMode mode; -} MountPoint; - -/* The first three entries we might need before SELinux is up. The - * fourth (securityfs) is needed by IMA to load a custom policy. The - * other ones we can delay until SELinux and IMA are loaded. */ -#define N_EARLY_MOUNT 4 - -static const MountPoint mount_table[] = { - { "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, - * we just list them here so that we know that we should ignore them */ - -static const char ignore_paths[] = - /* SELinux file systems */ - "/sys/fs/selinux\0" - "/selinux\0" - /* Legacy cgroup mount points */ - "/dev/cgroup\0" - "/cgroup\0" - /* Legacy kernel file system */ - "/proc/bus/usb\0" - /* Container bind mounts */ - "/proc/sys\0" - "/dev/console\0" - "/proc/kmsg\0"; - -bool mount_point_is_api(const char *path) { - unsigned i; - - /* Checks if this mount point is considered "API", and hence - * should be ignored */ - - for (i = 0; i < ELEMENTSOF(mount_table); i ++) - if (path_equal(path, mount_table[i].where)) - return true; - - return path_startswith(path, "/sys/fs/cgroup/"); -} - -bool mount_point_ignore(const char *path) { - const char *i; - - NULSTR_FOREACH(i, ignore_paths) - if (path_equal(path, i)) - return true; - - return false; -} - -static int mount_one(const MountPoint *p, bool relabel) { - int r; - - assert(p); - - if (p->condition_fn && !p->condition_fn()) - return 0; - - /* Relabel first, just in case */ - if (relabel) - label_fix(p->where, true, true); - - 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); - - log_debug("Mounting %s to %s of type %s with options %s.", - p->what, - p->where, - p->type, - strna(p->options)); - - if (mount(p->what, - p->where, - p->type, - p->flags, - p->options) < 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 */ - if (relabel) - label_fix(p->where, false, false); - - return 1; -} - -int mount_setup_early(void) { - unsigned i; - int r = 0; - - assert_cc(N_EARLY_MOUNT <= ELEMENTSOF(mount_table)); - - /* Do a minimal mount of /proc and friends to enable the most - * basic stuff, such as SELinux */ - for (i = 0; i < N_EARLY_MOUNT; i ++) { - int j; - - j = mount_one(mount_table + i, false); - if (r == 0) - r = j; - } - - return r; -} - -int mount_cgroup_controllers(char ***join_controllers) { - int r; - FILE *f; - char buf[LINE_MAX]; - Set *controllers; - - /* Mount all available cgroup controllers that are built into the kernel. */ - - f = fopen("/proc/cgroups", "re"); - if (!f) { - log_error("Failed to enumerate cgroup controllers: %m"); - return 0; - } - - controllers = set_new(string_hash_func, string_compare_func); - if (!controllers) { - r = log_oom(); - goto finish; - } - - /* Ignore the header line */ - (void) fgets(buf, sizeof(buf), f); - - for (;;) { - char *controller; - int enabled = 0; - - if (fscanf(f, "%ms %*i %*i %i", &controller, &enabled) != 2) { - - if (feof(f)) - break; - - log_error("Failed to parse /proc/cgroups."); - r = -EIO; - goto finish; - } - - if (!enabled) { - free(controller); - continue; - } - - r = set_put(controllers, controller); - if (r < 0) { - log_error("Failed to add controller to set."); - free(controller); - goto finish; - } - } - - for (;;) { - MountPoint p; - char *controller, *where, *options; - char ***k = NULL; - - controller = set_steal_first(controllers); - if (!controller) - break; - - if (join_controllers) - for (k = join_controllers; *k; k++) - if (strv_find(*k, controller)) - break; - - if (k && *k) { - char **i, **j; - - for (i = *k, j = *k; *i; i++) { - - if (!streq(*i, controller)) { - char *t; - - t = set_remove(controllers, *i); - if (!t) { - free(*i); - continue; - } - free(t); - } - - *(j++) = *i; - } - - *j = NULL; - - options = strv_join(*k, ","); - if (!options) { - free(controller); - r = log_oom(); - goto finish; - } - - } else { - options = controller; - controller = NULL; - } - - where = strappend("/sys/fs/cgroup/", options); - if (!where) { - free(options); - r = log_oom(); - goto finish; - } - - zero(p); - p.what = "cgroup"; - p.where = where; - p.type = "cgroup"; - p.options = options; - p.flags = MS_NOSUID|MS_NOEXEC|MS_NODEV; - - r = mount_one(&p, true); - free(controller); - free(where); - - if (r < 0) { - free(options); - goto finish; - } - - if (r > 0 && k && *k) { - char **i; - - for (i = *k; *i; i++) { - char *t; - - t = strappend("/sys/fs/cgroup/", *i); - if (!t) { - r = log_oom(); - free(options); - goto finish; - } - - r = symlink(options, t); - free(t); - - if (r < 0 && errno != EEXIST) { - log_error("Failed to create symlink: %m"); - r = -errno; - free(options); - goto finish; - } - } - } - - free(options); - } - - r = 0; - -finish: - set_free_free(controllers); - - fclose(f); - - return r; -} - -static int nftw_cb( - const char *fpath, - const struct stat *sb, - int tflag, - struct FTW *ftwbuf) { - - /* No need to label /dev twice in a row... */ - if (_unlikely_(ftwbuf->level == 0)) - return FTW_CONTINUE; - - label_fix(fpath, false, false); - - /* /run/initramfs is static data and big, no need to - * dynamically relabel its contents at boot... */ - if (_unlikely_(ftwbuf->level == 1 && - tflag == FTW_D && - streq(fpath, "/run/initramfs"))) - return FTW_SKIP_SUBTREE; - - return FTW_CONTINUE; -}; - -int mount_setup(bool loaded_policy) { - - static const char relabel[] = - "/run/initramfs/root-fsck\0" - "/run/initramfs/shutdown\0"; - - int r; - unsigned i; - const char *j; - - for (i = 0; i < ELEMENTSOF(mount_table); i ++) { - r = mount_one(mount_table + i, true); - - if (r < 0) - return r; - } - - /* Nodes in devtmpfs and /run need to be manually updated for - * the appropriate labels, after mounting. The other virtual - * API file systems like /sys and /proc do not need that, they - * use the same label for all their files. */ - if (loaded_policy) { - usec_t before_relabel, after_relabel; - char timespan[FORMAT_TIMESPAN_MAX]; - - before_relabel = now(CLOCK_MONOTONIC); - - nftw("/dev", nftw_cb, 64, FTW_MOUNT|FTW_PHYS|FTW_ACTIONRETVAL); - nftw("/run", nftw_cb, 64, FTW_MOUNT|FTW_PHYS|FTW_ACTIONRETVAL); - - /* Explicitly relabel these */ - NULSTR_FOREACH(j, relabel) - label_fix(j, true, false); - - after_relabel = now(CLOCK_MONOTONIC); - - log_info("Relabelled /dev and /run in %s.", - format_timespan(timespan, sizeof(timespan), after_relabel - before_relabel)); - } - - /* Create a few default symlinks, which are normally created - * by udevd, but some scripts might need them before we start - * udevd. */ - 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); - mkdir_label("/run/systemd/system", 0755); - - return 0; -} diff --git a/src/core/mount-setup.h b/src/core/mount-setup.h deleted file mode 100644 index 4b521ad0e1..0000000000 --- a/src/core/mount-setup.h +++ /dev/null @@ -1,33 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -#pragma once - -/*** - 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 <stdbool.h> - -int mount_setup_early(void); - -int mount_setup(bool loaded_policy); - -int mount_cgroup_controllers(char ***join_controllers); - -bool mount_point_is_api(const char *path); -bool mount_point_ignore(const char *path); diff --git a/src/core/mount.c b/src/core/mount.c deleted file mode 100644 index 14f4863dc6..0000000000 --- a/src/core/mount.c +++ /dev/null @@ -1,1885 +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 <stdio.h> -#include <mntent.h> -#include <sys/epoll.h> -#include <signal.h> - -#include "unit.h" -#include "mount.h" -#include "load-fragment.h" -#include "load-dropin.h" -#include "log.h" -#include "strv.h" -#include "mkdir.h" -#include "path-util.h" -#include "mount-setup.h" -#include "unit-name.h" -#include "dbus-mount.h" -#include "special.h" -#include "bus-errors.h" -#include "exit-status.h" -#include "def.h" - -static const UnitActiveState state_translation_table[_MOUNT_STATE_MAX] = { - [MOUNT_DEAD] = UNIT_INACTIVE, - [MOUNT_MOUNTING] = UNIT_ACTIVATING, - [MOUNT_MOUNTING_DONE] = UNIT_ACTIVE, - [MOUNT_MOUNTED] = UNIT_ACTIVE, - [MOUNT_REMOUNTING] = UNIT_RELOADING, - [MOUNT_UNMOUNTING] = UNIT_DEACTIVATING, - [MOUNT_MOUNTING_SIGTERM] = UNIT_DEACTIVATING, - [MOUNT_MOUNTING_SIGKILL] = UNIT_DEACTIVATING, - [MOUNT_REMOUNTING_SIGTERM] = UNIT_RELOADING, - [MOUNT_REMOUNTING_SIGKILL] = UNIT_RELOADING, - [MOUNT_UNMOUNTING_SIGTERM] = UNIT_DEACTIVATING, - [MOUNT_UNMOUNTING_SIGKILL] = UNIT_DEACTIVATING, - [MOUNT_FAILED] = UNIT_FAILED -}; - -static void mount_init(Unit *u) { - Mount *m = MOUNT(u); - - assert(u); - assert(u->load_state == UNIT_STUB); - - m->timeout_usec = DEFAULT_TIMEOUT_USEC; - m->directory_mode = 0755; - - exec_context_init(&m->exec_context); - - if (unit_has_name(u, "-.mount")) { - /* Don't allow start/stop for root directory */ - UNIT(m)->refuse_manual_start = true; - UNIT(m)->refuse_manual_stop = true; - } else { - /* The stdio/kmsg bridge socket is on /, in order to avoid a - * dep loop, don't use kmsg logging for -.mount */ - m->exec_context.std_output = u->manager->default_std_output; - m->exec_context.std_error = u->manager->default_std_error; - } - - kill_context_init(&m->kill_context); - - /* We need to make sure that /bin/mount is always called in - * the same process group as us, so that the autofs kernel - * side doesn't send us another mount request while we are - * already trying to comply its last one. */ - m->exec_context.same_pgrp = true; - - m->timer_watch.type = WATCH_INVALID; - - m->control_command_id = _MOUNT_EXEC_COMMAND_INVALID; - - UNIT(m)->ignore_on_isolate = true; -} - -static void mount_unwatch_control_pid(Mount *m) { - assert(m); - - if (m->control_pid <= 0) - return; - - unit_unwatch_pid(UNIT(m), m->control_pid); - m->control_pid = 0; -} - -static void mount_parameters_done(MountParameters *p) { - assert(p); - - free(p->what); - free(p->options); - free(p->fstype); - - p->what = p->options = p->fstype = NULL; -} - -static void mount_done(Unit *u) { - Mount *m = MOUNT(u); - - assert(m); - - free(m->where); - m->where = NULL; - - mount_parameters_done(&m->parameters_proc_self_mountinfo); - mount_parameters_done(&m->parameters_fragment); - - exec_context_done(&m->exec_context); - exec_command_done_array(m->exec_command, _MOUNT_EXEC_COMMAND_MAX); - m->control_command = NULL; - - mount_unwatch_control_pid(m); - - unit_unwatch_timer(u, &m->timer_watch); -} - -static MountParameters* get_mount_parameters_fragment(Mount *m) { - assert(m); - - if (m->from_fragment) - return &m->parameters_fragment; - - return NULL; -} - -static MountParameters* get_mount_parameters(Mount *m) { - assert(m); - - if (m->from_proc_self_mountinfo) - return &m->parameters_proc_self_mountinfo; - - return get_mount_parameters_fragment(m); -} - -static int mount_add_mount_links(Mount *m) { - Unit *other; - int r; - MountParameters *pm; - - assert(m); - - pm = get_mount_parameters_fragment(m); - - /* Adds in links to other mount points that might lie below or - * above us in the hierarchy */ - - LIST_FOREACH(units_by_type, other, UNIT(m)->manager->units_by_type[UNIT_MOUNT]) { - Mount *n = MOUNT(other); - MountParameters *pn; - - if (n == m) - continue; - - if (UNIT(n)->load_state != UNIT_LOADED) - continue; - - pn = get_mount_parameters_fragment(n); - - if (path_startswith(m->where, n->where)) { - - if ((r = unit_add_dependency(UNIT(m), UNIT_AFTER, UNIT(n), true)) < 0) - return r; - - if (pn) - if ((r = unit_add_dependency(UNIT(m), UNIT_REQUIRES, UNIT(n), true)) < 0) - return r; - - } else if (path_startswith(n->where, m->where)) { - - if ((r = unit_add_dependency(UNIT(n), UNIT_AFTER, UNIT(m), true)) < 0) - return r; - - if (pm) - if ((r = unit_add_dependency(UNIT(n), UNIT_REQUIRES, UNIT(m), true)) < 0) - return r; - - } else if (pm && pm->what && path_startswith(pm->what, n->where)) { - - if ((r = unit_add_dependency(UNIT(m), UNIT_AFTER, UNIT(n), true)) < 0) - return r; - - if ((r = unit_add_dependency(UNIT(m), UNIT_REQUIRES, UNIT(n), true)) < 0) - return r; - - } else if (pn && pn->what && path_startswith(pn->what, m->where)) { - - if ((r = unit_add_dependency(UNIT(n), UNIT_AFTER, UNIT(m), true)) < 0) - return r; - - if ((r = unit_add_dependency(UNIT(n), UNIT_REQUIRES, UNIT(m), true)) < 0) - return r; - } - } - - return 0; -} - -static int mount_add_swap_links(Mount *m) { - Unit *other; - int r; - - 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) - return r; - - return 0; -} - -static int mount_add_path_links(Mount *m) { - Unit *other; - int r; - - 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) - return r; - - return 0; -} - -static int mount_add_automount_links(Mount *m) { - Unit *other; - int r; - - 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) - return r; - - return 0; -} - -static int mount_add_socket_links(Mount *m) { - Unit *other; - int r; - - 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) - return r; - - return 0; -} - -static int mount_add_requires_mounts_links(Mount *m) { - Unit *other; - int r; - - assert(m); - - LIST_FOREACH(has_requires_mounts_for, other, UNIT(m)->manager->has_requires_mounts_for) { - r = unit_add_one_mount_link(other, m); - if (r < 0) - return r; - } - - return 0; -} - -static char* mount_test_option(const char *haystack, const char *needle) { - struct mntent me; - - assert(needle); - - /* Like glibc's hasmntopt(), but works on a string, not a - * struct mntent */ - - if (!haystack) - return NULL; - - zero(me); - me.mnt_opts = (char*) haystack; - - return hasmntopt(&me, needle); -} - -static bool mount_is_network(MountParameters *p) { - assert(p); - - if (mount_test_option(p->options, "_netdev")) - return true; - - if (p->fstype && fstype_is_network(p->fstype)) - return true; - - return false; -} - -static bool mount_is_bind(MountParameters *p) { - assert(p); - - if (mount_test_option(p->options, "bind")) - return true; - - if (p->fstype && streq(p->fstype, "bind")) - return true; - - return false; -} - -static bool needs_quota(MountParameters *p) { - assert(p); - - if (mount_is_network(p)) - return false; - - if (mount_is_bind(p)) - return false; - - return mount_test_option(p->options, "usrquota") || - mount_test_option(p->options, "grpquota") || - mount_test_option(p->options, "quota") || - mount_test_option(p->options, "usrjquota") || - mount_test_option(p->options, "grpjquota"); -} - -static int mount_add_device_links(Mount *m) { - MountParameters *p; - int r; - - assert(m); - - p = get_mount_parameters_fragment(m); - if (!p) - return 0; - - if (!p->what) - return 0; - - 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 && - UNIT(m)->manager->running_as == SYSTEMD_SYSTEM) { - char *name; - Unit *fsck; - /* Let's add in the fsck service */ - - /* aka SPECIAL_FSCK_SERVICE */ - name = unit_name_from_path_instance("systemd-fsck", p->what, ".service"); - if (!name) - return -ENOMEM; - - 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)); - free(name); - return r; - } - free(name); - - SERVICE(fsck)->fsck_passno = p->passno; - - r = unit_add_two_dependencies(UNIT(m), UNIT_AFTER, UNIT_REQUIRES, fsck, true); - if (r < 0) - return r; - } - - return 0; -} - -static int mount_add_quota_links(Mount *m) { - int r; - MountParameters *p; - - assert(m); - - if (UNIT(m)->manager->running_as != SYSTEMD_SYSTEM) - return 0; - - p = get_mount_parameters_fragment(m); - if (!p) - return 0; - - if (!needs_quota(p)) - return 0; - - r = unit_add_two_dependencies_by_name(UNIT(m), UNIT_BEFORE, UNIT_WANTS, SPECIAL_QUOTACHECK_SERVICE, NULL, true); - if (r < 0) - return r; - - r = unit_add_two_dependencies_by_name(UNIT(m), UNIT_BEFORE, UNIT_WANTS, SPECIAL_QUOTAON_SERVICE, NULL, true); - if (r < 0) - return r; - - return 0; -} - -static int mount_add_default_dependencies(Mount *m) { - int r; - MountParameters *p; - const char *after; - - assert(m); - - if (UNIT(m)->manager->running_as != SYSTEMD_SYSTEM) - return 0; - - p = get_mount_parameters_fragment(m); - if (!p) - return 0; - - if (path_equal(m->where, "/")) - return 0; - - if (mount_is_network(p)) - after = SPECIAL_REMOTE_FS_PRE_TARGET; - else - after = SPECIAL_LOCAL_FS_PRE_TARGET; - - r = unit_add_two_dependencies_by_name(UNIT(m), UNIT_WANTS, UNIT_AFTER, after, NULL, true); - if (r < 0) - return r; - - r = unit_add_two_dependencies_by_name(UNIT(m), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_UMOUNT_TARGET, NULL, true); - if (r < 0) - return r; - - return 0; -} - -static int mount_fix_timeouts(Mount *m) { - MountParameters *p; - const char *timeout = NULL; - Unit *other; - Iterator i; - usec_t u; - char *t; - int r; - - assert(m); - - p = get_mount_parameters_fragment(m); - if (!p) - return 0; - - /* Allow configuration how long we wait for a device that - * backs a mount point to show up. This is useful to support - * endless device timeouts for devices that show up only after - * user input, like crypto devices. */ - - if ((timeout = mount_test_option(p->options, "comment=systemd.device-timeout"))) - timeout += 31; - else if ((timeout = mount_test_option(p->options, "x-systemd.device-timeout"))) - timeout += 25; - else - return 0; - - t = strndup(timeout, strcspn(timeout, ",;" WHITESPACE)); - if (!t) - return -ENOMEM; - - r = parse_usec(t, &u); - free(t); - - if (r < 0) { - log_warning("Failed to parse timeout for %s, ignoring: %s", m->where, timeout); - return r; - } - - SET_FOREACH(other, UNIT(m)->dependencies[UNIT_AFTER], i) { - if (other->type != UNIT_DEVICE) - continue; - - other->job_timeout = u; - } - - return 0; -} - -static int mount_verify(Mount *m) { - bool b; - char *e; - assert(m); - - if (UNIT(m)->load_state != UNIT_LOADED) - return 0; - - if (!m->from_fragment && !m->from_proc_self_mountinfo) - return -ENOENT; - - if (!(e = unit_name_from_path(m->where, ".mount"))) - return -ENOMEM; - - b = unit_has_name(UNIT(m), e); - free(e); - - if (!b) { - log_error("%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); - return -EINVAL; - } - - if (UNIT(m)->fragment_path && !m->parameters_fragment.what) { - log_error("%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); - return -EINVAL; - } - - return 0; -} - -static int mount_add_extras(Mount *m) { - Unit *u = UNIT(m); - int r; - - if (UNIT(m)->fragment_path) - m->from_fragment = true; - - if (!m->where) { - m->where = unit_name_to_path(u->id); - if (!m->where) - return -ENOMEM; - } - - 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) - return r; - } - - r = mount_add_device_links(m); - if (r < 0) - return r; - - r = mount_add_mount_links(m); - if (r < 0) - return r; - - r = mount_add_socket_links(m); - if (r < 0) - return r; - - r = mount_add_swap_links(m); - if (r < 0) - return r; - - r = mount_add_path_links(m); - if (r < 0) - return r; - - r = mount_add_requires_mounts_links(m); - if (r < 0) - return r; - - r = mount_add_automount_links(m); - if (r < 0) - return r; - - r = mount_add_quota_links(m); - if (r < 0) - return r; - - if (UNIT(m)->default_dependencies) { - r = mount_add_default_dependencies(m); - if (r < 0) - return r; - } - - r = unit_add_default_cgroups(u); - if (r < 0) - return r; - - r = mount_fix_timeouts(m); - if (r < 0) - return r; - - return 0; -} - -static int mount_load(Unit *u) { - Mount *m = MOUNT(u); - int r; - - assert(u); - assert(u->load_state == UNIT_STUB); - - if (m->from_proc_self_mountinfo) - r = unit_load_fragment_and_dropin_optional(u); - else - 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) { - r = mount_add_extras(m); - if (r < 0) - return r; - - r = unit_exec_context_defaults(u, &m->exec_context); - if (r < 0) - return r; - } - - return mount_verify(m); -} - -static int mount_notify_automount(Mount *m, int status) { - Unit *p; - int r; - Iterator i; - - assert(m); - - SET_FOREACH(p, UNIT(m)->dependencies[UNIT_TRIGGERED_BY], i) - if (p->type == UNIT_AUTOMOUNT) { - r = automount_send_ready(AUTOMOUNT(p), status); - if (r < 0) - return r; - } - - return 0; -} - -static void mount_set_state(Mount *m, MountState state) { - MountState old_state; - assert(m); - - old_state = m->state; - m->state = state; - - if (state != MOUNT_MOUNTING && - state != MOUNT_MOUNTING_DONE && - state != MOUNT_REMOUNTING && - state != MOUNT_UNMOUNTING && - state != MOUNT_MOUNTING_SIGTERM && - state != MOUNT_MOUNTING_SIGKILL && - state != MOUNT_UNMOUNTING_SIGTERM && - state != MOUNT_UNMOUNTING_SIGKILL && - state != MOUNT_REMOUNTING_SIGTERM && - state != MOUNT_REMOUNTING_SIGKILL) { - unit_unwatch_timer(UNIT(m), &m->timer_watch); - mount_unwatch_control_pid(m); - m->control_command = NULL; - m->control_command_id = _MOUNT_EXEC_COMMAND_INVALID; - } - - if (state == MOUNT_MOUNTED || - state == MOUNT_REMOUNTING) - mount_notify_automount(m, 0); - else if (state == MOUNT_DEAD || - state == MOUNT_UNMOUNTING || - state == MOUNT_MOUNTING_SIGTERM || - state == MOUNT_MOUNTING_SIGKILL || - state == MOUNT_REMOUNTING_SIGTERM || - state == MOUNT_REMOUNTING_SIGKILL || - state == MOUNT_UNMOUNTING_SIGTERM || - state == MOUNT_UNMOUNTING_SIGKILL || - state == MOUNT_FAILED) { - if (state != old_state) - mount_notify_automount(m, -ENODEV); - } - - if (state != old_state) - log_debug("%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; -} - -static int mount_coldplug(Unit *u) { - Mount *m = MOUNT(u); - MountState new_state = MOUNT_DEAD; - int r; - - assert(m); - assert(m->state == MOUNT_DEAD); - - if (m->deserialized_state != m->state) - new_state = m->deserialized_state; - else if (m->from_proc_self_mountinfo) - new_state = MOUNT_MOUNTED; - - if (new_state != m->state) { - - if (new_state == MOUNT_MOUNTING || - new_state == MOUNT_MOUNTING_DONE || - new_state == MOUNT_REMOUNTING || - new_state == MOUNT_UNMOUNTING || - new_state == MOUNT_MOUNTING_SIGTERM || - new_state == MOUNT_MOUNTING_SIGKILL || - new_state == MOUNT_UNMOUNTING_SIGTERM || - new_state == MOUNT_UNMOUNTING_SIGKILL || - new_state == MOUNT_REMOUNTING_SIGTERM || - new_state == MOUNT_REMOUNTING_SIGKILL) { - - if (m->control_pid <= 0) - return -EBADMSG; - - if ((r = unit_watch_pid(UNIT(m), m->control_pid)) < 0) - return r; - - if ((r = unit_watch_timer(UNIT(m), m->timeout_usec, &m->timer_watch)) < 0) - return r; - } - - mount_set_state(m, new_state); - } - - return 0; -} - -static void mount_dump(Unit *u, FILE *f, const char *prefix) { - Mount *m = MOUNT(u); - MountParameters *p; - - assert(m); - assert(f); - - p = get_mount_parameters(m); - - fprintf(f, - "%sMount State: %s\n" - "%sResult: %s\n" - "%sWhere: %s\n" - "%sWhat: %s\n" - "%sFile System Type: %s\n" - "%sOptions: %s\n" - "%sFrom /proc/self/mountinfo: %s\n" - "%sFrom fragment: %s\n" - "%sDirectoryMode: %04o\n", - prefix, mount_state_to_string(m->state), - prefix, mount_result_to_string(m->result), - prefix, m->where, - prefix, strna(p->what), - prefix, strna(p->fstype), - prefix, strna(p->options), - prefix, yes_no(m->from_proc_self_mountinfo), - prefix, yes_no(m->from_fragment), - prefix, m->directory_mode); - - if (m->control_pid > 0) - fprintf(f, - "%sControl PID: %lu\n", - prefix, (unsigned long) m->control_pid); - - exec_context_dump(&m->exec_context, f, prefix); - kill_context_dump(&m->kill_context, f, prefix); -} - -static int mount_spawn(Mount *m, ExecCommand *c, pid_t *_pid) { - pid_t pid; - int r; - - assert(m); - assert(c); - assert(_pid); - - if ((r = unit_watch_timer(UNIT(m), m->timeout_usec, &m->timer_watch)) < 0) - goto fail; - - if ((r = exec_spawn(c, - NULL, - &m->exec_context, - NULL, 0, - UNIT(m)->manager->environment, - true, - true, - true, - UNIT(m)->manager->confirm_spawn, - UNIT(m)->cgroup_bondings, - UNIT(m)->cgroup_attributes, - NULL, - UNIT(m)->id, - NULL, - &pid)) < 0) - goto fail; - - if ((r = unit_watch_pid(UNIT(m), pid)) < 0) - /* FIXME: we need to do something here */ - goto fail; - - *_pid = pid; - - return 0; - -fail: - unit_unwatch_timer(UNIT(m), &m->timer_watch); - - return r; -} - -static void mount_enter_dead(Mount *m, MountResult f) { - assert(m); - - if (f != MOUNT_SUCCESS) - m->result = f; - - mount_set_state(m, m->result != MOUNT_SUCCESS ? MOUNT_FAILED : MOUNT_DEAD); -} - -static void mount_enter_mounted(Mount *m, MountResult f) { - assert(m); - - if (f != MOUNT_SUCCESS) - m->result = f; - - mount_set_state(m, MOUNT_MOUNTED); -} - -static void mount_enter_signal(Mount *m, MountState state, MountResult f) { - int r; - Set *pid_set = NULL; - bool wait_for_exit = false; - - assert(m); - - if (f != MOUNT_SUCCESS) - m->result = f; - - if (m->kill_context.kill_mode != KILL_NONE) { - int sig = (state == MOUNT_MOUNTING_SIGTERM || - state == MOUNT_UNMOUNTING_SIGTERM || - state == MOUNT_REMOUNTING_SIGTERM) ? m->kill_context.kill_signal : SIGKILL; - - 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); - else - wait_for_exit = true; - } - - if (m->kill_context.kill_mode == KILL_CONTROL_GROUP) { - - if (!(pid_set = set_new(trivial_hash_func, trivial_compare_func))) { - r = -ENOMEM; - goto fail; - } - - /* Exclude the control pid from being killed via the cgroup */ - if (m->control_pid > 0) - if ((r = set_put(pid_set, LONG_TO_PTR(m->control_pid))) < 0) - goto fail; - - 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)); - } else if (r > 0) - wait_for_exit = true; - - set_free(pid_set); - pid_set = NULL; - } - } - - if (wait_for_exit) { - if ((r = unit_watch_timer(UNIT(m), m->timeout_usec, &m->timer_watch)) < 0) - goto fail; - - mount_set_state(m, state); - } else if (state == MOUNT_REMOUNTING_SIGTERM || state == MOUNT_REMOUNTING_SIGKILL) - mount_enter_mounted(m, MOUNT_SUCCESS); - else - mount_enter_dead(m, MOUNT_SUCCESS); - - return; - -fail: - log_warning("%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); - else - mount_enter_dead(m, MOUNT_FAILURE_RESOURCES); - - if (pid_set) - set_free(pid_set); -} - -static void mount_enter_unmounting(Mount *m) { - int r; - - assert(m); - - m->control_command_id = MOUNT_EXEC_UNMOUNT; - m->control_command = m->exec_command + MOUNT_EXEC_UNMOUNT; - - if ((r = exec_command_set( - m->control_command, - "/bin/umount", - m->where, - NULL)) < 0) - goto fail; - - mount_unwatch_control_pid(m); - - if ((r = mount_spawn(m, m->control_command, &m->control_pid)) < 0) - goto fail; - - mount_set_state(m, MOUNT_UNMOUNTING); - - return; - -fail: - log_warning("%s failed to run 'umount' task: %s", UNIT(m)->id, strerror(-r)); - mount_enter_mounted(m, MOUNT_FAILURE_RESOURCES); -} - -static void mount_enter_mounting(Mount *m) { - int r; - MountParameters *p; - - assert(m); - - m->control_command_id = MOUNT_EXEC_MOUNT; - m->control_command = m->exec_command + MOUNT_EXEC_MOUNT; - - mkdir_p_label(m->where, m->directory_mode); - - if (dir_is_empty(m->where) <= 0) - log_notice("%s: Directory %s to mount over is not empty, mounting anyway. (To see the over-mounted files, please manually mount the underlying file system to a secondary location.)", 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)) - mkdir_p_label(p->what, m->directory_mode); - - if (m->from_fragment) - r = exec_command_set( - m->control_command, - "/bin/mount", - m->parameters_fragment.what, - m->where, - "-t", m->parameters_fragment.fstype ? m->parameters_fragment.fstype : "auto", - m->parameters_fragment.options ? "-o" : NULL, m->parameters_fragment.options, - NULL); - else - r = -ENOENT; - - if (r < 0) - goto fail; - - mount_unwatch_control_pid(m); - - r = mount_spawn(m, m->control_command, &m->control_pid); - if (r < 0) - goto fail; - - mount_set_state(m, MOUNT_MOUNTING); - - return; - -fail: - log_warning("%s failed to run 'mount' task: %s", UNIT(m)->id, strerror(-r)); - mount_enter_dead(m, MOUNT_FAILURE_RESOURCES); -} - -static void mount_enter_mounting_done(Mount *m) { - assert(m); - - mount_set_state(m, MOUNT_MOUNTING_DONE); -} - -static void mount_enter_remounting(Mount *m) { - int r; - - assert(m); - - m->control_command_id = MOUNT_EXEC_REMOUNT; - m->control_command = m->exec_command + MOUNT_EXEC_REMOUNT; - - if (m->from_fragment) { - char *buf = NULL; - const char *o; - - if (m->parameters_fragment.options) { - if (!(buf = strappend("remount,", m->parameters_fragment.options))) { - r = -ENOMEM; - goto fail; - } - - o = buf; - } else - o = "remount"; - - r = exec_command_set( - m->control_command, - "/bin/mount", - m->parameters_fragment.what, - m->where, - "-t", m->parameters_fragment.fstype ? m->parameters_fragment.fstype : "auto", - "-o", o, - NULL); - - free(buf); - } else - r = -ENOENT; - - if (r < 0) - goto fail; - - mount_unwatch_control_pid(m); - - if ((r = mount_spawn(m, m->control_command, &m->control_pid)) < 0) - goto fail; - - mount_set_state(m, MOUNT_REMOUNTING); - - return; - -fail: - log_warning("%s failed to run 'remount' task: %s", UNIT(m)->id, strerror(-r)); - m->reload_result = MOUNT_FAILURE_RESOURCES; - mount_enter_mounted(m, MOUNT_SUCCESS); -} - -static int mount_start(Unit *u) { - Mount *m = MOUNT(u); - - assert(m); - - /* We cannot fulfill this request right now, try again later - * please! */ - if (m->state == MOUNT_UNMOUNTING || - m->state == MOUNT_UNMOUNTING_SIGTERM || - m->state == MOUNT_UNMOUNTING_SIGKILL || - m->state == MOUNT_MOUNTING_SIGTERM || - m->state == MOUNT_MOUNTING_SIGKILL) - return -EAGAIN; - - /* Already on it! */ - if (m->state == MOUNT_MOUNTING) - return 0; - - assert(m->state == MOUNT_DEAD || m->state == MOUNT_FAILED); - - m->result = MOUNT_SUCCESS; - m->reload_result = MOUNT_SUCCESS; - - mount_enter_mounting(m); - return 0; -} - -static int mount_stop(Unit *u) { - Mount *m = MOUNT(u); - - assert(m); - - /* Already on it */ - if (m->state == MOUNT_UNMOUNTING || - m->state == MOUNT_UNMOUNTING_SIGKILL || - m->state == MOUNT_UNMOUNTING_SIGTERM || - m->state == MOUNT_MOUNTING_SIGTERM || - m->state == MOUNT_MOUNTING_SIGKILL) - return 0; - - assert(m->state == MOUNT_MOUNTING || - m->state == MOUNT_MOUNTING_DONE || - m->state == MOUNT_MOUNTED || - m->state == MOUNT_REMOUNTING || - m->state == MOUNT_REMOUNTING_SIGTERM || - m->state == MOUNT_REMOUNTING_SIGKILL); - - mount_enter_unmounting(m); - return 0; -} - -static int mount_reload(Unit *u) { - Mount *m = MOUNT(u); - - assert(m); - - if (m->state == MOUNT_MOUNTING_DONE) - return -EAGAIN; - - assert(m->state == MOUNT_MOUNTED); - - mount_enter_remounting(m); - return 0; -} - -static int mount_serialize(Unit *u, FILE *f, FDSet *fds) { - Mount *m = MOUNT(u); - - assert(m); - assert(f); - assert(fds); - - unit_serialize_item(u, f, "state", mount_state_to_string(m->state)); - unit_serialize_item(u, f, "result", mount_result_to_string(m->result)); - unit_serialize_item(u, f, "reload-result", mount_result_to_string(m->reload_result)); - - if (m->control_pid > 0) - unit_serialize_item_format(u, f, "control-pid", "%lu", (unsigned long) m->control_pid); - - if (m->control_command_id >= 0) - unit_serialize_item(u, f, "control-command", mount_exec_command_to_string(m->control_command_id)); - - return 0; -} - -static int mount_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) { - Mount *m = MOUNT(u); - - assert(u); - assert(key); - assert(value); - assert(fds); - - if (streq(key, "state")) { - MountState state; - - if ((state = mount_state_from_string(value)) < 0) - log_debug("Failed to parse state value %s", value); - else - m->deserialized_state = state; - } else if (streq(key, "result")) { - MountResult f; - - f = mount_result_from_string(value); - if (f < 0) - log_debug("Failed to parse result value %s", value); - else if (f != MOUNT_SUCCESS) - m->result = f; - - } else if (streq(key, "reload-result")) { - MountResult f; - - f = mount_result_from_string(value); - if (f < 0) - log_debug("Failed to parse reload result value %s", value); - else if (f != MOUNT_SUCCESS) - m->reload_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); - 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); - else { - m->control_command_id = id; - m->control_command = m->exec_command + id; - } - - } else - log_debug("Unknown serialization key '%s'", key); - - return 0; -} - -static UnitActiveState mount_active_state(Unit *u) { - assert(u); - - return state_translation_table[MOUNT(u)->state]; -} - -static const char *mount_sub_state_to_string(Unit *u) { - assert(u); - - return mount_state_to_string(MOUNT(u)->state); -} - -static bool mount_check_gc(Unit *u) { - Mount *m = MOUNT(u); - - assert(m); - - return m->from_proc_self_mountinfo; -} - -static void mount_sigchld_event(Unit *u, pid_t pid, int code, int status) { - Mount *m = MOUNT(u); - MountResult f; - - assert(m); - assert(pid >= 0); - - if (pid != m->control_pid) - return; - - m->control_pid = 0; - - if (is_clean_exit(code, status, NULL)) - f = MOUNT_SUCCESS; - else if (code == CLD_EXITED) - f = MOUNT_FAILURE_EXIT_CODE; - else if (code == CLD_KILLED) - f = MOUNT_FAILURE_SIGNAL; - else if (code == CLD_DUMPED) - f = MOUNT_FAILURE_CORE_DUMP; - else - assert_not_reached("Unknown code"); - - if (f != MOUNT_SUCCESS) - m->result = f; - - if (m->control_command) { - exec_status_exit(&m->control_command->exec_status, &m->exec_context, pid, code, status); - - m->control_command = NULL; - 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); - - /* Note that mount(8) returning and the kernel sending us a - * mount table change event might happen out-of-order. If an - * operation succeed we assume the kernel will follow soon too - * and already change into the resulting state. If it fails - * we check if the kernel still knows about the mount. and - * change state accordingly. */ - - switch (m->state) { - - case MOUNT_MOUNTING: - case MOUNT_MOUNTING_DONE: - case MOUNT_MOUNTING_SIGKILL: - case MOUNT_MOUNTING_SIGTERM: - - if (f == MOUNT_SUCCESS) - mount_enter_mounted(m, f); - else if (m->from_proc_self_mountinfo) - mount_enter_mounted(m, f); - else - mount_enter_dead(m, f); - break; - - case MOUNT_REMOUNTING: - case MOUNT_REMOUNTING_SIGKILL: - case MOUNT_REMOUNTING_SIGTERM: - - m->reload_result = f; - if (m->from_proc_self_mountinfo) - mount_enter_mounted(m, MOUNT_SUCCESS); - else - mount_enter_dead(m, MOUNT_SUCCESS); - - break; - - case MOUNT_UNMOUNTING: - case MOUNT_UNMOUNTING_SIGKILL: - case MOUNT_UNMOUNTING_SIGTERM: - - if (f == MOUNT_SUCCESS) - mount_enter_dead(m, f); - else if (m->from_proc_self_mountinfo) - mount_enter_mounted(m, f); - else - mount_enter_dead(m, f); - break; - - default: - assert_not_reached("Uh, control process died at wrong time."); - } - - /* Notify clients about changed exit status */ - unit_add_to_dbus_queue(u); -} - -static void mount_timer_event(Unit *u, uint64_t elapsed, Watch *w) { - Mount *m = MOUNT(u); - - assert(m); - assert(elapsed == 1); - assert(w == &m->timer_watch); - - switch (m->state) { - - case MOUNT_MOUNTING: - case MOUNT_MOUNTING_DONE: - log_warning("%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); - 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); - 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); - mount_enter_signal(m, MOUNT_MOUNTING_SIGKILL, MOUNT_FAILURE_TIMEOUT); - } else { - log_warning("%s mounting timed out. Skipping SIGKILL. Ignoring.", u->id); - - if (m->from_proc_self_mountinfo) - mount_enter_mounted(m, MOUNT_FAILURE_TIMEOUT); - else - mount_enter_dead(m, MOUNT_FAILURE_TIMEOUT); - } - break; - - case MOUNT_REMOUNTING_SIGTERM: - if (m->kill_context.send_sigkill) { - log_warning("%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); - - if (m->from_proc_self_mountinfo) - mount_enter_mounted(m, MOUNT_FAILURE_TIMEOUT); - else - mount_enter_dead(m, MOUNT_FAILURE_TIMEOUT); - } - break; - - case MOUNT_UNMOUNTING_SIGTERM: - if (m->kill_context.send_sigkill) { - log_warning("%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); - - if (m->from_proc_self_mountinfo) - mount_enter_mounted(m, MOUNT_FAILURE_TIMEOUT); - else - mount_enter_dead(m, MOUNT_FAILURE_TIMEOUT); - } - break; - - case MOUNT_MOUNTING_SIGKILL: - case MOUNT_REMOUNTING_SIGKILL: - case MOUNT_UNMOUNTING_SIGKILL: - log_warning("%s mount process still around after SIGKILL. Ignoring.", u->id); - - if (m->from_proc_self_mountinfo) - mount_enter_mounted(m, MOUNT_FAILURE_TIMEOUT); - else - mount_enter_dead(m, MOUNT_FAILURE_TIMEOUT); - break; - - default: - assert_not_reached("Timeout at wrong time."); - } -} - -static int mount_add_one( - Manager *m, - const char *what, - const char *where, - const char *options, - const char *fstype, - int passno, - bool set_flags) { - int r; - Unit *u; - bool delete; - char *e, *w = NULL, *o = NULL, *f = NULL; - MountParameters *p; - bool load_extras = false; - - assert(m); - assert(what); - assert(where); - assert(options); - assert(fstype); - - /* Ignore API mount points. They should never be referenced in - * dependencies ever. */ - if (mount_point_is_api(where) || mount_point_ignore(where)) - return 0; - - if (streq(fstype, "autofs")) - return 0; - - /* probably some kind of swap, ignore */ - if (!is_path(where)) - return 0; - - e = unit_name_from_path(where, ".mount"); - if (!e) - return -ENOMEM; - - u = manager_get_unit(m, e); - if (!u) { - delete = true; - - u = unit_new(m, sizeof(Mount)); - if (!u) { - free(e); - return -ENOMEM; - } - - r = unit_add_name(u, e); - free(e); - - if (r < 0) - goto fail; - - MOUNT(u)->where = strdup(where); - if (!MOUNT(u)->where) { - r = -ENOMEM; - goto fail; - } - - unit_add_to_load_queue(u); - } else { - 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; - - /* Load in the extras later on, after we - * finished initialization of the unit */ - load_extras = true; - } - } - - if (!(w = strdup(what)) || - !(o = strdup(options)) || - !(f = strdup(fstype))) { - r = -ENOMEM; - goto fail; - } - - p = &MOUNT(u)->parameters_proc_self_mountinfo; - if (set_flags) { - MOUNT(u)->is_mounted = true; - MOUNT(u)->just_mounted = !MOUNT(u)->from_proc_self_mountinfo; - MOUNT(u)->just_changed = !streq_ptr(p->options, o); - } - - MOUNT(u)->from_proc_self_mountinfo = true; - - free(p->what); - p->what = w; - - free(p->options); - p->options = o; - - free(p->fstype); - p->fstype = f; - - 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; - -fail: - free(w); - free(o); - free(f); - - if (delete && u) - unit_free(u); - - return r; -} - -static int mount_load_proc_self_mountinfo(Manager *m, bool set_flags) { - int r = 0; - unsigned i; - char *device, *path, *options, *options2, *fstype, *d, *p, *o; - - assert(m); - - rewind(m->proc_self_mountinfo); - - for (i = 1;; i++) { - int k; - - device = path = options = options2 = fstype = d = p = o = NULL; - - if ((k = fscanf(m->proc_self_mountinfo, - "%*s " /* (1) mount id */ - "%*s " /* (2) parent id */ - "%*s " /* (3) major:minor */ - "%*s " /* (4) root */ - "%ms " /* (5) mount point */ - "%ms" /* (6) mount options */ - "%*[^-]" /* (7) optional fields */ - "- " /* (8) separator */ - "%ms " /* (9) file system type */ - "%ms" /* (10) mount source */ - "%ms" /* (11) mount options 2 */ - "%*[^\n]", /* some rubbish at the end */ - &path, - &options, - &fstype, - &device, - &options2)) != 5) { - - if (k == EOF) - break; - - log_warning("Failed to parse /proc/self/mountinfo:%u.", i); - goto clean_up; - } - - o = strjoin(options, ",", options2, NULL); - if (!o) { - r = -ENOMEM; - goto finish; - } - - if (!(d = cunescape(device)) || - !(p = cunescape(path))) { - r = -ENOMEM; - goto finish; - } - - if ((k = mount_add_one(m, d, p, o, fstype, 0, set_flags)) < 0) - r = k; - -clean_up: - free(device); - free(path); - free(options); - free(options2); - free(fstype); - free(d); - free(p); - free(o); - } - -finish: - free(device); - free(path); - free(options); - free(options2); - free(fstype); - free(d); - free(p); - free(o); - - return r; -} - -static void mount_shutdown(Manager *m) { - assert(m); - - if (m->proc_self_mountinfo) { - fclose(m->proc_self_mountinfo); - m->proc_self_mountinfo = NULL; - } -} - -static int mount_enumerate(Manager *m) { - int r; - struct epoll_event ev; - assert(m); - - if (!m->proc_self_mountinfo) { - if (!(m->proc_self_mountinfo = fopen("/proc/self/mountinfo", "re"))) - return -errno; - - m->mount_watch.type = WATCH_MOUNT; - m->mount_watch.fd = fileno(m->proc_self_mountinfo); - - zero(ev); - ev.events = EPOLLPRI; - ev.data.ptr = &m->mount_watch; - - if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->mount_watch.fd, &ev) < 0) - return -errno; - } - - if ((r = mount_load_proc_self_mountinfo(m, false)) < 0) - goto fail; - - return 0; - -fail: - mount_shutdown(m); - return r; -} - -void mount_fd_event(Manager *m, int events) { - Unit *u; - int r; - - assert(m); - assert(events & EPOLLPRI); - - /* The manager calls this for every fd event happening on the - * /proc/self/mountinfo file, which informs us about mounting - * table changes */ - - if ((r = mount_load_proc_self_mountinfo(m, true)) < 0) { - log_error("Failed to reread /proc/self/mountinfo: %s", strerror(-r)); - - /* Reset flags, just in case, for later calls */ - LIST_FOREACH(units_by_type, u, m->units_by_type[UNIT_MOUNT]) { - Mount *mount = MOUNT(u); - - mount->is_mounted = mount->just_mounted = mount->just_changed = false; - } - - return; - } - - manager_dispatch_load_queue(m); - - LIST_FOREACH(units_by_type, u, m->units_by_type[UNIT_MOUNT]) { - Mount *mount = MOUNT(u); - - if (!mount->is_mounted) { - /* This has just been unmounted. */ - - mount->from_proc_self_mountinfo = false; - - switch (mount->state) { - - case MOUNT_MOUNTED: - mount_enter_dead(mount, MOUNT_SUCCESS); - break; - - default: - mount_set_state(mount, mount->state); - break; - - } - - } else if (mount->just_mounted || mount->just_changed) { - - /* New or changed mount entry */ - - switch (mount->state) { - - case MOUNT_DEAD: - case MOUNT_FAILED: - mount_enter_mounted(mount, MOUNT_SUCCESS); - break; - - case MOUNT_MOUNTING: - mount_enter_mounting_done(mount); - break; - - default: - /* Nothing really changed, but let's - * issue an notification call - * nonetheless, in case somebody is - * waiting for this. (e.g. file system - * ro/rw remounts.) */ - mount_set_state(mount, mount->state); - break; - } - } - - /* Reset the flags for later calls */ - mount->is_mounted = mount->just_mounted = mount->just_changed = false; - } -} - -static void mount_reset_failed(Unit *u) { - Mount *m = MOUNT(u); - - assert(m); - - if (m->state == MOUNT_FAILED) - mount_set_state(m, MOUNT_DEAD); - - m->result = MOUNT_SUCCESS; - m->reload_result = MOUNT_SUCCESS; -} - -static int mount_kill(Unit *u, KillWho who, int signo, DBusError *error) { - Mount *m = MOUNT(u); - int r = 0; - Set *pid_set = NULL; - - assert(m); - - if (who == KILL_MAIN) { - dbus_set_error(error, BUS_ERROR_NO_SUCH_PROCESS, "Mount units have no main processes"); - return -ESRCH; - } - - if (m->control_pid <= 0 && who == KILL_CONTROL) { - dbus_set_error(error, BUS_ERROR_NO_SUCH_PROCESS, "No control process to kill"); - return -ESRCH; - } - - if (who == KILL_CONTROL || who == KILL_ALL) - if (m->control_pid > 0) - if (kill(m->control_pid, signo) < 0) - r = -errno; - - if (who == KILL_ALL) { - int q; - - pid_set = set_new(trivial_hash_func, trivial_compare_func); - if (!pid_set) - return -ENOMEM; - - /* Exclude the control pid from being killed via the cgroup */ - if (m->control_pid > 0) { - q = set_put(pid_set, LONG_TO_PTR(m->control_pid)); - if (q < 0) { - r = q; - goto finish; - } - } - - q = cgroup_bonding_kill_list(UNIT(m)->cgroup_bondings, signo, false, false, pid_set, NULL); - if (q < 0 && q != -EAGAIN && q != -ESRCH && q != -ENOENT) - r = q; - } - -finish: - if (pid_set) - set_free(pid_set); - - return r; -} - -static const char* const mount_state_table[_MOUNT_STATE_MAX] = { - [MOUNT_DEAD] = "dead", - [MOUNT_MOUNTING] = "mounting", - [MOUNT_MOUNTING_DONE] = "mounting-done", - [MOUNT_MOUNTED] = "mounted", - [MOUNT_REMOUNTING] = "remounting", - [MOUNT_UNMOUNTING] = "unmounting", - [MOUNT_MOUNTING_SIGTERM] = "mounting-sigterm", - [MOUNT_MOUNTING_SIGKILL] = "mounting-sigkill", - [MOUNT_REMOUNTING_SIGTERM] = "remounting-sigterm", - [MOUNT_REMOUNTING_SIGKILL] = "remounting-sigkill", - [MOUNT_UNMOUNTING_SIGTERM] = "unmounting-sigterm", - [MOUNT_UNMOUNTING_SIGKILL] = "unmounting-sigkill", - [MOUNT_FAILED] = "failed" -}; - -DEFINE_STRING_TABLE_LOOKUP(mount_state, MountState); - -static const char* const mount_exec_command_table[_MOUNT_EXEC_COMMAND_MAX] = { - [MOUNT_EXEC_MOUNT] = "ExecMount", - [MOUNT_EXEC_UNMOUNT] = "ExecUnmount", - [MOUNT_EXEC_REMOUNT] = "ExecRemount", -}; - -DEFINE_STRING_TABLE_LOOKUP(mount_exec_command, MountExecCommand); - -static const char* const mount_result_table[_MOUNT_RESULT_MAX] = { - [MOUNT_SUCCESS] = "success", - [MOUNT_FAILURE_RESOURCES] = "resources", - [MOUNT_FAILURE_TIMEOUT] = "timeout", - [MOUNT_FAILURE_EXIT_CODE] = "exit-code", - [MOUNT_FAILURE_SIGNAL] = "signal", - [MOUNT_FAILURE_CORE_DUMP] = "core-dump" -}; - -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" - "Install\0", - - .no_alias = true, - .no_instances = true, - - .init = mount_init, - .load = mount_load, - .done = mount_done, - - .coldplug = mount_coldplug, - - .dump = mount_dump, - - .start = mount_start, - .stop = mount_stop, - .reload = mount_reload, - - .kill = mount_kill, - - .serialize = mount_serialize, - .deserialize_item = mount_deserialize_item, - - .active_state = mount_active_state, - .sub_state_to_string = mount_sub_state_to_string, - - .check_gc = mount_check_gc, - - .sigchld_event = mount_sigchld_event, - .timer_event = mount_timer_event, - - .reset_failed = mount_reset_failed, - - .bus_interface = "org.freedesktop.systemd1.Mount", - .bus_message_handler = bus_mount_message_handler, - .bus_invalidating_properties = bus_mount_invalidating_properties, - - .enumerate = mount_enumerate, - .shutdown = mount_shutdown, - - .status_message_formats = { - .starting_stopping = { - [0] = "Mounting %s...", - [1] = "Unmounting %s...", - }, - .finished_start_job = { - [JOB_DONE] = "Mounted %s.", - [JOB_FAILED] = "Failed to mount %s.", - [JOB_DEPENDENCY] = "Dependency failed for %s.", - [JOB_TIMEOUT] = "Timed out mounting %s.", - }, - .finished_stop_job = { - [JOB_DONE] = "Unmounted %s.", - [JOB_FAILED] = "Failed unmounting %s.", - [JOB_TIMEOUT] = "Timed out unmounting %s.", - }, - }, -}; diff --git a/src/core/mount.h b/src/core/mount.h deleted file mode 100644 index 67d6132a5d..0000000000 --- a/src/core/mount.h +++ /dev/null @@ -1,121 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -#pragma once - -/*** - 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/>. -***/ - -typedef struct Mount Mount; - -#include "unit.h" -#include "kill.h" - -typedef enum MountState { - MOUNT_DEAD, - MOUNT_MOUNTING, /* /bin/mount is running, but the mount is not done yet. */ - MOUNT_MOUNTING_DONE, /* /bin/mount is running, and the mount is done. */ - MOUNT_MOUNTED, - MOUNT_REMOUNTING, - MOUNT_UNMOUNTING, - MOUNT_MOUNTING_SIGTERM, - MOUNT_MOUNTING_SIGKILL, - MOUNT_REMOUNTING_SIGTERM, - MOUNT_REMOUNTING_SIGKILL, - MOUNT_UNMOUNTING_SIGTERM, - MOUNT_UNMOUNTING_SIGKILL, - MOUNT_FAILED, - _MOUNT_STATE_MAX, - _MOUNT_STATE_INVALID = -1 -} MountState; - -typedef enum MountExecCommand { - MOUNT_EXEC_MOUNT, - MOUNT_EXEC_UNMOUNT, - MOUNT_EXEC_REMOUNT, - _MOUNT_EXEC_COMMAND_MAX, - _MOUNT_EXEC_COMMAND_INVALID = -1 -} MountExecCommand; - -typedef struct MountParameters { - char *what; - char *options; - char *fstype; - int passno; -} MountParameters; - -typedef enum MountResult { - MOUNT_SUCCESS, - MOUNT_FAILURE_RESOURCES, - MOUNT_FAILURE_TIMEOUT, - MOUNT_FAILURE_EXIT_CODE, - MOUNT_FAILURE_SIGNAL, - MOUNT_FAILURE_CORE_DUMP, - _MOUNT_RESULT_MAX, - _MOUNT_RESULT_INVALID = -1 -} MountResult; - -struct Mount { - Unit meta; - - char *where; - - MountParameters parameters_proc_self_mountinfo; - MountParameters parameters_fragment; - - bool from_proc_self_mountinfo:1; - bool from_fragment:1; - - /* Used while looking for mount points that vanished or got - * added from/to /proc/self/mountinfo */ - bool is_mounted:1; - bool just_mounted:1; - bool just_changed:1; - - MountResult result; - MountResult reload_result; - - mode_t directory_mode; - - usec_t timeout_usec; - - ExecCommand exec_command[_MOUNT_EXEC_COMMAND_MAX]; - ExecContext exec_context; - KillContext kill_context; - - MountState state, deserialized_state; - - ExecCommand* control_command; - MountExecCommand control_command_id; - pid_t control_pid; - - Watch timer_watch; -}; - -extern const UnitVTable mount_vtable; - -void mount_fd_event(Manager *m, int events); - -const char* mount_state_to_string(MountState i); -MountState mount_state_from_string(const char *s); - -const char* mount_exec_command_to_string(MountExecCommand i); -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); diff --git a/src/core/namespace.c b/src/core/namespace.c deleted file mode 100644 index ba18ddc5b0..0000000000 --- a/src/core/namespace.c +++ /dev/null @@ -1,330 +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 <sys/mount.h> -#include <string.h> -#include <stdio.h> -#include <unistd.h> -#include <sys/stat.h> -#include <sys/types.h> -#include <sched.h> -#include <sys/syscall.h> -#include <limits.h> -#include <linux/fs.h> - -#include "strv.h" -#include "util.h" -#include "path-util.h" -#include "namespace.h" -#include "missing.h" - -typedef enum PathMode { - /* This is ordered by priority! */ - INACCESSIBLE, - READONLY, - 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) { - char **i; - - STRV_FOREACH(i, strv) { - - if (!path_is_absolute(*i)) - return -EINVAL; - - (*p)->path = *i; - (*p)->mode = mode; - (*p)++; - } - - return 0; -} - -static int path_compare(const void *a, const void *b) { - const Path *p = a, *q = b; - - if (path_equal(p->path, q->path)) { - - /* If the paths are equal, check the mode */ - if (p->mode < q->mode) - return -1; - - if (p->mode > q->mode) - return 1; - - return 0; - } - - /* If the paths are not equal, then order prefixes first */ - if (path_startswith(p->path, q->path)) - return 1; - - if (path_startswith(q->path, p->path)) - return -1; - - return 0; -} - -static void drop_duplicates(Path *p, unsigned *n, bool *need_inaccessible) { - Path *f, *t, *previous; - - assert(p); - assert(n); - assert(need_inaccessible); - - 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 == INACCESSIBLE) - *need_inaccessible = true; - - previous = t; - - t++; - } - - *n = t - p; -} - -static int apply_mount( - Path *p, - const char *tmp_dir, - const char *var_tmp_dir, - const char *inaccessible_dir) { - - const char *what; - int r; - - assert(p); - - switch (p->mode) { - - case INACCESSIBLE: - what = inaccessible_dir; - break; - - case READONLY: - case READWRITE: - what = p->path; - break; - - case PRIVATE_TMP: - what = tmp_dir; - break; - - case PRIVATE_VAR_TMP: - what = var_tmp_dir; - break; - - default: - assert_not_reached("Unknown mode"); - } - - assert(what); - - r = mount(what, p->path, NULL, MS_BIND|MS_REC, NULL); - if (r >= 0) - log_debug("Successfully mounted %s to %s", what, p->path); - - return r; -} - -static int make_read_only(Path *p) { - int 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( - char **writable, - char **readable, - char **inaccessible, - bool private_tmp, - unsigned long flags) { - - char - 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_inaccessible = false; - bool remove_tmp = false, remove_var_tmp = false, remove_inaccessible = false; - int r; - - if (!flags) - flags = MS_SHARED; - - n = - strv_length(writable) + - strv_length(readable) + - strv_length(inaccessible) + - (private_tmp ? 2 : 0); - - 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) - goto fail; - - if (private_tmp) { - p->path = "/tmp"; - p->mode = PRIVATE_TMP; - p++; - - p->path = "/var/tmp"; - p->mode = PRIVATE_VAR_TMP; - p++; - } - - assert(paths + n == p); - - qsort(paths, n, sizeof(Path), path_compare); - drop_duplicates(paths, &n, &need_inaccessible); - - if (need_inaccessible) { - mode_t u; - char *d; - - u = umask(0777); - d = mkdtemp(inaccessible_dir); - umask(u); - - if (!d) { - r = -errno; - goto fail; - } - - remove_inaccessible = true; - } - - if (private_tmp) { - mode_t u; - char *d; - - u = umask(0000); - 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); - - 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) { - r = -errno; - goto fail; - } - - /* 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; - goto fail; - } - - for (p = paths; p < paths + n; p++) { - r = apply_mount(p, tmp_dir, var_tmp_dir, inaccessible_dir); - if (r < 0) - goto undo_mounts; - } - - for (p = paths; p < paths + n; p++) { - r = make_read_only(p); - if (r < 0) - goto undo_mounts; - } - - /* Remount / as the desired mode */ - if (mount(NULL, "/", NULL, flags|MS_REC, NULL) < 0) { - r = -errno; - goto undo_mounts; - } - - return 0; - -undo_mounts: - for (p = paths; p < paths + n; p++) - if (p->done) - umount2(p->path, MNT_DETACH); - -fail: - if (remove_inaccessible) - rmdir(inaccessible_dir); - - if (remove_tmp) - rmdir(tmp_dir); - - if (remove_var_tmp) - rmdir(var_tmp_dir); - - return r; -} diff --git a/src/core/namespace.h b/src/core/namespace.h deleted file mode 100644 index 5d72ed91fb..0000000000 --- a/src/core/namespace.h +++ /dev/null @@ -1,31 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -#pragma once - -/*** - 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 <stdbool.h> - -int setup_namespace( - char **writable, - char **readable, - char **inaccessible, - bool private_tmp, - unsigned long flags); diff --git a/src/core/org.freedesktop.systemd1.conf b/src/core/org.freedesktop.systemd1.conf deleted file mode 100644 index a07a8e1ce3..0000000000 --- a/src/core/org.freedesktop.systemd1.conf +++ /dev/null @@ -1,92 +0,0 @@ -<?xml version="1.0"?> <!--*-nxml-*--> -<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN" - "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd"> - -<!-- - This file is part of systemd. - - 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. ---> - -<busconfig> - - <policy user="root"> - <allow own="org.freedesktop.systemd1"/> - - <!-- Root clients can do everything --> - <allow send_destination="org.freedesktop.systemd1"/> - <allow receive_sender="org.freedesktop.systemd1"/> - - <!-- systemd may receive activator requests --> - <allow receive_interface="org.freedesktop.systemd1.Activator" - receive_member="ActivationRequest"/> - </policy> - - <policy context="default"> - <deny send_destination="org.freedesktop.systemd1"/> - - <allow send_destination="org.freedesktop.systemd1" - send_interface="org.freedesktop.DBus.Introspectable"/> - - <allow send_destination="org.freedesktop.systemd1" - send_interface="org.freedesktop.DBus.Peer"/> - - <allow send_destination="org.freedesktop.systemd1" - send_interface="org.freedesktop.DBus.Properties" - send_member="Get"/> - - <allow send_destination="org.freedesktop.systemd1" - send_interface="org.freedesktop.DBus.Properties" - send_member="GetAll"/> - - <allow send_destination="org.freedesktop.systemd1" - send_interface="org.freedesktop.systemd1.Manager" - send_member="GetUnit"/> - - <allow send_destination="org.freedesktop.systemd1" - send_interface="org.freedesktop.systemd1.Manager" - send_member="GetUnitByPID"/> - - <allow send_destination="org.freedesktop.systemd1" - send_interface="org.freedesktop.systemd1.Manager" - send_member="LoadUnit"/> - - <allow send_destination="org.freedesktop.systemd1" - send_interface="org.freedesktop.systemd1.Manager" - send_member="GetJob"/> - - <allow send_destination="org.freedesktop.systemd1" - send_interface="org.freedesktop.systemd1.Manager" - send_member="ListUnits"/> - - <allow send_destination="org.freedesktop.systemd1" - send_interface="org.freedesktop.systemd1.Manager" - send_member="ListUnitFiles"/> - - <allow send_destination="org.freedesktop.systemd1" - send_interface="org.freedesktop.systemd1.Manager" - send_member="GetUnitFileState"/> - - <allow send_destination="org.freedesktop.systemd1" - send_interface="org.freedesktop.systemd1.Manager" - send_member="ListJobs"/> - - <allow send_destination="org.freedesktop.systemd1" - send_interface="org.freedesktop.systemd1.Manager" - send_member="Subscribe"/> - - <allow send_destination="org.freedesktop.systemd1" - send_interface="org.freedesktop.systemd1.Manager" - send_member="Unsubscribe"/> - - <allow send_destination="org.freedesktop.systemd1" - send_interface="org.freedesktop.systemd1.Manager" - send_member="Dump"/> - - <allow receive_sender="org.freedesktop.systemd1"/> - </policy> - -</busconfig> diff --git a/src/core/org.freedesktop.systemd1.policy.in.in b/src/core/org.freedesktop.systemd1.policy.in.in deleted file mode 100644 index 51bdafac45..0000000000 --- a/src/core/org.freedesktop.systemd1.policy.in.in +++ /dev/null @@ -1,41 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> <!--*-nxml-*--> -<!DOCTYPE policyconfig PUBLIC "-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN" - "http://www.freedesktop.org/standards/PolicyKit/1/policyconfig.dtd"> - -<!-- - This file is part of systemd. - - 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. ---> - -<policyconfig> - - <vendor>The systemd Project</vendor> - <vendor_url>http://www.freedesktop.org/wiki/Software/systemd</vendor_url> - - <action id="org.freedesktop.systemd1.reply-password"> - <_description>Send passphrase back to system</_description> - <_message>Authentication is required to send the entered passphrase back to the system.</_message> - <defaults> - <allow_any>no</allow_any> - <allow_inactive>no</allow_inactive> - <allow_active>auth_admin_keep</allow_active> - </defaults> - <annotate key="org.freedesktop.policykit.exec.path">@rootlibexecdir@/systemd-reply-password</annotate> - </action> - - <action id="org.freedesktop.systemd1.bus-access"> - <_description>Privileged system and service manager access</_description> - <_message>Authentication is required to access the system and service manager.</_message> - <defaults> - <allow_any>no</allow_any> - <allow_inactive>no</allow_inactive> - <allow_active>auth_admin_keep</allow_active> - </defaults> - <annotate key="org.freedesktop.policykit.exec.path">@bindir@/systemd-stdio-bridge</annotate> - </action> - -</policyconfig> diff --git a/src/core/org.freedesktop.systemd1.service b/src/core/org.freedesktop.systemd1.service deleted file mode 100644 index d4df3e93a2..0000000000 --- a/src/core/org.freedesktop.systemd1.service +++ /dev/null @@ -1,11 +0,0 @@ -# This file is part of systemd. -# -# 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. - -[D-BUS Service] -Name=org.freedesktop.systemd1 -Exec=/bin/false -User=root diff --git a/src/core/path.c b/src/core/path.c deleted file mode 100644 index 3936971b41..0000000000 --- a/src/core/path.c +++ /dev/null @@ -1,773 +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 <sys/inotify.h> -#include <sys/epoll.h> -#include <sys/ioctl.h> -#include <errno.h> -#include <unistd.h> - -#include "unit.h" -#include "unit-name.h" -#include "path.h" -#include "mkdir.h" -#include "dbus-path.h" -#include "special.h" -#include "bus-errors.h" -#include "path-util.h" - -static const UnitActiveState state_translation_table[_PATH_STATE_MAX] = { - [PATH_DEAD] = UNIT_INACTIVE, - [PATH_WAITING] = UNIT_ACTIVE, - [PATH_RUNNING] = UNIT_ACTIVE, - [PATH_FAILED] = UNIT_FAILED -}; - -int path_spec_watch(PathSpec *s, Unit *u) { - - static const int flags_table[_PATH_TYPE_MAX] = { - [PATH_EXISTS] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB, - [PATH_EXISTS_GLOB] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB, - [PATH_CHANGED] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB|IN_CLOSE_WRITE|IN_CREATE|IN_DELETE|IN_MOVED_FROM|IN_MOVED_TO, - [PATH_MODIFIED] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB|IN_CLOSE_WRITE|IN_CREATE|IN_DELETE|IN_MOVED_FROM|IN_MOVED_TO|IN_MODIFY, - [PATH_DIRECTORY_NOT_EMPTY] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB|IN_CREATE|IN_MOVED_TO - }; - - bool exists = false; - char *k, *slash; - int r; - - assert(u); - assert(s); - - path_spec_unwatch(s, u); - - if (!(k = strdup(s->path))) - return -ENOMEM; - - if ((s->inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC)) < 0) { - r = -errno; - goto fail; - } - - if (unit_watch_fd(u, s->inotify_fd, EPOLLIN, &s->watch) < 0) { - r = -errno; - goto fail; - } - - 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()! */ - slash = strrchr(k, '/'); - if (!slash) - break; - - /* Trim the path at the last slash. Keep the slash if it's the root dir. */ - slash[slash == k] = 0; - - flags = IN_MOVE_SELF; - if (!exists) - flags |= IN_DELETE_SELF | IN_ATTRIB | IN_CREATE | IN_MOVED_TO; - - if (inotify_add_watch(s->inotify_fd, k, flags) >= 0) - exists = true; - } while (slash != k); - - return 0; - -fail: - free(k); - - path_spec_unwatch(s, u); - return r; -} - -void path_spec_unwatch(PathSpec *s, Unit *u) { - - if (s->inotify_fd < 0) - return; - - unit_unwatch_fd(u, &s->watch); - - close_nointr_nofail(s->inotify_fd); - s->inotify_fd = -1; -} - -int path_spec_fd_event(PathSpec *s, uint32_t events) { - uint8_t *buf = NULL; - struct inotify_event *e; - ssize_t k; - int l; - int r = 0; - - if (events != EPOLLIN) { - log_error("Got Invalid poll event on inotify."); - r = -EINVAL; - goto out; - } - - if (ioctl(s->inotify_fd, FIONREAD, &l) < 0) { - log_error("FIONREAD failed: %m"); - r = -errno; - goto out; - } - - assert(l > 0); - - if (!(buf = malloc(l))) { - log_error("Failed to allocate buffer: %m"); - r = -errno; - goto out; - } - - if ((k = read(s->inotify_fd, buf, l)) < 0) { - log_error("Failed to read inotify event: %m"); - r = -errno; - goto out; - } - - e = (struct inotify_event*) buf; - - while (k > 0) { - size_t step; - - if ((s->type == PATH_CHANGED || s->type == PATH_MODIFIED) && - s->primary_wd == e->wd) - r = 1; - - step = sizeof(struct inotify_event) + e->len; - assert(step <= (size_t) k); - - e = (struct inotify_event*) ((uint8_t*) e + step); - k -= step; - } -out: - free(buf); - return r; -} - -static bool path_spec_check_good(PathSpec *s, bool initial) { - bool good = false; - - switch (s->type) { - - case PATH_EXISTS: - good = access(s->path, F_OK) >= 0; - break; - - case PATH_EXISTS_GLOB: - good = glob_exists(s->path) > 0; - break; - - case PATH_DIRECTORY_NOT_EMPTY: { - int k; - - k = dir_is_empty(s->path); - good = !(k == -ENOENT || k > 0); - break; - } - - case PATH_CHANGED: - case PATH_MODIFIED: { - bool b; - - b = access(s->path, F_OK) >= 0; - good = !initial && b != s->previous_exists; - s->previous_exists = b; - break; - } - - default: - ; - } - - return good; -} - -static bool path_spec_startswith(PathSpec *s, const char *what) { - return path_startswith(s->path, what); -} - -static void path_spec_mkdir(PathSpec *s, mode_t mode) { - int r; - - if (s->type == PATH_EXISTS || s->type == PATH_EXISTS_GLOB) - return; - - if ((r = mkdir_p_label(s->path, mode)) < 0) - log_warning("mkdir(%s) failed: %s", s->path, strerror(-r)); -} - -static void path_spec_dump(PathSpec *s, FILE *f, const char *prefix) { - fprintf(f, - "%s%s: %s\n", - prefix, - path_type_to_string(s->type), - s->path); -} - -void path_spec_done(PathSpec *s) { - assert(s); - assert(s->inotify_fd == -1); - - free(s->path); -} - -static void path_init(Unit *u) { - Path *p = PATH(u); - - assert(u); - assert(u->load_state == UNIT_STUB); - - p->directory_mode = 0755; -} - -static void path_done(Unit *u) { - Path *p = PATH(u); - PathSpec *s; - - assert(p); - - unit_ref_unset(&p->unit); - - while ((s = p->specs)) { - path_spec_unwatch(s, u); - LIST_REMOVE(PathSpec, spec, p->specs, s); - path_spec_done(s); - free(s); - } -} - -int path_add_one_mount_link(Path *p, Mount *m) { - PathSpec *s; - int r; - - assert(p); - assert(m); - - if (UNIT(p)->load_state != UNIT_LOADED || - UNIT(m)->load_state != UNIT_LOADED) - return 0; - - LIST_FOREACH(spec, s, p->specs) { - - if (!path_spec_startswith(s, m->where)) - continue; - - if ((r = unit_add_two_dependencies(UNIT(p), UNIT_AFTER, UNIT_REQUIRES, UNIT(m), true)) < 0) - return r; - } - - return 0; -} - -static int path_add_mount_links(Path *p) { - Unit *other; - int r; - - assert(p); - - LIST_FOREACH(units_by_type, other, UNIT(p)->manager->units_by_type[UNIT_MOUNT]) - if ((r = path_add_one_mount_link(p, MOUNT(other))) < 0) - return r; - - return 0; -} - -static int path_verify(Path *p) { - assert(p); - - if (UNIT(p)->load_state != UNIT_LOADED) - return 0; - - if (!p->specs) { - log_error("%s lacks path setting. Refusing.", UNIT(p)->id); - return -EINVAL; - } - - return 0; -} - -static int path_add_default_dependencies(Path *p) { - int r; - - assert(p); - - 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; - - if ((r = unit_add_two_dependencies_by_name(UNIT(p), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, NULL, true)) < 0) - return r; - } - - return unit_add_two_dependencies_by_name(UNIT(p), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, NULL, true); -} - -static int path_load(Unit *u) { - Path *p = PATH(u); - int r; - - assert(u); - assert(u->load_state == UNIT_STUB); - - if ((r = unit_load_fragment_and_dropin(u)) < 0) - return r; - - if (u->load_state == UNIT_LOADED) { - - if (!UNIT_DEREF(p->unit)) { - Unit *x; - - r = unit_load_related_unit(u, ".service", &x); - if (r < 0) - return r; - - unit_ref_set(&p->unit, x); - } - - r = unit_add_two_dependencies(u, UNIT_BEFORE, UNIT_TRIGGERS, UNIT_DEREF(p->unit), true); - if (r < 0) - return r; - - if ((r = path_add_mount_links(p)) < 0) - return r; - - if (UNIT(p)->default_dependencies) - if ((r = path_add_default_dependencies(p)) < 0) - return r; - } - - return path_verify(p); -} - -static void path_dump(Unit *u, FILE *f, const char *prefix) { - Path *p = PATH(u); - PathSpec *s; - - assert(p); - assert(f); - - fprintf(f, - "%sPath State: %s\n" - "%sResult: %s\n" - "%sUnit: %s\n" - "%sMakeDirectory: %s\n" - "%sDirectoryMode: %04o\n", - prefix, path_state_to_string(p->state), - prefix, path_result_to_string(p->result), - prefix, UNIT_DEREF(p->unit)->id, - prefix, yes_no(p->make_directory), - prefix, p->directory_mode); - - LIST_FOREACH(spec, s, p->specs) - path_spec_dump(s, f, prefix); -} - -static void path_unwatch(Path *p) { - PathSpec *s; - - assert(p); - - LIST_FOREACH(spec, s, p->specs) - path_spec_unwatch(s, UNIT(p)); -} - -static int path_watch(Path *p) { - int r; - PathSpec *s; - - assert(p); - - LIST_FOREACH(spec, s, p->specs) - if ((r = path_spec_watch(s, UNIT(p))) < 0) - return r; - - return 0; -} - -static void path_set_state(Path *p, PathState state) { - PathState old_state; - assert(p); - - old_state = p->state; - p->state = state; - - if (state != PATH_WAITING && - (state != PATH_RUNNING || p->inotify_triggered)) - path_unwatch(p); - - if (state != old_state) - log_debug("%s changed %s -> %s", - UNIT(p)->id, - path_state_to_string(old_state), - path_state_to_string(state)); - - unit_notify(UNIT(p), state_translation_table[old_state], state_translation_table[state], true); -} - -static void path_enter_waiting(Path *p, bool initial, bool recheck); - -static int path_coldplug(Unit *u) { - Path *p = PATH(u); - - assert(p); - assert(p->state == PATH_DEAD); - - if (p->deserialized_state != p->state) { - - if (p->deserialized_state == PATH_WAITING || - p->deserialized_state == PATH_RUNNING) - path_enter_waiting(p, true, true); - else - path_set_state(p, p->deserialized_state); - } - - return 0; -} - -static void path_enter_dead(Path *p, PathResult f) { - assert(p); - - if (f != PATH_SUCCESS) - p->result = f; - - path_set_state(p, p->result != PATH_SUCCESS ? PATH_FAILED : PATH_DEAD); -} - -static void path_enter_running(Path *p) { - int r; - DBusError error; - - assert(p); - dbus_error_init(&error); - - /* Don't start job if we are supposed to go down */ - if (UNIT(p)->job && UNIT(p)->job->type == JOB_STOP) - return; - - if ((r = manager_add_job(UNIT(p)->manager, JOB_START, UNIT_DEREF(p->unit), JOB_REPLACE, true, &error, NULL)) < 0) - goto fail; - - p->inotify_triggered = false; - - if ((r = path_watch(p)) < 0) - goto fail; - - path_set_state(p, PATH_RUNNING); - return; - -fail: - log_warning("%s failed to queue unit startup job: %s", UNIT(p)->id, bus_error(&error, r)); - path_enter_dead(p, PATH_FAILURE_RESOURCES); - - dbus_error_free(&error); -} - -static bool path_check_good(Path *p, bool initial) { - PathSpec *s; - bool good = false; - - assert(p); - - LIST_FOREACH(spec, s, p->specs) { - good = path_spec_check_good(s, initial); - - if (good) - break; - } - - return good; -} - -static void path_enter_waiting(Path *p, bool initial, bool recheck) { - int r; - - if (recheck) - if (path_check_good(p, initial)) { - log_debug("%s got triggered.", UNIT(p)->id); - path_enter_running(p); - return; - } - - if ((r = path_watch(p)) < 0) - goto fail; - - /* Hmm, so now we have created inotify watches, but the file - * might have appeared/been removed by now, so we must - * recheck */ - - if (recheck) - if (path_check_good(p, false)) { - log_debug("%s got triggered.", UNIT(p)->id); - path_enter_running(p); - return; - } - - path_set_state(p, PATH_WAITING); - return; - -fail: - log_warning("%s failed to enter waiting state: %s", UNIT(p)->id, strerror(-r)); - path_enter_dead(p, PATH_FAILURE_RESOURCES); -} - -static void path_mkdir(Path *p) { - PathSpec *s; - - assert(p); - - if (!p->make_directory) - return; - - LIST_FOREACH(spec, s, p->specs) - path_spec_mkdir(s, p->directory_mode); -} - -static int path_start(Unit *u) { - Path *p = PATH(u); - - assert(p); - assert(p->state == PATH_DEAD || p->state == PATH_FAILED); - - if (UNIT_DEREF(p->unit)->load_state != UNIT_LOADED) - return -ENOENT; - - path_mkdir(p); - - p->result = PATH_SUCCESS; - path_enter_waiting(p, true, true); - - return 0; -} - -static int path_stop(Unit *u) { - Path *p = PATH(u); - - assert(p); - assert(p->state == PATH_WAITING || p->state == PATH_RUNNING); - - path_enter_dead(p, PATH_SUCCESS); - return 0; -} - -static int path_serialize(Unit *u, FILE *f, FDSet *fds) { - Path *p = PATH(u); - - assert(u); - assert(f); - assert(fds); - - unit_serialize_item(u, f, "state", path_state_to_string(p->state)); - unit_serialize_item(u, f, "result", path_result_to_string(p->result)); - - return 0; -} - -static int path_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) { - Path *p = PATH(u); - - assert(u); - assert(key); - assert(value); - assert(fds); - - if (streq(key, "state")) { - PathState state; - - if ((state = path_state_from_string(value)) < 0) - log_debug("Failed to parse state value %s", value); - else - p->deserialized_state = state; - - } else if (streq(key, "result")) { - PathResult f; - - f = path_result_from_string(value); - if (f < 0) - log_debug("Failed to parse result value %s", value); - else if (f != PATH_SUCCESS) - p->result = f; - - } else - log_debug("Unknown serialization key '%s'", key); - - return 0; -} - -static UnitActiveState path_active_state(Unit *u) { - assert(u); - - return state_translation_table[PATH(u)->state]; -} - -static const char *path_sub_state_to_string(Unit *u) { - assert(u); - - return path_state_to_string(PATH(u)->state); -} - -static void path_fd_event(Unit *u, int fd, uint32_t events, Watch *w) { - Path *p = PATH(u); - PathSpec *s; - int changed; - - assert(p); - assert(fd >= 0); - - if (p->state != PATH_WAITING && - p->state != PATH_RUNNING) - return; - - /* log_debug("inotify wakeup on %s.", u->id); */ - - LIST_FOREACH(spec, s, p->specs) - if (path_spec_owns_inotify_fd(s, fd)) - break; - - if (!s) { - log_error("Got event on unknown fd."); - goto fail; - } - - changed = path_spec_fd_event(s, events); - if (changed < 0) - goto fail; - - /* If we are already running, then remember that one event was - * dispatched so that we restart the service only if something - * actually changed on disk */ - p->inotify_triggered = true; - - if (changed) - path_enter_running(p); - else - path_enter_waiting(p, false, true); - - return; - -fail: - path_enter_dead(p, PATH_FAILURE_RESOURCES); -} - -void path_unit_notify(Unit *u, UnitActiveState new_state) { - Iterator i; - Unit *k; - - if (u->type == UNIT_PATH) - return; - - SET_FOREACH(k, u->dependencies[UNIT_TRIGGERED_BY], i) { - Path *p; - - if (k->type != UNIT_PATH) - continue; - - if (k->load_state != UNIT_LOADED) - continue; - - p = PATH(k); - - if (p->state == PATH_RUNNING && new_state == UNIT_INACTIVE) { - log_debug("%s got notified about unit deactivation.", UNIT(p)->id); - - /* Hmm, so inotify was triggered since the - * last activation, so I guess we need to - * recheck what is going on. */ - path_enter_waiting(p, false, p->inotify_triggered); - } - } -} - -static void path_reset_failed(Unit *u) { - Path *p = PATH(u); - - assert(p); - - if (p->state == PATH_FAILED) - path_set_state(p, PATH_DEAD); - - p->result = PATH_SUCCESS; -} - -static const char* const path_state_table[_PATH_STATE_MAX] = { - [PATH_DEAD] = "dead", - [PATH_WAITING] = "waiting", - [PATH_RUNNING] = "running", - [PATH_FAILED] = "failed" -}; - -DEFINE_STRING_TABLE_LOOKUP(path_state, PathState); - -static const char* const path_type_table[_PATH_TYPE_MAX] = { - [PATH_EXISTS] = "PathExists", - [PATH_EXISTS_GLOB] = "PathExistsGlob", - [PATH_CHANGED] = "PathChanged", - [PATH_MODIFIED] = "PathModified", - [PATH_DIRECTORY_NOT_EMPTY] = "DirectoryNotEmpty" -}; - -DEFINE_STRING_TABLE_LOOKUP(path_type, PathType); - -static const char* const path_result_table[_PATH_RESULT_MAX] = { - [PATH_SUCCESS] = "success", - [PATH_FAILURE_RESOURCES] = "resources" -}; - -DEFINE_STRING_TABLE_LOOKUP(path_result, PathResult); - -const UnitVTable path_vtable = { - .object_size = sizeof(Path), - .sections = - "Unit\0" - "Path\0" - "Install\0", - - .init = path_init, - .done = path_done, - .load = path_load, - - .coldplug = path_coldplug, - - .dump = path_dump, - - .start = path_start, - .stop = path_stop, - - .serialize = path_serialize, - .deserialize_item = path_deserialize_item, - - .active_state = path_active_state, - .sub_state_to_string = path_sub_state_to_string, - - .fd_event = path_fd_event, - - .reset_failed = path_reset_failed, - - .bus_interface = "org.freedesktop.systemd1.Path", - .bus_message_handler = bus_path_message_handler, - .bus_invalidating_properties = bus_path_invalidating_properties -}; diff --git a/src/core/path.h b/src/core/path.h deleted file mode 100644 index 77926888ae..0000000000 --- a/src/core/path.h +++ /dev/null @@ -1,110 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -#pragma once - -/*** - 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/>. -***/ - -typedef struct Path Path; - -#include "unit.h" -#include "mount.h" - -typedef enum PathState { - PATH_DEAD, - PATH_WAITING, - PATH_RUNNING, - PATH_FAILED, - _PATH_STATE_MAX, - _PATH_STATE_INVALID = -1 -} PathState; - -typedef enum PathType { - PATH_EXISTS, - PATH_EXISTS_GLOB, - PATH_DIRECTORY_NOT_EMPTY, - PATH_CHANGED, - PATH_MODIFIED, - _PATH_TYPE_MAX, - _PATH_TYPE_INVALID = -1 -} PathType; - -typedef struct PathSpec { - char *path; - - Watch watch; - - LIST_FIELDS(struct PathSpec, spec); - - PathType type; - int inotify_fd; - int primary_wd; - - bool previous_exists; -} PathSpec; - -int path_spec_watch(PathSpec *s, Unit *u); -void path_spec_unwatch(PathSpec *s, Unit *u); -int path_spec_fd_event(PathSpec *s, uint32_t events); -void path_spec_done(PathSpec *s); - -static inline bool path_spec_owns_inotify_fd(PathSpec *s, int fd) { - return s->inotify_fd == fd; -} - -typedef enum PathResult { - PATH_SUCCESS, - PATH_FAILURE_RESOURCES, - _PATH_RESULT_MAX, - _PATH_RESULT_INVALID = -1 -} PathResult; - -struct Path { - Unit meta; - - LIST_HEAD(PathSpec, specs); - - UnitRef unit; - - PathState state, deserialized_state; - - bool inotify_triggered; - - bool make_directory; - mode_t directory_mode; - - PathResult result; -}; - -void path_unit_notify(Unit *u, UnitActiveState new_state); - -/* Called from the mount code figure out if a mount is a dependency of - * any of the paths of this path object */ -int path_add_one_mount_link(Path *p, Mount *m); - -extern const UnitVTable path_vtable; - -const char* path_state_to_string(PathState i); -PathState path_state_from_string(const char *s); - -const char* path_type_to_string(PathType i); -PathType path_type_from_string(const char *s); - -const char* path_result_to_string(PathResult i); -PathResult path_result_from_string(const char *s); diff --git a/src/core/securebits.h b/src/core/securebits.h deleted file mode 100644 index ba0bba5353..0000000000 --- a/src/core/securebits.h +++ /dev/null @@ -1,45 +0,0 @@ -#ifndef _LINUX_SECUREBITS_H -#define _LINUX_SECUREBITS_H 1 - -/* This is minimal version of Linux' linux/securebits.h header file, - * which is licensed GPL2 */ - -#define SECUREBITS_DEFAULT 0x00000000 - -/* When set UID 0 has no special privileges. When unset, we support - inheritance of root-permissions and suid-root executable under - compatibility mode. We raise the effective and inheritable bitmasks - *of the executable file* if the effective uid of the new process is - 0. If the real uid is 0, we raise the effective (legacy) bit of the - executable file. */ -#define SECURE_NOROOT 0 -#define SECURE_NOROOT_LOCKED 1 /* make bit-0 immutable */ - -/* When set, setuid to/from uid 0 does not trigger capability-"fixup". - When unset, to provide compatibility with old programs relying on - set*uid to gain/lose privilege, transitions to/from uid 0 cause - capabilities to be gained/lost. */ -#define SECURE_NO_SETUID_FIXUP 2 -#define SECURE_NO_SETUID_FIXUP_LOCKED 3 /* make bit-2 immutable */ - -/* When set, a process can retain its capabilities even after - transitioning to a non-root user (the set-uid fixup suppressed by - bit 2). Bit-4 is cleared when a process calls exec(); setting both - bit 4 and 5 will create a barrier through exec that no exec()'d - child can use this feature again. */ -#define SECURE_KEEP_CAPS 4 -#define SECURE_KEEP_CAPS_LOCKED 5 /* make bit-4 immutable */ - -/* Each securesetting is implemented using two bits. One bit specifies - whether the setting is on or off. The other bit specify whether the - setting is locked or not. A setting which is locked cannot be - changed from user-level. */ -#define issecure_mask(X) (1 << (X)) -#define issecure(X) (issecure_mask(X) & current_cred_xxx(securebits)) - -#define SECURE_ALL_BITS (issecure_mask(SECURE_NOROOT) | \ - issecure_mask(SECURE_NO_SETUID_FIXUP) | \ - issecure_mask(SECURE_KEEP_CAPS)) -#define SECURE_ALL_LOCKS (SECURE_ALL_BITS << 1) - -#endif /* !_LINUX_SECUREBITS_H */ diff --git a/src/core/selinux-access.c b/src/core/selinux-access.c deleted file mode 100644 index 6dfe8b45f3..0000000000 --- a/src/core/selinux-access.c +++ /dev/null @@ -1,441 +0,0 @@ -/*-*- 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 deleted file mode 100644 index 9183cbc9a6..0000000000 --- a/src/core/selinux-access.h +++ /dev/null @@ -1,62 +0,0 @@ -/*-*- 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 deleted file mode 100644 index e9c0de92f1..0000000000 --- a/src/core/selinux-setup.c +++ /dev/null @@ -1,123 +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 <unistd.h> -#include <stdio.h> -#include <errno.h> -#include <string.h> -#include <stdlib.h> - -#ifdef HAVE_SELINUX -#include <selinux/selinux.h> -#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" - -#ifdef HAVE_SELINUX -static int null_log(int type, const char *fmt, ...) { - return 0; -} -#endif - -int selinux_setup(bool *loaded_policy) { - -#ifdef HAVE_SELINUX - int enforce = 0; - usec_t before_load, after_load; - security_context_t con; - int r; - union selinux_callback cb; - - assert(loaded_policy); - - /* Turn off all of SELinux' own logging, we want to do that */ - cb.func_log = null_log; - selinux_set_callback(SELINUX_CB_LOG, cb); - - /* Make sure getcon() works, which needs /proc and /sys */ - mount_setup_early(); - - /* Already initialized by somebody else? */ - r = getcon_raw(&con); - if (r == 0) { - bool initialized; - - initialized = !streq(con, "kernel"); - freecon(con); - - if (initialized) - return 0; - } - - /* Make sure we have no fds open while loading the policy and - * transitioning */ - log_close(); - - /* 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; - - retest_selinux(); - - /* Transition to the new context */ - r = label_get_create_label_from_exe(SYSTEMD_BINARY_PATH, &label); - if (r < 0 || label == NULL) { - log_open(); - log_error("Failed to compute init label, ignoring."); - } else { - r = setcon(label); - - log_open(); - if (r < 0) - log_error("Failed to transition into init label '%s', ignoring.", label); - - label_free(label); - } - - after_load = now(CLOCK_MONOTONIC); - - log_info("Successfully loaded SELinux policy in %s.", - format_timespan(timespan, sizeof(timespan), after_load - before_load)); - - *loaded_policy = true; - - } else { - log_open(); - - if (enforce > 0) { - log_error("Failed to load SELinux policy. Freezing."); - return -EIO; - } else - log_debug("Unable to load SELinux policy. Ignoring."); - } -#endif - - return 0; -} diff --git a/src/core/selinux-setup.h b/src/core/selinux-setup.h deleted file mode 100644 index 39e2bc25bb..0000000000 --- a/src/core/selinux-setup.h +++ /dev/null @@ -1,26 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -#pragma once - -/*** - 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 <stdbool.h> - -int selinux_setup(bool *loaded_policy); diff --git a/src/core/service.c b/src/core/service.c deleted file mode 100644 index cf08485374..0000000000 --- a/src/core/service.c +++ /dev/null @@ -1,3941 +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 <signal.h> -#include <dirent.h> -#include <unistd.h> -#include <sys/reboot.h> - -#include "manager.h" -#include "unit.h" -#include "service.h" -#include "load-fragment.h" -#include "load-dropin.h" -#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" -#include "exit-status.h" -#include "def.h" -#include "path-util.h" -#include "util.h" -#include "utf8.h" - -#ifdef HAVE_SYSV_COMPAT - -#define DEFAULT_SYSV_TIMEOUT_USEC (5*USEC_PER_MINUTE) - -typedef enum RunlevelType { - RUNLEVEL_UP, - RUNLEVEL_DOWN, - RUNLEVEL_SYSINIT -} RunlevelType; - -static const struct { - const char *path; - const char *target; - const RunlevelType type; -} rcnd_table[] = { - /* Standard SysV runlevels for start-up */ - { "rc1.d", SPECIAL_RESCUE_TARGET, RUNLEVEL_UP }, - { "rc2.d", SPECIAL_RUNLEVEL2_TARGET, RUNLEVEL_UP }, - { "rc3.d", SPECIAL_RUNLEVEL3_TARGET, RUNLEVEL_UP }, - { "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 } - - /* Note that the order here matters, as we read the - 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 */ -}; - -#define RUNLEVELS_UP "12345" -/* #define RUNLEVELS_DOWN "06" */ -#define RUNLEVELS_BOOT "bBsS" -#endif - -static const UnitActiveState state_translation_table[_SERVICE_STATE_MAX] = { - [SERVICE_DEAD] = UNIT_INACTIVE, - [SERVICE_START_PRE] = UNIT_ACTIVATING, - [SERVICE_START] = UNIT_ACTIVATING, - [SERVICE_START_POST] = UNIT_ACTIVATING, - [SERVICE_RUNNING] = UNIT_ACTIVE, - [SERVICE_EXITED] = UNIT_ACTIVE, - [SERVICE_RELOAD] = UNIT_RELOADING, - [SERVICE_STOP] = UNIT_DEACTIVATING, - [SERVICE_STOP_SIGTERM] = UNIT_DEACTIVATING, - [SERVICE_STOP_SIGKILL] = UNIT_DEACTIVATING, - [SERVICE_STOP_POST] = UNIT_DEACTIVATING, - [SERVICE_FINAL_SIGTERM] = UNIT_DEACTIVATING, - [SERVICE_FINAL_SIGKILL] = UNIT_DEACTIVATING, - [SERVICE_FAILED] = UNIT_FAILED, - [SERVICE_AUTO_RESTART] = UNIT_ACTIVATING -}; - -/* For Type=idle we never want to delay any other jobs, hence we - * consider idle jobs active as soon as we start working on them */ -static const UnitActiveState state_translation_table_idle[_SERVICE_STATE_MAX] = { - [SERVICE_DEAD] = UNIT_INACTIVE, - [SERVICE_START_PRE] = UNIT_ACTIVE, - [SERVICE_START] = UNIT_ACTIVE, - [SERVICE_START_POST] = UNIT_ACTIVE, - [SERVICE_RUNNING] = UNIT_ACTIVE, - [SERVICE_EXITED] = UNIT_ACTIVE, - [SERVICE_RELOAD] = UNIT_RELOADING, - [SERVICE_STOP] = UNIT_DEACTIVATING, - [SERVICE_STOP_SIGTERM] = UNIT_DEACTIVATING, - [SERVICE_STOP_SIGKILL] = UNIT_DEACTIVATING, - [SERVICE_STOP_POST] = UNIT_DEACTIVATING, - [SERVICE_FINAL_SIGTERM] = UNIT_DEACTIVATING, - [SERVICE_FINAL_SIGKILL] = UNIT_DEACTIVATING, - [SERVICE_FAILED] = UNIT_FAILED, - [SERVICE_AUTO_RESTART] = UNIT_ACTIVATING -}; - -static void service_init(Unit *u) { - Service *s = SERVICE(u); - - assert(u); - assert(u->load_state == UNIT_STUB); - - 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; - - s->timer_watch.type = WATCH_INVALID; -#ifdef HAVE_SYSV_COMPAT - s->sysv_start_priority = -1; - s->sysv_start_priority_from_rcnd = -1; -#endif - s->socket_fd = -1; - s->guess_main_pid = true; - - exec_context_init(&s->exec_context); - kill_context_init(&s->kill_context); - - RATELIMIT_INIT(s->start_limit, 10*USEC_PER_SEC, 5); - - s->control_command_id = _SERVICE_EXEC_COMMAND_INVALID; -} - -static void service_unwatch_control_pid(Service *s) { - assert(s); - - if (s->control_pid <= 0) - return; - - unit_unwatch_pid(UNIT(s), s->control_pid); - s->control_pid = 0; -} - -static void service_unwatch_main_pid(Service *s) { - assert(s); - - if (s->main_pid <= 0) - return; - - unit_unwatch_pid(UNIT(s), s->main_pid); - s->main_pid = 0; -} - -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); - path_spec_unwatch(s->pid_file_pathspec, UNIT(s)); - path_spec_done(s->pid_file_pathspec); - free(s->pid_file_pathspec); - s->pid_file_pathspec = NULL; -} - -static int service_set_main_pid(Service *s, pid_t pid) { - pid_t ppid; - - assert(s); - - if (pid <= 1) - return -EINVAL; - - if (pid == getpid()) - return -EINVAL; - - s->main_pid = 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); - - s->main_pid_alien = true; - } else - s->main_pid_alien = false; - - exec_status_start(&s->main_exec_status, pid); - - return 0; -} - -static void service_close_socket_fd(Service *s) { - assert(s); - - if (s->socket_fd < 0) - return; - - close_nointr_nofail(s->socket_fd); - s->socket_fd = -1; -} - -static void service_connection_unref(Service *s) { - assert(s); - - if (!UNIT_DEREF(s->accept_socket)) - return; - - socket_connection_unref(SOCKET(UNIT_DEREF(s->accept_socket))); - unit_ref_unset(&s->accept_socket); -} - -static void service_stop_watchdog(Service *s) { - assert(s); - - unit_unwatch_timer(UNIT(s), &s->watchdog_watch); - s->watchdog_timestamp.realtime = 0; - s->watchdog_timestamp.monotonic = 0; -} - -static void service_enter_dead(Service *s, ServiceResult f, bool allow_restart); - -static void service_handle_watchdog(Service *s) { - usec_t offset; - int r; - - assert(s); - - if (s->watchdog_usec == 0) - return; - - offset = now(CLOCK_MONOTONIC) - s->watchdog_timestamp.monotonic; - if (offset >= s->watchdog_usec) { - log_error("%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); - if (r < 0) - log_warning("%s failed to install watchdog timer: %s", UNIT(s)->id, strerror(-r)); -} - -static void service_reset_watchdog(Service *s) { - assert(s); - - dual_timestamp_get(&s->watchdog_timestamp); - service_handle_watchdog(s); -} - -static void service_done(Unit *u) { - Service *s = SERVICE(u); - - assert(s); - - free(s->pid_file); - s->pid_file = NULL; - -#ifdef HAVE_SYSV_COMPAT - free(s->sysv_runlevels); - s->sysv_runlevels = NULL; -#endif - - free(s->status_text); - s->status_text = NULL; - - exec_context_done(&s->exec_context); - exec_command_free_array(s->exec_command, _SERVICE_EXEC_COMMAND_MAX); - 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); - service_unwatch_control_pid(s); - service_unwatch_pid_file(s); - - if (s->bus_name) { - unit_unwatch_bus_name(u, s->bus_name); - free(s->bus_name); - s->bus_name = NULL; - } - - service_close_socket_fd(s); - service_connection_unref(s); - - unit_ref_unset(&s->accept_socket); - - service_stop_watchdog(s); - - unit_unwatch_timer(u, &s->timer_watch); -} - -#ifdef HAVE_SYSV_COMPAT -static char *sysv_translate_name(const char *name) { - char *r; - - 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"); - - return r; -} - -static int sysv_translate_facility(const char *name, const char *filename, char **_r) { - - /* We silently ignore the $ prefix here. According to the LSB - * spec it simply indicates whether something is a - * standardized name or a distribution-specific one. Since we - * just follow what already exists and do not introduce new - * uses or names we don't care who introduced a new name. */ - - 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, - "syslog", SPECIAL_SYSLOG_TARGET, - "time", SPECIAL_TIME_SYNC_TARGET, - - /* common extensions */ - "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_SUSE - "smtp", SPECIAL_MAIL_TRANSFER_AGENT_TARGET, -#endif - }; - - unsigned i; - char *r; - const char *n; - - assert(name); - assert(_r); - - n = *name == '$' ? name + 1 : name; - - for (i = 0; i < ELEMENTSOF(table); i += 2) { - - if (!streq(table[i], n)) - continue; - - if (!table[i+1]) - return 0; - - if (!(r = strdup(table[i+1]))) - return -ENOMEM; - - goto finish; - } - - /* If we don't know this name, fallback heuristics to figure - * out whether something is a target or a service alias. */ - - if (*name == '$') { - if (!unit_prefix_is_valid(n)) - return -EINVAL; - - /* Facilities starting with $ are most likely targets */ - r = unit_name_build(n, NULL, ".target"); - } else if (filename && streq(name, filename)) - /* Names equaling the file name of the services are redundant */ - return 0; - else - /* Everything else we assume to be normal service names */ - r = sysv_translate_name(n); - - if (!r) - return -ENOMEM; - -finish: - *_r = r; - - return 1; -} - -static int sysv_fix_order(Service *s) { - Unit *other; - int r; - - assert(s); - - if (s->sysv_start_priority < 0) - return 0; - - /* For each pair of services where at least one lacks a LSB - * header, we use the start priority value to order things. */ - - LIST_FOREACH(units_by_type, other, UNIT(s)->manager->units_by_type[UNIT_SERVICE]) { - Service *t; - UnitDependency d; - bool special_s, special_t; - - t = SERVICE(other); - - if (s == t) - continue; - - if (UNIT(t)->load_state != UNIT_LOADED) - continue; - - if (t->sysv_start_priority < 0) - continue; - - /* If both units have modern headers we don't care - * about the priorities */ - if ((UNIT(s)->fragment_path || s->sysv_has_lsb) && - (UNIT(t)->fragment_path || t->sysv_has_lsb)) - continue; - - special_s = s->sysv_runlevels && !chars_intersect(RUNLEVELS_UP, s->sysv_runlevels); - special_t = t->sysv_runlevels && !chars_intersect(RUNLEVELS_UP, t->sysv_runlevels); - - if (special_t && !special_s) - d = UNIT_AFTER; - else if (special_s && !special_t) - d = UNIT_BEFORE; - else if (t->sysv_start_priority < s->sysv_start_priority) - d = UNIT_AFTER; - else if (t->sysv_start_priority > s->sysv_start_priority) - d = UNIT_BEFORE; - else - continue; - - /* FIXME: Maybe we should compare the name here lexicographically? */ - - if ((r = unit_add_dependency(UNIT(s), d, UNIT(t), true)) < 0) - return r; - } - - return 0; -} - -static ExecCommand *exec_command_new(const char *path, const char *arg1) { - ExecCommand *c; - - if (!(c = new0(ExecCommand, 1))) - return NULL; - - if (!(c->path = strdup(path))) { - free(c); - return NULL; - } - - if (!(c->argv = strv_new(path, arg1, NULL))) { - free(c->path); - free(c); - return NULL; - } - - return c; -} - -static int sysv_exec_commands(Service *s, const bool supports_reload) { - ExecCommand *c; - - assert(s); - assert(s->is_sysv); - assert(UNIT(s)->source_path); - - c = exec_command_new(UNIT(s)->source_path, "start"); - if (!c) - return -ENOMEM; - exec_command_append_list(s->exec_command+SERVICE_EXEC_START, c); - - c = exec_command_new(UNIT(s)->source_path, "stop"); - if (!c) - return -ENOMEM; - exec_command_append_list(s->exec_command+SERVICE_EXEC_STOP, 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; - unsigned line = 0; - int r; - enum { - NORMAL, - DESCRIPTION, - LSB, - 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); - - u = UNIT(s); - - f = fopen(path, "re"); - if (!f) { - r = errno == ENOENT ? 0 : -errno; - goto finish; - } - - if (fstat(fileno(f), &st) < 0) { - r = -errno; - goto finish; - } - - free(u->source_path); - u->source_path = strdup(path); - if (!u->source_path) { - r = -ENOMEM; - goto finish; - } - u->source_mtime = timespec_load(&st.st_mtim); - - if (null_or_empty(&st)) { - u->load_state = UNIT_MASKED; - r = 0; - goto finish; - } - - s->is_sysv = true; - - while (!feof(f)) { - char l[LINE_MAX], *t; - - if (!fgets(l, sizeof(l), f)) { - if (feof(f)) - break; - - r = -errno; - log_error("Failed to read configuration file '%s': %s", path, strerror(-r)); - goto finish; - } - - line++; - - t = strstrip(l); - 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; - s->sysv_has_lsb = true; - continue; - } - - if ((state == LSB_DESCRIPTION || state == LSB) && streq(t, "### END INIT INFO")) { - state = NORMAL; - continue; - } - - t++; - t += strspn(t, WHITESPACE); - - if (state == NORMAL) { - - /* Try to parse Red Hat style chkconfig headers */ - - if (startswith_no_case(t, "chkconfig:")) { - int start_priority; - char runlevels[16], *k; - - state = NORMAL; - - if (sscanf(t+10, "%15s %i %*i", - runlevels, - &start_priority) != 2) { - - log_warning("[%s:%u] Failed to parse chkconfig line. Ignoring.", path, line); - continue; - } - - /* A start priority gathered from the - * 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); - else - s->sysv_start_priority = start_priority; - - char_array_0(runlevels); - k = delete_chars(runlevels, WHITESPACE "-"); - - if (k[0]) { - char *d; - - if (!(d = strdup(k))) { - r = -ENOMEM; - goto finish; - } - - free(s->sysv_runlevels); - s->sysv_runlevels = d; - } - - } else if (startswith_no_case(t, "description:")) { - - size_t k = strlen(t); - char *d; - const char *j; - - if (t[k-1] == '\\') { - state = DESCRIPTION; - t[k-1] = 0; - } - - if ((j = strstrip(t+12)) && *j) { - if (!(d = strdup(j))) { - r = -ENOMEM; - goto finish; - } - } else - d = NULL; - - free(chkconfig_description); - chkconfig_description = d; - - } else if (startswith_no_case(t, "pidfile:")) { - - char *fn; - - state = NORMAL; - - fn = strstrip(t+8); - if (!path_is_absolute(fn)) { - log_warning("[%s:%u] PID file not absolute. Ignoring.", path, line); - continue; - } - - if (!(fn = strdup(fn))) { - r = -ENOMEM; - goto finish; - } - - free(s->pid_file); - s->pid_file = fn; - } - - } else if (state == DESCRIPTION) { - - /* Try to parse Red Hat style description - * continuation */ - - size_t k = strlen(t); - char *j; - - if (t[k-1] == '\\') - t[k-1] = 0; - else - state = NORMAL; - - if ((j = strstrip(t)) && *j) { - char *d = NULL; - - if (chkconfig_description) - d = strjoin(chkconfig_description, " ", j, NULL); - else - d = strdup(j); - - if (!d) { - r = -ENOMEM; - goto finish; - } - - free(chkconfig_description); - chkconfig_description = d; - } - - } else if (state == LSB || state == LSB_DESCRIPTION) { - - if (startswith_no_case(t, "Provides:")) { - char *i, *w; - size_t z; - - state = LSB; - - FOREACH_WORD_QUOTED(w, z, t+9, i) { - char *n, *m; - - if (!(n = strndup(w, z))) { - r = -ENOMEM; - goto finish; - } - - r = sysv_translate_facility(n, path_get_file_name(path), &m); - free(n); - - if (r < 0) - goto finish; - - if (r == 0) - continue; - - if (unit_name_to_type(m) == UNIT_SERVICE) - r = unit_add_name(u, m); - else - /* NB: SysV targets - * which are provided - * by a service are - * pulled in by the - * services, as an - * indication that the - * generic service is - * now available. This - * is strictly - * one-way. The - * targets do NOT pull - * in the SysV - * services! */ - 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)); - - free(m); - } - - } else if (startswith_no_case(t, "Required-Start:") || - startswith_no_case(t, "Should-Start:") || - startswith_no_case(t, "X-Start-Before:") || - startswith_no_case(t, "X-Start-After:")) { - char *i, *w; - size_t z; - - state = LSB; - - FOREACH_WORD_QUOTED(w, z, strchr(t, ':')+1, i) { - char *n, *m; - - if (!(n = strndup(w, z))) { - r = -ENOMEM; - goto finish; - } - - 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)); - free(n); - continue; - } - - free(n); - - if (r == 0) - continue; - - 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)); - - free(m); - } - } else if (startswith_no_case(t, "Default-Start:")) { - char *k, *d; - - state = LSB; - - k = delete_chars(t+14, WHITESPACE "-"); - - if (k[0] != 0) { - if (!(d = strdup(k))) { - r = -ENOMEM; - goto finish; - } - - free(s->sysv_runlevels); - s->sysv_runlevels = d; - } - - } else if (startswith_no_case(t, "Description:")) { - char *d, *j; - - state = LSB_DESCRIPTION; - - if ((j = strstrip(t+12)) && *j) { - if (!(d = strdup(j))) { - r = -ENOMEM; - goto finish; - } - } else - d = NULL; - - free(long_description); - long_description = d; - - } else if (startswith_no_case(t, "Short-Description:")) { - char *d, *j; - - state = LSB; - - if ((j = strstrip(t+18)) && *j) { - if (!(d = strdup(j))) { - r = -ENOMEM; - goto finish; - } - } else - d = NULL; - - free(short_description); - short_description = d; - - } else if (state == LSB_DESCRIPTION) { - - if (startswith(l, "#\t") || startswith(l, "# ")) { - char *j; - - if ((j = strstrip(t)) && *j) { - char *d = NULL; - - if (long_description) - d = strjoin(long_description, " ", t, NULL); - else - d = strdup(j); - - if (!d) { - r = -ENOMEM; - goto finish; - } - - free(long_description); - long_description = d; - } - - } else - state = LSB; - } - } - } - - 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 - * but none of the standard ones, then we assume this - * is some special kind of service (which might be - * needed for early boot) and don't create any links - * to it. */ - - UNIT(s)->default_dependencies = false; - - /* Don't timeout special services during boot (like fsck) */ - 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; - s->remain_after_exit = !s->pid_file; - s->guess_main_pid = false; - s->restart = SERVICE_RESTART_NO; - s->exec_context.ignore_sigpipe = false; - s->kill_context.kill_mode = KILL_PROCESS; - - /* We use the long description only if - * no short description is set. */ - - if (short_description) - description = short_description; - else if (chkconfig_description) - description = chkconfig_description; - else if (long_description) - description = long_description; - else - description = NULL; - - if (description) { - char *d; - - if (!(d = strappend(s->sysv_has_lsb ? "LSB: " : "SYSV: ", description))) { - r = -ENOMEM; - goto finish; - } - - u->description = d; - } - - /* The priority that has been set in /etc/rcN.d/ hierarchies - * takes precedence over what is stored as default in the LSB - * header */ - if (s->sysv_start_priority_from_rcnd >= 0) - s->sysv_start_priority = s->sysv_start_priority_from_rcnd; - - u->load_state = UNIT_LOADED; - r = 0; - -finish: - - if (f) - fclose(f); - - free(short_description); - free(long_description); - free(chkconfig_description); - - return r; -} - -static int service_load_sysv_name(Service *s, const char *name) { - char **p; - - assert(s); - assert(name); - - /* For SysV services we strip the boot.*, rc.* and *.sh - * prefixes/suffixes. */ -#if defined(TARGET_DEBIAN) || defined(TARGET_UBUNTU) || defined(TARGET_ANGSTROM) - if (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; - int r; - - path = strjoin(*p, "/", name, NULL); - if (!path) - return -ENOMEM; - - assert(endswith(path, ".service")); - path[strlen(path)-8] = 0; - - 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 */ - - path = strjoin(*p, "/rc.", name, NULL); - if (!path) - return -ENOMEM; - - /* Drop .service suffix */ - path[strlen(path)-8] = 0; - r = service_load_sysv_path(s, path); - free(path); - } -#endif - - if (r < 0) - return r; - - if ((UNIT(s)->load_state != UNIT_STUB)) - break; - } - - return 0; -} - -static int service_load_sysv(Service *s) { - const char *t; - Iterator i; - int r; - - assert(s); - - /* Load service data from SysV init scripts, preferably with - * LSB headers ... */ - - if (strv_isempty(UNIT(s)->manager->lookup_paths.sysvinit_path)) - return 0; - - if ((t = UNIT(s)->id)) - if ((r = service_load_sysv_name(s, t)) < 0) - return r; - - if (UNIT(s)->load_state == UNIT_STUB) - SET_FOREACH(t, UNIT(s)->names, i) { - if (t == UNIT(s)->id) - continue; - - if ((r = service_load_sysv_name(s, t)) < 0) - return r; - - if (UNIT(s)->load_state != UNIT_STUB) - break; - } - - return 0; -} -#endif - -static int fsck_fix_order(Service *s) { - Unit *other; - int r; - - assert(s); - - if (s->fsck_passno <= 0) - return 0; - - /* For each pair of services where both have an fsck priority - * we order things based on it. */ - - LIST_FOREACH(units_by_type, other, UNIT(s)->manager->units_by_type[UNIT_SERVICE]) { - Service *t; - UnitDependency d; - - t = SERVICE(other); - - if (s == t) - continue; - - if (UNIT(t)->load_state != UNIT_LOADED) - continue; - - if (t->fsck_passno <= 0) - continue; - - if (t->fsck_passno < s->fsck_passno) - d = UNIT_AFTER; - else if (t->fsck_passno > s->fsck_passno) - d = UNIT_BEFORE; - else - continue; - - if ((r = unit_add_dependency(UNIT(s), d, UNIT(t), true)) < 0) - return r; - } - - return 0; -} - -static int service_verify(Service *s) { - assert(s); - - if (UNIT(s)->load_state != UNIT_LOADED) - return 0; - - if (!s->exec_command[SERVICE_EXEC_START]) { - log_error("%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_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); - 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); - - 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); - return -EINVAL; - } - - return 0; -} - -static int service_add_default_dependencies(Service *s) { - int r; - - assert(s); - - /* Add a number of automatic dependencies useful for the - * majority of services. */ - - /* First, pull in base 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 == SYSTEMD_USER) { - - if ((r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SOCKETS_TARGET, NULL, true)) < 0) - return r; - } - - /* Second, activate normal shutdown */ - return unit_add_two_dependencies_by_name(UNIT(s), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, NULL, true); -} - -static void service_fix_output(Service *s) { - assert(s); - - /* If nothing has been explicitly configured, patch default - * output in. If input is socket/tty we avoid this however, - * since in that case we want output to default to the same - * place as we read input from. */ - - if (s->exec_context.std_error == EXEC_OUTPUT_INHERIT && - s->exec_context.std_output == EXEC_OUTPUT_INHERIT && - s->exec_context.std_input == EXEC_INPUT_NULL) - s->exec_context.std_error = UNIT(s)->manager->default_std_error; - - if (s->exec_context.std_output == EXEC_OUTPUT_INHERIT && - s->exec_context.std_input == EXEC_INPUT_NULL) - s->exec_context.std_output = UNIT(s)->manager->default_std_output; -} - -static int service_load(Unit *u) { - int r; - Service *s = SERVICE(u); - - assert(s); - - /* Load a .service file */ - if ((r = unit_load_fragment(u)) < 0) - return r; - -#ifdef HAVE_SYSV_COMPAT - /* Load a classic init script as a fallback, if we couldn't find anything */ - if (u->load_state == UNIT_STUB) - if ((r = service_load_sysv(s)) < 0) - return r; -#endif - - /* Still nothing found? Then let's give up */ - if (u->load_state == UNIT_STUB) - return -ENOENT; - - /* We were able to load something, then let's add in the - * dropin directories. */ - if ((r = unit_load_dropin(unit_follow_merge(u))) < 0) - return r; - - /* This is a new unit? Then let's add in some extras */ - if (u->load_state == UNIT_LOADED) { - if (s->type == _SERVICE_TYPE_INVALID) - s->type = s->bus_name ? SERVICE_DBUS : SERVICE_SIMPLE; - - /* 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); - - if ((r = unit_add_exec_dependencies(u, &s->exec_context)) < 0) - return r; - - if ((r = unit_add_default_cgroups(u)) < 0) - return r; - -#ifdef HAVE_SYSV_COMPAT - if ((r = sysv_fix_order(s)) < 0) - return r; -#endif - - if ((r = fsck_fix_order(s)) < 0) - return r; - - if (s->bus_name) - if ((r = unit_watch_bus_name(u, s->bus_name)) < 0) - return r; - - if (s->type == SERVICE_NOTIFY && s->notify_access == NOTIFY_NONE) - s->notify_access = NOTIFY_MAIN; - - if (s->watchdog_usec > 0 && s->notify_access == NOTIFY_NONE) - s->notify_access = NOTIFY_MAIN; - - if (s->type == SERVICE_DBUS || s->bus_name) - if ((r = unit_add_two_dependencies_by_name(u, UNIT_AFTER, UNIT_REQUIRES, SPECIAL_DBUS_SOCKET, NULL, true)) < 0) - return r; - - if (UNIT(s)->default_dependencies) - if ((r = service_add_default_dependencies(s)) < 0) - return r; - - r = unit_exec_context_defaults(u, &s->exec_context); - if (r < 0) - return r; - } - - return service_verify(s); -} - -static void service_dump(Unit *u, FILE *f, const char *prefix) { - - ServiceExecCommand c; - Service *s = SERVICE(u); - const char *prefix2; - char *p2; - - assert(s); - - p2 = strappend(prefix, "\t"); - prefix2 = p2 ? p2 : prefix; - - fprintf(f, - "%sService State: %s\n" - "%sResult: %s\n" - "%sReload Result: %s\n" - "%sPermissionsStartOnly: %s\n" - "%sRootDirectoryStartOnly: %s\n" - "%sRemainAfterExit: %s\n" - "%sGuessMainPID: %s\n" - "%sType: %s\n" - "%sRestart: %s\n" - "%sNotifyAccess: %s\n", - prefix, service_state_to_string(s->state), - prefix, service_result_to_string(s->result), - prefix, service_result_to_string(s->reload_result), - prefix, yes_no(s->permissions_start_only), - prefix, yes_no(s->root_directory_start_only), - prefix, yes_no(s->remain_after_exit), - prefix, yes_no(s->guess_main_pid), - prefix, service_type_to_string(s->type), - prefix, service_restart_to_string(s->restart), - prefix, notify_access_to_string(s->notify_access)); - - if (s->control_pid > 0) - fprintf(f, - "%sControl PID: %lu\n", - prefix, (unsigned long) s->control_pid); - - if (s->main_pid > 0) - fprintf(f, - "%sMain PID: %lu\n" - "%sMain PID Known: %s\n" - "%sMain PID Alien: %s\n", - prefix, (unsigned long) s->main_pid, - prefix, yes_no(s->main_pid_known), - prefix, yes_no(s->main_pid_alien)); - - if (s->pid_file) - fprintf(f, - "%sPIDFile: %s\n", - prefix, s->pid_file); - - if (s->bus_name) - fprintf(f, - "%sBusName: %s\n" - "%sBus Name Good: %s\n", - prefix, s->bus_name, - prefix, yes_no(s->bus_name_good)); - - kill_context_dump(&s->kill_context, f, prefix); - exec_context_dump(&s->exec_context, f, prefix); - - for (c = 0; c < _SERVICE_EXEC_COMMAND_MAX; c++) { - - if (!s->exec_command[c]) - continue; - - fprintf(f, "%s-> %s:\n", - prefix, service_exec_command_to_string(c)); - - exec_command_dump_list(s->exec_command[c], f, prefix2); - } - -#ifdef HAVE_SYSV_COMPAT - if (s->is_sysv) - fprintf(f, - "%sSysV Init Script has LSB Header: %s\n" - "%sSysVEnabled: %s\n", - prefix, yes_no(s->sysv_has_lsb), - prefix, yes_no(s->sysv_enabled)); - - if (s->sysv_start_priority >= 0) - fprintf(f, - "%sSysVStartPriority: %i\n", - prefix, s->sysv_start_priority); - - if (s->sysv_runlevels) - fprintf(f, "%sSysVRunLevels: %s\n", - prefix, s->sysv_runlevels); -#endif - - if (s->fsck_passno > 0) - fprintf(f, - "%sFsckPassNo: %i\n", - prefix, s->fsck_passno); - - if (s->status_text) - fprintf(f, "%sStatus Text: %s\n", - prefix, s->status_text); - - free(p2); -} - -static int service_load_pid_file(Service *s, bool may_warn) { - char *k; - int r; - pid_t pid; - - assert(s); - - if (!s->pid_file) - return -ENOENT; - - 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)); - return r; - } - - r = parse_pid(k, &pid); - free(k); - - if (r < 0) - return r; - - 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); - return -ESRCH; - } - - if (s->main_pid_known) { - if (pid == s->main_pid) - return 0; - - log_debug("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); - - if ((r = service_set_main_pid(s, pid)) < 0) - return r; - - if ((r = unit_watch_pid(UNIT(s), pid)) < 0) - /* FIXME: we need to do something here */ - return r; - - return 0; -} - -static int service_search_main_pid(Service *s) { - pid_t pid; - int r; - - assert(s); - - /* If we know it anyway, don't ever fallback to unreliable - * heuristics */ - if (s->main_pid_known) - return 0; - - if (!s->guess_main_pid) - return 0; - - assert(s->main_pid <= 0); - - if ((pid = cgroup_bonding_search_main_pid_list(UNIT(s)->cgroup_bondings)) <= 0) - return -ENOENT; - - log_debug("Main PID guessed: %lu", (unsigned long) pid); - if ((r = service_set_main_pid(s, pid)) < 0) - return r; - - if ((r = unit_watch_pid(UNIT(s), pid)) < 0) - /* FIXME: we need to do something here */ - return r; - - return 0; -} - -static void service_notify_sockets_dead(Service *s, bool failed_permanent) { - Iterator i; - Unit *u; - - assert(s); - - /* Notifies all our sockets when we die */ - - if (s->socket_fd >= 0) - return; - - SET_FOREACH(u, UNIT(s)->dependencies[UNIT_TRIGGERED_BY], i) - if (u->type == UNIT_SOCKET) - socket_notify_service_dead(SOCKET(u), failed_permanent); - - return; -} - -static void service_set_state(Service *s, ServiceState state) { - ServiceState old_state; - const UnitActiveState *table; - assert(s); - - table = s->type == SERVICE_IDLE ? state_translation_table_idle : state_translation_table; - - old_state = s->state; - s->state = state; - - service_unwatch_pid_file(s); - - if (state != SERVICE_START_PRE && - state != SERVICE_START && - state != SERVICE_START_POST && - state != SERVICE_RELOAD && - state != SERVICE_STOP && - state != SERVICE_STOP_SIGTERM && - state != SERVICE_STOP_SIGKILL && - state != SERVICE_STOP_POST && - state != SERVICE_FINAL_SIGTERM && - state != SERVICE_FINAL_SIGKILL && - state != SERVICE_AUTO_RESTART) - unit_unwatch_timer(UNIT(s), &s->timer_watch); - - if (state != SERVICE_START && - state != SERVICE_START_POST && - state != SERVICE_RUNNING && - state != SERVICE_RELOAD && - state != SERVICE_STOP && - state != SERVICE_STOP_SIGTERM && - state != SERVICE_STOP_SIGKILL) { - service_unwatch_main_pid(s); - s->main_command = NULL; - } - - if (state != SERVICE_START_PRE && - state != SERVICE_START && - state != SERVICE_START_POST && - state != SERVICE_RELOAD && - state != SERVICE_STOP && - state != SERVICE_STOP_SIGTERM && - state != SERVICE_STOP_SIGKILL && - state != SERVICE_STOP_POST && - state != SERVICE_FINAL_SIGTERM && - state != SERVICE_FINAL_SIGKILL) { - service_unwatch_control_pid(s); - s->control_command = NULL; - s->control_command_id = _SERVICE_EXEC_COMMAND_INVALID; - } - - if (state == SERVICE_DEAD || - state == SERVICE_STOP || - state == SERVICE_STOP_SIGTERM || - state == SERVICE_STOP_SIGKILL || - state == SERVICE_STOP_POST || - state == SERVICE_FINAL_SIGTERM || - state == SERVICE_FINAL_SIGKILL || - state == SERVICE_FAILED || - state == SERVICE_AUTO_RESTART) - service_notify_sockets_dead(s, false); - - if (state != SERVICE_START_PRE && - state != SERVICE_START && - state != SERVICE_START_POST && - state != SERVICE_RUNNING && - state != SERVICE_RELOAD && - state != SERVICE_STOP && - state != SERVICE_STOP_SIGTERM && - state != SERVICE_STOP_SIGKILL && - state != SERVICE_STOP_POST && - state != SERVICE_FINAL_SIGTERM && - state != SERVICE_FINAL_SIGKILL && - !(state == SERVICE_DEAD && UNIT(s)->job)) { - service_close_socket_fd(s); - service_connection_unref(s); - } - - if (state == SERVICE_STOP) - service_stop_watchdog(s); - - /* For the inactive states unit_notify() will trim the cgroup, - * but for exit we have to do that ourselves... */ - if (state == SERVICE_EXITED && UNIT(s)->manager->n_reloading <= 0) - cgroup_bonding_trim_list(UNIT(s)->cgroup_bondings, true); - - if (old_state != state) - log_struct(LOG_DEBUG, - "UNIT=%s", UNIT(s)->id, - "MESSAGE=%s changed %s -> %s", UNIT(s)->id, - service_state_to_string(old_state), - service_state_to_string(state), - NULL); - - unit_notify(UNIT(s), table[old_state], table[state], s->reload_result == SERVICE_SUCCESS); - s->reload_result = SERVICE_SUCCESS; -} - -static int service_coldplug(Unit *u) { - Service *s = SERVICE(u); - int r; - - assert(s); - assert(s->state == SERVICE_DEAD); - - if (s->deserialized_state != s->state) { - - if (s->deserialized_state == SERVICE_START_PRE || - s->deserialized_state == SERVICE_START || - s->deserialized_state == SERVICE_START_POST || - s->deserialized_state == SERVICE_RELOAD || - s->deserialized_state == SERVICE_STOP || - s->deserialized_state == SERVICE_STOP_SIGTERM || - s->deserialized_state == SERVICE_STOP_SIGKILL || - s->deserialized_state == SERVICE_STOP_POST || - 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_start_usec > 0) { - usec_t k; - - 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) - return r; - } - } - - if ((s->deserialized_state == SERVICE_START && - (s->type == SERVICE_FORKING || - s->type == SERVICE_DBUS || - s->type == SERVICE_ONESHOT || - s->type == SERVICE_NOTIFY)) || - s->deserialized_state == SERVICE_START_POST || - s->deserialized_state == SERVICE_RUNNING || - s->deserialized_state == SERVICE_RELOAD || - s->deserialized_state == SERVICE_STOP || - s->deserialized_state == SERVICE_STOP_SIGTERM || - s->deserialized_state == SERVICE_STOP_SIGKILL) - if (s->main_pid > 0) - if ((r = unit_watch_pid(UNIT(s), s->main_pid)) < 0) - return r; - - if (s->deserialized_state == SERVICE_START_PRE || - s->deserialized_state == SERVICE_START || - s->deserialized_state == SERVICE_START_POST || - s->deserialized_state == SERVICE_RELOAD || - s->deserialized_state == SERVICE_STOP || - s->deserialized_state == SERVICE_STOP_SIGTERM || - s->deserialized_state == SERVICE_STOP_SIGKILL || - s->deserialized_state == SERVICE_STOP_POST || - s->deserialized_state == SERVICE_FINAL_SIGTERM || - s->deserialized_state == SERVICE_FINAL_SIGKILL) - if (s->control_pid > 0) - if ((r = unit_watch_pid(UNIT(s), s->control_pid)) < 0) - return r; - - if (s->deserialized_state == SERVICE_START_POST || - s->deserialized_state == SERVICE_RUNNING) - service_handle_watchdog(s); - - service_set_state(s, s->deserialized_state); - } - return 0; -} - -static int service_collect_fds(Service *s, int **fds, unsigned *n_fds) { - Iterator i; - int r; - int *rfds = NULL; - unsigned rn_fds = 0; - Unit *u; - - assert(s); - assert(fds); - assert(n_fds); - - if (s->socket_fd >= 0) - return 0; - - SET_FOREACH(u, UNIT(s)->dependencies[UNIT_TRIGGERED_BY], i) { - int *cfds; - unsigned cn_fds; - Socket *sock; - - if (u->type != UNIT_SOCKET) - continue; - - sock = SOCKET(u); - - if ((r = socket_collect_fds(sock, &cfds, &cn_fds)) < 0) - goto fail; - - if (!cfds) - continue; - - if (!rfds) { - rfds = cfds; - rn_fds = cn_fds; - } else { - int *t; - - if (!(t = new(int, rn_fds+cn_fds))) { - free(cfds); - r = -ENOMEM; - goto fail; - } - - memcpy(t, rfds, rn_fds * sizeof(int)); - memcpy(t+rn_fds, cfds, cn_fds * sizeof(int)); - free(rfds); - free(cfds); - - rfds = t; - rn_fds = rn_fds+cn_fds; - } - } - - *fds = rfds; - *n_fds = rn_fds; - - return 0; - -fail: - free(rfds); - - return r; -} - -static int service_spawn( - Service *s, - ExecCommand *c, - bool timeout, - bool pass_fds, - bool apply_permissions, - bool apply_chroot, - bool apply_tty_stdin, - bool set_notify_socket, - bool is_control, - pid_t *_pid) { - - pid_t pid; - int r; - int *fds = NULL, *fdsbuf = NULL; - unsigned n_fds = 0, n_env = 0; - char **argv = NULL, **final_env = NULL, **our_env = NULL; - - assert(s); - assert(c); - assert(_pid); - - if (pass_fds || - s->exec_context.std_input == EXEC_INPUT_SOCKET || - s->exec_context.std_output == EXEC_OUTPUT_SOCKET || - s->exec_context.std_error == EXEC_OUTPUT_SOCKET) { - - if (s->socket_fd >= 0) { - fds = &s->socket_fd; - n_fds = 1; - } else { - if ((r = service_collect_fds(s, &fdsbuf, &n_fds)) < 0) - goto fail; - - fds = fdsbuf; - } - } - - if (timeout && s->timeout_start_usec) { - r = unit_watch_timer(UNIT(s), s->timeout_start_usec, &s->timer_watch); - if (r < 0) - goto fail; - } else - unit_unwatch_timer(UNIT(s), &s->timer_watch); - - if (!(argv = unit_full_printf_strv(UNIT(s), c->argv))) { - r = -ENOMEM; - goto fail; - } - - our_env = new0(char*, 5); - if (!our_env) { - r = -ENOMEM; - goto fail; - } - - if (set_notify_socket) - if (asprintf(our_env + n_env++, "NOTIFY_SOCKET=%s", UNIT(s)->manager->notify_socket) < 0) { - r = -ENOMEM; - goto fail; - } - - if (s->main_pid > 0) - if (asprintf(our_env + n_env++, "MAINPID=%lu", (unsigned long) s->main_pid) < 0) { - r = -ENOMEM; - goto fail; - } - - if (s->watchdog_usec > 0) - if (asprintf(our_env + n_env++, "WATCHDOG_USEC=%llu", (unsigned long long) s->watchdog_usec) < 0) { - r = -ENOMEM; - goto fail; - } - - 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; - } - - r = exec_spawn(c, - argv, - &s->exec_context, - fds, n_fds, - final_env, - apply_permissions, - apply_chroot, - apply_tty_stdin, - UNIT(s)->manager->confirm_spawn, - UNIT(s)->cgroup_bondings, - UNIT(s)->cgroup_attributes, - is_control ? "control" : NULL, - UNIT(s)->id, - s->type == SERVICE_IDLE ? UNIT(s)->manager->idle_pipe : NULL, - &pid); - - if (r < 0) - goto fail; - - if ((r = unit_watch_pid(UNIT(s), pid)) < 0) - /* FIXME: we need to do something here */ - goto fail; - - free(fdsbuf); - strv_free(argv); - strv_free(our_env); - strv_free(final_env); - - *_pid = pid; - - return 0; - -fail: - free(fdsbuf); - strv_free(argv); - strv_free(our_env); - strv_free(final_env); - - if (timeout) - unit_unwatch_timer(UNIT(s), &s->timer_watch); - - return r; -} - -static int main_pid_good(Service *s) { - assert(s); - - /* Returns 0 if the pid is dead, 1 if it is good, -1 if we - * don't know */ - - /* If we know the pid file, then lets just check if it is - * still valid */ - if (s->main_pid_known) { - - /* If it's an alien child let's check if it is still - * alive ... */ - if (s->main_pid_alien) - return kill(s->main_pid, 0) >= 0 || errno != ESRCH; - - /* .. otherwise assume we'll get a SIGCHLD for it, - * which we really should wait for to collect exit - * status and code */ - return s->main_pid > 0; - } - - /* We don't know the pid */ - return -EAGAIN; -} - -static int control_pid_good(Service *s) { - assert(s); - - return s->control_pid > 0; -} - -static int cgroup_good(Service *s) { - int r; - - assert(s); - - if ((r = cgroup_bonding_is_empty_list(UNIT(s)->cgroup_bondings)) < 0) - return r; - - return !r; -} - -static void service_enter_dead(Service *s, ServiceResult f, bool allow_restart) { - int r; - assert(s); - - if (f != SERVICE_SUCCESS) - s->result = f; - - service_set_state(s, s->result != SERVICE_SUCCESS ? SERVICE_FAILED : SERVICE_DEAD); - - if (allow_restart && - !s->forbid_restart && - (s->restart == SERVICE_RESTART_ALWAYS || - (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))) && - (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), s->restart_usec, &s->timer_watch); - if (r < 0) - goto fail; - - service_set_state(s, SERVICE_AUTO_RESTART); - } - - s->forbid_restart = false; - - return; - -fail: - log_warning("%s failed to run install restart timer: %s", UNIT(s)->id, strerror(-r)); - service_enter_dead(s, SERVICE_FAILURE_RESOURCES, false); -} - -static void service_enter_signal(Service *s, ServiceState state, ServiceResult f); - -static void service_enter_stop_post(Service *s, ServiceResult f) { - int r; - assert(s); - - if (f != SERVICE_SUCCESS) - s->result = f; - - service_unwatch_control_pid(s); - - if ((s->control_command = s->exec_command[SERVICE_EXEC_STOP_POST])) { - s->control_command_id = SERVICE_EXEC_STOP_POST; - - r = service_spawn(s, - s->control_command, - true, - false, - !s->permissions_start_only, - !s->root_directory_start_only, - true, - false, - true, - &s->control_pid); - if (r < 0) - goto fail; - - - service_set_state(s, SERVICE_STOP_POST); - } else - service_enter_signal(s, SERVICE_FINAL_SIGTERM, SERVICE_SUCCESS); - - return; - -fail: - log_warning("%s failed to run 'stop-post' task: %s", UNIT(s)->id, strerror(-r)); - service_enter_signal(s, SERVICE_FINAL_SIGTERM, SERVICE_FAILURE_RESOURCES); -} - -static void service_enter_signal(Service *s, ServiceState state, ServiceResult f) { - int r; - Set *pid_set = NULL; - bool wait_for_exit = false; - - assert(s); - - if (f != SERVICE_SUCCESS) - s->result = f; - - if (s->kill_context.kill_mode != KILL_NONE) { - int sig = (state == SERVICE_STOP_SIGTERM || state == SERVICE_FINAL_SIGTERM) ? s->kill_context.kill_signal : SIGKILL; - - 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); - 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); - else - wait_for_exit = true; - } - - if (s->kill_context.kill_mode == KILL_CONTROL_GROUP) { - - pid_set = set_new(trivial_hash_func, trivial_compare_func); - if (!pid_set) { - r = -ENOMEM; - goto fail; - } - - /* Exclude the main/control pids from being killed via the cgroup */ - if (s->main_pid > 0) - if ((r = set_put(pid_set, LONG_TO_PTR(s->main_pid))) < 0) - goto fail; - - if (s->control_pid > 0) - if ((r = set_put(pid_set, LONG_TO_PTR(s->control_pid))) < 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)); - } else if (r > 0) - wait_for_exit = true; - - set_free(pid_set); - pid_set = NULL; - } - } - - if (wait_for_exit) { - if (s->timeout_stop_usec > 0) { - r = unit_watch_timer(UNIT(s), 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) - service_enter_stop_post(s, SERVICE_SUCCESS); - else - service_enter_dead(s, SERVICE_SUCCESS, true); - - return; - -fail: - log_warning("%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); - else - service_enter_dead(s, SERVICE_FAILURE_RESOURCES, true); - - if (pid_set) - set_free(pid_set); -} - -static void service_enter_stop(Service *s, ServiceResult f) { - int r; - - assert(s); - - if (f != SERVICE_SUCCESS) - s->result = f; - - service_unwatch_control_pid(s); - - if ((s->control_command = s->exec_command[SERVICE_EXEC_STOP])) { - s->control_command_id = SERVICE_EXEC_STOP; - - r = service_spawn(s, - s->control_command, - true, - false, - !s->permissions_start_only, - !s->root_directory_start_only, - false, - false, - true, - &s->control_pid); - if (r < 0) - goto fail; - - service_set_state(s, SERVICE_STOP); - } else - service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_SUCCESS); - - return; - -fail: - log_warning("%s failed to run 'stop' task: %s", UNIT(s)->id, strerror(-r)); - service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_FAILURE_RESOURCES); -} - -static void service_enter_running(Service *s, ServiceResult f) { - int main_pid_ok, cgroup_ok; - assert(s); - - if (f != SERVICE_SUCCESS) - s->result = f; - - main_pid_ok = main_pid_good(s); - cgroup_ok = cgroup_good(s); - - if ((main_pid_ok > 0 || (main_pid_ok < 0 && cgroup_ok != 0)) && - (s->bus_name_good || s->type != SERVICE_DBUS)) - service_set_state(s, SERVICE_RUNNING); - else if (s->remain_after_exit) - service_set_state(s, SERVICE_EXITED); - else - service_enter_stop(s, SERVICE_SUCCESS); -} - -static void service_enter_start_post(Service *s) { - int r; - assert(s); - - service_unwatch_control_pid(s); - - if (s->watchdog_usec > 0) - service_reset_watchdog(s); - - if ((s->control_command = s->exec_command[SERVICE_EXEC_START_POST])) { - s->control_command_id = SERVICE_EXEC_START_POST; - - r = service_spawn(s, - s->control_command, - true, - false, - !s->permissions_start_only, - !s->root_directory_start_only, - false, - false, - true, - &s->control_pid); - if (r < 0) - goto fail; - - service_set_state(s, SERVICE_START_POST); - } else - service_enter_running(s, SERVICE_SUCCESS); - - return; - -fail: - log_warning("%s failed to run 'start-post' task: %s", UNIT(s)->id, strerror(-r)); - service_enter_stop(s, SERVICE_FAILURE_RESOURCES); -} - -static void service_enter_start(Service *s) { - pid_t pid; - int r; - ExecCommand *c; - - assert(s); - - assert(s->exec_command[SERVICE_EXEC_START]); - assert(!s->exec_command[SERVICE_EXEC_START]->command_next || s->type == SERVICE_ONESHOT); - - if (s->type == SERVICE_FORKING) - service_unwatch_control_pid(s); - else - service_unwatch_main_pid(s); - - /* We want to ensure that nobody leaks processes from - * START_PRE here, so let's go on a killing spree, People - * should not spawn long running processes from START_PRE. */ - cgroup_bonding_kill_list(UNIT(s)->cgroup_bondings, SIGKILL, true, true, NULL, "control"); - - if (s->type == SERVICE_FORKING) { - s->control_command_id = SERVICE_EXEC_START; - c = s->control_command = s->exec_command[SERVICE_EXEC_START]; - - s->main_command = NULL; - } else { - s->control_command_id = _SERVICE_EXEC_COMMAND_INVALID; - s->control_command = NULL; - - c = s->main_command = s->exec_command[SERVICE_EXEC_START]; - } - - r = service_spawn(s, - c, - s->type == SERVICE_FORKING || s->type == SERVICE_DBUS || s->type == SERVICE_NOTIFY || s->type == SERVICE_ONESHOT, - true, - true, - true, - true, - s->notify_access != NOTIFY_NONE, - false, - &pid); - if (r < 0) - goto fail; - - if (s->type == SERVICE_SIMPLE || s->type == SERVICE_IDLE) { - /* For simple services we immediately start - * the START_POST binaries. */ - - service_set_main_pid(s, pid); - service_enter_start_post(s); - - } else if (s->type == SERVICE_FORKING) { - - /* For forking services we wait until the start - * process exited. */ - - s->control_pid = pid; - service_set_state(s, SERVICE_START); - - } else if (s->type == SERVICE_ONESHOT || - s->type == SERVICE_DBUS || - s->type == SERVICE_NOTIFY) { - - /* For oneshot services we wait until the start - * process exited, too, but it is our main process. */ - - /* For D-Bus services we know the main pid right away, - * but wait for the bus name to appear on the - * bus. Notify services are similar. */ - - service_set_main_pid(s, pid); - service_set_state(s, SERVICE_START); - } else - assert_not_reached("Unknown service type"); - - return; - -fail: - log_warning("%s failed to run 'start' task: %s", UNIT(s)->id, strerror(-r)); - service_enter_signal(s, SERVICE_FINAL_SIGTERM, SERVICE_FAILURE_RESOURCES); -} - -static void service_enter_start_pre(Service *s) { - int r; - - assert(s); - - service_unwatch_control_pid(s); - - if ((s->control_command = s->exec_command[SERVICE_EXEC_START_PRE])) { - - /* Before we start anything, let's clear up what might - * be left from previous runs. */ - cgroup_bonding_kill_list(UNIT(s)->cgroup_bondings, SIGKILL, true, true, NULL, "control"); - - s->control_command_id = SERVICE_EXEC_START_PRE; - - r = service_spawn(s, - s->control_command, - true, - false, - !s->permissions_start_only, - !s->root_directory_start_only, - true, - false, - true, - &s->control_pid); - if (r < 0) - goto fail; - - service_set_state(s, SERVICE_START_PRE); - } else - service_enter_start(s); - - return; - -fail: - log_warning("%s failed to run 'start-pre' task: %s", UNIT(s)->id, strerror(-r)); - service_enter_dead(s, SERVICE_FAILURE_RESOURCES, true); -} - -static void service_enter_restart(Service *s) { - int r; - DBusError error; - - assert(s); - dbus_error_init(&error); - - 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."); - - r = unit_watch_timer(UNIT(s), s->restart_usec, &s->timer_watch); - if (r < 0) - goto fail; - - return; - } - - /* Any units that are bound to this service must also be - * restarted. We use JOB_RESTART (instead of the more obvious - * JOB_START) here so that those dependency jobs will be added - * as well. */ - r = manager_add_job(UNIT(s)->manager, JOB_RESTART, UNIT(s), JOB_FAIL, false, &error, NULL); - if (r < 0) - goto fail; - - /* Note that we stay in the SERVICE_AUTO_RESTART state here, - * 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); - return; - -fail: - log_warning("%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); -} - -static void service_enter_reload(Service *s) { - int r; - - assert(s); - - service_unwatch_control_pid(s); - - if ((s->control_command = s->exec_command[SERVICE_EXEC_RELOAD])) { - s->control_command_id = SERVICE_EXEC_RELOAD; - - r = service_spawn(s, - s->control_command, - true, - false, - !s->permissions_start_only, - !s->root_directory_start_only, - false, - false, - true, - &s->control_pid); - if (r < 0) - goto fail; - - service_set_state(s, SERVICE_RELOAD); - } else - service_enter_running(s, SERVICE_SUCCESS); - - return; - -fail: - log_warning("%s failed to run 'reload' task: %s", UNIT(s)->id, strerror(-r)); - s->reload_result = SERVICE_FAILURE_RESOURCES; - service_enter_running(s, SERVICE_SUCCESS); -} - -static void service_run_next_control(Service *s) { - int r; - - assert(s); - assert(s->control_command); - assert(s->control_command->command_next); - - assert(s->control_command_id != SERVICE_EXEC_START); - - s->control_command = s->control_command->command_next; - service_unwatch_control_pid(s); - - r = service_spawn(s, - s->control_command, - true, - false, - !s->permissions_start_only, - !s->root_directory_start_only, - s->control_command_id == SERVICE_EXEC_START_PRE || - s->control_command_id == SERVICE_EXEC_STOP_POST, - false, - true, - &s->control_pid); - if (r < 0) - goto fail; - - return; - -fail: - log_warning("%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); - else if (s->state == SERVICE_STOP) - service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_FAILURE_RESOURCES); - else if (s->state == SERVICE_STOP_POST) - service_enter_dead(s, SERVICE_FAILURE_RESOURCES, true); - else if (s->state == SERVICE_RELOAD) { - s->reload_result = SERVICE_FAILURE_RESOURCES; - service_enter_running(s, SERVICE_SUCCESS); - } else - service_enter_stop(s, SERVICE_FAILURE_RESOURCES); -} - -static void service_run_next_main(Service *s) { - pid_t pid; - int r; - - assert(s); - assert(s->main_command); - assert(s->main_command->command_next); - assert(s->type == SERVICE_ONESHOT); - - s->main_command = s->main_command->command_next; - service_unwatch_main_pid(s); - - r = service_spawn(s, - s->main_command, - true, - true, - true, - true, - true, - s->notify_access != NOTIFY_NONE, - false, - &pid); - if (r < 0) - goto fail; - - service_set_main_pid(s, pid); - - return; - -fail: - log_warning("%s failed to run next main task: %s", UNIT(s)->id, strerror(-r)); - service_enter_stop(s, SERVICE_FAILURE_RESOURCES); -} - -static int service_start_limit_test(Service *s) { - assert(s); - - if (ratelimit_test(&s->start_limit)) - return 0; - - switch (s->start_limit_action) { - - case SERVICE_START_LIMIT_NONE: - log_warning("%s start request repeated too quickly, refusing to start.", UNIT(s)->id); - break; - - case SERVICE_START_LIMIT_REBOOT: { - DBusError error; - int r; - - dbus_error_init(&error); - - log_warning("%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)); - dbus_error_free(&error); - } - - break; - } - - case SERVICE_START_LIMIT_REBOOT_FORCE: - log_warning("%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); - reboot(RB_AUTOBOOT); - break; - - default: - log_error("start limit action=%i", s->start_limit_action); - assert_not_reached("Unknown StartLimitAction."); - } - - return -ECANCELED; -} - -static int service_start(Unit *u) { - Service *s = SERVICE(u); - int r; - - assert(s); - - /* We cannot fulfill this request right now, try again later - * please! */ - if (s->state == SERVICE_STOP || - s->state == SERVICE_STOP_SIGTERM || - s->state == SERVICE_STOP_SIGKILL || - s->state == SERVICE_STOP_POST || - s->state == SERVICE_FINAL_SIGTERM || - s->state == SERVICE_FINAL_SIGKILL) - return -EAGAIN; - - /* Already on it! */ - if (s->state == SERVICE_START_PRE || - s->state == SERVICE_START || - s->state == SERVICE_START_POST) - return 0; - - /* A service that will be restarted must be stopped first to - * trigger BindsTo and/or OnFailure dependencies. If a user - * does not want to wait for the holdoff time to elapse, the - * service should be manually restarted, not started. We - * simply return EAGAIN here, so that any start jobs stay - * queued, and assume that the auto restart timer will - * eventually trigger the restart. */ - if (s->state == SERVICE_AUTO_RESTART) - return -EAGAIN; - - assert(s->state == SERVICE_DEAD || s->state == SERVICE_FAILED); - - /* Make sure we don't enter a busy loop of some kind. */ - r = service_start_limit_test(s); - if (r < 0) { - service_enter_dead(s, SERVICE_FAILURE_START_LIMIT, false); - return r; - } - - s->result = SERVICE_SUCCESS; - s->reload_result = SERVICE_SUCCESS; - s->main_pid_known = false; - s->main_pid_alien = false; - s->forbid_restart = false; - - service_enter_start_pre(s); - return 0; -} - -static int service_stop(Unit *u) { - Service *s = SERVICE(u); - - assert(s); - - /* Don't create restart jobs from here. */ - s->forbid_restart = true; - - /* Already on it */ - if (s->state == SERVICE_STOP || - s->state == SERVICE_STOP_SIGTERM || - s->state == SERVICE_STOP_SIGKILL || - s->state == SERVICE_STOP_POST || - s->state == SERVICE_FINAL_SIGTERM || - s->state == SERVICE_FINAL_SIGKILL) - return 0; - - /* A restart will be scheduled or is in progress. */ - if (s->state == SERVICE_AUTO_RESTART) { - service_set_state(s, SERVICE_DEAD); - return 0; - } - - /* If there's already something running we go directly into - * kill mode. */ - if (s->state == SERVICE_START_PRE || - s->state == SERVICE_START || - s->state == SERVICE_START_POST || - s->state == SERVICE_RELOAD) { - service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_SUCCESS); - return 0; - } - - assert(s->state == SERVICE_RUNNING || - s->state == SERVICE_EXITED); - - service_enter_stop(s, SERVICE_SUCCESS); - return 0; -} - -static int service_reload(Unit *u) { - Service *s = SERVICE(u); - - assert(s); - - assert(s->state == SERVICE_RUNNING || s->state == SERVICE_EXITED); - - service_enter_reload(s); - return 0; -} - -static bool service_can_reload(Unit *u) { - Service *s = SERVICE(u); - - assert(s); - - return !!s->exec_command[SERVICE_EXEC_RELOAD]; -} - -static int service_serialize(Unit *u, FILE *f, FDSet *fds) { - Service *s = SERVICE(u); - - assert(u); - assert(f); - assert(fds); - - unit_serialize_item(u, f, "state", service_state_to_string(s->state)); - unit_serialize_item(u, f, "result", service_result_to_string(s->result)); - unit_serialize_item(u, f, "reload-result", service_result_to_string(s->reload_result)); - - if (s->control_pid > 0) - unit_serialize_item_format(u, f, "control-pid", "%lu", (unsigned long) s->control_pid); - - if (s->main_pid_known && s->main_pid > 0) - unit_serialize_item_format(u, f, "main-pid", "%lu", (unsigned long) s->main_pid); - - unit_serialize_item(u, f, "main-pid-known", yes_no(s->main_pid_known)); - - if (s->status_text) - unit_serialize_item(u, f, "status-text", s->status_text); - - /* FIXME: There's a minor uncleanliness here: if there are - * multiple commands attached here, we will start from the - * first one again */ - if (s->control_command_id >= 0) - unit_serialize_item(u, f, "control-command", service_exec_command_to_string(s->control_command_id)); - - if (s->socket_fd >= 0) { - int copy; - - if ((copy = fdset_put_dup(fds, s->socket_fd)) < 0) - return copy; - - unit_serialize_item_format(u, f, "socket-fd", "%i", copy); - } - - if (s->main_exec_status.pid > 0) { - unit_serialize_item_format(u, f, "main-exec-status-pid", "%lu", (unsigned long) s->main_exec_status.pid); - dual_timestamp_serialize(f, "main-exec-status-start", &s->main_exec_status.start_timestamp); - dual_timestamp_serialize(f, "main-exec-status-exit", &s->main_exec_status.exit_timestamp); - - if (dual_timestamp_is_set(&s->main_exec_status.exit_timestamp)) { - unit_serialize_item_format(u, f, "main-exec-status-code", "%i", s->main_exec_status.code); - unit_serialize_item_format(u, f, "main-exec-status-status", "%i", s->main_exec_status.status); - } - } - if (dual_timestamp_is_set(&s->watchdog_timestamp)) - dual_timestamp_serialize(f, "watchdog-timestamp", &s->watchdog_timestamp); - - return 0; -} - -static int service_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) { - Service *s = SERVICE(u); - - assert(u); - assert(key); - assert(value); - assert(fds); - - if (streq(key, "state")) { - ServiceState state; - - if ((state = service_state_from_string(value)) < 0) - log_debug("Failed to parse state value %s", value); - else - s->deserialized_state = state; - } else if (streq(key, "result")) { - ServiceResult f; - - f = service_result_from_string(value); - if (f < 0) - log_debug("Failed to parse result value %s", value); - else if (f != SERVICE_SUCCESS) - s->result = f; - - } else if (streq(key, "reload-result")) { - ServiceResult f; - - f = service_result_from_string(value); - if (f < 0) - log_debug("Failed to parse reload result value %s", value); - else if (f != SERVICE_SUCCESS) - s->reload_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); - 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); - 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); - else - s->main_pid_known = b; - } else if (streq(key, "status-text")) { - char *t; - - if ((t = strdup(value))) { - free(s->status_text); - s->status_text = t; - } - - } else if (streq(key, "control-command")) { - ServiceExecCommand id; - - if ((id = service_exec_command_from_string(value)) < 0) - log_debug("Failed to parse exec-command value %s", value); - else { - s->control_command_id = id; - s->control_command = s->exec_command[id]; - } - } else if (streq(key, "socket-fd")) { - int fd; - - if (safe_atoi(value, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd)) - log_debug("Failed to parse socket-fd value %s", value); - else { - - if (s->socket_fd >= 0) - close_nointr_nofail(s->socket_fd); - s->socket_fd = fdset_remove(fds, fd); - } - } else if (streq(key, "main-exec-status-pid")) { - pid_t pid; - - if (parse_pid(value, &pid) < 0) - log_debug("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); - 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); - else - s->main_exec_status.status = i; - } else if (streq(key, "main-exec-status-start")) - dual_timestamp_deserialize(value, &s->main_exec_status.start_timestamp); - else if (streq(key, "main-exec-status-exit")) - dual_timestamp_deserialize(value, &s->main_exec_status.exit_timestamp); - else if (streq(key, "watchdog-timestamp")) - dual_timestamp_deserialize(value, &s->watchdog_timestamp); - else - log_debug("Unknown serialization key '%s'", key); - - return 0; -} - -static UnitActiveState service_active_state(Unit *u) { - const UnitActiveState *table; - - assert(u); - - table = SERVICE(u)->type == SERVICE_IDLE ? state_translation_table_idle : state_translation_table; - - return table[SERVICE(u)->state]; -} - -static const char *service_sub_state_to_string(Unit *u) { - assert(u); - - return service_state_to_string(SERVICE(u)->state); -} - -static bool service_check_gc(Unit *u) { - Service *s = SERVICE(u); - - assert(s); - - /* Never clean up services that still have a process around, - * even if the service is formally dead. */ - if (cgroup_good(s) > 0 || - main_pid_good(s) > 0 || - control_pid_good(s) > 0) - return true; - -#ifdef HAVE_SYSV_COMPAT - if (s->is_sysv) - return true; -#endif - - return false; -} - -static bool service_check_snapshot(Unit *u) { - Service *s = SERVICE(u); - - assert(s); - - return !s->got_socket_fd; -} - -static int service_retry_pid_file(Service *s) { - int r; - - assert(s->pid_file); - assert(s->state == SERVICE_START || s->state == SERVICE_START_POST); - - r = service_load_pid_file(s, false); - if (r < 0) - return r; - - service_unwatch_pid_file(s); - - service_enter_running(s, SERVICE_SUCCESS); - return 0; -} - -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); - r = path_spec_watch(s->pid_file_pathspec, UNIT(s)); - if (r < 0) - goto fail; - - /* the pidfile might have appeared just before we set the watch */ - service_retry_pid_file(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)); - service_unwatch_pid_file(s); - return r; -} - -static int service_demand_pid_file(Service *s) { - PathSpec *ps; - - assert(s->pid_file); - assert(!s->pid_file_pathspec); - - ps = new0(PathSpec, 1); - if (!ps) - return -ENOMEM; - - ps->path = strdup(s->pid_file); - if (!ps->path) { - free(ps); - return -ENOMEM; - } - - path_kill_slashes(ps->path); - - /* PATH_CHANGED would not be enough. There are daemons (sendmail) that - * keep their PID file open all the time. */ - ps->type = PATH_MODIFIED; - ps->inotify_fd = -1; - - s->pid_file_pathspec = ps; - - return service_watch_pid_file(s); -} - -static void service_fd_event(Unit *u, int fd, uint32_t events, Watch *w) { - Service *s = SERVICE(u); - - assert(s); - assert(fd >= 0); - assert(s->state == SERVICE_START || s->state == SERVICE_START_POST); - assert(s->pid_file_pathspec); - assert(path_spec_owns_inotify_fd(s->pid_file_pathspec, fd)); - - log_debug("inotify event for %s", u->id); - - if (path_spec_fd_event(s->pid_file_pathspec, events) < 0) - goto fail; - - if (service_retry_pid_file(s) == 0) - return; - - if (service_watch_pid_file(s) < 0) - goto fail; - - return; -fail: - service_unwatch_pid_file(s); - service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_FAILURE_RESOURCES); -} - -static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { - Service *s = SERVICE(u); - ServiceResult f; - - assert(s); - assert(pid >= 0); - - 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; - else if (code == CLD_KILLED) - f = SERVICE_FAILURE_SIGNAL; - else if (code == CLD_DUMPED) - f = SERVICE_FAILURE_CORE_DUMP; - else - assert_not_reached("Unknown code"); - - if (s->main_pid == pid) { - /* Forking services may occasionally move to a new PID. - * As long as they update the PID file before exiting the old - * PID, they're fine. */ - if (service_load_pid_file(s, false) == 0) - return; - - s->main_pid = 0; - exec_status_exit(&s->main_exec_status, &s->exec_context, pid, code, status); - - /* If this is not a forking service than the main - * process got started and hence we copy the exit - * status so that it is recorded both as main and as - * control process exit status */ - if (s->main_command) { - s->main_command->exec_status = s->main_exec_status; - - if (s->main_command->ignore) - f = SERVICE_SUCCESS; - } - - 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; - - if (s->main_command && - s->main_command->command_next && - f == SERVICE_SUCCESS) { - - /* 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)); - service_run_next_main(s); - - } else { - - /* The service exited, so the service is officially - * gone. */ - s->main_command = NULL; - - switch (s->state) { - - case SERVICE_START_POST: - case SERVICE_RELOAD: - case SERVICE_STOP: - /* Need to wait until the operation is - * done */ - break; - - case SERVICE_START: - if (s->type == SERVICE_ONESHOT) { - /* This was our main goal, so let's go on */ - if (f == SERVICE_SUCCESS) - service_enter_start_post(s); - else - service_enter_signal(s, SERVICE_FINAL_SIGTERM, f); - break; - } - - /* Fall through */ - - case SERVICE_RUNNING: - service_enter_running(s, f); - break; - - case SERVICE_STOP_SIGTERM: - case SERVICE_STOP_SIGKILL: - - if (!control_pid_good(s)) - service_enter_stop_post(s, f); - - /* If there is still a control process, wait for that first */ - break; - - default: - assert_not_reached("Uh, main process died at wrong time."); - } - } - - } else if (s->control_pid == pid) { - - s->control_pid = 0; - - if (s->control_command) { - exec_status_exit(&s->control_command->exec_status, &s->exec_context, pid, code, status); - - if (s->control_command->ignore) - 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); - - if (f != SERVICE_SUCCESS) - s->result = f; - - /* Immediately get rid of the cgroup, so that the - * kernel doesn't delay the cgroup empty messages for - * the service cgroup any longer than necessary */ - cgroup_bonding_kill_list(UNIT(s)->cgroup_bondings, SIGKILL, true, true, NULL, "control"); - - if (s->control_command && - s->control_command->command_next && - f == SERVICE_SUCCESS) { - - /* 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)); - service_run_next_control(s); - - } else { - /* No further commands for this step, so let's - * figure out what to do next */ - - 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)); - - switch (s->state) { - - case SERVICE_START_PRE: - if (f == SERVICE_SUCCESS) - service_enter_start(s); - else - service_enter_signal(s, SERVICE_FINAL_SIGTERM, f); - break; - - case SERVICE_START: - 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); - break; - } - - if (s->pid_file) { - bool has_start_post; - int r; - - /* Let's try to load the pid file here if we can. - * The PID file might actually be created by a START_POST - * script. In that case don't worry if the loading fails. */ - - has_start_post = !!s->exec_command[SERVICE_EXEC_START_POST]; - r = service_load_pid_file(s, !has_start_post); - if (!has_start_post && r < 0) { - r = service_demand_pid_file(s); - if (r < 0 || !cgroup_good(s)) - service_enter_signal(s, SERVICE_FINAL_SIGTERM, SERVICE_FAILURE_RESOURCES); - break; - } - } else - service_search_main_pid(s); - - service_enter_start_post(s); - break; - - case SERVICE_START_POST: - if (f != SERVICE_SUCCESS) { - service_enter_stop(s, f); - break; - } - - if (s->pid_file) { - int r; - - r = service_load_pid_file(s, true); - if (r < 0) { - r = service_demand_pid_file(s); - if (r < 0 || !cgroup_good(s)) - service_enter_stop(s, SERVICE_FAILURE_RESOURCES); - break; - } - } else - service_search_main_pid(s); - - service_enter_running(s, SERVICE_SUCCESS); - break; - - case SERVICE_RELOAD: - if (f == SERVICE_SUCCESS) { - service_load_pid_file(s, true); - service_search_main_pid(s); - } - - s->reload_result = f; - service_enter_running(s, SERVICE_SUCCESS); - break; - - case SERVICE_STOP: - service_enter_signal(s, SERVICE_STOP_SIGTERM, f); - break; - - case SERVICE_STOP_SIGTERM: - case SERVICE_STOP_SIGKILL: - if (main_pid_good(s) <= 0) - service_enter_stop_post(s, f); - - /* If there is still a service - * process around, wait until - * that one quit, too */ - break; - - case SERVICE_STOP_POST: - case SERVICE_FINAL_SIGTERM: - case SERVICE_FINAL_SIGKILL: - service_enter_dead(s, f, true); - break; - - default: - assert_not_reached("Uh, control process died at wrong time."); - } - } - } - - /* Notify clients about changed exit status */ - unit_add_to_dbus_queue(u); -} - -static void service_timer_event(Unit *u, uint64_t elapsed, Watch* w) { - Service *s = SERVICE(u); - - assert(s); - assert(elapsed == 1); - - if (w == &s->watchdog_watch) { - service_handle_watchdog(s); - return; - } - - assert(w == &s->timer_watch); - - switch (s->state) { - - case SERVICE_START_PRE: - case SERVICE_START: - log_warning("%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); - service_enter_stop(s, SERVICE_FAILURE_TIMEOUT); - break; - - case SERVICE_RELOAD: - log_warning("%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); - 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); - service_enter_signal(s, SERVICE_STOP_SIGKILL, SERVICE_FAILURE_TIMEOUT); - } else { - log_warning("%s stopping timed out. Skipping SIGKILL.", u->id); - service_enter_stop_post(s, SERVICE_FAILURE_TIMEOUT); - } - - break; - - case SERVICE_STOP_SIGKILL: - /* Uh, we sent a SIGKILL and it is still not gone? - * 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); - service_enter_stop_post(s, SERVICE_FAILURE_TIMEOUT); - break; - - case SERVICE_STOP_POST: - log_warning("%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); - 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); - 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); - service_enter_dead(s, SERVICE_FAILURE_TIMEOUT, true); - break; - - case SERVICE_AUTO_RESTART: - log_info("%s holdoff time over, scheduling restart.", u->id); - service_enter_restart(s); - break; - - default: - assert_not_reached("Timeout at wrong time."); - } -} - -static void service_cgroup_notify_event(Unit *u) { - Service *s = SERVICE(u); - - assert(u); - - log_debug("%s: cgroup is empty", u->id); - - switch (s->state) { - - /* Waiting for SIGCHLD is usually more interesting, - * because it includes return codes/signals. Which is - * why we ignore the cgroup events for most cases, - * except when we don't know pid which to expect the - * SIGCHLD for. */ - - case SERVICE_START: - case SERVICE_START_POST: - /* 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); - service_unwatch_pid_file(s); - if (s->state == SERVICE_START) - service_enter_signal(s, SERVICE_FINAL_SIGTERM, SERVICE_FAILURE_RESOURCES); - else - service_enter_stop(s, SERVICE_FAILURE_RESOURCES); - } - break; - - case SERVICE_RUNNING: - /* service_enter_running() will figure out what to do */ - service_enter_running(s, SERVICE_SUCCESS); - break; - - case SERVICE_STOP_SIGTERM: - case SERVICE_STOP_SIGKILL: - - if (main_pid_good(s) <= 0 && !control_pid_good(s)) - service_enter_stop_post(s, SERVICE_SUCCESS); - - break; - - case SERVICE_FINAL_SIGTERM: - case SERVICE_FINAL_SIGKILL: - if (main_pid_good(s) <= 0 && !control_pid_good(s)) - service_enter_dead(s, SERVICE_SUCCESS, true); - - break; - - default: - ; - } -} - -static void service_notify_message(Unit *u, pid_t pid, char **tags) { - Service *s = SERVICE(u); - const char *e; - - 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); - 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); - return; - } - - log_debug("%s: Got message", u->id); - - /* Interpret MAINPID= */ - if ((e = strv_find_prefix(tags, "MAINPID=")) && - (s->state == SERVICE_START || - s->state == SERVICE_START_POST || - s->state == SERVICE_RUNNING || - s->state == SERVICE_RELOAD)) { - - if (parse_pid(e + 8, &pid) < 0) - log_warning("Failed to parse notification message %s", e); - else { - log_debug("%s: got %s", u->id, e); - service_set_main_pid(s, pid); - } - } - - /* Interpret READY= */ - if (s->type == SERVICE_NOTIFY && - s->state == SERVICE_START && - strv_find(tags, "READY=1")) { - log_debug("%s: got READY=1", u->id); - - service_enter_start_post(s); - } - - /* Interpret STATUS= */ - e = strv_find_prefix(tags, "STATUS="); - if (e) { - char *t; - - if (e[7]) { - - if (!utf8_is_valid(e+7)) { - log_warning("Status message in notification is not UTF-8 clean."); - return; - } - - t = strdup(e+7); - if (!t) { - log_error("Failed to allocate string."); - return; - } - - log_debug("%s: got %s", u->id, e); - - free(s->status_text); - s->status_text = t; - } else { - free(s->status_text); - s->status_text = NULL; - } - - } - if (strv_find(tags, "WATCHDOG=1")) { - log_debug("%s: got WATCHDOG=1", u->id); - service_reset_watchdog(s); - } - - /* Notify clients about changed status or main pid */ - unit_add_to_dbus_queue(u); -} - -#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; - DIR *d = NULL; - char *path = NULL, *fpath = NULL, *name = NULL; - Set *runlevel_services[ELEMENTSOF(rcnd_table)], *shutdown_services = NULL; - Unit *service; - Iterator j; - int r; - - assert(m); - - if (m->running_as != SYSTEMD_SYSTEM) - return 0; - - zero(runlevel_services); - - STRV_FOREACH(p, m->lookup_paths.sysvrcnd_path) - for (i = 0; i < ELEMENTSOF(rcnd_table); i ++) { - struct dirent *de; - - free(path); - path = strjoin(*p, "/", rcnd_table[i].path, NULL); - if (!path) { - r = -ENOMEM; - goto finish; - } - - if (d) - closedir(d); - - if (!(d = opendir(path))) { - if (errno != ENOENT) - log_warning("opendir() failed on %s: %s", path, strerror(errno)); - - continue; - } - - while ((de = readdir(d))) { - int a, b; - - if (ignore_file(de->d_name)) - continue; - - if (de->d_name[0] != 'S' && de->d_name[0] != 'K') - continue; - - if (strlen(de->d_name) < 4) - continue; - - a = undecchar(de->d_name[1]); - b = undecchar(de->d_name[2]); - - if (a < 0 || b < 0) - continue; - - free(fpath); - fpath = strjoin(path, "/", de->d_name, NULL); - if (!fpath) { - r = -ENOMEM; - goto finish; - } - - if (access(fpath, X_OK) < 0) { - - if (errno != ENOENT) - log_warning("access() failed on %s: %s", fpath, strerror(errno)); - - continue; - } - - free(name); - if (!(name = sysv_translate_name(de->d_name + 3))) { - r = -ENOMEM; - goto finish; - } - - if ((r = manager_load_unit_prepare(m, name, NULL, NULL, &service)) < 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) { - SERVICE(service)->sysv_start_priority_from_rcnd = - MAX(a*10 + b, SERVICE(service)->sysv_start_priority_from_rcnd); - - SERVICE(service)->sysv_enabled = true; - } - - if ((r = set_ensure_allocated(&runlevel_services[i], trivial_hash_func, trivial_compare_func)) < 0) - goto finish; - - if ((r = set_put(runlevel_services[i], service)) < 0) - goto finish; - - } else if (de->d_name[0] == 'K' && - (rcnd_table[i].type == RUNLEVEL_DOWN || - rcnd_table[i].type == RUNLEVEL_SYSINIT)) { - - if ((r = set_ensure_allocated(&shutdown_services, trivial_hash_func, trivial_compare_func)) < 0) - goto finish; - - if ((r = set_put(shutdown_services, service)) < 0) - goto finish; - } - } - } - - /* Now we loaded all stubs and are aware of the lowest - start-up priority for all services, not let's actually load - the services, this will also tell us which services are - actually native now */ - manager_dispatch_load_queue(m); - - /* If this is a native service, rely on native ways to pull in - * a service, don't pull it in via sysv rcN.d links. */ - for (i = 0; i < ELEMENTSOF(rcnd_table); i ++) - SET_FOREACH(service, runlevel_services[i], j) { - service = unit_follow_merge(service); - - if (service->fragment_path) - continue; - - if ((r = unit_add_two_dependencies_by_name_inverse(service, UNIT_AFTER, UNIT_WANTS, rcnd_table[i].target, NULL, true)) < 0) - goto finish; - } - - /* We honour K links only for halt/reboot. For the normal - * 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.*/ - SET_FOREACH(service, shutdown_services, j) { - service = unit_follow_merge(service); - - if (service->fragment_path) - continue; - - if ((r = unit_add_two_dependencies_by_name(service, UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, NULL, true)) < 0) - goto finish; - } - - r = 0; - -#ifdef TARGET_SUSE - sysv_facility_in_insserv_conf (m); -#endif - -finish: - free(path); - free(fpath); - free(name); - - for (i = 0; i < ELEMENTSOF(rcnd_table); i++) - set_free(runlevel_services[i]); - set_free(shutdown_services); - - if (d) - closedir(d); - - return r; -} -#endif - -static void service_bus_name_owner_change( - Unit *u, - const char *name, - const char *old_owner, - const char *new_owner) { - - Service *s = SERVICE(u); - - assert(s); - assert(name); - - assert(streq(s->bus_name, name)); - 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); - else if (old_owner) - log_debug("%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); - - s->bus_name_good = !!new_owner; - - if (s->type == SERVICE_DBUS) { - - /* service_enter_running() will figure out what to - * do */ - if (s->state == SERVICE_RUNNING) - service_enter_running(s, SERVICE_SUCCESS); - else if (s->state == SERVICE_START && new_owner) - service_enter_start_post(s); - - } else if (new_owner && - s->main_pid <= 0 && - (s->state == SERVICE_START || - s->state == SERVICE_START_POST || - s->state == SERVICE_RUNNING || - s->state == SERVICE_RELOAD)) { - - /* Try to acquire PID from bus service */ - log_debug("Trying to acquire PID from D-Bus name..."); - - bus_query_pid(u->manager, name); - } -} - -static void service_bus_query_pid_done( - Unit *u, - const char *name, - pid_t pid) { - - Service *s = SERVICE(u); - - assert(s); - assert(name); - - log_debug("%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 || - s->state == SERVICE_START_POST || - s->state == SERVICE_RUNNING || - s->state == SERVICE_RELOAD)) - service_set_main_pid(s, pid); -} - -int service_set_socket_fd(Service *s, int fd, Socket *sock) { - - assert(s); - assert(fd >= 0); - - /* This is called by the socket code when instantiating a new - * service for a stream socket and the socket needs to be - * configured. */ - - if (UNIT(s)->load_state != UNIT_LOADED) - return -EINVAL; - - if (s->socket_fd >= 0) - return -EBUSY; - - if (s->state != SERVICE_DEAD) - return -EAGAIN; - - s->socket_fd = fd; - s->got_socket_fd = true; - - unit_ref_set(&s->accept_socket, UNIT(sock)); - - return unit_add_two_dependencies(UNIT(sock), UNIT_BEFORE, UNIT_TRIGGERS, UNIT(s), false); -} - -static void service_reset_failed(Unit *u) { - Service *s = SERVICE(u); - - assert(s); - - if (s->state == SERVICE_FAILED) - service_set_state(s, SERVICE_DEAD); - - s->result = SERVICE_SUCCESS; - s->reload_result = SERVICE_SUCCESS; - - RATELIMIT_RESET(s->start_limit); -} - -static int service_kill(Unit *u, KillWho who, int signo, DBusError *error) { - Service *s = SERVICE(u); - int r = 0; - Set *pid_set = NULL; - - assert(s); - - if (s->main_pid <= 0 && who == KILL_MAIN) { - dbus_set_error(error, BUS_ERROR_NO_SUCH_PROCESS, "No main process to kill"); - return -ESRCH; - } - - if (s->control_pid <= 0 && who == KILL_CONTROL) { - dbus_set_error(error, BUS_ERROR_NO_SUCH_PROCESS, "No control process to kill"); - return -ESRCH; - } - - if (who == KILL_CONTROL || who == KILL_ALL) - if (s->control_pid > 0) - if (kill(s->control_pid, signo) < 0) - r = -errno; - - if (who == KILL_MAIN || who == KILL_ALL) - if (s->main_pid > 0) - if (kill(s->main_pid, signo) < 0) - r = -errno; - - if (who == KILL_ALL) { - int q; - - pid_set = set_new(trivial_hash_func, trivial_compare_func); - if (!pid_set) - return -ENOMEM; - - /* Exclude the control/main pid from being killed via the cgroup */ - if (s->control_pid > 0) { - q = set_put(pid_set, LONG_TO_PTR(s->control_pid)); - if (q < 0) { - r = q; - goto finish; - } - } - - if (s->main_pid > 0) { - q = set_put(pid_set, LONG_TO_PTR(s->main_pid)); - if (q < 0) { - r = q; - goto finish; - } - } - - q = cgroup_bonding_kill_list(UNIT(s)->cgroup_bondings, signo, false, false, pid_set, NULL); - if (q < 0 && q != -EAGAIN && q != -ESRCH && q != -ENOENT) - r = q; - } - -finish: - if (pid_set) - set_free(pid_set); - - return r; -} - -static const char* const service_state_table[_SERVICE_STATE_MAX] = { - [SERVICE_DEAD] = "dead", - [SERVICE_START_PRE] = "start-pre", - [SERVICE_START] = "start", - [SERVICE_START_POST] = "start-post", - [SERVICE_RUNNING] = "running", - [SERVICE_EXITED] = "exited", - [SERVICE_RELOAD] = "reload", - [SERVICE_STOP] = "stop", - [SERVICE_STOP_SIGTERM] = "stop-sigterm", - [SERVICE_STOP_SIGKILL] = "stop-sigkill", - [SERVICE_STOP_POST] = "stop-post", - [SERVICE_FINAL_SIGTERM] = "final-sigterm", - [SERVICE_FINAL_SIGKILL] = "final-sigkill", - [SERVICE_FAILED] = "failed", - [SERVICE_AUTO_RESTART] = "auto-restart", -}; - -DEFINE_STRING_TABLE_LOOKUP(service_state, ServiceState); - -static const char* const service_restart_table[_SERVICE_RESTART_MAX] = { - [SERVICE_RESTART_NO] = "no", - [SERVICE_RESTART_ON_SUCCESS] = "on-success", - [SERVICE_RESTART_ON_FAILURE] = "on-failure", - [SERVICE_RESTART_ON_ABORT] = "on-abort", - [SERVICE_RESTART_ALWAYS] = "always" -}; - -DEFINE_STRING_TABLE_LOOKUP(service_restart, ServiceRestart); - -static const char* const service_type_table[_SERVICE_TYPE_MAX] = { - [SERVICE_SIMPLE] = "simple", - [SERVICE_FORKING] = "forking", - [SERVICE_ONESHOT] = "oneshot", - [SERVICE_DBUS] = "dbus", - [SERVICE_NOTIFY] = "notify", - [SERVICE_IDLE] = "idle" -}; - -DEFINE_STRING_TABLE_LOOKUP(service_type, ServiceType); - -static const char* const service_exec_command_table[_SERVICE_EXEC_COMMAND_MAX] = { - [SERVICE_EXEC_START_PRE] = "ExecStartPre", - [SERVICE_EXEC_START] = "ExecStart", - [SERVICE_EXEC_START_POST] = "ExecStartPost", - [SERVICE_EXEC_RELOAD] = "ExecReload", - [SERVICE_EXEC_STOP] = "ExecStop", - [SERVICE_EXEC_STOP_POST] = "ExecStopPost", -}; - -DEFINE_STRING_TABLE_LOOKUP(service_exec_command, ServiceExecCommand); - -static const char* const notify_access_table[_NOTIFY_ACCESS_MAX] = { - [NOTIFY_NONE] = "none", - [NOTIFY_MAIN] = "main", - [NOTIFY_ALL] = "all" -}; - -DEFINE_STRING_TABLE_LOOKUP(notify_access, NotifyAccess); - -static const char* const service_result_table[_SERVICE_RESULT_MAX] = { - [SERVICE_SUCCESS] = "success", - [SERVICE_FAILURE_RESOURCES] = "resources", - [SERVICE_FAILURE_TIMEOUT] = "timeout", - [SERVICE_FAILURE_EXIT_CODE] = "exit-code", - [SERVICE_FAILURE_SIGNAL] = "signal", - [SERVICE_FAILURE_CORE_DUMP] = "core-dump", - [SERVICE_FAILURE_WATCHDOG] = "watchdog", - [SERVICE_FAILURE_START_LIMIT] = "start-limit" -}; - -DEFINE_STRING_TABLE_LOOKUP(service_result, ServiceResult); - -static const char* const start_limit_action_table[_SERVICE_START_LIMIT_MAX] = { - [SERVICE_START_LIMIT_NONE] = "none", - [SERVICE_START_LIMIT_REBOOT] = "reboot", - [SERVICE_START_LIMIT_REBOOT_FORCE] = "reboot-force", - [SERVICE_START_LIMIT_REBOOT_IMMEDIATE] = "reboot-immediate" -}; -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" - "Install\0", - - .init = service_init, - .done = service_done, - .load = service_load, - - .coldplug = service_coldplug, - - .dump = service_dump, - - .start = service_start, - .stop = service_stop, - .reload = service_reload, - - .can_reload = service_can_reload, - - .kill = service_kill, - - .serialize = service_serialize, - .deserialize_item = service_deserialize_item, - - .active_state = service_active_state, - .sub_state_to_string = service_sub_state_to_string, - - .check_gc = service_check_gc, - .check_snapshot = service_check_snapshot, - - .sigchld_event = service_sigchld_event, - .timer_event = service_timer_event, - .fd_event = service_fd_event, - - .reset_failed = service_reset_failed, - - .cgroup_notify_empty = service_cgroup_notify_event, - .notify_message = service_notify_message, - - .bus_name_owner_change = service_bus_name_owner_change, - .bus_query_pid_done = service_bus_query_pid_done, - - .bus_interface = "org.freedesktop.systemd1.Service", - .bus_message_handler = bus_service_message_handler, - .bus_invalidating_properties = bus_service_invalidating_properties, - -#ifdef HAVE_SYSV_COMPAT - .enumerate = service_enumerate, -#endif - .status_message_formats = { - .starting_stopping = { - [0] = "Starting %s...", - [1] = "Stopping %s...", - }, - .finished_start_job = { - [JOB_DONE] = "Started %s.", - [JOB_FAILED] = "Failed to start %s.", - [JOB_DEPENDENCY] = "Dependency failed for %s.", - [JOB_TIMEOUT] = "Timed out starting %s.", - }, - .finished_stop_job = { - [JOB_DONE] = "Stopped %s.", - [JOB_FAILED] = "Stopped (with error) %s.", - [JOB_TIMEOUT] = "Timed out stopping %s.", - }, - }, -}; diff --git a/src/core/service.h b/src/core/service.h deleted file mode 100644 index d1e53bf727..0000000000 --- a/src/core/service.h +++ /dev/null @@ -1,225 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -#pragma once - -/*** - 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/>. -***/ - -typedef struct Service Service; - -#include "unit.h" -#include "path.h" -#include "ratelimit.h" -#include "service.h" -#include "kill.h" -#include "exit-status.h" - -typedef enum ServiceState { - SERVICE_DEAD, - SERVICE_START_PRE, - SERVICE_START, - SERVICE_START_POST, - SERVICE_RUNNING, - SERVICE_EXITED, /* Nothing is running anymore, but RemainAfterExit is true hence this is OK */ - SERVICE_RELOAD, - SERVICE_STOP, /* No STOP_PRE state, instead just register multiple STOP executables */ - SERVICE_STOP_SIGTERM, - SERVICE_STOP_SIGKILL, - SERVICE_STOP_POST, - SERVICE_FINAL_SIGTERM, /* In case the STOP_POST executable hangs, we shoot that down, too */ - SERVICE_FINAL_SIGKILL, - SERVICE_FAILED, - SERVICE_AUTO_RESTART, - _SERVICE_STATE_MAX, - _SERVICE_STATE_INVALID = -1 -} ServiceState; - -typedef enum ServiceRestart { - SERVICE_RESTART_NO, - SERVICE_RESTART_ON_SUCCESS, - SERVICE_RESTART_ON_FAILURE, - SERVICE_RESTART_ON_ABORT, - SERVICE_RESTART_ALWAYS, - _SERVICE_RESTART_MAX, - _SERVICE_RESTART_INVALID = -1 -} ServiceRestart; - -typedef enum ServiceType { - SERVICE_SIMPLE, /* we fork and go on right-away (i.e. modern socket activated daemons) */ - SERVICE_FORKING, /* forks by itself (i.e. traditional daemons) */ - SERVICE_ONESHOT, /* we fork and wait until the program finishes (i.e. programs like fsck which run and need to finish before we continue) */ - SERVICE_DBUS, /* we fork and wait until a specific D-Bus name appears on the bus */ - SERVICE_NOTIFY, /* we fork and wait until a daemon sends us a ready message with sd_notify() */ - SERVICE_IDLE, /* much like simple, but delay exec() until all jobs are dispatched. */ - _SERVICE_TYPE_MAX, - _SERVICE_TYPE_INVALID = -1 -} ServiceType; - -typedef enum ServiceExecCommand { - SERVICE_EXEC_START_PRE, - SERVICE_EXEC_START, - SERVICE_EXEC_START_POST, - SERVICE_EXEC_RELOAD, - SERVICE_EXEC_STOP, - SERVICE_EXEC_STOP_POST, - _SERVICE_EXEC_COMMAND_MAX, - _SERVICE_EXEC_COMMAND_INVALID = -1 -} ServiceExecCommand; - -typedef enum NotifyAccess { - NOTIFY_NONE, - NOTIFY_ALL, - NOTIFY_MAIN, - _NOTIFY_ACCESS_MAX, - _NOTIFY_ACCESS_INVALID = -1 -} NotifyAccess; - -typedef enum ServiceResult { - SERVICE_SUCCESS, - SERVICE_FAILURE_RESOURCES, - SERVICE_FAILURE_TIMEOUT, - SERVICE_FAILURE_EXIT_CODE, - SERVICE_FAILURE_SIGNAL, - SERVICE_FAILURE_CORE_DUMP, - SERVICE_FAILURE_WATCHDOG, - SERVICE_FAILURE_START_LIMIT, - _SERVICE_RESULT_MAX, - _SERVICE_RESULT_INVALID = -1 -} ServiceResult; - -typedef enum StartLimitAction { - SERVICE_START_LIMIT_NONE, - SERVICE_START_LIMIT_REBOOT, - SERVICE_START_LIMIT_REBOOT_FORCE, - SERVICE_START_LIMIT_REBOOT_IMMEDIATE, - _SERVICE_START_LIMIT_MAX, - _SERVICE_START_LIMIT_INVALID = -1 -} StartLimitAction; - -struct Service { - Unit meta; - - 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_start_usec; - usec_t timeout_stop_usec; - - dual_timestamp watchdog_timestamp; - usec_t watchdog_usec; - Watch watchdog_watch; - - ExecCommand* exec_command[_SERVICE_EXEC_COMMAND_MAX]; - - ExecContext exec_context; - KillContext kill_context; - - ServiceState state, deserialized_state; - - /* The exit status of the real main process */ - ExecStatus main_exec_status; - - /* The currently executed control process */ - ExecCommand *control_command; - - /* The currently executed main process, which may be NULL if - * the main process got started via forking mode and not by - * us */ - ExecCommand *main_command; - - /* The ID of the control command currently being executed */ - ServiceExecCommand control_command_id; - - pid_t main_pid, control_pid; - int socket_fd; - - int fsck_passno; - - bool permissions_start_only; - bool root_directory_start_only; - bool remain_after_exit; - bool guess_main_pid; - - /* If we shut down, remember why */ - ServiceResult result; - ServiceResult reload_result; - - bool main_pid_known:1; - bool main_pid_alien:1; - bool bus_name_good:1; - bool forbid_restart:1; - bool got_socket_fd:1; - bool start_timeout_defined:1; -#ifdef HAVE_SYSV_COMPAT - bool is_sysv:1; - bool sysv_has_lsb:1; - bool sysv_enabled:1; - int sysv_start_priority_from_rcnd; - int sysv_start_priority; - - char *sysv_runlevels; -#endif - - char *bus_name; - - char *status_text; - - RateLimit start_limit; - StartLimitAction start_limit_action; - - UnitRef accept_socket; - - Watch timer_watch; - PathSpec *pid_file_pathspec; - - NotifyAccess notify_access; -}; - -extern const UnitVTable service_vtable; - -struct Socket; - -int service_set_socket_fd(Service *s, int fd, struct Socket *socket); - -const char* service_state_to_string(ServiceState i); -ServiceState service_state_from_string(const char *s); - -const char* service_restart_to_string(ServiceRestart i); -ServiceRestart service_restart_from_string(const char *s); - -const char* service_type_to_string(ServiceType i); -ServiceType service_type_from_string(const char *s); - -const char* service_exec_command_to_string(ServiceExecCommand i); -ServiceExecCommand service_exec_command_from_string(const char *s); - -const char* notify_access_to_string(NotifyAccess i); -NotifyAccess notify_access_from_string(const char *s); - -const char* service_result_to_string(ServiceResult i); -ServiceResult service_result_from_string(const char *s); - -const char* start_limit_action_to_string(StartLimitAction i); -StartLimitAction start_limit_action_from_string(const char *s); diff --git a/src/core/shutdown.c b/src/core/shutdown.c deleted file mode 100644 index cc8c57bd2d..0000000000 --- a/src/core/shutdown.c +++ /dev/null @@ -1,316 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -/*** - This file is part of systemd. - - Copyright 2010 ProFUSION embedded systems - - 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 <sys/mman.h> -#include <sys/types.h> -#include <sys/reboot.h> -#include <linux/reboot.h> -#include <sys/wait.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <sys/mount.h> -#include <sys/syscall.h> -#include <fcntl.h> -#include <dirent.h> -#include <errno.h> -#include <unistd.h> -#include <signal.h> -#include <stdbool.h> -#include <stdlib.h> -#include <string.h> - -#include "missing.h" -#include "log.h" -#include "umount.h" -#include "util.h" -#include "mkdir.h" -#include "virt.h" -#include "watchdog.h" -#include "killall.h" - -#define FINALIZE_ATTEMPTS 50 - -static int prepare_new_root(void) { - static const char dirs[] = - "/run/initramfs/oldroot\0" - "/run/initramfs/proc\0" - "/run/initramfs/sys\0" - "/run/initramfs/dev\0" - "/run/initramfs/run\0"; - - const char *dir; - - if (mount("/run/initramfs", "/run/initramfs", NULL, MS_BIND, NULL) < 0) { - log_error("Failed to mount bind /run/initramfs on /run/initramfs: %m"); - return -errno; - } - - if (mount(NULL, "/run/initramfs", NULL, MS_PRIVATE, NULL) < 0) { - log_error("Failed to make /run/initramfs private mount: %m"); - return -errno; - } - - NULSTR_FOREACH(dir, dirs) - if (mkdir_p_label(dir, 0755) < 0 && errno != EEXIST) { - log_error("Failed to mkdir %s: %m", dir); - return -errno; - } - - if (mount("/sys", "/run/initramfs/sys", NULL, MS_BIND, NULL) < 0) { - log_error("Failed to mount bind /sys on /run/initramfs/sys: %m"); - return -errno; - } - - if (mount("/proc", "/run/initramfs/proc", NULL, MS_BIND, NULL) < 0) { - log_error("Failed to mount bind /proc on /run/initramfs/proc: %m"); - return -errno; - } - - if (mount("/dev", "/run/initramfs/dev", NULL, MS_BIND, NULL) < 0) { - log_error("Failed to mount bind /dev on /run/initramfs/dev: %m"); - return -errno; - } - - if (mount("/run", "/run/initramfs/run", NULL, MS_BIND, NULL) < 0) { - log_error("Failed to mount bind /run on /run/initramfs/run: %m"); - return -errno; - } - - return 0; -} - -static int pivot_to_new_root(void) { - - if (chdir("/run/initramfs") < 0) { - log_error("Failed to change directory to /run/initramfs: %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"); - /* only chroot if pivot root succeeded */ - return -errno; - } - - chroot("."); - - setsid(); - make_console_stdio(); - - log_info("Successfully changed into root pivot."); - - return 0; -} - -int main(int argc, char *argv[]) { - int cmd, r; - unsigned retries; - bool need_umount = true, need_swapoff = true, need_loop_detach = true, need_dm_detach = true; - bool in_container, use_watchdog = false; - char *arguments[3]; - - log_parse_environment(); - log_set_target(LOG_TARGET_CONSOLE); /* syslog will die if not gone yet */ - log_open(); - - umask(0022); - - if (getpid() != 1) { - log_error("Not executed by init (pid 1)."); - r = -EPERM; - goto error; - } - - if (argc != 2) { - log_error("Invalid number of arguments."); - r = -EINVAL; - goto error; - } - - in_container = detect_container(NULL) > 0; - - if (streq(argv[1], "reboot")) - cmd = RB_AUTOBOOT; - else if (streq(argv[1], "poweroff")) - cmd = RB_POWER_OFF; - else if (streq(argv[1], "halt")) - cmd = RB_HALT_SYSTEM; - else if (streq(argv[1], "kexec")) - cmd = LINUX_REBOOT_CMD_KEXEC; - else { - log_error("Unknown action '%s'.", argv[1]); - r = -EINVAL; - goto error; - } - - use_watchdog = !!getenv("WATCHDOG_USEC"); - - /* lock us into memory */ - mlockall(MCL_CURRENT|MCL_FUTURE); - - log_info("Sending SIGTERM to remaining processes..."); - broadcast_signal(SIGTERM, true); - - log_info("Sending SIGKILL to remaining processes..."); - broadcast_signal(SIGKILL, true); - - if (in_container) { - need_swapoff = false; - need_dm_detach = false; - need_loop_detach = false; - } - - /* Unmount all mountpoints, swaps, and loopback devices */ - for (retries = 0; retries < FINALIZE_ATTEMPTS; retries++) { - bool changed = false; - - if (use_watchdog) - watchdog_ping(); - - if (need_umount) { - log_info("Unmounting file systems."); - r = umount_all(&changed); - if (r == 0) - need_umount = false; - 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."); - r = swapoff_all(&changed); - if (r == 0) - need_swapoff = false; - else if (r > 0) - log_info("Not all swaps are turned off, %d left.", r); - else - log_error("Failed to turn off swaps: %s", strerror(-r)); - } - - if (need_loop_detach) { - log_info("Detaching loop devices."); - r = loopback_detach_all(&changed); - if (r == 0) - need_loop_detach = false; - 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)); - } - - if (need_dm_detach) { - log_info("Detaching DM devices."); - r = dm_detach_all(&changed); - if (r == 0) - need_dm_detach = false; - else if (r > 0) - log_warning("Not all DM devices detached, %d left.", r); - else - log_error("Failed to detach DM devices: %s", strerror(-r)); - } - - if (!need_umount && !need_swapoff && !need_loop_detach && !need_dm_detach) { - if (retries > 0) - log_info("All filesystems, swaps, loop devices, DM devices detached."); - /* Yay, done */ - break; - } - - /* If in this iteration we didn't manage to - * unmount/deactivate anything, we simply give up */ - if (!changed) { - log_error("Cannot finalize remaining file systems and devices, giving up."); - break; - } - - log_debug("Couldn't finalize remaining file systems and devices after %u retries, trying again.", retries+1); - } - - if (retries >= FINALIZE_ATTEMPTS) - log_error("Too many iterations, giving up."); - - arguments[0] = NULL; - arguments[1] = argv[1]; - arguments[2] = NULL; - execute_directory(SYSTEM_SHUTDOWN_PATH, NULL, arguments); - - if (!in_container && - access("/run/initramfs/shutdown", X_OK) == 0) { - - if (prepare_new_root() >= 0 && - pivot_to_new_root() >= 0) { - execv("/shutdown", argv); - log_error("Failed to execute shutdown binary: %m"); - } - } - - if (cmd == LINUX_REBOOT_CMD_KEXEC) { - - 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; - - error: - log_error("Critical error while doing system shutdown: %s", strerror(-r)); - - freeze(); - return EXIT_FAILURE; -} diff --git a/src/core/snapshot.c b/src/core/snapshot.c deleted file mode 100644 index 5c2a319cb6..0000000000 --- a/src/core/snapshot.c +++ /dev/null @@ -1,308 +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 "unit.h" -#include "snapshot.h" -#include "unit-name.h" -#include "dbus-snapshot.h" -#include "bus-errors.h" - -static const UnitActiveState state_translation_table[_SNAPSHOT_STATE_MAX] = { - [SNAPSHOT_DEAD] = UNIT_INACTIVE, - [SNAPSHOT_ACTIVE] = UNIT_ACTIVE -}; - -static void snapshot_init(Unit *u) { - Snapshot *s = SNAPSHOT(u); - - assert(s); - assert(UNIT(s)->load_state == UNIT_STUB); - - UNIT(s)->ignore_on_isolate = true; - UNIT(s)->ignore_on_snapshot = true; -} - -static void snapshot_set_state(Snapshot *s, SnapshotState state) { - SnapshotState old_state; - assert(s); - - old_state = s->state; - s->state = state; - - if (state != old_state) - log_debug("%s changed %s -> %s", - UNIT(s)->id, - snapshot_state_to_string(old_state), - snapshot_state_to_string(state)); - - unit_notify(UNIT(s), state_translation_table[old_state], state_translation_table[state], true); -} - -static int snapshot_load(Unit *u) { - Snapshot *s = SNAPSHOT(u); - - assert(u); - assert(u->load_state == UNIT_STUB); - - /* Make sure that only snapshots created via snapshot_create() - * can be loaded */ - if (!s->by_snapshot_create && UNIT(s)->manager->n_reloading <= 0) - return -ENOENT; - - u->load_state = UNIT_LOADED; - return 0; -} - -static int snapshot_coldplug(Unit *u) { - Snapshot *s = SNAPSHOT(u); - - assert(s); - assert(s->state == SNAPSHOT_DEAD); - - if (s->deserialized_state != s->state) - snapshot_set_state(s, s->deserialized_state); - - return 0; -} - -static void snapshot_dump(Unit *u, FILE *f, const char *prefix) { - Snapshot *s = SNAPSHOT(u); - - assert(s); - assert(f); - - fprintf(f, - "%sSnapshot State: %s\n" - "%sClean Up: %s\n", - prefix, snapshot_state_to_string(s->state), - prefix, yes_no(s->cleanup)); -} - -static int snapshot_start(Unit *u) { - Snapshot *s = SNAPSHOT(u); - - assert(s); - assert(s->state == SNAPSHOT_DEAD); - - snapshot_set_state(s, SNAPSHOT_ACTIVE); - - if (s->cleanup) - unit_add_to_cleanup_queue(u); - - return 0; -} - -static int snapshot_stop(Unit *u) { - Snapshot *s = SNAPSHOT(u); - - assert(s); - assert(s->state == SNAPSHOT_ACTIVE); - - snapshot_set_state(s, SNAPSHOT_DEAD); - return 0; -} - -static int snapshot_serialize(Unit *u, FILE *f, FDSet *fds) { - Snapshot *s = SNAPSHOT(u); - Unit *other; - Iterator i; - - assert(s); - assert(f); - assert(fds); - - unit_serialize_item(u, f, "state", snapshot_state_to_string(s->state)); - unit_serialize_item(u, f, "cleanup", yes_no(s->cleanup)); - SET_FOREACH(other, u->dependencies[UNIT_WANTS], i) - unit_serialize_item(u, f, "wants", other->id); - - return 0; -} - -static int snapshot_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) { - Snapshot *s = SNAPSHOT(u); - int r; - - assert(u); - assert(key); - assert(value); - assert(fds); - - if (streq(key, "state")) { - SnapshotState state; - - if ((state = snapshot_state_from_string(value)) < 0) - log_debug("Failed to parse state value %s", value); - else - s->deserialized_state = state; - - } else if (streq(key, "cleanup")) { - - if ((r = parse_boolean(value)) < 0) - log_debug("Failed to parse cleanup value %s", value); - else - s->cleanup = r; - - } else if (streq(key, "wants")) { - - if ((r = unit_add_two_dependencies_by_name(u, UNIT_AFTER, UNIT_WANTS, value, NULL, true)) < 0) - return r; - } else - log_debug("Unknown serialization key '%s'", key); - - return 0; -} - -static UnitActiveState snapshot_active_state(Unit *u) { - assert(u); - - return state_translation_table[SNAPSHOT(u)->state]; -} - -static const char *snapshot_sub_state_to_string(Unit *u) { - assert(u); - - return snapshot_state_to_string(SNAPSHOT(u)->state); -} - -int snapshot_create(Manager *m, const char *name, bool cleanup, DBusError *e, Snapshot **_s) { - Iterator i; - Unit *other, *u = NULL; - char *n = NULL; - int r; - const char *k; - - assert(m); - assert(_s); - - if (name) { - if (!unit_name_is_valid(name, false)) { - dbus_set_error(e, BUS_ERROR_INVALID_NAME, "Unit name %s is not valid.", name); - return -EINVAL; - } - - if (unit_name_to_type(name) != UNIT_SNAPSHOT) { - dbus_set_error(e, BUS_ERROR_UNIT_TYPE_MISMATCH, "Unit name %s lacks snapshot suffix.", name); - return -EINVAL; - } - - if (manager_get_unit(m, name)) { - dbus_set_error(e, BUS_ERROR_UNIT_EXISTS, "Snapshot %s exists already.", name); - return -EEXIST; - } - - } else { - - for (;;) { - if (asprintf(&n, "snapshot-%u.snapshot", ++ m->n_snapshots) < 0) - return -ENOMEM; - - if (!manager_get_unit(m, n)) - break; - - free(n); - } - - name = n; - } - - r = manager_load_unit_prepare(m, name, NULL, e, &u); - free(n); - - if (r < 0) - goto fail; - - SNAPSHOT(u)->by_snapshot_create = true; - manager_dispatch_load_queue(m); - assert(u->load_state == UNIT_LOADED); - - HASHMAP_FOREACH_KEY(other, k, m->units, i) { - - if (other->ignore_on_snapshot) - continue; - - if (k != other->id) - continue; - - if (UNIT_VTABLE(other)->check_snapshot) - if (!UNIT_VTABLE(other)->check_snapshot(other)) - continue; - - if (!UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other))) - continue; - - if ((r = unit_add_two_dependencies(u, UNIT_AFTER, UNIT_WANTS, other, true)) < 0) - goto fail; - } - - SNAPSHOT(u)->cleanup = cleanup; - *_s = SNAPSHOT(u); - - return 0; - -fail: - if (u) - unit_add_to_cleanup_queue(u); - - return r; -} - -void snapshot_remove(Snapshot *s) { - assert(s); - - unit_add_to_cleanup_queue(UNIT(s)); -} - -static const char* const snapshot_state_table[_SNAPSHOT_STATE_MAX] = { - [SNAPSHOT_DEAD] = "dead", - [SNAPSHOT_ACTIVE] = "active" -}; - -DEFINE_STRING_TABLE_LOOKUP(snapshot_state, SnapshotState); - -const UnitVTable snapshot_vtable = { - .object_size = sizeof(Snapshot), - - .no_alias = true, - .no_instances = true, - .no_gc = true, - - .init = snapshot_init, - - .load = snapshot_load, - .coldplug = snapshot_coldplug, - - .dump = snapshot_dump, - - .start = snapshot_start, - .stop = snapshot_stop, - - .serialize = snapshot_serialize, - .deserialize_item = snapshot_deserialize_item, - - .active_state = snapshot_active_state, - .sub_state_to_string = snapshot_sub_state_to_string, - - .bus_interface = "org.freedesktop.systemd1.Snapshot", - .bus_message_handler = bus_snapshot_message_handler -}; diff --git a/src/core/snapshot.h b/src/core/snapshot.h deleted file mode 100644 index 9662d93164..0000000000 --- a/src/core/snapshot.h +++ /dev/null @@ -1,50 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -#pragma once - -/*** - 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/>. -***/ - -typedef struct Snapshot Snapshot; - -#include "unit.h" - -typedef enum SnapshotState { - SNAPSHOT_DEAD, - SNAPSHOT_ACTIVE, - _SNAPSHOT_STATE_MAX, - _SNAPSHOT_STATE_INVALID = -1 -} SnapshotState; - -struct Snapshot { - Unit meta; - - SnapshotState state, deserialized_state; - - bool cleanup; - bool by_snapshot_create:1; -}; - -extern const UnitVTable snapshot_vtable; - -int snapshot_create(Manager *m, const char *name, bool cleanup, DBusError *e, Snapshot **s); -void snapshot_remove(Snapshot *s); - -const char* snapshot_state_to_string(SnapshotState i); -SnapshotState snapshot_state_from_string(const char *s); diff --git a/src/core/socket.c b/src/core/socket.c deleted file mode 100644 index c0959815c1..0000000000 --- a/src/core/socket.c +++ /dev/null @@ -1,2289 +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 <sys/types.h> -#include <sys/stat.h> -#include <unistd.h> -#include <errno.h> -#include <fcntl.h> -#include <sys/epoll.h> -#include <signal.h> -#include <arpa/inet.h> -#include <mqueue.h> -#include <attr/xattr.h> - -#include "unit.h" -#include "socket.h" -#include "netinet/tcp.h" -#include "log.h" -#include "load-dropin.h" -#include "load-fragment.h" -#include "strv.h" -#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" -#include "bus-errors.h" -#include "label.h" -#include "exit-status.h" -#include "def.h" - -static const UnitActiveState state_translation_table[_SOCKET_STATE_MAX] = { - [SOCKET_DEAD] = UNIT_INACTIVE, - [SOCKET_START_PRE] = UNIT_ACTIVATING, - [SOCKET_START_POST] = UNIT_ACTIVATING, - [SOCKET_LISTENING] = UNIT_ACTIVE, - [SOCKET_RUNNING] = UNIT_ACTIVE, - [SOCKET_STOP_PRE] = UNIT_DEACTIVATING, - [SOCKET_STOP_PRE_SIGTERM] = UNIT_DEACTIVATING, - [SOCKET_STOP_PRE_SIGKILL] = UNIT_DEACTIVATING, - [SOCKET_STOP_POST] = UNIT_DEACTIVATING, - [SOCKET_FINAL_SIGTERM] = UNIT_DEACTIVATING, - [SOCKET_FINAL_SIGKILL] = UNIT_DEACTIVATING, - [SOCKET_FAILED] = UNIT_FAILED -}; - -static void socket_init(Unit *u) { - Socket *s = SOCKET(u); - - assert(u); - assert(u->load_state == UNIT_STUB); - - s->backlog = SOMAXCONN; - s->timeout_usec = DEFAULT_TIMEOUT_USEC; - s->directory_mode = 0755; - s->socket_mode = 0666; - - s->max_connections = 64; - - s->priority = -1; - s->ip_tos = -1; - s->ip_ttl = -1; - s->mark = -1; - - exec_context_init(&s->exec_context); - s->exec_context.std_output = u->manager->default_std_output; - s->exec_context.std_error = u->manager->default_std_error; - kill_context_init(&s->kill_context); - - s->control_command_id = _SOCKET_EXEC_COMMAND_INVALID; -} - -static void socket_unwatch_control_pid(Socket *s) { - assert(s); - - if (s->control_pid <= 0) - return; - - unit_unwatch_pid(UNIT(s), s->control_pid); - s->control_pid = 0; -} - -static void socket_done(Unit *u) { - Socket *s = SOCKET(u); - SocketPort *p; - - assert(s); - - while ((p = s->ports)) { - LIST_REMOVE(SocketPort, port, s->ports, p); - - if (p->fd >= 0) { - unit_unwatch_fd(UNIT(s), &p->fd_watch); - close_nointr_nofail(p->fd); - } - - free(p->path); - free(p); - } - - exec_context_done(&s->exec_context); - exec_command_free_array(s->exec_command, _SOCKET_EXEC_COMMAND_MAX); - s->control_command = NULL; - - socket_unwatch_control_pid(s); - - unit_ref_unset(&s->service); - - free(s->tcp_congestion); - s->tcp_congestion = NULL; - - 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); -} - -static int socket_instantiate_service(Socket *s) { - char *prefix, *name; - int r; - Unit *u; - - assert(s); - - /* This fills in s->service if it isn't filled in yet. For - * Accept=yes sockets we create the next connection service - * here. For Accept=no this is mostly a NOP since the service - * is figured out at load time anyway. */ - - if (UNIT_DEREF(s->service)) - return 0; - - assert(s->accept); - - if (!(prefix = unit_name_to_prefix(UNIT(s)->id))) - return -ENOMEM; - - r = asprintf(&name, "%s@%u.service", prefix, s->n_accepted); - free(prefix); - - if (r < 0) - return -ENOMEM; - - r = manager_load_unit(UNIT(s)->manager, name, NULL, NULL, &u); - free(name); - - if (r < 0) - return r; - -#ifdef HAVE_SYSV_COMPAT - if (SERVICE(u)->is_sysv) { - log_error("Using SysV services for socket activation is not supported. Refusing."); - return -ENOENT; - } -#endif - - u->no_gc = true; - unit_ref_set(&s->service, u); - - return unit_add_two_dependencies(UNIT(s), UNIT_BEFORE, UNIT_TRIGGERS, u, false); -} - -static bool have_non_accept_socket(Socket *s) { - SocketPort *p; - - assert(s); - - if (!s->accept) - return true; - - LIST_FOREACH(port, p, s->ports) { - - if (p->type != SOCKET_SOCKET) - return true; - - if (!socket_address_can_accept(&p->address)) - return true; - } - - return false; -} - -static int socket_verify(Socket *s) { - assert(s); - - if (UNIT(s)->load_state != UNIT_LOADED) - return 0; - - if (!s->ports) { - log_error("%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); - return -EINVAL; - } - - if (s->accept && s->max_connections <= 0) { - log_error("%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); - 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); - return -EINVAL; - } - - return 0; -} - -static bool socket_needs_mount(Socket *s, const char *prefix) { - SocketPort *p; - - assert(s); - - LIST_FOREACH(port, p, s->ports) { - - if (p->type == SOCKET_SOCKET) { - if (socket_address_needs_mount(&p->address, prefix)) - return true; - } else if (p->type == SOCKET_FIFO || p->type == SOCKET_SPECIAL) { - if (path_startswith(p->path, prefix)) - return true; - } - } - - return false; -} - -int socket_add_one_mount_link(Socket *s, Mount *m) { - int r; - - assert(s); - assert(m); - - if (UNIT(s)->load_state != UNIT_LOADED || - UNIT(m)->load_state != UNIT_LOADED) - return 0; - - if (!socket_needs_mount(s, m->where)) - return 0; - - r = unit_add_two_dependencies(UNIT(s), UNIT_AFTER, UNIT_REQUIRES, UNIT(m), true); - if (r < 0) - return r; - - return 0; -} - -static int socket_add_mount_links(Socket *s) { - Unit *other; - int r; - - assert(s); - - 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; -} - -static int socket_add_device_link(Socket *s) { - char *t; - int r; - - assert(s); - - if (!s->bind_to_device) - return 0; - - if (asprintf(&t, "/sys/subsystem/net/devices/%s", s->bind_to_device) < 0) - return -ENOMEM; - - r = unit_add_node_link(UNIT(s), t, false); - free(t); - - return r; -} - -static int socket_add_default_dependencies(Socket *s) { - int r; - assert(s); - - 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; - - if ((r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, NULL, true)) < 0) - return r; - } - - return unit_add_two_dependencies_by_name(UNIT(s), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, NULL, true); -} - -static bool socket_has_exec(Socket *s) { - unsigned i; - assert(s); - - for (i = 0; i < _SOCKET_EXEC_COMMAND_MAX; i++) - if (s->exec_command[i]) - return true; - - return false; -} - -static int socket_load(Unit *u) { - Socket *s = SOCKET(u); - int r; - - assert(u); - assert(u->load_state == UNIT_STUB); - - if ((r = unit_load_fragment_and_dropin(u)) < 0) - return r; - - /* This is a new unit? Then let's add in some extras */ - if (u->load_state == UNIT_LOADED) { - - if (have_non_accept_socket(s)) { - - if (!UNIT_DEREF(s->service)) { - Unit *x; - - r = unit_load_related_unit(u, ".service", &x); - if (r < 0) - return r; - - unit_ref_set(&s->service, x); - } - - r = unit_add_two_dependencies(u, UNIT_BEFORE, UNIT_TRIGGERS, UNIT_DEREF(s->service), true); - if (r < 0) - return r; - } - - if ((r = socket_add_mount_links(s)) < 0) - return r; - - if ((r = socket_add_device_link(s)) < 0) - return r; - - if (socket_has_exec(s)) - if ((r = unit_add_exec_dependencies(u, &s->exec_context)) < 0) - return r; - - if ((r = unit_add_default_cgroups(u)) < 0) - return r; - - if (UNIT(s)->default_dependencies) - if ((r = socket_add_default_dependencies(s)) < 0) - return r; - - r = unit_exec_context_defaults(u, &s->exec_context); - if (r < 0) - return r; - } - - return socket_verify(s); -} - -static const char* listen_lookup(int family, int type) { - - if (family == AF_NETLINK) - return "ListenNetlink"; - - if (type == SOCK_STREAM) - return "ListenStream"; - else if (type == SOCK_DGRAM) - return "ListenDatagram"; - else if (type == SOCK_SEQPACKET) - return "ListenSequentialPacket"; - - assert_not_reached("Unknown socket type"); - return NULL; -} - -static void socket_dump(Unit *u, FILE *f, const char *prefix) { - - SocketExecCommand c; - Socket *s = SOCKET(u); - SocketPort *p; - const char *prefix2; - char *p2; - - assert(s); - assert(f); - - p2 = strappend(prefix, "\t"); - prefix2 = p2 ? p2 : prefix; - - fprintf(f, - "%sSocket State: %s\n" - "%sResult: %s\n" - "%sBindIPv6Only: %s\n" - "%sBacklog: %u\n" - "%sSocketMode: %04o\n" - "%sDirectoryMode: %04o\n" - "%sKeepAlive: %s\n" - "%sFreeBind: %s\n" - "%sTransparent: %s\n" - "%sBroadcast: %s\n" - "%sPassCredentials: %s\n" - "%sPassSecurity: %s\n" - "%sTCPCongestion: %s\n", - prefix, socket_state_to_string(s->state), - prefix, socket_result_to_string(s->result), - prefix, socket_address_bind_ipv6_only_to_string(s->bind_ipv6_only), - prefix, s->backlog, - prefix, s->socket_mode, - prefix, s->directory_mode, - prefix, yes_no(s->keep_alive), - prefix, yes_no(s->free_bind), - prefix, yes_no(s->transparent), - prefix, yes_no(s->broadcast), - prefix, yes_no(s->pass_cred), - prefix, yes_no(s->pass_sec), - prefix, strna(s->tcp_congestion)); - - if (s->control_pid > 0) - fprintf(f, - "%sControl PID: %lu\n", - prefix, (unsigned long) s->control_pid); - - if (s->bind_to_device) - fprintf(f, - "%sBindToDevice: %s\n", - prefix, s->bind_to_device); - - if (s->accept) - fprintf(f, - "%sAccepted: %u\n" - "%sNConnections: %u\n" - "%sMaxConnections: %u\n", - prefix, s->n_accepted, - prefix, s->n_connections, - prefix, s->max_connections); - - if (s->priority >= 0) - fprintf(f, - "%sPriority: %i\n", - prefix, s->priority); - - if (s->receive_buffer > 0) - fprintf(f, - "%sReceiveBuffer: %zu\n", - prefix, s->receive_buffer); - - if (s->send_buffer > 0) - fprintf(f, - "%sSendBuffer: %zu\n", - prefix, s->send_buffer); - - if (s->ip_tos >= 0) - fprintf(f, - "%sIPTOS: %i\n", - prefix, s->ip_tos); - - if (s->ip_ttl >= 0) - fprintf(f, - "%sIPTTL: %i\n", - prefix, s->ip_ttl); - - if (s->pipe_size > 0) - fprintf(f, - "%sPipeSize: %zu\n", - prefix, s->pipe_size); - - if (s->mark >= 0) - fprintf(f, - "%sMark: %i\n", - prefix, s->mark); - - if (s->mq_maxmsg > 0) - fprintf(f, - "%sMessageQueueMaxMessages: %li\n", - prefix, s->mq_maxmsg); - - if (s->mq_msgsize > 0) - fprintf(f, - "%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) { - const char *t; - int r; - char *k = NULL; - - if ((r = socket_address_print(&p->address, &k)) < 0) - t = strerror(-r); - else - t = k; - - fprintf(f, "%s%s: %s\n", prefix, listen_lookup(socket_address_family(&p->address), p->address.type), t); - free(k); - } else if (p->type == SOCKET_SPECIAL) - fprintf(f, "%sListenSpecial: %s\n", prefix, p->path); - else if (p->type == SOCKET_MQUEUE) - fprintf(f, "%sListenMessageQueue: %s\n", prefix, p->path); - else - fprintf(f, "%sListenFIFO: %s\n", prefix, p->path); - } - - exec_context_dump(&s->exec_context, f, prefix); - kill_context_dump(&s->kill_context, f, prefix); - - for (c = 0; c < _SOCKET_EXEC_COMMAND_MAX; c++) { - if (!s->exec_command[c]) - continue; - - fprintf(f, "%s-> %s:\n", - prefix, socket_exec_command_to_string(c)); - - exec_command_dump_list(s->exec_command[c], f, prefix2); - } - - free(p2); -} - -static int instance_from_socket(int fd, unsigned nr, char **instance) { - socklen_t l; - char *r; - union { - struct sockaddr sa; - struct sockaddr_un un; - struct sockaddr_in in; - struct sockaddr_in6 in6; - struct sockaddr_storage storage; - } local, remote; - - assert(fd >= 0); - assert(instance); - - l = sizeof(local); - if (getsockname(fd, &local.sa, &l) < 0) - return -errno; - - l = sizeof(remote); - if (getpeername(fd, &remote.sa, &l) < 0) - return -errno; - - switch (local.sa.sa_family) { - - case AF_INET: { - uint32_t - a = ntohl(local.in.sin_addr.s_addr), - b = ntohl(remote.in.sin_addr.s_addr); - - if (asprintf(&r, - "%u-%u.%u.%u.%u:%u-%u.%u.%u.%u:%u", - nr, - a >> 24, (a >> 16) & 0xFF, (a >> 8) & 0xFF, a & 0xFF, - ntohs(local.in.sin_port), - b >> 24, (b >> 16) & 0xFF, (b >> 8) & 0xFF, b & 0xFF, - ntohs(remote.in.sin_port)) < 0) - return -ENOMEM; - - break; - } - - case AF_INET6: { - static const unsigned char ipv4_prefix[] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF - }; - - if (memcmp(&local.in6.sin6_addr, ipv4_prefix, sizeof(ipv4_prefix)) == 0 && - memcmp(&remote.in6.sin6_addr, ipv4_prefix, sizeof(ipv4_prefix)) == 0) { - const uint8_t - *a = local.in6.sin6_addr.s6_addr+12, - *b = remote.in6.sin6_addr.s6_addr+12; - - if (asprintf(&r, - "%u-%u.%u.%u.%u:%u-%u.%u.%u.%u:%u", - nr, - a[0], a[1], a[2], a[3], - ntohs(local.in6.sin6_port), - b[0], b[1], b[2], b[3], - ntohs(remote.in6.sin6_port)) < 0) - return -ENOMEM; - } else { - char a[INET6_ADDRSTRLEN], b[INET6_ADDRSTRLEN]; - - if (asprintf(&r, - "%u-%s:%u-%s:%u", - nr, - inet_ntop(AF_INET6, &local.in6.sin6_addr, a, sizeof(a)), - ntohs(local.in6.sin6_port), - inet_ntop(AF_INET6, &remote.in6.sin6_addr, b, sizeof(b)), - ntohs(remote.in6.sin6_port)) < 0) - return -ENOMEM; - } - - break; - } - - case AF_UNIX: { - struct ucred ucred; - - l = sizeof(ucred); - if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &l) < 0) - return -errno; - - if (asprintf(&r, - "%u-%lu-%lu", - nr, - (unsigned long) ucred.pid, - (unsigned long) ucred.uid) < 0) - return -ENOMEM; - - break; - } - - default: - assert_not_reached("Unhandled socket type."); - } - - *instance = r; - return 0; -} - -static void socket_close_fds(Socket *s) { - SocketPort *p; - - assert(s); - - LIST_FOREACH(port, p, s->ports) { - if (p->fd < 0) - continue; - - unit_unwatch_fd(UNIT(s), &p->fd_watch); - close_nointr_nofail(p->fd); - - /* One little note: we should never delete any sockets - * in the file system here! After all some other - * process we spawned might still have a reference of - * this fd and wants to continue to use it. Therefore - * we delete sockets in the file system before we - * create a new one, not after we stopped using - * one! */ - - p->fd = -1; - } -} - -static void socket_apply_socket_options(Socket *s, int fd) { - assert(s); - assert(fd >= 0); - - 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"); - } - - if (s->broadcast) { - int one = 1; - if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &one, sizeof(one)) < 0) - log_warning("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"); - } - - if (s->pass_sec) { - int one = 1; - if (setsockopt(fd, SOL_SOCKET, SO_PASSSEC, &one, sizeof(one)) < 0) - log_warning("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"); - - if (s->receive_buffer > 0) { - int value = (int) s->receive_buffer; - - /* We first try with SO_RCVBUFFORCE, in case we have the perms for that */ - - 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"); - } - - 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"); - } - - if (s->mark >= 0) - if (setsockopt(fd, SOL_SOCKET, SO_MARK, &s->mark, sizeof(s->mark)) < 0) - log_warning("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"); - - if (s->ip_ttl >= 0) { - int r, x; - - r = setsockopt(fd, IPPROTO_IP, IP_TTL, &s->ip_ttl, sizeof(s->ip_ttl)); - - if (socket_ipv6_is_supported()) - x = setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &s->ip_ttl, sizeof(s->ip_ttl)); - else { - x = -1; - errno = EAFNOSUPPORT; - } - - if (r < 0 && x < 0) - log_warning("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"); - - if (s->smack_ip_in) - if (fsetxattr(fd, "security.SMACK64IPIN", s->smack_ip_in, strlen(s->smack_ip_in), 0) < 0) - log_error("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("fsetxattr(\"security.SMACK64IPOUT\"): %m"); -} - -static void socket_apply_fifo_options(Socket *s, int fd) { - assert(s); - assert(fd >= 0); - - if (s->pipe_size > 0) - if (fcntl(fd, F_SETPIPE_SZ, s->pipe_size) < 0) - log_warning("F_SETPIPE_SZ: %m"); - - if (s->smack) - if (fsetxattr(fd, "security.SMACK64", s->smack, strlen(s->smack), 0) < 0) - log_error("fsetxattr(\"security.SMACK64\"): %m"); -} - -static int fifo_address_create( - const char *path, - mode_t directory_mode, - mode_t socket_mode, - int *_fd) { - - int fd = -1, r = 0; - struct stat st; - mode_t old_mask; - - assert(path); - assert(_fd); - - mkdir_parents_label(path, directory_mode); - - r = label_context_set(path, S_IFIFO); - if (r < 0) - goto fail; - - /* Enforce the right access mode for the fifo */ - old_mask = umask(~ socket_mode); - - /* Include the original umask in our mask */ - umask(~socket_mode | old_mask); - - r = mkfifo(path, socket_mode); - umask(old_mask); - - if (r < 0 && errno != EEXIST) { - r = -errno; - goto fail; - } - - if ((fd = open(path, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK|O_NOFOLLOW)) < 0) { - r = -errno; - goto fail; - } - - label_context_clear(); - - if (fstat(fd, &st) < 0) { - r = -errno; - goto fail; - } - - if (!S_ISFIFO(st.st_mode) || - (st.st_mode & 0777) != (socket_mode & ~old_mask) || - st.st_uid != getuid() || - st.st_gid != getgid()) { - - r = -EEXIST; - goto fail; - } - - *_fd = fd; - return 0; - -fail: - label_context_clear(); - - if (fd >= 0) - close_nointr_nofail(fd); - - return r; -} - -static int special_address_create( - const char *path, - int *_fd) { - - int fd = -1, r = 0; - struct stat st; - - assert(path); - assert(_fd); - - if ((fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NONBLOCK|O_NOFOLLOW)) < 0) { - r = -errno; - goto fail; - } - - if (fstat(fd, &st) < 0) { - r = -errno; - goto fail; - } - - /* Check whether this is a /proc, /sys or /dev file or char device */ - if (!S_ISREG(st.st_mode) && !S_ISCHR(st.st_mode)) { - r = -EEXIST; - goto fail; - } - - *_fd = fd; - return 0; - -fail: - if (fd >= 0) - close_nointr_nofail(fd); - - return r; -} - -static int mq_address_create( - const char *path, - mode_t mq_mode, - long maxmsg, - long msgsize, - int *_fd) { - - int fd = -1, r = 0; - struct stat st; - mode_t old_mask; - struct mq_attr _attr, *attr = NULL; - - assert(path); - assert(_fd); - - if (maxmsg > 0 && msgsize > 0) { - zero(_attr); - _attr.mq_flags = O_NONBLOCK; - _attr.mq_maxmsg = maxmsg; - _attr.mq_msgsize = msgsize; - attr = &_attr; - } - - /* Enforce the right access mode for the mq */ - old_mask = umask(~ mq_mode); - - /* Include the original umask in our mask */ - umask(~mq_mode | old_mask); - - fd = mq_open(path, O_RDONLY|O_CLOEXEC|O_NONBLOCK|O_CREAT, mq_mode, attr); - umask(old_mask); - - if (fd < 0) { - r = -errno; - goto fail; - } - - if (fstat(fd, &st) < 0) { - r = -errno; - goto fail; - } - - if ((st.st_mode & 0777) != (mq_mode & ~old_mask) || - st.st_uid != getuid() || - st.st_gid != getgid()) { - - r = -EEXIST; - goto fail; - } - - *_fd = fd; - return 0; - -fail: - if (fd >= 0) - close_nointr_nofail(fd); - - return r; -} - -static int socket_open_fds(Socket *s) { - SocketPort *p; - int r; - char *label = NULL; - bool know_label = false; - - assert(s); - - LIST_FOREACH(port, p, s->ports) { - - if (p->fd >= 0) - continue; - - if (p->type == SOCKET_SOCKET) { - - if (!know_label) { - - if ((r = socket_instantiate_service(s)) < 0) - return r; - - if (UNIT_DEREF(s->service) && - SERVICE(UNIT_DEREF(s->service))->exec_command[SERVICE_EXEC_START]) { - r = label_get_create_label_from_exe(SERVICE(UNIT_DEREF(s->service))->exec_command[SERVICE_EXEC_START]->path, &label); - - if (r < 0) { - if (r != -EPERM) - return r; - } - } - - know_label = true; - } - - if ((r = socket_address_listen( - &p->address, - s->backlog, - s->bind_ipv6_only, - s->bind_to_device, - s->free_bind, - s->transparent, - s->directory_mode, - s->socket_mode, - label, - &p->fd)) < 0) - goto rollback; - - socket_apply_socket_options(s, p->fd); - - } else if (p->type == SOCKET_SPECIAL) { - - if ((r = special_address_create( - p->path, - &p->fd)) < 0) - goto rollback; - - } else if (p->type == SOCKET_FIFO) { - - if ((r = fifo_address_create( - p->path, - s->directory_mode, - s->socket_mode, - &p->fd)) < 0) - goto rollback; - - socket_apply_fifo_options(s, p->fd); - } else if (p->type == SOCKET_MQUEUE) { - - if ((r = mq_address_create( - p->path, - s->socket_mode, - s->mq_maxmsg, - s->mq_msgsize, - &p->fd)) < 0) - goto rollback; - } else - assert_not_reached("Unknown port type"); - } - - label_free(label); - return 0; - -rollback: - socket_close_fds(s); - label_free(label); - return r; -} - -static void socket_unwatch_fds(Socket *s) { - SocketPort *p; - - assert(s); - - LIST_FOREACH(port, p, s->ports) { - if (p->fd < 0) - continue; - - unit_unwatch_fd(UNIT(s), &p->fd_watch); - } -} - -static int socket_watch_fds(Socket *s) { - SocketPort *p; - int r; - - assert(s); - - LIST_FOREACH(port, p, s->ports) { - if (p->fd < 0) - continue; - - p->fd_watch.socket_accept = - s->accept && - p->type == SOCKET_SOCKET && - socket_address_can_accept(&p->address); - - if ((r = unit_watch_fd(UNIT(s), p->fd, EPOLLIN, &p->fd_watch)) < 0) - goto fail; - } - - return 0; - -fail: - socket_unwatch_fds(s); - return r; -} - -static void socket_set_state(Socket *s, SocketState state) { - SocketState old_state; - assert(s); - - old_state = s->state; - s->state = state; - - if (state != SOCKET_START_PRE && - state != SOCKET_START_POST && - state != SOCKET_STOP_PRE && - state != SOCKET_STOP_PRE_SIGTERM && - state != SOCKET_STOP_PRE_SIGKILL && - state != SOCKET_STOP_POST && - state != SOCKET_FINAL_SIGTERM && - state != SOCKET_FINAL_SIGKILL) { - unit_unwatch_timer(UNIT(s), &s->timer_watch); - socket_unwatch_control_pid(s); - s->control_command = NULL; - s->control_command_id = _SOCKET_EXEC_COMMAND_INVALID; - } - - if (state != SOCKET_LISTENING) - socket_unwatch_fds(s); - - if (state != SOCKET_START_POST && - state != SOCKET_LISTENING && - state != SOCKET_RUNNING && - state != SOCKET_STOP_PRE && - state != SOCKET_STOP_PRE_SIGTERM && - state != SOCKET_STOP_PRE_SIGKILL) - 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)); - - unit_notify(UNIT(s), state_translation_table[old_state], state_translation_table[state], true); -} - -static int socket_coldplug(Unit *u) { - Socket *s = SOCKET(u); - int r; - - assert(s); - assert(s->state == SOCKET_DEAD); - - if (s->deserialized_state != s->state) { - - if (s->deserialized_state == SOCKET_START_PRE || - s->deserialized_state == SOCKET_START_POST || - s->deserialized_state == SOCKET_STOP_PRE || - s->deserialized_state == SOCKET_STOP_PRE_SIGTERM || - s->deserialized_state == SOCKET_STOP_PRE_SIGKILL || - s->deserialized_state == SOCKET_STOP_POST || - s->deserialized_state == SOCKET_FINAL_SIGTERM || - s->deserialized_state == SOCKET_FINAL_SIGKILL) { - - if (s->control_pid <= 0) - return -EBADMSG; - - if ((r = unit_watch_pid(UNIT(s), s->control_pid)) < 0) - return r; - - if ((r = unit_watch_timer(UNIT(s), s->timeout_usec, &s->timer_watch)) < 0) - return r; - } - - if (s->deserialized_state == SOCKET_START_POST || - s->deserialized_state == SOCKET_LISTENING || - s->deserialized_state == SOCKET_RUNNING || - s->deserialized_state == SOCKET_STOP_PRE || - s->deserialized_state == SOCKET_STOP_PRE_SIGTERM || - s->deserialized_state == SOCKET_STOP_PRE_SIGKILL) - if ((r = socket_open_fds(s)) < 0) - return r; - - if (s->deserialized_state == SOCKET_LISTENING) - if ((r = socket_watch_fds(s)) < 0) - return r; - - socket_set_state(s, s->deserialized_state); - } - - return 0; -} - -static int socket_spawn(Socket *s, ExecCommand *c, pid_t *_pid) { - pid_t pid; - int r; - char **argv; - - assert(s); - assert(c); - assert(_pid); - - if ((r = unit_watch_timer(UNIT(s), s->timeout_usec, &s->timer_watch)) < 0) - goto fail; - - if (!(argv = unit_full_printf_strv(UNIT(s), c->argv))) { - r = -ENOMEM; - goto fail; - } - - r = exec_spawn(c, - argv, - &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); - - strv_free(argv); - if (r < 0) - goto fail; - - if ((r = unit_watch_pid(UNIT(s), pid)) < 0) - /* FIXME: we need to do something here */ - goto fail; - - *_pid = pid; - - return 0; - -fail: - unit_unwatch_timer(UNIT(s), &s->timer_watch); - - return r; -} - -static void socket_enter_dead(Socket *s, SocketResult f) { - assert(s); - - if (f != SOCKET_SUCCESS) - s->result = f; - - socket_set_state(s, s->result != SOCKET_SUCCESS ? SOCKET_FAILED : SOCKET_DEAD); -} - -static void socket_enter_signal(Socket *s, SocketState state, SocketResult f); - -static void socket_enter_stop_post(Socket *s, SocketResult f) { - int r; - assert(s); - - if (f != SOCKET_SUCCESS) - s->result = f; - - socket_unwatch_control_pid(s); - - s->control_command_id = SOCKET_EXEC_STOP_POST; - - if ((s->control_command = s->exec_command[SOCKET_EXEC_STOP_POST])) { - if ((r = socket_spawn(s, s->control_command, &s->control_pid)) < 0) - goto fail; - - socket_set_state(s, SOCKET_STOP_POST); - } else - socket_enter_signal(s, SOCKET_FINAL_SIGTERM, SOCKET_SUCCESS); - - return; - -fail: - log_warning("%s failed to run 'stop-post' task: %s", UNIT(s)->id, strerror(-r)); - socket_enter_signal(s, SOCKET_FINAL_SIGTERM, SOCKET_FAILURE_RESOURCES); -} - -static void socket_enter_signal(Socket *s, SocketState state, SocketResult f) { - int r; - Set *pid_set = NULL; - bool wait_for_exit = false; - - assert(s); - - if (f != SOCKET_SUCCESS) - s->result = f; - - if (s->kill_context.kill_mode != KILL_NONE) { - int sig = (state == SOCKET_STOP_PRE_SIGTERM || state == SOCKET_FINAL_SIGTERM) ? s->kill_context.kill_signal : SIGKILL; - - 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); - 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; - 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) - 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)); - } else if (r > 0) - wait_for_exit = true; - - set_free(pid_set); - pid_set = NULL; - } - } - - if (wait_for_exit) { - if ((r = unit_watch_timer(UNIT(s), s->timeout_usec, &s->timer_watch)) < 0) - goto fail; - - socket_set_state(s, state); - } else if (state == SOCKET_STOP_PRE_SIGTERM || state == SOCKET_STOP_PRE_SIGKILL) - socket_enter_stop_post(s, SOCKET_SUCCESS); - else - socket_enter_dead(s, SOCKET_SUCCESS); - - return; - -fail: - log_warning("%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); - else - socket_enter_dead(s, SOCKET_FAILURE_RESOURCES); - - if (pid_set) - set_free(pid_set); -} - -static void socket_enter_stop_pre(Socket *s, SocketResult f) { - int r; - assert(s); - - if (f != SOCKET_SUCCESS) - s->result = f; - - socket_unwatch_control_pid(s); - - s->control_command_id = SOCKET_EXEC_STOP_PRE; - - if ((s->control_command = s->exec_command[SOCKET_EXEC_STOP_PRE])) { - if ((r = socket_spawn(s, s->control_command, &s->control_pid)) < 0) - goto fail; - - socket_set_state(s, SOCKET_STOP_PRE); - } else - socket_enter_stop_post(s, SOCKET_SUCCESS); - - return; - -fail: - log_warning("%s failed to run 'stop-pre' task: %s", UNIT(s)->id, strerror(-r)); - socket_enter_stop_post(s, SOCKET_FAILURE_RESOURCES); -} - -static void socket_enter_listening(Socket *s) { - int r; - assert(s); - - r = socket_watch_fds(s); - if (r < 0) { - log_warning("%s failed to watch sockets: %s", UNIT(s)->id, strerror(-r)); - goto fail; - } - - socket_set_state(s, SOCKET_LISTENING); - return; - -fail: - socket_enter_stop_pre(s, SOCKET_FAILURE_RESOURCES); -} - -static void socket_enter_start_post(Socket *s) { - int r; - assert(s); - - r = socket_open_fds(s); - if (r < 0) { - log_warning("%s failed to listen on sockets: %s", UNIT(s)->id, strerror(-r)); - goto fail; - } - - socket_unwatch_control_pid(s); - - s->control_command_id = SOCKET_EXEC_START_POST; - - 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)); - goto fail; - } - - socket_set_state(s, SOCKET_START_POST); - } else - socket_enter_listening(s); - - return; - -fail: - socket_enter_stop_pre(s, SOCKET_FAILURE_RESOURCES); -} - -static void socket_enter_start_pre(Socket *s) { - int r; - assert(s); - - socket_unwatch_control_pid(s); - - s->control_command_id = SOCKET_EXEC_START_PRE; - - if ((s->control_command = s->exec_command[SOCKET_EXEC_START_PRE])) { - if ((r = socket_spawn(s, s->control_command, &s->control_pid)) < 0) - goto fail; - - socket_set_state(s, SOCKET_START_PRE); - } else - socket_enter_start_post(s); - - return; - -fail: - log_warning("%s failed to run 'start-pre' task: %s", UNIT(s)->id, strerror(-r)); - socket_enter_dead(s, SOCKET_FAILURE_RESOURCES); -} - -static void socket_enter_running(Socket *s, int cfd) { - int r; - DBusError error; - - assert(s); - dbus_error_init(&error); - - /* 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); - - if (cfd >= 0) - close_nointr_nofail(cfd); - else { - /* Flush all sockets by closing and reopening them */ - socket_close_fds(s); - - r = socket_watch_fds(s); - if (r < 0) { - log_warning("%s failed to watch sockets: %s", UNIT(s)->id, strerror(-r)); - socket_enter_stop_pre(s, SOCKET_FAILURE_RESOURCES); - } - } - - return; - } - - if (cfd < 0) { - Iterator i; - Unit *u; - bool pending = false; - - /* If there's already a start pending don't bother to - * do anything */ - SET_FOREACH(u, UNIT(s)->dependencies[UNIT_TRIGGERS], i) - if (unit_pending_active(u)) { - pending = true; - break; - } - - if (!pending) { - r = manager_add_job(UNIT(s)->manager, JOB_START, UNIT_DEREF(s->service), JOB_REPLACE, true, &error, NULL); - if (r < 0) - goto fail; - } - - socket_set_state(s, SOCKET_RUNNING); - } else { - char *prefix, *instance = NULL, *name; - Service *service; - - if (s->n_connections >= s->max_connections) { - log_warning("Too many incoming connections (%u)", s->n_connections); - close_nointr_nofail(cfd); - return; - } - - r = socket_instantiate_service(s); - if (r < 0) - goto fail; - - r = instance_from_socket(cfd, s->n_accepted, &instance); - if (r < 0) { - if (r != -ENOTCONN) - goto fail; - - /* ENOTCONN is legitimate if TCP RST was received. - * This connection is over, but the socket unit lives on. */ - close_nointr_nofail(cfd); - return; - } - - prefix = unit_name_to_prefix(UNIT(s)->id); - if (!prefix) { - free(instance); - r = -ENOMEM; - goto fail; - } - - name = unit_name_build(prefix, instance, ".service"); - free(prefix); - free(instance); - - if (!name) { - r = -ENOMEM; - goto fail; - } - - r = unit_add_name(UNIT_DEREF(s->service), name); - if (r < 0) { - free(name); - goto fail; - } - - service = SERVICE(UNIT_DEREF(s->service)); - unit_ref_unset(&s->service); - s->n_accepted ++; - - UNIT(service)->no_gc = false; - - unit_choose_id(UNIT(service), name); - free(name); - - r = service_set_socket_fd(service, cfd, s); - if (r < 0) - goto fail; - - cfd = -1; - s->n_connections ++; - - r = manager_add_job(UNIT(s)->manager, JOB_START, UNIT(service), JOB_REPLACE, true, &error, NULL); - if (r < 0) - goto fail; - - /* Notify clients about changed counters */ - unit_add_to_dbus_queue(UNIT(s)); - } - - return; - -fail: - log_warning("%s failed to queue socket startup job: %s", UNIT(s)->id, bus_error(&error, r)); - socket_enter_stop_pre(s, SOCKET_FAILURE_RESOURCES); - - if (cfd >= 0) - close_nointr_nofail(cfd); - - dbus_error_free(&error); -} - -static void socket_run_next(Socket *s) { - int r; - - assert(s); - assert(s->control_command); - assert(s->control_command->command_next); - - socket_unwatch_control_pid(s); - - s->control_command = s->control_command->command_next; - - if ((r = socket_spawn(s, s->control_command, &s->control_pid)) < 0) - goto fail; - - return; - -fail: - log_warning("%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); - else if (s->state == SOCKET_STOP_POST) - socket_enter_dead(s, SOCKET_FAILURE_RESOURCES); - else - socket_enter_signal(s, SOCKET_FINAL_SIGTERM, SOCKET_FAILURE_RESOURCES); -} - -static int socket_start(Unit *u) { - Socket *s = SOCKET(u); - - assert(s); - - /* We cannot fulfill this request right now, try again later - * please! */ - if (s->state == SOCKET_STOP_PRE || - s->state == SOCKET_STOP_PRE_SIGKILL || - s->state == SOCKET_STOP_PRE_SIGTERM || - s->state == SOCKET_STOP_POST || - s->state == SOCKET_FINAL_SIGTERM || - s->state == SOCKET_FINAL_SIGKILL) - return -EAGAIN; - - if (s->state == SOCKET_START_PRE || - s->state == SOCKET_START_POST) - return 0; - - /* Cannot run this without the service being around */ - if (UNIT_DEREF(s->service)) { - Service *service; - - service = SERVICE(UNIT_DEREF(s->service)); - - if (UNIT(service)->load_state != UNIT_LOADED) { - log_error("Socket service %s not loaded, refusing.", UNIT(service)->id); - return -ENOENT; - } - - /* If the service is already active we cannot start the - * socket */ - 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); - return -EBUSY; - } - -#ifdef HAVE_SYSV_COMPAT - if (service->is_sysv) { - log_error("Using SysV services for socket activation is not supported. Refusing."); - return -ENOENT; - } -#endif - } - - assert(s->state == SOCKET_DEAD || s->state == SOCKET_FAILED); - - s->result = SOCKET_SUCCESS; - socket_enter_start_pre(s); - return 0; -} - -static int socket_stop(Unit *u) { - Socket *s = SOCKET(u); - - assert(s); - - /* Already on it */ - if (s->state == SOCKET_STOP_PRE || - s->state == SOCKET_STOP_PRE_SIGTERM || - s->state == SOCKET_STOP_PRE_SIGKILL || - s->state == SOCKET_STOP_POST || - s->state == SOCKET_FINAL_SIGTERM || - s->state == SOCKET_FINAL_SIGKILL) - return 0; - - /* If there's already something running we go directly into - * kill mode. */ - if (s->state == SOCKET_START_PRE || - s->state == SOCKET_START_POST) { - socket_enter_signal(s, SOCKET_STOP_PRE_SIGTERM, SOCKET_SUCCESS); - return -EAGAIN; - } - - assert(s->state == SOCKET_LISTENING || s->state == SOCKET_RUNNING); - - socket_enter_stop_pre(s, SOCKET_SUCCESS); - return 0; -} - -static int socket_serialize(Unit *u, FILE *f, FDSet *fds) { - Socket *s = SOCKET(u); - SocketPort *p; - int r; - - assert(u); - assert(f); - assert(fds); - - unit_serialize_item(u, f, "state", socket_state_to_string(s->state)); - unit_serialize_item(u, f, "result", socket_result_to_string(s->result)); - unit_serialize_item_format(u, f, "n-accepted", "%u", s->n_accepted); - - if (s->control_pid > 0) - unit_serialize_item_format(u, f, "control-pid", "%lu", (unsigned long) s->control_pid); - - if (s->control_command_id >= 0) - unit_serialize_item(u, f, "control-command", socket_exec_command_to_string(s->control_command_id)); - - LIST_FOREACH(port, p, s->ports) { - int copy; - - if (p->fd < 0) - continue; - - if ((copy = fdset_put_dup(fds, p->fd)) < 0) - return copy; - - if (p->type == SOCKET_SOCKET) { - char *t; - - if ((r = socket_address_print(&p->address, &t)) < 0) - return r; - - if (socket_address_family(&p->address) == AF_NETLINK) - unit_serialize_item_format(u, f, "netlink", "%i %s", copy, t); - else - unit_serialize_item_format(u, f, "socket", "%i %i %s", copy, p->address.type, t); - free(t); - } else if (p->type == SOCKET_SPECIAL) - unit_serialize_item_format(u, f, "special", "%i %s", copy, p->path); - else { - assert(p->type == SOCKET_FIFO); - unit_serialize_item_format(u, f, "fifo", "%i %s", copy, p->path); - } - } - - return 0; -} - -static int socket_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) { - Socket *s = SOCKET(u); - - assert(u); - assert(key); - assert(value); - assert(fds); - - if (streq(key, "state")) { - SocketState state; - - if ((state = socket_state_from_string(value)) < 0) - log_debug("Failed to parse state value %s", value); - else - s->deserialized_state = state; - } else if (streq(key, "result")) { - SocketResult f; - - f = socket_result_from_string(value); - if (f < 0) - log_debug("Failed to parse result value %s", value); - else if (f != SOCKET_SUCCESS) - s->result = f; - - } else if (streq(key, "n-accepted")) { - unsigned k; - - if (safe_atou(value, &k) < 0) - log_debug("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); - 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); - else { - s->control_command_id = id; - s->control_command = s->exec_command[id]; - } - } else if (streq(key, "fifo")) { - int fd, skip = 0; - 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); - else { - - LIST_FOREACH(port, p, s->ports) - if (p->type == SOCKET_FIFO && - 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, "special")) { - int fd, skip = 0; - 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); - else { - - LIST_FOREACH(port, p, s->ports) - if (p->type == SOCKET_SPECIAL && - 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); - else { - - LIST_FOREACH(port, p, s->ports) - if (socket_address_is(&p->address, value+skip, type)) - break; - - if (p) { - if (p->fd >= 0) - close_nointr_nofail(p->fd); - p->fd = fdset_remove(fds, fd); - } - } - - } else if (streq(key, "netlink")) { - int fd, skip = 0; - 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); - else { - - LIST_FOREACH(port, p, s->ports) - if (socket_address_is_netlink(&p->address, value+skip)) - break; - - if (p) { - if (p->fd >= 0) - close_nointr_nofail(p->fd); - p->fd = fdset_remove(fds, fd); - } - } - - } else - log_debug("Unknown serialization key '%s'", key); - - return 0; -} - -static UnitActiveState socket_active_state(Unit *u) { - assert(u); - - return state_translation_table[SOCKET(u)->state]; -} - -static const char *socket_sub_state_to_string(Unit *u) { - assert(u); - - return socket_state_to_string(SOCKET(u)->state); -} - -static bool socket_check_gc(Unit *u) { - Socket *s = SOCKET(u); - - assert(u); - - return s->n_connections > 0; -} - -static void socket_fd_event(Unit *u, int fd, uint32_t events, Watch *w) { - Socket *s = SOCKET(u); - int cfd = -1; - - assert(s); - assert(fd >= 0); - - if (s->state != SOCKET_LISTENING) - return; - - log_debug("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); - else - log_error("%s: Got unexpected poll event (0x%x) on socket.", u->id, events); - - goto fail; - } - - if (w->socket_accept) { - for (;;) { - - cfd = accept4(fd, NULL, NULL, SOCK_NONBLOCK); - if (cfd < 0) { - - if (errno == EINTR) - continue; - - log_error("Failed to accept socket: %m"); - goto fail; - } - - break; - } - - socket_apply_socket_options(s, cfd); - } - - socket_enter_running(s, cfd); - return; - -fail: - socket_enter_stop_pre(s, SOCKET_FAILURE_RESOURCES); -} - -static void socket_sigchld_event(Unit *u, pid_t pid, int code, int status) { - Socket *s = SOCKET(u); - SocketResult f; - - assert(s); - assert(pid >= 0); - - if (pid != s->control_pid) - return; - - s->control_pid = 0; - - if (is_clean_exit(code, status, NULL)) - f = SOCKET_SUCCESS; - else if (code == CLD_EXITED) - f = SOCKET_FAILURE_EXIT_CODE; - else if (code == CLD_KILLED) - f = SOCKET_FAILURE_SIGNAL; - else if (code == CLD_DUMPED) - f = SOCKET_FAILURE_CORE_DUMP; - else - assert_not_reached("Unknown code"); - - if (s->control_command) { - exec_status_exit(&s->control_command->exec_status, &s->exec_context, pid, code, status); - - if (s->control_command->ignore) - 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); - - if (f != SOCKET_SUCCESS) - s->result = f; - - if (s->control_command && - 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)); - socket_run_next(s); - } else { - s->control_command = NULL; - s->control_command_id = _SOCKET_EXEC_COMMAND_INVALID; - - /* 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)); - - switch (s->state) { - - case SOCKET_START_PRE: - if (f == SOCKET_SUCCESS) - socket_enter_start_post(s); - else - socket_enter_signal(s, SOCKET_FINAL_SIGTERM, f); - break; - - case SOCKET_START_POST: - if (f == SOCKET_SUCCESS) - socket_enter_listening(s); - else - socket_enter_stop_pre(s, f); - break; - - case SOCKET_STOP_PRE: - case SOCKET_STOP_PRE_SIGTERM: - case SOCKET_STOP_PRE_SIGKILL: - socket_enter_stop_post(s, f); - break; - - case SOCKET_STOP_POST: - case SOCKET_FINAL_SIGTERM: - case SOCKET_FINAL_SIGKILL: - socket_enter_dead(s, f); - break; - - default: - assert_not_reached("Uh, control process died at wrong time."); - } - } - - /* Notify clients about changed exit status */ - unit_add_to_dbus_queue(u); -} - -static void socket_timer_event(Unit *u, uint64_t elapsed, Watch *w) { - Socket *s = SOCKET(u); - - assert(s); - assert(elapsed == 1); - assert(w == &s->timer_watch); - - switch (s->state) { - - case SOCKET_START_PRE: - log_warning("%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); - socket_enter_stop_pre(s, SOCKET_FAILURE_TIMEOUT); - break; - - case SOCKET_STOP_PRE: - log_warning("%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); - socket_enter_signal(s, SOCKET_STOP_PRE_SIGKILL, SOCKET_FAILURE_TIMEOUT); - } else { - log_warning("%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); - socket_enter_stop_post(s, SOCKET_FAILURE_TIMEOUT); - break; - - case SOCKET_STOP_POST: - log_warning("%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); - socket_enter_signal(s, SOCKET_FINAL_SIGKILL, SOCKET_FAILURE_TIMEOUT); - } else { - log_warning("%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); - socket_enter_dead(s, SOCKET_FAILURE_TIMEOUT); - break; - - default: - assert_not_reached("Timeout at wrong time."); - } -} - -int socket_collect_fds(Socket *s, int **fds, unsigned *n_fds) { - int *rfds; - unsigned rn_fds, k; - SocketPort *p; - - assert(s); - assert(fds); - assert(n_fds); - - /* Called from the service code for requesting our fds */ - - rn_fds = 0; - LIST_FOREACH(port, p, s->ports) - if (p->fd >= 0) - rn_fds++; - - if (rn_fds <= 0) { - *fds = NULL; - *n_fds = 0; - return 0; - } - - if (!(rfds = new(int, rn_fds))) - return -ENOMEM; - - k = 0; - LIST_FOREACH(port, p, s->ports) - if (p->fd >= 0) - rfds[k++] = p->fd; - - assert(k == rn_fds); - - *fds = rfds; - *n_fds = rn_fds; - - return 0; -} - -void socket_notify_service_dead(Socket *s, bool failed_permanent) { - assert(s); - - /* The service is dead. Dang! - * - * This is strictly for one-instance-for-all-connections - * services. */ - - if (s->state == SOCKET_RUNNING) { - log_debug("%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 - socket_enter_listening(s); - } -} - -void socket_connection_unref(Socket *s) { - assert(s); - - /* The service is dead. Yay! - * - * This is strictly for one-instance-per-connection - * services. */ - - assert(s->n_connections > 0); - s->n_connections--; - - log_debug("%s: One connection closed, %u left.", UNIT(s)->id, s->n_connections); -} - -static void socket_reset_failed(Unit *u) { - Socket *s = SOCKET(u); - - assert(s); - - if (s->state == SOCKET_FAILED) - socket_set_state(s, SOCKET_DEAD); - - s->result = SOCKET_SUCCESS; -} - -static int socket_kill(Unit *u, KillWho who, int signo, DBusError *error) { - Socket *s = SOCKET(u); - int r = 0; - Set *pid_set = NULL; - - assert(s); - - if (who == KILL_MAIN) { - dbus_set_error(error, BUS_ERROR_NO_SUCH_PROCESS, "Socket units have no main processes"); - return -ESRCH; - } - - if (s->control_pid <= 0 && who == KILL_CONTROL) { - dbus_set_error(error, BUS_ERROR_NO_SUCH_PROCESS, "No control process to kill"); - return -ESRCH; - } - - if (who == KILL_CONTROL || who == KILL_ALL) - if (s->control_pid > 0) - if (kill(s->control_pid, signo) < 0) - r = -errno; - - if (who == KILL_ALL) { - int q; - - pid_set = set_new(trivial_hash_func, trivial_compare_func); - if (!pid_set) - return -ENOMEM; - - /* Exclude the control pid from being killed via the cgroup */ - if (s->control_pid > 0) { - q = set_put(pid_set, LONG_TO_PTR(s->control_pid)); - if (q < 0) { - r = q; - goto finish; - } - } - - q = cgroup_bonding_kill_list(UNIT(s)->cgroup_bondings, signo, false, false, pid_set, NULL); - if (q < 0 && q != -EAGAIN && q != -ESRCH && q != -ENOENT) - r = q; - } - -finish: - if (pid_set) - set_free(pid_set); - - return r; -} - -static const char* const socket_state_table[_SOCKET_STATE_MAX] = { - [SOCKET_DEAD] = "dead", - [SOCKET_START_PRE] = "start-pre", - [SOCKET_START_POST] = "start-post", - [SOCKET_LISTENING] = "listening", - [SOCKET_RUNNING] = "running", - [SOCKET_STOP_PRE] = "stop-pre", - [SOCKET_STOP_PRE_SIGTERM] = "stop-pre-sigterm", - [SOCKET_STOP_PRE_SIGKILL] = "stop-pre-sigkill", - [SOCKET_STOP_POST] = "stop-post", - [SOCKET_FINAL_SIGTERM] = "final-sigterm", - [SOCKET_FINAL_SIGKILL] = "final-sigkill", - [SOCKET_FAILED] = "failed" -}; - -DEFINE_STRING_TABLE_LOOKUP(socket_state, SocketState); - -static const char* const socket_exec_command_table[_SOCKET_EXEC_COMMAND_MAX] = { - [SOCKET_EXEC_START_PRE] = "StartPre", - [SOCKET_EXEC_START_POST] = "StartPost", - [SOCKET_EXEC_STOP_PRE] = "StopPre", - [SOCKET_EXEC_STOP_POST] = "StopPost" -}; - -DEFINE_STRING_TABLE_LOOKUP(socket_exec_command, SocketExecCommand); - -static const char* const socket_result_table[_SOCKET_RESULT_MAX] = { - [SOCKET_SUCCESS] = "success", - [SOCKET_FAILURE_RESOURCES] = "resources", - [SOCKET_FAILURE_TIMEOUT] = "timeout", - [SOCKET_FAILURE_EXIT_CODE] = "exit-code", - [SOCKET_FAILURE_SIGNAL] = "signal", - [SOCKET_FAILURE_CORE_DUMP] = "core-dump", - [SOCKET_FAILURE_SERVICE_FAILED_PERMANENT] = "service-failed-permanent" -}; - -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" - "Install\0", - - .init = socket_init, - .done = socket_done, - .load = socket_load, - - .kill = socket_kill, - - .coldplug = socket_coldplug, - - .dump = socket_dump, - - .start = socket_start, - .stop = socket_stop, - - .serialize = socket_serialize, - .deserialize_item = socket_deserialize_item, - - .active_state = socket_active_state, - .sub_state_to_string = socket_sub_state_to_string, - - .check_gc = socket_check_gc, - - .fd_event = socket_fd_event, - .sigchld_event = socket_sigchld_event, - .timer_event = socket_timer_event, - - .reset_failed = socket_reset_failed, - - .bus_interface = "org.freedesktop.systemd1.Socket", - .bus_message_handler = bus_socket_message_handler, - .bus_invalidating_properties = bus_socket_invalidating_properties, - - .status_message_formats = { - /*.starting_stopping = { - [0] = "Starting socket %s...", - [1] = "Stopping socket %s...", - },*/ - .finished_start_job = { - [JOB_DONE] = "Listening on %s.", - [JOB_FAILED] = "Failed to listen on %s.", - [JOB_DEPENDENCY] = "Dependency failed for %s.", - [JOB_TIMEOUT] = "Timed out starting %s.", - }, - .finished_stop_job = { - [JOB_DONE] = "Closed %s.", - [JOB_FAILED] = "Failed stopping %s.", - [JOB_TIMEOUT] = "Timed out stopping %s.", - }, - }, -}; diff --git a/src/core/socket.h b/src/core/socket.h deleted file mode 100644 index f099520dce..0000000000 --- a/src/core/socket.h +++ /dev/null @@ -1,175 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -#pragma once - -/*** - 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/>. -***/ - -typedef struct Socket Socket; - -#include "manager.h" -#include "unit.h" -#include "socket-util.h" -#include "mount.h" -#include "service.h" - -typedef enum SocketState { - SOCKET_DEAD, - SOCKET_START_PRE, - SOCKET_START_POST, - SOCKET_LISTENING, - SOCKET_RUNNING, - SOCKET_STOP_PRE, - SOCKET_STOP_PRE_SIGTERM, - SOCKET_STOP_PRE_SIGKILL, - SOCKET_STOP_POST, - SOCKET_FINAL_SIGTERM, - SOCKET_FINAL_SIGKILL, - SOCKET_FAILED, - _SOCKET_STATE_MAX, - _SOCKET_STATE_INVALID = -1 -} SocketState; - -typedef enum SocketExecCommand { - SOCKET_EXEC_START_PRE, - SOCKET_EXEC_START_POST, - SOCKET_EXEC_STOP_PRE, - SOCKET_EXEC_STOP_POST, - _SOCKET_EXEC_COMMAND_MAX, - _SOCKET_EXEC_COMMAND_INVALID = -1 -} SocketExecCommand; - -typedef enum SocketType { - SOCKET_SOCKET, - SOCKET_FIFO, - SOCKET_SPECIAL, - SOCKET_MQUEUE, - _SOCKET_FIFO_MAX, - _SOCKET_FIFO_INVALID = -1 -} SocketType; - -typedef enum SocketResult { - SOCKET_SUCCESS, - SOCKET_FAILURE_RESOURCES, - SOCKET_FAILURE_TIMEOUT, - SOCKET_FAILURE_EXIT_CODE, - SOCKET_FAILURE_SIGNAL, - SOCKET_FAILURE_CORE_DUMP, - SOCKET_FAILURE_SERVICE_FAILED_PERMANENT, - _SOCKET_RESULT_MAX, - _SOCKET_RESULT_INVALID = -1 -} SocketResult; - -typedef struct SocketPort { - SocketType type; - int fd; - - SocketAddress address; - char *path; - Watch fd_watch; - - LIST_FIELDS(struct SocketPort, port); -} SocketPort; - -struct Socket { - Unit meta; - - LIST_HEAD(SocketPort, ports); - - unsigned n_accepted; - unsigned n_connections; - unsigned max_connections; - - unsigned backlog; - usec_t timeout_usec; - - ExecCommand* exec_command[_SOCKET_EXEC_COMMAND_MAX]; - ExecContext exec_context; - KillContext kill_context; - - /* For Accept=no sockets refers to the one service we'll - activate. For Accept=yes sockets is either NULL, or filled - when the next service we spawn. */ - UnitRef service; - - SocketState state, deserialized_state; - - Watch timer_watch; - - ExecCommand* control_command; - SocketExecCommand control_command_id; - pid_t control_pid; - - mode_t directory_mode; - mode_t socket_mode; - - SocketResult result; - - bool accept; - - /* Socket options */ - bool keep_alive; - bool free_bind; - bool transparent; - bool broadcast; - bool pass_cred; - bool pass_sec; - int priority; - int mark; - size_t receive_buffer; - size_t send_buffer; - int ip_tos; - int ip_ttl; - size_t pipe_size; - char *bind_to_device; - char *tcp_congestion; - long mq_maxmsg; - long mq_msgsize; - - /* 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 */ -int socket_collect_fds(Socket *s, int **fds, unsigned *n_fds); - -/* Called from the service when it shut down */ -void socket_notify_service_dead(Socket *s, bool failed_permanent); - -/* Called from the mount code figure out if a mount is a dependency of - * any of the sockets of this socket */ -int socket_add_one_mount_link(Socket *s, Mount *m); - -/* Called from the service code when a per-connection service ended */ -void socket_connection_unref(Socket *s); - -extern const UnitVTable socket_vtable; - -const char* socket_state_to_string(SocketState i); -SocketState socket_state_from_string(const char *s); - -const char* socket_exec_command_to_string(SocketExecCommand i); -SocketExecCommand socket_exec_command_from_string(const char *s); - -const char* socket_result_to_string(SocketResult i); -SocketResult socket_result_from_string(const char *s); diff --git a/src/core/special.h b/src/core/special.h deleted file mode 100644 index ef72260ecd..0000000000 --- a/src/core/special.h +++ /dev/null @@ -1,113 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -#pragma once - -/*** - 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/>. -***/ - -#define SPECIAL_DEFAULT_TARGET "default.target" - -/* Shutdown targets */ -#define SPECIAL_UMOUNT_TARGET "umount.target" -/* This is not really intended to be started by directly. This is - * mostly so that other targets (reboot/halt/poweroff) can depend on - * it to bring all services down that want to be brought down on - * system shutdown. */ -#define SPECIAL_SHUTDOWN_TARGET "shutdown.target" -#define SPECIAL_HALT_TARGET "halt.target" -#define SPECIAL_POWEROFF_TARGET "poweroff.target" -#define SPECIAL_REBOOT_TARGET "reboot.target" -#define SPECIAL_KEXEC_TARGET "kexec.target" -#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" -#define SPECIAL_EMERGENCY_TARGET "emergency.target" - -/* Early boot targets */ -#define SPECIAL_SYSINIT_TARGET "sysinit.target" -#define SPECIAL_SOCKETS_TARGET "sockets.target" -#define SPECIAL_LOCAL_FS_TARGET "local-fs.target" /* LSB's $local_fs */ -#define SPECIAL_LOCAL_FS_PRE_TARGET "local-fs-pre.target" -#define SPECIAL_REMOTE_FS_TARGET "remote-fs.target" /* LSB's $remote_fs */ -#define SPECIAL_REMOTE_FS_PRE_TARGET "remote-fs-pre.target" -#define SPECIAL_SWAP_TARGET "swap.target" -#define SPECIAL_BASIC_TARGET "basic.target" - -/* LSB compatibility */ -#define SPECIAL_NETWORK_TARGET "network.target" /* LSB's $network */ -#define SPECIAL_NSS_LOOKUP_TARGET "nss-lookup.target" /* LSB's $named */ -#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" /* 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" -#define SPECIAL_QUOTACHECK_SERVICE "systemd-quotacheck.service" -#define SPECIAL_QUOTAON_SERVICE "quotaon.service" -#define SPECIAL_REMOUNT_FS_SERVICE "systemd-remount-fs.service" - -/* Services systemd relies on */ -#define SPECIAL_DBUS_SERVICE "dbus.service" -#define SPECIAL_DBUS_SOCKET "dbus.socket" -#define SPECIAL_JOURNALD_SOCKET "systemd-journald.socket" -#define SPECIAL_JOURNALD_SERVICE "systemd-journald.service" - -/* Magic init signals */ -#define SPECIAL_KBREQUEST_TARGET "kbrequest.target" -#define SPECIAL_SIGPWR_TARGET "sigpwr.target" -#define SPECIAL_CTRL_ALT_DEL_TARGET "ctrl-alt-del.target" - -/* For SysV compatibility. Usually an alias for a saner target. On - * SysV-free systems this doesn't exist. */ -#define SPECIAL_RUNLEVEL2_TARGET "runlevel2.target" -#define SPECIAL_RUNLEVEL3_TARGET "runlevel3.target" -#define SPECIAL_RUNLEVEL4_TARGET "runlevel4.target" -#define SPECIAL_RUNLEVEL5_TARGET "runlevel5.target" diff --git a/src/core/swap.c b/src/core/swap.c deleted file mode 100644 index 97145a9974..0000000000 --- a/src/core/swap.c +++ /dev/null @@ -1,1433 +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 <limits.h> -#include <unistd.h> -#include <fcntl.h> -#include <sys/epoll.h> -#include <sys/stat.h> -#include <sys/swap.h> -#include <libudev.h> - -#include "unit.h" -#include "swap.h" -#include "load-fragment.h" -#include "load-dropin.h" -#include "unit-name.h" -#include "dbus-swap.h" -#include "special.h" -#include "bus-errors.h" -#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, - [SWAP_ACTIVATING] = UNIT_ACTIVATING, - [SWAP_ACTIVE] = UNIT_ACTIVE, - [SWAP_DEACTIVATING] = UNIT_DEACTIVATING, - [SWAP_ACTIVATING_SIGTERM] = UNIT_DEACTIVATING, - [SWAP_ACTIVATING_SIGKILL] = UNIT_DEACTIVATING, - [SWAP_DEACTIVATING_SIGTERM] = UNIT_DEACTIVATING, - [SWAP_DEACTIVATING_SIGKILL] = UNIT_DEACTIVATING, - [SWAP_FAILED] = UNIT_FAILED -}; - -static void swap_unset_proc_swaps(Swap *s) { - Swap *first; - Hashmap *swaps; - - assert(s); - - if (!s->parameters_proc_swaps.what) - return; - - /* Remove this unit from the chain of swaps which share the - * same kernel swap device. */ - 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(swaps, - s->parameters_proc_swaps.what, - first->parameters_proc_swaps.what, - first); - else - hashmap_remove(swaps, s->parameters_proc_swaps.what); - - free(s->parameters_proc_swaps.what); - s->parameters_proc_swaps.what = NULL; -} - -static void swap_init(Unit *u) { - Swap *s = SWAP(u); - - assert(s); - assert(UNIT(s)->load_state == UNIT_STUB); - - s->timeout_usec = DEFAULT_TIMEOUT_USEC; - - exec_context_init(&s->exec_context); - s->exec_context.std_output = u->manager->default_std_output; - s->exec_context.std_error = u->manager->default_std_error; - kill_context_init(&s->kill_context); - - s->parameters_proc_swaps.priority = s->parameters_fragment.priority = -1; - - s->timer_watch.type = WATCH_INVALID; - - s->control_command_id = _SWAP_EXEC_COMMAND_INVALID; - - UNIT(s)->ignore_on_isolate = true; -} - -static void swap_unwatch_control_pid(Swap *s) { - assert(s); - - if (s->control_pid <= 0) - return; - - unit_unwatch_pid(UNIT(s), s->control_pid); - s->control_pid = 0; -} - -static void swap_done(Unit *u) { - Swap *s = SWAP(u); - - assert(s); - - swap_unset_proc_swaps(s); - - free(s->what); - s->what = NULL; - - free(s->parameters_fragment.what); - s->parameters_fragment.what = NULL; - - exec_context_done(&s->exec_context); - exec_command_done_array(s->exec_command, _SWAP_EXEC_COMMAND_MAX); - s->control_command = NULL; - - swap_unwatch_control_pid(s); - - unit_unwatch_timer(u, &s->timer_watch); -} - -int swap_add_one_mount_link(Swap *s, Mount *m) { - int r; - - assert(s); - assert(m); - - if (UNIT(s)->load_state != UNIT_LOADED || - UNIT(m)->load_state != UNIT_LOADED) - return 0; - - if (is_device_path(s->what)) - return 0; - - if (!path_startswith(s->what, m->where)) - return 0; - - r = unit_add_two_dependencies(UNIT(s), UNIT_AFTER, UNIT_REQUIRES, UNIT(m), true); - if (r < 0) - return r; - - return 0; -} - -static int swap_add_mount_links(Swap *s) { - Unit *other; - int r; - - assert(s); - - LIST_FOREACH(units_by_type, other, UNIT(s)->manager->units_by_type[UNIT_MOUNT]) - if ((r = swap_add_one_mount_link(s, MOUNT(other))) < 0) - return r; - - return 0; -} - -static int swap_add_device_links(Swap *s) { - SwapParameters *p; - - assert(s); - - if (!s->what) - return 0; - - if (s->from_fragment) - p = &s->parameters_fragment; - else - return 0; - - if (is_device_path(s->what)) - return unit_add_node_link(UNIT(s), s->what, - !p->noauto && p->nofail && - 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 - * writable file system. */ - return unit_add_dependency_by_name(UNIT(s), UNIT_AFTER, SPECIAL_REMOUNT_FS_SERVICE, NULL, true); -} - -static int swap_add_default_dependencies(Swap *s) { - int r; - - assert(s); - - 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); - if (r < 0) - return r; - - return 0; -} - -static int swap_verify(Swap *s) { - bool b; - char _cleanup_free_ *e = NULL; - - if (UNIT(s)->load_state != UNIT_LOADED) - return 0; - - e = unit_name_from_path(s->what, ".swap"); - if (e == NULL) - return log_oom(); - - b = unit_has_name(UNIT(s), e); - if (!b) { - log_error("%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 to load.", UNIT(s)->id); - return -EINVAL; - } - - return 0; -} - -static int swap_load(Unit *u) { - int r; - Swap *s = SWAP(u); - - assert(s); - assert(u->load_state == UNIT_STUB); - - /* Load a .swap file */ - r = unit_load_fragment_and_dropin_optional(u); - if (r < 0) - return r; - - if (u->load_state == UNIT_LOADED) { - r = unit_add_exec_dependencies(u, &s->exec_context); - if (r < 0) - return r; - - if (UNIT(s)->fragment_path) - s->from_fragment = true; - - if (!s->what) { - if (s->parameters_fragment.what) - s->what = strdup(s->parameters_fragment.what); - else if (s->parameters_proc_swaps.what) - s->what = strdup(s->parameters_proc_swaps.what); - else - s->what = unit_name_to_path(u->id); - - if (!s->what) - return -ENOMEM; - } - - path_kill_slashes(s->what); - - if (!UNIT(s)->description) - if ((r = unit_set_description(u, s->what)) < 0) - return r; - - r = swap_add_device_links(s); - if (r < 0) - return r; - - r = swap_add_mount_links(s); - if (r < 0) - return r; - - r = unit_add_default_cgroups(u); - if (r < 0) - return r; - - 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) - return r; - } - - return swap_verify(s); -} - -static int swap_add_one( - Manager *m, - const char *what, - const char *what_proc_swaps, - int priority, - bool noauto, - bool nofail, - bool set_flags) { - - Unit *u = NULL; - char _cleanup_free_ *e = NULL; - char *wp = NULL; - bool delete = false; - int r; - SwapParameters *p; - Swap *first; - - assert(m); - assert(what); - assert(what_proc_swaps); - - e = unit_name_from_path(what, ".swap"); - if (!e) - return log_oom(); - - u = manager_get_unit(m, e); - - if (u && - SWAP(u)->from_proc_swaps && - !path_equal(SWAP(u)->parameters_proc_swaps.what, what_proc_swaps)) - return -EEXIST; - - if (!u) { - delete = true; - - u = unit_new(m, sizeof(Swap)); - if (!u) - return log_oom(); - - r = unit_add_name(u, e); - if (r < 0) - goto fail; - - SWAP(u)->what = strdup(what); - if (!SWAP(u)->what) { - r = log_oom(); - goto fail; - } - - unit_add_to_load_queue(u); - } else - delete = false; - - p = &SWAP(u)->parameters_proc_swaps; - - if (!p->what) { - wp = strdup(what_proc_swaps); - if (!wp) { - r = log_oom(); - goto fail; - } - - 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; - - first = hashmap_get(m->swaps_by_proc_swaps, wp); - LIST_PREPEND(Swap, same_proc_swaps, first, SWAP(u)); - - r = hashmap_replace(m->swaps_by_proc_swaps, wp, first); - if (r < 0) - goto fail; - } - - if (set_flags) { - SWAP(u)->is_active = true; - SWAP(u)->just_activated = !SWAP(u)->from_proc_swaps; - } - - SWAP(u)->from_proc_swaps = true; - - p->priority = priority; - p->noauto = noauto; - p->nofail = nofail; - - unit_add_to_dbus_queue(u); - - return 0; - -fail: - log_warning("Failed to load swap unit: %s", strerror(-r)); - - free(wp); - - if (delete && u) - unit_free(u); - - return r; -} - -static int swap_process_new_swap(Manager *m, const char *device, int prio, bool set_flags) { - struct stat st; - int r = 0, k; - - assert(m); - - if (stat(device, &st) >= 0 && S_ISBLK(st.st_mode)) { - struct udev_device *d; - const char *dn; - struct udev_list_entry *item = NULL, *first = NULL; - - /* So this is a proper swap device. Create swap units - * for all names this swap device is known under */ - - d = udev_device_new_from_devnum(m->udev, 'b', st.st_rdev); - if (!d) - return log_oom(); - - dn = udev_device_get_devnode(d); - /* 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 */ - first = udev_device_get_devlinks_list_entry(d); - udev_list_entry_foreach(item, first) { - const char *p; - - /* Don't bother with the /dev/block links */ - p = udev_list_entry_get_name(item); - - if (path_startswith(p, "/dev/block/")) - continue; - - if (stat(p, &st) >= 0) - 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); - if (k < 0) - r = k; - } - - udev_device_unref(d); - } - - k = swap_add_one(m, device, device, prio, false, false, set_flags); - if (k < 0) - r = k; - - return r; -} - -static void swap_set_state(Swap *s, SwapState state) { - SwapState old_state; - - assert(s); - - old_state = s->state; - s->state = state; - - if (state != SWAP_ACTIVATING && - state != SWAP_ACTIVATING_SIGTERM && - state != SWAP_ACTIVATING_SIGKILL && - state != SWAP_DEACTIVATING && - state != SWAP_DEACTIVATING_SIGTERM && - state != SWAP_DEACTIVATING_SIGKILL) { - unit_unwatch_timer(UNIT(s), &s->timer_watch); - swap_unwatch_control_pid(s); - s->control_command = NULL; - s->control_command_id = _SWAP_EXEC_COMMAND_INVALID; - } - - 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); -} - -static int swap_coldplug(Unit *u) { - Swap *s = SWAP(u); - SwapState new_state = SWAP_DEAD; - int r; - - assert(s); - assert(s->state == SWAP_DEAD); - - if (s->deserialized_state != s->state) - new_state = s->deserialized_state; - else if (s->from_proc_swaps) - new_state = SWAP_ACTIVE; - - if (new_state != s->state) { - - if (new_state == SWAP_ACTIVATING || - new_state == SWAP_ACTIVATING_SIGTERM || - new_state == SWAP_ACTIVATING_SIGKILL || - new_state == SWAP_DEACTIVATING || - new_state == SWAP_DEACTIVATING_SIGTERM || - new_state == SWAP_DEACTIVATING_SIGKILL) { - - if (s->control_pid <= 0) - return -EBADMSG; - - r = unit_watch_pid(UNIT(s), s->control_pid); - if (r < 0) - return r; - - r = unit_watch_timer(UNIT(s), s->timeout_usec, &s->timer_watch); - if (r < 0) - return r; - } - - swap_set_state(s, new_state); - } - - return 0; -} - -static void swap_dump(Unit *u, FILE *f, const char *prefix) { - Swap *s = SWAP(u); - SwapParameters *p; - - assert(s); - assert(f); - - if (s->from_proc_swaps) - p = &s->parameters_proc_swaps; - else if (s->from_fragment) - p = &s->parameters_fragment; - else - p = NULL; - - fprintf(f, - "%sSwap State: %s\n" - "%sResult: %s\n" - "%sWhat: %s\n" - "%sFrom /proc/swaps: %s\n" - "%sFrom fragment: %s\n", - prefix, swap_state_to_string(s->state), - prefix, swap_result_to_string(s->result), - prefix, s->what, - prefix, yes_no(s->from_proc_swaps), - prefix, yes_no(s->from_fragment)); - - if (p) - fprintf(f, - "%sPriority: %i\n" - "%sNoAuto: %s\n" - "%sNoFail: %s\n", - prefix, p->priority, - prefix, yes_no(p->noauto), - prefix, yes_no(p->nofail)); - - if (s->control_pid > 0) - fprintf(f, - "%sControl PID: %lu\n", - prefix, (unsigned long) s->control_pid); - - exec_context_dump(&s->exec_context, f, prefix); - kill_context_dump(&s->kill_context, f, prefix); -} - -static int swap_spawn(Swap *s, ExecCommand *c, pid_t *_pid) { - pid_t pid; - int r; - - assert(s); - assert(c); - assert(_pid); - - r = unit_watch_timer(UNIT(s), s->timeout_usec, &s->timer_watch); - if (r < 0) - goto fail; - - 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; - - r = unit_watch_pid(UNIT(s), pid); - if (r < 0) - /* FIXME: we need to do something here */ - goto fail; - - *_pid = pid; - - return 0; - -fail: - unit_unwatch_timer(UNIT(s), &s->timer_watch); - - return r; -} - -static void swap_enter_dead(Swap *s, SwapResult f) { - assert(s); - - if (f != SWAP_SUCCESS) - s->result = f; - - swap_set_state(s, s->result != SWAP_SUCCESS ? SWAP_FAILED : SWAP_DEAD); -} - -static void swap_enter_active(Swap *s, SwapResult f) { - assert(s); - - if (f != SWAP_SUCCESS) - s->result = f; - - swap_set_state(s, SWAP_ACTIVE); -} - -static void swap_enter_signal(Swap *s, SwapState state, SwapResult f) { - int r; - Set *pid_set = NULL; - bool wait_for_exit = false; - - assert(s); - - if (f != SWAP_SUCCESS) - s->result = f; - - if (s->kill_context.kill_mode != KILL_NONE) { - int sig = (state == SWAP_ACTIVATING_SIGTERM || - state == SWAP_DEACTIVATING_SIGTERM) ? s->kill_context.kill_signal : SIGKILL; - - 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); - else - wait_for_exit = true; - } - - if (s->kill_context.kill_mode == KILL_CONTROL_GROUP) { - - 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) { - 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)); - } else if (r > 0) - wait_for_exit = true; - - set_free(pid_set); - pid_set = NULL; - } - } - - if (wait_for_exit) { - r = unit_watch_timer(UNIT(s), s->timeout_usec, &s->timer_watch); - if (r < 0) - goto fail; - - swap_set_state(s, state); - } else - swap_enter_dead(s, SWAP_SUCCESS); - - return; - -fail: - log_warning("%s failed to kill processes: %s", UNIT(s)->id, strerror(-r)); - - swap_enter_dead(s, SWAP_FAILURE_RESOURCES); - - if (pid_set) - set_free(pid_set); -} - -static void swap_enter_activating(Swap *s) { - int r, priority; - - assert(s); - - s->control_command_id = SWAP_EXEC_ACTIVATE; - s->control_command = s->exec_command + SWAP_EXEC_ACTIVATE; - - if (s->from_fragment) - priority = s->parameters_fragment.priority; - else - priority = -1; - - if (priority >= 0) { - char p[LINE_MAX]; - - snprintf(p, sizeof(p), "%i", priority); - char_array_0(p); - - r = exec_command_set( - s->control_command, - "/sbin/swapon", - "-p", - p, - s->what, - NULL); - } else - r = exec_command_set( - s->control_command, - "/sbin/swapon", - s->what, - NULL); - - if (r < 0) - goto fail; - - swap_unwatch_control_pid(s); - - r = swap_spawn(s, s->control_command, &s->control_pid); - if (r < 0) - goto fail; - - swap_set_state(s, SWAP_ACTIVATING); - - return; - -fail: - log_warning("%s failed to run 'swapon' task: %s", UNIT(s)->id, strerror(-r)); - swap_enter_dead(s, SWAP_FAILURE_RESOURCES); -} - -static void swap_enter_deactivating(Swap *s) { - int r; - - assert(s); - - s->control_command_id = SWAP_EXEC_DEACTIVATE; - s->control_command = s->exec_command + SWAP_EXEC_DEACTIVATE; - - r = exec_command_set(s->control_command, - "/sbin/swapoff", - s->what, - NULL); - if (r < 0) - goto fail; - - swap_unwatch_control_pid(s); - - r = swap_spawn(s, s->control_command, &s->control_pid); - if (r < 0) - goto fail; - - swap_set_state(s, SWAP_DEACTIVATING); - - return; - -fail: - log_warning("%s failed to run 'swapoff' task: %s", UNIT(s)->id, strerror(-r)); - swap_enter_active(s, SWAP_FAILURE_RESOURCES); -} - -static int swap_start(Unit *u) { - Swap *s = SWAP(u); - - assert(s); - - /* We cannot fulfill this request right now, try again later - * please! */ - - if (s->state == SWAP_DEACTIVATING || - s->state == SWAP_DEACTIVATING_SIGTERM || - s->state == SWAP_DEACTIVATING_SIGKILL || - s->state == SWAP_ACTIVATING_SIGTERM || - s->state == SWAP_ACTIVATING_SIGKILL) - return -EAGAIN; - - if (s->state == SWAP_ACTIVATING) - return 0; - - 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; -} - -static int swap_stop(Unit *u) { - Swap *s = SWAP(u); - - assert(s); - - if (s->state == SWAP_DEACTIVATING || - s->state == SWAP_DEACTIVATING_SIGTERM || - s->state == SWAP_DEACTIVATING_SIGKILL || - s->state == SWAP_ACTIVATING_SIGTERM || - s->state == SWAP_ACTIVATING_SIGKILL) - return 0; - - assert(s->state == SWAP_ACTIVATING || - s->state == SWAP_ACTIVE); - - if (detect_container(NULL) > 0) - return -EPERM; - - swap_enter_deactivating(s); - return 0; -} - -static int swap_serialize(Unit *u, FILE *f, FDSet *fds) { - Swap *s = SWAP(u); - - assert(s); - assert(f); - assert(fds); - - unit_serialize_item(u, f, "state", swap_state_to_string(s->state)); - unit_serialize_item(u, f, "result", swap_result_to_string(s->result)); - - if (s->control_pid > 0) - unit_serialize_item_format(u, f, "control-pid", "%lu", (unsigned long) s->control_pid); - - if (s->control_command_id >= 0) - unit_serialize_item(u, f, "control-command", swap_exec_command_to_string(s->control_command_id)); - - return 0; -} - -static int swap_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) { - Swap *s = SWAP(u); - - assert(s); - assert(fds); - - if (streq(key, "state")) { - SwapState state; - - state = swap_state_from_string(value); - if (state < 0) - log_debug("Failed to parse state value %s", value); - else - s->deserialized_state = state; - } else if (streq(key, "result")) { - SwapResult f; - - f = swap_result_from_string(value); - if (f < 0) - log_debug("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); - else - s->control_pid = pid; - - } else if (streq(key, "control-command")) { - SwapExecCommand id; - - id = swap_exec_command_from_string(value); - if (id < 0) - log_debug("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); - - return 0; -} - -static UnitActiveState swap_active_state(Unit *u) { - assert(u); - - return state_translation_table[SWAP(u)->state]; -} - -static const char *swap_sub_state_to_string(Unit *u) { - assert(u); - - return swap_state_to_string(SWAP(u)->state); -} - -static bool swap_check_gc(Unit *u) { - Swap *s = SWAP(u); - - assert(s); - - return s->from_proc_swaps; -} - -static void swap_sigchld_event(Unit *u, pid_t pid, int code, int status) { - Swap *s = SWAP(u); - SwapResult f; - - assert(s); - assert(pid >= 0); - - if (pid != s->control_pid) - return; - - s->control_pid = 0; - - if (is_clean_exit(code, status, NULL)) - f = SWAP_SUCCESS; - else if (code == CLD_EXITED) - f = SWAP_FAILURE_EXIT_CODE; - else if (code == CLD_KILLED) - f = SWAP_FAILURE_SIGNAL; - else if (code == CLD_DUMPED) - f = SWAP_FAILURE_CORE_DUMP; - else - assert_not_reached("Unknown code"); - - if (f != SWAP_SUCCESS) - s->result = f; - - if (s->control_command) { - exec_status_exit(&s->control_command->exec_status, &s->exec_context, pid, code, status); - - s->control_command = NULL; - 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); - - switch (s->state) { - - case SWAP_ACTIVATING: - case SWAP_ACTIVATING_SIGTERM: - case SWAP_ACTIVATING_SIGKILL: - - if (f == SWAP_SUCCESS) - swap_enter_active(s, f); - else - swap_enter_dead(s, f); - break; - - case SWAP_DEACTIVATING: - case SWAP_DEACTIVATING_SIGKILL: - case SWAP_DEACTIVATING_SIGTERM: - - if (f == SWAP_SUCCESS) - swap_enter_dead(s, f); - else - swap_enter_dead(s, f); - break; - - default: - assert_not_reached("Uh, control process died at wrong time."); - } - - /* Notify clients about changed exit status */ - unit_add_to_dbus_queue(u); - - /* Request a reload of /proc/swaps, so that following units - * can follow our state change */ - u->manager->request_reload = true; -} - -static void swap_timer_event(Unit *u, uint64_t elapsed, Watch *w) { - Swap *s = SWAP(u); - - assert(s); - assert(elapsed == 1); - assert(w == &s->timer_watch); - - switch (s->state) { - - case SWAP_ACTIVATING: - log_warning("%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); - 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); - swap_enter_signal(s, SWAP_ACTIVATING_SIGKILL, SWAP_FAILURE_TIMEOUT); - } else { - log_warning("%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); - swap_enter_signal(s, SWAP_DEACTIVATING_SIGKILL, SWAP_FAILURE_TIMEOUT); - } else { - log_warning("%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); - swap_enter_dead(s, SWAP_FAILURE_TIMEOUT); - break; - - default: - assert_not_reached("Timeout at wrong time."); - } -} - -static int swap_load_proc_swaps(Manager *m, bool set_flags) { - unsigned i; - int r = 0; - - assert(m); - - rewind(m->proc_swaps); - - (void) fscanf(m->proc_swaps, "%*s %*s %*s %*s %*s\n"); - - for (i = 1;; i++) { - char *dev = NULL, *d; - int prio = 0, k; - - 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); - free(dev); - continue; - } - - d = cunescape(dev); - free(dev); - - if (!d) - return -ENOMEM; - - k = swap_process_new_swap(m, d, prio, set_flags); - free(d); - - if (k < 0) - r = k; - } - - return r; -} - -int swap_dispatch_reload(Manager *m) { - /* This function should go as soon as the kernel properly notifies us */ - - if (_likely_(!m->request_reload)) - return 0; - - m->request_reload = false; - - return swap_fd_event(m, EPOLLPRI); -} - -int swap_fd_event(Manager *m, int events) { - Unit *u; - int r; - - assert(m); - assert(events & EPOLLPRI); - - 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 */ - LIST_FOREACH(units_by_type, u, m->units_by_type[UNIT_SWAP]) { - Swap *swap = SWAP(u); - - swap->is_active = swap->just_activated = false; - } - - return 0; - } - - manager_dispatch_load_queue(m); - - LIST_FOREACH(units_by_type, u, m->units_by_type[UNIT_SWAP]) { - Swap *swap = SWAP(u); - - if (!swap->is_active) { - /* This has just been deactivated */ - - swap->from_proc_swaps = false; - swap_unset_proc_swaps(swap); - - switch (swap->state) { - - case SWAP_ACTIVE: - swap_enter_dead(swap, SWAP_SUCCESS); - break; - - default: - swap_set_state(swap, swap->state); - break; - } - - } else if (swap->just_activated) { - - /* New swap entry */ - - switch (swap->state) { - - case SWAP_DEAD: - case SWAP_FAILED: - swap_enter_active(swap, SWAP_SUCCESS); - break; - - default: - /* Nothing really changed, but let's - * issue an notification call - * nonetheless, in case somebody is - * waiting for this. */ - swap_set_state(swap, swap->state); - break; - } - } - - /* Reset the flags for later calls */ - swap->is_active = swap->just_activated = false; - } - - return 1; -} - -static Unit *swap_following(Unit *u) { - Swap *s = SWAP(u); - Swap *other, *first = NULL; - - assert(s); - - if (streq_ptr(s->what, s->parameters_proc_swaps.what)) - return NULL; - - /* Make everybody follow the unit that's named after the swap - * device in the kernel */ - - LIST_FOREACH_AFTER(same_proc_swaps, other, s) - if (streq_ptr(other->what, other->parameters_proc_swaps.what)) - return UNIT(other); - - LIST_FOREACH_BEFORE(same_proc_swaps, other, s) { - if (streq_ptr(other->what, other->parameters_proc_swaps.what)) - return UNIT(other); - - first = other; - } - - return UNIT(first); -} - -static int swap_following_set(Unit *u, Set **_set) { - Swap *s = SWAP(u); - Swap *other; - Set *set; - int r; - - assert(s); - assert(_set); - - if (LIST_JUST_US(same_proc_swaps, s)) { - *_set = NULL; - return 0; - } - - if (!(set = set_new(NULL, NULL))) - return -ENOMEM; - - LIST_FOREACH_AFTER(same_proc_swaps, other, s) - if ((r = set_put(set, other)) < 0) - goto fail; - - LIST_FOREACH_BEFORE(same_proc_swaps, other, s) - if ((r = set_put(set, other)) < 0) - goto fail; - - *_set = set; - return 1; - -fail: - set_free(set); - return r; -} - -static void swap_shutdown(Manager *m) { - assert(m); - - if (m->proc_swaps) { - fclose(m->proc_swaps); - m->proc_swaps = NULL; - } - - hashmap_free(m->swaps_by_proc_swaps); - m->swaps_by_proc_swaps = NULL; -} - -static int swap_enumerate(Manager *m) { - int r; - struct epoll_event ev; - assert(m); - - if (!m->proc_swaps) { - m->proc_swaps = fopen("/proc/swaps", "re"); - if (!m->proc_swaps) - return (errno == ENOENT) ? 0 : -errno; - - m->swap_watch.type = WATCH_SWAP; - m->swap_watch.fd = fileno(m->proc_swaps); - - zero(ev); - ev.events = EPOLLPRI; - ev.data.ptr = &m->swap_watch; - - if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->swap_watch.fd, &ev) < 0) - return -errno; - } - - r = swap_load_proc_swaps(m, false); - if (r < 0) - swap_shutdown(m); - - return r; -} - -static void swap_reset_failed(Unit *u) { - Swap *s = SWAP(u); - - assert(s); - - if (s->state == SWAP_FAILED) - swap_set_state(s, SWAP_DEAD); - - s->result = SWAP_SUCCESS; -} - -static int swap_kill(Unit *u, KillWho who, int signo, DBusError *error) { - Swap *s = SWAP(u); - int r = 0; - Set *pid_set = NULL; - - assert(s); - - if (who == KILL_MAIN) { - dbus_set_error(error, BUS_ERROR_NO_SUCH_PROCESS, "Swap units have no main processes"); - return -ESRCH; - } - - if (s->control_pid <= 0 && who == KILL_CONTROL) { - dbus_set_error(error, BUS_ERROR_NO_SUCH_PROCESS, "No control process to kill"); - return -ESRCH; - } - - if (who == KILL_CONTROL || who == KILL_ALL) - if (s->control_pid > 0) - if (kill(s->control_pid, signo) < 0) - r = -errno; - - if (who == KILL_ALL) { - int q; - - pid_set = set_new(trivial_hash_func, trivial_compare_func); - if (!pid_set) - return -ENOMEM; - - /* Exclude the control pid from being killed via the cgroup */ - if (s->control_pid > 0) { - q = set_put(pid_set, LONG_TO_PTR(s->control_pid)); - if (q < 0) { - r = q; - goto finish; - } - } - - q = cgroup_bonding_kill_list(UNIT(s)->cgroup_bondings, signo, false, false, pid_set, NULL); - if (q < 0 && q != -EAGAIN && q != -ESRCH && q != -ENOENT) - r = q; - } - -finish: - if (pid_set) - set_free(pid_set); - - return r; -} - -static const char* const swap_state_table[_SWAP_STATE_MAX] = { - [SWAP_DEAD] = "dead", - [SWAP_ACTIVATING] = "activating", - [SWAP_ACTIVE] = "active", - [SWAP_DEACTIVATING] = "deactivating", - [SWAP_ACTIVATING_SIGTERM] = "activating-sigterm", - [SWAP_ACTIVATING_SIGKILL] = "activating-sigkill", - [SWAP_DEACTIVATING_SIGTERM] = "deactivating-sigterm", - [SWAP_DEACTIVATING_SIGKILL] = "deactivating-sigkill", - [SWAP_FAILED] = "failed" -}; - -DEFINE_STRING_TABLE_LOOKUP(swap_state, SwapState); - -static const char* const swap_exec_command_table[_SWAP_EXEC_COMMAND_MAX] = { - [SWAP_EXEC_ACTIVATE] = "ExecActivate", - [SWAP_EXEC_DEACTIVATE] = "ExecDeactivate", -}; - -DEFINE_STRING_TABLE_LOOKUP(swap_exec_command, SwapExecCommand); - -static const char* const swap_result_table[_SWAP_RESULT_MAX] = { - [SWAP_SUCCESS] = "success", - [SWAP_FAILURE_RESOURCES] = "resources", - [SWAP_FAILURE_TIMEOUT] = "timeout", - [SWAP_FAILURE_EXIT_CODE] = "exit-code", - [SWAP_FAILURE_SIGNAL] = "signal", - [SWAP_FAILURE_CORE_DUMP] = "core-dump" -}; - -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" - "Install\0", - - .no_alias = true, - .no_instances = true, - - .init = swap_init, - .load = swap_load, - .done = swap_done, - - .coldplug = swap_coldplug, - - .dump = swap_dump, - - .start = swap_start, - .stop = swap_stop, - - .kill = swap_kill, - - .serialize = swap_serialize, - .deserialize_item = swap_deserialize_item, - - .active_state = swap_active_state, - .sub_state_to_string = swap_sub_state_to_string, - - .check_gc = swap_check_gc, - - .sigchld_event = swap_sigchld_event, - .timer_event = swap_timer_event, - - .reset_failed = swap_reset_failed, - - .bus_interface = "org.freedesktop.systemd1.Swap", - .bus_message_handler = bus_swap_message_handler, - .bus_invalidating_properties = bus_swap_invalidating_properties, - - .following = swap_following, - .following_set = swap_following_set, - - .enumerate = swap_enumerate, - .shutdown = swap_shutdown, - - .status_message_formats = { - .starting_stopping = { - [0] = "Activating swap %s...", - [1] = "Deactivating swap %s...", - }, - .finished_start_job = { - [JOB_DONE] = "Activated swap %s.", - [JOB_FAILED] = "Failed to activate swap %s.", - [JOB_DEPENDENCY] = "Dependency failed for %s.", - [JOB_TIMEOUT] = "Timed out activating swap %s.", - }, - .finished_stop_job = { - [JOB_DONE] = "Deactivated swap %s.", - [JOB_FAILED] = "Failed deactivating swap %s.", - [JOB_TIMEOUT] = "Timed out deactivating swap %s.", - }, - }, -}; diff --git a/src/core/swap.h b/src/core/swap.h deleted file mode 100644 index 35d47fd46f..0000000000 --- a/src/core/swap.h +++ /dev/null @@ -1,121 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -#pragma once - -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - Copyright 2010 Maarten Lankhorst - - 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/>. -***/ - -typedef struct Swap Swap; - -#include "unit.h" - -typedef enum SwapState { - SWAP_DEAD, - SWAP_ACTIVATING, - SWAP_ACTIVE, - SWAP_DEACTIVATING, - SWAP_ACTIVATING_SIGTERM, - SWAP_ACTIVATING_SIGKILL, - SWAP_DEACTIVATING_SIGTERM, - SWAP_DEACTIVATING_SIGKILL, - SWAP_FAILED, - _SWAP_STATE_MAX, - _SWAP_STATE_INVALID = -1 -} SwapState; - -typedef enum SwapExecCommand { - SWAP_EXEC_ACTIVATE, - SWAP_EXEC_DEACTIVATE, - _SWAP_EXEC_COMMAND_MAX, - _SWAP_EXEC_COMMAND_INVALID = -1 -} SwapExecCommand; - -typedef struct SwapParameters { - char *what; - int priority; - bool noauto:1; - bool nofail:1; -} SwapParameters; - -typedef enum SwapResult { - SWAP_SUCCESS, - SWAP_FAILURE_RESOURCES, - SWAP_FAILURE_TIMEOUT, - SWAP_FAILURE_EXIT_CODE, - SWAP_FAILURE_SIGNAL, - SWAP_FAILURE_CORE_DUMP, - _SWAP_RESULT_MAX, - _SWAP_RESULT_INVALID = -1 -} SwapResult; - -struct Swap { - Unit meta; - - char *what; - - SwapParameters parameters_proc_swaps; - SwapParameters parameters_fragment; - - bool from_proc_swaps:1; - bool from_fragment:1; - - /* Used while looking for swaps that vanished or got added - * from/to /proc/swaps */ - bool is_active:1; - bool just_activated:1; - - SwapResult result; - - usec_t timeout_usec; - - ExecCommand exec_command[_SWAP_EXEC_COMMAND_MAX]; - ExecContext exec_context; - KillContext kill_context; - - SwapState state, deserialized_state; - - ExecCommand* control_command; - SwapExecCommand control_command_id; - pid_t control_pid; - - Watch timer_watch; - - /* In order to be able to distinguish dependencies on - different device nodes we might end up creating multiple - devices for the same swap. We chain them up here. */ - - LIST_FIELDS(struct Swap, same_proc_swaps); -}; - -extern const UnitVTable swap_vtable; - -int swap_add_one_mount_link(Swap *s, Mount *m); - -int swap_dispatch_reload(Manager *m); -int swap_fd_event(Manager *m, int events); - -const char* swap_state_to_string(SwapState i); -SwapState swap_state_from_string(const char *s); - -const char* swap_exec_command_to_string(SwapExecCommand i); -SwapExecCommand swap_exec_command_from_string(const char *s); - -const char* swap_result_to_string(SwapResult i); -SwapResult swap_result_from_string(const char *s); diff --git a/src/core/switch-root.c b/src/core/switch-root.c deleted file mode 100644 index 150332a858..0000000000 --- a/src/core/switch-root.c +++ /dev/null @@ -1,142 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -/*** - This file is part of systemd. - - Copyright 2012 Harald Hoyer, 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 <sys/stat.h> -#include <stdbool.h> -#include <errno.h> -#include <string.h> -#include <sys/mount.h> -#include <unistd.h> -#include <fcntl.h> - -#include "util.h" -#include "path-util.h" -#include "switch-root.h" - -int switch_root(const char *new_root) { - - /* Don't try to unmount/move the old "/", there's no way to do it. */ - static const char move_mounts[] = - "/dev\0" - "/proc\0" - "/sys\0" - "/run\0"; - - int r, old_root_fd = -1; - struct stat new_root_stat; - bool old_root_remove; - const char *i; - - if (path_equal(new_root, "/")) - return 0; - - old_root_remove = in_initrd(); - - if (stat(new_root, &new_root_stat) < 0) { - r = -errno; - log_error("Failed to stat directory %s: %m", 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; - - snprintf(new_mount, sizeof(new_mount), "%s%s", new_root, i); - char_array_0(new_mount); - - if ((stat(new_mount, &sb) < 0) || - sb.st_dev != new_root_stat.st_dev) { - - /* Mount point seems to be mounted already or - * stat failed. Unmount the old mount - * point. */ - if (umount2(i, MNT_DETACH) < 0) - log_warning("Failed to unmount %s: %m", i); - continue; - } - - if (mount(i, new_mount, NULL, MS_MOVE, NULL) < 0) { - log_error("Failed to move mount %s to %s, forcing unmount: %m", i, new_mount); - - if (umount2(i, MNT_FORCE) < 0) - log_warning("Failed to unmount %s: %m", i); - } - } - - if (chdir(new_root) < 0) { - r = -errno; - log_error("Failed to change directory to %s: %m", new_root); - goto fail; - } - - if (old_root_remove) { - old_root_fd = open("/", O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_NOCTTY|O_DIRECTORY); - if (old_root_fd < 0) - log_warning("Failed to open root directory: %m"); - } - - if (mount(new_root, "/", NULL, MS_MOVE, NULL) < 0) { - r = -errno; - log_error("Failed to mount moving %s to /: %m", new_root); - goto fail; - } - - if (chroot(".") < 0) { - r = -errno; - log_error("Failed to change root: %m"); - goto fail; - } - - if (chdir("/") < 0) { - r = -errno; - log_error("Failed to change directory: %m"); - goto fail; - } - - if (old_root_fd >= 0) { - struct stat rb; - - if (fstat(old_root_fd, &rb) < 0) - log_warning("Failed to stat old root directory, leaving: %m"); - else { - rm_rf_children(old_root_fd, false, false, &rb); - old_root_fd = -1; - } - } - - r = 0; - -fail: - if (old_root_fd >= 0) - close_nointr_nofail(old_root_fd); - - return r; -} diff --git a/src/core/switch-root.h b/src/core/switch-root.h deleted file mode 100644 index 0c4cd1e403..0000000000 --- a/src/core/switch-root.h +++ /dev/null @@ -1,27 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -#ifndef fooswitchroothfoo -#define fooswitchroothfoo - -/*** - This file is part of systemd. - - Copyright 2012 Harald Hoyer, 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 switch_root(const char *switch_root); - -#endif diff --git a/src/core/syscall-list.c b/src/core/syscall-list.c deleted file mode 100644 index 05fad3e158..0000000000 --- a/src/core/syscall-list.c +++ /dev/null @@ -1,55 +0,0 @@ -/*-*- 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 <sys/syscall.h> -#include <string.h> - -#include "util.h" - -#include "syscall-list.h" - -const struct syscall_name *lookup_syscall(register const char *str, register unsigned int len); - -#include "syscall-to-name.h" -#include "syscall-from-name.h" - -const char *syscall_to_name(int id) { - if (id < 0 || id >= (int) ELEMENTSOF(syscall_names)) - return NULL; - - return syscall_names[id]; -} - -int syscall_from_name(const char *name) { - const struct syscall_name *sc; - - assert(name); - - sc = lookup_syscall(name, strlen(name)); - if (!sc) - return -1; - - return sc->id; -} - -int syscall_max(void) { - return ELEMENTSOF(syscall_names); -} diff --git a/src/core/syscall-list.h b/src/core/syscall-list.h deleted file mode 100644 index 0fc6859605..0000000000 --- a/src/core/syscall-list.h +++ /dev/null @@ -1,30 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -#ifndef foosyscalllisthfoo -#define foosyscalllisthfoo - -/*** - 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/>. -***/ - -const char *syscall_to_name(int id); -int syscall_from_name(const char *name); - -int syscall_max(void); - -#endif diff --git a/src/core/sysfs-show.h b/src/core/sysfs-show.h deleted file mode 100644 index 9ffd129c48..0000000000 --- a/src/core/sysfs-show.h +++ /dev/null @@ -1,24 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -#pragma once - -/*** - This file is part of systemd. - - Copyright 2011 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 show_sysfs(const char *seat, const char *prefix, unsigned columns); diff --git a/src/core/system.conf b/src/core/system.conf deleted file mode 100644 index 68076d9735..0000000000 --- a/src/core/system.conf +++ /dev/null @@ -1,43 +0,0 @@ -# This file is part of systemd. -# -# 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. -# -# See systemd.conf(5) for details - -[Manager] -#LogLevel=info -#LogTarget=journal-or-kmsg -#LogColor=yes -#LogLocation=no -#DumpCore=yes -#CrashShell=no -#ShowStatus=yes -#CrashChVT=1 -#CPUAffinity=1 2 -#DefaultControllers=cpu -#DefaultStandardOutput=journal -#DefaultStandardError=inherit -#JoinControllers=cpu,cpuacct,cpuset net_cls,net_prio -#RuntimeWatchdogSec=0 -#ShutdownWatchdogSec=10min -#CapabilityBoundingSet= -#TimerSlackNSec= -#DefaultLimitCPU= -#DefaultLimitFSIZE= -#DefaultLimitDATA= -#DefaultLimitSTACK= -#DefaultLimitCORE= -#DefaultLimitRSS= -#DefaultLimitNOFILE= -#DefaultLimitAS= -#DefaultLimitNPROC= -#DefaultLimitMEMLOCK= -#DefaultLimitLOCKS= -#DefaultLimitSIGPENDING= -#DefaultLimitMSGQUEUE= -#DefaultLimitNICE= -#DefaultLimitRTPRIO= -#DefaultLimitRTTIME= diff --git a/src/core/systemd.pc.in b/src/core/systemd.pc.in deleted file mode 100644 index 2f49d5df52..0000000000 --- a/src/core/systemd.pc.in +++ /dev/null @@ -1,23 +0,0 @@ -# This file is part of systemd. -# -# 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. - -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 -systemduserunitpath=${systemduserconfdir}:/etc/systemd/user:/run/systemd/user:/usr/local/lib/systemd/user:/usr/local/share/systemd/user:${systemduserunitdir}:/usr/lib/systemd/user:/usr/share/systemd/user - -Name: systemd -Description: systemd System and Service Manager -URL: @PACKAGE_URL@ -Version: @PACKAGE_VERSION@ diff --git a/src/core/target.c b/src/core/target.c deleted file mode 100644 index 981424132b..0000000000 --- a/src/core/target.c +++ /dev/null @@ -1,239 +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 <signal.h> -#include <unistd.h> - -#include "unit.h" -#include "target.h" -#include "load-fragment.h" -#include "log.h" -#include "dbus-target.h" -#include "special.h" -#include "unit-name.h" - -static const UnitActiveState state_translation_table[_TARGET_STATE_MAX] = { - [TARGET_DEAD] = UNIT_INACTIVE, - [TARGET_ACTIVE] = UNIT_ACTIVE -}; - -static void target_set_state(Target *t, TargetState state) { - TargetState old_state; - assert(t); - - old_state = t->state; - t->state = state; - - if (state != old_state) - log_debug("%s changed %s -> %s", - UNIT(t)->id, - target_state_to_string(old_state), - target_state_to_string(state)); - - unit_notify(UNIT(t), state_translation_table[old_state], state_translation_table[state], true); -} - -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_PART_OF - }; - - Iterator i; - Unit *other; - int r; - unsigned k; - - assert(t); - - /* Imply ordering for requirement dependencies on target - * units. Note that when the user created a contradicting - * ordering manually we won't add anything in here to make - * sure we don't create a loop. */ - - for (k = 0; k < ELEMENTSOF(deps); k++) - 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); -} - -static int target_load(Unit *u) { - Target *t = TARGET(u); - int r; - - assert(t); - - 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 && u->default_dependencies) { - r = target_add_default_dependencies(t); - if (r < 0) - return r; - } - - return 0; -} - -static int target_coldplug(Unit *u) { - Target *t = TARGET(u); - - assert(t); - assert(t->state == TARGET_DEAD); - - if (t->deserialized_state != t->state) - target_set_state(t, t->deserialized_state); - - return 0; -} - -static void target_dump(Unit *u, FILE *f, const char *prefix) { - Target *t = TARGET(u); - - assert(t); - assert(f); - - fprintf(f, - "%sTarget State: %s\n", - prefix, target_state_to_string(t->state)); -} - -static int target_start(Unit *u) { - Target *t = TARGET(u); - - assert(t); - assert(t->state == TARGET_DEAD); - - target_set_state(t, TARGET_ACTIVE); - return 0; -} - -static int target_stop(Unit *u) { - Target *t = TARGET(u); - - assert(t); - assert(t->state == TARGET_ACTIVE); - - target_set_state(t, TARGET_DEAD); - return 0; -} - -static int target_serialize(Unit *u, FILE *f, FDSet *fds) { - Target *s = TARGET(u); - - assert(s); - assert(f); - assert(fds); - - unit_serialize_item(u, f, "state", target_state_to_string(s->state)); - return 0; -} - -static int target_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) { - Target *s = TARGET(u); - - assert(u); - assert(key); - assert(value); - assert(fds); - - if (streq(key, "state")) { - TargetState state; - - state = target_state_from_string(value); - if (state < 0) - log_debug("Failed to parse state value %s", value); - else - s->deserialized_state = state; - - } else - log_debug("Unknown serialization key '%s'", key); - - return 0; -} - -static UnitActiveState target_active_state(Unit *u) { - assert(u); - - return state_translation_table[TARGET(u)->state]; -} - -static const char *target_sub_state_to_string(Unit *u) { - assert(u); - - return target_state_to_string(TARGET(u)->state); -} - -static const char* const target_state_table[_TARGET_STATE_MAX] = { - [TARGET_DEAD] = "dead", - [TARGET_ACTIVE] = "active" -}; - -DEFINE_STRING_TABLE_LOOKUP(target_state, TargetState); - -const UnitVTable target_vtable = { - .object_size = sizeof(Target), - .sections = - "Unit\0" - "Target\0" - "Install\0", - - .load = target_load, - .coldplug = target_coldplug, - - .dump = target_dump, - - .start = target_start, - .stop = target_stop, - - .serialize = target_serialize, - .deserialize_item = target_deserialize_item, - - .active_state = target_active_state, - .sub_state_to_string = target_sub_state_to_string, - - .bus_interface = "org.freedesktop.systemd1.Target", - .bus_message_handler = bus_target_message_handler, - - .status_message_formats = { - .finished_start_job = { - [JOB_DONE] = "Reached target %s.", - [JOB_DEPENDENCY] = "Dependency failed for %s.", - }, - .finished_stop_job = { - [JOB_DONE] = "Stopped target %s.", - }, - }, -}; diff --git a/src/core/target.h b/src/core/target.h deleted file mode 100644 index 1676553add..0000000000 --- a/src/core/target.h +++ /dev/null @@ -1,44 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -#pragma once - -/*** - 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/>. -***/ - -typedef struct Target Target; - -#include "unit.h" - -typedef enum TargetState { - TARGET_DEAD, - TARGET_ACTIVE, - _TARGET_STATE_MAX, - _TARGET_STATE_INVALID = -1 -} TargetState; - -struct Target { - Unit meta; - - TargetState state, deserialized_state; -}; - -extern const UnitVTable target_vtable; - -const char* target_state_to_string(TargetState i); -TargetState target_state_from_string(const char *s); diff --git a/src/core/tcpwrap.c b/src/core/tcpwrap.c deleted file mode 100644 index 6c630fac60..0000000000 --- a/src/core/tcpwrap.c +++ /dev/null @@ -1,68 +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 <sys/socket.h> -#include <sys/un.h> -#include <arpa/inet.h> -#include <netinet/in.h> - -#ifdef HAVE_LIBWRAP -#include <tcpd.h> -#endif - -#include "tcpwrap.h" -#include "log.h" - -bool socket_tcpwrap(int fd, const char *name) { -#ifdef HAVE_LIBWRAP - struct request_info req; - union { - struct sockaddr sa; - struct sockaddr_in in; - struct sockaddr_in6 in6; - struct sockaddr_un un; - struct sockaddr_storage storage; - } sa_union; - socklen_t l = sizeof(sa_union); - - if (getsockname(fd, &sa_union.sa, &l) < 0) - return true; - - if (sa_union.sa.sa_family != AF_INET && - sa_union.sa.sa_family != AF_INET6) - return true; - - request_init(&req, - RQ_DAEMON, name, - RQ_FILE, fd, - NULL); - - fromhost(&req); - - if (!hosts_access(&req)) { - log_warning("Connection refused by tcpwrap."); - return false; - } - - log_debug("Connection accepted by tcpwrap."); -#endif - return true; -} diff --git a/src/core/tcpwrap.h b/src/core/tcpwrap.h deleted file mode 100644 index 3353b6596e..0000000000 --- a/src/core/tcpwrap.h +++ /dev/null @@ -1,26 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -#pragma once - -/*** - 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 <stdbool.h> - -bool socket_tcpwrap(int fd, const char *name); diff --git a/src/core/timer.c b/src/core/timer.c deleted file mode 100644 index 7080b32c6b..0000000000 --- a/src/core/timer.c +++ /dev/null @@ -1,519 +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 "unit.h" -#include "unit-name.h" -#include "timer.h" -#include "dbus-timer.h" -#include "special.h" -#include "bus-errors.h" - -static const UnitActiveState state_translation_table[_TIMER_STATE_MAX] = { - [TIMER_DEAD] = UNIT_INACTIVE, - [TIMER_WAITING] = UNIT_ACTIVE, - [TIMER_RUNNING] = UNIT_ACTIVE, - [TIMER_ELAPSED] = UNIT_ACTIVE, - [TIMER_FAILED] = UNIT_FAILED -}; - -static void timer_init(Unit *u) { - Timer *t = TIMER(u); - - assert(u); - assert(u->load_state == UNIT_STUB); - - t->next_elapse = (usec_t) -1; -} - -static void timer_done(Unit *u) { - Timer *t = TIMER(u); - TimerValue *v; - - assert(t); - - while ((v = t->values)) { - LIST_REMOVE(TimerValue, value, t->values, v); - free(v); - } - - unit_unwatch_timer(u, &t->timer_watch); - - unit_ref_unset(&t->unit); -} - -static int timer_verify(Timer *t) { - assert(t); - - if (UNIT(t)->load_state != UNIT_LOADED) - return 0; - - if (!t->values) { - log_error("%s lacks value setting. Refusing.", UNIT(t)->id); - return -EINVAL; - } - - return 0; -} - -static int timer_add_default_dependencies(Timer *t) { - int r; - - assert(t); - - if (UNIT(t)->manager->running_as == SYSTEMD_SYSTEM) { - if ((r = unit_add_dependency_by_name(UNIT(t), UNIT_BEFORE, SPECIAL_BASIC_TARGET, NULL, true)) < 0) - return r; - - if ((r = unit_add_two_dependencies_by_name(UNIT(t), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, NULL, true)) < 0) - return r; - } - - return unit_add_two_dependencies_by_name(UNIT(t), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, NULL, true); -} - -static int timer_load(Unit *u) { - Timer *t = TIMER(u); - int r; - - assert(u); - assert(u->load_state == UNIT_STUB); - - if ((r = unit_load_fragment_and_dropin(u)) < 0) - return r; - - if (u->load_state == UNIT_LOADED) { - - if (!UNIT_DEREF(t->unit)) { - Unit *x; - - r = unit_load_related_unit(u, ".service", &x); - if (r < 0) - return r; - - unit_ref_set(&t->unit, x); - } - - r = unit_add_two_dependencies(u, UNIT_BEFORE, UNIT_TRIGGERS, UNIT_DEREF(t->unit), true); - if (r < 0) - return r; - - if (UNIT(t)->default_dependencies) - if ((r = timer_add_default_dependencies(t)) < 0) - return r; - } - - return timer_verify(t); -} - -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" - "%sResult: %s\n" - "%sUnit: %s\n", - prefix, timer_state_to_string(t->state), - 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))); -} - -static void timer_set_state(Timer *t, TimerState state) { - TimerState old_state; - assert(t); - - old_state = t->state; - t->state = state; - - if (state != TIMER_WAITING) - unit_unwatch_timer(UNIT(t), &t->timer_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)); - - unit_notify(UNIT(t), state_translation_table[old_state], state_translation_table[state], true); -} - -static void timer_enter_waiting(Timer *t, bool initial); - -static int timer_coldplug(Unit *u) { - Timer *t = TIMER(u); - - assert(t); - assert(t->state == TIMER_DEAD); - - if (t->deserialized_state != t->state) { - - if (t->deserialized_state == TIMER_WAITING) - timer_enter_waiting(t, false); - else - timer_set_state(t, t->deserialized_state); - } - - return 0; -} - -static void timer_enter_dead(Timer *t, TimerResult f) { - assert(t); - - if (f != TIMER_SUCCESS) - t->result = f; - - timer_set_state(t, t->result != TIMER_SUCCESS ? TIMER_FAILED : TIMER_DEAD); -} - -static void timer_enter_waiting(Timer *t, bool initial) { - TimerValue *v; - usec_t base = 0, delay, n; - bool found = false; - int r; - - n = now(CLOCK_MONOTONIC); - - LIST_FOREACH(value, v, t->values) { - - if (v->disabled) - continue; - - switch (v->base) { - - case TIMER_ACTIVE: - if (state_translation_table[t->state] == UNIT_ACTIVE) - base = UNIT(t)->inactive_exit_timestamp.monotonic; - else - base = n; - break; - - case TIMER_BOOT: - /* CLOCK_MONOTONIC equals the uptime on Linux */ - base = 0; - break; - - case TIMER_STARTUP: - base = UNIT(t)->manager->userspace_timestamp.monotonic; - break; - - case TIMER_UNIT_ACTIVE: - - if (UNIT_DEREF(t->unit)->inactive_exit_timestamp.monotonic <= 0) - continue; - - base = UNIT_DEREF(t->unit)->inactive_exit_timestamp.monotonic; - break; - - case TIMER_UNIT_INACTIVE: - - if (UNIT_DEREF(t->unit)->inactive_enter_timestamp.monotonic <= 0) - continue; - - base = UNIT_DEREF(t->unit)->inactive_enter_timestamp.monotonic; - break; - - default: - assert_not_reached("Unknown timer base"); - } - - v->next_elapse = base + v->value; - - if (!initial && v->next_elapse < n) { - v->disabled = true; - continue; - } - - if (!found) - t->next_elapse = v->next_elapse; - else - t->next_elapse = MIN(t->next_elapse, v->next_elapse); - - found = true; - } - - if (!found) { - timer_set_state(t, TIMER_ELAPSED); - return; - } - - delay = n < t->next_elapse ? t->next_elapse - n : 0; - - if ((r = unit_watch_timer(UNIT(t), delay, &t->timer_watch)) < 0) - goto fail; - - timer_set_state(t, TIMER_WAITING); - return; - -fail: - log_warning("%s failed to enter waiting state: %s", UNIT(t)->id, strerror(-r)); - timer_enter_dead(t, TIMER_FAILURE_RESOURCES); -} - -static void timer_enter_running(Timer *t) { - DBusError error; - int r; - - assert(t); - dbus_error_init(&error); - - /* Don't start job if we are supposed to go down */ - 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) - 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)); - timer_enter_dead(t, TIMER_FAILURE_RESOURCES); - - dbus_error_free(&error); -} - -static int timer_start(Unit *u) { - Timer *t = TIMER(u); - - assert(t); - assert(t->state == TIMER_DEAD || t->state == TIMER_FAILED); - - if (UNIT_DEREF(t->unit)->load_state != UNIT_LOADED) - return -ENOENT; - - t->result = TIMER_SUCCESS; - timer_enter_waiting(t, true); - return 0; -} - -static int timer_stop(Unit *u) { - Timer *t = TIMER(u); - - assert(t); - assert(t->state == TIMER_WAITING || t->state == TIMER_RUNNING || t->state == TIMER_ELAPSED); - - timer_enter_dead(t, TIMER_SUCCESS); - return 0; -} - -static int timer_serialize(Unit *u, FILE *f, FDSet *fds) { - Timer *t = TIMER(u); - - assert(u); - assert(f); - assert(fds); - - unit_serialize_item(u, f, "state", timer_state_to_string(t->state)); - unit_serialize_item(u, f, "result", timer_result_to_string(t->result)); - - return 0; -} - -static int timer_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) { - Timer *t = TIMER(u); - - assert(u); - assert(key); - assert(value); - assert(fds); - - if (streq(key, "state")) { - TimerState state; - - if ((state = timer_state_from_string(value)) < 0) - log_debug("Failed to parse state value %s", value); - else - t->deserialized_state = state; - } else if (streq(key, "result")) { - TimerResult f; - - f = timer_result_from_string(value); - if (f < 0) - log_debug("Failed to parse result value %s", value); - else if (f != TIMER_SUCCESS) - t->result = f; - - } else - log_debug("Unknown serialization key '%s'", key); - - return 0; -} - -static UnitActiveState timer_active_state(Unit *u) { - assert(u); - - return state_translation_table[TIMER(u)->state]; -} - -static const char *timer_sub_state_to_string(Unit *u) { - assert(u); - - return timer_state_to_string(TIMER(u)->state); -} - -static void timer_timer_event(Unit *u, uint64_t elapsed, Watch *w) { - Timer *t = TIMER(u); - - assert(t); - assert(elapsed == 1); - - if (t->state != TIMER_WAITING) - return; - - log_debug("Timer elapsed on %s", u->id); - timer_enter_running(t); -} - -void timer_unit_notify(Unit *u, UnitActiveState new_state) { - Iterator i; - Unit *k; - - if (u->type == UNIT_TIMER) - return; - - SET_FOREACH(k, u->dependencies[UNIT_TRIGGERED_BY], i) { - Timer *t; - TimerValue *v; - - if (k->type != UNIT_TIMER) - continue; - - if (k->load_state != UNIT_LOADED) - continue; - - t = TIMER(k); - - /* Reenable all timers that depend on unit state */ - LIST_FOREACH(value, v, t->values) - if (v->base == TIMER_UNIT_ACTIVE || - v->base == TIMER_UNIT_INACTIVE) - v->disabled = false; - - switch (t->state) { - - case TIMER_WAITING: - case TIMER_ELAPSED: - - /* Recalculate sleep time */ - timer_enter_waiting(t, false); - break; - - case TIMER_RUNNING: - - if (UNIT_IS_INACTIVE_OR_FAILED(new_state)) { - log_debug("%s got notified about unit deactivation.", UNIT(t)->id); - timer_enter_waiting(t, false); - } - - break; - - case TIMER_DEAD: - case TIMER_FAILED: - break; - - default: - assert_not_reached("Unknown timer state"); - } - } -} - -static void timer_reset_failed(Unit *u) { - Timer *t = TIMER(u); - - assert(t); - - if (t->state == TIMER_FAILED) - timer_set_state(t, TIMER_DEAD); - - t->result = TIMER_SUCCESS; -} - -static const char* const timer_state_table[_TIMER_STATE_MAX] = { - [TIMER_DEAD] = "dead", - [TIMER_WAITING] = "waiting", - [TIMER_RUNNING] = "running", - [TIMER_ELAPSED] = "elapsed", - [TIMER_FAILED] = "failed" -}; - -DEFINE_STRING_TABLE_LOOKUP(timer_state, TimerState); - -static const char* const timer_base_table[_TIMER_BASE_MAX] = { - [TIMER_ACTIVE] = "OnActiveSec", - [TIMER_BOOT] = "OnBootSec", - [TIMER_STARTUP] = "OnStartupSec", - [TIMER_UNIT_ACTIVE] = "OnUnitActiveSec", - [TIMER_UNIT_INACTIVE] = "OnUnitInactiveSec" -}; - -DEFINE_STRING_TABLE_LOOKUP(timer_base, TimerBase); - -static const char* const timer_result_table[_TIMER_RESULT_MAX] = { - [TIMER_SUCCESS] = "success", - [TIMER_FAILURE_RESOURCES] = "resources" -}; - -DEFINE_STRING_TABLE_LOOKUP(timer_result, TimerResult); - -const UnitVTable timer_vtable = { - .object_size = sizeof(Timer), - .sections = - "Unit\0" - "Timer\0" - "Install\0", - - .init = timer_init, - .done = timer_done, - .load = timer_load, - - .coldplug = timer_coldplug, - - .dump = timer_dump, - - .start = timer_start, - .stop = timer_stop, - - .serialize = timer_serialize, - .deserialize_item = timer_deserialize_item, - - .active_state = timer_active_state, - .sub_state_to_string = timer_sub_state_to_string, - - .timer_event = timer_timer_event, - - .reset_failed = timer_reset_failed, - - .bus_interface = "org.freedesktop.systemd1.Timer", - .bus_message_handler = bus_timer_message_handler, - .bus_invalidating_properties = bus_timer_invalidating_properties -}; diff --git a/src/core/timer.h b/src/core/timer.h deleted file mode 100644 index c6d1d42e44..0000000000 --- a/src/core/timer.h +++ /dev/null @@ -1,90 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -#pragma once - -/*** - 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/>. -***/ - -typedef struct Timer Timer; - -#include "unit.h" - -typedef enum TimerState { - TIMER_DEAD, - TIMER_WAITING, - TIMER_RUNNING, - TIMER_ELAPSED, - TIMER_FAILED, - _TIMER_STATE_MAX, - _TIMER_STATE_INVALID = -1 -} TimerState; - -typedef enum TimerBase { - TIMER_ACTIVE, - TIMER_BOOT, - TIMER_STARTUP, - TIMER_UNIT_ACTIVE, - TIMER_UNIT_INACTIVE, - _TIMER_BASE_MAX, - _TIMER_BASE_INVALID = -1 -} TimerBase; - -typedef struct TimerValue { - usec_t value; - usec_t next_elapse; - - LIST_FIELDS(struct TimerValue, value); - - TimerBase base; - bool disabled; -} TimerValue; - -typedef enum TimerResult { - TIMER_SUCCESS, - TIMER_FAILURE_RESOURCES, - _TIMER_RESULT_MAX, - _TIMER_RESULT_INVALID = -1 -} TimerResult; - -struct Timer { - Unit meta; - - LIST_HEAD(TimerValue, values); - usec_t next_elapse; - - TimerState state, deserialized_state; - UnitRef unit; - - Watch timer_watch; - - TimerResult result; -}; - -void timer_unit_notify(Unit *u, UnitActiveState new_state); - -extern const UnitVTable timer_vtable; - -const char *timer_state_to_string(TimerState i); -TimerState timer_state_from_string(const char *s); - -const char *timer_base_to_string(TimerBase i); -TimerBase timer_base_from_string(const char *s); - -const char* timer_result_to_string(TimerResult i); -TimerResult timer_result_from_string(const char *s); diff --git a/src/core/transaction.c b/src/core/transaction.c deleted file mode 100644 index 4bce942012..0000000000 --- a/src/core/transaction.c +++ /dev/null @@ -1,1087 +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 <unistd.h> -#include <fcntl.h> - -#include "transaction.h" -#include "bus-errors.h" - -static void transaction_unlink_job(Transaction *tr, Job *j, bool delete_dependencies); - -static void transaction_delete_job(Transaction *tr, Job *j, bool delete_dependencies) { - assert(tr); - assert(j); - - /* Deletes one job from the transaction */ - - transaction_unlink_job(tr, j, delete_dependencies); - - job_free(j); -} - -static void transaction_delete_unit(Transaction *tr, Unit *u) { - Job *j; - - /* Deletes all jobs associated with a certain unit from the - * transaction */ - - while ((j = hashmap_get(tr->jobs, u))) - transaction_delete_job(tr, j, true); -} - -void transaction_abort(Transaction *tr) { - Job *j; - - assert(tr); - - while ((j = hashmap_first(tr->jobs))) - transaction_delete_job(tr, j, false); - - assert(hashmap_isempty(tr->jobs)); -} - -static void transaction_find_jobs_that_matter_to_anchor(Job *j, unsigned generation) { - JobDependency *l; - - /* A recursive sweep through the graph that marks all units - * that matter to the anchor job, i.e. are directly or - * indirectly a dependency of the anchor job via paths that - * are fully marked as mattering. */ - - j->matters_to_anchor = true; - j->generation = generation; - - LIST_FOREACH(subject, l, j->subject_list) { - - /* This link does not matter */ - if (!l->matters) - continue; - - /* This unit has already been marked */ - if (l->object->generation == generation) - continue; - - transaction_find_jobs_that_matter_to_anchor(l->object, generation); - } -} - -static void transaction_merge_and_delete_job(Transaction *tr, Job *j, Job *other, JobType t) { - JobDependency *l, *last; - - assert(j); - assert(other); - assert(j->unit == other->unit); - assert(!j->installed); - - /* Merges 'other' into 'j' and then deletes 'other'. */ - - j->type = t; - j->state = JOB_WAITING; - j->override = j->override || other->override; - - j->matters_to_anchor = j->matters_to_anchor || other->matters_to_anchor; - - /* Patch us in as new owner of the JobDependency objects */ - last = NULL; - LIST_FOREACH(subject, l, other->subject_list) { - assert(l->subject == other); - l->subject = j; - last = l; - } - - /* Merge both lists */ - if (last) { - last->subject_next = j->subject_list; - if (j->subject_list) - j->subject_list->subject_prev = last; - j->subject_list = other->subject_list; - } - - /* Patch us in as new owner of the JobDependency objects */ - last = NULL; - LIST_FOREACH(object, l, other->object_list) { - assert(l->object == other); - l->object = j; - last = l; - } - - /* Merge both lists */ - if (last) { - last->object_next = j->object_list; - if (j->object_list) - j->object_list->object_prev = last; - j->object_list = other->object_list; - } - - /* Kill the other job */ - other->subject_list = NULL; - other->object_list = NULL; - transaction_delete_job(tr, other, true); -} - -static bool job_is_conflicted_by(Job *j) { - JobDependency *l; - - assert(j); - - /* Returns true if this job is pulled in by a least one - * ConflictedBy dependency. */ - - LIST_FOREACH(object, l, j->object_list) - if (l->conflicts) - return true; - - return false; -} - -static int delete_one_unmergeable_job(Transaction *tr, Job *j) { - Job *k; - - assert(j); - - /* Tries to delete one item in the linked list - * j->transaction_next->transaction_next->... that conflicts - * with another one, in an attempt to make an inconsistent - * transaction work. */ - - /* We rely here on the fact that if a merged with b does not - * merge with c, either a or b merge with c neither */ - LIST_FOREACH(transaction, j, j) - LIST_FOREACH(transaction, k, j->transaction_next) { - Job *d; - - /* Is this one mergeable? Then skip it */ - if (job_type_is_mergeable(j->type, k->type)) - continue; - - /* Ok, we found two that conflict, let's see if we can - * drop one of them */ - if (!j->matters_to_anchor && !k->matters_to_anchor) { - - /* Both jobs don't matter, so let's - * find the one that is smarter to - * remove. Let's think positive and - * rather remove stops then starts -- - * except if something is being - * stopped because it is conflicted by - * 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))); - - if (j->type == JOB_STOP) { - - if (job_is_conflicted_by(j)) - d = k; - else - d = j; - - } else if (k->type == JOB_STOP) { - - if (job_is_conflicted_by(k)) - d = j; - else - d = k; - } else - d = j; - - } else if (!j->matters_to_anchor) - d = j; - else if (!k->matters_to_anchor) - d = k; - else - 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)); - transaction_delete_job(tr, d, true); - return 0; - } - - return -EINVAL; -} - -static int transaction_merge_jobs(Transaction *tr, DBusError *e) { - Job *j; - Iterator i; - int r; - - assert(tr); - - /* First step, check whether any of the jobs for one specific - * task conflict. If so, try to drop one of them. */ - HASHMAP_FOREACH(j, tr->jobs, i) { - JobType t; - Job *k; - - t = j->type; - LIST_FOREACH(transaction, k, j->transaction_next) { - if (job_type_merge_and_collapse(&t, k->type, j->unit) >= 0) - continue; - - /* OK, we could not merge all jobs for this - * action. Let's see if we can get rid of one - * of them */ - - r = delete_one_unmergeable_job(tr, j); - if (r >= 0) - /* Ok, we managed to drop one, now - * let's ask our callers to call us - * again after garbage collecting */ - return -EAGAIN; - - /* We couldn't merge anything. Failure */ - dbus_set_error(e, BUS_ERROR_TRANSACTION_JOBS_CONFLICTING, "Transaction contains conflicting jobs '%s' and '%s' for %s. Probably contradicting requirement dependencies configured.", - job_type_to_string(t), job_type_to_string(k->type), k->unit->id); - return r; - } - } - - /* Second step, merge the jobs. */ - HASHMAP_FOREACH(j, tr->jobs, i) { - JobType t = j->type; - Job *k; - - /* Merge all transaction jobs for j->unit */ - LIST_FOREACH(transaction, k, j->transaction_next) - assert_se(job_type_merge_and_collapse(&t, k->type, j->unit) == 0); - - while ((k = j->transaction_next)) { - if (tr->anchor_job == k) { - transaction_merge_and_delete_job(tr, k, j, t); - j = k; - } else - transaction_merge_and_delete_job(tr, j, k, t); - } - - assert(!j->transaction_next); - assert(!j->transaction_prev); - } - - return 0; -} - -static void transaction_drop_redundant(Transaction *tr) { - Job *j; - Iterator i; - - /* Goes through the transaction and removes all jobs of the units - * whose jobs are all noops. If not all of a unit's jobs are - * redundant, they are kept. */ - - assert(tr); - -rescan: - HASHMAP_FOREACH(j, tr->jobs, i) { - Job *k; - - LIST_FOREACH(transaction, k, j) { - - if (tr->anchor_job == k || - !job_type_is_redundant(k->type, unit_active_state(k->unit)) || - (k->unit->job && job_type_is_conflicting(k->type, k->unit->job->type))) - goto next_unit; - } - - /* log_debug("Found redundant job %s/%s, dropping.", j->unit->id, job_type_to_string(j->type)); */ - transaction_delete_job(tr, j, false); - goto rescan; - next_unit:; - } -} - -static bool unit_matters_to_anchor(Unit *u, Job *j) { - assert(u); - assert(!j->transaction_prev); - - /* Checks whether at least one of the jobs for this unit - * matters to the anchor. */ - - LIST_FOREACH(transaction, j, j) - if (j->matters_to_anchor) - return true; - - return false; -} - -static int transaction_verify_order_one(Transaction *tr, Job *j, Job *from, unsigned generation, DBusError *e) { - Iterator i; - Unit *u; - int r; - - assert(tr); - assert(j); - assert(!j->transaction_prev); - - /* Does a recursive sweep through the ordering graph, looking - * for a cycle. If we find cycle we try to break it. */ - - /* Have we seen this before? */ - if (j->generation == generation) { - Job *k, *delete; - - /* If the marker is NULL we have been here already and - * decided the job was loop-free from here. Hence - * shortcut things and return right-away. */ - if (!j->marker) - return 0; - - /* So, the marker is not NULL and we already have been - * here. We have a cycle. Let's try to break it. We go - * backwards in our path and try to find a suitable - * 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)); - - 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)); - - if (!delete && - !unit_matters_to_anchor(k->unit, k)) { - /* Ok, we can drop this one, so let's - * do so. */ - delete = k; - } - - /* Check if this in fact was the beginning of - * the cycle */ - if (k == j) - break; - } - - - if (delete) { - log_warning("Breaking ordering cycle by deleting job %s/%s", delete->unit->id, job_type_to_string(delete->type)); - 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."); - return -ENOEXEC; - } - - /* Make the marker point to where we come from, so that we can - * find our way backwards if we want to break a cycle. We use - * a special marker for the beginning: we point to - * ourselves. */ - j->marker = from ? from : j; - j->generation = generation; - - /* We assume that the dependencies are bidirectional, and - * hence can ignore UNIT_AFTER */ - SET_FOREACH(u, j->unit->dependencies[UNIT_BEFORE], i) { - Job *o; - - /* Is there a job for this unit? */ - o = hashmap_get(tr->jobs, u); - if (!o) { - /* Ok, there is no job for this in the - * transaction, but maybe there is already one - * running? */ - o = u->job; - if (!o) - continue; - } - - r = transaction_verify_order_one(tr, o, j, generation, e); - if (r < 0) - return r; - } - - /* Ok, let's backtrack, and remember that this entry is not on - * our path anymore. */ - j->marker = NULL; - - return 0; -} - -static int transaction_verify_order(Transaction *tr, unsigned *generation, DBusError *e) { - Job *j; - int r; - Iterator i; - unsigned g; - - assert(tr); - assert(generation); - - /* Check if the ordering graph is cyclic. If it is, try to fix - * that up by dropping one of the jobs. */ - - g = (*generation)++; - - HASHMAP_FOREACH(j, tr->jobs, i) - if ((r = transaction_verify_order_one(tr, j, NULL, g, e)) < 0) - return r; - - return 0; -} - -static void transaction_collect_garbage(Transaction *tr) { - Iterator i; - Job *j; - - assert(tr); - - /* Drop jobs that are not required by any other job */ - -rescan: - HASHMAP_FOREACH(j, tr->jobs, i) { - if (tr->anchor_job == j || j->object_list) { - /* log_debug("Keeping job %s/%s because of %s/%s", */ - /* j->unit->id, job_type_to_string(j->type), */ - /* j->object_list->subject ? j->object_list->subject->unit->id : "root", */ - /* j->object_list->subject ? job_type_to_string(j->object_list->subject->type) : "root"); */ - continue; - } - - /* log_debug("Garbage collecting job %s/%s", j->unit->id, job_type_to_string(j->type)); */ - transaction_delete_job(tr, j, true); - goto rescan; - } -} - -static int transaction_is_destructive(Transaction *tr, DBusError *e) { - Iterator i; - Job *j; - - assert(tr); - - /* Checks whether applying this transaction means that - * existing jobs would be replaced */ - - HASHMAP_FOREACH(j, tr->jobs, i) { - - /* Assume merged */ - assert(!j->transaction_prev); - assert(!j->transaction_next); - - if (j->unit->job && - !job_type_is_superset(j->type, j->unit->job->type)) { - - dbus_set_error(e, BUS_ERROR_TRANSACTION_IS_DESTRUCTIVE, "Transaction is destructive."); - return -EEXIST; - } - } - - return 0; -} - -static void transaction_minimize_impact(Transaction *tr) { - Job *j; - Iterator i; - - assert(tr); - - /* Drops all unnecessary jobs that reverse already active jobs - * or that stop a running service. */ - -rescan: - HASHMAP_FOREACH(j, tr->jobs, i) { - LIST_FOREACH(transaction, j, j) { - bool stops_running_service, changes_existing_job; - - /* If it matters, we shouldn't drop it */ - if (j->matters_to_anchor) - continue; - - /* Would this stop a running service? - * Would this change an existing job? - * If so, let's drop this entry */ - - stops_running_service = - j->type == JOB_STOP && UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(j->unit)); - - changes_existing_job = - j->unit->job && - job_type_is_conflicting(j->type, j->unit->job->type); - - if (!stops_running_service && !changes_existing_job) - continue; - - if (stops_running_service) - log_debug("%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)); - - /* Ok, let's get rid of this */ - log_debug("Deleting %s/%s to minimize impact.", j->unit->id, job_type_to_string(j->type)); - - transaction_delete_job(tr, j, true); - goto rescan; - } - } -} - -static int transaction_apply(Transaction *tr, Manager *m, JobMode mode) { - Iterator i; - Job *j; - int r; - - /* Moves the transaction jobs to the set of active jobs */ - - if (mode == JOB_ISOLATE) { - - /* When isolating first kill all installed jobs which - * aren't part of the new transaction */ - HASHMAP_FOREACH(j, m->jobs, i) { - assert(j->installed); - - if (hashmap_get(tr->jobs, j->unit)) - continue; - - /* Not invalidating recursively. Avoids triggering - * OnFailure= actions of dependent jobs. Also avoids - * invalidating our iterator. */ - job_finish_and_invalidate(j, JOB_CANCELED, false); - } - } - - HASHMAP_FOREACH(j, tr->jobs, i) { - /* Assume merged */ - assert(!j->transaction_prev); - assert(!j->transaction_next); - - r = hashmap_put(m->jobs, UINT32_TO_PTR(j->id), j); - if (r < 0) - goto rollback; - } - - while ((j = hashmap_steal_first(tr->jobs))) { - Job *installed_job; - - /* Clean the job dependencies */ - transaction_unlink_job(tr, j, false); - - installed_job = job_install(j); - if (installed_job != j) { - /* j has been merged into a previously installed job */ - if (tr->anchor_job == j) - tr->anchor_job = installed_job; - hashmap_remove(m->jobs, UINT32_TO_PTR(j->id)); - job_free(j); - j = installed_job; - } - - job_add_to_run_queue(j); - job_add_to_dbus_queue(j); - job_start_timer(j); - } - - return 0; - -rollback: - - HASHMAP_FOREACH(j, tr->jobs, i) - hashmap_remove(m->jobs, UINT32_TO_PTR(j->id)); - - return r; -} - -int transaction_activate(Transaction *tr, Manager *m, JobMode mode, DBusError *e) { - Iterator i; - Job *j; - int r; - unsigned generation = 1; - - assert(tr); - - /* This applies the changes recorded in tr->jobs to - * the actual list of jobs, if possible. */ - - /* Reset the generation counter of all installed jobs. The detection of cycles - * looks at installed jobs. If they had a non-zero generation from some previous - * walk of the graph, the algorithm would break. */ - HASHMAP_FOREACH(j, m->jobs, i) - j->generation = 0; - - /* First step: figure out which jobs matter */ - transaction_find_jobs_that_matter_to_anchor(tr->anchor_job, generation++); - - /* Second step: Try not to stop any running services if - * we don't have to. Don't try to reverse running - * jobs if we don't have to. */ - if (mode == JOB_FAIL) - transaction_minimize_impact(tr); - - /* Third step: Drop redundant jobs */ - transaction_drop_redundant(tr); - - for (;;) { - /* Fourth step: Let's remove unneeded jobs that might - * be lurking. */ - if (mode != JOB_ISOLATE) - transaction_collect_garbage(tr); - - /* Fifth step: verify order makes sense and correct - * cycles if necessary and possible */ - r = transaction_verify_order(tr, &generation, e); - if (r >= 0) - break; - - if (r != -EAGAIN) { - log_warning("Requested transaction contains an unfixable cyclic ordering dependency: %s", bus_error(e, r)); - return r; - } - - /* Let's see if the resulting transaction ordering - * graph is still cyclic... */ - } - - for (;;) { - /* Sixth step: let's drop unmergeable entries if - * necessary and possible, merge entries we can - * merge */ - r = transaction_merge_jobs(tr, e); - if (r >= 0) - break; - - if (r != -EAGAIN) { - log_warning("Requested transaction contains unmergeable jobs: %s", bus_error(e, r)); - return r; - } - - /* Seventh step: an entry got dropped, let's garbage - * collect its dependencies. */ - if (mode != JOB_ISOLATE) - transaction_collect_garbage(tr); - - /* Let's see if the resulting transaction still has - * unmergeable entries ... */ - } - - /* Eights step: Drop redundant jobs again, if the merging now allows us to drop more. */ - transaction_drop_redundant(tr); - - /* Ninth step: check whether we can actually apply this */ - if (mode == JOB_FAIL) { - r = transaction_is_destructive(tr, e); - if (r < 0) { - log_notice("Requested transaction contradicts existing jobs: %s", bus_error(e, r)); - return r; - } - } - - /* Tenth step: apply changes */ - r = transaction_apply(tr, m, mode); - if (r < 0) { - log_warning("Failed to apply transaction: %s", strerror(-r)); - return r; - } - - assert(hashmap_isempty(tr->jobs)); - - if (!hashmap_isempty(m->jobs)) { - /* Are there any jobs now? Then make sure we have the - * idle pipe around. We don't really care too much - * whether this works or not, as the idle pipe is a - * feature for cosmetics, not actually useful for - * anything beyond that. */ - - if (m->idle_pipe[0] < 0 && m->idle_pipe[1] < 0) - pipe2(m->idle_pipe, O_NONBLOCK|O_CLOEXEC); - } - - return 0; -} - -static Job* transaction_add_one_job(Transaction *tr, JobType type, Unit *unit, bool override, bool *is_new) { - Job *j, *f; - - assert(tr); - assert(unit); - - /* Looks for an existing prospective job and returns that. If - * it doesn't exist it is created and added to the prospective - * jobs list. */ - - f = hashmap_get(tr->jobs, unit); - - LIST_FOREACH(transaction, j, f) { - assert(j->unit == unit); - - if (j->type == type) { - if (is_new) - *is_new = false; - return j; - } - } - - j = job_new(unit, type); - if (!j) - return NULL; - - j->generation = 0; - j->marker = NULL; - j->matters_to_anchor = false; - j->override = override; - - LIST_PREPEND(Job, transaction, f, j); - - if (hashmap_replace(tr->jobs, unit, f) < 0) { - LIST_REMOVE(Job, transaction, f, j); - job_free(j); - return NULL; - } - - if (is_new) - *is_new = true; - - /* log_debug("Added job %s/%s to transaction.", unit->id, job_type_to_string(type)); */ - - return j; -} - -static void transaction_unlink_job(Transaction *tr, Job *j, bool delete_dependencies) { - assert(tr); - assert(j); - - if (j->transaction_prev) - j->transaction_prev->transaction_next = j->transaction_next; - else if (j->transaction_next) - hashmap_replace(tr->jobs, j->unit, j->transaction_next); - else - hashmap_remove_value(tr->jobs, j->unit, j); - - if (j->transaction_next) - j->transaction_next->transaction_prev = j->transaction_prev; - - j->transaction_prev = j->transaction_next = NULL; - - while (j->subject_list) - job_dependency_free(j->subject_list); - - while (j->object_list) { - Job *other = j->object_list->matters ? j->object_list->subject : NULL; - - 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)); - transaction_delete_job(tr, other, delete_dependencies); - } - } -} - -int transaction_add_job_and_dependencies( - Transaction *tr, - JobType type, - Unit *unit, - Job *by, - bool matters, - bool override, - bool conflicts, - bool ignore_requirements, - bool ignore_order, - DBusError *e) { - Job *ret; - Iterator i; - Unit *dep; - int r; - bool is_new; - - assert(tr); - assert(type < _JOB_TYPE_MAX); - assert(type < _JOB_TYPE_MAX_IN_TRANSACTION); - assert(unit); - - /* log_debug("Pulling in %s/%s from %s/%s", */ - /* unit->id, job_type_to_string(type), */ - /* by ? by->unit->id : "NA", */ - /* by ? job_type_to_string(by->type) : "NA"); */ - - if (unit->load_state != UNIT_LOADED && - unit->load_state != UNIT_ERROR && - unit->load_state != UNIT_MASKED) { - dbus_set_error(e, BUS_ERROR_LOAD_FAILED, "Unit %s is not loaded properly.", unit->id); - return -EINVAL; - } - - if (type != JOB_STOP && unit->load_state == UNIT_ERROR) { - dbus_set_error(e, BUS_ERROR_LOAD_FAILED, - "Unit %s failed to load: %s. " - "See system logs and 'systemctl status %s' for details.", - unit->id, - strerror(-unit->load_error), - unit->id); - return -EINVAL; - } - - if (type != JOB_STOP && unit->load_state == UNIT_MASKED) { - dbus_set_error(e, BUS_ERROR_MASKED, "Unit %s is masked.", unit->id); - return -EADDRNOTAVAIL; - } - - if (!unit_job_is_applicable(unit, type)) { - dbus_set_error(e, BUS_ERROR_JOB_TYPE_NOT_APPLICABLE, "Job type %s is not applicable for unit %s.", job_type_to_string(type), unit->id); - return -EBADR; - } - - /* First add the job. */ - ret = transaction_add_one_job(tr, type, unit, override, &is_new); - if (!ret) - return -ENOMEM; - - ret->ignore_order = ret->ignore_order || ignore_order; - - /* Then, add a link to the job. */ - if (by) { - if (!job_dependency_new(by, ret, matters, conflicts)) - return -ENOMEM; - } else { - /* If the job has no parent job, it is the anchor job. */ - assert(!tr->anchor_job); - tr->anchor_job = ret; - } - - if (is_new && !ignore_requirements && type != JOB_NOP) { - Set *following; - - /* If we are following some other unit, make sure we - * add all dependencies of everybody following. */ - if (unit_following_set(ret->unit, &following) > 0) { - 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)); - - if (e) - dbus_error_free(e); - } - } - - set_free(following); - } - - /* Finally, recursively add in all dependencies. */ - if (type == JOB_START || type == JOB_RESTART) { - SET_FOREACH(dep, ret->unit->dependencies[UNIT_REQUIRES], i) { - r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, true, override, false, false, ignore_order, e); - if (r < 0) { - if (r != -EBADR) - goto fail; - - if (e) - dbus_error_free(e); - } - } - - SET_FOREACH(dep, ret->unit->dependencies[UNIT_BINDS_TO], i) { - r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, true, override, false, false, ignore_order, e); - if (r < 0) { - if (r != -EBADR) - goto fail; - - if (e) - dbus_error_free(e); - } - } - - 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)); - - if (e) - dbus_error_free(e); - } - } - - 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)); - - if (e) - dbus_error_free(e); - } - } - - SET_FOREACH(dep, ret->unit->dependencies[UNIT_REQUISITE], i) { - r = transaction_add_job_and_dependencies(tr, JOB_VERIFY_ACTIVE, dep, ret, true, override, false, false, ignore_order, e); - if (r < 0) { - if (r != -EBADR) - goto fail; - - if (e) - dbus_error_free(e); - } - } - - 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)); - - if (e) - dbus_error_free(e); - } - } - - SET_FOREACH(dep, ret->unit->dependencies[UNIT_CONFLICTS], i) { - r = transaction_add_job_and_dependencies(tr, JOB_STOP, dep, ret, true, override, true, false, ignore_order, e); - if (r < 0) { - if (r != -EBADR) - goto fail; - - if (e) - dbus_error_free(e); - } - } - - 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)); - - if (e) - dbus_error_free(e); - } - } - - } - - if (type == JOB_STOP || type == JOB_RESTART) { - - SET_FOREACH(dep, ret->unit->dependencies[UNIT_REQUIRED_BY], i) { - r = transaction_add_job_and_dependencies(tr, type, dep, ret, true, override, false, false, ignore_order, e); - if (r < 0) { - if (r != -EBADR) - goto fail; - - if (e) - dbus_error_free(e); - } - } - - SET_FOREACH(dep, ret->unit->dependencies[UNIT_BOUND_BY], i) { - r = transaction_add_job_and_dependencies(tr, type, dep, ret, true, override, false, false, ignore_order, e); - if (r < 0) { - if (r != -EBADR) - goto fail; - - if (e) - dbus_error_free(e); - } - } - - SET_FOREACH(dep, ret->unit->dependencies[UNIT_CONSISTS_OF], i) { - r = transaction_add_job_and_dependencies(tr, type, dep, ret, true, override, false, false, ignore_order, e); - if (r < 0) { - if (r != -EBADR) - goto fail; - - if (e) - dbus_error_free(e); - } - } - - } - - if (type == JOB_RELOAD) { - - 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)); - - if (e) - dbus_error_free(e); - } - } - } - - /* JOB_VERIFY_STARTED require no dependency handling */ - } - - return 0; - -fail: - return r; -} - -int transaction_add_isolate_jobs(Transaction *tr, Manager *m) { - Iterator i; - Unit *u; - char *k; - int r; - - assert(tr); - assert(m); - - HASHMAP_FOREACH_KEY(u, k, m->units, i) { - - /* ignore aliases */ - if (u->id != k) - continue; - - if (u->ignore_on_isolate) - continue; - - /* No need to stop inactive jobs */ - if (UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(u)) && !u->job) - continue; - - /* Is there already something listed for this? */ - if (hashmap_get(tr->jobs, u)) - continue; - - 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)); - } - - return 0; -} - -Transaction *transaction_new(void) { - Transaction *tr; - - tr = new0(Transaction, 1); - if (!tr) - return NULL; - - tr->jobs = hashmap_new(trivial_hash_func, trivial_compare_func); - if (!tr->jobs) { - free(tr); - return NULL; - } - - return tr; -} - -void transaction_free(Transaction *tr) { - assert(hashmap_isempty(tr->jobs)); - hashmap_free(tr->jobs); - free(tr); -} diff --git a/src/core/transaction.h b/src/core/transaction.h deleted file mode 100644 index 67ace4da0b..0000000000 --- a/src/core/transaction.h +++ /dev/null @@ -1,56 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -#ifndef footransactionhfoo -#define footransactionhfoo - -/*** - 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/>. -***/ - -typedef struct Transaction Transaction; - -#include "unit.h" -#include "manager.h" -#include "job.h" -#include "hashmap.h" - -struct Transaction { - /* Jobs to be added */ - Hashmap *jobs; /* Unit object => Job object list 1:1 */ - Job *anchor_job; /* the job the user asked for */ -}; - -Transaction *transaction_new(void); -void transaction_free(Transaction *tr); - -int transaction_add_job_and_dependencies( - Transaction *tr, - JobType type, - Unit *unit, - Job *by, - bool matters, - bool override, - bool conflicts, - bool ignore_requirements, - bool ignore_order, - DBusError *e); -int transaction_activate(Transaction *tr, Manager *m, JobMode mode, DBusError *e); -int transaction_add_isolate_jobs(Transaction *tr, Manager *m); -void transaction_abort(Transaction *tr); - -#endif diff --git a/src/core/umount.c b/src/core/umount.c deleted file mode 100644 index 83c9de3e82..0000000000 --- a/src/core/umount.c +++ /dev/null @@ -1,639 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -/*** - This file is part of systemd. - - Copyright 2010 ProFUSION embedded systems - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - -#include <errno.h> -#include <fcntl.h> -#include <string.h> -#include <sys/mount.h> -#include <sys/swap.h> -#include <unistd.h> -#include <linux/loop.h> -#include <linux/dm-ioctl.h> -#include <libudev.h> - -#include "list.h" -#include "mount-setup.h" -#include "umount.h" -#include "path-util.h" -#include "util.h" -#include "virt.h" - -typedef struct MountPoint { - char *path; - dev_t devnum; - LIST_FIELDS (struct MountPoint, mount_point); -} MountPoint; - -static void mount_point_free(MountPoint **head, MountPoint *m) { - assert(head); - assert(m); - - LIST_REMOVE(MountPoint, mount_point, *head, m); - - free(m->path); - free(m); -} - -static void mount_points_list_free(MountPoint **head) { - assert(head); - - while (*head) - mount_point_free(head, *head); -} - -static int mount_points_list_get(MountPoint **head) { - FILE *proc_self_mountinfo; - char *path, *p; - unsigned int i; - int r; - - assert(head); - - if (!(proc_self_mountinfo = fopen("/proc/self/mountinfo", "re"))) - return -errno; - - for (i = 1;; i++) { - int k; - MountPoint *m; - - path = p = NULL; - - if ((k = fscanf(proc_self_mountinfo, - "%*s " /* (1) mount id */ - "%*s " /* (2) parent id */ - "%*s " /* (3) major:minor */ - "%*s " /* (4) root */ - "%ms " /* (5) mount point */ - "%*s" /* (6) mount options */ - "%*[^-]" /* (7) optional fields */ - "- " /* (8) separator */ - "%*s " /* (9) file system type */ - "%*s" /* (10) mount source */ - "%*s" /* (11) mount options 2 */ - "%*[^\n]", /* some rubbish at the end */ - &path)) != 1) { - if (k == EOF) - break; - - log_warning("Failed to parse /proc/self/mountinfo:%u.", i); - - free(path); - continue; - } - - p = cunescape(path); - free(path); - - if (!p) { - r = -ENOMEM; - goto finish; - } - - /* Ignore mount points we can't unmount because they - * are API or because we are keeping them open (like - * /dev/console) */ - if (mount_point_is_api(p) || - mount_point_ignore(p) || - path_equal(p, "/dev/console")) { - free(p); - continue; - } - - if (!(m = new0(MountPoint, 1))) { - free(p); - r = -ENOMEM; - goto finish; - } - - m->path = p; - LIST_PREPEND(MountPoint, mount_point, *head, m); - } - - r = 0; - -finish: - fclose(proc_self_mountinfo); - - return r; -} - -static int swap_list_get(MountPoint **head) { - FILE *proc_swaps; - unsigned int i; - int r; - - assert(head); - - if (!(proc_swaps = fopen("/proc/swaps", "re"))) - return (errno == ENOENT) ? 0 : -errno; - - (void) fscanf(proc_swaps, "%*s %*s %*s %*s %*s\n"); - - for (i = 2;; i++) { - MountPoint *swap; - char *dev = NULL, *d; - int k; - - if ((k = fscanf(proc_swaps, - "%ms " /* device/file */ - "%*s " /* type of swap */ - "%*s " /* swap size */ - "%*s " /* used */ - "%*s\n", /* priority */ - &dev)) != 1) { - - if (k == EOF) - break; - - log_warning("Failed to parse /proc/swaps:%u.", i); - - free(dev); - continue; - } - - if (endswith(dev, "(deleted)")) { - free(dev); - continue; - } - - d = cunescape(dev); - free(dev); - - if (!d) { - r = -ENOMEM; - goto finish; - } - - if (!(swap = new0(MountPoint, 1))) { - free(d); - r = -ENOMEM; - goto finish; - } - - swap->path = d; - LIST_PREPEND(MountPoint, mount_point, *head, swap); - } - - r = 0; - -finish: - fclose(proc_swaps); - - return r; -} - -static int loopback_list_get(MountPoint **head) { - int r; - struct udev *udev; - struct udev_enumerate *e = NULL; - struct udev_list_entry *item = NULL, *first = NULL; - - assert(head); - - if (!(udev = udev_new())) { - r = -ENOMEM; - goto finish; - } - - if (!(e = udev_enumerate_new(udev))) { - r = -ENOMEM; - goto finish; - } - - if (udev_enumerate_add_match_subsystem(e, "block") < 0 || - udev_enumerate_add_match_sysname(e, "loop*") < 0) { - r = -EIO; - goto finish; - } - - if (udev_enumerate_scan_devices(e) < 0) { - r = -EIO; - goto finish; - } - - first = udev_enumerate_get_list_entry(e); - udev_list_entry_foreach(item, first) { - MountPoint *lb; - struct udev_device *d; - char *loop; - const char *dn; - - if (!(d = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item)))) { - r = -ENOMEM; - goto finish; - } - - if (!(dn = udev_device_get_devnode(d))) { - udev_device_unref(d); - continue; - } - - loop = strdup(dn); - udev_device_unref(d); - - if (!loop) { - r = -ENOMEM; - goto finish; - } - - if (!(lb = new0(MountPoint, 1))) { - free(loop); - r = -ENOMEM; - goto finish; - } - - lb->path = loop; - LIST_PREPEND(MountPoint, mount_point, *head, lb); - } - - r = 0; - -finish: - if (e) - udev_enumerate_unref(e); - - if (udev) - udev_unref(udev); - - return r; -} - -static int dm_list_get(MountPoint **head) { - int r; - struct udev *udev; - struct udev_enumerate *e = NULL; - struct udev_list_entry *item = NULL, *first = NULL; - - assert(head); - - if (!(udev = udev_new())) { - r = -ENOMEM; - goto finish; - } - - if (!(e = udev_enumerate_new(udev))) { - r = -ENOMEM; - goto finish; - } - - if (udev_enumerate_add_match_subsystem(e, "block") < 0 || - udev_enumerate_add_match_sysname(e, "dm-*") < 0) { - r = -EIO; - goto finish; - } - - if (udev_enumerate_scan_devices(e) < 0) { - r = -EIO; - goto finish; - } - - first = udev_enumerate_get_list_entry(e); - - udev_list_entry_foreach(item, first) { - MountPoint *m; - struct udev_device *d; - dev_t devnum; - char *node; - const char *dn; - - if (!(d = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item)))) { - r = -ENOMEM; - goto finish; - } - - devnum = udev_device_get_devnum(d); - dn = udev_device_get_devnode(d); - - if (major(devnum) == 0 || !dn) { - udev_device_unref(d); - continue; - } - - node = strdup(dn); - udev_device_unref(d); - - if (!node) { - r = -ENOMEM; - goto finish; - } - - if (!(m = new(MountPoint, 1))) { - free(node); - r = -ENOMEM; - goto finish; - } - - m->path = node; - m->devnum = devnum; - LIST_PREPEND(MountPoint, mount_point, *head, m); - } - - r = 0; - -finish: - if (e) - udev_enumerate_unref(e); - - if (udev) - udev_unref(udev); - - return r; -} - -static int delete_loopback(const char *device) { - int fd, r; - - if ((fd = open(device, O_RDONLY|O_CLOEXEC)) < 0) - return errno == ENOENT ? 0 : -errno; - - r = ioctl(fd, LOOP_CLR_FD, 0); - close_nointr_nofail(fd); - - if (r >= 0) - return 1; - - /* ENXIO: not bound, so no error */ - if (errno == ENXIO) - return 0; - - return -errno; -} - -static int delete_dm(dev_t devnum) { - int fd, r; - struct dm_ioctl dm; - - assert(major(devnum) != 0); - - if ((fd = open("/dev/mapper/control", O_RDWR|O_CLOEXEC)) < 0) - return -errno; - - zero(dm); - dm.version[0] = DM_VERSION_MAJOR; - dm.version[1] = DM_VERSION_MINOR; - dm.version[2] = DM_VERSION_PATCHLEVEL; - - dm.data_size = sizeof(dm); - dm.dev = devnum; - - r = ioctl(fd, DM_DEV_REMOVE, &dm); - close_nointr_nofail(fd); - - return r >= 0 ? 0 : -errno; -} - -static int mount_points_list_umount(MountPoint **head, bool *changed, bool log_error) { - MountPoint *m, *n; - int n_failed = 0; - - assert(head); - - LIST_FOREACH_SAFE(mount_point, m, n, *head) { - 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) */ - if (umount2(m->path, MNT_FORCE) == 0) { - log_info("Unmounted %s.", m->path); - if (changed) - *changed = true; - - mount_point_free(head, m); - } else if (log_error) { - log_warning("Could not unmount %s: %m", m->path); - n_failed++; - } - } - - 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_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; - - assert(head); - - LIST_FOREACH_SAFE(mount_point, m, n, *head) { - if (swapoff(m->path) == 0) { - if (changed) - *changed = true; - - mount_point_free(head, m); - } else { - log_warning("Could not deactivate swap %s: %m", m->path); - n_failed++; - } - } - - return n_failed; -} - -static int loopback_points_list_detach(MountPoint **head, bool *changed) { - MountPoint *m, *n; - int n_failed = 0, k; - struct stat root_st; - - assert(head); - - k = lstat("/", &root_st); - - LIST_FOREACH_SAFE(mount_point, m, n, *head) { - int r; - struct stat loopback_st; - - if (k >= 0 && - major(root_st.st_dev) != 0 && - lstat(m->path, &loopback_st) >= 0 && - root_st.st_dev == loopback_st.st_rdev) { - n_failed ++; - continue; - } - - if ((r = delete_loopback(m->path)) >= 0) { - - if (r > 0 && changed) - *changed = true; - - mount_point_free(head, m); - } else { - log_warning("Could not delete loopback %s: %m", m->path); - n_failed++; - } - } - - return n_failed; -} - -static int dm_points_list_detach(MountPoint **head, bool *changed) { - MountPoint *m, *n; - int n_failed = 0, k; - struct stat root_st; - - assert(head); - - k = lstat("/", &root_st); - - LIST_FOREACH_SAFE(mount_point, m, n, *head) { - int r; - - if (k >= 0 && - major(root_st.st_dev) != 0 && - root_st.st_dev == m->devnum) { - n_failed ++; - continue; - } - - if ((r = delete_dm(m->devnum)) >= 0) { - - if (r > 0 && changed) - *changed = true; - - mount_point_free(head, m); - } else { - log_warning("Could not delete dm %s: %m", m->path); - n_failed++; - } - } - - return n_failed; -} - -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; - - /* retry umount, until nothing can be umounted anymore */ - do { - umount_changed = false; - - mount_points_list_umount(&mp_list_head, &umount_changed, false); - if (umount_changed) - *changed = true; - - } while (umount_changed); - - /* umount one more time with logging enabled */ - r = mount_points_list_umount(&mp_list_head, &umount_changed, true); - if (r <= 0) - goto end; - - /* 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) - r = mount_points_list_remount_read_only(&mp_list_head, changed); - - end: - mount_points_list_free(&mp_list_head); - - return r; -} - -int swapoff_all(bool *changed) { - int r; - LIST_HEAD(MountPoint, swap_list_head); - - LIST_HEAD_INIT(MountPoint, swap_list_head); - - r = swap_list_get(&swap_list_head); - if (r < 0) - goto end; - - r = swap_points_list_off(&swap_list_head, changed); - - end: - mount_points_list_free(&swap_list_head); - - return r; -} - -int loopback_detach_all(bool *changed) { - int r; - LIST_HEAD(MountPoint, loopback_list_head); - - LIST_HEAD_INIT(MountPoint, loopback_list_head); - - r = loopback_list_get(&loopback_list_head); - if (r < 0) - goto end; - - r = loopback_points_list_detach(&loopback_list_head, changed); - - end: - mount_points_list_free(&loopback_list_head); - - return r; -} - -int dm_detach_all(bool *changed) { - int r; - LIST_HEAD(MountPoint, dm_list_head); - - LIST_HEAD_INIT(MountPoint, dm_list_head); - - r = dm_list_get(&dm_list_head); - if (r < 0) - goto end; - - r = dm_points_list_detach(&dm_list_head, changed); - - end: - mount_points_list_free(&dm_list_head); - - return r; -} diff --git a/src/core/umount.h b/src/core/umount.h deleted file mode 100644 index 8439ffe58f..0000000000 --- a/src/core/umount.h +++ /dev/null @@ -1,30 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -#pragma once - -/*** - This file is part of systemd. - - Copyright 2010 ProFUSION embedded systems - - 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 umount_all(bool *changed); - -int swapoff_all(bool *changed); - -int loopback_detach_all(bool *changed); - -int dm_detach_all(bool *changed); diff --git a/src/core/unit-printf.c b/src/core/unit-printf.c deleted file mode 100644 index cbae45d9f7..0000000000 --- a/src/core/unit-printf.c +++ /dev/null @@ -1,346 +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 "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; - - assert(u); - - c = unit_get_exec_context(u); - if (!c) - return NULL; - - /* get USER env from our own env if set */ - if (!c->user) - return getusername_malloc(); - - /* fish username from passwd */ - username = c->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) { - Unit *u = userdata; - ExecContext *c; - int r; - const char *username, *home; - - assert(u); - - c = unit_get_exec_context(u); - if (!c) - return NULL; - - /* return HOME if set, otherwise from passwd */ - if (!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); - if (!c) - return NULL; - - /* return HOME if set, otherwise from passwd */ - if (!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 }, - { '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/unit-printf.h b/src/core/unit-printf.h deleted file mode 100644 index d2f4ccd178..0000000000 --- a/src/core/unit-printf.h +++ /dev/null @@ -1,28 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -#pragma once - -/*** - 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 "unit.h" - -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 deleted file mode 100644 index 99e1c27948..0000000000 --- a/src/core/unit.c +++ /dev/null @@ -1,2738 +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 <assert.h> -#include <errno.h> -#include <string.h> -#include <sys/epoll.h> -#include <sys/timerfd.h> -#include <sys/poll.h> -#include <stdlib.h> -#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" -#include "strv.h" -#include "path-util.h" -#include "load-fragment.h" -#include "load-dropin.h" -#include "log.h" -#include "unit-name.h" -#include "dbus-unit.h" -#include "special.h" -#include "cgroup-util.h" -#include "missing.h" -#include "cgroup-attr.h" - -const UnitVTable * const unit_vtable[_UNIT_TYPE_MAX] = { - [UNIT_SERVICE] = &service_vtable, - [UNIT_TIMER] = &timer_vtable, - [UNIT_SOCKET] = &socket_vtable, - [UNIT_TARGET] = &target_vtable, - [UNIT_DEVICE] = &device_vtable, - [UNIT_MOUNT] = &mount_vtable, - [UNIT_AUTOMOUNT] = &automount_vtable, - [UNIT_SNAPSHOT] = &snapshot_vtable, - [UNIT_SWAP] = &swap_vtable, - [UNIT_PATH] = &path_vtable -}; - -Unit *unit_new(Manager *m, size_t size) { - Unit *u; - - assert(m); - assert(size >= sizeof(Unit)); - - u = malloc0(size); - if (!u) - return NULL; - - u->names = set_new(string_hash_func, string_compare_func); - if (!u->names) { - free(u); - return NULL; - } - - u->manager = m; - u->type = _UNIT_TYPE_INVALID; - u->deserialized_job = _JOB_TYPE_INVALID; - u->default_dependencies = true; - u->unit_file_state = _UNIT_FILE_STATE_INVALID; - - return u; -} - -bool unit_has_name(Unit *u, const char *name) { - assert(u); - assert(name); - - return !!set_get(u->names, (char*) name); -} - -int unit_add_name(Unit *u, const char *text) { - UnitType t; - char *s, *i = NULL; - int r; - - assert(u); - assert(text); - - if (unit_name_is_template(text)) { - if (!u->instance) - return -EINVAL; - - s = unit_name_replace_instance(text, u->instance); - } else - s = strdup(text); - - if (!s) - return -ENOMEM; - - if (!unit_name_is_valid(s, false)) { - r = -EINVAL; - goto fail; - } - - assert_se((t = unit_name_to_type(s)) >= 0); - - if (u->type != _UNIT_TYPE_INVALID && t != u->type) { - r = -EINVAL; - goto fail; - } - - if ((r = unit_name_to_instance(s, &i)) < 0) - goto fail; - - if (i && unit_vtable[t]->no_instances) { - r = -EINVAL; - goto fail; - } - - /* Ensure that this unit is either instanced or not instanced, - * but not both. */ - if (u->type != _UNIT_TYPE_INVALID && !u->instance != !i) { - r = -EINVAL; - goto fail; - } - - if (unit_vtable[t]->no_alias && - !set_isempty(u->names) && - !set_get(u->names, s)) { - r = -EEXIST; - goto fail; - } - - if (hashmap_size(u->manager->units) >= MANAGER_MAX_NAMES) { - r = -E2BIG; - goto fail; - } - - if ((r = set_put(u->names, s)) < 0) { - if (r == -EEXIST) - r = 0; - goto fail; - } - - if ((r = hashmap_put(u->manager->units, s, u)) < 0) { - set_remove(u->names, s); - goto fail; - } - - if (u->type == _UNIT_TYPE_INVALID) { - - u->type = t; - u->id = s; - u->instance = i; - - LIST_PREPEND(Unit, units_by_type, u->manager->units_by_type[t], u); - - if (UNIT_VTABLE(u)->init) - UNIT_VTABLE(u)->init(u); - } else - free(i); - - unit_add_to_dbus_queue(u); - return 0; - -fail: - free(s); - free(i); - - return r; -} - -int unit_choose_id(Unit *u, const char *name) { - char *s, *t = NULL, *i; - int r; - - assert(u); - assert(name); - - if (unit_name_is_template(name)) { - - if (!u->instance) - return -EINVAL; - - if (!(t = unit_name_replace_instance(name, u->instance))) - return -ENOMEM; - - name = t; - } - - /* Selects one of the names of this unit as the id */ - s = set_get(u->names, (char*) name); - free(t); - - if (!s) - return -ENOENT; - - if ((r = unit_name_to_instance(s, &i)) < 0) - return r; - - u->id = s; - - free(u->instance); - u->instance = i; - - unit_add_to_dbus_queue(u); - - return 0; -} - -int unit_set_description(Unit *u, const char *description) { - char *s; - - assert(u); - - if (!(s = strdup(description))) - return -ENOMEM; - - free(u->description); - u->description = s; - - unit_add_to_dbus_queue(u); - return 0; -} - -bool unit_check_gc(Unit *u) { - assert(u); - - if (u->load_state == UNIT_STUB) - return true; - - if (UNIT_VTABLE(u)->no_gc) - return true; - - if (u->no_gc) - return true; - - if (u->job) - return true; - - if (u->nop_job) - return true; - - 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; - - return false; -} - -void unit_add_to_load_queue(Unit *u) { - assert(u); - assert(u->type != _UNIT_TYPE_INVALID); - - if (u->load_state != UNIT_STUB || u->in_load_queue) - return; - - LIST_PREPEND(Unit, load_queue, u->manager->load_queue, u); - u->in_load_queue = true; -} - -void unit_add_to_cleanup_queue(Unit *u) { - assert(u); - - if (u->in_cleanup_queue) - return; - - LIST_PREPEND(Unit, cleanup_queue, u->manager->cleanup_queue, u); - u->in_cleanup_queue = true; -} - -void unit_add_to_gc_queue(Unit *u) { - assert(u); - - if (u->in_gc_queue || u->in_cleanup_queue) - return; - - if (unit_check_gc(u)) - return; - - LIST_PREPEND(Unit, gc_queue, u->manager->gc_queue, u); - u->in_gc_queue = true; - - u->manager->n_in_gc_queue ++; - - if (u->manager->gc_queue_timestamp <= 0) - u->manager->gc_queue_timestamp = now(CLOCK_MONOTONIC); -} - -void unit_add_to_dbus_queue(Unit *u) { - assert(u); - assert(u->type != _UNIT_TYPE_INVALID); - - if (u->load_state == UNIT_STUB || u->in_dbus_queue) - return; - - /* Shortcut things if nobody cares */ - if (!bus_has_subscriber(u->manager)) { - u->sent_dbus_new_signal = true; - return; - } - - LIST_PREPEND(Unit, dbus_queue, u->manager->dbus_unit_queue, u); - u->in_dbus_queue = true; -} - -static void bidi_set_free(Unit *u, Set *s) { - Iterator i; - Unit *other; - - assert(u); - - /* Frees the set and makes sure we are dropped from the - * inverse pointers */ - - SET_FOREACH(other, s, i) { - UnitDependency d; - - for (d = 0; d < _UNIT_DEPENDENCY_MAX; d++) - set_remove(other->dependencies[d], u); - - unit_add_to_gc_queue(other); - } - - set_free(s); -} - -void unit_free(Unit *u) { - UnitDependency d; - Iterator i; - char *t; - - assert(u); - - bus_unit_send_removed_signal(u); - - if (u->load_state != UNIT_STUB) - if (UNIT_VTABLE(u)->done) - UNIT_VTABLE(u)->done(u); - - SET_FOREACH(t, u->names, i) - hashmap_remove_value(u->manager->units, t, u); - - if (u->job) { - Job *j = u->job; - job_uninstall(j); - job_free(j); - } - - if (u->nop_job) { - Job *j = u->nop_job; - job_uninstall(j); - job_free(j); - } - - for (d = 0; d < _UNIT_DEPENDENCY_MAX; d++) - bidi_set_free(u, u->dependencies[d]); - - if (u->requires_mounts_for) { - LIST_REMOVE(Unit, has_requires_mounts_for, u->manager->has_requires_mounts_for, u); - strv_free(u->requires_mounts_for); - } - - if (u->type != _UNIT_TYPE_INVALID) - LIST_REMOVE(Unit, units_by_type, u->manager->units_by_type[u->type], u); - - if (u->in_load_queue) - LIST_REMOVE(Unit, load_queue, u->manager->load_queue, u); - - if (u->in_dbus_queue) - LIST_REMOVE(Unit, dbus_queue, u->manager->dbus_unit_queue, u); - - if (u->in_cleanup_queue) - LIST_REMOVE(Unit, cleanup_queue, u->manager->cleanup_queue, u); - - if (u->in_gc_queue) { - LIST_REMOVE(Unit, gc_queue, u->manager->gc_queue, u); - u->manager->n_in_gc_queue--; - } - - cgroup_bonding_free_list(u->cgroup_bondings, u->manager->n_reloading <= 0); - cgroup_attribute_free_list(u->cgroup_attributes); - - free(u->description); - strv_free(u->documentation); - free(u->fragment_path); - free(u->source_path); - free(u->instance); - - set_free_free(u->names); - - condition_free_list(u->conditions); - - while (u->refs) - unit_ref_unset(u->refs); - - free(u); -} - -UnitActiveState unit_active_state(Unit *u) { - assert(u); - - if (u->load_state == UNIT_MERGED) - return unit_active_state(unit_follow_merge(u)); - - /* After a reload it might happen that a unit is not correctly - * loaded but still has a process around. That's why we won't - * shortcut failed loading to UNIT_INACTIVE_FAILED. */ - - return UNIT_VTABLE(u)->active_state(u); -} - -const char* unit_sub_state_to_string(Unit *u) { - assert(u); - - return UNIT_VTABLE(u)->sub_state_to_string(u); -} - -static void complete_move(Set **s, Set **other) { - assert(s); - assert(other); - - if (!*other) - return; - - if (*s) - set_move(*s, *other); - else { - *s = *other; - *other = NULL; - } -} - -static void merge_names(Unit *u, Unit *other) { - char *t; - Iterator i; - - assert(u); - assert(other); - - complete_move(&u->names, &other->names); - - set_free_free(other->names); - other->names = NULL; - other->id = NULL; - - SET_FOREACH(t, u->names, i) - assert_se(hashmap_replace(u->manager->units, t, u) == 0); -} - -static void merge_dependencies(Unit *u, Unit *other, UnitDependency d) { - Iterator i; - Unit *back; - int r; - - assert(u); - assert(other); - assert(d < _UNIT_DEPENDENCY_MAX); - - /* Fix backwards pointers */ - SET_FOREACH(back, other->dependencies[d], i) { - UnitDependency k; - - for (k = 0; k < _UNIT_DEPENDENCY_MAX; k++) - if ((r = set_remove_and_put(back->dependencies[k], other, u)) < 0) { - - if (r == -EEXIST) - set_remove(back->dependencies[k], other); - else - assert(r == -ENOENT); - } - } - - complete_move(&u->dependencies[d], &other->dependencies[d]); - - set_free(other->dependencies[d]); - other->dependencies[d] = NULL; -} - -int unit_merge(Unit *u, Unit *other) { - UnitDependency d; - - assert(u); - assert(other); - assert(u->manager == other->manager); - assert(u->type != _UNIT_TYPE_INVALID); - - other = unit_follow_merge(other); - - if (other == u) - return 0; - - if (u->type != other->type) - return -EINVAL; - - if (!u->instance != !other->instance) - return -EINVAL; - - if (other->load_state != UNIT_STUB && - other->load_state != UNIT_ERROR) - return -EEXIST; - - if (other->job) - return -EEXIST; - - if (other->nop_job) - return -EEXIST; - - if (!UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(other))) - return -EEXIST; - - /* Merge names */ - merge_names(u, other); - - /* Redirect all references */ - while (other->refs) - unit_ref_set(other->refs, u); - - /* Merge dependencies */ - for (d = 0; d < _UNIT_DEPENDENCY_MAX; d++) - merge_dependencies(u, other, d); - - other->load_state = UNIT_MERGED; - other->merged_into = u; - - /* If there is still some data attached to the other node, we - * don't need it anymore, and can free it. */ - if (other->load_state != UNIT_STUB) - if (UNIT_VTABLE(other)->done) - UNIT_VTABLE(other)->done(other); - - unit_add_to_dbus_queue(u); - unit_add_to_cleanup_queue(other); - - return 0; -} - -int unit_merge_by_name(Unit *u, const char *name) { - Unit *other; - int r; - char *s = NULL; - - assert(u); - assert(name); - - if (unit_name_is_template(name)) { - if (!u->instance) - return -EINVAL; - - if (!(s = unit_name_replace_instance(name, u->instance))) - return -ENOMEM; - - name = s; - } - - if (!(other = manager_get_unit(u->manager, name))) - r = unit_add_name(u, name); - else - r = unit_merge(u, other); - - free(s); - return r; -} - -Unit* unit_follow_merge(Unit *u) { - assert(u); - - while (u->load_state == UNIT_MERGED) - assert_se(u = u->merged_into); - - return u; -} - -int unit_add_exec_dependencies(Unit *u, ExecContext *c) { - int r; - - assert(u); - assert(c); - - if (c->std_output != EXEC_OUTPUT_KMSG && - c->std_output != EXEC_OUTPUT_SYSLOG && - c->std_output != EXEC_OUTPUT_JOURNAL && - c->std_output != EXEC_OUTPUT_KMSG_AND_CONSOLE && - c->std_output != EXEC_OUTPUT_SYSLOG_AND_CONSOLE && - c->std_output != EXEC_OUTPUT_JOURNAL_AND_CONSOLE && - c->std_error != EXEC_OUTPUT_KMSG && - c->std_error != EXEC_OUTPUT_SYSLOG && - c->std_error != EXEC_OUTPUT_JOURNAL && - c->std_error != EXEC_OUTPUT_KMSG_AND_CONSOLE && - c->std_error != EXEC_OUTPUT_JOURNAL_AND_CONSOLE && - c->std_error != EXEC_OUTPUT_SYSLOG_AND_CONSOLE) - return 0; - - /* If syslog or kernel logging is requested, make sure our own - * logging daemon is run first. */ - - 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; - - return 0; -} - -const char *unit_description(Unit *u) { - assert(u); - - if (u->description) - return u->description; - - return strna(u->id); -} - -void unit_dump(Unit *u, FILE *f, const char *prefix) { - char *t, **j; - UnitDependency d; - Iterator i; - char *p2; - const char *prefix2; - char - timestamp1[FORMAT_TIMESTAMP_MAX], - timestamp2[FORMAT_TIMESTAMP_MAX], - timestamp3[FORMAT_TIMESTAMP_MAX], - timestamp4[FORMAT_TIMESTAMP_MAX], - timespan[FORMAT_TIMESPAN_MAX]; - Unit *following; - - assert(u); - assert(u->type >= 0); - - if (!prefix) - prefix = ""; - p2 = strappend(prefix, "\t"); - prefix2 = p2 ? p2 : prefix; - - fprintf(f, - "%s-> Unit %s:\n" - "%s\tDescription: %s\n" - "%s\tInstance: %s\n" - "%s\tUnit Load State: %s\n" - "%s\tUnit Active State: %s\n" - "%s\tInactive Exit Timestamp: %s\n" - "%s\tActive Enter Timestamp: %s\n" - "%s\tActive Exit Timestamp: %s\n" - "%s\tInactive Enter Timestamp: %s\n" - "%s\tGC Check Good: %s\n" - "%s\tNeed Daemon Reload: %s\n", - prefix, u->id, - prefix, unit_description(u), - prefix, strna(u->instance), - prefix, unit_load_state_to_string(u->load_state), - prefix, unit_active_state_to_string(unit_active_state(u)), - prefix, strna(format_timestamp(timestamp1, sizeof(timestamp1), u->inactive_exit_timestamp.realtime)), - prefix, strna(format_timestamp(timestamp2, sizeof(timestamp2), u->active_enter_timestamp.realtime)), - prefix, strna(format_timestamp(timestamp3, sizeof(timestamp3), u->active_exit_timestamp.realtime)), - prefix, strna(format_timestamp(timestamp4, sizeof(timestamp4), u->inactive_enter_timestamp.realtime)), - prefix, yes_no(unit_check_gc(u)), - prefix, yes_no(unit_need_daemon_reload(u))); - - SET_FOREACH(t, u->names, i) - fprintf(f, "%s\tName: %s\n", prefix, t); - - STRV_FOREACH(j, u->documentation) - fprintf(f, "%s\tDocumentation: %s\n", prefix, *j); - - if ((following = unit_following(u))) - fprintf(f, "%s\tFollowing: %s\n", prefix, following->id); - - if (u->fragment_path) - fprintf(f, "%s\tFragment Path: %s\n", prefix, u->fragment_path); - - if (u->source_path) - fprintf(f, "%s\tSource Path: %s\n", prefix, u->source_path); - - if (u->job_timeout > 0) - fprintf(f, "%s\tJob Timeout: %s\n", prefix, format_timespan(timespan, sizeof(timespan), u->job_timeout)); - - condition_dump_list(u->conditions, f, prefix); - - if (dual_timestamp_is_set(&u->condition_timestamp)) - fprintf(f, - "%s\tCondition Timestamp: %s\n" - "%s\tCondition Result: %s\n", - prefix, strna(format_timestamp(timestamp1, sizeof(timestamp1), u->condition_timestamp.realtime)), - prefix, yes_no(u->condition_result)); - - for (d = 0; d < _UNIT_DEPENDENCY_MAX; d++) { - Unit *other; - - SET_FOREACH(other, u->dependencies[d], i) - fprintf(f, "%s\t%s: %s\n", prefix, unit_dependency_to_string(d), other->id); - } - - if (!strv_isempty(u->requires_mounts_for)) { - fprintf(f, - "%s\tRequiresMountsFor:", prefix); - - STRV_FOREACH(j, u->requires_mounts_for) - fprintf(f, " %s", *j); - - fputs("\n", f); - } - - if (u->load_state == UNIT_LOADED) { - CGroupBonding *b; - CGroupAttribute *a; - - fprintf(f, - "%s\tStopWhenUnneeded: %s\n" - "%s\tRefuseManualStart: %s\n" - "%s\tRefuseManualStop: %s\n" - "%s\tDefaultDependencies: %s\n" - "%s\tOnFailureIsolate: %s\n" - "%s\tIgnoreOnIsolate: %s\n" - "%s\tIgnoreOnSnapshot: %s\n", - prefix, yes_no(u->stop_when_unneeded), - prefix, yes_no(u->refuse_manual_start), - prefix, yes_no(u->refuse_manual_stop), - prefix, yes_no(u->default_dependencies), - prefix, yes_no(u->on_failure_isolate), - prefix, yes_no(u->ignore_on_isolate), - prefix, yes_no(u->ignore_on_snapshot)); - - LIST_FOREACH(by_unit, b, u->cgroup_bondings) - fprintf(f, "%s\tControlGroup: %s:%s\n", - prefix, b->controller, b->path); - - LIST_FOREACH(by_unit, a, u->cgroup_attributes) { - char *v = NULL; - - if (a->map_callback) - a->map_callback(a->controller, a->name, a->value, &v); - - fprintf(f, "%s\tControlGroupAttribute: %s %s \"%s\"\n", - prefix, a->controller, a->name, v ? v : a->value); - - free(v); - } - - if (UNIT_VTABLE(u)->dump) - UNIT_VTABLE(u)->dump(u, f, prefix2); - - } else if (u->load_state == UNIT_MERGED) - fprintf(f, - "%s\tMerged into: %s\n", - prefix, u->merged_into->id); - else if (u->load_state == UNIT_ERROR) - fprintf(f, "%s\tLoad Error Code: %s\n", prefix, strerror(-u->load_error)); - - - if (u->job) - job_dump(u->job, f, prefix2); - - if (u->nop_job) - job_dump(u->nop_job, f, prefix2); - - free(p2); -} - -/* Common implementation for multiple backends */ -int unit_load_fragment_and_dropin(Unit *u) { - int r; - - assert(u); - - /* Load a .service file */ - if ((r = unit_load_fragment(u)) < 0) - return r; - - if (u->load_state == UNIT_STUB) - return -ENOENT; - - /* Load drop-in directory data */ - if ((r = unit_load_dropin(unit_follow_merge(u))) < 0) - return r; - - return 0; -} - -/* Common implementation for multiple backends */ -int unit_load_fragment_and_dropin_optional(Unit *u) { - int r; - - assert(u); - - /* Same as unit_load_fragment_and_dropin(), but whether - * something can be loaded or not doesn't matter. */ - - /* Load a .service file */ - if ((r = unit_load_fragment(u)) < 0) - return r; - - if (u->load_state == UNIT_STUB) - u->load_state = UNIT_LOADED; - - /* Load drop-in directory data */ - if ((r = unit_load_dropin(unit_follow_merge(u))) < 0) - return r; - - return 0; -} - -int unit_add_default_target_dependency(Unit *u, Unit *target) { - assert(u); - assert(target); - - if (target->type != UNIT_TARGET) - return 0; - - /* Only add the dependency if both units are loaded, so that - * that loop check below is reliable */ - if (u->load_state != UNIT_LOADED || - target->load_state != UNIT_LOADED) - return 0; - - /* If either side wants no automatic dependencies, then let's - * skip this */ - if (!u->default_dependencies || - !target->default_dependencies) - return 0; - - /* Don't create loops */ - if (set_get(target->dependencies[UNIT_BEFORE], u)) - return 0; - - return unit_add_dependency(target, UNIT_AFTER, u, true); -} - -static int unit_add_default_dependencies(Unit *u) { - static const UnitDependency deps[] = { - UNIT_REQUIRED_BY, - UNIT_REQUIRED_BY_OVERRIDABLE, - UNIT_WANTED_BY, - UNIT_BOUND_BY - }; - - Unit *target; - Iterator i; - int r; - unsigned k; - - assert(u); - - for (k = 0; k < ELEMENTSOF(deps); k++) - SET_FOREACH(target, u->dependencies[deps[k]], i) - if ((r = unit_add_default_target_dependency(u, target)) < 0) - return r; - - return 0; -} - -int unit_load(Unit *u) { - int r; - - assert(u); - - if (u->in_load_queue) { - LIST_REMOVE(Unit, load_queue, u->manager->load_queue, u); - u->in_load_queue = false; - } - - if (u->type == _UNIT_TYPE_INVALID) - return -EINVAL; - - if (u->load_state != UNIT_STUB) - return 0; - - if (UNIT_VTABLE(u)->load) - if ((r = UNIT_VTABLE(u)->load(u)) < 0) - goto fail; - - if (u->load_state == UNIT_STUB) { - r = -ENOENT; - goto fail; - } - - if (u->load_state == UNIT_LOADED && - u->default_dependencies) - if ((r = unit_add_default_dependencies(u)) < 0) - goto fail; - - if (u->load_state == UNIT_LOADED) { - r = unit_add_mount_links(u); - if (r < 0) - return r; - } - - if (u->on_failure_isolate && - set_size(u->dependencies[UNIT_ON_FAILURE]) > 1) { - - log_error("More than one OnFailure= dependencies specified for %s but OnFailureIsolate= enabled. Refusing.", - u->id); - - r = -EINVAL; - goto fail; - } - - assert((u->load_state != UNIT_MERGED) == !u->merged_into); - - unit_add_to_dbus_queue(unit_follow_merge(u)); - unit_add_to_gc_queue(u); - - return 0; - -fail: - u->load_state = UNIT_ERROR; - u->load_error = r; - unit_add_to_dbus_queue(u); - unit_add_to_gc_queue(u); - - log_debug("Failed to load configuration for %s: %s", u->id, strerror(-r)); - - return r; -} - -bool unit_condition_test(Unit *u) { - assert(u); - - dual_timestamp_get(&u->condition_timestamp); - u->condition_result = condition_test_list(u->conditions); - - return u->condition_result; -} - -static const char* unit_get_status_message_format(Unit *u, JobType t) { - const UnitStatusMessageFormats *format_table; - - 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 NULL; - - return format_table->starting_stopping[t == JOB_STOP]; -} - -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. - * -EAGAIN: An operation is already in progress. Retry later. - * -ECANCELED: Too many requests for now. - */ -int unit_start(Unit *u) { - UnitActiveState state; - Unit *following; - - assert(u); - - if (u->load_state != UNIT_LOADED) - return -EINVAL; - - /* If this is already started, then this will succeed. Note - * that this will even succeed if this unit is not startable - * by the user. This is relied on to detect when we need to - * wait for units and when waiting is finished. */ - state = unit_active_state(u); - if (UNIT_IS_ACTIVE_OR_RELOADING(state)) - return -EALREADY; - - /* If the conditions failed, don't do anything at all. If we - * already are activating this call might still be useful to - * speed up activation in case there is some hold-off time, - * but we don't want to recheck the condition in that case. */ - if (state != UNIT_ACTIVATING && - !unit_condition_test(u)) { - log_debug("Starting of %s requested but condition failed. Ignoring.", u->id); - return -EALREADY; - } - - /* Forward to the main object, if we aren't it. */ - if ((following = unit_following(u))) { - log_debug("Redirecting start request from %s to %s.", u->id, following->id); - return unit_start(following); - } - - 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) - return -EBADR; - - /* We don't suppress calls to ->start() here when we are - * already starting, to allow this request to be used as a - * "hurry up" call, for example when the unit is in some "auto - * restart" state where it waits for a holdoff timer to elapse - * before it will start again. */ - - unit_add_to_dbus_queue(u); - - return UNIT_VTABLE(u)->start(u); -} - -bool unit_can_start(Unit *u) { - assert(u); - - return !!UNIT_VTABLE(u)->start; -} - -bool unit_can_isolate(Unit *u) { - assert(u); - - return unit_can_start(u) && - u->allow_isolate; -} - -/* Errors: - * -EBADR: This unit type does not support stopping. - * -EALREADY: Unit is already stopped. - * -EAGAIN: An operation is already in progress. Retry later. - */ -int unit_stop(Unit *u) { - UnitActiveState state; - Unit *following; - - assert(u); - - state = unit_active_state(u); - if (UNIT_IS_INACTIVE_OR_FAILED(state)) - return -EALREADY; - - if ((following = unit_following(u))) { - log_debug("Redirecting stop request from %s to %s.", u->id, following->id); - return unit_stop(following); - } - - unit_status_log_starting_stopping_reloading(u, JOB_STOP); - unit_status_print_starting_stopping(u, JOB_STOP); - - if (!UNIT_VTABLE(u)->stop) - return -EBADR; - - unit_add_to_dbus_queue(u); - - return UNIT_VTABLE(u)->stop(u); -} - -/* Errors: - * -EBADR: This unit type does not support reloading. - * -ENOEXEC: Unit is not started. - * -EAGAIN: An operation is already in progress. Retry later. - */ -int unit_reload(Unit *u) { - UnitActiveState state; - Unit *following; - - assert(u); - - if (u->load_state != UNIT_LOADED) - return -EINVAL; - - if (!unit_can_reload(u)) - return -EBADR; - - state = unit_active_state(u); - if (state == UNIT_RELOADING) - return -EALREADY; - - if (state != UNIT_ACTIVE) - return -ENOEXEC; - - if ((following = unit_following(u))) { - log_debug("Redirecting reload request from %s to %s.", u->id, following->id); - 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); -} - -bool unit_can_reload(Unit *u) { - assert(u); - - if (!UNIT_VTABLE(u)->reload) - return false; - - if (!UNIT_VTABLE(u)->can_reload) - return true; - - return UNIT_VTABLE(u)->can_reload(u); -} - -static void unit_check_unneeded(Unit *u) { - Iterator i; - Unit *other; - - assert(u); - - /* If this service shall be shut down when unneeded then do - * so. */ - - if (!u->stop_when_unneeded) - return; - - if (!UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(u))) - return; - - SET_FOREACH(other, u->dependencies[UNIT_REQUIRED_BY], i) - if (unit_pending_active(other)) - return; - - SET_FOREACH(other, u->dependencies[UNIT_REQUIRED_BY_OVERRIDABLE], i) - if (unit_pending_active(other)) - return; - - SET_FOREACH(other, u->dependencies[UNIT_WANTED_BY], i) - if (unit_pending_active(other)) - return; - - SET_FOREACH(other, u->dependencies[UNIT_BOUND_BY], i) - if (unit_pending_active(other)) - return; - - log_info("Service %s is not needed anymore. Stopping.", u->id); - - /* Ok, nobody needs us anymore. Sniff. Then let's commit suicide */ - manager_add_job(u->manager, JOB_STOP, u, JOB_FAIL, true, NULL, NULL); -} - -static void retroactively_start_dependencies(Unit *u) { - Iterator i; - Unit *other; - - assert(u); - assert(UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(u))); - - SET_FOREACH(other, u->dependencies[UNIT_REQUIRES], i) - if (!set_get(u->dependencies[UNIT_AFTER], other) && - !UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other))) - manager_add_job(u->manager, JOB_START, other, JOB_REPLACE, true, NULL, NULL); - - SET_FOREACH(other, u->dependencies[UNIT_BINDS_TO], i) - if (!set_get(u->dependencies[UNIT_AFTER], other) && - !UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other))) - manager_add_job(u->manager, JOB_START, other, JOB_REPLACE, true, NULL, NULL); - - SET_FOREACH(other, u->dependencies[UNIT_REQUIRES_OVERRIDABLE], i) - if (!set_get(u->dependencies[UNIT_AFTER], other) && - !UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other))) - manager_add_job(u->manager, JOB_START, other, JOB_FAIL, false, NULL, NULL); - - SET_FOREACH(other, u->dependencies[UNIT_REQUISITE], i) - if (!set_get(u->dependencies[UNIT_AFTER], other) && - !UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other))) - manager_add_job(u->manager, JOB_START, other, JOB_REPLACE, true, NULL, NULL); - - SET_FOREACH(other, u->dependencies[UNIT_WANTS], i) - if (!set_get(u->dependencies[UNIT_AFTER], other) && - !UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other))) - manager_add_job(u->manager, JOB_START, other, JOB_FAIL, false, NULL, NULL); - - SET_FOREACH(other, u->dependencies[UNIT_CONFLICTS], i) - if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other))) - manager_add_job(u->manager, JOB_STOP, other, JOB_REPLACE, true, NULL, NULL); - - SET_FOREACH(other, u->dependencies[UNIT_CONFLICTED_BY], i) - if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other))) - manager_add_job(u->manager, JOB_STOP, other, JOB_REPLACE, true, NULL, NULL); -} - -static void retroactively_stop_dependencies(Unit *u) { - Iterator i; - Unit *other; - - assert(u); - assert(UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(u))); - - /* Pull down units which are bound to us recursively if enabled */ - SET_FOREACH(other, u->dependencies[UNIT_BOUND_BY], i) - if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other))) - manager_add_job(u->manager, JOB_STOP, other, JOB_REPLACE, true, NULL, NULL); -} - -static void check_unneeded_dependencies(Unit *u) { - Iterator i; - Unit *other; - - assert(u); - assert(UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(u))); - - /* Garbage collect services that might not be needed anymore, if enabled */ - SET_FOREACH(other, u->dependencies[UNIT_REQUIRES], i) - if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other))) - unit_check_unneeded(other); - SET_FOREACH(other, u->dependencies[UNIT_REQUIRES_OVERRIDABLE], i) - if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other))) - unit_check_unneeded(other); - SET_FOREACH(other, u->dependencies[UNIT_WANTS], i) - if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other))) - unit_check_unneeded(other); - SET_FOREACH(other, u->dependencies[UNIT_REQUISITE], i) - if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other))) - unit_check_unneeded(other); - SET_FOREACH(other, u->dependencies[UNIT_REQUISITE_OVERRIDABLE], i) - if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other))) - unit_check_unneeded(other); - SET_FOREACH(other, u->dependencies[UNIT_BINDS_TO], i) - if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other))) - unit_check_unneeded(other); -} - -void unit_trigger_on_failure(Unit *u) { - Unit *other; - Iterator i; - - assert(u); - - if (set_size(u->dependencies[UNIT_ON_FAILURE]) <= 0) - return; - - log_info("Triggering OnFailure= dependencies of %s.", u->id); - - SET_FOREACH(other, u->dependencies[UNIT_ON_FAILURE], i) { - int r; - - if ((r = manager_add_job(u->manager, JOB_START, other, u->on_failure_isolate ? JOB_ISOLATE : JOB_REPLACE, true, NULL, NULL)) < 0) - log_error("Failed to enqueue OnFailure= job: %s", strerror(-r)); - } -} - -void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_success) { - bool unexpected; - - assert(u); - assert(os < _UNIT_ACTIVE_STATE_MAX); - assert(ns < _UNIT_ACTIVE_STATE_MAX); - - /* 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 - * behavior here. For example: if a mount point is remounted - * this function will be called too! */ - - if (u->manager->n_reloading <= 0) { - dual_timestamp ts; - - dual_timestamp_get(&ts); - - if (UNIT_IS_INACTIVE_OR_FAILED(os) && !UNIT_IS_INACTIVE_OR_FAILED(ns)) - u->inactive_exit_timestamp = ts; - else if (!UNIT_IS_INACTIVE_OR_FAILED(os) && UNIT_IS_INACTIVE_OR_FAILED(ns)) - u->inactive_enter_timestamp = ts; - - if (!UNIT_IS_ACTIVE_OR_RELOADING(os) && UNIT_IS_ACTIVE_OR_RELOADING(ns)) - u->active_enter_timestamp = ts; - else if (UNIT_IS_ACTIVE_OR_RELOADING(os) && !UNIT_IS_ACTIVE_OR_RELOADING(ns)) - u->active_exit_timestamp = ts; - - timer_unit_notify(u, ns); - path_unit_notify(u, ns); - } - - if (UNIT_IS_INACTIVE_OR_FAILED(ns)) - cgroup_bonding_trim_list(u->cgroup_bondings, true); - - if (u->job) { - unexpected = false; - - if (u->job->state == JOB_WAITING) - - /* So we reached a different state for this - * job. Let's see if we can run it now if it - * failed previously due to EAGAIN. */ - job_add_to_run_queue(u->job); - - /* Let's check whether this state change constitutes a - * finished job, or maybe contradicts a running job and - * hence needs to invalidate jobs. */ - - switch (u->job->type) { - - case JOB_START: - case JOB_VERIFY_ACTIVE: - - if (UNIT_IS_ACTIVE_OR_RELOADING(ns)) - job_finish_and_invalidate(u->job, JOB_DONE, true); - else if (u->job->state == JOB_RUNNING && ns != UNIT_ACTIVATING) { - unexpected = true; - - if (UNIT_IS_INACTIVE_OR_FAILED(ns)) - job_finish_and_invalidate(u->job, ns == UNIT_FAILED ? JOB_FAILED : JOB_DONE, true); - } - - break; - - case JOB_RELOAD: - case JOB_RELOAD_OR_START: - - if (u->job->state == JOB_RUNNING) { - if (ns == UNIT_ACTIVE) - job_finish_and_invalidate(u->job, reload_success ? JOB_DONE : JOB_FAILED, true); - else if (ns != UNIT_ACTIVATING && ns != UNIT_RELOADING) { - unexpected = true; - - if (UNIT_IS_INACTIVE_OR_FAILED(ns)) - job_finish_and_invalidate(u->job, ns == UNIT_FAILED ? JOB_FAILED : JOB_DONE, true); - } - } - - break; - - case JOB_STOP: - case JOB_RESTART: - case JOB_TRY_RESTART: - - if (UNIT_IS_INACTIVE_OR_FAILED(ns)) - job_finish_and_invalidate(u->job, JOB_DONE, true); - else if (u->job->state == JOB_RUNNING && ns != UNIT_DEACTIVATING) { - unexpected = true; - job_finish_and_invalidate(u->job, JOB_FAILED, true); - } - - break; - - default: - assert_not_reached("Job type unknown"); - } - - } else - unexpected = true; - - if (u->manager->n_reloading <= 0) { - - /* If this state change happened without being - * requested by a job, then let's retroactively start - * or stop dependencies. We skip that step when - * deserializing, since we don't want to create any - * additional jobs just because something is already - * activated. */ - - if (unexpected) { - if (UNIT_IS_INACTIVE_OR_FAILED(os) && UNIT_IS_ACTIVE_OR_ACTIVATING(ns)) - retroactively_start_dependencies(u); - else if (UNIT_IS_ACTIVE_OR_ACTIVATING(os) && UNIT_IS_INACTIVE_OR_DEACTIVATING(ns)) - retroactively_stop_dependencies(u); - } - - /* stop unneeded units regardless if going down was expected or not */ - if (UNIT_IS_ACTIVE_OR_ACTIVATING(os) && UNIT_IS_INACTIVE_OR_DEACTIVATING(ns)) - check_unneeded_dependencies(u); - - if (ns != os && ns == UNIT_FAILED) { - log_struct(LOG_NOTICE, - "MESSAGE=Unit %s entered failed state", u->id, - "UNIT=%s", u->id, - NULL); - unit_trigger_on_failure(u); - } - } - - /* Some names are special */ - if (UNIT_IS_ACTIVE_OR_RELOADING(ns)) { - - if (unit_has_name(u, SPECIAL_DBUS_SERVICE)) - /* The bus just might have become available, - * hence try to connect to it, if we aren't - * yet connected. */ - bus_init(u->manager, true); - - if (u->type == UNIT_SERVICE && - !UNIT_IS_ACTIVE_OR_RELOADING(os) && - u->manager->n_reloading <= 0) { - /* Write audit record if we have just finished starting up */ - manager_send_unit_audit(u->manager, u, AUDIT_SERVICE_START, true); - u->in_audit = true; - } - - if (!UNIT_IS_ACTIVE_OR_RELOADING(os)) - manager_send_unit_plymouth(u->manager, u); - - } else { - - /* We don't care about D-Bus here, since we'll get an - * asynchronous notification for it anyway. */ - - if (u->type == UNIT_SERVICE && - UNIT_IS_INACTIVE_OR_FAILED(ns) && - !UNIT_IS_INACTIVE_OR_FAILED(os) && - u->manager->n_reloading <= 0) { - - /* Hmm, if there was no start record written - * write it now, so that we always have a nice - * pair */ - if (!u->in_audit) { - manager_send_unit_audit(u->manager, u, AUDIT_SERVICE_START, ns == UNIT_INACTIVE); - - if (ns == UNIT_INACTIVE) - manager_send_unit_audit(u->manager, u, AUDIT_SERVICE_STOP, true); - } else - /* Write audit record if we have just finished shutting down */ - manager_send_unit_audit(u->manager, u, AUDIT_SERVICE_STOP, ns == UNIT_INACTIVE); - - u->in_audit = false; - } - } - - manager_recheck_journal(u->manager); - - /* Maybe we finished startup and are now ready for being - * stopped because unneeded? */ - unit_check_unneeded(u); - - unit_add_to_dbus_queue(u); - unit_add_to_gc_queue(u); -} - -int unit_watch_fd(Unit *u, int fd, uint32_t events, Watch *w) { - struct epoll_event ev; - - assert(u); - assert(fd >= 0); - assert(w); - assert(w->type == WATCH_INVALID || (w->type == WATCH_FD && w->fd == fd && w->data.unit == u)); - - zero(ev); - ev.data.ptr = w; - ev.events = events; - - if (epoll_ctl(u->manager->epoll_fd, - w->type == WATCH_INVALID ? EPOLL_CTL_ADD : EPOLL_CTL_MOD, - fd, - &ev) < 0) - return -errno; - - w->fd = fd; - w->type = WATCH_FD; - w->data.unit = u; - - return 0; -} - -void unit_unwatch_fd(Unit *u, Watch *w) { - assert(u); - assert(w); - - if (w->type == WATCH_INVALID) - return; - - assert(w->type == WATCH_FD); - assert(w->data.unit == u); - assert_se(epoll_ctl(u->manager->epoll_fd, EPOLL_CTL_DEL, w->fd, NULL) >= 0); - - w->fd = -1; - w->type = WATCH_INVALID; - w->data.unit = NULL; -} - -int unit_watch_pid(Unit *u, pid_t pid) { - assert(u); - assert(pid >= 1); - - /* Watch a specific PID. We only support one unit watching - * each PID for now. */ - - return hashmap_put(u->manager->watch_pids, LONG_TO_PTR(pid), u); -} - -void unit_unwatch_pid(Unit *u, pid_t pid) { - assert(u); - assert(pid >= 1); - - hashmap_remove_value(u->manager->watch_pids, LONG_TO_PTR(pid), u); -} - -int unit_watch_timer(Unit *u, usec_t delay, Watch *w) { - struct itimerspec its; - int flags, fd; - bool ours; - - assert(u); - assert(w); - assert(w->type == WATCH_INVALID || (w->type == WATCH_UNIT_TIMER && w->data.unit == u)); - - /* This will try to reuse the old timer if there is one */ - - if (w->type == WATCH_UNIT_TIMER) { - assert(w->data.unit == u); - assert(w->fd >= 0); - - ours = false; - fd = w->fd; - } else if (w->type == WATCH_INVALID) { - - ours = true; - if ((fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK|TFD_CLOEXEC)) < 0) - return -errno; - } else - assert_not_reached("Invalid watch type"); - - zero(its); - - if (delay <= 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; - its.it_value.tv_nsec = 1; - - flags = TFD_TIMER_ABSTIME; - } else { - timespec_store(&its.it_value, delay); - flags = 0; - } - - /* This will also flush the elapse counter */ - if (timerfd_settime(fd, flags, &its, NULL) < 0) - goto fail; - - if (w->type == WATCH_INVALID) { - struct epoll_event ev; - - zero(ev); - ev.data.ptr = w; - ev.events = EPOLLIN; - - if (epoll_ctl(u->manager->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0) - goto fail; - } - - w->type = WATCH_UNIT_TIMER; - w->fd = fd; - w->data.unit = u; - - return 0; - -fail: - if (ours) - close_nointr_nofail(fd); - - return -errno; -} - -void unit_unwatch_timer(Unit *u, Watch *w) { - assert(u); - assert(w); - - if (w->type == WATCH_INVALID) - return; - - assert(w->type == WATCH_UNIT_TIMER); - assert(w->data.unit == u); - assert(w->fd >= 0); - - assert_se(epoll_ctl(u->manager->epoll_fd, EPOLL_CTL_DEL, w->fd, NULL) >= 0); - close_nointr_nofail(w->fd); - - w->fd = -1; - w->type = WATCH_INVALID; - w->data.unit = NULL; -} - -bool unit_job_is_applicable(Unit *u, JobType j) { - assert(u); - assert(j >= 0 && j < _JOB_TYPE_MAX); - - switch (j) { - - case JOB_VERIFY_ACTIVE: - case JOB_START: - case JOB_STOP: - case JOB_NOP: - return true; - - case JOB_RESTART: - case JOB_TRY_RESTART: - return unit_can_start(u); - - case JOB_RELOAD: - return unit_can_reload(u); - - case JOB_RELOAD_OR_START: - return unit_can_reload(u) && unit_can_start(u); - - default: - assert_not_reached("Invalid job type"); - } -} - -int unit_add_dependency(Unit *u, UnitDependency d, Unit *other, bool add_reference) { - - static const UnitDependency inverse_table[_UNIT_DEPENDENCY_MAX] = { - [UNIT_REQUIRES] = UNIT_REQUIRED_BY, - [UNIT_REQUIRES_OVERRIDABLE] = UNIT_REQUIRED_BY_OVERRIDABLE, - [UNIT_WANTS] = UNIT_WANTED_BY, - [UNIT_REQUISITE] = UNIT_REQUIRED_BY, - [UNIT_REQUISITE_OVERRIDABLE] = UNIT_REQUIRED_BY_OVERRIDABLE, - [UNIT_BINDS_TO] = UNIT_BOUND_BY, - [UNIT_PART_OF] = UNIT_CONSISTS_OF, - [UNIT_REQUIRED_BY] = _UNIT_DEPENDENCY_INVALID, - [UNIT_REQUIRED_BY_OVERRIDABLE] = _UNIT_DEPENDENCY_INVALID, - [UNIT_WANTED_BY] = _UNIT_DEPENDENCY_INVALID, - [UNIT_BOUND_BY] = UNIT_BINDS_TO, - [UNIT_CONSISTS_OF] = UNIT_PART_OF, - [UNIT_CONFLICTS] = UNIT_CONFLICTED_BY, - [UNIT_CONFLICTED_BY] = UNIT_CONFLICTS, - [UNIT_BEFORE] = UNIT_AFTER, - [UNIT_AFTER] = UNIT_BEFORE, - [UNIT_ON_FAILURE] = _UNIT_DEPENDENCY_INVALID, - [UNIT_REFERENCES] = UNIT_REFERENCED_BY, - [UNIT_REFERENCED_BY] = UNIT_REFERENCES, - [UNIT_TRIGGERS] = UNIT_TRIGGERED_BY, - [UNIT_TRIGGERED_BY] = UNIT_TRIGGERS, - [UNIT_PROPAGATES_RELOAD_TO] = UNIT_RELOAD_PROPAGATED_FROM, - [UNIT_RELOAD_PROPAGATED_FROM] = UNIT_PROPAGATES_RELOAD_TO, - }; - int r, q = 0, v = 0, w = 0; - - assert(u); - assert(d >= 0 && d < _UNIT_DEPENDENCY_MAX); - assert(other); - - u = unit_follow_merge(u); - other = unit_follow_merge(other); - - /* We won't allow dependencies on ourselves. We will not - * consider them an error however. */ - if (u == other) - return 0; - - if ((r = set_ensure_allocated(&u->dependencies[d], trivial_hash_func, trivial_compare_func)) < 0) - return r; - - if (inverse_table[d] != _UNIT_DEPENDENCY_INVALID) - if ((r = set_ensure_allocated(&other->dependencies[inverse_table[d]], trivial_hash_func, trivial_compare_func)) < 0) - return r; - - if (add_reference) - if ((r = set_ensure_allocated(&u->dependencies[UNIT_REFERENCES], trivial_hash_func, trivial_compare_func)) < 0 || - (r = set_ensure_allocated(&other->dependencies[UNIT_REFERENCED_BY], trivial_hash_func, trivial_compare_func)) < 0) - return r; - - if ((q = set_put(u->dependencies[d], other)) < 0) - return q; - - if (inverse_table[d] != _UNIT_DEPENDENCY_INVALID) - if ((v = set_put(other->dependencies[inverse_table[d]], u)) < 0) { - r = v; - goto fail; - } - - if (add_reference) { - if ((w = set_put(u->dependencies[UNIT_REFERENCES], other)) < 0) { - r = w; - goto fail; - } - - if ((r = set_put(other->dependencies[UNIT_REFERENCED_BY], u)) < 0) - goto fail; - } - - unit_add_to_dbus_queue(u); - return 0; - -fail: - if (q > 0) - set_remove(u->dependencies[d], other); - - if (v > 0) - set_remove(other->dependencies[inverse_table[d]], u); - - if (w > 0) - set_remove(u->dependencies[UNIT_REFERENCES], other); - - return r; -} - -int unit_add_two_dependencies(Unit *u, UnitDependency d, UnitDependency e, Unit *other, bool add_reference) { - int r; - - assert(u); - - if ((r = unit_add_dependency(u, d, other, add_reference)) < 0) - return r; - - if ((r = unit_add_dependency(u, e, other, add_reference)) < 0) - return r; - - return 0; -} - -static const char *resolve_template(Unit *u, const char *name, const char*path, char **p) { - char *s; - - assert(u); - assert(name || path); - - if (!name) - name = path_get_file_name(path); - - if (!unit_name_is_template(name)) { - *p = NULL; - return name; - } - - if (u->instance) - s = unit_name_replace_instance(name, u->instance); - else { - char *i; - - if (!(i = unit_name_to_prefix(u->id))) - return NULL; - - s = unit_name_replace_instance(name, i); - free(i); - } - - if (!s) - return NULL; - - *p = s; - return s; -} - -int unit_add_dependency_by_name(Unit *u, UnitDependency d, const char *name, const char *path, bool add_reference) { - Unit *other; - int r; - char *s; - - assert(u); - assert(name || path); - - if (!(name = resolve_template(u, name, path, &s))) - return -ENOMEM; - - if ((r = manager_load_unit(u->manager, name, path, NULL, &other)) < 0) - goto finish; - - r = unit_add_dependency(u, d, other, add_reference); - -finish: - free(s); - return r; -} - -int unit_add_two_dependencies_by_name(Unit *u, UnitDependency d, UnitDependency e, const char *name, const char *path, bool add_reference) { - Unit *other; - int r; - char *s; - - assert(u); - assert(name || path); - - if (!(name = resolve_template(u, name, path, &s))) - return -ENOMEM; - - if ((r = manager_load_unit(u->manager, name, path, NULL, &other)) < 0) - goto finish; - - r = unit_add_two_dependencies(u, d, e, other, add_reference); - -finish: - free(s); - return r; -} - -int unit_add_dependency_by_name_inverse(Unit *u, UnitDependency d, const char *name, const char *path, bool add_reference) { - Unit *other; - int r; - char *s; - - assert(u); - assert(name || path); - - if (!(name = resolve_template(u, name, path, &s))) - return -ENOMEM; - - if ((r = manager_load_unit(u->manager, name, path, NULL, &other)) < 0) - goto finish; - - r = unit_add_dependency(other, d, u, add_reference); - -finish: - free(s); - return r; -} - -int unit_add_two_dependencies_by_name_inverse(Unit *u, UnitDependency d, UnitDependency e, const char *name, const char *path, bool add_reference) { - Unit *other; - int r; - char *s; - - assert(u); - assert(name || path); - - if (!(name = resolve_template(u, name, path, &s))) - return -ENOMEM; - - if ((r = manager_load_unit(u->manager, name, path, NULL, &other)) < 0) - goto finish; - - if ((r = unit_add_two_dependencies(other, d, e, u, add_reference)) < 0) - goto finish; - -finish: - free(s); - return r; -} - -int set_unit_path(const char *p) { - char *cwd, *c; - int r; - - /* This is mostly for debug purposes */ - - if (path_is_absolute(p)) { - if (!(c = strdup(p))) - return -ENOMEM; - } else { - if (!(cwd = get_current_dir_name())) - return -errno; - - r = asprintf(&c, "%s/%s", cwd, p); - free(cwd); - - if (r < 0) - return -ENOMEM; - } - - if (setenv("SYSTEMD_UNIT_PATH", c, 0) < 0) { - r = -errno; - free(c); - return r; - } - - return 0; -} - -char *unit_dbus_path(Unit *u) { - assert(u); - - if (!u->id) - return NULL; - - return unit_dbus_path_from_name(u->id); -} - -int unit_add_cgroup(Unit *u, CGroupBonding *b) { - int r; - - assert(u); - assert(b); - - assert(b->path); - - if (!b->controller) { - if (!(b->controller = strdup(SYSTEMD_CGROUP_CONTROLLER))) - return -ENOMEM; - - b->ours = true; - } - - /* Ensure this hasn't been added yet */ - assert(!b->unit); - - if (streq(b->controller, SYSTEMD_CGROUP_CONTROLLER)) { - CGroupBonding *l; - - l = hashmap_get(u->manager->cgroup_bondings, b->path); - LIST_PREPEND(CGroupBonding, by_path, l, b); - - if ((r = hashmap_replace(u->manager->cgroup_bondings, b->path, l)) < 0) { - LIST_REMOVE(CGroupBonding, by_path, l, b); - return r; - } - } - - LIST_PREPEND(CGroupBonding, by_unit, u->cgroup_bondings, b); - b->unit = u; - - return 0; -} - -char *unit_default_cgroup_path(Unit *u) { - char *p; - - assert(u); - - if (u->instance) { - char *t; - - t = unit_name_template(u->id); - if (!t) - return NULL; - - p = strjoin(u->manager->cgroup_hierarchy, "/", t, "/", u->instance, NULL); - free(t); - } else - p = strjoin(u->manager->cgroup_hierarchy, "/", u->id, NULL); - - return p; -} - -int unit_add_cgroup_from_text(Unit *u, const char *name) { - char *controller = NULL, *path = NULL; - CGroupBonding *b = NULL; - bool ours = false; - int r; - - assert(u); - assert(name); - - if ((r = cg_split_spec(name, &controller, &path)) < 0) - return r; - - if (!path) { - path = unit_default_cgroup_path(u); - ours = true; - } - - if (!controller) { - controller = strdup(SYSTEMD_CGROUP_CONTROLLER); - ours = true; - } - - if (!path || !controller) { - free(path); - free(controller); - - return -ENOMEM; - } - - if (cgroup_bonding_find_list(u->cgroup_bondings, controller)) { - r = -EEXIST; - goto fail; - } - - if (!(b = new0(CGroupBonding, 1))) { - r = -ENOMEM; - goto fail; - } - - b->controller = controller; - b->path = path; - b->ours = ours; - b->essential = streq(controller, SYSTEMD_CGROUP_CONTROLLER); - - if ((r = unit_add_cgroup(u, b)) < 0) - goto fail; - - return 0; - -fail: - free(path); - free(controller); - free(b); - - return r; -} - -static int unit_add_one_default_cgroup(Unit *u, const char *controller) { - CGroupBonding *b = NULL; - int r = -ENOMEM; - - assert(u); - - if (!controller) - controller = SYSTEMD_CGROUP_CONTROLLER; - - if (cgroup_bonding_find_list(u->cgroup_bondings, controller)) - return 0; - - if (!(b = new0(CGroupBonding, 1))) - return -ENOMEM; - - if (!(b->controller = strdup(controller))) - goto fail; - - b->path = unit_default_cgroup_path(u); - if (!b->path) - goto fail; - - b->ours = true; - b->essential = streq(controller, SYSTEMD_CGROUP_CONTROLLER); - - if ((r = unit_add_cgroup(u, b)) < 0) - goto fail; - - return 0; - -fail: - free(b->path); - free(b->controller); - free(b); - - return r; -} - -int unit_add_default_cgroups(Unit *u) { - CGroupAttribute *a; - char **c; - int r; - - assert(u); - - /* Adds in the default cgroups, if they weren't specified - * otherwise. */ - - if (!u->manager->cgroup_hierarchy) - return 0; - - if ((r = unit_add_one_default_cgroup(u, NULL)) < 0) - return r; - - STRV_FOREACH(c, u->manager->default_controllers) - unit_add_one_default_cgroup(u, *c); - - LIST_FOREACH(by_unit, a, u->cgroup_attributes) - unit_add_one_default_cgroup(u, a->controller); - - return 0; -} - -CGroupBonding* unit_get_default_cgroup(Unit *u) { - assert(u); - - return cgroup_bonding_find_list(u->cgroup_bondings, SYSTEMD_CGROUP_CONTROLLER); -} - -int unit_add_cgroup_attribute(Unit *u, const char *controller, const char *name, const char *value, CGroupAttributeMapCallback map_callback) { - int r; - char *c = NULL; - CGroupAttribute *a; - - assert(u); - assert(name); - assert(value); - - if (!controller) { - const char *dot; - - dot = strchr(name, '.'); - if (!dot) - return -EINVAL; - - c = strndup(name, dot - name); - if (!c) - return -ENOMEM; - - controller = c; - } - - if (streq(controller, SYSTEMD_CGROUP_CONTROLLER)) { - r = -EINVAL; - goto finish; - } - - a = new0(CGroupAttribute, 1); - if (!a) { - r = -ENOMEM; - goto finish; - } - - if (c) { - a->controller = c; - c = NULL; - } else - a->controller = strdup(controller); - - a->name = strdup(name); - a->value = strdup(value); - - if (!a->controller || !a->name || !a->value) { - free(a->controller); - free(a->name); - free(a->value); - free(a); - - return -ENOMEM; - } - - a->map_callback = map_callback; - - LIST_PREPEND(CGroupAttribute, by_unit, u->cgroup_attributes, a); - - r = 0; - -finish: - free(c); - return r; -} - -int unit_load_related_unit(Unit *u, const char *type, Unit **_found) { - char *t; - int r; - - assert(u); - assert(type); - assert(_found); - - if (!(t = unit_name_change_suffix(u->id, type))) - return -ENOMEM; - - assert(!unit_has_name(u, t)); - - r = manager_load_unit(u->manager, t, NULL, NULL, _found); - free(t); - - assert(r < 0 || *_found != u); - - return r; -} - -int unit_get_related_unit(Unit *u, const char *type, Unit **_found) { - Unit *found; - char *t; - - assert(u); - assert(type); - assert(_found); - - if (!(t = unit_name_change_suffix(u->id, type))) - return -ENOMEM; - - assert(!unit_has_name(u, t)); - - found = manager_get_unit(u->manager, t); - free(t); - - if (!found) - return -ENOENT; - - *_found = found; - return 0; -} - -int unit_watch_bus_name(Unit *u, const char *name) { - assert(u); - assert(name); - - /* Watch a specific name on the bus. We only support one unit - * watching each name for now. */ - - return hashmap_put(u->manager->watch_bus, name, u); -} - -void unit_unwatch_bus_name(Unit *u, const char *name) { - assert(u); - assert(name); - - hashmap_remove_value(u->manager->watch_bus, name, u); -} - -bool unit_can_serialize(Unit *u) { - assert(u); - - return UNIT_VTABLE(u)->serialize && UNIT_VTABLE(u)->deserialize_item; -} - -int unit_serialize(Unit *u, FILE *f, FDSet *fds, bool serialize_jobs) { - int r; - - assert(u); - assert(f); - assert(fds); - - if (!unit_can_serialize(u)) - return 0; - - if ((r = UNIT_VTABLE(u)->serialize(u, f, fds)) < 0) - return r; - - - if (serialize_jobs) { - if (u->job) { - fprintf(f, "job\n"); - job_serialize(u->job, f, fds); - } - - if (u->nop_job) { - fprintf(f, "job\n"); - job_serialize(u->nop_job, f, fds); - } - } - - dual_timestamp_serialize(f, "inactive-exit-timestamp", &u->inactive_exit_timestamp); - dual_timestamp_serialize(f, "active-enter-timestamp", &u->active_enter_timestamp); - dual_timestamp_serialize(f, "active-exit-timestamp", &u->active_exit_timestamp); - dual_timestamp_serialize(f, "inactive-enter-timestamp", &u->inactive_enter_timestamp); - dual_timestamp_serialize(f, "condition-timestamp", &u->condition_timestamp); - - if (dual_timestamp_is_set(&u->condition_timestamp)) - unit_serialize_item(u, f, "condition-result", yes_no(u->condition_result)); - - /* End marker */ - fputc('\n', f); - return 0; -} - -void unit_serialize_item_format(Unit *u, FILE *f, const char *key, const char *format, ...) { - va_list ap; - - assert(u); - assert(f); - assert(key); - assert(format); - - fputs(key, f); - fputc('=', f); - - va_start(ap, format); - vfprintf(f, format, ap); - va_end(ap); - - fputc('\n', f); -} - -void unit_serialize_item(Unit *u, FILE *f, const char *key, const char *value) { - assert(u); - assert(f); - assert(key); - assert(value); - - fprintf(f, "%s=%s\n", key, value); -} - -int unit_deserialize(Unit *u, FILE *f, FDSet *fds) { - int r; - - assert(u); - assert(f); - assert(fds); - - if (!unit_can_serialize(u)) - return 0; - - for (;;) { - char line[LINE_MAX], *l, *v; - size_t k; - - if (!fgets(line, sizeof(line), f)) { - if (feof(f)) - return 0; - return -errno; - } - - char_array_0(line); - l = strstrip(line); - - /* End marker */ - if (l[0] == 0) - return 0; - - k = strcspn(l, "="); - - if (l[k] == '=') { - l[k] = 0; - v = l+k+1; - } else - v = l+k; - - if (streq(l, "job")) { - if (v[0] == '\0') { - /* new-style serialized job */ - Job *j = job_new_raw(u); - if (!j) - return -ENOMEM; - - r = job_deserialize(j, f, fds); - if (r < 0) { - job_free(j); - return r; - } - - r = hashmap_put(u->manager->jobs, UINT32_TO_PTR(j->id), j); - if (r < 0) { - job_free(j); - return r; - } - - r = job_install_deserialized(j); - if (r < 0) { - hashmap_remove(u->manager->jobs, UINT32_TO_PTR(j->id)); - job_free(j); - return r; - } - } else { - /* legacy */ - JobType type = job_type_from_string(v); - if (type < 0) - log_debug("Failed to parse job type value %s", v); - else - u->deserialized_job = type; - } - continue; - } else if (streq(l, "inactive-exit-timestamp")) { - dual_timestamp_deserialize(v, &u->inactive_exit_timestamp); - continue; - } else if (streq(l, "active-enter-timestamp")) { - dual_timestamp_deserialize(v, &u->active_enter_timestamp); - continue; - } else if (streq(l, "active-exit-timestamp")) { - dual_timestamp_deserialize(v, &u->active_exit_timestamp); - continue; - } else if (streq(l, "inactive-enter-timestamp")) { - dual_timestamp_deserialize(v, &u->inactive_enter_timestamp); - continue; - } else if (streq(l, "condition-timestamp")) { - dual_timestamp_deserialize(v, &u->condition_timestamp); - continue; - } else if (streq(l, "condition-result")) { - int b; - - if ((b = parse_boolean(v)) < 0) - log_debug("Failed to parse condition result value %s", v); - else - u->condition_result = b; - - continue; - } - - if ((r = UNIT_VTABLE(u)->deserialize_item(u, l, v, fds)) < 0) - return r; - } -} - -int unit_add_node_link(Unit *u, const char *what, bool wants) { - Unit *device; - char *e; - int r; - - assert(u); - - if (!what) - return 0; - - /* Adds in links to the device node that this unit is based on */ - - if (!is_device_path(what)) - return 0; - - e = unit_name_from_path(what, ".device"); - if (!e) - return -ENOMEM; - - r = manager_load_unit(u->manager, e, NULL, NULL, &device); - free(e); - if (r < 0) - return r; - - r = unit_add_two_dependencies(u, UNIT_AFTER, UNIT_BINDS_TO, device, true); - if (r < 0) - return r; - - if (wants) { - r = unit_add_dependency(device, UNIT_WANTS, u, false); - if (r < 0) - return r; - } - - return 0; -} - -int unit_coldplug(Unit *u) { - int r; - - assert(u); - - if (UNIT_VTABLE(u)->coldplug) - if ((r = UNIT_VTABLE(u)->coldplug(u)) < 0) - return r; - - if (u->job) { - r = job_coldplug(u->job); - if (r < 0) - return r; - } else if (u->deserialized_job >= 0) { - /* legacy */ - r = manager_add_job(u->manager, u->deserialized_job, u, JOB_IGNORE_REQUIREMENTS, false, NULL, NULL); - if (r < 0) - return r; - - u->deserialized_job = _JOB_TYPE_INVALID; - } - - return 0; -} - -void unit_status_printf(Unit *u, const char *status, const char *format, ...) { - va_list ap; - - assert(u); - assert(format); - - if (!manager_get_show_status(u->manager)) - return; - - if (!manager_is_booting_or_shutting_down(u->manager)) - return; - - va_start(ap, format); - status_vprintf(status, true, format, ap); - va_end(ap); -} - -bool unit_need_daemon_reload(Unit *u) { - struct stat st; - - assert(u); - - if (u->fragment_path) { - zero(st); - if (stat(u->fragment_path, &st) < 0) - /* What, cannot access this anymore? */ - return true; - - if (u->fragment_mtime > 0 && - timespec_load(&st.st_mtim) != u->fragment_mtime) - return true; - } - - if (u->source_path) { - zero(st); - if (stat(u->source_path, &st) < 0) - return true; - - if (u->source_mtime > 0 && - timespec_load(&st.st_mtim) != u->source_mtime) - return true; - } - - return false; -} - -void unit_reset_failed(Unit *u) { - assert(u); - - if (UNIT_VTABLE(u)->reset_failed) - UNIT_VTABLE(u)->reset_failed(u); -} - -Unit *unit_following(Unit *u) { - assert(u); - - if (UNIT_VTABLE(u)->following) - return UNIT_VTABLE(u)->following(u); - - return NULL; -} - -bool unit_pending_inactive(Unit *u) { - assert(u); - - /* Returns true if the unit is inactive or going down */ - - if (UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(u))) - return true; - - if (u->job && u->job->type == JOB_STOP) - return true; - - return false; -} - -bool unit_pending_active(Unit *u) { - assert(u); - - /* Returns true if the unit is active or going up */ - - if (UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(u))) - return true; - - if (u->job && - (u->job->type == JOB_START || - u->job->type == JOB_RELOAD_OR_START || - u->job->type == JOB_RESTART)) - return true; - - return false; -} - -int unit_kill(Unit *u, KillWho w, int signo, DBusError *error) { - assert(u); - assert(w >= 0 && w < _KILL_WHO_MAX); - assert(signo > 0); - assert(signo < _NSIG); - - if (!UNIT_VTABLE(u)->kill) - return -ENOTSUP; - - return UNIT_VTABLE(u)->kill(u, w, signo, error); -} - -int unit_following_set(Unit *u, Set **s) { - assert(u); - assert(s); - - if (UNIT_VTABLE(u)->following_set) - return UNIT_VTABLE(u)->following_set(u, s); - - *s = NULL; - return 0; -} - -UnitFileState unit_get_unit_file_state(Unit *u) { - assert(u); - - if (u->unit_file_state < 0 && u->fragment_path) - u->unit_file_state = unit_file_get_state( - 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; -} - -Unit* unit_ref_set(UnitRef *ref, Unit *u) { - assert(ref); - assert(u); - - if (ref->unit) - unit_ref_unset(ref); - - ref->unit = u; - LIST_PREPEND(UnitRef, refs, u->refs, ref); - return u; -} - -void unit_ref_unset(UnitRef *ref) { - assert(ref); - - if (!ref->unit) - return; - - LIST_REMOVE(UnitRef, refs, ref->unit->refs, ref); - ref->unit = NULL; -} - -int unit_add_one_mount_link(Unit *u, Mount *m) { - char **i; - - assert(u); - assert(m); - - if (u->load_state != UNIT_LOADED || - UNIT(m)->load_state != UNIT_LOADED) - return 0; - - STRV_FOREACH(i, u->requires_mounts_for) { - - if (UNIT(m) == u) - continue; - - if (!path_startswith(*i, m->where)) - continue; - - return unit_add_two_dependencies(u, UNIT_AFTER, UNIT_REQUIRES, UNIT(m), true); - } - - return 0; -} - -int unit_add_mount_links(Unit *u) { - Unit *other; - int r; - - assert(u); - - LIST_FOREACH(units_by_type, other, u->manager->units_by_type[UNIT_MOUNT]) { - r = unit_add_one_mount_link(u, MOUNT(other)); - if (r < 0) - return r; - } - - return 0; -} - -int unit_exec_context_defaults(Unit *u, ExecContext *c) { - unsigned i; - int r; - - assert(u); - assert(c); - - /* This only copies in the ones that need memory */ - - for (i = 0; i < RLIMIT_NLIMITS; i++) - if (u->manager->rlimit[i] && !c->rlimit[i]) { - c->rlimit[i] = newdup(struct rlimit, u->manager->rlimit[i], 1); - if (!c->rlimit[i]) - return -ENOMEM; - } - - if (u->manager->running_as == SYSTEMD_USER && - !c->working_directory) { - - r = get_home_dir(&c->working_directory); - if (r < 0) - return r; - } - - 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", - [UNIT_INACTIVE] = "inactive", - [UNIT_FAILED] = "failed", - [UNIT_ACTIVATING] = "activating", - [UNIT_DEACTIVATING] = "deactivating" -}; - -DEFINE_STRING_TABLE_LOOKUP(unit_active_state, UnitActiveState); - -static const char* const unit_dependency_table[_UNIT_DEPENDENCY_MAX] = { - [UNIT_REQUIRES] = "Requires", - [UNIT_REQUIRES_OVERRIDABLE] = "RequiresOverridable", - [UNIT_REQUISITE] = "Requisite", - [UNIT_REQUISITE_OVERRIDABLE] = "RequisiteOverridable", - [UNIT_WANTS] = "Wants", - [UNIT_BINDS_TO] = "BindsTo", - [UNIT_PART_OF] = "PartOf", - [UNIT_REQUIRED_BY] = "RequiredBy", - [UNIT_REQUIRED_BY_OVERRIDABLE] = "RequiredByOverridable", - [UNIT_WANTED_BY] = "WantedBy", - [UNIT_BOUND_BY] = "BoundBy", - [UNIT_CONSISTS_OF] = "ConsistsOf", - [UNIT_CONFLICTS] = "Conflicts", - [UNIT_CONFLICTED_BY] = "ConflictedBy", - [UNIT_BEFORE] = "Before", - [UNIT_AFTER] = "After", - [UNIT_ON_FAILURE] = "OnFailure", - [UNIT_TRIGGERS] = "Triggers", - [UNIT_TRIGGERED_BY] = "TriggeredBy", - [UNIT_PROPAGATES_RELOAD_TO] = "PropagatesReloadTo", - [UNIT_RELOAD_PROPAGATED_FROM] = "ReloadPropagatedFrom", - [UNIT_REFERENCES] = "References", - [UNIT_REFERENCED_BY] = "ReferencedBy", -}; - -DEFINE_STRING_TABLE_LOOKUP(unit_dependency, UnitDependency); diff --git a/src/core/unit.h b/src/core/unit.h deleted file mode 100644 index bf961c2aac..0000000000 --- a/src/core/unit.h +++ /dev/null @@ -1,551 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -#pragma once - -/*** - 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 <stdbool.h> -#include <stdlib.h> - -typedef struct Unit Unit; -typedef struct UnitVTable UnitVTable; -typedef enum UnitActiveState UnitActiveState; -typedef enum UnitDependency UnitDependency; -typedef struct UnitRef UnitRef; -typedef struct UnitStatusMessageFormats UnitStatusMessageFormats; - -#include "set.h" -#include "util.h" -#include "list.h" -#include "socket-util.h" -#include "execute.h" -#include "condition.h" -#include "install.h" -#include "unit-name.h" - -enum UnitActiveState { - UNIT_ACTIVE, - UNIT_RELOADING, - UNIT_INACTIVE, - UNIT_FAILED, - UNIT_ACTIVATING, - UNIT_DEACTIVATING, - _UNIT_ACTIVE_STATE_MAX, - _UNIT_ACTIVE_STATE_INVALID = -1 -}; - -static inline bool UNIT_IS_ACTIVE_OR_RELOADING(UnitActiveState t) { - return t == UNIT_ACTIVE || t == UNIT_RELOADING; -} - -static inline bool UNIT_IS_ACTIVE_OR_ACTIVATING(UnitActiveState t) { - return t == UNIT_ACTIVE || t == UNIT_ACTIVATING || t == UNIT_RELOADING; -} - -static inline bool UNIT_IS_INACTIVE_OR_DEACTIVATING(UnitActiveState t) { - return t == UNIT_INACTIVE || t == UNIT_FAILED || t == UNIT_DEACTIVATING; -} - -static inline bool UNIT_IS_INACTIVE_OR_FAILED(UnitActiveState t) { - return t == UNIT_INACTIVE || t == UNIT_FAILED; -} - -enum UnitDependency { - /* Positive dependencies */ - UNIT_REQUIRES, - UNIT_REQUIRES_OVERRIDABLE, - UNIT_REQUISITE, - UNIT_REQUISITE_OVERRIDABLE, - UNIT_WANTS, - UNIT_BINDS_TO, - UNIT_PART_OF, - - /* Inverse of the above */ - UNIT_REQUIRED_BY, /* inverse of 'requires' and 'requisite' is 'required_by' */ - UNIT_REQUIRED_BY_OVERRIDABLE, /* inverse of 'requires_overridable' and 'requisite_overridable' is 'soft_required_by' */ - UNIT_WANTED_BY, /* inverse of 'wants' */ - UNIT_BOUND_BY, /* inverse of 'binds_to' */ - UNIT_CONSISTS_OF, /* inverse of 'part_of' */ - - /* Negative dependencies */ - UNIT_CONFLICTS, /* inverse of 'conflicts' is 'conflicted_by' */ - UNIT_CONFLICTED_BY, - - /* Order */ - UNIT_BEFORE, /* inverse of 'before' is 'after' and vice versa */ - UNIT_AFTER, - - /* On Failure */ - UNIT_ON_FAILURE, - - /* Triggers (i.e. a socket triggers a service) */ - UNIT_TRIGGERS, - UNIT_TRIGGERED_BY, - - /* Propagate reloads */ - UNIT_PROPAGATES_RELOAD_TO, - UNIT_RELOAD_PROPAGATED_FROM, - - /* Reference information for GC logic */ - UNIT_REFERENCES, /* Inverse of 'references' is 'referenced_by' */ - UNIT_REFERENCED_BY, - - _UNIT_DEPENDENCY_MAX, - _UNIT_DEPENDENCY_INVALID = -1 -}; - -#include "manager.h" -#include "job.h" -#include "cgroup.h" -#include "cgroup-attr.h" - -struct Unit { - Manager *manager; - - UnitType type; - UnitLoadState load_state; - Unit *merged_into; - - char *id; /* One name is special because we use it for identification. Points to an entry in the names set */ - char *instance; - - Set *names; - Set *dependencies[_UNIT_DEPENDENCY_MAX]; - - char **requires_mounts_for; - - char *description; - char **documentation; - - char *fragment_path; /* if loaded from a config file this is the primary path to it */ - char *source_path; /* if converted, the source file */ - usec_t fragment_mtime; - usec_t source_mtime; - - /* If there is something to do with this unit, then this is the installed job for it */ - Job *job; - - /* JOB_NOP jobs are special and can be installed without disturbing the real job. */ - Job *nop_job; - - usec_t job_timeout; - - /* References to this */ - LIST_HEAD(UnitRef, refs); - - /* Conditions to check */ - LIST_HEAD(Condition, conditions); - - dual_timestamp condition_timestamp; - - dual_timestamp inactive_exit_timestamp; - dual_timestamp active_enter_timestamp; - dual_timestamp active_exit_timestamp; - dual_timestamp inactive_enter_timestamp; - - /* Counterparts in the cgroup filesystem */ - CGroupBonding *cgroup_bondings; - CGroupAttribute *cgroup_attributes; - - /* Per type list */ - LIST_FIELDS(Unit, units_by_type); - - /* All units which have requires_mounts_for set */ - LIST_FIELDS(Unit, has_requires_mounts_for); - - /* Load queue */ - LIST_FIELDS(Unit, load_queue); - - /* D-Bus queue */ - LIST_FIELDS(Unit, dbus_queue); - - /* Cleanup queue */ - LIST_FIELDS(Unit, cleanup_queue); - - /* GC queue */ - LIST_FIELDS(Unit, gc_queue); - - /* Used during GC sweeps */ - unsigned gc_marker; - - /* When deserializing, temporarily store the job type for this - * unit here, if there was a job scheduled. - * Only for deserializing from a legacy version. New style uses full - * serialized jobs. */ - int deserialized_job; /* This is actually of type JobType */ - - /* Error code when we didn't manage to load the unit (negative) */ - int load_error; - - /* Cached unit file state */ - UnitFileState unit_file_state; - - /* Garbage collect us we nobody wants or requires us anymore */ - bool stop_when_unneeded; - - /* Create default dependencies */ - bool default_dependencies; - - /* Refuse manual starting, allow starting only indirectly via dependency. */ - bool refuse_manual_start; - - /* Don't allow the user to stop this unit manually, allow stopping only indirectly via dependency. */ - bool refuse_manual_stop; - - /* Allow isolation requests */ - bool allow_isolate; - - /* Isolate OnFailure unit */ - bool on_failure_isolate; - - /* Ignore this unit when isolating */ - bool ignore_on_isolate; - - /* Ignore this unit when snapshotting */ - bool ignore_on_snapshot; - - /* Did the last condition check succeed? */ - bool condition_result; - - bool in_load_queue:1; - bool in_dbus_queue:1; - bool in_cleanup_queue:1; - bool in_gc_queue:1; - - bool sent_dbus_new_signal:1; - - bool no_gc:1; - - bool in_audit:1; -}; - -struct UnitRef { - /* Keeps tracks of references to a unit. This is useful so - * that we can merge two units if necessary and correct all - * references to them */ - - Unit* unit; - LIST_FIELDS(UnitRef, refs); -}; - -struct UnitStatusMessageFormats { - const char *starting_stopping[2]; - const char *finished_start_job[_JOB_RESULT_MAX]; - const char *finished_stop_job[_JOB_RESULT_MAX]; -}; - -#include "service.h" -#include "timer.h" -#include "socket.h" -#include "target.h" -#include "device.h" -#include "mount.h" -#include "automount.h" -#include "snapshot.h" -#include "swap.h" -#include "path.h" - -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; - - /* This should reset all type-specific variables. This should - * not allocate memory, and is called with zero-initialized - * data. It should hence only initialize variables that need - * to be set != 0. */ - void (*init)(Unit *u); - - /* This should free all type-specific variables. It should be - * idempotent. */ - void (*done)(Unit *u); - - /* Actually load data from disk. This may fail, and should set - * load_state to UNIT_LOADED, UNIT_MERGED or leave it at - * UNIT_STUB if no configuration could be found. */ - int (*load)(Unit *u); - - /* 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); - - void (*dump)(Unit *u, FILE *f, const char *prefix); - - int (*start)(Unit *u); - int (*stop)(Unit *u); - int (*reload)(Unit *u); - - int (*kill)(Unit *u, KillWho w, int signo, DBusError *error); - - bool (*can_reload)(Unit *u); - - /* Write all data that cannot be restored from other sources - * away using unit_serialize_item() */ - int (*serialize)(Unit *u, FILE *f, FDSet *fds); - - /* Restore one item from the serialization */ - int (*deserialize_item)(Unit *u, const char *key, const char *data, 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); - - /* Returns the substate specific to this unit type as - * string. This is purely information so that we can give the - * user a more fine grained explanation in which actual state a - * unit is in. */ - const char* (*sub_state_to_string)(Unit *u); - - /* Return true when there is reason to keep this entry around - * even nothing references it and it isn't active in any - * way */ - bool (*check_gc)(Unit *u); - - /* Return true when this unit is suitable for snapshotting */ - bool (*check_snapshot)(Unit *u); - - void (*fd_event)(Unit *u, int fd, uint32_t events, Watch *w); - void (*sigchld_event)(Unit *u, pid_t pid, int code, int status); - void (*timer_event)(Unit *u, uint64_t n_elapsed, Watch *w); - - /* Reset failed state if we are in failed state */ - void (*reset_failed)(Unit *u); - - /* Called whenever any of the cgroups this unit watches for - * ran empty */ - void (*cgroup_notify_empty)(Unit *u); - - /* Called whenever a process of this unit sends us a message */ - void (*notify_message)(Unit *u, pid_t pid, char **tags); - - /* Called whenever a name thus Unit registered for comes or - * goes away. */ - void (*bus_name_owner_change)(Unit *u, const char *name, const char *old_owner, const char *new_owner); - - /* Called whenever a bus PID lookup finishes */ - void (*bus_query_pid_done)(Unit *u, const char *name, pid_t pid); - - /* Called for each message received on the bus */ - DBusHandlerResult (*bus_message_handler)(Unit *u, DBusConnection *c, DBusMessage *message); - - /* Return the unit this unit is following */ - Unit *(*following)(Unit *u); - - /* Return the set of units that are following each other */ - int (*following_set)(Unit *u, Set **s); - - /* 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 - * inactive state. It is the job of the coldplug() call above - * to put the units into the initial state. */ - int (*enumerate)(Manager *m); - - /* Type specific cleanups. */ - void (*shutdown)(Manager *m); - - /* When sending out PropertiesChanged signal, which properties - * shall be invalidated? This is a NUL separated list of - * strings, to minimize relocations a little. */ - const char *bus_invalidating_properties; - - /* The interface name */ - const char *bus_interface; - - UnitStatusMessageFormats status_message_formats; - - /* Can units of this type have multiple names? */ - bool no_alias:1; - - /* Instances make no sense for this type */ - bool no_instances:1; - - /* Exclude from automatic gc */ - bool no_gc:1; -}; - -extern const UnitVTable * const unit_vtable[_UNIT_TYPE_MAX]; - -#define UNIT_VTABLE(u) unit_vtable[(u)->type] - -/* For casting a unit into the various unit types */ -#define DEFINE_CAST(UPPERCASE, MixedCase) \ - static inline MixedCase* UPPERCASE(Unit *u) { \ - if (_unlikely_(!u || u->type != UNIT_##UPPERCASE)) \ - return NULL; \ - \ - return (MixedCase*) u; \ - } - -/* For casting the various unit types into a unit */ -#define UNIT(u) (&(u)->meta) - -DEFINE_CAST(SOCKET, Socket); -DEFINE_CAST(TIMER, Timer); -DEFINE_CAST(SERVICE, Service); -DEFINE_CAST(TARGET, Target); -DEFINE_CAST(DEVICE, Device); -DEFINE_CAST(MOUNT, Mount); -DEFINE_CAST(AUTOMOUNT, Automount); -DEFINE_CAST(SNAPSHOT, Snapshot); -DEFINE_CAST(SWAP, Swap); -DEFINE_CAST(PATH, Path); - -Unit *unit_new(Manager *m, size_t size); -void unit_free(Unit *u); - -int unit_add_name(Unit *u, const char *name); - -int unit_add_dependency(Unit *u, UnitDependency d, Unit *other, bool add_reference); -int unit_add_two_dependencies(Unit *u, UnitDependency d, UnitDependency e, Unit *other, bool add_reference); - -int unit_add_dependency_by_name(Unit *u, UnitDependency d, const char *name, const char *filename, bool add_reference); -int unit_add_two_dependencies_by_name(Unit *u, UnitDependency d, UnitDependency e, const char *name, const char *path, bool add_reference); - -int unit_add_dependency_by_name_inverse(Unit *u, UnitDependency d, const char *name, const char *filename, bool add_reference); -int unit_add_two_dependencies_by_name_inverse(Unit *u, UnitDependency d, UnitDependency e, const char *name, const char *path, bool add_reference); - -int unit_add_exec_dependencies(Unit *u, ExecContext *c); - -int unit_add_cgroup(Unit *u, CGroupBonding *b); -int unit_add_cgroup_from_text(Unit *u, const char *name); -int unit_add_default_cgroups(Unit *u); -CGroupBonding* unit_get_default_cgroup(Unit *u); -int unit_add_cgroup_attribute(Unit *u, const char *controller, const char *name, const char *value, CGroupAttributeMapCallback map_callback); - -int unit_choose_id(Unit *u, const char *name); -int unit_set_description(Unit *u, const char *description); - -bool unit_check_gc(Unit *u); - -void unit_add_to_load_queue(Unit *u); -void unit_add_to_dbus_queue(Unit *u); -void unit_add_to_cleanup_queue(Unit *u); -void unit_add_to_gc_queue(Unit *u); - -int unit_merge(Unit *u, Unit *other); -int unit_merge_by_name(Unit *u, const char *other); - -Unit *unit_follow_merge(Unit *u); - -int unit_load_fragment_and_dropin(Unit *u); -int unit_load_fragment_and_dropin_optional(Unit *u); -int unit_load(Unit *unit); - -const char *unit_description(Unit *u); - -bool unit_has_name(Unit *u, const char *name); - -UnitActiveState unit_active_state(Unit *u); - -const char* unit_sub_state_to_string(Unit *u); - -void unit_dump(Unit *u, FILE *f, const char *prefix); - -bool unit_can_reload(Unit *u); -bool unit_can_start(Unit *u); -bool unit_can_isolate(Unit *u); - -int unit_start(Unit *u); -int unit_stop(Unit *u); -int unit_reload(Unit *u); - -int unit_kill(Unit *u, KillWho w, int signo, DBusError *error); - -void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_success); - -int unit_watch_fd(Unit *u, int fd, uint32_t events, Watch *w); -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); -void unit_unwatch_timer(Unit *u, Watch *w); - -int unit_watch_bus_name(Unit *u, const char *name); -void unit_unwatch_bus_name(Unit *u, const char *name); - -bool unit_job_is_applicable(Unit *u, JobType j); - -int set_unit_path(const char *p); - -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); - -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); -void unit_serialize_item(Unit *u, FILE *f, const char *key, const char *value); -int unit_deserialize(Unit *u, FILE *f, FDSet *fds); - -int unit_add_node_link(Unit *u, const char *what, bool wants); - -int unit_coldplug(Unit *u); - -void unit_status_printf(Unit *u, const char *status, const char *format, ...); - -bool unit_need_daemon_reload(Unit *u); - -void unit_reset_failed(Unit *u); - -Unit *unit_following(Unit *u); - -bool unit_pending_inactive(Unit *u); -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); - -bool unit_condition_test(Unit *u); - -UnitFileState unit_get_unit_file_state(Unit *u); - -Unit* unit_ref_set(UnitRef *ref, Unit *u); -void unit_ref_unset(UnitRef *ref); - -#define UNIT_DEREF(ref) ((ref).unit) - -int unit_add_one_mount_link(Unit *u, Mount *m); -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); diff --git a/src/core/user.conf b/src/core/user.conf deleted file mode 100644 index e02b46bc3a..0000000000 --- a/src/core/user.conf +++ /dev/null @@ -1,17 +0,0 @@ -# This file is part of systemd. -# -# 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. -# -# See systemd.conf(5) for details - -[Manager] -#LogLevel=info -#LogTarget=console -#LogColor=yes -#LogLocation=no -#DefaultControllers=cpu -#DefaultStandardOutput=inherit -#DefaultStandardError=inherit |