X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=manager.c;h=6c73acf403fc0abe43be94abea08ec05aaa3e495;hp=ac1c79c5f6f634a2c6e98235801a3e194c39ecdf;hb=6c1a047834c524598ee87c2bc289a1cd472ba53e;hpb=ef734fd6c2ec4e5602bbfe2a0d26dcf39c14d2bf diff --git a/manager.c b/manager.c index ac1c79c5f..6c73acf40 100644 --- a/manager.c +++ b/manager.c @@ -1,5 +1,24 @@ /*-*- Mode: C; c-basic-offset: 8 -*-*/ +/*** + 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 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 . +***/ + #include #include #include @@ -9,6 +28,10 @@ #include #include #include +#include +#include +#include +#include #include "manager.h" #include "hashmap.h" @@ -16,6 +39,9 @@ #include "strv.h" #include "log.h" #include "util.h" +#include "ratelimit.h" +#include "cgroup.h" +#include "mount-setup.h" static int manager_setup_signals(Manager *m) { sigset_t mask; @@ -23,8 +49,6 @@ static int manager_setup_signals(Manager *m) { assert(m); - assert_se(reset_all_signal_handlers() == 0); - assert_se(sigemptyset(&mask) == 0); assert_se(sigaddset(&mask, SIGCHLD) == 0); assert_se(sigaddset(&mask, SIGINT) == 0); /* Kernel sends us this on control-alt-del */ @@ -34,6 +58,8 @@ static int manager_setup_signals(Manager *m) { assert_se(sigaddset(&mask, SIGUSR1) == 0); assert_se(sigaddset(&mask, SIGUSR2) == 0); assert_se(sigaddset(&mask, SIGPIPE) == 0); + assert_se(sigaddset(&mask, SIGPWR) == 0); + assert_se(sigaddset(&mask, SIGTTIN) == 0); assert_se(sigprocmask(SIG_SETMASK, &mask, NULL) == 0); m->signal_watch.type = WATCH_SIGNAL; @@ -47,16 +73,203 @@ 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_INIT) { + /* Enable that we get SIGINT on control-alt-del */ + if (reboot(RB_DISABLE_CAD) < 0) + log_warning("Failed to enable ctrl-alt-del handling: %s", strerror(errno)); + + /* Enable that we get SIGWINCH on kbrequest */ + if (ioctl(0, KDSIGACCEPT, SIGWINCH) < 0) + log_warning("Failed to enable kbrequest handling: %s", strerror(errno)); + } + + return 0; +} + +static char** session_dirs(void) { + const char *home, *e; + char *config_home = NULL, *data_home = NULL; + char **config_dirs = NULL, **data_dirs = NULL; + char **r = NULL, **t; + + /* Implement the mechanisms defined in + * + * http://standards.freedesktop.org/basedir-spec/basedir-spec-0.6.html + * + * We look in both the config and the data dirs because we + * want to encourage that distributors ship their unit files + * as data, and allow overriding as configuration. + */ + + home = getenv("HOME"); + + if ((e = getenv("XDG_CONFIG_HOME"))) { + if (asprintf(&config_home, "%s/systemd/session", e) < 0) + goto fail; + + } else if (home) { + if (asprintf(&config_home, "%s/.config/systemd/session", home) < 0) + goto fail; + } + + if ((e = getenv("XDG_CONFIG_DIRS"))) + config_dirs = strv_split(e, ":"); + else + config_dirs = strv_new("/etc/xdg", NULL); + + if (!config_dirs) + goto fail; + + if ((e = getenv("XDG_DATA_HOME"))) { + if (asprintf(&data_home, "%s/systemd/session", e) < 0) + goto fail; + + } else if (home) { + if (asprintf(&data_home, "%s/.local/share/systemd/session", home) < 0) + goto fail; + } + + if ((e = getenv("XDG_DATA_DIRS"))) + data_dirs = strv_split(e, ":"); + else + data_dirs = strv_new("/usr/local/share", "/usr/share", NULL); + + if (!data_dirs) + goto fail; + + /* Now merge everything we found. */ + if (config_home) { + if (!(t = strv_append(r, config_home))) + goto fail; + strv_free(r); + r = t; + } + + if (!(t = strv_merge_concat(r, config_dirs, "/systemd/session"))) + goto finish; + strv_free(r); + r = t; + + if (!(t = strv_append(r, SESSION_CONFIG_UNIT_PATH))) + goto fail; + strv_free(r); + r = t; + + if (data_home) { + if (!(t = strv_append(r, data_home))) + goto fail; + strv_free(r); + r = t; + } + + if (!(t = strv_merge_concat(r, data_dirs, "/systemd/session"))) + goto fail; + strv_free(r); + r = t; + + if (!(t = strv_append(r, SESSION_DATA_UNIT_PATH))) + goto fail; + strv_free(r); + r = t; + + if (!strv_path_make_absolute_cwd(r)) + goto fail; + +finish: + free(config_home); + strv_free(config_dirs); + free(data_home); + strv_free(data_dirs); + + return r; + +fail: + strv_free(r); + r = NULL; + goto finish; +} + +static int manager_find_paths(Manager *m) { + const char *e; + char *t; + + assert(m); + + /* First priority is whatever has been passed to us via env + * vars */ + if ((e = getenv("SYSTEMD_UNIT_PATH"))) + if (!(m->unit_path = split_path_and_make_absolute(e))) + return -ENOMEM; + + if (strv_isempty(m->unit_path)) { + + /* Nothing is set, so let's figure something out. */ + strv_free(m->unit_path); + + if (m->running_as == MANAGER_SESSION) { + if (!(m->unit_path = session_dirs())) + return -ENOMEM; + } else + if (!(m->unit_path = strv_new( + SYSTEM_CONFIG_UNIT_PATH, /* /etc/systemd/system/ */ + SYSTEM_DATA_UNIT_PATH, /* /lib/systemd/system/ */ + NULL))) + return -ENOMEM; + } + + if (m->running_as == MANAGER_INIT) { + /* /etc/init.d/ compativility does not matter to users */ + + if ((e = getenv("SYSTEMD_SYSVINIT_PATH"))) + if (!(m->sysvinit_path = split_path_and_make_absolute(e))) + return -ENOMEM; + + if (strv_isempty(m->sysvinit_path)) { + strv_free(m->sysvinit_path); + + if (!(m->sysvinit_path = strv_new( + SYSTEM_SYSVINIT_PATH, /* /etc/init.d/ */ + NULL))) + return -ENOMEM; + } + } + + strv_uniq(m->unit_path); + strv_uniq(m->sysvinit_path); + + assert(!strv_isempty(m->unit_path)); + if (!(t = strv_join(m->unit_path, "\n\t"))) + return -ENOMEM; + log_debug("Looking for unit files in:\n\t%s", t); + free(t); + + if (!strv_isempty(m->sysvinit_path)) { + + if (!(t = strv_join(m->sysvinit_path, "\n\t"))) + return -ENOMEM; + + log_debug("Looking for SysV init scripts in:\n\t%s", t); + free(t); + } else + log_debug("Ignoring SysV init scripts."); + return 0; } -Manager* manager_new(void) { +int manager_new(ManagerRunningAs running_as, Manager **_m) { Manager *m; + int r = -ENOMEM; + + assert(_m); + assert(running_as >= 0); + assert(running_as < _MANAGER_RUNNING_AS_MAX); if (!(m = new0(Manager, 1))) - return NULL; + return -ENOMEM; - m->signal_watch.fd = m->mount_watch.fd = m->epoll_fd = -1; + m->running_as = running_as; + m->signal_watch.fd = m->mount_watch.fd = m->udev_watch.fd = m->epoll_fd = -1; + m->current_job_id = 1; /* start as id #1, so that we can leave #0 around as "null-like" value */ if (!(m->units = hashmap_new(string_hash_func, string_compare_func))) goto fail; @@ -70,17 +283,48 @@ Manager* manager_new(void) { 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->epoll_fd = epoll_create1(EPOLL_CLOEXEC)) < 0) goto fail; - if (manager_setup_signals(m) < 0) + if ((r = manager_find_paths(m)) < 0) + goto fail; + + if ((r = manager_setup_signals(m)) < 0) + goto fail; + + if ((r = manager_setup_cgroup(m)) < 0) goto fail; - return m; + /* Try to connect to the busses, if possible. */ + if ((r = bus_init_system(m)) < 0 || + (r = bus_init_api(m)) < 0) + goto fail; + + *_m = m; + return 0; fail: manager_free(m); - return NULL; + return r; +} + +static unsigned manager_dispatch_cleanup_queue(Manager *m) { + Meta *meta; + unsigned n = 0; + + assert(m); + + while ((meta = m->cleanup_queue)) { + assert(meta->in_cleanup_queue); + + unit_free(UNIT(meta)); + n++; + } + + return n; } void manager_free(Manager *m) { @@ -96,10 +340,17 @@ void manager_free(Manager *m) { while ((u = hashmap_first(m->units))) unit_free(u); + manager_dispatch_cleanup_queue(m); + for (c = 0; c < _UNIT_TYPE_MAX; c++) if (unit_vtable[c]->shutdown) unit_vtable[c]->shutdown(m); + manager_shutdown_cgroup(m); + + bus_done_api(m); + bus_done_system(m); + hashmap_free(m->units); hashmap_free(m->jobs); hashmap_free(m->transaction_jobs); @@ -110,6 +361,15 @@ void manager_free(Manager *m) { if (m->signal_watch.fd >= 0) close_nointr(m->signal_watch.fd); + strv_free(m->unit_path); + strv_free(m->sysvinit_path); + + free(m->cgroup_controller); + free(m->cgroup_hierarchy); + + assert(hashmap_isempty(m->cgroup_bondings)); + hashmap_free(m->cgroup_bondings); + free(m); } @@ -146,13 +406,13 @@ int manager_coldplug(Manager *m) { return 0; } -static void transaction_delete_job(Manager *m, Job *j) { +static void transaction_delete_job(Manager *m, Job *j, bool delete_dependencies) { assert(m); assert(j); /* Deletes one job from the transaction */ - manager_transaction_unlink_job(m, j); + manager_transaction_unlink_job(m, j, delete_dependencies); if (!j->installed) job_free(j); @@ -165,7 +425,7 @@ static void transaction_delete_unit(Manager *m, Unit *u) { * transaction */ while ((j = hashmap_get(m->transaction_jobs, u))) - transaction_delete_job(m, j); + transaction_delete_job(m, j, true); } static void transaction_clean_dependencies(Manager *m) { @@ -193,7 +453,7 @@ static void transaction_abort(Manager *m) { while ((j = hashmap_first(m->transaction_jobs))) if (j->installed) - transaction_delete_job(m, j); + transaction_delete_job(m, j, true); else job_free(j); @@ -285,7 +545,7 @@ static void transaction_merge_and_delete_job(Manager *m, Job *j, Job *other, Job /* Kill the other job */ other->subject_list = NULL; other->object_list = NULL; - transaction_delete_job(m, other); + transaction_delete_job(m, other, true); } static int delete_one_unmergeable_job(Manager *m, Job *j) { @@ -318,8 +578,8 @@ static int delete_one_unmergeable_job(Manager *m, Job *j) { return -ENOEXEC; /* Ok, we can drop one, so let's do so. */ - log_debug("Try to fix job merging by deleting job %s/%s", unit_id(d->unit), job_type_to_string(d->type)); - transaction_delete_job(m, d); + log_debug("Trying to fix job merging by deleting job %s/%s", unit_id(d->unit), job_type_to_string(d->type)); + transaction_delete_job(m, d, true); return 0; } @@ -387,6 +647,46 @@ static int transaction_merge_jobs(Manager *m) { return 0; } +static void transaction_drop_redundant(Manager *m) { + bool again; + + assert(m); + + /* Goes through the transaction and removes all jobs that are + * a noop */ + + do { + Job *j; + Iterator i; + + again = false; + + HASHMAP_FOREACH(j, m->transaction_jobs, i) { + bool changes_something = false; + Job *k; + + LIST_FOREACH(transaction, k, j) { + + if (!job_is_anchor(k) && + job_type_is_redundant(k->type, unit_active_state(k->unit))) + continue; + + changes_something = true; + break; + } + + if (changes_something) + continue; + + log_debug("Found redundant job %s/%s, dropping.", unit_id(j->unit), job_type_to_string(j->type)); + transaction_delete_job(m, j, false); + again = true; + break; + } + + } while (again); +} + static bool unit_matters_to_anchor(Unit *u, Job *j) { assert(u); assert(!j->transaction_prev); @@ -424,11 +724,11 @@ static int transaction_verify_order_one(Manager *m, Job *j, Job *from, unsigned * since smart how we are we stored our way back in * there. */ - log_debug("Found cycle on %s/%s", unit_id(j->unit), job_type_to_string(j->type)); + log_debug("Found ordering cycle on %s/%s", unit_id(j->unit), job_type_to_string(j->type)); for (k = from; k; k = (k->generation == generation ? k->marker : NULL)) { - log_debug("Walked on cycle path to %s/%s", unit_id(j->unit), job_type_to_string(j->type)); + log_debug("Walked on cycle path to %s/%s", unit_id(k->unit), job_type_to_string(k->type)); if (!k->installed && !unit_matters_to_anchor(k->unit, k)) { @@ -516,7 +816,7 @@ static void transaction_collect_garbage(Manager *m) { continue; log_debug("Garbage collecting job %s/%s", unit_id(j->unit), job_type_to_string(j->type)); - transaction_delete_job(m, j); + transaction_delete_job(m, j, true); again = true; break; } @@ -591,7 +891,7 @@ static void transaction_minimize_impact(Manager *m) { /* Ok, let's get rid of this */ log_debug("Deleting %s/%s to minimize impact.", unit_id(j->unit), job_type_to_string(j->type)); - transaction_delete_job(m, j); + transaction_delete_job(m, j, true); again = true; break; } @@ -638,7 +938,8 @@ static int transaction_apply(Manager *m, JobMode mode) { assert(!j->transaction_next); assert(!j->transaction_prev); - job_schedule_run(j); + job_add_to_run_queue(j); + job_add_to_dbus_queue(j); } /* As last step, kill all remaining job dependencies. */ @@ -675,12 +976,15 @@ static int transaction_activate(Manager *m, JobMode mode) { * jobs if we don't have to. */ transaction_minimize_impact(m); + /* Third step: Drop redundant jobs */ + transaction_drop_redundant(m); + for (;;) { - /* Third step: Let's remove unneeded jobs that might + /* Fourth step: Let's remove unneeded jobs that might * be lurking. */ transaction_collect_garbage(m); - /* Fourth step: verify order makes sense and correct + /* Fifth step: verify order makes sense and correct * cycles if necessary and possible */ if ((r = transaction_verify_order(m, &generation)) >= 0) break; @@ -695,7 +999,7 @@ static int transaction_activate(Manager *m, JobMode mode) { } for (;;) { - /* Fifth step: let's drop unmergeable entries if + /* Sixth step: let's drop unmergeable entries if * necessary and possible, merge entries we can * merge */ if ((r = transaction_merge_jobs(m)) >= 0) @@ -706,7 +1010,7 @@ static int transaction_activate(Manager *m, JobMode mode) { goto rollback; } - /* Sixth step: an entry got dropped, let's garbage + /* Seventh step: an entry got dropped, let's garbage * collect its dependencies. */ transaction_collect_garbage(m); @@ -714,14 +1018,17 @@ static int transaction_activate(Manager *m, JobMode mode) { * unmergeable entries ... */ } - /* Seventh step: check whether we can actually apply this */ + /* Eights step: Drop redundant jobs again, if the merging now allows us to drop more. */ + transaction_drop_redundant(m); + + /* Ninth step: check whether we can actually apply this */ if (mode == JOB_FAIL) if ((r = transaction_is_destructive(m, mode)) < 0) { log_debug("Requested transaction contradicts existing jobs: %s", strerror(-r)); goto rollback; } - /* Eights step: apply changes */ + /* Tenth step: apply changes */ if ((r = transaction_apply(m, mode)) < 0) { log_debug("Failed to apply transaction: %s", strerror(-r)); goto rollback; @@ -780,10 +1087,12 @@ static Job* transaction_add_one_job(Manager *m, JobType type, Unit *unit, bool f if (is_new) *is_new = true; + log_debug("Added job %s/%s to transaction.", unit_id(unit), job_type_to_string(type)); + return j; } -void manager_transaction_unlink_job(Manager *m, Job *j) { +void manager_transaction_unlink_job(Manager *m, Job *j, bool delete_dependencies) { assert(m); assert(j); @@ -807,11 +1116,11 @@ void manager_transaction_unlink_job(Manager *m, Job *j) { job_dependency_free(j->object_list); - if (other) { + if (other && delete_dependencies) { log_debug("Deleting job %s/%s as dependency of job %s/%s", unit_id(other->unit), job_type_to_string(other->type), unit_id(j->unit), job_type_to_string(j->type)); - transaction_delete_job(m, other); + transaction_delete_job(m, other, delete_dependencies); } } } @@ -851,8 +1160,8 @@ static int transaction_add_job_and_dependencies(Manager *m, JobType type, Unit * if ((r = transaction_add_job_and_dependencies(m, JOB_START, dep, ret, !force, force, NULL)) < 0 && r != -EBADR) goto fail; SET_FOREACH(dep, ret->unit->meta.dependencies[UNIT_WANTS], i) - if ((r = transaction_add_job_and_dependencies(m, JOB_START, dep, ret, false, force, NULL)) < 0 && r != -EBADR) - goto fail; + if ((r = transaction_add_job_and_dependencies(m, JOB_START, dep, ret, false, force, NULL)) < 0) + log_warning("Cannot add dependency job for unit %s, ignoring: %s", unit_id(dep), strerror(-r)); SET_FOREACH(dep, ret->unit->meta.dependencies[UNIT_REQUISITE], i) if ((r = transaction_add_job_and_dependencies(m, JOB_VERIFY_ACTIVE, dep, ret, true, force, NULL)) < 0 && r != -EBADR) goto fail; @@ -873,6 +1182,9 @@ static int transaction_add_job_and_dependencies(Manager *m, JobType type, Unit * /* JOB_VERIFY_STARTED, JOB_RELOAD require no dependency handling */ } + if (_ret) + *_ret = ret; + return 0; fail: @@ -890,7 +1202,7 @@ int manager_add_job(Manager *m, JobType type, Unit *unit, JobMode mode, bool for log_debug("Trying to enqueue job %s/%s", unit_id(unit), job_type_to_string(type)); - if ((r = transaction_add_job_and_dependencies(m, type, unit, NULL, true, force, &ret))) { + if ((r = transaction_add_job_and_dependencies(m, type, unit, NULL, true, force, &ret)) < 0) { transaction_abort(m); return r; } @@ -898,7 +1210,7 @@ int manager_add_job(Manager *m, JobType type, Unit *unit, JobMode mode, bool for if ((r = transaction_activate(m, mode)) < 0) return r; - log_debug("Enqueued job %s/%s", unit_id(unit), job_type_to_string(type)); + log_debug("Enqueued job %s/%s as %u", unit_id(unit), job_type_to_string(type), (unsigned) ret->id); if (_ret) *_ret = ret; @@ -919,14 +1231,15 @@ Unit *manager_get_unit(Manager *m, const char *name) { return hashmap_get(m->units, name); } -void manager_dispatch_load_queue(Manager *m) { +unsigned manager_dispatch_load_queue(Manager *m) { Meta *meta; + unsigned n = 0; assert(m); /* Make sure we are not run recursively */ if (m->dispatching_load_queue) - return; + return 0; m->dispatching_load_queue = true; @@ -937,9 +1250,11 @@ void manager_dispatch_load_queue(Manager *m) { assert(meta->in_load_queue); unit_load(UNIT(meta)); + n++; } m->dispatching_load_queue = false; + return n; } int manager_load_unit(Manager *m, const char *path, Unit **_ret) { @@ -965,7 +1280,7 @@ int manager_load_unit(Manager *m, const char *path, Unit **_ret) { return -ENOMEM; if (is_path(path)) { - if (!(ret->meta.load_path = strdup(path))) { + if (!(ret->meta.fragment_path = strdup(path))) { unit_free(ret); return -ENOMEM; } @@ -977,9 +1292,11 @@ int manager_load_unit(Manager *m, const char *path, Unit **_ret) { } unit_add_to_load_queue(ret); + unit_add_to_dbus_queue(ret); + manager_dispatch_load_queue(m); - *_ret = ret; + *_ret = unit_follow_merge(ret); return 0; } @@ -1018,11 +1335,12 @@ void manager_clear_jobs(Manager *m) { job_free(j); } -void manager_dispatch_run_queue(Manager *m) { +unsigned manager_dispatch_run_queue(Manager *m) { Job *j; + unsigned n = 0; if (m->dispatching_run_queue) - return; + return 0; m->dispatching_run_queue = true; @@ -1031,9 +1349,41 @@ void manager_dispatch_run_queue(Manager *m) { 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; + Meta *meta; + unsigned n = 0; + + assert(m); + + if (m->dispatching_dbus_queue) + return 0; + + m->dispatching_dbus_queue = true; + + while ((meta = m->dbus_unit_queue)) { + assert(meta->in_dbus_queue); + + bus_unit_send_change_signal(UNIT(meta)); + 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_dispatch_sigchld(Manager *m) { @@ -1060,11 +1410,13 @@ static int manager_dispatch_sigchld(Manager *m) { if (si.si_code != CLD_EXITED && si.si_code != CLD_KILLED && si.si_code != CLD_DUMPED) continue; - log_debug("child %llu died (code=%s, status=%i)", (long long unsigned) si.si_pid, sigchld_code(si.si_code), si.si_status); + log_debug("child %llu died (code=%s, status=%i)", (long long unsigned) si.si_pid, sigchld_code_to_string(si.si_code), si.si_status); if (!(u = hashmap_remove(m->watch_pids, UINT32_TO_PTR(si.si_pid)))) continue; + log_debug("child %llu belongs to %s", (long long unsigned) si.si_pid, unit_id(u)); + UNIT_VTABLE(u)->sigchld_event(u, si.si_pid, si.si_code, si.si_status); } @@ -1098,8 +1450,50 @@ static int manager_process_signal_fd(Manager *m, bool *quit) { case SIGINT: case SIGTERM: - *quit = true; - return 0; + + if (m->running_as != MANAGER_INIT) { + *quit = true; + return 0; + + } else { + Unit *target; + int r; + + if ((r = manager_load_unit(m, SPECIAL_CTRL_ALT_DEL_TARGET, &target)) < 0) + log_error("Failed to load ctrl-alt-del target: %s", strerror(-r)); + else if ((r = manager_add_job(m, JOB_START, target, JOB_REPLACE, true, NULL)) < 0) + log_error("Failed to enqueue ctrl-alt-del job: %s", strerror(-r)); + + break; + } + + case SIGWINCH: + + if (m->running_as == MANAGER_INIT) { + Unit *target; + int r; + + if ((r = manager_load_unit(m, SPECIAL_KBREQUEST_TARGET, &target)) < 0) + log_error("Failed to load kbrequest target: %s", strerror(-r)); + else if ((r = manager_add_job(m, JOB_START, target, JOB_REPLACE, true, NULL)) < 0) + log_error("Failed to enqueue kbrequest job: %s", strerror(-r)); + + break; + } + + /* This is a nop on non-init systemd's */ + + break; + + case SIGUSR1: + + printf("→ By units:\n"); + manager_dump_units(m, stdout, "\t"); + + printf("→ By jobs:\n"); + manager_dump_jobs(m, stdout, "\t"); + + break; default: log_info("Got unhandled signal <%s>.", strsignal(sfsi.ssi_signo)); @@ -1126,7 +1520,7 @@ static int process_event(Manager *m, struct epoll_event *ev, bool *quit) { case WATCH_SIGNAL: /* An incoming signal? */ - if (ev->events != POLLIN) + if (ev->events != EPOLLIN) return -EINVAL; if ((r = manager_process_signal_fd(m, quit)) < 0) @@ -1137,7 +1531,7 @@ static int process_event(Manager *m, struct epoll_event *ev, bool *quit) { case WATCH_FD: /* Some fd event, to be dispatched to the units */ - UNIT_VTABLE(w->unit)->fd_event(w->unit, w->fd, ev->events, w); + UNIT_VTABLE(w->data.unit)->fd_event(w->data.unit, w->fd, ev->events, w); break; case WATCH_TIMER: { @@ -1145,7 +1539,7 @@ static int process_event(Manager *m, struct epoll_event *ev, bool *quit) { ssize_t k; /* Some timer event, to be dispatched to the units */ - if ((k = read(ev->data.fd, &v, sizeof(v))) != sizeof(v)) { + if ((k = read(w->fd, &v, sizeof(v))) != sizeof(v)) { if (k < 0 && (errno == EINTR || errno == EAGAIN)) break; @@ -1153,7 +1547,7 @@ static int process_event(Manager *m, struct epoll_event *ev, bool *quit) { return k < 0 ? -errno : -EIO; } - UNIT_VTABLE(w->unit)->timer_event(w->unit, v, w); + UNIT_VTABLE(w->data.unit)->timer_event(w->data.unit, v, w); break; } @@ -1162,6 +1556,19 @@ static int process_event(Manager *m, struct epoll_event *ev, bool *quit) { mount_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: assert_not_reached("Unknown epoll event type."); } @@ -1173,13 +1580,34 @@ int manager_loop(Manager *m) { int r; bool quit = false; + RATELIMIT_DEFINE(rl, 1*USEC_PER_SEC, 1000); + assert(m); - for (;;) { + do { struct epoll_event event; int n; - manager_dispatch_run_queue(m); + if (!ratelimit_test(&rl)) { + /* Yay, something is going seriously wrong, pause a little */ + log_warning("Looping too fast. Throttling execution a little."); + sleep(1); + } + + if (manager_dispatch_cleanup_queue(m) > 0) + 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_dbus_queue(m) > 0) + continue; if ((n = epoll_wait(m->epoll_fd, &event, 1, -1)) < 0) { @@ -1193,8 +1621,63 @@ int manager_loop(Manager *m) { if ((r = process_event(m, &event, &quit)) < 0) return r; + } while (!quit); - if (quit) - return 0; - } + return 0; } + +int manager_get_unit_from_dbus_path(Manager *m, const char *s, Unit **_u) { + char *n; + Unit *u; + + assert(m); + assert(s); + assert(_u); + + if (!startswith(s, "/org/freedesktop/systemd1/unit/")) + return -EINVAL; + + if (!(n = bus_path_unescape(s+31))) + return -ENOMEM; + + u = manager_get_unit(m, n); + free(n); + + if (!u) + return -ENOENT; + + *_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; +} + +static const char* const manager_running_as_table[_MANAGER_RUNNING_AS_MAX] = { + [MANAGER_INIT] = "init", + [MANAGER_SYSTEM] = "system", + [MANAGER_SESSION] = "session" +}; + +DEFINE_STRING_TABLE_LOOKUP(manager_running_as, ManagerRunningAs);