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