]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/core/dbus-job.c
dbus-job: allow multiple bus clients
[thirdparty/systemd.git] / src / core / dbus-job.c
CommitLineData
d6c9574f 1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
ea430986 2
a7334b09
LP
3/***
4 This file is part of systemd.
5
6 Copyright 2010 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
5430f7f2
LP
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
a7334b09
LP
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
5430f7f2 16 Lesser General Public License for more details.
a7334b09 17
5430f7f2 18 You should have received a copy of the GNU Lesser General Public License
a7334b09
LP
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
86fbf370
LP
22#include <errno.h>
23
ea430986
LP
24#include "dbus.h"
25#include "log.h"
4139c1b2 26#include "dbus-job.h"
bfebab7f 27#include "dbus-common.h"
ea430986 28
4288f619
LP
29#define BUS_JOB_INTERFACE \
30 " <interface name=\"org.freedesktop.systemd1.Job\">\n" \
31 " <method name=\"Cancel\"/>\n" \
4288f619
LP
32 " <property name=\"Id\" type=\"u\" access=\"read\"/>\n" \
33 " <property name=\"Unit\" type=\"(so)\" access=\"read\"/>\n" \
34 " <property name=\"JobType\" type=\"s\" access=\"read\"/>\n" \
35 " <property name=\"State\" type=\"s\" access=\"read\"/>\n" \
36 " </interface>\n"
37
38#define INTROSPECTION \
39 DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \
40 "<node>\n" \
41 BUS_JOB_INTERFACE \
42 BUS_PROPERTIES_INTERFACE \
c4e2ceae 43 BUS_PEER_INTERFACE \
4288f619
LP
44 BUS_INTROSPECTABLE_INTERFACE \
45 "</node>\n"
46
9a60da28 47const char bus_job_interface[] _introspect_("Job") = BUS_JOB_INTERFACE;
ea430986 48
05feefe0
LP
49#define INTERFACES_LIST \
50 BUS_GENERIC_INTERFACES_LIST \
51 "org.freedesktop.systemd1.Job\0"
52
c4e2ceae 53#define INVALIDATING_PROPERTIES \
34df5a34 54 "State\0"
c4e2ceae 55
4139c1b2
LP
56static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_job_append_state, job_state, JobState);
57static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_job_append_type, job_type, JobType);
86fbf370 58
bfebab7f 59static int bus_job_append_unit(DBusMessageIter *i, const char *property, void *data) {
86fbf370
LP
60 Job *j = data;
61 DBusMessageIter sub;
62 char *p;
86fbf370 63
86fbf370
LP
64 assert(i);
65 assert(property);
66 assert(j);
67
68 if (!dbus_message_iter_open_container(i, DBUS_TYPE_STRUCT, NULL, &sub))
69 return -ENOMEM;
70
71 if (!(p = unit_dbus_path(j->unit)))
72 return -ENOMEM;
73
ac155bb8 74 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &j->unit->id) ||
86fbf370
LP
75 !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &p)) {
76 free(p);
77 return -ENOMEM;
78 }
79
80 free(p);
81
82 if (!dbus_message_iter_close_container(i, &sub))
83 return -ENOMEM;
84
85 return 0;
86}
87
d200735e
MS
88static const BusProperty bus_job_properties[] = {
89 { "Id", bus_property_append_uint32, "u", offsetof(Job, id) },
90 { "State", bus_job_append_state, "s", offsetof(Job, state) },
91 { "JobType", bus_job_append_type, "s", offsetof(Job, type) },
92 { "Unit", bus_job_append_unit, "(so)", 0 },
93 { NULL, }
94};
86fbf370 95
d200735e 96static DBusHandlerResult bus_job_message_dispatch(Job *j, DBusConnection *connection, DBusMessage *message) {
b548631a 97 DBusMessage *reply = NULL;
b548631a
LP
98
99 if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Job", "Cancel")) {
100 if (!(reply = dbus_message_new_method_return(message)))
101 goto oom;
102
c77bc38d 103 job_finish_and_invalidate(j, JOB_CANCELED);
b548631a 104
d200735e
MS
105 } else {
106 const BusBoundProperties bps[] = {
107 { "org.freedesktop.systemd1.Job", bus_job_properties, j },
108 { NULL, }
109 };
110 return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, bps);
111 }
b548631a
LP
112
113 if (reply) {
5e8d1c9a 114 if (!dbus_connection_send(connection, reply, NULL))
b548631a
LP
115 goto oom;
116
117 dbus_message_unref(reply);
118 }
119
120 return DBUS_HANDLER_RESULT_HANDLED;
121
122oom:
123 if (reply)
124 dbus_message_unref(reply);
125
126 return DBUS_HANDLER_RESULT_NEED_MEMORY;
86fbf370
LP
127}
128
5e8d1c9a 129static DBusHandlerResult bus_job_message_handler(DBusConnection *connection, DBusMessage *message, void *data) {
ea430986 130 Manager *m = data;
86fbf370
LP
131 Job *j;
132 int r;
2cccbca4 133 DBusMessage *reply;
ea430986
LP
134
135 assert(connection);
136 assert(message);
137 assert(m);
138
2cccbca4
LP
139 if (streq(dbus_message_get_path(message), "/org/freedesktop/systemd1/job")) {
140 /* Be nice to gdbus and return introspection data for our mid-level paths */
141
142 if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect")) {
143 char *introspection = NULL;
144 FILE *f;
145 Iterator i;
146 size_t size;
147
148 if (!(reply = dbus_message_new_method_return(message)))
149 goto oom;
150
151 /* We roll our own introspection code here, instead of
152 * relying on bus_default_message_handler() because we
153 * need to generate our introspection string
154 * dynamically. */
155
156 if (!(f = open_memstream(&introspection, &size)))
157 goto oom;
158
159 fputs(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
160 "<node>\n", f);
161
162 fputs(BUS_INTROSPECTABLE_INTERFACE, f);
163 fputs(BUS_PEER_INTERFACE, f);
164
165 HASHMAP_FOREACH(j, m->jobs, i)
166 fprintf(f, "<node name=\"job/%lu\"/>", (unsigned long) j->id);
167
168 fputs("</node>\n", f);
169
170 if (ferror(f)) {
171 fclose(f);
172 free(introspection);
173 goto oom;
174 }
175
176 fclose(f);
177
178 if (!introspection)
179 goto oom;
180
181 if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &introspection, DBUS_TYPE_INVALID)) {
182 free(introspection);
183 goto oom;
184 }
185
186 free(introspection);
187
188 if (!dbus_connection_send(connection, reply, NULL))
189 goto oom;
190
191 dbus_message_unref(reply);
192
193 return DBUS_HANDLER_RESULT_HANDLED;
194 }
195
196 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
197 }
198
86fbf370
LP
199 if ((r = manager_get_job_from_dbus_path(m, dbus_message_get_path(message), &j)) < 0) {
200
201 if (r == -ENOMEM)
202 return DBUS_HANDLER_RESULT_NEED_MEMORY;
203
08672cb5
LP
204 if (r == -ENOENT) {
205 DBusError e;
b8a021c9
AB
206
207 dbus_error_init(&e);
08672cb5 208 dbus_set_error_const(&e, DBUS_ERROR_UNKNOWN_OBJECT, "Unknown job");
bfebab7f 209 return bus_send_error_reply(connection, message, &e, r);
08672cb5 210 }
86fbf370 211
bfebab7f 212 return bus_send_error_reply(connection, message, NULL, r);
86fbf370
LP
213 }
214
5e8d1c9a 215 return bus_job_message_dispatch(j, connection, message);
2cccbca4
LP
216
217oom:
218 if (reply)
219 dbus_message_unref(reply);
220
221 return DBUS_HANDLER_RESULT_NEED_MEMORY;
ea430986
LP
222}
223
224const DBusObjectPathVTable bus_job_vtable = {
225 .message_function = bus_job_message_handler
226};
c1e1601e 227
97e6a119
MS
228static int job_send_message(Job *j, DBusMessage* (*new_message)(Job *j)) {
229 DBusMessage *m = NULL;
a567261a
LP
230 int r;
231
232 assert(j);
97e6a119 233 assert(new_message);
a567261a
LP
234
235 if (bus_has_subscriber(j->manager)) {
97e6a119
MS
236 m = new_message(j);
237 if (!m)
238 goto oom;
239 r = bus_broadcast(j->manager, m);
240 dbus_message_unref(m);
241 if (r < 0)
a567261a
LP
242 return r;
243
97e6a119 244 } else {
a567261a 245 /* If nobody is subscribed, we just send the message
97e6a119
MS
246 * to the client(s) which created the job */
247 JobBusClient *cl;
248 assert(j->bus_client_list);
249 LIST_FOREACH(client, cl, j->bus_client_list) {
250 assert(cl->bus);
251
252 m = new_message(j);
253 if (!m)
254 goto oom;
a567261a 255
97e6a119
MS
256 if (!dbus_message_set_destination(m, cl->name))
257 goto oom;
a567261a 258
97e6a119
MS
259 if (!dbus_connection_send(cl->bus, m, NULL))
260 goto oom;
a567261a 261
97e6a119
MS
262 dbus_message_unref(m);
263 m = NULL;
264 }
a567261a
LP
265 }
266
267 return 0;
97e6a119
MS
268oom:
269 if (m)
270 dbus_message_unref(m);
271 return -ENOMEM;
a567261a
LP
272}
273
97e6a119 274static DBusMessage* new_change_signal_message(Job *j) {
c1e1601e 275 DBusMessage *m = NULL;
97e6a119 276 char *p = NULL;
c1e1601e 277
97e6a119
MS
278 p = job_dbus_path(j);
279 if (!p)
c1e1601e
LP
280 goto oom;
281
282 if (j->sent_dbus_new_signal) {
c4e2ceae 283 /* Send a properties changed signal */
97e6a119
MS
284 m = bus_properties_changed_new(p, "org.freedesktop.systemd1.Job", INVALIDATING_PROPERTIES);
285 if (!m)
c1e1601e 286 goto oom;
c4e2ceae 287
c1e1601e
LP
288 } else {
289 /* Send a new signal */
290
97e6a119
MS
291 m = dbus_message_new_signal("/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "JobNew");
292 if (!m)
c1e1601e
LP
293 goto oom;
294
295 if (!dbus_message_append_args(m,
296 DBUS_TYPE_UINT32, &j->id,
297 DBUS_TYPE_OBJECT_PATH, &p,
298 DBUS_TYPE_INVALID))
299 goto oom;
300 }
301
97e6a119 302 return m;
c1e1601e
LP
303
304oom:
c1e1601e
LP
305 if (m)
306 dbus_message_unref(m);
97e6a119
MS
307 free(p);
308 return NULL;
c1e1601e
LP
309}
310
97e6a119 311static DBusMessage* new_removed_signal_message(Job *j) {
c1e1601e 312 DBusMessage *m = NULL;
97e6a119 313 char *p = NULL;
5d44db4a 314 const char *r;
c1e1601e 315
97e6a119
MS
316 p = job_dbus_path(j);
317 if (!p)
c1e1601e
LP
318 goto oom;
319
97e6a119
MS
320 m = dbus_message_new_signal("/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "JobRemoved");
321 if (!m)
c1e1601e
LP
322 goto oom;
323
5d44db4a
LP
324 r = job_result_to_string(j->result);
325
c1e1601e
LP
326 if (!dbus_message_append_args(m,
327 DBUS_TYPE_UINT32, &j->id,
328 DBUS_TYPE_OBJECT_PATH, &p,
5d44db4a 329 DBUS_TYPE_STRING, &r,
c1e1601e
LP
330 DBUS_TYPE_INVALID))
331 goto oom;
332
97e6a119 333 return m;
c1e1601e 334
97e6a119
MS
335oom:
336 if (m)
337 dbus_message_unref(m);
c1e1601e 338 free(p);
97e6a119
MS
339 return NULL;
340}
341
342void bus_job_send_change_signal(Job *j) {
343 assert(j);
344
345 if (j->in_dbus_queue) {
346 LIST_REMOVE(Job, dbus_queue, j->manager->dbus_job_queue, j);
347 j->in_dbus_queue = false;
348 }
349
350 if (!bus_has_subscriber(j->manager) && !j->bus_client_list) {
351 j->sent_dbus_new_signal = true;
352 return;
353 }
354
355 if (job_send_message(j, new_change_signal_message) < 0)
356 goto oom;
357
358 j->sent_dbus_new_signal = true;
c1e1601e
LP
359
360 return;
361
362oom:
97e6a119
MS
363 log_error("Failed to allocate job change signal.");
364}
c1e1601e 365
97e6a119
MS
366void bus_job_send_removed_signal(Job *j) {
367 assert(j);
c1e1601e 368
97e6a119
MS
369 if (!bus_has_subscriber(j->manager) && !j->bus_client_list)
370 return;
371
372 if (!j->sent_dbus_new_signal)
373 bus_job_send_change_signal(j);
374
375 if (job_send_message(j, new_removed_signal_message) < 0)
376 goto oom;
377
378 return;
379
380oom:
c1e1601e
LP
381 log_error("Failed to allocate job remove signal.");
382}