]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
core: add Ref()/Unref() bus calls for units
authorLennart Poettering <lennart@poettering.net>
Mon, 15 Aug 2016 16:12:01 +0000 (18:12 +0200)
committerLennart Poettering <lennart@poettering.net>
Mon, 22 Aug 2016 14:14:21 +0000 (16:14 +0200)
This adds two (privileged) bus calls Ref() and Unref() to the Unit interface.
The two calls may be used by clients to pin a unit into memory, so that various
runtime properties aren't flushed out by the automatic GC. This is necessary
to permit clients to race-freely acquire runtime results (such as process exit
status/code or accumulated CPU time) on successful service termination.

Ref() and Unref() are fully recursive, hence act like the usual reference
counting concept in C. Taking a reference is a privileged operation, as this
allows pinning units into memory which consumes resources.

Transient units may also gain a reference at the time of creation, via the new
AddRef property (that is only defined for transient units at the time of
creation).

14 files changed:
man/systemctl.xml
src/core/dbus-manager.c
src/core/dbus-unit.c
src/core/dbus-unit.h
src/core/dbus.c
src/core/dbus.h
src/core/job.c
src/core/job.h
src/core/manager.c
src/core/org.freedesktop.systemd1.conf
src/core/unit.c
src/core/unit.h
src/libsystemd/sd-bus/bus-common-errors.c
src/libsystemd/sd-bus/bus-common-errors.h

index fde4f4f3bbc8e7dbe89b9c168771cb8c244c044a..7e0ac9613aa344e2a995e10b81aa87ccea3b5e4d 100644 (file)
           <term><command>list-units <optional><replaceable>PATTERN</replaceable>...</optional></command></term>
 
           <listitem>
-            <para>List units that <command>systemd</command> has loaded. This includes units that
-            are either referenced directly or through a dependency, or units that were active in the
-            past and have failed. By default only units which are active, have pending jobs, or have
+            <para>List units that <command>systemd</command> has loaded. This includes units that are either referenced
+            directly or through a dependency, units that are pinned by applications programmatically, or units that
+            were active in the past and have failed. By default only units which are active, have pending jobs, or have
             failed are shown; this can be changed with option <option>--all</option>. If one or more
-            <replaceable>PATTERN</replaceable>s are specified, only units matching one of them are
-            shown. The units that are shown are additionally filtered by <option>--type=</option>
-            and <option>--state=</option> if those options are specified.</para>
+            <replaceable>PATTERN</replaceable>s are specified, only units matching one of them are shown. The units
+            that are shown are additionally filtered by <option>--type=</option> and <option>--state=</option> if those
+            options are specified.</para>
 
             <para>This is the default command.</para>
           </listitem>
index ef05a75a8b393c16003c2fc470ddc52953b1e726..ea7ced2fd00ec9998102b63690ec7a817c354376 100644 (file)
@@ -643,6 +643,54 @@ static int method_set_unit_properties(sd_bus_message *message, void *userdata, s
         return bus_unit_method_set_properties(message, u, error);
 }
 
