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