]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/core/dbus-unit.c
logind: when creating the scope job fails, return this immediately to the client...
[thirdparty/systemd.git] / src / core / dbus-unit.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
ea430986
LP
22#include <errno.h>
23
24#include "dbus.h"
25#include "log.h"
4139c1b2 26#include "dbus-unit.h"
398ef8ba 27#include "bus-errors.h"
bfebab7f 28#include "dbus-common.h"
e2417e41 29#include "selinux-access.h"
246aa6dd
LP
30#include "cgroup-util.h"
31#include "strv.h"
32#include "path-util.h"
a5c32cff 33#include "fileio.h"
ea430986 34
9a60da28 35const char bus_unit_interface[] _introspect_("Unit") = BUS_UNIT_INTERFACE;
4288f619 36
c4e2ceae
LP
37#define INVALIDATING_PROPERTIES \
38 "LoadState\0" \
39 "ActiveState\0" \
40 "SubState\0" \
41 "InactiveExitTimestamp\0" \
42 "ActiveEnterTimestamp\0" \
43 "ActiveExitTimestamp\0" \
44 "InactiveEnterTimestamp\0" \
45 "Job\0" \
34df5a34 46 "NeedDaemonReload\0"
c4e2ceae 47
e2110e5d 48static int bus_unit_append_names(DBusMessageIter *i, const char *property, void *data) {
4139c1b2
LP
49 char *t;
50 Iterator j;
51 DBusMessageIter sub;
52 Unit *u = data;
53
54 if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "s", &sub))
55 return -ENOMEM;
56
ac155bb8 57 SET_FOREACH(t, u->names, j)
4139c1b2
LP
58 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &t))
59 return -ENOMEM;
60
61 if (!dbus_message_iter_close_container(i, &sub))
62 return -ENOMEM;
63
64 return 0;
65}
66
e2110e5d 67static int bus_unit_append_following(DBusMessageIter *i, const char *property, void *data) {
a7f241db 68 Unit *u = data, *f;
8fe914ec
LP
69 const char *d;
70
8fe914ec
LP
71 assert(i);
72 assert(property);
73 assert(u);
74
a7f241db 75 f = unit_following(u);
ac155bb8 76 d = f ? f->id : "";
8fe914ec
LP
77
78 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &d))
79 return -ENOMEM;
80
81 return 0;
82}
83
4ec9a8a4
LP
84static int bus_unit_append_slice(DBusMessageIter *i, const char *property, void *data) {
85 Unit *u = data;
86 const char *d;
87
88 assert(i);
89 assert(property);
90 assert(u);
91
9444b1f2 92 d = strempty(unit_slice_name(u));
4ec9a8a4
LP
93
94 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &d))
95 return -ENOMEM;
96
97 return 0;
98}
99
e2110e5d 100static int bus_unit_append_dependencies(DBusMessageIter *i, const char *property, void *data) {
5301be81
LP
101 Unit *u;
102 Iterator j;
103 DBusMessageIter sub;
104 Set *s = data;
105
106 if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "s", &sub))
107 return -ENOMEM;
108
109 SET_FOREACH(u, s, j)
ac155bb8 110 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &u->id))
5301be81
LP
111 return -ENOMEM;
112
113 if (!dbus_message_iter_close_container(i, &sub))
114 return -ENOMEM;
115
116 return 0;
117}
118
e2110e5d 119static int bus_unit_append_description(DBusMessageIter *i, const char *property, void *data) {
ea430986
LP
120 Unit *u = data;
121 const char *d;
122
ea430986
LP
123 assert(i);
124 assert(property);
125 assert(u);
126
127 d = unit_description(u);
128
129 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &d))
130 return -ENOMEM;
131
132 return 0;
133}
134
e2110e5d 135static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_unit_append_load_state, unit_load_state, UnitLoadState);
ea430986 136
e2110e5d 137static int bus_unit_append_active_state(DBusMessageIter *i, const char *property, void *data) {
ea430986
LP
138 Unit *u = data;
139 const char *state;
140
ea430986
LP
141 assert(i);
142 assert(property);
143 assert(u);
144
145 state = unit_active_state_to_string(unit_active_state(u));
146
147 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &state))
148 return -ENOMEM;
149
150 return 0;
151}
152
e2110e5d 153static int bus_unit_append_sub_state(DBusMessageIter *i, const char *property, void *data) {
10a94420
LP
154 Unit *u = data;
155 const char *state;
156
10a94420
LP
157 assert(i);
158 assert(property);
159 assert(u);
160
161 state = unit_sub_state_to_string(u);
162
163 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &state))
164 return -ENOMEM;
165
166 return 0;
167}
168
e2110e5d 169static int bus_unit_append_file_state(DBusMessageIter *i, const char *property, void *data) {
a4375746
LP
170 Unit *u = data;
171 const char *state;
172
173 assert(i);
174 assert(property);
175 assert(u);
176
177 state = strempty(unit_file_state_to_string(unit_get_unit_file_state(u)));
178
179 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &state))
180 return -ENOMEM;
181
182 return 0;
183}
184
e2110e5d 185static int bus_unit_append_can_start(DBusMessageIter *i, const char *property, void *data) {
38131695
LP
186 Unit *u = data;
187 dbus_bool_t b;
188
38131695
LP
189 assert(i);
190 assert(property);
191 assert(u);
192
064f51fa 193 b = unit_can_start(u) &&
ac155bb8 194 !u->refuse_manual_start;
b5e9dba8
LP
195
196 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
197 return -ENOMEM;
198
199 return 0;
200}
201
e2110e5d 202static int bus_unit_append_can_stop(DBusMessageIter *i, const char *property, void *data) {
b5e9dba8
LP
203 Unit *u = data;
204 dbus_bool_t b;
205
b5e9dba8
LP
206 assert(i);
207 assert(property);
208 assert(u);
209
210 /* On the lower levels we assume that every unit we can start
211 * we can also stop */
212
213 b = unit_can_start(u) &&
ac155bb8 214 !u->refuse_manual_stop;
38131695
LP
215
216 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
217 return -ENOMEM;
218
219 return 0;
220}
221
e2110e5d 222static int bus_unit_append_can_reload(DBusMessageIter *i, const char *property, void *data) {
38131695
LP
223 Unit *u = data;
224 dbus_bool_t b;
225
38131695
LP
226 assert(i);
227 assert(property);
228 assert(u);
229
4139c1b2 230 b = unit_can_reload(u);
38131695
LP
231
232 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
233 return -ENOMEM;
234
235 return 0;
236}
237
e2110e5d 238static int bus_unit_append_can_isolate(DBusMessageIter *i, const char *property, void *data) {
2528a7a6
LP
239 Unit *u = data;
240 dbus_bool_t b;
241
2528a7a6
LP
242 assert(i);
243 assert(property);
244 assert(u);
245
246 b = unit_can_isolate(u) &&
ac155bb8 247 !u->refuse_manual_start;
2528a7a6
LP
248
249 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
250 return -ENOMEM;
251
252 return 0;
253}
254
e2110e5d 255static int bus_unit_append_job(DBusMessageIter *i, const char *property, void *data) {
38131695
LP
256 Unit *u = data;
257 DBusMessageIter sub;
f93891f3 258 _cleanup_free_ char *p = NULL;
38131695 259
38131695
LP
260 assert(i);
261 assert(property);
262 assert(u);
263
264 if (!dbus_message_iter_open_container(i, DBUS_TYPE_STRUCT, NULL, &sub))
265 return -ENOMEM;
266
ac155bb8 267 if (u->job) {
38131695 268
f93891f3
ZJS
269 p = job_dbus_path(u->job);
270 if (!p)
38131695
LP
271 return -ENOMEM;
272
ac155bb8 273 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT32, &u->job->id) ||
f93891f3 274 !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &p))
38131695 275 return -ENOMEM;
38131695
LP
276 } else {
277 uint32_t id = 0;
278
279 /* No job, so let's fill in some placeholder
280 * data. Since we need to fill in a valid path we
281 * simple point to ourselves. */
282
f93891f3
ZJS
283 p = unit_dbus_path(u);
284 if (!p)
38131695
LP
285 return -ENOMEM;
286
287 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT32, &id) ||
f93891f3 288 !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &p))
38131695 289 return -ENOMEM;
38131695
LP
290 }
291
38131695
LP
292 if (!dbus_message_iter_close_container(i, &sub))
293 return -ENOMEM;
294
295 return 0;
296}
297
e2110e5d 298static int bus_unit_append_need_daemon_reload(DBusMessageIter *i, const char *property, void *data) {
45fb0699
LP
299 Unit *u = data;
300 dbus_bool_t b;
301
45fb0699
LP
302 assert(i);
303 assert(property);
304 assert(u);
305
306 b = unit_need_daemon_reload(u);
307
308 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
309 return -ENOMEM;
310
311 return 0;
312}
313
e2110e5d 314static int bus_unit_append_load_error(DBusMessageIter *i, const char *property, void *data) {
9f39404c
LP
315 Unit *u = data;
316 const char *name, *message;
317 DBusMessageIter sub;
318
319 assert(i);
320 assert(property);
321 assert(u);
322
ac155bb8
MS
323 if (u->load_error != 0) {
324 name = bus_errno_to_dbus(u->load_error);
325 message = strempty(strerror(-u->load_error));
9f39404c
LP
326 } else
327 name = message = "";
328
329 if (!dbus_message_iter_open_container(i, DBUS_TYPE_STRUCT, NULL, &sub) ||
330 !dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &name) ||
331 !dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &message) ||
332 !dbus_message_iter_close_container(i, &sub))
333 return -ENOMEM;
334
335 return 0;
336}
337
5e8d1c9a 338static DBusHandlerResult bus_unit_message_dispatch(Unit *u, DBusConnection *connection, DBusMessage *message) {
cad45ba1 339 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
b548631a
LP
340 DBusError error;
341 JobType job_type = _JOB_TYPE_INVALID;
6f28c033 342 bool reload_if_possible = false;
e2417e41 343 int r;
b548631a
LP
344
345 dbus_error_init(&error);
346
347 if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Start"))
348 job_type = JOB_START;
349 else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Stop"))
350 job_type = JOB_STOP;
351 else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Reload"))
352 job_type = JOB_RELOAD;
353 else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Restart"))
354 job_type = JOB_RESTART;
9a1ac7b9
LP
355 else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "TryRestart"))
356 job_type = JOB_TRY_RESTART;
6f28c033
LP
357 else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "ReloadOrRestart")) {
358 reload_if_possible = true;
359 job_type = JOB_RESTART;
360 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "ReloadOrTryRestart")) {
361 reload_if_possible = true;
362 job_type = JOB_TRY_RESTART;
8a0867d6 363 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Kill")) {
c74f17d9 364 const char *swho;
8a0867d6 365 int32_t signo;
8a0867d6 366 KillWho who;
8a0867d6
LP
367
368 if (!dbus_message_get_args(
369 message,
370 &error,
371 DBUS_TYPE_STRING, &swho,
8a0867d6
LP
372 DBUS_TYPE_INT32, &signo,
373 DBUS_TYPE_INVALID))
bfebab7f 374 return bus_send_error_reply(connection, message, &error, -EINVAL);
8a0867d6 375
0a524ba7
LP
376 if (isempty(swho))
377 who = KILL_ALL;
378 else {
379 who = kill_who_from_string(swho);
380 if (who < 0)
381 return bus_send_error_reply(connection, message, &error, -EINVAL);
382 }
383
0a524ba7 384 if (signo <= 0 || signo >= _NSIG)
bfebab7f 385 return bus_send_error_reply(connection, message, &error, -EINVAL);
8a0867d6 386
cad45ba1
LP
387 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "stop");
388
c74f17d9
LP
389 r = unit_kill(u, who, signo, &error);
390 if (r < 0)
bfebab7f 391 return bus_send_error_reply(connection, message, &error, r);
8a0867d6 392
c74f17d9
LP
393 reply = dbus_message_new_method_return(message);
394 if (!reply)
8a0867d6
LP
395 goto oom;
396
fdf20a31 397 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "ResetFailed")) {
5632e374 398
cad45ba1
LP
399 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "reload");
400
fdf20a31 401 unit_reset_failed(u);
5632e374 402
cad45ba1
LP
403 reply = dbus_message_new_method_return(message);
404 if (!reply)
5632e374 405 goto oom;
8e2af478
LP
406 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "SetProperties")) {
407 DBusMessageIter iter;
408 dbus_bool_t runtime;
409
410 if (!dbus_message_iter_init(message, &iter))
411 goto oom;
412
413 if (bus_iter_get_basic_and_next(&iter, DBUS_TYPE_BOOLEAN, &runtime, true) < 0)
414 return bus_send_error_reply(connection, message, NULL, -EINVAL);
415
416 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "start");
417
c2756a68 418 r = bus_unit_set_properties(u, &iter, runtime ? UNIT_RUNTIME : UNIT_PERSISTENT, true, &error);
8e2af478
LP
419 if (r < 0)
420 return bus_send_error_reply(connection, message, &error, r);
421
422 reply = dbus_message_new_method_return(message);
423 if (!reply)
424 goto oom;
5632e374 425
6f28c033 426 } else if (UNIT_VTABLE(u)->bus_message_handler)
5e8d1c9a 427 return UNIT_VTABLE(u)->bus_message_handler(u, connection, message);
b548631a 428 else
4139c1b2 429 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
b548631a
LP
430
431 if (job_type != _JOB_TYPE_INVALID) {
432 const char *smode;
433 JobMode mode;
bc0f8771 434
b548631a
LP
435 if (!dbus_message_get_args(
436 message,
437 &error,
438 DBUS_TYPE_STRING, &smode,
439 DBUS_TYPE_INVALID))
bfebab7f 440 return bus_send_error_reply(connection, message, &error, -EINVAL);
b548631a 441
cad45ba1
LP
442 mode = job_mode_from_string(smode);
443 if (mode < 0) {
398ef8ba 444 dbus_set_error(&error, BUS_ERROR_INVALID_JOB_MODE, "Job mode %s is invalid.", smode);
bfebab7f 445 return bus_send_error_reply(connection, message, &error, -EINVAL);
398ef8ba 446 }
b548631a 447
cad45ba1 448 return bus_unit_queue_job(connection, message, u, job_type, mode, reload_if_possible);
b548631a
LP
449 }
450
cad45ba1 451 if (reply)
c6a818c8 452 if (!bus_maybe_send_reply(connection, message, reply))
b548631a
LP
453 goto oom;
454
b548631a
LP
455 return DBUS_HANDLER_RESULT_HANDLED;
456
457oom:
b548631a 458 dbus_error_free(&error);
b548631a 459 return DBUS_HANDLER_RESULT_NEED_MEMORY;
ea430986
LP
460}
461
5e8d1c9a 462static DBusHandlerResult bus_unit_message_handler(DBusConnection *connection, DBusMessage *message, void *data) {
ea430986
LP
463 Manager *m = data;
464 Unit *u;
465 int r;
cad45ba1 466 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
80fbf05e 467 DBusError error;
ea430986
LP
468
469 assert(connection);
470 assert(message);
471 assert(m);
472
80fbf05e
MS
473 dbus_error_init(&error);
474
2cccbca4
LP
475 if (streq(dbus_message_get_path(message), "/org/freedesktop/systemd1/unit")) {
476 /* Be nice to gdbus and return introspection data for our mid-level paths */
477
ffc227c9 478 SELINUX_ACCESS_CHECK(connection, message, "status");
cad45ba1 479
2cccbca4
LP
480 if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect")) {
481 char *introspection = NULL;
482 FILE *f;
483 Iterator i;
484 const char *k;
485 size_t size;
486
cad45ba1
LP
487 reply = dbus_message_new_method_return(message);
488 if (!reply)
2cccbca4
LP
489 goto oom;
490
491 /* We roll our own introspection code here, instead of
492 * relying on bus_default_message_handler() because we
493 * need to generate our introspection string
494 * dynamically. */
495
cad45ba1
LP
496 f = open_memstream(&introspection, &size);
497 if (!f)
2cccbca4
LP
498 goto oom;
499
500 fputs(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
501 "<node>\n", f);
502
503 fputs(BUS_INTROSPECTABLE_INTERFACE, f);
504 fputs(BUS_PEER_INTERFACE, f);
505
506 HASHMAP_FOREACH_KEY(u, k, m->units, i) {
507 char *p;
508
ac155bb8 509 if (k != u->id)
2cccbca4
LP
510 continue;
511
cad45ba1
LP
512 p = bus_path_escape(k);
513 if (!p) {
2cccbca4
LP
514 fclose(f);
515 free(introspection);
516 goto oom;
517 }
518
519 fprintf(f, "<node name=\"%s\"/>", p);
520 free(p);
521 }
522
523 fputs("</node>\n", f);
524
525 if (ferror(f)) {
526 fclose(f);
527 free(introspection);
528 goto oom;
529 }
530
531 fclose(f);
532
533 if (!introspection)
534 goto oom;
535
536 if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &introspection, DBUS_TYPE_INVALID)) {
537 free(introspection);
538 goto oom;
539 }
540
541 free(introspection);
542
c6a818c8 543 if (!bus_maybe_send_reply(connection, message, reply))
2cccbca4
LP
544 goto oom;
545
2cccbca4
LP
546 return DBUS_HANDLER_RESULT_HANDLED;
547 }
548
549 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
550 }
551
80fbf05e 552 r = manager_load_unit_from_dbus_path(m, dbus_message_get_path(message), &error, &u);
cad45ba1
LP
553 if (r == -ENOMEM)
554 goto oom;
555 if (r < 0)
80fbf05e 556 return bus_send_error_reply(connection, message, &error, r);
ea430986 557
5e8d1c9a 558 return bus_unit_message_dispatch(u, connection, message);
2cccbca4
LP
559
560oom:
80fbf05e
MS
561 dbus_error_free(&error);
562
2cccbca4 563 return DBUS_HANDLER_RESULT_NEED_MEMORY;
ea430986
LP
564}
565
566const DBusObjectPathVTable bus_unit_vtable = {
567 .message_function = bus_unit_message_handler
568};
c1e1601e
LP
569
570void bus_unit_send_change_signal(Unit *u) {
f93891f3 571 _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
b170dd80
LP
572 _cleanup_free_ char *p = NULL;
573 int r;
c1e1601e
LP
574
575 assert(u);
c1e1601e 576
ac155bb8
MS
577 if (u->in_dbus_queue) {
578 LIST_REMOVE(Unit, dbus_queue, u->manager->dbus_unit_queue, u);
579 u->in_dbus_queue = false;
c0bd0cf7 580 }
c1e1601e 581
ac155bb8 582 if (!u->id)
04ade7d2
LP
583 return;
584
ac155bb8
MS
585 if (!bus_has_subscriber(u->manager)) {
586 u->sent_dbus_new_signal = true;
c1e1601e 587 return;
94b6dfa2 588 }
c1e1601e 589
f93891f3 590 p = unit_dbus_path(u);
b170dd80
LP
591 if (!p) {
592 log_oom();
593 return;
594 }
c1e1601e 595
ac155bb8 596 if (u->sent_dbus_new_signal) {
c4e2ceae
LP
597 /* Send a properties changed signal. First for the
598 * specific type, then for the generic unit. The
599 * clients may rely on this order to get atomic
c5315881 600 * behavior if needed. */
c4e2ceae
LP
601
602 if (UNIT_VTABLE(u)->bus_invalidating_properties) {
603
f93891f3
ZJS
604 m = bus_properties_changed_new(p,
605 UNIT_VTABLE(u)->bus_interface,
606 UNIT_VTABLE(u)->bus_invalidating_properties);
b170dd80
LP
607 if (!m) {
608 log_oom();
609 return;
610 }
c4e2ceae 611
b170dd80
LP
612 r = bus_broadcast(u->manager, m);
613 if (r < 0) {
614 log_error("Failed to broadcast change message: %s", strerror(-r));
615 return;
616 }
c1e1601e 617
c4e2ceae
LP
618 dbus_message_unref(m);
619 }
620
f93891f3
ZJS
621 m = bus_properties_changed_new(p, "org.freedesktop.systemd1.Unit",
622 INVALIDATING_PROPERTIES);
b170dd80
LP
623 if (!m) {
624 log_oom();
625 return;
626 }
c4e2ceae 627
c1e1601e 628 } else {
c1e1601e
LP
629 /* Send a new signal */
630
f93891f3
ZJS
631 m = dbus_message_new_signal("/org/freedesktop/systemd1",
632 "org.freedesktop.systemd1.Manager",
633 "UnitNew");
b170dd80
LP
634 if (!m) {
635 log_oom();
636 return;
637 }
c1e1601e 638
c1e1601e 639 if (!dbus_message_append_args(m,
ac155bb8 640 DBUS_TYPE_STRING, &u->id,
c1e1601e 641 DBUS_TYPE_OBJECT_PATH, &p,
b170dd80
LP
642 DBUS_TYPE_INVALID)) {
643 log_oom();
644 return;
645 }
c1e1601e
LP
646 }
647
b170dd80
LP
648 r = bus_broadcast(u->manager, m);
649 if (r < 0) {
650 log_error("Failed to broadcast UnitNew/PropertiesChanged message.");
651 return;
652 }
c1e1601e 653
ac155bb8 654 u->sent_dbus_new_signal = true;
c1e1601e
LP
655}
656
657void bus_unit_send_removed_signal(Unit *u) {
f93891f3
ZJS
658 _cleanup_free_ char *p = NULL;
659 _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
c1e1601e
LP
660
661 assert(u);
662
ac155bb8 663 if (!bus_has_subscriber(u->manager))
c1e1601e
LP
664 return;
665
ac155bb8 666 if (!u->sent_dbus_new_signal)
7535cc78
LP
667 bus_unit_send_change_signal(u);
668
ac155bb8 669 if (!u->id)
04ade7d2
LP
670 return;
671
f93891f3
ZJS
672 p = unit_dbus_path(u);
673 if (!p)
c1e1601e
LP
674 goto oom;
675
f93891f3
ZJS
676 m = dbus_message_new_signal("/org/freedesktop/systemd1",
677 "org.freedesktop.systemd1.Manager",
678 "UnitRemoved");
679 if (!m)
c1e1601e
LP
680 goto oom;
681
c1e1601e 682 if (!dbus_message_append_args(m,
ac155bb8 683 DBUS_TYPE_STRING, &u->id,
c1e1601e
LP
684 DBUS_TYPE_OBJECT_PATH, &p,
685 DBUS_TYPE_INVALID))
686 goto oom;
687
ac155bb8 688 if (bus_broadcast(u->manager, m) < 0)
c1e1601e
LP
689 goto oom;
690
c1e1601e
LP
691 return;
692
693oom:
f93891f3 694 log_oom();
c1e1601e 695}
e2110e5d 696
cad45ba1
LP
697DBusHandlerResult bus_unit_queue_job(
698 DBusConnection *connection,
699 DBusMessage *message,
700 Unit *u,
701 JobType type,
702 JobMode mode,
703 bool reload_if_possible) {
704
705 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
706 _cleanup_free_ char *path = NULL;
707 Job *j;
708 JobBusClient *cl;
709 DBusError error;
710 int r;
711
712 assert(connection);
713 assert(message);
714 assert(u);
715 assert(type >= 0 && type < _JOB_TYPE_MAX);
716 assert(mode >= 0 && mode < _JOB_MODE_MAX);
717
718 dbus_error_init(&error);
719
720 if (reload_if_possible && unit_can_reload(u)) {
721 if (type == JOB_RESTART)
722 type = JOB_RELOAD_OR_START;
723 else if (type == JOB_TRY_RESTART)
724 type = JOB_RELOAD;
725 }
726
727 SELINUX_UNIT_ACCESS_CHECK(u, connection, message,
728 (type == JOB_START || type == JOB_RESTART || type == JOB_TRY_RESTART) ? "start" :
729 type == JOB_STOP ? "stop" : "reload");
730
c2756a68 731 if (type == JOB_STOP && (u->load_state == UNIT_NOT_FOUND || u->load_state == UNIT_ERROR) && unit_active_state(u) == UNIT_INACTIVE) {
cad45ba1
LP
732 dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s not loaded.", u->id);
733 return bus_send_error_reply(connection, message, &error, -EPERM);
734 }
735
736 if ((type == JOB_START && u->refuse_manual_start) ||
737 (type == JOB_STOP && u->refuse_manual_stop) ||
738 ((type == JOB_RESTART || type == JOB_TRY_RESTART) && (u->refuse_manual_start || u->refuse_manual_stop))) {
f93891f3
ZJS
739 dbus_set_error(&error, BUS_ERROR_ONLY_BY_DEPENDENCY,
740 "Operation refused, unit %s may be requested by dependency only.", u->id);
cad45ba1
LP
741 return bus_send_error_reply(connection, message, &error, -EPERM);
742 }
743
744 r = manager_add_job(u->manager, type, u, mode, true, &error, &j);
745 if (r < 0)
746 return bus_send_error_reply(connection, message, &error, r);
747
748 cl = job_bus_client_new(connection, bus_message_get_sender_with_fallback(message));
749 if (!cl)
750 goto oom;
751
752 LIST_PREPEND(JobBusClient, client, j->bus_client_list, cl);
753
754 reply = dbus_message_new_method_return(message);
755 if (!reply)
756 goto oom;
757
758 path = job_dbus_path(j);
759 if (!path)
760 goto oom;
761
762 if (!dbus_message_append_args(
763 reply,
764 DBUS_TYPE_OBJECT_PATH, &path,
765 DBUS_TYPE_INVALID))
766 goto oom;
767
c6a818c8 768 if (!bus_maybe_send_reply(connection, message, reply))
cad45ba1
LP
769 goto oom;
770
771 return DBUS_HANDLER_RESULT_HANDLED;
772
773oom:
774 dbus_error_free(&error);
775
776 return DBUS_HANDLER_RESULT_NEED_MEMORY;
777}
778
9f2e86af
LP
779static int bus_unit_set_transient_property(
780 Unit *u,
781 const char *name,
782 DBusMessageIter *i,
783 UnitSetPropertiesMode mode,
784 DBusError *error) {
785
786 int r;
787
788 assert(u);
789 assert(name);
790 assert(i);
791
792 if (streq(name, "Description")) {
9f2e86af
LP
793 if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_STRING)
794 return -EINVAL;
795
8aec412f 796 if (mode != UNIT_CHECK) {
b9316fb0 797 _cleanup_free_ char *contents = NULL;
8aec412f 798 const char *description;
9f2e86af 799
8aec412f
LP
800 dbus_message_iter_get_basic(i, &description);
801
802 r = unit_set_description(u, description);
803 if (r < 0)
804 return r;
b9316fb0
LP
805
806 contents = strjoin("[Unit]\nDescription=", description, "\n", NULL);
807 if (!contents)
808 return -ENOMEM;
809
810 unit_write_drop_in(u, mode, "Description", contents);
8aec412f 811 }
9f2e86af
LP
812
813 return 1;
c221420b
LP
814
815 } else if (streq(name, "Slice") && unit_get_cgroup_context(u)) {
816 const char *s;
817 Unit *slice;
818
819 if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_STRING)
820 return -EINVAL;
821
822 dbus_message_iter_get_basic(i, &s);
823
8aec412f
LP
824 if (isempty(s)) {
825 if (mode != UNIT_CHECK)
826 unit_ref_unset(&u->slice);
827 } else {
b9316fb0
LP
828 _cleanup_free_ char *contents = NULL;
829
8aec412f
LP
830 r = manager_load_unit(u->manager, s, NULL, error, &slice);
831 if (r < 0)
832 return r;
c221420b 833
8aec412f
LP
834 if (slice->type != UNIT_SLICE)
835 return -EINVAL;
c221420b 836
8aec412f
LP
837 if (mode != UNIT_CHECK)
838 unit_ref_set(&u->slice, slice);
b9316fb0
LP
839
840 contents = strjoin("[", UNIT_VTABLE(u)->private_section, "]\nSlice=", s, NULL);
841 if (!contents)
842 return -ENOMEM;
843
844 unit_write_drop_in(u, mode, "Slice", contents);
8aec412f 845 }
c221420b 846 return 1;
9f2e86af
LP
847 }
848
849 return 0;
850}
851
c2756a68
LP
852int bus_unit_set_properties(
853 Unit *u,
854 DBusMessageIter *iter,
855 UnitSetPropertiesMode mode,
856 bool commit,
857 DBusError *error) {
858
8e2af478
LP
859 bool for_real = false;
860 DBusMessageIter sub;
861 unsigned n = 0;
862 int r;
863
864 assert(u);
865 assert(iter);
866
c2756a68
LP
867 if (u->transient)
868 mode &= UNIT_RUNTIME;
869
8e2af478
LP
870 /* We iterate through the array twice. First run we just check
871 * if all passed data is valid, second run actually applies
872 * it. This is to implement transaction-like behaviour without
873 * actually providing full transactions. */
874
875 if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY ||
876 dbus_message_iter_get_element_type(iter) != DBUS_TYPE_STRUCT)
877 return -EINVAL;
878
879 dbus_message_iter_recurse(iter, &sub);
8e2af478
LP
880 for (;;) {
881 DBusMessageIter sub2, sub3;
882 const char *name;
883
884 if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_INVALID) {
885
886 if (for_real)
887 break;
888
889 /* Reached EOF. Let's try again, and this time for realz... */
890 dbus_message_iter_recurse(iter, &sub);
891 for_real = true;
892 continue;
893 }
894
895 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT)
896 return -EINVAL;
897
898 dbus_message_iter_recurse(&sub, &sub2);
899
900 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &name, true) < 0 ||
901 dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_VARIANT)
902 return -EINVAL;
903
904 if (!UNIT_VTABLE(u)->bus_set_property) {
905 dbus_set_error(error, DBUS_ERROR_PROPERTY_READ_ONLY, "Objects of this type do not support setting properties.");
906 return -ENOENT;
907 }
908
909 dbus_message_iter_recurse(&sub2, &sub3);
910 r = UNIT_VTABLE(u)->bus_set_property(u, name, &sub3, for_real ? mode : UNIT_CHECK, error);
9f2e86af
LP
911 if (r == 0 && u->transient && u->load_state == UNIT_STUB)
912 r = bus_unit_set_transient_property(u, name, &sub3, for_real ? mode : UNIT_CHECK, error);
8e2af478
LP
913 if (r < 0)
914 return r;
915 if (r == 0) {
916 dbus_set_error(error, DBUS_ERROR_PROPERTY_READ_ONLY, "Cannot set property %s, or unknown property.", name);
917 return -ENOENT;
918 }
919
920 dbus_message_iter_next(&sub);
921
922 n += for_real;
923 }
924
c2756a68 925 if (commit && n > 0 && UNIT_VTABLE(u)->bus_commit_properties)
8e2af478
LP
926 UNIT_VTABLE(u)->bus_commit_properties(u);
927
241da328 928 return n;
8e2af478
LP
929}
930
e2110e5d
MS
931const BusProperty bus_unit_properties[] = {
932 { "Id", bus_property_append_string, "s", offsetof(Unit, id), true },
933 { "Names", bus_unit_append_names, "as", 0 },
934 { "Following", bus_unit_append_following, "s", 0 },
935 { "Requires", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUIRES]), true },
936 { "RequiresOverridable", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUIRES_OVERRIDABLE]), true },
937 { "Requisite", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUISITE]), true },
938 { "RequisiteOverridable", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUISITE_OVERRIDABLE]), true },
939 { "Wants", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_WANTS]), true },
7f2cddae 940 { "BindsTo", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_BINDS_TO]), true },
b05afff1 941 { "PartOf", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_PART_OF]), true },
e2110e5d
MS
942 { "RequiredBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUIRED_BY]), true },
943 { "RequiredByOverridable",bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUIRED_BY_OVERRIDABLE]), true },
944 { "WantedBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_WANTED_BY]), true },
945 { "BoundBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_BOUND_BY]), true },
b05afff1 946 { "ConsistsOf", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_CONSISTS_OF]), true },
e2110e5d
MS
947 { "Conflicts", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_CONFLICTS]), true },
948 { "ConflictedBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_CONFLICTED_BY]), true },
949 { "Before", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_BEFORE]), true },
950 { "After", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_AFTER]), true },
951 { "OnFailure", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_ON_FAILURE]), true },
952 { "Triggers", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_TRIGGERS]), true },
953 { "TriggeredBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_TRIGGERED_BY]), true },
7f2cddae
LP
954 { "PropagatesReloadTo", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_PROPAGATES_RELOAD_TO]), true },
955 { "ReloadPropagatedFrom", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_RELOAD_PROPAGATED_FROM]), true },
7c8fa05c 956 { "RequiresMountsFor", bus_property_append_strv, "as", offsetof(Unit, requires_mounts_for), true },
49dbfa7b 957 { "Documentation", bus_property_append_strv, "as", offsetof(Unit, documentation), true },
e2110e5d
MS
958 { "Description", bus_unit_append_description, "s", 0 },
959 { "LoadState", bus_unit_append_load_state, "s", offsetof(Unit, load_state) },
960 { "ActiveState", bus_unit_append_active_state, "s", 0 },
961 { "SubState", bus_unit_append_sub_state, "s", 0 },
962 { "FragmentPath", bus_property_append_string, "s", offsetof(Unit, fragment_path), true },
1b64d026 963 { "SourcePath", bus_property_append_string, "s", offsetof(Unit, source_path), true },
2875e22b 964 { "DropInPaths", bus_property_append_strv, "as", offsetof(Unit, dropin_paths), true },
e2110e5d
MS
965 { "UnitFileState", bus_unit_append_file_state, "s", 0 },
966 { "InactiveExitTimestamp",bus_property_append_usec, "t", offsetof(Unit, inactive_exit_timestamp.realtime) },
967 { "InactiveExitTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, inactive_exit_timestamp.monotonic) },
968 { "ActiveEnterTimestamp", bus_property_append_usec, "t", offsetof(Unit, active_enter_timestamp.realtime) },
969 { "ActiveEnterTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, active_enter_timestamp.monotonic) },
970 { "ActiveExitTimestamp", bus_property_append_usec, "t", offsetof(Unit, active_exit_timestamp.realtime) },
971 { "ActiveExitTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, active_exit_timestamp.monotonic) },
972 { "InactiveEnterTimestamp", bus_property_append_usec, "t", offsetof(Unit, inactive_enter_timestamp.realtime) },
973 { "InactiveEnterTimestampMonotonic",bus_property_append_usec, "t", offsetof(Unit, inactive_enter_timestamp.monotonic) },
974 { "CanStart", bus_unit_append_can_start, "b", 0 },
975 { "CanStop", bus_unit_append_can_stop, "b", 0 },
976 { "CanReload", bus_unit_append_can_reload, "b", 0 },
977 { "CanIsolate", bus_unit_append_can_isolate, "b", 0 },
978 { "Job", bus_unit_append_job, "(uo)", 0 },
979 { "StopWhenUnneeded", bus_property_append_bool, "b", offsetof(Unit, stop_when_unneeded) },
980 { "RefuseManualStart", bus_property_append_bool, "b", offsetof(Unit, refuse_manual_start) },
981 { "RefuseManualStop", bus_property_append_bool, "b", offsetof(Unit, refuse_manual_stop) },
982 { "AllowIsolate", bus_property_append_bool, "b", offsetof(Unit, allow_isolate) },
983 { "DefaultDependencies", bus_property_append_bool, "b", offsetof(Unit, default_dependencies) },
984 { "OnFailureIsolate", bus_property_append_bool, "b", offsetof(Unit, on_failure_isolate) },
985 { "IgnoreOnIsolate", bus_property_append_bool, "b", offsetof(Unit, ignore_on_isolate) },
986 { "IgnoreOnSnapshot", bus_property_append_bool, "b", offsetof(Unit, ignore_on_snapshot) },
e2110e5d
MS
987 { "NeedDaemonReload", bus_unit_append_need_daemon_reload, "b", 0 },
988 { "JobTimeoutUSec", bus_property_append_usec, "t", offsetof(Unit, job_timeout) },
989 { "ConditionTimestamp", bus_property_append_usec, "t", offsetof(Unit, condition_timestamp.realtime) },
990 { "ConditionTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, condition_timestamp.monotonic) },
991 { "ConditionResult", bus_property_append_bool, "b", offsetof(Unit, condition_result) },
992 { "LoadError", bus_unit_append_load_error, "(ss)", 0 },
c2756a68 993 { "Transient", bus_property_append_bool, "b", offsetof(Unit, transient) },
246aa6dd
LP
994 { NULL, }
995};
d7550a67
LP
996
997const BusProperty bus_unit_cgroup_properties[] = {
998 { "Slice", bus_unit_append_slice, "s", 0 },
999 { "ControlGroup", bus_property_append_string, "s", offsetof(Unit, cgroup_path), true },
1000};