]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/core/dbus-unit.c
b6f5c398100aaf7fd66e89890c2894fab10eb75f
[thirdparty/systemd.git] / src / core / dbus-unit.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
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
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
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
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <errno.h>
23
24 #include "dbus.h"
25 #include "log.h"
26 #include "dbus-unit.h"
27 #include "bus-errors.h"
28 #include "dbus-common.h"
29 #include "selinux-access.h"
30 #include "cgroup-util.h"
31 #include "strv.h"
32 #include "path-util.h"
33 #include "fileio.h"
34
35 const char bus_unit_interface[] _introspect_("Unit") = BUS_UNIT_INTERFACE;
36
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" \
46 "NeedDaemonReload\0"
47
48 static int bus_unit_append_names(DBusMessageIter *i, const char *property, void *data) {
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
57 SET_FOREACH(t, u->names, j)
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
67 static int bus_unit_append_following(DBusMessageIter *i, const char *property, void *data) {
68 Unit *u = data, *f;
69 const char *d;
70
71 assert(i);
72 assert(property);
73 assert(u);
74
75 f = unit_following(u);
76 d = f ? f->id : "";
77
78 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &d))
79 return -ENOMEM;
80
81 return 0;
82 }
83
84 static 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
92 d = strempty(unit_slice_name(u));
93
94 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &d))
95 return -ENOMEM;
96
97 return 0;
98 }
99
100 static int bus_unit_append_dependencies(DBusMessageIter *i, const char *property, void *data) {
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)
110 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &u->id))
111 return -ENOMEM;
112
113 if (!dbus_message_iter_close_container(i, &sub))
114 return -ENOMEM;
115
116 return 0;
117 }
118
119 static int bus_unit_append_description(DBusMessageIter *i, const char *property, void *data) {
120 Unit *u = data;
121 const char *d;
122
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
135 static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_unit_append_load_state, unit_load_state, UnitLoadState);
136
137 static int bus_unit_append_active_state(DBusMessageIter *i, const char *property, void *data) {
138 Unit *u = data;
139 const char *state;
140
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
153 static int bus_unit_append_sub_state(DBusMessageIter *i, const char *property, void *data) {
154 Unit *u = data;
155 const char *state;
156
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
169 static int bus_unit_append_file_state(DBusMessageIter *i, const char *property, void *data) {
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
185 static int bus_unit_append_can_start(DBusMessageIter *i, const char *property, void *data) {
186 Unit *u = data;
187 dbus_bool_t b;
188
189 assert(i);
190 assert(property);
191 assert(u);
192
193 b = unit_can_start(u) &&
194 !u->refuse_manual_start;
195
196 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
197 return -ENOMEM;
198
199 return 0;
200 }
201
202 static int bus_unit_append_can_stop(DBusMessageIter *i, const char *property, void *data) {
203 Unit *u = data;
204 dbus_bool_t b;
205
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) &&
214 !u->refuse_manual_stop;
215
216 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
217 return -ENOMEM;
218
219 return 0;
220 }
221
222 static int bus_unit_append_can_reload(DBusMessageIter *i, const char *property, void *data) {
223 Unit *u = data;
224 dbus_bool_t b;
225
226 assert(i);
227 assert(property);
228 assert(u);
229
230 b = unit_can_reload(u);
231
232 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
233 return -ENOMEM;
234
235 return 0;
236 }
237
238 static int bus_unit_append_can_isolate(DBusMessageIter *i, const char *property, void *data) {
239 Unit *u = data;
240 dbus_bool_t b;
241
242 assert(i);
243 assert(property);
244 assert(u);
245
246 b = unit_can_isolate(u) &&
247 !u->refuse_manual_start;
248
249 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
250 return -ENOMEM;
251
252 return 0;
253 }
254
255 static int bus_unit_append_job(DBusMessageIter *i, const char *property, void *data) {
256 Unit *u = data;
257 DBusMessageIter sub;
258 _cleanup_free_ char *p = NULL;
259
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
267 if (u->job) {
268
269 p = job_dbus_path(u->job);
270 if (!p)
271 return -ENOMEM;
272
273 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT32, &u->job->id) ||
274 !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &p))
275 return -ENOMEM;
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
283 p = unit_dbus_path(u);
284 if (!p)
285 return -ENOMEM;
286
287 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT32, &id) ||
288 !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &p))
289 return -ENOMEM;
290 }
291
292 if (!dbus_message_iter_close_container(i, &sub))
293 return -ENOMEM;
294
295 return 0;
296 }
297
298 static int bus_unit_append_need_daemon_reload(DBusMessageIter *i, const char *property, void *data) {
299 Unit *u = data;
300 dbus_bool_t b;
301
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
314 static int bus_property_append_condition(DBusMessageIter *i, const char *property, void *data) {
315 Condition **cp = data;
316 Condition *c;
317 const char *name, *param;
318 dbus_bool_t trigger, negate;
319 dbus_int32_t state;
320 DBusMessageIter sub;
321
322 assert(i);
323 assert(property);
324 assert(cp);
325
326 c = *cp;
327 assert(c);
328
329 name = condition_type_to_string(c->type);
330 param = c->parameter;
331 trigger = c->trigger;
332 negate = c->negate;
333 state = c->state;
334
335 if (!dbus_message_iter_open_container(i, DBUS_TYPE_STRUCT, NULL, &sub) ||
336 !dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &name) ||
337 !dbus_message_iter_append_basic(&sub, DBUS_TYPE_BOOLEAN, &trigger) ||
338 !dbus_message_iter_append_basic(&sub, DBUS_TYPE_BOOLEAN, &negate) ||
339 !dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &param) ||
340 !dbus_message_iter_append_basic(&sub, DBUS_TYPE_INT32, &state) ||
341 !dbus_message_iter_close_container(i, &sub))
342 return -ENOMEM;
343
344 return 0;
345 }
346
347 static int bus_property_append_condition_list(DBusMessageIter *i, const char *property, void *data) {
348 Condition **first = data, *c;
349 DBusMessageIter sub;
350
351 assert(i);
352 assert(data);
353
354 if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "(sbbsi)", &sub))
355 return -ENOMEM;
356
357 LIST_FOREACH(conditions, c, *first)
358 bus_property_append_condition(&sub, property, &c);
359
360 if (!dbus_message_iter_close_container(i, &sub))
361 return -ENOMEM;
362
363 return 0;
364 }
365
366 static int bus_unit_append_load_error(DBusMessageIter *i, const char *property, void *data) {
367 Unit *u = data;
368 const char *name, *message;
369 DBusMessageIter sub;
370
371 assert(i);
372 assert(property);
373 assert(u);
374
375 if (u->load_error != 0) {
376 name = bus_errno_to_dbus(u->load_error);
377 message = strempty(strerror(-u->load_error));
378 } else
379 name = message = "";
380
381 if (!dbus_message_iter_open_container(i, DBUS_TYPE_STRUCT, NULL, &sub) ||
382 !dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &name) ||
383 !dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &message) ||
384 !dbus_message_iter_close_container(i, &sub))
385 return -ENOMEM;
386
387 return 0;
388 }
389
390 static DBusHandlerResult bus_unit_message_dispatch(Unit *u, DBusConnection *connection, DBusMessage *message) {
391 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
392 DBusError error;
393 JobType job_type = _JOB_TYPE_INVALID;
394 bool reload_if_possible = false;
395 int r;
396
397 dbus_error_init(&error);
398
399 if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Start"))
400 job_type = JOB_START;
401 else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Stop"))
402 job_type = JOB_STOP;
403 else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Reload"))
404 job_type = JOB_RELOAD;
405 else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Restart"))
406 job_type = JOB_RESTART;
407 else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "TryRestart"))
408 job_type = JOB_TRY_RESTART;
409 else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "ReloadOrRestart")) {
410 reload_if_possible = true;
411 job_type = JOB_RESTART;
412 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "ReloadOrTryRestart")) {
413 reload_if_possible = true;
414 job_type = JOB_TRY_RESTART;
415 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Kill")) {
416 const char *swho;
417 int32_t signo;
418 KillWho who;
419
420 if (!dbus_message_get_args(
421 message,
422 &error,
423 DBUS_TYPE_STRING, &swho,
424 DBUS_TYPE_INT32, &signo,
425 DBUS_TYPE_INVALID))
426 return bus_send_error_reply(connection, message, &error, -EINVAL);
427
428 if (isempty(swho))
429 who = KILL_ALL;
430 else {
431 who = kill_who_from_string(swho);
432 if (who < 0)
433 return bus_send_error_reply(connection, message, &error, -EINVAL);
434 }
435
436 if (signo <= 0 || signo >= _NSIG)
437 return bus_send_error_reply(connection, message, &error, -EINVAL);
438
439 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "stop");
440
441 r = unit_kill(u, who, signo, &error);
442 if (r < 0)
443 return bus_send_error_reply(connection, message, &error, r);
444
445 reply = dbus_message_new_method_return(message);
446 if (!reply)
447 goto oom;
448
449 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "ResetFailed")) {
450
451 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "reload");
452
453 unit_reset_failed(u);
454
455 reply = dbus_message_new_method_return(message);
456 if (!reply)
457 goto oom;
458 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "SetProperties")) {
459 DBusMessageIter iter;
460 dbus_bool_t runtime;
461
462 if (!dbus_message_iter_init(message, &iter))
463 goto oom;
464
465 if (bus_iter_get_basic_and_next(&iter, DBUS_TYPE_BOOLEAN, &runtime, true) < 0)
466 return bus_send_error_reply(connection, message, NULL, -EINVAL);
467
468 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "start");
469
470 r = bus_unit_set_properties(u, &iter, runtime ? UNIT_RUNTIME : UNIT_PERSISTENT, true, &error);
471 if (r < 0)
472 return bus_send_error_reply(connection, message, &error, r);
473
474 reply = dbus_message_new_method_return(message);
475 if (!reply)
476 goto oom;
477
478 } else if (UNIT_VTABLE(u)->bus_message_handler)
479 return UNIT_VTABLE(u)->bus_message_handler(u, connection, message);
480 else
481 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
482
483 if (job_type != _JOB_TYPE_INVALID) {
484 const char *smode;
485 JobMode mode;
486
487 if (!dbus_message_get_args(
488 message,
489 &error,
490 DBUS_TYPE_STRING, &smode,
491 DBUS_TYPE_INVALID))
492 return bus_send_error_reply(connection, message, &error, -EINVAL);
493
494 mode = job_mode_from_string(smode);
495 if (mode < 0) {
496 dbus_set_error(&error, BUS_ERROR_INVALID_JOB_MODE, "Job mode %s is invalid.", smode);
497 return bus_send_error_reply(connection, message, &error, -EINVAL);
498 }
499
500 return bus_unit_queue_job(connection, message, u, job_type, mode, reload_if_possible);
501 }
502
503 if (reply)
504 if (!bus_maybe_send_reply(connection, message, reply))
505 goto oom;
506
507 return DBUS_HANDLER_RESULT_HANDLED;
508
509 oom:
510 dbus_error_free(&error);
511 return DBUS_HANDLER_RESULT_NEED_MEMORY;
512 }
513
514 static DBusHandlerResult bus_unit_message_handler(DBusConnection *connection, DBusMessage *message, void *data) {
515 Manager *m = data;
516 Unit *u;
517 int r;
518 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
519 DBusError error;
520
521 assert(connection);
522 assert(message);
523 assert(m);
524
525 dbus_error_init(&error);
526
527 if (streq(dbus_message_get_path(message), "/org/freedesktop/systemd1/unit")) {
528 /* Be nice to gdbus and return introspection data for our mid-level paths */
529
530 SELINUX_ACCESS_CHECK(connection, message, "status");
531
532 if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect")) {
533 char *introspection = NULL;
534 FILE *f;
535 Iterator i;
536 const char *k;
537 size_t size;
538
539 reply = dbus_message_new_method_return(message);
540 if (!reply)
541 goto oom;
542
543 /* We roll our own introspection code here, instead of
544 * relying on bus_default_message_handler() because we
545 * need to generate our introspection string
546 * dynamically. */
547
548 f = open_memstream(&introspection, &size);
549 if (!f)
550 goto oom;
551
552 fputs(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
553 "<node>\n", f);
554
555 fputs(BUS_INTROSPECTABLE_INTERFACE, f);
556 fputs(BUS_PEER_INTERFACE, f);
557
558 HASHMAP_FOREACH_KEY(u, k, m->units, i) {
559 char *p;
560
561 if (k != u->id)
562 continue;
563
564 p = bus_path_escape(k);
565 if (!p) {
566 fclose(f);
567 free(introspection);
568 goto oom;
569 }
570
571 fprintf(f, "<node name=\"%s\"/>", p);
572 free(p);
573 }
574
575 fputs("</node>\n", f);
576
577 if (ferror(f)) {
578 fclose(f);
579 free(introspection);
580 goto oom;
581 }
582
583 fclose(f);
584
585 if (!introspection)
586 goto oom;
587
588 if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &introspection, DBUS_TYPE_INVALID)) {
589 free(introspection);
590 goto oom;
591 }
592
593 free(introspection);
594
595 if (!bus_maybe_send_reply(connection, message, reply))
596 goto oom;
597
598 return DBUS_HANDLER_RESULT_HANDLED;
599 }
600
601 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
602 }
603
604 r = manager_load_unit_from_dbus_path(m, dbus_message_get_path(message), &error, &u);
605 if (r == -ENOMEM)
606 goto oom;
607 if (r < 0)
608 return bus_send_error_reply(connection, message, &error, r);
609
610 return bus_unit_message_dispatch(u, connection, message);
611
612 oom:
613 dbus_error_free(&error);
614
615 return DBUS_HANDLER_RESULT_NEED_MEMORY;
616 }
617
618 const DBusObjectPathVTable bus_unit_vtable = {
619 .message_function = bus_unit_message_handler
620 };
621
622 void bus_unit_send_change_signal(Unit *u) {
623 _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
624 _cleanup_free_ char *p = NULL;
625 int r;
626
627 assert(u);
628
629 if (u->in_dbus_queue) {
630 LIST_REMOVE(dbus_queue, u->manager->dbus_unit_queue, u);
631 u->in_dbus_queue = false;
632 }
633
634 if (!u->id)
635 return;
636
637 if (!bus_has_subscriber(u->manager)) {
638 u->sent_dbus_new_signal = true;
639 return;
640 }
641
642 p = unit_dbus_path(u);
643 if (!p) {
644 log_oom();
645 return;
646 }
647
648 if (u->sent_dbus_new_signal) {
649 /* Send a properties changed signal. First for the
650 * specific type, then for the generic unit. The
651 * clients may rely on this order to get atomic
652 * behavior if needed. */
653
654 if (UNIT_VTABLE(u)->bus_invalidating_properties) {
655
656 m = bus_properties_changed_new(p,
657 UNIT_VTABLE(u)->bus_interface,
658 UNIT_VTABLE(u)->bus_invalidating_properties);
659 if (!m) {
660 log_oom();
661 return;
662 }
663
664 r = bus_broadcast(u->manager, m);
665 if (r < 0) {
666 log_error("Failed to broadcast change message: %s", strerror(-r));
667 return;
668 }
669
670 dbus_message_unref(m);
671 }
672
673 m = bus_properties_changed_new(p, "org.freedesktop.systemd1.Unit",
674 INVALIDATING_PROPERTIES);
675 if (!m) {
676 log_oom();
677 return;
678 }
679
680 } else {
681 /* Send a new signal */
682
683 m = dbus_message_new_signal("/org/freedesktop/systemd1",
684 "org.freedesktop.systemd1.Manager",
685 "UnitNew");
686 if (!m) {
687 log_oom();
688 return;
689 }
690
691 if (!dbus_message_append_args(m,
692 DBUS_TYPE_STRING, &u->id,
693 DBUS_TYPE_OBJECT_PATH, &p,
694 DBUS_TYPE_INVALID)) {
695 log_oom();
696 return;
697 }
698 }
699
700 r = bus_broadcast(u->manager, m);
701 if (r < 0) {
702 log_error("Failed to broadcast UnitNew/PropertiesChanged message.");
703 return;
704 }
705
706 u->sent_dbus_new_signal = true;
707 }
708
709 void bus_unit_send_removed_signal(Unit *u) {
710 _cleanup_free_ char *p = NULL;
711 _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
712
713 assert(u);
714
715 if (!bus_has_subscriber(u->manager))
716 return;
717
718 if (!u->sent_dbus_new_signal)
719 bus_unit_send_change_signal(u);
720
721 if (!u->id)
722 return;
723
724 p = unit_dbus_path(u);
725 if (!p)
726 goto oom;
727
728 m = dbus_message_new_signal("/org/freedesktop/systemd1",
729 "org.freedesktop.systemd1.Manager",
730 "UnitRemoved");
731 if (!m)
732 goto oom;
733
734 if (!dbus_message_append_args(m,
735 DBUS_TYPE_STRING, &u->id,
736 DBUS_TYPE_OBJECT_PATH, &p,
737 DBUS_TYPE_INVALID))
738 goto oom;
739
740 if (bus_broadcast(u->manager, m) < 0)
741 goto oom;
742
743 return;
744
745 oom:
746 log_oom();
747 }
748
749 DBusHandlerResult bus_unit_queue_job(
750 DBusConnection *connection,
751 DBusMessage *message,
752 Unit *u,
753 JobType type,
754 JobMode mode,
755 bool reload_if_possible) {
756
757 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
758 _cleanup_free_ char *path = NULL;
759 Job *j;
760 JobBusClient *cl;
761 DBusError error;
762 int r;
763
764 assert(connection);
765 assert(message);
766 assert(u);
767 assert(type >= 0 && type < _JOB_TYPE_MAX);
768 assert(mode >= 0 && mode < _JOB_MODE_MAX);
769
770 dbus_error_init(&error);
771
772 if (reload_if_possible && unit_can_reload(u)) {
773 if (type == JOB_RESTART)
774 type = JOB_RELOAD_OR_START;
775 else if (type == JOB_TRY_RESTART)
776 type = JOB_RELOAD;
777 }
778
779 SELINUX_UNIT_ACCESS_CHECK(u, connection, message,
780 (type == JOB_START || type == JOB_RESTART || type == JOB_TRY_RESTART) ? "start" :
781 type == JOB_STOP ? "stop" : "reload");
782
783 if (type == JOB_STOP && (u->load_state == UNIT_NOT_FOUND || u->load_state == UNIT_ERROR) && unit_active_state(u) == UNIT_INACTIVE) {
784 dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s not loaded.", u->id);
785 return bus_send_error_reply(connection, message, &error, -EPERM);
786 }
787
788 if ((type == JOB_START && u->refuse_manual_start) ||
789 (type == JOB_STOP && u->refuse_manual_stop) ||
790 ((type == JOB_RESTART || type == JOB_TRY_RESTART) && (u->refuse_manual_start || u->refuse_manual_stop))) {
791 dbus_set_error(&error, BUS_ERROR_ONLY_BY_DEPENDENCY,
792 "Operation refused, unit %s may be requested by dependency only.", u->id);
793 return bus_send_error_reply(connection, message, &error, -EPERM);
794 }
795
796 r = manager_add_job(u->manager, type, u, mode, true, &error, &j);
797 if (r < 0)
798 return bus_send_error_reply(connection, message, &error, r);
799
800 cl = job_bus_client_new(connection, bus_message_get_sender_with_fallback(message));
801 if (!cl)
802 goto oom;
803
804 LIST_PREPEND(client, j->bus_client_list, cl);
805
806 reply = dbus_message_new_method_return(message);
807 if (!reply)
808 goto oom;
809
810 path = job_dbus_path(j);
811 if (!path)
812 goto oom;
813
814 if (!dbus_message_append_args(
815 reply,
816 DBUS_TYPE_OBJECT_PATH, &path,
817 DBUS_TYPE_INVALID))
818 goto oom;
819
820 if (!bus_maybe_send_reply(connection, message, reply))
821 goto oom;
822
823 return DBUS_HANDLER_RESULT_HANDLED;
824
825 oom:
826 dbus_error_free(&error);
827
828 return DBUS_HANDLER_RESULT_NEED_MEMORY;
829 }
830
831 static int bus_unit_set_transient_property(
832 Unit *u,
833 const char *name,
834 DBusMessageIter *i,
835 UnitSetPropertiesMode mode,
836 DBusError *error) {
837
838 int r;
839
840 assert(u);
841 assert(name);
842 assert(i);
843
844 if (streq(name, "Description")) {
845 if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_STRING)
846 return -EINVAL;
847
848 if (mode != UNIT_CHECK) {
849 const char *description;
850
851 dbus_message_iter_get_basic(i, &description);
852
853 r = unit_set_description(u, description);
854 if (r < 0)
855 return r;
856
857 unit_write_drop_in_format(u, mode, name, "[Unit]\nDescription=%s\n", description);
858 }
859
860 return 1;
861
862 } else if (streq(name, "Slice") && unit_get_cgroup_context(u)) {
863 const char *s;
864
865 if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_STRING)
866 return -EINVAL;
867
868 dbus_message_iter_get_basic(i, &s);
869
870 if (isempty(s)) {
871 if (mode != UNIT_CHECK) {
872 unit_ref_unset(&u->slice);
873 unit_remove_drop_in(u, mode, name);
874 }
875 } else {
876 Unit *slice;
877
878 r = manager_load_unit(u->manager, s, NULL, error, &slice);
879 if (r < 0)
880 return r;
881
882 if (slice->type != UNIT_SLICE)
883 return -EINVAL;
884
885 if (mode != UNIT_CHECK) {
886 unit_ref_set(&u->slice, slice);
887 unit_write_drop_in_private_format(u, mode, name, "Slice=%s\n", s);
888 }
889 }
890
891 return 1;
892
893 } else if (streq(name, "Requires") ||
894 streq(name, "RequiresOverridable") ||
895 streq(name, "Requisite") ||
896 streq(name, "RequisiteOverridable") ||
897 streq(name, "Wants") ||
898 streq(name, "BindsTo") ||
899 streq(name, "Conflicts") ||
900 streq(name, "Before") ||
901 streq(name, "After") ||
902 streq(name, "OnFailure") ||
903 streq(name, "PropagatesReloadTo") ||
904 streq(name, "ReloadPropagatedFrom") ||
905 streq(name, "PartOf")) {
906
907 UnitDependency d;
908 DBusMessageIter sub;
909
910 d = unit_dependency_from_string(name);
911 if (d < 0)
912 return -EINVAL;
913
914 if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_ARRAY ||
915 dbus_message_iter_get_element_type(i) != DBUS_TYPE_STRING)
916 return -EINVAL;
917
918 dbus_message_iter_recurse(i, &sub);
919 while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING) {
920 const char *other;
921
922 dbus_message_iter_get_basic(&sub, &other);
923
924 if (!unit_name_is_valid(other, false))
925 return -EINVAL;
926
927 if (mode != UNIT_CHECK) {
928 _cleanup_free_ char *label = NULL;
929
930 r = unit_add_dependency_by_name(u, d, other, NULL, true);
931 if (r < 0)
932 return r;
933
934 label = strjoin(name, "-", other, NULL);
935 if (!label)
936 return -ENOMEM;
937
938 unit_write_drop_in_format(u, mode, label, "[Unit]\n%s=%s\n", name, other);
939 }
940
941 dbus_message_iter_next(&sub);
942 }
943
944 return 1;
945 }
946
947 return 0;
948 }
949
950 int bus_unit_set_properties(
951 Unit *u,
952 DBusMessageIter *iter,
953 UnitSetPropertiesMode mode,
954 bool commit,
955 DBusError *error) {
956
957 bool for_real = false;
958 DBusMessageIter sub;
959 unsigned n = 0;
960 int r;
961
962 assert(u);
963 assert(iter);
964
965 if (u->transient)
966 mode &= UNIT_RUNTIME;
967
968 /* We iterate through the array twice. First run we just check
969 * if all passed data is valid, second run actually applies
970 * it. This is to implement transaction-like behaviour without
971 * actually providing full transactions. */
972
973 if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY ||
974 dbus_message_iter_get_element_type(iter) != DBUS_TYPE_STRUCT)
975 return -EINVAL;
976
977 dbus_message_iter_recurse(iter, &sub);
978 for (;;) {
979 DBusMessageIter sub2, sub3;
980 const char *name;
981
982 if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_INVALID) {
983
984 if (for_real || mode == UNIT_CHECK)
985 break;
986
987 /* Reached EOF. Let's try again, and this time for realz... */
988 dbus_message_iter_recurse(iter, &sub);
989 for_real = true;
990 continue;
991 }
992
993 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT)
994 return -EINVAL;
995
996 dbus_message_iter_recurse(&sub, &sub2);
997
998 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &name, true) < 0 ||
999 dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_VARIANT)
1000 return -EINVAL;
1001
1002 if (!UNIT_VTABLE(u)->bus_set_property) {
1003 dbus_set_error(error, DBUS_ERROR_PROPERTY_READ_ONLY, "Objects of this type do not support setting properties.");
1004 return -ENOENT;
1005 }
1006
1007 dbus_message_iter_recurse(&sub2, &sub3);
1008 r = UNIT_VTABLE(u)->bus_set_property(u, name, &sub3, for_real ? mode : UNIT_CHECK, error);
1009 if (r == 0 && u->transient && u->load_state == UNIT_STUB)
1010 r = bus_unit_set_transient_property(u, name, &sub3, for_real ? mode : UNIT_CHECK, error);
1011 if (r < 0)
1012 return r;
1013 if (r == 0) {
1014 dbus_set_error(error, DBUS_ERROR_PROPERTY_READ_ONLY, "Cannot set property %s, or unknown property.", name);
1015 return -ENOENT;
1016 }
1017
1018 dbus_message_iter_next(&sub);
1019
1020 n += for_real;
1021 }
1022
1023 if (commit && n > 0 && UNIT_VTABLE(u)->bus_commit_properties)
1024 UNIT_VTABLE(u)->bus_commit_properties(u);
1025
1026 return n;
1027 }
1028
1029 const BusProperty bus_unit_properties[] = {
1030 { "Id", bus_property_append_string, "s", offsetof(Unit, id), true },
1031 { "Names", bus_unit_append_names, "as", 0 },
1032 { "Following", bus_unit_append_following, "s", 0 },
1033 { "Requires", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUIRES]), true },
1034 { "RequiresOverridable", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUIRES_OVERRIDABLE]), true },
1035 { "Requisite", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUISITE]), true },
1036 { "RequisiteOverridable", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUISITE_OVERRIDABLE]), true },
1037 { "Wants", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_WANTS]), true },
1038 { "BindsTo", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_BINDS_TO]), true },
1039 { "PartOf", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_PART_OF]), true },
1040 { "RequiredBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUIRED_BY]), true },
1041 { "RequiredByOverridable", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUIRED_BY_OVERRIDABLE]), true },
1042 { "WantedBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_WANTED_BY]), true },
1043 { "BoundBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_BOUND_BY]), true },
1044 { "ConsistsOf", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_CONSISTS_OF]), true },
1045 { "Conflicts", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_CONFLICTS]), true },
1046 { "ConflictedBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_CONFLICTED_BY]), true },
1047 { "Before", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_BEFORE]), true },
1048 { "After", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_AFTER]), true },
1049 { "OnFailure", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_ON_FAILURE]), true },
1050 { "Triggers", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_TRIGGERS]), true },
1051 { "TriggeredBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_TRIGGERED_BY]), true },
1052 { "PropagatesReloadTo", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_PROPAGATES_RELOAD_TO]), true },
1053 { "ReloadPropagatedFrom", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_RELOAD_PROPAGATED_FROM]), true },
1054 { "RequiresMountsFor", bus_property_append_strv, "as", offsetof(Unit, requires_mounts_for), true },
1055 { "Documentation", bus_property_append_strv, "as", offsetof(Unit, documentation), true },
1056 { "Description", bus_unit_append_description, "s", 0 },
1057 { "LoadState", bus_unit_append_load_state, "s", offsetof(Unit, load_state) },
1058 { "ActiveState", bus_unit_append_active_state, "s", 0 },
1059 { "SubState", bus_unit_append_sub_state, "s", 0 },
1060 { "FragmentPath", bus_property_append_string, "s", offsetof(Unit, fragment_path), true },
1061 { "SourcePath", bus_property_append_string, "s", offsetof(Unit, source_path), true },
1062 { "DropInPaths", bus_property_append_strv, "as", offsetof(Unit, dropin_paths), true },
1063 { "UnitFileState", bus_unit_append_file_state, "s", 0 },
1064 { "InactiveExitTimestamp", bus_property_append_usec, "t", offsetof(Unit, inactive_exit_timestamp.realtime) },
1065 { "InactiveExitTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, inactive_exit_timestamp.monotonic) },
1066 { "ActiveEnterTimestamp", bus_property_append_usec, "t", offsetof(Unit, active_enter_timestamp.realtime) },
1067 { "ActiveEnterTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, active_enter_timestamp.monotonic) },
1068 { "ActiveExitTimestamp", bus_property_append_usec, "t", offsetof(Unit, active_exit_timestamp.realtime) },
1069 { "ActiveExitTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, active_exit_timestamp.monotonic) },
1070 { "InactiveEnterTimestamp", bus_property_append_usec, "t", offsetof(Unit, inactive_enter_timestamp.realtime) },
1071 { "InactiveEnterTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, inactive_enter_timestamp.monotonic) },
1072 { "CanStart", bus_unit_append_can_start, "b", 0 },
1073 { "CanStop", bus_unit_append_can_stop, "b", 0 },
1074 { "CanReload", bus_unit_append_can_reload, "b", 0 },
1075 { "CanIsolate", bus_unit_append_can_isolate, "b", 0 },
1076 { "Job", bus_unit_append_job, "(uo)", 0 },
1077 { "StopWhenUnneeded", bus_property_append_bool, "b", offsetof(Unit, stop_when_unneeded) },
1078 { "RefuseManualStart", bus_property_append_bool, "b", offsetof(Unit, refuse_manual_start) },
1079 { "RefuseManualStop", bus_property_append_bool, "b", offsetof(Unit, refuse_manual_stop) },
1080 { "AllowIsolate", bus_property_append_bool, "b", offsetof(Unit, allow_isolate) },
1081 { "DefaultDependencies", bus_property_append_bool, "b", offsetof(Unit, default_dependencies) },
1082 { "OnFailureIsolate", bus_property_append_bool, "b", offsetof(Unit, on_failure_isolate) },
1083 { "IgnoreOnIsolate", bus_property_append_bool, "b", offsetof(Unit, ignore_on_isolate) },
1084 { "IgnoreOnSnapshot", bus_property_append_bool, "b", offsetof(Unit, ignore_on_snapshot) },
1085 { "NeedDaemonReload", bus_unit_append_need_daemon_reload, "b", 0 },
1086 { "JobTimeoutUSec", bus_property_append_usec, "t", offsetof(Unit, job_timeout) },
1087 { "ConditionTimestamp", bus_property_append_usec, "t", offsetof(Unit, condition_timestamp.realtime) },
1088 { "ConditionTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, condition_timestamp.monotonic) },
1089 { "ConditionResult", bus_property_append_bool, "b", offsetof(Unit, condition_result) },
1090 { "Conditions", bus_property_append_condition_list, "a(sbbsi)", offsetof(Unit, conditions) },
1091 { "LoadError", bus_unit_append_load_error, "(ss)", 0 },
1092 { "Transient", bus_property_append_bool, "b", offsetof(Unit, transient) },
1093 {}
1094 };
1095
1096 const BusProperty bus_unit_cgroup_properties[] = {
1097 { "Slice", bus_unit_append_slice, "s", 0 },
1098 { "ControlGroup", bus_property_append_string, "s", offsetof(Unit, cgroup_path), true },
1099 {}
1100 };