chiark / gitweb /
service: never do automatic restarts for user requested stops
[elogind.git] / dbus.c
diff --git a/dbus.c b/dbus.c
index 546a7c59eb0a5333fa439a8b6fb92884b4aa7d26..0054d1519e3bfdfd936dc0edec533f7b4a52063e 100644 (file)
--- a/dbus.c
+++ b/dbus.c
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
-#include <dbus/dbus.h>
-
 #include <sys/epoll.h>
 #include <sys/timerfd.h>
 #include <errno.h>
 #include <unistd.h>
+#include <dbus/dbus.h>
 
 #include "dbus.h"
 #include "log.h"
 #include "strv.h"
 #include "cgroup.h"
+#include "dbus-unit.h"
+#include "dbus-job.h"
+#include "dbus-manager.h"
 
 static void api_bus_dispatch_status(DBusConnection *bus, DBusDispatchStatus status, void *data)  {
         Manager *m = data;
 
         assert(bus);
         assert(m);
+
+        if (!m->api_bus)
+                return;
+
         assert(m->api_bus == bus);
 
         m->request_api_bus_dispatch = status != DBUS_DISPATCH_COMPLETE;
@@ -46,6 +52,10 @@ static void system_bus_dispatch_status(DBusConnection *bus, DBusDispatchStatus s
 
         assert(bus);
         assert(m);
+
+        if (!m->system_bus)
+                return;
+
         assert(m->system_bus == bus);
 
         m->request_system_bus_dispatch = status != DBUS_DISPATCH_COMPLETE;
@@ -304,21 +314,29 @@ static DBusHandlerResult api_bus_message_filter(DBusConnection  *connection, DBu
         /*           dbus_message_get_path(message)); */
 
         if (dbus_message_is_signal(message, DBUS_INTERFACE_LOCAL, "Disconnected")) {
-                log_error("Warning! D-Bus connection terminated.");
+                log_error("Warning! API D-Bus connection terminated.");
                 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);
                 }
         }
 
@@ -342,7 +360,7 @@ static DBusHandlerResult system_bus_message_filter(DBusConnection  *connection,
         /*           dbus_message_get_path(message)); */
 
         if (dbus_message_is_signal(message, DBUS_INTERFACE_LOCAL, "Disconnected")) {
-                log_error("Warning! D-Bus connection terminated.");
+                log_error("Warning! System D-Bus connection terminated.");
                 bus_done_system(m);
 
         } if (dbus_message_is_signal(message, "org.freedesktop.systemd1.Agent", "Released")) {
@@ -363,22 +381,36 @@ static DBusHandlerResult system_bus_message_filter(DBusConnection  *connection,
 unsigned bus_dispatch(Manager *m) {
         assert(m);
 
-        if (m->request_api_bus_dispatch)
-                if (dbus_connection_dispatch(m->api_bus) == DBUS_DISPATCH_COMPLETE) {
+        if (m->queued_message) {
+                /* If we cannot get rid of this message we won't
+                 * dispatch any D-Bus messages, so that we won't end
+                 * up wanting to queue another message. */
+
+                if (!dbus_connection_send(m->api_bus, m->queued_message, NULL))
+                        return 0;
+
+                dbus_message_unref(m->queued_message);
+                m->queued_message = NULL;
+        }
+
+        if (m->request_api_bus_dispatch) {
+                if (dbus_connection_dispatch(m->api_bus) == DBUS_DISPATCH_COMPLETE)
                         m->request_api_bus_dispatch = false;
-                        return 1;
-                }
 
-        if (m->request_system_bus_dispatch)
-                if (dbus_connection_dispatch(m->system_bus) == DBUS_DISPATCH_COMPLETE) {
+                return 1;
+        }
+
+        if (m->request_system_bus_dispatch) {
+                if (dbus_connection_dispatch(m->system_bus) == DBUS_DISPATCH_COMPLETE)
                         m->request_system_bus_dispatch = false;
-                        return 1;
-                }
+
+                return 1;
+        }
 
         return 0;
 }
 
-static void pending_cb(DBusPendingCall *pending, void *userdata) {
+static void request_name_pending_cb(DBusPendingCall *pending, void *userdata) {
         DBusMessage *reply;
         DBusError error;
 
@@ -422,48 +454,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) {
@@ -471,6 +504,7 @@ static int bus_setup_loop(Manager *m, DBusConnection *bus) {
         assert(bus);
 
         dbus_connection_set_exit_on_disconnect(bus, FALSE);
+
         if (!dbus_connection_set_watch_functions(bus, bus_add_watch, bus_remove_watch, bus_toggle_watch, m, NULL) ||
             !dbus_connection_set_timeout_functions(bus, bus_add_timeout, bus_remove_timeout, bus_toggle_timeout, m, NULL))
                 return -ENOMEM;
@@ -499,12 +533,13 @@ int bus_init_system(Manager *m) {
                         return 0;
                 }
 
+                dbus_connection_set_dispatch_status_function(m->system_bus, system_bus_dispatch_status, m, NULL);
+                m->request_system_bus_dispatch = true;
+
                 if ((r = bus_setup_loop(m, m->system_bus)) < 0) {
                         bus_done_system(m);
                         return r;
                 }
-
-                dbus_connection_set_dispatch_status_function(m->system_bus, system_bus_dispatch_status, m, NULL);
         }
 
         if (!dbus_connection_add_filter(m->system_bus, system_bus_message_filter, m, NULL)) {
@@ -530,8 +565,6 @@ int bus_init_system(Manager *m) {
                   strnull(dbus_bus_get_unique_name(m->system_bus)));
         dbus_free(id);
 
-        m->request_system_bus_dispatch = true;
-
         return 0;
 }
 
@@ -547,6 +580,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 {
@@ -556,12 +593,13 @@ int bus_init_api(Manager *m) {
                         return 0;
                 }
 
+                dbus_connection_set_dispatch_status_function(m->api_bus, api_bus_dispatch_status, m, NULL);
+                m->request_api_bus_dispatch = true;
+
                 if ((r = bus_setup_loop(m, m->api_bus)) < 0) {
                         bus_done_api(m);
                         return r;
                 }
-
-                dbus_connection_set_dispatch_status_function(m->api_bus, api_bus_dispatch_status, m, NULL);
         }
 
         if (!dbus_connection_register_object_path(m->api_bus, "/org/freedesktop/systemd1", &bus_manager_vtable, m) ||
@@ -600,8 +638,6 @@ int bus_init_api(Manager *m) {
                 if (!(m->subscribed = set_new(string_hash_func, string_compare_func)))
                         return -ENOMEM;
 
-        m->request_api_bus_dispatch = true;
-
         return 0;
 }
 
@@ -612,10 +648,11 @@ void bus_done_api(Manager *m) {
                 if (m->system_bus == m->api_bus)
                         m->system_bus = NULL;
 
+                dbus_connection_set_dispatch_status_function(m->api_bus, NULL, NULL, NULL);
+                dbus_connection_flush(m->api_bus);
                 dbus_connection_close(m->api_bus);
                 dbus_connection_unref(m->api_bus);
                 m->api_bus = NULL;
-
         }
 
         if (m->subscribed) {
@@ -627,6 +664,14 @@ 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);
+
+       if (m->queued_message) {
+               dbus_message_unref(m->queued_message);
+               m->queued_message = NULL;
+       }
 }
 
 void bus_done_system(Manager *m) {
@@ -636,12 +681,110 @@ void bus_done_system(Manager *m) {
                 bus_done_api(m);
 
         if (m->system_bus) {
+                dbus_connection_set_dispatch_status_function(m->system_bus, NULL, NULL, NULL);
+                dbus_connection_flush(m->system_bus);
                 dbus_connection_close(m->system_bus);
                 dbus_connection_unref(m->system_bus);
                 m->system_bus = NULL;
         }
 }
 
+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;
@@ -687,7 +830,7 @@ DBusHandlerResult bus_default_message_handler(Manager *m, DBusMessage *message,
                         if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT, p->signature, &sub))
                                 goto oom;
 
-                        if ((r = p->append(m, &sub, property, p->data)) < 0) {
+                        if ((r = p->append(m, &sub, property, (void*) p->data)) < 0) {
 
                                 if (r == -ENOMEM)
                                         goto oom;
@@ -729,7 +872,7 @@ DBusHandlerResult bus_default_message_handler(Manager *m, DBusMessage *message,
                             !dbus_message_iter_open_container(&sub2, DBUS_TYPE_VARIANT, p->signature, &sub3))
                                 goto oom;
 
-                        if ((r = p->append(m, &sub3, p->property, p->data)) < 0) {
+                        if ((r = p->append(m, &sub3, p->property, (void*) p->data)) < 0) {
 
                                 if (r == -ENOMEM)
                                         goto oom;
@@ -901,6 +1044,10 @@ int bus_property_append_uint64(Manager *m, DBusMessageIter *i, const char *prope
         assert(property);
         assert(data);
 
+        /* Let's ensure that pid_t is actually 64bit, and hence this
+         * function can be used for usec_t */
+        assert_cc(sizeof(uint64_t) == sizeof(usec_t));
+
         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_UINT64, data))
                 return -ENOMEM;
 
@@ -913,8 +1060,28 @@ int bus_property_append_uint32(Manager *m, DBusMessageIter *i, const char *prope
         assert(property);
         assert(data);
 
+        /* Let's ensure that pid_t and mode_t is actually 32bit, and
+         * hence this function can be used for pid_t/mode_t */
+        assert_cc(sizeof(uint32_t) == sizeof(pid_t));
+        assert_cc(sizeof(uint32_t) == sizeof(mode_t));
+        assert_cc(sizeof(uint32_t) == sizeof(unsigned));
+
         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_UINT32, data))
                 return -ENOMEM;
 
         return 0;
 }
+
+int bus_property_append_int32(Manager *m, DBusMessageIter *i, const char *property, void *data) {
+        assert(m);
+        assert(i);
+        assert(property);
+        assert(data);
+
+        assert_cc(sizeof(int32_t) == sizeof(int));
+
+        if (!dbus_message_iter_append_basic(i, DBUS_TYPE_INT32, data))
+                return -ENOMEM;
+
+        return 0;
+}