]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/core/dbus-unit.c
core: add transient units
[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_unit_append_load_error(DBusMessageIter *i, const char *property, void *data) {
315 Unit *u = data;
316 const char *name, *message;
317 DBusMessageIter sub;
318
319 assert(i);
320 assert(property);
321 assert(u);
322
323 if (u->load_error != 0) {
324 name = bus_errno_to_dbus(u->load_error);
325 message = strempty(strerror(-u->load_error));
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
338 static DBusHandlerResult bus_unit_message_dispatch(Unit *u, DBusConnection *connection, DBusMessage *message) {
339 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
340 DBusError error;
341 JobType job_type = _JOB_TYPE_INVALID;
342 bool reload_if_possible = false;
343 int r;
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;
355 else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "TryRestart"))
356 job_type = JOB_TRY_RESTART;
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;
363 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Kill")) {
364 const char *swho;
365 int32_t signo;
366 KillWho who;
367
368 if (!dbus_message_get_args(
369 message,
370 &error,
371 DBUS_TYPE_STRING, &swho,
372 DBUS_TYPE_INT32, &signo,
373 DBUS_TYPE_INVALID))
374 return bus_send_error_reply(connection, message, &error, -EINVAL);
375
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
384 if (signo <= 0 || signo >= _NSIG)
385 return bus_send_error_reply(connection, message, &error, -EINVAL);
386
387 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "stop");
388
389 r = unit_kill(u, who, signo, &error);
390 if (r < 0)
391 return bus_send_error_reply(connection, message, &error, r);
392
393 reply = dbus_message_new_method_return(message);
394 if (!reply)
395 goto oom;
396
397 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "ResetFailed")) {
398
399 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "reload");
400
401 unit_reset_failed(u);
402
403 reply = dbus_message_new_method_return(message);
404 if (!reply)
405 goto oom;
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
418 r = bus_unit_set_properties(u, &iter, runtime ? UNIT_RUNTIME : UNIT_PERSISTENT, true, &error);
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;
425
426 } else if (UNIT_VTABLE(u)->bus_message_handler)
427 return UNIT_VTABLE(u)->bus_message_handler(u, connection, message);
428 else
429 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
430
431 if (job_type != _JOB_TYPE_INVALID) {
432 const char *smode;
433 JobMode mode;
434
435 if (!dbus_message_get_args(
436 message,
437 &error,
438 DBUS_TYPE_STRING, &smode,
439 DBUS_TYPE_INVALID))
440 return bus_send_error_reply(connection, message, &error, -EINVAL);
441
442 mode = job_mode_from_string(smode);
443 if (mode < 0) {
444 dbus_set_error(&error, BUS_ERROR_INVALID_JOB_MODE, "Job mode %s is invalid.", smode);
445 return bus_send_error_reply(connection, message, &error, -EINVAL);
446 }
447
448 return bus_unit_queue_job(connection, message, u, job_type, mode, reload_if_possible);
449 }
450
451 if (reply)
452 if (!bus_maybe_send_reply(connection, message, reply))
453 goto oom;
454
455 return DBUS_HANDLER_RESULT_HANDLED;
456
457 oom:
458 dbus_error_free(&error);
459 return DBUS_HANDLER_RESULT_NEED_MEMORY;
460 }
461
462 static DBusHandlerResult bus_unit_message_handler(DBusConnection *connection, DBusMessage *message, void *data) {
463 Manager *m = data;
464 Unit *u;
465 int r;
466 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
467 DBusError error;
468
469 assert(connection);
470 assert(message);
471 assert(m);
472
473 dbus_error_init(&error);
474
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
478 SELINUX_ACCESS_CHECK(connection, message, "status");
479
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
487 reply = dbus_message_new_method_return(message);
488 if (!reply)
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
496 f = open_memstream(&introspection, &size);
497 if (!f)
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
509 if (k != u->id)
510 continue;
511
512 p = bus_path_escape(k);
513 if (!p) {
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
543 if (!bus_maybe_send_reply(connection, message, reply))
544 goto oom;
545
546 return DBUS_HANDLER_RESULT_HANDLED;
547 }
548
549 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
550 }
551
552 r = manager_load_unit_from_dbus_path(m, dbus_message_get_path(message), &error, &u);
553 if (r == -ENOMEM)
554 goto oom;
555 if (r < 0)
556 return bus_send_error_reply(connection, message, &error, r);
557
558 return bus_unit_message_dispatch(u, connection, message);
559
560 oom:
561 dbus_error_free(&error);
562
563 return DBUS_HANDLER_RESULT_NEED_MEMORY;
564 }
565
566 const DBusObjectPathVTable bus_unit_vtable = {
567 .message_function = bus_unit_message_handler
568 };
569
570 void bus_unit_send_change_signal(Unit *u) {
571 _cleanup_free_ char *p = NULL;
572 _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
573
574 assert(u);
575
576 if (u->in_dbus_queue) {
577 LIST_REMOVE(Unit, dbus_queue, u->manager->dbus_unit_queue, u);
578 u->in_dbus_queue = false;
579 }
580
581 if (!u->id)
582 return;
583
584 if (!bus_has_subscriber(u->manager)) {
585 u->sent_dbus_new_signal = true;
586 return;
587 }
588
589 p = unit_dbus_path(u);
590 if (!p)
591 goto oom;
592
593 if (u->sent_dbus_new_signal) {
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
597 * behavior if needed. */
598
599 if (UNIT_VTABLE(u)->bus_invalidating_properties) {
600
601 m = bus_properties_changed_new(p,
602 UNIT_VTABLE(u)->bus_interface,
603 UNIT_VTABLE(u)->bus_invalidating_properties);
604 if (!m)
605 goto oom;
606
607 if (bus_broadcast(u->manager, m) < 0)
608 goto oom;
609
610 dbus_message_unref(m);
611 }
612
613 m = bus_properties_changed_new(p, "org.freedesktop.systemd1.Unit",
614 INVALIDATING_PROPERTIES);
615 if (!m)
616 goto oom;
617
618 } else {
619 /* Send a new signal */
620
621 m = dbus_message_new_signal("/org/freedesktop/systemd1",
622 "org.freedesktop.systemd1.Manager",
623 "UnitNew");
624 if (!m)
625 goto oom;
626
627 if (!dbus_message_append_args(m,
628 DBUS_TYPE_STRING, &u->id,
629 DBUS_TYPE_OBJECT_PATH, &p,
630 DBUS_TYPE_INVALID))
631 goto oom;
632 }
633
634 if (bus_broadcast(u->manager, m) < 0)
635 goto oom;
636
637 u->sent_dbus_new_signal = true;
638
639 return;
640
641 oom:
642 log_oom();
643 }
644
645 void bus_unit_send_removed_signal(Unit *u) {
646 _cleanup_free_ char *p = NULL;
647 _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
648
649 assert(u);
650
651 if (!bus_has_subscriber(u->manager))
652 return;
653
654 if (!u->sent_dbus_new_signal)
655 bus_unit_send_change_signal(u);
656
657 if (!u->id)
658 return;
659
660 p = unit_dbus_path(u);
661 if (!p)
662 goto oom;
663
664 m = dbus_message_new_signal("/org/freedesktop/systemd1",
665 "org.freedesktop.systemd1.Manager",
666 "UnitRemoved");
667 if (!m)
668 goto oom;
669
670 if (!dbus_message_append_args(m,
671 DBUS_TYPE_STRING, &u->id,
672 DBUS_TYPE_OBJECT_PATH, &p,
673 DBUS_TYPE_INVALID))
674 goto oom;
675
676 if (bus_broadcast(u->manager, m) < 0)
677 goto oom;
678
679 return;
680
681 oom:
682 log_oom();
683 }
684
685 DBusHandlerResult 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
719 if (type == JOB_STOP && (u->load_state == UNIT_NOT_FOUND || u->load_state == UNIT_ERROR) && unit_active_state(u) == UNIT_INACTIVE) {
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))) {
727 dbus_set_error(&error, BUS_ERROR_ONLY_BY_DEPENDENCY,
728 "Operation refused, unit %s may be requested by dependency only.", u->id);
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
756 if (!bus_maybe_send_reply(connection, message, reply))
757 goto oom;
758
759 return DBUS_HANDLER_RESULT_HANDLED;
760
761 oom:
762 dbus_error_free(&error);
763
764 return DBUS_HANDLER_RESULT_NEED_MEMORY;
765 }
766
767 int bus_unit_set_properties(
768 Unit *u,
769 DBusMessageIter *iter,
770 UnitSetPropertiesMode mode,
771 bool commit,
772 DBusError *error) {
773
774 bool for_real = false;
775 DBusMessageIter sub;
776 unsigned n = 0;
777 int r;
778
779 assert(u);
780 assert(iter);
781
782 if (u->transient)
783 mode &= UNIT_RUNTIME;
784
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);
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
838 if (commit && n > 0 && UNIT_VTABLE(u)->bus_commit_properties)
839 UNIT_VTABLE(u)->bus_commit_properties(u);
840
841 return n;
842 }
843
844 const 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 },
848 { "Slice", bus_unit_append_slice, "s", 0 },
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 },
854 { "BindsTo", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_BINDS_TO]), true },
855 { "PartOf", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_PART_OF]), true },
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 },
860 { "ConsistsOf", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_CONSISTS_OF]), true },
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 },
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 },
870 { "RequiresMountsFor", bus_property_append_strv, "as", offsetof(Unit, requires_mounts_for), true },
871 { "Documentation", bus_property_append_strv, "as", offsetof(Unit, documentation), true },
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 },
877 { "SourcePath", bus_property_append_string, "s", offsetof(Unit, source_path), true },
878 { "DropInPaths", bus_property_append_strv, "as", offsetof(Unit, dropin_paths), true },
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) },
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 },
907 { "ControlGroup", bus_property_append_string, "s", offsetof(Unit, cgroup_path), true },
908 { "Transient", bus_property_append_bool, "b", offsetof(Unit, transient) },
909 { NULL, }
910 };