diff options
Diffstat (limited to 'src/core/manager.c')
-rw-r--r-- | src/core/manager.c | 646 |
1 files changed, 474 insertions, 172 deletions
diff --git a/src/core/manager.c b/src/core/manager.c index 84adb9c666..930df4e23a 100644 --- a/src/core/manager.c +++ b/src/core/manager.c @@ -1,22 +1,4 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ #include <errno.h> #include <fcntl.h> @@ -41,6 +23,7 @@ #include "sd-path.h" #include "alloc-util.h" +#include "all-units.h" #include "audit-fd.h" #include "boot-timestamps.h" #include "bus-common-errors.h" @@ -76,14 +59,17 @@ #include "path-util.h" #include "process-util.h" #include "ratelimit.h" +#include "rlimit-util.h" #include "rm-rf.h" #include "signal-util.h" +#include "socket-util.h" #include "special.h" #include "stat-util.h" #include "string-table.h" #include "string-util.h" #include "strv.h" #include "strxcpyx.h" +#include "syslog-util.h" #include "terminal-util.h" #include "time-util.h" #include "transaction.h" @@ -118,6 +104,7 @@ static int manager_dispatch_user_lookup_fd(sd_event_source *source, int fd, uint static int manager_dispatch_jobs_in_progress(sd_event_source *source, usec_t usec, void *userdata); static int manager_dispatch_run_queue(sd_event_source *source, void *userdata); static int manager_dispatch_sigchld(sd_event_source *source, void *userdata); +static int manager_dispatch_timezone_change(sd_event_source *source, const struct inotify_event *event, void *userdata); static int manager_run_environment_generators(Manager *m); static int manager_run_generators(Manager *m); @@ -362,35 +349,27 @@ static void manager_close_idle_pipe(Manager *m) { static int manager_setup_time_change(Manager *m) { int r; - /* We only care for the cancellation event, hence we set the - * timeout to the latest possible value. */ - struct itimerspec its = { - .it_value.tv_sec = TIME_T_MAX, - }; - assert(m); - assert_cc(sizeof(time_t) == sizeof(TIME_T_MAX)); if (m->test_run_flags) return 0; - /* Uses TFD_TIMER_CANCEL_ON_SET to get notifications whenever - * CLOCK_REALTIME makes a jump relative to CLOCK_MONOTONIC */ + m->time_change_event_source = sd_event_source_unref(m->time_change_event_source); + m->time_change_fd = safe_close(m->time_change_fd); - m->time_change_fd = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK|TFD_CLOEXEC); + m->time_change_fd = time_change_fd(); if (m->time_change_fd < 0) - return log_error_errno(errno, "Failed to create timerfd: %m"); - - if (timerfd_settime(m->time_change_fd, TFD_TIMER_ABSTIME|TFD_TIMER_CANCEL_ON_SET, &its, NULL) < 0) { - log_debug_errno(errno, "Failed to set up TFD_TIMER_CANCEL_ON_SET, ignoring: %m"); - m->time_change_fd = safe_close(m->time_change_fd); - return 0; - } + return log_error_errno(m->time_change_fd, "Failed to create timer change timer fd: %m"); r = sd_event_add_io(m->event, &m->time_change_event_source, m->time_change_fd, EPOLLIN, manager_dispatch_time_change_fd, m); if (r < 0) return log_error_errno(r, "Failed to create time change event source: %m"); + /* Schedule this slightly earlier than the .timer event sources */ + r = sd_event_source_set_priority(m->time_change_event_source, SD_EVENT_PRIORITY_NORMAL-1); + if (r < 0) + return log_error_errno(r, "Failed to set priority of time change event sources: %m"); + (void) sd_event_source_set_description(m->time_change_event_source, "manager-time-change"); log_debug("Set up TFD_TIMER_CANCEL_ON_SET timerfd."); @@ -398,6 +377,70 @@ static int manager_setup_time_change(Manager *m) { return 0; } +static int manager_read_timezone_stat(Manager *m) { + struct stat st; + bool changed; + + assert(m); + + /* Read the current stat() data of /etc/localtime so that we detect changes */ + if (lstat("/etc/localtime", &st) < 0) { + log_debug_errno(errno, "Failed to stat /etc/localtime, ignoring: %m"); + changed = m->etc_localtime_accessible; + m->etc_localtime_accessible = false; + } else { + usec_t k; + + k = timespec_load(&st.st_mtim); + changed = !m->etc_localtime_accessible || k != m->etc_localtime_mtime; + + m->etc_localtime_mtime = k; + m->etc_localtime_accessible = true; + } + + return changed; +} + +static int manager_setup_timezone_change(Manager *m) { + _cleanup_(sd_event_source_unrefp) sd_event_source *new_event = NULL; + int r; + + assert(m); + + if (m->test_run_flags != 0) + return 0; + + /* We watch /etc/localtime for three events: change of the link count (which might mean removal from /etc even + * though another link might be kept), renames, and file close operations after writing. Note we don't bother + * with IN_DELETE_SELF, as that would just report when the inode is removed entirely, i.e. after the link count + * went to zero and all fds to it are closed. + * + * Note that we never follow symlinks here. This is a simplification, but should cover almost all cases + * correctly. + * + * Note that we create the new event source first here, before releasing the old one. This should optimize + * behaviour as this way sd-event can reuse the old watch in case the inode didn't change. */ + + r = sd_event_add_inotify(m->event, &new_event, "/etc/localtime", + IN_ATTRIB|IN_MOVE_SELF|IN_CLOSE_WRITE|IN_DONT_FOLLOW, manager_dispatch_timezone_change, m); + if (r == -ENOENT) /* If the file doesn't exist yet, subscribe to /etc instead, and wait until it is created + * either by O_CREATE or by rename() */ + r = sd_event_add_inotify(m->event, &new_event, "/etc", + IN_CREATE|IN_MOVED_TO|IN_ONLYDIR, manager_dispatch_timezone_change, m); + if (r < 0) + return log_error_errno(r, "Failed to create timezone change event source: %m"); + + /* Schedule this slightly earlier than the .timer event sources */ + r = sd_event_source_set_priority(new_event, SD_EVENT_PRIORITY_NORMAL-1); + if (r < 0) + return log_error_errno(r, "Failed to set priority of timezone change event sources: %m"); + + sd_event_source_unref(m->timezone_change_event_source); + m->timezone_change_event_source = TAKE_PTR(new_event); + + return 0; +} + static int enable_special_signals(Manager *m) { _cleanup_close_ int fd = -1; @@ -426,6 +469,8 @@ static int enable_special_signals(Manager *m) { return 0; } +#define RTSIG_IF_AVAILABLE(signum) (signum <= SIGRTMAX ? signum : -1) + static int manager_setup_signals(Manager *m) { struct sigaction sa = { .sa_handler = SIG_DFL, @@ -479,22 +524,22 @@ static int manager_setup_signals(Manager *m) { /* .. one free signal here ... */ -#if !defined(__hppa64__) && !defined(__hppa__) - /* Apparently Linux on hppa has fewer RT - * signals (SIGRTMAX is SIGRTMIN+25 there), - * hence let's not try to make use of them - * here. Since these commands are accessible - * by different means and only really a safety - * net, the missing functionality on hppa - * shouldn't matter. */ - - 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 (obsolete) */ + /* Apparently Linux on hppa had fewer RT signals until v3.18, + * SIGRTMAX was SIGRTMIN+25, and then SIGRTMIN was lowered, + * see commit v3.17-7614-g1f25df2eff. + * + * We cannot unconditionally make use of those signals here, + * so let's use a runtime check. Since these commands are + * accessible by different means and only really a safety + * net, the missing functionality on hppa shouldn't matter. + */ + + RTSIG_IF_AVAILABLE(SIGRTMIN+26), /* systemd: set log target to journal-or-kmsg */ + RTSIG_IF_AVAILABLE(SIGRTMIN+27), /* systemd: set log target to console */ + RTSIG_IF_AVAILABLE(SIGRTMIN+28), /* systemd: set log target to kmsg */ + RTSIG_IF_AVAILABLE(SIGRTMIN+29), /* systemd: set log target to syslog-or-kmsg (obsolete) */ /* ... one free signal here SIGRTMIN+30 ... */ -#endif -1); assert_se(sigprocmask(SIG_SETMASK, &mask, NULL) == 0); @@ -667,7 +712,7 @@ static int manager_setup_sigchld_event_source(Manager *m) { } int manager_new(UnitFileScope scope, unsigned test_run_flags, Manager **_m) { - Manager *m; + _cleanup_(manager_freep) Manager *m = NULL; int r; assert(_m); @@ -686,6 +731,8 @@ int manager_new(UnitFileScope scope, unsigned test_run_flags, Manager **_m) { m->default_timeout_start_usec = DEFAULT_TIMEOUT_USEC; m->default_timeout_stop_usec = DEFAULT_TIMEOUT_USEC; m->default_restart_usec = DEFAULT_RESTART_USEC; + m->original_log_level = -1; + m->original_log_target = _LOG_TARGET_INVALID; #if ENABLE_EFI if (MANAGER_IS_SYSTEM(m) && detect_container() <= 0) @@ -729,62 +776,74 @@ int manager_new(UnitFileScope scope, unsigned test_run_flags, Manager **_m) { r = manager_default_environment(m); if (r < 0) - goto fail; + return r; r = hashmap_ensure_allocated(&m->units, &string_hash_ops); if (r < 0) - goto fail; + return r; r = hashmap_ensure_allocated(&m->jobs, NULL); if (r < 0) - goto fail; + return r; r = hashmap_ensure_allocated(&m->cgroup_unit, &path_hash_ops); if (r < 0) - goto fail; + return r; r = hashmap_ensure_allocated(&m->watch_bus, &string_hash_ops); if (r < 0) - goto fail; + return r; + + r = manager_setup_prefix(m); + if (r < 0) + return r; + + m->udev = udev_new(); + if (!m->udev) + return -ENOMEM; r = sd_event_default(&m->event); if (r < 0) - goto fail; + return r; r = manager_setup_run_queue(m); if (r < 0) - goto fail; + return r; - r = manager_setup_signals(m); - if (r < 0) - goto fail; + if (test_run_flags == MANAGER_TEST_RUN_MINIMAL) { + m->cgroup_root = strdup(""); + if (!m->cgroup_root) + return -ENOMEM; + } else { + r = manager_setup_signals(m); + if (r < 0) + return r; - r = manager_setup_cgroup(m); - if (r < 0) - goto fail; + r = manager_setup_cgroup(m); + if (r < 0) + return r; - r = manager_setup_time_change(m); - if (r < 0) - goto fail; + r = manager_setup_time_change(m); + if (r < 0) + return r; - r = manager_setup_sigchld_event_source(m); - if (r < 0) - goto fail; + r = manager_read_timezone_stat(m); + if (r < 0) + return r; - m->udev = udev_new(); - if (!m->udev) { - r = -ENOMEM; - goto fail; - } + r = manager_setup_timezone_change(m); + if (r < 0) + return r; - r = manager_setup_prefix(m); - if (r < 0) - goto fail; + r = manager_setup_sigchld_event_source(m); + if (r < 0) + return r; + } if (MANAGER_IS_SYSTEM(m) && test_run_flags == 0) { r = mkdir_label("/run/systemd/units", 0755); if (r < 0 && r != -EEXIST) - goto fail; + return r; } m->taint_usr = @@ -794,12 +853,9 @@ int manager_new(UnitFileScope scope, unsigned test_run_flags, Manager **_m) { /* Note that we do not set up the notify fd here. We do that after deserialization, * since they might have gotten serialized across the reexec. */ - *_m = m; - return 0; + *_m = TAKE_PTR(m); -fail: - manager_free(m); - return r; + return 0; } static int manager_setup_notify(Manager *m) { @@ -841,8 +897,7 @@ static int manager_setup_notify(Manager *m) { if (r < 0) return log_error_errno(errno, "SO_PASSCRED failed: %m"); - m->notify_fd = fd; - fd = -1; + m->notify_fd = TAKE_FD(fd); log_debug("Using notification socket %s", m->notify_socket); } @@ -1183,7 +1238,6 @@ static void manager_clear_jobs_and_units(Manager *m) { Manager* manager_free(Manager *m) { UnitType c; - int i; ExecDirectoryType dt; if (!m) @@ -1222,6 +1276,7 @@ Manager* manager_free(Manager *m) { sd_event_source_unref(m->notify_event_source); sd_event_source_unref(m->cgroups_agent_event_source); sd_event_source_unref(m->time_change_event_source); + sd_event_source_unref(m->timezone_change_event_source); sd_event_source_unref(m->jobs_in_progress_event_source); sd_event_source_unref(m->run_queue_event_source); sd_event_source_unref(m->user_lookup_event_source); @@ -1251,8 +1306,7 @@ Manager* manager_free(Manager *m) { free(m->switch_root); free(m->switch_root_init); - for (i = 0; i < _RLIMIT_MAX; i++) - m->rlimit[i] = mfree(m->rlimit[i]); + rlimit_free_all(m->rlimit); assert(hashmap_isempty(m->units_requiring_mounts_for)); hashmap_free(m->units_requiring_mounts_for); @@ -1266,23 +1320,37 @@ Manager* manager_free(Manager *m) { return mfree(m); } -void manager_enumerate(Manager *m) { +static void manager_enumerate_perpetual(Manager *m) { UnitType c; assert(m); - /* Let's ask every type to load all units from disk/kernel - * that it might know */ + /* 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_type_supported(c)) { log_debug("Unit type .%s is not supported on this system.", unit_type_to_string(c)); continue; } - if (!unit_vtable[c]->enumerate) + if (unit_vtable[c]->enumerate_perpetual) + unit_vtable[c]->enumerate_perpetual(m); + } +} + +static void manager_enumerate(Manager *m) { + 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_type_supported(c)) { + log_debug("Unit type .%s is not supported on this system.", unit_type_to_string(c)); continue; + } - unit_vtable[c]->enumerate(m); + if (unit_vtable[c]->enumerate) + unit_vtable[c]->enumerate(m); } manager_dispatch_load_queue(m); @@ -1296,7 +1364,9 @@ static void manager_coldplug(Manager *m) { assert(m); - /* Then, let's set up their initial state. */ + log_debug("Invoking unit coldplug() handlers…"); + + /* Let's place the units back into their deserialized state */ HASHMAP_FOREACH_KEY(u, k, m->units, i) { /* ignore aliases */ @@ -1309,6 +1379,26 @@ static void manager_coldplug(Manager *m) { } } +static void manager_catchup(Manager *m) { + Iterator i; + Unit *u; + char *k; + + assert(m); + + log_debug("Invoking unit catchup() handlers…"); + + /* Let's catch up on any state changes that happened while we were reloading/reexecing */ + HASHMAP_FOREACH_KEY(u, k, m->units, i) { + + /* ignore aliases */ + if (u->id != k) + continue; + + unit_catchup(u); + } +} + static void manager_build_unit_path_cache(Manager *m) { char **i; int r; @@ -1409,6 +1499,48 @@ static bool manager_dbus_is_running(Manager *m, bool deserialized) { return true; } +static void manager_setup_bus(Manager *m) { + assert(m); + + /* Let's set up our private bus connection now, unconditionally */ + (void) bus_init_private(m); + + /* If we are in --user mode also connect to the system bus now */ + if (MANAGER_IS_USER(m)) + (void) bus_init_system(m); + + /* Let's connect to the bus now, but only if the unit is supposed to be up */ + if (manager_dbus_is_running(m, MANAGER_IS_RELOADING(m))) { + (void) bus_init_api(m); + + if (MANAGER_IS_SYSTEM(m)) + (void) bus_init_system(m); + } +} + +static void manager_preset_all(Manager *m) { + int r; + + assert(m); + + if (m->first_boot <= 0) + return; + + if (!MANAGER_IS_SYSTEM(m)) + return; + + if (m->test_run_flags != 0) + return; + + /* If this is the first boot, and we are in the host system, then preset everything */ + r = unit_file_preset_all(UNIT_FILE_SYSTEM, 0, NULL, UNIT_FILE_PRESET_ENABLE_ONLY, NULL, 0); + if (r < 0) + log_full_errno(r == -EEXIST ? LOG_NOTICE : LOG_WARNING, r, + "Failed to populate /etc with preset unit settings, ignoring: %m"); + else + log_info("Populated /etc with preset unit settings."); +} + int manager_startup(Manager *m, FILE *serialization, FDSet *fds) { int r; @@ -1432,19 +1564,7 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) { if (r < 0) return r; - /* If this is the first boot, and we are in the host system, then preset everything */ - if (m->first_boot > 0 && - MANAGER_IS_SYSTEM(m) && - !m->test_run_flags) { - - r = unit_file_preset_all(UNIT_FILE_SYSTEM, 0, NULL, UNIT_FILE_PRESET_ENABLE_ONLY, NULL, 0); - if (r < 0) - log_full_errno(r == -EEXIST ? LOG_NOTICE : LOG_WARNING, r, - "Failed to populate /etc with preset unit settings, ignoring: %m"); - else - log_info("Populated /etc with preset unit settings."); - } - + manager_preset_all(m); lookup_paths_reduce(&m->lookup_paths); manager_build_unit_path_cache(m); @@ -1456,6 +1576,7 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) { /* First, enumerate what we can from all config files */ dual_timestamp_get(m->timestamps + MANAGER_TIMESTAMP_UNITS_LOAD_START); + manager_enumerate_perpetual(m); manager_enumerate(m); dual_timestamp_get(m->timestamps + MANAGER_TIMESTAMP_UNITS_LOAD_FINISH); @@ -1489,20 +1610,8 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) { /* This shouldn't fail, except if things are really broken. */ return r; - /* Let's set up our private bus connection now, unconditionally */ - (void) bus_init_private(m); - - /* If we are in --user mode also connect to the system bus now */ - if (MANAGER_IS_USER(m)) - (void) bus_init_system(m); - - /* Let's connect to the bus now, but only if the unit is supposed to be up */ - if (manager_dbus_is_running(m, !!serialization)) { - (void) bus_init_api(m); - - if (MANAGER_IS_SYSTEM(m)) - (void) bus_init_system(m); - } + /* Connect to the bus if we are good for it */ + manager_setup_bus(m); /* Now that we are connected to all possible busses, let's deserialize who is tracking us. */ (void) bus_track_coldplug(m, &m->subscribed, false, m->deserialized_subscribed); @@ -1530,6 +1639,9 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) { m->send_reloading_done = true; } + /* Let's finally catch up with any changes that took place while we were reloading/reexecing */ + manager_catchup(m); + return 0; } @@ -1668,6 +1780,42 @@ Unit *manager_get_unit(Manager *m, const char *name) { return hashmap_get(m->units, name); } +static int manager_dispatch_target_deps_queue(Manager *m) { + Unit *u; + unsigned k; + int r = 0; + + static const UnitDependency deps[] = { + UNIT_REQUIRED_BY, + UNIT_REQUISITE_OF, + UNIT_WANTED_BY, + UNIT_BOUND_BY + }; + + assert(m); + + while ((u = m->target_deps_queue)) { + assert(u->in_target_deps_queue); + + LIST_REMOVE(target_deps_queue, u->manager->target_deps_queue, u); + u->in_target_deps_queue = false; + + for (k = 0; k < ELEMENTSOF(deps); k++) { + Unit *target; + Iterator i; + void *v; + + HASHMAP_FOREACH_KEY(v, target, u->dependencies[deps[k]], i) { + r = unit_add_default_target_dependency(u, target); + if (r < 0) + return r; + } + } + } + + return r; +} + unsigned manager_dispatch_load_queue(Manager *m) { Unit *u; unsigned n = 0; @@ -1691,6 +1839,11 @@ unsigned manager_dispatch_load_queue(Manager *m) { } m->dispatching_load_queue = false; + + /* Dispatch the units waiting for their target dependencies to be added now, as all targets that we know about + * should be loaded and have aliases resolved */ + (void) manager_dispatch_target_deps_queue(m); + return n; } @@ -1701,6 +1854,7 @@ int manager_load_unit_prepare( sd_bus_error *e, Unit **_ret) { + _cleanup_(unit_freep) Unit *cleanup_ret = NULL; Unit *ret; UnitType t; int r; @@ -1733,29 +1887,26 @@ int manager_load_unit_prepare( return 1; } - ret = unit_new(m, unit_vtable[t]->object_size); + ret = cleanup_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); + if (!ret->fragment_path) return -ENOMEM; - } } r = unit_add_name(ret, name); - if (r < 0) { - unit_free(ret); + if (r < 0) return r; - } unit_add_to_load_queue(ret); unit_add_to_dbus_queue(ret); unit_add_to_gc_queue(ret); *_ret = ret; + cleanup_ret = NULL; return 0; } @@ -1782,7 +1933,32 @@ int manager_load_unit( manager_dispatch_load_queue(m); *_ret = unit_follow_merge(*_ret); + return 0; +} + +int manager_load_startable_unit_or_warn( + Manager *m, + const char *name, + const char *path, + Unit **ret) { + + /* Load a unit, make sure it loaded fully and is not masked. */ + + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + Unit *unit; + int r; + r = manager_load_unit(m, name, path, &error, &unit); + if (r < 0) + return log_error_errno(r, "Failed to load %s %s: %s", + name ? "unit" : "unit file", name ?: path, + bus_error_message(&error, r)); + + r = bus_unit_validate_load_state(unit, &error); + if (r < 0) + return log_error_errno(r, "%s", bus_error_message(&error, r)); + + *ret = unit; return 0; } @@ -1853,8 +2029,7 @@ int manager_get_dump_string(Manager *m, char **ret) { f = safe_fclose(f); - *ret = dump; - dump = NULL; + *ret = TAKE_PTR(dump); return 0; } @@ -2049,7 +2224,7 @@ static int manager_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t _cleanup_free_ Unit **array_copy = NULL; Unit *u1, *u2, **array; int r, *fd_array = NULL; - unsigned n_fds = 0; + size_t n_fds = 0; bool found = false; ssize_t n; @@ -2283,7 +2458,7 @@ static void manager_handle_ctrl_alt_del(Manager *m) { * 7 times within 2s, we reboot/shutdown immediately, * unless it was disabled in system.conf */ - if (ratelimit_test(&m->ctrl_alt_del_ratelimit) || m->cad_burst_action == EMERGENCY_ACTION_NONE) + if (ratelimit_below(&m->ctrl_alt_del_ratelimit) || m->cad_burst_action == EMERGENCY_ACTION_NONE) manager_start_target(m, SPECIAL_CTRL_ALT_DEL_TARGET, JOB_REPLACE_IRREVERSIBLY); else emergency_action(m, m->cad_burst_action, NULL, @@ -2335,8 +2510,7 @@ static int manager_dispatch_signal_fd(sd_event_source *source, int fd, uint32_t case SIGTERM: if (MANAGER_IS_SYSTEM(m)) { - /* This is for compatibility with the - * original sysvinit */ + /* This is for compatibility with the original sysvinit */ r = verify_run_space_and_log("Refusing to reexecute"); if (r >= 0) m->exit_code = MANAGER_REEXECUTE; @@ -2353,21 +2527,20 @@ static int manager_dispatch_signal_fd(sd_event_source *source, int fd, uint32_t break; case SIGWINCH: + /* This is a nop on non-init */ if (MANAGER_IS_SYSTEM(m)) manager_start_target(m, SPECIAL_KBREQUEST_TARGET, JOB_REPLACE); - /* This is a nop on non-init */ break; case SIGPWR: + /* This is a nop on non-init */ if (MANAGER_IS_SYSTEM(m)) manager_start_target(m, SPECIAL_SIGPWR_TARGET, JOB_REPLACE); - /* This is a nop on non-init */ break; case SIGUSR1: - if (manager_dbus_is_running(m, false)) { log_info("Trying to reconnect to bus..."); @@ -2450,13 +2623,11 @@ static int manager_dispatch_signal_fd(sd_event_source *source, int fd, uint32_t break; case 22: - log_set_max_level(LOG_DEBUG); - log_info("Setting log level to debug."); + manager_override_log_level(m, LOG_DEBUG); break; case 23: - log_set_max_level(LOG_INFO); - log_info("Setting log level to info."); + manager_restore_original_log_level(m); break; case 24: @@ -2470,18 +2641,15 @@ static int manager_dispatch_signal_fd(sd_event_source *source, int fd, uint32_t case 26: case 29: /* compatibility: used to be mapped to LOG_TARGET_SYSLOG_OR_KMSG */ - log_set_target(LOG_TARGET_JOURNAL_OR_KMSG); - log_notice("Setting log target to journal-or-kmsg."); + manager_restore_original_log_target(m); break; case 27: - log_set_target(LOG_TARGET_CONSOLE); - log_notice("Setting log target to console."); + manager_override_log_target(m, LOG_TARGET_CONSOLE); break; case 28: - log_set_target(LOG_TARGET_KMSG); - log_notice("Setting log target to kmsg."); + manager_override_log_target(m, LOG_TARGET_KMSG); break; default: @@ -2502,14 +2670,10 @@ static int manager_dispatch_time_change_fd(sd_event_source *source, int fd, uint log_struct(LOG_DEBUG, "MESSAGE_ID=" SD_MESSAGE_TIME_CHANGE_STR, - LOG_MESSAGE("Time has been changed"), - NULL); + LOG_MESSAGE("Time has been changed")); /* Restart the watch */ - m->time_change_event_source = sd_event_source_unref(m->time_change_event_source); - m->time_change_fd = safe_close(m->time_change_fd); - - manager_setup_time_change(m); + (void) manager_setup_time_change(m); HASHMAP_FOREACH(u, m->units, i) if (UNIT_VTABLE(u)->time_change) @@ -2518,6 +2682,41 @@ static int manager_dispatch_time_change_fd(sd_event_source *source, int fd, uint return 0; } +static int manager_dispatch_timezone_change( + sd_event_source *source, + const struct inotify_event *e, + void *userdata) { + + Manager *m = userdata; + int changed; + Iterator i; + Unit *u; + + assert(m); + + log_debug("inotify event for /etc/localtime"); + + changed = manager_read_timezone_stat(m); + if (changed < 0) + return changed; + if (!changed) + return 0; + + /* Something changed, restart the watch, to ensure we watch the new /etc/localtime if it changed */ + (void) manager_setup_timezone_change(m); + + /* Read the new timezone */ + tzset(); + + log_debug("Timezone has been changed (now: %s).", tzname[daylight]); + + HASHMAP_FOREACH(u, m->units, i) + if (UNIT_VTABLE(u)->timezone_change) + UNIT_VTABLE(u)->timezone_change(u); + + return 0; +} + static int manager_dispatch_idle_pipe_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata) { Manager *m = userdata; @@ -2580,7 +2779,7 @@ int manager_loop(Manager *m) { if (m->runtime_watchdog > 0 && m->runtime_watchdog != USEC_INFINITY && MANAGER_IS_SYSTEM(m)) watchdog_ping(); - if (!ratelimit_test(&rl)) { + if (!ratelimit_below(&rl)) { /* Yay, something is going seriously wrong, pause a little */ log_warning("Looping too fast. Throttling execution a little."); sleep(1); @@ -2644,12 +2843,19 @@ int manager_load_unit_from_dbus_path(Manager *m, const char *s, sd_bus_error *e, return 0; } - return sd_bus_error_setf(e, BUS_ERROR_NO_UNIT_FOR_INVOCATION_ID, "No unit with the specified invocation ID " SD_ID128_FORMAT_STR " known.", SD_ID128_FORMAT_VAL(invocation_id)); + return sd_bus_error_setf(e, BUS_ERROR_NO_UNIT_FOR_INVOCATION_ID, + "No unit with the specified invocation ID " SD_ID128_FORMAT_STR " known.", + SD_ID128_FORMAT_VAL(invocation_id)); } /* If this didn't work, we check if this is a unit name */ - if (!unit_name_is_valid(n, UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE)) - return sd_bus_error_setf(e, SD_BUS_ERROR_INVALID_ARGS, "Unit name %s is neither a valid invocation ID nor unit name.", n); + if (!unit_name_is_valid(n, UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE)) { + _cleanup_free_ char *nn = NULL; + + nn = cescape(n); + return sd_bus_error_setf(e, SD_BUS_ERROR_INVALID_ARGS, + "Unit name %s is neither a valid invocation ID nor unit name.", strnull(nn)); + } r = manager_load_unit(m, n, NULL, e, &u); if (r < 0) @@ -2813,6 +3019,11 @@ int manager_serialize(Manager *m, FILE *f, FDSet *fds, bool switching_root) { fprintf(f, "taint-logged=%s\n", yes_no(m->taint_logged)); fprintf(f, "service-watchdogs=%s\n", yes_no(m->service_watchdogs)); + if (m->log_level_overridden) + fprintf(f, "log-level-override=%i\n", log_get_max_level()); + if (m->log_target_overridden) + fprintf(f, "log-target-override=%s\n", log_target_to_string(log_get_target())); + for (q = 0; q < _MANAGER_TIMESTAMP_MAX; q++) { /* The userspace and finish timestamps only apply to the host system, hence only serialize them there */ if (in_initrd() && IN_SET(q, MANAGER_TIMESTAMP_USERSPACE, MANAGER_TIMESTAMP_FINISH)) @@ -2897,8 +3108,9 @@ int manager_serialize(Manager *m, FILE *f, FDSet *fds, bool switching_root) { assert(m->n_reloading > 0); m->n_reloading--; - if (ferror(f)) - return -EIO; + r = fflush_and_check(f); + if (r < 0) + return r; r = bus_fdset_add_all(m, fds); if (r < 0) @@ -2996,6 +3208,24 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) { else m->service_watchdogs = b; + } else if ((val = startswith(l, "log-level-override="))) { + int level; + + level = log_level_from_string(val); + if (level < 0) + log_notice("Failed to parse log-level-override value '%s', ignoring.", val); + else + manager_override_log_level(m, level); + + } else if ((val = startswith(l, "log-target-override="))) { + LogTarget target; + + target = log_target_from_string(val); + if (target < 0) + log_notice("Failed to parse log-target-override value '%s', ignoring.", val); + else + manager_override_log_target(m, target); + } else if (startswith(l, "env=")) { r = deserialize_environment(&m->environment, l); if (r == -ENOMEM) @@ -3126,6 +3356,17 @@ finish: return r; } +static void manager_flush_finished_jobs(Manager *m) { + Job *j; + + while ((j = set_steal_first(m->pending_finished_jobs))) { + bus_job_send_removed_signal(j); + job_free(j); + } + + m->pending_finished_jobs = set_free(m->pending_finished_jobs); +} + int manager_reload(Manager *m) { int r, q; _cleanup_fclose_ FILE *f = NULL; @@ -3194,8 +3435,7 @@ int manager_reload(Manager *m) { r = q; } - fclose(f); - f = NULL; + f = safe_fclose(f); /* Re-register notify_fd as event source */ q = manager_setup_notify(m); @@ -3222,17 +3462,23 @@ int manager_reload(Manager *m) { exec_runtime_vacuum(m); + assert(m->n_reloading > 0); + m->n_reloading--; + /* It might be safe to log to the journal now and connect to dbus */ manager_recheck_journal(m); manager_recheck_dbus(m); + /* Let's finally catch up with any changes that took place while we were reloading/reexecing */ + manager_catchup(m); + /* Sync current state of bus names with our set of listening units */ q = manager_enqueue_sync_bus_names(m); if (q < 0 && r >= 0) r = q; - assert(m->n_reloading > 0); - m->n_reloading--; + if (!MANAGER_IS_RELOADING(m)) + manager_flush_finished_jobs(m); m->send_reloading_done = true; @@ -3280,8 +3526,7 @@ static void log_taint_string(Manager *m) { log_struct(LOG_NOTICE, LOG_MESSAGE("System is tainted: %s", taint), "TAINT=%s", taint, - "MESSAGE_ID=" SD_MESSAGE_TAINTED_STR, - NULL); + "MESSAGE_ID=" SD_MESSAGE_TAINTED_STR); } static void manager_notify_finished(Manager *m) { @@ -3328,8 +3573,7 @@ static void manager_notify_finished(Manager *m) { format_timespan(kernel, sizeof(kernel), kernel_usec, USEC_PER_MSEC), format_timespan(initrd, sizeof(initrd), initrd_usec, USEC_PER_MSEC), format_timespan(userspace, sizeof(userspace), userspace_usec, USEC_PER_MSEC), - format_timespan(sum, sizeof(sum), total_usec, USEC_PER_MSEC)), - NULL); + format_timespan(sum, sizeof(sum), total_usec, USEC_PER_MSEC))); } else { /* The initrd-less case on bare-metal*/ @@ -3344,8 +3588,7 @@ static void manager_notify_finished(Manager *m) { buf, format_timespan(kernel, sizeof(kernel), kernel_usec, USEC_PER_MSEC), format_timespan(userspace, sizeof(userspace), userspace_usec, USEC_PER_MSEC), - format_timespan(sum, sizeof(sum), total_usec, USEC_PER_MSEC)), - NULL); + format_timespan(sum, sizeof(sum), total_usec, USEC_PER_MSEC))); } } else { /* The container and --user case */ @@ -3356,8 +3599,7 @@ static void manager_notify_finished(Manager *m) { "MESSAGE_ID=" SD_MESSAGE_USER_STARTUP_FINISHED_STR, "USERSPACE_USEC="USEC_FMT, userspace_usec, LOG_MESSAGE("Startup finished in %s.", - format_timespan(sum, sizeof(sum), total_usec, USEC_PER_MSEC)), - NULL); + format_timespan(sum, sizeof(sum), total_usec, USEC_PER_MSEC))); } bus_manager_send_finished(m, firmware_usec, loader_usec, kernel_usec, initrd_usec, userspace_usec, total_usec); @@ -3597,6 +3839,9 @@ void manager_recheck_dbus(Manager *m) { * connection of the API bus). That's because the system bus after all runs as service of the system instance, * while in the user instance we can assume it's already there. */ + if (MANAGER_IS_RELOADING(m)) + return; /* don't check while we are reloading… */ + if (manager_dbus_is_running(m, false)) { (void) bus_init_api(m); @@ -3647,6 +3892,10 @@ void manager_recheck_journal(Manager *m) { if (getpid_cached() != 1) return; + /* Don't check this while we are reloading, things might still change */ + if (MANAGER_IS_RELOADING(m)) + return; + /* The journal is fully and entirely up? If so, let's permit logging to it, if that's configured. If the * journal is down, don't ever log to it, otherwise we might end up deadlocking ourselves as we might trigger * an activation ourselves we can't fulfill. */ @@ -3786,7 +4035,7 @@ Set *manager_get_units_requiring_mounts_for(Manager *m, const char *path) { assert(path); strcpy(p, path); - path_kill_slashes(p); + path_simplify(p, false); return hashmap_get(m->units_requiring_mounts_for, streq(p, "/") ? "" : p); } @@ -4215,6 +4464,59 @@ void manager_unref_console(Manager *m) { m->no_console_output = false; /* unset no_console_output flag, since the console is definitely free now */ } +void manager_override_log_level(Manager *m, int level) { + _cleanup_free_ char *s = NULL; + assert(m); + + if (!m->log_level_overridden) { + m->original_log_level = log_get_max_level(); + m->log_level_overridden = true; + } + + (void) log_level_to_string_alloc(level, &s); + log_info("Setting log level to %s.", strna(s)); + + log_set_max_level(level); +} + +void manager_restore_original_log_level(Manager *m) { + _cleanup_free_ char *s = NULL; + assert(m); + + if (!m->log_level_overridden) + return; + + (void) log_level_to_string_alloc(m->original_log_level, &s); + log_info("Restoring log level to original (%s).", strna(s)); + + log_set_max_level(m->original_log_level); + m->log_level_overridden = false; +} + +void manager_override_log_target(Manager *m, LogTarget target) { + assert(m); + + if (!m->log_target_overridden) { + m->original_log_target = log_get_target(); + m->log_target_overridden = true; + } + + log_info("Setting log target to %s.", log_target_to_string(target)); + log_set_target(target); +} + +void manager_restore_original_log_target(Manager *m) { + assert(m); + + if (!m->log_target_overridden) + return; + + log_info("Restoring log target to original %s.", log_target_to_string(m->original_log_target)); + + log_set_target(m->original_log_target); + m->log_target_overridden = false; +} + static const char *const manager_state_table[_MANAGER_STATE_MAX] = { [MANAGER_INITIALIZING] = "initializing", [MANAGER_STARTING] = "starting", |