chiark / gitweb /
machine: move symbols referenced by shared code from main to shared file
[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 match_job_removed(sd_bus *bus, sd_bus_message *message, void *userdata) {
346         const char *path, *result, *unit;
347         Manager *m = userdata;
348         Machine *machine;
349         uint32_t id;
350         int r;
351
352         assert(bus);
353         assert(message);
354         assert(m);
355
356         r = sd_bus_message_read(message, "uoss", &id, &path, &unit, &result);
357         if (r < 0) {
358                 log_error("Failed to parse JobRemoved message: %s", strerror(-r));
359                 return 0;
360         }
361
362         machine = hashmap_get(m->machine_units, unit);
363         if (!machine)
364                 return 0;
365
366         if (streq_ptr(path, machine->scope_job)) {
367                 free(machine->scope_job);
368                 machine->scope_job = NULL;
369
370                 if (machine->started) {
371                         if (streq(result, "done"))
372                                 machine_send_create_reply(machine, NULL);
373                         else {
374                                 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
375
376                                 sd_bus_error_setf(&error, BUS_ERROR_JOB_FAILED, "Start job for unit %s failed with '%s'", unit, result);
377
378                                 machine_send_create_reply(machine, &error);
379                         }
380                 } else
381                         machine_save(machine);
382         }
383
384         machine_add_to_gc_queue(machine);
385         return 0;
386 }
387
388 int match_properties_changed(sd_bus *bus, sd_bus_message *message, void *userdata) {
389         _cleanup_free_ char *unit = NULL;
390         Manager *m = userdata;
391         Machine *machine;
392         const char *path;
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         unit_name_from_dbus_path(path, &unit);
403         if (!unit)
404                 return 0;
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) {
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                 log_error("Failed to parse UnitRemoved message: %s", strerror(-r));
426                 return 0;
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) {
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                 log_error("Failed to parse Reloading message: %s", strerror(-r));
447                 return 0;
448         }
449
450         if (b)
451                 return 0;
452
453         /* systemd finished reloading, let's recheck all our machines */
454         log_debug("System manager has been reloaded, rechecking machines...");
455
456         HASHMAP_FOREACH(machine, m->machines, i)
457                 machine_add_to_gc_queue(machine);
458
459         return 0;
460 }
461
462 int manager_start_scope(
463                 Manager *manager,
464                 const char *scope,
465                 pid_t pid,
466                 const char *slice,
467                 const char *description,
468                 sd_bus_message *more_properties,
469                 sd_bus_error *error,
470                 char **job) {
471
472         _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
473         int r;
474
475         assert(manager);
476         assert(scope);
477         assert(pid > 1);
478
479         r = sd_bus_message_new_method_call(
480                         manager->bus,
481                         "org.freedesktop.systemd1",
482                         "/org/freedesktop/systemd1",
483                         "org.freedesktop.systemd1.Manager",
484                         "StartTransientUnit",
485                         &m);
486         if (r < 0)
487                 return r;
488
489         r = sd_bus_message_append(m, "ss", strempty(scope), "fail");
490         if (r < 0)
491                 return r;
492
493         r = sd_bus_message_open_container(m, 'a', "(sv)");
494         if (r < 0)
495                 return r;
496
497         if (!isempty(slice)) {
498                 r = sd_bus_message_append(m, "(sv)", "Slice", "s", slice);
499                 if (r < 0)
500                         return r;
501         }
502
503         if (!isempty(description)) {
504                 r = sd_bus_message_append(m, "(sv)", "Description", "s", description);
505                 if (r < 0)
506                         return r;
507         }
508
509         /* cgroup empty notification is not available in containers
510          * currently. To make this less problematic, let's shorten the
511          * stop timeout for machines, so that we don't wait
512          * forever. */
513         r = sd_bus_message_append(m, "(sv)", "TimeoutStopUSec", "t", 500 * USEC_PER_MSEC);
514         if (r < 0)
515                 return r;
516
517         r = sd_bus_message_append(m, "(sv)", "PIDs", "au", 1, pid);
518         if (r < 0)
519                 return r;
520
521         if (more_properties) {
522                 r = sd_bus_message_copy(m, more_properties, true);
523                 if (r < 0)
524                         return r;
525         }
526
527         r = sd_bus_message_close_container(m);
528         if (r < 0)
529                 return r;
530
531         r = sd_bus_send_with_reply_and_block(manager->bus, m, 0, error, &reply);
532         if (r < 0)
533                 return r;
534
535         if (job) {
536                 const char *j;
537                 char *copy;
538
539                 r = sd_bus_message_read(reply, "o", &j);
540                 if (r < 0)
541                         return r;
542
543                 copy = strdup(j);
544                 if (!copy)
545                         return -ENOMEM;
546
547                 *job = copy;
548         }
549
550         return 1;
551 }
552
553 int manager_stop_unit(Manager *manager, const char *unit, sd_bus_error *error, char **job) {
554         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
555         int r;
556
557         assert(manager);
558         assert(unit);
559
560         r = sd_bus_call_method(
561                         manager->bus,
562                         "org.freedesktop.systemd1",
563                         "/org/freedesktop/systemd1",
564                         "org.freedesktop.systemd1.Manager",
565                         "StopUnit",
566                         error,
567                         &reply,
568                         "ss", unit, "fail");
569         if (r < 0) {
570                 if (sd_bus_error_has_name(error, BUS_ERROR_NO_SUCH_UNIT) ||
571                     sd_bus_error_has_name(error, BUS_ERROR_LOAD_FAILED)) {
572
573                         if (job)
574                                 *job = NULL;
575
576                         sd_bus_error_free(error);
577                         return 0;
578                 }
579
580                 return r;
581         }
582
583         if (job) {
584                 const char *j;
585                 char *copy;
586
587                 r = sd_bus_message_read(reply, "o", &j);
588                 if (r < 0)
589                         return r;
590
591                 copy = strdup(j);
592                 if (!copy)
593                         return -ENOMEM;
594
595                 *job = copy;
596         }
597
598         return 1;
599 }
600
601 int manager_kill_unit(Manager *manager, const char *unit, KillWho who, int signo, sd_bus_error *error) {
602         assert(manager);
603         assert(unit);
604
605         return sd_bus_call_method(
606                         manager->bus,
607                         "org.freedesktop.systemd1",
608                         "/org/freedesktop/systemd1",
609                         "org.freedesktop.systemd1.Manager",
610                         "KillUnit",
611                         error,
612                         NULL,
613                         "ssi", unit, who == KILL_LEADER ? "main" : "all", signo);
614 }
615
616 int manager_unit_is_active(Manager *manager, const char *unit) {
617         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
618         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
619         _cleanup_free_ char *path = NULL;
620         const char *state;
621         int r;
622
623         assert(manager);
624         assert(unit);
625
626         path = unit_dbus_path_from_name(unit);
627         if (!path)
628                 return -ENOMEM;
629
630         r = sd_bus_get_property(
631                         manager->bus,
632                         "org.freedesktop.systemd1",
633                         path,
634                         "org.freedesktop.systemd1.Unit",
635                         "ActiveState",
636                         &error,
637                         &reply,
638                         "s");
639         if (r < 0) {
640                 if (sd_bus_error_has_name(&error, SD_BUS_ERROR_NO_REPLY) ||
641                     sd_bus_error_has_name(&error, SD_BUS_ERROR_DISCONNECTED))
642                         return true;
643
644                 if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_UNIT) ||
645                     sd_bus_error_has_name(&error, BUS_ERROR_LOAD_FAILED))
646                         return false;
647
648                 return r;
649         }
650
651         r = sd_bus_message_read(reply, "s", &state);
652         if (r < 0)
653                 return -EINVAL;
654
655         return !streq(state, "inactive") && !streq(state, "failed");
656 }
657
658 int manager_job_is_active(Manager *manager, const char *path) {
659         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
660         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
661         int r;
662
663         assert(manager);
664         assert(path);
665
666         r = sd_bus_get_property(
667                         manager->bus,
668                         "org.freedesktop.systemd1",
669                         path,
670                         "org.freedesktop.systemd1.Job",
671                         "State",
672                         &error,
673                         &reply,
674                         "s");
675         if (r < 0) {
676                 if (sd_bus_error_has_name(&error, SD_BUS_ERROR_NO_REPLY) ||
677                     sd_bus_error_has_name(&error, SD_BUS_ERROR_DISCONNECTED))
678                         return true;
679
680                 if (sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_OBJECT))
681                         return false;
682
683                 return r;
684         }
685
686         /* We don't actually care about the state really. The fact
687          * that we could read the job state is enough for us */
688
689         return true;
690 }
691
692 int manager_get_machine_by_pid(Manager *m, pid_t pid, Machine **machine) {
693         _cleanup_free_ char *unit = NULL;
694         Machine *mm;
695         int r;
696
697         assert(m);
698         assert(pid >= 1);
699         assert(machine);
700
701         r = cg_pid_get_unit(pid, &unit);
702         if (r < 0)
703                 mm = hashmap_get(m->machine_leaders, UINT_TO_PTR(pid));
704         else
705                 mm = hashmap_get(m->machine_units, unit);
706
707         if (!mm)
708                 return 0;
709
710         *machine = mm;
711         return 1;
712 }
713
714 int manager_add_machine(Manager *m, const char *name, Machine **_machine) {
715         Machine *machine;
716
717         assert(m);
718         assert(name);
719
720         machine = hashmap_get(m->machines, name);
721         if (!machine) {
722                 machine = machine_new(m, name);
723                 if (!machine)
724                         return -ENOMEM;
725         }
726
727         if (_machine)
728                 *_machine = machine;
729
730         return 0;
731 }