From 701cc384c283206a29b21e4e7302e5cf5f2d9433 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 21 Apr 2010 06:01:13 +0200 Subject: [PATCH] manager: automatically GC unreferenced units --- automount.c | 15 ++++- dbus-job.c | 4 +- dbus-manager.c | 2 +- dbus-unit.c | 4 +- device.c | 2 +- initctl.c | 2 +- job.c | 4 +- load-dropin.c | 2 +- load-fragment.c | 2 +- manager.c | 80 +++++++++++++++++++++++ manager.h | 9 +++ mount.c | 32 ++++++---- org.freedesktop.systemd1.conf | 14 ++--- service.c | 40 +++++++++--- service.h | 2 + snapshot.c | 13 ++-- socket.c | 2 +- systemadm.vala | 2 +- systemctl.vala | 2 +- unit.c | 115 ++++++++++++++++++++++++++++------ unit.h | 40 +++++++++--- 21 files changed, 314 insertions(+), 74 deletions(-) diff --git a/automount.c b/automount.c index 7aa55b4f9..b124fc5b0 100644 --- a/automount.c +++ b/automount.c @@ -149,7 +149,7 @@ static int automount_load(Unit *u) { if ((r = unit_load_related_unit(u, ".mount", (Unit**) &a->mount)) < 0) return r; - if ((r = unit_add_dependency(u, UNIT_BEFORE, UNIT(a->mount))) < 0) + if ((r = unit_add_dependency(u, UNIT_BEFORE, UNIT(a->mount), true)) < 0) return r; } @@ -640,13 +640,20 @@ static const char *automount_sub_state_to_string(Unit *u) { return automount_state_to_string(AUTOMOUNT(u)->state); } +static bool automount_check_gc(Unit *u) { + Automount *a = AUTOMOUNT(u); + + assert(a); + + return UNIT_VTABLE(UNIT(a->mount))->check_gc(UNIT(a->mount)); +} + static void automount_fd_event(Unit *u, int fd, uint32_t events, Watch *w) { + Automount *a = AUTOMOUNT(u); union autofs_v5_packet_union packet; ssize_t l; int r; - Automount *a = AUTOMOUNT(u); - assert(a); assert(fd == a->pipe_fd); @@ -729,6 +736,8 @@ const UnitVTable automount_vtable = { .active_state = automount_active_state, .sub_state_to_string = automount_sub_state_to_string, + .check_gc = automount_check_gc, + .fd_event = automount_fd_event, .bus_message_handler = bus_automount_message_handler, diff --git a/dbus-job.c b/dbus-job.c index bec5bca93..f14f92f9a 100644 --- a/dbus-job.c +++ b/dbus-job.c @@ -166,7 +166,7 @@ void bus_job_send_change_signal(Job *j) { } else { /* Send a new signal */ - if (!(m = dbus_message_new_signal("/org/freedesktop/systemd1", "org.freedesktop.systemd1", "JobNew"))) + if (!(m = dbus_message_new_signal("/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "JobNew"))) goto oom; if (!dbus_message_append_args(m, @@ -207,7 +207,7 @@ void bus_job_send_removed_signal(Job *j) { if (!(p = job_dbus_path(j))) goto oom; - if (!(m = dbus_message_new_signal("/org/freedesktop/systemd1", "org.freedesktop.systemd1", "JobRemoved"))) + if (!(m = dbus_message_new_signal("/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "JobRemoved"))) goto oom; if (!dbus_message_append_args(m, diff --git a/dbus-manager.c b/dbus-manager.c index a8bf7e52c..926324811 100644 --- a/dbus-manager.c +++ b/dbus-manager.c @@ -199,7 +199,7 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection DBUS_TYPE_INVALID)) goto oom; - } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1", "GetJob")) { + } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "GetJob")) { uint32_t id; Job *j; diff --git a/dbus-unit.c b/dbus-unit.c index cbc2812e3..ccaaef99a 100644 --- a/dbus-unit.c +++ b/dbus-unit.c @@ -362,7 +362,7 @@ void bus_unit_send_change_signal(Unit *u) { } else { /* Send a new signal */ - if (!(m = dbus_message_new_signal("/org/freedesktop/systemd1", "org.freedesktop.systemd1", "UnitNew"))) + if (!(m = dbus_message_new_signal("/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "UnitNew"))) goto oom; if (!dbus_message_append_args(m, @@ -403,7 +403,7 @@ void bus_unit_send_removed_signal(Unit *u) { if (!(p = unit_dbus_path(u))) goto oom; - if (!(m = dbus_message_new_signal("/org/freedesktop/systemd1", "org.freedesktop.systemd1", "UnitRemoved"))) + if (!(m = dbus_message_new_signal("/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "UnitRemoved"))) goto oom; if (!dbus_message_append_args(m, diff --git a/device.c b/device.c index 4812a8687..a65d41726 100644 --- a/device.c +++ b/device.c @@ -244,7 +244,7 @@ static int device_process_new_device(Manager *m, struct udev_device *dev, bool u goto fail; } - r = unit_add_dependency_by_name(u, UNIT_WANTS, NULL, e); + r = unit_add_dependency_by_name(u, UNIT_WANTS, NULL, e, true); free(e); if (r < 0) diff --git a/initctl.c b/initctl.c index 96a4d9900..715600886 100644 --- a/initctl.c +++ b/initctl.c @@ -115,7 +115,7 @@ static void change_runlevel(Server *s, int runlevel) { log_debug("Running request %s", target); - if (!(m = dbus_message_new_method_call("org.freedesktop.systemd1", "/org/freedesktop/systemd1", "org.freedesktop.systemd1", "GetUnit"))) { + if (!(m = dbus_message_new_method_call("org.freedesktop.systemd1", "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "GetUnit"))) { log_error("Could not allocate message."); goto finish; } diff --git a/job.c b/job.c index c1b00714e..3210bb42c 100644 --- a/job.c +++ b/job.c @@ -58,8 +58,10 @@ void job_free(Job *j) { if (j->installed) { bus_job_send_removed_signal(j); - if (j->unit->meta.job == j) + if (j->unit->meta.job == j) { j->unit->meta.job = NULL; + unit_add_to_gc_queue(j->unit); + } hashmap_remove(j->manager->jobs, UINT32_TO_PTR(j->id)); j->installed = false; diff --git a/load-dropin.c b/load-dropin.c index 89e142f9a..2101e3300 100644 --- a/load-dropin.c +++ b/load-dropin.c @@ -52,7 +52,7 @@ static int iterate_dir(Unit *u, const char *path) { goto finish; } - r = unit_add_dependency_by_name(u, UNIT_WANTS, de->d_name, f); + r = unit_add_dependency_by_name(u, UNIT_WANTS, de->d_name, f, true); free(f); if (r < 0) diff --git a/load-fragment.c b/load-fragment.c index f79af635b..03205f14b 100644 --- a/load-fragment.c +++ b/load-fragment.c @@ -97,7 +97,7 @@ static int config_parse_deps( if (!k) return -ENOMEM; - r = unit_add_dependency_by_name(u, d, k, NULL); + r = unit_add_dependency_by_name(u, d, k, NULL, true); free(k); if (r < 0) diff --git a/manager.c b/manager.c index 9458aa51b..f10345b43 100644 --- a/manager.c +++ b/manager.c @@ -52,6 +52,12 @@ #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; @@ -389,6 +395,77 @@ static unsigned manager_dispatch_cleanup_queue(Manager *m) { return n; } +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; + m->gc_marker = MIN(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; @@ -1770,6 +1847,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; diff --git a/manager.h b/manager.h index 356e168bf..bd0c82ee7 100644 --- a/manager.h +++ b/manager.h @@ -145,8 +145,12 @@ struct Manager { LIST_HEAD(Meta, dbus_unit_queue); LIST_HEAD(Job, dbus_job_queue); + /* Units to remove */ LIST_HEAD(Meta, cleanup_queue); + /* Units to check when doing GC */ + LIST_HEAD(Meta, gc_queue); + /* Jobs to be added */ Hashmap *transaction_jobs; /* Unit object => Job object list 1:1 */ JobDependency *transaction_anchor; @@ -193,6 +197,11 @@ struct Manager { char *cgroup_controller; char *cgroup_hierarchy; + usec_t gc_queue_timestamp; + + int gc_marker; + unsigned n_in_gc_queue; + /* Flags */ ManagerRunningAs running_as; ManagerExitCode exit_code:4; diff --git a/mount.c b/mount.c index 187c6a373..15cce6f8e 100644 --- a/mount.c +++ b/mount.c @@ -144,15 +144,15 @@ static int mount_add_node_links(Mount *m) { if (r < 0) return r; - if ((r = unit_add_dependency(UNIT(m), UNIT_AFTER, device)) < 0) + if ((r = unit_add_dependency(UNIT(m), UNIT_AFTER, device, true)) < 0) return r; - if ((r = unit_add_dependency(UNIT(m), UNIT_REQUIRES, device)) < 0) + if ((r = unit_add_dependency(UNIT(m), UNIT_REQUIRES, device, true)) < 0) return r; if (UNIT(m)->meta.manager->running_as == MANAGER_INIT || UNIT(m)->meta.manager->running_as == MANAGER_SYSTEM) - if ((r = unit_add_dependency(device, UNIT_WANTS, UNIT(m))) < 0) + if ((r = unit_add_dependency(device, UNIT_WANTS, UNIT(m), false)) < 0) return r; return 0; @@ -180,20 +180,20 @@ static int mount_add_path_links(Mount *m) { if (path_startswith(m->where, n->where)) { - if ((r = unit_add_dependency(UNIT(m), UNIT_AFTER, UNIT(other))) < 0) + if ((r = unit_add_dependency(UNIT(m), UNIT_AFTER, UNIT(other), true)) < 0) return r; if (n->from_etc_fstab || n->from_fragment) - if ((r = unit_add_dependency(UNIT(m), UNIT_REQUIRES, UNIT(other))) < 0) + if ((r = unit_add_dependency(UNIT(m), UNIT_REQUIRES, UNIT(other), true)) < 0) return r; } else if (path_startswith(n->where, m->where)) { - if ((r = unit_add_dependency(UNIT(m), UNIT_BEFORE, UNIT(other))) < 0) + if ((r = unit_add_dependency(UNIT(m), UNIT_BEFORE, UNIT(other), true)) < 0) return r; if (m->from_etc_fstab || m->from_fragment) - if ((r = unit_add_dependency(UNIT(other), UNIT_REQUIRES, UNIT(m))) < 0) + if ((r = unit_add_dependency(UNIT(other), UNIT_REQUIRES, UNIT(m), true)) < 0) return r; } } @@ -256,18 +256,18 @@ static int mount_add_target_links(Mount *m) { if ((r = unit_load_related_unit(UNIT(m), ".automount", &am)) < 0) return r; - if ((r = unit_add_dependency(tu, UNIT_WANTS, UNIT(am))) < 0) + if ((r = unit_add_dependency(tu, UNIT_WANTS, UNIT(am), true)) < 0) return r; - return unit_add_dependency(UNIT(am), UNIT_BEFORE, tu); + return unit_add_dependency(UNIT(am), UNIT_BEFORE, tu, true); } else { if (handle) - if ((r = unit_add_dependency(tu, UNIT_WANTS, UNIT(m))) < 0) + if ((r = unit_add_dependency(tu, UNIT_WANTS, UNIT(m), true)) < 0) return r; - return unit_add_dependency(UNIT(m), UNIT_BEFORE, tu); + return unit_add_dependency(UNIT(m), UNIT_BEFORE, tu, true); } } @@ -870,6 +870,14 @@ static const char *mount_sub_state_to_string(Unit *u) { return mount_state_to_string(MOUNT(u)->state); } +static bool mount_check_gc(Unit *u) { + Mount *m = MOUNT(u); + + assert(m); + + return m->from_etc_fstab || m->from_proc_self_mountinfo; +} + static void mount_sigchld_event(Unit *u, pid_t pid, int code, int status) { Mount *m = MOUNT(u); bool success; @@ -1473,6 +1481,8 @@ const UnitVTable mount_vtable = { .active_state = mount_active_state, .sub_state_to_string = mount_sub_state_to_string, + .check_gc = mount_check_gc, + .sigchld_event = mount_sigchld_event, .timer_event = mount_timer_event, diff --git a/org.freedesktop.systemd1.conf b/org.freedesktop.systemd1.conf index 4ebc8ab87..dfdc475de 100644 --- a/org.freedesktop.systemd1.conf +++ b/org.freedesktop.systemd1.conf @@ -45,31 +45,31 @@ send_member="GetAll"/> diff --git a/service.c b/service.c index 25641768a..4008ac20f 100644 --- a/service.c +++ b/service.c @@ -225,7 +225,7 @@ static int sysv_chkconfig_order(Service *s) { /* FIXME: Maybe we should compare the name here lexicographically? */ - if (!(r = unit_add_dependency(UNIT(s), d, UNIT(t))) < 0) + if (!(r = unit_add_dependency(UNIT(s), d, UNIT(t), true)) < 0) return r; } @@ -524,8 +524,8 @@ static int service_load_sysv_path(Service *s, const char *path) { if (unit_name_to_type(m) == UNIT_SERVICE) r = unit_add_name(u, m); else { - if ((r = unit_add_dependency_by_name_inverse(u, UNIT_REQUIRES, m, NULL)) >= 0) - r = unit_add_dependency_by_name(u, UNIT_BEFORE, m, NULL); + if ((r = unit_add_dependency_by_name_inverse(u, UNIT_REQUIRES, m, NULL, true)) >= 0) + r = unit_add_dependency_by_name(u, UNIT_BEFORE, m, NULL, true); } free(m); @@ -558,7 +558,7 @@ static int service_load_sysv_path(Service *s, const char *path) { if (r == 0) continue; - r = unit_add_dependency_by_name(u, UNIT_AFTER, m, NULL); + r = unit_add_dependency_by_name(u, UNIT_AFTER, m, NULL, true); free(m); if (r < 0) @@ -654,8 +654,8 @@ static int service_load_sysv_path(Service *s, const char *path) { * needed for early boot) and don't create any links * to it. */ - if ((r = unit_add_dependency_by_name(u, UNIT_REQUIRES, SPECIAL_BASIC_TARGET, NULL)) < 0 || - (r = unit_add_dependency_by_name(u, UNIT_AFTER, SPECIAL_BASIC_TARGET, NULL)) < 0) + if ((r = unit_add_dependency_by_name(u, UNIT_REQUIRES, SPECIAL_BASIC_TARGET, NULL, true)) < 0 || + (r = unit_add_dependency_by_name(u, UNIT_AFTER, SPECIAL_BASIC_TARGET, NULL, true)) < 0) goto finish; } @@ -1890,6 +1890,22 @@ static const char *service_sub_state_to_string(Unit *u) { return service_state_to_string(SERVICE(u)->state); } +static bool service_check_gc(Unit *u) { + Service *s = SERVICE(u); + + assert(s); + + return !!s->sysv_path; +} + +static bool service_check_snapshot(Unit *u) { + Service *s = SERVICE(u); + + assert(s); + + return !s->got_socket_fd; +} + static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { Service *s = SERVICE(u); bool success; @@ -2221,10 +2237,10 @@ static int service_enumerate(Manager *m) { goto finish; if (de->d_name[0] == 'S') { - if ((r = unit_add_dependency(runlevel, UNIT_WANTS, service)) < 0) + if ((r = unit_add_dependency(runlevel, UNIT_WANTS, service, true)) < 0) goto finish; - if ((r = unit_add_dependency(runlevel, UNIT_AFTER, service)) < 0) + if ((r = unit_add_dependency(runlevel, UNIT_AFTER, service, true)) < 0) goto finish; } else if (de->d_name[0] == 'K' && @@ -2238,10 +2254,10 @@ static int service_enumerate(Manager *m) { * implicitly added by the * core logic. */ - if ((r = unit_add_dependency(runlevel, UNIT_CONFLICTS, service)) < 0) + if ((r = unit_add_dependency(runlevel, UNIT_CONFLICTS, service, true)) < 0) goto finish; - if ((r = unit_add_dependency(runlevel, UNIT_BEFORE, service)) < 0) + if ((r = unit_add_dependency(runlevel, UNIT_BEFORE, service, true)) < 0) goto finish; } } @@ -2342,6 +2358,7 @@ int service_set_socket_fd(Service *s, int fd) { return -EAGAIN; s->socket_fd = fd; + s->got_socket_fd = true; return 0; } @@ -2416,6 +2433,9 @@ const UnitVTable service_vtable = { .active_state = service_active_state, .sub_state_to_string = service_sub_state_to_string, + .check_gc = service_check_gc, + .check_snapshot = service_check_snapshot, + .sigchld_event = service_sigchld_event, .timer_event = service_timer_event, diff --git a/service.h b/service.h index dc597aa0e..6700229b5 100644 --- a/service.h +++ b/service.h @@ -110,6 +110,8 @@ struct Service { bool bus_name_good:1; + bool got_socket_fd:1; + bool sysv_has_lsb:1; char *sysv_path; int sysv_start_priority; diff --git a/snapshot.c b/snapshot.c index d0aaa51fe..397d09be1 100644 --- a/snapshot.c +++ b/snapshot.c @@ -139,10 +139,10 @@ static int snapshot_deserialize_item(Unit *u, const char *key, const char *value } else if (streq(key, "requires")) { - if ((r = unit_add_dependency_by_name(u, UNIT_AFTER, value, NULL)) < 0) + if ((r = unit_add_dependency_by_name(u, UNIT_AFTER, value, NULL, true)) < 0) return r; - if ((r = unit_add_dependency_by_name(u, UNIT_REQUIRES, value, NULL)) < 0) + if ((r = unit_add_dependency_by_name(u, UNIT_REQUIRES, value, NULL, true)) < 0) return r; } else log_debug("Unknown serialization key '%s'", key); @@ -211,13 +211,17 @@ int snapshot_create(Manager *m, const char *name, bool cleanup, Snapshot **_s) { if (k != other->meta.id) continue; + if (UNIT_VTABLE(other)->check_snapshot) + if (!UNIT_VTABLE(other)->check_snapshot(other)) + continue; + if (!UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other))) continue; - if ((r = unit_add_dependency(u, UNIT_REQUIRES, other)) < 0) + if ((r = unit_add_dependency(u, UNIT_REQUIRES, other, true)) < 0) goto fail; - if ((r = unit_add_dependency(u, UNIT_AFTER, other)) < 0) + if ((r = unit_add_dependency(u, UNIT_AFTER, other, true)) < 0) goto fail; } @@ -252,6 +256,7 @@ const UnitVTable snapshot_vtable = { .no_alias = true, .no_instances = true, .no_snapshots = true, + .no_gc = true, .load = unit_load_nop, .coldplug = snapshot_coldplug, diff --git a/socket.c b/socket.c index 63346a2bf..3e7b0f8ba 100644 --- a/socket.c +++ b/socket.c @@ -157,7 +157,7 @@ static int socket_load(Unit *u) { if ((r = unit_load_related_unit(u, ".service", (Unit**) &s->service))) return r; - if ((r = unit_add_dependency(u, UNIT_BEFORE, UNIT(s->service))) < 0) + if ((r = unit_add_dependency(u, UNIT_BEFORE, UNIT(s->service), true)) < 0) return r; } diff --git a/systemadm.vala b/systemadm.vala index c761511c1..b1ac12a85 100644 --- a/systemadm.vala +++ b/systemadm.vala @@ -216,7 +216,7 @@ public class MainWindow : Window { manager = bus.get_object( "org.freedesktop.systemd1", "/org/freedesktop/systemd1", - "org.freedesktop.systemd1") as Manager; + "org.freedesktop.systemd1.Manager") as Manager; manager.unit_new += on_unit_new; manager.job_new += on_job_new; diff --git a/systemctl.vala b/systemctl.vala index c435e558e..a0237bb73 100644 --- a/systemctl.vala +++ b/systemctl.vala @@ -101,7 +101,7 @@ int main (string[] args) { Manager manager = bus.get_object ( "org.freedesktop.systemd1", "/org/freedesktop/systemd1", - "org.freedesktop.systemd1") as Manager; + "org.freedesktop.systemd1.Manager") as Manager; if (args[1] == "list-units" || args.length <= 1) { var list = manager.list_units(); diff --git a/unit.c b/unit.c index 0d459d676..5c19b3b2d 100644 --- a/unit.c +++ b/unit.c @@ -204,6 +204,25 @@ int unit_set_description(Unit *u, const char *description) { return 0; } +bool unit_check_gc(Unit *u) { + assert(u); + + if (UNIT_VTABLE(u)->no_gc) + return true; + + if (u->meta.job) + return true; + + if (unit_active_state(u) != UNIT_INACTIVE) + return true; + + if (UNIT_VTABLE(u)->check_gc) + if (UNIT_VTABLE(u)->check_gc(u)) + return true; + + return false; +} + void unit_add_to_load_queue(Unit *u) { assert(u); assert(u->meta.type != _UNIT_TYPE_INVALID); @@ -225,6 +244,24 @@ void unit_add_to_cleanup_queue(Unit *u) { u->meta.in_cleanup_queue = true; } +void unit_add_to_gc_queue(Unit *u) { + assert(u); + + if (u->meta.in_gc_queue || u->meta.in_cleanup_queue) + return; + + if (unit_check_gc(u)) + return; + + LIST_PREPEND(Meta, gc_queue, u->meta.manager->gc_queue, &u->meta); + u->meta.in_gc_queue = true; + + u->meta.manager->n_in_gc_queue ++; + + if (u->meta.manager->gc_queue_timestamp <= 0) + u->meta.manager->gc_queue_timestamp = now(CLOCK_MONOTONIC); +} + void unit_add_to_dbus_queue(Unit *u) { assert(u); assert(u->meta.type != _UNIT_TYPE_INVALID); @@ -250,6 +287,8 @@ static void bidi_set_free(Unit *u, Set *s) { for (d = 0; d < _UNIT_DEPENDENCY_MAX; d++) set_remove(other->meta.dependencies[d], u); + + unit_add_to_gc_queue(other); } set_free(s); @@ -280,6 +319,11 @@ void unit_free(Unit *u) { if (u->meta.in_cleanup_queue) LIST_REMOVE(Meta, cleanup_queue, u->meta.manager->cleanup_queue, &u->meta); + if (u->meta.in_gc_queue) { + LIST_REMOVE(Meta, gc_queue, u->meta.manager->gc_queue, &u->meta); + u->meta.manager->n_in_gc_queue--; + } + /* Free data and next 'smaller' objects */ if (u->meta.job) job_free(u->meta.job); @@ -482,11 +526,11 @@ int unit_add_exec_dependencies(Unit *u, ExecContext *c) { /* If syslog or kernel logging is requested, make sure our own * logging daemon is run first. */ - if ((r = unit_add_dependency_by_name(u, UNIT_AFTER, SPECIAL_LOGGER_SOCKET, NULL)) < 0) + if ((r = unit_add_dependency_by_name(u, UNIT_AFTER, SPECIAL_LOGGER_SOCKET, NULL, true)) < 0) return r; if (u->meta.manager->running_as != MANAGER_SESSION) - if ((r = unit_add_dependency_by_name(u, UNIT_REQUIRES, SPECIAL_LOGGER_SOCKET, NULL)) < 0) + if ((r = unit_add_dependency_by_name(u, UNIT_REQUIRES, SPECIAL_LOGGER_SOCKET, NULL, true)) < 0) return r; return 0; @@ -525,14 +569,16 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) { "%s\tUnit Load State: %s\n" "%s\tUnit Active State: %s\n" "%s\tActive Enter Timestamp: %s\n" - "%s\tActive Exit Timestamp: %s\n", + "%s\tActive Exit Timestamp: %s\n" + "%s\tGC Check Good: %s\n", prefix, u->meta.id, prefix, unit_description(u), prefix, strna(u->meta.instance), prefix, unit_load_state_to_string(u->meta.load_state), prefix, unit_active_state_to_string(unit_active_state(u)), prefix, strna(format_timestamp(timestamp1, sizeof(timestamp1), u->meta.active_enter_timestamp)), - prefix, strna(format_timestamp(timestamp2, sizeof(timestamp2), u->meta.active_exit_timestamp))); + prefix, strna(format_timestamp(timestamp2, sizeof(timestamp2), u->meta.active_exit_timestamp)), + prefix, yes_no(unit_check_gc(u))); SET_FOREACH(t, u->meta.names, i) fprintf(f, "%s\tName: %s\n", prefix, t); @@ -653,6 +699,7 @@ int unit_load(Unit *u) { assert((u->meta.load_state != UNIT_MERGED) == !u->meta.merged_into); unit_add_to_dbus_queue(unit_follow_merge(u)); + unit_add_to_gc_queue(u); return 0; @@ -987,6 +1034,7 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns) { unit_check_uneeded(u); unit_add_to_dbus_queue(u); + unit_add_to_gc_queue(u); } int unit_watch_fd(Unit *u, int fd, uint32_t events, Watch *w) { @@ -1152,7 +1200,7 @@ bool unit_job_is_applicable(Unit *u, JobType j) { } } -int unit_add_dependency(Unit *u, UnitDependency d, Unit *other) { +int unit_add_dependency(Unit *u, UnitDependency d, Unit *other, bool add_reference) { static const UnitDependency inverse_table[_UNIT_DEPENDENCY_MAX] = { [UNIT_REQUIRES] = UNIT_REQUIRED_BY, @@ -1165,9 +1213,11 @@ int unit_add_dependency(Unit *u, UnitDependency d, Unit *other) { [UNIT_WANTED_BY] = _UNIT_DEPENDENCY_INVALID, [UNIT_CONFLICTS] = UNIT_CONFLICTS, [UNIT_BEFORE] = UNIT_AFTER, - [UNIT_AFTER] = UNIT_BEFORE + [UNIT_AFTER] = UNIT_BEFORE, + [UNIT_REFERENCES] = UNIT_REFERENCED_BY, + [UNIT_REFERENCED_BY] = UNIT_REFERENCES }; - int r; + int r, q = 0, v = 0, w = 0; assert(u); assert(d >= 0 && d < _UNIT_DEPENDENCY_MAX); @@ -1187,22 +1237,47 @@ int unit_add_dependency(Unit *u, UnitDependency d, Unit *other) { return -EINVAL; } - if ((r = set_ensure_allocated(&u->meta.dependencies[d], trivial_hash_func, trivial_compare_func)) < 0) + if ((r = set_ensure_allocated(&u->meta.dependencies[d], trivial_hash_func, trivial_compare_func)) < 0 || + (r = set_ensure_allocated(&other->meta.dependencies[inverse_table[d]], trivial_hash_func, trivial_compare_func)) < 0) return r; - if ((r = set_ensure_allocated(&other->meta.dependencies[inverse_table[d]], trivial_hash_func, trivial_compare_func)) < 0) - return r; + if (add_reference) + if ((r = set_ensure_allocated(&u->meta.dependencies[UNIT_REFERENCES], trivial_hash_func, trivial_compare_func)) < 0 || + (r = set_ensure_allocated(&other->meta.dependencies[UNIT_REFERENCED_BY], trivial_hash_func, trivial_compare_func)) < 0) + return r; - if ((r = set_put(u->meta.dependencies[d], other)) < 0) - return r; + if ((q = set_put(u->meta.dependencies[d], other)) < 0) + return q; - if ((r = set_put(other->meta.dependencies[inverse_table[d]], u)) < 0) { - set_remove(u->meta.dependencies[d], other); - return r; + if ((v = set_put(other->meta.dependencies[inverse_table[d]], u)) < 0) { + r = v; + goto fail; + } + + if (add_reference) { + if ((w = set_put(u->meta.dependencies[UNIT_REFERENCES], other)) < 0) { + r = w; + goto fail; + } + + if ((r = set_put(other->meta.dependencies[UNIT_REFERENCED_BY], u)) < 0) + goto fail; } unit_add_to_dbus_queue(u); return 0; + +fail: + if (q > 0) + set_remove(u->meta.dependencies[d], other); + + if (v > 0) + set_remove(other->meta.dependencies[inverse_table[d]], u); + + if (w > 0) + set_remove(u->meta.dependencies[UNIT_REFERENCES], other); + + return r; } static const char *resolve_template(Unit *u, const char *name, const char*path, char **p) { @@ -1238,7 +1313,7 @@ static const char *resolve_template(Unit *u, const char *name, const char*path, return s; } -int unit_add_dependency_by_name(Unit *u, UnitDependency d, const char *name, const char *path) { +int unit_add_dependency_by_name(Unit *u, UnitDependency d, const char *name, const char *path, bool add_reference) { Unit *other; int r; char *s; @@ -1252,14 +1327,14 @@ int unit_add_dependency_by_name(Unit *u, UnitDependency d, const char *name, con if ((r = manager_load_unit(u->meta.manager, name, path, &other)) < 0) goto finish; - r = unit_add_dependency(u, d, other); + r = unit_add_dependency(u, d, other, add_reference); finish: free(s); return r; } -int unit_add_dependency_by_name_inverse(Unit *u, UnitDependency d, const char *name, const char *path) { +int unit_add_dependency_by_name_inverse(Unit *u, UnitDependency d, const char *name, const char *path, bool add_reference) { Unit *other; int r; char *s; @@ -1273,7 +1348,7 @@ int unit_add_dependency_by_name_inverse(Unit *u, UnitDependency d, const char *n if ((r = manager_load_unit(u->meta.manager, name, path, &other)) < 0) goto finish; - r = unit_add_dependency(other, d, u); + r = unit_add_dependency(other, d, u, add_reference); finish: free(s); @@ -1794,6 +1869,8 @@ static const char* const unit_dependency_table[_UNIT_DEPENDENCY_MAX] = { [UNIT_CONFLICTS] = "Conflicts", [UNIT_BEFORE] = "Before", [UNIT_AFTER] = "After", + [UNIT_REFERENCES] = "References", + [UNIT_REFERENCED_BY] = "ReferencedBy" }; DEFINE_STRING_TABLE_LOOKUP(unit_dependency, UnitDependency); diff --git a/unit.h b/unit.h index d3d4b0e9f..9155b2eda 100644 --- a/unit.h +++ b/unit.h @@ -113,9 +113,13 @@ enum UnitDependency { UNIT_CONFLICTS, /* inverse of 'conflicts' is 'conflicts' */ /* Order */ - UNIT_BEFORE, /* inverse of before is after and vice versa */ + UNIT_BEFORE, /* inverse of 'before' is 'after' and vice versa */ UNIT_AFTER, + /* Reference information for GC logic */ + UNIT_REFERENCES, /* Inverse of 'references' is 'referenced_by' */ + UNIT_REFERENCED_BY, + _UNIT_DEPENDENCY_MAX, _UNIT_DEPENDENCY_INVALID = -1 }; @@ -150,18 +154,24 @@ struct Meta { /* Counterparts in the cgroup filesystem */ CGroupBonding *cgroup_bondings; - /* Load queue */ - LIST_FIELDS(Meta, load_queue); - /* Per type list */ LIST_FIELDS(Meta, units_per_type); + /* Load queue */ + LIST_FIELDS(Meta, load_queue); + /* D-Bus queue */ LIST_FIELDS(Meta, dbus_queue); /* Cleanup queue */ LIST_FIELDS(Meta, cleanup_queue); + /* GC queue */ + LIST_FIELDS(Meta, gc_queue); + + /* Used during GC sweeps */ + int gc_marker; + /* If we go down, pull down everything that depends on us, too */ bool recursive_stop; @@ -171,6 +181,8 @@ struct Meta { bool in_load_queue:1; bool in_dbus_queue:1; bool in_cleanup_queue:1; + bool in_gc_queue:1; + bool sent_dbus_new_signal:1; }; @@ -242,6 +254,14 @@ struct UnitVTable { * unit is in. */ const char* (*sub_state_to_string)(Unit *u); + /* Return true when there is reason to keep this entry around + * even nothing references it and it isn't active in any + * way */ + bool (*check_gc)(Unit *u); + + /* Return true when this unit is suitable for snapshotting */ + bool (*check_snapshot)(Unit *u); + void (*fd_event)(Unit *u, int fd, uint32_t events, Watch *w); void (*sigchld_event)(Unit *u, pid_t pid, int code, int status); void (*timer_event)(Unit *u, uint64_t n_elapsed, Watch *w); @@ -283,6 +303,9 @@ struct UnitVTable { /* Exclude this type from snapshots */ bool no_snapshots:1; + + /* Exclude from automatic gc */ + bool no_gc:1; }; extern const UnitVTable * const unit_vtable[_UNIT_TYPE_MAX]; @@ -315,9 +338,9 @@ void unit_free(Unit *u); int unit_add_name(Unit *u, const char *name); -int unit_add_dependency(Unit *u, UnitDependency d, Unit *other); -int unit_add_dependency_by_name(Unit *u, UnitDependency d, const char *name, const char *filename); -int unit_add_dependency_by_name_inverse(Unit *u, UnitDependency d, const char *name, const char *filename); +int unit_add_dependency(Unit *u, UnitDependency d, Unit *other, bool add_reference); +int unit_add_dependency_by_name(Unit *u, UnitDependency d, const char *name, const char *filename, bool add_reference); +int unit_add_dependency_by_name_inverse(Unit *u, UnitDependency d, const char *name, const char *filename, bool add_reference); int unit_add_exec_dependencies(Unit *u, ExecContext *c); @@ -329,9 +352,12 @@ CGroupBonding* unit_get_default_cgroup(Unit *u); int unit_choose_id(Unit *u, const char *name); int unit_set_description(Unit *u, const char *description); +bool unit_check_gc(Unit *u); + void unit_add_to_load_queue(Unit *u); void unit_add_to_dbus_queue(Unit *u); void unit_add_to_cleanup_queue(Unit *u); +void unit_add_to_gc_queue(Unit *u); int unit_merge(Unit *u, Unit *other); int unit_merge_by_name(Unit *u, const char *other); -- 2.30.2