]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/machine/machined.c
Merge pull request #7661 from keszybz/slice-cleanups
[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 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
190 int r;
191
192 assert(m);
193 assert(!m->bus);
194
195 r = sd_bus_default_system(&m->bus);
196 if (r < 0)
197 return log_error_errno(r, "Failed to connect to system bus: %m");
198
199 r = sd_bus_add_object_vtable(m->bus, NULL, "/org/freedesktop/machine1", "org.freedesktop.machine1.Manager", manager_vtable, m);
200 if (r < 0)
201 return log_error_errno(r, "Failed to add manager object vtable: %m");
202
203 r = sd_bus_add_fallback_vtable(m->bus, NULL, "/org/freedesktop/machine1/machine", "org.freedesktop.machine1.Machine", machine_vtable, machine_object_find, m);
204 if (r < 0)
205 return log_error_errno(r, "Failed to add machine object vtable: %m");
206
207 r = sd_bus_add_node_enumerator(m->bus, NULL, "/org/freedesktop/machine1/machine", machine_node_enumerator, m);
208 if (r < 0)
209 return log_error_errno(r, "Failed to add machine enumerator: %m");
210
211 r = sd_bus_add_fallback_vtable(m->bus, NULL, "/org/freedesktop/machine1/image", "org.freedesktop.machine1.Image", image_vtable, image_object_find, m);
212 if (r < 0)
213 return log_error_errno(r, "Failed to add image object vtable: %m");
214
215 r = sd_bus_add_node_enumerator(m->bus, NULL, "/org/freedesktop/machine1/image", image_node_enumerator, m);
216 if (r < 0)
217 return log_error_errno(r, "Failed to add image enumerator: %m");
218
219 r = sd_bus_add_match(m->bus,
220 NULL,
221 "type='signal',"
222 "sender='org.freedesktop.systemd1',"
223 "interface='org.freedesktop.systemd1.Manager',"
224 "member='JobRemoved',"
225 "path='/org/freedesktop/systemd1'",
226 match_job_removed,
227 m);
228 if (r < 0)
229 return log_error_errno(r, "Failed to add match for JobRemoved: %m");
230
231 r = sd_bus_add_match(m->bus,
232 NULL,
233 "type='signal',"
234 "sender='org.freedesktop.systemd1',"
235 "interface='org.freedesktop.systemd1.Manager',"
236 "member='UnitRemoved',"
237 "path='/org/freedesktop/systemd1'",
238 match_unit_removed,
239 m);
240 if (r < 0)
241 return log_error_errno(r, "Failed to add match for UnitRemoved: %m");
242
243 r = sd_bus_add_match(m->bus,
244 NULL,
245 "type='signal',"
246 "sender='org.freedesktop.systemd1',"
247 "interface='org.freedesktop.DBus.Properties',"
248 "member='PropertiesChanged',"
249 "arg0='org.freedesktop.systemd1.Unit'",
250 match_properties_changed,
251 m);
252 if (r < 0)
253 return log_error_errno(r, "Failed to add match for PropertiesChanged: %m");
254
255 r = sd_bus_add_match(m->bus,
256 NULL,
257 "type='signal',"
258 "sender='org.freedesktop.systemd1',"
259 "interface='org.freedesktop.systemd1.Manager',"
260 "member='Reloading',"
261 "path='/org/freedesktop/systemd1'",
262 match_reloading,
263 m);
264 if (r < 0)
265 return log_error_errno(r, "Failed to add match for Reloading: %m");
266
267 r = sd_bus_call_method(
268 m->bus,
269 "org.freedesktop.systemd1",
270 "/org/freedesktop/systemd1",
271 "org.freedesktop.systemd1.Manager",
272 "Subscribe",
273 &error,
274 NULL, NULL);
275 if (r < 0) {
276 log_error("Failed to enable subscription: %s", bus_error_message(&error, r));
277 return r;
278 }
279
280 r = sd_bus_request_name(m->bus, "org.freedesktop.machine1", 0);
281 if (r < 0)
282 return log_error_errno(r, "Failed to register name: %m");
283
284 r = sd_bus_attach_event(m->bus, m->event, 0);
285 if (r < 0)
286 return log_error_errno(r, "Failed to attach bus to event loop: %m");
287
288 return 0;
289 }
290
291 void manager_gc(Manager *m, bool drop_not_started) {
292 Machine *machine;
293
294 assert(m);
295
296 while ((machine = m->machine_gc_queue)) {
297 LIST_REMOVE(gc_queue, m->machine_gc_queue, machine);
298 machine->in_gc_queue = false;
299
300 /* First, if we are not closing yet, initiate stopping */
301 if (!machine_check_gc(machine, drop_not_started) &&
302 machine_get_state(machine) != MACHINE_CLOSING)
303 machine_stop(machine);
304
305 /* Now, the stop probably made this referenced
306 * again, but if it didn't, then it's time to let it
307 * go entirely. */
308 if (!machine_check_gc(machine, drop_not_started)) {
309 machine_finalize(machine);
310 machine_free(machine);
311 }
312 }
313 }
314
315 int manager_startup(Manager *m) {
316 Machine *machine;
317 Iterator i;
318 int r;
319
320 assert(m);
321
322 /* Connect to the bus */
323 r = manager_connect_bus(m);
324 if (r < 0)
325 return r;
326
327 /* Deserialize state */
328 manager_enumerate_machines(m);
329
330 /* Remove stale objects before we start them */
331 manager_gc(m, false);
332
333 /* And start everything */
334 HASHMAP_FOREACH(machine, m->machines, i)
335 machine_start(machine, NULL, NULL);
336
337 return 0;
338 }
339
340 static bool check_idle(void *userdata) {
341 Manager *m = userdata;
342
343 if (m->operations)
344 return false;
345
346 manager_gc(m, true);
347
348 return hashmap_isempty(m->machines);
349 }
350
351 int manager_run(Manager *m) {
352 assert(m);
353
354 return bus_event_loop_with_idle(
355 m->event,
356 m->bus,
357 "org.freedesktop.machine1",
358 DEFAULT_EXIT_USEC,
359 check_idle, m);
360 }
361
362 int main(int argc, char *argv[]) {
363 Manager *m = NULL;
364 int r;
365
366 log_set_target(LOG_TARGET_AUTO);
367 log_set_facility(LOG_AUTH);
368 log_parse_environment();
369 log_open();
370
371 umask(0022);
372
373 if (argc != 1) {
374 log_error("This program takes no arguments.");
375 r = -EINVAL;
376 goto finish;
377 }
378
379 /* Always create the directories people can create inotify
380 * watches in. Note that some applications might check for the
381 * existence of /run/systemd/machines/ to determine whether
382 * machined is available, so please always make sure this
383 * check stays in. */
384 mkdir_label("/run/systemd/machines", 0755);
385
386 assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD, -1) >= 0);
387
388 m = manager_new();
389 if (!m) {
390 r = log_oom();
391 goto finish;
392 }
393
394 r = manager_startup(m);
395 if (r < 0) {
396 log_error_errno(r, "Failed to fully start up daemon: %m");
397 goto finish;
398 }
399
400 log_debug("systemd-machined running as pid "PID_FMT, getpid_cached());
401
402 sd_notify(false,
403 "READY=1\n"
404 "STATUS=Processing requests...");
405
406 r = manager_run(m);
407
408 log_debug("systemd-machined stopped as pid "PID_FMT, getpid_cached());
409
410 finish:
411 manager_free(m);
412
413 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
414 }