]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/machine/machined-dbus.c
machined: split out machine registration stuff from logind
[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 \
45 " <interface name=\"org.freedesktop.machine1.Manager\">\n" \
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
119 const char *name, *service, *class, *slice, *root_directory;
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
183 dbus_message_iter_get_basic(&iter, &slice);
184 if (!(isempty(slice) || (unit_name_is_valid(slice, false) && endswith(slice, ".slice"))) ||
185 !dbus_message_iter_next(&iter) ||
186 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
187 return -EINVAL;
188
189 dbus_message_iter_get_basic(&iter, &root_directory);
190
191 if (!(isempty(root_directory) || path_is_absolute(root_directory)))
192 return -EINVAL;
193
194 if (hashmap_get(manager->machines, name))
195 return -EEXIST;
196
197 if (leader <= 0) {
198 leader = bus_get_unix_process_id(manager->bus, dbus_message_get_sender(message), NULL);
199 if (leader == 0)
200 return -EINVAL;
201 }
202
203 r = manager_add_machine(manager, name, &m);
204 if (r < 0)
205 goto fail;
206
207 m->leader = leader;
208 m->class = c;
209 m->id = id;
210
211 if (!isempty(service)) {
212 m->service = strdup(service);
213 if (!m->service) {
214 r = -ENOMEM;
215 goto fail;
216 }
217 }
218
219 if (!isempty(root_directory)) {
220 m->root_directory = strdup(root_directory);
221 if (!m->root_directory) {
222 r = -ENOMEM;
223 goto fail;
224 }
225 }
226
227 r = machine_start(m);
228 if (r < 0)
229 goto fail;
230
231 m->create_message = dbus_message_ref(message);
232
233 return 0;
234
235fail:
236 if (m)
237 machine_add_to_gc_queue(m);
238
239 return r;
240}
241
242static DBusHandlerResult manager_message_handler(
243 DBusConnection *connection,
244 DBusMessage *message,
245 void *userdata) {
246
247 Manager *m = userdata;
248
249 DBusError error;
250 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
251 int r;
252
253 assert(connection);
254 assert(message);
255 assert(m);
256
257 dbus_error_init(&error);
258
259 if (dbus_message_is_method_call(message, "org.freedesktop.machine1.Manager", "GetMachine")) {
260 Machine *machine;
261 const char *name;
262 char *p;
263 bool b;
264
265 if (!dbus_message_get_args(
266 message,
267 &error,
268 DBUS_TYPE_STRING, &name,
269 DBUS_TYPE_INVALID))
270 return bus_send_error_reply(connection, message, &error, -EINVAL);
271
272 machine = hashmap_get(m->machines, name);
273 if (!machine)
274 return bus_send_error_reply(connection, message, &error, -ENOENT);
275
276 reply = dbus_message_new_method_return(message);
277 if (!reply)
278 goto oom;
279
280 p = machine_bus_path(machine);
281 if (!p)
282 goto oom;
283
284 b = dbus_message_append_args(
285 reply,
286 DBUS_TYPE_OBJECT_PATH, &p,
287 DBUS_TYPE_INVALID);
288 free(p);
289
290 if (!b)
291 goto oom;
292
293 } else if (dbus_message_is_method_call(message, "org.freedesktop.machine1.Manager", "GetMachineByPID")) {
294 uint32_t pid;
295 char *p;
296 Machine *machine;
297 bool b;
298
299 if (!dbus_message_get_args(
300 message,
301 &error,
302 DBUS_TYPE_UINT32, &pid,
303 DBUS_TYPE_INVALID))
304 return bus_send_error_reply(connection, message, &error, -EINVAL);
305
306 r = manager_get_machine_by_pid(m, pid, &machine);
307 if (r <= 0)
308 return bus_send_error_reply(connection, message, NULL, r < 0 ? r : -ENOENT);
309
310 reply = dbus_message_new_method_return(message);
311 if (!reply)
312 goto oom;
313
314 p = machine_bus_path(machine);
315 if (!p)
316 goto oom;
317
318 b = dbus_message_append_args(
319 reply,
320 DBUS_TYPE_OBJECT_PATH, &p,
321 DBUS_TYPE_INVALID);
322 free(p);
323
324 if (!b)
325 goto oom;
326
327 } else if (dbus_message_is_method_call(message, "org.freedesktop.machine1.Manager", "ListMachines")) {
328 Machine *machine;
329 Iterator i;
330 DBusMessageIter iter, sub;
331
332 reply = dbus_message_new_method_return(message);
333 if (!reply)
334 goto oom;
335
336 dbus_message_iter_init_append(reply, &iter);
337
338 if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(ssso)", &sub))
339 goto oom;
340
341 HASHMAP_FOREACH(machine, m->machines, i) {
342 _cleanup_free_ char *p = NULL;
343 DBusMessageIter sub2;
344 const char *class;
345
346 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2))
347 goto oom;
348
349 p = machine_bus_path(machine);
350 if (!p)
351 goto oom;
352
353 class = strempty(machine_class_to_string(machine->class));
354
355 if (!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &machine->name) ||
356 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &class) ||
357 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &machine->service) ||
358 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &p)) {
359 free(p);
360 goto oom;
361 }
362
363 if (!dbus_message_iter_close_container(&sub, &sub2))
364 goto oom;
365 }
366
367 if (!dbus_message_iter_close_container(&iter, &sub))
368 goto oom;
369
370 } else if (dbus_message_is_method_call(message, "org.freedesktop.machine1.Manager", "CreateMachine")) {
371
372 r = bus_manager_create_machine(m, message);
373 if (r < 0)
374 return bus_send_error_reply(connection, message, NULL, r);
375
376 } else if (dbus_message_is_method_call(message, "org.freedesktop.machine1.Manager", "KillMachine")) {
377 const char *swho;
378 int32_t signo;
379 KillWho who;
380 const char *name;
381 Machine *machine;
382
383 if (!dbus_message_get_args(
384 message,
385 &error,
386 DBUS_TYPE_STRING, &name,
387 DBUS_TYPE_STRING, &swho,
388 DBUS_TYPE_INT32, &signo,
389 DBUS_TYPE_INVALID))
390 return bus_send_error_reply(connection, message, &error, -EINVAL);
391
392 if (isempty(swho))
393 who = KILL_ALL;
394 else {
395 who = kill_who_from_string(swho);
396 if (who < 0)
397 return bus_send_error_reply(connection, message, &error, -EINVAL);
398 }
399
400 if (signo <= 0 || signo >= _NSIG)
401 return bus_send_error_reply(connection, message, &error, -EINVAL);
402
403 machine = hashmap_get(m->machines, name);
404 if (!machine)
405 return bus_send_error_reply(connection, message, &error, -ENOENT);
406
407 r = machine_kill(machine, who, signo);
408 if (r < 0)
409 return bus_send_error_reply(connection, message, NULL, r);
410
411 reply = dbus_message_new_method_return(message);
412 if (!reply)
413 goto oom;
414
415 } else if (dbus_message_is_method_call(message, "org.freedesktop.machine1.Manager", "TerminateMachine")) {
416 const char *name;
417 Machine *machine;
418
419 if (!dbus_message_get_args(
420 message,
421 &error,
422 DBUS_TYPE_STRING, &name,
423 DBUS_TYPE_INVALID))
424 return bus_send_error_reply(connection, message, &error, -EINVAL);
425
426 machine = hashmap_get(m->machines, name);
427 if (!machine)
428 return bus_send_error_reply(connection, message, &error, -ENOENT);
429
430 r = machine_stop(machine);
431 if (r < 0)
432 return bus_send_error_reply(connection, message, NULL, r);
433
434 reply = dbus_message_new_method_return(message);
435 if (!reply)
436 goto oom;
437
438 } else if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect")) {
439 char *introspection = NULL;
440 FILE *f;
441 Iterator i;
442 Machine *machine;
443 size_t size;
444 char *p;
445
446 reply = dbus_message_new_method_return(message);
447 if (!reply)
448 goto oom;
449
450 /* We roll our own introspection code here, instead of
451 * relying on bus_default_message_handler() because we
452 * need to generate our introspection string
453 * dynamically. */
454
455 f = open_memstream(&introspection, &size);
456 if (!f)
457 goto oom;
458
459 fputs(INTROSPECTION_BEGIN, f);
460
461 HASHMAP_FOREACH(machine, m->machines, i) {
462 p = bus_path_escape(machine->name);
463
464 if (p) {
465 fprintf(f, "<node name=\"machine/%s\"/>", p);
466 free(p);
467 }
468 }
469
470 fputs(INTROSPECTION_END, f);
471
472 if (ferror(f)) {
473 fclose(f);
474 free(introspection);
475 goto oom;
476 }
477
478 fclose(f);
479
480 if (!introspection)
481 goto oom;
482
483 if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &introspection, DBUS_TYPE_INVALID)) {
484 free(introspection);
485 goto oom;
486 }
487
488 free(introspection);
489 } else
490 return bus_default_message_handler(connection, message, NULL, INTERFACES_LIST, NULL);
491
492 if (reply) {
493 if (!bus_maybe_send_reply(connection, message, reply))
494 goto oom;
495 }
496
497 return DBUS_HANDLER_RESULT_HANDLED;
498
499oom:
500 dbus_error_free(&error);
501
502 return DBUS_HANDLER_RESULT_NEED_MEMORY;
503}
504
505const DBusObjectPathVTable bus_manager_vtable = {
506 .message_function = manager_message_handler
507};
508
509DBusHandlerResult bus_message_filter(
510 DBusConnection *connection,
511 DBusMessage *message,
512 void *userdata) {
513
514 Manager *m = userdata;
515 DBusError error;
516
517 assert(m);
518 assert(connection);
519 assert(message);
520
521 dbus_error_init(&error);
522
523 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)));
524
525 if (dbus_message_is_signal(message, "org.freedesktop.systemd1.Manager", "JobRemoved")) {
526 const char *path, *result, *unit;
527 Machine *mm;
528 uint32_t id;
529
530 if (!dbus_message_get_args(message, &error,
531 DBUS_TYPE_UINT32, &id,
532 DBUS_TYPE_OBJECT_PATH, &path,
533 DBUS_TYPE_STRING, &unit,
534 DBUS_TYPE_STRING, &result,
535 DBUS_TYPE_INVALID)) {
536 log_error("Failed to parse JobRemoved message: %s", bus_error_message(&error));
537 goto finish;
538 }
539
540
541 mm = hashmap_get(m->machine_units, unit);
542 if (mm) {
543 if (streq_ptr(path, mm->scope_job)) {
544 free(mm->scope_job);
545 mm->scope_job = NULL;
546
547 if (mm->started) {
548 if (streq(result, "done"))
549 machine_send_create_reply(mm, NULL);
550 else {
551 dbus_set_error(&error, BUS_ERROR_JOB_FAILED, "Start job for unit %s failed with '%s'", unit, result);
552 machine_send_create_reply(mm, &error);
553 }
554 }
555 }
556
557 machine_add_to_gc_queue(mm);
558 }
559
560 } else if (dbus_message_is_signal(message, "org.freedesktop.DBus.Properties", "PropertiesChanged")) {
561
562 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
563 _cleanup_free_ char *unit = NULL;
564 const char *path;
565
566 path = dbus_message_get_path(message);
567 if (!path)
568 goto finish;
569
570 unit_name_from_dbus_path(path, &unit);
571 if (unit) {
572 Machine *mm;
573
574 mm = hashmap_get(m->machine_units, unit);
575 if (mm)
576 machine_add_to_gc_queue(mm);
577 }
578 }
579
580finish:
581 dbus_error_free(&error);
582
583 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
584}
585
586int manager_start_scope(
587 Manager *manager,
588 const char *scope,
589 pid_t pid,
590 const char *slice,
591 const char *description,
592 DBusError *error,
593 char **job) {
594
595 _cleanup_dbus_message_unref_ DBusMessage *m = NULL, *reply = NULL;
596 DBusMessageIter iter, sub, sub2, sub3, sub4;
597 const char *timeout_stop_property = "TimeoutStopUSec";
598 const char *pids_property = "PIDs";
599 uint64_t timeout = 500 * USEC_PER_MSEC;
600 const char *fail = "fail";
601 uint32_t u;
602
603 assert(manager);
604 assert(scope);
605 assert(pid > 1);
606
607 if (!slice)
608 slice = "";
609
610 m = dbus_message_new_method_call(
611 "org.freedesktop.systemd1",
612 "/org/freedesktop/systemd1",
613 "org.freedesktop.systemd1.Manager",
614 "StartTransientUnit");
615 if (!m)
616 return log_oom();
617
618 dbus_message_iter_init_append(m, &iter);
619
620 if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &scope) ||
621 !dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &fail) ||
622 !dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(sv)", &sub))
623 return log_oom();
624
625 if (!isempty(slice)) {
626 const char *slice_property = "Slice";
627
628 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
629 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &slice_property) ||
630 !dbus_message_iter_open_container(&sub2, DBUS_TYPE_VARIANT, "s", &sub3) ||
631 !dbus_message_iter_append_basic(&sub3, DBUS_TYPE_STRING, &slice) ||
632 !dbus_message_iter_close_container(&sub2, &sub3) ||
633 !dbus_message_iter_close_container(&sub, &sub2))
634 return log_oom();
635 }
636
637 if (!isempty(description)) {
638 const char *description_property = "Description";
639
640 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
641 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &description_property) ||
642 !dbus_message_iter_open_container(&sub2, DBUS_TYPE_VARIANT, "s", &sub3) ||
643 !dbus_message_iter_append_basic(&sub3, DBUS_TYPE_STRING, &description) ||
644 !dbus_message_iter_close_container(&sub2, &sub3) ||
645 !dbus_message_iter_close_container(&sub, &sub2))
646 return log_oom();
647 }
648
649 /* cgroup empty notification is not available in containers
650 * currently. To make this less problematic, let's shorten the
651 * stop timeout for sessions, so that we don't wait
652 * forever. */
653
654 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
655 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &timeout_stop_property) ||
656 !dbus_message_iter_open_container(&sub2, DBUS_TYPE_VARIANT, "t", &sub3) ||
657 !dbus_message_iter_append_basic(&sub3, DBUS_TYPE_UINT64, &timeout) ||
658 !dbus_message_iter_close_container(&sub2, &sub3) ||
659 !dbus_message_iter_close_container(&sub, &sub2))
660 return log_oom();
661
662 u = pid;
663 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
664 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &pids_property) ||
665 !dbus_message_iter_open_container(&sub2, DBUS_TYPE_VARIANT, "au", &sub3) ||
666 !dbus_message_iter_open_container(&sub3, DBUS_TYPE_ARRAY, "u", &sub4) ||
667 !dbus_message_iter_append_basic(&sub4, DBUS_TYPE_UINT32, &u) ||
668 !dbus_message_iter_close_container(&sub3, &sub4) ||
669 !dbus_message_iter_close_container(&sub2, &sub3) ||
670 !dbus_message_iter_close_container(&sub, &sub2) ||
671 !dbus_message_iter_close_container(&iter, &sub))
672 return log_oom();
673
674 reply = dbus_connection_send_with_reply_and_block(manager->bus, m, -1, error);
675 if (!reply)
676 return -EIO;
677
678 if (job) {
679 const char *j;
680 char *copy;
681
682 if (!dbus_message_get_args(reply, error, DBUS_TYPE_OBJECT_PATH, &j, DBUS_TYPE_INVALID))
683 return -EIO;
684
685 copy = strdup(j);
686 if (!copy)
687 return -ENOMEM;
688
689 *job = copy;
690 }
691
692 return 0;
693}
694
695int manager_stop_unit(Manager *manager, const char *unit, DBusError *error, char **job) {
696 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
697 const char *fail = "fail";
698 int r;
699
700 assert(manager);
701 assert(unit);
702
703 r = bus_method_call_with_reply(
704 manager->bus,
705 "org.freedesktop.systemd1",
706 "/org/freedesktop/systemd1",
707 "org.freedesktop.systemd1.Manager",
708 "StopUnit",
709 &reply,
710 error,
711 DBUS_TYPE_STRING, &unit,
712 DBUS_TYPE_STRING, &fail,
713 DBUS_TYPE_INVALID);
714 if (r < 0) {
715 log_error("Failed to stop unit %s: %s", unit, bus_error(error, r));
716 return r;
717 }
718
719 if (job) {
720 const char *j;
721 char *copy;
722
723 if (!dbus_message_get_args(reply, error,
724 DBUS_TYPE_OBJECT_PATH, &j,
725 DBUS_TYPE_INVALID)) {
726 log_error("Failed to parse reply.");
727 return -EIO;
728 }
729
730 copy = strdup(j);
731 if (!copy)
732 return -ENOMEM;
733
734 *job = copy;
735 }
736
737 return 0;
738}
739
740int manager_kill_unit(Manager *manager, const char *unit, KillWho who, int signo, DBusError *error) {
741 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
742 const char *w;
743 int r;
744
745 assert(manager);
746 assert(unit);
747
748 w = who == KILL_LEADER ? "process" : "cgroup";
749 assert_cc(sizeof(signo) == sizeof(int32_t));
750
751 r = bus_method_call_with_reply(
752 manager->bus,
753 "org.freedesktop.systemd1",
754 "/org/freedesktop/systemd1",
755 "org.freedesktop.systemd1.Manager",
756 "KillUnit",
757 &reply,
758 error,
759 DBUS_TYPE_STRING, &unit,
760 DBUS_TYPE_STRING, &w,
761 DBUS_TYPE_INT32, &signo,
762 DBUS_TYPE_INVALID);
763 if (r < 0) {
764 log_error("Failed to stop unit %s: %s", unit, bus_error(error, r));
765 return r;
766 }
767
768 return 0;
769}
770
771int manager_unit_is_active(Manager *manager, const char *unit) {
772
773 const char *interface = "org.freedesktop.systemd1.Unit";
774 const char *property = "ActiveState";
775 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
776 _cleanup_free_ char *path = NULL;
777 DBusMessageIter iter, sub;
778 const char *state;
779 DBusError error;
780 int r;
781
782 assert(manager);
783 assert(unit);
784
785 dbus_error_init(&error);
786
787 path = unit_dbus_path_from_name(unit);
788 if (!path)
789 return -ENOMEM;
790
791 r = bus_method_call_with_reply(
792 manager->bus,
793 "org.freedesktop.systemd1",
794 path,
795 "org.freedesktop.DBus.Properties",
796 "Get",
797 &reply,
798 &error,
799 DBUS_TYPE_STRING, &interface,
800 DBUS_TYPE_STRING, &property,
801 DBUS_TYPE_INVALID);
802
803 if (r < 0) {
804 log_error("Failed to query ActiveState: %s", bus_error(&error, r));
805 dbus_error_free(&error);
806 return r;
807 }
808
809 if (!dbus_message_iter_init(reply, &iter) ||
810 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
811 log_error("Failed to parse reply.");
812 return -EINVAL;
813 }
814
815 dbus_message_iter_recurse(&iter, &sub);
816 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING) {
817 log_error("Failed to parse reply.");
818 return -EINVAL;
819 }
820
821 dbus_message_iter_get_basic(&sub, &state);
822
823 return !streq(state, "inactive") && !streq(state, "failed");
824}