summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2010-04-21 06:01:13 +0200
committerLennart Poettering <lennart@poettering.net>2010-04-21 06:01:13 +0200
commit701cc384c283206a29b21e4e7302e5cf5f2d9433 (patch)
tree0e8e47d12cb3ea47794bad390658c900ed61d691
parent48507e6621596b0d5503fad6cd9e0685917603f5 (diff)
manager: automatically GC unreferenced units
-rw-r--r--automount.c15
-rw-r--r--dbus-job.c4
-rw-r--r--dbus-manager.c2
-rw-r--r--dbus-unit.c4
-rw-r--r--device.c2
-rw-r--r--initctl.c2
-rw-r--r--job.c4
-rw-r--r--load-dropin.c2
-rw-r--r--load-fragment.c2
-rw-r--r--manager.c80
-rw-r--r--manager.h9
-rw-r--r--mount.c32
-rw-r--r--org.freedesktop.systemd1.conf14
-rw-r--r--service.c40
-rw-r--r--service.h2
-rw-r--r--snapshot.c13
-rw-r--r--socket.c2
-rw-r--r--systemadm.vala2
-rw-r--r--systemctl.vala2
-rw-r--r--unit.c115
-rw-r--r--unit.h40
21 files changed, 314 insertions, 74 deletions
diff --git a/automount.c b/automount.c
index 7aa55b4f9f..b124fc5b0e 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 bec5bca93c..f14f92f9aa 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 a8bf7e52c9..9263248111 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 cbc2812e31..ccaaef99a2 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 4812a86878..a65d41726b 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 96a4d99003..7156008864 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 c1b00714ec..3210bb42c2 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 89e142f9a8..2101e33004 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 f79af635b8..03205f14b4 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 9458aa51ba..f10345b43a 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 356e168bf6..bd0c82ee77 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 187c6a373a..15cce6f8e4 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 4ebc8ab87c..dfdc475de4 100644
--- a/org.freedesktop.systemd1.conf
+++ b/org.freedesktop.systemd1.conf
@@ -45,31 +45,31 @@
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"/>
diff --git a/service.c b/service.c
index 25641768a9..4008ac20ff 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 dc597aa0e9..6700229b5c 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 d0aaa51fee..397d09be19 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 63346a2bfc..3e7b0f8ba5 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 c761511c1c..b1ac12a85f 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 c435e558e8..a0237bb734 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 0d459d6760..5c19b3b2da 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 d3d4b0e9f6..9155b2eda1 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);