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