]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/machine/machined.c
machined: implement exit-on-idle
[thirdparty/systemd.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 }