]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/core/dbus-unit.c
core: add bus API and systemctl commands for altering cgroup parameters during runtime
[thirdparty/systemd.git] / src / core / dbus-unit.c
CommitLineData
d6c9574f 1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
ea430986 2
a7334b09
LP
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
5430f7f2
LP
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
a7334b09
LP
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
5430f7f2 16 Lesser General Public License for more details.
a7334b09 17
5430f7f2 18 You should have received a copy of the GNU Lesser General Public License
a7334b09
LP
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
ea430986
LP
22#include <errno.h>
23
24#include "dbus.h"
25#include "log.h"
4139c1b2 26#include "dbus-unit.h"
398ef8ba 27#include "bus-errors.h"
bfebab7f 28#include "dbus-common.h"
e2417e41 29#include "selinux-access.h"
246aa6dd
LP
30#include "cgroup-util.h"
31#include "strv.h"
32#include "path-util.h"
ea430986 33
9a60da28 34const char bus_unit_interface[] _introspect_("Unit") = BUS_UNIT_INTERFACE;
4288f619 35
c4e2ceae
LP
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" \
34df5a34 45 "NeedDaemonReload\0"
c4e2ceae 46
e2110e5d 47static int bus_unit_append_names(DBusMessageIter *i, const char *property, void *data) {
4139c1b2
LP
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
ac155bb8 56 SET_FOREACH(t, u->names, j)
4139c1b2
LP
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
e2110e5d 66static int bus_unit_append_following(DBusMessageIter *i, const char *property, void *data) {
a7f241db 67 Unit *u = data, *f;
8fe914ec
LP
68 const char *d;
69
8fe914ec
LP
70 assert(i);
71 assert(property);
72 assert(u);
73
a7f241db 74 f = unit_following(u);
ac155bb8 75 d = f ? f->id : "";
8fe914ec
LP
76
77 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &d))
78 return -ENOMEM;
79
80 return 0;
81}
82
e2110e5d 83static int bus_unit_append_dependencies(DBusMessageIter *i, const char *property, void *data) {
5301be81
LP
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)
ac155bb8 93 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &u->id))
5301be81
LP
94 return -ENOMEM;
95
96 if (!dbus_message_iter_close_container(i, &sub))
97 return -ENOMEM;
98
99 return 0;
100}
101
e2110e5d 102static int bus_unit_append_description(DBusMessageIter *i, const char *property, void *data) {
ea430986
LP
103 Unit *u = data;
104 const char *d;
105
ea430986
LP
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
e2110e5d 118static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_unit_append_load_state, unit_load_state, UnitLoadState);
ea430986 119
e2110e5d 120static int bus_unit_append_active_state(DBusMessageIter *i, const char *property, void *data) {
ea430986
LP
121 Unit *u = data;
122 const char *state;
123
ea430986
LP
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
e2110e5d 136static int bus_unit_append_sub_state(DBusMessageIter *i, const char *property, void *data) {
10a94420
LP
137 Unit *u = data;
138 const char *state;
139
10a94420
LP
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
e2110e5d 152static int bus_unit_append_file_state(DBusMessageIter *i, const char *property, void *data) {
a4375746
LP
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
e2110e5d 168static int bus_unit_append_can_start(DBusMessageIter *i, const char *property, void *data) {
38131695
LP
169 Unit *u = data;
170 dbus_bool_t b;
171
38131695
LP
172 assert(i);
173 assert(property);
174 assert(u);
175
064f51fa 176 b = unit_can_start(u) &&
ac155bb8 177 !u->refuse_manual_start;
b5e9dba8
LP
178
179 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
180 return -ENOMEM;
181
182 return 0;
183}
184
e2110e5d 185static int bus_unit_append_can_stop(DBusMessageIter *i, const char *property, void *data) {
b5e9dba8
LP
186 Unit *u = data;
187 dbus_bool_t b;
188
b5e9dba8
LP
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) &&
ac155bb8 197 !u->refuse_manual_stop;
38131695
LP
198
199 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
200 return -ENOMEM;
201
202 return 0;
203}
204
e2110e5d 205static int bus_unit_append_can_reload(DBusMessageIter *i, const char *property, void *data) {
38131695
LP
206 Unit *u = data;
207 dbus_bool_t b;
208
38131695
LP
209 assert(i);
210 assert(property);
211 assert(u);
212
4139c1b2 213 b = unit_can_reload(u);
38131695
LP
214
215 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
216 return -ENOMEM;
217
218 return 0;
219}
220
e2110e5d 221static int bus_unit_append_can_isolate(DBusMessageIter *i, const char *property, void *data) {
2528a7a6
LP
222 Unit *u = data;
223 dbus_bool_t b;
224
2528a7a6
LP
225 assert(i);
226 assert(property);
227 assert(u);
228
229 b = unit_can_isolate(u) &&
ac155bb8 230 !u->refuse_manual_start;
2528a7a6
LP
231
232 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
233 return -ENOMEM;
234
235 return 0;
236}
237
e2110e5d 238static int bus_unit_append_job(DBusMessageIter *i, const char *property, void *data) {
38131695
LP
239 Unit *u = data;
240 DBusMessageIter sub;
f93891f3 241 _cleanup_free_ char *p = NULL;
38131695 242
38131695
LP
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
ac155bb8 250 if (u->job) {
38131695 251
f93891f3
ZJS
252 p = job_dbus_path(u->job);
253 if (!p)
38131695
LP
254 return -ENOMEM;
255
ac155bb8 256 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT32, &u->job->id) ||
f93891f3 257 !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &p))
38131695 258 return -ENOMEM;
38131695
LP
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
f93891f3
ZJS
266 p = unit_dbus_path(u);
267 if (!p)
38131695
LP
268 return -ENOMEM;
269
270 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT32, &id) ||
f93891f3 271 !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &p))
38131695 272 return -ENOMEM;
38131695
LP
273 }
274
38131695
LP
275 if (!dbus_message_iter_close_container(i, &sub))
276 return -ENOMEM;
277
278 return 0;
279}
280
e2110e5d 281static int bus_unit_append_default_cgroup(DBusMessageIter *i, const char *property, void *data) {
4139c1b2
LP
282 Unit *u = data;
283 char *t;
284 CGroupBonding *cgb;
285 bool success;
ea430986 286
4139c1b2
LP
287 assert(i);
288 assert(property);
289 assert(u);
290
f93891f3
ZJS
291 cgb = unit_get_default_cgroup(u);
292 if (cgb) {
293 t = cgroup_bonding_to_string(cgb);
294 if (!t)
4139c1b2
LP
295 return -ENOMEM;
296 } else
297 t = (char*) "";
ea430986 298
4139c1b2
LP
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
e2110e5d 307static int bus_unit_append_cgroups(DBusMessageIter *i, const char *property, void *data) {
4139c1b2
LP
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
ac155bb8 315 LIST_FOREACH(by_unit, cgb, u->cgroup_bondings) {
f93891f3 316 char _cleanup_free_ *t = NULL;
4139c1b2
LP
317 bool success;
318
f93891f3
ZJS
319 t = cgroup_bonding_to_string(cgb);
320 if (!t)
4139c1b2
LP
321 return -ENOMEM;
322
323 success = dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &t);
4139c1b2
LP
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
e2110e5d 334static int bus_unit_append_cgroup_attrs(DBusMessageIter *i, const char *property, void *data) {
d8bbda91
LP
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
ac155bb8 342 LIST_FOREACH(by_unit, a, u->cgroup_attributes) {
f93891f3 343 char _cleanup_free_ *v = NULL;
d8bbda91
LP
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);
d8bbda91
LP
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
e2110e5d 365static int bus_unit_append_need_daemon_reload(DBusMessageIter *i, const char *property, void *data) {
45fb0699
LP
366 Unit *u = data;
367 dbus_bool_t b;
368
45fb0699
LP
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
e2110e5d 381static int bus_unit_append_load_error(DBusMessageIter *i, const char *property, void *data) {
9f39404c
LP
382 Unit *u = data;
383 const char *name, *message;
384 DBusMessageIter sub;
385
386 assert(i);
387 assert(property);
388 assert(u);
389
ac155bb8
MS
390 if (u->load_error != 0) {
391 name = bus_errno_to_dbus(u->load_error);
392 message = strempty(strerror(-u->load_error));
9f39404c
LP
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
5e8d1c9a 405static DBusHandlerResult bus_unit_message_dispatch(Unit *u, DBusConnection *connection, DBusMessage *message) {
cad45ba1 406 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
b548631a
LP
407 DBusError error;
408 JobType job_type = _JOB_TYPE_INVALID;
6f28c033 409 bool reload_if_possible = false;
e2417e41 410 int r;
b548631a
LP
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;
9a1ac7b9
LP
422 else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "TryRestart"))
423 job_type = JOB_TRY_RESTART;
6f28c033
LP
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;
8a0867d6 430 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Kill")) {
c74f17d9 431 const char *swho;
8a0867d6 432 int32_t signo;
8a0867d6 433 KillWho who;
8a0867d6
LP
434
435 if (!dbus_message_get_args(
436 message,
437 &error,
438 DBUS_TYPE_STRING, &swho,
8a0867d6
LP
439 DBUS_TYPE_INT32, &signo,
440 DBUS_TYPE_INVALID))
bfebab7f 441 return bus_send_error_reply(connection, message, &error, -EINVAL);
8a0867d6 442
0a524ba7
LP
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
0a524ba7 451 if (signo <= 0 || signo >= _NSIG)
bfebab7f 452 return bus_send_error_reply(connection, message, &error, -EINVAL);
8a0867d6 453
cad45ba1
LP
454 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "stop");
455
c74f17d9
LP
456 r = unit_kill(u, who, signo, &error);
457 if (r < 0)
bfebab7f 458 return bus_send_error_reply(connection, message, &error, r);
8a0867d6 459
c74f17d9
LP
460 reply = dbus_message_new_method_return(message);
461 if (!reply)
8a0867d6
LP
462 goto oom;
463
fdf20a31 464 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "ResetFailed")) {
5632e374 465
cad45ba1
LP
466 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "reload");
467
fdf20a31 468 unit_reset_failed(u);
5632e374 469
cad45ba1
LP
470 reply = dbus_message_new_method_return(message);
471 if (!reply)
5632e374
LP
472 goto oom;
473
246aa6dd
LP
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
6f28c033 537 } else if (UNIT_VTABLE(u)->bus_message_handler)
5e8d1c9a 538 return UNIT_VTABLE(u)->bus_message_handler(u, connection, message);
b548631a 539 else
4139c1b2 540 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
b548631a
LP
541
542 if (job_type != _JOB_TYPE_INVALID) {
543 const char *smode;
544 JobMode mode;
bc0f8771 545
b548631a
LP
546 if (!dbus_message_get_args(
547 message,
548 &error,
549 DBUS_TYPE_STRING, &smode,
550 DBUS_TYPE_INVALID))
bfebab7f 551 return bus_send_error_reply(connection, message, &error, -EINVAL);
b548631a 552
cad45ba1
LP
553 mode = job_mode_from_string(smode);
554 if (mode < 0) {
398ef8ba 555 dbus_set_error(&error, BUS_ERROR_INVALID_JOB_MODE, "Job mode %s is invalid.", smode);
bfebab7f 556 return bus_send_error_reply(connection, message, &error, -EINVAL);
398ef8ba 557 }
b548631a 558
cad45ba1 559 return bus_unit_queue_job(connection, message, u, job_type, mode, reload_if_possible);
b548631a
LP
560 }
561
cad45ba1 562 if (reply)
5e8d1c9a 563 if (!dbus_connection_send(connection, reply, NULL))
b548631a
LP
564 goto oom;
565
b548631a
LP
566 return DBUS_HANDLER_RESULT_HANDLED;
567
568oom:
b548631a 569 dbus_error_free(&error);
b548631a 570 return DBUS_HANDLER_RESULT_NEED_MEMORY;
ea430986
LP
571}
572
5e8d1c9a 573static DBusHandlerResult bus_unit_message_handler(DBusConnection *connection, DBusMessage *message, void *data) {
ea430986
LP
574 Manager *m = data;
575 Unit *u;
576 int r;
cad45ba1 577 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
80fbf05e 578 DBusError error;
ea430986
LP
579
580 assert(connection);
581 assert(message);
582 assert(m);
583
80fbf05e
MS
584 dbus_error_init(&error);
585
2cccbca4
LP
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
ffc227c9 589 SELINUX_ACCESS_CHECK(connection, message, "status");
cad45ba1 590
2cccbca4
LP
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
cad45ba1
LP
598 reply = dbus_message_new_method_return(message);
599 if (!reply)
2cccbca4
LP
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
cad45ba1
LP
607 f = open_memstream(&introspection, &size);
608 if (!f)
2cccbca4
LP
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
ac155bb8 620 if (k != u->id)
2cccbca4
LP
621 continue;
622
cad45ba1
LP
623 p = bus_path_escape(k);
624 if (!p) {
2cccbca4
LP
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
2cccbca4
LP
657 return DBUS_HANDLER_RESULT_HANDLED;
658 }
659
660 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
661 }
662
80fbf05e 663 r = manager_load_unit_from_dbus_path(m, dbus_message_get_path(message), &error, &u);
cad45ba1
LP
664 if (r == -ENOMEM)
665 goto oom;
666 if (r < 0)
80fbf05e 667 return bus_send_error_reply(connection, message, &error, r);
ea430986 668
5e8d1c9a 669 return bus_unit_message_dispatch(u, connection, message);
2cccbca4
LP
670
671oom:
80fbf05e
MS
672 dbus_error_free(&error);
673
2cccbca4 674 return DBUS_HANDLER_RESULT_NEED_MEMORY;
ea430986
LP
675}
676
677const DBusObjectPathVTable bus_unit_vtable = {
678 .message_function = bus_unit_message_handler
679};
c1e1601e
LP
680
681void bus_unit_send_change_signal(Unit *u) {
f93891f3
ZJS
682 _cleanup_free_ char *p = NULL;
683 _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
c1e1601e
LP
684
685 assert(u);
c1e1601e 686
ac155bb8
MS
687 if (u->in_dbus_queue) {
688 LIST_REMOVE(Unit, dbus_queue, u->manager->dbus_unit_queue, u);
689 u->in_dbus_queue = false;
c0bd0cf7 690 }
c1e1601e 691
ac155bb8 692 if (!u->id)
04ade7d2
LP
693 return;
694
ac155bb8
MS
695 if (!bus_has_subscriber(u->manager)) {
696 u->sent_dbus_new_signal = true;
c1e1601e 697 return;
94b6dfa2 698 }
c1e1601e 699
f93891f3
ZJS
700 p = unit_dbus_path(u);
701 if (!p)
c1e1601e
LP
702 goto oom;
703
ac155bb8 704 if (u->sent_dbus_new_signal) {
c4e2ceae
LP
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
c5315881 708 * behavior if needed. */
c4e2ceae
LP
709
710 if (UNIT_VTABLE(u)->bus_invalidating_properties) {
711
f93891f3
ZJS
712 m = bus_properties_changed_new(p,
713 UNIT_VTABLE(u)->bus_interface,
714 UNIT_VTABLE(u)->bus_invalidating_properties);
715 if (!m)
c4e2ceae
LP
716 goto oom;
717
ac155bb8 718 if (bus_broadcast(u->manager, m) < 0)
c4e2ceae 719 goto oom;
c1e1601e 720
c4e2ceae
LP
721 dbus_message_unref(m);
722 }
723
f93891f3
ZJS
724 m = bus_properties_changed_new(p, "org.freedesktop.systemd1.Unit",
725 INVALIDATING_PROPERTIES);
726 if (!m)
c1e1601e 727 goto oom;
c4e2ceae 728
c1e1601e 729 } else {
c1e1601e
LP
730 /* Send a new signal */
731
f93891f3
ZJS
732 m = dbus_message_new_signal("/org/freedesktop/systemd1",
733 "org.freedesktop.systemd1.Manager",
734 "UnitNew");
735 if (!m)
c1e1601e
LP
736 goto oom;
737
c1e1601e 738 if (!dbus_message_append_args(m,
ac155bb8 739 DBUS_TYPE_STRING, &u->id,
c1e1601e
LP
740 DBUS_TYPE_OBJECT_PATH, &p,
741 DBUS_TYPE_INVALID))
742 goto oom;
743 }
744
ac155bb8 745 if (bus_broadcast(u->manager, m) < 0)
c1e1601e
LP
746 goto oom;
747
ac155bb8 748 u->sent_dbus_new_signal = true;
c1e1601e
LP
749
750 return;
751
752oom:
f93891f3 753 log_oom();
c1e1601e
LP
754}
755
756void bus_unit_send_removed_signal(Unit *u) {
f93891f3
ZJS
757 _cleanup_free_ char *p = NULL;
758 _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
c1e1601e
LP
759
760 assert(u);
761
ac155bb8 762 if (!bus_has_subscriber(u->manager))
c1e1601e
LP
763 return;
764
ac155bb8 765 if (!u->sent_dbus_new_signal)
7535cc78
LP
766 bus_unit_send_change_signal(u);
767
ac155bb8 768 if (!u->id)
04ade7d2
LP
769 return;
770
f93891f3
ZJS
771 p = unit_dbus_path(u);
772 if (!p)
c1e1601e
LP
773 goto oom;
774
f93891f3
ZJS
775 m = dbus_message_new_signal("/org/freedesktop/systemd1",
776 "org.freedesktop.systemd1.Manager",
777 "UnitRemoved");
778 if (!m)
c1e1601e
LP
779 goto oom;
780
c1e1601e 781 if (!dbus_message_append_args(m,
ac155bb8 782 DBUS_TYPE_STRING, &u->id,
c1e1601e
LP
783 DBUS_TYPE_OBJECT_PATH, &p,
784 DBUS_TYPE_INVALID))
785 goto oom;
786
ac155bb8 787 if (bus_broadcast(u->manager, m) < 0)
c1e1601e
LP
788 goto oom;
789
c1e1601e
LP
790 return;
791
792oom:
f93891f3 793 log_oom();
c1e1601e 794}
e2110e5d 795
cad45ba1
LP
796DBusHandlerResult 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))) {
f93891f3
ZJS
838 dbus_set_error(&error, BUS_ERROR_ONLY_BY_DEPENDENCY,
839 "Operation refused, unit %s may be requested by dependency only.", u->id);
cad45ba1
LP
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
872oom:
873 dbus_error_free(&error);
874
875 return DBUS_HANDLER_RESULT_NEED_MEMORY;
876}
877
246aa6dd
LP
878int 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
925int 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
968int 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
1026int 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
e2110e5d
MS
1052const 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 },
7f2cddae 1061 { "BindsTo", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_BINDS_TO]), true },
b05afff1 1062 { "PartOf", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_PART_OF]), true },
e2110e5d
MS
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 },
b05afff1 1067 { "ConsistsOf", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_CONSISTS_OF]), true },
e2110e5d
MS
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 },
7f2cddae
LP
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 },
7c8fa05c 1077 { "RequiresMountsFor", bus_property_append_strv, "as", offsetof(Unit, requires_mounts_for), true },
49dbfa7b 1078 { "Documentation", bus_property_append_strv, "as", offsetof(Unit, documentation), true },
e2110e5d
MS
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 },
1b64d026 1084 { "SourcePath", bus_property_append_string, "s", offsetof(Unit, source_path), true },
e2110e5d
MS
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) },
e2110e5d
MS
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};
246aa6dd
LP
1115
1116const 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};