X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Fmanager.c;h=9640fca6003a74ee97bbf277562f517c770042df;hp=9fa4877016a786be24bfc56ff821d42626c37dd1;hb=7f4e08056de0184b205a20632e62db73d299937e;hpb=ead8e4788ee31bbdc38b4cd3c6e71c8a95bbc95a diff --git a/src/manager.c b/src/manager.c index 9fa487701..9640fca60 100644 --- a/src/manager.c +++ b/src/manager.c @@ -433,6 +433,8 @@ void manager_free(Manager *m) { * around */ manager_shutdown_cgroup(m, m->exit_code != MANAGER_REEXECUTE); + manager_undo_generators(m); + bus_done(m); hashmap_free(m->units); @@ -567,6 +569,8 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) { assert(m); + manager_run_generators(m); + manager_build_unit_path_cache(m); /* If we will deserialize make sure that during enumeration @@ -1133,7 +1137,8 @@ static void transaction_minimize_impact(Manager *m) { j->type == JOB_STOP && UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(j->unit)); changes_existing_job = - j->unit->meta.job && job_type_is_conflicting(j->type, j->unit->meta.job->type); + j->unit->meta.job && + job_type_is_conflicting(j->type, j->unit->meta.job->type); if (!stops_running_service && !changes_existing_job) continue; @@ -1234,7 +1239,7 @@ static int transaction_activate(Manager *m, JobMode mode, DBusError *e) { /* 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_ISOLATE) + if (mode == JOB_FAIL) transaction_minimize_impact(m); /* Third step: Drop redundant jobs */ @@ -1527,7 +1532,7 @@ static int transaction_add_isolate_jobs(Manager *m) { continue; /* No need to stop inactive jobs */ - if (UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(u))) + if (UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(u)) && !u->meta.job) continue; /* Is there already something listed for this? */ @@ -2500,6 +2505,7 @@ int manager_serialize(Manager *m, FILE *f, FDSet *fds) { assert(f); assert(fds); + 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); @@ -2554,7 +2560,9 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) { if (l[0] == 0) break; - if (startswith(l, "startup-timestamp=")) + 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, "finish-timestamp=")) dual_timestamp_deserialize(l+17, &m->finish_timestamp); @@ -2622,12 +2630,17 @@ 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)) < 0) r = q; + manager_run_generators(m); + + manager_build_unit_path_cache(m); + m->n_deserializing ++; /* First, enumerate what we can from all config files */ @@ -2714,7 +2727,7 @@ bool manager_unit_pending_inactive(Manager *m, const char *name) { } void manager_check_finished(Manager *m) { - char userspace[FORMAT_TIMESPAN_MAX], kernel[FORMAT_TIMESPAN_MAX], sum[FORMAT_TIMESPAN_MAX]; + char userspace[FORMAT_TIMESPAN_MAX], initrd[FORMAT_TIMESPAN_MAX], kernel[FORMAT_TIMESPAN_MAX], sum[FORMAT_TIMESPAN_MAX]; assert(m); @@ -2726,21 +2739,189 @@ void manager_check_finished(Manager *m) { dual_timestamp_get(&m->finish_timestamp); - if (m->running_as == MANAGER_SYSTEM) - log_info("Startup finished in %s (kernel) + %s (userspace) = %s.", - format_timespan(kernel, sizeof(kernel), - m->startup_timestamp.monotonic), - format_timespan(userspace, sizeof(userspace), - m->finish_timestamp.monotonic - m->startup_timestamp.monotonic), - format_timespan(sum, sizeof(sum), - m->finish_timestamp.monotonic)); - else + if (m->running_as == MANAGER_SYSTEM) { + 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), + m->initrd_timestamp.monotonic), + format_timespan(initrd, sizeof(initrd), + m->startup_timestamp.monotonic - m->initrd_timestamp.monotonic), + format_timespan(userspace, sizeof(userspace), + m->finish_timestamp.monotonic - m->startup_timestamp.monotonic), + format_timespan(sum, sizeof(sum), + m->finish_timestamp.monotonic)); + } else + log_info("Startup finished in %s (kernel) + %s (userspace) = %s.", + format_timespan(kernel, sizeof(kernel), + m->startup_timestamp.monotonic), + format_timespan(userspace, sizeof(userspace), + m->finish_timestamp.monotonic - m->startup_timestamp.monotonic), + format_timespan(sum, sizeof(sum), + m->finish_timestamp.monotonic)); + } else log_debug("Startup finished in %s.", format_timespan(userspace, sizeof(userspace), m->finish_timestamp.monotonic - m->startup_timestamp.monotonic)); } +void manager_run_generators(Manager *m) { + DIR *d = NULL; + struct dirent *de; + Hashmap *pids = NULL; + const char *generator_path; + + assert(m); + + generator_path = m->running_as == MANAGER_SYSTEM ? SYSTEM_GENERATOR_PATH : SESSION_GENERATOR_PATH; + if (!(d = opendir(generator_path))) { + + if (errno == ENOENT) + return; + + log_error("Failed to enumerate generator directory: %m"); + return; + } + + if (!m->generator_unit_path) { + char *p; + char system_path[] = "/dev/.systemd/generator-XXXXXX", + session_path[] = "/tmp/systemd-generator-XXXXXX"; + + if (!(p = mkdtemp(m->running_as == MANAGER_SYSTEM ? system_path : session_path))) { + log_error("Failed to generate generator directory: %m"); + goto finish; + } + + if (!(m->generator_unit_path = strdup(p))) { + log_error("Failed to allocate generator unit path."); + goto finish; + } + } + + if (!(pids = hashmap_new(trivial_hash_func, trivial_compare_func))) { + log_error("Failed to allocate set."); + goto finish; + } + + while ((de = readdir(d))) { + char *path; + pid_t pid; + int k; + + if (ignore_file(de->d_name)) + continue; + + if (de->d_type != DT_REG && + de->d_type != DT_LNK && + de->d_type != DT_UNKNOWN) + continue; + + if (asprintf(&path, "%s/%s", generator_path, de->d_name) < 0) { + log_error("Out of memory"); + continue; + } + + if ((pid = fork()) < 0) { + log_error("Failed to fork: %m"); + free(path); + continue; + } + + if (pid == 0) { + const char *arguments[5]; + /* Child */ + + arguments[0] = path; + arguments[1] = m->generator_unit_path; + arguments[2] = NULL; + + execv(path, (char **) arguments); + + log_error("Failed to execute %s: %m", path); + _exit(EXIT_FAILURE); + } + + log_debug("Spawned generator %s as %lu", path, (unsigned long) pid); + + if ((k = hashmap_put(pids, UINT_TO_PTR(pid), path)) < 0) { + log_error("Failed to add PID to set: %s", strerror(-k)); + free(path); + } + } + + while (!hashmap_isempty(pids)) { + siginfo_t si; + char *path; + + zero(si); + if (waitid(P_ALL, 0, &si, WEXITED) < 0) { + + if (errno == EINTR) + continue; + + log_error("waitid() failed: %m"); + goto finish; + } + + if ((path = hashmap_remove(pids, UINT_TO_PTR(si.si_pid)))) { + if (!is_clean_exit(si.si_code, si.si_status)) { + if (si.si_code == CLD_EXITED) + log_error("%s exited with exit status %i.", path, si.si_status); + else + log_error("%s terminated by signal %s.", path, signal_to_string(si.si_status)); + } else + log_debug("Generator %s exited successfully.", path); + + free(path); + } + } + + 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); + } + +finish: + if (d) + closedir(d); + + if (pids) + hashmap_free_free(pids); +} + +void manager_undo_generators(Manager *m) { + assert(m); + + if (!m->generator_unit_path) + return; + + strv_remove(m->lookup_paths.unit_path, m->generator_unit_path); + rm_rf(m->generator_unit_path, false, true); + + free(m->generator_unit_path); + m->generator_unit_path = NULL; +} + static const char* const manager_running_as_table[_MANAGER_RUNNING_AS_MAX] = { [MANAGER_SYSTEM] = "system", [MANAGER_SESSION] = "session"