chiark / gitweb /
machined: fix enumeration of existing machines on restart
[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, "/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, "/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, "/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                              "type='signal',"
164                              "sender='org.freedesktop.systemd1',"
165                              "interface='org.freedesktop.systemd1.Manager',"
166                              "member='JobRemoved',"
167                              "path='/org/freedesktop/systemd1'",
168                              match_job_removed,
169                              m);
170         if (r < 0) {
171                 log_error("Failed to add match for JobRemoved: %s", strerror(-r));
172                 return r;
173         }
174
175         r = sd_bus_add_match(m->bus,
176                              "type='signal',"
177                              "sender='org.freedesktop.systemd1',"
178                              "interface='org.freedesktop.systemd1.Manager',"
179                              "member='UnitRemoved',"
180                              "path='/org/freedesktop/systemd1'",
181                              match_unit_removed,
182                              m);
183         if (r < 0) {
184                 log_error("Failed to add match for UnitRemoved: %s", strerror(-r));
185                 return r;
186         }
187
188         r = sd_bus_add_match(m->bus,
189                              "type='signal',"
190                              "sender='org.freedesktop.systemd1',"
191                              "interface='org.freedesktop.DBus.Properties',"
192                              "member='PropertiesChanged'",
193                              match_properties_changed,
194                              m);
195         if (r < 0) {
196                 log_error("Failed to add match for PropertiesChanged: %s", strerror(-r));
197                 return r;
198         }
199
200         r = sd_bus_add_match(m->bus,
201                              "type='signal',"
202                              "sender='org.freedesktop.systemd1',"
203                              "interface='org.freedesktop.systemd1.Manager',"
204                              "member='Reloading',"
205                              "path='/org/freedesktop/systemd1'",
206                              match_reloading,
207                              m);
208         if (r < 0) {
209                 log_error("Failed to add match for Reloading: %s", strerror(-r));
210                 return r;
211         }
212
213         r = sd_bus_call_method(
214                         m->bus,
215                         "org.freedesktop.systemd1",
216                         "/org/freedesktop/systemd1",
217                         "org.freedesktop.systemd1.Manager",
218                         "Subscribe",
219                         &error,
220                         NULL, NULL);
221         if (r < 0) {
222                 log_error("Failed to enable subscription: %s", bus_error_message(&error, r));
223                 return r;
224         }
225
226         r = sd_bus_request_name(m->bus, "org.freedesktop.machine1", 0);
227         if (r < 0) {
228                 log_error("Failed to register name: %s", strerror(-r));
229                 return r;
230         }
231
232         r = sd_bus_attach_event(m->bus, m->event, 0);
233         if (r < 0) {
234                 log_error("Failed to attach bus to event loop: %s", strerror(-r));
235                 return r;
236         }
237
238         return 0;
239 }
240
241 void manager_gc(Manager *m, bool drop_not_started) {
242         Machine *machine;
243
244         assert(m);
245
246         while ((machine = m->machine_gc_queue)) {
247                 LIST_REMOVE(gc_queue, m->machine_gc_queue, machine);
248                 machine->in_gc_queue = false;
249
250                 if (!machine_check_gc(machine, drop_not_started)) {
251                         machine_stop(machine);
252                         machine_free(machine);
253                 }
254         }
255 }
256
257 int manager_startup(Manager *m) {
258         Machine *machine;
259         Iterator i;
260         int r;
261
262         assert(m);
263
264         /* Connect to the bus */
265         r = manager_connect_bus(m);
266         if (r < 0)
267                 return r;
268
269         /* Deserialize state */
270         manager_enumerate_machines(m);
271
272         /* Remove stale objects before we start them */
273         manager_gc(m, false);
274
275         /* And start everything */
276         HASHMAP_FOREACH(machine, m->machines, i)
277                 machine_start(machine, NULL, NULL);
278
279         return 0;
280 }
281
282 static bool check_idle(void *userdata) {
283         Manager *m = userdata;
284
285         manager_gc(m, true);
286
287         return hashmap_isempty(m->machines);
288 }
289
290 int manager_run(Manager *m) {
291         assert(m);
292
293         return bus_event_loop_with_idle(
294                         m->event,
295                         m->bus,
296                         "org.freedesktop.machine1",
297                         DEFAULT_EXIT_USEC,
298                         check_idle, m);
299 }
300
301 int main(int argc, char *argv[]) {
302         Manager *m = NULL;
303         int r;
304
305         log_set_target(LOG_TARGET_AUTO);
306         log_set_facility(LOG_AUTH);
307         log_parse_environment();
308         log_open();
309
310         umask(0022);
311
312         if (argc != 1) {
313                 log_error("This program takes no arguments.");
314                 r = -EINVAL;
315                 goto finish;
316         }
317
318         /* Always create the directories people can create inotify
319          * watches in. Note that some applications might check for the
320          * existence of /run/systemd/machines/ to determine whether
321          * machined is available, so please always make sure this
322          * check stays in. */
323         mkdir_label("/run/systemd/machines", 0755);
324
325         m = manager_new();
326         if (!m) {
327                 r = log_oom();
328                 goto finish;
329         }
330
331         r = manager_startup(m);
332         if (r < 0) {
333                 log_error("Failed to fully start up daemon: %s", strerror(-r));
334                 goto finish;
335         }
336
337         log_debug("systemd-machined running as pid %lu", (unsigned long) getpid());
338
339         sd_notify(false,
340                   "READY=1\n"
341                   "STATUS=Processing requests...");
342
343         r = manager_run(m);
344
345         log_debug("systemd-machined stopped as pid %lu", (unsigned long) getpid());
346
347 finish:
348         sd_notify(false,
349                   "STATUS=Shutting down...");
350
351         if (m)
352                 manager_free(m);
353
354         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
355 }