]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/machine/machined-dbus.c
update TODO
[thirdparty/systemd.git] / src / machine / machined-dbus.c
CommitLineData
1ee306e1
LP
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2011 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#include <string.h>
24#include <unistd.h>
25#include <pwd.h>
26
27#include <systemd/sd-id128.h>
28#include <systemd/sd-messages.h>
29
30#include "machined.h"
31#include "dbus-common.h"
32#include "strv.h"
33#include "mkdir.h"
34#include "path-util.h"
35#include "special.h"
36#include "sleep-config.h"
37#include "fileio-label.h"
38#include "label.h"
39#include "utf8.h"
40#include "unit-name.h"
41#include "bus-errors.h"
42#include "virt.h"
43
44#define BUS_MANAGER_INTERFACE \
6a4e0b13 45 " <interface name=\"org.freedesktop.machine1.Manager\">\n" \
1ee306e1
LP
46 " <method name=\"GetMachine\">\n" \
47 " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
48 " <arg name=\"machine\" type=\"o\" direction=\"out\"/>\n" \
49 " </method>\n" \
50 " <method name=\"GetMachineByPID\">\n" \
51 " <arg name=\"pid\" type=\"u\" direction=\"in\"/>\n" \
52 " <arg name=\"machine\" type=\"o\" direction=\"out\"/>\n" \
53 " </method>\n" \
54 " <method name=\"ListMachines\">\n" \
55 " <arg name=\"machines\" type=\"a(ssso)\" direction=\"out\"/>\n" \
56 " </method>\n" \
57 " <method name=\"CreateMachine\">\n" \
58 " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
59 " <arg name=\"id\" type=\"ay\" direction=\"in\"/>\n" \
60 " <arg name=\"service\" type=\"s\" direction=\"in\"/>\n" \
61 " <arg name=\"class\" type=\"s\" direction=\"in\"/>\n" \
62 " <arg name=\"leader\" type=\"u\" direction=\"in\"/>\n" \
63 " <arg name=\"root_directory\" type=\"s\" direction=\"in\"/>\n" \
64 " <arg name=\"scope_properties\" type=\"a(sv)\" direction=\"in\"/>\n" \
65 " <arg name=\"path\" type=\"o\" direction=\"out\"/>\n" \
66 " </method>\n" \
67 " <method name=\"KillMachine\">\n" \
68 " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
69 " <arg name=\"who\" type=\"s\" direction=\"in\"/>\n" \
70 " <arg name=\"signal\" type=\"s\" direction=\"in\"/>\n" \
71 " </method>\n" \
72 " <method name=\"TerminateMachine\">\n" \
73 " <arg name=\"id\" type=\"s\" direction=\"in\"/>\n" \
74 " </method>\n" \
75 " <signal name=\"MachineNew\">\n" \
76 " <arg name=\"machine\" type=\"s\"/>\n" \
77 " <arg name=\"path\" type=\"o\"/>\n" \
78 " </signal>\n" \
79 " <signal name=\"MachineRemoved\">\n" \
80 " <arg name=\"machine\" type=\"s\"/>\n" \
81 " <arg name=\"path\" type=\"o\"/>\n" \
82 " </signal>\n" \
83 " </interface>\n"
84
85#define INTROSPECTION_BEGIN \
86 DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \
87 "<node>\n" \
88 BUS_MANAGER_INTERFACE \
89 BUS_PROPERTIES_INTERFACE \
90 BUS_PEER_INTERFACE \
91 BUS_INTROSPECTABLE_INTERFACE
92
93#define INTROSPECTION_END \
94 "</node>\n"
95
96#define INTERFACES_LIST \
97 BUS_GENERIC_INTERFACES_LIST \
98 "org.freedesktop.machine1.Manager\0"
99
100static bool valid_machine_name(const char *p) {
101 size_t l;
102
103 if (!filename_is_safe(p))
104 return false;
105
106 if (!ascii_is_valid(p))
107 return false;
108
109 l = strlen(p);
110
111 if (l < 1 || l> 64)
112 return false;
113
114 return true;
115}
116
117static int bus_manager_create_machine(Manager *manager, DBusMessage *message) {
118
8aec412f 119 const char *name, *service, *class, *root_directory;
1ee306e1
LP
120 _cleanup_free_ char *p = NULL;
121 DBusMessageIter iter, sub;
122 MachineClass c;
123 uint32_t leader;
124 sd_id128_t id;
125 Machine *m;
126 int n, r;
127 void *v;
128
129 assert(manager);
130 assert(message);
131
132 if (!dbus_message_iter_init(message, &iter) ||
133 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
134 return -EINVAL;
135
136 dbus_message_iter_get_basic(&iter, &name);
137
138 if (!valid_machine_name(name) ||
139 !dbus_message_iter_next(&iter) ||
140 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
141 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_BYTE)
142 return -EINVAL;
143
144 dbus_message_iter_recurse(&iter, &sub);
145 dbus_message_iter_get_fixed_array(&sub, &v, &n);
146
147 if (n == 0)
148 id = SD_ID128_NULL;
149 else if (n == 16)
150 memcpy(&id, v, n);
151 else
152 return -EINVAL;
153
154 if (!dbus_message_iter_next(&iter) ||
155 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
156 return -EINVAL;
157
158 dbus_message_iter_get_basic(&iter, &service);
159
160 if (!dbus_message_iter_next(&iter) ||
161 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
162 return -EINVAL;
163
164 dbus_message_iter_get_basic(&iter, &class);
165
166 if (isempty(class))
167 c = _MACHINE_CLASS_INVALID;
168 else {
169 c = machine_class_from_string(class);
170 if (c < 0)
171 return -EINVAL;
172 }
173
174 if (!dbus_message_iter_next(&iter) ||
175 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_UINT32)
176 return -EINVAL;
177
178 dbus_message_iter_get_basic(&iter, &leader);
179 if (!dbus_message_iter_next(&iter) ||
180 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
181 return -EINVAL;
182
1ee306e1
LP
183 dbus_message_iter_get_basic(&iter, &root_directory);
184
185 if (!(isempty(root_directory) || path_is_absolute(root_directory)))
186 return -EINVAL;
187
188 if (hashmap_get(manager->machines, name))
189 return -EEXIST;
190
191 if (leader <= 0) {
192 leader = bus_get_unix_process_id(manager->bus, dbus_message_get_sender(message), NULL);
193 if (leader == 0)
194 return -EINVAL;
195 }
196
197 r = manager_add_machine(manager, name, &m);
198 if (r < 0)
199 goto fail;
200
201 m->leader = leader;
202 m->class = c;
203 m->id = id;
204
205 if (!isempty(service)) {
206 m->service = strdup(service);
207 if (!m->service) {
208 r = -ENOMEM;
209 goto fail;
210 }
211 }
212
213 if (!isempty(root_directory)) {
214 m->root_directory = strdup(root_directory);
215 if (!m->root_directory) {
216 r = -ENOMEM;
217 goto fail;
218 }
219 }
220
221 r = machine_start(m);
222 if (r < 0)
223 goto fail;
224
225 m->create_message = dbus_message_ref(message);
226
227 return 0;
228
229fail:
230 if (m)
231 machine_add_to_gc_queue(m);
232
233 return r;
234}
235
236static DBusHandlerResult manager_message_handler(
237 DBusConnection *connection,
238 DBusMessage *message,
239 void *userdata) {
240
241 Manager *m = userdata;
242
243 DBusError error;
244 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
245 int r;
246
247 assert(connection);
248 assert(message);
249 assert(m);
250
251 dbus_error_init(&error);
252
253 if (dbus_message_is_method_call(message, "org.freedesktop.machine1.Manager", "GetMachine")) {
254 Machine *machine;
255 const char *name;
256 char *p;
257 bool b;
258
259 if (!dbus_message_get_args(
260 message,
261 &error,
262 DBUS_TYPE_STRING, &name,
263 DBUS_TYPE_INVALID))
264 return bus_send_error_reply(connection, message, &error, -EINVAL);
265
266 machine = hashmap_get(m->machines, name);
267 if (!machine)
268 return bus_send_error_reply(connection, message, &error, -ENOENT);
269
270 reply = dbus_message_new_method_return(message);
271 if (!reply)
272 goto oom;
273
274 p = machine_bus_path(machine);
275 if (!p)
276 goto oom;
277
278 b = dbus_message_append_args(
279 reply,
280 DBUS_TYPE_OBJECT_PATH, &p,
281 DBUS_TYPE_INVALID);
282 free(p);
283
284 if (!b)
285 goto oom;
286
287 } else if (dbus_message_is_method_call(message, "org.freedesktop.machine1.Manager", "GetMachineByPID")) {
288 uint32_t pid;
289 char *p;
290 Machine *machine;
291 bool b;
292
293 if (!dbus_message_get_args(
294 message,
295 &error,
296 DBUS_TYPE_UINT32, &pid,
297 DBUS_TYPE_INVALID))
298 return bus_send_error_reply(connection, message, &error, -EINVAL);
299
300 r = manager_get_machine_by_pid(m, pid, &machine);
301 if (r <= 0)
302 return bus_send_error_reply(connection, message, NULL, r < 0 ? r : -ENOENT);
303
304 reply = dbus_message_new_method_return(message);
305 if (!reply)
306 goto oom;
307
308 p = machine_bus_path(machine);
309 if (!p)
310 goto oom;
311
312 b = dbus_message_append_args(
313 reply,
314 DBUS_TYPE_OBJECT_PATH, &p,
315 DBUS_TYPE_INVALID);
316 free(p);
317
318 if (!b)
319 goto oom;
320
321 } else if (dbus_message_is_method_call(message, "org.freedesktop.machine1.Manager", "ListMachines")) {
322 Machine *machine;
323 Iterator i;
324 DBusMessageIter iter, sub;
325
326 reply = dbus_message_new_method_return(message);
327 if (!reply)
328 goto oom;
329
330 dbus_message_iter_init_append(reply, &iter);
331
332 if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(ssso)", &sub))
333 goto oom;
334
335 HASHMAP_FOREACH(machine, m->machines, i) {
336 _cleanup_free_ char *p = NULL;
337 DBusMessageIter sub2;
338 const char *class;
339
340 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2))
341 goto oom;
342
343 p = machine_bus_path(machine);
344 if (!p)
345 goto oom;
346
347 class = strempty(machine_class_to_string(machine->class));
348
349 if (!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &machine->name) ||
350 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &class) ||
351 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &machine->service) ||
352 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &p)) {
353 free(p);
354 goto oom;
355 }
356
357 if (!dbus_message_iter_close_container(&sub, &sub2))
358 goto oom;
359 }
360
361 if (!dbus_message_iter_close_container(&iter, &sub))
362 goto oom;
363
364 } else if (dbus_message_is_method_call(message, "org.freedesktop.machine1.Manager", "CreateMachine")) {
365
366 r = bus_manager_create_machine(m, message);
367 if (r < 0)
368 return bus_send_error_reply(connection, message, NULL, r);
369
370 } else if (dbus_message_is_method_call(message, "org.freedesktop.machine1.Manager", "KillMachine")) {
371 const char *swho;
372 int32_t signo;
373 KillWho who;
374 const char *name;
375 Machine *machine;
376
377 if (!dbus_message_get_args(
378 message,
379 &error,
380 DBUS_TYPE_STRING, &name,
381 DBUS_TYPE_STRING, &swho,
382 DBUS_TYPE_INT32, &signo,
383 DBUS_TYPE_INVALID))
384 return bus_send_error_reply(connection, message, &error, -EINVAL);
385
386 if (isempty(swho))
387 who = KILL_ALL;
388 else {
389 who = kill_who_from_string(swho);
390 if (who < 0)
391 return bus_send_error_reply(connection, message, &error, -EINVAL);
392 }
393
394 if (signo <= 0 || signo >= _NSIG)
395 return bus_send_error_reply(connection, message, &error, -EINVAL);
396
397 machine = hashmap_get(m->machines, name);
398 if (!machine)
399 return bus_send_error_reply(connection, message, &error, -ENOENT);
400
401 r = machine_kill(machine, who, signo);
402 if (r < 0)
403 return bus_send_error_reply(connection, message, NULL, r);
404
405 reply = dbus_message_new_method_return(message);
406 if (!reply)
407 goto oom;
408
409 } else if (dbus_message_is_method_call(message, "org.freedesktop.machine1.Manager", "TerminateMachine")) {
410 const char *name;
411 Machine *machine;
412
413 if (!dbus_message_get_args(
414 message,
415 &error,
416 DBUS_TYPE_STRING, &name,
417 DBUS_TYPE_INVALID))
418 return bus_send_error_reply(connection, message, &error, -EINVAL);
419
420 machine = hashmap_get(m->machines, name);
421 if (!machine)
422 return bus_send_error_reply(connection, message, &error, -ENOENT);
423
424 r = machine_stop(machine);
425 if (r < 0)
426 return bus_send_error_reply(connection, message, NULL, r);
427
428 reply = dbus_message_new_method_return(message);
429 if (!reply)
430 goto oom;
431
432 } else if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect")) {
433 char *introspection = NULL;
434 FILE *f;
435 Iterator i;
436 Machine *machine;
437 size_t size;
438 char *p;
439
440 reply = dbus_message_new_method_return(message);
441 if (!reply)
442 goto oom;
443
444 /* We roll our own introspection code here, instead of
445 * relying on bus_default_message_handler() because we
446 * need to generate our introspection string
447 * dynamically. */
448
449 f = open_memstream(&introspection, &size);
450 if (!f)
451 goto oom;
452
453 fputs(INTROSPECTION_BEGIN, f);
454
455 HASHMAP_FOREACH(machine, m->machines, i) {
456 p = bus_path_escape(machine->name);
457
458 if (p) {
459 fprintf(f, "<node name=\"machine/%s\"/>", p);
460 free(p);
461 }
462 }
463
464 fputs(INTROSPECTION_END, f);
465
466 if (ferror(f)) {
467 fclose(f);
468 free(introspection);
469 goto oom;
470 }
471
472 fclose(f);
473
474 if (!introspection)
475 goto oom;
476
477 if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &introspection, DBUS_TYPE_INVALID)) {
478 free(introspection);
479 goto oom;
480 }
481
482 free(introspection);
483 } else
484 return bus_default_message_handler(connection, message, NULL, INTERFACES_LIST, NULL);
485
486 if (reply) {
487 if (!bus_maybe_send_reply(connection, message, reply))
488 goto oom;
489 }
490
491 return DBUS_HANDLER_RESULT_HANDLED;
492
493oom:
494 dbus_error_free(&error);
495
496 return DBUS_HANDLER_RESULT_NEED_MEMORY;
497}
498
499const DBusObjectPathVTable bus_manager_vtable = {
500 .message_function = manager_message_handler
501};
502
503DBusHandlerResult bus_message_filter(
504 DBusConnection *connection,
505 DBusMessage *message,
506 void *userdata) {
507
508 Manager *m = userdata;
509 DBusError error;
510
511 assert(m);
512 assert(connection);
513 assert(message);
514
515 dbus_error_init(&error);
516
517 log_debug("Got message: %s %s %s", strna(dbus_message_get_sender(message)), strna(dbus_message_get_interface(message)), strna(dbus_message_get_member(message)));
518
519 if (dbus_message_is_signal(message, "org.freedesktop.systemd1.Manager", "JobRemoved")) {
520 const char *path, *result, *unit;
521 Machine *mm;
522 uint32_t id;
523
524 if (!dbus_message_get_args(message, &error,
525 DBUS_TYPE_UINT32, &id,
526 DBUS_TYPE_OBJECT_PATH, &path,
527 DBUS_TYPE_STRING, &unit,
528 DBUS_TYPE_STRING, &result,
529 DBUS_TYPE_INVALID)) {
530 log_error("Failed to parse JobRemoved message: %s", bus_error_message(&error));
531 goto finish;
532 }
533
1ee306e1
LP
534 mm = hashmap_get(m->machine_units, unit);
535 if (mm) {
536 if (streq_ptr(path, mm->scope_job)) {
537 free(mm->scope_job);
538 mm->scope_job = NULL;
8aec412f 539 machine_save(mm);
1ee306e1
LP
540
541 if (mm->started) {
542 if (streq(result, "done"))
543 machine_send_create_reply(mm, NULL);
544 else {
545 dbus_set_error(&error, BUS_ERROR_JOB_FAILED, "Start job for unit %s failed with '%s'", unit, result);
546 machine_send_create_reply(mm, &error);
547 }
548 }
549 }
550
551 machine_add_to_gc_queue(mm);
552 }
553
554 } else if (dbus_message_is_signal(message, "org.freedesktop.DBus.Properties", "PropertiesChanged")) {
555
556 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
557 _cleanup_free_ char *unit = NULL;
558 const char *path;
559
560 path = dbus_message_get_path(message);
561 if (!path)
562 goto finish;
563
564 unit_name_from_dbus_path(path, &unit);
565 if (unit) {
566 Machine *mm;
567
568 mm = hashmap_get(m->machine_units, unit);
569 if (mm)
570 machine_add_to_gc_queue(mm);
571 }
943aca8e
LP
572
573 } else if (dbus_message_is_signal(message, "org.freedesktop.systemd1.Manager", "UnitRemoved")) {
574 const char *path, *unit;
575 Machine *mm;
576
577 if (!dbus_message_get_args(message, &error,
578 DBUS_TYPE_STRING, &unit,
579 DBUS_TYPE_OBJECT_PATH, &path,
580 DBUS_TYPE_INVALID)) {
581 log_error("Failed to parse UnitRemoved message: %s", bus_error_message(&error));
582 goto finish;
583 }
584
585 mm = hashmap_get(m->machine_units, unit);
6797c324 586 if (mm)
943aca8e 587 machine_add_to_gc_queue(mm);
6797c324
LP
588
589 } else if (dbus_message_is_signal(message, "org.freedesktop.systemd1.Manager", "Reloading")) {
590 dbus_bool_t b;
591
592 if (!dbus_message_get_args(message, &error,
593 DBUS_TYPE_BOOLEAN, &b,
594 DBUS_TYPE_INVALID)) {
595 log_error("Failed to parse Reloading message: %s", bus_error_message(&error));
596 goto finish;
597 }
598
599 /* systemd finished reloading, let's recheck all our machines */
600 if (!b) {
601 Machine *mm;
602 Iterator i;
603
604 log_debug("System manager has been reloaded, rechecking machines...");
605
606 HASHMAP_FOREACH(mm, m->machines, i)
607 machine_add_to_gc_queue(mm);
943aca8e 608 }
1ee306e1
LP
609 }
610
611finish:
612 dbus_error_free(&error);
613
614 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
615}
616
617int manager_start_scope(
618 Manager *manager,
619 const char *scope,
620 pid_t pid,
621 const char *slice,
622 const char *description,
623 DBusError *error,
624 char **job) {
625
626 _cleanup_dbus_message_unref_ DBusMessage *m = NULL, *reply = NULL;
627 DBusMessageIter iter, sub, sub2, sub3, sub4;
628 const char *timeout_stop_property = "TimeoutStopUSec";
629 const char *pids_property = "PIDs";
630 uint64_t timeout = 500 * USEC_PER_MSEC;
631 const char *fail = "fail";
632 uint32_t u;
633
634 assert(manager);
635 assert(scope);
636 assert(pid > 1);
637
638 if (!slice)
639 slice = "";
640
641 m = dbus_message_new_method_call(
642 "org.freedesktop.systemd1",
643 "/org/freedesktop/systemd1",
644 "org.freedesktop.systemd1.Manager",
645 "StartTransientUnit");
646 if (!m)
647 return log_oom();
648
649 dbus_message_iter_init_append(m, &iter);
650
651 if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &scope) ||
652 !dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &fail) ||
653 !dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(sv)", &sub))
654 return log_oom();
655
656 if (!isempty(slice)) {
657 const char *slice_property = "Slice";
658
659 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
660 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &slice_property) ||
661 !dbus_message_iter_open_container(&sub2, DBUS_TYPE_VARIANT, "s", &sub3) ||
662 !dbus_message_iter_append_basic(&sub3, DBUS_TYPE_STRING, &slice) ||
663 !dbus_message_iter_close_container(&sub2, &sub3) ||
664 !dbus_message_iter_close_container(&sub, &sub2))
665 return log_oom();
666 }
667
668 if (!isempty(description)) {
669 const char *description_property = "Description";
670
671 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
672 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &description_property) ||
673 !dbus_message_iter_open_container(&sub2, DBUS_TYPE_VARIANT, "s", &sub3) ||
674 !dbus_message_iter_append_basic(&sub3, DBUS_TYPE_STRING, &description) ||
675 !dbus_message_iter_close_container(&sub2, &sub3) ||
676 !dbus_message_iter_close_container(&sub, &sub2))
677 return log_oom();
678 }
679
680 /* cgroup empty notification is not available in containers
681 * currently. To make this less problematic, let's shorten the
682 * stop timeout for sessions, so that we don't wait
683 * forever. */
684
685 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
686 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &timeout_stop_property) ||
687 !dbus_message_iter_open_container(&sub2, DBUS_TYPE_VARIANT, "t", &sub3) ||
688 !dbus_message_iter_append_basic(&sub3, DBUS_TYPE_UINT64, &timeout) ||
689 !dbus_message_iter_close_container(&sub2, &sub3) ||
690 !dbus_message_iter_close_container(&sub, &sub2))
691 return log_oom();
692
693 u = pid;
694 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
695 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &pids_property) ||
696 !dbus_message_iter_open_container(&sub2, DBUS_TYPE_VARIANT, "au", &sub3) ||
697 !dbus_message_iter_open_container(&sub3, DBUS_TYPE_ARRAY, "u", &sub4) ||
698 !dbus_message_iter_append_basic(&sub4, DBUS_TYPE_UINT32, &u) ||
699 !dbus_message_iter_close_container(&sub3, &sub4) ||
700 !dbus_message_iter_close_container(&sub2, &sub3) ||
701 !dbus_message_iter_close_container(&sub, &sub2) ||
702 !dbus_message_iter_close_container(&iter, &sub))
703 return log_oom();
704
705 reply = dbus_connection_send_with_reply_and_block(manager->bus, m, -1, error);
706 if (!reply)
707 return -EIO;
708
709 if (job) {
710 const char *j;
711 char *copy;
712
713 if (!dbus_message_get_args(reply, error, DBUS_TYPE_OBJECT_PATH, &j, DBUS_TYPE_INVALID))
714 return -EIO;
715
716 copy = strdup(j);
717 if (!copy)
718 return -ENOMEM;
719
720 *job = copy;
721 }
722
723 return 0;
724}
725
726int manager_stop_unit(Manager *manager, const char *unit, DBusError *error, char **job) {
727 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
728 const char *fail = "fail";
729 int r;
730
731 assert(manager);
732 assert(unit);
733
734 r = bus_method_call_with_reply(
735 manager->bus,
736 "org.freedesktop.systemd1",
737 "/org/freedesktop/systemd1",
738 "org.freedesktop.systemd1.Manager",
739 "StopUnit",
740 &reply,
741 error,
742 DBUS_TYPE_STRING, &unit,
743 DBUS_TYPE_STRING, &fail,
744 DBUS_TYPE_INVALID);
745 if (r < 0) {
6797c324
LP
746 if (dbus_error_has_name(error, BUS_ERROR_NO_SUCH_UNIT) ||
747 dbus_error_has_name(error, BUS_ERROR_LOAD_FAILED)) {
748
749 if (job)
750 *job = NULL;
751
752 dbus_error_free(error);
753 return 0;
754 }
755
1ee306e1
LP
756 log_error("Failed to stop unit %s: %s", unit, bus_error(error, r));
757 return r;
758 }
759
760 if (job) {
761 const char *j;
762 char *copy;
763
764 if (!dbus_message_get_args(reply, error,
765 DBUS_TYPE_OBJECT_PATH, &j,
766 DBUS_TYPE_INVALID)) {
767 log_error("Failed to parse reply.");
768 return -EIO;
769 }
770
771 copy = strdup(j);
772 if (!copy)
773 return -ENOMEM;
774
775 *job = copy;
776 }
777
6797c324 778 return 1;
1ee306e1
LP
779}
780
781int manager_kill_unit(Manager *manager, const char *unit, KillWho who, int signo, DBusError *error) {
782 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
783 const char *w;
784 int r;
785
786 assert(manager);
787 assert(unit);
788
789 w = who == KILL_LEADER ? "process" : "cgroup";
790 assert_cc(sizeof(signo) == sizeof(int32_t));
791
792 r = bus_method_call_with_reply(
793 manager->bus,
794 "org.freedesktop.systemd1",
795 "/org/freedesktop/systemd1",
796 "org.freedesktop.systemd1.Manager",
797 "KillUnit",
798 &reply,
799 error,
800 DBUS_TYPE_STRING, &unit,
801 DBUS_TYPE_STRING, &w,
802 DBUS_TYPE_INT32, &signo,
803 DBUS_TYPE_INVALID);
804 if (r < 0) {
805 log_error("Failed to stop unit %s: %s", unit, bus_error(error, r));
806 return r;
807 }
808
809 return 0;
810}
811
812int manager_unit_is_active(Manager *manager, const char *unit) {
813
814 const char *interface = "org.freedesktop.systemd1.Unit";
815 const char *property = "ActiveState";
816 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
817 _cleanup_free_ char *path = NULL;
818 DBusMessageIter iter, sub;
819 const char *state;
820 DBusError error;
821 int r;
822
823 assert(manager);
824 assert(unit);
825
826 dbus_error_init(&error);
827
828 path = unit_dbus_path_from_name(unit);
829 if (!path)
830 return -ENOMEM;
831
832 r = bus_method_call_with_reply(
833 manager->bus,
834 "org.freedesktop.systemd1",
835 path,
836 "org.freedesktop.DBus.Properties",
837 "Get",
838 &reply,
839 &error,
840 DBUS_TYPE_STRING, &interface,
841 DBUS_TYPE_STRING, &property,
842 DBUS_TYPE_INVALID);
1ee306e1 843 if (r < 0) {
6797c324
LP
844 if (dbus_error_has_name(&error, DBUS_ERROR_NO_REPLY) ||
845 dbus_error_has_name(&error, DBUS_ERROR_DISCONNECTED)) {
846 dbus_error_free(&error);
847 return true;
848 }
849
850 if (dbus_error_has_name(&error, BUS_ERROR_NO_SUCH_UNIT) ||
851 dbus_error_has_name(&error, BUS_ERROR_LOAD_FAILED)) {
852 dbus_error_free(&error);
853 return false;
854 }
855
1ee306e1
LP
856 log_error("Failed to query ActiveState: %s", bus_error(&error, r));
857 dbus_error_free(&error);
858 return r;
859 }
860
861 if (!dbus_message_iter_init(reply, &iter) ||
862 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
863 log_error("Failed to parse reply.");
864 return -EINVAL;
865 }
866
867 dbus_message_iter_recurse(&iter, &sub);
868 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING) {
869 log_error("Failed to parse reply.");
870 return -EINVAL;
871 }
872
873 dbus_message_iter_get_basic(&sub, &state);
874
875 return !streq(state, "inactive") && !streq(state, "failed");
876}