]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/core/dbus-unit.c
core: add transient units
[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
ZJS
571 _cleanup_free_ char *p = NULL;
572 _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
c1e1601e
LP
573
574 assert(u);
c1e1601e 575
ac155bb8
MS
576 if (u->in_dbus_queue) {
577 LIST_REMOVE(Unit, dbus_queue, u->manager->dbus_unit_queue, u);
578 u->in_dbus_queue = false;
c0bd0cf7 579 }
c1e1601e 580
ac155bb8 581 if (!u->id)
04ade7d2
LP
582 return;
583
ac155bb8
MS
584 if (!bus_has_subscriber(u->manager)) {
585 u->sent_dbus_new_signal = true;
c1e1601e 586 return;
94b6dfa2 587 }
c1e1601e 588
f93891f3
ZJS
589 p = unit_dbus_path(u);
590 if (!p)
c1e1601e
LP
591 goto oom;
592
ac155bb8 593 if (u->sent_dbus_new_signal) {
c4e2ceae
LP
594 /* Send a properties changed signal. First for the
595 * specific type, then for the generic unit. The
596 * clients may rely on this order to get atomic
c5315881 597 * behavior if needed. */
c4e2ceae
LP
598
599 if (UNIT_VTABLE(u)->bus_invalidating_properties) {
600
f93891f3
ZJS
601 m = bus_properties_changed_new(p,
602 UNIT_VTABLE(u)->bus_interface,
603 UNIT_VTABLE(u)->bus_invalidating_properties);
604 if (!m)
c4e2ceae
LP
605 goto oom;
606
ac155bb8 607 if (bus_broadcast(u->manager, m) < 0)
c4e2ceae 608 goto oom;
c1e1601e 609
c4e2ceae
LP
610 dbus_message_unref(m);
611 }
612
f93891f3
ZJS
613 m = bus_properties_changed_new(p, "org.freedesktop.systemd1.Unit",
614 INVALIDATING_PROPERTIES);
615 if (!m)
c1e1601e 616 goto oom;
c4e2ceae 617
c1e1601e 618 } else {
c1e1601e
LP
619 /* Send a new signal */
620
f93891f3
ZJS
621 m = dbus_message_new_signal("/org/freedesktop/systemd1",
622 "org.freedesktop.systemd1.Manager",
623 "UnitNew");
624 if (!m)
c1e1601e
LP
625 goto oom;
626
c1e1601e 627 if (!dbus_message_append_args(m,
ac155bb8 628 DBUS_TYPE_STRING, &u->id,
c1e1601e
LP
629 DBUS_TYPE_OBJECT_PATH, &p,
630 DBUS_TYPE_INVALID))
631 goto oom;
632 }
633
ac155bb8 634 if (bus_broadcast(u->manager, m) < 0)
c1e1601e
LP
635 goto oom;
636
ac155bb8 637 u->sent_dbus_new_signal = true;
c1e1601e
LP
638
639 return;
640
641oom:
f93891f3 642 log_oom();
c1e1601e
LP
643}
644
645void bus_unit_send_removed_signal(Unit *u) {
f93891f3
ZJS
646 _cleanup_free_ char *p = NULL;
647 _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
c1e1601e
LP
648
649 assert(u);
650
ac155bb8 651 if (!bus_has_subscriber(u->manager))
c1e1601e
LP
652 return;
653
ac155bb8 654 if (!u->sent_dbus_new_signal)
7535cc78
LP
655 bus_unit_send_change_signal(u);
656
ac155bb8 657 if (!u->id)
04ade7d2
LP
658 return;
659
f93891f3
ZJS
660 p = unit_dbus_path(u);
661 if (!p)
c1e1601e
LP
662 goto oom;
663
f93891f3
ZJS
664 m = dbus_message_new_signal("/org/freedesktop/systemd1",
665 "org.freedesktop.systemd1.Manager",
666 "UnitRemoved");
667 if (!m)
c1e1601e
LP
668 goto oom;
669
c1e1601e 670 if (!dbus_message_append_args(m,
ac155bb8 671 DBUS_TYPE_STRING, &u->id,
c1e1601e
LP
672 DBUS_TYPE_OBJECT_PATH, &p,
673 DBUS_TYPE_INVALID))
674 goto oom;
675
ac155bb8 676 if (bus_broadcast(u->manager, m) < 0)
c1e1601e
LP
677 goto oom;
678
c1e1601e
LP
679 return;
680
681oom:
f93891f3 682 log_oom();
c1e1601e 683}
e2110e5d 684
cad45ba1
LP
685DBusHandlerResult bus_unit_queue_job(
686 DBusConnection *connection,
687 DBusMessage *message,
688 Unit *u,
689 JobType type,
690 JobMode mode,
691 bool reload_if_possible) {
692
693 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
694 _cleanup_free_ char *path = NULL;
695 Job *j;
696 JobBusClient *cl;
697 DBusError error;
698 int r;
699
700 assert(connection);
701 assert(message);
702 assert(u);
703 assert(type >= 0 && type < _JOB_TYPE_MAX);
704 assert(mode >= 0 && mode < _JOB_MODE_MAX);
705
706 dbus_error_init(&error);
707
708 if (reload_if_possible && unit_can_reload(u)) {
709 if (type == JOB_RESTART)
710 type = JOB_RELOAD_OR_START;
711 else if (type == JOB_TRY_RESTART)
712 type = JOB_RELOAD;
713 }
714
715 SELINUX_UNIT_ACCESS_CHECK(u, connection, message,
716 (type == JOB_START || type == JOB_RESTART || type == JOB_TRY_RESTART) ? "start" :
717 type == JOB_STOP ? "stop" : "reload");
718
c2756a68 719 if (type == JOB_STOP && (u->load_state == UNIT_NOT_FOUND || u->load_state == UNIT_ERROR) && unit_active_state(u) == UNIT_INACTIVE) {
cad45ba1
LP
720 dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s not loaded.", u->id);
721 return bus_send_error_reply(connection, message, &error, -EPERM);
722 }
723
724 if ((type == JOB_START && u->refuse_manual_start) ||
725 (type == JOB_STOP && u->refuse_manual_stop) ||
726 ((type == JOB_RESTART || type == JOB_TRY_RESTART) && (u->refuse_manual_start || u->refuse_manual_stop))) {
f93891f3
ZJS
727 dbus_set_error(&error, BUS_ERROR_ONLY_BY_DEPENDENCY,
728 "Operation refused, unit %s may be requested by dependency only.", u->id);
cad45ba1
LP
729 return bus_send_error_reply(connection, message, &error, -EPERM);
730 }
731
732 r = manager_add_job(u->manager, type, u, mode, true, &error, &j);
733 if (r < 0)
734 return bus_send_error_reply(connection, message, &error, r);
735
736 cl = job_bus_client_new(connection, bus_message_get_sender_with_fallback(message));
737 if (!cl)
738 goto oom;
739
740 LIST_PREPEND(JobBusClient, client, j->bus_client_list, cl);
741
742 reply = dbus_message_new_method_return(message);
743 if (!reply)
744 goto oom;
745
746 path = job_dbus_path(j);
747 if (!path)
748 goto oom;
749
750 if (!dbus_message_append_args(
751 reply,
752 DBUS_TYPE_OBJECT_PATH, &path,
753 DBUS_TYPE_INVALID))
754 goto oom;
755
c6a818c8 756 if (!bus_maybe_send_reply(connection, message, reply))
cad45ba1
LP
757 goto oom;
758
759 return DBUS_HANDLER_RESULT_HANDLED;
760
761oom:
762 dbus_error_free(&error);
763
764 return DBUS_HANDLER_RESULT_NEED_MEMORY;
765}
766
c2756a68
LP
767int bus_unit_set_properties(
768 Unit *u,
769 DBusMessageIter *iter,
770 UnitSetPropertiesMode mode,
771 bool commit,
772 DBusError *error) {
773
8e2af478
LP
774 bool for_real = false;
775 DBusMessageIter sub;
776 unsigned n = 0;
777 int r;
778
779 assert(u);
780 assert(iter);
781
c2756a68
LP
782 if (u->transient)
783 mode &= UNIT_RUNTIME;
784
8e2af478
LP
785 /* We iterate through the array twice. First run we just check
786 * if all passed data is valid, second run actually applies
787 * it. This is to implement transaction-like behaviour without
788 * actually providing full transactions. */
789
790 if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY ||
791 dbus_message_iter_get_element_type(iter) != DBUS_TYPE_STRUCT)
792 return -EINVAL;
793
794 dbus_message_iter_recurse(iter, &sub);
8e2af478
LP
795 for (;;) {
796 DBusMessageIter sub2, sub3;
797 const char *name;
798
799 if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_INVALID) {
800
801 if (for_real)
802 break;
803
804 /* Reached EOF. Let's try again, and this time for realz... */
805 dbus_message_iter_recurse(iter, &sub);
806 for_real = true;
807 continue;
808 }
809
810 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT)
811 return -EINVAL;
812
813 dbus_message_iter_recurse(&sub, &sub2);
814
815 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &name, true) < 0 ||
816 dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_VARIANT)
817 return -EINVAL;
818
819 if (!UNIT_VTABLE(u)->bus_set_property) {
820 dbus_set_error(error, DBUS_ERROR_PROPERTY_READ_ONLY, "Objects of this type do not support setting properties.");
821 return -ENOENT;
822 }
823
824 dbus_message_iter_recurse(&sub2, &sub3);
825 r = UNIT_VTABLE(u)->bus_set_property(u, name, &sub3, for_real ? mode : UNIT_CHECK, error);
826 if (r < 0)
827 return r;
828 if (r == 0) {
829 dbus_set_error(error, DBUS_ERROR_PROPERTY_READ_ONLY, "Cannot set property %s, or unknown property.", name);
830 return -ENOENT;
831 }
832
833 dbus_message_iter_next(&sub);
834
835 n += for_real;
836 }
837
c2756a68 838 if (commit && n > 0 && UNIT_VTABLE(u)->bus_commit_properties)
8e2af478
LP
839 UNIT_VTABLE(u)->bus_commit_properties(u);
840
241da328 841 return n;
8e2af478
LP
842}
843
e2110e5d
MS
844const BusProperty bus_unit_properties[] = {
845 { "Id", bus_property_append_string, "s", offsetof(Unit, id), true },
846 { "Names", bus_unit_append_names, "as", 0 },
847 { "Following", bus_unit_append_following, "s", 0 },
4ec9a8a4 848 { "Slice", bus_unit_append_slice, "s", 0 },
e2110e5d
MS
849 { "Requires", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUIRES]), true },
850 { "RequiresOverridable", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUIRES_OVERRIDABLE]), true },
851 { "Requisite", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUISITE]), true },
852 { "RequisiteOverridable", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUISITE_OVERRIDABLE]), true },
853 { "Wants", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_WANTS]), true },
7f2cddae 854 { "BindsTo", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_BINDS_TO]), true },
b05afff1 855 { "PartOf", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_PART_OF]), true },
e2110e5d
MS
856 { "RequiredBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUIRED_BY]), true },
857 { "RequiredByOverridable",bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUIRED_BY_OVERRIDABLE]), true },
858 { "WantedBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_WANTED_BY]), true },
859 { "BoundBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_BOUND_BY]), true },
b05afff1 860 { "ConsistsOf", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_CONSISTS_OF]), true },
e2110e5d
MS
861 { "Conflicts", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_CONFLICTS]), true },
862 { "ConflictedBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_CONFLICTED_BY]), true },
863 { "Before", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_BEFORE]), true },
864 { "After", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_AFTER]), true },
865 { "OnFailure", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_ON_FAILURE]), true },
866 { "Triggers", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_TRIGGERS]), true },
867 { "TriggeredBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_TRIGGERED_BY]), true },
7f2cddae
LP
868 { "PropagatesReloadTo", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_PROPAGATES_RELOAD_TO]), true },
869 { "ReloadPropagatedFrom", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_RELOAD_PROPAGATED_FROM]), true },
7c8fa05c 870 { "RequiresMountsFor", bus_property_append_strv, "as", offsetof(Unit, requires_mounts_for), true },
49dbfa7b 871 { "Documentation", bus_property_append_strv, "as", offsetof(Unit, documentation), true },
e2110e5d
MS
872 { "Description", bus_unit_append_description, "s", 0 },
873 { "LoadState", bus_unit_append_load_state, "s", offsetof(Unit, load_state) },
874 { "ActiveState", bus_unit_append_active_state, "s", 0 },
875 { "SubState", bus_unit_append_sub_state, "s", 0 },
876 { "FragmentPath", bus_property_append_string, "s", offsetof(Unit, fragment_path), true },
1b64d026 877 { "SourcePath", bus_property_append_string, "s", offsetof(Unit, source_path), true },
2875e22b 878 { "DropInPaths", bus_property_append_strv, "as", offsetof(Unit, dropin_paths), true },
e2110e5d
MS
879 { "UnitFileState", bus_unit_append_file_state, "s", 0 },
880 { "InactiveExitTimestamp",bus_property_append_usec, "t", offsetof(Unit, inactive_exit_timestamp.realtime) },
881 { "InactiveExitTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, inactive_exit_timestamp.monotonic) },
882 { "ActiveEnterTimestamp", bus_property_append_usec, "t", offsetof(Unit, active_enter_timestamp.realtime) },
883 { "ActiveEnterTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, active_enter_timestamp.monotonic) },
884 { "ActiveExitTimestamp", bus_property_append_usec, "t", offsetof(Unit, active_exit_timestamp.realtime) },
885 { "ActiveExitTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, active_exit_timestamp.monotonic) },
886 { "InactiveEnterTimestamp", bus_property_append_usec, "t", offsetof(Unit, inactive_enter_timestamp.realtime) },
887 { "InactiveEnterTimestampMonotonic",bus_property_append_usec, "t", offsetof(Unit, inactive_enter_timestamp.monotonic) },
888 { "CanStart", bus_unit_append_can_start, "b", 0 },
889 { "CanStop", bus_unit_append_can_stop, "b", 0 },
890 { "CanReload", bus_unit_append_can_reload, "b", 0 },
891 { "CanIsolate", bus_unit_append_can_isolate, "b", 0 },
892 { "Job", bus_unit_append_job, "(uo)", 0 },
893 { "StopWhenUnneeded", bus_property_append_bool, "b", offsetof(Unit, stop_when_unneeded) },
894 { "RefuseManualStart", bus_property_append_bool, "b", offsetof(Unit, refuse_manual_start) },
895 { "RefuseManualStop", bus_property_append_bool, "b", offsetof(Unit, refuse_manual_stop) },
896 { "AllowIsolate", bus_property_append_bool, "b", offsetof(Unit, allow_isolate) },
897 { "DefaultDependencies", bus_property_append_bool, "b", offsetof(Unit, default_dependencies) },
898 { "OnFailureIsolate", bus_property_append_bool, "b", offsetof(Unit, on_failure_isolate) },
899 { "IgnoreOnIsolate", bus_property_append_bool, "b", offsetof(Unit, ignore_on_isolate) },
900 { "IgnoreOnSnapshot", bus_property_append_bool, "b", offsetof(Unit, ignore_on_snapshot) },
e2110e5d
MS
901 { "NeedDaemonReload", bus_unit_append_need_daemon_reload, "b", 0 },
902 { "JobTimeoutUSec", bus_property_append_usec, "t", offsetof(Unit, job_timeout) },
903 { "ConditionTimestamp", bus_property_append_usec, "t", offsetof(Unit, condition_timestamp.realtime) },
904 { "ConditionTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, condition_timestamp.monotonic) },
905 { "ConditionResult", bus_property_append_bool, "b", offsetof(Unit, condition_result) },
906 { "LoadError", bus_unit_append_load_error, "(ss)", 0 },
4ad49000 907 { "ControlGroup", bus_property_append_string, "s", offsetof(Unit, cgroup_path), true },
c2756a68 908 { "Transient", bus_property_append_bool, "b", offsetof(Unit, transient) },
246aa6dd
LP
909 { NULL, }
910};