chiark / gitweb /
python-systemd: rename Journal to Reader
[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 #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->map_callback)
348                         a->map_callback(a->controller, a->name, 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), "SetControlGroups")) {
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), "UnsetControlGroups")) {
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_set(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), "GetControlGroupAttributes")) {
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), "SetControlGroupAttributes")) {
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), "UnsetControlGroupAttributes")) {
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 (!dbus_connection_send(connection, reply, NULL))
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 (!dbus_connection_send(connection, reply, NULL))
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 (!dbus_connection_send(connection, reply, NULL))
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 next_and_parse_mode(DBusMessageIter *iter, bool *runtime) {
901         const char *mode;
902
903         assert(iter);
904         assert(runtime);
905
906         dbus_message_iter_next(iter);
907         if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING)
908                 return -EINVAL;
909
910         dbus_message_iter_get_basic(iter, &mode);
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         int r;
923         _cleanup_strv_free_ char **a = NULL;
924         char **name;
925         bool runtime;
926
927         assert(u);
928         assert(iter);
929
930         if (!unit_get_exec_context(u))
931                 return -EINVAL;
932
933         r = bus_parse_strv_iter(iter, &a);
934         if (r < 0)
935                 return r;
936
937         r = next_and_parse_mode(iter, &runtime);
938         if (r < 0)
939                 return r;
940
941         STRV_FOREACH(name, a) {
942                 _cleanup_free_ char *controller = NULL, *old_path = NULL, *new_path = NULL, *contents = NULL;
943                 CGroupBonding *b;
944
945                 r = cg_split_spec(*name, &controller, &new_path);
946                 if (r < 0)
947                         return r;
948
949                 b = cgroup_bonding_find_list(u->cgroup_bondings, controller);
950                 if (b) {
951                         old_path = strdup(b->path);
952                         if (!old_path)
953                                 return -ENOMEM;
954                 }
955
956                 r = unit_add_cgroup_from_text(u, *name, true, &b);
957                 if (r < 0)
958                         return r;
959
960                 if (r > 0) {
961                         /* Try to move things to the new place, and clean up the old place */
962                         cgroup_bonding_realize(b);
963                         cgroup_bonding_migrate(b, u->cgroup_bondings);
964
965                         if (old_path)
966                                 cg_trim(controller, old_path, true);
967                 }
968
969                 contents = strjoin("[", UNIT_VTABLE(u)->exec_section, "]\n"
970                                    "ControlGroup=", *name, "\n", NULL);
971                 if (!contents)
972                         return -ENOMEM;
973
974                 r = unit_write_drop_in(u, runtime, *name, contents);
975                 if (r < 0)
976                         return r;
977         }
978
979         return 0;
980 }
981
982 int bus_unit_cgroup_unset(Unit *u, DBusMessageIter *iter) {
983         _cleanup_strv_free_ char **a = NULL;
984         char **name;
985         int r;
986         bool runtime;
987
988         assert(u);
989         assert(iter);
990
991         if (!unit_get_exec_context(u))
992                 return -EINVAL;
993
994         r = bus_parse_strv_iter(iter, &a);
995         if (r < 0)
996                 return r;
997
998         r = next_and_parse_mode(iter, &runtime);
999         if (r < 0)
1000                 return r;
1001
1002         STRV_FOREACH(name, a) {
1003                 _cleanup_free_ char *controller = NULL, *path = NULL, *target = NULL;
1004                 CGroupBonding *b;
1005
1006                 r = cg_split_spec(*name, &controller, &path);
1007                 if (r < 0)
1008                         return r;
1009
1010                 if (!controller || streq(controller, SYSTEMD_CGROUP_CONTROLLER))
1011                         return -EINVAL;
1012
1013                 unit_remove_drop_in(u, runtime, *name);
1014
1015                 b = cgroup_bonding_find_list(u->cgroup_bondings, controller);
1016                 if (!b)
1017                         continue;
1018
1019                 if (path && !path_equal(path, b->path))
1020                         continue;
1021
1022                 if (b->essential)
1023                         return -EINVAL;
1024
1025                 /* Try to migrate the old group away */
1026                 if (cg_get_by_pid(controller, 0, &target) >= 0)
1027                         cgroup_bonding_migrate_to(u->cgroup_bondings, target, false);
1028
1029                 cgroup_bonding_free(b, true);
1030         }
1031
1032         return 0;
1033 }
1034
1035 int bus_unit_cgroup_attribute_get(Unit *u, DBusMessageIter *iter, char ***_result) {
1036         _cleanup_strv_free_ char **l = NULL, **result = NULL;
1037         char **name;
1038         int r;
1039
1040         assert(u);
1041         assert(iter);
1042         assert(_result);
1043
1044         if (!unit_get_exec_context(u))
1045                 return -EINVAL;
1046
1047         r = bus_parse_strv_iter(iter, &l);
1048         if (r < 0)
1049                 return r;
1050
1051         STRV_FOREACH(name, l) {
1052                 _cleanup_free_ char *controller = NULL;
1053                 CGroupAttribute *a;
1054                 CGroupBonding *b;
1055
1056                 r = cg_controller_from_attr(*name, &controller);
1057                 if (r < 0)
1058                         return r;
1059
1060                 /* First attempt, read the value from the kernel */
1061                 b = cgroup_bonding_find_list(u->cgroup_bondings, controller);
1062                 if (b) {
1063                         _cleanup_free_ char *p = NULL, *v = NULL;
1064
1065                         r = cg_get_path(b->controller, b->path, *name, &p);
1066                         if (r < 0)
1067                                 return r;
1068
1069                         r = read_full_file(p, &v, NULL);
1070                         if (r >= 0) {
1071                                 r = strv_extend(&result, v);
1072                                 if (r < 0)
1073                                         return r;
1074
1075                                 continue;
1076                         } else if (r != -ENOENT)
1077                                 return r;
1078                 }
1079
1080                 /* If that didn't work, read our cached value */
1081                 a = cgroup_attribute_find_list(u->cgroup_attributes, NULL, *name);
1082                 if (a) {
1083                         r = strv_extend(&result, a->value);
1084                         if (r < 0)
1085                                 return r;
1086
1087                         continue;
1088                 }
1089
1090                 return -ENOENT;
1091         }
1092
1093         *_result = result;
1094         result = NULL;
1095
1096         return 0;
1097 }
1098
1099 int bus_unit_cgroup_attribute_set(Unit *u, DBusMessageIter *iter) {
1100         _cleanup_strv_free_ char **l = NULL;
1101         int r;
1102         bool runtime = false;
1103         char **name, **value;
1104
1105         assert(u);
1106         assert(iter);
1107
1108         if (!unit_get_exec_context(u))
1109                 return -EINVAL;
1110
1111         r = bus_parse_strv_pairs_iter(iter, &l);
1112         if (r < 0)
1113                 return r;
1114
1115         r = next_and_parse_mode(iter, &runtime);
1116         if (r < 0)
1117                 return r;
1118
1119         STRV_FOREACH_PAIR(name, value, l) {
1120                 _cleanup_free_ char *contents = NULL;
1121                 CGroupAttribute *a;
1122
1123                 r = unit_add_cgroup_attribute(u, NULL, *name, *value, NULL, &a);
1124                 if (r < 0)
1125                         return r;
1126
1127                 if (r > 0) {
1128                         CGroupBonding *b;
1129
1130                         b = cgroup_bonding_find_list(u->cgroup_bondings, a->controller);
1131                         if (!b) {
1132                                 /* Doesn't exist yet? Then let's add it */
1133                                 r = unit_add_cgroup_from_text(u, a->controller, false, &b);
1134                                 if (r < 0)
1135                                         return r;
1136
1137                                 if (r > 0) {
1138                                         cgroup_bonding_realize(b);
1139                                         cgroup_bonding_migrate(b, u->cgroup_bondings);
1140                                 }
1141                         }
1142
1143                         /* Make it count */
1144                         cgroup_attribute_apply(a, u->cgroup_bondings);
1145                 }
1146
1147                 contents = strjoin("[", UNIT_VTABLE(u)->exec_section, "]\n"
1148                                    "ControlGroupAttribute=", *name, " ", *value, "\n", NULL);
1149                 if (!contents)
1150                         return -ENOMEM;
1151
1152                 r = unit_write_drop_in(u, runtime, *name, contents);
1153                 if (r < 0)
1154                         return r;
1155         }
1156
1157         return 0;
1158 }
1159
1160 int bus_unit_cgroup_attribute_unset(Unit *u, DBusMessageIter *iter) {
1161         _cleanup_strv_free_ char **l = NULL;
1162         char **name;
1163         bool runtime;
1164         int r;
1165
1166         assert(u);
1167         assert(iter);
1168
1169         if (!unit_get_exec_context(u))
1170                 return -EINVAL;
1171
1172         r = bus_parse_strv_iter(iter, &l);
1173         if (r < 0)
1174                 return r;
1175
1176         r = next_and_parse_mode(iter, &runtime);
1177         if (r < 0)
1178                 return r;
1179
1180         STRV_FOREACH(name, l) {
1181                 CGroupAttribute *a;
1182
1183                 a = cgroup_attribute_find_list(u->cgroup_attributes, NULL, *name);
1184                 if (a)
1185                         cgroup_attribute_free(a);
1186
1187                 unit_remove_drop_in(u, runtime, *name);
1188         }
1189
1190         return 0;
1191 }
1192
1193 const BusProperty bus_unit_properties[] = {
1194         { "Id",                   bus_property_append_string,         "s", offsetof(Unit, id),                                         true },
1195         { "Names",                bus_unit_append_names,             "as", 0 },
1196         { "Following",            bus_unit_append_following,          "s", 0 },
1197         { "Requires",             bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_REQUIRES]),                true },
1198         { "RequiresOverridable",  bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_REQUIRES_OVERRIDABLE]),    true },
1199         { "Requisite",            bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_REQUISITE]),               true },
1200         { "RequisiteOverridable", bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_REQUISITE_OVERRIDABLE]),   true },
1201         { "Wants",                bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_WANTS]),                   true },
1202         { "BindsTo",              bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_BINDS_TO]),                true },
1203         { "PartOf",               bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_PART_OF]),                 true },
1204         { "RequiredBy",           bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_REQUIRED_BY]),             true },
1205         { "RequiredByOverridable",bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_REQUIRED_BY_OVERRIDABLE]), true },
1206         { "WantedBy",             bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_WANTED_BY]),               true },
1207         { "BoundBy",              bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_BOUND_BY]),                true },
1208         { "ConsistsOf",           bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_CONSISTS_OF]),             true },
1209         { "Conflicts",            bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_CONFLICTS]),               true },
1210         { "ConflictedBy",         bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_CONFLICTED_BY]),           true },
1211         { "Before",               bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_BEFORE]),                  true },
1212         { "After",                bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_AFTER]),                   true },
1213         { "OnFailure",            bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_ON_FAILURE]),              true },
1214         { "Triggers",             bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_TRIGGERS]),                true },
1215         { "TriggeredBy",          bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_TRIGGERED_BY]),            true },
1216         { "PropagatesReloadTo",   bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_PROPAGATES_RELOAD_TO]),    true },
1217         { "ReloadPropagatedFrom", bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_RELOAD_PROPAGATED_FROM]),  true },
1218         { "RequiresMountsFor",    bus_property_append_strv,          "as", offsetof(Unit, requires_mounts_for),                        true },
1219         { "Documentation",        bus_property_append_strv,          "as", offsetof(Unit, documentation),                              true },
1220         { "Description",          bus_unit_append_description,        "s", 0 },
1221         { "LoadState",            bus_unit_append_load_state,         "s", offsetof(Unit, load_state)                         },
1222         { "ActiveState",          bus_unit_append_active_state,       "s", 0 },
1223         { "SubState",             bus_unit_append_sub_state,          "s", 0 },
1224         { "FragmentPath",         bus_property_append_string,         "s", offsetof(Unit, fragment_path),                              true },
1225         { "SourcePath",           bus_property_append_string,         "s", offsetof(Unit, source_path),                                true },
1226         { "UnitFileState",        bus_unit_append_file_state,         "s", 0 },
1227         { "InactiveExitTimestamp",bus_property_append_usec,           "t", offsetof(Unit, inactive_exit_timestamp.realtime)   },
1228         { "InactiveExitTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, inactive_exit_timestamp.monotonic)  },
1229         { "ActiveEnterTimestamp", bus_property_append_usec,           "t", offsetof(Unit, active_enter_timestamp.realtime)    },
1230         { "ActiveEnterTimestampMonotonic", bus_property_append_usec,  "t", offsetof(Unit, active_enter_timestamp.monotonic)   },
1231         { "ActiveExitTimestamp",  bus_property_append_usec,           "t", offsetof(Unit, active_exit_timestamp.realtime)     },
1232         { "ActiveExitTimestampMonotonic",  bus_property_append_usec,  "t", offsetof(Unit, active_exit_timestamp.monotonic)    },
1233         { "InactiveEnterTimestamp", bus_property_append_usec,         "t", offsetof(Unit, inactive_enter_timestamp.realtime)  },
1234         { "InactiveEnterTimestampMonotonic",bus_property_append_usec, "t", offsetof(Unit, inactive_enter_timestamp.monotonic) },
1235         { "CanStart",             bus_unit_append_can_start,          "b", 0 },
1236         { "CanStop",              bus_unit_append_can_stop,           "b", 0 },
1237         { "CanReload",            bus_unit_append_can_reload,         "b", 0 },
1238         { "CanIsolate",           bus_unit_append_can_isolate,        "b", 0 },
1239         { "Job",                  bus_unit_append_job,             "(uo)", 0 },
1240         { "StopWhenUnneeded",     bus_property_append_bool,           "b", offsetof(Unit, stop_when_unneeded)                 },
1241         { "RefuseManualStart",    bus_property_append_bool,           "b", offsetof(Unit, refuse_manual_start)                },
1242         { "RefuseManualStop",     bus_property_append_bool,           "b", offsetof(Unit, refuse_manual_stop)                 },
1243         { "AllowIsolate",         bus_property_append_bool,           "b", offsetof(Unit, allow_isolate)                      },
1244         { "DefaultDependencies",  bus_property_append_bool,           "b", offsetof(Unit, default_dependencies)               },
1245         { "OnFailureIsolate",     bus_property_append_bool,           "b", offsetof(Unit, on_failure_isolate)                 },
1246         { "IgnoreOnIsolate",      bus_property_append_bool,           "b", offsetof(Unit, ignore_on_isolate)                  },
1247         { "IgnoreOnSnapshot",     bus_property_append_bool,           "b", offsetof(Unit, ignore_on_snapshot)                 },
1248         { "NeedDaemonReload",     bus_unit_append_need_daemon_reload, "b", 0 },
1249         { "JobTimeoutUSec",       bus_property_append_usec,           "t", offsetof(Unit, job_timeout)                        },
1250         { "ConditionTimestamp",   bus_property_append_usec,           "t", offsetof(Unit, condition_timestamp.realtime)       },
1251         { "ConditionTimestampMonotonic", bus_property_append_usec,    "t", offsetof(Unit, condition_timestamp.monotonic)      },
1252         { "ConditionResult",      bus_property_append_bool,           "b", offsetof(Unit, condition_result)                   },
1253         { "LoadError",            bus_unit_append_load_error,      "(ss)", 0 },
1254         { NULL, }
1255 };
1256
1257 const BusProperty bus_unit_cgroup_properties[] = {
1258         { "DefaultControlGroup",    bus_unit_append_default_cgroup,     "s", 0 },
1259         { "ControlGroups",          bus_unit_append_cgroups,           "as", 0 },
1260         { "ControlGroupAttributes", bus_unit_append_cgroup_attrs,  "a(sss)", 0 },
1261         { NULL, }
1262 };