]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/machine/machined.c
util-lib: introduce dirent-util.[ch] for directory entry calls
[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 <string.h>
24 #include <unistd.h>
25
26 #include "sd-daemon.h"
27
28 #include "bus-error.h"
29 #include "bus-util.h"
30 #include "cgroup-util.h"
31 #include "dirent-util.h"
32 #include "fd-util.h"
33 #include "formats-util.h"
34 #include "hostname-util.h"
35 #include "label.h"
36 #include "machine-image.h"
37 #include "machined.h"
38 #include "signal-util.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 static int manager_add_host_machine(Manager *m) {
97 _cleanup_free_ char *rd = NULL, *unit = NULL;
98 sd_id128_t mid;
99 Machine *t;
100 int r;
101
102 if (m->host_machine)
103 return 0;
104
105 r = sd_id128_get_machine(&mid);
106 if (r < 0)
107 return log_error_errno(r, "Failed to get machine ID: %m");
108
109 rd = strdup("/");
110 if (!rd)
111 return log_oom();
112
113 unit = strdup("-.slice");
114 if (!unit)
115 return log_oom();
116
117 t = machine_new(m, MACHINE_HOST, ".host");
118 if (!t)
119 return log_oom();
120
121 t->leader = 1;
122 t->id = mid;
123
124 t->root_directory = rd;
125 t->unit = unit;
126 rd = unit = NULL;
127
128 dual_timestamp_from_boottime_or_monotonic(&t->timestamp, 0);
129
130 m->host_machine = t;
131
132 return 0;
133 }
134
135 int manager_enumerate_machines(Manager *m) {
136 _cleanup_closedir_ DIR *d = NULL;
137 struct dirent *de;
138 int r = 0;
139
140 assert(m);
141
142 r = manager_add_host_machine(m);
143 if (r < 0)
144 return r;
145
146 /* Read in machine data stored on disk */
147 d = opendir("/run/systemd/machines");
148 if (!d) {
149 if (errno == ENOENT)
150 return 0;
151
152 log_error_errno(errno, "Failed to open /run/systemd/machines: %m");
153 return -errno;
154 }
155
156 FOREACH_DIRENT(de, d, return -errno) {
157 struct Machine *machine;
158 int k;
159
160 if (!dirent_is_file(de))
161 continue;
162
163 /* Ignore symlinks that map the unit name to the machine */
164 if (startswith(de->d_name, "unit:"))
165 continue;
166
167 if (!machine_name_is_valid(de->d_name))
168 continue;
169
170 k = manager_add_machine(m, de->d_name, &machine);
171 if (k < 0) {
172 r = log_error_errno(k, "Failed to add machine by file name %s: %m", de->d_name);
173 continue;
174 }
175
176 machine_add_to_gc_queue(machine);
177
178 k = machine_load(machine);
179 if (k < 0)
180 r = k;
181 }
182
183 return r;
184 }
185
186 static int manager_connect_bus(Manager *m) {
187 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
188 int r;
189
190 assert(m);
191 assert(!m->bus);
192
193 r = sd_bus_default_system(&m->bus);
194 if (r < 0)
195 return log_error_errno(r, "Failed to connect to system bus: %m");
196
197 r = sd_bus_add_object_vtable(m->bus, NULL, "/org/freedesktop/machine1", "org.freedesktop.machine1.Manager", manager_vtable, m);
198 if (r < 0)
199 return log_error_errno(r, "Failed to add manager object vtable: %m");
200
201 r = sd_bus_add_fallback_vtable(m->bus, NULL, "/org/freedesktop/machine1/machine", "org.freedesktop.machine1.Machine", machine_vtable, machine_object_find, m);
202 if (r < 0)
203 return log_error_errno(r, "Failed to add machine object vtable: %m");
204
205 r = sd_bus_add_node_enumerator(m->bus, NULL, "/org/freedesktop/machine1/machine", machine_node_enumerator, m);
206 if (r < 0)
207 return log_error_errno(r, "Failed to add machine enumerator: %m");
208
209 r = sd_bus_add_fallback_vtable(m->bus, NULL, "/org/freedesktop/machine1/image", "org.freedesktop.machine1.Image", image_vtable, image_object_find, m);
210 if (r < 0)
211 return log_error_errno(r, "Failed to add image object vtable: %m");
212
213 r = sd_bus_add_node_enumerator(m->bus, NULL, "/org/freedesktop/machine1/image", image_node_enumerator, m);
214 if (r < 0)
215 return log_error_errno(r, "Failed to add image enumerator: %m");
216
217 r = sd_bus_add_match(m->bus,
218 NULL,
219 "type='signal',"
220 "sender='org.freedesktop.systemd1',"
221 "interface='org.freedesktop.systemd1.Manager',"
222 "member='JobRemoved',"
223 "path='/org/freedesktop/systemd1'",
224 match_job_removed,
225 m);
226 if (r < 0)
227 return log_error_errno(r, "Failed to add match for JobRemoved: %m");
228
229 r = sd_bus_add_match(m->bus,
230 NULL,
231 "type='signal',"
232 "sender='org.freedesktop.systemd1',"
233 "interface='org.freedesktop.systemd1.Manager',"
234 "member='UnitRemoved',"
235 "path='/org/freedesktop/systemd1'",
236 match_unit_removed,
237 m);
238 if (r < 0)
239 return log_error_errno(r, "Failed to add match for UnitRemoved: %m");
240
241 r = sd_bus_add_match(m->bus,
242 NULL,
243 "type='signal',"
244 "sender='org.freedesktop.systemd1',"
245 "interface='org.freedesktop.DBus.Properties',"
246 "member='PropertiesChanged',"
247 "arg0='org.freedesktop.systemd1.Unit'",
248 match_properties_changed,
249 m);
250 if (r < 0)
251 return log_error_errno(r, "Failed to add match for PropertiesChanged: %m");
252
253 r = sd_bus_add_match(m->bus,
254 NULL,
255 "type='signal',"
256 "sender='org.freedesktop.systemd1',"
257 "interface='org.freedesktop.systemd1.Manager',"
258 "member='Reloading',"
259 "path='/org/freedesktop/systemd1'",
260 match_reloading,
261 m);
262 if (r < 0)
263 return log_error_errno(r, "Failed to add match for Reloading: %m");
264
265 r = sd_bus_call_method(
266 m->bus,
267 "org.freedesktop.systemd1",
268 "/org/freedesktop/systemd1",
269 "org.freedesktop.systemd1.Manager",
270 "Subscribe",
271 &error,
272 NULL, NULL);
273 if (r < 0) {
274 log_error("Failed to enable subscription: %s", bus_error_message(&error, r));
275 return r;
276 }
277
278 r = sd_bus_request_name(m->bus, "org.freedesktop.machine1", 0);
279 if (r < 0)
280 return log_error_errno(r, "Failed to register name: %m");
281
282 r = sd_bus_attach_event(m->bus, m->event, 0);
283 if (r < 0)
284 return log_error_errno(r, "Failed to attach bus to event loop: %m");
285
286 return 0;
287 }
288
289 void manager_gc(Manager *m, bool drop_not_started) {
290 Machine *machine;
291
292 assert(m);
293
294 while ((machine = m->machine_gc_queue)) {
295 LIST_REMOVE(gc_queue, m->machine_gc_queue, machine);
296 machine->in_gc_queue = false;
297
298 /* First, if we are not closing yet, initiate stopping */
299 if (!machine_check_gc(machine, drop_not_started) &&
300 machine_get_state(machine) != MACHINE_CLOSING)
301 machine_stop(machine);
302
303 /* Now, the stop stop probably made this referenced
304 * again, but if it didn't, then it's time to let it
305 * go entirely. */
306 if (!machine_check_gc(machine, drop_not_started)) {
307 machine_finalize(machine);
308 machine_free(machine);
309 }
310 }
311 }
312
313 int manager_startup(Manager *m) {
314 Machine *machine;
315 Iterator i;
316 int r;
317
318 assert(m);
319
320 /* Connect to the bus */
321 r = manager_connect_bus(m);
322 if (r < 0)
323 return r;
324
325 /* Deserialize state */
326 manager_enumerate_machines(m);
327
328 /* Remove stale objects before we start them */
329 manager_gc(m, false);
330
331 /* And start everything */
332 HASHMAP_FOREACH(machine, m->machines, i)
333 machine_start(machine, NULL, NULL);
334
335 return 0;
336 }
337
338 static bool check_idle(void *userdata) {
339 Manager *m = userdata;
340
341 manager_gc(m, true);
342
343 return hashmap_isempty(m->machines);
344 }
345
346 int manager_run(Manager *m) {
347 assert(m);
348
349 return bus_event_loop_with_idle(
350 m->event,
351 m->bus,
352 "org.freedesktop.machine1",
353 DEFAULT_EXIT_USEC,
354 check_idle, m);
355 }
356
357 int main(int argc, char *argv[]) {
358 Manager *m = NULL;
359 int r;
360
361 log_set_target(LOG_TARGET_AUTO);
362 log_set_facility(LOG_AUTH);
363 log_parse_environment();
364 log_open();
365
366 umask(0022);
367
368 if (argc != 1) {
369 log_error("This program takes no arguments.");
370 r = -EINVAL;
371 goto finish;
372 }
373
374 /* Always create the directories people can create inotify
375 * watches in. Note that some applications might check for the
376 * existence of /run/systemd/machines/ to determine whether
377 * machined is available, so please always make sure this
378 * check stays in. */
379 mkdir_label("/run/systemd/machines", 0755);
380
381 assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD, -1) >= 0);
382
383 m = manager_new();
384 if (!m) {
385 r = log_oom();
386 goto finish;
387 }
388
389 r = manager_startup(m);
390 if (r < 0) {
391 log_error_errno(r, "Failed to fully start up daemon: %m");
392 goto finish;
393 }
394
395 log_debug("systemd-machined running as pid "PID_FMT, getpid());
396
397 sd_notify(false,
398 "READY=1\n"
399 "STATUS=Processing requests...");
400
401 r = manager_run(m);
402
403 log_debug("systemd-machined stopped as pid "PID_FMT, getpid());
404
405 finish:
406 manager_free(m);
407
408 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
409 }