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