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