]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/machine/machined-dbus.c
logind,machined,run: properly invoke StartTransientUnit() 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 "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_message_append(m, "a(sa(sv))", 0);
531 if (r < 0)
532 return r;
533
534 r = sd_bus_call(manager->bus, m, 0, error, &reply);
535 if (r < 0)
536 return r;
537
538 if (job) {
539 const char *j;
540 char *copy;
541
542 r = sd_bus_message_read(reply, "o", &j);
543 if (r < 0)
544 return r;
545
546 copy = strdup(j);
547 if (!copy)
548 return -ENOMEM;
549
550 *job = copy;
551 }
552
553 return 1;
554 }
555
556 int manager_stop_unit(Manager *manager, const char *unit, sd_bus_error *error, char **job) {
557 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
558 int r;
559
560 assert(manager);
561 assert(unit);
562
563 r = sd_bus_call_method(
564 manager->bus,
565 "org.freedesktop.systemd1",
566 "/org/freedesktop/systemd1",
567 "org.freedesktop.systemd1.Manager",
568 "StopUnit",
569 error,
570 &reply,
571 "ss", unit, "fail");
572 if (r < 0) {
573 if (sd_bus_error_has_name(error, BUS_ERROR_NO_SUCH_UNIT) ||
574 sd_bus_error_has_name(error, BUS_ERROR_LOAD_FAILED)) {
575
576 if (job)
577 *job = NULL;
578
579 sd_bus_error_free(error);
580 return 0;
581 }
582
583 return r;
584 }
585
586 if (job) {
587 const char *j;
588 char *copy;
589
590 r = sd_bus_message_read(reply, "o", &j);
591 if (r < 0)
592 return r;
593
594 copy = strdup(j);
595 if (!copy)
596 return -ENOMEM;
597
598 *job = copy;
599 }
600
601 return 1;
602 }
603
604 int manager_kill_unit(Manager *manager, const char *unit, KillWho who, int signo, sd_bus_error *error) {
605 assert(manager);
606 assert(unit);
607
608 return sd_bus_call_method(
609 manager->bus,
610 "org.freedesktop.systemd1",
611 "/org/freedesktop/systemd1",
612 "org.freedesktop.systemd1.Manager",
613 "KillUnit",
614 error,
615 NULL,
616 "ssi", unit, who == KILL_LEADER ? "main" : "all", signo);
617 }
618
619 int manager_unit_is_active(Manager *manager, const char *unit) {
620 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
621 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
622 _cleanup_free_ char *path = NULL;
623 const char *state;
624 int r;
625
626 assert(manager);
627 assert(unit);
628
629 path = unit_dbus_path_from_name(unit);
630 if (!path)
631 return -ENOMEM;
632
633 r = sd_bus_get_property(
634 manager->bus,
635 "org.freedesktop.systemd1",
636 path,
637 "org.freedesktop.systemd1.Unit",
638 "ActiveState",
639 &error,
640 &reply,
641 "s");
642 if (r < 0) {
643 if (sd_bus_error_has_name(&error, SD_BUS_ERROR_NO_REPLY) ||
644 sd_bus_error_has_name(&error, SD_BUS_ERROR_DISCONNECTED))
645 return true;
646
647 if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_UNIT) ||
648 sd_bus_error_has_name(&error, BUS_ERROR_LOAD_FAILED))
649 return false;
650
651 return r;
652 }
653
654 r = sd_bus_message_read(reply, "s", &state);
655 if (r < 0)
656 return -EINVAL;
657
658 return !streq(state, "inactive") && !streq(state, "failed");
659 }
660
661 int manager_job_is_active(Manager *manager, const char *path) {
662 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
663 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
664 int r;
665
666 assert(manager);
667 assert(path);
668
669 r = sd_bus_get_property(
670 manager->bus,
671 "org.freedesktop.systemd1",
672 path,
673 "org.freedesktop.systemd1.Job",
674 "State",
675 &error,
676 &reply,
677 "s");
678 if (r < 0) {
679 if (sd_bus_error_has_name(&error, SD_BUS_ERROR_NO_REPLY) ||
680 sd_bus_error_has_name(&error, SD_BUS_ERROR_DISCONNECTED))
681 return true;
682
683 if (sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_OBJECT))
684 return false;
685
686 return r;
687 }
688
689 /* We don't actually care about the state really. The fact
690 * that we could read the job state is enough for us */
691
692 return true;
693 }
694
695 int manager_get_machine_by_pid(Manager *m, pid_t pid, Machine **machine) {
696 _cleanup_free_ char *unit = NULL;
697 Machine *mm;
698 int r;
699
700 assert(m);
701 assert(pid >= 1);
702 assert(machine);
703
704 r = cg_pid_get_unit(pid, &unit);
705 if (r < 0)
706 mm = hashmap_get(m->machine_leaders, UINT_TO_PTR(pid));
707 else
708 mm = hashmap_get(m->machine_units, unit);
709
710 if (!mm)
711 return 0;
712
713 *machine = mm;
714 return 1;
715 }
716
717 int manager_add_machine(Manager *m, const char *name, Machine **_machine) {
718 Machine *machine;
719
720 assert(m);
721 assert(name);
722
723 machine = hashmap_get(m->machines, name);
724 if (!machine) {
725 machine = machine_new(m, name);
726 if (!machine)
727 return -ENOMEM;
728 }
729
730 if (_machine)
731 *_machine = machine;
732
733 return 0;
734 }