]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/vmspawn/vmspawn-scope.c
vmspawn: fix `sd_bus_message_append()` arguments and add missing error check
[thirdparty/systemd.git] / src / vmspawn / vmspawn-scope.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <stdio.h>
4
5 #include "sd-bus.h"
6
7 #include "bus-error.h"
8 #include "bus-locator.h"
9 #include "bus-unit-util.h"
10 #include "bus-util.h"
11 #include "bus-wait-for-jobs.h"
12 #include "escape.h"
13 #include "macro.h"
14 #include "process-util.h"
15 #include "random-util.h"
16 #include "socket-util.h"
17 #include "strv.h"
18 #include "unit-def.h"
19 #include "unit-name.h"
20 #include "vmspawn-scope.h"
21
22 int start_transient_scope(sd_bus *bus, const char *machine_name, bool allow_pidfd, char **ret_scope) {
23 _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL;
24 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
25 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL, *m = NULL;
26 _cleanup_free_ char *scope = NULL, *description = NULL;
27 const char *object;
28 int r;
29
30 assert(bus);
31 assert(machine_name);
32
33 /* Creates a transient scope unit which tracks the lifetime of the current process */
34
35 r = bus_wait_for_jobs_new(bus, &w);
36 if (r < 0)
37 return log_error_errno(r, "Could not watch job: %m");
38
39 if (asprintf(&scope, "machine-%"PRIu64"-%s.scope", random_u64(), machine_name) < 0)
40 return log_oom();
41
42 description = strjoin("Virtual Machine ", machine_name);
43 if (!description)
44 return log_oom();
45
46 r = bus_message_new_method_call(bus, &m, bus_systemd_mgr, "StartTransientUnit");
47 if (r < 0)
48 return bus_log_create_error(r);
49
50 r = sd_bus_message_append(m, "ss", /* name */ scope, /* mode */ "fail");
51 if (r < 0)
52 return bus_log_create_error(r);
53
54 /* Properties */
55 r = sd_bus_message_open_container(m, 'a', "(sv)");
56 if (r < 0)
57 return bus_log_create_error(r);
58
59 r = sd_bus_message_append(m, "(sv)(sv)(sv)",
60 "Description", "s", description,
61 "AddRef", "b", 1,
62 "CollectMode", "s", "inactive-or-failed");
63 if (r < 0)
64 return bus_log_create_error(r);
65
66 _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
67 r = pidref_set_self(&pidref);
68 if (r < 0)
69 return log_error_errno(r, "Failed to allocate PID reference: %m");
70
71 r = bus_append_scope_pidref(m, &pidref, allow_pidfd);
72 if (r < 0)
73 return bus_log_create_error(r);
74
75 r = sd_bus_message_close_container(m);
76 if (r < 0)
77 return bus_log_create_error(r);
78
79 /* No auxiliary units */
80 r = sd_bus_message_append(
81 m,
82 "a(sa(sv))",
83 0);
84 if (r < 0)
85 return bus_log_create_error(r);
86
87 r = sd_bus_call(bus, m, 0, &error, &reply);
88 if (r < 0) {
89 /* If this failed with a property we couldn't write, this is quite likely because the server
90 * doesn't support PIDFDs yet, let's try without. */
91 if (allow_pidfd &&
92 sd_bus_error_has_names(&error, SD_BUS_ERROR_UNKNOWN_PROPERTY, SD_BUS_ERROR_PROPERTY_READ_ONLY))
93 return start_transient_scope(bus, machine_name, false, ret_scope);
94
95 return log_error_errno(r, "Failed to start transient scope unit: %s", bus_error_message(&error, r));
96 }
97
98 r = sd_bus_message_read(reply, "o", &object);
99 if (r < 0)
100 return bus_log_parse_error(r);
101
102 r = bus_wait_for_jobs_one(w, object, /* quiet */ false, NULL);
103 if (r < 0)
104 return r;
105
106 if (ret_scope)
107 *ret_scope = TAKE_PTR(scope);
108
109 return 0;
110 }
111
112 static int message_add_commands(sd_bus_message *m, const char *exec_type, char ***commands, size_t n_commands) {
113 int r;
114
115 assert(m);
116 assert(exec_type);
117 assert(commands || n_commands == 0);
118
119 /* A small helper for adding an ExecStart / ExecStopPost / etc.. property to an sd_bus_message */
120
121 r = sd_bus_message_open_container(m, 'r', "sv");
122 if (r < 0)
123 return bus_log_create_error(r);
124
125 r = sd_bus_message_append(m, "s", exec_type);
126 if (r < 0)
127 return bus_log_create_error(r);
128
129 r = sd_bus_message_open_container(m, 'v', "a(sasb)");
130 if (r < 0)
131 return bus_log_create_error(r);
132
133 r = sd_bus_message_open_container(m, 'a', "(sasb)");
134 if (r < 0)
135 return bus_log_create_error(r);
136
137 FOREACH_ARRAY(cmd, commands, n_commands) {
138 char **cmdline = *cmd;
139
140 r = sd_bus_message_open_container(m, 'r', "sasb");
141 if (r < 0)
142 return bus_log_create_error(r);
143
144 r = sd_bus_message_append(m, "s", cmdline[0]);
145 if (r < 0)
146 return bus_log_create_error(r);
147
148 r = sd_bus_message_append_strv(m, cmdline);
149 if (r < 0)
150 return bus_log_create_error(r);
151
152 r = sd_bus_message_append(m, "b", 0);
153 if (r < 0)
154 return bus_log_create_error(r);
155
156 r = sd_bus_message_close_container(m);
157 if (r < 0)
158 return bus_log_create_error(r);
159 }
160
161 r = sd_bus_message_close_container(m);
162 if (r < 0)
163 return bus_log_create_error(r);
164
165 r = sd_bus_message_close_container(m);
166 if (r < 0)
167 return bus_log_create_error(r);
168
169 r = sd_bus_message_close_container(m);
170 if (r < 0)
171 return bus_log_create_error(r);
172
173 return 0;
174 }
175
176 void socket_service_pair_done(SocketServicePair *p) {
177 assert(p);
178
179 p->exec_start_pre = strv_free(p->exec_start_pre);
180 p->exec_start = strv_free(p->exec_start);
181 p->exec_stop_post = strv_free(p->exec_stop_post);
182 p->unit_name_prefix = mfree(p->unit_name_prefix);
183 p->runtime_directory = mfree(p->runtime_directory);
184 p->listen_address = mfree(p->listen_address);
185 p->socket_type = 0;
186 }
187
188 int start_socket_service_pair(sd_bus *bus, const char *scope, SocketServicePair *p) {
189 _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL;
190 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
191 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
192 _cleanup_free_ char *service_desc = NULL, *service_name = NULL, *socket_name = NULL;
193 const char *object, *socket_type_str;
194 int r;
195
196 /* Starts a socket/service unit pair bound to the given scope. */
197
198 assert(bus);
199 assert(scope);
200 assert(p);
201 assert(p->unit_name_prefix);
202 assert(p->exec_start);
203 assert(p->listen_address);
204
205 r = bus_wait_for_jobs_new(bus, &w);
206 if (r < 0)
207 return log_error_errno(r, "Could not watch job: %m");
208
209 socket_name = strjoin(p->unit_name_prefix, ".socket");
210 if (!socket_name)
211 return log_oom();
212
213 service_name = strjoin(p->unit_name_prefix, ".service");
214 if (!service_name)
215 return log_oom();
216
217 service_desc = quote_command_line(p->exec_start, SHELL_ESCAPE_EMPTY);
218 if (!service_desc)
219 return log_oom();
220
221 socket_type_str = socket_address_type_to_string(p->socket_type);
222 if (!socket_type_str)
223 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Invalid socket type: %d", p->socket_type);
224
225 r = bus_message_new_method_call(bus, &m, bus_systemd_mgr, "StartTransientUnit");
226 if (r < 0)
227 return bus_log_create_error(r);
228
229 r = sd_bus_message_append(m, "ssa(sv)",
230 /* ss - name, mode */
231 socket_name, "fail",
232 /* a(sv) - Properties */
233 5,
234 "Description", "s", p->listen_address,
235 "AddRef", "b", 1,
236 "BindsTo", "as", 1, scope,
237 "Listen", "a(ss)", 1, socket_type_str, p->listen_address,
238 "CollectMode", "s", "inactive-or-failed");
239 if (r < 0)
240 return bus_log_create_error(r);
241
242 /* aux */
243 r = sd_bus_message_open_container(m, 'a', "(sa(sv))");
244 if (r < 0)
245 return bus_log_create_error(r);
246
247 r = sd_bus_message_open_container(m, 'r', "sa(sv)");
248 if (r < 0)
249 return bus_log_create_error(r);
250
251 r = sd_bus_message_append(m, "s", service_name);
252 if (r < 0)
253 return bus_log_create_error(r);
254
255 r = sd_bus_message_open_container(m, 'a', "(sv)");
256 if (r < 0)
257 return bus_log_create_error(r);
258
259 r = sd_bus_message_append(m, "(sv)(sv)(sv)(sv)",
260 "Description", "s", service_desc,
261 "AddRef", "b", 1,
262 "BindsTo", "as", 1, scope,
263 "CollectMode", "s", "inactive-or-failed");
264 if (r < 0)
265 return bus_log_create_error(r);
266
267 if (p->runtime_directory) {
268 r = sd_bus_message_append(m, "(sv)", "RuntimeDirectory", "as", 1, p->runtime_directory);
269 if (r < 0)
270 return bus_log_create_error(r);
271 }
272
273 if (p->exec_start_pre) {
274 r = message_add_commands(m, "ExecStartPre", &p->exec_start_pre, 1);
275 if (r < 0)
276 return r;
277 }
278
279 r = message_add_commands(m, "ExecStart", &p->exec_start, 1);
280 if (r < 0)
281 return r;
282
283 if (p->exec_stop_post) {
284 r = message_add_commands(m, "ExecStopPost", &p->exec_stop_post, 1);
285 if (r < 0)
286 return r;
287 }
288
289 r = sd_bus_message_close_container(m);
290 if (r < 0)
291 return bus_log_create_error(r);
292
293 r = sd_bus_message_close_container(m);
294 if (r < 0)
295 return bus_log_create_error(r);
296
297 r = sd_bus_message_close_container(m);
298 if (r < 0)
299 return bus_log_create_error(r);
300
301 r = sd_bus_call(bus, m, 0, &error, &reply);
302 if (r < 0)
303 return log_error_errno(r, "Failed to start %s as transient unit: %s", p->exec_start[0], bus_error_message(&error, r));
304
305 r = sd_bus_message_read(reply, "o", &object);
306 if (r < 0)
307 return bus_log_parse_error(r);
308
309 return bus_wait_for_jobs_one(w, object, /* quiet */ false, NULL);
310 }