chiark / gitweb /
service: optionally, trie dbus name cycle to service cycle
authorLennart Poettering <lennart@poettering.net>
Thu, 15 Apr 2010 21:16:16 +0000 (23:16 +0200)
committerLennart Poettering <lennart@poettering.net>
Thu, 15 Apr 2010 21:16:16 +0000 (23:16 +0200)
dbus.c
dbus.h
load-fragment.c
manager.c
manager.h
service.c
service.h
unit.c
unit.h

diff --git a/dbus.c b/dbus.c
index e2f8f3c..a1a3606 100644 (file)
--- a/dbus.c
+++ b/dbus.c
@@ -316,17 +316,25 @@ static DBusHandlerResult api_bus_message_filter(DBusConnection  *connection, DBu
                 bus_done_api(m);
 
         } else if (dbus_message_is_signal(message, DBUS_INTERFACE_DBUS, "NameOwnerChanged")) {
-                const char *name, *old, *new;
+                const char *name, *old_owner, *new_owner;
 
                 if (!dbus_message_get_args(message, &error,
                                            DBUS_TYPE_STRING, &name,
-                                           DBUS_TYPE_STRING, &old,
-                                           DBUS_TYPE_STRING, &new,
+                                           DBUS_TYPE_STRING, &old_owner,
+                                           DBUS_TYPE_STRING, &new_owner,
                                            DBUS_TYPE_INVALID))
                         log_error("Failed to parse NameOwnerChanged message: %s", error.message);
                 else  {
                         if (set_remove(m->subscribed, (char*) name))
                                 log_debug("Subscription client vanished: %s (left: %u)", name, set_size(m->subscribed));
+
+                        if (old_owner[0] == 0)
+                                old_owner = NULL;
+
+                        if (new_owner[0] == 0)
+                                new_owner = NULL;
+
+                        manager_dispatch_bus_name_owner_changed(m, name, old_owner, new_owner);
                 }
         }
 
@@ -388,7 +396,7 @@ unsigned bus_dispatch(Manager *m) {
         return 0;
 }
 
