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