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