]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/core/dbus-job.c
hwdb: update
[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"
cad45ba1 28#include "selinux-access.h"
ea430986 29
4288f619
LP
30#define BUS_JOB_INTERFACE \
31 " <interface name=\"org.freedesktop.systemd1.Job\">\n" \
32 " <method name=\"Cancel\"/>\n" \
4288f619
LP
33 " <property name=\"Id\" type=\"u\" access=\"read\"/>\n" \
34 " <property name=\"Unit\" type=\"(so)\" access=\"read\"/>\n" \
35 " <property name=\"JobType\" type=\"s\" access=\"read\"/>\n" \
36 " <property name=\"State\" type=\"s\" access=\"read\"/>\n" \
37 " </interface>\n"
38
39#define INTROSPECTION \
40 DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \
41 "<node>\n" \
42 BUS_JOB_INTERFACE \
43 BUS_PROPERTIES_INTERFACE \
c4e2ceae 44 BUS_PEER_INTERFACE \
4288f619
LP
45 BUS_INTROSPECTABLE_INTERFACE \
46 "</node>\n"
47
9a60da28 48const char bus_job_interface[] _introspect_("Job") = BUS_JOB_INTERFACE;
ea430986 49
05feefe0
LP
50#define INTERFACES_LIST \
51 BUS_GENERIC_INTERFACES_LIST \
52 "org.freedesktop.systemd1.Job\0"
53
c4e2ceae 54#define INVALIDATING_PROPERTIES \
34df5a34 55 "State\0"
c4e2ceae 56
4139c1b2
LP
57static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_job_append_state, job_state, JobState);
58static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_job_append_type, job_type, JobType);
86fbf370 59
bfebab7f 60static int bus_job_append_unit(DBusMessageIter *i, const char *property, void *data) {
86fbf370
LP
61 Job *j = data;
62 DBusMessageIter sub;
63 char *p;
86fbf370 64
86fbf370
LP
65 assert(i);
66 assert(property);
67 assert(j);
68
69 if (!dbus_message_iter_open_container(i, DBUS_TYPE_STRUCT, NULL, &sub))
70 return -ENOMEM;
71
cad45ba1
LP
72 p = unit_dbus_path(j->unit);
73 if (!p)
86fbf370
LP
74 return -ENOMEM;
75
ac155bb8 76 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &j->unit->id) ||
86fbf370
LP
77 !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &p)) {
78 free(p);
79 return -ENOMEM;
80 }
81
82 free(p);
83
84 if (!dbus_message_iter_close_container(i, &sub))
85 return -ENOMEM;
86
87 return 0;
88}
89
d200735e
MS
90static const BusProperty bus_job_properties[] = {
91 { "Id", bus_property_append_uint32, "u", offsetof(Job, id) },
92 { "State", bus_job_append_state, "s", offsetof(Job, state) },
93 { "JobType", bus_job_append_type, "s", offsetof(Job, type) },
94 { "Unit", bus_job_append_unit, "(so)", 0 },
95 { NULL, }
96};
86fbf370 97
d200735e 98static DBusHandlerResult bus_job_message_dispatch(Job *j, DBusConnection *connection, DBusMessage *message) {
cad45ba1 99 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
b548631a
LP
100
101 if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Job", "Cancel")) {
b548631a 102
cad45ba1 103 SELINUX_UNIT_ACCESS_CHECK(j->unit, connection, message, "stop");
5dd9014f 104 job_finish_and_invalidate(j, JOB_CANCELED, true);
cad45ba1
LP
105
106 reply = dbus_message_new_method_return(message);
107 if (!reply)
108 return DBUS_HANDLER_RESULT_NEED_MEMORY;
d200735e
MS
109 } else {
110 const BusBoundProperties bps[] = {
111 { "org.freedesktop.systemd1.Job", bus_job_properties, j },
112 { NULL, }
113 };
b548631a 114
cad45ba1 115 SELINUX_UNIT_ACCESS_CHECK(j->unit, connection, message, "status");
cad45ba1 116 return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, bps);
b548631a
LP
117 }
118
c6a818c8 119 if (!bus_maybe_send_reply(connection, message, reply))
cad45ba1 120 return DBUS_HANDLER_RESULT_NEED_MEMORY;
b548631a 121
cad45ba1 122 return DBUS_HANDLER_RESULT_HANDLED;
86fbf370
LP
123}
124
5e8d1c9a 125static DBusHandlerResult bus_job_message_handler(DBusConnection *connection, DBusMessage *message, void *data) {
ea430986 126 Manager *m = data;
86fbf370
LP
127 Job *j;
128 int r;
cad45ba1 129 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
ea430986
LP
130
131 assert(connection);
132 assert(message);
133 assert(m);
134
2cccbca4
LP
135 if (streq(dbus_message_get_path(message), "/org/freedesktop/systemd1/job")) {
136 /* Be nice to gdbus and return introspection data for our mid-level paths */
137
138 if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect")) {
139 char *introspection = NULL;
140 FILE *f;
141 Iterator i;
142 size_t size;
143
ffc227c9 144 SELINUX_ACCESS_CHECK(connection, message, "status");
cad45ba1
LP
145
146 reply = dbus_message_new_method_return(message);
147 if (!reply)
2cccbca4
LP
148 goto oom;
149
150 /* We roll our own introspection code here, instead of
151 * relying on bus_default_message_handler() because we
152 * need to generate our introspection string
153 * dynamically. */
154
cad45ba1
LP
155 f = open_memstream(&introspection, &size);
156 if (!f)
2cccbca4
LP
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
c6a818c8 188 if (!bus_maybe_send_reply(connection, message, reply))
2cccbca4
LP
189 goto oom;
190
2cccbca4
LP
191 return DBUS_HANDLER_RESULT_HANDLED;
192 }
193
194 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
195 }
196
cad45ba1
LP
197 r = manager_get_job_from_dbus_path(m, dbus_message_get_path(message), &j);
198 if (r == -ENOMEM)
199 goto oom;
200 if (r == -ENOENT) {
201 DBusError e;
86fbf370 202
cad45ba1
LP
203 dbus_error_init(&e);
204 dbus_set_error_const(&e, DBUS_ERROR_UNKNOWN_OBJECT, "Unknown job");
205 return bus_send_error_reply(connection, message, &e, r);
86fbf370 206 }
cad45ba1
LP
207 if (r < 0)
208 return bus_send_error_reply(connection, message, NULL, r);
86fbf370 209
5e8d1c9a 210 return bus_job_message_dispatch(j, connection, message);
2cccbca4
LP
211
212oom:
2cccbca4 213 return DBUS_HANDLER_RESULT_NEED_MEMORY;
ea430986
LP
214}
215
216const DBusObjectPathVTable bus_job_vtable = {
217 .message_function = bus_job_message_handler
218};
c1e1601e 219
97e6a119 220static int job_send_message(Job *j, DBusMessage* (*new_message)(Job *j)) {
84286536 221 _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
a567261a
LP
222 int r;
223
224 assert(j);
97e6a119 225 assert(new_message);
a567261a 226
39a18c60 227 if (bus_has_subscriber(j->manager) || j->forgot_bus_clients) {
97e6a119
MS
228 m = new_message(j);
229 if (!m)
84286536
LP
230 return -ENOMEM;
231
97e6a119 232 r = bus_broadcast(j->manager, m);
97e6a119 233 if (r < 0)
a567261a
LP
234 return r;
235
97e6a119 236 } else {
a567261a 237 /* If nobody is subscribed, we just send the message
97e6a119
MS
238 * to the client(s) which created the job */
239 JobBusClient *cl;
240 assert(j->bus_client_list);
84286536 241
97e6a119
MS
242 LIST_FOREACH(client, cl, j->bus_client_list) {
243 assert(cl->bus);
244
245 m = new_message(j);
246 if (!m)
84286536 247 return -ENOMEM;
a567261a 248
97e6a119 249 if (!dbus_message_set_destination(m, cl->name))
84286536 250 return -ENOMEM;
a567261a 251
97e6a119 252 if (!dbus_connection_send(cl->bus, m, NULL))
84286536 253 return -ENOMEM;
a567261a 254
97e6a119
MS
255 dbus_message_unref(m);
256 m = NULL;
257 }
a567261a
LP
258 }
259
260 return 0;
261}
262
97e6a119 263static DBusMessage* new_change_signal_message(Job *j) {
1508e858
LP
264 _cleanup_free_ char *p = NULL;
265 DBusMessage *m;
c1e1601e 266
97e6a119
MS
267 p = job_dbus_path(j);
268 if (!p)
1508e858 269 return NULL;
c1e1601e
LP
270
271 if (j->sent_dbus_new_signal) {
c4e2ceae 272 /* Send a properties changed signal */
97e6a119
MS
273 m = bus_properties_changed_new(p, "org.freedesktop.systemd1.Job", INVALIDATING_PROPERTIES);
274 if (!m)
1508e858 275 return NULL;
c4e2ceae 276
c1e1601e
LP
277 } else {
278 /* Send a new signal */
279
97e6a119
MS
280 m = dbus_message_new_signal("/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "JobNew");
281 if (!m)
1508e858 282 return NULL;
c1e1601e
LP
283
284 if (!dbus_message_append_args(m,
285 DBUS_TYPE_UINT32, &j->id,
286 DBUS_TYPE_OBJECT_PATH, &p,
06dab8e1 287 DBUS_TYPE_STRING, &j->unit->id,
1508e858
LP
288 DBUS_TYPE_INVALID)) {
289 dbus_message_unref(m);
290 return NULL;
291 }
c1e1601e
LP
292 }
293
97e6a119 294 return m;
c1e1601e
LP
295}
296
97e6a119 297static DBusMessage* new_removed_signal_message(Job *j) {
1508e858
LP
298 _cleanup_free_ char *p = NULL;
299 DBusMessage *m;
5d44db4a 300 const char *r;
c1e1601e 301
97e6a119
MS
302 p = job_dbus_path(j);
303 if (!p)
1508e858 304 return NULL;
c1e1601e 305
97e6a119
MS
306 m = dbus_message_new_signal("/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "JobRemoved");
307 if (!m)
1508e858 308 return NULL;
c1e1601e 309
5d44db4a
LP
310 r = job_result_to_string(j->result);
311
c1e1601e
LP
312 if (!dbus_message_append_args(m,
313 DBUS_TYPE_UINT32, &j->id,
314 DBUS_TYPE_OBJECT_PATH, &p,
06dab8e1 315 DBUS_TYPE_STRING, &j->unit->id,
5d44db4a 316 DBUS_TYPE_STRING, &r,
1508e858
LP
317 DBUS_TYPE_INVALID)) {
318 dbus_message_unref(m);
319 return NULL;
320 }
c1e1601e 321
97e6a119 322 return m;
97e6a119
MS
323}
324
325void bus_job_send_change_signal(Job *j) {
326 assert(j);
327
328 if (j->in_dbus_queue) {
329 LIST_REMOVE(Job, dbus_queue, j->manager->dbus_job_queue, j);
330 j->in_dbus_queue = false;
331 }
332
39a18c60 333 if (!bus_has_subscriber(j->manager) && !j->bus_client_list && !j->forgot_bus_clients) {
97e6a119
MS
334 j->sent_dbus_new_signal = true;
335 return;
336 }
337
338 if (job_send_message(j, new_change_signal_message) < 0)
339 goto oom;
340
341 j->sent_dbus_new_signal = true;
c1e1601e
LP
342
343 return;
344
345oom:
97e6a119
MS
346 log_error("Failed to allocate job change signal.");
347}
c1e1601e 348
97e6a119
MS
349void bus_job_send_removed_signal(Job *j) {
350 assert(j);
c1e1601e 351
39a18c60 352 if (!bus_has_subscriber(j->manager) && !j->bus_client_list && !j->forgot_bus_clients)
97e6a119
MS
353 return;
354
355 if (!j->sent_dbus_new_signal)
356 bus_job_send_change_signal(j);
357
358 if (job_send_message(j, new_removed_signal_message) < 0)
359 goto oom;
360
361 return;
362
363oom:
c1e1601e
LP
364 log_error("Failed to allocate job remove signal.");
365}