]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/machine/machined.c
machined: split manager linking out of machine_new into machine_link
[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(MACHINE_HOST, ".host", &t);
136 if (r < 0)
137 return log_error_errno(r, "Failed to create machine: %m");
138
139 r = machine_link(m, t);
140 if (r < 0)
141 return log_error_errno(r, "Failed to link machine to manager: %m");
142
143 t->leader = TAKE_PIDREF(pidref);
144 t->id = mid;
145
146 t->root_directory = TAKE_PTR(rd);
147 t->unit = TAKE_PTR(unit);
148
149 dual_timestamp_from_boottime(&t->timestamp, 0);
150
151 m->host_machine = t;
152
153 return 0;
154 }
155
156 static int manager_enumerate_machines(Manager *m) {
157 _cleanup_closedir_ DIR *d = NULL;
158 int r;
159
160 assert(m);
161
162 r = manager_add_host_machine(m);
163 if (r < 0)
164 return r;
165
166 /* Read in machine data stored on disk */
167 d = opendir("/run/systemd/machines");
168 if (!d) {
169 if (errno == ENOENT)
170 return 0;
171
172 return log_error_errno(errno, "Failed to open /run/systemd/machines: %m");
173 }
174
175 FOREACH_DIRENT(de, d, return -errno) {
176 struct Machine *machine;
177 int k;
178
179 if (!dirent_is_file(de))
180 continue;
181
182 /* Ignore symlinks that map the unit name to the machine */
183 if (startswith(de->d_name, "unit:"))
184 continue;
185
186 if (!hostname_is_valid(de->d_name, 0))
187 continue;
188
189 k = manager_add_machine(m, de->d_name, &machine);
190 if (k < 0) {
191 r = log_error_errno(k, "Failed to add machine by file name %s: %m", de->d_name);
192 continue;
193 }
194
195 machine_add_to_gc_queue(machine);
196
197 k = machine_load(machine);
198 if (k < 0)
199 r = k;
200 }
201
202 return r;
203 }
204
205 static int manager_connect_bus(Manager *m) {
206 int r;
207
208 assert(m);
209 assert(!m->bus);
210
211 r = sd_bus_default_system(&m->bus);
212 if (r < 0)
213 return log_error_errno(r, "Failed to connect to system bus: %m");
214
215 r = bus_add_implementation(m->bus, &manager_object, m);
216 if (r < 0)
217 return r;
218
219 r = bus_match_signal_async(m->bus, NULL, bus_systemd_mgr, "JobRemoved", match_job_removed, NULL, m);
220 if (r < 0)
221 return log_error_errno(r, "Failed to add match for JobRemoved: %m");
222
223 r = bus_match_signal_async(m->bus, NULL, bus_systemd_mgr, "UnitRemoved", match_unit_removed, NULL, m);
224 if (r < 0)
225 return log_error_errno(r, "Failed to request match for UnitRemoved: %m");
226
227 r = sd_bus_match_signal_async(
228 m->bus,
229 NULL,
230 "org.freedesktop.systemd1",
231 NULL,
232 "org.freedesktop.DBus.Properties",
233 "PropertiesChanged",
234 match_properties_changed, NULL, m);
235 if (r < 0)
236 return log_error_errno(r, "Failed to request match for PropertiesChanged: %m");
237
238 r = bus_match_signal_async(m->bus, NULL, bus_systemd_mgr, "Reloading", match_reloading, NULL, m);
239 if (r < 0)
240 return log_error_errno(r, "Failed to request match for Reloading: %m");
241
242 r = bus_call_method_async(m->bus, NULL, bus_systemd_mgr, "Subscribe", NULL, NULL, NULL);
243 if (r < 0)
244 return log_error_errno(r, "Failed to enable subscription: %m");
245
246 r = bus_log_control_api_register(m->bus);
247 if (r < 0)
248 return r;
249
250 r = sd_bus_request_name_async(m->bus, NULL, "org.freedesktop.machine1", 0, NULL, NULL);
251 if (r < 0)
252 return log_error_errno(r, "Failed to request name: %m");
253
254 r = sd_bus_attach_event(m->bus, m->event, 0);
255 if (r < 0)
256 return log_error_errno(r, "Failed to attach bus to event loop: %m");
257
258 return 0;
259 }
260
261 static void manager_gc(Manager *m, bool drop_not_started) {
262 Machine *machine;
263
264 assert(m);
265
266 while ((machine = LIST_POP(gc_queue, m->machine_gc_queue))) {
267 machine->in_gc_queue = false;
268
269 /* First, if we are not closing yet, initiate stopping */
270 if (machine_may_gc(machine, drop_not_started) &&
271 machine_get_state(machine) != MACHINE_CLOSING)
272 machine_stop(machine);
273
274 /* Now, the stop probably made this referenced
275 * again, but if it didn't, then it's time to let it
276 * go entirely. */
277 if (machine_may_gc(machine, drop_not_started)) {
278 machine_finalize(machine);
279 machine_free(machine);
280 }
281 }
282 }
283
284 static int manager_startup(Manager *m) {
285 Machine *machine;
286 int r;
287
288 assert(m);
289
290 /* Connect to the bus */
291 r = manager_connect_bus(m);
292 if (r < 0)
293 return r;
294
295 /* Set up Varlink service */
296 r = manager_varlink_init(m);
297 if (r < 0)
298 return r;
299
300 /* Deserialize state */
301 manager_enumerate_machines(m);
302
303 /* Remove stale objects before we start them */
304 manager_gc(m, false);
305
306 /* And start everything */
307 HASHMAP_FOREACH(machine, m->machines)
308 machine_start(machine, NULL, NULL);
309
310 return 0;
311 }
312
313 static bool check_idle(void *userdata) {
314 Manager *m = userdata;
315
316 if (m->operations)
317 return false;
318
319 if (varlink_server_current_connections(m->varlink_server) > 0)
320 return false;
321
322 manager_gc(m, true);
323
324 return hashmap_isempty(m->machines);
325 }
326
327 static int run(int argc, char *argv[]) {
328 _cleanup_(manager_unrefp) Manager *m = NULL;
329 int r;
330
331 log_set_facility(LOG_AUTH);
332 log_setup();
333
334 r = service_parse_argv("systemd-machined.service",
335 "Manage registrations of local VMs and containers.",
336 BUS_IMPLEMENTATIONS(&manager_object,
337 &log_control_object),
338 argc, argv);
339 if (r <= 0)
340 return r;
341
342 umask(0022);
343
344 /* Always create the directories people can create inotify watches in. Note that some applications might check
345 * for the existence of /run/systemd/machines/ to determine whether machined is available, so please always
346 * make sure this check stays in. */
347 (void) mkdir_label("/run/systemd/machines", 0755);
348
349 assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD, SIGTERM, SIGINT, SIGRTMIN+18) >= 0);
350
351 r = manager_new(&m);
352 if (r < 0)
353 return log_error_errno(r, "Failed to allocate manager object: %m");
354
355 r = manager_startup(m);
356 if (r < 0)
357 return log_error_errno(r, "Failed to fully start up daemon: %m");
358
359 r = sd_notify(false, NOTIFY_READY);
360 if (r < 0)
361 log_warning_errno(r, "Failed to send readiness notification, ignoring: %m");
362
363 r = bus_event_loop_with_idle(
364 m->event,
365 m->bus,
366 "org.freedesktop.machine1",
367 DEFAULT_EXIT_USEC,
368 check_idle, m);
369 if (r < 0)
370 return log_error_errno(r, "Failed to run main loop: %m");
371
372 return 0;
373 }
374
375 DEFINE_MAIN_FUNCTION(run);