chiark / gitweb /
sd-bus: support connecting to remote hosts, directly into containers
[elogind.git] / src / machine / machined.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2013 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 <pwd.h>
24 #include <fcntl.h>
25 #include <string.h>
26 #include <unistd.h>
27 #include <sys/epoll.h>
28
29 #include "sd-daemon.h"
30
31 #include "strv.h"
32 #include "conf-parser.h"
33 #include "cgroup-util.h"
34 #include "mkdir.h"
35 #include "bus-util.h"
36 #include "bus-error.h"
37 #include "machined.h"
38
39 Manager *manager_new(void) {
40         Manager *m;
41         int r;
42
43         m = new0(Manager, 1);
44         if (!m)
45                 return NULL;
46
47         m->machines = hashmap_new(string_hash_func, string_compare_func);
48         m->machine_units = hashmap_new(string_hash_func, string_compare_func);
49         m->machine_leaders = hashmap_new(trivial_hash_func, trivial_compare_func);
50
51         if (!m->machines || !m->machine_units || !m->machine_leaders) {
52                 manager_free(m);
53                 return NULL;
54         }
55
56         r = sd_event_default(&m->event);
57         if (r < 0) {
58                 manager_free(m);
59                 return NULL;
60         }
61
62         sd_event_set_watchdog(m->event, true);
63
64         return m;
65 }
66
67 void manager_free(Manager *m) {
68         Machine *machine;
69
70         assert(m);
71
72         while ((machine = hashmap_first(m->machines)))
73                 machine_free(machine);
74
75         hashmap_free(m->machines);
76         hashmap_free(m->machine_units);
77         hashmap_free(m->machine_leaders);
78
79         sd_bus_unref(m->bus);
80         sd_event_unref(m->event);
81
82         free(m);
83 }
84
85 int manager_enumerate_machines(Manager *m) {
86         _cleanup_closedir_ DIR *d = NULL;
87         struct dirent *de;
88         int r = 0;
89
90         assert(m);
91
92         /* Read in machine data stored on disk */
93         d = opendir("/run/systemd/machines");
94         if (!d) {
95                 if (errno == ENOENT)
96                         return 0;
97
98                 log_error("Failed to open /run/systemd/machines: %m");
99                 return -errno;
100         }
101
102         FOREACH_DIRENT(de, d, return -errno) {
103                 struct Machine *machine;
104                 int k;
105
106                 if (!dirent_is_file(de))
107                         continue;
108
109                 /* Ignore symlinks that map the unit name to the machine */
110                 if (startswith(de->d_name, "unit:"))
111                         continue;
112
113                 k = manager_add_machine(m, de->d_name, &machine);
114                 if (k < 0) {
115                         log_error("Failed to add machine by file name %s: %s", de->d_name, strerror(-k));
116
117                         r = k;
118                         continue;
119                 }
120
121                 machine_add_to_gc_queue(machine);
122
123                 k = machine_load(machine);
124                 if (k < 0)
125                         r = k;
126         }
127
128         return r;
129 }
130
131 static int manager_connect_bus(Manager *m) {
132         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
133         int r;
134
135         assert(m);
136         assert(!m->bus);
137
138         r = sd_bus_default_system(&m->bus);
139         if (r < 0) {
140                 log_error("Failed to connect to system bus: %s", strerror(-r));
141                 return r;
142         }
143
144         r = sd_bus_add_object_vtable(m->bus, NULL, "/org/freedesktop/machine1", "org.freedesktop.machine1.Manager", manager_vtable, m);
145         if (r < 0) {
146                 log_error("Failed to add manager object vtable: %s", strerror(-r));
147                 return r;
148         }
149
150         r = sd_bus_add_fallback_vtable(m->bus, NULL, "/org/freedesktop/machine1/machine", "org.freedesktop.machine1.Machine", machine_vtable, machine_object_find, m);
151         if (r < 0) {
152                 log_error("Failed to add machine object vtable: %s", strerror(-r));
153                 return r;
154         }
155
156         r = sd_bus_add_node_enumerator(m->bus, NULL, "/org/freedesktop/machine1/machine", machine_node_enumerator, m);
157         if (r < 0) {
158                 log_error("Failed to add machine enumerator: %s", strerror(-r));
159                 return r;
160         }
161
162         r = sd_bus_add_match(m->bus,
163                              NULL,
164                              "type='signal',"
165                              "sender='org.freedesktop.systemd1',"
166                              "interface='org.freedesktop.systemd1.Manager',"
167                              "member='JobRemoved',"
168                              "path='/org/freedesktop/systemd1'",
169                              match_job_removed,
170                              m);
171         if (r < 0) {
172                 log_error("Failed to add match for JobRemoved: %s", strerror(-r));
173                 return r;
174         }
175
176         r = sd_bus_add_match(m->bus,
177                              NULL,
178                              "type='signal',"
179                              "sender='org.freedesktop.systemd1',"
180                              "interface='org.freedesktop.systemd1.Manager',"
181                              "member='UnitRemoved',"
182                              "path='/org/freedesktop/systemd1'",
183                              match_unit_removed,
184                              m);
185         if (r < 0) {
186                 log_error("Failed to add match for UnitRemoved: %s", strerror(-r));
187                 return r;
188         }
189
190         r = sd_bus_add_match(m->bus,
191                              NULL,
192                              "type='signal',"
193                              "sender='org.freedesktop.systemd1',"
194                              "interface='org.freedesktop.DBus.Properties',"
195                              "member='PropertiesChanged'",
196                              match_properties_changed,
197                              m);
198         if (r < 0) {
199                 log_error("Failed to add match for PropertiesChanged: %s", strerror(-r));
200                 return r;
201         }
202
203         r = sd_bus_add_match(m->bus,
204                              NULL,
205                              "type='signal',"
206                              "sender='org.freedesktop.systemd1',"
207                              "interface='org.freedesktop.systemd1.Manager',"
208                              "member='Reloading',"
209                              "path='/org/freedesktop/systemd1'",
210                              match_reloading,
211                              m);
212         if (r < 0) {
213                 log_error("Failed to add match for Reloading: %s", strerror(-r));
214                 return r;
215         }
216
217         r = sd_bus_call_method(
218                         m->bus,
219                         "org.freedesktop.systemd1",
220                         "/org/freedesktop/systemd1",
221                         "org.freedesktop.systemd1.Manager",
222                         "Subscribe",
223                         &error,
224                         NULL, NULL);
225         if (r < 0) {
226                 log_error("Failed to enable subscription: %s", bus_error_message(&error, r));
227                 return r;
228         }
229
230         r = sd_bus_request_name(m->bus, "org.freedesktop.machine1", 0);
231         if (r < 0) {
232                 log_error("Failed to register name: %s", strerror(-r));
233                 return r;
234         }
235
236         r = sd_bus_attach_event(m->bus, m->event, 0);
237         if (r < 0) {
238                 log_error("Failed to attach bus to event loop: %s", strerror(-r));
239                 return r;
240         }
241
242         return 0;
243 }
244
245 void manager_gc(Manager *m, bool drop_not_started) {
246         Machine *machine;
247
248         assert(m);
249
250         while ((machine = m->machine_gc_queue)) {
251                 LIST_REMOVE(gc_queue, m->machine_gc_queue, machine);
252                 machine->in_gc_queue = false;
253
254                 if (!machine_check_gc(machine, drop_not_started)) {
255                         machine_stop(machine);
256                         machine_free(machine);
257                 }
258         }
259 }
260
261 int manager_startup(Manager *m) {
262         Machine *machine;
263         Iterator i;
264         int r;
265
266         assert(m);
267
268         /* Connect to the bus */
269         r = manager_connect_bus(m);
270         if (r < 0)
271                 return r;
272
273         /* Deserialize state */
274         manager_enumerate_machines(m);
275
276         /* Remove stale objects before we start them */
277         manager_gc(m, false);
278
279         /* And start everything */
280         HASHMAP_FOREACH(machine, m->machines, i)
281                 machine_start(machine, NULL, NULL);
282
283         return 0;
284 }
285
286 static bool check_idle(void *userdata) {
287         Manager *m = userdata;
288
289         manager_gc(m, true);
290
291         return hashmap_isempty(m->machines);
292 }
293
294 int manager_run(Manager *m) {
295         assert(m);
296
297         return bus_event_loop_with_idle(
298                         m->event,
299                         m->bus,
300                         "org.freedesktop.machine1",
301                         DEFAULT_EXIT_USEC,
302                         check_idle, m);
303 }
304
305 int main(int argc, char *argv[]) {
306         Manager *m = NULL;
307         int r;
308
309         log_set_target(LOG_TARGET_AUTO);
310         log_set_facility(LOG_AUTH);
311         log_parse_environment();
312         log_open();
313
314         umask(0022);
315
316         if (argc != 1) {
317                 log_error("This program takes no arguments.");
318                 r = -EINVAL;
319                 goto finish;
320         }
321
322         /* Always create the directories people can create inotify
323          * watches in. Note that some applications might check for the
324          * existence of /run/systemd/machines/ to determine whether
325          * machined is available, so please always make sure this
326          * check stays in. */
327         mkdir_label("/run/systemd/machines", 0755);
328
329         m = manager_new();
330         if (!m) {
331                 r = log_oom();
332                 goto finish;
333         }
334
335         r = manager_startup(m);
336         if (r < 0) {
337                 log_error("Failed to fully start up daemon: %s", strerror(-r));
338                 goto finish;
339         }
340
341         log_debug("systemd-machined running as pid "PID_FMT, getpid());
342
343         sd_notify(false,
344                   "READY=1\n"
345                   "STATUS=Processing requests...");
346
347         r = manager_run(m);
348
349         log_debug("systemd-machined stopped as pid "PID_FMT, getpid());
350
351 finish:
352         sd_notify(false,
353                   "STATUS=Shutting down...");
354
355         if (m)
356                 manager_free(m);
357
358         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
359 }