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