]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/core/dbus-job.c
TODO
[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
5273510e 103 job_finish_and_invalidate(j, JOB_CANCELED, true);
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 234
39a18c60 235 if (bus_has_subscriber(j->manager) || j->forgot_bus_clients) {
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,
06dab8e1 298 DBUS_TYPE_STRING, &j->unit->id,
c1e1601e
LP
299 DBUS_TYPE_INVALID))
300 goto oom;
301 }
302
97e6a119 303 return m;
c1e1601e
LP
304
305oom:
c1e1601e
LP
306 if (m)
307 dbus_message_unref(m);
97e6a119
MS
308 free(p);
309 return NULL;
c1e1601e
LP
310}
311
97e6a119 312static DBusMessage* new_removed_signal_message(Job *j) {
c1e1601e 313 DBusMessage *m = NULL;
97e6a119 314 char *p = NULL;
5d44db4a 315 const char *r;
c1e1601e 316
97e6a119
MS
317 p = job_dbus_path(j);
318 if (!p)
c1e1601e
LP
319 goto oom;
320
97e6a119
MS
321 m = dbus_message_new_signal("/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "JobRemoved");
322 if (!m)
c1e1601e
LP
323 goto oom;
324
5d44db4a
LP
325 r = job_result_to_string(j->result);
326
c1e1601e
LP
327 if (!dbus_message_append_args(m,
328 DBUS_TYPE_UINT32, &j->id,
329 DBUS_TYPE_OBJECT_PATH, &p,
06dab8e1 330 DBUS_TYPE_STRING, &j->unit->id,
5d44db4a 331 DBUS_TYPE_STRING, &r,
c1e1601e
LP
332 DBUS_TYPE_INVALID))
333 goto oom;
334
97e6a119 335 return m;
c1e1601e 336
97e6a119
MS
337oom:
338 if (m)
339 dbus_message_unref(m);
c1e1601e 340 free(p);
97e6a119
MS
341 return NULL;
342}
343
344void bus_job_send_change_signal(Job *j) {
345 assert(j);
346
347 if (j->in_dbus_queue) {
348 LIST_REMOVE(Job, dbus_queue, j->manager->dbus_job_queue, j);
349 j->in_dbus_queue = false;
350 }
351
39a18c60 352 if (!bus_has_subscriber(j->manager) && !j->bus_client_list && !j->forgot_bus_clients) {
97e6a119
MS
353 j->sent_dbus_new_signal = true;
354 return;
355 }
356
357 if (job_send_message(j, new_change_signal_message) < 0)
358 goto oom;
359
360 j->sent_dbus_new_signal = true;
c1e1601e
LP
361
362 return;
363
364oom:
97e6a119
MS
365 log_error("Failed to allocate job change signal.");
366}
c1e1601e 367
97e6a119
MS
368void bus_job_send_removed_signal(Job *j) {
369 assert(j);
c1e1601e 370
39a18c60 371 if (!bus_has_subscriber(j->manager) && !j->bus_client_list && !j->forgot_bus_clients)
97e6a119
MS
372 return;
373
374 if (!j->sent_dbus_new_signal)
375 bus_job_send_change_signal(j);
376
377 if (job_send_message(j, new_removed_signal_message) < 0)
378 goto oom;
379
380 return;
381
382oom:
c1e1601e
LP
383 log_error("Failed to allocate job remove signal.");
384}