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