]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/nspawn/nspawn-register.c
tree-wide: prefer sending pifds over pids when creating scope units
[thirdparty/systemd.git] / src / nspawn / nspawn-register.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include "sd-bus.h"
4
5 #include "bus-error.h"
6 #include "bus-locator.h"
7 #include "bus-unit-util.h"
8 #include "bus-util.h"
9 #include "bus-wait-for-jobs.h"
10 #include "nspawn-register.h"
11 #include "special.h"
12 #include "stat-util.h"
13 #include "strv.h"
14
15 static int append_machine_properties(
16 sd_bus_message *m,
17 CustomMount *mounts,
18 unsigned n_mounts,
19 int kill_signal) {
20
21 unsigned j;
22 int r;
23
24 assert(m);
25
26 r = sd_bus_message_append(m, "(sv)", "DevicePolicy", "s", "closed");
27 if (r < 0)
28 return bus_log_create_error(r);
29
30 /* If you make changes here, also make sure to update systemd-nspawn@.service, to keep the device policies in
31 * sync regardless if we are run with or without the --keep-unit switch. */
32 r = sd_bus_message_append(m, "(sv)", "DeviceAllow", "a(ss)", 2,
33 /* Allow the container to
34 * access and create the API
35 * device nodes, so that
36 * PrivateDevices= in the
37 * container can work
38 * fine */
39 "/dev/net/tun", "rwm",
40 /* Allow the container
41 * access to ptys. However,
42 * do not permit the
43 * container to ever create
44 * these device nodes. */
45 "char-pts", "rw");
46 if (r < 0)
47 return bus_log_create_error(r);
48
49 for (j = 0; j < n_mounts; j++) {
50 CustomMount *cm = mounts + j;
51
52 if (cm->type != CUSTOM_MOUNT_BIND)
53 continue;
54
55 r = is_device_node(cm->source);
56 if (r == -ENOENT) {
57 /* The bind source might only appear as the image is put together, hence don't complain */
58 log_debug_errno(r, "Bind mount source %s not found, ignoring: %m", cm->source);
59 continue;
60 }
61 if (r < 0)
62 return log_error_errno(r, "Failed to stat %s: %m", cm->source);
63
64 if (r) {
65 r = sd_bus_message_append(m, "(sv)", "DeviceAllow", "a(ss)", 1,
66 cm->source, cm->read_only ? "r" : "rw");
67 if (r < 0)
68 return log_error_errno(r, "Failed to append message arguments: %m");
69 }
70 }
71
72 if (kill_signal != 0) {
73 r = sd_bus_message_append(m, "(sv)", "KillSignal", "i", kill_signal);
74 if (r < 0)
75 return bus_log_create_error(r);
76
77 r = sd_bus_message_append(m, "(sv)", "KillMode", "s", "mixed");
78 if (r < 0)
79 return bus_log_create_error(r);
80 }
81
82 return 0;
83 }
84
85 static int append_controller_property(sd_bus *bus, sd_bus_message *m) {
86 const char *unique;
87 int r;
88
89 assert(bus);
90 assert(m);
91
92 r = sd_bus_get_unique_name(bus, &unique);
93 if (r < 0)
94 return log_error_errno(r, "Failed to get unique name: %m");
95
96 r = sd_bus_message_append(m, "(sv)", "Controller", "s", unique);
97 if (r < 0)
98 return bus_log_create_error(r);
99
100 return 0;
101 }
102
103 int register_machine(
104 sd_bus *bus,
105 const char *machine_name,
106 pid_t pid,
107 const char *directory,
108 sd_id128_t uuid,
109 int local_ifindex,
110 const char *slice,
111 CustomMount *mounts,
112 unsigned n_mounts,
113 int kill_signal,
114 char **properties,
115 sd_bus_message *properties_message,
116 bool keep_unit,
117 const char *service) {
118
119 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
120 int r;
121
122 assert(bus);
123
124 if (keep_unit) {
125 r = bus_call_method(
126 bus,
127 bus_machine_mgr,
128 "RegisterMachineWithNetwork",
129 &error,
130 NULL,
131 "sayssusai",
132 machine_name,
133 SD_BUS_MESSAGE_APPEND_ID128(uuid),
134 service,
135 "container",
136 (uint32_t) pid,
137 strempty(directory),
138 local_ifindex > 0 ? 1 : 0, local_ifindex);
139 } else {
140 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
141
142 r = bus_message_new_method_call(bus, &m, bus_machine_mgr, "CreateMachineWithNetwork");
143 if (r < 0)
144 return bus_log_create_error(r);
145
146 r = sd_bus_message_append(
147 m,
148 "sayssusai",
149 machine_name,
150 SD_BUS_MESSAGE_APPEND_ID128(uuid),
151 service,
152 "container",
153 (uint32_t) pid,
154 strempty(directory),
155 local_ifindex > 0 ? 1 : 0, local_ifindex);
156 if (r < 0)
157 return bus_log_create_error(r);
158
159 r = sd_bus_message_open_container(m, 'a', "(sv)");
160 if (r < 0)
161 return bus_log_create_error(r);
162
163 if (!isempty(slice)) {
164 r = sd_bus_message_append(m, "(sv)", "Slice", "s", slice);
165 if (r < 0)
166 return bus_log_create_error(r);
167 }
168
169 r = append_controller_property(bus, m);
170 if (r < 0)
171 return r;
172
173 r = append_machine_properties(
174 m,
175 mounts,
176 n_mounts,
177 kill_signal);
178 if (r < 0)
179 return r;
180
181 if (properties_message) {
182 r = sd_bus_message_copy(m, properties_message, true);
183 if (r < 0)
184 return bus_log_create_error(r);
185 }
186
187 r = bus_append_unit_property_assignment_many(m, UNIT_SERVICE, properties);
188 if (r < 0)
189 return r;
190
191 r = sd_bus_message_close_container(m);
192 if (r < 0)
193 return bus_log_create_error(r);
194
195 r = sd_bus_call(bus, m, 0, &error, NULL);
196 }
197 if (r < 0)
198 return log_error_errno(r, "Failed to register machine: %s", bus_error_message(&error, r));
199
200 return 0;
201 }
202
203 int unregister_machine(
204 sd_bus *bus,
205 const char *machine_name) {
206
207 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
208 int r;
209
210 assert(bus);
211
212 r = bus_call_method(bus, bus_machine_mgr, "UnregisterMachine", &error, NULL, "s", machine_name);
213 if (r < 0)
214 log_debug("Failed to unregister machine: %s", bus_error_message(&error, r));
215
216 return 0;
217 }
218
219 int allocate_scope(
220 sd_bus *bus,
221 const char *machine_name,
222 pid_t pid,
223 const char *slice,
224 CustomMount *mounts,
225 unsigned n_mounts,
226 int kill_signal,
227 char **properties,
228 sd_bus_message *properties_message,
229 bool allow_pidfd) {
230
231 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
232 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
233 _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL;
234 _cleanup_free_ char *scope = NULL;
235 const char *description, *object;
236 int r;
237
238 assert(bus);
239
240 r = bus_wait_for_jobs_new(bus, &w);
241 if (r < 0)
242 return log_error_errno(r, "Could not watch job: %m");
243
244 r = unit_name_mangle_with_suffix(machine_name, "as machine name", 0, ".scope", &scope);
245 if (r < 0)
246 return log_error_errno(r, "Failed to mangle scope name: %m");
247
248 r = bus_message_new_method_call(bus, &m, bus_systemd_mgr, "StartTransientUnit");
249 if (r < 0)
250 return bus_log_create_error(r);
251
252 r = sd_bus_message_append(m, "ss", scope, "fail");
253 if (r < 0)
254 return bus_log_create_error(r);
255
256 /* Properties */
257 r = sd_bus_message_open_container(m, 'a', "(sv)");
258 if (r < 0)
259 return bus_log_create_error(r);
260
261 description = strjoina("Container ", machine_name);
262
263 if (allow_pidfd) {
264 _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
265 r = pidref_set_pid(&pidref, pid);
266 if (r < 0)
267 return log_error_errno(r, "Failed to allocate PID reference: %m");
268
269 r = bus_append_scope_pidref(m, &pidref);
270 } else
271 r = sd_bus_message_append(m, "(sv)", "PIDs", "au", 1, pid);
272 if (r < 0)
273 return bus_log_create_error(r);
274
275 r = sd_bus_message_append(m, "(sv)(sv)(sv)(sv)(sv)",
276 "Description", "s", description,
277 "Delegate", "b", 1,
278 "CollectMode", "s", "inactive-or-failed",
279 "AddRef", "b", 1,
280 "Slice", "s", isempty(slice) ? SPECIAL_MACHINE_SLICE : slice);
281 if (r < 0)
282 return bus_log_create_error(r);
283
284 r = append_controller_property(bus, m);
285 if (r < 0)
286 return r;
287
288 if (properties_message) {
289 r = sd_bus_message_copy(m, properties_message, true);
290 if (r < 0)
291 return bus_log_create_error(r);
292 }
293
294 r = append_machine_properties(
295 m,
296 mounts,
297 n_mounts,
298 kill_signal);
299 if (r < 0)
300 return r;
301
302 r = bus_append_unit_property_assignment_many(m, UNIT_SCOPE, properties);
303 if (r < 0)
304 return r;
305
306 r = sd_bus_message_close_container(m);
307 if (r < 0)
308 return bus_log_create_error(r);
309
310 /* No auxiliary units */
311 r = sd_bus_message_append(
312 m,
313 "a(sa(sv))",
314 0);
315 if (r < 0)
316 return bus_log_create_error(r);
317
318 r = sd_bus_call(bus, m, 0, &error, &reply);
319 if (r < 0) {
320 /* If this failed with a property we couldn't write, this is quite likely because the server
321 * doesn't support PIDFDs yet, let's try without. */
322 if (allow_pidfd &&
323 sd_bus_error_has_names(&error, SD_BUS_ERROR_UNKNOWN_PROPERTY, SD_BUS_ERROR_PROPERTY_READ_ONLY))
324 return allocate_scope(bus, machine_name, pid, slice, mounts, n_mounts, kill_signal, properties, properties_message, /* allow_pidfd= */ false);
325
326 return log_error_errno(r, "Failed to allocate scope: %s", bus_error_message(&error, r));
327 }
328
329 r = sd_bus_message_read(reply, "o", &object);
330 if (r < 0)
331 return bus_log_parse_error(r);
332
333 r = bus_wait_for_jobs_one(w, object, false, NULL);
334 if (r < 0)
335 return r;
336
337 return 0;
338 }
339
340 int terminate_scope(
341 sd_bus *bus,
342 const char *machine_name) {
343
344 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
345 _cleanup_free_ char *scope = NULL;
346 int r;
347
348 r = unit_name_mangle_with_suffix(machine_name, "to terminate", 0, ".scope", &scope);
349 if (r < 0)
350 return log_error_errno(r, "Failed to mangle scope name: %m");
351
352 r = bus_call_method(bus, bus_systemd_mgr, "AbandonScope", &error, NULL, "s", scope);
353 if (r < 0) {
354 log_debug_errno(r, "Failed to abandon scope '%s', ignoring: %s", scope, bus_error_message(&error, r));
355 sd_bus_error_free(&error);
356 }
357
358 r = bus_call_method(
359 bus,
360 bus_systemd_mgr,
361 "KillUnit",
362 &error,
363 NULL,
364 "ssi",
365 scope,
366 "all",
367 (int32_t) SIGKILL);
368 if (r < 0) {
369 log_debug_errno(r, "Failed to SIGKILL scope '%s', ignoring: %s", scope, bus_error_message(&error, r));
370 sd_bus_error_free(&error);
371 }
372
373 r = bus_call_method(bus, bus_systemd_mgr, "UnrefUnit", &error, NULL, "s", scope);
374 if (r < 0)
375 log_debug_errno(r, "Failed to drop reference to scope '%s', ignoring: %s", scope, bus_error_message(&error, r));
376
377 return 0;
378 }