]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/machine/machined.c
Merge pull request #11031 from poettering/gcc-attr-cleanup
[thirdparty/systemd.git] / src / machine / machined.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2
3 #include <errno.h>
4 #include <string.h>
5 #include <unistd.h>
6
7 #include "sd-daemon.h"
8
9 #include "alloc-util.h"
10 #include "bus-error.h"
11 #include "bus-util.h"
12 #include "cgroup-util.h"
13 #include "dirent-util.h"
14 #include "fd-util.h"
15 #include "format-util.h"
16 #include "hostname-util.h"
17 #include "label.h"
18 #include "machine-image.h"
19 #include "machined.h"
20 #include "main-func.h"
21 #include "process-util.h"
22 #include "signal-util.h"
23 #include "special.h"
24
25 static Manager* manager_unref(Manager *m);
26 DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_unref);
27
28 DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(machine_hash_ops, void, trivial_hash_func, trivial_compare_func,
29 Machine, machine_free);
30
31 static int manager_new(Manager **ret) {
32 _cleanup_(manager_unrefp) Manager *m = NULL;
33 int r;
34
35 assert(ret);
36
37 m = new0(Manager, 1);
38 if (!m)
39 return -ENOMEM;
40
41 m->machines = hashmap_new(&string_hash_ops);
42 m->machine_units = hashmap_new(&string_hash_ops);
43 m->machine_leaders = hashmap_new(&machine_hash_ops);
44
45 if (!m->machines || !m->machine_units || !m->machine_leaders)
46 return -ENOMEM;
47
48 r = sd_event_default(&m->event);
49 if (r < 0)
50 return r;
51
52 r = sd_event_add_signal(m->event, NULL, SIGINT, NULL, NULL);
53 if (r < 0)
54 return r;
55
56 r = sd_event_add_signal(m->event, NULL, SIGTERM, NULL, NULL);
57 if (r < 0)
58 return r;
59
60 (void) sd_event_set_watchdog(m->event, true);
61
62 *ret = TAKE_PTR(m);
63 return 0;
64 }
65
66 static Manager* manager_unref(Manager *m) {
67 if (!m)
68 return NULL;
69
70 while (m->operations)
71 operation_free(m->operations);
72
73 assert(m->n_operations == 0);
74
75 hashmap_free(m->machines);
76 hashmap_free(m->machine_units);
77 hashmap_free(m->machine_leaders);
78 hashmap_free(m->image_cache);
79
80 sd_event_source_unref(m->image_cache_defer_event);
81
82 bus_verify_polkit_async_registry_free(m->polkit_registry);
83
84 sd_bus_unref(m->bus);
85 sd_event_unref(m->event);
86
87 return mfree(m);
88 }
89
90 static int manager_add_host_machine(Manager *m) {
91 _cleanup_free_ char *rd = NULL, *unit = NULL;
92 sd_id128_t mid;
93 Machine *t;
94 int r;
95
96 if (m->host_machine)
97 return 0;
98
99 r = sd_id128_get_machine(&mid);
100 if (r < 0)
101 return log_error_errno(r, "Failed to get machine ID: %m");
102
103 rd = strdup("/");
104 if (!rd)
105 return log_oom();
106
107 unit = strdup(SPECIAL_ROOT_SLICE);
108 if (!unit)
109 return log_oom();
110
111 t = machine_new(m, MACHINE_HOST, ".host");
112 if (!t)
113 return log_oom();
114
115 t->leader = 1;
116 t->id = mid;
117
118 t->root_directory = TAKE_PTR(rd);
119 t->unit = TAKE_PTR(unit);
120
121 dual_timestamp_from_boottime_or_monotonic(&t->timestamp, 0);
122
123 m->host_machine = t;
124
125 return 0;
126 }
127
128 static int manager_enumerate_machines(Manager *m) {
129 _cleanup_closedir_ DIR *d = NULL;
130 struct dirent *de;
131 int r = 0;
132
133 assert(m);
134
135 r = manager_add_host_machine(m);
136 if (r < 0)
137 return r;
138
139 /* Read in machine data stored on disk */
140 d = opendir("/run/systemd/machines");
141 if (!d) {
142 if (errno == ENOENT)
143 return 0;
144
145 return log_error_errno(errno, "Failed to open /run/systemd/machines: %m");
146 }
147
148 FOREACH_DIRENT(de, d, return -errno) {
149 struct Machine *machine;
150 int k;
151
152 if (!dirent_is_file(de))
153 continue;
154
155 /* Ignore symlinks that map the unit name to the machine */
156 if (startswith(de->d_name, "unit:"))
157 continue;
158
159 if (!machine_name_is_valid(de->d_name))
160 continue;
161
162 k = manager_add_machine(m, de->d_name, &machine);
163 if (k < 0) {
164 r = log_error_errno(k, "Failed to add machine by file name %s: %m", de->d_name);
165 continue;
166 }
167
168 machine_add_to_gc_queue(machine);
169
170 k = machine_load(machine);
171 if (k < 0)
172 r = k;
173 }
174
175 return r;
176 }
177
178 static int manager_connect_bus(Manager *m) {
179 int r;
180
181 assert(m);
182 assert(!m->bus);
183
184 r = sd_bus_default_system(&m->bus);
185 if (r < 0)
186 return log_error_errno(r, "Failed to connect to system bus: %m");
187
188 r = sd_bus_add_object_vtable(m->bus, NULL, "/org/freedesktop/machine1", "org.freedesktop.machine1.Manager", manager_vtable, m);
189 if (r < 0)
190 return log_error_errno(r, "Failed to add manager object vtable: %m");
191
192 r = sd_bus_add_fallback_vtable(m->bus, NULL, "/org/freedesktop/machine1/machine", "org.freedesktop.machine1.Machine", machine_vtable, machine_object_find, m);
193 if (r < 0)
194 return log_error_errno(r, "Failed to add machine object vtable: %m");
195
196 r = sd_bus_add_node_enumerator(m->bus, NULL, "/org/freedesktop/machine1/machine", machine_node_enumerator, m);
197 if (r < 0)
198 return log_error_errno(r, "Failed to add machine enumerator: %m");
199
200 r = sd_bus_add_fallback_vtable(m->bus, NULL, "/org/freedesktop/machine1/image", "org.freedesktop.machine1.Image", image_vtable, image_object_find, m);
201 if (r < 0)
202 return log_error_errno(r, "Failed to add image object vtable: %m");
203
204 r = sd_bus_add_node_enumerator(m->bus, NULL, "/org/freedesktop/machine1/image", image_node_enumerator, m);
205 if (r < 0)
206 return log_error_errno(r, "Failed to add image enumerator: %m");
207
208 r = sd_bus_match_signal_async(
209 m->bus,
210 NULL,
211 "org.freedesktop.systemd1",
212 "/org/freedesktop/systemd1",
213 "org.freedesktop.systemd1.Manager",
214 "JobRemoved",
215 match_job_removed, NULL, m);
216 if (r < 0)
217 return log_error_errno(r, "Failed to add match for JobRemoved: %m");
218
219 r = sd_bus_match_signal_async(
220 m->bus,
221 NULL,
222 "org.freedesktop.systemd1",
223 "/org/freedesktop/systemd1",
224 "org.freedesktop.systemd1.Manager",
225 "UnitRemoved",
226 match_unit_removed, NULL, m);
227 if (r < 0)
228 return log_error_errno(r, "Failed to request match for UnitRemoved: %m");
229
230 r = sd_bus_match_signal_async(
231 m->bus,
232 NULL,
233 "org.freedesktop.systemd1",
234 NULL,
235 "org.freedesktop.DBus.Properties",
236 "PropertiesChanged",
237 match_properties_changed, NULL, m);
238 if (r < 0)
239 return log_error_errno(r, "Failed to request match for PropertiesChanged: %m");
240
241 r = sd_bus_match_signal_async(
242 m->bus,
243 NULL,
244 "org.freedesktop.systemd1",
245 "/org/freedesktop/systemd1",
246 "org.freedesktop.systemd1.Manager",
247 "Reloading",
248 match_reloading, NULL, m);
249 if (r < 0)
250 return log_error_errno(r, "Failed to request match for Reloading: %m");
251
252 r = sd_bus_call_method_async(
253 m->bus,
254 NULL,
255 "org.freedesktop.systemd1",
256 "/org/freedesktop/systemd1",
257 "org.freedesktop.systemd1.Manager",
258 "Subscribe",
259 NULL, NULL,
260 NULL);
261 if (r < 0)
262 return log_error_errno(r, "Failed to enable subscription: %m");
263
264 r = sd_bus_request_name_async(m->bus, NULL, "org.freedesktop.machine1", 0, NULL, NULL);
265 if (r < 0)
266 return log_error_errno(r, "Failed to request name: %m");
267
268 r = sd_bus_attach_event(m->bus, m->event, 0);
269 if (r < 0)
270 return log_error_errno(r, "Failed to attach bus to event loop: %m");
271
272 return 0;
273 }
274
275 static void manager_gc(Manager *m, bool drop_not_started) {
276 Machine *machine;
277
278 assert(m);
279
280 while ((machine = m->machine_gc_queue)) {
281 LIST_REMOVE(gc_queue, m->machine_gc_queue, machine);
282 machine->in_gc_queue = false;
283
284 /* First, if we are not closing yet, initiate stopping */
285 if (machine_may_gc(machine, drop_not_started) &&
286 machine_get_state(machine) != MACHINE_CLOSING)
287 machine_stop(machine);
288
289 /* Now, the stop probably made this referenced
290 * again, but if it didn't, then it's time to let it
291 * go entirely. */
292 if (machine_may_gc(machine, drop_not_started)) {
293 machine_finalize(machine);
294 machine_free(machine);
295 }
296 }
297 }
298
299 static int manager_startup(Manager *m) {
300 Machine *machine;
301 Iterator i;
302 int r;
303
304 assert(m);
305
306 /* Connect to the bus */
307 r = manager_connect_bus(m);
308 if (r < 0)
309 return r;
310
311 /* Deserialize state */
312 manager_enumerate_machines(m);
313
314 /* Remove stale objects before we start them */
315 manager_gc(m, false);
316
317 /* And start everything */
318 HASHMAP_FOREACH(machine, m->machines, i)
319 machine_start(machine, NULL, NULL);
320
321 return 0;
322 }
323
324 static bool check_idle(void *userdata) {
325 Manager *m = userdata;
326
327 if (m->operations)
328 return false;
329
330 manager_gc(m, true);
331
332 return hashmap_isempty(m->machines);
333 }
334
335 static int manager_run(Manager *m) {
336 assert(m);
337
338 return bus_event_loop_with_idle(
339 m->event,
340 m->bus,
341 "org.freedesktop.machine1",
342 DEFAULT_EXIT_USEC,
343 check_idle, m);
344 }
345
346 static int run(int argc, char *argv[]) {
347 _cleanup_(manager_unrefp) Manager *m = NULL;
348 int r;
349
350 log_set_facility(LOG_AUTH);
351 log_setup_service();
352
353 umask(0022);
354
355 if (argc != 1) {
356 log_error("This program takes no arguments.");
357 return -EINVAL;
358 }
359
360 /* Always create the directories people can create inotify watches in. Note that some applications might check
361 * for the existence of /run/systemd/machines/ to determine whether machined is available, so please always
362 * make sure this check stays in. */
363 (void) mkdir_label("/run/systemd/machines", 0755);
364
365 assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD, SIGTERM, SIGINT, -1) >= 0);
366
367 r = manager_new(&m);
368 if (r < 0)
369 return log_error_errno(r, "Failed to allocate manager object: %m");
370
371 r = manager_startup(m);
372 if (r < 0)
373 return log_error_errno(r, "Failed to fully start up daemon: %m");
374
375 log_debug("systemd-machined running as pid "PID_FMT, getpid_cached());
376 (void) sd_notify(false,
377 "READY=1\n"
378 "STATUS=Processing requests...");
379
380 r = manager_run(m);
381
382 log_debug("systemd-machined stopped as pid "PID_FMT, getpid_cached());
383 (void) sd_notify(false,
384 "STOPPING=1\n"
385 "STATUS=Shutting down...");
386
387 return r;
388 }
389
390 DEFINE_MAIN_FUNCTION(run);