+static int method_ref_unit(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+        Manager *m = userdata;
+        const char *name;
+        Unit *u;
+        int r;
+
+        assert(message);
+        assert(m);
+
+        r = sd_bus_message_read(message, "s", &name);
+        if (r < 0)
+                return r;
+
+        r = manager_load_unit(m, name, NULL, error, &u);
+        if (r < 0)
+                return r;
+
+        r = bus_unit_check_load_state(u, error);
+        if (r < 0)
+                return r;
+
+        return bus_unit_method_ref(message, u, error);
+}
+
+static int method_unref_unit(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+        Manager *m = userdata;
+        const char *name;
+        Unit *u;
+        int r;
+
+        assert(message);
+        assert(m);
+
+        r = sd_bus_message_read(message, "s", &name);
+        if (r < 0)
+                return r;
+
+        r = manager_load_unit(m, name, NULL, error, &u);
+        if (r < 0)
+                return r;
+
+        r = bus_unit_check_load_state(u, error);
+        if (r < 0)
+                return r;
+
+        return bus_unit_method_unref(message, u, error);
+}
+
 static int reply_unit_info(sd_bus_message *reply, Unit *u) {
         _cleanup_free_ char *unit_path = NULL, *job_path = NULL;
         Unit *following;
@@ -781,6 +829,13 @@ static int transient_unit_from_message(
         if (r < 0)
                 return r;
 
+        /* If the client asked for it, automatically add a reference to this unit. */
+        if (u->bus_track_add) {
+                r = bus_unit_track_add_sender(u, message);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to watch sender: %m");
+        }
+
         /* Now load the missing bits of the unit we just created */
         unit_add_to_load_queue(u);
         manager_dispatch_load_queue(m);
@@ -2211,6 +2266,8 @@ const sd_bus_vtable bus_manager_vtable[] = {
         SD_BUS_METHOD("KillUnit", "ssi", NULL, method_kill_unit, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("ResetFailedUnit", "s", NULL, method_reset_failed_unit, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("SetUnitProperties", "sba(sv)", NULL, method_set_unit_properties, SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD("RefUnit", "s", NULL, method_ref_unit, SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD("UnrefUnit", "s", NULL, method_unref_unit, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("StartTransientUnit", "ssa(sv)a(sa(sv))", "o", method_start_transient_unit, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("GetUnitProcesses", "s", "a(sus)", method_get_unit_processes, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("GetJob", "u", "o", method_get_job, SD_BUS_VTABLE_UNPRIVILEGED),
index 89e56a2e51f6949b6061d633da00d6251ba083b3..1b86bdde438f75358b652009311a76a23f51a3ae 100644 (file)
@@ -418,6 +418,7 @@ static int bus_verify_manage_units_async_full(
                 const char *verb,
                 int capability,
                 const char *polkit_message,
+                bool interactive,
                 sd_bus_message *call,
                 sd_bus_error *error) {
 
@@ -433,7 +434,15 @@ static int bus_verify_manage_units_async_full(
                 details[7] = GETTEXT_PACKAGE;
         }
 
-        return bus_verify_polkit_async(call, capability, "org.freedesktop.systemd1.manage-units", details, false, UID_INVALID, &u->manager->polkit_registry, error);
+        return bus_verify_polkit_async(
+                        call,
+                        capability,
+                        "org.freedesktop.systemd1.manage-units",
+                        details,
+                        interactive,
+                        UID_INVALID,
+                        &u->manager->polkit_registry,
+                        error);
 }
 
 int bus_unit_method_start_generic(
@@ -486,6 +495,7 @@ int bus_unit_method_start_generic(
                         verb,
                         CAP_SYS_ADMIN,
                         job_type < _JOB_TYPE_MAX ? polkit_message_for_job[job_type] : NULL,
+                        true,
                         message,
                         error);
         if (r < 0)
@@ -558,6 +568,7 @@ int bus_unit_method_kill(sd_bus_message *message, void *userdata, sd_bus_error *
                         "kill",
                         CAP_KILL,
                         N_("Authentication is required to kill '$(unit)'."),
+                        true,
                         message,
                         error);
         if (r < 0)
@@ -588,6 +599,7 @@ int bus_unit_method_reset_failed(sd_bus_message *message, void *userdata, sd_bus
                         "reset-failed",
                         CAP_SYS_ADMIN,
                         N_("Authentication is required to reset the \"failed\" state of '$(unit)'."),
+                        true,
                         message,
                         error);
         if (r < 0)
@@ -620,6 +632,7 @@ int bus_unit_method_set_properties(sd_bus_message *message, void *userdata, sd_b
                         "set-property",
                         CAP_SYS_ADMIN,
                         N_("Authentication is required to set properties on '$(unit)'."),
+                        true,
                         message,
                         error);
         if (r < 0)
@@ -634,6 +647,53 @@ int bus_unit_method_set_properties(sd_bus_message *message, void *userdata, sd_b
         return sd_bus_reply_method_return(message, NULL);
 }
 
+int bus_unit_method_ref(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+        Unit *u = userdata;
+        int r;
+
+        assert(message);
+        assert(u);
+
+        r = mac_selinux_unit_access_check(u, message, "start", error);
+        if (r < 0)
+                return r;
+
+        r = bus_verify_manage_units_async_full(
+                        u,
+                        "ref",
+                        CAP_SYS_ADMIN,
+                        NULL,
+                        false,
+                        message,
+                        error);
+        if (r < 0)
+                return r;
+        if (r == 0)
+                return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
+
+        r = bus_unit_track_add_sender(u, message);
+        if (r < 0)
+                return r;
+
+        return sd_bus_reply_method_return(message, NULL);
+}
+
+int bus_unit_method_unref(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+        Unit *u = userdata;
+        int r;
+
+        assert(message);
+        assert(u);
+
+        r = bus_unit_track_remove_sender(u, message);
+        if (r == -EUNATCH)
+                return sd_bus_error_setf(error, BUS_ERROR_NOT_REFERENCED, "Unit has not been referenced yet.");
+        if (r < 0)
+                return r;
+
+        return sd_bus_reply_method_return(message, NULL);
+}
+
 const sd_bus_vtable bus_unit_vtable[] = {
         SD_BUS_VTABLE_START(0),
 
@@ -715,6 +775,8 @@ const sd_bus_vtable bus_unit_vtable[] = {
         SD_BUS_METHOD("Kill", "si", NULL, bus_unit_method_kill, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("ResetFailed", NULL, NULL, bus_unit_method_reset_failed, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("SetProperties", "ba(sv)", NULL, bus_unit_method_set_properties, SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD("Ref", NULL, NULL, bus_unit_method_ref, SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD("Unref", NULL, NULL, bus_unit_method_unref, SD_BUS_VTABLE_UNPRIVILEGED),
 
         /* Obsolete properties or obsolete alias names */
         SD_BUS_PROPERTY("RequiresOverridable", "as", property_get_obsolete_dependencies, 0, SD_BUS_VTABLE_HIDDEN),
@@ -1318,6 +1380,29 @@ static int bus_unit_set_transient_property(
                         return r;
 
                 return 1;
+
+        } else if (streq(name, "AddRef")) {
+
+                int b;
+
+                /* Why is this called "AddRef" rather than just "Ref", or "Reference"? There's already a "Ref()" method
+                 * on the Unit interface, and it's probably not a good idea to expose a property and a method on the
+                 * same interface (well, strictly speaking AddRef isn't exposed as full property, we just read it for
+                 * transient units, but still). And "References" and "ReferencedBy" is already used as unit reference
+                 * dependency type, hence let's not confuse things with that.
+                 *
+                 * Note that we don't acually add the reference to the bus track. We do that only after the setup of
+                 * the transient unit is complete, so that setting this property multiple times in the same transient
+                 * unit creation call doesn't count as individual references. */
+
+                r = sd_bus_message_read(message, "b", &b);
+                if (r < 0)
+                        return r;
+
+                if (mode != UNIT_CHECK)
+                        u->bus_track_add = b;
+
+                return 1;
         }
 
         return 0;
@@ -1422,3 +1507,71 @@ int bus_unit_check_load_state(Unit *u, sd_bus_error *error) {
 
         return sd_bus_error_set_errnof(error, u->load_error, "Unit %s is not loaded properly: %m.", u->id);
 }
+
+static int bus_track_handler(sd_bus_track *t, void *userdata) {
+        Unit *u = userdata;
+
+        assert(t);
+        assert(u);
+
+        u->bus_track = sd_bus_track_unref(u->bus_track); /* make sure we aren't called again */
+
+        unit_add_to_gc_queue(u);
+        return 0;
+}
+
+static int allocate_bus_track(Unit *u) {
+        int r;
+
+        assert(u);
+
+        if (u->bus_track)
+                return 0;
+
+        r = sd_bus_track_new(u->manager->api_bus, &u->bus_track, bus_track_handler, u);
+        if (r < 0)
+                return r;
+
+        r = sd_bus_track_set_recursive(u->bus_track, true);
+        if (r < 0) {
+                u->bus_track = sd_bus_track_unref(u->bus_track);
+                return r;
+        }
+
+        return 0;
+}
+
+int bus_unit_track_add_name(Unit *u, const char *name) {
+        int r;
+
+        assert(u);
+
+        r = allocate_bus_track(u);
+        if (r < 0)
+                return r;
+
+        return sd_bus_track_add_name(u->bus_track, name);
+}
+
+int bus_unit_track_add_sender(Unit *u, sd_bus_message *m) {
+        int r;
+
+        assert(u);
+
+        r = allocate_bus_track(u);
+        if (r < 0)
+                return r;
+
+        return sd_bus_track_add_sender(u->bus_track, m);
+}
+
+int bus_unit_track_remove_sender(Unit *u, sd_bus_message *m) {
+        assert(u);
+
+        /* If we haven't allocated the bus track object yet, then there's definitely no reference taken yet, return an
+         * error */
+        if (!u->bus_track)
+                return -EUNATCH;
+
+        return sd_bus_track_remove_sender(u->bus_track, m);
+}
index 4db88dbebc17e402792699b75519bcf335d22e33..b280de7a1d700258ad83dd90c96d9729d3c10832 100644 (file)
@@ -33,9 +33,15 @@ int bus_unit_method_start_generic(sd_bus_message *message, Unit *u, JobType job_
 int bus_unit_method_kill(sd_bus_message *message, void *userdata, sd_bus_error *error);
 int bus_unit_method_reset_failed(sd_bus_message *message, void *userdata, sd_bus_error *error);
 
-int bus_unit_queue_job(sd_bus_message *message, Unit *u, JobType type, JobMode mode, bool reload_if_possible, sd_bus_error *error);
 int bus_unit_set_properties(Unit *u, sd_bus_message *message, UnitSetPropertiesMode mode, bool commit, sd_bus_error *error);
 int bus_unit_method_set_properties(sd_bus_message *message, void *userdata, sd_bus_error *error);
 int bus_unit_method_get_processes(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_unit_method_ref(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_unit_method_unref(sd_bus_message *message, void *userdata, sd_bus_error *error);
 
+int bus_unit_queue_job(sd_bus_message *message, Unit *u, JobType type, JobMode mode, bool reload_if_possible, sd_bus_error *error);
 int bus_unit_check_load_state(Unit *u, sd_bus_error *error);
+
+int bus_unit_track_add_name(Unit *u, const char *name);
+int bus_unit_track_add_sender(Unit *u, sd_bus_message *m);
+int bus_unit_track_remove_sender(Unit *u, sd_bus_message *m);
index 3422a02d680324ea5ed5f33358fed13fc61bc4dd..1e41a42aa64162c0d539346780955c46f4a2bb9e 100644 (file)
@@ -1168,60 +1168,57 @@ int bus_foreach_bus(
         return ret;
 }
 
-void bus_track_serialize(sd_bus_track *t, FILE *f) {
+void bus_track_serialize(sd_bus_track *t, FILE *f, const char *prefix) {
         const char *n;
 
         assert(f);
+        assert(prefix);
 
-        for (n = sd_bus_track_first(t); n; n = sd_bus_track_next(t))
-                fprintf(f, "subscribed=%s\n", n);
-}
-
-int bus_track_deserialize_item(char ***l, const char *line) {
-        const char *e;
-        int r;
-
-        assert(l);
-        assert(line);
-
-        e = startswith(line, "subscribed=");
-        if (!e)
-                return 0;
+        for (n = sd_bus_track_first(t); n; n = sd_bus_track_next(t)) {
+                int c, j;
 
-        r = strv_extend(l, e);
-        if (r < 0)
-                return r;
+                c = sd_bus_track_count_name(t, n);
 
-        return 1;
+                for (j = 0; j < c; j++) {
+                        fputs(prefix, f);
+                        fputc('=', f);
+                        fputs(n, f);
+                        fputc('\n', f);
+                }
+        }
 }
 
-int bus_track_coldplug(Manager *m, sd_bus_track **t, char ***l) {
+int bus_track_coldplug(Manager *m, sd_bus_track **t, bool recursive, char **l) {
+        char **i;
         int r = 0;
 
         assert(m);
         assert(t);
-        assert(l);
-
-        if (!strv_isempty(*l) && m->api_bus) {
-                char **i;
 
-                if (!*t) {
-                        r = sd_bus_track_new(m->api_bus, t, NULL, NULL);
-                        if (r < 0)
-                                return r;
-                }
+        if (strv_isempty(l))
+                return 0;
 
-                r = 0;
-                STRV_FOREACH(i, *l) {
-                        int k;
+        if (!m->api_bus)
+                return 0;
 
-                        k = sd_bus_track_add_name(*t, *i);
-                        if (k < 0)
-                                r = k;
-                }
+        if (!*t) {
+                r = sd_bus_track_new(m->api_bus, t, NULL, NULL);
+                if (r < 0)
+                        return r;
         }
 
-        *l = strv_free(*l);
+        r = sd_bus_track_set_recursive(*t, recursive);
+        if (r < 0)
+                return r;
+
+        r = 0;
+        STRV_FOREACH(i, l) {
+                int k;
+
+                k = sd_bus_track_add_name(*t, *i);
+                if (k < 0)
+                        r = k;
+        }
 
         return r;
 }
index 6baaffbd75e37d2608b4ed1e029539e313b11cfd..a092ed9d766b6687ebbe6c6b5f5bc2503864082c 100644 (file)
@@ -28,9 +28,8 @@ void bus_done(Manager *m);
 
 int bus_fdset_add_all(Manager *m, FDSet *fds);
 
-void bus_track_serialize(sd_bus_track *t, FILE *f);
-int bus_track_deserialize_item(char ***l, const char *line);
-int bus_track_coldplug(Manager *m, sd_bus_track **t, char ***l);
+void bus_track_serialize(sd_bus_track *t, FILE *f, const char *prefix);
+int bus_track_coldplug(Manager *m, sd_bus_track **t, bool recursive, char **l);
 
 int manager_sync_bus_names(Manager *m, sd_bus *bus);
 
index 7557874d4d6358cb5aa47f9e238f96d8d310d9aa..7faf2ef686b36f9f7e24d87fb7ea3443b727570d 100644 (file)
@@ -997,7 +997,10 @@ char *job_dbus_path(Job *j) {
         return p;
 }
 
-int job_serialize(Job *j, FILE *f, FDSet *fds) {
+int job_serialize(Job *j, FILE *f) {
+        assert(j);
+        assert(f);
+
         fprintf(f, "job-id=%u\n", j->id);
         fprintf(f, "job-type=%s\n", job_type_to_string(j->type));
         fprintf(f, "job-state=%s\n", job_state_to_string(j->state));
@@ -1008,15 +1011,16 @@ int job_serialize(Job *j, FILE *f, FDSet *fds) {
         if (j->begin_usec > 0)
                 fprintf(f, "job-begin="USEC_FMT"\n", j->begin_usec);
 
-        bus_track_serialize(j->clients, f);
+        bus_track_serialize(j->clients, f, "subscribed");
 
         /* End marker */
         fputc('\n', f);
         return 0;
 }
 
-int job_deserialize(Job *j, FILE *f, FDSet *fds) {
+int job_deserialize(Job *j, FILE *f) {
         assert(j);
+        assert(f);
 
         for (;;) {
                 char line[LINE_MAX], *l, *v;
@@ -1106,7 +1110,7 @@ int job_deserialize(Job *j, FILE *f, FDSet *fds) {
                 } else if (streq(l, "subscribed")) {
 
                         if (strv_extend(&j->deserialized_clients, v) < 0)
-                                return log_oom();
+                                log_oom();
                 }
         }
 }
@@ -1118,9 +1122,8 @@ int job_coldplug(Job *j) {
 
         /* After deserialization is complete and the bus connection
          * set up again, let's start watching our subscribers again */
-        r = bus_track_coldplug(j->manager, &j->clients, &j->deserialized_clients);
-        if (r < 0)
-                return r;
+        (void) bus_track_coldplug(j->manager, &j->clients, false, j->deserialized_clients);
+        j->deserialized_clients = strv_free(j->deserialized_clients);
 
         if (j->state == JOB_WAITING)
                 job_add_to_run_queue(j);
index d359e8bb3ed8bca02df4f7922a048bcf985b9964..85368f0d30359a84a661588f84d67f3c493ea86f 100644 (file)
@@ -177,8 +177,8 @@ Job* job_install(Job *j);
 int job_install_deserialized(Job *j);
 void job_uninstall(Job *j);
 void job_dump(Job *j, FILE*f, const char *prefix);
-int job_serialize(Job *j, FILE *f, FDSet *fds);
-int job_deserialize(Job *j, FILE *f, FDSet *fds);
+int job_serialize(Job *j, FILE *f);
+int job_deserialize(Job *j, FILE *f);
 int job_coldplug(Job *j);
 
 JobDependency* job_dependency_new(Job *subject, Job *object, bool matters, bool conflicts);
index 7576d038a2277c05cce4d56d761262d057d9194f..b58f68fa7ae1fee09899a7736b392d7cb5c2730d 100644 (file)
@@ -1287,10 +1287,11 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) {
         if (q < 0 && r == 0)
                 r = q;
 
-        /* We might have deserialized the kdbus control fd, but if we
-         * didn't, then let's create the bus now. */
-        manager_connect_bus(m, !!serialization);
-        bus_track_coldplug(m, &m->subscribed, &m->deserialized_subscribed);
+        /* We might have deserialized the kdbus control fd, but if we didn't, then let's create the bus now. */
+        (void) manager_connect_bus(m, !!serialization);
+
+        (void) bus_track_coldplug(m, &m->subscribed, false, m->deserialized_subscribed);
+        m->deserialized_subscribed = strv_free(m->deserialized_subscribed);
 
         /* Third, fire things up! */
         manager_coldplug(m);
@@ -2490,7 +2491,7 @@ int manager_serialize(Manager *m, FILE *f, FDSet *fds, bool switching_root) {
                 fprintf(f, "kdbus-fd=%i\n", copy);
         }
 
-        bus_track_serialize(m->subscribed, f);
+        bus_track_serialize(m->subscribed, f, "subscribed");
 
         r = dynamic_user_serialize(m, f, fds);
         if (r < 0)
@@ -2693,15 +2694,13 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) {
                         manager_deserialize_uid_refs_one(m, l + 16);
                 else if (startswith(l, "destroy-ipc-gid="))
                         manager_deserialize_gid_refs_one(m, l + 16);
-                else {
-                        int k;
-
-                        k = bus_track_deserialize_item(&m->deserialized_subscribed, l);
-                        if (k < 0)
-                                log_debug_errno(k, "Failed to deserialize bus tracker object: %m");
-                        else if (k == 0)
-                                log_debug("Unknown serialization item '%s'", l);
-                }
+                else if (startswith(l, "subscribed=")) {
+
+                        if (strv_extend(&m->deserialized_subscribed, l+11) < 0)
+                                log_oom();
+
+                } else
+                        log_debug("Unknown serialization item '%s'", l);
         }
 
         for (;;) {
index 14f6aec0299dbafbc327545ad97ba5c6dc3f2d97..647e5f736c4f7df386c2224938b4a5f02fdde2ae 100644 (file)
                        send_interface="org.freedesktop.systemd1.Manager"
                        send_member="Reexecute"/>
 
+                <allow send_destination="org.freedesktop.systemd1"
+                       send_interface="org.freedesktop.systemd1.Manager"
+                       send_member="RefUnit"/>
+
+                <allow send_destination="org.freedesktop.systemd1"
+                       send_interface="org.freedesktop.systemd1.Manager"
+                       send_member="UnrefUnit"/>
+
                 <allow send_destination="org.freedesktop.systemd1"
                        send_interface="org.freedesktop.systemd1.Manager"
                        send_member="EnableUnitFiles"/>
index c58c501bc3b893d3553a17f3ff6affcf4b75acb2..6d92eb0c30e4d857678b97fd908cb341f9baeaa7 100644 (file)
@@ -329,6 +329,9 @@ bool unit_check_gc(Unit *u) {
         if (u->refs)
                 return true;
 
+        if (sd_bus_track_count(u->bus_track) > 0)
+                return true;
+
         if (UNIT_VTABLE(u)->check_gc)
                 if (UNIT_VTABLE(u)->check_gc(u))
                         return true;
@@ -509,6 +512,9 @@ void unit_free(Unit *u) {
 
         sd_bus_slot_unref(u->match_bus_slot);
 
+        sd_bus_track_unref(u->bus_track);
+        u->deserialized_refs = strv_free(u->deserialized_refs);
+
         unit_free_requires_mounts_for(u);
 
         SET_FOREACH(t, u->names, i)
@@ -897,6 +903,7 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
         Unit *following;
         _cleanup_set_free_ Set *following_set = NULL;
         int r;
+        const char *n;
 
         assert(u);
         assert(u->type >= 0);
@@ -1038,6 +1045,8 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
         else if (u->load_state == UNIT_ERROR)
                 fprintf(f, "%s\tLoad Error Code: %s\n", prefix, strerror(-u->load_error));
 
+        for (n = sd_bus_track_first(u->bus_track); n; n = sd_bus_track_next(u->bus_track))
+                fprintf(f, "%s\tBus Ref: %s\n", prefix, n);
 
         if (u->job)
                 job_dump(u->job, f, prefix2);
@@ -2622,15 +2631,17 @@ int unit_serialize(Unit *u, FILE *f, FDSet *fds, bool serialize_jobs) {
         if (gid_is_valid(u->ref_gid))
                 unit_serialize_item_format(u, f, "ref-gid", GID_FMT, u->ref_gid);
 
+        bus_track_serialize(u->bus_track, f, "ref");
+
         if (serialize_jobs) {
                 if (u->job) {
                         fprintf(f, "job\n");
-                        job_serialize(u->job, f, fds);
+                        job_serialize(u->job, f);
                 }
 
                 if (u->nop_job) {
                         fprintf(f, "job\n");
-                        job_serialize(u->nop_job, f, fds);
+                        job_serialize(u->nop_job, f);
                 }
         }
 
@@ -2760,7 +2771,7 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) {
                                 if (!j)
                                         return log_oom();
 
-                                r = job_deserialize(j, f, fds);
+                                r = job_deserialize(j, f);
                                 if (r < 0) {
                                         job_free(j);
                                         return r;
@@ -2880,6 +2891,12 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) {
                         else
                                 unit_ref_uid_gid(u, UID_INVALID, gid);
 
+                } else if (streq(l, "ref")) {
+
+                        r = strv_extend(&u->deserialized_refs, v);
+                        if (r < 0)
+                                log_oom();
+
                         continue;
                 }
 
@@ -2955,7 +2972,8 @@ int unit_add_node_link(Unit *u, const char *what, bool wants, UnitDependency dep
 }
 
 int unit_coldplug(Unit *u) {
-        int r = 0, q = 0;
+        int r = 0, q;
+        char **i;
 
         assert(u);
 
@@ -2966,18 +2984,26 @@ int unit_coldplug(Unit *u) {
 
         u->coldplugged = true;
 
-        if (UNIT_VTABLE(u)->coldplug)
-                r = UNIT_VTABLE(u)->coldplug(u);
+        STRV_FOREACH(i, u->deserialized_refs) {
+                q = bus_unit_track_add_name(u, *i);
+                if (q < 0 && r >= 0)
+                        r = q;
+        }
+        u->deserialized_refs = strv_free(u->deserialized_refs);
 
-        if (u->job)
-                q = job_coldplug(u->job);
+        if (UNIT_VTABLE(u)->coldplug) {
+                q = UNIT_VTABLE(u)->coldplug(u);
+                if (q < 0 && r >= 0)
+                        r = q;
+        }
 
-        if (r < 0)
-                return r;
-        if (q < 0)
-                return q;
+        if (u->job) {
+                q = job_coldplug(u->job);
+                if (q < 0 && r >= 0)
+                        r = q;
+        }
 
-        return 0;
+        return r;
 }
 
 static bool fragment_mtime_newer(const char *path, usec_t mtime) {
index 53875653d7b465c8f063e0e0451a29f55630fdc9..e5a2a77b7bcf872c29bab7b92611ca315f90c4a5 100644 (file)
@@ -108,6 +108,10 @@ struct Unit {
         /* The slot used for watching NameOwnerChanged signals */
         sd_bus_slot *match_bus_slot;
 
+        /* References to this unit from clients */
+        sd_bus_track *bus_track;
+        char **deserialized_refs;
+
         /* Job timeout and action to take */
         usec_t job_timeout;
         FailureAction job_timeout_action;
@@ -247,6 +251,9 @@ struct Unit {
 
         /* Did we already invoke unit_coldplug() for this unit? */
         bool coldplugged:1;
+
+        /* For transient units: whether to add a bus track reference after creating the unit */
+        bool bus_track_add:1;
 };
 
 struct UnitStatusMessageFormats {
index 32be3cdc38e8245cd0ed5ad0e96f949835775eca..a69193aa32d6103ec9ea134a947b7cfa4e4ca438 100644 (file)
@@ -45,6 +45,7 @@ BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map bus_common_errors[] = {
         SD_BUS_ERROR_MAP(BUS_ERROR_SHUTTING_DOWN,                ECANCELED),
         SD_BUS_ERROR_MAP(BUS_ERROR_SCOPE_NOT_RUNNING,            EHOSTDOWN),
         SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_DYNAMIC_USER,         ESRCH),
+        SD_BUS_ERROR_MAP(BUS_ERROR_NOT_REFERENCED,               EUNATCH),
 
         SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_MACHINE,              ENXIO),
         SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_IMAGE,                ENOENT),
index befb6fbfe086fa394d62e0233dfa046f54e96da9..5df21c8926e6a5244b5f2aba139b2085f5915afa 100644 (file)
@@ -41,6 +41,7 @@
 #define BUS_ERROR_SHUTTING_DOWN "org.freedesktop.systemd1.ShuttingDown"
 #define BUS_ERROR_SCOPE_NOT_RUNNING "org.freedesktop.systemd1.ScopeNotRunning"
 #define BUS_ERROR_NO_SUCH_DYNAMIC_USER "org.freedesktop.systemd1.NoSuchDynamicUser"
+#define BUS_ERROR_NOT_REFERENCED "org.freedesktop.systemd1.NotReferenced"
 
 #define BUS_ERROR_NO_SUCH_MACHINE "org.freedesktop.machine1.NoSuchMachine"
 #define BUS_ERROR_NO_SUCH_IMAGE "org.freedesktop.machine1.NoSuchImage"