]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/machine/machined-dbus.c
machined: relax access to GetMachine()
[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
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
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
534
535 mm = hashmap_get(m->machine_units, unit);
536 if (mm) {
537 if (streq_ptr(path, mm->scope_job)) {
538 free(mm->scope_job);
539 mm->scope_job = NULL;
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 }
572 }
573
574finish:
575 dbus_error_free(&error);
576
577 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
578}
579
580int manager_start_scope(
581 Manager *manager,
582 const char *scope,
583 pid_t pid,
584 const char *slice,
585 const char *description,
586 DBusError *error,
587 char **job) {
588
589 _cleanup_dbus_message_unref_ DBusMessage *m = NULL, *reply = NULL;
590 DBusMessageIter iter, sub, sub2, sub3, sub4;
591 const char *timeout_stop_property = "TimeoutStopUSec";
592 const char *pids_property = "PIDs";
593 uint64_t timeout = 500 * USEC_PER_MSEC;
594 const char *fail = "fail";
595 uint32_t u;
596
597 assert(manager);
598 assert(scope);
599 assert(pid > 1);
600
601 if (!slice)
602 slice = "";
603
604 m = dbus_message_new_method_call(
605 "org.freedesktop.systemd1",
606 "/org/freedesktop/systemd1",
607 "org.freedesktop.systemd1.Manager",
608 "StartTransientUnit");
609 if (!m)
610 return log_oom();
611
612 dbus_message_iter_init_append(m, &iter);
613
614 if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &scope) ||
615 !dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &fail) ||
616 !dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(sv)", &sub))
617 return log_oom();
618
619 if (!isempty(slice)) {
620 const char *slice_property = "Slice";
621
622 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
623 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &slice_property) ||
624 !dbus_message_iter_open_container(&sub2, DBUS_TYPE_VARIANT, "s", &sub3) ||
625 !dbus_message_iter_append_basic(&sub3, DBUS_TYPE_STRING, &slice) ||
626 !dbus_message_iter_close_container(&sub2, &sub3) ||
627 !dbus_message_iter_close_container(&sub, &sub2))
628 return log_oom();
629 }
630
631 if (!isempty(description)) {
632 const char *description_property = "Description";
633
634 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
635 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &description_property) ||
636 !dbus_message_iter_open_container(&sub2, DBUS_TYPE_VARIANT, "s", &sub3) ||
637 !dbus_message_iter_append_basic(&sub3, DBUS_TYPE_STRING, &description) ||
638 !dbus_message_iter_close_container(&sub2, &sub3) ||
639 !dbus_message_iter_close_container(&sub, &sub2))
640 return log_oom();
641 }
642
643 /* cgroup empty notification is not available in containers
644 * currently. To make this less problematic, let's shorten the
645 * stop timeout for sessions, so that we don't wait
646 * forever. */
647
648 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
649 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &timeout_stop_property) ||
650 !dbus_message_iter_open_container(&sub2, DBUS_TYPE_VARIANT, "t", &sub3) ||
651 !dbus_message_iter_append_basic(&sub3, DBUS_TYPE_UINT64, &timeout) ||
652 !dbus_message_iter_close_container(&sub2, &sub3) ||
653 !dbus_message_iter_close_container(&sub, &sub2))
654 return log_oom();
655
656 u = pid;
657 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
658 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &pids_property) ||
659 !dbus_message_iter_open_container(&sub2, DBUS_TYPE_VARIANT, "au", &sub3) ||
660 !dbus_message_iter_open_container(&sub3, DBUS_TYPE_ARRAY, "u", &sub4) ||
661 !dbus_message_iter_append_basic(&sub4, DBUS_TYPE_UINT32, &u) ||
662 !dbus_message_iter_close_container(&sub3, &sub4) ||
663 !dbus_message_iter_close_container(&sub2, &sub3) ||
664 !dbus_message_iter_close_container(&sub, &sub2) ||
665 !dbus_message_iter_close_container(&iter, &sub))
666 return log_oom();
667
668 reply = dbus_connection_send_with_reply_and_block(manager->bus, m, -1, error);
669 if (!reply)
670 return -EIO;
671
672 if (job) {
673 const char *j;
674 char *copy;
675
676 if (!dbus_message_get_args(reply, error, DBUS_TYPE_OBJECT_PATH, &j, DBUS_TYPE_INVALID))
677 return -EIO;
678
679 copy = strdup(j);
680 if (!copy)
681 return -ENOMEM;
682
683 *job = copy;
684 }
685
686 return 0;
687}
688
689int manager_stop_unit(Manager *manager, const char *unit, DBusError *error, char **job) {
690 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
691 const char *fail = "fail";
692 int r;
693
694 assert(manager);
695 assert(unit);
696
697 r = bus_method_call_with_reply(
698 manager->bus,
699 "org.freedesktop.systemd1",
700 "/org/freedesktop/systemd1",
701 "org.freedesktop.systemd1.Manager",
702 "StopUnit",
703 &reply,
704 error,
705 DBUS_TYPE_STRING, &unit,
706 DBUS_TYPE_STRING, &fail,
707 DBUS_TYPE_INVALID);
708 if (r < 0) {
709 log_error("Failed to stop unit %s: %s", unit, bus_error(error, r));
710 return r;
711 }
712
713 if (job) {
714 const char *j;
715 char *copy;
716
717 if (!dbus_message_get_args(reply, error,
718 DBUS_TYPE_OBJECT_PATH, &j,
719 DBUS_TYPE_INVALID)) {
720 log_error("Failed to parse reply.");
721 return -EIO;
722 }
723
724 copy = strdup(j);
725 if (!copy)
726 return -ENOMEM;
727
728 *job = copy;
729 }
730
731 return 0;
732}
733
734int manager_kill_unit(Manager *manager, const char *unit, KillWho who, int signo, DBusError *error) {
735 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
736 const char *w;
737 int r;
738
739 assert(manager);
740 assert(unit);
741
742 w = who == KILL_LEADER ? "process" : "cgroup";
743 assert_cc(sizeof(signo) == sizeof(int32_t));
744
745 r = bus_method_call_with_reply(
746 manager->bus,
747 "org.freedesktop.systemd1",
748 "/org/freedesktop/systemd1",
749 "org.freedesktop.systemd1.Manager",
750 "KillUnit",
751 &reply,
752 error,
753 DBUS_TYPE_STRING, &unit,
754 DBUS_TYPE_STRING, &w,
755 DBUS_TYPE_INT32, &signo,
756 DBUS_TYPE_INVALID);
757 if (r < 0) {
758 log_error("Failed to stop unit %s: %s", unit, bus_error(error, r));
759 return r;
760 }
761
762 return 0;
763}
764
765int manager_unit_is_active(Manager *manager, const char *unit) {
766
767 const char *interface = "org.freedesktop.systemd1.Unit";
768 const char *property = "ActiveState";
769 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
770 _cleanup_free_ char *path = NULL;
771 DBusMessageIter iter, sub;
772 const char *state;
773 DBusError error;
774 int r;
775
776 assert(manager);
777 assert(unit);
778
779 dbus_error_init(&error);
780
781 path = unit_dbus_path_from_name(unit);
782 if (!path)
783 return -ENOMEM;
784
785 r = bus_method_call_with_reply(
786 manager->bus,
787 "org.freedesktop.systemd1",
788 path,
789 "org.freedesktop.DBus.Properties",
790 "Get",
791 &reply,
792 &error,
793 DBUS_TYPE_STRING, &interface,
794 DBUS_TYPE_STRING, &property,
795 DBUS_TYPE_INVALID);
796
797 if (r < 0) {
798 log_error("Failed to query ActiveState: %s", bus_error(&error, r));
799 dbus_error_free(&error);
800 return r;
801 }
802
803 if (!dbus_message_iter_init(reply, &iter) ||
804 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
805 log_error("Failed to parse reply.");
806 return -EINVAL;
807 }
808
809 dbus_message_iter_recurse(&iter, &sub);
810 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING) {
811 log_error("Failed to parse reply.");
812 return -EINVAL;
813 }
814
815 dbus_message_iter_get_basic(&sub, &state);
816
817 return !streq(state, "inactive") && !streq(state, "failed");
818}