-static void pending_cb(DBusPendingCall *pending, void *userdata) {
+static void request_name_pending_cb(DBusPendingCall *pending, void *userdata) {
         DBusMessage *reply;
         DBusError error;
 
@@ -432,48 +440,49 @@ static void pending_cb(DBusPendingCall *pending, void *userdata) {
 }
 
 static int request_name(Manager *m) {
-        DBusMessage *message;
         const char *name = "org.freedesktop.systemd1";
         uint32_t flags = 0;
-        DBusPendingCall *pending;
+        DBusMessage *message = NULL;
+        DBusPendingCall *pending = NULL;
 
         if (!(message = dbus_message_new_method_call(
                               DBUS_SERVICE_DBUS,
                               DBUS_PATH_DBUS,
                               DBUS_INTERFACE_DBUS,
                               "RequestName")))
-                return -ENOMEM;
+                goto oom;
 
         if (!dbus_message_append_args(
                             message,
                             DBUS_TYPE_STRING, &name,
                             DBUS_TYPE_UINT32, &flags,
-                            DBUS_TYPE_INVALID)) {
-                dbus_message_unref(message);
-                return -ENOMEM;
-        }
+                            DBUS_TYPE_INVALID))
+                goto oom;
 
-        if (!dbus_connection_send_with_reply(m->api_bus, message, &pending, -1)) {
-                dbus_message_unref(message);
-                return -ENOMEM;
-        }
+        if (!dbus_connection_send_with_reply(m->api_bus, message, &pending, -1))
+                goto oom;
 
+        if (!dbus_pending_call_set_notify(pending, request_name_pending_cb, m, NULL))
+                goto oom;
 
         dbus_message_unref(message);
-
-        if (!dbus_pending_call_set_notify(pending, pending_cb, NULL, NULL)) {
-                dbus_pending_call_cancel(pending);
-                dbus_pending_call_unref(pending);
-                return -ENOMEM;
-        }
-
-
         dbus_pending_call_unref(pending);
 
         /* We simple ask for the name and don't wait for it. Sooner or
          * later we'll have it. */
 
         return 0;
+
+oom:
+        if (pending) {
+                dbus_pending_call_cancel(pending);
+                dbus_pending_call_unref(pending);
+        }
+
+        if (message)
+                dbus_message_unref(message);
+
+        return -ENOMEM;
 }
 
 static int bus_setup_loop(Manager *m, DBusConnection *bus) {
@@ -557,6 +566,10 @@ int bus_init_api(Manager *m) {
         if (m->api_bus)
                 return 0;
 
+        if (m->name_data_slot < 0)
+                if (!dbus_pending_call_allocate_data_slot(&m->name_data_slot))
+                        return -ENOMEM;
+
         if (m->running_as != MANAGER_SESSION && m->system_bus)
                 m->api_bus = m->system_bus;
         else {
@@ -636,6 +649,9 @@ void bus_done_api(Manager *m) {
                 set_free(m->subscribed);
                 m->subscribed = NULL;
         }
+
+       if (m->name_data_slot >= 0)
+               dbus_pending_call_free_data_slot(&m->name_data_slot);
 }
 
 void bus_done_system(Manager *m) {
@@ -652,6 +668,102 @@ void bus_done_system(Manager *m) {
         }
 }
 
+static void query_pid_pending_cb(DBusPendingCall *pending, void *userdata) {
+        Manager *m = userdata;
+        DBusMessage *reply;
+        DBusError error;
+        const char *name;
+
+        dbus_error_init(&error);
+
+        assert_se(name = dbus_pending_call_get_data(pending, m->name_data_slot));
+        assert_se(reply = dbus_pending_call_steal_reply(pending));
+
+        switch (dbus_message_get_type(reply)) {
+
+        case DBUS_MESSAGE_TYPE_ERROR:
+
+                assert_se(dbus_set_error_from_message(&error, reply));
+                log_warning("GetConnectionUnixProcessID() failed: %s", error.message);
+                break;
+
+        case DBUS_MESSAGE_TYPE_METHOD_RETURN: {
+                uint32_t r;
+
+                if (!dbus_message_get_args(reply,
+                                           &error,
+                                           DBUS_TYPE_UINT32, &r,
+                                           DBUS_TYPE_INVALID)) {
+                        log_error("Failed to parse GetConnectionUnixProcessID() reply: %s", error.message);
+                        break;
+                }
+
+                manager_dispatch_bus_query_pid_done(m, name, (pid_t) r);
+                break;
+        }
+
+        default:
+                assert_not_reached("Invalid reply message");
+        }
+
+        dbus_message_unref(reply);
+        dbus_error_free(&error);
+}
+
+int bus_query_pid(Manager *m, const char *name) {
+        DBusMessage *message = NULL;
+        DBusPendingCall *pending = NULL;
+        char *n = NULL;
+
+        assert(m);
+        assert(name);
+
+        if (!(message = dbus_message_new_method_call(
+                              DBUS_SERVICE_DBUS,
+                              DBUS_PATH_DBUS,
+                              DBUS_INTERFACE_DBUS,
+                              "GetConnectionUnixProcessID")))
+                goto oom;
+
+        if (!(dbus_message_append_args(
+                              message,
+                              DBUS_TYPE_STRING, &name,
+                              DBUS_TYPE_INVALID)))
+                goto oom;
+
+        if (!dbus_connection_send_with_reply(m->api_bus, message, &pending, -1))
+                goto oom;
+
+        if (!(n = strdup(name)))
+                goto oom;
+
+        if (!dbus_pending_call_set_data(pending, m->name_data_slot, n, free))
+                goto oom;
+
+        n = NULL;
+
+        if (!dbus_pending_call_set_notify(pending, query_pid_pending_cb, m, NULL))
+                goto oom;
+
+        dbus_message_unref(message);
+        dbus_pending_call_unref(pending);
+
+        return 0;
+
+oom:
+        free(n);
+
+        if (pending) {
+                dbus_pending_call_cancel(pending);
+                dbus_pending_call_unref(pending);
+        }
+
+        if (message)
+                dbus_message_unref(message);
+
+        return -ENOMEM;
+}
+
 DBusHandlerResult bus_default_message_handler(Manager *m, DBusMessage *message, const char*introspection, const BusProperty *properties) {
         DBusError error;
         DBusMessage *reply = NULL;
diff --git a/dbus.h b/dbus.h
index 5b41877..412c668 100644 (file)
--- a/dbus.h
+++ b/dbus.h
@@ -70,6 +70,8 @@ DBusHandlerResult bus_default_message_handler(Manager *m, DBusMessage *message,
 
 DBusHandlerResult bus_send_error_reply(Manager *m, DBusMessage *message, DBusError *bus_error, int error);
 
+int bus_query_pid(Manager *m, const char *name);
+
 int bus_property_append_string(Manager *m, DBusMessageIter *i, const char *property, void *data);
 int bus_property_append_strv(Manager *m, DBusMessageIter *i, const char *property, void *data);
 int bus_property_append_bool(Manager *m, DBusMessageIter *i, const char *property, void *data);
index 5e98ae8..8e42188 100644 (file)
@@ -1182,6 +1182,7 @@ static int load_from_path(Unit *u, const char *path) {
                 { "SysVStartPriority",      config_parse_sysv_priority,   &u->service.sysv_start_priority,                 "Service" },
                 { "KillMode",               config_parse_kill_mode,       &u->service.kill_mode,                           "Service" },
                 { "NonBlocking",            config_parse_bool,            &u->service.exec_context.non_blocking,           "Service" },
+                { "BusName",                config_parse_string,          &u->service.bus_name,                            "Service" },
                 EXEC_CONTEXT_CONFIG_ITEMS(u->service.exec_context, "Service"),
 
                 { "ListenStream",           config_parse_listen,          &u->socket,                                      "Socket"  },
index 07b2d4a..e5dc209 100644 (file)
--- a/manager.c
+++ b/manager.c
@@ -320,6 +320,7 @@ int manager_new(ManagerRunningAs running_as, bool confirm_spawn, Manager **_m) {
 
         m->running_as = running_as;
         m->confirm_spawn = confirm_spawn;
+        m->name_data_slot = -1;
 
         m->signal_watch.fd = m->mount_watch.fd = m->udev_watch.fd = m->epoll_fd = -1;
         m->current_job_id = 1; /* start as id #1, so that we can leave #0 around as "null-like" value */
@@ -339,6 +340,9 @@ int manager_new(ManagerRunningAs running_as, bool confirm_spawn, Manager **_m) {
         if (!(m->cgroup_bondings = hashmap_new(string_hash_func, string_compare_func)))
                 goto fail;
 
+        if (!(m->watch_bus = hashmap_new(string_hash_func, string_compare_func)))
+                goto fail;
+
         if ((m->epoll_fd = epoll_create1(EPOLL_CLOEXEC)) < 0)
                 goto fail;
 
@@ -408,6 +412,7 @@ void manager_free(Manager *m) {
         hashmap_free(m->jobs);
         hashmap_free(m->transaction_jobs);
         hashmap_free(m->watch_pids);
+        hashmap_free(m->watch_bus);
 
         if (m->epoll_fd >= 0)
                 close_nointr(m->epoll_fd);
@@ -1866,6 +1871,40 @@ void manager_write_utmp_runlevel(Manager *m, Unit *u) {
         }
 }
 
+void manager_dispatch_bus_name_owner_changed(
+                Manager *m,
+                const char *name,
+                const char* old_owner,
+                const char *new_owner) {
+
+        Unit *u;
+
+        assert(m);
+        assert(name);
+
+        if (!(u = hashmap_get(m->watch_bus, name)))
+                return;
+
+        UNIT_VTABLE(u)->bus_name_owner_change(u, name, old_owner, new_owner);
+}
+
+void manager_dispatch_bus_query_pid_done(
+                Manager *m,
+                const char *name,
+                pid_t pid) {
+
+        Unit *u;
+
+        assert(m);
+        assert(name);
+        assert(pid >= 1);
+
+        if (!(u = hashmap_get(m->watch_bus, name)))
+                return;
+
+        UNIT_VTABLE(u)->bus_query_pid_done(u, name, pid);
+}
+
 static const char* const manager_running_as_table[_MANAGER_RUNNING_AS_MAX] = {
         [MANAGER_INIT] = "init",
         [MANAGER_SYSTEM] = "system",
index ae9cd9f..cb4af82 100644 (file)
--- a/manager.h
+++ b/manager.h
@@ -178,6 +178,9 @@ struct Manager {
         DBusConnection *api_bus, *system_bus;
         Set *subscribed;
 
+        Hashmap *watch_bus;  /* D-Bus names => Unit object n:1 */
+        int32_t name_data_slot;
+
         /* Data specific to the cgroup subsystem */
         Hashmap *cgroup_bondings; /* path string => CGroupBonding object 1:n */
         char *cgroup_controller;
@@ -215,11 +218,13 @@ unsigned manager_dispatch_dbus_queue(Manager *m);
 
 int manager_loop(Manager *m);
 
-const char *manager_running_as_to_string(ManagerRunningAs i);
-ManagerRunningAs manager_running_as_from_string(const char *s);
-
 void manager_write_utmp_reboot(Manager *m);
-
 void manager_write_utmp_runlevel(Manager *m, Unit *t);
 
+void manager_dispatch_bus_name_owner_changed(Manager *m, const char *name, const char* old_owner, const char *new_owner);
+void manager_dispatch_bus_query_pid_done(Manager *m, const char *name, pid_t pid);
+
+const char *manager_running_as_to_string(ManagerRunningAs i);
+ManagerRunningAs manager_running_as_from_string(const char *s);
+
 #endif
index 96e9d83..bd248a0 100644 (file)
--- a/service.c
+++ b/service.c
@@ -118,6 +118,12 @@ static void service_done(Unit *u) {
         service_unwatch_main_pid(s);
         service_unwatch_control_pid(s);
 
+        if (s->bus_name)  {
+                unit_unwatch_bus_name(UNIT(u), s->bus_name);
+                free(s->bus_name);
+                s->bus_name = NULL;
+        }
+
         service_close_socket_fd(s);
 
         unit_unwatch_timer(u, &s->timer_watch);
@@ -712,6 +718,22 @@ static int service_load_sysv(Service *s) {
         return 0;
 }
 
+static int service_add_bus_name(Service *s) {
+        char *n;
+        int r;
+
+        assert(s);
+        assert(s->bus_name);
+
+        if (asprintf(&n, "dbus-%s.service", s->bus_name) < 0)
+                return 0;
+
+        r = unit_merge_by_name(UNIT(s), n);
+        free(n);
+
+        return r;
+}
+
 static void service_init(Unit *u) {
         Service *s = SERVICE(u);
 
@@ -741,6 +763,7 @@ static void service_init(Unit *u) {
         s->failure = false;
 
         s->socket_fd = -1;
+        s->bus_name_good = false;
 
         RATELIMIT_INIT(s->ratelimit, 10*USEC_PER_SEC, 5);
 }
@@ -756,6 +779,11 @@ static int service_verify(Service *s) {
                 return -EINVAL;
         }
 
+        if (s->type == SERVICE_DBUS && !s->bus_name) {
+                log_error("%s is of type D-Bus but no D-Bus service name has been specified. Refusing.", UNIT(s)->meta.id);
+                return -EINVAL;
+        }
+
         return 0;
 }
 
@@ -793,6 +821,14 @@ static int service_load(Unit *u) {
 
                 if ((r = sysv_chkconfig_order(s)) < 0)
                         return r;
+
+                if (s->bus_name) {
+                        if ((r = service_add_bus_name(s)) < 0)
+                                return r;
+
+                        if ((r = unit_watch_bus_name(u, s->bus_name)) < 0)
+                            return r;
+                }
         }
 
         return service_verify(s);
@@ -839,6 +875,13 @@ static void service_dump(Unit *u, FILE *f, const char *prefix) {
                         "%sPIDFile: %s\n",
                         prefix, s->pid_file);
 
+        if (s->bus_name)
+                fprintf(f,
+                        "%sBusName: %s\n"
+                        "%sBus Name Good: %s\n",
+                        prefix, s->bus_name,
+                        prefix, yes_no(s->bus_name_good));
+
         exec_context_dump(&s->exec_context, f, prefix);
 
         for (c = 0; c < _SERVICE_EXEC_COMMAND_MAX; c++) {
@@ -1373,7 +1416,9 @@ static void service_enter_running(Service *s, bool success) {
         if (!success)
                 s->failure = true;
 
-        if (main_pid_good(s) != 0 && cgroup_good(s) != 0)
+        if (main_pid_good(s) != 0 &&
+            cgroup_good(s) != 0 &&
+            (s->bus_name_good || s->type != SERVICE_DBUS))
                 service_set_state(s, SERVICE_RUNNING);
         else if (s->valid_no_process)
                 service_set_state(s, SERVICE_EXITED);
@@ -1425,7 +1470,7 @@ static void service_enter_start(Service *s) {
 
         if ((r = service_spawn(s,
                                s->exec_command[SERVICE_EXEC_START],
-                               s->type == SERVICE_FORKING,
+                               s->type == SERVICE_FORKING || s->type == SERVICE_DBUS,
                                true,
                                true,
                                true,
@@ -1451,15 +1496,18 @@ static void service_enter_start(Service *s) {
                 s->control_command = s->exec_command[SERVICE_EXEC_START];
                 service_set_state(s, SERVICE_START);
 
-        } else if (s->type == SERVICE_FINISH) {
+        } else if (s->type == SERVICE_FINISH ||
+                   s->type == SERVICE_DBUS) {
 
                 /* For finishing services we wait until the start
                  * process exited, too, but it is our main process. */
 
+                /* For D-Bus services we know the main pid right away,
+                 * but wait for the bus name to appear on the bus. */
+
                 s->main_pid = pid;
                 s->main_pid_known = true;
 
-                s->control_command = s->exec_command[SERVICE_EXEC_START];
                 service_set_state(s, SERVICE_START);
         } else
                 assert_not_reached("Unknown service type");
@@ -2046,6 +2094,72 @@ finish:
         return r;
 }
 
+static void service_bus_name_owner_change(
+                Unit *u,
+                const char *name,
+                const char *old_owner,
+                const char *new_owner) {
+
+        Service *s = SERVICE(u);
+
+        assert(s);
+        assert(name);
+
+        assert(streq(s->bus_name, name));
+        assert(old_owner || new_owner);
+
+        if (old_owner && new_owner)
+                log_debug("%s's D-Bus name %s changed owner from %s to %s", u->meta.id, name, old_owner, new_owner);
+        else if (old_owner)
+                log_debug("%s's D-Bus name %s no longer registered by %s", u->meta.id, name, old_owner);
+        else
+                log_debug("%s's D-Bus name %s now registered by %s", u->meta.id, name, new_owner);
+
+        s->bus_name_good = !!new_owner;
+
+        if (s->type == SERVICE_DBUS) {
+
+                /* service_enter_running() will figure out what to
+                 * do */
+                if (s->state == SERVICE_RUNNING)
+                        service_enter_running(s, true);
+                else if (s->state == SERVICE_START && new_owner)
+                        service_enter_start_post(s);
+
+        } else if (new_owner &&
+                   s->main_pid <= 0 &&
+                   (s->state == SERVICE_START ||
+                    s->state == SERVICE_START_POST ||
+                    s->state == SERVICE_RUNNING ||
+                    s->state == SERVICE_RELOAD)) {
+
+                /* Try to acquire PID from bus service */
+                log_debug("Trying to acquire PID from D-Bus name...");
+
+                bus_query_pid(u->meta.manager, name);
+        }
+}
+
+static void service_bus_query_pid_done(
+                Unit *u,
+                const char *name,
+                pid_t pid) {
+
+        Service *s = SERVICE(u);
+
+        assert(s);
+        assert(name);
+
+        log_debug("%s's D-Bus name %s is now owned by process %u", u->meta.id, name, (unsigned) pid);
+
+        if (s->main_pid <= 0 &&
+            (s->state == SERVICE_START ||
+             s->state == SERVICE_START_POST ||
+             s->state == SERVICE_RUNNING ||
+             s->state == SERVICE_RELOAD))
+                s->main_pid = pid;
+}
+
 int service_set_socket_fd(Service *s, int fd) {
         assert(s);
         assert(fd >= 0);
@@ -2098,7 +2212,8 @@ DEFINE_STRING_TABLE_LOOKUP(service_restart, ServiceRestart);
 static const char* const service_type_table[_SERVICE_TYPE_MAX] = {
         [SERVICE_FORKING] = "forking",
         [SERVICE_SIMPLE] = "simple",
-        [SERVICE_FINISH] = "finish"
+        [SERVICE_FINISH] = "finish",
+        [SERVICE_DBUS] = "dbus"
 };
 
 DEFINE_STRING_TABLE_LOOKUP(service_type, ServiceType);
@@ -2137,5 +2252,8 @@ const UnitVTable service_vtable = {
 
         .cgroup_notify_empty = service_cgroup_notify_event,
 
+        .bus_name_owner_change = service_bus_name_owner_change,
+        .bus_query_pid_done = service_bus_query_pid_done,
+
         .enumerate = service_enumerate
 };
index 5ddc180..e603ff7 100644 (file)
--- a/service.h
+++ b/service.h
@@ -57,8 +57,9 @@ typedef enum ServiceRestart {
 
 typedef enum ServiceType {
         SERVICE_FORKING,  /* forks by itself (i.e. traditional daemons) */
-        SERVICE_SIMPLE,   /* we fork and go on right-away (i.e. modern socket activated daemons)*/
+        SERVICE_SIMPLE,   /* we fork and go on right-away (i.e. modern socket activated daemons) */
         SERVICE_FINISH,   /* we fork and wait until the program finishes (i.e. programs like fsck which run and need to finish before we continue) */
+        SERVICE_DBUS,     /* we fork and wait until a specific D-Bus name appears on the bus */
         _SERVICE_TYPE_MAX,
         _SERVICE_TYPE_INVALID = -1
 } ServiceType;
@@ -103,13 +104,18 @@ struct Service {
         pid_t main_pid, control_pid;
         bool main_pid_known:1;
 
-        bool failure:1; /* if we shut down, remember why */
+        /* If we shut down, remember why */
+        bool failure:1;
+
+        bool bus_name_good:1;
 
         bool sysv_has_lsb:1;
         char *sysv_path;
         int sysv_start_priority;
         char *sysv_runlevels;
 
+        char *bus_name;
+
         RateLimit ratelimit;
 
         int socket_fd;
diff --git a/unit.c b/unit.c
index 872abf3..900c76a 100644 (file)
--- a/unit.c
+++ b/unit.c
@@ -1023,6 +1023,9 @@ int unit_watch_pid(Unit *u, pid_t pid) {
         assert(u);
         assert(pid >= 1);
 
+        /* Watch a specific PID. We only support one unit watching
+         * each PID for now. */
+
         return hashmap_put(u->meta.manager->watch_pids, UINT32_TO_PTR(pid), u);
 }
 
@@ -1030,7 +1033,7 @@ void unit_unwatch_pid(Unit *u, pid_t pid) {
         assert(u);
         assert(pid >= 1);
 
-        hashmap_remove(u->meta.manager->watch_pids, UINT32_TO_PTR(pid));
+        hashmap_remove_value(u->meta.manager->watch_pids, UINT32_TO_PTR(pid), u);
 }
 
 int unit_watch_timer(Unit *u, usec_t delay, Watch *w) {
@@ -1606,6 +1609,23 @@ fail:
         return NULL;
 }
 
+int unit_watch_bus_name(Unit *u, const char *name) {
+        assert(u);
+        assert(name);
+
+        /* Watch a specific name on the bus. We only support one unit
+         * watching each name for now. */
+
+        return hashmap_put(u->meta.manager->watch_bus, name, u);
+}
+
+void unit_unwatch_bus_name(Unit *u, const char *name) {
+        assert(u);
+        assert(name);
+
+        hashmap_remove_value(u->meta.manager->watch_bus, name, u);
+}
+
 static const char* const unit_type_table[_UNIT_TYPE_MAX] = {
         [UNIT_SERVICE] = "service",
         [UNIT_TIMER] = "timer",
diff --git a/unit.h b/unit.h
index c042335..338a58b 100644 (file)
--- a/unit.h
+++ b/unit.h
@@ -250,8 +250,17 @@ struct UnitVTable {
         void (*sigchld_event)(Unit *u, pid_t pid, int code, int status);
         void (*timer_event)(Unit *u, uint64_t n_elapsed, Watch *w);
 
+        /* Called whenever any of the cgroups this unit watches for
+         * ran empty */
         void (*cgroup_notify_empty)(Unit *u);
 
+        /* Called whenever a name thus Unit registered for comes or
+         * goes away. */
+        void (*bus_name_owner_change)(Unit *u, const char *name, const char *old_owner, const char *new_owner);
+
+        /* Called whenever a bus PID lookup finishes */
+        void (*bus_query_pid_done)(Unit *u, const char *name, pid_t pid);
+
         /* This is called for each unit type and should be used to
          * enumerate existing devices and load them. However,
          * everything that is loaded here should still stay in
@@ -348,6 +357,9 @@ void unit_unwatch_pid(Unit *u, pid_t pid);
 int unit_watch_timer(Unit *u, usec_t delay, Watch *w);
 void unit_unwatch_timer(Unit *u, Watch *w);
 
+int unit_watch_bus_name(Unit *u, const char *name);
+void unit_unwatch_bus_name(Unit *u, const char *name);
+
 bool unit_job_is_applicable(Unit *u, JobType j);
 
 int set_unit_path(const char *p);