]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/machine/machined.c
man/systemd-sysext.xml: document mutable extensions
[thirdparty/systemd.git] / src / machine / machined.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
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 "alloc-util.h"
10 #include "bus-error.h"
11 #include "bus-locator.h"
12 #include "bus-log-control-api.h"
13 #include "bus-polkit.h"
14 #include "cgroup-util.h"
15 #include "common-signal.h"
16 #include "daemon-util.h"
17 #include "dirent-util.h"
18 #include "discover-image.h"
19 #include "fd-util.h"
20 #include "format-util.h"
21 #include "hostname-util.h"
22 #include "machined-varlink.h"
23 #include "machined.h"
24 #include "main-func.h"
25 #include "mkdir-label.h"
26 #include "process-util.h"
27 #include "service-util.h"
28 #include "signal-util.h"
29 #include "special.h"
30
31 static Manager* manager_unref(Manager *m);
32 DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_unref);
33
34 DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(machine_hash_ops, char, string_hash_func, string_compare_func, Machine, machine_free);
35
36 static int manager_new(Manager **ret) {
37 _cleanup_(manager_unrefp) Manager *m = NULL;
38 int r;
39
40 assert(ret);
41
42 m = new0(Manager, 1);
43 if (!m)
44 return -ENOMEM;
45
46 m->machines = hashmap_new(&machine_hash_ops);
47 m->machine_units = hashmap_new(&string_hash_ops);
48 m->machine_leaders = hashmap_new(NULL);
49
50 if (!m->machines || !m->machine_units || !m->machine_leaders)
51 return -ENOMEM;
52
53 r = sd_event_default(&m->event);
54 if (r < 0)
55 return r;
56
57 r = sd_event_add_signal(m->event, NULL, SIGINT, NULL, NULL);
58 if (r < 0)
59 return r;
60
61 r = sd_event_add_signal(m->event, NULL, SIGTERM, NULL, NULL);
62 if (r < 0)
63 return r;
64
65 r = sd_event_add_signal(m->event, NULL, SIGRTMIN+18, sigrtmin18_handler, NULL);
66 if (r < 0)
67 return r;
68
69 r = sd_event_add_memory_pressure(m->event, NULL, NULL, NULL);
70 if (r < 0)
71 log_full_errno(ERRNO_IS_NOT_SUPPORTED(r) || ERRNO_IS_PRIVILEGE(r) || r == -EHOSTDOWN ? LOG_DEBUG : LOG_NOTICE, r,
72 "Unable to create memory pressure event source, ignoring: %m");
73
74 (void) sd_event_set_watchdog(m->event, true);
75
76 *ret = TAKE_PTR(m);
77 return 0;
78 }
79
80 static Manager* manager_unref(Manager *m) {
81 if (!m)
82 return NULL;
83
84 while (m->operations)
85 operation_free(m->operations);
86
87 assert(m->n_operations == 0);
88
89 hashmap_free(m->machines); /* This will free all machines, so that the machine_units/machine_leaders is empty */
90 hashmap_free(m->machine_units);
91 hashmap_free(m->machine_leaders);
92 hashmap_free(m->image_cache);
93
94 sd_event_source_unref(m->image_cache_defer_event);
95 #if ENABLE_NSCD
96 sd_event_source_unref(m->nscd_cache_flush_event);
97 #endif
98
99 hashmap_free(m->polkit_registry);
100
101 manager_varlink_done(m);
102
103 sd_bus_flush_close_unref(m->bus);
104 sd_event_unref(m->event);
105
106 return mfree(m);
107 }
108
109 static int manager_add_host_machine(Manager *m) {
110 _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
111 _cleanup_free_ char *rd = NULL, *unit = NULL;
112 sd_id128_t mid;
113 Machine *t;
114 int r;
115
116 if (m->host_machine)
117 return 0;
118
119 r = sd_id128_get_machine(&mid);
120 if (r < 0)
121 return log_error_errno(r, "Failed to get machine ID: %m");
122
123 rd = strdup("/");
124 if (!rd)
125 return log_oom();
126
127 unit = strdup(SPECIAL_ROOT_SLICE);
128 if (!unit)
129 return log_oom();
130
131 r = pidref_set_pid(&pidref, 1);
132 if (r < 0)
133 return log_error_errno(r, "Failed to open reference to PID 1: %m");
134
135 r = machine_new(m, MACHINE_HOST, ".host", &t);
136 if (r < 0)
137 return log_error_errno(r, "Failed to create machine: %m");
138
139 t->leader = TAKE_PIDREF(pidref);
140 t->id = mid;
141
142 t->root_directory = TAKE_PTR(rd);
143 t->unit = TAKE_PTR(unit);
144
145 dual_timestamp_from_boottime(&t->timestamp, 0);
146
147 m->host_machine = t;
148
149 return 0;
150 }
151
152 static int manager_enumerate_machines(Manager *m) {
153 _cleanup_closedir_ DIR *d = NULL;
154 int r;
155
156 assert(m);
157
158 r = manager_add_host_machine(m);
159 if (r < 0)
160 return r;
161
162 /* Read in machine data stored on disk */
163 d = opendir("/run/systemd/machines");
164 if (!d) {
165 if (errno == ENOENT)
166 return 0;
167
168 return log_error_errno(errno, "Failed to open /run/systemd/machines: %m");
169 }
170
171 FOREACH_DIRENT(de, d, return -errno) {
172 struct Machine *machine;
173 int k;
174
175 if (!dirent_is_file(de))
176 continue;
177
178 /* Ignore symlinks that map the unit name to the machine */
179 if (startswith(de->d_name, "unit:"))
180 continue;
181
182 if (!hostname_is_valid(de->d_name, 0))
183 continue;
184
185 k = manager_add_machine(m, de->d_name, &machine);
186 if (k < 0) {
187 r = log_error_errno(k, "Failed to add machine by file name %s: %m", de->d_name);
188 continue;
189 }
190
191 machine_add_to_gc_queue(machine);
192
193 k = machine_load(machine);
194 if (k < 0)
195 r = k;
196 }
197
198 return r;
199 }
200
201 static int manager_connect_bus(Manager *m) {
202 int r;
203
204 assert(m);
205 assert(!m->bus);
206
207 r = sd_bus_default_system(&m->bus);
208 if (r < 0)
209 return log_error_errno(r, "Failed to connect to system bus: %m");
210
211 r = bus_add_implementation(m->bus, &manager_object, m);
212 if (r < 0)
213 return r;
214
215 r = bus_match_signal_async(m->bus, NULL, bus_systemd_mgr, "JobRemoved", 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 = bus_match_signal_async(m->bus, NULL, bus_systemd_mgr, "UnitRemoved", match_unit_removed, NULL, m);
220 if (r < 0)
221 return log_error_errno(r, "Failed to request match for UnitRemoved: %m");
222
223 r = sd_bus_match_signal_async(
224 m->bus,
225 NULL,
226 "org.freedesktop.systemd1",
227 NULL,
228 "org.freedesktop.DBus.Properties",
229 "PropertiesChanged",
230 match_properties_changed, NULL, m);
231 if (r < 0)
232 return log_error_errno(r, "Failed to request match for PropertiesChanged: %m");
233
234 r = bus_match_signal_async(m->bus, NULL, bus_systemd_mgr, "Reloading", match_reloading, NULL, m);
235 if (r < 0)
236 return log_error_errno(r, "Failed to request match for Reloading: %m");
237
238 r = bus_call_method_async(m->bus, NULL, bus_systemd_mgr, "Subscribe", NULL, NULL, NULL);
239 if (r < 0)
240 return log_error_errno(r, "Failed to enable subscription: %m");
241
242 r = bus_log_control_api_register(m->bus);
243 if (r < 0)
244 return r;
245
246 r = sd_bus_request_name_async(m->bus, NULL, "org.freedesktop.machine1", 0, NULL, NULL);
247 if (r < 0)
248 return log_error_errno(r, "Failed to request name: %m");
249
250 r = sd_bus_attach_event(m->bus, m->event, 0);
251 if (r < 0)
252 return log_error_errno(r, "Failed to attach bus to event loop: %m");
253
254 return 0;
255 }
256
257 static void manager_gc(Manager *m, bool drop_not_started) {
258 Machine *machine;
259
260 assert(m);
261
262 while ((machine = LIST_POP(gc_queue, m->machine_gc_queue))) {
263 machine->in_gc_queue = false;
264
265 /* First, if we are not closing yet, initiate stopping */
266 if (machine_may_gc(machine, drop_not_started) &&
267 machine_get_state(machine) != MACHINE_CLOSING)
268 machine_stop(machine);
269
270 /* Now, the stop probably made this referenced
271 * again, but if it didn't, then it's time to let it
272 * go entirely. */
273 if (machine_may_gc(machine, drop_not_started)) {
274 machine_finalize(machine);
275 machine_free(machine);
276 }
277 }
278 }
279
280 static int manager_startup(Manager *m) {
281 Machine *machine;
282 int r;
283
284 assert(m);
285
286 /* Connect to the bus */
287 r = manager_connect_bus(m);
288 if (r < 0)
289 return r;
290
291 /* Set up Varlink service */
292 r = manager_varlink_init(m);
293 if (r < 0)
294 return r;
295
296 /* Deserialize state */
297 manager_enumerate_machines(m);
298
299 /* Remove stale objects before we start them */
300 manager_gc(m, false);
301
302 /* And start everything */
303 HASHMAP_FOREACH(machine, m->machines)
304 machine_start(machine, NULL, NULL);
305
306 return 0;
307 }
308
309 static bool check_idle(void *userdata) {
310 Manager *m = userdata;
311
312 if (m->operations)
313 return false;
314
315 if (varlink_server_current_connections(m->varlink_server) > 0)
316 return false;
317
318 manager_gc(m, true);
319
320 return hashmap_isempty(m->machines);
321 }
322
323 static int run(int argc, char *argv[]) {
324 _cleanup_(manager_unrefp) Manager *m = NULL;
325 int r;
326
327 log_set_facility(LOG_AUTH);
328 log_setup();
329
330 r = service_parse_argv("systemd-machined.service",
331 "Manage registrations of local VMs and containers.",
332 BUS_IMPLEMENTATIONS(&manager_object,
333 &log_control_object),
334 argc, argv);
335 if (r <= 0)
336 return r;
337
338 umask(0022);
339
340 /* Always create the directories people can create inotify watches in. Note that some applications might check
341 * for the existence of /run/systemd/machines/ to determine whether machined is available, so please always
342 * make sure this check stays in. */
343 (void) mkdir_label("/run/systemd/machines", 0755);
344
345 assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD, SIGTERM, SIGINT, SIGRTMIN+18, -1) >= 0);
346
347 r = manager_new(&m);
348 if (r < 0)
349 return log_error_errno(r, "Failed to allocate manager object: %m");
350
351 r = manager_startup(m);
352 if (r < 0)
353 return log_error_errno(r, "Failed to fully start up daemon: %m");
354
355 r = sd_notify(false, NOTIFY_READY);
356 if (r < 0)
357 log_warning_errno(r, "Failed to send readiness notification, ignoring: %m");
358
359 r = bus_event_loop_with_idle(
360 m->event,
361 m->bus,
362 "org.freedesktop.machine1",
363 DEFAULT_EXIT_USEC,
364 check_idle, m);
365 if (r < 0)
366 return log_error_errno(r, "Failed to run main loop: %m");
367
368 return 0;
369 }
370
371 DEFINE_MAIN_FUNCTION(run);