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