X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=manager.c;h=b574c6380dad18b6886851b1732efe7186c2eedc;hp=fd219b03041f7eb5b58c19b2ef2f3750cdb655a9;hb=0acb83e9e63206af9a2c6a35ca054453d235458a;hpb=9f04bd5251b3c2e17c53cf28feb95e36e1991038 diff --git a/manager.c b/manager.c index fd219b030..b574c6380 100644 --- a/manager.c +++ b/manager.c @@ -16,6 +16,7 @@ #include "strv.h" #include "log.h" #include "util.h" +#include "ratelimit.h" static int manager_setup_signals(Manager *m) { sigset_t mask; @@ -36,7 +37,7 @@ static int manager_setup_signals(Manager *m) { assert_se(sigaddset(&mask, SIGPIPE) == 0); assert_se(sigprocmask(SIG_SETMASK, &mask, NULL) == 0); - m->signal_watch.type = WATCH_SIGNAL_FD; + m->signal_watch.type = WATCH_SIGNAL; if ((m->signal_watch.fd = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC)) < 0) return -errno; @@ -56,7 +57,8 @@ Manager* manager_new(void) { if (!(m = new0(Manager, 1))) return NULL; - m->signal_watch.fd = m->epoll_fd = -1; + 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; @@ -76,6 +78,10 @@ Manager* manager_new(void) { if (manager_setup_signals(m) < 0) goto fail; + /* FIXME: this should be called only when the D-Bus bus daemon is running */ + if (bus_init(m) < 0) + goto fail; + return m; fail: @@ -100,6 +106,8 @@ void manager_free(Manager *m) { if (unit_vtable[c]->shutdown) unit_vtable[c]->shutdown(m); + bus_done(m); + hashmap_free(m->units); hashmap_free(m->jobs); hashmap_free(m->transaction_jobs); @@ -563,6 +571,7 @@ static void transaction_minimize_impact(Manager *m) { HASHMAP_FOREACH(j, m->transaction_jobs, i) { LIST_FOREACH(transaction, j, j) { + bool stops_running_service, changes_existing_job; /* If it matters, we shouldn't drop it */ if (j->matters_to_anchor) @@ -571,12 +580,25 @@ static void transaction_minimize_impact(Manager *m) { /* Would this stop a running service? * Would this change an existing job? * If so, let's drop this entry */ - if ((j->type != JOB_STOP || UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(j->unit))) && - (!j->unit->meta.job || job_type_is_conflicting(j->type, j->unit->meta.job->state))) + + stops_running_service = + 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->state); + + if (!stops_running_service && !changes_existing_job) continue; + if (stops_running_service) + log_debug("%s/%s would stop a running service.", unit_id(j->unit), job_type_to_string(j->type)); + + if (changes_existing_job) + log_debug("%s/%s would change existing job.", unit_id(j->unit), job_type_to_string(j->type)); + /* 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)); + log_debug("Deleting %s/%s to minimize impact.", unit_id(j->unit), job_type_to_string(j->type)); + transaction_delete_job(m, j); again = true; break; @@ -1046,7 +1068,7 @@ 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; @@ -1109,10 +1131,10 @@ static int process_event(Manager *m, struct epoll_event *ev, bool *quit) { switch (w->type) { - case WATCH_SIGNAL_FD: + 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) @@ -1123,7 +1145,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: { @@ -1139,10 +1161,28 @@ 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; } + case WATCH_MOUNT: + /* Some mount table change, intended for the mount subsystem */ + 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."); } @@ -1154,14 +1194,27 @@ int manager_loop(Manager *m) { int r; bool quit = false; + RATELIMIT_DEFINE(rl, 1*USEC_PER_SEC, 1000); + assert(m); for (;;) { struct epoll_event event; int n; + if (!ratelimit_test(&rl)) { + /* Yay, something is going seriously wrong, pause a little */ + log_warning("Looping too fast. Throttling execution a little."); + sleep(1); + } + manager_dispatch_run_queue(m); + if (m->request_bus_dispatch) { + bus_dispatch(m); + continue; + } + if ((n = epoll_wait(m->epoll_fd, &event, 1, -1)) < 0) { if (errno == -EINTR) @@ -1179,3 +1232,51 @@ int manager_loop(Manager *m) { 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; +}