* systemctl daemon-reload is kaputt
-* get rid of Subscribe() in systemctl
-
* Turn around negative options
* Add missing man pages: update systemd.1, finish daemon.7
.message_function = bus_job_message_handler
};
+static int job_send_message(Job *j, DBusMessage *m) {
+ int r;
+
+ assert(j);
+ assert(m);
+
+ if (bus_has_subscriber(j->manager)) {
+ if ((r = bus_broadcast(j->manager, m)) < 0)
+ return r;
+
+ } else if (j->bus_client) {
+ /* If nobody is subscribed, we just send the message
+ * to the client which created the job */
+
+ assert(j->bus);
+
+ if (!dbus_message_set_destination(m, j->bus_client))
+ return -ENOMEM;
+
+ if (!dbus_connection_send(j->bus, m, NULL))
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
void bus_job_send_change_signal(Job *j) {
char *p = NULL;
DBusMessage *m = NULL;
LIST_REMOVE(Job, dbus_queue, j->manager->dbus_job_queue, j);
j->in_dbus_queue = false;
- if (set_isempty(j->manager->subscribed)) {
+ if (!bus_has_subscriber(j->manager) && !j->bus_client) {
j->sent_dbus_new_signal = true;
return;
}
goto oom;
}
- if (bus_broadcast(j->manager, m) < 0)
+ if (job_send_message(j, m) < 0)
goto oom;
free(p);
assert(j);
- if (set_isempty(j->manager->subscribed))
+ if (!bus_has_subscriber(j->manager) && !j->bus_client)
return;
if (!j->sent_dbus_new_signal)
DBUS_TYPE_INVALID))
goto oom;
- if (bus_broadcast(j->manager, m) < 0)
+ if (job_send_message(j, m) < 0)
goto oom;
free(p);
} else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Subscribe")) {
char *client;
+ Set *s;
+
+ if (!(s = BUS_CONNECTION_SUBSCRIBED(m, connection))) {
+ if (!(s = set_new(string_hash_func, string_compare_func)))
+ goto oom;
+
+ if (!(dbus_connection_set_data(connection, m->subscribed_data_slot, s, NULL))) {
+ set_free(s);
+ goto oom;
+ }
+ }
if (!(client = strdup(dbus_message_get_sender(message))))
goto oom;
- r = set_put(m->subscribed, client);
-
- if (r < 0)
+ if ((r = set_put(s, client)) < 0) {
+ free(client);
return bus_send_error_reply(m, connection, message, NULL, r);
+ }
if (!(reply = dbus_message_new_method_return(message)))
goto oom;
} else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Unsubscribe")) {
char *client;
- if (!(client = set_remove(m->subscribed, (char*) dbus_message_get_sender(message))))
+ if (!(client = set_remove(BUS_CONNECTION_SUBSCRIBED(m, connection), (char*) dbus_message_get_sender(message))))
return bus_send_error_reply(m, connection, message, NULL, -ENOENT);
free(client);
if ((r = manager_add_job(m, job_type, u, mode, true, &j)) < 0)
return bus_send_error_reply(m, connection, message, NULL, r);
+ if (!(j->bus_client = strdup(dbus_message_get_sender(message))))
+ goto oom;
+
+ j->bus = connection;
+
if (!(reply = dbus_message_new_method_return(message)))
goto oom;
DBUS_TYPE_OBJECT_PATH, &path,
DBUS_TYPE_INVALID))
goto oom;
+
}
free(path);
LIST_REMOVE(Meta, dbus_queue, u->meta.manager->dbus_unit_queue, &u->meta);
u->meta.in_dbus_queue = false;
- if (set_isempty(u->meta.manager->subscribed)) {
+ if (!bus_has_subscriber(u->meta.manager)) {
u->meta.sent_dbus_new_signal = true;
return;
}
assert(u);
- if (set_isempty(u->meta.manager->subscribed))
+ if (!bus_has_subscriber(u->meta.manager))
return;
if (!u->meta.sent_dbus_new_signal)
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 (set_remove(BUS_CONNECTION_SUBSCRIBED(m, connection), (char*) name))
+ log_debug("Subscription client vanished: %s (left: %u)", name, set_size(BUS_CONNECTION_SUBSCRIBED(m, connection)));
if (old_owner[0] == 0)
old_owner = NULL;
strnull(dbus_bus_get_unique_name(m->api_bus)));
dbus_free(id);
- if (!m->subscribed)
- if (!(m->subscribed = set_new(string_hash_func, string_compare_func))) {
- log_error("Not enough memory");
- r = -ENOMEM;
- goto fail;
- }
-
return 0;
fail:
return -ENOMEM;
}
+ if (m->subscribed_data_slot < 0)
+ if (!dbus_pending_call_allocate_data_slot(&m->subscribed_data_slot)) {
+ log_error("Not enough memory");
+ return -ENOMEM;
+ }
+
if ((r = bus_init_system(m)) < 0 ||
(r = bus_init_api(m)) < 0 ||
(r = bus_init_private(m)) < 0)
}
static void shutdown_connection(Manager *m, DBusConnection *c) {
+ Set *s;
+ Job *j;
+ Iterator i;
+
+ HASHMAP_FOREACH(j, m->jobs, i)
+ if (j->bus == c) {
+ free(j->bus_client);
+ j->bus_client = NULL;
+
+ j->bus = NULL;
+ }
+
set_remove(m->bus_connections, c);
set_remove(m->bus_connections_for_dispatch, c);
+ if ((s = BUS_CONNECTION_SUBSCRIBED(m, c))) {
+ char *t;
+
+ while ((t = set_steal_first(s)))
+ free(t);
+
+ set_free(s);
+ }
+
dbus_connection_set_dispatch_status_function(c, NULL, NULL, NULL);
dbus_connection_flush(c);
dbus_connection_close(c);
m->api_bus = NULL;
}
- if (m->subscribed) {
- char *c;
-
- while ((c = set_steal_first(m->subscribed)))
- free(c);
-
- set_free(m->subscribed);
- m->subscribed = NULL;
- }
if (m->queued_message) {
dbus_message_unref(m->queued_message);
if (m->name_data_slot >= 0)
dbus_pending_call_free_data_slot(&m->name_data_slot);
+
+ if (m->subscribed_data_slot >= 0)
+ dbus_pending_call_free_data_slot(&m->subscribed_data_slot);
}
static void query_pid_pending_cb(DBusPendingCall *pending, void *userdata) {
dbus_error_init(&error);
- assert_se(name = dbus_pending_call_get_data(pending, m->name_data_slot));
+ assert_se(name = BUS_PENDING_CALL_NAME(m, pending));
assert_se(reply = dbus_pending_call_steal_reply(pending));
switch (dbus_message_get_type(reply)) {
return 0;
}
+
+bool bus_has_subscriber(Manager *m) {
+ Iterator i;
+ DBusConnection *c;
+
+ assert(m);
+
+ SET_FOREACH(c, m->bus_connections_for_dispatch, i)
+ if (bus_connection_has_subscriber(m, c))
+ return true;
+
+ SET_FOREACH(c, m->bus_connections, i)
+ if (bus_connection_has_subscriber(m, c))
+ return true;
+
+ return false;
+}
+
+bool bus_connection_has_subscriber(Manager *m, DBusConnection *c) {
+ assert(m);
+ assert(c);
+
+ return !set_isempty(BUS_CONNECTION_SUBSCRIBED(m, c));
+}
int bus_parse_strv(DBusMessage *m, char ***_l);
+bool bus_has_subscriber(Manager *m);
+bool bus_connection_has_subscriber(Manager *m, DBusConnection *c);
+
+#define BUS_CONNECTION_SUBSCRIBED(m, c) dbus_connection_get_data((c), (m)->subscribed_data_slot)
+#define BUS_PENDING_CALL_NAME(m, p) dbus_pending_call_get_data((p), (m)->name_data_slot)
+
extern const char * const bus_interface_table[];
#endif
if (j->in_dbus_queue)
LIST_REMOVE(Job, dbus_queue, j->manager->dbus_job_queue, j);
+ free(j->bus_client);
free(j);
}
if (j->in_dbus_queue)
return;
- if (set_isempty(j->manager->subscribed)) {
- j->sent_dbus_new_signal = true;
- return;
- }
+ /* We don't check if anybody is subscribed here, since this
+ * job might just have been created and not yet assigned to a
+ * connection/client. */
LIST_PREPEND(Job, dbus_queue, j->manager->dbus_job_queue, j);
j->in_dbus_queue = true;
JobType type;
JobState state;
+ /* Note that this bus object is not ref counted here. */
+ DBusConnection *bus;
+ char *bus_client;
+
bool installed:1;
bool in_run_queue:1;
bool matters_to_anchor:1;
m->running_as = running_as;
m->confirm_spawn = confirm_spawn;
- m->name_data_slot = -1;
+ m->name_data_slot = m->subscribed_data_slot = -1;
m->exit_code = _MANAGER_EXIT_CODE_INVALID;
m->pin_cgroupfs_fd = -1;
DBusServer *private_bus;
Set *bus_connections, *bus_connections_for_dispatch;
- Set *subscribed;
DBusMessage *queued_message; /* This is used during reloading:
* before the reload we queue the
* reply message here, and
Hashmap *watch_bus; /* D-Bus names => Unit object n:1 */
int32_t name_data_slot;
+ int32_t subscribed_data_slot;
/* Data specific to the Automount subsystem */
int dev_autofs_fd;
static int enable_wait_for_jobs(DBusConnection *bus) {
DBusError error;
- DBusMessage *m = NULL, *reply = NULL;
- int r;
assert(bus);
dbus_error_init(&error);
-
dbus_bus_add_match(bus,
"type='signal',"
"sender='org.freedesktop.systemd1',"
if (dbus_error_is_set(&error)) {
log_error("Failed to add match: %s", error.message);
- r = -EIO;
- goto finish;
- }
-
- if (!(m = dbus_message_new_method_call(
- "org.freedesktop.systemd1",
- "/org/freedesktop/systemd1",
- "org.freedesktop.systemd1.Manager",
- "Subscribe"))) {
- log_error("Could not allocate message.");
- r = -ENOMEM;
- goto finish;
- }
-
- if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
- log_error("Failed to issue method call: %s", error.message);
- r = -EIO;
- goto finish;
+ dbus_error_free(&error);
+ return -EIO;
}
- r = 0;
-
-finish:
/* This is slightly dirty, since we don't undo the match registrations. */
-
- if (m)
- dbus_message_unref(m);
-
- if (reply)
- dbus_message_unref(reply);
-
- dbus_error_free(&error);
-
- return r;
+ return 0;
}
static int wait_for_jobs(DBusConnection *bus, Set *s) {
if (u->meta.load_state == UNIT_STUB || u->meta.in_dbus_queue)
return;
- if (set_isempty(u->meta.manager->subscribed)) {
+ /* Shortcut things if nobody cares */
+ if (!bus_has_subscriber(u->meta.manager)) {
u->meta.sent_dbus_new_signal = true;
return;
}