]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/machine/machined-dbus.c
build-sys: add ./configure --enable-address-sanitizer
[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 DBusMessageIter iter, sub;
121 MachineClass c;
122 uint32_t leader;
123 sd_id128_t id;
124 Machine *m;
125 int n, r;
126 void *v;
127
128 assert(manager);
129 assert(message);
130
131 if (!dbus_message_iter_init(message, &iter) ||
132 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
133 return -EINVAL;
134
135 dbus_message_iter_get_basic(&iter, &name);
136
137 if (!valid_machine_name(name) ||
138 !dbus_message_iter_next(&iter) ||
139 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
140 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_BYTE)
141 return -EINVAL;
142
143 dbus_message_iter_recurse(&iter, &sub);
144 dbus_message_iter_get_fixed_array(&sub, &v, &n);
145
146 if (n == 0)
147 id = SD_ID128_NULL;
148 else if (n == 16)
149 memcpy(&id, v, n);
150 else
151 return -EINVAL;
152
153 if (!dbus_message_iter_next(&iter) ||
154 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
155 return -EINVAL;
156
157 dbus_message_iter_get_basic(&iter, &service);
158
159 if (!dbus_message_iter_next(&iter) ||
160 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
161 return -EINVAL;
162
163 dbus_message_iter_get_basic(&iter, &class);
164
165 if (isempty(class))
166 c = _MACHINE_CLASS_INVALID;
167 else {
168 c = machine_class_from_string(class);
169 if (c < 0)
170 return -EINVAL;
171 }
172
173 if (!dbus_message_iter_next(&iter) ||
174 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_UINT32)
175 return -EINVAL;
176
177 dbus_message_iter_get_basic(&iter, &leader);
178 if (!dbus_message_iter_next(&iter) ||
179 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
180 return -EINVAL;
181
1ee306e1
LP
182 dbus_message_iter_get_basic(&iter, &root_directory);
183
184 if (!(isempty(root_directory) || path_is_absolute(root_directory)))
185 return -EINVAL;
186
554604b3
LP
187 if (!dbus_message_iter_next(&iter) ||
188 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
189 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT)
190 return -EINVAL;
191
192 dbus_message_iter_recurse(&iter, &sub);
193
1ee306e1
LP
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
554604b3 227 r = machine_start(m, &sub);
1ee306e1
LP
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
1ee306e1
LP
540 mm = hashmap_get(m->machine_units, unit);
541 if (mm) {
542 if (streq_ptr(path, mm->scope_job)) {
543 free(mm->scope_job);
544 mm->scope_job = NULL;
545
546 if (mm->started) {
547 if (streq(result, "done"))
548 machine_send_create_reply(mm, NULL);
549 else {
550 dbus_set_error(&error, BUS_ERROR_JOB_FAILED, "Start job for unit %s failed with '%s'", unit, result);
551 machine_send_create_reply(mm, &error);
552 }
76e66585
LP
553 } else
554 machine_save(mm);
1ee306e1
LP
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
1ee306e1
LP
562 _cleanup_free_ char *unit = NULL;
563 const char *path;
564
565 path = dbus_message_get_path(message);
566 if (!path)
567 goto finish;
568
569 unit_name_from_dbus_path(path, &unit);
570 if (unit) {
571 Machine *mm;
572
573 mm = hashmap_get(m->machine_units, unit);
574 if (mm)
575 machine_add_to_gc_queue(mm);
576 }
943aca8e
LP
577
578 } else if (dbus_message_is_signal(message, "org.freedesktop.systemd1.Manager", "UnitRemoved")) {
579 const char *path, *unit;
580 Machine *mm;
581
582 if (!dbus_message_get_args(message, &error,
583 DBUS_TYPE_STRING, &unit,
584 DBUS_TYPE_OBJECT_PATH, &path,
585 DBUS_TYPE_INVALID)) {
586 log_error("Failed to parse UnitRemoved message: %s", bus_error_message(&error));
587 goto finish;
588 }
589
590 mm = hashmap_get(m->machine_units, unit);
6797c324 591 if (mm)
943aca8e 592 machine_add_to_gc_queue(mm);
6797c324
LP
593
594 } else if (dbus_message_is_signal(message, "org.freedesktop.systemd1.Manager", "Reloading")) {
595 dbus_bool_t b;
596
597 if (!dbus_message_get_args(message, &error,
598 DBUS_TYPE_BOOLEAN, &b,
599 DBUS_TYPE_INVALID)) {
600 log_error("Failed to parse Reloading message: %s", bus_error_message(&error));
601 goto finish;
602 }
603
604 /* systemd finished reloading, let's recheck all our machines */
605 if (!b) {
606 Machine *mm;
607 Iterator i;
608
609 log_debug("System manager has been reloaded, rechecking machines...");
610
611 HASHMAP_FOREACH(mm, m->machines, i)
612 machine_add_to_gc_queue(mm);
943aca8e 613 }
1ee306e1
LP
614 }
615
616finish:
617 dbus_error_free(&error);
618
619 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
620}
621
554604b3
LP
622static int copy_many_fields(DBusMessageIter *dest, DBusMessageIter *src);
623
624static int copy_one_field(DBusMessageIter *dest, DBusMessageIter *src) {
625 int type, r;
626
627 type = dbus_message_iter_get_arg_type(src);
628
629 switch (type) {
630
631 case DBUS_TYPE_STRUCT: {
632 DBusMessageIter dest_sub, src_sub;
633
634 dbus_message_iter_recurse(src, &src_sub);
635
636 if (!dbus_message_iter_open_container(dest, DBUS_TYPE_STRUCT, NULL, &dest_sub))
637 return log_oom();
638
639 r = copy_many_fields(&dest_sub, &src_sub);
640 if (r < 0)
641 return r;
642
643 if (!dbus_message_iter_close_container(dest, &dest_sub))
644 return log_oom();
645
646 return 0;
647 }
648
649 case DBUS_TYPE_ARRAY: {
650 DBusMessageIter dest_sub, src_sub;
651
652 dbus_message_iter_recurse(src, &src_sub);
653
654 if (!dbus_message_iter_open_container(dest, DBUS_TYPE_ARRAY, dbus_message_iter_get_signature(&src_sub), &dest_sub))
655 return log_oom();
656
657 r = copy_many_fields(&dest_sub, &src_sub);
658 if (r < 0)
659 return r;
660
661 if (!dbus_message_iter_close_container(dest, &dest_sub))
662 return log_oom();
663
664 return 0;
665 }
666
667 case DBUS_TYPE_VARIANT: {
668 DBusMessageIter dest_sub, src_sub;
669
670 dbus_message_iter_recurse(src, &src_sub);
671
672 if (!dbus_message_iter_open_container(dest, DBUS_TYPE_VARIANT, dbus_message_iter_get_signature(&src_sub), &dest_sub))
673 return log_oom();
674
675 r = copy_one_field(&dest_sub, &src_sub);
676 if (r < 0)
677 return r;
678
679 if (!dbus_message_iter_close_container(dest, &dest_sub))
680 return log_oom();
681
682 return 0;
683 }
684
685 case DBUS_TYPE_STRING:
686 case DBUS_TYPE_OBJECT_PATH:
687 case DBUS_TYPE_BYTE:
688 case DBUS_TYPE_BOOLEAN:
689 case DBUS_TYPE_UINT16:
690 case DBUS_TYPE_INT16:
691 case DBUS_TYPE_UINT32:
692 case DBUS_TYPE_INT32:
693 case DBUS_TYPE_UINT64:
694 case DBUS_TYPE_INT64:
695 case DBUS_TYPE_DOUBLE:
696 case DBUS_TYPE_SIGNATURE: {
697 const void *p;
698
699 dbus_message_iter_get_basic(src, &p);
700 dbus_message_iter_append_basic(dest, type, &p);
701 return 0;
702 }
703
704 default:
705 return -EINVAL;
706 }
707}
708
709static int copy_many_fields(DBusMessageIter *dest, DBusMessageIter *src) {
710 int r;
711
712 assert(dest);
713 assert(src);
714
715 while (dbus_message_iter_get_arg_type(src) != DBUS_TYPE_INVALID) {
716
717 r = copy_one_field(dest, src);
718 if (r < 0)
719 return r;
720
721 dbus_message_iter_next(src);
722 }
723
724 return 0;
725}
726
1ee306e1
LP
727int manager_start_scope(
728 Manager *manager,
729 const char *scope,
730 pid_t pid,
731 const char *slice,
732 const char *description,
554604b3 733 DBusMessageIter *more_properties,
1ee306e1
LP
734 DBusError *error,
735 char **job) {
736
737 _cleanup_dbus_message_unref_ DBusMessage *m = NULL, *reply = NULL;
738 DBusMessageIter iter, sub, sub2, sub3, sub4;
739 const char *timeout_stop_property = "TimeoutStopUSec";
740 const char *pids_property = "PIDs";
741 uint64_t timeout = 500 * USEC_PER_MSEC;
742 const char *fail = "fail";
743 uint32_t u;
554604b3 744 int r;
1ee306e1
LP
745
746 assert(manager);
747 assert(scope);
748 assert(pid > 1);
749
750 if (!slice)
751 slice = "";
752
753 m = dbus_message_new_method_call(
754 "org.freedesktop.systemd1",
755 "/org/freedesktop/systemd1",
756 "org.freedesktop.systemd1.Manager",
757 "StartTransientUnit");
758 if (!m)
759 return log_oom();
760
761 dbus_message_iter_init_append(m, &iter);
762
763 if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &scope) ||
764 !dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &fail) ||
765 !dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(sv)", &sub))
766 return log_oom();
767
768 if (!isempty(slice)) {
769 const char *slice_property = "Slice";
770
771 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
772 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &slice_property) ||
773 !dbus_message_iter_open_container(&sub2, DBUS_TYPE_VARIANT, "s", &sub3) ||
774 !dbus_message_iter_append_basic(&sub3, DBUS_TYPE_STRING, &slice) ||
775 !dbus_message_iter_close_container(&sub2, &sub3) ||
776 !dbus_message_iter_close_container(&sub, &sub2))
777 return log_oom();
778 }
779
780 if (!isempty(description)) {
781 const char *description_property = "Description";
782
783 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
784 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &description_property) ||
785 !dbus_message_iter_open_container(&sub2, DBUS_TYPE_VARIANT, "s", &sub3) ||
786 !dbus_message_iter_append_basic(&sub3, DBUS_TYPE_STRING, &description) ||
787 !dbus_message_iter_close_container(&sub2, &sub3) ||
788 !dbus_message_iter_close_container(&sub, &sub2))
789 return log_oom();
790 }
791
792 /* cgroup empty notification is not available in containers
793 * currently. To make this less problematic, let's shorten the
794 * stop timeout for sessions, so that we don't wait
795 * forever. */
796
797 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
798 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &timeout_stop_property) ||
799 !dbus_message_iter_open_container(&sub2, DBUS_TYPE_VARIANT, "t", &sub3) ||
800 !dbus_message_iter_append_basic(&sub3, DBUS_TYPE_UINT64, &timeout) ||
801 !dbus_message_iter_close_container(&sub2, &sub3) ||
802 !dbus_message_iter_close_container(&sub, &sub2))
803 return log_oom();
804
805 u = pid;
806 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
807 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &pids_property) ||
808 !dbus_message_iter_open_container(&sub2, DBUS_TYPE_VARIANT, "au", &sub3) ||
809 !dbus_message_iter_open_container(&sub3, DBUS_TYPE_ARRAY, "u", &sub4) ||
810 !dbus_message_iter_append_basic(&sub4, DBUS_TYPE_UINT32, &u) ||
811 !dbus_message_iter_close_container(&sub3, &sub4) ||
812 !dbus_message_iter_close_container(&sub2, &sub3) ||
554604b3
LP
813 !dbus_message_iter_close_container(&sub, &sub2))
814 return log_oom();
815
816 if (more_properties) {
817 r = copy_many_fields(&sub, more_properties);
818 if (r < 0)
819 return r;
820 }
821
822 if (!dbus_message_iter_close_container(&iter, &sub))
1ee306e1
LP
823 return log_oom();
824
825 reply = dbus_connection_send_with_reply_and_block(manager->bus, m, -1, error);
826 if (!reply)
827 return -EIO;
828
829 if (job) {
830 const char *j;
831 char *copy;
832
833 if (!dbus_message_get_args(reply, error, DBUS_TYPE_OBJECT_PATH, &j, DBUS_TYPE_INVALID))
834 return -EIO;
835
836 copy = strdup(j);
837 if (!copy)
838 return -ENOMEM;
839
840 *job = copy;
841 }
842
843 return 0;
844}
845
846int manager_stop_unit(Manager *manager, const char *unit, DBusError *error, char **job) {
847 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
848 const char *fail = "fail";
849 int r;
850
851 assert(manager);
852 assert(unit);
853
854 r = bus_method_call_with_reply(
855 manager->bus,
856 "org.freedesktop.systemd1",
857 "/org/freedesktop/systemd1",
858 "org.freedesktop.systemd1.Manager",
859 "StopUnit",
860 &reply,
861 error,
862 DBUS_TYPE_STRING, &unit,
863 DBUS_TYPE_STRING, &fail,
864 DBUS_TYPE_INVALID);
865 if (r < 0) {
6797c324
LP
866 if (dbus_error_has_name(error, BUS_ERROR_NO_SUCH_UNIT) ||
867 dbus_error_has_name(error, BUS_ERROR_LOAD_FAILED)) {
868
869 if (job)
870 *job = NULL;
871
872 dbus_error_free(error);
873 return 0;
874 }
875
1ee306e1
LP
876 log_error("Failed to stop unit %s: %s", unit, bus_error(error, r));
877 return r;
878 }
879
880 if (job) {
881 const char *j;
882 char *copy;
883
884 if (!dbus_message_get_args(reply, error,
885 DBUS_TYPE_OBJECT_PATH, &j,
886 DBUS_TYPE_INVALID)) {
887 log_error("Failed to parse reply.");
888 return -EIO;
889 }
890
891 copy = strdup(j);
892 if (!copy)
893 return -ENOMEM;
894
895 *job = copy;
896 }
897
6797c324 898 return 1;
1ee306e1
LP
899}
900
901int manager_kill_unit(Manager *manager, const char *unit, KillWho who, int signo, DBusError *error) {
902 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
903 const char *w;
904 int r;
905
906 assert(manager);
907 assert(unit);
908
909 w = who == KILL_LEADER ? "process" : "cgroup";
910 assert_cc(sizeof(signo) == sizeof(int32_t));
911
912 r = bus_method_call_with_reply(
913 manager->bus,
914 "org.freedesktop.systemd1",
915 "/org/freedesktop/systemd1",
916 "org.freedesktop.systemd1.Manager",
917 "KillUnit",
918 &reply,
919 error,
920 DBUS_TYPE_STRING, &unit,
921 DBUS_TYPE_STRING, &w,
922 DBUS_TYPE_INT32, &signo,
923 DBUS_TYPE_INVALID);
924 if (r < 0) {
925 log_error("Failed to stop unit %s: %s", unit, bus_error(error, r));
926 return r;
927 }
928
929 return 0;
930}
931
932int manager_unit_is_active(Manager *manager, const char *unit) {
933
934 const char *interface = "org.freedesktop.systemd1.Unit";
935 const char *property = "ActiveState";
936 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
937 _cleanup_free_ char *path = NULL;
938 DBusMessageIter iter, sub;
939 const char *state;
940 DBusError error;
941 int r;
942
943 assert(manager);
944 assert(unit);
945
946 dbus_error_init(&error);
947
948 path = unit_dbus_path_from_name(unit);
949 if (!path)
950 return -ENOMEM;
951
952 r = bus_method_call_with_reply(
953 manager->bus,
954 "org.freedesktop.systemd1",
955 path,
956 "org.freedesktop.DBus.Properties",
957 "Get",
958 &reply,
959 &error,
960 DBUS_TYPE_STRING, &interface,
961 DBUS_TYPE_STRING, &property,
962 DBUS_TYPE_INVALID);
1ee306e1 963 if (r < 0) {
6797c324
LP
964 if (dbus_error_has_name(&error, DBUS_ERROR_NO_REPLY) ||
965 dbus_error_has_name(&error, DBUS_ERROR_DISCONNECTED)) {
966 dbus_error_free(&error);
967 return true;
968 }
969
970 if (dbus_error_has_name(&error, BUS_ERROR_NO_SUCH_UNIT) ||
971 dbus_error_has_name(&error, BUS_ERROR_LOAD_FAILED)) {
972 dbus_error_free(&error);
973 return false;
974 }
975
1ee306e1
LP
976 log_error("Failed to query ActiveState: %s", bus_error(&error, r));
977 dbus_error_free(&error);
978 return r;
979 }
980
981 if (!dbus_message_iter_init(reply, &iter) ||
982 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
983 log_error("Failed to parse reply.");
984 return -EINVAL;
985 }
986
987 dbus_message_iter_recurse(&iter, &sub);
988 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING) {
989 log_error("Failed to parse reply.");
990 return -EINVAL;
991 }
992
993 dbus_message_iter_get_basic(&sub, &state);
994
995 return !streq(state, "inactive") && !streq(state, "failed");
996}