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