Merging of jobs can result in more than one client being interested in a job.
.message_function = bus_job_message_handler
};
.message_function = bus_job_message_handler
};
-static int job_send_message(Job *j, DBusMessage *m) {
+static int job_send_message(Job *j, DBusMessage* (*new_message)(Job *j)) {
+ DBusMessage *m = NULL;
if (bus_has_subscriber(j->manager)) {
if (bus_has_subscriber(j->manager)) {
- if ((r = bus_broadcast(j->manager, m)) < 0)
+ m = new_message(j);
+ if (!m)
+ goto oom;
+ r = bus_broadcast(j->manager, m);
+ dbus_message_unref(m);
+ if (r < 0)
- } else if (j->bus_client) {
/* If nobody is subscribed, we just send the message
/* If nobody is subscribed, we just send the message
- * to the client which created the job */
+ * to the client(s) which created the job */
+ JobBusClient *cl;
+ assert(j->bus_client_list);
+ LIST_FOREACH(client, cl, j->bus_client_list) {
+ assert(cl->bus);
+
+ m = new_message(j);
+ if (!m)
+ goto oom;
+ if (!dbus_message_set_destination(m, cl->name))
+ goto oom;
- if (!dbus_message_set_destination(m, j->bus_client))
- return -ENOMEM;
+ if (!dbus_connection_send(cl->bus, m, NULL))
+ goto oom;
- if (!dbus_connection_send(j->bus, m, NULL))
- return -ENOMEM;
+ dbus_message_unref(m);
+ m = NULL;
+ }
+oom:
+ if (m)
+ dbus_message_unref(m);
+ return -ENOMEM;
-void bus_job_send_change_signal(Job *j) {
- char *p = NULL;
+static DBusMessage* new_change_signal_message(Job *j) {
- assert(j);
-
- if (j->in_dbus_queue) {
- LIST_REMOVE(Job, dbus_queue, j->manager->dbus_job_queue, j);
- j->in_dbus_queue = false;
- }
-
- if (!bus_has_subscriber(j->manager) && !j->bus_client) {
- j->sent_dbus_new_signal = true;
- return;
- }
-
- if (!(p = job_dbus_path(j)))
+ p = job_dbus_path(j);
+ if (!p)
goto oom;
if (j->sent_dbus_new_signal) {
/* Send a properties changed signal */
goto oom;
if (j->sent_dbus_new_signal) {
/* Send a properties changed signal */
-
- if (!(m = bus_properties_changed_new(p, "org.freedesktop.systemd1.Job", INVALIDATING_PROPERTIES)))
+ m = bus_properties_changed_new(p, "org.freedesktop.systemd1.Job", INVALIDATING_PROPERTIES);
+ if (!m)
goto oom;
} else {
/* Send a new signal */
goto oom;
} else {
/* Send a new signal */
- if (!(m = dbus_message_new_signal("/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "JobNew")))
+ m = dbus_message_new_signal("/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "JobNew");
+ if (!m)
goto oom;
if (!dbus_message_append_args(m,
goto oom;
if (!dbus_message_append_args(m,
- if (job_send_message(j, m) < 0)
- goto oom;
-
- free(p);
- dbus_message_unref(m);
-
- j->sent_dbus_new_signal = true;
-
- return;
if (m)
dbus_message_unref(m);
if (m)
dbus_message_unref(m);
-
- log_error("Failed to allocate job change signal.");
+ free(p);
+ return NULL;
-void bus_job_send_removed_signal(Job *j) {
- char *p = NULL;
+static DBusMessage* new_removed_signal_message(Job *j) {
- assert(j);
-
- if (!bus_has_subscriber(j->manager) && !j->bus_client)
- return;
-
- if (!j->sent_dbus_new_signal)
- bus_job_send_change_signal(j);
-
- if (!(p = job_dbus_path(j)))
+ p = job_dbus_path(j);
+ if (!p)
- if (!(m = dbus_message_new_signal("/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "JobRemoved")))
+ m = dbus_message_new_signal("/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "JobRemoved");
+ if (!m)
goto oom;
r = job_result_to_string(j->result);
goto oom;
r = job_result_to_string(j->result);
DBUS_TYPE_INVALID))
goto oom;
DBUS_TYPE_INVALID))
goto oom;
- if (job_send_message(j, m) < 0)
- goto oom;
+oom:
+ if (m)
+ dbus_message_unref(m);
+ return NULL;
+}
+
+void bus_job_send_change_signal(Job *j) {
+ assert(j);
+
+ if (j->in_dbus_queue) {
+ LIST_REMOVE(Job, dbus_queue, j->manager->dbus_job_queue, j);
+ j->in_dbus_queue = false;
+ }
+
+ if (!bus_has_subscriber(j->manager) && !j->bus_client_list) {
+ j->sent_dbus_new_signal = true;
+ return;
+ }
+
+ if (job_send_message(j, new_change_signal_message) < 0)
+ goto oom;
+
+ j->sent_dbus_new_signal = true;
+ log_error("Failed to allocate job change signal.");
+}
- if (m)
- dbus_message_unref(m);
+void bus_job_send_removed_signal(Job *j) {
+ assert(j);
+ if (!bus_has_subscriber(j->manager) && !j->bus_client_list)
+ return;
+
+ if (!j->sent_dbus_new_signal)
+ bus_job_send_change_signal(j);
+
+ if (job_send_message(j, new_removed_signal_message) < 0)
+ goto oom;
+
+ return;
+
+oom:
log_error("Failed to allocate job remove signal.");
}
log_error("Failed to allocate job remove signal.");
}
const char *name, *smode, *old_name = NULL;
JobMode mode;
Job *j;
const char *name, *smode, *old_name = NULL;
JobMode mode;
Job *j;
if ((r = manager_add_job(m, job_type, u, mode, true, &error, &j)) < 0)
return bus_send_error_reply(connection, message, &error, r);
if ((r = manager_add_job(m, job_type, u, mode, true, &error, &j)) < 0)
return bus_send_error_reply(connection, message, &error, r);
- if (!(j->bus_client = strdup(message_get_sender_with_fallback(message))))
+ cl = job_bus_client_new(connection, message_get_sender_with_fallback(message));
+ if (!cl)
+ LIST_PREPEND(JobBusClient, client, j->bus_client_list, cl);
if (!(reply = dbus_message_new_method_return(message)))
goto oom;
if (!(reply = dbus_message_new_method_return(message)))
goto oom;
- HASHMAP_FOREACH(j, m->jobs, i)
- if (j->bus == c) {
- free(j->bus_client);
- j->bus_client = NULL;
-
- j->bus = NULL;
+ HASHMAP_FOREACH(j, m->jobs, i) {
+ JobBusClient *cl, *nextcl;
+ LIST_FOREACH_SAFE(client, cl, nextcl, j->bus_client_list) {
+ if (cl->bus == c) {
+ LIST_REMOVE(JobBusClient, client, j->bus_client_list, cl);
+ free(cl);
+ }
set_remove(m->bus_connections, c);
set_remove(m->bus_connections_for_dispatch, c);
set_remove(m->bus_connections, c);
set_remove(m->bus_connections_for_dispatch, c);
#include "log.h"
#include "dbus-job.h"
#include "log.h"
#include "dbus-job.h"
+JobBusClient* job_bus_client_new(DBusConnection *connection, const char *name) {
+ JobBusClient *cl;
+ size_t name_len;
+
+ name_len = strlen(name);
+ cl = malloc0(sizeof(JobBusClient) + name_len + 1);
+ if (!cl)
+ return NULL;
+
+ cl->bus = connection;
+ memcpy(cl->name, name, name_len + 1);
+ return cl;
+}
+
Job* job_new(Unit *unit, JobType type) {
Job *j;
Job* job_new(Unit *unit, JobType type) {
Job *j;
}
void job_free(Job *j) {
}
void job_free(Job *j) {
assert(j);
assert(!j->installed);
assert(!j->transaction_prev);
assert(j);
assert(!j->installed);
assert(!j->transaction_prev);
close_nointr_nofail(j->timer_watch.fd);
}
close_nointr_nofail(j->timer_watch.fd);
}
+ while ((cl = j->bus_client_list)) {
+ LIST_REMOVE(JobBusClient, client, j->bus_client_list, cl);
+ free(cl);
+ }
typedef struct Job Job;
typedef struct JobDependency JobDependency;
typedef struct Job Job;
typedef struct JobDependency JobDependency;
+typedef struct JobBusClient JobBusClient;
typedef enum JobType JobType;
typedef enum JobState JobState;
typedef enum JobMode JobMode;
typedef enum JobType JobType;
typedef enum JobState JobState;
typedef enum JobMode JobMode;
+struct JobBusClient {
+ LIST_FIELDS(JobBusClient, client);
+ /* Note that this bus object is not ref counted here. */
+ DBusConnection *bus;
+ char name[0];
+};
+
struct Job {
Manager *manager;
Unit *unit;
struct Job {
Manager *manager;
Unit *unit;
- /* Note that this bus object is not ref counted here. */
- DBusConnection *bus;
- char *bus_client;
+ /* There can be more than one client, because of job merging. */
+ LIST_HEAD(JobBusClient, bus_client_list);
+JobBusClient* job_bus_client_new(DBusConnection *connection, const char *name);
+
Job* job_new(Unit *unit, JobType type);
void job_free(Job *job);
Job* job_install(Job *j);
Job* job_new(Unit *unit, JobType type);
void job_free(Job *job);
Job* job_install(Job *j);