chiark / gitweb /
core: add bus API and systemctl commands for altering cgroup parameters during runtime
[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), "SetControlGroupAttributes")) {
506                 DBusMessageIter iter;
507
508                 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "start");
509
510                 if (!dbus_message_iter_init(message, &iter))
511                         goto oom;
512
513                 r = bus_unit_cgroup_attribute_set(u, &iter);
514                 if (r < 0)
515                         return bus_send_error_reply(connection, message, NULL, r);
516
517                 reply = dbus_message_new_method_return(message);
518                 if (!reply)
519                         goto oom;
520
521         } else if (streq_ptr(dbus_message_get_member(message), "UnsetControlGroupAttributes")) {
522                 DBusMessageIter iter;
523
524                 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "stop");
525
526                 if (!dbus_message_iter_init(message, &iter))
527                         goto oom;
528
529                 r = bus_unit_cgroup_attribute_unset(u, &iter);
530                 if (r < 0)
531                         return bus_send_error_reply(connection, message, NULL, r);
532
533                 reply = dbus_message_new_method_return(message);
534                 if (!reply)
535                         goto oom;
536
537         } else if (UNIT_VTABLE(u)->bus_message_handler)
538                 return UNIT_VTABLE(u)->bus_message_handler(u, connection, message);
539         else
540                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
541
542         if (job_type != _JOB_TYPE_INVALID) {
543                 const char *smode;
544                 JobMode mode;
545
546                 if (!dbus_message_get_args(
547                                     message,
548                                     &error,
549                                     DBUS_TYPE_STRING, &smode,
550                                     DBUS_TYPE_INVALID))
551                         return bus_send_error_reply(connection, message, &error, -EINVAL);
552
553                 mode = job_mode_from_string(smode);
554                 if (mode < 0) {
555                         dbus_set_error(&error, BUS_ERROR_INVALID_JOB_MODE, "Job mode %s is invalid.", smode);
556                         return bus_send_error_reply(connection, message, &error, -EINVAL);
557                 }
558
559                 return bus_unit_queue_job(connection, message, u, job_type, mode, reload_if_possible);
560         }
561
562         if (reply)
563                 if (!dbus_connection_send(connection, reply, NULL))
564                         goto oom;
565
566         return DBUS_HANDLER_RESULT_HANDLED;
567
568 oom:
569         dbus_error_free(&error);
570         return DBUS_HANDLER_RESULT_NEED_MEMORY;
571 }
572
573 static DBusHandlerResult bus_unit_message_handler(DBusConnection *connection, DBusMessage  *message, void *data) {
574         Manager *m = data;
575         Unit *u;
576         int r;
577         _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
578         DBusError error;
579
580         assert(connection);
581         assert(message);
582         assert(m);
583
584         dbus_error_init(&error);
585
586         if (streq(dbus_message_get_path(message), "/org/freedesktop/systemd1/unit")) {
587                 /* Be nice to gdbus and return introspection data for our mid-level paths */
588
589                 SELINUX_ACCESS_CHECK(connection, message, "status");
590
591                 if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect")) {
592                         char *introspection = NULL;
593                         FILE *f;
594                         Iterator i;
595                         const char *k;
596                         size_t size;
597
598                         reply = dbus_message_new_method_return(message);
599                         if (!reply)
600                                 goto oom;
601
602                         /* We roll our own introspection code here, instead of
603                          * relying on bus_default_message_handler() because we
604                          * need to generate our introspection string
605                          * dynamically. */
606
607                         f = open_memstream(&introspection, &size);
608                         if (!f)
609                                 goto oom;
610
611                         fputs(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
612                               "<node>\n", f);
613
614                         fputs(BUS_INTROSPECTABLE_INTERFACE, f);
615                         fputs(BUS_PEER_INTERFACE, f);
616
617                         HASHMAP_FOREACH_KEY(u, k, m->units, i) {
618                                 char *p;
619
620                                 if (k != u->id)
621                                         continue;
622
623                                 p = bus_path_escape(k);
624                                 if (!p) {
625                                         fclose(f);
626                                         free(introspection);
627                                         goto oom;
628                                 }
629
630                                 fprintf(f, "<node name=\"%s\"/>", p);
631                                 free(p);
632                         }
633
634                         fputs("</node>\n", f);
635
636                         if (ferror(f)) {
637                                 fclose(f);
638                                 free(introspection);
639                                 goto oom;
640                         }
641
642                         fclose(f);
643
644                         if (!introspection)
645                                 goto oom;
646
647                         if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &introspection, DBUS_TYPE_INVALID)) {
648                                 free(introspection);
649                                 goto oom;
650                         }
651
652                         free(introspection);
653
654                         if (!dbus_connection_send(connection, reply, NULL))
655                                 goto oom;
656
657                         return DBUS_HANDLER_RESULT_HANDLED;
658                 }
659
660                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
661         }
662
663         r = manager_load_unit_from_dbus_path(m, dbus_message_get_path(message), &error, &u);
664         if (r == -ENOMEM)
665                 goto oom;
666         if (r < 0)
667                 return bus_send_error_reply(connection, message, &error, r);
668
669         return bus_unit_message_dispatch(u, connection, message);
670
671 oom:
672         dbus_error_free(&error);
673
674         return DBUS_HANDLER_RESULT_NEED_MEMORY;
675 }
676
677 const DBusObjectPathVTable bus_unit_vtable = {
678         .message_function = bus_unit_message_handler
679 };
680
681 void bus_unit_send_change_signal(Unit *u) {
682         _cleanup_free_ char *p = NULL;
683         _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
684
685         assert(u);
686
687         if (u->in_dbus_queue) {
688                 LIST_REMOVE(Unit, dbus_queue, u->manager->dbus_unit_queue, u);
689                 u->in_dbus_queue = false;
690         }
691
692         if (!u->id)
693                 return;
694
695         if (!bus_has_subscriber(u->manager)) {
696                 u->sent_dbus_new_signal = true;
697                 return;
698         }
699
700         p = unit_dbus_path(u);
701         if (!p)
702                 goto oom;
703
704         if (u->sent_dbus_new_signal) {
705                 /* Send a properties changed signal. First for the
706                  * specific type, then for the generic unit. The
707                  * clients may rely on this order to get atomic
708                  * behavior if needed. */
709
710                 if (UNIT_VTABLE(u)->bus_invalidating_properties) {
711
712                         m = bus_properties_changed_new(p,
713                                                        UNIT_VTABLE(u)->bus_interface,
714                                                        UNIT_VTABLE(u)->bus_invalidating_properties);
715                         if (!m)
716                                 goto oom;
717
718                         if (bus_broadcast(u->manager, m) < 0)
719                                 goto oom;
720
721                         dbus_message_unref(m);
722                 }
723
724                 m = bus_properties_changed_new(p, "org.freedesktop.systemd1.Unit",
725                                                INVALIDATING_PROPERTIES);
726                 if (!m)
727                         goto oom;
728
729         } else {
730                 /* Send a new signal */
731
732                 m = dbus_message_new_signal("/org/freedesktop/systemd1",
733                                             "org.freedesktop.systemd1.Manager",
734                                             "UnitNew");
735                 if (!m)
736                         goto oom;
737
738                 if (!dbus_message_append_args(m,
739                                               DBUS_TYPE_STRING, &u->id,
740                                               DBUS_TYPE_OBJECT_PATH, &p,
741                                               DBUS_TYPE_INVALID))
742                         goto oom;
743         }
744
745         if (bus_broadcast(u->manager, m) < 0)
746                 goto oom;
747
748         u->sent_dbus_new_signal = true;
749
750         return;
751
752 oom:
753         log_oom();
754 }
755
756 void bus_unit_send_removed_signal(Unit *u) {
757         _cleanup_free_ char *p = NULL;
758         _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
759
760         assert(u);
761
762         if (!bus_has_subscriber(u->manager))
763                 return;
764
765         if (!u->sent_dbus_new_signal)
766                 bus_unit_send_change_signal(u);
767
768         if (!u->id)
769                 return;
770
771         p = unit_dbus_path(u);
772         if (!p)
773                 goto oom;
774
775         m = dbus_message_new_signal("/org/freedesktop/systemd1",
776                                     "org.freedesktop.systemd1.Manager",
777                                     "UnitRemoved");
778         if (!m)
779                 goto oom;
780
781         if (!dbus_message_append_args(m,
782                                       DBUS_TYPE_STRING, &u->id,
783                                       DBUS_TYPE_OBJECT_PATH, &p,
784                                       DBUS_TYPE_INVALID))
785                 goto oom;
786
787         if (bus_broadcast(u->manager, m) < 0)
788                 goto oom;
789
790         return;
791
792 oom:
793         log_oom();
794 }
795
796 DBusHandlerResult bus_unit_queue_job(
797                 DBusConnection *connection,
798                 DBusMessage *message,
799                 Unit *u,
800                 JobType type,
801                 JobMode mode,
802                 bool reload_if_possible) {
803
804         _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
805         _cleanup_free_ char *path = NULL;
806         Job *j;
807         JobBusClient *cl;
808         DBusError error;
809         int r;
810
811         assert(connection);
812         assert(message);
813         assert(u);
814         assert(type >= 0 && type < _JOB_TYPE_MAX);
815         assert(mode >= 0 && mode < _JOB_MODE_MAX);
816
817         dbus_error_init(&error);
818
819         if (reload_if_possible && unit_can_reload(u)) {
820                 if (type == JOB_RESTART)
821                         type = JOB_RELOAD_OR_START;
822                 else if (type == JOB_TRY_RESTART)
823                         type = JOB_RELOAD;
824         }
825
826         SELINUX_UNIT_ACCESS_CHECK(u, connection, message,
827                                   (type == JOB_START || type == JOB_RESTART || type == JOB_TRY_RESTART) ? "start" :
828                                   type == JOB_STOP ? "stop" : "reload");
829
830         if (type == JOB_STOP && u->load_state == UNIT_ERROR && unit_active_state(u) == UNIT_INACTIVE) {
831                 dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s not loaded.", u->id);
832                 return bus_send_error_reply(connection, message, &error, -EPERM);
833         }
834
835         if ((type == JOB_START && u->refuse_manual_start) ||
836             (type == JOB_STOP && u->refuse_manual_stop) ||
837             ((type == JOB_RESTART || type == JOB_TRY_RESTART) && (u->refuse_manual_start || u->refuse_manual_stop))) {
838                 dbus_set_error(&error, BUS_ERROR_ONLY_BY_DEPENDENCY,
839                                "Operation refused, unit %s may be requested by dependency only.", u->id);
840                 return bus_send_error_reply(connection, message, &error, -EPERM);
841         }
842
843         r = manager_add_job(u->manager, type, u, mode, true, &error, &j);
844         if (r < 0)
845                 return bus_send_error_reply(connection, message, &error, r);
846
847         cl = job_bus_client_new(connection, bus_message_get_sender_with_fallback(message));
848         if (!cl)
849                 goto oom;
850
851         LIST_PREPEND(JobBusClient, client, j->bus_client_list, cl);
852
853         reply = dbus_message_new_method_return(message);
854         if (!reply)
855                 goto oom;
856
857         path = job_dbus_path(j);
858         if (!path)
859                 goto oom;
860
861         if (!dbus_message_append_args(
862                             reply,
863                             DBUS_TYPE_OBJECT_PATH, &path,
864                             DBUS_TYPE_INVALID))
865                 goto oom;
866
867         if (!dbus_connection_send(connection, reply, NULL))
868                 goto oom;
869
870         return DBUS_HANDLER_RESULT_HANDLED;
871
872 oom:
873         dbus_error_free(&error);
874
875         return DBUS_HANDLER_RESULT_NEED_MEMORY;
876 }
877
878 int bus_unit_cgroup_set(Unit *u, DBusMessageIter *iter) {
879         int r;
880         _cleanup_strv_free_ char **a = NULL;
881         char **name;
882
883         assert(u);
884         assert(iter);
885
886         if (!unit_get_exec_context(u))
887                 return -EINVAL;
888
889         r = bus_parse_strv_iter(iter, &a);
890         if (r < 0)
891                 return r;
892
893         STRV_FOREACH(name, a) {
894                 _cleanup_free_ char *controller = NULL, *old_path = NULL, *new_path = NULL;
895                 CGroupBonding *b;
896
897                 r = cg_split_spec(*name, &controller, &new_path);
898                 if (r < 0)
899                         return r;
900
901                 b = cgroup_bonding_find_list(u->cgroup_bondings, controller);
902                 if (b) {
903                         old_path = strdup(b->path);
904                         if (!old_path)
905                                 return -ENOMEM;
906                 }
907
908                 r = unit_add_cgroup_from_text(u, *name, true, &b);
909                 if (r < 0)
910                         return r;
911
912                 if (r > 0) {
913                         /* Try to move things to the new place, and clean up the old place */
914                         cgroup_bonding_realize(b);
915                         cgroup_bonding_migrate(b, u->cgroup_bondings);
916
917                         if (old_path)
918                                 cg_trim(controller, old_path, true);
919                 }
920         }
921
922         return 0;
923 }
924
925 int bus_unit_cgroup_unset(Unit *u, DBusMessageIter *iter) {
926         _cleanup_strv_free_ char **a = NULL;
927         char **name;
928         int r;
929
930         assert(u);
931         assert(iter);
932
933         if (!unit_get_exec_context(u))
934                 return -EINVAL;
935
936         r = bus_parse_strv_iter(iter, &a);
937         if (r < 0)
938                 return r;
939
940         STRV_FOREACH(name, a) {
941                 _cleanup_free_ char *controller = NULL, *path = NULL, *target = NULL;
942                 CGroupBonding *b;
943
944                 r = cg_split_spec(*name, &controller, &path);
945                 if (r < 0)
946                         return r;
947
948                 b = cgroup_bonding_find_list(u->cgroup_bondings, controller);
949                 if (!b)
950                         continue;
951
952                 if (path && !path_equal(path, b->path))
953                         continue;
954
955                 if (b->essential)
956                         return -EINVAL;
957
958                 /* Try to migrate the old group away */
959                 if (cg_get_by_pid(controller, 0, &target) >= 0)
960                         cgroup_bonding_migrate_to(u->cgroup_bondings, target, false);
961
962                 cgroup_bonding_free(b, true);
963         }
964
965         return 0;
966 }
967
968 int bus_unit_cgroup_attribute_set(Unit *u, DBusMessageIter *iter) {
969         DBusMessageIter sub, sub2;
970         int r;
971
972         assert(u);
973         assert(iter);
974
975         if (!unit_get_exec_context(u))
976                 return -EINVAL;
977
978         if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY ||
979             dbus_message_iter_get_element_type(iter) != DBUS_TYPE_STRUCT)
980             return -EINVAL;
981
982         dbus_message_iter_recurse(iter, &sub);
983
984         while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
985                 const char *name, *value;
986                 CGroupAttribute *a;
987
988                 assert_se(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT);
989
990                 dbus_message_iter_recurse(&sub, &sub2);
991
992                 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &name, true) < 0 ||
993                     bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &value, false) < 0)
994                         return -EINVAL;
995
996                 dbus_message_iter_next(&sub);
997
998                 r = unit_add_cgroup_attribute(u, NULL, name, value, NULL, &a);
999                 if (r < 0)
1000                         return r;
1001
1002                 if (r > 0) {
1003                         CGroupBonding *b;
1004
1005                         b = cgroup_bonding_find_list(u->cgroup_bondings, a->controller);
1006                         if (!b) {
1007                                 /* Doesn't exist yet? Then let's add it */
1008                                 r = unit_add_cgroup_from_text(u, a->controller, false, &b);
1009                                 if (r < 0)
1010                                         return r;
1011
1012                                 if (r > 0) {
1013                                         cgroup_bonding_realize(b);
1014                                         cgroup_bonding_migrate(b, u->cgroup_bondings);
1015                                 }
1016                         }
1017
1018                         /* Make it count */
1019                         cgroup_attribute_apply(a, u->cgroup_bondings);
1020                 }
1021         }
1022
1023         return 0;
1024 }
1025
1026 int bus_unit_cgroup_attribute_unset(Unit *u, DBusMessageIter *iter) {
1027         _cleanup_strv_free_ char **l = NULL;
1028         char **name;
1029         int r;
1030
1031         assert(u);
1032         assert(iter);
1033
1034         if (!unit_get_exec_context(u))
1035                 return -EINVAL;
1036
1037         r = bus_parse_strv_iter(iter, &l);
1038         if (r < 0)
1039                 return r;
1040
1041         STRV_FOREACH(name, l) {
1042                 CGroupAttribute *a;
1043
1044                 a = cgroup_attribute_find_list(u->cgroup_attributes, NULL, *name);
1045                 if (a)
1046                         cgroup_attribute_free(a);
1047         }
1048
1049         return 0;
1050 }
1051
1052 const BusProperty bus_unit_properties[] = {
1053         { "Id",                   bus_property_append_string,         "s", offsetof(Unit, id),                                         true },
1054         { "Names",                bus_unit_append_names,             "as", 0 },
1055         { "Following",            bus_unit_append_following,          "s", 0 },
1056         { "Requires",             bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_REQUIRES]),                true },
1057         { "RequiresOverridable",  bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_REQUIRES_OVERRIDABLE]),    true },
1058         { "Requisite",            bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_REQUISITE]),               true },
1059         { "RequisiteOverridable", bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_REQUISITE_OVERRIDABLE]),   true },
1060         { "Wants",                bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_WANTS]),                   true },
1061         { "BindsTo",              bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_BINDS_TO]),                true },
1062         { "PartOf",               bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_PART_OF]),                 true },
1063         { "RequiredBy",           bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_REQUIRED_BY]),             true },
1064         { "RequiredByOverridable",bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_REQUIRED_BY_OVERRIDABLE]), true },
1065         { "WantedBy",             bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_WANTED_BY]),               true },
1066         { "BoundBy",              bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_BOUND_BY]),                true },
1067         { "ConsistsOf",           bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_CONSISTS_OF]),             true },
1068         { "Conflicts",            bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_CONFLICTS]),               true },
1069         { "ConflictedBy",         bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_CONFLICTED_BY]),           true },
1070         { "Before",               bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_BEFORE]),                  true },
1071         { "After",                bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_AFTER]),                   true },
1072         { "OnFailure",            bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_ON_FAILURE]),              true },
1073         { "Triggers",             bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_TRIGGERS]),                true },
1074         { "TriggeredBy",          bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_TRIGGERED_BY]),            true },
1075         { "PropagatesReloadTo",   bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_PROPAGATES_RELOAD_TO]),    true },
1076         { "ReloadPropagatedFrom", bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_RELOAD_PROPAGATED_FROM]),  true },
1077         { "RequiresMountsFor",    bus_property_append_strv,          "as", offsetof(Unit, requires_mounts_for),                        true },
1078         { "Documentation",        bus_property_append_strv,          "as", offsetof(Unit, documentation),                              true },
1079         { "Description",          bus_unit_append_description,        "s", 0 },
1080         { "LoadState",            bus_unit_append_load_state,         "s", offsetof(Unit, load_state)                         },
1081         { "ActiveState",          bus_unit_append_active_state,       "s", 0 },
1082         { "SubState",             bus_unit_append_sub_state,          "s", 0 },
1083         { "FragmentPath",         bus_property_append_string,         "s", offsetof(Unit, fragment_path),                              true },
1084         { "SourcePath",           bus_property_append_string,         "s", offsetof(Unit, source_path),                                true },
1085         { "UnitFileState",        bus_unit_append_file_state,         "s", 0 },
1086         { "InactiveExitTimestamp",bus_property_append_usec,           "t", offsetof(Unit, inactive_exit_timestamp.realtime)   },
1087         { "InactiveExitTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, inactive_exit_timestamp.monotonic)  },
1088         { "ActiveEnterTimestamp", bus_property_append_usec,           "t", offsetof(Unit, active_enter_timestamp.realtime)    },
1089         { "ActiveEnterTimestampMonotonic", bus_property_append_usec,  "t", offsetof(Unit, active_enter_timestamp.monotonic)   },
1090         { "ActiveExitTimestamp",  bus_property_append_usec,           "t", offsetof(Unit, active_exit_timestamp.realtime)     },
1091         { "ActiveExitTimestampMonotonic",  bus_property_append_usec,  "t", offsetof(Unit, active_exit_timestamp.monotonic)    },
1092         { "InactiveEnterTimestamp", bus_property_append_usec,         "t", offsetof(Unit, inactive_enter_timestamp.realtime)  },
1093         { "InactiveEnterTimestampMonotonic",bus_property_append_usec, "t", offsetof(Unit, inactive_enter_timestamp.monotonic) },
1094         { "CanStart",             bus_unit_append_can_start,          "b", 0 },
1095         { "CanStop",              bus_unit_append_can_stop,           "b", 0 },
1096         { "CanReload",            bus_unit_append_can_reload,         "b", 0 },
1097         { "CanIsolate",           bus_unit_append_can_isolate,        "b", 0 },
1098         { "Job",                  bus_unit_append_job,             "(uo)", 0 },
1099         { "StopWhenUnneeded",     bus_property_append_bool,           "b", offsetof(Unit, stop_when_unneeded)                 },
1100         { "RefuseManualStart",    bus_property_append_bool,           "b", offsetof(Unit, refuse_manual_start)                },
1101         { "RefuseManualStop",     bus_property_append_bool,           "b", offsetof(Unit, refuse_manual_stop)                 },
1102         { "AllowIsolate",         bus_property_append_bool,           "b", offsetof(Unit, allow_isolate)                      },
1103         { "DefaultDependencies",  bus_property_append_bool,           "b", offsetof(Unit, default_dependencies)               },
1104         { "OnFailureIsolate",     bus_property_append_bool,           "b", offsetof(Unit, on_failure_isolate)                 },
1105         { "IgnoreOnIsolate",      bus_property_append_bool,           "b", offsetof(Unit, ignore_on_isolate)                  },
1106         { "IgnoreOnSnapshot",     bus_property_append_bool,           "b", offsetof(Unit, ignore_on_snapshot)                 },
1107         { "NeedDaemonReload",     bus_unit_append_need_daemon_reload, "b", 0 },
1108         { "JobTimeoutUSec",       bus_property_append_usec,           "t", offsetof(Unit, job_timeout)                        },
1109         { "ConditionTimestamp",   bus_property_append_usec,           "t", offsetof(Unit, condition_timestamp.realtime)       },
1110         { "ConditionTimestampMonotonic", bus_property_append_usec,    "t", offsetof(Unit, condition_timestamp.monotonic)      },
1111         { "ConditionResult",      bus_property_append_bool,           "b", offsetof(Unit, condition_result)                   },
1112         { "LoadError",            bus_unit_append_load_error,      "(ss)", 0 },
1113         { NULL, }
1114 };
1115
1116 const BusProperty bus_unit_cgroup_properties[] = {
1117         { "DefaultControlGroup",    bus_unit_append_default_cgroup,     "s", 0 },
1118         { "ControlGroups",          bus_unit_append_cgroups,           "as", 0 },
1119         { "ControlGroupAttributes", bus_unit_append_cgroup_attrs,  "a(sss)", 0 },
1120         { NULL, }
1121 };