X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Fcore%2Fmanager.c;h=1ddd8bae62497407e555bbe4cfa47a03f507754e;hp=c6baf22ae510bb2401458082de18e5f84c8ce550;hb=8a3a1704c7738a49385e1e1a2a5041f50e79f57e;hpb=9eb977db5b89b44f254ab40c1876a76b7d7ea2d0 diff --git a/src/core/manager.c b/src/core/manager.c index c6baf22ae..1ddd8bae6 100644 --- a/src/core/manager.c +++ b/src/core/manager.c @@ -36,12 +36,15 @@ #include #include #include +#include #ifdef HAVE_AUDIT #include #endif -#include +#include "systemd/sd-daemon.h" +#include "systemd/sd-id128.h" +#include "systemd/sd-messages.h" #include "manager.h" #include "transaction.h" @@ -66,6 +69,7 @@ #include "watchdog.h" #include "cgroup-util.h" #include "path-util.h" +#include "audit-fd.h" /* As soon as 16 units are in our GC queue, make sure to run a gc sweep */ #define GC_QUEUE_ENTRIES_MAX 16 @@ -74,8 +78,7 @@ #define GC_QUEUE_USEC_MAX (10*USEC_PER_SEC) /* Where clients shall send notification messages to */ -#define NOTIFY_SOCKET_SYSTEM "/run/systemd/notify" -#define NOTIFY_SOCKET_USER "@/org/freedesktop/systemd1/notify" +#define NOTIFY_SOCKET "@/org/freedesktop/systemd1/notify" static int manager_setup_notify(Manager *m) { union { @@ -83,13 +86,13 @@ static int manager_setup_notify(Manager *m) { struct sockaddr_un un; } sa; struct epoll_event ev; - int one = 1, r; - mode_t u; + int one = 1; assert(m); m->notify_watch.type = WATCH_NOTIFY; - if ((m->notify_watch.fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0)) < 0) { + 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; } @@ -97,21 +100,14 @@ static int manager_setup_notify(Manager *m) { zero(sa); sa.sa.sa_family = AF_UNIX; - if (getpid() != 1) - snprintf(sa.un.sun_path, sizeof(sa.un.sun_path), NOTIFY_SOCKET_USER "/%llu", random_ull()); - else { - unlink(NOTIFY_SOCKET_SYSTEM); - strncpy(sa.un.sun_path, NOTIFY_SOCKET_SYSTEM, sizeof(sa.un.sun_path)); - } - - if (sa.un.sun_path[0] == '@') - sa.un.sun_path[0] = 0; + 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)); - u = umask(0111); - r = bind(m->notify_watch.fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + 1 + strlen(sa.un.sun_path+1)); - umask(u); + sa.un.sun_path[0] = 0; - if (r < 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; } @@ -125,28 +121,75 @@ static int manager_setup_notify(Manager *m) { ev.events = EPOLLIN; ev.data.ptr = &m->notify_watch; - if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->notify_watch.fd, &ev) < 0) + if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->notify_watch.fd, &ev) < 0) { + log_error("Failed to add timer change fd to epoll: %m"); return -errno; + } - if (sa.un.sun_path[0] == 0) - sa.un.sun_path[0] = '@'; - - if (!(m->notify_socket = strdup(sa.un.sun_path))) - return -ENOMEM; + sa.un.sun_path[0] = '@'; + m->notify_socket = strdup(sa.un.sun_path); + if (!m->notify_socket) + return log_oom(); log_debug("Using notification socket %s", m->notify_socket); return 0; } +static int manager_setup_time_change(Manager *m) { + struct epoll_event ev; + struct itimerspec its; + + assert(m); + assert(m->time_change_watch.type == WATCH_INVALID); + + /* Uses TFD_TIMER_CANCEL_ON_SET to get notifications whenever + * CLOCK_REALTIME makes a jump relative to CLOCK_MONOTONIC */ + + m->time_change_watch.type = WATCH_TIME_CHANGE; + m->time_change_watch.fd = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK|TFD_CLOEXEC); + if (m->time_change_watch.fd < 0) { + log_error("Failed to create timerfd: %m"); + return -errno; + } + + zero(its); + + /* We only care for the cancellation event, hence we set the + * timeout to the latest possible value. */ + assert_cc(sizeof(time_t) == sizeof(long)); + its.it_value.tv_sec = LONG_MAX; + + if (timerfd_settime(m->time_change_watch.fd, TFD_TIMER_ABSTIME|TFD_TIMER_CANCEL_ON_SET, &its, NULL) < 0) { + log_debug("Failed to set up TFD_TIMER_CANCEL_ON_SET, ignoring: %m"); + close_nointr_nofail(m->time_change_watch.fd); + watch_init(&m->time_change_watch); + return 0; + } + + zero(ev); + ev.events = EPOLLIN; + ev.data.ptr = &m->time_change_watch; + + if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->time_change_watch.fd, &ev) < 0) { + log_error("Failed to add timer change fd to epoll: %m"); + return -errno; + } + + log_debug("Set up TFD_TIMER_CANCEL_ON_SET timerfd."); + + return 0; +} + static int enable_special_signals(Manager *m) { int fd; assert(m); /* Enable that we get SIGINT on control-alt-del. In containers - * this will fail with EPERM, so ignore that. */ - if (reboot(RB_DISABLE_CAD) < 0 && errno != EPERM) + * this will fail with EPERM (older) or EINVAL (newer), so + * ignore that. */ + if (reboot(RB_DISABLE_CAD) < 0 && errno != EPERM && errno != EINVAL) log_warning("Failed to enable ctrl-alt-del handling: %m"); fd = open_terminal("/dev/tty0", O_RDWR|O_NOCTTY|O_CLOEXEC); @@ -204,6 +247,7 @@ static int manager_setup_signals(Manager *m) { SIGRTMIN+21, /* systemd: disable status messages */ SIGRTMIN+22, /* systemd: set log level to LOG_DEBUG */ SIGRTMIN+23, /* systemd: set log level to LOG_INFO */ + SIGRTMIN+24, /* systemd: Immediate exit (--user only) */ SIGRTMIN+26, /* systemd: set log target to journal-or-kmsg */ SIGRTMIN+27, /* systemd: set log target to console */ SIGRTMIN+28, /* systemd: set log target to kmsg */ @@ -212,7 +256,8 @@ static int manager_setup_signals(Manager *m) { assert_se(sigprocmask(SIG_SETMASK, &mask, NULL) == 0); m->signal_watch.type = WATCH_SIGNAL; - if ((m->signal_watch.fd = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC)) < 0) + m->signal_watch.fd = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC); + if (m->signal_watch.fd < 0) return -errno; zero(ev); @@ -222,7 +267,7 @@ static int manager_setup_signals(Manager *m) { if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->signal_watch.fd, &ev) < 0) return -errno; - if (m->running_as == MANAGER_SYSTEM) + if (m->running_as == SYSTEMD_SYSTEM) return enable_special_signals(m); return 0; @@ -243,18 +288,19 @@ static void manager_strip_environment(Manager *m) { strv_remove_prefix(m->environment, "RD_"); } -int manager_new(ManagerRunningAs running_as, Manager **_m) { +int manager_new(SystemdRunningAs running_as, Manager **_m) { Manager *m; int r = -ENOMEM; assert(_m); assert(running_as >= 0); - assert(running_as < _MANAGER_RUNNING_AS_MAX); + assert(running_as < _SYSTEMD_RUNNING_AS_MAX); - if (!(m = new0(Manager, 1))) + m = new0(Manager, 1); + if (!m) return -ENOMEM; - dual_timestamp_get(&m->startup_timestamp); + dual_timestamp_get(&m->userspace_timestamp); m->running_as = running_as; m->name_data_slot = m->conn_data_slot = m->subscribed_data_slot = -1; @@ -262,11 +308,13 @@ int manager_new(ManagerRunningAs running_as, Manager **_m) { m->pin_cgroupfs_fd = -1; m->idle_pipe[0] = m->idle_pipe[1] = -1; -#ifdef HAVE_AUDIT - m->audit_fd = -1; -#endif + watch_init(&m->signal_watch); + watch_init(&m->mount_watch); + watch_init(&m->swap_watch); + watch_init(&m->udev_watch); + watch_init(&m->time_change_watch); - m->signal_watch.fd = m->mount_watch.fd = m->udev_watch.fd = m->epoll_fd = m->dev_autofs_fd = m->swap_watch.fd = -1; + m->epoll_fd = m->dev_autofs_fd = -1; m->current_job_id = 1; /* start as id #1, so that we can leave #0 around as "null-like" value */ m->environment = strv_copy(environ); @@ -275,7 +323,7 @@ int manager_new(ManagerRunningAs running_as, Manager **_m) { manager_strip_environment(m); - if (running_as == MANAGER_SYSTEM) { + if (running_as == SYSTEMD_SYSTEM) { m->default_controllers = strv_new("cpu", NULL); if (!m->default_controllers) goto fail; @@ -296,33 +344,31 @@ int manager_new(ManagerRunningAs running_as, Manager **_m) { if (!(m->watch_bus = hashmap_new(string_hash_func, string_compare_func))) goto fail; - if ((m->epoll_fd = epoll_create1(EPOLL_CLOEXEC)) < 0) + m->epoll_fd = epoll_create1(EPOLL_CLOEXEC); + if (m->epoll_fd < 0) goto fail; - if ((r = lookup_paths_init(&m->lookup_paths, m->running_as, true)) < 0) + r = manager_setup_signals(m); + if (r < 0) goto fail; - if ((r = manager_setup_signals(m)) < 0) + r = manager_setup_cgroup(m); + if (r < 0) goto fail; - if ((r = manager_setup_cgroup(m)) < 0) + r = manager_setup_notify(m); + if (r < 0) goto fail; - if ((r = manager_setup_notify(m)) < 0) + r = manager_setup_time_change(m); + if (r < 0) goto fail; /* Try to connect to the busses, if possible. */ - if ((r = bus_init(m, running_as != MANAGER_SYSTEM)) < 0) + r = bus_init(m, running_as != SYSTEMD_SYSTEM); + if (r < 0) goto fail; -#ifdef HAVE_AUDIT - if ((m->audit_fd = audit_open()) < 0 && - /* If the kernel lacks netlink or audit support, - * don't worry about it. */ - errno != EAFNOSUPPORT && errno != EPROTONOSUPPORT) - log_error("Failed to connect to audit log: %m"); -#endif - m->taint_usr = dir_is_empty("/usr") > 0; *_m = m; @@ -476,6 +522,7 @@ static void manager_clear_jobs_and_units(Manager *m) { void manager_free(Manager *m) { UnitType c; + int i; assert(m); @@ -504,11 +551,8 @@ void manager_free(Manager *m) { close_nointr_nofail(m->signal_watch.fd); if (m->notify_watch.fd >= 0) close_nointr_nofail(m->notify_watch.fd); - -#ifdef HAVE_AUDIT - if (m->audit_fd >= 0) - audit_close(m->audit_fd); -#endif + if (m->time_change_watch.fd >= 0) + close_nointr_nofail(m->time_change_watch.fd); free(m->notify_socket); @@ -522,6 +566,12 @@ void manager_free(Manager *m) { 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); } @@ -595,7 +645,7 @@ static void manager_build_unit_path_cache(Manager *m) { if (ignore_file(de->d_name)) continue; - p = join(streq(*i, "/") ? "" : *i, "/", de->d_name, NULL); + p = strjoin(streq(*i, "/") ? "" : *i, "/", de->d_name, NULL); if (!p) { r = -ENOMEM; goto fail; @@ -630,6 +680,14 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) { 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 @@ -642,12 +700,15 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) { r = manager_enumerate(m); /* Second, deserialize if there is something to deserialize */ - if (serialization) - if ((q = manager_deserialize(m, serialization, fds)) < 0) + if (serialization) { + q = manager_deserialize(m, serialization, fds); + if (q < 0) r = q; + } /* Third, fire things up! */ - if ((q = manager_coldplug(m)) < 0) + q = manager_coldplug(m); + if (q < 0) r = q; if (serialization) { @@ -724,7 +785,8 @@ int manager_add_job_by_name(Manager *m, JobType type, const char *name, JobMode assert(name); assert(mode < _JOB_MODE_MAX); - if ((r = manager_load_unit(m, name, NULL, NULL, &unit)) < 0) + r = manager_load_unit(m, name, NULL, NULL, &unit); + if (r < 0) return r; return manager_add_job(m, type, unit, mode, override, e, _ret); @@ -790,7 +852,7 @@ int manager_load_unit_prepare(Manager *m, const char *name, const char *path, DB t = unit_name_to_type(name); - if (t == _UNIT_TYPE_INVALID || !unit_name_is_valid_no_type(name, false)) { + 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; } @@ -836,7 +898,8 @@ int manager_load_unit(Manager *m, const char *name, const char *path, DBusError /* This will load the service information files, but not actually * start any services or anything. */ - if ((r = manager_load_unit_prepare(m, name, path, e, _ret)) != 0) + r = manager_load_unit_prepare(m, name, path, e, _ret); + if (r != 0) return r; manager_dispatch_load_queue(m); @@ -1133,7 +1196,7 @@ static int manager_process_signal_fd(Manager *m) { break; case SIGTERM: - if (m->running_as == MANAGER_SYSTEM) { + if (m->running_as == SYSTEMD_SYSTEM) { /* This is for compatibility with the * original sysvinit */ m->exit_code = MANAGER_REEXECUTE; @@ -1143,7 +1206,7 @@ static int manager_process_signal_fd(Manager *m) { /* Fall through */ case SIGINT: - if (m->running_as == MANAGER_SYSTEM) { + if (m->running_as == SYSTEMD_SYSTEM) { manager_start_target(m, SPECIAL_CTRL_ALT_DEL_TARGET, JOB_REPLACE); break; } @@ -1157,14 +1220,14 @@ static int manager_process_signal_fd(Manager *m) { break; case SIGWINCH: - if (m->running_as == MANAGER_SYSTEM) + if (m->running_as == SYSTEMD_SYSTEM) manager_start_target(m, SPECIAL_KBREQUEST_TARGET, JOB_REPLACE); /* This is a nop on non-init */ break; case SIGPWR: - if (m->running_as == MANAGER_SYSTEM) + if (m->running_as == SYSTEMD_SYSTEM) manager_start_target(m, SPECIAL_SIGPWR_TARGET, JOB_REPLACE); /* This is a nop on non-init */ @@ -1276,6 +1339,15 @@ static int manager_process_signal_fd(Manager *m) { log_notice("Setting log level to info."); break; + case 24: + if (m->running_as == SYSTEMD_USER) { + m->exit_code = MANAGER_EXIT; + return 0; + } + + /* This is a nop on init */ + break; + case 26: log_set_target(LOG_TARGET_JOURNAL_OR_KMSG); log_notice("Setting log target to journal-or-kmsg."); @@ -1357,11 +1429,13 @@ static int process_event(Manager *m, struct epoll_event *ev) { ssize_t k; /* Some timer event, to be dispatched to the units */ - if ((k = read(w->fd, &v, sizeof(v))) != sizeof(v)) { + k = read(w->fd, &v, sizeof(v)); + if (k != sizeof(v)) { if (k < 0 && (errno == EINTR || errno == EAGAIN)) break; + log_error("Failed to read timer event counter: %s", k < 0 ? strerror(-k) : "Short read"); return k < 0 ? -errno : -EIO; } @@ -1395,6 +1469,28 @@ static int process_event(Manager *m, struct epoll_event *ev) { bus_timeout_event(m, w, ev->events); break; + case WATCH_TIME_CHANGE: { + Unit *u; + Iterator i; + + log_struct(LOG_INFO, + MESSAGE_ID(SD_MESSAGE_TIME_CHANGE), + "MESSAGE=Time has been changed", + NULL); + + /* Restart the watch */ + close_nointr_nofail(m->time_change_watch.fd); + watch_init(&m->time_change_watch); + manager_setup_time_change(m); + + HASHMAP_FOREACH(u, m->units, i) { + if (UNIT_VTABLE(u)->time_change) + UNIT_VTABLE(u)->time_change(u); + } + + break; + } + default: log_error("event type=%i", w->type); assert_not_reached("Unknown epoll event type."); @@ -1428,7 +1524,7 @@ int manager_loop(Manager *m) { int n; int wait_msec = -1; - if (m->runtime_watchdog > 0 && m->running_as == MANAGER_SYSTEM) + if (m->runtime_watchdog > 0 && m->running_as == SYSTEMD_SYSTEM) watchdog_ping(); if (!ratelimit_test(&rl)) { @@ -1460,7 +1556,7 @@ int manager_loop(Manager *m) { continue; /* Sleep for half the watchdog time */ - if (m->runtime_watchdog > 0 && m->running_as == MANAGER_SYSTEM) { + if (m->runtime_watchdog > 0 && m->running_as == SYSTEMD_SYSTEM) { wait_msec = (int) (m->runtime_watchdog / 2 / USEC_PER_MSEC); if (wait_msec <= 0) wait_msec = 1; @@ -1487,9 +1583,10 @@ int manager_loop(Manager *m) { return m->exit_code; } -int manager_get_unit_from_dbus_path(Manager *m, const char *s, Unit **_u) { +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); @@ -1498,14 +1595,15 @@ int manager_get_unit_from_dbus_path(Manager *m, const char *s, Unit **_u) { if (!startswith(s, "/org/freedesktop/systemd1/unit/")) return -EINVAL; - if (!(n = bus_path_unescape(s+31))) + n = bus_path_unescape(s+31); + if (!n) return -ENOMEM; - u = manager_get_unit(m, n); + r = manager_load_unit(m, n, NULL, e, &u); free(n); - if (!u) - return -ENOENT; + if (r < 0) + return r; *_u = u; @@ -1524,10 +1622,12 @@ int manager_get_job_from_dbus_path(Manager *m, const char *s, Job **_j) { if (!startswith(s, "/org/freedesktop/systemd1/job/")) return -EINVAL; - if ((r = safe_atou(s + 30, &id)) < 0) + r = safe_atou(s + 30, &id); + if (r < 0) return r; - if (!(j = manager_get_job(m, id))) + j = manager_get_job(m, id); + if (!j) return -ENOENT; *_j = j; @@ -1539,8 +1639,10 @@ void manager_send_unit_audit(Manager *m, Unit *u, int type, bool success) { #ifdef HAVE_AUDIT char *p; + int audit_fd; - if (m->audit_fd < 0) + audit_fd = get_audit_fd(); + if (audit_fd < 0) return; /* Don't generate audit events if the service was already @@ -1548,7 +1650,7 @@ void manager_send_unit_audit(Manager *m, Unit *u, int type, bool success) { if (m->n_reloading > 0) return; - if (m->running_as != MANAGER_SYSTEM) + if (m->running_as != SYSTEMD_SYSTEM) return; if (u->type != UNIT_SERVICE) @@ -1559,12 +1661,11 @@ void manager_send_unit_audit(Manager *m, Unit *u, int type, bool success) { return; } - if (audit_log_user_comm_message(m->audit_fd, type, "", p, NULL, NULL, NULL, success) < 0) { + if (audit_log_user_comm_message(audit_fd, type, "", p, NULL, NULL, NULL, success) < 0) { if (errno == EPERM) { /* We aren't allowed to send audit messages? * Then let's not retry again. */ - audit_close(m->audit_fd); - m->audit_fd = -1; + close_audit_fd(); } else log_warning("Failed to send audit message: %m"); } @@ -1585,7 +1686,7 @@ void manager_send_unit_plymouth(Manager *m, Unit *u) { if (m->n_reloading > 0) return; - if (m->running_as != MANAGER_SYSTEM) + if (m->running_as != SYSTEMD_SYSTEM) return; if (u->type != UNIT_SERVICE && @@ -1617,7 +1718,7 @@ void manager_send_unit_plymouth(Manager *m, Unit *u) { } if (asprintf(&message, "U\002%c%s%n", (int) (strlen(u->id) + 1), u->id, &n) < 0) { - log_error("Out of memory"); + log_oom(); goto finish; } @@ -1684,7 +1785,7 @@ int manager_open_serialization(Manager *m, FILE **_f) { assert(_f); - if (m->running_as == MANAGER_SYSTEM) + if (m->running_as == SYSTEMD_SYSTEM) asprintf(&path, "/run/systemd/dump-%lu-XXXXXX", (unsigned long) getpid()); else asprintf(&path, "/tmp/systemd-dump-%lu-XXXXXX", (unsigned long) getpid()); @@ -1714,7 +1815,7 @@ int manager_open_serialization(Manager *m, FILE **_f) { return 0; } -int manager_serialize(Manager *m, FILE *f, FDSet *fds) { +int manager_serialize(Manager *m, FILE *f, FDSet *fds, bool serialize_jobs) { Iterator i; Unit *u; const char *t; @@ -1728,10 +1829,18 @@ int manager_serialize(Manager *m, FILE *f, FDSet *fds) { 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); - dual_timestamp_serialize(f, "startup-timestamp", &m->startup_timestamp); - dual_timestamp_serialize(f, "finish-timestamp", &m->finish_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); @@ -1746,7 +1855,7 @@ int manager_serialize(Manager *m, FILE *f, FDSet *fds) { fputs(u->id, f); fputc('\n', f); - if ((r = unit_serialize(u, f, fds)) < 0) { + if ((r = unit_serialize(u, f, fds, serialize_jobs)) < 0) { m->n_reloading --; return r; } @@ -1800,6 +1909,20 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) { 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; @@ -1807,10 +1930,16 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) { log_debug("Failed to parse taint /usr flag %s", l+10); else m->taint_usr = m->taint_usr || b; - } else if (startswith(l, "initrd-timestamp=")) + } else if (startswith(l, "firmware-timestamp=")) + dual_timestamp_deserialize(l+19, &m->firmware_timestamp); + else if (startswith(l, "loader-timestamp=")) + dual_timestamp_deserialize(l+17, &m->loader_timestamp); + else if (startswith(l, "kernel-timestamp=")) + dual_timestamp_deserialize(l+17, &m->kernel_timestamp); + else if (startswith(l, "initrd-timestamp=")) dual_timestamp_deserialize(l+17, &m->initrd_timestamp); - else if (startswith(l, "startup-timestamp=")) - dual_timestamp_deserialize(l+18, &m->startup_timestamp); + else if (startswith(l, "userspace-timestamp=")) + dual_timestamp_deserialize(l+20, &m->userspace_timestamp); else if (startswith(l, "finish-timestamp=")) dual_timestamp_deserialize(l+17, &m->finish_timestamp); else @@ -1859,18 +1988,21 @@ int manager_reload(Manager *m) { assert(m); - if ((r = manager_open_serialization(m, &f)) < 0) + r = manager_open_serialization(m, &f); + if (r < 0) return r; m->n_reloading ++; - if (!(fds = fdset_new())) { + fds = fdset_new(); + if (!fds) { m->n_reloading --; r = -ENOMEM; goto finish; } - if ((r = manager_serialize(m, f, fds)) < 0) { + r = manager_serialize(m, f, fds, true); + if (r < 0) { m->n_reloading --; goto finish; } @@ -1884,29 +2016,37 @@ int manager_reload(Manager *m) { /* From here on there is no way back. */ manager_clear_jobs_and_units(m); manager_undo_generators(m); - - /* Find new unit paths */ lookup_paths_free(&m->lookup_paths); - if ((q = lookup_paths_init(&m->lookup_paths, m->running_as, true)) < 0) - r = q; + /* 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 */ - if ((q = manager_enumerate(m)) < 0) + q = manager_enumerate(m); + if (q < 0) r = q; /* Second, deserialize our stored data */ - if ((q = manager_deserialize(m, f, fds)) < 0) + q = manager_deserialize(m, f, fds); + if (q < 0) r = q; fclose(f); f = NULL; /* Third, fire things up! */ - if ((q = manager_coldplug(m)) < 0) + q = manager_coldplug(m); + if (q < 0) r = q; assert(m->n_reloading > 0); @@ -1964,7 +2104,7 @@ bool manager_unit_pending_inactive(Manager *m, const char *name) { void manager_check_finished(Manager *m) { char userspace[FORMAT_TIMESPAN_MAX], initrd[FORMAT_TIMESPAN_MAX], kernel[FORMAT_TIMESPAN_MAX], sum[FORMAT_TIMESPAN_MAX]; - usec_t kernel_usec, initrd_usec, userspace_usec, total_usec; + usec_t firmware_usec, loader_usec, kernel_usec, initrd_usec, userspace_usec, total_usec; assert(m); @@ -1974,61 +2114,144 @@ void manager_check_finished(Manager *m) { /* 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 == MANAGER_SYSTEM && detect_container(NULL) <= 0) { + if (m->running_as == SYSTEMD_SYSTEM && detect_container(NULL) <= 0) { - userspace_usec = m->finish_timestamp.monotonic - m->startup_timestamp.monotonic; - total_usec = m->finish_timestamp.monotonic; + /* Note that m->kernel_usec.monotonic is always at 0, + * and m->firmware_usec.monotonic and + * m->loader_usec.monotonic should be considered + * negative values. */ - if (dual_timestamp_is_set(&m->initrd_timestamp)) { + firmware_usec = m->firmware_timestamp.monotonic - m->loader_timestamp.monotonic; + loader_usec = m->loader_timestamp.monotonic - m->kernel_timestamp.monotonic; + userspace_usec = m->finish_timestamp.monotonic - m->userspace_timestamp.monotonic; + total_usec = m->firmware_timestamp.monotonic + m->finish_timestamp.monotonic; - kernel_usec = m->initrd_timestamp.monotonic; - initrd_usec = m->startup_timestamp.monotonic - m->initrd_timestamp.monotonic; + if (dual_timestamp_is_set(&m->initrd_timestamp)) { - log_info("Startup finished in %s (kernel) + %s (initrd) + %s (userspace) = %s.", - format_timespan(kernel, sizeof(kernel), kernel_usec), - format_timespan(initrd, sizeof(initrd), initrd_usec), - format_timespan(userspace, sizeof(userspace), userspace_usec), - format_timespan(sum, sizeof(sum), total_usec)); + kernel_usec = m->initrd_timestamp.monotonic - m->kernel_timestamp.monotonic; + initrd_usec = m->userspace_timestamp.monotonic - m->initrd_timestamp.monotonic; + + if (!log_on_console()) + log_struct(LOG_INFO, + MESSAGE_ID(SD_MESSAGE_STARTUP_FINISHED), + "KERNEL_USEC=%llu", (unsigned long long) kernel_usec, + "INITRD_USEC=%llu", (unsigned long long) initrd_usec, + "USERSPACE_USEC=%llu", (unsigned long long) userspace_usec, + "MESSAGE=Startup finished in %s (kernel) + %s (initrd) + %s (userspace) = %s.", + format_timespan(kernel, sizeof(kernel), kernel_usec), + format_timespan(initrd, sizeof(initrd), initrd_usec), + format_timespan(userspace, sizeof(userspace), userspace_usec), + format_timespan(sum, sizeof(sum), total_usec), + NULL); } else { - kernel_usec = m->startup_timestamp.monotonic; + kernel_usec = m->userspace_timestamp.monotonic - m->kernel_timestamp.monotonic; initrd_usec = 0; - log_info("Startup finished in %s (kernel) + %s (userspace) = %s.", - format_timespan(kernel, sizeof(kernel), kernel_usec), - format_timespan(userspace, sizeof(userspace), userspace_usec), - format_timespan(sum, sizeof(sum), total_usec)); + if (!log_on_console()) + log_struct(LOG_INFO, + MESSAGE_ID(SD_MESSAGE_STARTUP_FINISHED), + "KERNEL_USEC=%llu", (unsigned long long) kernel_usec, + "USERSPACE_USEC=%llu", (unsigned long long) userspace_usec, + "MESSAGE=Startup finished in %s (kernel) + %s (userspace) = %s.", + format_timespan(kernel, sizeof(kernel), kernel_usec), + format_timespan(userspace, sizeof(userspace), userspace_usec), + format_timespan(sum, sizeof(sum), total_usec), + NULL); } } else { - userspace_usec = initrd_usec = kernel_usec = 0; - total_usec = m->finish_timestamp.monotonic - m->startup_timestamp.monotonic; - - log_debug("Startup finished in %s.", - format_timespan(sum, sizeof(sum), total_usec)); + firmware_usec = loader_usec = initrd_usec = kernel_usec = 0; + total_usec = userspace_usec = m->finish_timestamp.monotonic - m->userspace_timestamp.monotonic; + + if (!log_on_console()) + log_struct(LOG_INFO, + MESSAGE_ID(SD_MESSAGE_STARTUP_FINISHED), + "USERSPACE_USEC=%llu", (unsigned long long) userspace_usec, + "MESSAGE=Startup finished in %s.", + format_timespan(sum, sizeof(sum), total_usec), + NULL); } - bus_broadcast_finished(m, kernel_usec, initrd_usec, userspace_usec, total_usec); + bus_broadcast_finished(m, firmware_usec, loader_usec, kernel_usec, initrd_usec, userspace_usec, total_usec); sd_notifyf(false, "READY=1\nSTATUS=Startup finished in %s.", 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[3]; + const char *argv[5]; mode_t u; + int r; assert(m); - generator_path = m->running_as == MANAGER_SYSTEM ? SYSTEM_GENERATOR_PATH : USER_GENERATOR_PATH; - if (!(d = opendir(generator_path))) { - + generator_path = m->running_as == SYSTEMD_SYSTEM ? SYSTEM_GENERATOR_PATH : USER_GENERATOR_PATH; + d = opendir(generator_path); + if (!d) { if (errno == ENOENT) return; @@ -2036,79 +2259,57 @@ void manager_run_generators(Manager *m) { return; } - if (!m->generator_unit_path) { - const char *p; - char user_path[] = "/tmp/systemd-generator-XXXXXX"; - - if (m->running_as == MANAGER_SYSTEM && getpid() == 1) { - p = "/run/systemd/generator"; - - if (mkdir_p(p, 0755) < 0) { - log_error("Failed to create generator directory: %m"); - goto finish; - } + r = create_generator_dir(m, &m->generator_unit_path, "generator"); + if (r < 0) + goto finish; - } else { - if (!(p = mkdtemp(user_path))) { - log_error("Failed to create generator directory: %m"); - goto finish; - } - } + r = create_generator_dir(m, &m->generator_unit_path_early, "generator.early"); + if (r < 0) + goto finish; - if (!(m->generator_unit_path = strdup(p))) { - log_error("Failed to allocate generator unit path."); - 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] = NULL; + 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); - if (rmdir(m->generator_unit_path) >= 0) { - /* Uh? we were able to remove this dir? I guess that - * means the directory was empty, hence let's shortcut - * this */ - - free(m->generator_unit_path); - m->generator_unit_path = NULL; - goto finish; - } - - if (!strv_find(m->lookup_paths.unit_path, m->generator_unit_path)) { - char **l; - - if (!(l = strv_append(m->lookup_paths.unit_path, m->generator_unit_path))) { - log_error("Failed to add generator directory to unit search path: %m"); - goto finish; - } - - strv_free(m->lookup_paths.unit_path); - m->lookup_paths.unit_path = l; - - log_debug("Added generator unit path %s to search path.", m->generator_unit_path); - } + 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); } -void manager_undo_generators(Manager *m) { +static void remove_generator_dir(Manager *m, char **generator) { assert(m); + assert(generator); - if (!m->generator_unit_path) + if (!*generator) return; - strv_remove(m->lookup_paths.unit_path, m->generator_unit_path); - rm_rf(m->generator_unit_path, false, true, false); + 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); - free(m->generator_unit_path); - m->generator_unit_path = NULL; + 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) { @@ -2128,12 +2329,29 @@ int manager_set_default_controllers(Manager *m, char **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 != MANAGER_SYSTEM) + if (m->running_as != SYSTEMD_SYSTEM) return; u = manager_get_unit(m, SPECIAL_JOURNALD_SOCKET); @@ -2156,7 +2374,7 @@ void manager_recheck_journal(Manager *m) { void manager_set_show_status(Manager *m, bool b) { assert(m); - if (m->running_as != MANAGER_SYSTEM) + if (m->running_as != SYSTEMD_SYSTEM) return; m->show_status = b; @@ -2170,7 +2388,7 @@ void manager_set_show_status(Manager *m, bool b) { bool manager_get_show_status(Manager *m) { assert(m); - if (m->running_as != MANAGER_SYSTEM) + if (m->running_as != SYSTEMD_SYSTEM) return false; if (m->show_status) @@ -2182,9 +2400,9 @@ bool manager_get_show_status(Manager *m) { return plymouth_running(); } -static const char* const manager_running_as_table[_MANAGER_RUNNING_AS_MAX] = { - [MANAGER_SYSTEM] = "system", - [MANAGER_USER] = "user" -}; +void watch_init(Watch *w) { + assert(w); -DEFINE_STRING_TABLE_LOOKUP(manager_running_as, ManagerRunningAs); + w->type = WATCH_INVALID; + w->fd = -1; +}