]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/machine/machined-dbus.c
machined: forward scope properties array from client to systemd
[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 (!dbus_message_iter_next(&iter) ||
189 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
190 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT)
191 return -EINVAL;
192
193 dbus_message_iter_recurse(&iter, &sub);
194
195 if (hashmap_get(manager->machines, name))
196 return -EEXIST;
197
198 if (leader <= 0) {
199 leader = bus_get_unix_process_id(manager->bus, dbus_message_get_sender(message), NULL);
200 if (leader == 0)
201 return -EINVAL;
202 }
203
204 r = manager_add_machine(manager, name, &m);
205 if (r < 0)
206 goto fail;
207
208 m->leader = leader;
209 m->class = c;
210 m->id = id;
211
212 if (!isempty(service)) {
213 m->service = strdup(service);
214 if (!m->service) {
215 r = -ENOMEM;
216 goto fail;
217 }
218 }
219
220 if (!isempty(root_directory)) {
221 m->root_directory = strdup(root_directory);
222 if (!m->root_directory) {
223 r = -ENOMEM;
224 goto fail;
225 }
226 }
227
228 r = machine_start(m, &sub);
229 if (r < 0)
230 goto fail;
231
232 m->create_message = dbus_message_ref(message);
233
234 return 0;
235
236 fail:
237 if (m)
238 machine_add_to_gc_queue(m);
239
240 return r;
241 }
242
243 static DBusHandlerResult manager_message_handler(
244 DBusConnection *connection,
245 DBusMessage *message,
246 void *userdata) {
247
248 Manager *m = userdata;
249
250 DBusError error;
251 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
252 int r;
253
254 assert(connection);
255 assert(message);
256 assert(m);
257
258 dbus_error_init(&error);
259
260 if (dbus_message_is_method_call(message, "org.freedesktop.machine1.Manager", "GetMachine")) {
261 Machine *machine;
262 const char *name;
263 char *p;
264 bool b;
265
266 if (!dbus_message_get_args(
267 message,
268 &error,
269 DBUS_TYPE_STRING, &name,
270 DBUS_TYPE_INVALID))
271 return bus_send_error_reply(connection, message, &error, -EINVAL);
272
273 machine = hashmap_get(m->machines, name);
274 if (!machine)
275 return bus_send_error_reply(connection, message, &error, -ENOENT);
276
277 reply = dbus_message_new_method_return(message);
278 if (!reply)
279 goto oom;
280
281 p = machine_bus_path(machine);
282 if (!p)
283 goto oom;
284
285 b = dbus_message_append_args(
286 reply,
287 DBUS_TYPE_OBJECT_PATH, &p,
288 DBUS_TYPE_INVALID);
289 free(p);
290
291 if (!b)
292 goto oom;
293
294 } else if (dbus_message_is_method_call(message, "org.freedesktop.machine1.Manager", "GetMachineByPID")) {
295 uint32_t pid;
296 char *p;
297 Machine *machine;
298 bool b;
299
300 if (!dbus_message_get_args(
301 message,
302 &error,
303 DBUS_TYPE_UINT32, &pid,
304 DBUS_TYPE_INVALID))
305 return bus_send_error_reply(connection, message, &error, -EINVAL);
306
307 r = manager_get_machine_by_pid(m, pid, &machine);
308 if (r <= 0)
309 return bus_send_error_reply(connection, message, NULL, r < 0 ? r : -ENOENT);
310
311 reply = dbus_message_new_method_return(message);
312 if (!reply)
313 goto oom;
314
315 p = machine_bus_path(machine);
316 if (!p)
317 goto oom;
318
319 b = dbus_message_append_args(
320 reply,
321 DBUS_TYPE_OBJECT_PATH, &p,
322 DBUS_TYPE_INVALID);
323 free(p);
324
325 if (!b)
326 goto oom;
327
328 } else if (dbus_message_is_method_call(message, "org.freedesktop.machine1.Manager", "ListMachines")) {
329 Machine *machine;
330 Iterator i;
331 DBusMessageIter iter, sub;
332
333 reply = dbus_message_new_method_return(message);
334 if (!reply)
335 goto oom;
336
337 dbus_message_iter_init_append(reply, &iter);
338
339 if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(ssso)", &sub))
340 goto oom;
341
342 HASHMAP_FOREACH(machine, m->machines, i) {
343 _cleanup_free_ char *p = NULL;
344 DBusMessageIter sub2;
345 const char *class;
346
347 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2))
348 goto oom;
349
350 p = machine_bus_path(machine);
351 if (!p)
352 goto oom;
353
354 class = strempty(machine_class_to_string(machine->class));
355
356 if (!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &machine->name) ||
357 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &class) ||
358 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &machine->service) ||
359 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &p)) {
360 free(p);
361 goto oom;
362 }
363
364 if (!dbus_message_iter_close_container(&sub, &sub2))
365 goto oom;
366 }
367
368 if (!dbus_message_iter_close_container(&iter, &sub))
369 goto oom;
370
371 } else if (dbus_message_is_method_call(message, "org.freedesktop.machine1.Manager", "CreateMachine")) {
372
373 r = bus_manager_create_machine(m, message);
374 if (r < 0)
375 return bus_send_error_reply(connection, message, NULL, r);
376
377 } else if (dbus_message_is_method_call(message, "org.freedesktop.machine1.Manager", "KillMachine")) {
378 const char *swho;
379 int32_t signo;
380 KillWho who;
381 const char *name;
382 Machine *machine;
383
384 if (!dbus_message_get_args(
385 message,
386 &error,
387 DBUS_TYPE_STRING, &name,
388 DBUS_TYPE_STRING, &swho,
389 DBUS_TYPE_INT32, &signo,
390 DBUS_TYPE_INVALID))
391 return bus_send_error_reply(connection, message, &error, -EINVAL);
392
393 if (isempty(swho))
394 who = KILL_ALL;
395 else {
396 who = kill_who_from_string(swho);
397 if (who < 0)
398 return bus_send_error_reply(connection, message, &error, -EINVAL);
399 }
400
401 if (signo <= 0 || signo >= _NSIG)
402 return bus_send_error_reply(connection, message, &error, -EINVAL);
403
404 machine = hashmap_get(m->machines, name);
405 if (!machine)
406 return bus_send_error_reply(connection, message, &error, -ENOENT);
407
408 r = machine_kill(machine, who, signo);
409 if (r < 0)
410 return bus_send_error_reply(connection, message, NULL, r);
411
412 reply = dbus_message_new_method_return(message);
413 if (!reply)
414 goto oom;
415
416 } else if (dbus_message_is_method_call(message, "org.freedesktop.machine1.Manager", "TerminateMachine")) {
417 const char *name;
418 Machine *machine;
419
420 if (!dbus_message_get_args(
421 message,
422 &error,
423 DBUS_TYPE_STRING, &name,
424 DBUS_TYPE_INVALID))
425 return bus_send_error_reply(connection, message, &error, -EINVAL);
426
427 machine = hashmap_get(m->machines, name);
428 if (!machine)
429 return bus_send_error_reply(connection, message, &error, -ENOENT);
430
431 r = machine_stop(machine);
432 if (r < 0)
433 return bus_send_error_reply(connection, message, NULL, r);
434
435 reply = dbus_message_new_method_return(message);
436 if (!reply)
437 goto oom;
438
439 } else if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect")) {
440 char *introspection = NULL;
441 FILE *f;
442 Iterator i;
443 Machine *machine;
444 size_t size;
445 char *p;
446
447 reply = dbus_message_new_method_return(message);
448 if (!reply)
449 goto oom;
450
451 /* We roll our own introspection code here, instead of
452 * relying on bus_default_message_handler() because we
453 * need to generate our introspection string
454 * dynamically. */
455
456 f = open_memstream(&introspection, &size);
457 if (!f)
458 goto oom;
459
460 fputs(INTROSPECTION_BEGIN, f);
461
462 HASHMAP_FOREACH(machine, m->machines, i) {
463 p = bus_path_escape(machine->name);
464
465 if (p) {
466 fprintf(f, "<node name=\"machine/%s\"/>", p);
467 free(p);
468 }
469 }
470
471 fputs(INTROSPECTION_END, f);
472
473 if (ferror(f)) {
474 fclose(f);
475 free(introspection);
476 goto oom;
477 }
478
479 fclose(f);
480
481 if (!introspection)
482 goto oom;
483
484 if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &introspection, DBUS_TYPE_INVALID)) {
485 free(introspection);
486 goto oom;
487 }
488
489 free(introspection);
490 } else
491 return bus_default_message_handler(connection, message, NULL, INTERFACES_LIST, NULL);
492
493 if (reply) {
494 if (!bus_maybe_send_reply(connection, message, reply))
495 goto oom;
496 }
497
498 return DBUS_HANDLER_RESULT_HANDLED;
499
500 oom:
501 dbus_error_free(&error);
502
503 return DBUS_HANDLER_RESULT_NEED_MEMORY;
504 }
505
506 const DBusObjectPathVTable bus_manager_vtable = {
507 .message_function = manager_message_handler
508 };
509
510 DBusHandlerResult bus_message_filter(
511 DBusConnection *connection,
512 DBusMessage *message,
513 void *userdata) {
514
515 Manager *m = userdata;
516 DBusError error;
517
518 assert(m);
519 assert(connection);
520 assert(message);
521
522 dbus_error_init(&error);
523
524 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)));
525
526 if (dbus_message_is_signal(message, "org.freedesktop.systemd1.Manager", "JobRemoved")) {
527 const char *path, *result, *unit;
528 Machine *mm;
529 uint32_t id;
530
531 if (!dbus_message_get_args(message, &error,
532 DBUS_TYPE_UINT32, &id,
533 DBUS_TYPE_OBJECT_PATH, &path,
534 DBUS_TYPE_STRING, &unit,
535 DBUS_TYPE_STRING, &result,
536 DBUS_TYPE_INVALID)) {
537 log_error("Failed to parse JobRemoved message: %s", bus_error_message(&error));
538 goto finish;
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 machine_save(mm);
547
548 if (mm->started) {
549 if (streq(result, "done"))
550 machine_send_create_reply(mm, NULL);
551 else {
552 dbus_set_error(&error, BUS_ERROR_JOB_FAILED, "Start job for unit %s failed with '%s'", unit, result);
553 machine_send_create_reply(mm, &error);
554 }
555 }
556 }
557
558 machine_add_to_gc_queue(mm);
559 }
560
561 } else if (dbus_message_is_signal(message, "org.freedesktop.DBus.Properties", "PropertiesChanged")) {
562
563 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
564 _cleanup_free_ char *unit = NULL;
565 const char *path;
566
567 path = dbus_message_get_path(message);
568 if (!path)
569 goto finish;
570
571 unit_name_from_dbus_path(path, &unit);
572 if (unit) {
573 Machine *mm;
574
575 mm = hashmap_get(m->machine_units, unit);
576 if (mm)
577 machine_add_to_gc_queue(mm);
578 }
579
580 } else if (dbus_message_is_signal(message, "org.freedesktop.systemd1.Manager", "UnitRemoved")) {
581 const char *path, *unit;
582 Machine *mm;
583
584 if (!dbus_message_get_args(message, &error,
585 DBUS_TYPE_STRING, &unit,
586 DBUS_TYPE_OBJECT_PATH, &path,
587 DBUS_TYPE_INVALID)) {
588 log_error("Failed to parse UnitRemoved message: %s", bus_error_message(&error));
589 goto finish;
590 }
591
592 mm = hashmap_get(m->machine_units, unit);
593 if (mm)
594 machine_add_to_gc_queue(mm);
595
596 } else if (dbus_message_is_signal(message, "org.freedesktop.systemd1.Manager", "Reloading")) {
597 dbus_bool_t b;
598
599 if (!dbus_message_get_args(message, &error,
600 DBUS_TYPE_BOOLEAN, &b,
601 DBUS_TYPE_INVALID)) {
602 log_error("Failed to parse Reloading message: %s", bus_error_message(&error));
603 goto finish;
604 }
605
606 /* systemd finished reloading, let's recheck all our machines */
607 if (!b) {
608 Machine *mm;
609 Iterator i;
610
611 log_debug("System manager has been reloaded, rechecking machines...");
612
613 HASHMAP_FOREACH(mm, m->machines, i)
614 machine_add_to_gc_queue(mm);
615 }
616 }
617
618 finish:
619 dbus_error_free(&error);
620
621 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
622 }
623
624 static int copy_many_fields(DBusMessageIter *dest, DBusMessageIter *src);
625
626 static int copy_one_field(DBusMessageIter *dest, DBusMessageIter *src) {
627 int type, r;
628
629 type = dbus_message_iter_get_arg_type(src);
630
631 switch (type) {
632
633 case DBUS_TYPE_STRUCT: {
634 DBusMessageIter dest_sub, src_sub;
635
636 dbus_message_iter_recurse(src, &src_sub);
637
638 if (!dbus_message_iter_open_container(dest, DBUS_TYPE_STRUCT, NULL, &dest_sub))
639 return log_oom();
640
641 r = copy_many_fields(&dest_sub, &src_sub);
642 if (r < 0)
643 return r;
644
645 if (!dbus_message_iter_close_container(dest, &dest_sub))
646 return log_oom();
647
648 return 0;
649 }
650
651 case DBUS_TYPE_ARRAY: {
652 DBusMessageIter dest_sub, src_sub;
653
654 dbus_message_iter_recurse(src, &src_sub);
655
656 if (!dbus_message_iter_open_container(dest, DBUS_TYPE_ARRAY, dbus_message_iter_get_signature(&src_sub), &dest_sub))
657 return log_oom();
658
659 r = copy_many_fields(&dest_sub, &src_sub);
660 if (r < 0)
661 return r;
662
663 if (!dbus_message_iter_close_container(dest, &dest_sub))
664 return log_oom();
665
666 return 0;
667 }
668
669 case DBUS_TYPE_VARIANT: {
670 DBusMessageIter dest_sub, src_sub;
671
672 dbus_message_iter_recurse(src, &src_sub);
673
674 if (!dbus_message_iter_open_container(dest, DBUS_TYPE_VARIANT, dbus_message_iter_get_signature(&src_sub), &dest_sub))
675 return log_oom();
676
677 r = copy_one_field(&dest_sub, &src_sub);
678 if (r < 0)
679 return r;
680
681 if (!dbus_message_iter_close_container(dest, &dest_sub))
682 return log_oom();
683
684 return 0;
685 }
686
687 case DBUS_TYPE_STRING:
688 case DBUS_TYPE_OBJECT_PATH:
689 case DBUS_TYPE_BYTE:
690 case DBUS_TYPE_BOOLEAN:
691 case DBUS_TYPE_UINT16:
692 case DBUS_TYPE_INT16:
693 case DBUS_TYPE_UINT32:
694 case DBUS_TYPE_INT32:
695 case DBUS_TYPE_UINT64:
696 case DBUS_TYPE_INT64:
697 case DBUS_TYPE_DOUBLE:
698 case DBUS_TYPE_SIGNATURE: {
699 const void *p;
700
701 dbus_message_iter_get_basic(src, &p);
702 dbus_message_iter_append_basic(dest, type, &p);
703 return 0;
704 }
705
706 default:
707 return -EINVAL;
708 }
709 }
710
711 static int copy_many_fields(DBusMessageIter *dest, DBusMessageIter *src) {
712 int r;
713
714 assert(dest);
715 assert(src);
716
717 while (dbus_message_iter_get_arg_type(src) != DBUS_TYPE_INVALID) {
718
719 r = copy_one_field(dest, src);
720 if (r < 0)
721 return r;
722
723 dbus_message_iter_next(src);
724 }
725
726 return 0;
727 }
728
729 int manager_start_scope(
730 Manager *manager,
731 const char *scope,
732 pid_t pid,
733 const char *slice,
734 const char *description,
735 DBusMessageIter *more_properties,
736 DBusError *error,
737 char **job) {
738
739 _cleanup_dbus_message_unref_ DBusMessage *m = NULL, *reply = NULL;
740 DBusMessageIter iter, sub, sub2, sub3, sub4;
741 const char *timeout_stop_property = "TimeoutStopUSec";
742 const char *pids_property = "PIDs";
743 uint64_t timeout = 500 * USEC_PER_MSEC;
744 const char *fail = "fail";
745 uint32_t u;
746 int r;
747
748 assert(manager);
749 assert(scope);
750 assert(pid > 1);
751
752 if (!slice)
753 slice = "";
754
755 m = dbus_message_new_method_call(
756 "org.freedesktop.systemd1",
757 "/org/freedesktop/systemd1",
758 "org.freedesktop.systemd1.Manager",
759 "StartTransientUnit");
760 if (!m)
761 return log_oom();
762
763 dbus_message_iter_init_append(m, &iter);
764
765 if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &scope) ||
766 !dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &fail) ||
767 !dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(sv)", &sub))
768 return log_oom();
769
770 if (!isempty(slice)) {
771 const char *slice_property = "Slice";
772
773 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
774 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &slice_property) ||
775 !dbus_message_iter_open_container(&sub2, DBUS_TYPE_VARIANT, "s", &sub3) ||
776 !dbus_message_iter_append_basic(&sub3, DBUS_TYPE_STRING, &slice) ||
777 !dbus_message_iter_close_container(&sub2, &sub3) ||
778 !dbus_message_iter_close_container(&sub, &sub2))
779 return log_oom();
780 }
781
782 if (!isempty(description)) {
783 const char *description_property = "Description";
784
785 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
786 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &description_property) ||
787 !dbus_message_iter_open_container(&sub2, DBUS_TYPE_VARIANT, "s", &sub3) ||
788 !dbus_message_iter_append_basic(&sub3, DBUS_TYPE_STRING, &description) ||
789 !dbus_message_iter_close_container(&sub2, &sub3) ||
790 !dbus_message_iter_close_container(&sub, &sub2))
791 return log_oom();
792 }
793
794 /* cgroup empty notification is not available in containers
795 * currently. To make this less problematic, let's shorten the
796 * stop timeout for sessions, so that we don't wait
797 * forever. */
798
799 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
800 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &timeout_stop_property) ||
801 !dbus_message_iter_open_container(&sub2, DBUS_TYPE_VARIANT, "t", &sub3) ||
802 !dbus_message_iter_append_basic(&sub3, DBUS_TYPE_UINT64, &timeout) ||
803 !dbus_message_iter_close_container(&sub2, &sub3) ||
804 !dbus_message_iter_close_container(&sub, &sub2))
805 return log_oom();
806
807 u = pid;
808 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
809 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &pids_property) ||
810 !dbus_message_iter_open_container(&sub2, DBUS_TYPE_VARIANT, "au", &sub3) ||
811 !dbus_message_iter_open_container(&sub3, DBUS_TYPE_ARRAY, "u", &sub4) ||
812 !dbus_message_iter_append_basic(&sub4, DBUS_TYPE_UINT32, &u) ||
813 !dbus_message_iter_close_container(&sub3, &sub4) ||
814 !dbus_message_iter_close_container(&sub2, &sub3) ||
815 !dbus_message_iter_close_container(&sub, &sub2))
816 return log_oom();
817
818 if (more_properties) {
819 r = copy_many_fields(&sub, more_properties);
820 if (r < 0)
821 return r;
822 }
823
824 if (!dbus_message_iter_close_container(&iter, &sub))
825 return log_oom();
826
827 reply = dbus_connection_send_with_reply_and_block(manager->bus, m, -1, error);
828 if (!reply)
829 return -EIO;
830
831 if (job) {
832 const char *j;
833 char *copy;
834
835 if (!dbus_message_get_args(reply, error, DBUS_TYPE_OBJECT_PATH, &j, DBUS_TYPE_INVALID))
836 return -EIO;
837
838 copy = strdup(j);
839 if (!copy)
840 return -ENOMEM;
841
842 *job = copy;
843 }
844
845 return 0;
846 }
847
848 int manager_stop_unit(Manager *manager, const char *unit, DBusError *error, char **job) {
849 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
850 const char *fail = "fail";
851 int r;
852
853 assert(manager);
854 assert(unit);
855
856 r = bus_method_call_with_reply(
857 manager->bus,
858 "org.freedesktop.systemd1",
859 "/org/freedesktop/systemd1",
860 "org.freedesktop.systemd1.Manager",
861 "StopUnit",
862 &reply,
863 error,
864 DBUS_TYPE_STRING, &unit,
865 DBUS_TYPE_STRING, &fail,
866 DBUS_TYPE_INVALID);
867 if (r < 0) {
868 if (dbus_error_has_name(error, BUS_ERROR_NO_SUCH_UNIT) ||
869 dbus_error_has_name(error, BUS_ERROR_LOAD_FAILED)) {
870
871 if (job)
872 *job = NULL;
873
874 dbus_error_free(error);
875 return 0;
876 }
877
878 log_error("Failed to stop unit %s: %s", unit, bus_error(error, r));
879 return r;
880 }
881
882 if (job) {
883 const char *j;
884 char *copy;
885
886 if (!dbus_message_get_args(reply, error,
887 DBUS_TYPE_OBJECT_PATH, &j,
888 DBUS_TYPE_INVALID)) {
889 log_error("Failed to parse reply.");
890 return -EIO;
891 }
892
893 copy = strdup(j);
894 if (!copy)
895 return -ENOMEM;
896
897 *job = copy;
898 }
899
900 return 1;
901 }
902
903 int manager_kill_unit(Manager *manager, const char *unit, KillWho who, int signo, DBusError *error) {
904 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
905 const char *w;
906 int r;
907
908 assert(manager);
909 assert(unit);
910
911 w = who == KILL_LEADER ? "process" : "cgroup";
912 assert_cc(sizeof(signo) == sizeof(int32_t));
913
914 r = bus_method_call_with_reply(
915 manager->bus,
916 "org.freedesktop.systemd1",
917 "/org/freedesktop/systemd1",
918 "org.freedesktop.systemd1.Manager",
919 "KillUnit",
920 &reply,
921 error,
922 DBUS_TYPE_STRING, &unit,
923 DBUS_TYPE_STRING, &w,
924 DBUS_TYPE_INT32, &signo,
925 DBUS_TYPE_INVALID);
926 if (r < 0) {
927 log_error("Failed to stop unit %s: %s", unit, bus_error(error, r));
928 return r;
929 }
930
931 return 0;
932 }
933
934 int manager_unit_is_active(Manager *manager, const char *unit) {
935
936 const char *interface = "org.freedesktop.systemd1.Unit";
937 const char *property = "ActiveState";
938 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
939 _cleanup_free_ char *path = NULL;
940 DBusMessageIter iter, sub;
941 const char *state;
942 DBusError error;
943 int r;
944
945 assert(manager);
946 assert(unit);
947
948 dbus_error_init(&error);
949
950 path = unit_dbus_path_from_name(unit);
951 if (!path)
952 return -ENOMEM;
953
954 r = bus_method_call_with_reply(
955 manager->bus,
956 "org.freedesktop.systemd1",
957 path,
958 "org.freedesktop.DBus.Properties",
959 "Get",
960 &reply,
961 &error,
962 DBUS_TYPE_STRING, &interface,
963 DBUS_TYPE_STRING, &property,
964 DBUS_TYPE_INVALID);
965 if (r < 0) {
966 if (dbus_error_has_name(&error, DBUS_ERROR_NO_REPLY) ||
967 dbus_error_has_name(&error, DBUS_ERROR_DISCONNECTED)) {
968 dbus_error_free(&error);
969 return true;
970 }
971
972 if (dbus_error_has_name(&error, BUS_ERROR_NO_SUCH_UNIT) ||
973 dbus_error_has_name(&error, BUS_ERROR_LOAD_FAILED)) {
974 dbus_error_free(&error);
975 return false;
976 }
977
978 log_error("Failed to query ActiveState: %s", bus_error(&error, r));
979 dbus_error_free(&error);
980 return r;
981 }
982
983 if (!dbus_message_iter_init(reply, &iter) ||
984 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
985 log_error("Failed to parse reply.");
986 return -EINVAL;
987 }
988
989 dbus_message_iter_recurse(&iter, &sub);
990 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING) {
991 log_error("Failed to parse reply.");
992 return -EINVAL;
993 }
994
995 dbus_message_iter_get_basic(&sub, &state);
996
997 return !streq(state, "inactive") && !streq(state, "failed");
998 }