X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=manager.c;h=5cb5c3d2d2f3a8008453407c22f1dd6ada4134d3;hp=07b2d4ac9a5eb85e00447550bf329b2a34ec858d;hb=c9c0cadbf8d8e4c1b99c35a8150c08558f361ebd;hpb=9e2f7c11fb6ba35ffec2274da3e2d08b10d23965 diff --git a/manager.c b/manager.c index 07b2d4ac9..5cb5c3d2d 100644 --- a/manager.c +++ b/manager.c @@ -35,6 +35,8 @@ #include #include #include +#include +#include #include "manager.h" #include "hashmap.h" @@ -47,6 +49,14 @@ #include "mount-setup.h" #include "utmp-wtmp.h" #include "unit-name.h" +#include "dbus-unit.h" +#include "dbus-job.h" + +/* As soon as 16 units are in our GC queue, make sure to run a gc sweep */ +#define GC_QUEUE_ENTRIES_MAX 16 + +/* As soon as 5s passed since a unit was added to our GC queue, make sure to run a gc sweep */ +#define GC_QUEUE_USEC_MAX (5*USEC_PER_SEC) static int enable_special_signals(Manager *m) { char fd; @@ -320,8 +330,10 @@ int manager_new(ManagerRunningAs running_as, bool confirm_spawn, Manager **_m) { m->running_as = running_as; m->confirm_spawn = confirm_spawn; + m->name_data_slot = -1; + m->exit_code = _MANAGER_EXIT_CODE_INVALID; - m->signal_watch.fd = m->mount_watch.fd = m->udev_watch.fd = m->epoll_fd = -1; + m->signal_watch.fd = m->mount_watch.fd = m->udev_watch.fd = 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 */ if (!(m->units = hashmap_new(string_hash_func, string_compare_func))) @@ -339,6 +351,9 @@ int manager_new(ManagerRunningAs running_as, bool confirm_spawn, Manager **_m) { if (!(m->cgroup_bondings = hashmap_new(string_hash_func, string_compare_func))) goto fail; + if (!(m->watch_bus = hashmap_new(string_hash_func, string_compare_func))) + goto fail; + if ((m->epoll_fd = epoll_create1(EPOLL_CLOEXEC)) < 0) goto fail; @@ -380,10 +395,82 @@ static unsigned manager_dispatch_cleanup_queue(Manager *m) { return n; } -void manager_free(Manager *m) { - UnitType c; - Unit *u; +static void unit_gc_sweep(Unit *u, int gc_marker) { + Iterator i; + Unit *other; + + assert(u); + + if (u->meta.gc_marker == gc_marker || + u->meta.gc_marker == -gc_marker) + return; + + if (u->meta.in_cleanup_queue) + goto bad; + + if (unit_check_gc(u)) + goto good; + + SET_FOREACH(other, u->meta.dependencies[UNIT_REFERENCED_BY], i) { + unit_gc_sweep(other, gc_marker); + + if (other->meta.gc_marker == gc_marker) + goto good; + } + +bad: + /* So there is no reason to keep this unit around, hence let's get rid of it */ + u->meta.gc_marker = -gc_marker; + return; + +good: + u->meta.gc_marker = gc_marker; +} + +static unsigned manager_dispatch_gc_queue(Manager *m) { + Meta *meta; + unsigned n = 0; + int gc_marker; + + assert(m); + + if ((m->n_in_gc_queue < GC_QUEUE_ENTRIES_MAX) && + (m->gc_queue_timestamp <= 0 || + (m->gc_queue_timestamp + GC_QUEUE_USEC_MAX) > now(CLOCK_MONOTONIC))) + return 0; + + log_debug("Running GC..."); + + gc_marker = ++m->gc_marker; + + if (m->gc_marker < 0) + m->gc_marker = 1; + + while ((meta = m->gc_queue)) { + assert(meta->in_gc_queue); + + LIST_REMOVE(Meta, gc_queue, m->gc_queue, meta); + meta->in_gc_queue = false; + + n++; + + unit_gc_sweep(UNIT(meta), gc_marker); + + if (meta->gc_marker == -gc_marker) { + log_debug("Collecting %s", meta->id); + unit_add_to_cleanup_queue(UNIT(meta)); + } + } + + m->n_in_gc_queue = 0; + m->gc_queue_timestamp = 0; + + return n; +} + +static void manager_clear_jobs_and_units(Manager *m) { Job *j; + Unit *u; assert(m); @@ -392,14 +479,22 @@ void manager_free(Manager *m) { while ((u = hashmap_first(m->units))) unit_free(u); +} + +void manager_free(Manager *m) { + UnitType c; + + assert(m); - manager_dispatch_cleanup_queue(m); + manager_clear_jobs_and_units(m); for (c = 0; c < _UNIT_TYPE_MAX; c++) if (unit_vtable[c]->shutdown) unit_vtable[c]->shutdown(m); - manager_shutdown_cgroup(m); + /* If we reexecute ourselves, we keep the root cgroup + * around */ + manager_shutdown_cgroup(m, m->exit_code != MANAGER_REEXECUTE); bus_done_api(m); bus_done_system(m); @@ -408,11 +503,12 @@ void manager_free(Manager *m) { hashmap_free(m->jobs); hashmap_free(m->transaction_jobs); hashmap_free(m->watch_pids); + hashmap_free(m->watch_bus); if (m->epoll_fd >= 0) - close_nointr(m->epoll_fd); + close_nointr_nofail(m->epoll_fd); if (m->signal_watch.fd >= 0) - close_nointr(m->signal_watch.fd); + close_nointr_nofail(m->signal_watch.fd); strv_free(m->unit_path); strv_free(m->sysvinit_path); @@ -421,29 +517,35 @@ void manager_free(Manager *m) { free(m->cgroup_controller); free(m->cgroup_hierarchy); - assert(hashmap_isempty(m->cgroup_bondings)); hashmap_free(m->cgroup_bondings); free(m); } -int manager_coldplug(Manager *m) { - int r; +int manager_enumerate(Manager *m) { + int r = 0, q; UnitType c; - Iterator i; - Unit *u; - char *k; assert(m); - /* First, let's ask every type to load all units from - * disk/kernel that it might know */ + /* Let's ask every type to load all units from disk/kernel + * that it might know */ for (c = 0; c < _UNIT_TYPE_MAX; c++) if (unit_vtable[c]->enumerate) - if ((r = unit_vtable[c]->enumerate(m)) < 0) - return r; + if ((q = unit_vtable[c]->enumerate(m)) < 0) + r = q; manager_dispatch_load_queue(m); + return r; +} + +int manager_coldplug(Manager *m) { + int r = 0, q; + Iterator i; + Unit *u; + char *k; + + assert(m); /* Then, let's set up their initial state. */ HASHMAP_FOREACH_KEY(u, k, m->units, i) { @@ -453,15 +555,35 @@ int manager_coldplug(Manager *m) { continue; if (UNIT_VTABLE(u)->coldplug) - if ((r = UNIT_VTABLE(u)->coldplug(u)) < 0) - return r; + if ((q = UNIT_VTABLE(u)->coldplug(u)) < 0) + r = q; } + return r; +} + +int manager_startup(Manager *m, FILE *serialization, FDSet *fds) { + int r, q; + + assert(m); + + /* First, enumerate what we can from all config files */ + r = manager_enumerate(m); + + /* Second, deserialize if there is something to deserialize */ + if (serialization) + if ((q = manager_deserialize(m, serialization, fds)) < 0) + r = q; + + /* Third, fire things up! */ + if ((q = manager_coldplug(m)) < 0) + r = q; + /* Now that the initial devices are available, let's see if we * can write the utmp file */ manager_write_utmp_reboot(m); - return 0; + return r; } static void transaction_delete_job(Manager *m, Job *j, bool delete_dependencies) { @@ -1546,7 +1668,7 @@ static void manager_start_target(Manager *m, const char *name) { log_error("Failed to enqueue %s job: %s", name, strerror(-r)); } -static int manager_process_signal_fd(Manager *m, bool *quit) { +static int manager_process_signal_fd(Manager *m) { ssize_t n; struct signalfd_siginfo sfsi; bool sigchld = false; @@ -1579,7 +1701,7 @@ static int manager_process_signal_fd(Manager *m, bool *quit) { break; } - *quit = true; + m->exit_code = MANAGER_EXIT; return 0; case SIGWINCH: @@ -1621,6 +1743,10 @@ static int manager_process_signal_fd(Manager *m, bool *quit) { break; } + case SIGHUP: + m->exit_code = MANAGER_RELOAD; + break; + default: log_info("Got unhandled signal <%s>.", strsignal(sfsi.ssi_signo)); } @@ -1632,7 +1758,7 @@ static int manager_process_signal_fd(Manager *m, bool *quit) { return 0; } -static int process_event(Manager *m, struct epoll_event *ev, bool *quit) { +static int process_event(Manager *m, struct epoll_event *ev) { int r; Watch *w; @@ -1649,7 +1775,7 @@ static int process_event(Manager *m, struct epoll_event *ev, bool *quit) { if (ev->events != EPOLLIN) return -EINVAL; - if ((r = manager_process_signal_fd(m, quit)) < 0) + if ((r = manager_process_signal_fd(m)) < 0) return r; break; @@ -1704,13 +1830,13 @@ static int process_event(Manager *m, struct epoll_event *ev, bool *quit) { int manager_loop(Manager *m) { int r; - bool quit = false; RATELIMIT_DEFINE(rl, 1*USEC_PER_SEC, 1000); assert(m); + m->exit_code = MANAGER_RUNNING; - do { + while (m->exit_code == MANAGER_RUNNING) { struct epoll_event event; int n; @@ -1723,6 +1849,9 @@ int manager_loop(Manager *m) { if (manager_dispatch_cleanup_queue(m) > 0) continue; + if (manager_dispatch_gc_queue(m) > 0) + continue; + if (manager_dispatch_load_queue(m) > 0) continue; @@ -1745,11 +1874,11 @@ int manager_loop(Manager *m) { assert(n == 1); - if ((r = process_event(m, &event, &quit)) < 0) + if ((r = process_event(m, &event)) < 0) return r; - } while (!quit); + } - return 0; + return m->exit_code; } int manager_get_unit_from_dbus_path(Manager *m, const char *s, Unit **_u) { @@ -1866,6 +1995,190 @@ void manager_write_utmp_runlevel(Manager *m, Unit *u) { } } +void manager_dispatch_bus_name_owner_changed( + Manager *m, + const char *name, + const char* old_owner, + const char *new_owner) { + + Unit *u; + + assert(m); + assert(name); + + if (!(u = hashmap_get(m->watch_bus, name))) + return; + + UNIT_VTABLE(u)->bus_name_owner_change(u, name, old_owner, new_owner); +} + +void manager_dispatch_bus_query_pid_done( + Manager *m, + const char *name, + pid_t pid) { + + Unit *u; + + assert(m); + assert(name); + assert(pid >= 1); + + if (!(u = hashmap_get(m->watch_bus, name))) + return; + + UNIT_VTABLE(u)->bus_query_pid_done(u, name, pid); +} + +int manager_open_serialization(FILE **_f) { + char *path; + mode_t saved_umask; + int fd; + FILE *f; + + assert(_f); + + if (asprintf(&path, "/dev/shm/systemd-%u.dump-XXXXXX", (unsigned) getpid()) < 0) + return -ENOMEM; + + saved_umask = umask(0077); + fd = mkostemp(path, O_RDWR|O_CLOEXEC); + umask(saved_umask); + + if (fd < 0) { + free(path); + return -errno; + } + + unlink(path); + + log_debug("Serializing state to %s", path); + free(path); + + if (!(f = fdopen(fd, "w+")) < 0) + return -errno; + + *_f = f; + + return 0; +} + +int manager_serialize(Manager *m, FILE *f, FDSet *fds) { + Iterator i; + Unit *u; + const char *t; + int r; + + assert(m); + assert(f); + assert(fds); + + HASHMAP_FOREACH_KEY(u, t, m->units, i) { + if (u->meta.id != t) + continue; + + if (!unit_can_serialize(u)) + continue; + + /* Start marker */ + fputs(u->meta.id, f); + fputc('\n', f); + + if ((r = unit_serialize(u, f, fds)) < 0) + return r; + } + + if (ferror(f)) + return -EIO; + + return 0; +} + +int manager_deserialize(Manager *m, FILE *f, FDSet *fds) { + int r = 0; + + assert(m); + assert(f); + + log_debug("Deserializing state..."); + + for (;;) { + Unit *u; + char name[UNIT_NAME_MAX+2]; + + /* Start marker */ + if (!fgets(name, sizeof(name), f)) { + if (feof(f)) + break; + + return -errno; + } + + char_array_0(name); + + if ((r = manager_load_unit(m, strstrip(name), NULL, &u)) < 0) + return r; + + if ((r = unit_deserialize(u, f, fds)) < 0) + return r; + } + + if (ferror(f)) + return -EIO; + + return 0; +} + +int manager_reload(Manager *m) { + int r, q; + FILE *f; + FDSet *fds; + + assert(m); + + if ((r = manager_open_serialization(&f)) < 0) + return r; + + if (!(fds = fdset_new())) { + r = -ENOMEM; + goto finish; + } + + if ((r = manager_serialize(m, f, fds)) < 0) + goto finish; + + if (fseeko(f, 0, SEEK_SET) < 0) { + r = -errno; + goto finish; + } + + /* From here on there is no way back. */ + manager_clear_jobs_and_units(m); + + /* First, enumerate what we can from all config files */ + if ((q = manager_enumerate(m)) < 0) + r = q; + + /* Second, deserialize our stored data */ + if ((q = manager_deserialize(m, f, fds)) < 0) + r = q; + + fclose(f); + f = NULL; + + /* Third, fire things up! */ + if ((q = manager_coldplug(m)) < 0) + r = q; + +finish: + if (f) + fclose(f); + + if (fds) + fdset_free(fds); + + return r; +} + static const char* const manager_running_as_table[_MANAGER_RUNNING_AS_MAX] = { [MANAGER_INIT] = "init", [MANAGER_SYSTEM] = "system",