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