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