]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/machine/machined.c
tree-wide: drop 'This file is part of systemd' blurb
[thirdparty/systemd.git] / src / machine / machined.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3 Copyright 2013 Lennart Poettering
4 ***/
5
6 #include <errno.h>
7 #include <string.h>
8 #include <unistd.h>
9
10 #include "sd-daemon.h"
11
12 #include "alloc-util.h"
13 #include "bus-error.h"
14 #include "bus-util.h"
15 #include "cgroup-util.h"
16 #include "dirent-util.h"
17 #include "fd-util.h"
18 #include "format-util.h"
19 #include "hostname-util.h"
20 #include "label.h"
21 #include "machine-image.h"
22 #include "machined.h"
23 #include "process-util.h"
24 #include "signal-util.h"
25 #include "special.h"
26
27 static Manager* manager_unref(Manager *m);
28 DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_unref);
29
30 static int manager_new(Manager **ret) {
31 _cleanup_(manager_unrefp) Manager *m = NULL;
32 int r;
33
34 assert(ret);
35
36 m = new0(Manager, 1);
37 if (!m)
38 return -ENOMEM;
39
40 m->machines = hashmap_new(&string_hash_ops);
41 m->machine_units = hashmap_new(&string_hash_ops);
42 m->machine_leaders = hashmap_new(NULL);
43
44 if (!m->machines || !m->machine_units || !m->machine_leaders)
45 return -ENOMEM;
46
47 r = sd_event_default(&m->event);
48 if (r < 0)
49 return r;
50
51 r = sd_event_add_signal(m->event, NULL, SIGINT, NULL, NULL);
52 if (r < 0)
53 return r;
54
55 r = sd_event_add_signal(m->event, NULL, SIGTERM, NULL, NULL);
56 if (r < 0)
57 return r;
58
59 (void) sd_event_set_watchdog(m->event, true);
60
61 *ret = TAKE_PTR(m);
62 return 0;
63 }
64
65 static Manager* manager_unref(Manager *m) {
66 Machine *machine;
67
68 if (!m)
69 return NULL;
70
71 while (m->operations)
72 operation_free(m->operations);
73
74 assert(m->n_operations == 0);
75
76 while ((machine = hashmap_first(m->machines)))
77 machine_free(machine);
78
79 hashmap_free(m->machines);
80 hashmap_free(m->machine_units);
81 hashmap_free(m->machine_leaders);
82
83 hashmap_free_with_destructor(m->image_cache, image_unref);
84
85 sd_event_source_unref(m->image_cache_defer_event);
86
87 bus_verify_polkit_async_registry_free(m->polkit_registry);
88
89 sd_bus_unref(m->bus);
90 sd_event_unref(m->event);
91
92 return mfree(m);
93 }
94
95 static int manager_add_host_machine(Manager *m) {
96 _cleanup_free_ char *rd = NULL, *unit = NULL;
97 sd_id128_t mid;
98 Machine *t;
99 int r;
100
101 if (m->host_machine)
102 return 0;
103
104 r = sd_id128_get_machine(&mid);
105 if (r < 0)
106 return log_error_errno(r, "Failed to get machine ID: %m");
107
108 rd = strdup("/");
109 if (!rd)
110 return log_oom();
111
112 unit = strdup(SPECIAL_ROOT_SLICE);
113 if (!unit)
114 return log_oom();
115
116 t = machine_new(m, MACHINE_HOST, ".host");
117 if (!t)
118 return log_oom();
119
120 t->leader = 1;
121 t->id = mid;
122
123 t->root_directory = TAKE_PTR(rd);
124 t->unit = TAKE_PTR(unit);
125
126 dual_timestamp_from_boottime_or_monotonic(&t->timestamp, 0);
127
128 m->host_machine = t;
129
130 return 0;
131 }
132
133 static int manager_enumerate_machines(Manager *m) {
134 _cleanup_closedir_ DIR *d = NULL;
135 struct dirent *de;
136 int r = 0;
137
138 assert(m);
139
140 r = manager_add_host_machine(m);
141 if (r < 0)
142 return r;
143
144 /* Read in machine data stored on disk */
145 d = opendir("/run/systemd/machines");
146 if (!d) {
147 if (errno == ENOENT)
148 return 0;
149
150 return log_error_errno(errno, "Failed to open /run/systemd/machines: %m");
151 }
152
153 FOREACH_DIRENT(de, d, return -errno) {
154 struct Machine *machine;
155 int k;
156
157 if (!dirent_is_file(de))
158 continue;
159
160 /* Ignore symlinks that map the unit name to the machine */
161 if (startswith(de->d_name, "unit:"))
162 continue;
163
164 if (!machine_name_is_valid(de->d_name))
165 continue;
166
167 k = manager_add_machine(m, de->d_name, &machine);
168 if (k < 0) {
169 r = log_error_errno(k, "Failed to add machine by file name %s: %m", de->d_name);
170 continue;
171 }
172
173 machine_add_to_gc_queue(machine);
174
175 k = machine_load(machine);
176 if (k < 0)
177 r = k;
178 }
179
180 return r;
181 }
182
183 static int manager_connect_bus(Manager *m) {
184 int r;
185
186 assert(m);
187 assert(!m->bus);
188
189 r = sd_bus_default_system(&m->bus);
190 if (r < 0)
191 return log_error_errno(r, "Failed to connect to system bus: %m");
192
193 r = sd_bus_add_object_vtable(m->bus, NULL, "/org/freedesktop/machine1", "org.freedesktop.machine1.Manager", manager_vtable, m);
194 if (r < 0)
195 return log_error_errno(r, "Failed to add manager object vtable: %m");
196
197 r = sd_bus_add_fallback_vtable(m->bus, NULL, "/org/freedesktop/machine1/machine", "org.freedesktop.machine1.Machine", machine_vtable, machine_object_find, m);
198 if (r < 0)
199 return log_error_errno(r, "Failed to add machine object vtable: %m");
200
201 r = sd_bus_add_node_enumerator(m->bus, NULL, "/org/freedesktop/machine1/machine", machine_node_enumerator, m);
202 if (r < 0)
203 return log_error_errno(r, "Failed to add machine enumerator: %m");
204
205 r = sd_bus_add_fallback_vtable(m->bus, NULL, "/org/freedesktop/machine1/image", "org.freedesktop.machine1.Image", image_vtable, image_object_find, m);
206 if (r < 0)
207 return log_error_errno(r, "Failed to add image object vtable: %m");
208
209 r = sd_bus_add_node_enumerator(m->bus, NULL, "/org/freedesktop/machine1/image", image_node_enumerator, m);
210 if (r < 0)
211 return log_error_errno(r, "Failed to add image enumerator: %m");
212
213 r = sd_bus_match_signal_async(
214 m->bus,
215 NULL,
216 "org.freedesktop.systemd1",
217 "/org/freedesktop/systemd1",
218 "org.freedesktop.systemd1.Manager",
219 "JobRemoved",
220 match_job_removed, NULL, m);
221 if (r < 0)
222 return log_error_errno(r, "Failed to add match for JobRemoved: %m");
223
224 r = sd_bus_match_signal_async(
225 m->bus,
226 NULL,
227 "org.freedesktop.systemd1",
228 "/org/freedesktop/systemd1",
229 "org.freedesktop.systemd1.Manager",
230 "UnitRemoved",
231 match_unit_removed, NULL, m);
232 if (r < 0)
233 return log_error_errno(r, "Failed to request match for UnitRemoved: %m");
234
235 r = sd_bus_match_signal_async(
236 m->bus,
237 NULL,
238 "org.freedesktop.systemd1",
239 NULL,
240 "org.freedesktop.DBus.Properties",
241 "PropertiesChanged",
242 match_properties_changed, NULL, m);
243 if (r < 0)
244 return log_error_errno(r, "Failed to request match for PropertiesChanged: %m");
245
246 r = sd_bus_match_signal_async(
247 m->bus,
248 NULL,
249 "org.freedesktop.systemd1",
250 "/org/freedesktop/systemd1",
251 "org.freedesktop.systemd1.Manager",
252 "Reloading",
253 match_reloading, NULL, m);
254 if (r < 0)
255 return log_error_errno(r, "Failed to request match for Reloading: %m");
256
257 r = sd_bus_call_method_async(
258 m->bus,
259 NULL,
260 "org.freedesktop.systemd1",
261 "/org/freedesktop/systemd1",
262 "org.freedesktop.systemd1.Manager",
263 "Subscribe",
264 NULL, NULL,
265 NULL);
266 if (r < 0)
267 return log_error_errno(r, "Failed to enable subscription: %m");
268
269 r = sd_bus_request_name_async(m->bus, NULL, "org.freedesktop.machine1", 0, NULL, NULL);
270 if (r < 0)
271 return log_error_errno(r, "Failed to request name: %m");
272
273 r = sd_bus_attach_event(m->bus, m->event, 0);
274 if (r < 0)
275 return log_error_errno(r, "Failed to attach bus to event loop: %m");
276
277 return 0;
278 }
279
280 static void manager_gc(Manager *m, bool drop_not_started) {
281 Machine *machine;
282
283 assert(m);
284
285 while ((machine = m->machine_gc_queue)) {
286 LIST_REMOVE(gc_queue, m->machine_gc_queue, machine);
287 machine->in_gc_queue = false;
288
289 /* First, if we are not closing yet, initiate stopping */
290 if (machine_may_gc(machine, drop_not_started) &&
291 machine_get_state(machine) != MACHINE_CLOSING)
292 machine_stop(machine);
293
294 /* Now, the stop probably made this referenced
295 * again, but if it didn't, then it's time to let it
296 * go entirely. */
297 if (machine_may_gc(machine, drop_not_started)) {
298 machine_finalize(machine);
299 machine_free(machine);
300 }
301 }
302 }
303
304 static int manager_startup(Manager *m) {
305 Machine *machine;
306 Iterator i;
307 int r;
308
309 assert(m);
310
311 /* Connect to the bus */
312 r = manager_connect_bus(m);
313 if (r < 0)
314 return r;
315
316 /* Deserialize state */
317 manager_enumerate_machines(m);
318
319 /* Remove stale objects before we start them */
320 manager_gc(m, false);
321
322 /* And start everything */
323 HASHMAP_FOREACH(machine, m->machines, i)
324 machine_start(machine, NULL, NULL);
325
326 return 0;
327 }
328
329 static bool check_idle(void *userdata) {
330 Manager *m = userdata;
331
332 if (m->operations)
333 return false;
334
335 manager_gc(m, true);
336
337 return hashmap_isempty(m->machines);
338 }
339
340 static int manager_run(Manager *m) {
341 assert(m);
342
343 return bus_event_loop_with_idle(
344 m->event,
345 m->bus,
346 "org.freedesktop.machine1",
347 DEFAULT_EXIT_USEC,
348 check_idle, m);
349 }
350
351 int main(int argc, char *argv[]) {
352 _cleanup_(manager_unrefp) Manager *m = NULL;
353 int r;
354
355 log_set_target(LOG_TARGET_AUTO);
356 log_set_facility(LOG_AUTH);
357 log_parse_environment();
358 log_open();
359
360 umask(0022);
361
362 if (argc != 1) {
363 log_error("This program takes no arguments.");
364 r = -EINVAL;
365 goto finish;
366 }
367
368 /* Always create the directories people can create inotify watches in. Note that some applications might check
369 * for the existence of /run/systemd/machines/ to determine whether machined is available, so please always
370 * make sure this check stays in. */
371 (void) mkdir_label("/run/systemd/machines", 0755);
372
373 assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD, SIGTERM, SIGINT, -1) >= 0);
374
375 r = manager_new(&m);
376 if (r < 0) {
377 log_error_errno(r, "Failed to allocate manager object: %m");
378 goto finish;
379 }
380
381 r = manager_startup(m);
382 if (r < 0) {
383 log_error_errno(r, "Failed to fully start up daemon: %m");
384 goto finish;
385 }
386
387 log_debug("systemd-machined running as pid "PID_FMT, getpid_cached());
388
389 (void) sd_notify(false,
390 "READY=1\n"
391 "STATUS=Processing requests...");
392
393 r = manager_run(m);
394
395 log_debug("systemd-machined stopped as pid "PID_FMT, getpid_cached());
396
397 (void) sd_notify(false,
398 "STOPPING=1\n"
399 "STATUS=Shutting down...");
400
401 finish:
402 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
403 }