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