]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/machine/machined.c
Merge pull request #7476 from jhxie/ycm-meson-backend
[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
72 assert(m);
73
74 while (m->operations)
75 operation_free(m->operations);
76
77 assert(m->n_operations == 0);
78
79 while ((machine = hashmap_first(m->machines)))
80 machine_free(machine);
81
82 hashmap_free(m->machines);
83 hashmap_free(m->machine_units);
84 hashmap_free(m->machine_leaders);
85
86 hashmap_free_with_destructor(m->image_cache, image_unref);
87
88 sd_event_source_unref(m->image_cache_defer_event);
89
90 bus_verify_polkit_async_registry_free(m->polkit_registry);
91
92 sd_bus_unref(m->bus);
93 sd_event_unref(m->event);
94
95 free(m);
96 }
97
98 static int manager_add_host_machine(Manager *m) {
99 _cleanup_free_ char *rd = NULL, *unit = NULL;
100 sd_id128_t mid;
101 Machine *t;
102 int r;
103
104 if (m->host_machine)
105 return 0;
106
107 r = sd_id128_get_machine(&mid);
108 if (r < 0)
109 return log_error_errno(r, "Failed to get machine ID: %m");
110
111 rd = strdup("/");
112 if (!rd)
113 return log_oom();
114
115 unit = strdup("-.slice");
116 if (!unit)
117 return log_oom();
118
119 t = machine_new(m, MACHINE_HOST, ".host");
120 if (!t)
121 return log_oom();
122
123 t->leader = 1;
124 t->id = mid;
125
126 t->root_directory = rd;
127 t->unit = unit;
128 rd = unit = NULL;
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 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
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_add_match(m->bus,
219 NULL,
220 "type='signal',"
221 "sender='org.freedesktop.systemd1',"
222 "interface='org.freedesktop.systemd1.Manager',"
223 "member='JobRemoved',"
224 "path='/org/freedesktop/systemd1'",
225 match_job_removed,
226 m);
227 if (r < 0)
228 return log_error_errno(r, "Failed to add match for JobRemoved: %m");
229
230 r = sd_bus_add_match(m->bus,
231 NULL,
232 "type='signal',"
233 "sender='org.freedesktop.systemd1',"
234 "interface='org.freedesktop.systemd1.Manager',"
235 "member='UnitRemoved',"
236 "path='/org/freedesktop/systemd1'",
237 match_unit_removed,
238 m);
239 if (r < 0)
240 return log_error_errno(r, "Failed to add match for UnitRemoved: %m");
241
242 r = sd_bus_add_match(m->bus,
243 NULL,
244 "type='signal',"
245 "sender='org.freedesktop.systemd1',"
246 "interface='org.freedesktop.DBus.Properties',"
247 "member='PropertiesChanged',"
248 "arg0='org.freedesktop.systemd1.Unit'",
249 match_properties_changed,
250 m);
251 if (r < 0)
252 return log_error_errno(r, "Failed to add match for PropertiesChanged: %m");
253
254 r = sd_bus_add_match(m->bus,
255 NULL,
256 "type='signal',"
257 "sender='org.freedesktop.systemd1',"
258 "interface='org.freedesktop.systemd1.Manager',"
259 "member='Reloading',"
260 "path='/org/freedesktop/systemd1'",
261 match_reloading,
262 m);
263 if (r < 0)
264 return log_error_errno(r, "Failed to add match for Reloading: %m");
265
266 r = sd_bus_call_method(
267 m->bus,
268 "org.freedesktop.systemd1",
269 "/org/freedesktop/systemd1",
270 "org.freedesktop.systemd1.Manager",
271 "Subscribe",
272 &error,
273 NULL, NULL);
274 if (r < 0) {
275 log_error("Failed to enable subscription: %s", bus_error_message(&error, r));
276 return r;
277 }
278
279 r = sd_bus_request_name(m->bus, "org.freedesktop.machine1", 0);
280 if (r < 0)
281 return log_error_errno(r, "Failed to register name: %m");
282
283 r = sd_bus_attach_event(m->bus, m->event, 0);
284 if (r < 0)
285 return log_error_errno(r, "Failed to attach bus to event loop: %m");
286
287 return 0;
288 }
289
290 void manager_gc(Manager *m, bool drop_not_started) {
291 Machine *machine;
292
293 assert(m);
294
295 while ((machine = m->machine_gc_queue)) {
296 LIST_REMOVE(gc_queue, m->machine_gc_queue, machine);
297 machine->in_gc_queue = false;
298
299 /* First, if we are not closing yet, initiate stopping */
300 if (!machine_check_gc(machine, drop_not_started) &&
301 machine_get_state(machine) != MACHINE_CLOSING)
302 machine_stop(machine);
303
304 /* Now, the stop probably made this referenced
305 * again, but if it didn't, then it's time to let it
306 * go entirely. */
307 if (!machine_check_gc(machine, drop_not_started)) {
308 machine_finalize(machine);
309 machine_free(machine);
310 }
311 }
312 }
313
314 int manager_startup(Manager *m) {
315 Machine *machine;
316 Iterator i;
317 int r;
318
319 assert(m);
320
321 /* Connect to the bus */
322 r = manager_connect_bus(m);
323 if (r < 0)
324 return r;
325
326 /* Deserialize state */
327 manager_enumerate_machines(m);
328
329 /* Remove stale objects before we start them */
330 manager_gc(m, false);
331
332 /* And start everything */
333 HASHMAP_FOREACH(machine, m->machines, i)
334 machine_start(machine, NULL, NULL);
335
336 return 0;
337 }
338
339 static bool check_idle(void *userdata) {
340 Manager *m = userdata;
341
342 if (m->operations)
343 return false;
344
345 manager_gc(m, true);
346
347 return hashmap_isempty(m->machines);
348 }
349
350 int manager_run(Manager *m) {
351 assert(m);
352
353 return bus_event_loop_with_idle(
354 m->event,
355 m->bus,
356 "org.freedesktop.machine1",
357 DEFAULT_EXIT_USEC,
358 check_idle, m);
359 }
360
361 int main(int argc, char *argv[]) {
362 Manager *m = NULL;
363 int r;
364
365 log_set_target(LOG_TARGET_AUTO);
366 log_set_facility(LOG_AUTH);
367 log_parse_environment();
368 log_open();
369
370 umask(0022);
371
372 if (argc != 1) {
373 log_error("This program takes no arguments.");
374 r = -EINVAL;
375 goto finish;
376 }
377
378 /* Always create the directories people can create inotify
379 * watches in. Note that some applications might check for the
380 * existence of /run/systemd/machines/ to determine whether
381 * machined is available, so please always make sure this
382 * check stays in. */
383 mkdir_label("/run/systemd/machines", 0755);
384
385 assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD, -1) >= 0);
386
387 m = manager_new();
388 if (!m) {
389 r = log_oom();
390 goto finish;
391 }
392
393 r = manager_startup(m);
394 if (r < 0) {
395 log_error_errno(r, "Failed to fully start up daemon: %m");
396 goto finish;
397 }
398
399 log_debug("systemd-machined running as pid "PID_FMT, getpid_cached());
400
401 sd_notify(false,
402 "READY=1\n"
403 "STATUS=Processing requests...");
404
405 r = manager_run(m);
406
407 log_debug("systemd-machined stopped as pid "PID_FMT, getpid_cached());
408
409 finish:
410 manager_free(m);
411
412 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
413 }