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