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