]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/machine/machined-dbus.c
man: /usr/bin may contain binaries in any compatible arch, not just the primary one
[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 #include <sys/capability.h>
27
28 #include "sd-id128.h"
29 #include "sd-messages.h"
30 #include "strv.h"
31 #include "mkdir.h"
32 #include "path-util.h"
33 #include "special.h"
34 #include "fileio-label.h"
35 #include "label.h"
36 #include "utf8.h"
37 #include "unit-name.h"
38 #include "bus-util.h"
39 #include "bus-errors.h"
40 #include "time-util.h"
41 #include "cgroup-util.h"
42 #include "machined.h"
43
44 static bool valid_machine_name(const char *p) {
45 size_t l;
46
47 if (!filename_is_safe(p))
48 return false;
49
50 if (!ascii_is_valid(p))
51 return false;
52
53 l = strlen(p);
54
55 if (l < 1 || l> 64)
56 return false;
57
58 return true;
59 }
60
61 static int method_get_machine(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
62 _cleanup_free_ char *p = NULL;
63 Manager *m = userdata;
64 Machine *machine;
65 const char *name;
66 int r;
67
68 assert(bus);
69 assert(message);
70 assert(m);
71
72 r = sd_bus_message_read(message, "s", &name);
73 if (r < 0)
74 return r;
75
76 machine = hashmap_get(m->machines, name);
77 if (!machine)
78 return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name);
79
80 p = machine_bus_path(machine);
81 if (!p)
82 return -ENOMEM;
83
84 return sd_bus_reply_method_return(message, "o", p);
85 }
86
87 static int method_get_machine_by_pid(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
88 _cleanup_free_ char *p = NULL;
89 Manager *m = userdata;
90 Machine *machine = NULL;
91 pid_t pid;
92 int r;
93
94 assert(bus);
95 assert(message);
96 assert(m);
97
98 assert_cc(sizeof(pid_t) == sizeof(uint32_t));
99
100 r = sd_bus_message_read(message, "u", &pid);
101 if (r < 0)
102 return r;
103
104 if (pid == 0) {
105 _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
106
107 r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_PID, &creds);
108 if (r < 0)
109 return r;
110
111 r = sd_bus_creds_get_pid(creds, &pid);
112 if (r < 0)
113 return r;
114 }
115
116 r = manager_get_machine_by_pid(m, pid, &machine);
117 if (r < 0)
118 return r;
119 if (!machine)
120 return sd_bus_error_setf(error, BUS_ERROR_NO_MACHINE_FOR_PID, "PID "PID_FMT" does not belong to any known machine", pid);
121
122 p = machine_bus_path(machine);
123 if (!p)
124 return -ENOMEM;
125
126 return sd_bus_reply_method_return(message, "o", p);
127 }
128
129 static int method_list_machines(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
130 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
131 Manager *m = userdata;
132 Machine *machine;
133 Iterator i;
134 int r;
135
136 assert(bus);
137 assert(message);
138 assert(m);
139
140 r = sd_bus_message_new_method_return(message, &reply);
141 if (r < 0)
142 return sd_bus_error_set_errno(error, r);
143
144 r = sd_bus_message_open_container(reply, 'a', "(ssso)");
145 if (r < 0)
146 return sd_bus_error_set_errno(error, r);
147
148 HASHMAP_FOREACH(machine, m->machines, i) {
149 _cleanup_free_ char *p = NULL;
150
151 p = machine_bus_path(machine);
152 if (!p)
153 return -ENOMEM;
154
155 r = sd_bus_message_append(reply, "(ssso)",
156 machine->name,
157 strempty(machine_class_to_string(machine->class)),
158 machine->service,
159 p);
160 if (r < 0)
161 return sd_bus_error_set_errno(error, r);
162 }
163
164 r = sd_bus_message_close_container(reply);
165 if (r < 0)
166 return sd_bus_error_set_errno(error, r);
167
168 return sd_bus_send(bus, reply, NULL);
169 }
170
171 static int method_create_or_register_machine(Manager *manager, sd_bus_message *message, Machine **_m, sd_bus_error *error) {
172 const char *name, *service, *class, *root_directory;
173 MachineClass c;
174 uint32_t leader;
175 sd_id128_t id;
176 const void *v;
177 Machine *m;
178 size_t n;
179 int r;
180
181 assert(manager);
182 assert(message);
183 assert(_m);
184
185 r = sd_bus_message_read(message, "s", &name);
186 if (r < 0)
187 return r;
188 if (!valid_machine_name(name))
189 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid machine name");
190
191 r = sd_bus_message_read_array(message, 'y', &v, &n);
192 if (r < 0)
193 return r;
194 if (n == 0)
195 id = SD_ID128_NULL;
196 else if (n == 16)
197 memcpy(&id, v, n);
198 else
199 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid machine ID parameter");
200
201 r = sd_bus_message_read(message, "ssus", &service, &class, &leader, &root_directory);
202 if (r < 0)
203 return r;
204
205 if (isempty(class))
206 c = _MACHINE_CLASS_INVALID;
207 else {
208 c = machine_class_from_string(class);
209 if (c < 0)
210 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid machine class parameter");
211 }
212
213 if (leader == 1)
214 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid leader PID");
215
216 if (!isempty(root_directory) && !path_is_absolute(root_directory))
217 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Root directory must be empty or an absolute path");
218
219 if (leader == 0) {
220 _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
221
222 r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_PID, &creds);
223 if (r < 0)
224 return r;
225
226 assert_cc(sizeof(uint32_t) == sizeof(pid_t));
227
228 r = sd_bus_creds_get_pid(creds, (pid_t*) &leader);
229 if (r < 0)
230 return r;
231 }
232
233 if (hashmap_get(manager->machines, name))
234 return sd_bus_error_setf(error, BUS_ERROR_MACHINE_EXISTS, "Machine '%s' already exists", name);
235
236 r = manager_add_machine(manager, name, &m);
237 if (r < 0)
238 return r;
239
240 m->leader = leader;
241 m->class = c;
242 m->id = id;
243
244 if (!isempty(service)) {
245 m->service = strdup(service);
246 if (!m->service) {
247 r = -ENOMEM;
248 goto fail;
249 }
250 }
251
252 if (!isempty(root_directory)) {
253 m->root_directory = strdup(root_directory);
254 if (!m->root_directory) {
255 r = -ENOMEM;
256 goto fail;
257 }
258 }
259
260 *_m = m;
261
262 return 1;
263
264 fail:
265 machine_add_to_gc_queue(m);
266 return r;
267 }
268
269 static int method_create_machine(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
270 Manager *manager = userdata;
271 Machine *m = NULL;
272 int r;
273
274 r = method_create_or_register_machine(manager, message, &m, error);
275 if (r < 0)
276 return r;
277
278 r = sd_bus_message_enter_container(message, 'a', "(sv)");
279 if (r < 0)
280 goto fail;
281
282 r = machine_start(m, message, error);
283 if (r < 0)
284 goto fail;
285
286 m->create_message = sd_bus_message_ref(message);
287 return 1;
288
289 fail:
290 machine_add_to_gc_queue(m);
291 return r;
292 }
293
294 static int method_register_machine(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
295 Manager *manager = userdata;
296 _cleanup_free_ char *p = NULL;
297 Machine *m = NULL;
298 int r;
299
300 r = method_create_or_register_machine(manager, message, &m, error);
301 if (r < 0)
302 return r;
303
304 r = cg_pid_get_unit(m->leader, &m->unit);
305 if (r < 0) {
306 r = sd_bus_error_set_errnof(error, r, "Failed to determine unit of process "PID_FMT" : %s", m->leader, strerror(-r));
307 goto fail;
308 }
309
310 r = machine_start(m, NULL, error);
311 if (r < 0)
312 goto fail;
313
314 p = machine_bus_path(m);
315 if (!p) {
316 r = -ENOMEM;
317 goto fail;
318 }
319
320 return sd_bus_reply_method_return(message, "o", p);
321
322 fail:
323 machine_add_to_gc_queue(m);
324 return r;
325 }
326
327 static int method_terminate_machine(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
328 Manager *m = userdata;
329 Machine *machine;
330 const char *name;
331 int r;
332
333 assert(bus);
334 assert(message);
335 assert(m);
336
337 r = sd_bus_message_read(message, "s", &name);
338 if (r < 0)
339 return sd_bus_error_set_errno(error, r);
340
341 machine = hashmap_get(m->machines, name);
342 if (!machine)
343 return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name);
344
345 return bus_machine_method_terminate(bus, message, machine, error);
346 }
347
348 static int method_kill_machine(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
349 Manager *m = userdata;
350 Machine *machine;
351 const char *name;
352 int r;
353
354 assert(bus);
355 assert(message);
356 assert(m);
357
358 r = sd_bus_message_read(message, "s", &name);
359 if (r < 0)
360 return sd_bus_error_set_errno(error, r);
361
362 machine = hashmap_get(m->machines, name);
363 if (!machine)
364 return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name);
365
366 return bus_machine_method_kill(bus, message, machine, error);
367 }
368
369 static int method_get_machine_addresses(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
370 Manager *m = userdata;
371 Machine *machine;
372 const char *name;
373 int r;
374
375 assert(bus);
376 assert(message);
377 assert(m);
378
379 r = sd_bus_message_read(message, "s", &name);
380 if (r < 0)
381 return sd_bus_error_set_errno(error, r);
382
383 machine = hashmap_get(m->machines, name);
384 if (!machine)
385 return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name);
386
387 return bus_machine_method_get_addresses(bus, message, machine, error);
388 }
389
390 const sd_bus_vtable manager_vtable[] = {
391 SD_BUS_VTABLE_START(0),
392 SD_BUS_METHOD("GetMachine", "s", "o", method_get_machine, SD_BUS_VTABLE_UNPRIVILEGED),
393 SD_BUS_METHOD("GetMachineByPID", "u", "o", method_get_machine_by_pid, SD_BUS_VTABLE_UNPRIVILEGED),
394 SD_BUS_METHOD("ListMachines", NULL, "a(ssso)", method_list_machines, SD_BUS_VTABLE_UNPRIVILEGED),
395 SD_BUS_METHOD("CreateMachine", "sayssusa(sv)", "o", method_create_machine, 0),
396 SD_BUS_METHOD("RegisterMachine", "sayssus", "o", method_register_machine, 0),
397 SD_BUS_METHOD("KillMachine", "ssi", NULL, method_kill_machine, SD_BUS_VTABLE_CAPABILITY(CAP_KILL)),
398 SD_BUS_METHOD("TerminateMachine", "s", NULL, method_terminate_machine, SD_BUS_VTABLE_CAPABILITY(CAP_KILL)),
399 SD_BUS_METHOD("GetMachineAddresses", "s", "a(yay)", method_get_machine_addresses, SD_BUS_VTABLE_UNPRIVILEGED),
400 SD_BUS_SIGNAL("MachineNew", "so", 0),
401 SD_BUS_SIGNAL("MachineRemoved", "so", 0),
402 SD_BUS_VTABLE_END
403 };
404
405 int match_job_removed(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
406 const char *path, *result, *unit;
407 Manager *m = userdata;
408 Machine *machine;
409 uint32_t id;
410 int r;
411
412 assert(bus);
413 assert(message);
414 assert(m);
415
416 r = sd_bus_message_read(message, "uoss", &id, &path, &unit, &result);
417 if (r < 0) {
418 bus_log_parse_error(r);
419 return r;
420 }
421
422 machine = hashmap_get(m->machine_units, unit);
423 if (!machine)
424 return 0;
425
426 if (streq_ptr(path, machine->scope_job)) {
427 free(machine->scope_job);
428 machine->scope_job = NULL;
429
430 if (machine->started) {
431 if (streq(result, "done"))
432 machine_send_create_reply(machine, NULL);
433 else {
434 _cleanup_bus_error_free_ sd_bus_error e = SD_BUS_ERROR_NULL;
435
436 sd_bus_error_setf(&e, BUS_ERROR_JOB_FAILED, "Start job for unit %s failed with '%s'", unit, result);
437
438 machine_send_create_reply(machine, &e);
439 }
440 } else
441 machine_save(machine);
442 }
443
444 machine_add_to_gc_queue(machine);
445 return 0;
446 }
447
448 int match_properties_changed(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
449 _cleanup_free_ char *unit = NULL;
450 Manager *m = userdata;
451 Machine *machine;
452 const char *path;
453 int r;
454
455 assert(bus);
456 assert(message);
457 assert(m);
458
459 path = sd_bus_message_get_path(message);
460 if (!path)
461 return 0;
462
463 r = unit_name_from_dbus_path(path, &unit);
464 if (r < 0)
465 return r;
466
467 machine = hashmap_get(m->machine_units, unit);
468 if (machine)
469 machine_add_to_gc_queue(machine);
470
471 return 0;
472 }
473
474 int match_unit_removed(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
475 const char *path, *unit;
476 Manager *m = userdata;
477 Machine *machine;
478 int r;
479
480 assert(bus);
481 assert(message);
482 assert(m);
483
484 r = sd_bus_message_read(message, "so", &unit, &path);
485 if (r < 0) {
486 bus_log_parse_error(r);
487 return r;
488 }
489
490 machine = hashmap_get(m->machine_units, unit);
491 if (machine)
492 machine_add_to_gc_queue(machine);
493
494 return 0;
495 }
496
497 int match_reloading(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
498 Manager *m = userdata;
499 Machine *machine;
500 Iterator i;
501 int b, r;
502
503 assert(bus);
504
505 r = sd_bus_message_read(message, "b", &b);
506 if (r < 0) {
507 bus_log_parse_error(r);
508 return r;
509 }
510 if (b)
511 return 0;
512
513 /* systemd finished reloading, let's recheck all our machines */
514 log_debug("System manager has been reloaded, rechecking machines...");
515
516 HASHMAP_FOREACH(machine, m->machines, i)
517 machine_add_to_gc_queue(machine);
518
519 return 0;
520 }
521
522 int manager_start_scope(
523 Manager *manager,
524 const char *scope,
525 pid_t pid,
526 const char *slice,
527 const char *description,
528 sd_bus_message *more_properties,
529 sd_bus_error *error,
530 char **job) {
531
532 _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
533 int r;
534
535 assert(manager);
536 assert(scope);
537 assert(pid > 1);
538
539 r = sd_bus_message_new_method_call(
540 manager->bus,
541 &m,
542 "org.freedesktop.systemd1",
543 "/org/freedesktop/systemd1",
544 "org.freedesktop.systemd1.Manager",
545 "StartTransientUnit");
546 if (r < 0)
547 return r;
548
549 r = sd_bus_message_append(m, "ss", strempty(scope), "fail");
550 if (r < 0)
551 return r;
552
553 r = sd_bus_message_open_container(m, 'a', "(sv)");
554 if (r < 0)
555 return r;
556
557 if (!isempty(slice)) {
558 r = sd_bus_message_append(m, "(sv)", "Slice", "s", slice);
559 if (r < 0)
560 return r;
561 }
562
563 if (!isempty(description)) {
564 r = sd_bus_message_append(m, "(sv)", "Description", "s", description);
565 if (r < 0)
566 return r;
567 }
568
569 r = sd_bus_message_append(m, "(sv)", "PIDs", "au", 1, pid);
570 if (r < 0)
571 return r;
572
573 if (more_properties) {
574 r = sd_bus_message_copy(m, more_properties, true);
575 if (r < 0)
576 return r;
577 }
578
579 r = sd_bus_message_close_container(m);
580 if (r < 0)
581 return r;
582
583 r = sd_bus_message_append(m, "a(sa(sv))", 0);
584 if (r < 0)
585 return r;
586
587 r = sd_bus_call(manager->bus, m, 0, error, &reply);
588 if (r < 0)
589 return r;
590
591 if (job) {
592 const char *j;
593 char *copy;
594
595 r = sd_bus_message_read(reply, "o", &j);
596 if (r < 0)
597 return r;
598
599 copy = strdup(j);
600 if (!copy)
601 return -ENOMEM;
602
603 *job = copy;
604 }
605
606 return 1;
607 }
608
609 int manager_stop_unit(Manager *manager, const char *unit, sd_bus_error *error, char **job) {
610 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
611 int r;
612
613 assert(manager);
614 assert(unit);
615
616 r = sd_bus_call_method(
617 manager->bus,
618 "org.freedesktop.systemd1",
619 "/org/freedesktop/systemd1",
620 "org.freedesktop.systemd1.Manager",
621 "StopUnit",
622 error,
623 &reply,
624 "ss", unit, "fail");
625 if (r < 0) {
626 if (sd_bus_error_has_name(error, BUS_ERROR_NO_SUCH_UNIT) ||
627 sd_bus_error_has_name(error, BUS_ERROR_LOAD_FAILED)) {
628
629 if (job)
630 *job = NULL;
631
632 sd_bus_error_free(error);
633 return 0;
634 }
635
636 return r;
637 }
638
639 if (job) {
640 const char *j;
641 char *copy;
642
643 r = sd_bus_message_read(reply, "o", &j);
644 if (r < 0)
645 return r;
646
647 copy = strdup(j);
648 if (!copy)
649 return -ENOMEM;
650
651 *job = copy;
652 }
653
654 return 1;
655 }
656
657 int manager_kill_unit(Manager *manager, const char *unit, int signo, sd_bus_error *error) {
658 assert(manager);
659 assert(unit);
660
661 return sd_bus_call_method(
662 manager->bus,
663 "org.freedesktop.systemd1",
664 "/org/freedesktop/systemd1",
665 "org.freedesktop.systemd1.Manager",
666 "KillUnit",
667 error,
668 NULL,
669 "ssi", unit, "all", signo);
670 }
671
672 int manager_unit_is_active(Manager *manager, const char *unit) {
673 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
674 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
675 _cleanup_free_ char *path = NULL;
676 const char *state;
677 int r;
678
679 assert(manager);
680 assert(unit);
681
682 path = unit_dbus_path_from_name(unit);
683 if (!path)
684 return -ENOMEM;
685
686 r = sd_bus_get_property(
687 manager->bus,
688 "org.freedesktop.systemd1",
689 path,
690 "org.freedesktop.systemd1.Unit",
691 "ActiveState",
692 &error,
693 &reply,
694 "s");
695 if (r < 0) {
696 if (sd_bus_error_has_name(&error, SD_BUS_ERROR_NO_REPLY) ||
697 sd_bus_error_has_name(&error, SD_BUS_ERROR_DISCONNECTED))
698 return true;
699
700 if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_UNIT) ||
701 sd_bus_error_has_name(&error, BUS_ERROR_LOAD_FAILED))
702 return false;
703
704 return r;
705 }
706
707 r = sd_bus_message_read(reply, "s", &state);
708 if (r < 0)
709 return -EINVAL;
710
711 return !streq(state, "inactive") && !streq(state, "failed");
712 }
713
714 int manager_job_is_active(Manager *manager, const char *path) {
715 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
716 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
717 int r;
718
719 assert(manager);
720 assert(path);
721
722 r = sd_bus_get_property(
723 manager->bus,
724 "org.freedesktop.systemd1",
725 path,
726 "org.freedesktop.systemd1.Job",
727 "State",
728 &error,
729 &reply,
730 "s");
731 if (r < 0) {
732 if (sd_bus_error_has_name(&error, SD_BUS_ERROR_NO_REPLY) ||
733 sd_bus_error_has_name(&error, SD_BUS_ERROR_DISCONNECTED))
734 return true;
735
736 if (sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_OBJECT))
737 return false;
738
739 return r;
740 }
741
742 /* We don't actually care about the state really. The fact
743 * that we could read the job state is enough for us */
744
745 return true;
746 }
747
748 int manager_get_machine_by_pid(Manager *m, pid_t pid, Machine **machine) {
749 _cleanup_free_ char *unit = NULL;
750 Machine *mm;
751 int r;
752
753 assert(m);
754 assert(pid >= 1);
755 assert(machine);
756
757 r = cg_pid_get_unit(pid, &unit);
758 if (r < 0)
759 mm = hashmap_get(m->machine_leaders, UINT_TO_PTR(pid));
760 else
761 mm = hashmap_get(m->machine_units, unit);
762
763 if (!mm)
764 return 0;
765
766 *machine = mm;
767 return 1;
768 }
769
770 int manager_add_machine(Manager *m, const char *name, Machine **_machine) {
771 Machine *machine;
772
773 assert(m);
774 assert(name);
775
776 machine = hashmap_get(m->machines, name);
777 if (!machine) {
778 machine = machine_new(m, name);
779 if (!machine)
780 return -ENOMEM;
781 }
782
783 if (_machine)
784 *_machine = machine;
785
786 return 0;
787 }