chiark / gitweb /
logind: automatically determine client side PID if GetSessionByPID() is called with...
[elogind.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 }