]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/core/dbus-unit.c
nspawn: introduce the new /machine/ tree in the cgroup tree and move containers there
[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_dependencies(DBusMessageIter *i, const char *property, void *data) {
85 Unit *u;
86 Iterator j;
87 DBusMessageIter sub;
88 Set *s = data;
89
90 if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "s", &sub))
91 return -ENOMEM;
92
93 SET_FOREACH(u, s, j)
94 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &u->id))
95 return -ENOMEM;
96
97 if (!dbus_message_iter_close_container(i, &sub))
98 return -ENOMEM;
99
100 return 0;
101 }
102
103 static int bus_unit_append_description(DBusMessageIter *i, const char *property, void *data) {
104 Unit *u = data;
105 const char *d;
106
107 assert(i);
108 assert(property);
109 assert(u);
110
111 d = unit_description(u);
112
113 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &d))
114 return -ENOMEM;
115
116 return 0;
117 }
118
119 static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_unit_append_load_state, unit_load_state, UnitLoadState);
120
121 static int bus_unit_append_active_state(DBusMessageIter *i, const char *property, void *data) {
122 Unit *u = data;
123 const char *state;
124
125 assert(i);
126 assert(property);
127 assert(u);
128
129 state = unit_active_state_to_string(unit_active_state(u));
130
131 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &state))
132 return -ENOMEM;
133
134 return 0;
135 }
136
137 static int bus_unit_append_sub_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_sub_state_to_string(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_file_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 = strempty(unit_file_state_to_string(unit_get_unit_file_state(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_can_start(DBusMessageIter *i, const char *property, void *data) {
170 Unit *u = data;
171 dbus_bool_t b;
172
173 assert(i);
174 assert(property);
175 assert(u);
176
177 b = unit_can_start(u) &&
178 !u->refuse_manual_start;
179
180 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
181 return -ENOMEM;
182
183 return 0;
184 }
185
186 static int bus_unit_append_can_stop(DBusMessageIter *i, const char *property, void *data) {
187 Unit *u = data;
188 dbus_bool_t b;
189
190 assert(i);
191 assert(property);
192 assert(u);
193
194 /* On the lower levels we assume that every unit we can start
195 * we can also stop */
196
197 b = unit_can_start(u) &&
198 !u->refuse_manual_stop;
199
200 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
201 return -ENOMEM;
202
203 return 0;
204 }
205
206 static int bus_unit_append_can_reload(DBusMessageIter *i, const char *property, void *data) {
207 Unit *u = data;
208 dbus_bool_t b;
209
210 assert(i);
211 assert(property);
212 assert(u);
213
214 b = unit_can_reload(u);
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_isolate(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_isolate(u) &&
231 !u->refuse_manual_start;
232
233 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
234 return -ENOMEM;
235
236 return 0;
237 }
238
239 static int bus_unit_append_job(DBusMessageIter *i, const char *property, void *data) {
240 Unit *u = data;
241 DBusMessageIter sub;
242 _cleanup_free_ char *p = NULL;
243
244 assert(i);
245 assert(property);
246 assert(u);
247
248 if (!dbus_message_iter_open_container(i, DBUS_TYPE_STRUCT, NULL, &sub))
249 return -ENOMEM;
250
251 if (u->job) {
252
253 p = job_dbus_path(u->job);
254 if (!p)
255 return -ENOMEM;
256
257 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT32, &u->job->id) ||
258 !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &p))
259 return -ENOMEM;
260 } else {
261 uint32_t id = 0;
262
263 /* No job, so let's fill in some placeholder
264 * data. Since we need to fill in a valid path we
265 * simple point to ourselves. */
266
267 p = unit_dbus_path(u);
268 if (!p)
269 return -ENOMEM;
270
271 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT32, &id) ||
272 !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &p))
273 return -ENOMEM;
274 }
275
276 if (!dbus_message_iter_close_container(i, &sub))
277 return -ENOMEM;
278
279 return 0;
280 }
281
282 static int bus_unit_append_default_cgroup(DBusMessageIter *i, const char *property, void *data) {
283 Unit *u = data;
284 char *t;
285 CGroupBonding *cgb;
286 bool success;
287
288 assert(i);
289 assert(property);
290 assert(u);
291
292 cgb = unit_get_default_cgroup(u);
293 if (cgb) {
294 t = cgroup_bonding_to_string(cgb);
295 if (!t)
296 return -ENOMEM;
297 } else
298 t = (char*) "";
299
300 success = dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &t);
301
302 if (cgb)
303 free(t);
304
305 return success ? 0 : -ENOMEM;
306 }
307
308 static int bus_unit_append_cgroups(DBusMessageIter *i, const char *property, void *data) {
309 Unit *u = data;
310 CGroupBonding *cgb;
311 DBusMessageIter sub;
312
313 if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "s", &sub))
314 return -ENOMEM;
315
316 LIST_FOREACH(by_unit, cgb, u->cgroup_bondings) {
317 char _cleanup_free_ *t = NULL;
318 bool success;
319
320 t = cgroup_bonding_to_string(cgb);
321 if (!t)
322 return -ENOMEM;
323
324 success = dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &t);
325 if (!success)
326 return -ENOMEM;
327 }
328
329 if (!dbus_message_iter_close_container(i, &sub))
330 return -ENOMEM;
331
332 return 0;
333 }
334
335 static int bus_unit_append_cgroup_attrs(DBusMessageIter *i, const char *property, void *data) {
336 Unit *u = data;
337 CGroupAttribute *a;
338 DBusMessageIter sub, sub2;
339
340 if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "(sss)", &sub))
341 return -ENOMEM;
342
343 LIST_FOREACH(by_unit, a, u->cgroup_attributes) {
344 char _cleanup_free_ *v = NULL;
345 bool success;
346
347 if (a->semantics && a->semantics->map_write)
348 a->semantics->map_write(a->semantics, a->value, &v);
349
350 success =
351 dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) &&
352 dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &a->controller) &&
353 dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &a->name) &&
354 dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, v ? &v : &a->value) &&
355 dbus_message_iter_close_container(&sub, &sub2);
356 if (!success)
357 return -ENOMEM;
358 }
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_need_daemon_reload(DBusMessageIter *i, const char *property, void *data) {
367 Unit *u = data;
368 dbus_bool_t b;
369
370 assert(i);
371 assert(property);
372 assert(u);
373
374 b = unit_need_daemon_reload(u);
375
376 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
377 return -ENOMEM;
378
379 return 0;
380 }
381
382 static int bus_unit_append_load_error(DBusMessageIter *i, const char *property, void *data) {
383 Unit *u = data;
384 const char *name, *message;
385 DBusMessageIter sub;
386
387 assert(i);
388 assert(property);
389 assert(u);
390
391 if (u->load_error != 0) {
392 name = bus_errno_to_dbus(u->load_error);
393 message = strempty(strerror(-u->load_error));
394 } else
395 name = message = "";
396
397 if (!dbus_message_iter_open_container(i, DBUS_TYPE_STRUCT, NULL, &sub) ||
398 !dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &name) ||
399 !dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &message) ||
400 !dbus_message_iter_close_container(i, &sub))
401 return -ENOMEM;
402
403 return 0;
404 }
405
406 static DBusHandlerResult bus_unit_message_dispatch(Unit *u, DBusConnection *connection, DBusMessage *message) {
407 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
408 DBusError error;
409 JobType job_type = _JOB_TYPE_INVALID;
410 bool reload_if_possible = false;
411 int r;
412
413 dbus_error_init(&error);
414
415 if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Start"))
416 job_type = JOB_START;
417 else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Stop"))
418 job_type = JOB_STOP;
419 else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Reload"))
420 job_type = JOB_RELOAD;
421 else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Restart"))
422 job_type = JOB_RESTART;
423 else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "TryRestart"))
424 job_type = JOB_TRY_RESTART;
425 else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "ReloadOrRestart")) {
426 reload_if_possible = true;
427 job_type = JOB_RESTART;
428 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "ReloadOrTryRestart")) {
429 reload_if_possible = true;
430 job_type = JOB_TRY_RESTART;
431 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Kill")) {
432 const char *swho;
433 int32_t signo;
434 KillWho who;
435
436 if (!dbus_message_get_args(
437 message,
438 &error,
439 DBUS_TYPE_STRING, &swho,
440 DBUS_TYPE_INT32, &signo,
441 DBUS_TYPE_INVALID))
442 return bus_send_error_reply(connection, message, &error, -EINVAL);
443
444 if (isempty(swho))
445 who = KILL_ALL;
446 else {
447 who = kill_who_from_string(swho);
448 if (who < 0)
449 return bus_send_error_reply(connection, message, &error, -EINVAL);
450 }
451
452 if (signo <= 0 || signo >= _NSIG)
453 return bus_send_error_reply(connection, message, &error, -EINVAL);
454
455 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "stop");
456
457 r = unit_kill(u, who, signo, &error);
458 if (r < 0)
459 return bus_send_error_reply(connection, message, &error, r);
460
461 reply = dbus_message_new_method_return(message);
462 if (!reply)
463 goto oom;
464
465 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "ResetFailed")) {
466
467 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "reload");
468
469 unit_reset_failed(u);
470
471 reply = dbus_message_new_method_return(message);
472 if (!reply)
473 goto oom;
474
475 } else if (streq_ptr(dbus_message_get_member(message), "SetControlGroup")) {
476 DBusMessageIter iter;
477
478 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "start");
479
480 if (!dbus_message_iter_init(message, &iter))
481 goto oom;
482
483 r = bus_unit_cgroup_set(u, &iter);
484 if (r < 0)
485 return bus_send_error_reply(connection, message, NULL, r);
486
487 reply = dbus_message_new_method_return(message);
488 if (!reply)
489 goto oom;
490
491 } else if (streq_ptr(dbus_message_get_member(message), "UnsetControlGroup")) {
492 DBusMessageIter iter;
493
494 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "stop");
495
496 if (!dbus_message_iter_init(message, &iter))
497 goto oom;
498
499 r = bus_unit_cgroup_unset(u, &iter);
500 if (r < 0)
501 return bus_send_error_reply(connection, message, NULL, r);
502
503 reply = dbus_message_new_method_return(message);
504 if (!reply)
505 goto oom;
506 } else if (streq_ptr(dbus_message_get_member(message), "GetControlGroupAttribute")) {
507 DBusMessageIter iter;
508 _cleanup_strv_free_ char **list = NULL;
509
510 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "status");
511
512 if (!dbus_message_iter_init(message, &iter))
513 goto oom;
514
515 r = bus_unit_cgroup_attribute_get(u, &iter, &list);
516 if (r < 0)
517 return bus_send_error_reply(connection, message, NULL, r);
518
519 reply = dbus_message_new_method_return(message);
520 if (!reply)
521 goto oom;
522
523 dbus_message_iter_init_append(reply, &iter);
524 if (bus_append_strv_iter(&iter, list) < 0)
525 goto oom;
526
527 } else if (streq_ptr(dbus_message_get_member(message), "SetControlGroupAttribute")) {
528 DBusMessageIter iter;
529
530 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "start");
531
532 if (!dbus_message_iter_init(message, &iter))
533 goto oom;
534
535 r = bus_unit_cgroup_attribute_set(u, &iter);
536 if (r < 0)
537 return bus_send_error_reply(connection, message, NULL, r);
538
539 reply = dbus_message_new_method_return(message);
540 if (!reply)
541 goto oom;
542
543 } else if (streq_ptr(dbus_message_get_member(message), "UnsetControlGroupAttribute")) {
544 DBusMessageIter iter;
545
546 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "stop");
547
548 if (!dbus_message_iter_init(message, &iter))
549 goto oom;
550
551 r = bus_unit_cgroup_attribute_unset(u, &iter);
552 if (r < 0)
553 return bus_send_error_reply(connection, message, NULL, r);
554
555 reply = dbus_message_new_method_return(message);
556 if (!reply)
557 goto oom;
558
559 } else if (UNIT_VTABLE(u)->bus_message_handler)
560 return UNIT_VTABLE(u)->bus_message_handler(u, connection, message);
561 else
562 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
563
564 if (job_type != _JOB_TYPE_INVALID) {
565 const char *smode;
566 JobMode mode;
567
568 if (!dbus_message_get_args(
569 message,
570 &error,
571 DBUS_TYPE_STRING, &smode,
572 DBUS_TYPE_INVALID))
573 return bus_send_error_reply(connection, message, &error, -EINVAL);
574
575 mode = job_mode_from_string(smode);
576 if (mode < 0) {
577 dbus_set_error(&error, BUS_ERROR_INVALID_JOB_MODE, "Job mode %s is invalid.", smode);
578 return bus_send_error_reply(connection, message, &error, -EINVAL);
579 }
580
581 return bus_unit_queue_job(connection, message, u, job_type, mode, reload_if_possible);
582 }
583
584 if (reply)
585 if (!bus_maybe_send_reply(connection, message, reply))
586 goto oom;
587
588 return DBUS_HANDLER_RESULT_HANDLED;
589
590 oom:
591 dbus_error_free(&error);
592 return DBUS_HANDLER_RESULT_NEED_MEMORY;
593 }
594
595 static DBusHandlerResult bus_unit_message_handler(DBusConnection *connection, DBusMessage *message, void *data) {
596 Manager *m = data;
597 Unit *u;
598 int r;
599 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
600 DBusError error;
601
602 assert(connection);
603 assert(message);
604 assert(m);
605
606 dbus_error_init(&error);
607
608 if (streq(dbus_message_get_path(message), "/org/freedesktop/systemd1/unit")) {
609 /* Be nice to gdbus and return introspection data for our mid-level paths */
610
611 SELINUX_ACCESS_CHECK(connection, message, "status");
612
613 if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect")) {
614 char *introspection = NULL;
615 FILE *f;
616 Iterator i;
617 const char *k;
618 size_t size;
619
620 reply = dbus_message_new_method_return(message);
621 if (!reply)
622 goto oom;
623
624 /* We roll our own introspection code here, instead of
625 * relying on bus_default_message_handler() because we
626 * need to generate our introspection string
627 * dynamically. */
628
629 f = open_memstream(&introspection, &size);
630 if (!f)
631 goto oom;
632
633 fputs(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
634 "<node>\n", f);
635
636 fputs(BUS_INTROSPECTABLE_INTERFACE, f);
637 fputs(BUS_PEER_INTERFACE, f);
638
639 HASHMAP_FOREACH_KEY(u, k, m->units, i) {
640 char *p;
641
642 if (k != u->id)
643 continue;
644
645 p = bus_path_escape(k);
646 if (!p) {
647 fclose(f);
648 free(introspection);
649 goto oom;
650 }
651
652 fprintf(f, "<node name=\"%s\"/>", p);
653 free(p);
654 }
655
656 fputs("</node>\n", f);
657
658 if (ferror(f)) {
659 fclose(f);
660 free(introspection);
661 goto oom;
662 }
663
664 fclose(f);
665
666 if (!introspection)
667 goto oom;
668
669 if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &introspection, DBUS_TYPE_INVALID)) {
670 free(introspection);
671 goto oom;
672 }
673
674 free(introspection);
675
676 if (!bus_maybe_send_reply(connection, message, reply))
677 goto oom;
678
679 return DBUS_HANDLER_RESULT_HANDLED;
680 }
681
682 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
683 }
684
685 r = manager_load_unit_from_dbus_path(m, dbus_message_get_path(message), &error, &u);
686 if (r == -ENOMEM)
687 goto oom;
688 if (r < 0)
689 return bus_send_error_reply(connection, message, &error, r);
690
691 return bus_unit_message_dispatch(u, connection, message);
692
693 oom:
694 dbus_error_free(&error);
695
696 return DBUS_HANDLER_RESULT_NEED_MEMORY;
697 }
698
699 const DBusObjectPathVTable bus_unit_vtable = {
700 .message_function = bus_unit_message_handler
701 };
702
703 void bus_unit_send_change_signal(Unit *u) {
704 _cleanup_free_ char *p = NULL;
705 _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
706
707 assert(u);
708
709 if (u->in_dbus_queue) {
710 LIST_REMOVE(Unit, dbus_queue, u->manager->dbus_unit_queue, u);
711 u->in_dbus_queue = false;
712 }
713
714 if (!u->id)
715 return;
716
717 if (!bus_has_subscriber(u->manager)) {
718 u->sent_dbus_new_signal = true;
719 return;
720 }
721
722 p = unit_dbus_path(u);
723 if (!p)
724 goto oom;
725
726 if (u->sent_dbus_new_signal) {
727 /* Send a properties changed signal. First for the
728 * specific type, then for the generic unit. The
729 * clients may rely on this order to get atomic
730 * behavior if needed. */
731
732 if (UNIT_VTABLE(u)->bus_invalidating_properties) {
733
734 m = bus_properties_changed_new(p,
735 UNIT_VTABLE(u)->bus_interface,
736 UNIT_VTABLE(u)->bus_invalidating_properties);
737 if (!m)
738 goto oom;
739
740 if (bus_broadcast(u->manager, m) < 0)
741 goto oom;
742
743 dbus_message_unref(m);
744 }
745
746 m = bus_properties_changed_new(p, "org.freedesktop.systemd1.Unit",
747 INVALIDATING_PROPERTIES);
748 if (!m)
749 goto oom;
750
751 } else {
752 /* Send a new signal */
753
754 m = dbus_message_new_signal("/org/freedesktop/systemd1",
755 "org.freedesktop.systemd1.Manager",
756 "UnitNew");
757 if (!m)
758 goto oom;
759
760 if (!dbus_message_append_args(m,
761 DBUS_TYPE_STRING, &u->id,
762 DBUS_TYPE_OBJECT_PATH, &p,
763 DBUS_TYPE_INVALID))
764 goto oom;
765 }
766
767 if (bus_broadcast(u->manager, m) < 0)
768 goto oom;
769
770 u->sent_dbus_new_signal = true;
771
772 return;
773
774 oom:
775 log_oom();
776 }
777
778 void bus_unit_send_removed_signal(Unit *u) {
779 _cleanup_free_ char *p = NULL;
780 _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
781
782 assert(u);
783
784 if (!bus_has_subscriber(u->manager))
785 return;
786
787 if (!u->sent_dbus_new_signal)
788 bus_unit_send_change_signal(u);
789
790 if (!u->id)
791 return;
792
793 p = unit_dbus_path(u);
794 if (!p)
795 goto oom;
796
797 m = dbus_message_new_signal("/org/freedesktop/systemd1",
798 "org.freedesktop.systemd1.Manager",
799 "UnitRemoved");
800 if (!m)
801 goto oom;
802
803 if (!dbus_message_append_args(m,
804 DBUS_TYPE_STRING, &u->id,
805 DBUS_TYPE_OBJECT_PATH, &p,
806 DBUS_TYPE_INVALID))
807 goto oom;
808
809 if (bus_broadcast(u->manager, m) < 0)
810 goto oom;
811
812 return;
813
814 oom:
815 log_oom();
816 }
817
818 DBusHandlerResult bus_unit_queue_job(
819 DBusConnection *connection,
820 DBusMessage *message,
821 Unit *u,
822 JobType type,
823 JobMode mode,
824 bool reload_if_possible) {
825
826 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
827 _cleanup_free_ char *path = NULL;
828 Job *j;
829 JobBusClient *cl;
830 DBusError error;
831 int r;
832
833 assert(connection);
834 assert(message);
835 assert(u);
836 assert(type >= 0 && type < _JOB_TYPE_MAX);
837 assert(mode >= 0 && mode < _JOB_MODE_MAX);
838
839 dbus_error_init(&error);
840
841 if (reload_if_possible && unit_can_reload(u)) {
842 if (type == JOB_RESTART)
843 type = JOB_RELOAD_OR_START;
844 else if (type == JOB_TRY_RESTART)
845 type = JOB_RELOAD;
846 }
847
848 SELINUX_UNIT_ACCESS_CHECK(u, connection, message,
849 (type == JOB_START || type == JOB_RESTART || type == JOB_TRY_RESTART) ? "start" :
850 type == JOB_STOP ? "stop" : "reload");
851
852 if (type == JOB_STOP && u->load_state == UNIT_ERROR && unit_active_state(u) == UNIT_INACTIVE) {
853 dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s not loaded.", u->id);
854 return bus_send_error_reply(connection, message, &error, -EPERM);
855 }
856
857 if ((type == JOB_START && u->refuse_manual_start) ||
858 (type == JOB_STOP && u->refuse_manual_stop) ||
859 ((type == JOB_RESTART || type == JOB_TRY_RESTART) && (u->refuse_manual_start || u->refuse_manual_stop))) {
860 dbus_set_error(&error, BUS_ERROR_ONLY_BY_DEPENDENCY,
861 "Operation refused, unit %s may be requested by dependency only.", u->id);
862 return bus_send_error_reply(connection, message, &error, -EPERM);
863 }
864
865 r = manager_add_job(u->manager, type, u, mode, true, &error, &j);
866 if (r < 0)
867 return bus_send_error_reply(connection, message, &error, r);
868
869 cl = job_bus_client_new(connection, bus_message_get_sender_with_fallback(message));
870 if (!cl)
871 goto oom;
872
873 LIST_PREPEND(JobBusClient, client, j->bus_client_list, cl);
874
875 reply = dbus_message_new_method_return(message);
876 if (!reply)
877 goto oom;
878
879 path = job_dbus_path(j);
880 if (!path)
881 goto oom;
882
883 if (!dbus_message_append_args(
884 reply,
885 DBUS_TYPE_OBJECT_PATH, &path,
886 DBUS_TYPE_INVALID))
887 goto oom;
888
889 if (!bus_maybe_send_reply(connection, message, reply))
890 goto oom;
891
892 return DBUS_HANDLER_RESULT_HANDLED;
893
894 oom:
895 dbus_error_free(&error);
896
897 return DBUS_HANDLER_RESULT_NEED_MEMORY;
898 }
899
900 static int parse_mode(DBusMessageIter *iter, bool *runtime, bool next) {
901 const char *mode;
902 int r;
903
904 assert(iter);
905 assert(runtime);
906
907 r = bus_iter_get_basic_and_next(iter, DBUS_TYPE_STRING, &mode, next);
908 if (r < 0)
909 return r;
910
911 if (streq(mode, "runtime"))
912 *runtime = true;
913 else if (streq(mode, "persistent"))
914 *runtime = false;
915 else
916 return -EINVAL;
917
918 return 0;
919 }
920
921 int bus_unit_cgroup_set(Unit *u, DBusMessageIter *iter) {
922 _cleanup_free_ char *controller = NULL, *old_path = NULL, *new_path = NULL, *contents = NULL;
923 const char *name;
924 CGroupBonding *b;
925 bool runtime;
926 int r;
927
928 assert(u);
929 assert(iter);
930
931 if (!unit_get_exec_context(u))
932 return -EINVAL;
933
934 r = bus_iter_get_basic_and_next(iter, DBUS_TYPE_STRING, &name, true);
935 if (r < 0)
936 return r;
937
938 r = parse_mode(iter, &runtime, false);
939 if (r < 0)
940 return r;
941
942 r = cg_split_spec(name, &controller, &new_path);
943 if (r < 0)
944 return r;
945
946 if (!new_path) {
947 new_path = unit_default_cgroup_path(u);
948 if (!new_path)
949 return -ENOMEM;
950 }
951
952 if (!controller || streq(controller, SYSTEMD_CGROUP_CONTROLLER))
953 return -EINVAL;
954
955 b = cgroup_bonding_find_list(u->cgroup_bondings, controller);
956 if (b) {
957 if (streq(b->path, new_path))
958 return 0;
959
960 if (b->essential)
961 return -EINVAL;
962
963 old_path = strdup(b->path);
964 if (!old_path)
965 return -ENOMEM;
966 }
967
968 r = unit_add_cgroup_from_text(u, name, true, &b);
969 if (r < 0)
970 return r;
971 if (r > 0) {
972 CGroupAttribute *a;
973
974 /* Try to move things to the new place, and clean up the old place */
975 cgroup_bonding_realize(b);
976 cgroup_bonding_migrate(b, u->cgroup_bondings);
977
978 if (old_path)
979 cg_trim(controller, old_path, true);
980
981 /* Apply the attributes to the new group */
982 LIST_FOREACH(by_unit, a, u->cgroup_attributes)
983 if (streq(a->controller, controller))
984 cgroup_attribute_apply(a, b);
985 }
986
987 contents = strjoin("[", UNIT_VTABLE(u)->exec_section, "]\n"
988 "ControlGroup=", name, "\n", NULL);
989 if (!contents)
990 return -ENOMEM;
991
992 return unit_write_drop_in(u, runtime, controller, contents);
993 }
994
995 int bus_unit_cgroup_unset(Unit *u, DBusMessageIter *iter) {
996 _cleanup_free_ char *controller = NULL, *path = NULL, *target = NULL;
997 const char *name;
998 CGroupAttribute *a, *n;
999 CGroupBonding *b;
1000 bool runtime;
1001 int r;
1002
1003 assert(u);
1004 assert(iter);
1005
1006 if (!unit_get_exec_context(u))
1007 return -EINVAL;
1008
1009 r = bus_iter_get_basic_and_next(iter, DBUS_TYPE_STRING, &name, true);
1010 if (r < 0)
1011 return r;
1012
1013 r = parse_mode(iter, &runtime, false);
1014 if (r < 0)
1015 return r;
1016
1017 r = cg_split_spec(name, &controller, &path);
1018 if (r < 0)
1019 return r;
1020
1021 if (!controller || streq(controller, SYSTEMD_CGROUP_CONTROLLER))
1022 return -EINVAL;
1023
1024 b = cgroup_bonding_find_list(u->cgroup_bondings, controller);
1025 if (!b)
1026 return -ENOENT;
1027
1028 if (path && !path_equal(path, b->path))
1029 return -ENOENT;
1030
1031 if (b->essential)
1032 return -EINVAL;
1033
1034 unit_remove_drop_in(u, runtime, controller);
1035
1036 /* Try to migrate the old group away */
1037 if (cg_pid_get_path(controller, 0, &target) >= 0)
1038 cgroup_bonding_migrate_to(u->cgroup_bondings, target, false);
1039
1040 cgroup_bonding_free(b, true);
1041
1042 /* Drop all attributes of this controller */
1043 LIST_FOREACH_SAFE(by_unit, a, n, u->cgroup_attributes) {
1044 if (!streq(a->controller, controller))
1045 continue;
1046
1047 unit_remove_drop_in(u, runtime, a->name);
1048 cgroup_attribute_free(a);
1049 }
1050
1051 return 0;
1052 }
1053
1054 int bus_unit_cgroup_attribute_get(Unit *u, DBusMessageIter *iter, char ***_result) {
1055 _cleanup_free_ char *controller = NULL;
1056 CGroupAttribute *a;
1057 CGroupBonding *b;
1058 const char *name;
1059 char **l = NULL;
1060 int r;
1061
1062 assert(u);
1063 assert(iter);
1064 assert(_result);
1065
1066 if (!unit_get_exec_context(u))
1067 return -EINVAL;
1068
1069 r = bus_iter_get_basic_and_next(iter, DBUS_TYPE_STRING, &name, false);
1070 if (r < 0)
1071 return r;
1072
1073 r = cg_controller_from_attr(name, &controller);
1074 if (r < 0)
1075 return r;
1076
1077 /* First attempt, read the value from the kernel */
1078 b = cgroup_bonding_find_list(u->cgroup_bondings, controller);
1079 if (b) {
1080 _cleanup_free_ char *p = NULL, *v = NULL;
1081
1082 r = cg_get_path(b->controller, b->path, name, &p);
1083 if (r < 0)
1084 return r;
1085
1086 r = read_full_file(p, &v, NULL);
1087 if (r >= 0) {
1088 /* Split on new lines */
1089 l = strv_split_newlines(v);
1090 if (!l)
1091 return -ENOMEM;
1092
1093 *_result = l;
1094 return 0;
1095
1096 }
1097 }
1098
1099 /* If that didn't work, read our cached value */
1100 LIST_FOREACH(by_unit, a, u->cgroup_attributes) {
1101
1102 if (!cgroup_attribute_matches(a, controller, name))
1103 continue;
1104
1105 r = strv_extend(&l, a->value);
1106 if (r < 0) {
1107 strv_free(l);
1108 return r;
1109 }
1110 }
1111
1112 if (!l)
1113 return -ENOENT;
1114
1115 *_result = l;
1116 return 0;
1117 }
1118
1119 static int update_attribute_drop_in(Unit *u, bool runtime, const char *name) {
1120 _cleanup_free_ char *buf = NULL;
1121 CGroupAttribute *a;
1122
1123 assert(u);
1124 assert(name);
1125
1126 LIST_FOREACH(by_unit, a, u->cgroup_attributes) {
1127 if (!cgroup_attribute_matches(a, NULL, name))
1128 continue;
1129
1130 if (!buf) {
1131 buf = strjoin("[", UNIT_VTABLE(u)->exec_section, "]\n"
1132 "ControlGroupAttribute=", a->name, " ", a->value, "\n", NULL);
1133
1134 if (!buf)
1135 return -ENOMEM;
1136 } else {
1137 char *b;
1138
1139 b = strjoin(buf,
1140 "ControlGroupAttribute=", a->name, " ", a->value, "\n", NULL);
1141
1142 if (!b)
1143 return -ENOMEM;
1144
1145 free(buf);
1146 buf = b;
1147 }
1148 }
1149
1150 if (buf)
1151 return unit_write_drop_in(u, runtime, name, buf);
1152 else
1153 return unit_remove_drop_in(u, runtime, name);
1154 }
1155
1156 int bus_unit_cgroup_attribute_set(Unit *u, DBusMessageIter *iter) {
1157 _cleanup_strv_free_ char **l = NULL;
1158 int r;
1159 bool runtime = false;
1160 char **value;
1161 const char *name;
1162
1163 assert(u);
1164 assert(iter);
1165
1166 if (!unit_get_exec_context(u))
1167 return -EINVAL;
1168
1169 r = bus_iter_get_basic_and_next(iter, DBUS_TYPE_STRING, &name, true);
1170 if (r < 0)
1171 return r;
1172
1173 r = bus_parse_strv_iter(iter, &l);
1174 if (r < 0)
1175 return r;
1176
1177 if (!dbus_message_iter_next(iter))
1178 return -EINVAL;
1179
1180 r = parse_mode(iter, &runtime, false);
1181 if (r < 0)
1182 return r;
1183
1184 STRV_FOREACH(value, l) {
1185 _cleanup_free_ char *v = NULL;
1186 CGroupAttribute *a;
1187 const CGroupSemantics *s;
1188
1189 r = cgroup_semantics_find(NULL, name, *value, &v, &s);
1190 if (r < 0)
1191 return r;
1192
1193 if (s && !s->multiple && l[1])
1194 return -EINVAL;
1195
1196 r = unit_add_cgroup_attribute(u, s, NULL, name, v ? v : *value, &a);
1197 if (r < 0)
1198 return r;
1199
1200 if (r > 0) {
1201 CGroupBonding *b;
1202
1203 b = cgroup_bonding_find_list(u->cgroup_bondings, a->controller);
1204 if (!b) {
1205 /* Doesn't exist yet? Then let's add it */
1206 r = unit_add_cgroup_from_text(u, a->controller, false, &b);
1207 if (r < 0)
1208 return r;
1209
1210 if (r > 0) {
1211 cgroup_bonding_realize(b);
1212 cgroup_bonding_migrate(b, u->cgroup_bondings);
1213 }
1214 }
1215
1216 /* Make it count */
1217 cgroup_attribute_apply(a, u->cgroup_bondings);
1218 }
1219
1220 }
1221
1222 r = update_attribute_drop_in(u, runtime, name);
1223 if (r < 0)
1224 return r;
1225
1226 return 0;
1227 }
1228
1229 int bus_unit_cgroup_attribute_unset(Unit *u, DBusMessageIter *iter) {
1230 const char *name;
1231 bool runtime;
1232 int r;
1233
1234 assert(u);
1235 assert(iter);
1236
1237 if (!unit_get_exec_context(u))
1238 return -EINVAL;
1239
1240 r = bus_iter_get_basic_and_next(iter, DBUS_TYPE_STRING, &name, true);
1241 if (r < 0)
1242 return r;
1243
1244 r = parse_mode(iter, &runtime, false);
1245 if (r < 0)
1246 return r;
1247
1248 cgroup_attribute_free_some(u->cgroup_attributes, NULL, name);
1249 update_attribute_drop_in(u, runtime, name);
1250
1251 return 0;
1252 }
1253
1254 const BusProperty bus_unit_properties[] = {
1255 { "Id", bus_property_append_string, "s", offsetof(Unit, id), true },
1256 { "Names", bus_unit_append_names, "as", 0 },
1257 { "Following", bus_unit_append_following, "s", 0 },
1258 { "Requires", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUIRES]), true },
1259 { "RequiresOverridable", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUIRES_OVERRIDABLE]), true },
1260 { "Requisite", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUISITE]), true },
1261 { "RequisiteOverridable", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUISITE_OVERRIDABLE]), true },
1262 { "Wants", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_WANTS]), true },
1263 { "BindsTo", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_BINDS_TO]), true },
1264 { "PartOf", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_PART_OF]), true },
1265 { "RequiredBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUIRED_BY]), true },
1266 { "RequiredByOverridable",bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUIRED_BY_OVERRIDABLE]), true },
1267 { "WantedBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_WANTED_BY]), true },
1268 { "BoundBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_BOUND_BY]), true },
1269 { "ConsistsOf", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_CONSISTS_OF]), true },
1270 { "Conflicts", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_CONFLICTS]), true },
1271 { "ConflictedBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_CONFLICTED_BY]), true },
1272 { "Before", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_BEFORE]), true },
1273 { "After", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_AFTER]), true },
1274 { "OnFailure", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_ON_FAILURE]), true },
1275 { "Triggers", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_TRIGGERS]), true },
1276 { "TriggeredBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_TRIGGERED_BY]), true },
1277 { "PropagatesReloadTo", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_PROPAGATES_RELOAD_TO]), true },
1278 { "ReloadPropagatedFrom", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_RELOAD_PROPAGATED_FROM]), true },
1279 { "RequiresMountsFor", bus_property_append_strv, "as", offsetof(Unit, requires_mounts_for), true },
1280 { "Documentation", bus_property_append_strv, "as", offsetof(Unit, documentation), true },
1281 { "Description", bus_unit_append_description, "s", 0 },
1282 { "LoadState", bus_unit_append_load_state, "s", offsetof(Unit, load_state) },
1283 { "ActiveState", bus_unit_append_active_state, "s", 0 },
1284 { "SubState", bus_unit_append_sub_state, "s", 0 },
1285 { "FragmentPath", bus_property_append_string, "s", offsetof(Unit, fragment_path), true },
1286 { "SourcePath", bus_property_append_string, "s", offsetof(Unit, source_path), true },
1287 { "DropInPaths", bus_property_append_strv, "as", offsetof(Unit, dropin_paths), true },
1288 { "UnitFileState", bus_unit_append_file_state, "s", 0 },
1289 { "InactiveExitTimestamp",bus_property_append_usec, "t", offsetof(Unit, inactive_exit_timestamp.realtime) },
1290 { "InactiveExitTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, inactive_exit_timestamp.monotonic) },
1291 { "ActiveEnterTimestamp", bus_property_append_usec, "t", offsetof(Unit, active_enter_timestamp.realtime) },
1292 { "ActiveEnterTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, active_enter_timestamp.monotonic) },
1293 { "ActiveExitTimestamp", bus_property_append_usec, "t", offsetof(Unit, active_exit_timestamp.realtime) },
1294 { "ActiveExitTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, active_exit_timestamp.monotonic) },
1295 { "InactiveEnterTimestamp", bus_property_append_usec, "t", offsetof(Unit, inactive_enter_timestamp.realtime) },
1296 { "InactiveEnterTimestampMonotonic",bus_property_append_usec, "t", offsetof(Unit, inactive_enter_timestamp.monotonic) },
1297 { "CanStart", bus_unit_append_can_start, "b", 0 },
1298 { "CanStop", bus_unit_append_can_stop, "b", 0 },
1299 { "CanReload", bus_unit_append_can_reload, "b", 0 },
1300 { "CanIsolate", bus_unit_append_can_isolate, "b", 0 },
1301 { "Job", bus_unit_append_job, "(uo)", 0 },
1302 { "StopWhenUnneeded", bus_property_append_bool, "b", offsetof(Unit, stop_when_unneeded) },
1303 { "RefuseManualStart", bus_property_append_bool, "b", offsetof(Unit, refuse_manual_start) },
1304 { "RefuseManualStop", bus_property_append_bool, "b", offsetof(Unit, refuse_manual_stop) },
1305 { "AllowIsolate", bus_property_append_bool, "b", offsetof(Unit, allow_isolate) },
1306 { "DefaultDependencies", bus_property_append_bool, "b", offsetof(Unit, default_dependencies) },
1307 { "OnFailureIsolate", bus_property_append_bool, "b", offsetof(Unit, on_failure_isolate) },
1308 { "IgnoreOnIsolate", bus_property_append_bool, "b", offsetof(Unit, ignore_on_isolate) },
1309 { "IgnoreOnSnapshot", bus_property_append_bool, "b", offsetof(Unit, ignore_on_snapshot) },
1310 { "NeedDaemonReload", bus_unit_append_need_daemon_reload, "b", 0 },
1311 { "JobTimeoutUSec", bus_property_append_usec, "t", offsetof(Unit, job_timeout) },
1312 { "ConditionTimestamp", bus_property_append_usec, "t", offsetof(Unit, condition_timestamp.realtime) },
1313 { "ConditionTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, condition_timestamp.monotonic) },
1314 { "ConditionResult", bus_property_append_bool, "b", offsetof(Unit, condition_result) },
1315 { "LoadError", bus_unit_append_load_error, "(ss)", 0 },
1316 { NULL, }
1317 };
1318
1319 const BusProperty bus_unit_cgroup_properties[] = {
1320 { "DefaultControlGroup", bus_unit_append_default_cgroup, "s", 0 },
1321 { "ControlGroups", bus_unit_append_cgroups, "as", 0 },
1322 { "ControlGroupAttributes", bus_unit_append_cgroup_attrs, "a(sss)", 0 },
1323 { NULL, }
1324 };