chiark / gitweb /
manager: automatically GC unreferenced units
authorLennart Poettering <lennart@poettering.net>
Wed, 21 Apr 2010 04:01:13 +0000 (06:01 +0200)
committerLennart Poettering <lennart@poettering.net>
Wed, 21 Apr 2010 04:01:13 +0000 (06:01 +0200)
21 files changed:
automount.c
dbus-job.c
dbus-manager.c
dbus-unit.c
device.c
initctl.c
job.c
load-dropin.c
load-fragment.c
manager.c
manager.h
mount.c
org.freedesktop.systemd1.conf
service.c
service.h
snapshot.c
socket.c
systemadm.vala
systemctl.vala
unit.c
unit.h

index 7aa55b4..b124fc5 100644 (file)
@@ -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,
index bec5bca..f14f92f 100644 (file)
@@ -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,
index a8bf7e5..9263248 100644 (file)
@@ -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;
 
index cbc2812..ccaaef9 100644 (file)
@@ -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,
index 4812a86..a65d417 100644 (file)
--- 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)
index 96a4d99..7156008 100644 (file)
--- 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 c1b0071..3210bb4 100644 (file)
--- 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;
index 89e142f..2101e33 100644 (file)
@@ -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)
index f79af63..03205f1 100644 (file)
@@ -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)
index 9458aa5..f10345b 100644 (file)
--- a/manager.c
+++ b/manager.c
 #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;
 
index 356e168..bd0c82e 100644 (file)
--- 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 187c6a3..15cce6f 100644 (file)
--- 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,
 
index 4ebc8ab..dfdc475 100644 (file)
                        send_member="GetAll"/>
 
                 <allow send_destination="org.freedesktop.systemd1"
-                       send_interface="org.freedesktop.systemd1"
+                       send_interface="org.freedesktop.systemd1.Manager"
                        send_member="GetUnit"/>
 
                 <allow send_destination="org.freedesktop.systemd1"
-                       send_interface="org.freedesktop.systemd1"
+                       send_interface="org.freedesktop.systemd1.Manager"
                        send_member="GetJob"/>
 
                 <allow send_destination="org.freedesktop.systemd1"
-                       send_interface="org.freedesktop.systemd1"
+                       send_interface="org.freedesktop.systemd1.Manager"
                        send_member="ListUnits"/>
 
                 <allow send_destination="org.freedesktop.systemd1"
-                       send_interface="org.freedesktop.systemd1"
+                       send_interface="org.freedesktop.systemd1.Manager"
                        send_member="ListJobs"/>
 
                 <allow send_destination="org.freedesktop.systemd1"
-                       send_interface="org.freedesktop.systemd1"
+                       send_interface="org.freedesktop.systemd1.Manager"
                        send_member="Subscribe"/>
 
                 <allow send_destination="org.freedesktop.systemd1"
-                       send_interface="org.freedesktop.systemd1"
+                       send_interface="org.freedesktop.systemd1.Manager"
                        send_member="Unsubscribe"/>
 
                 <allow send_destination="org.freedesktop.systemd1"
-                       send_interface="org.freedesktop.systemd1"
+                       send_interface="org.freedesktop.systemd1.Manager"
                        send_member="Dump"/>
 
                 <allow receive_sender="org.freedesktop.systemd1"/>
index 2564176..4008ac2 100644 (file)
--- 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,
 
index dc597aa..6700229 100644 (file)
--- 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;
index d0aaa51..397d09b 100644 (file)
@@ -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,
index 63346a2..3e7b0f8 100644 (file)
--- 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;
                 }
 
index c761511..b1ac12a 100644 (file)
@@ -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;
index c435e55..a0237bb 100644 (file)
@@ -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 0d459d6..5c19b3b 100644 (file)
--- 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 d3d4b0e..9155b2e 100644 (file)
--- 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);