]>
Commit | Line | Data |
---|---|---|
db9ecf05 | 1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
a7334b09 | 2 | |
034c6ed7 LP |
3 | #include <errno.h> |
4 | #include <fcntl.h> | |
8dd4c05b | 5 | #include <poll.h> |
ac8db36c | 6 | #include <sys/file.h> |
f3e43635 | 7 | #include <sys/mman.h> |
8dd4c05b | 8 | #include <sys/personality.h> |
94f04347 | 9 | #include <sys/prctl.h> |
d2ffa389 | 10 | #include <sys/shm.h> |
d2ffa389 | 11 | #include <sys/types.h> |
8dd4c05b LP |
12 | #include <sys/un.h> |
13 | #include <unistd.h> | |
023a4f67 | 14 | #include <utmpx.h> |
5cb5a6ff | 15 | |
9c0c6701 DDM |
16 | #include <linux/fs.h> /* Must be included after <sys/mount.h> */ |
17 | ||
24882e06 | 18 | #include "sd-messages.h" |
8dd4c05b LP |
19 | |
20 | #include "af-list.h" | |
b5efdb8a | 21 | #include "alloc-util.h" |
8dd4c05b | 22 | #include "async.h" |
8dd4c05b | 23 | #include "cap-list.h" |
430f0182 | 24 | #include "capability-util.h" |
fdb3deca | 25 | #include "cgroup-setup.h" |
28db6fbf | 26 | #include "constants.h" |
da681e1b | 27 | #include "cpu-set-util.h" |
af189d7b | 28 | #include "dev-setup.h" |
686d13b9 | 29 | #include "env-file.h" |
4d1a6904 | 30 | #include "env-util.h" |
17df7223 | 31 | #include "errno-list.h" |
8a62620e | 32 | #include "escape.h" |
43962c30 | 33 | #include "exec-credential.h" |
3ffd4af2 | 34 | #include "execute.h" |
bb5232b6 | 35 | #include "execute-serialize.h" |
8dd4c05b | 36 | #include "exit-status.h" |
3ffd4af2 | 37 | #include "fd-util.h" |
154eb43f | 38 | #include "fileio.h" |
f97b34a6 | 39 | #include "format-util.h" |
7d50b32a | 40 | #include "glob-util.h" |
0389f4fa | 41 | #include "hexdecoct.h" |
032b3afb | 42 | #include "ioprio-util.h" |
9c0c6701 | 43 | #include "lock-util.h" |
8dd4c05b LP |
44 | #include "log.h" |
45 | #include "macro.h" | |
e8a565cb | 46 | #include "manager.h" |
2a341bb9 | 47 | #include "manager-dump.h" |
0a970718 | 48 | #include "memory-util.h" |
f5947a5e | 49 | #include "missing_fs.h" |
7a114ed4 | 50 | #include "missing_prctl.h" |
35cd0ba5 | 51 | #include "mkdir-label.h" |
8dd4c05b | 52 | #include "namespace.h" |
6bedfcbb | 53 | #include "parse-util.h" |
8dd4c05b | 54 | #include "path-util.h" |
0b452006 | 55 | #include "process-util.h" |
78f22b97 | 56 | #include "rlimit-util.h" |
8dd4c05b | 57 | #include "rm-rf.h" |
3ffd4af2 | 58 | #include "seccomp-util.h" |
07d46372 | 59 | #include "securebits-util.h" |
8dd4c05b | 60 | #include "selinux-util.h" |
bb5232b6 | 61 | #include "serialize.h" |
a2ab603c | 62 | #include "sort-util.h" |
fd63e712 | 63 | #include "special.h" |
949befd3 | 64 | #include "stat-util.h" |
8b43440b | 65 | #include "string-table.h" |
07630cea | 66 | #include "string-util.h" |
8dd4c05b | 67 | #include "strv.h" |
7ccbd1ae | 68 | #include "syslog-util.h" |
8dd4c05b | 69 | #include "terminal-util.h" |
bb0c0d6f | 70 | #include "tmpfile-util.h" |
566b7d23 | 71 | #include "umask-util.h" |
2d3b784d | 72 | #include "unit-serialize.h" |
b1d4f8e1 | 73 | #include "user-util.h" |
8dd4c05b | 74 | #include "utmp-wtmp.h" |
5cb5a6ff | 75 | |
75689fb2 | 76 | const char *exec_context_tty_path(const ExecContext *context) { |
80876c20 LP |
77 | assert(context); |
78 | ||
1e22b5cd LP |
79 | if (context->stdio_as_fds) |
80 | return NULL; | |
81 | ||
80876c20 LP |
82 | if (context->tty_path) |
83 | return context->tty_path; | |
84 | ||
85 | return "/dev/console"; | |
86 | } | |
87 | ||
75689fb2 | 88 | int exec_context_tty_size(const ExecContext *context, unsigned *ret_rows, unsigned *ret_cols) { |
4d62ee55 DDM |
89 | unsigned rows, cols; |
90 | const char *tty; | |
4d62ee55 DDM |
91 | |
92 | assert(context); | |
93 | assert(ret_rows); | |
94 | assert(ret_cols); | |
95 | ||
96 | rows = context->tty_rows; | |
97 | cols = context->tty_cols; | |
98 | ||
99 | tty = exec_context_tty_path(context); | |
29f5a5ae DDM |
100 | if (tty) |
101 | (void) proc_cmdline_tty_size(tty, rows == UINT_MAX ? &rows : NULL, cols == UINT_MAX ? &cols : NULL); | |
4d62ee55 DDM |
102 | |
103 | *ret_rows = rows; | |
104 | *ret_cols = cols; | |
105 | ||
106 | return 0; | |
107 | } | |
108 | ||
75689fb2 | 109 | void exec_context_tty_reset(const ExecContext *context, const ExecParameters *p) { |
af189d7b | 110 | _cleanup_close_ int _fd = -EBADF, lock_fd = -EBADF; |
af189d7b | 111 | int fd; |
1e22b5cd | 112 | |
adabcbab LP |
113 | assert(context); |
114 | ||
115 | const char *path = exec_context_tty_path(context); | |
116 | ||
af189d7b ZJS |
117 | if (p && p->stdin_fd >= 0) |
118 | fd = p->stdin_fd; | |
119 | else if (path) { | |
120 | fd = _fd = open_terminal(path, O_RDWR|O_NOCTTY|O_CLOEXEC|O_NONBLOCK); | |
75689fb2 | 121 | if (fd < 0) |
adabcbab | 122 | return (void) log_debug_errno(fd, "Failed to open terminal '%s', ignoring: %m", path); |
75689fb2 LB |
123 | } else |
124 | return; /* nothing to do */ | |
b83d5050 | 125 | |
af189d7b | 126 | /* Take a synchronization lock for the duration of the setup that we do here. |
adabcbab LP |
127 | * systemd-vconsole-setup.service also takes the lock to avoid being interrupted. We open a new fd |
128 | * that will be closed automatically, and operate on it for convenience. */ | |
af189d7b ZJS |
129 | lock_fd = lock_dev_console(); |
130 | if (lock_fd < 0) | |
131 | return (void) log_debug_errno(lock_fd, | |
132 | "Failed to lock /dev/console: %m"); | |
133 | ||
75689fb2 LB |
134 | if (context->tty_vhangup) |
135 | (void) terminal_vhangup_fd(fd); | |
49590d67 | 136 | |
75689fb2 LB |
137 | if (context->tty_reset) |
138 | (void) reset_terminal_fd(fd, true); | |
49590d67 | 139 | |
75689fb2 LB |
140 | if (p && p->stdin_fd >= 0) { |
141 | unsigned rows = context->tty_rows, cols = context->tty_cols; | |
9f71ba8d | 142 | |
75689fb2 LB |
143 | (void) exec_context_tty_size(context, &rows, &cols); |
144 | (void) terminal_set_size_fd(p->stdin_fd, path, rows, cols); | |
d35fbf6b | 145 | } |
e66cf1a3 | 146 | |
75689fb2 LB |
147 | if (context->tty_vt_disallocate && path) |
148 | (void) vt_disallocate(path); | |
149 | } | |
e66cf1a3 | 150 | |
75689fb2 LB |
151 | static bool is_terminal_input(ExecInput i) { |
152 | return IN_SET(i, | |
153 | EXEC_INPUT_TTY, | |
154 | EXEC_INPUT_TTY_FORCE, | |
155 | EXEC_INPUT_TTY_FAIL); | |
156 | } | |
e66cf1a3 | 157 | |
75689fb2 LB |
158 | static bool is_terminal_output(ExecOutput o) { |
159 | return IN_SET(o, | |
160 | EXEC_OUTPUT_TTY, | |
161 | EXEC_OUTPUT_KMSG_AND_CONSOLE, | |
162 | EXEC_OUTPUT_JOURNAL_AND_CONSOLE); | |
163 | } | |
f4170c67 | 164 | |
75689fb2 LB |
165 | bool exec_needs_network_namespace(const ExecContext *context) { |
166 | assert(context); | |
37ac2744 | 167 | |
75689fb2 LB |
168 | return context->private_network || context->network_namespace_path; |
169 | } | |
3b8bddde | 170 | |
75689fb2 LB |
171 | static bool exec_needs_ephemeral(const ExecContext *context) { |
172 | return (context->root_image || context->root_directory) && context->root_ephemeral; | |
173 | } | |
755d4b67 | 174 | |
75689fb2 LB |
175 | bool exec_needs_ipc_namespace(const ExecContext *context) { |
176 | assert(context); | |
fa97f630 | 177 | |
75689fb2 LB |
178 | return context->private_ipc || context->ipc_namespace_path; |
179 | } | |
165a31c0 | 180 | |
75689fb2 LB |
181 | bool exec_needs_mount_namespace( |
182 | const ExecContext *context, | |
183 | const ExecParameters *params, | |
184 | const ExecRuntime *runtime) { | |
755d4b67 | 185 | |
75689fb2 | 186 | assert(context); |
d35fbf6b | 187 | |
75689fb2 LB |
188 | if (context->root_image) |
189 | return true; | |
5cd9cd35 | 190 | |
75689fb2 LB |
191 | if (!strv_isempty(context->read_write_paths) || |
192 | !strv_isempty(context->read_only_paths) || | |
193 | !strv_isempty(context->inaccessible_paths) || | |
194 | !strv_isempty(context->exec_paths) || | |
195 | !strv_isempty(context->no_exec_paths)) | |
196 | return true; | |
5cd9cd35 | 197 | |
75689fb2 LB |
198 | if (context->n_bind_mounts > 0) |
199 | return true; | |
5b6319dc | 200 | |
75689fb2 LB |
201 | if (context->n_temporary_filesystems > 0) |
202 | return true; | |
d35fbf6b | 203 | |
75689fb2 LB |
204 | if (context->n_mount_images > 0) |
205 | return true; | |
04aa0cb9 | 206 | |
75689fb2 LB |
207 | if (context->n_extension_images > 0) |
208 | return true; | |
f4170c67 | 209 | |
75689fb2 LB |
210 | if (!strv_isempty(context->extension_directories)) |
211 | return true; | |
f4170c67 | 212 | |
75689fb2 LB |
213 | if (!IN_SET(context->mount_propagation_flag, 0, MS_SHARED)) |
214 | return true; | |
f69567cb | 215 | |
75689fb2 LB |
216 | if (context->private_tmp && runtime && runtime->shared && (runtime->shared->tmp_dir || runtime->shared->var_tmp_dir)) |
217 | return true; | |
add00535 | 218 | |
75689fb2 LB |
219 | if (context->private_devices || |
220 | context->private_mounts > 0 || | |
221 | (context->private_mounts < 0 && exec_needs_network_namespace(context)) || | |
222 | context->protect_system != PROTECT_SYSTEM_NO || | |
223 | context->protect_home != PROTECT_HOME_NO || | |
224 | context->protect_kernel_tunables || | |
225 | context->protect_kernel_modules || | |
226 | context->protect_kernel_logs || | |
227 | context->protect_control_groups || | |
228 | context->protect_proc != PROTECT_PROC_DEFAULT || | |
229 | context->proc_subset != PROC_SUBSET_ALL || | |
230 | exec_needs_ipc_namespace(context)) | |
231 | return true; | |
502d704e | 232 | |
75689fb2 LB |
233 | if (context->root_directory) { |
234 | if (exec_context_get_effective_mount_apivfs(context)) | |
235 | return true; | |
59eeb84b | 236 | |
75689fb2 LB |
237 | for (ExecDirectoryType t = 0; t < _EXEC_DIRECTORY_TYPE_MAX; t++) { |
238 | if (params && !params->prefix[t]) | |
239 | continue; | |
84703040 | 240 | |
75689fb2 LB |
241 | if (context->directories[t].n_items > 0) |
242 | return true; | |
fc64760d | 243 | } |
75689fb2 | 244 | } |
fc64760d | 245 | |
75689fb2 LB |
246 | if (context->dynamic_user && |
247 | (context->directories[EXEC_DIRECTORY_STATE].n_items > 0 || | |
248 | context->directories[EXEC_DIRECTORY_CACHE].n_items > 0 || | |
249 | context->directories[EXEC_DIRECTORY_LOGS].n_items > 0)) | |
250 | return true; | |
469830d1 | 251 | |
75689fb2 LB |
252 | if (context->log_namespace) |
253 | return true; | |
ba128bb8 | 254 | |
75689fb2 LB |
255 | return false; |
256 | } | |
78e864e5 | 257 | |
75689fb2 LB |
258 | bool exec_directory_is_private(const ExecContext *context, ExecDirectoryType type) { |
259 | assert(context); | |
9df2cdd8 | 260 | |
75689fb2 LB |
261 | if (!context->dynamic_user) |
262 | return false; | |
b1994387 | 263 | |
75689fb2 LB |
264 | if (type == EXEC_DIRECTORY_CONFIGURATION) |
265 | return false; | |
b1994387 | 266 | |
75689fb2 LB |
267 | if (type == EXEC_DIRECTORY_RUNTIME && context->runtime_directory_preserve_mode == EXEC_PRESERVE_NO) |
268 | return false; | |
034c6ed7 | 269 | |
75689fb2 LB |
270 | return true; |
271 | } | |
00819cc1 | 272 | |
75689fb2 LB |
273 | int exec_params_get_cgroup_path( |
274 | const ExecParameters *params, | |
275 | const CGroupContext *c, | |
276 | char **ret) { | |
00819cc1 | 277 | |
75689fb2 LB |
278 | const char *subgroup = NULL; |
279 | char *p; | |
00819cc1 | 280 | |
75689fb2 LB |
281 | assert(params); |
282 | assert(ret); | |
f331434d | 283 | |
75689fb2 LB |
284 | if (!params->cgroup_path) |
285 | return -EINVAL; | |
f331434d | 286 | |
75689fb2 LB |
287 | /* If we are called for a unit where cgroup delegation is on, and the payload created its own populated |
288 | * subcgroup (which we expect it to do, after all it asked for delegation), then we cannot place the control | |
289 | * processes started after the main unit's process in the unit's main cgroup because it is now an inner one, | |
290 | * and inner cgroups may not contain processes. Hence, if delegation is on, and this is a control process, | |
291 | * let's use ".control" as subcgroup instead. Note that we do so only for ExecStartPost=, ExecReload=, | |
292 | * ExecStop=, ExecStopPost=, i.e. for the commands where the main process is already forked. For ExecStartPre= | |
293 | * this is not necessary, the cgroup is still empty. We distinguish these cases with the EXEC_CONTROL_CGROUP | |
294 | * flag, which is only passed for the former statements, not for the latter. */ | |
034c6ed7 | 295 | |
75689fb2 LB |
296 | if (FLAGS_SET(params->flags, EXEC_CGROUP_DELEGATE) && (FLAGS_SET(params->flags, EXEC_CONTROL_CGROUP) || c->delegate_subgroup)) { |
297 | if (FLAGS_SET(params->flags, EXEC_IS_CONTROL)) | |
298 | subgroup = ".control"; | |
299 | else | |
300 | subgroup = c->delegate_subgroup; | |
301 | } | |
dd305ec9 | 302 | |
75689fb2 LB |
303 | if (subgroup) |
304 | p = path_join(params->cgroup_path, subgroup); | |
305 | else | |
306 | p = strdup(params->cgroup_path); | |
307 | if (!p) | |
308 | return -ENOMEM; | |
5686391b | 309 | |
75689fb2 LB |
310 | *ret = p; |
311 | return !!subgroup; | |
312 | } | |
5686391b | 313 | |
75689fb2 LB |
314 | bool exec_context_get_cpu_affinity_from_numa(const ExecContext *c) { |
315 | assert(c); | |
5686391b | 316 | |
75689fb2 LB |
317 | return c->cpu_affinity_from_numa; |
318 | } | |
5686391b | 319 | |
75689fb2 LB |
320 | static void log_command_line(Unit *unit, const char *msg, const char *executable, char **argv) { |
321 | assert(unit); | |
322 | assert(msg); | |
323 | assert(executable); | |
5686391b | 324 | |
75689fb2 LB |
325 | if (!DEBUG_LOGGING) |
326 | return; | |
5686391b | 327 | |
75689fb2 | 328 | _cleanup_free_ char *cmdline = quote_command_line(argv, SHELL_ESCAPE_EMPTY); |
12145637 | 329 | |
75689fb2 LB |
330 | log_unit_struct(unit, LOG_DEBUG, |
331 | "EXECUTABLE=%s", executable, | |
332 | LOG_UNIT_MESSAGE(unit, "%s: %s", msg, strnull(cmdline)), | |
333 | LOG_UNIT_INVOCATION_ID(unit)); | |
d35fbf6b | 334 | } |
81a2b7ce | 335 | |
75689fb2 LB |
336 | static int exec_context_load_environment(const Unit *unit, const ExecContext *c, char ***l); |
337 | ||
f2341e0a LP |
338 | int exec_spawn(Unit *unit, |
339 | ExecCommand *command, | |
d35fbf6b | 340 | const ExecContext *context, |
154eb43f | 341 | ExecParameters *params, |
28135da3 | 342 | ExecRuntime *runtime, |
6bb00842 | 343 | const CGroupContext *cgroup_context, |
d35fbf6b | 344 | pid_t *ret) { |
8351ceae | 345 | |
bb5232b6 LB |
346 | char serialization_fd_number[DECIMAL_STR_MAX(int) + 1]; |
347 | _cleanup_free_ char *subcgroup_path = NULL, *log_level = NULL, *executor_path = NULL; | |
348 | _cleanup_fdset_free_ FDSet *fdset = NULL; | |
349 | _cleanup_fclose_ FILE *f = NULL; | |
d35fbf6b | 350 | pid_t pid; |
154eb43f | 351 | int r; |
8351ceae | 352 | |
f2341e0a | 353 | assert(unit); |
154eb43f | 354 | assert(unit->manager); |
bb5232b6 | 355 | assert(unit->manager->executor_fd >= 0); |
d35fbf6b DM |
356 | assert(command); |
357 | assert(context); | |
358 | assert(ret); | |
359 | assert(params); | |
25b583d7 | 360 | assert(params->fds || (params->n_socket_fds + params->n_storage_fds <= 0)); |
b646fc32 | 361 | assert(!params->files_env); /* We fill this field, ensure it comes NULL-initialized to us */ |
4298d0b5 | 362 | |
4b2af439 DDM |
363 | LOG_CONTEXT_PUSH_UNIT(unit); |
364 | ||
154eb43f | 365 | r = exec_context_load_environment(unit, context, ¶ms->files_env); |
ff0af2a1 | 366 | if (r < 0) |
f2341e0a | 367 | return log_unit_error_errno(unit, r, "Failed to load environment files: %m"); |
034c6ed7 | 368 | |
9f71ba8d ZJS |
369 | /* Fork with up-to-date SELinux label database, so the child inherits the up-to-date db |
370 | and, until the next SELinux policy changes, we save further reloads in future children. */ | |
2df2152c CG |
371 | mac_selinux_maybe_reload(); |
372 | ||
3ff67ec4 ZJS |
373 | /* We won't know the real executable path until we create the mount namespace in the child, but we |
374 | want to log from the parent, so we use the possibly inaccurate path here. */ | |
75689fb2 | 375 | log_command_line(unit, "About to execute", command->path, command->argv); |
12145637 | 376 | |
78f93209 | 377 | if (params->cgroup_path) { |
75689fb2 | 378 | r = exec_params_get_cgroup_path(params, cgroup_context, &subcgroup_path); |
78f93209 LP |
379 | if (r < 0) |
380 | return log_unit_error_errno(unit, r, "Failed to acquire subcgroup path: %m"); | |
18c1e481 LP |
381 | if (r > 0) { |
382 | /* If there's a subcgroup, then let's create it here now (the main cgroup was already | |
383 | * realized by the unit logic) */ | |
384 | ||
78f93209 LP |
385 | r = cg_create(SYSTEMD_CGROUP_CONTROLLER, subcgroup_path); |
386 | if (r < 0) | |
a8b993dc | 387 | return log_unit_error_errno(unit, r, "Failed to create subcgroup '%s': %m", subcgroup_path); |
78f93209 LP |
388 | } |
389 | } | |
390 | ||
bb5232b6 LB |
391 | /* In order to avoid copy-on-write traps and OOM-kills when pid1's memory.current is above the |
392 | * child's memory.max, serialize all the state needed to start the unit, and pass it to the | |
393 | * systemd-executor binary. clone() with CLONE_VM + CLONE_VFORK will pause the parent until the exec | |
394 | * and ensure all memory is shared. The child immediately execs the new binary so the delay should | |
395 | * be minimal. Once glibc provides a clone3 wrapper we can switch to that, and clone directly in the | |
396 | * target cgroup. */ | |
d35fbf6b | 397 | |
bb5232b6 LB |
398 | r = open_serialization_file("sd-executor-state", &f); |
399 | if (r < 0) | |
400 | return log_unit_error_errno(unit, r, "Failed to open serialization stream: %m"); | |
ff0af2a1 | 401 | |
bb5232b6 LB |
402 | fdset = fdset_new(); |
403 | if (!fdset) | |
404 | return log_oom(); | |
12145637 | 405 | |
bb5232b6 LB |
406 | r = exec_serialize_invocation(f, fdset, context, command, params, runtime, cgroup_context); |
407 | if (r < 0) | |
408 | return log_unit_error_errno(unit, r, "Failed to serialize parameters: %m"); | |
e1714f02 | 409 | |
86cbbc6d | 410 | if (fseeko(f, 0, SEEK_SET) < 0) |
bb5232b6 | 411 | return log_unit_error_errno(unit, errno, "Failed to reseek on serialization stream: %m"); |
4c2630eb | 412 | |
bb5232b6 LB |
413 | r = fd_cloexec(fileno(f), false); |
414 | if (r < 0) | |
415 | return log_unit_error_errno(unit, r, "Failed to set O_CLOEXEC on serialization fd: %m"); | |
416 | ||
417 | r = fdset_cloexec(fdset, false); | |
418 | if (r < 0) | |
419 | return log_unit_error_errno(unit, r, "Failed to set O_CLOEXEC on serialized fds: %m"); | |
420 | ||
421 | r = log_level_to_string_alloc(log_get_max_level(), &log_level); | |
422 | if (r < 0) | |
423 | return log_unit_error_errno(unit, r, "Failed to convert log level to string: %m"); | |
424 | ||
425 | r = fd_get_path(unit->manager->executor_fd, &executor_path); | |
426 | if (r < 0) | |
427 | return log_unit_error_errno(unit, r, "Failed to get executor path from fd: %m"); | |
428 | ||
429 | xsprintf(serialization_fd_number, "%i", fileno(f)); | |
430 | ||
431 | /* The executor binary is pinned, to avoid compatibility problems during upgrades. */ | |
4520681d MY |
432 | r = posix_spawn_wrapper( |
433 | FORMAT_PROC_FD_PATH(unit->manager->executor_fd), | |
bb5232b6 LB |
434 | STRV_MAKE(executor_path, |
435 | "--deserialize", serialization_fd_number, | |
436 | "--log-level", log_level, | |
437 | "--log-target", log_target_to_string(manager_get_executor_log_target(unit->manager))), | |
438 | environ, | |
439 | &pid); | |
440 | if (r < 0) | |
441 | return log_unit_error_errno(unit, r, "Failed to spawn executor: %m"); | |
034c6ed7 | 442 | |
f2341e0a | 443 | log_unit_debug(unit, "Forked %s as "PID_FMT, command->path, pid); |
23635a85 | 444 | |
78f93209 LP |
445 | /* We add the new process to the cgroup both in the child (so that we can be sure that no user code is ever |
446 | * executed outside of the cgroup) and in the parent (so that we can be sure that when we kill the cgroup the | |
447 | * process will be killed too). */ | |
448 | if (subcgroup_path) | |
449 | (void) cg_attach(SYSTEMD_CGROUP_CONTROLLER, subcgroup_path, pid); | |
2da3263a | 450 | |
b58b4116 | 451 | exec_status_start(&command->exec_status, pid); |
9fb86720 | 452 | |
034c6ed7 | 453 | *ret = pid; |
5cb5a6ff LP |
454 | return 0; |
455 | } | |
456 | ||
034c6ed7 LP |
457 | void exec_context_init(ExecContext *c) { |
458 | assert(c); | |
459 | ||
154eb43f LB |
460 | /* When initializing a bool member to 'true', make sure to serialize in execute-serialize.c using |
461 | * serialize_bool() instead of serialize_bool_elide(). */ | |
462 | ||
02131627 LB |
463 | *c = (ExecContext) { |
464 | .umask = 0022, | |
465 | .ioprio = IOPRIO_DEFAULT_CLASS_AND_PRIO, | |
466 | .cpu_sched_policy = SCHED_OTHER, | |
467 | .syslog_priority = LOG_DAEMON|LOG_INFO, | |
468 | .syslog_level_prefix = true, | |
469 | .ignore_sigpipe = true, | |
470 | .timer_slack_nsec = NSEC_INFINITY, | |
471 | .personality = PERSONALITY_INVALID, | |
472 | .timeout_clean_usec = USEC_INFINITY, | |
473 | .capability_bounding_set = CAP_MASK_UNSET, | |
474 | .restrict_namespaces = NAMESPACE_FLAGS_INITIAL, | |
475 | .log_level_max = -1, | |
005bfaf1 | 476 | #if HAVE_SECCOMP |
02131627 | 477 | .syscall_errno = SECCOMP_ERROR_NUMBER_KILL, |
005bfaf1 | 478 | #endif |
02131627 LB |
479 | .tty_rows = UINT_MAX, |
480 | .tty_cols = UINT_MAX, | |
481 | .private_mounts = -1, | |
482 | .memory_ksm = -1, | |
854eca4a | 483 | .set_login_environment = -1, |
02131627 LB |
484 | }; |
485 | ||
59026bcc MY |
486 | FOREACH_ARRAY(d, c->directories, _EXEC_DIRECTORY_TYPE_MAX) |
487 | d->mode = 0755; | |
02131627 | 488 | |
b070c7c0 | 489 | numa_policy_reset(&c->numa_policy); |
02131627 LB |
490 | |
491 | assert_cc(NAMESPACE_FLAGS_INITIAL != NAMESPACE_FLAGS_ALL); | |
034c6ed7 LP |
492 | } |
493 | ||
613b411c | 494 | void exec_context_done(ExecContext *c) { |
5cb5a6ff LP |
495 | assert(c); |
496 | ||
6796073e LP |
497 | c->environment = strv_free(c->environment); |
498 | c->environment_files = strv_free(c->environment_files); | |
b4c14404 | 499 | c->pass_environment = strv_free(c->pass_environment); |
00819cc1 | 500 | c->unset_environment = strv_free(c->unset_environment); |
8c7be95e | 501 | |
31ce987c | 502 | rlimit_free_all(c->rlimit); |
034c6ed7 | 503 | |
5b10116e | 504 | for (size_t l = 0; l < 3; l++) { |
52c239d7 | 505 | c->stdio_fdname[l] = mfree(c->stdio_fdname[l]); |
2038c3f5 LP |
506 | c->stdio_file[l] = mfree(c->stdio_file[l]); |
507 | } | |
52c239d7 | 508 | |
a1e58e8e LP |
509 | c->working_directory = mfree(c->working_directory); |
510 | c->root_directory = mfree(c->root_directory); | |
915e6d16 | 511 | c->root_image = mfree(c->root_image); |
18d73705 | 512 | c->root_image_options = mount_options_free_all(c->root_image_options); |
0389f4fa LB |
513 | c->root_hash = mfree(c->root_hash); |
514 | c->root_hash_size = 0; | |
515 | c->root_hash_path = mfree(c->root_hash_path); | |
d4d55b0d LB |
516 | c->root_hash_sig = mfree(c->root_hash_sig); |
517 | c->root_hash_sig_size = 0; | |
518 | c->root_hash_sig_path = mfree(c->root_hash_sig_path); | |
0389f4fa | 519 | c->root_verity = mfree(c->root_verity); |
93f59701 | 520 | c->extension_images = mount_image_free_many(c->extension_images, &c->n_extension_images); |
a07b9926 | 521 | c->extension_directories = strv_free(c->extension_directories); |
a1e58e8e LP |
522 | c->tty_path = mfree(c->tty_path); |
523 | c->syslog_identifier = mfree(c->syslog_identifier); | |
524 | c->user = mfree(c->user); | |
525 | c->group = mfree(c->group); | |
034c6ed7 | 526 | |
6796073e | 527 | c->supplementary_groups = strv_free(c->supplementary_groups); |
94f04347 | 528 | |
a1e58e8e | 529 | c->pam_name = mfree(c->pam_name); |
5b6319dc | 530 | |
2a624c36 AP |
531 | c->read_only_paths = strv_free(c->read_only_paths); |
532 | c->read_write_paths = strv_free(c->read_write_paths); | |
533 | c->inaccessible_paths = strv_free(c->inaccessible_paths); | |
ddc155b2 TM |
534 | c->exec_paths = strv_free(c->exec_paths); |
535 | c->no_exec_paths = strv_free(c->no_exec_paths); | |
8c35c10d | 536 | c->exec_search_path = strv_free(c->exec_search_path); |
82c121a4 | 537 | |
d2d6c096 | 538 | bind_mount_free_many(c->bind_mounts, c->n_bind_mounts); |
8e06d57c YW |
539 | c->bind_mounts = NULL; |
540 | c->n_bind_mounts = 0; | |
2abd4e38 YW |
541 | temporary_filesystem_free_many(c->temporary_filesystems, c->n_temporary_filesystems); |
542 | c->temporary_filesystems = NULL; | |
543 | c->n_temporary_filesystems = 0; | |
b3d13314 | 544 | c->mount_images = mount_image_free_many(c->mount_images, &c->n_mount_images); |
d2d6c096 | 545 | |
0985c7c4 | 546 | cpu_set_reset(&c->cpu_set); |
b070c7c0 | 547 | numa_policy_reset(&c->numa_policy); |
86a3475b | 548 | |
a1e58e8e LP |
549 | c->utmp_id = mfree(c->utmp_id); |
550 | c->selinux_context = mfree(c->selinux_context); | |
551 | c->apparmor_profile = mfree(c->apparmor_profile); | |
5b8e1b77 | 552 | c->smack_process_label = mfree(c->smack_process_label); |
eef65bf3 | 553 | |
9b412709 | 554 | c->restrict_filesystems = set_free_free(c->restrict_filesystems); |
b1994387 | 555 | |
8cfa775f | 556 | c->syscall_filter = hashmap_free(c->syscall_filter); |
525d3cc7 LP |
557 | c->syscall_archs = set_free(c->syscall_archs); |
558 | c->address_families = set_free(c->address_families); | |
e66cf1a3 | 559 | |
fc932ed4 MY |
560 | FOREACH_ARRAY(d, c->directories, _EXEC_DIRECTORY_TYPE_MAX) |
561 | exec_directory_done(d); | |
d3070fbd LP |
562 | |
563 | c->log_level_max = -1; | |
564 | ||
565 | exec_context_free_log_extra_fields(c); | |
9b412709 FS |
566 | c->log_filter_allowed_patterns = set_free_free(c->log_filter_allowed_patterns); |
567 | c->log_filter_denied_patterns = set_free_free(c->log_filter_denied_patterns); | |
08f3be7a | 568 | |
5ac1530e ZJS |
569 | c->log_ratelimit_interval_usec = 0; |
570 | c->log_ratelimit_burst = 0; | |
90fc172e | 571 | |
08f3be7a LP |
572 | c->stdin_data = mfree(c->stdin_data); |
573 | c->stdin_data_size = 0; | |
a8d08f39 LP |
574 | |
575 | c->network_namespace_path = mfree(c->network_namespace_path); | |
71d1e583 | 576 | c->ipc_namespace_path = mfree(c->ipc_namespace_path); |
91dd5f7c LP |
577 | |
578 | c->log_namespace = mfree(c->log_namespace); | |
bb0c0d6f | 579 | |
43144be4 | 580 | c->load_credentials = hashmap_free(c->load_credentials); |
bb0c0d6f | 581 | c->set_credentials = hashmap_free(c->set_credentials); |
9b412709 | 582 | c->import_credentials = set_free_free(c->import_credentials); |
84be0c71 LP |
583 | |
584 | c->root_image_policy = image_policy_free(c->root_image_policy); | |
585 | c->mount_image_policy = image_policy_free(c->mount_image_policy); | |
586 | c->extension_image_policy = image_policy_free(c->extension_image_policy); | |
e66cf1a3 LP |
587 | } |
588 | ||
34cf6c43 | 589 | int exec_context_destroy_runtime_directory(const ExecContext *c, const char *runtime_prefix) { |
e66cf1a3 LP |
590 | assert(c); |
591 | ||
592 | if (!runtime_prefix) | |
593 | return 0; | |
594 | ||
f7df8adb | 595 | FOREACH_ARRAY(i, c->directories[EXEC_DIRECTORY_RUNTIME].items, c->directories[EXEC_DIRECTORY_RUNTIME].n_items) { |
c2b2df60 | 596 | _cleanup_free_ char *p = NULL; |
e66cf1a3 | 597 | |
494d0247 | 598 | if (exec_directory_is_private(c, EXEC_DIRECTORY_RUNTIME)) |
f7df8adb | 599 | p = path_join(runtime_prefix, "private", i->path); |
494d0247 | 600 | else |
f7df8adb | 601 | p = path_join(runtime_prefix, i->path); |
e66cf1a3 LP |
602 | if (!p) |
603 | return -ENOMEM; | |
604 | ||
7bc4bf4a LP |
605 | /* We execute this synchronously, since we need to be sure this is gone when we start the |
606 | * service next. */ | |
c6878637 | 607 | (void) rm_rf(p, REMOVE_ROOT); |
211a3d87 | 608 | |
f7df8adb | 609 | STRV_FOREACH(symlink, i->symlinks) { |
211a3d87 LB |
610 | _cleanup_free_ char *symlink_abs = NULL; |
611 | ||
612 | if (exec_directory_is_private(c, EXEC_DIRECTORY_RUNTIME)) | |
613 | symlink_abs = path_join(runtime_prefix, "private", *symlink); | |
614 | else | |
615 | symlink_abs = path_join(runtime_prefix, *symlink); | |
616 | if (!symlink_abs) | |
617 | return -ENOMEM; | |
618 | ||
619 | (void) unlink(symlink_abs); | |
620 | } | |
e66cf1a3 LP |
621 | } |
622 | ||
623 | return 0; | |
5cb5a6ff LP |
624 | } |
625 | ||
b9f976fb MK |
626 | int exec_context_destroy_mount_ns_dir(Unit *u) { |
627 | _cleanup_free_ char *p = NULL; | |
628 | ||
629 | if (!u || !MANAGER_IS_SYSTEM(u->manager)) | |
630 | return 0; | |
631 | ||
632 | p = path_join("/run/systemd/propagate/", u->id); | |
633 | if (!p) | |
634 | return -ENOMEM; | |
635 | ||
636 | /* This is only filled transiently (see mount_in_namespace()), should be empty or even non-existent*/ | |
637 | if (rmdir(p) < 0 && errno != ENOENT) | |
638 | log_unit_debug_errno(u, errno, "Unable to remove propagation dir '%s', ignoring: %m", p); | |
639 | ||
640 | return 0; | |
641 | } | |
642 | ||
bb5232b6 | 643 | void exec_command_done(ExecCommand *c) { |
43d0fcbd LP |
644 | assert(c); |
645 | ||
a1e58e8e | 646 | c->path = mfree(c->path); |
6796073e | 647 | c->argv = strv_free(c->argv); |
43d0fcbd LP |
648 | } |
649 | ||
da6053d0 | 650 | void exec_command_done_array(ExecCommand *c, size_t n) { |
f7df8adb MY |
651 | FOREACH_ARRAY(i, c, n) |
652 | exec_command_done(i); | |
43d0fcbd LP |
653 | } |
654 | ||
f1acf85a | 655 | ExecCommand* exec_command_free_list(ExecCommand *c) { |
5cb5a6ff LP |
656 | ExecCommand *i; |
657 | ||
52e3671b | 658 | while ((i = LIST_POP(command, c))) { |
43d0fcbd | 659 | exec_command_done(i); |
5cb5a6ff LP |
660 | free(i); |
661 | } | |
f1acf85a ZJS |
662 | |
663 | return NULL; | |
5cb5a6ff LP |
664 | } |
665 | ||
da6053d0 | 666 | void exec_command_free_array(ExecCommand **c, size_t n) { |
f7df8adb MY |
667 | FOREACH_ARRAY(i, c, n) |
668 | *i = exec_command_free_list(*i); | |
034c6ed7 LP |
669 | } |
670 | ||
6a1d4d9f | 671 | void exec_command_reset_status_array(ExecCommand *c, size_t n) { |
f7df8adb MY |
672 | FOREACH_ARRAY(i, c, n) |
673 | exec_status_reset(&i->exec_status); | |
6a1d4d9f LP |
674 | } |
675 | ||
676 | void exec_command_reset_status_list_array(ExecCommand **c, size_t n) { | |
f7df8adb MY |
677 | FOREACH_ARRAY(i, c, n) |
678 | LIST_FOREACH(command, z, *i) | |
6a1d4d9f | 679 | exec_status_reset(&z->exec_status); |
6a1d4d9f LP |
680 | } |
681 | ||
039f0e70 | 682 | typedef struct InvalidEnvInfo { |
34cf6c43 | 683 | const Unit *unit; |
039f0e70 LP |
684 | const char *path; |
685 | } InvalidEnvInfo; | |
686 | ||
687 | static void invalid_env(const char *p, void *userdata) { | |
688 | InvalidEnvInfo *info = userdata; | |
689 | ||
f2341e0a | 690 | log_unit_error(info->unit, "Ignoring invalid environment assignment '%s': %s", p, info->path); |
039f0e70 LP |
691 | } |
692 | ||
52c239d7 LB |
693 | const char* exec_context_fdname(const ExecContext *c, int fd_index) { |
694 | assert(c); | |
695 | ||
696 | switch (fd_index) { | |
5073ff6b | 697 | |
52c239d7 LB |
698 | case STDIN_FILENO: |
699 | if (c->std_input != EXEC_INPUT_NAMED_FD) | |
700 | return NULL; | |
5073ff6b | 701 | |
52c239d7 | 702 | return c->stdio_fdname[STDIN_FILENO] ?: "stdin"; |
5073ff6b | 703 | |
52c239d7 LB |
704 | case STDOUT_FILENO: |
705 | if (c->std_output != EXEC_OUTPUT_NAMED_FD) | |
706 | return NULL; | |
5073ff6b | 707 | |
52c239d7 | 708 | return c->stdio_fdname[STDOUT_FILENO] ?: "stdout"; |
5073ff6b | 709 | |
52c239d7 LB |
710 | case STDERR_FILENO: |
711 | if (c->std_error != EXEC_OUTPUT_NAMED_FD) | |
712 | return NULL; | |
5073ff6b | 713 | |
52c239d7 | 714 | return c->stdio_fdname[STDERR_FILENO] ?: "stderr"; |
5073ff6b | 715 | |
52c239d7 LB |
716 | default: |
717 | return NULL; | |
718 | } | |
719 | } | |
720 | ||
398a5009 ZJS |
721 | static int exec_context_load_environment(const Unit *unit, const ExecContext *c, char ***ret) { |
722 | _cleanup_strv_free_ char **v = NULL; | |
398a5009 | 723 | int r; |
8c7be95e LP |
724 | |
725 | assert(c); | |
398a5009 | 726 | assert(ret); |
8c7be95e LP |
727 | |
728 | STRV_FOREACH(i, c->environment_files) { | |
7fd1b19b | 729 | _cleanup_globfree_ glob_t pglob = {}; |
398a5009 ZJS |
730 | bool ignore = false; |
731 | char *fn = *i; | |
8c7be95e LP |
732 | |
733 | if (fn[0] == '-') { | |
734 | ignore = true; | |
313cefa1 | 735 | fn++; |
8c7be95e LP |
736 | } |
737 | ||
738 | if (!path_is_absolute(fn)) { | |
8c7be95e LP |
739 | if (ignore) |
740 | continue; | |
8c7be95e LP |
741 | return -EINVAL; |
742 | } | |
743 | ||
2bef10ab | 744 | /* Filename supports globbing, take all matching files */ |
398a5009 ZJS |
745 | r = safe_glob(fn, 0, &pglob); |
746 | if (r < 0) { | |
2bef10ab PL |
747 | if (ignore) |
748 | continue; | |
398a5009 | 749 | return r; |
2bef10ab | 750 | } |
8c7be95e | 751 | |
d8c92e8b ZJS |
752 | /* When we don't match anything, -ENOENT should be returned */ |
753 | assert(pglob.gl_pathc > 0); | |
754 | ||
f7df8adb | 755 | FOREACH_ARRAY(path, pglob.gl_pathv, pglob.gl_pathc) { |
398a5009 ZJS |
756 | _cleanup_strv_free_ char **p = NULL; |
757 | ||
f7df8adb | 758 | r = load_env_file(NULL, *path, &p); |
398a5009 | 759 | if (r < 0) { |
2bef10ab PL |
760 | if (ignore) |
761 | continue; | |
398a5009 | 762 | return r; |
e9c1ea9d | 763 | } |
398a5009 | 764 | |
ebc05a09 | 765 | /* Log invalid environment variables with filename */ |
039f0e70 LP |
766 | if (p) { |
767 | InvalidEnvInfo info = { | |
f2341e0a | 768 | .unit = unit, |
f7df8adb | 769 | .path = *path, |
039f0e70 LP |
770 | }; |
771 | ||
772 | p = strv_env_clean_with_callback(p, invalid_env, &info); | |
773 | } | |
8c7be95e | 774 | |
398a5009 ZJS |
775 | if (!v) |
776 | v = TAKE_PTR(p); | |
2bef10ab | 777 | else { |
398a5009 | 778 | char **m = strv_env_merge(v, p); |
c84a9488 | 779 | if (!m) |
2bef10ab | 780 | return -ENOMEM; |
2bef10ab | 781 | |
398a5009 | 782 | strv_free_and_replace(v, m); |
2bef10ab | 783 | } |
8c7be95e LP |
784 | } |
785 | } | |
786 | ||
398a5009 | 787 | *ret = TAKE_PTR(v); |
8c7be95e LP |
788 | |
789 | return 0; | |
790 | } | |
791 | ||
6ac8fdc9 | 792 | static bool tty_may_match_dev_console(const char *tty) { |
7b912648 | 793 | _cleanup_free_ char *resolved = NULL; |
6ac8fdc9 | 794 | |
1e22b5cd LP |
795 | if (!tty) |
796 | return true; | |
797 | ||
a119ec7c | 798 | tty = skip_dev_prefix(tty); |
6ac8fdc9 MS |
799 | |
800 | /* trivial identity? */ | |
801 | if (streq(tty, "console")) | |
802 | return true; | |
803 | ||
7b912648 LP |
804 | if (resolve_dev_console(&resolved) < 0) |
805 | return true; /* if we could not resolve, assume it may */ | |
6ac8fdc9 MS |
806 | |
807 | /* "tty0" means the active VC, so it may be the same sometimes */ | |
955f1c85 | 808 | return path_equal(resolved, tty) || (streq(resolved, "tty0") && tty_is_vc(tty)); |
6ac8fdc9 MS |
809 | } |
810 | ||
6c0ae739 LP |
811 | static bool exec_context_may_touch_tty(const ExecContext *ec) { |
812 | assert(ec); | |
1e22b5cd | 813 | |
6c0ae739 | 814 | return ec->tty_reset || |
1e22b5cd LP |
815 | ec->tty_vhangup || |
816 | ec->tty_vt_disallocate || | |
6ac8fdc9 MS |
817 | is_terminal_input(ec->std_input) || |
818 | is_terminal_output(ec->std_output) || | |
6c0ae739 LP |
819 | is_terminal_output(ec->std_error); |
820 | } | |
821 | ||
822 | bool exec_context_may_touch_console(const ExecContext *ec) { | |
823 | ||
824 | return exec_context_may_touch_tty(ec) && | |
1e22b5cd | 825 | tty_may_match_dev_console(exec_context_tty_path(ec)); |
6ac8fdc9 MS |
826 | } |
827 | ||
15ae422b | 828 | static void strv_fprintf(FILE *f, char **l) { |
15ae422b LP |
829 | assert(f); |
830 | ||
831 | STRV_FOREACH(g, l) | |
832 | fprintf(f, " %s", *g); | |
833 | } | |
834 | ||
ddc155b2 TM |
835 | static void strv_dump(FILE* f, const char *prefix, const char *name, char **strv) { |
836 | assert(f); | |
837 | assert(prefix); | |
838 | assert(name); | |
839 | ||
840 | if (!strv_isempty(strv)) { | |
a7bd1656 | 841 | fprintf(f, "%s%s:", prefix, name); |
ddc155b2 TM |
842 | strv_fprintf(f, strv); |
843 | fputs("\n", f); | |
844 | } | |
845 | } | |
846 | ||
97f53fec LB |
847 | void exec_params_dump(const ExecParameters *p, FILE* f, const char *prefix) { |
848 | assert(p); | |
849 | assert(f); | |
850 | ||
851 | prefix = strempty(prefix); | |
852 | ||
853 | fprintf(f, | |
854 | "%sRuntimeScope: %s\n" | |
855 | "%sExecFlags: %u\n" | |
856 | "%sSELinuxContextNetwork: %s\n" | |
857 | "%sCgroupSupportedMask: %u\n" | |
858 | "%sCgroupPath: %s\n" | |
859 | "%sCrededentialsDirectory: %s\n" | |
860 | "%sEncryptedCredentialsDirectory: %s\n" | |
861 | "%sConfirmSpawn: %s\n" | |
862 | "%sShallConfirmSpawn: %s\n" | |
863 | "%sWatchdogUSec: " USEC_FMT "\n" | |
864 | "%sNotifySocket: %s\n" | |
865 | "%sFallbackSmackProcessLabel: %s\n", | |
866 | prefix, runtime_scope_to_string(p->runtime_scope), | |
867 | prefix, p->flags, | |
868 | prefix, yes_no(p->selinux_context_net), | |
869 | prefix, p->cgroup_supported, | |
870 | prefix, p->cgroup_path, | |
871 | prefix, strempty(p->received_credentials_directory), | |
872 | prefix, strempty(p->received_encrypted_credentials_directory), | |
873 | prefix, strempty(p->confirm_spawn), | |
874 | prefix, yes_no(p->shall_confirm_spawn), | |
875 | prefix, p->watchdog_usec, | |
876 | prefix, strempty(p->notify_socket), | |
877 | prefix, strempty(p->fallback_smack_process_label)); | |
878 | ||
879 | strv_dump(f, prefix, "FdNames", p->fd_names); | |
880 | strv_dump(f, prefix, "Environment", p->environment); | |
881 | strv_dump(f, prefix, "Prefix", p->prefix); | |
882 | ||
883 | LIST_FOREACH(open_files, file, p->open_files) | |
884 | fprintf(f, "%sOpenFile: %s %s", prefix, file->path, open_file_flags_to_string(file->flags)); | |
885 | ||
886 | strv_dump(f, prefix, "FilesEnv", p->files_env); | |
887 | } | |
888 | ||
34cf6c43 | 889 | void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) { |
add00535 | 890 | int r; |
9eba9da4 | 891 | |
5cb5a6ff LP |
892 | assert(c); |
893 | assert(f); | |
894 | ||
4ad49000 | 895 | prefix = strempty(prefix); |
5cb5a6ff LP |
896 | |
897 | fprintf(f, | |
94f04347 LP |
898 | "%sUMask: %04o\n" |
899 | "%sWorkingDirectory: %s\n" | |
451a074f | 900 | "%sRootDirectory: %s\n" |
9c0c6701 | 901 | "%sRootEphemeral: %s\n" |
15ae422b | 902 | "%sNonBlocking: %s\n" |
64747e2d | 903 | "%sPrivateTmp: %s\n" |
7f112f50 | 904 | "%sPrivateDevices: %s\n" |
59eeb84b | 905 | "%sProtectKernelTunables: %s\n" |
e66a2f65 | 906 | "%sProtectKernelModules: %s\n" |
84703040 | 907 | "%sProtectKernelLogs: %s\n" |
fc64760d | 908 | "%sProtectClock: %s\n" |
59eeb84b | 909 | "%sProtectControlGroups: %s\n" |
d251207d LP |
910 | "%sPrivateNetwork: %s\n" |
911 | "%sPrivateUsers: %s\n" | |
1b8689f9 LP |
912 | "%sProtectHome: %s\n" |
913 | "%sProtectSystem: %s\n" | |
5d997827 | 914 | "%sMountAPIVFS: %s\n" |
f3e43635 | 915 | "%sIgnoreSIGPIPE: %s\n" |
f4170c67 | 916 | "%sMemoryDenyWriteExecute: %s\n" |
b1edf445 | 917 | "%sRestrictRealtime: %s\n" |
f69567cb | 918 | "%sRestrictSUIDSGID: %s\n" |
aecd5ac6 | 919 | "%sKeyringMode: %s\n" |
4e399953 LP |
920 | "%sProtectHostname: %s\n" |
921 | "%sProtectProc: %s\n" | |
922 | "%sProcSubset: %s\n", | |
5cb5a6ff | 923 | prefix, c->umask, |
14eb3285 LP |
924 | prefix, empty_to_root(c->working_directory), |
925 | prefix, empty_to_root(c->root_directory), | |
9c0c6701 | 926 | prefix, yes_no(c->root_ephemeral), |
15ae422b | 927 | prefix, yes_no(c->non_blocking), |
64747e2d | 928 | prefix, yes_no(c->private_tmp), |
7f112f50 | 929 | prefix, yes_no(c->private_devices), |
59eeb84b | 930 | prefix, yes_no(c->protect_kernel_tunables), |
e66a2f65 | 931 | prefix, yes_no(c->protect_kernel_modules), |
84703040 | 932 | prefix, yes_no(c->protect_kernel_logs), |
fc64760d | 933 | prefix, yes_no(c->protect_clock), |
59eeb84b | 934 | prefix, yes_no(c->protect_control_groups), |
d251207d LP |
935 | prefix, yes_no(c->private_network), |
936 | prefix, yes_no(c->private_users), | |
1b8689f9 LP |
937 | prefix, protect_home_to_string(c->protect_home), |
938 | prefix, protect_system_to_string(c->protect_system), | |
5e98086d | 939 | prefix, yes_no(exec_context_get_effective_mount_apivfs(c)), |
f3e43635 | 940 | prefix, yes_no(c->ignore_sigpipe), |
f4170c67 | 941 | prefix, yes_no(c->memory_deny_write_execute), |
b1edf445 | 942 | prefix, yes_no(c->restrict_realtime), |
f69567cb | 943 | prefix, yes_no(c->restrict_suid_sgid), |
aecd5ac6 | 944 | prefix, exec_keyring_mode_to_string(c->keyring_mode), |
4e399953 LP |
945 | prefix, yes_no(c->protect_hostname), |
946 | prefix, protect_proc_to_string(c->protect_proc), | |
947 | prefix, proc_subset_to_string(c->proc_subset)); | |
fb33a393 | 948 | |
06120a15 MY |
949 | if (c->set_login_environment >= 0) |
950 | fprintf(f, "%sSetLoginEnvironment: %s\n", prefix, yes_no(c->set_login_environment > 0)); | |
951 | ||
915e6d16 LP |
952 | if (c->root_image) |
953 | fprintf(f, "%sRootImage: %s\n", prefix, c->root_image); | |
954 | ||
18d73705 | 955 | if (c->root_image_options) { |
18d73705 LB |
956 | fprintf(f, "%sRootImageOptions:", prefix); |
957 | LIST_FOREACH(mount_options, o, c->root_image_options) | |
958 | if (!isempty(o->options)) | |
9ece6444 LB |
959 | fprintf(f, " %s:%s", |
960 | partition_designator_to_string(o->partition_designator), | |
961 | o->options); | |
18d73705 LB |
962 | fprintf(f, "\n"); |
963 | } | |
964 | ||
0389f4fa LB |
965 | if (c->root_hash) { |
966 | _cleanup_free_ char *encoded = NULL; | |
967 | encoded = hexmem(c->root_hash, c->root_hash_size); | |
968 | if (encoded) | |
969 | fprintf(f, "%sRootHash: %s\n", prefix, encoded); | |
970 | } | |
971 | ||
972 | if (c->root_hash_path) | |
973 | fprintf(f, "%sRootHash: %s\n", prefix, c->root_hash_path); | |
974 | ||
d4d55b0d LB |
975 | if (c->root_hash_sig) { |
976 | _cleanup_free_ char *encoded = NULL; | |
977 | ssize_t len; | |
978 | len = base64mem(c->root_hash_sig, c->root_hash_sig_size, &encoded); | |
979 | if (len) | |
980 | fprintf(f, "%sRootHashSignature: base64:%s\n", prefix, encoded); | |
981 | } | |
982 | ||
983 | if (c->root_hash_sig_path) | |
984 | fprintf(f, "%sRootHashSignature: %s\n", prefix, c->root_hash_sig_path); | |
985 | ||
0389f4fa LB |
986 | if (c->root_verity) |
987 | fprintf(f, "%sRootVerity: %s\n", prefix, c->root_verity); | |
988 | ||
8c7be95e LP |
989 | STRV_FOREACH(e, c->environment) |
990 | fprintf(f, "%sEnvironment: %s\n", prefix, *e); | |
991 | ||
992 | STRV_FOREACH(e, c->environment_files) | |
993 | fprintf(f, "%sEnvironmentFile: %s\n", prefix, *e); | |
94f04347 | 994 | |
b4c14404 FB |
995 | STRV_FOREACH(e, c->pass_environment) |
996 | fprintf(f, "%sPassEnvironment: %s\n", prefix, *e); | |
997 | ||
00819cc1 LP |
998 | STRV_FOREACH(e, c->unset_environment) |
999 | fprintf(f, "%sUnsetEnvironment: %s\n", prefix, *e); | |
1000 | ||
53f47dfc YW |
1001 | fprintf(f, "%sRuntimeDirectoryPreserve: %s\n", prefix, exec_preserve_mode_to_string(c->runtime_directory_preserve_mode)); |
1002 | ||
5b10116e | 1003 | for (ExecDirectoryType dt = 0; dt < _EXEC_DIRECTORY_TYPE_MAX; dt++) { |
3536f49e YW |
1004 | fprintf(f, "%s%sMode: %04o\n", prefix, exec_directory_type_to_string(dt), c->directories[dt].mode); |
1005 | ||
211a3d87 LB |
1006 | for (size_t i = 0; i < c->directories[dt].n_items; i++) { |
1007 | fprintf(f, "%s%s: %s\n", prefix, exec_directory_type_to_string(dt), c->directories[dt].items[i].path); | |
1008 | ||
1009 | STRV_FOREACH(d, c->directories[dt].items[i].symlinks) | |
1010 | fprintf(f, "%s%s: %s:%s\n", prefix, exec_directory_type_symlink_to_string(dt), c->directories[dt].items[i].path, *d); | |
1011 | } | |
3536f49e | 1012 | } |
c2bbd90b | 1013 | |
5291f26d | 1014 | fprintf(f, "%sTimeoutCleanSec: %s\n", prefix, FORMAT_TIMESPAN(c->timeout_clean_usec, USEC_PER_SEC)); |
12213aed | 1015 | |
06120a15 MY |
1016 | if (c->memory_ksm >= 0) |
1017 | fprintf(f, "%sMemoryKSM: %s\n", prefix, yes_no(c->memory_ksm > 0)); | |
1018 | ||
fb33a393 | 1019 | if (c->nice_set) |
5291f26d | 1020 | fprintf(f, "%sNice: %i\n", prefix, c->nice); |
fb33a393 | 1021 | |
dd6c17b1 | 1022 | if (c->oom_score_adjust_set) |
5291f26d | 1023 | fprintf(f, "%sOOMScoreAdjust: %i\n", prefix, c->oom_score_adjust); |
9eba9da4 | 1024 | |
ad21e542 | 1025 | if (c->coredump_filter_set) |
5291f26d | 1026 | fprintf(f, "%sCoredumpFilter: 0x%"PRIx64"\n", prefix, c->coredump_filter); |
ad21e542 | 1027 | |
5b10116e | 1028 | for (unsigned i = 0; i < RLIM_NLIMITS; i++) |
3c11da9d | 1029 | if (c->rlimit[i]) { |
4c3a2b84 | 1030 | fprintf(f, "%sLimit%s: " RLIM_FMT "\n", |
3c11da9d | 1031 | prefix, rlimit_to_string(i), c->rlimit[i]->rlim_max); |
4c3a2b84 | 1032 | fprintf(f, "%sLimit%sSoft: " RLIM_FMT "\n", |
3c11da9d EV |
1033 | prefix, rlimit_to_string(i), c->rlimit[i]->rlim_cur); |
1034 | } | |
94f04347 | 1035 | |
f8b69d1d | 1036 | if (c->ioprio_set) { |
1756a011 | 1037 | _cleanup_free_ char *class_str = NULL; |
f8b69d1d | 1038 | |
5bead76e | 1039 | r = ioprio_class_to_string_alloc(ioprio_prio_class(c->ioprio), &class_str); |
837df140 YW |
1040 | if (r >= 0) |
1041 | fprintf(f, "%sIOSchedulingClass: %s\n", prefix, class_str); | |
1042 | ||
5bead76e | 1043 | fprintf(f, "%sIOPriority: %d\n", prefix, ioprio_prio_data(c->ioprio)); |
f8b69d1d | 1044 | } |
94f04347 | 1045 | |
f8b69d1d | 1046 | if (c->cpu_sched_set) { |
1756a011 | 1047 | _cleanup_free_ char *policy_str = NULL; |
f8b69d1d | 1048 | |
837df140 YW |
1049 | r = sched_policy_to_string_alloc(c->cpu_sched_policy, &policy_str); |
1050 | if (r >= 0) | |
1051 | fprintf(f, "%sCPUSchedulingPolicy: %s\n", prefix, policy_str); | |
1052 | ||
94f04347 | 1053 | fprintf(f, |
38b48754 LP |
1054 | "%sCPUSchedulingPriority: %i\n" |
1055 | "%sCPUSchedulingResetOnFork: %s\n", | |
38b48754 LP |
1056 | prefix, c->cpu_sched_priority, |
1057 | prefix, yes_no(c->cpu_sched_reset_on_fork)); | |
b929bf04 | 1058 | } |
94f04347 | 1059 | |
0985c7c4 | 1060 | if (c->cpu_set.set) { |
e7fca352 MS |
1061 | _cleanup_free_ char *affinity = NULL; |
1062 | ||
1063 | affinity = cpu_set_to_range_string(&c->cpu_set); | |
1064 | fprintf(f, "%sCPUAffinity: %s\n", prefix, affinity); | |
94f04347 LP |
1065 | } |
1066 | ||
b070c7c0 MS |
1067 | if (mpol_is_valid(numa_policy_get_type(&c->numa_policy))) { |
1068 | _cleanup_free_ char *nodes = NULL; | |
1069 | ||
1070 | nodes = cpu_set_to_range_string(&c->numa_policy.nodes); | |
1071 | fprintf(f, "%sNUMAPolicy: %s\n", prefix, mpol_to_string(numa_policy_get_type(&c->numa_policy))); | |
1072 | fprintf(f, "%sNUMAMask: %s\n", prefix, strnull(nodes)); | |
1073 | } | |
1074 | ||
3a43da28 | 1075 | if (c->timer_slack_nsec != NSEC_INFINITY) |
ccd06097 | 1076 | fprintf(f, "%sTimerSlackNSec: "NSEC_FMT "\n", prefix, c->timer_slack_nsec); |
94f04347 LP |
1077 | |
1078 | fprintf(f, | |
80876c20 LP |
1079 | "%sStandardInput: %s\n" |
1080 | "%sStandardOutput: %s\n" | |
1081 | "%sStandardError: %s\n", | |
1082 | prefix, exec_input_to_string(c->std_input), | |
1083 | prefix, exec_output_to_string(c->std_output), | |
1084 | prefix, exec_output_to_string(c->std_error)); | |
1085 | ||
befc4a80 LP |
1086 | if (c->std_input == EXEC_INPUT_NAMED_FD) |
1087 | fprintf(f, "%sStandardInputFileDescriptorName: %s\n", prefix, c->stdio_fdname[STDIN_FILENO]); | |
1088 | if (c->std_output == EXEC_OUTPUT_NAMED_FD) | |
1089 | fprintf(f, "%sStandardOutputFileDescriptorName: %s\n", prefix, c->stdio_fdname[STDOUT_FILENO]); | |
1090 | if (c->std_error == EXEC_OUTPUT_NAMED_FD) | |
1091 | fprintf(f, "%sStandardErrorFileDescriptorName: %s\n", prefix, c->stdio_fdname[STDERR_FILENO]); | |
1092 | ||
1093 | if (c->std_input == EXEC_INPUT_FILE) | |
1094 | fprintf(f, "%sStandardInputFile: %s\n", prefix, c->stdio_file[STDIN_FILENO]); | |
1095 | if (c->std_output == EXEC_OUTPUT_FILE) | |
1096 | fprintf(f, "%sStandardOutputFile: %s\n", prefix, c->stdio_file[STDOUT_FILENO]); | |
566b7d23 ZD |
1097 | if (c->std_output == EXEC_OUTPUT_FILE_APPEND) |
1098 | fprintf(f, "%sStandardOutputFileToAppend: %s\n", prefix, c->stdio_file[STDOUT_FILENO]); | |
8d7dab1f LW |
1099 | if (c->std_output == EXEC_OUTPUT_FILE_TRUNCATE) |
1100 | fprintf(f, "%sStandardOutputFileToTruncate: %s\n", prefix, c->stdio_file[STDOUT_FILENO]); | |
befc4a80 LP |
1101 | if (c->std_error == EXEC_OUTPUT_FILE) |
1102 | fprintf(f, "%sStandardErrorFile: %s\n", prefix, c->stdio_file[STDERR_FILENO]); | |
566b7d23 ZD |
1103 | if (c->std_error == EXEC_OUTPUT_FILE_APPEND) |
1104 | fprintf(f, "%sStandardErrorFileToAppend: %s\n", prefix, c->stdio_file[STDERR_FILENO]); | |
8d7dab1f LW |
1105 | if (c->std_error == EXEC_OUTPUT_FILE_TRUNCATE) |
1106 | fprintf(f, "%sStandardErrorFileToTruncate: %s\n", prefix, c->stdio_file[STDERR_FILENO]); | |
befc4a80 | 1107 | |
80876c20 LP |
1108 | if (c->tty_path) |
1109 | fprintf(f, | |
6ea832a2 LP |
1110 | "%sTTYPath: %s\n" |
1111 | "%sTTYReset: %s\n" | |
1112 | "%sTTYVHangup: %s\n" | |
51462135 DDM |
1113 | "%sTTYVTDisallocate: %s\n" |
1114 | "%sTTYRows: %u\n" | |
1115 | "%sTTYColumns: %u\n", | |
6ea832a2 LP |
1116 | prefix, c->tty_path, |
1117 | prefix, yes_no(c->tty_reset), | |
1118 | prefix, yes_no(c->tty_vhangup), | |
51462135 DDM |
1119 | prefix, yes_no(c->tty_vt_disallocate), |
1120 | prefix, c->tty_rows, | |
1121 | prefix, c->tty_cols); | |
94f04347 | 1122 | |
9f6444eb | 1123 | if (IN_SET(c->std_output, |
9f6444eb LP |
1124 | EXEC_OUTPUT_KMSG, |
1125 | EXEC_OUTPUT_JOURNAL, | |
9f6444eb LP |
1126 | EXEC_OUTPUT_KMSG_AND_CONSOLE, |
1127 | EXEC_OUTPUT_JOURNAL_AND_CONSOLE) || | |
1128 | IN_SET(c->std_error, | |
9f6444eb LP |
1129 | EXEC_OUTPUT_KMSG, |
1130 | EXEC_OUTPUT_JOURNAL, | |
9f6444eb LP |
1131 | EXEC_OUTPUT_KMSG_AND_CONSOLE, |
1132 | EXEC_OUTPUT_JOURNAL_AND_CONSOLE)) { | |
f8b69d1d | 1133 | |
5ce70e5b | 1134 | _cleanup_free_ char *fac_str = NULL, *lvl_str = NULL; |
f8b69d1d | 1135 | |
837df140 YW |
1136 | r = log_facility_unshifted_to_string_alloc(c->syslog_priority >> 3, &fac_str); |
1137 | if (r >= 0) | |
1138 | fprintf(f, "%sSyslogFacility: %s\n", prefix, fac_str); | |
f8b69d1d | 1139 | |
837df140 YW |
1140 | r = log_level_to_string_alloc(LOG_PRI(c->syslog_priority), &lvl_str); |
1141 | if (r >= 0) | |
1142 | fprintf(f, "%sSyslogLevel: %s\n", prefix, lvl_str); | |
f8b69d1d | 1143 | } |
94f04347 | 1144 | |
d3070fbd LP |
1145 | if (c->log_level_max >= 0) { |
1146 | _cleanup_free_ char *t = NULL; | |
1147 | ||
1148 | (void) log_level_to_string_alloc(c->log_level_max, &t); | |
1149 | ||
1150 | fprintf(f, "%sLogLevelMax: %s\n", prefix, strna(t)); | |
1151 | } | |
1152 | ||
5291f26d | 1153 | if (c->log_ratelimit_interval_usec > 0) |
90fc172e AZ |
1154 | fprintf(f, |
1155 | "%sLogRateLimitIntervalSec: %s\n", | |
5291f26d | 1156 | prefix, FORMAT_TIMESPAN(c->log_ratelimit_interval_usec, USEC_PER_SEC)); |
90fc172e | 1157 | |
5ac1530e ZJS |
1158 | if (c->log_ratelimit_burst > 0) |
1159 | fprintf(f, "%sLogRateLimitBurst: %u\n", prefix, c->log_ratelimit_burst); | |
90fc172e | 1160 | |
523ea123 QD |
1161 | if (!set_isempty(c->log_filter_allowed_patterns) || !set_isempty(c->log_filter_denied_patterns)) { |
1162 | fprintf(f, "%sLogFilterPatterns:", prefix); | |
1163 | ||
1164 | char *pattern; | |
1165 | SET_FOREACH(pattern, c->log_filter_allowed_patterns) | |
1166 | fprintf(f, " %s", pattern); | |
1167 | SET_FOREACH(pattern, c->log_filter_denied_patterns) | |
1168 | fprintf(f, " ~%s", pattern); | |
1169 | fputc('\n', f); | |
1170 | } | |
1171 | ||
f7df8adb | 1172 | FOREACH_ARRAY(field, c->log_extra_fields, c->n_log_extra_fields) { |
5b10116e | 1173 | fprintf(f, "%sLogExtraFields: ", prefix); |
f7df8adb | 1174 | fwrite(field->iov_base, 1, field->iov_len, f); |
5b10116e | 1175 | fputc('\n', f); |
d3070fbd LP |
1176 | } |
1177 | ||
91dd5f7c LP |
1178 | if (c->log_namespace) |
1179 | fprintf(f, "%sLogNamespace: %s\n", prefix, c->log_namespace); | |
1180 | ||
07d46372 YW |
1181 | if (c->secure_bits) { |
1182 | _cleanup_free_ char *str = NULL; | |
1183 | ||
1184 | r = secure_bits_to_string_alloc(c->secure_bits, &str); | |
1185 | if (r >= 0) | |
1186 | fprintf(f, "%sSecure Bits: %s\n", prefix, str); | |
1187 | } | |
94f04347 | 1188 | |
3fd5190b | 1189 | if (c->capability_bounding_set != CAP_MASK_UNSET) { |
dd1f5bd0 | 1190 | _cleanup_free_ char *str = NULL; |
94f04347 | 1191 | |
8142d735 | 1192 | r = capability_set_to_string(c->capability_bounding_set, &str); |
dd1f5bd0 YW |
1193 | if (r >= 0) |
1194 | fprintf(f, "%sCapabilityBoundingSet: %s\n", prefix, str); | |
755d4b67 IP |
1195 | } |
1196 | ||
1197 | if (c->capability_ambient_set != 0) { | |
dd1f5bd0 | 1198 | _cleanup_free_ char *str = NULL; |
755d4b67 | 1199 | |
8142d735 | 1200 | r = capability_set_to_string(c->capability_ambient_set, &str); |
dd1f5bd0 YW |
1201 | if (r >= 0) |
1202 | fprintf(f, "%sAmbientCapabilities: %s\n", prefix, str); | |
94f04347 LP |
1203 | } |
1204 | ||
1205 | if (c->user) | |
f2d3769a | 1206 | fprintf(f, "%sUser: %s\n", prefix, c->user); |
94f04347 | 1207 | if (c->group) |
f2d3769a | 1208 | fprintf(f, "%sGroup: %s\n", prefix, c->group); |
94f04347 | 1209 | |
29206d46 LP |
1210 | fprintf(f, "%sDynamicUser: %s\n", prefix, yes_no(c->dynamic_user)); |
1211 | ||
ddc155b2 | 1212 | strv_dump(f, prefix, "SupplementaryGroups", c->supplementary_groups); |
94f04347 | 1213 | |
5b6319dc | 1214 | if (c->pam_name) |
f2d3769a | 1215 | fprintf(f, "%sPAMName: %s\n", prefix, c->pam_name); |
5b6319dc | 1216 | |
ddc155b2 TM |
1217 | strv_dump(f, prefix, "ReadWritePaths", c->read_write_paths); |
1218 | strv_dump(f, prefix, "ReadOnlyPaths", c->read_only_paths); | |
1219 | strv_dump(f, prefix, "InaccessiblePaths", c->inaccessible_paths); | |
1220 | strv_dump(f, prefix, "ExecPaths", c->exec_paths); | |
1221 | strv_dump(f, prefix, "NoExecPaths", c->no_exec_paths); | |
8c35c10d | 1222 | strv_dump(f, prefix, "ExecSearchPath", c->exec_search_path); |
2e22afe9 | 1223 | |
f7df8adb | 1224 | FOREACH_ARRAY(mount, c->bind_mounts, c->n_bind_mounts) |
5b10116e | 1225 | fprintf(f, "%s%s: %s%s:%s:%s\n", prefix, |
f7df8adb MY |
1226 | mount->read_only ? "BindReadOnlyPaths" : "BindPaths", |
1227 | mount->ignore_enoent ? "-": "", | |
1228 | mount->source, | |
1229 | mount->destination, | |
1230 | mount->recursive ? "rbind" : "norbind"); | |
2abd4e38 | 1231 | |
f7df8adb | 1232 | FOREACH_ARRAY(tmpfs, c->temporary_filesystems, c->n_temporary_filesystems) |
5b10116e | 1233 | fprintf(f, "%sTemporaryFileSystem: %s%s%s\n", prefix, |
f7df8adb MY |
1234 | tmpfs->path, |
1235 | isempty(tmpfs->options) ? "" : ":", | |
1236 | strempty(tmpfs->options)); | |
2abd4e38 | 1237 | |
169c1bda LP |
1238 | if (c->utmp_id) |
1239 | fprintf(f, | |
1240 | "%sUtmpIdentifier: %s\n", | |
1241 | prefix, c->utmp_id); | |
7b52a628 MS |
1242 | |
1243 | if (c->selinux_context) | |
1244 | fprintf(f, | |
5f8640fb LP |
1245 | "%sSELinuxContext: %s%s\n", |
1246 | prefix, c->selinux_context_ignore ? "-" : "", c->selinux_context); | |
17df7223 | 1247 | |
80c21aea WC |
1248 | if (c->apparmor_profile) |
1249 | fprintf(f, | |
1250 | "%sAppArmorProfile: %s%s\n", | |
1251 | prefix, c->apparmor_profile_ignore ? "-" : "", c->apparmor_profile); | |
1252 | ||
1253 | if (c->smack_process_label) | |
1254 | fprintf(f, | |
1255 | "%sSmackProcessLabel: %s%s\n", | |
1256 | prefix, c->smack_process_label_ignore ? "-" : "", c->smack_process_label); | |
1257 | ||
050f7277 | 1258 | if (c->personality != PERSONALITY_INVALID) |
ac45f971 LP |
1259 | fprintf(f, |
1260 | "%sPersonality: %s\n", | |
1261 | prefix, strna(personality_to_string(c->personality))); | |
1262 | ||
78e864e5 TM |
1263 | fprintf(f, |
1264 | "%sLockPersonality: %s\n", | |
1265 | prefix, yes_no(c->lock_personality)); | |
1266 | ||
17df7223 | 1267 | if (c->syscall_filter) { |
17df7223 | 1268 | fprintf(f, |
57183d11 | 1269 | "%sSystemCallFilter: ", |
17df7223 LP |
1270 | prefix); |
1271 | ||
6b000af4 | 1272 | if (!c->syscall_allow_list) |
17df7223 LP |
1273 | fputc('~', f); |
1274 | ||
349cc4a5 | 1275 | #if HAVE_SECCOMP |
d5a99b7c JJ |
1276 | void *id, *val; |
1277 | bool first = true; | |
90e74a66 | 1278 | HASHMAP_FOREACH_KEY(val, id, c->syscall_filter) { |
17df7223 | 1279 | _cleanup_free_ char *name = NULL; |
8cfa775f YW |
1280 | const char *errno_name = NULL; |
1281 | int num = PTR_TO_INT(val); | |
17df7223 LP |
1282 | |
1283 | if (first) | |
1284 | first = false; | |
1285 | else | |
1286 | fputc(' ', f); | |
1287 | ||
57183d11 | 1288 | name = seccomp_syscall_resolve_num_arch(SCMP_ARCH_NATIVE, PTR_TO_INT(id) - 1); |
17df7223 | 1289 | fputs(strna(name), f); |
8cfa775f YW |
1290 | |
1291 | if (num >= 0) { | |
005bfaf1 | 1292 | errno_name = seccomp_errno_or_action_to_string(num); |
8cfa775f YW |
1293 | if (errno_name) |
1294 | fprintf(f, ":%s", errno_name); | |
1295 | else | |
1296 | fprintf(f, ":%d", num); | |
1297 | } | |
17df7223 | 1298 | } |
351a19b1 | 1299 | #endif |
17df7223 LP |
1300 | |
1301 | fputc('\n', f); | |
1302 | } | |
1303 | ||
57183d11 | 1304 | if (c->syscall_archs) { |
57183d11 LP |
1305 | fprintf(f, |
1306 | "%sSystemCallArchitectures:", | |
1307 | prefix); | |
1308 | ||
349cc4a5 | 1309 | #if HAVE_SECCOMP |
d5a99b7c | 1310 | void *id; |
90e74a66 | 1311 | SET_FOREACH(id, c->syscall_archs) |
57183d11 LP |
1312 | fprintf(f, " %s", strna(seccomp_arch_to_string(PTR_TO_UINT32(id) - 1))); |
1313 | #endif | |
1314 | fputc('\n', f); | |
1315 | } | |
1316 | ||
add00535 LP |
1317 | if (exec_context_restrict_namespaces_set(c)) { |
1318 | _cleanup_free_ char *s = NULL; | |
1319 | ||
86c2a9f1 | 1320 | r = namespace_flags_to_string(c->restrict_namespaces, &s); |
add00535 LP |
1321 | if (r >= 0) |
1322 | fprintf(f, "%sRestrictNamespaces: %s\n", | |
dd0395b5 | 1323 | prefix, strna(s)); |
add00535 LP |
1324 | } |
1325 | ||
b1994387 | 1326 | #if HAVE_LIBBPF |
8fe84dc8 YW |
1327 | if (exec_context_restrict_filesystems_set(c)) { |
1328 | char *fs; | |
1329 | SET_FOREACH(fs, c->restrict_filesystems) | |
1330 | fprintf(f, "%sRestrictFileSystems: %s\n", prefix, fs); | |
1331 | } | |
b1994387 ILG |
1332 | #endif |
1333 | ||
a8d08f39 LP |
1334 | if (c->network_namespace_path) |
1335 | fprintf(f, | |
1336 | "%sNetworkNamespacePath: %s\n", | |
1337 | prefix, c->network_namespace_path); | |
1338 | ||
3df90f24 | 1339 | if (c->syscall_errno > 0) { |
3df90f24 YW |
1340 | fprintf(f, "%sSystemCallErrorNumber: ", prefix); |
1341 | ||
005bfaf1 | 1342 | #if HAVE_SECCOMP |
d5a99b7c | 1343 | const char *errno_name = seccomp_errno_or_action_to_string(c->syscall_errno); |
3df90f24 | 1344 | if (errno_name) |
005bfaf1 | 1345 | fputs(errno_name, f); |
3df90f24 | 1346 | else |
005bfaf1 TM |
1347 | fprintf(f, "%d", c->syscall_errno); |
1348 | #endif | |
1349 | fputc('\n', f); | |
3df90f24 | 1350 | } |
b3d13314 | 1351 | |
f7df8adb | 1352 | FOREACH_ARRAY(mount, c->mount_images, c->n_mount_images) { |
79e20ceb | 1353 | fprintf(f, "%sMountImages: %s%s:%s", prefix, |
f7df8adb MY |
1354 | mount->ignore_enoent ? "-": "", |
1355 | mount->source, | |
1356 | mount->destination); | |
1357 | LIST_FOREACH(mount_options, o, mount->mount_options) | |
79e20ceb | 1358 | fprintf(f, ":%s:%s", |
427353f6 | 1359 | partition_designator_to_string(o->partition_designator), |
79e20ceb | 1360 | strempty(o->options)); |
427353f6 LB |
1361 | fprintf(f, "\n"); |
1362 | } | |
93f59701 | 1363 | |
f7df8adb | 1364 | FOREACH_ARRAY(mount, c->extension_images, c->n_extension_images) { |
93f59701 | 1365 | fprintf(f, "%sExtensionImages: %s%s", prefix, |
f7df8adb MY |
1366 | mount->ignore_enoent ? "-": "", |
1367 | mount->source); | |
1368 | LIST_FOREACH(mount_options, o, mount->mount_options) | |
93f59701 LB |
1369 | fprintf(f, ":%s:%s", |
1370 | partition_designator_to_string(o->partition_designator), | |
1371 | strempty(o->options)); | |
1372 | fprintf(f, "\n"); | |
1373 | } | |
a07b9926 LB |
1374 | |
1375 | strv_dump(f, prefix, "ExtensionDirectories", c->extension_directories); | |
5cb5a6ff LP |
1376 | } |
1377 | ||
34cf6c43 | 1378 | bool exec_context_maintains_privileges(const ExecContext *c) { |
a931ad47 LP |
1379 | assert(c); |
1380 | ||
61233823 | 1381 | /* Returns true if the process forked off would run under |
a931ad47 LP |
1382 | * an unchanged UID or as root. */ |
1383 | ||
1384 | if (!c->user) | |
1385 | return true; | |
1386 | ||
1387 | if (streq(c->user, "root") || streq(c->user, "0")) | |
1388 | return true; | |
1389 | ||
1390 | return false; | |
1391 | } | |
1392 | ||
34cf6c43 | 1393 | int exec_context_get_effective_ioprio(const ExecContext *c) { |
7f452159 LP |
1394 | int p; |
1395 | ||
1396 | assert(c); | |
1397 | ||
1398 | if (c->ioprio_set) | |
1399 | return c->ioprio; | |
1400 | ||
1401 | p = ioprio_get(IOPRIO_WHO_PROCESS, 0); | |
1402 | if (p < 0) | |
0692548c | 1403 | return IOPRIO_DEFAULT_CLASS_AND_PRIO; |
7f452159 | 1404 | |
8b330d7d | 1405 | return ioprio_normalize(p); |
7f452159 LP |
1406 | } |
1407 | ||
5e98086d ZJS |
1408 | bool exec_context_get_effective_mount_apivfs(const ExecContext *c) { |
1409 | assert(c); | |
1410 | ||
61198784 | 1411 | /* Explicit setting wins */ |
5e98086d ZJS |
1412 | if (c->mount_apivfs_set) |
1413 | return c->mount_apivfs; | |
1414 | ||
61198784 | 1415 | /* Default to "yes" if root directory or image are specified */ |
74e12520 | 1416 | if (exec_context_with_rootfs(c)) |
61198784 ZJS |
1417 | return true; |
1418 | ||
5e98086d ZJS |
1419 | return false; |
1420 | } | |
1421 | ||
d3070fbd | 1422 | void exec_context_free_log_extra_fields(ExecContext *c) { |
d3070fbd LP |
1423 | assert(c); |
1424 | ||
f7df8adb MY |
1425 | FOREACH_ARRAY(field, c->log_extra_fields, c->n_log_extra_fields) |
1426 | free(field->iov_base); | |
1427 | ||
d3070fbd LP |
1428 | c->log_extra_fields = mfree(c->log_extra_fields); |
1429 | c->n_log_extra_fields = 0; | |
1430 | } | |
1431 | ||
6f765baf | 1432 | void exec_context_revert_tty(ExecContext *c) { |
254d1313 | 1433 | _cleanup_close_ int fd = -EBADF; |
0ba976e8 LP |
1434 | const char *path; |
1435 | struct stat st; | |
6f765baf LP |
1436 | int r; |
1437 | ||
1438 | assert(c); | |
1439 | ||
1440 | /* First, reset the TTY (possibly kicking everybody else from the TTY) */ | |
1441 | exec_context_tty_reset(c, NULL); | |
1442 | ||
1443 | /* And then undo what chown_terminal() did earlier. Note that we only do this if we have a path | |
1444 | * configured. If the TTY was passed to us as file descriptor we assume the TTY is opened and managed | |
1445 | * by whoever passed it to us and thus knows better when and how to chmod()/chown() it back. */ | |
0ba976e8 LP |
1446 | if (!exec_context_may_touch_tty(c)) |
1447 | return; | |
6f765baf | 1448 | |
0ba976e8 LP |
1449 | path = exec_context_tty_path(c); |
1450 | if (!path) | |
1451 | return; | |
6f765baf | 1452 | |
0ba976e8 LP |
1453 | fd = open(path, O_PATH|O_CLOEXEC); |
1454 | if (fd < 0) | |
1455 | return (void) log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_WARNING, errno, | |
1456 | "Failed to open TTY inode of '%s' to adjust ownership/access mode, ignoring: %m", | |
1457 | path); | |
1458 | ||
1459 | if (fstat(fd, &st) < 0) | |
1460 | return (void) log_warning_errno(errno, "Failed to stat TTY '%s', ignoring: %m", path); | |
1461 | ||
1462 | /* Let's add a superficial check that we only do this for stuff that looks like a TTY. We only check | |
1463 | * if things are a character device, since a proper check either means we'd have to open the TTY and | |
1464 | * use isatty(), but we'd rather not do that since opening TTYs comes with all kinds of side-effects | |
1465 | * and is slow. Or we'd have to hardcode dev_t major information, which we'd rather avoid. Why bother | |
1466 | * with this at all? → https://github.com/systemd/systemd/issues/19213 */ | |
1467 | if (!S_ISCHR(st.st_mode)) | |
1468 | return log_warning("Configured TTY '%s' is not actually a character device, ignoring.", path); | |
1469 | ||
1470 | r = fchmod_and_chown(fd, TTY_MODE, 0, TTY_GID); | |
1471 | if (r < 0) | |
1472 | log_warning_errno(r, "Failed to reset TTY ownership/access mode of %s, ignoring: %m", path); | |
6f765baf LP |
1473 | } |
1474 | ||
4c2f5842 LP |
1475 | int exec_context_get_clean_directories( |
1476 | ExecContext *c, | |
1477 | char **prefix, | |
1478 | ExecCleanMask mask, | |
1479 | char ***ret) { | |
1480 | ||
1481 | _cleanup_strv_free_ char **l = NULL; | |
4c2f5842 LP |
1482 | int r; |
1483 | ||
1484 | assert(c); | |
1485 | assert(prefix); | |
1486 | assert(ret); | |
1487 | ||
5b10116e | 1488 | for (ExecDirectoryType t = 0; t < _EXEC_DIRECTORY_TYPE_MAX; t++) { |
4c2f5842 LP |
1489 | if (!FLAGS_SET(mask, 1U << t)) |
1490 | continue; | |
1491 | ||
1492 | if (!prefix[t]) | |
1493 | continue; | |
1494 | ||
f7df8adb | 1495 | FOREACH_ARRAY(i, c->directories[t].items, c->directories[t].n_items) { |
4c2f5842 LP |
1496 | char *j; |
1497 | ||
f7df8adb | 1498 | j = path_join(prefix[t], i->path); |
4c2f5842 LP |
1499 | if (!j) |
1500 | return -ENOMEM; | |
1501 | ||
1502 | r = strv_consume(&l, j); | |
1503 | if (r < 0) | |
1504 | return r; | |
7f622a19 YW |
1505 | |
1506 | /* Also remove private directories unconditionally. */ | |
1507 | if (t != EXEC_DIRECTORY_CONFIGURATION) { | |
f7df8adb | 1508 | j = path_join(prefix[t], "private", i->path); |
211a3d87 LB |
1509 | if (!j) |
1510 | return -ENOMEM; | |
1511 | ||
1512 | r = strv_consume(&l, j); | |
1513 | if (r < 0) | |
1514 | return r; | |
1515 | } | |
1516 | ||
f7df8adb | 1517 | STRV_FOREACH(symlink, i->symlinks) { |
211a3d87 | 1518 | j = path_join(prefix[t], *symlink); |
7f622a19 YW |
1519 | if (!j) |
1520 | return -ENOMEM; | |
1521 | ||
1522 | r = strv_consume(&l, j); | |
1523 | if (r < 0) | |
1524 | return r; | |
1525 | } | |
4c2f5842 LP |
1526 | } |
1527 | } | |
1528 | ||
1529 | *ret = TAKE_PTR(l); | |
1530 | return 0; | |
1531 | } | |
1532 | ||
1533 | int exec_context_get_clean_mask(ExecContext *c, ExecCleanMask *ret) { | |
1534 | ExecCleanMask mask = 0; | |
1535 | ||
1536 | assert(c); | |
1537 | assert(ret); | |
1538 | ||
1539 | for (ExecDirectoryType t = 0; t < _EXEC_DIRECTORY_TYPE_MAX; t++) | |
211a3d87 | 1540 | if (c->directories[t].n_items > 0) |
4c2f5842 LP |
1541 | mask |= 1U << t; |
1542 | ||
1543 | *ret = mask; | |
1544 | return 0; | |
1545 | } | |
1546 | ||
ef44aa83 DDM |
1547 | int exec_context_get_oom_score_adjust(const ExecContext *c) { |
1548 | int n = 0, r; | |
1549 | ||
1550 | assert(c); | |
1551 | ||
1552 | if (c->oom_score_adjust_set) | |
1553 | return c->oom_score_adjust; | |
1554 | ||
1555 | r = get_oom_score_adjust(&n); | |
1556 | if (r < 0) | |
1557 | log_debug_errno(r, "Failed to read /proc/self/oom_score_adj, ignoring: %m"); | |
1558 | ||
1559 | return n; | |
1560 | } | |
1561 | ||
1562 | uint64_t exec_context_get_coredump_filter(const ExecContext *c) { | |
1563 | _cleanup_free_ char *t = NULL; | |
1564 | uint64_t n = COREDUMP_FILTER_MASK_DEFAULT; | |
1565 | int r; | |
1566 | ||
1567 | assert(c); | |
1568 | ||
1569 | if (c->coredump_filter_set) | |
1570 | return c->coredump_filter; | |
1571 | ||
1572 | r = read_one_line_file("/proc/self/coredump_filter", &t); | |
1573 | if (r < 0) | |
1574 | log_debug_errno(r, "Failed to read /proc/self/coredump_filter, ignoring: %m"); | |
1575 | else { | |
1576 | r = safe_atoux64(t, &n); | |
1577 | if (r < 0) | |
1578 | log_debug_errno(r, "Failed to parse \"%s\" from /proc/self/coredump_filter, ignoring: %m", t); | |
1579 | } | |
1580 | ||
1581 | return n; | |
1582 | } | |
1583 | ||
1584 | int exec_context_get_nice(const ExecContext *c) { | |
1585 | int n; | |
1586 | ||
1587 | assert(c); | |
1588 | ||
1589 | if (c->nice_set) | |
1590 | return c->nice; | |
1591 | ||
1592 | errno = 0; | |
1593 | n = getpriority(PRIO_PROCESS, 0); | |
1594 | if (errno > 0) { | |
1595 | log_debug_errno(errno, "Failed to get process nice value, ignoring: %m"); | |
1596 | n = 0; | |
1597 | } | |
1598 | ||
1599 | return n; | |
1600 | } | |
1601 | ||
1602 | int exec_context_get_cpu_sched_policy(const ExecContext *c) { | |
1603 | int n; | |
1604 | ||
1605 | assert(c); | |
1606 | ||
1607 | if (c->cpu_sched_set) | |
1608 | return c->cpu_sched_policy; | |
1609 | ||
1610 | n = sched_getscheduler(0); | |
1611 | if (n < 0) | |
1612 | log_debug_errno(errno, "Failed to get scheduler policy, ignoring: %m"); | |
1613 | ||
1614 | return n < 0 ? SCHED_OTHER : n; | |
1615 | } | |
1616 | ||
1617 | int exec_context_get_cpu_sched_priority(const ExecContext *c) { | |
1618 | struct sched_param p = {}; | |
1619 | int r; | |
1620 | ||
1621 | assert(c); | |
1622 | ||
1623 | if (c->cpu_sched_set) | |
1624 | return c->cpu_sched_priority; | |
1625 | ||
1626 | r = sched_getparam(0, &p); | |
1627 | if (r < 0) | |
1628 | log_debug_errno(errno, "Failed to get scheduler priority, ignoring: %m"); | |
1629 | ||
1630 | return r >= 0 ? p.sched_priority : 0; | |
1631 | } | |
1632 | ||
1633 | uint64_t exec_context_get_timer_slack_nsec(const ExecContext *c) { | |
1634 | int r; | |
1635 | ||
1636 | assert(c); | |
1637 | ||
1638 | if (c->timer_slack_nsec != NSEC_INFINITY) | |
1639 | return c->timer_slack_nsec; | |
1640 | ||
1641 | r = prctl(PR_GET_TIMERSLACK); | |
1642 | if (r < 0) | |
1643 | log_debug_errno(r, "Failed to get timer slack, ignoring: %m"); | |
1644 | ||
1645 | return (uint64_t) MAX(r, 0); | |
1646 | } | |
1647 | ||
1648 | char** exec_context_get_syscall_filter(const ExecContext *c) { | |
1649 | _cleanup_strv_free_ char **l = NULL; | |
1650 | ||
1651 | assert(c); | |
1652 | ||
1653 | #if HAVE_SECCOMP | |
1654 | void *id, *val; | |
1655 | HASHMAP_FOREACH_KEY(val, id, c->syscall_filter) { | |
1656 | _cleanup_free_ char *name = NULL; | |
1657 | const char *e = NULL; | |
1658 | char *s; | |
1659 | int num = PTR_TO_INT(val); | |
1660 | ||
1661 | if (c->syscall_allow_list && num >= 0) | |
1662 | /* syscall with num >= 0 in allow-list is denied. */ | |
1663 | continue; | |
1664 | ||
1665 | name = seccomp_syscall_resolve_num_arch(SCMP_ARCH_NATIVE, PTR_TO_INT(id) - 1); | |
1666 | if (!name) | |
1667 | continue; | |
1668 | ||
1669 | if (num >= 0) { | |
1670 | e = seccomp_errno_or_action_to_string(num); | |
1671 | if (e) { | |
1672 | s = strjoin(name, ":", e); | |
1673 | if (!s) | |
1674 | return NULL; | |
1675 | } else { | |
1676 | if (asprintf(&s, "%s:%d", name, num) < 0) | |
1677 | return NULL; | |
1678 | } | |
1679 | } else | |
1680 | s = TAKE_PTR(name); | |
1681 | ||
1682 | if (strv_consume(&l, s) < 0) | |
1683 | return NULL; | |
1684 | } | |
1685 | ||
1686 | strv_sort(l); | |
1687 | #endif | |
1688 | ||
1689 | return l ? TAKE_PTR(l) : strv_new(NULL); | |
1690 | } | |
1691 | ||
1692 | char** exec_context_get_syscall_archs(const ExecContext *c) { | |
1693 | _cleanup_strv_free_ char **l = NULL; | |
1694 | ||
1695 | assert(c); | |
1696 | ||
1697 | #if HAVE_SECCOMP | |
1698 | void *id; | |
1699 | SET_FOREACH(id, c->syscall_archs) { | |
1700 | const char *name; | |
1701 | ||
1702 | name = seccomp_arch_to_string(PTR_TO_UINT32(id) - 1); | |
1703 | if (!name) | |
1704 | continue; | |
1705 | ||
1706 | if (strv_extend(&l, name) < 0) | |
1707 | return NULL; | |
1708 | } | |
1709 | ||
1710 | strv_sort(l); | |
1711 | #endif | |
1712 | ||
1713 | return l ? TAKE_PTR(l) : strv_new(NULL); | |
1714 | } | |
1715 | ||
1716 | char** exec_context_get_syscall_log(const ExecContext *c) { | |
1717 | _cleanup_strv_free_ char **l = NULL; | |
1718 | ||
1719 | assert(c); | |
1720 | ||
1721 | #if HAVE_SECCOMP | |
1722 | void *id, *val; | |
1723 | HASHMAP_FOREACH_KEY(val, id, c->syscall_log) { | |
1724 | char *name = NULL; | |
1725 | ||
1726 | name = seccomp_syscall_resolve_num_arch(SCMP_ARCH_NATIVE, PTR_TO_INT(id) - 1); | |
1727 | if (!name) | |
1728 | continue; | |
1729 | ||
1730 | if (strv_consume(&l, name) < 0) | |
1731 | return NULL; | |
1732 | } | |
1733 | ||
1734 | strv_sort(l); | |
1735 | #endif | |
1736 | ||
1737 | return l ? TAKE_PTR(l) : strv_new(NULL); | |
1738 | } | |
1739 | ||
1740 | char** exec_context_get_address_families(const ExecContext *c) { | |
1741 | _cleanup_strv_free_ char **l = NULL; | |
1742 | void *af; | |
1743 | ||
1744 | assert(c); | |
1745 | ||
1746 | SET_FOREACH(af, c->address_families) { | |
1747 | const char *name; | |
1748 | ||
1749 | name = af_to_name(PTR_TO_INT(af)); | |
1750 | if (!name) | |
1751 | continue; | |
1752 | ||
1753 | if (strv_extend(&l, name) < 0) | |
1754 | return NULL; | |
1755 | } | |
1756 | ||
1757 | strv_sort(l); | |
1758 | ||
1759 | return l ? TAKE_PTR(l) : strv_new(NULL); | |
1760 | } | |
1761 | ||
1762 | char** exec_context_get_restrict_filesystems(const ExecContext *c) { | |
1763 | _cleanup_strv_free_ char **l = NULL; | |
1764 | ||
1765 | assert(c); | |
1766 | ||
1767 | #if HAVE_LIBBPF | |
1768 | l = set_get_strv(c->restrict_filesystems); | |
1769 | if (!l) | |
1770 | return NULL; | |
1771 | ||
1772 | strv_sort(l); | |
1773 | #endif | |
1774 | ||
1775 | return l ? TAKE_PTR(l) : strv_new(NULL); | |
1776 | } | |
1777 | ||
b58b4116 | 1778 | void exec_status_start(ExecStatus *s, pid_t pid) { |
034c6ed7 | 1779 | assert(s); |
5cb5a6ff | 1780 | |
2ed26ed0 LP |
1781 | *s = (ExecStatus) { |
1782 | .pid = pid, | |
1783 | }; | |
1784 | ||
b58b4116 LP |
1785 | dual_timestamp_get(&s->start_timestamp); |
1786 | } | |
1787 | ||
34cf6c43 | 1788 | void exec_status_exit(ExecStatus *s, const ExecContext *context, pid_t pid, int code, int status) { |
b58b4116 LP |
1789 | assert(s); |
1790 | ||
d46b79bb | 1791 | if (s->pid != pid) |
2ed26ed0 LP |
1792 | *s = (ExecStatus) { |
1793 | .pid = pid, | |
1794 | }; | |
b58b4116 | 1795 | |
63983207 | 1796 | dual_timestamp_get(&s->exit_timestamp); |
9fb86720 | 1797 | |
034c6ed7 LP |
1798 | s->code = code; |
1799 | s->status = status; | |
169c1bda | 1800 | |
6f765baf LP |
1801 | if (context && context->utmp_id) |
1802 | (void) utmp_put_dead_process(context->utmp_id, pid, code, status); | |
9fb86720 LP |
1803 | } |
1804 | ||
6a1d4d9f LP |
1805 | void exec_status_reset(ExecStatus *s) { |
1806 | assert(s); | |
1807 | ||
1808 | *s = (ExecStatus) {}; | |
1809 | } | |
1810 | ||
34cf6c43 | 1811 | void exec_status_dump(const ExecStatus *s, FILE *f, const char *prefix) { |
9fb86720 LP |
1812 | assert(s); |
1813 | assert(f); | |
1814 | ||
9fb86720 LP |
1815 | if (s->pid <= 0) |
1816 | return; | |
1817 | ||
4c940960 LP |
1818 | prefix = strempty(prefix); |
1819 | ||
9fb86720 | 1820 | fprintf(f, |
ccd06097 ZJS |
1821 | "%sPID: "PID_FMT"\n", |
1822 | prefix, s->pid); | |
9fb86720 | 1823 | |
af9d16e1 | 1824 | if (dual_timestamp_is_set(&s->start_timestamp)) |
9fb86720 LP |
1825 | fprintf(f, |
1826 | "%sStart Timestamp: %s\n", | |
04f5c018 | 1827 | prefix, FORMAT_TIMESTAMP(s->start_timestamp.realtime)); |
9fb86720 | 1828 | |
af9d16e1 | 1829 | if (dual_timestamp_is_set(&s->exit_timestamp)) |
9fb86720 LP |
1830 | fprintf(f, |
1831 | "%sExit Timestamp: %s\n" | |
1832 | "%sExit Code: %s\n" | |
1833 | "%sExit Status: %i\n", | |
04f5c018 | 1834 | prefix, FORMAT_TIMESTAMP(s->exit_timestamp.realtime), |
9fb86720 LP |
1835 | prefix, sigchld_code_to_string(s->code), |
1836 | prefix, s->status); | |
5cb5a6ff | 1837 | } |
44d8db9e | 1838 | |
34cf6c43 | 1839 | static void exec_command_dump(ExecCommand *c, FILE *f, const char *prefix) { |
e1d75803 | 1840 | _cleanup_free_ char *cmd = NULL; |
4c940960 | 1841 | const char *prefix2; |
44d8db9e LP |
1842 | |
1843 | assert(c); | |
1844 | assert(f); | |
1845 | ||
4c940960 | 1846 | prefix = strempty(prefix); |
63c372cb | 1847 | prefix2 = strjoina(prefix, "\t"); |
44d8db9e | 1848 | |
4ef15008 | 1849 | cmd = quote_command_line(c->argv, SHELL_ESCAPE_EMPTY); |
38553034 | 1850 | |
44d8db9e LP |
1851 | fprintf(f, |
1852 | "%sCommand Line: %s\n", | |
38553034 | 1853 | prefix, strnull(cmd)); |
44d8db9e | 1854 | |
9fb86720 | 1855 | exec_status_dump(&c->exec_status, f, prefix2); |
44d8db9e LP |
1856 | } |
1857 | ||
1858 | void exec_command_dump_list(ExecCommand *c, FILE *f, const char *prefix) { | |
1859 | assert(f); | |
1860 | ||
4c940960 | 1861 | prefix = strempty(prefix); |
44d8db9e | 1862 | |
03677889 YW |
1863 | LIST_FOREACH(command, i, c) |
1864 | exec_command_dump(i, f, prefix); | |
44d8db9e | 1865 | } |
94f04347 | 1866 | |
a6a80b4f LP |
1867 | void exec_command_append_list(ExecCommand **l, ExecCommand *e) { |
1868 | ExecCommand *end; | |
1869 | ||
1870 | assert(l); | |
1871 | assert(e); | |
1872 | ||
1873 | if (*l) { | |
35b8ca3a | 1874 | /* It's kind of important, that we keep the order here */ |
cc232fa0 | 1875 | end = LIST_FIND_TAIL(command, *l); |
71fda00f | 1876 | LIST_INSERT_AFTER(command, *l, end, e); |
a6a80b4f | 1877 | } else |
3ff67ec4 | 1878 | *l = e; |
a6a80b4f LP |
1879 | } |
1880 | ||
26fd040d LP |
1881 | int exec_command_set(ExecCommand *c, const char *path, ...) { |
1882 | va_list ap; | |
1883 | char **l, *p; | |
1884 | ||
1885 | assert(c); | |
1886 | assert(path); | |
1887 | ||
1888 | va_start(ap, path); | |
1889 | l = strv_new_ap(path, ap); | |
1890 | va_end(ap); | |
1891 | ||
1892 | if (!l) | |
1893 | return -ENOMEM; | |
1894 | ||
250a918d LP |
1895 | p = strdup(path); |
1896 | if (!p) { | |
26fd040d LP |
1897 | strv_free(l); |
1898 | return -ENOMEM; | |
1899 | } | |
1900 | ||
6897dfe8 | 1901 | free_and_replace(c->path, p); |
26fd040d | 1902 | |
130d3d22 | 1903 | return strv_free_and_replace(c->argv, l); |
26fd040d LP |
1904 | } |
1905 | ||
86b23b07 | 1906 | int exec_command_append(ExecCommand *c, const char *path, ...) { |
e63ff941 | 1907 | _cleanup_strv_free_ char **l = NULL; |
86b23b07 | 1908 | va_list ap; |
86b23b07 JS |
1909 | int r; |
1910 | ||
1911 | assert(c); | |
1912 | assert(path); | |
1913 | ||
1914 | va_start(ap, path); | |
1915 | l = strv_new_ap(path, ap); | |
1916 | va_end(ap); | |
1917 | ||
1918 | if (!l) | |
1919 | return -ENOMEM; | |
1920 | ||
e287086b | 1921 | r = strv_extend_strv(&c->argv, l, false); |
e63ff941 | 1922 | if (r < 0) |
86b23b07 | 1923 | return r; |
86b23b07 JS |
1924 | |
1925 | return 0; | |
1926 | } | |
1927 | ||
437f3e35 LP |
1928 | static char *destroy_tree(char *path) { |
1929 | if (!path) | |
1930 | return NULL; | |
9c0c6701 | 1931 | |
437f3e35 LP |
1932 | if (!path_equal(path, RUN_SYSTEMD_EMPTY)) { |
1933 | log_debug("Spawning process to nuke '%s'", path); | |
9c0c6701 | 1934 | |
437f3e35 LP |
1935 | (void) asynchronous_rm_rf(path, REMOVE_ROOT|REMOVE_SUBVOLUME|REMOVE_PHYSICAL); |
1936 | } | |
9c0c6701 | 1937 | |
437f3e35 | 1938 | return mfree(path); |
9c0c6701 DDM |
1939 | } |
1940 | ||
bb5232b6 | 1941 | void exec_shared_runtime_done(ExecSharedRuntime *rt) { |
e8a565cb | 1942 | if (!rt) |
bb5232b6 | 1943 | return; |
e8a565cb YW |
1944 | |
1945 | if (rt->manager) | |
e76506b7 | 1946 | (void) hashmap_remove(rt->manager->exec_shared_runtime_by_id, rt->id); |
e8a565cb | 1947 | |
e52a696a DDM |
1948 | rt->id = mfree(rt->id); |
1949 | rt->tmp_dir = mfree(rt->tmp_dir); | |
1950 | rt->var_tmp_dir = mfree(rt->var_tmp_dir); | |
1951 | safe_close_pair(rt->netns_storage_socket); | |
1952 | safe_close_pair(rt->ipcns_storage_socket); | |
bb5232b6 LB |
1953 | } |
1954 | ||
1955 | static ExecSharedRuntime* exec_shared_runtime_free(ExecSharedRuntime *rt) { | |
1956 | exec_shared_runtime_done(rt); | |
1957 | ||
e52a696a DDM |
1958 | return mfree(rt); |
1959 | } | |
1960 | ||
1961 | DEFINE_TRIVIAL_UNREF_FUNC(ExecSharedRuntime, exec_shared_runtime, exec_shared_runtime_free); | |
1962 | DEFINE_TRIVIAL_CLEANUP_FUNC(ExecSharedRuntime*, exec_shared_runtime_free); | |
1963 | ||
1964 | ExecSharedRuntime* exec_shared_runtime_destroy(ExecSharedRuntime *rt) { | |
e52a696a DDM |
1965 | if (!rt) |
1966 | return NULL; | |
1967 | ||
1968 | assert(rt->n_ref > 0); | |
1969 | rt->n_ref--; | |
1970 | ||
1971 | if (rt->n_ref > 0) | |
1972 | return NULL; | |
56a13a49 | 1973 | |
437f3e35 LP |
1974 | rt->tmp_dir = destroy_tree(rt->tmp_dir); |
1975 | rt->var_tmp_dir = destroy_tree(rt->var_tmp_dir); | |
e8a565cb | 1976 | |
e52a696a | 1977 | return exec_shared_runtime_free(rt); |
e8a565cb YW |
1978 | } |
1979 | ||
e76506b7 | 1980 | static int exec_shared_runtime_allocate(ExecSharedRuntime **ret, const char *id) { |
56a13a49 | 1981 | _cleanup_free_ char *id_copy = NULL; |
e76506b7 | 1982 | ExecSharedRuntime *n; |
613b411c | 1983 | |
8e8009dc | 1984 | assert(ret); |
613b411c | 1985 | |
56a13a49 ZJS |
1986 | id_copy = strdup(id); |
1987 | if (!id_copy) | |
1988 | return -ENOMEM; | |
1989 | ||
e76506b7 | 1990 | n = new(ExecSharedRuntime, 1); |
8e8009dc | 1991 | if (!n) |
613b411c LP |
1992 | return -ENOMEM; |
1993 | ||
e76506b7 | 1994 | *n = (ExecSharedRuntime) { |
56a13a49 | 1995 | .id = TAKE_PTR(id_copy), |
19ee48a6 YW |
1996 | .netns_storage_socket = PIPE_EBADF, |
1997 | .ipcns_storage_socket = PIPE_EBADF, | |
8e8009dc LP |
1998 | }; |
1999 | ||
2000 | *ret = n; | |
613b411c LP |
2001 | return 0; |
2002 | } | |
2003 | ||
e76506b7 | 2004 | static int exec_shared_runtime_add( |
e8a565cb YW |
2005 | Manager *m, |
2006 | const char *id, | |
56a13a49 ZJS |
2007 | char **tmp_dir, |
2008 | char **var_tmp_dir, | |
2009 | int netns_storage_socket[2], | |
a70581ff | 2010 | int ipcns_storage_socket[2], |
e76506b7 | 2011 | ExecSharedRuntime **ret) { |
e8a565cb | 2012 | |
e76506b7 | 2013 | _cleanup_(exec_shared_runtime_freep) ExecSharedRuntime *rt = NULL; |
613b411c LP |
2014 | int r; |
2015 | ||
e8a565cb | 2016 | assert(m); |
613b411c LP |
2017 | assert(id); |
2018 | ||
a70581ff | 2019 | /* tmp_dir, var_tmp_dir, {net,ipc}ns_storage_socket fds are donated on success */ |
56a13a49 | 2020 | |
e76506b7 | 2021 | r = exec_shared_runtime_allocate(&rt, id); |
613b411c LP |
2022 | if (r < 0) |
2023 | return r; | |
2024 | ||
e76506b7 | 2025 | r = hashmap_ensure_put(&m->exec_shared_runtime_by_id, &string_hash_ops, rt->id, rt); |
56a13a49 ZJS |
2026 | if (r < 0) |
2027 | return r; | |
e8a565cb | 2028 | |
56a13a49 ZJS |
2029 | assert(!!rt->tmp_dir == !!rt->var_tmp_dir); /* We require both to be set together */ |
2030 | rt->tmp_dir = TAKE_PTR(*tmp_dir); | |
2031 | rt->var_tmp_dir = TAKE_PTR(*var_tmp_dir); | |
e8a565cb YW |
2032 | |
2033 | if (netns_storage_socket) { | |
56a13a49 ZJS |
2034 | rt->netns_storage_socket[0] = TAKE_FD(netns_storage_socket[0]); |
2035 | rt->netns_storage_socket[1] = TAKE_FD(netns_storage_socket[1]); | |
613b411c LP |
2036 | } |
2037 | ||
a70581ff XR |
2038 | if (ipcns_storage_socket) { |
2039 | rt->ipcns_storage_socket[0] = TAKE_FD(ipcns_storage_socket[0]); | |
2040 | rt->ipcns_storage_socket[1] = TAKE_FD(ipcns_storage_socket[1]); | |
2041 | } | |
2042 | ||
e8a565cb YW |
2043 | rt->manager = m; |
2044 | ||
2045 | if (ret) | |
2046 | *ret = rt; | |
e76506b7 | 2047 | /* do not remove created ExecSharedRuntime object when the operation succeeds. */ |
56a13a49 | 2048 | TAKE_PTR(rt); |
e8a565cb YW |
2049 | return 0; |
2050 | } | |
2051 | ||
e76506b7 | 2052 | static int exec_shared_runtime_make( |
74aaf59b LP |
2053 | Manager *m, |
2054 | const ExecContext *c, | |
2055 | const char *id, | |
e76506b7 | 2056 | ExecSharedRuntime **ret) { |
74aaf59b | 2057 | |
56a13a49 | 2058 | _cleanup_(namespace_cleanup_tmpdirp) char *tmp_dir = NULL, *var_tmp_dir = NULL; |
19ee48a6 | 2059 | _cleanup_close_pair_ int netns_storage_socket[2] = PIPE_EBADF, ipcns_storage_socket[2] = PIPE_EBADF; |
e8a565cb YW |
2060 | int r; |
2061 | ||
2062 | assert(m); | |
2063 | assert(c); | |
2064 | assert(id); | |
2065 | ||
e76506b7 | 2066 | /* It is not necessary to create ExecSharedRuntime object. */ |
fde36d25 | 2067 | if (!exec_needs_network_namespace(c) && !exec_needs_ipc_namespace(c) && !c->private_tmp) { |
74aaf59b | 2068 | *ret = NULL; |
e8a565cb | 2069 | return 0; |
74aaf59b | 2070 | } |
e8a565cb | 2071 | |
efa2f3a1 TM |
2072 | if (c->private_tmp && |
2073 | !(prefixed_path_strv_contains(c->inaccessible_paths, "/tmp") && | |
2074 | (prefixed_path_strv_contains(c->inaccessible_paths, "/var/tmp") || | |
2075 | prefixed_path_strv_contains(c->inaccessible_paths, "/var")))) { | |
e8a565cb | 2076 | r = setup_tmp_dirs(id, &tmp_dir, &var_tmp_dir); |
613b411c LP |
2077 | if (r < 0) |
2078 | return r; | |
2079 | } | |
2080 | ||
fbbb9697 | 2081 | if (exec_needs_network_namespace(c)) { |
e8a565cb YW |
2082 | if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, netns_storage_socket) < 0) |
2083 | return -errno; | |
2084 | } | |
2085 | ||
fde36d25 | 2086 | if (exec_needs_ipc_namespace(c)) { |
a70581ff XR |
2087 | if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, ipcns_storage_socket) < 0) |
2088 | return -errno; | |
2089 | } | |
2090 | ||
e76506b7 | 2091 | r = exec_shared_runtime_add(m, id, &tmp_dir, &var_tmp_dir, netns_storage_socket, ipcns_storage_socket, ret); |
e8a565cb YW |
2092 | if (r < 0) |
2093 | return r; | |
2094 | ||
613b411c LP |
2095 | return 1; |
2096 | } | |
2097 | ||
e76506b7 DDM |
2098 | int exec_shared_runtime_acquire(Manager *m, const ExecContext *c, const char *id, bool create, ExecSharedRuntime **ret) { |
2099 | ExecSharedRuntime *rt; | |
e8a565cb | 2100 | int r; |
613b411c | 2101 | |
e8a565cb YW |
2102 | assert(m); |
2103 | assert(id); | |
2104 | assert(ret); | |
2105 | ||
e76506b7 | 2106 | rt = hashmap_get(m->exec_shared_runtime_by_id, id); |
e8a565cb | 2107 | if (rt) |
e76506b7 | 2108 | /* We already have an ExecSharedRuntime object, let's increase the ref count and reuse it */ |
e8a565cb YW |
2109 | goto ref; |
2110 | ||
74aaf59b LP |
2111 | if (!create) { |
2112 | *ret = NULL; | |
e8a565cb | 2113 | return 0; |
74aaf59b | 2114 | } |
e8a565cb YW |
2115 | |
2116 | /* If not found, then create a new object. */ | |
e76506b7 | 2117 | r = exec_shared_runtime_make(m, c, id, &rt); |
74aaf59b | 2118 | if (r < 0) |
e8a565cb | 2119 | return r; |
74aaf59b | 2120 | if (r == 0) { |
e76506b7 | 2121 | /* When r == 0, it is not necessary to create ExecSharedRuntime object. */ |
74aaf59b LP |
2122 | *ret = NULL; |
2123 | return 0; | |
2124 | } | |
613b411c | 2125 | |
e8a565cb YW |
2126 | ref: |
2127 | /* increment reference counter. */ | |
2128 | rt->n_ref++; | |
2129 | *ret = rt; | |
2130 | return 1; | |
2131 | } | |
613b411c | 2132 | |
e76506b7 DDM |
2133 | int exec_shared_runtime_serialize(const Manager *m, FILE *f, FDSet *fds) { |
2134 | ExecSharedRuntime *rt; | |
e8a565cb YW |
2135 | |
2136 | assert(m); | |
613b411c LP |
2137 | assert(f); |
2138 | assert(fds); | |
2139 | ||
e76506b7 | 2140 | HASHMAP_FOREACH(rt, m->exec_shared_runtime_by_id) { |
e8a565cb | 2141 | fprintf(f, "exec-runtime=%s", rt->id); |
613b411c | 2142 | |
e8a565cb YW |
2143 | if (rt->tmp_dir) |
2144 | fprintf(f, " tmp-dir=%s", rt->tmp_dir); | |
613b411c | 2145 | |
e8a565cb YW |
2146 | if (rt->var_tmp_dir) |
2147 | fprintf(f, " var-tmp-dir=%s", rt->var_tmp_dir); | |
613b411c | 2148 | |
e8a565cb YW |
2149 | if (rt->netns_storage_socket[0] >= 0) { |
2150 | int copy; | |
613b411c | 2151 | |
e8a565cb YW |
2152 | copy = fdset_put_dup(fds, rt->netns_storage_socket[0]); |
2153 | if (copy < 0) | |
2154 | return copy; | |
613b411c | 2155 | |
e8a565cb YW |
2156 | fprintf(f, " netns-socket-0=%i", copy); |
2157 | } | |
613b411c | 2158 | |
e8a565cb YW |
2159 | if (rt->netns_storage_socket[1] >= 0) { |
2160 | int copy; | |
613b411c | 2161 | |
e8a565cb YW |
2162 | copy = fdset_put_dup(fds, rt->netns_storage_socket[1]); |
2163 | if (copy < 0) | |
2164 | return copy; | |
613b411c | 2165 | |
e8a565cb YW |
2166 | fprintf(f, " netns-socket-1=%i", copy); |
2167 | } | |
2168 | ||
a70581ff XR |
2169 | if (rt->ipcns_storage_socket[0] >= 0) { |
2170 | int copy; | |
2171 | ||
2172 | copy = fdset_put_dup(fds, rt->ipcns_storage_socket[0]); | |
2173 | if (copy < 0) | |
2174 | return copy; | |
2175 | ||
2176 | fprintf(f, " ipcns-socket-0=%i", copy); | |
2177 | } | |
2178 | ||
2179 | if (rt->ipcns_storage_socket[1] >= 0) { | |
2180 | int copy; | |
2181 | ||
2182 | copy = fdset_put_dup(fds, rt->ipcns_storage_socket[1]); | |
2183 | if (copy < 0) | |
2184 | return copy; | |
2185 | ||
2186 | fprintf(f, " ipcns-socket-1=%i", copy); | |
2187 | } | |
2188 | ||
e8a565cb | 2189 | fputc('\n', f); |
613b411c LP |
2190 | } |
2191 | ||
2192 | return 0; | |
2193 | } | |
2194 | ||
e76506b7 DDM |
2195 | int exec_shared_runtime_deserialize_compat(Unit *u, const char *key, const char *value, FDSet *fds) { |
2196 | _cleanup_(exec_shared_runtime_freep) ExecSharedRuntime *rt_create = NULL; | |
154eb43f | 2197 | ExecSharedRuntime *rt = NULL; |
613b411c LP |
2198 | int r; |
2199 | ||
e8a565cb YW |
2200 | /* This is for the migration from old (v237 or earlier) deserialization text. |
2201 | * Due to the bug #7790, this may not work with the units that use JoinsNamespaceOf=. | |
e76506b7 | 2202 | * Even if the ExecSharedRuntime object originally created by the other unit, we cannot judge |
e8a565cb YW |
2203 | * so or not from the serialized text, then we always creates a new object owned by this. */ |
2204 | ||
2205 | assert(u); | |
613b411c LP |
2206 | assert(key); |
2207 | assert(value); | |
2208 | ||
e76506b7 | 2209 | /* Manager manages ExecSharedRuntime objects by the unit id. |
e8a565cb YW |
2210 | * So, we omit the serialized text when the unit does not have id (yet?)... */ |
2211 | if (isempty(u->id)) { | |
2212 | log_unit_debug(u, "Invocation ID not found. Dropping runtime parameter."); | |
2213 | return 0; | |
2214 | } | |
613b411c | 2215 | |
154eb43f LB |
2216 | if (u->manager) { |
2217 | if (hashmap_ensure_allocated(&u->manager->exec_shared_runtime_by_id, &string_hash_ops) < 0) | |
2218 | return log_oom(); | |
e8a565cb | 2219 | |
154eb43f LB |
2220 | rt = hashmap_get(u->manager->exec_shared_runtime_by_id, u->id); |
2221 | } | |
e8a565cb | 2222 | if (!rt) { |
e76506b7 | 2223 | if (exec_shared_runtime_allocate(&rt_create, u->id) < 0) |
f2341e0a | 2224 | return log_oom(); |
613b411c | 2225 | |
e8a565cb YW |
2226 | rt = rt_create; |
2227 | } | |
2228 | ||
2229 | if (streq(key, "tmp-dir")) { | |
cbc165d1 ZJS |
2230 | if (free_and_strdup_warn(&rt->tmp_dir, value) < 0) |
2231 | return -ENOMEM; | |
613b411c LP |
2232 | |
2233 | } else if (streq(key, "var-tmp-dir")) { | |
cbc165d1 ZJS |
2234 | if (free_and_strdup_warn(&rt->var_tmp_dir, value) < 0) |
2235 | return -ENOMEM; | |
613b411c LP |
2236 | |
2237 | } else if (streq(key, "netns-socket-0")) { | |
e8a565cb YW |
2238 | |
2239 | safe_close(rt->netns_storage_socket[0]); | |
dff9808a LP |
2240 | rt->netns_storage_socket[0] = deserialize_fd(fds, value); |
2241 | if (rt->netns_storage_socket[0] < 0) | |
2242 | return 0; | |
e8a565cb | 2243 | |
613b411c | 2244 | } else if (streq(key, "netns-socket-1")) { |
e8a565cb YW |
2245 | |
2246 | safe_close(rt->netns_storage_socket[1]); | |
dff9808a LP |
2247 | rt->netns_storage_socket[1] = deserialize_fd(fds, value); |
2248 | if (rt->netns_storage_socket[1] < 0) | |
2249 | return 0; | |
613b411c LP |
2250 | } else |
2251 | return 0; | |
2252 | ||
e76506b7 | 2253 | /* If the object is newly created, then put it to the hashmap which manages ExecSharedRuntime objects. */ |
154eb43f | 2254 | if (rt_create && u->manager) { |
e76506b7 | 2255 | r = hashmap_put(u->manager->exec_shared_runtime_by_id, rt_create->id, rt_create); |
e8a565cb | 2256 | if (r < 0) { |
3fe91079 | 2257 | log_unit_debug_errno(u, r, "Failed to put runtime parameter to manager's storage: %m"); |
e8a565cb YW |
2258 | return 0; |
2259 | } | |
613b411c | 2260 | |
e8a565cb | 2261 | rt_create->manager = u->manager; |
613b411c | 2262 | |
e8a565cb | 2263 | /* Avoid cleanup */ |
56a13a49 | 2264 | TAKE_PTR(rt_create); |
e8a565cb | 2265 | } |
98b47d54 | 2266 | |
e8a565cb YW |
2267 | return 1; |
2268 | } | |
613b411c | 2269 | |
e76506b7 | 2270 | int exec_shared_runtime_deserialize_one(Manager *m, const char *value, FDSet *fds) { |
56a13a49 ZJS |
2271 | _cleanup_free_ char *tmp_dir = NULL, *var_tmp_dir = NULL; |
2272 | char *id = NULL; | |
a70581ff | 2273 | int r, netns_fdpair[] = {-1, -1}, ipcns_fdpair[] = {-1, -1}; |
99534007 | 2274 | const char *p, *v = ASSERT_PTR(value); |
e8a565cb | 2275 | size_t n; |
613b411c | 2276 | |
e8a565cb | 2277 | assert(m); |
e8a565cb | 2278 | assert(fds); |
98b47d54 | 2279 | |
e8a565cb | 2280 | n = strcspn(v, " "); |
2f82562b | 2281 | id = strndupa_safe(v, n); |
e8a565cb YW |
2282 | if (v[n] != ' ') |
2283 | goto finalize; | |
2284 | p = v + n + 1; | |
2285 | ||
2286 | v = startswith(p, "tmp-dir="); | |
2287 | if (v) { | |
2288 | n = strcspn(v, " "); | |
56a13a49 ZJS |
2289 | tmp_dir = strndup(v, n); |
2290 | if (!tmp_dir) | |
2291 | return log_oom(); | |
e8a565cb YW |
2292 | if (v[n] != ' ') |
2293 | goto finalize; | |
2294 | p = v + n + 1; | |
2295 | } | |
2296 | ||
2297 | v = startswith(p, "var-tmp-dir="); | |
2298 | if (v) { | |
2299 | n = strcspn(v, " "); | |
56a13a49 ZJS |
2300 | var_tmp_dir = strndup(v, n); |
2301 | if (!var_tmp_dir) | |
2302 | return log_oom(); | |
e8a565cb YW |
2303 | if (v[n] != ' ') |
2304 | goto finalize; | |
2305 | p = v + n + 1; | |
2306 | } | |
2307 | ||
2308 | v = startswith(p, "netns-socket-0="); | |
2309 | if (v) { | |
2310 | char *buf; | |
2311 | ||
2312 | n = strcspn(v, " "); | |
2f82562b | 2313 | buf = strndupa_safe(v, n); |
c413bb28 | 2314 | |
dff9808a | 2315 | netns_fdpair[0] = deserialize_fd(fds, buf); |
e652663a | 2316 | if (netns_fdpair[0] < 0) |
dff9808a | 2317 | return netns_fdpair[0]; |
e8a565cb YW |
2318 | if (v[n] != ' ') |
2319 | goto finalize; | |
2320 | p = v + n + 1; | |
613b411c LP |
2321 | } |
2322 | ||
e8a565cb YW |
2323 | v = startswith(p, "netns-socket-1="); |
2324 | if (v) { | |
2325 | char *buf; | |
98b47d54 | 2326 | |
e8a565cb | 2327 | n = strcspn(v, " "); |
2f82562b | 2328 | buf = strndupa_safe(v, n); |
a70581ff | 2329 | |
dff9808a | 2330 | netns_fdpair[1] = deserialize_fd(fds, buf); |
e652663a | 2331 | if (netns_fdpair[1] < 0) |
dff9808a | 2332 | return netns_fdpair[1]; |
a70581ff XR |
2333 | if (v[n] != ' ') |
2334 | goto finalize; | |
2335 | p = v + n + 1; | |
2336 | } | |
2337 | ||
2338 | v = startswith(p, "ipcns-socket-0="); | |
2339 | if (v) { | |
2340 | char *buf; | |
2341 | ||
2342 | n = strcspn(v, " "); | |
2f82562b | 2343 | buf = strndupa_safe(v, n); |
a70581ff | 2344 | |
dff9808a | 2345 | ipcns_fdpair[0] = deserialize_fd(fds, buf); |
e652663a | 2346 | if (ipcns_fdpair[0] < 0) |
dff9808a | 2347 | return ipcns_fdpair[0]; |
a70581ff XR |
2348 | if (v[n] != ' ') |
2349 | goto finalize; | |
2350 | p = v + n + 1; | |
2351 | } | |
2352 | ||
2353 | v = startswith(p, "ipcns-socket-1="); | |
2354 | if (v) { | |
2355 | char *buf; | |
2356 | ||
2357 | n = strcspn(v, " "); | |
2f82562b | 2358 | buf = strndupa_safe(v, n); |
a70581ff | 2359 | |
dff9808a | 2360 | ipcns_fdpair[1] = deserialize_fd(fds, buf); |
e652663a | 2361 | if (ipcns_fdpair[1] < 0) |
dff9808a | 2362 | return ipcns_fdpair[1]; |
e8a565cb | 2363 | } |
98b47d54 | 2364 | |
e8a565cb | 2365 | finalize: |
e76506b7 | 2366 | r = exec_shared_runtime_add(m, id, &tmp_dir, &var_tmp_dir, netns_fdpair, ipcns_fdpair, NULL); |
7d853ca6 | 2367 | if (r < 0) |
56a13a49 ZJS |
2368 | return log_debug_errno(r, "Failed to add exec-runtime: %m"); |
2369 | return 0; | |
e8a565cb | 2370 | } |
613b411c | 2371 | |
e76506b7 DDM |
2372 | void exec_shared_runtime_vacuum(Manager *m) { |
2373 | ExecSharedRuntime *rt; | |
e8a565cb YW |
2374 | |
2375 | assert(m); | |
2376 | ||
e76506b7 | 2377 | /* Free unreferenced ExecSharedRuntime objects. This is used after manager deserialization process. */ |
e8a565cb | 2378 | |
e76506b7 | 2379 | HASHMAP_FOREACH(rt, m->exec_shared_runtime_by_id) { |
e8a565cb YW |
2380 | if (rt->n_ref > 0) |
2381 | continue; | |
2382 | ||
e52a696a | 2383 | (void) exec_shared_runtime_free(rt); |
e8a565cb | 2384 | } |
613b411c LP |
2385 | } |
2386 | ||
9c0c6701 DDM |
2387 | int exec_runtime_make( |
2388 | const Unit *unit, | |
2389 | const ExecContext *context, | |
2390 | ExecSharedRuntime *shared, | |
2391 | DynamicCreds *creds, | |
2392 | ExecRuntime **ret) { | |
2393 | _cleanup_close_pair_ int ephemeral_storage_socket[2] = PIPE_EBADF; | |
2394 | _cleanup_free_ char *ephemeral = NULL; | |
28135da3 | 2395 | _cleanup_(exec_runtime_freep) ExecRuntime *rt = NULL; |
9c0c6701 | 2396 | int r; |
28135da3 | 2397 | |
9c0c6701 DDM |
2398 | assert(unit); |
2399 | assert(context); | |
28135da3 DDM |
2400 | assert(ret); |
2401 | ||
9c0c6701 | 2402 | if (!shared && !creds && !exec_needs_ephemeral(context)) { |
28135da3 DDM |
2403 | *ret = NULL; |
2404 | return 0; | |
2405 | } | |
2406 | ||
9c0c6701 DDM |
2407 | if (exec_needs_ephemeral(context)) { |
2408 | r = mkdir_p("/var/lib/systemd/ephemeral-trees", 0755); | |
2409 | if (r < 0) | |
2410 | return r; | |
2411 | ||
2412 | r = tempfn_random_child("/var/lib/systemd/ephemeral-trees", unit->id, &ephemeral); | |
2413 | if (r < 0) | |
2414 | return r; | |
2415 | ||
2416 | if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, ephemeral_storage_socket) < 0) | |
2417 | return -errno; | |
2418 | } | |
2419 | ||
28135da3 DDM |
2420 | rt = new(ExecRuntime, 1); |
2421 | if (!rt) | |
2422 | return -ENOMEM; | |
2423 | ||
2424 | *rt = (ExecRuntime) { | |
2425 | .shared = shared, | |
15220772 | 2426 | .dynamic_creds = creds, |
9c0c6701 DDM |
2427 | .ephemeral_copy = TAKE_PTR(ephemeral), |
2428 | .ephemeral_storage_socket[0] = TAKE_FD(ephemeral_storage_socket[0]), | |
2429 | .ephemeral_storage_socket[1] = TAKE_FD(ephemeral_storage_socket[1]), | |
28135da3 DDM |
2430 | }; |
2431 | ||
2432 | *ret = TAKE_PTR(rt); | |
2433 | return 1; | |
2434 | } | |
2435 | ||
2436 | ExecRuntime* exec_runtime_free(ExecRuntime *rt) { | |
2437 | if (!rt) | |
2438 | return NULL; | |
2439 | ||
2440 | exec_shared_runtime_unref(rt->shared); | |
15220772 | 2441 | dynamic_creds_unref(rt->dynamic_creds); |
9c0c6701 | 2442 | |
437f3e35 | 2443 | rt->ephemeral_copy = destroy_tree(rt->ephemeral_copy); |
9c0c6701 | 2444 | |
9c0c6701 | 2445 | safe_close_pair(rt->ephemeral_storage_socket); |
28135da3 DDM |
2446 | return mfree(rt); |
2447 | } | |
2448 | ||
2449 | ExecRuntime* exec_runtime_destroy(ExecRuntime *rt) { | |
2450 | if (!rt) | |
2451 | return NULL; | |
2452 | ||
2453 | rt->shared = exec_shared_runtime_destroy(rt->shared); | |
15220772 | 2454 | rt->dynamic_creds = dynamic_creds_destroy(rt->dynamic_creds); |
28135da3 DDM |
2455 | return exec_runtime_free(rt); |
2456 | } | |
2457 | ||
bb5232b6 LB |
2458 | void exec_runtime_clear(ExecRuntime *rt) { |
2459 | if (!rt) | |
2460 | return; | |
2461 | ||
2462 | safe_close_pair(rt->ephemeral_storage_socket); | |
2463 | rt->ephemeral_copy = mfree(rt->ephemeral_copy); | |
2464 | } | |
2465 | ||
b9c04eaf YW |
2466 | void exec_params_clear(ExecParameters *p) { |
2467 | if (!p) | |
2468 | return; | |
2469 | ||
c3f8a065 LP |
2470 | p->environment = strv_free(p->environment); |
2471 | p->fd_names = strv_free(p->fd_names); | |
154eb43f | 2472 | p->files_env = strv_free(p->files_env); |
c3f8a065 LP |
2473 | p->fds = mfree(p->fds); |
2474 | p->exec_fd = safe_close(p->exec_fd); | |
b646fc32 | 2475 | p->user_lookup_fd = -EBADF; |
154eb43f | 2476 | p->bpf_outer_map_fd = -EBADF; |
b646fc32 LB |
2477 | p->unit_id = mfree(p->unit_id); |
2478 | p->invocation_id = SD_ID128_NULL; | |
2479 | p->invocation_id_string[0] = '\0'; | |
bb5232b6 LB |
2480 | p->confirm_spawn = mfree(p->confirm_spawn); |
2481 | } | |
2482 | ||
2483 | void exec_params_serialized_done(ExecParameters *p) { | |
2484 | if (!p) | |
2485 | return; | |
2486 | ||
d3eb74f8 | 2487 | close_many_unset(p->fds, p->n_socket_fds + p->n_storage_fds); |
bb5232b6 LB |
2488 | |
2489 | p->cgroup_path = mfree(p->cgroup_path); | |
2490 | ||
d31330c1 | 2491 | if (p->prefix) { |
fc932ed4 | 2492 | free_many_charp(p->prefix, _EXEC_DIRECTORY_TYPE_MAX); |
d31330c1 FS |
2493 | p->prefix = mfree(p->prefix); |
2494 | } | |
2495 | ||
bb5232b6 LB |
2496 | p->received_credentials_directory = mfree(p->received_credentials_directory); |
2497 | p->received_encrypted_credentials_directory = mfree(p->received_encrypted_credentials_directory); | |
2498 | ||
26f937e5 LP |
2499 | if (p->idle_pipe) { |
2500 | close_many_and_free(p->idle_pipe, 4); | |
2501 | p->idle_pipe = NULL; | |
2502 | } | |
bb5232b6 LB |
2503 | |
2504 | p->stdin_fd = safe_close(p->stdin_fd); | |
2505 | p->stdout_fd = safe_close(p->stdout_fd); | |
2506 | p->stderr_fd = safe_close(p->stderr_fd); | |
2507 | ||
2508 | p->notify_socket = mfree(p->notify_socket); | |
2509 | ||
2510 | open_file_free_many(&p->open_files); | |
2511 | ||
2512 | p->fallback_smack_process_label = mfree(p->fallback_smack_process_label); | |
2513 | ||
2514 | exec_params_clear(p); | |
b9c04eaf YW |
2515 | } |
2516 | ||
211a3d87 LB |
2517 | void exec_directory_done(ExecDirectory *d) { |
2518 | if (!d) | |
2519 | return; | |
2520 | ||
f7df8adb MY |
2521 | FOREACH_ARRAY(i, d->items, d->n_items) { |
2522 | free(i->path); | |
2523 | strv_free(i->symlinks); | |
211a3d87 LB |
2524 | } |
2525 | ||
2526 | d->items = mfree(d->items); | |
2527 | d->n_items = 0; | |
2528 | d->mode = 0755; | |
2529 | } | |
2530 | ||
564e5c98 YW |
2531 | static ExecDirectoryItem *exec_directory_find(ExecDirectory *d, const char *path) { |
2532 | assert(d); | |
2533 | assert(path); | |
2534 | ||
f7df8adb MY |
2535 | FOREACH_ARRAY(i, d->items, d->n_items) |
2536 | if (path_equal(i->path, path)) | |
2537 | return i; | |
564e5c98 YW |
2538 | |
2539 | return NULL; | |
2540 | } | |
2541 | ||
2542 | int exec_directory_add(ExecDirectory *d, const char *path, const char *symlink) { | |
211a3d87 LB |
2543 | _cleanup_strv_free_ char **s = NULL; |
2544 | _cleanup_free_ char *p = NULL; | |
564e5c98 YW |
2545 | ExecDirectoryItem *existing; |
2546 | int r; | |
211a3d87 LB |
2547 | |
2548 | assert(d); | |
211a3d87 LB |
2549 | assert(path); |
2550 | ||
564e5c98 YW |
2551 | existing = exec_directory_find(d, path); |
2552 | if (existing) { | |
2553 | r = strv_extend(&existing->symlinks, symlink); | |
2554 | if (r < 0) | |
2555 | return r; | |
2556 | ||
2557 | return 0; /* existing item is updated */ | |
2558 | } | |
2559 | ||
211a3d87 LB |
2560 | p = strdup(path); |
2561 | if (!p) | |
2562 | return -ENOMEM; | |
2563 | ||
564e5c98 YW |
2564 | if (symlink) { |
2565 | s = strv_new(symlink); | |
211a3d87 LB |
2566 | if (!s) |
2567 | return -ENOMEM; | |
2568 | } | |
2569 | ||
564e5c98 | 2570 | if (!GREEDY_REALLOC(d->items, d->n_items + 1)) |
211a3d87 LB |
2571 | return -ENOMEM; |
2572 | ||
564e5c98 | 2573 | d->items[d->n_items++] = (ExecDirectoryItem) { |
211a3d87 LB |
2574 | .path = TAKE_PTR(p), |
2575 | .symlinks = TAKE_PTR(s), | |
2576 | }; | |
2577 | ||
564e5c98 | 2578 | return 1; /* new item is added */ |
211a3d87 LB |
2579 | } |
2580 | ||
a2ab603c YW |
2581 | static int exec_directory_item_compare_func(const ExecDirectoryItem *a, const ExecDirectoryItem *b) { |
2582 | assert(a); | |
2583 | assert(b); | |
2584 | ||
2585 | return path_compare(a->path, b->path); | |
2586 | } | |
2587 | ||
2588 | void exec_directory_sort(ExecDirectory *d) { | |
2589 | assert(d); | |
2590 | ||
2591 | /* Sort the exec directories to make always parent directories processed at first in | |
2592 | * setup_exec_directory(), e.g., even if StateDirectory=foo/bar foo, we need to create foo at first, | |
2593 | * then foo/bar. Also, set .only_create flag if one of the parent directories is contained in the | |
2594 | * list. See also comments in setup_exec_directory() and issue #24783. */ | |
2595 | ||
2596 | if (d->n_items <= 1) | |
2597 | return; | |
2598 | ||
2599 | typesafe_qsort(d->items, d->n_items, exec_directory_item_compare_func); | |
2600 | ||
2601 | for (size_t i = 1; i < d->n_items; i++) | |
2602 | for (size_t j = 0; j < i; j++) | |
2603 | if (path_startswith(d->items[i].path, d->items[j].path)) { | |
2604 | d->items[i].only_create = true; | |
2605 | break; | |
2606 | } | |
211a3d87 LB |
2607 | } |
2608 | ||
4fb8f1e8 LP |
2609 | ExecCleanMask exec_clean_mask_from_string(const char *s) { |
2610 | ExecDirectoryType t; | |
2611 | ||
2612 | assert(s); | |
2613 | ||
2614 | if (streq(s, "all")) | |
2615 | return EXEC_CLEAN_ALL; | |
2616 | if (streq(s, "fdstore")) | |
2617 | return EXEC_CLEAN_FDSTORE; | |
2618 | ||
2619 | t = exec_resource_type_from_string(s); | |
2620 | if (t < 0) | |
2621 | return (ExecCleanMask) t; | |
2622 | ||
2623 | return 1U << t; | |
2624 | } | |
2625 | ||
80876c20 LP |
2626 | static const char* const exec_input_table[_EXEC_INPUT_MAX] = { |
2627 | [EXEC_INPUT_NULL] = "null", | |
2628 | [EXEC_INPUT_TTY] = "tty", | |
2629 | [EXEC_INPUT_TTY_FORCE] = "tty-force", | |
4f2d528d | 2630 | [EXEC_INPUT_TTY_FAIL] = "tty-fail", |
52c239d7 LB |
2631 | [EXEC_INPUT_SOCKET] = "socket", |
2632 | [EXEC_INPUT_NAMED_FD] = "fd", | |
08f3be7a | 2633 | [EXEC_INPUT_DATA] = "data", |
2038c3f5 | 2634 | [EXEC_INPUT_FILE] = "file", |
80876c20 LP |
2635 | }; |
2636 | ||
8a0867d6 LP |
2637 | DEFINE_STRING_TABLE_LOOKUP(exec_input, ExecInput); |
2638 | ||
94f04347 | 2639 | static const char* const exec_output_table[_EXEC_OUTPUT_MAX] = { |
80876c20 | 2640 | [EXEC_OUTPUT_INHERIT] = "inherit", |
94f04347 | 2641 | [EXEC_OUTPUT_NULL] = "null", |
80876c20 | 2642 | [EXEC_OUTPUT_TTY] = "tty", |
9a6bca7a | 2643 | [EXEC_OUTPUT_KMSG] = "kmsg", |
28dbc1e8 | 2644 | [EXEC_OUTPUT_KMSG_AND_CONSOLE] = "kmsg+console", |
706343f4 LP |
2645 | [EXEC_OUTPUT_JOURNAL] = "journal", |
2646 | [EXEC_OUTPUT_JOURNAL_AND_CONSOLE] = "journal+console", | |
52c239d7 LB |
2647 | [EXEC_OUTPUT_SOCKET] = "socket", |
2648 | [EXEC_OUTPUT_NAMED_FD] = "fd", | |
2038c3f5 | 2649 | [EXEC_OUTPUT_FILE] = "file", |
566b7d23 | 2650 | [EXEC_OUTPUT_FILE_APPEND] = "append", |
8d7dab1f | 2651 | [EXEC_OUTPUT_FILE_TRUNCATE] = "truncate", |
94f04347 LP |
2652 | }; |
2653 | ||
2654 | DEFINE_STRING_TABLE_LOOKUP(exec_output, ExecOutput); | |
023a4f67 LP |
2655 | |
2656 | static const char* const exec_utmp_mode_table[_EXEC_UTMP_MODE_MAX] = { | |
2657 | [EXEC_UTMP_INIT] = "init", | |
2658 | [EXEC_UTMP_LOGIN] = "login", | |
2659 | [EXEC_UTMP_USER] = "user", | |
2660 | }; | |
2661 | ||
2662 | DEFINE_STRING_TABLE_LOOKUP(exec_utmp_mode, ExecUtmpMode); | |
53f47dfc YW |
2663 | |
2664 | static const char* const exec_preserve_mode_table[_EXEC_PRESERVE_MODE_MAX] = { | |
2665 | [EXEC_PRESERVE_NO] = "no", | |
2666 | [EXEC_PRESERVE_YES] = "yes", | |
2667 | [EXEC_PRESERVE_RESTART] = "restart", | |
2668 | }; | |
2669 | ||
2670 | DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(exec_preserve_mode, ExecPreserveMode, EXEC_PRESERVE_YES); | |
3536f49e | 2671 | |
6b7b2ed9 | 2672 | /* This table maps ExecDirectoryType to the setting it is configured with in the unit */ |
72fd1768 | 2673 | static const char* const exec_directory_type_table[_EXEC_DIRECTORY_TYPE_MAX] = { |
3536f49e YW |
2674 | [EXEC_DIRECTORY_RUNTIME] = "RuntimeDirectory", |
2675 | [EXEC_DIRECTORY_STATE] = "StateDirectory", | |
2676 | [EXEC_DIRECTORY_CACHE] = "CacheDirectory", | |
2677 | [EXEC_DIRECTORY_LOGS] = "LogsDirectory", | |
2678 | [EXEC_DIRECTORY_CONFIGURATION] = "ConfigurationDirectory", | |
2679 | }; | |
2680 | ||
2681 | DEFINE_STRING_TABLE_LOOKUP(exec_directory_type, ExecDirectoryType); | |
b1edf445 | 2682 | |
211a3d87 LB |
2683 | /* This table maps ExecDirectoryType to the symlink setting it is configured with in the unit */ |
2684 | static const char* const exec_directory_type_symlink_table[_EXEC_DIRECTORY_TYPE_MAX] = { | |
2685 | [EXEC_DIRECTORY_RUNTIME] = "RuntimeDirectorySymlink", | |
2686 | [EXEC_DIRECTORY_STATE] = "StateDirectorySymlink", | |
2687 | [EXEC_DIRECTORY_CACHE] = "CacheDirectorySymlink", | |
2688 | [EXEC_DIRECTORY_LOGS] = "LogsDirectorySymlink", | |
2689 | [EXEC_DIRECTORY_CONFIGURATION] = "ConfigurationDirectorySymlink", | |
2690 | }; | |
2691 | ||
2692 | DEFINE_STRING_TABLE_LOOKUP(exec_directory_type_symlink, ExecDirectoryType); | |
2693 | ||
ef44aa83 DDM |
2694 | static const char* const exec_directory_type_mode_table[_EXEC_DIRECTORY_TYPE_MAX] = { |
2695 | [EXEC_DIRECTORY_RUNTIME] = "RuntimeDirectoryMode", | |
2696 | [EXEC_DIRECTORY_STATE] = "StateDirectoryMode", | |
2697 | [EXEC_DIRECTORY_CACHE] = "CacheDirectoryMode", | |
2698 | [EXEC_DIRECTORY_LOGS] = "LogsDirectoryMode", | |
2699 | [EXEC_DIRECTORY_CONFIGURATION] = "ConfigurationDirectoryMode", | |
2700 | }; | |
2701 | ||
2702 | DEFINE_STRING_TABLE_LOOKUP(exec_directory_type_mode, ExecDirectoryType); | |
2703 | ||
6b7b2ed9 LP |
2704 | /* And this table maps ExecDirectoryType too, but to a generic term identifying the type of resource. This |
2705 | * one is supposed to be generic enough to be used for unit types that don't use ExecContext and per-unit | |
2706 | * directories, specifically .timer units with their timestamp touch file. */ | |
2707 | static const char* const exec_resource_type_table[_EXEC_DIRECTORY_TYPE_MAX] = { | |
2708 | [EXEC_DIRECTORY_RUNTIME] = "runtime", | |
2709 | [EXEC_DIRECTORY_STATE] = "state", | |
2710 | [EXEC_DIRECTORY_CACHE] = "cache", | |
2711 | [EXEC_DIRECTORY_LOGS] = "logs", | |
2712 | [EXEC_DIRECTORY_CONFIGURATION] = "configuration", | |
2713 | }; | |
2714 | ||
2715 | DEFINE_STRING_TABLE_LOOKUP(exec_resource_type, ExecDirectoryType); | |
2716 | ||
b1edf445 LP |
2717 | static const char* const exec_keyring_mode_table[_EXEC_KEYRING_MODE_MAX] = { |
2718 | [EXEC_KEYRING_INHERIT] = "inherit", | |
2719 | [EXEC_KEYRING_PRIVATE] = "private", | |
2720 | [EXEC_KEYRING_SHARED] = "shared", | |
2721 | }; | |
2722 | ||
2723 | DEFINE_STRING_TABLE_LOOKUP(exec_keyring_mode, ExecKeyringMode); |