X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=src%2Fcore%2Fmanager.c;h=d33112df2f3699af4db9c3a5a812bd97c6c578e8;hb=cc3bc3e6203e0c615e31b8b68796362e1385f28a;hp=519b37438244c0a2bafe363d67c6f4054baf89b7;hpb=9c3349e23b14db27e7ba45f82cf647899c563ea9;p=elogind.git diff --git a/src/core/manager.c b/src/core/manager.c index 519b37438..d33112df2 100644 --- a/src/core/manager.c +++ b/src/core/manager.c @@ -19,7 +19,6 @@ along with systemd; If not, see . ***/ -#include #include #include #include @@ -27,14 +26,10 @@ #include #include #include -#include #include #include #include -#include #include -#include -#include #include #include @@ -43,7 +38,6 @@ #endif #include "sd-daemon.h" -#include "sd-id128.h" #include "sd-messages.h" #include "manager.h" @@ -56,7 +50,6 @@ #include "mkdir.h" #include "ratelimit.h" #include "locale-setup.h" -#include "mount-setup.h" #include "unit-name.h" #include "missing.h" #include "path-lookup.h" @@ -64,7 +57,6 @@ #include "exit-status.h" #include "virt.h" #include "watchdog.h" -#include "cgroup-util.h" #include "path-util.h" #include "audit-fd.h" #include "boot-timestamps.h" @@ -90,17 +82,19 @@ static int manager_dispatch_time_change_fd(sd_event_source *source, int fd, uint static int manager_dispatch_idle_pipe_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata); 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_run_generators(Manager *m); +static void manager_undo_generators(Manager *m); -static int manager_watch_jobs_in_progress(Manager *m) { +static void manager_watch_jobs_in_progress(Manager *m) { usec_t next; assert(m); if (m->jobs_in_progress_event_source) - return 0; + return; next = now(CLOCK_MONOTONIC) + JOBS_IN_PROGRESS_WAIT_USEC; - return sd_event_add_time( + (void) sd_event_add_time( m->event, &m->jobs_in_progress_event_source, CLOCK_MONOTONIC, @@ -184,8 +178,10 @@ static void manager_print_jobs_in_progress(Manager *m) { m->jobs_in_progress_iteration++; - if (m->n_running_jobs > 1) - asprintf(&job_of_n, "(%u of %u) ", counter, m->n_running_jobs); + if (m->n_running_jobs > 1) { + if (asprintf(&job_of_n, "(%u of %u) ", counter, m->n_running_jobs) < 0) + job_of_n = NULL; + } format_timespan(time, sizeof(time), now(CLOCK_MONOTONIC) - j->begin_usec, 1*USEC_PER_SEC); if (job_get_timeout(j, &x) > 0) @@ -547,6 +543,9 @@ int manager_new(SystemdRunningAs running_as, bool test_run, Manager **_m) { m->test_run = test_run; + /* Reboot immediately if the user hits C-A-D more often than 7x per 2s */ + RATELIMIT_INIT(m->ctrl_alt_del_ratelimit, 2 * USEC_PER_SEC, 7); + r = manager_default_environment(m); if (r < 0) goto fail; @@ -703,7 +702,7 @@ static int manager_setup_kdbus(Manager *m) { if (m->test_run || m->kdbus_fd >= 0) return 0; - if (getpid() == 1) + if (m->running_as == SYSTEMD_SYSTEM && detect_container(NULL) <= 0) bus_kernel_fix_attach_mask(); m->kdbus_fd = bus_kernel_create_bus( @@ -839,7 +838,8 @@ static unsigned manager_dispatch_gc_queue(Manager *m) { if (u->gc_marker == gc_marker + GC_OFFSET_BAD || u->gc_marker == gc_marker + GC_OFFSET_UNSURE) { - log_unit_debug(u->id, "Collecting %s", u->id); + if (u->id) + log_unit_debug(u->id, "Collecting %s", u->id); u->gc_marker = gc_marker + GC_OFFSET_BAD; unit_add_to_cleanup_queue(u); } @@ -956,7 +956,7 @@ int manager_enumerate(Manager *m) { int q; if (unit_vtable[c]->supported && !unit_vtable[c]->supported(m)) { - log_info("Unit type .%s is not supported on this system.", unit_type_to_string(c)); + log_debug("Unit type .%s is not supported on this system.", unit_type_to_string(c)); continue; } @@ -978,7 +978,28 @@ static int manager_coldplug(Manager *m) { Unit *u; char *k; - assert(m); + /* + * Some unit types tend to spawn jobs or check other units' state + * during coldplug. This is wrong because it is undefined whether the + * units in question have been already coldplugged (i. e. their state + * restored). This way, we can easily re-start an already started unit + * or otherwise make a wrong decision based on the unit's state. + * + * Solve this by providing a way for coldplug functions to defer + * such actions until after all units have been coldplugged. + * + * We store Unit* -> int(*)(Unit*). + * + * https://bugs.freedesktop.org/show_bug.cgi?id=88401 + */ + _cleanup_hashmap_free_ Hashmap *deferred_work = NULL; + int(*proc)(Unit*); + + assert(m); + + deferred_work = hashmap_new(&trivial_hash_ops); + if (!deferred_work) + return -ENOMEM; /* Then, let's set up their initial state. */ HASHMAP_FOREACH_KEY(u, k, m->units, i) { @@ -988,7 +1009,17 @@ static int manager_coldplug(Manager *m) { if (u->id != k) continue; - q = unit_coldplug(u); + q = unit_coldplug(u, deferred_work); + if (q < 0) + r = q; + } + + /* After coldplugging and setting up initial state of the units, + * let's perform operations which spawn jobs or query units' state. */ + HASHMAP_FOREACH_KEY(proc, u, deferred_work, i) { + int q; + + q = proc(u); if (q < 0) r = q; } @@ -1083,8 +1114,10 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) { assert(m); dual_timestamp_get(&m->generators_start_timestamp); - manager_run_generators(m); + r = manager_run_generators(m); dual_timestamp_get(&m->generators_finish_timestamp); + if (r < 0) + return r; r = lookup_paths_init( &m->lookup_paths, m->running_as, true, @@ -1449,7 +1482,7 @@ static unsigned manager_dispatch_dbus_queue(Manager *m) { return n; } -static void manager_invoke_notify_message(Manager *m, Unit *u, pid_t pid, char *buf, size_t n) { +static void manager_invoke_notify_message(Manager *m, Unit *u, pid_t pid, char *buf, size_t n, FDSet *fds) { _cleanup_strv_free_ char **tags = NULL; assert(m); @@ -1466,12 +1499,13 @@ static void manager_invoke_notify_message(Manager *m, Unit *u, pid_t pid, char * log_unit_debug(u->id, "Got notification message for unit %s", u->id); if (UNIT_VTABLE(u)->notify_message) - UNIT_VTABLE(u)->notify_message(u, pid, tags); + UNIT_VTABLE(u)->notify_message(u, pid, tags, fds); } static int manager_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata) { Manager *m = userdata; ssize_t n; + int r; assert(m); assert(m->notify_fd == fd); @@ -1482,73 +1516,99 @@ static int manager_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t } for (;;) { - char buf[4096]; + _cleanup_fdset_free_ FDSet *fds = NULL; + char buf[NOTIFY_BUFFER_MAX+1]; struct iovec iovec = { .iov_base = buf, .iov_len = sizeof(buf)-1, }; - bool found = false; - union { struct cmsghdr cmsghdr; - uint8_t buf[CMSG_SPACE(sizeof(struct ucred))]; + uint8_t buf[CMSG_SPACE(sizeof(struct ucred)) + + CMSG_SPACE(sizeof(int) * NOTIFY_FD_MAX)]; } control = {}; - struct msghdr msghdr = { .msg_iov = &iovec, .msg_iovlen = 1, .msg_control = &control, .msg_controllen = sizeof(control), }; - struct ucred *ucred; + struct cmsghdr *cmsg; + struct ucred *ucred = NULL; + bool found = false; Unit *u1, *u2, *u3; + int *fd_array = NULL; + unsigned n_fds = 0; - n = recvmsg(m->notify_fd, &msghdr, MSG_DONTWAIT); - if (n <= 0) { - if (n == 0) - return -EIO; - + n = recvmsg(m->notify_fd, &msghdr, MSG_DONTWAIT|MSG_CMSG_CLOEXEC); + if (n < 0) { 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."); + for (cmsg = CMSG_FIRSTHDR(&msghdr); cmsg; cmsg = CMSG_NXTHDR(&msghdr, cmsg)) { + if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) { + + fd_array = (int*) CMSG_DATA(cmsg); + n_fds = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int); + + } else if (cmsg->cmsg_level == SOL_SOCKET && + cmsg->cmsg_type == SCM_CREDENTIALS && + cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred))) { + + ucred = (struct ucred*) CMSG_DATA(cmsg); + } + } + + if (n_fds > 0) { + assert(fd_array); + + r = fdset_new_array(&fds, fd_array, n_fds); + if (r < 0) { + close_many(fd_array, n_fds); + return log_oom(); + } + } + + if (!ucred || ucred->pid <= 0) { + log_warning("Received notify message without valid credentials. Ignoring."); continue; } - ucred = (struct ucred*) CMSG_DATA(&control.cmsghdr); + if ((size_t) n >= sizeof(buf)) { + log_warning("Received notify message exceeded maximum size. Ignoring."); + continue; + } - assert((size_t) n < sizeof(buf)); buf[n] = 0; /* Notify every unit that might be interested, but try * to avoid notifying the same one multiple times. */ u1 = manager_get_unit_by_pid(m, ucred->pid); if (u1) { - manager_invoke_notify_message(m, u1, ucred->pid, buf, n); + manager_invoke_notify_message(m, u1, ucred->pid, buf, n, fds); found = true; } u2 = hashmap_get(m->watch_pids1, LONG_TO_PTR(ucred->pid)); if (u2 && u2 != u1) { - manager_invoke_notify_message(m, u2, ucred->pid, buf, n); + manager_invoke_notify_message(m, u2, ucred->pid, buf, n, fds); found = true; } u3 = hashmap_get(m->watch_pids2, LONG_TO_PTR(ucred->pid)); if (u3 && u3 != u2 && u3 != u1) { - manager_invoke_notify_message(m, u3, ucred->pid, buf, n); + manager_invoke_notify_message(m, u3, ucred->pid, buf, n, fds); found = true; } if (!found) log_warning("Cannot find unit for notify message of PID "PID_FMT".", ucred->pid); + + if (fdset_size(fds) > 0) + log_warning("Got auxiliary fds with notification message, closing all."); } return 0; @@ -1690,7 +1750,19 @@ static int manager_dispatch_signal_fd(sd_event_source *source, int fd, uint32_t case SIGINT: if (m->running_as == SYSTEMD_SYSTEM) { - manager_start_target(m, SPECIAL_CTRL_ALT_DEL_TARGET, JOB_REPLACE_IRREVERSIBLY); + + /* If the user presses C-A-D more than + * 7 times within 2s, we reboot + * immediately. */ + + if (ratelimit_test(&m->ctrl_alt_del_ratelimit)) + manager_start_target(m, SPECIAL_CTRL_ALT_DEL_TARGET, JOB_REPLACE_IRREVERSIBLY); + else { + log_notice("Ctrl-Alt-Del was pressed more than 7 times within 2s, rebooting immediately."); + status_printf(NULL, true, false, "Ctrl-Alt-Del was pressed more than 7 times within 2s, rebooting immediately."); + m->exit_code = MANAGER_REBOOT; + } + break; } @@ -2060,8 +2132,7 @@ void manager_send_unit_audit(Manager *m, Unit *u, int type, bool success) { return; } - msg = strappenda("unit=", p); - + msg = strjoina("unit=", p); if (audit_log_user_comm_message(audit_fd, type, msg, "systemd", NULL, NULL, NULL, success) < 0) { if (errno == EPERM) /* We aren't allowed to send audit messages? @@ -2179,7 +2250,7 @@ int manager_serialize(Manager *m, FILE *f, FDSet *fds, bool switching_root) { m->n_reloading ++; - fprintf(f, "current-job-id=%i\n", m->current_job_id); + fprintf(f, "current-job-id=%"PRIu32"\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); @@ -2485,7 +2556,9 @@ int manager_reload(Manager *m) { lookup_paths_free(&m->lookup_paths); /* Find new unit paths */ - manager_run_generators(m); + q = manager_run_generators(m); + if (q < 0 && r >= 0) + r = q; q = lookup_paths_init( &m->lookup_paths, m->running_as, true, @@ -2493,19 +2566,19 @@ int manager_reload(Manager *m) { m->generator_unit_path, m->generator_unit_path_early, m->generator_unit_path_late); - if (q < 0) + if (q < 0 && r >= 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) + if (q < 0 && r >= 0) r = q; /* Second, deserialize our stored data */ q = manager_deserialize(m, f, fds); - if (q < 0) + if (q < 0 && r >= 0) r = q; fclose(f); @@ -2513,12 +2586,12 @@ int manager_reload(Manager *m) { /* Re-register notify_fd as event source */ q = manager_setup_notify(m); - if (q < 0) + if (q < 0 && r >= 0) r = q; /* Third, fire things up! */ q = manager_coldplug(m); - if (q < 0) + if (q < 0 && r >= 0) r = q; assert(m->n_reloading > 0); @@ -2637,7 +2710,9 @@ void manager_check_finished(Manager *m) { if (hashmap_size(m->jobs) > 0) { if (m->jobs_in_progress_event_source) - sd_event_source_set_time(m->jobs_in_progress_event_source, now(CLOCK_MONOTONIC) + JOBS_IN_PROGRESS_WAIT_USEC); + /* Ignore any failure, this is only for feedback */ + (void) sd_event_source_set_time(m->jobs_in_progress_event_source, + now(CLOCK_MONOTONIC) + JOBS_IN_PROGRESS_WAIT_USEC); return; } @@ -2743,28 +2818,33 @@ static void trim_generator_dir(Manager *m, char **generator) { return; } -void manager_run_generators(Manager *m) { - _cleanup_closedir_ DIR *d = NULL; - const char *generator_path; +static int manager_run_generators(Manager *m) { + _cleanup_free_ char **paths = NULL; const char *argv[5]; + char **path; int r; assert(m); if (m->test_run) - return; + return 0; - generator_path = m->running_as == SYSTEMD_SYSTEM ? SYSTEM_GENERATOR_PATH : USER_GENERATOR_PATH; - d = opendir(generator_path); - if (!d) { - if (errno == ENOENT) - return; + paths = generator_paths(m->running_as); + if (!paths) + return log_oom(); - log_error_errno(errno, "Failed to enumerate generator directory %s: %m", - generator_path); - return; + /* Optimize by skipping the whole process by not creating output directories + * if no generators are found. */ + STRV_FOREACH(path, paths) { + r = access(*path, F_OK); + if (r == 0) + goto found; + if (errno != ENOENT) + log_warning_errno(errno, "Failed to open generator directory %s: %m", *path); } + return 0; + found: r = create_generator_dir(m, &m->generator_unit_path, "generator"); if (r < 0) goto finish; @@ -2784,12 +2864,13 @@ void manager_run_generators(Manager *m) { argv[4] = NULL; RUN_WITH_UMASK(0022) - execute_directory(generator_path, d, DEFAULT_TIMEOUT_USEC, (char**) argv); + execute_directories((const char* const*) paths, DEFAULT_TIMEOUT_USEC, (char**) argv); finish: 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); + return r; } static void remove_generator_dir(Manager *m, char **generator) { @@ -2806,7 +2887,7 @@ static void remove_generator_dir(Manager *m, char **generator) { *generator = NULL; } -void manager_undo_generators(Manager *m) { +static void manager_undo_generators(Manager *m) { assert(m); remove_generator_dir(m, &m->generator_unit_path); @@ -3008,6 +3089,24 @@ const char *manager_get_runtime_prefix(Manager *m) { getenv("XDG_RUNTIME_DIR"); } +void manager_update_failed_units(Manager *m, Unit *u, bool failed) { + unsigned size; + + assert(m); + assert(u->manager == m); + + size = set_size(m->failed_units); + + if (failed) { + if (set_put(m->failed_units, u) < 0) + log_oom(); + } else + set_remove(m->failed_units, u); + + if (set_size(m->failed_units) != size) + bus_manager_send_change_signal(m); +} + ManagerState manager_state(Manager *m) { Unit *u;