]> git.ipfire.org Git - people/ms/systemd.git/blame - execute.c
execute: when running in session mode, still enforce proper ordering of logger socket
[people/ms/systemd.git] / execute.c
CommitLineData
5cb5a6ff
LP
1/*-*- Mode: C; c-basic-offset: 8 -*-*/
2
a7334b09
LP
3/***
4 This file is part of systemd.
5
6 Copyright 2010 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
5cb5a6ff 22#include <assert.h>
034c6ed7
LP
23#include <dirent.h>
24#include <errno.h>
25#include <fcntl.h>
26#include <unistd.h>
44d8db9e 27#include <string.h>
309bff19 28#include <signal.h>
071830ff
LP
29#include <sys/socket.h>
30#include <sys/un.h>
94f04347 31#include <sys/prctl.h>
66ee3769 32#include <linux/sched.h>
451a074f
LP
33#include <sys/types.h>
34#include <sys/stat.h>
81a2b7ce
LP
35#include <grp.h>
36#include <pwd.h>
5cb5a6ff
LP
37
38#include "execute.h"
39#include "strv.h"
40#include "macro.h"
41#include "util.h"
acbb0225 42#include "log.h"
9eba9da4 43#include "ioprio.h"
94f04347 44#include "securebits.h"
5cb5a6ff 45
034c6ed7
LP
46static int close_fds(int except[], unsigned n_except) {
47 DIR *d;
48 struct dirent *de;
49 int r = 0;
50
51 /* Modifies the fds array! (sorts it) */
52
53 if (!(d = opendir("/proc/self/fd")))
54 return -errno;
55
56 while ((de = readdir(d))) {
57 int fd;
58
59 if (de->d_name[0] == '.')
60 continue;
61
62 if ((r = safe_atoi(de->d_name, &fd)) < 0)
63 goto finish;
64
65 if (fd < 3)
66 continue;
67
68 if (fd == dirfd(d))
69 continue;
70
71 if (except) {
72 bool found;
73 unsigned i;
74
75 found = false;
76 for (i = 0; i < n_except; i++)
77 if (except[i] == fd) {
78 found = true;
79 break;
80 }
81
82 if (found)
83 continue;
84 }
85
86 if ((r = close_nointr(fd)) < 0)
87 goto finish;
88 }
89
90finish:
91 closedir(d);
92 return r;
93}
94
95static int shift_fds(int fds[], unsigned n_fds) {
96 int start, restart_from;
97
98 if (n_fds <= 0)
99 return 0;
100
101 assert(fds);
102
103 start = 0;
104 for (;;) {
105 int i;
106
107 restart_from = -1;
108
109 for (i = start; i < (int) n_fds; i++) {
110 int nfd;
111
112 /* Already at right index? */
113 if (fds[i] == i+3)
114 continue;
115
116 if ((nfd = fcntl(fds[i], F_DUPFD, i+3)) < 0)
117 return -errno;
118
2da3263a 119 assert_se(close_nointr(fds[i]) == 0);
034c6ed7
LP
120 fds[i] = nfd;
121
122 /* Hmm, the fd we wanted isn't free? Then
123 * let's remember that and try again from here*/
124 if (nfd != i+3 && restart_from < 0)
125 restart_from = i;
126 }
127
128 if (restart_from < 0)
129 break;
130
131 start = restart_from;
132 }
133
134 return 0;
135}
136
451a074f 137static int flags_fds(int fds[], unsigned n_fds, bool nonblock) {
47a71eed
LP
138 unsigned i;
139
140 if (n_fds <= 0)
141 return 0;
142
143 assert(fds);
144
451a074f 145 /* Drops/Sets O_NONBLOCK and FD_CLOEXEC from the file flags */
47a71eed
LP
146
147 for (i = 0; i < n_fds; i++) {
148 int flags;
149
150 if ((flags = fcntl(fds[i], F_GETFL, 0)) < 0)
151 return -errno;
152
451a074f
LP
153 if (nonblock)
154 flags |= O_NONBLOCK;
155 else
156 flags &= ~O_NONBLOCK;
47a71eed 157
451a074f 158 if (fcntl(fds[i], F_SETFL, flags) < 0)
47a71eed
LP
159 return -errno;
160
451a074f
LP
161 /* We unconditionally drop FD_CLOEXEC from the fds,
162 * since after all we want to pass these fds to our
163 * children */
47a71eed
LP
164 if ((flags = fcntl(fds[i], F_GETFD, 0)) < 0)
165 return -errno;
166
47a71eed
LP
167 if (fcntl(fds[i], F_SETFD, flags &~FD_CLOEXEC) < 0)
168 return -errno;
169 }
170
171 return 0;
172}
173
071830ff
LP
174static int replace_null_fd(int fd, int flags) {
175 int nfd;
176 assert(fd >= 0);
177
178 close_nointr(fd);
179
180 if ((nfd = open("/dev/null", flags|O_NOCTTY)) < 0)
181 return -errno;
182
183 if (nfd != fd) {
184 close_nointr_nofail(nfd);
185 return -EIO;
186 }
187
188 return 0;
189}
190
191static int setup_output(const ExecContext *context, const char *ident) {
192 int r;
193
194 assert(context);
195
196 switch (context->output) {
197
94f04347 198 case EXEC_OUTPUT_CONSOLE:
071830ff
LP
199 return 0;
200
94f04347 201 case EXEC_OUTPUT_NULL:
071830ff 202
94f04347 203 if ((r = replace_null_fd(STDOUT_FILENO, O_WRONLY)) < 0 ||
071830ff
LP
204 (r = replace_null_fd(STDERR_FILENO, O_WRONLY)) < 0)
205 return r;
206
207 return 0;
208
94f04347
LP
209 case EXEC_OUTPUT_KERNEL:
210 case EXEC_OUTPUT_SYSLOG: {
071830ff
LP
211
212 int fd;
213 union {
214 struct sockaddr sa;
215 struct sockaddr_un un;
216 } sa;
217
071830ff
LP
218 close_nointr(STDOUT_FILENO);
219 close_nointr(STDERR_FILENO);
220
221 if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
222 return -errno;
223
224 if (fd != STDOUT_FILENO) {
225 close_nointr_nofail(fd);
226 return -EIO;
227 }
228
229 zero(sa);
230 sa.sa.sa_family = AF_UNIX;
231 strncpy(sa.un.sun_path+1, LOGGER_SOCKET, sizeof(sa.un.sun_path)-1);
232
233 if (connect(fd, &sa.sa, sizeof(sa)) < 0) {
234 close_nointr_nofail(fd);
235 return -errno;
236 }
237
238 if (shutdown(fd, SHUT_RD) < 0) {
239 close_nointr_nofail(fd);
240 return -errno;
241 }
242
243 if ((fd = dup(fd)) < 0) {
244 close_nointr_nofail(fd);
245 return -errno;
246 }
247
248 if (fd != STDERR_FILENO) {
249 close_nointr_nofail(fd);
250 return -EIO;
251 }
252
253 /* We speak a very simple protocol between log server
254 * and client: one line for the log destination (kmsg
255 * or syslog), followed by the priority field,
256 * followed by the process name. Since we replaced
257 * stdin/stderr we simple use stdio to write to
258 * it. Note that we use stderr, to minimize buffer
259 * flushing issues. */
260
261 fprintf(stderr,
262 "%s\n"
263 "%i\n"
264 "%s\n",
94f04347 265 context->output == EXEC_OUTPUT_KERNEL ? "kmsg" : "syslog",
071830ff
LP
266 context->syslog_priority,
267 context->syslog_identifier ? context->syslog_identifier : ident);
268
269 return 0;
270 }
94f04347
LP
271
272 default:
273 assert_not_reached("Unknown output type");
071830ff 274 }
94f04347
LP
275}
276
47be870b 277static int setup_input(const ExecContext *context) {
94f04347
LP
278 int r;
279
280 assert(context);
071830ff 281
94f04347
LP
282 switch (context->input) {
283
284 case EXEC_INPUT_CONSOLE:
285 return 0;
286
287 case EXEC_INPUT_NULL:
288 if ((r = replace_null_fd(STDIN_FILENO, O_RDONLY)) < 0)
289 return r;
290
291 return 0;
292
293 default:
294 assert_not_reached("Unknown input type");
295 }
071830ff
LP
296}
297
81a2b7ce
LP
298static int get_group_creds(const char *groupname, gid_t *gid) {
299 struct group *g;
300 unsigned long lu;
301
302 assert(groupname);
303 assert(gid);
304
305 /* We enforce some special rules for gid=0: in order to avoid
306 * NSS lookups for root we hardcode its data. */
307
308 if (streq(groupname, "root") || streq(groupname, "0")) {
309 *gid = 0;
310 return 0;
311 }
312
313 if (safe_atolu(groupname, &lu) >= 0) {
314 errno = 0;
315 g = getgrgid((gid_t) lu);
316 } else {
317 errno = 0;
318 g = getgrnam(groupname);
319 }
320
321 if (!g)
322 return errno != 0 ? -errno : -ESRCH;
323
324 *gid = g->gr_gid;
325 return 0;
326}
327
328static int get_user_creds(const char **username, uid_t *uid, gid_t *gid, const char **home) {
329 struct passwd *p;
330 unsigned long lu;
331
332 assert(username);
333 assert(*username);
334 assert(uid);
335 assert(gid);
336 assert(home);
337
338 /* We enforce some special rules for uid=0: in order to avoid
339 * NSS lookups for root we hardcode its data. */
340
341 if (streq(*username, "root") || streq(*username, "0")) {
342 *username = "root";
343 *uid = 0;
344 *gid = 0;
345 *home = "/root";
346 return 0;
347 }
348
349 if (safe_atolu(*username, &lu) >= 0) {
350 errno = 0;
351 p = getpwuid((uid_t) lu);
352
353 /* If there are multiple users with the same id, make
354 * sure to leave $USER to the configured value instead
355 * of the first occurence in the database. However if
356 * the uid was configured by a numeric uid, then let's
357 * pick the real username from /etc/passwd. */
358 if (*username && p)
359 *username = p->pw_name;
360 } else {
361 errno = 0;
362 p = getpwnam(*username);
363 }
364
365 if (!p)
366 return errno != 0 ? -errno : -ESRCH;
367
368 *uid = p->pw_uid;
369 *gid = p->pw_gid;
370 *home = p->pw_dir;
371 return 0;
372}
373
374static int enforce_groups(const ExecContext *context, const char *username, gid_t gid) {
375 bool keep_groups = false;
376 int r;
377
378 assert(context);
379
380 /* Lookup and ser GID and supplementary group list. Here too
381 * we avoid NSS lookups for gid=0. */
382
383 if (context->group || username) {
384
385 if (context->group)
386 if ((r = get_group_creds(context->group, &gid)) < 0)
387 return r;
388
389 /* First step, initialize groups from /etc/groups */
390 if (username && gid != 0) {
391 if (initgroups(username, gid) < 0)
392 return -errno;
393
394 keep_groups = true;
395 }
396
397 /* Second step, set our gids */
398 if (setresgid(gid, gid, gid) < 0)
399 return -errno;
400 }
401
402 if (context->supplementary_groups) {
403 int ngroups_max, k;
404 gid_t *gids;
405 char **i;
406
407 /* Final step, initialize any manually set supplementary groups */
408 ngroups_max = (int) sysconf(_SC_NGROUPS_MAX);
409
410 if (!(gids = new(gid_t, ngroups_max)))
411 return -ENOMEM;
412
413 if (keep_groups) {
414 if ((k = getgroups(ngroups_max, gids)) < 0) {
415 free(gids);
416 return -errno;
417 }
418 } else
419 k = 0;
420
421 STRV_FOREACH(i, context->supplementary_groups) {
422
423 if (k >= ngroups_max) {
424 free(gids);
425 return -E2BIG;
426 }
427
428 if ((r = get_group_creds(*i, gids+k)) < 0) {
429 free(gids);
430 return r;
431 }
432
433 k++;
434 }
435
436 if (setgroups(k, gids) < 0) {
437 free(gids);
438 return -errno;
439 }
440
441 free(gids);
442 }
443
444 return 0;
445}
446
447static int enforce_user(const ExecContext *context, uid_t uid) {
448 int r;
449 assert(context);
450
451 /* Sets (but doesn't lookup) the uid and make sure we keep the
452 * capabilities while doing so. */
453
454 if (context->capabilities) {
455 cap_t d;
456 static const cap_value_t bits[] = {
457 CAP_SETUID, /* Necessary so that we can run setresuid() below */
458 CAP_SETPCAP /* Necessary so that we can set PR_SET_SECUREBITS later on */
459 };
460
461 /* First step: If we need to keep capabilities but
462 * drop privileges we need to make sure we keep our
463 * caps, whiel we drop priviliges. */
693ced48
LP
464 if (uid != 0) {
465 int sb = context->secure_bits|SECURE_KEEP_CAPS;
466
467 if (prctl(PR_GET_SECUREBITS) != sb)
468 if (prctl(PR_SET_SECUREBITS, sb) < 0)
469 return -errno;
470 }
81a2b7ce
LP
471
472 /* Second step: set the capabilites. This will reduce
473 * the capabilities to the minimum we need. */
474
475 if (!(d = cap_dup(context->capabilities)))
476 return -errno;
477
478 if (cap_set_flag(d, CAP_EFFECTIVE, ELEMENTSOF(bits), bits, CAP_SET) < 0 ||
479 cap_set_flag(d, CAP_PERMITTED, ELEMENTSOF(bits), bits, CAP_SET) < 0) {
480 r = -errno;
481 cap_free(d);
482 return r;
483 }
484
485 if (cap_set_proc(d) < 0) {
486 r = -errno;
487 cap_free(d);
488 return r;
489 }
490
491 cap_free(d);
492 }
493
494 /* Third step: actually set the uids */
495 if (setresuid(uid, uid, uid) < 0)
496 return -errno;
497
498 /* At this point we should have all necessary capabilities but
499 are otherwise a normal user. However, the caps might got
500 corrupted due to the setresuid() so we need clean them up
501 later. This is done outside of this call. */
502
503 return 0;
504}
505
506int exec_spawn(const ExecCommand *command,
507 const ExecContext *context,
508 int *fds, unsigned n_fds,
509 bool apply_permissions,
510 bool apply_chroot,
511 pid_t *ret) {
512
034c6ed7
LP
513 pid_t pid;
514
5cb5a6ff
LP
515 assert(command);
516 assert(context);
517 assert(ret);
034c6ed7
LP
518 assert(fds || n_fds <= 0);
519
81a2b7ce 520 log_debug("About to execute %s", command->path);
acbb0225 521
034c6ed7
LP
522 if ((pid = fork()) < 0)
523 return -errno;
524
525 if (pid == 0) {
034c6ed7 526 int i, r;
309bff19 527 sigset_t ss;
81a2b7ce
LP
528 const char *username = NULL, *home = NULL;
529 uid_t uid = (uid_t) -1;
530 gid_t gid = (gid_t) -1;
531 char **our_env = NULL, **final_env = NULL;
532 unsigned n_env = 0;
309bff19 533
034c6ed7 534 /* child */
5cb5a6ff 535
309bff19
LP
536 if (sigemptyset(&ss) < 0 ||
537 sigprocmask(SIG_SETMASK, &ss, NULL) < 0) {
538 r = EXIT_SIGNAL_MASK;
539 goto fail;
540 }
541
9eba9da4
LP
542 if (setpgid(0, 0) < 0) {
543 r = EXIT_PGID;
034c6ed7
LP
544 goto fail;
545 }
546
9eba9da4
LP
547 umask(context->umask);
548
94f04347
LP
549 if (setup_input(context) < 0) {
550 r = EXIT_INPUT;
551 goto fail;
552 }
553
071830ff
LP
554 if (setup_output(context, file_name_from_path(command->path)) < 0) {
555 r = EXIT_OUTPUT;
556 goto fail;
557 }
558
fb33a393
LP
559 if (context->oom_adjust_set) {
560 char t[16];
034c6ed7 561
fb33a393
LP
562 snprintf(t, sizeof(t), "%i", context->oom_adjust);
563 char_array_0(t);
034c6ed7 564
fb33a393
LP
565 if (write_one_line_file("/proc/self/oom_adj", t) < 0) {
566 r = EXIT_OOM_ADJUST;
567 goto fail;
568 }
034c6ed7
LP
569 }
570
fb33a393
LP
571 if (context->nice_set)
572 if (setpriority(PRIO_PROCESS, 0, context->nice) < 0) {
573 r = EXIT_NICE;
574 goto fail;
575 }
576
94f04347
LP
577 if (context->cpu_sched_set) {
578 struct sched_param param;
579
580 zero(param);
581 param.sched_priority = context->cpu_sched_priority;
582
38b48754
LP
583 if (sched_setscheduler(0, context->cpu_sched_policy |
584 (context->cpu_sched_reset_on_fork ? SCHED_RESET_ON_FORK : 0), &param) < 0) {
94f04347
LP
585 r = EXIT_SETSCHEDULER;
586 goto fail;
587 }
588 }
589
590 if (context->cpu_affinity_set)
591 if (sched_setaffinity(0, sizeof(context->cpu_affinity), &context->cpu_affinity) < 0) {
592 r = EXIT_CPUAFFINITY;
593 goto fail;
594 }
595
9eba9da4
LP
596 if (context->ioprio_set)
597 if (ioprio_set(IOPRIO_WHO_PROCESS, 0, context->ioprio) < 0) {
598 r = EXIT_IOPRIO;
599 goto fail;
600 }
601
94f04347
LP
602 if (context->timer_slack_ns_set)
603 if (prctl(PR_SET_TIMERSLACK, context->timer_slack_ns_set) < 0) {
604 r = EXIT_TIMERSLACK;
605 goto fail;
606 }
607
81a2b7ce
LP
608 if (context->user) {
609 username = context->user;
610 if (get_user_creds(&username, &uid, &gid, &home) < 0) {
611 r = EXIT_USER;
612 goto fail;
613 }
614 }
615
616 if (apply_permissions)
617 if (enforce_groups(context, username, uid) < 0) {
618 r = EXIT_GROUP;
619 goto fail;
620 }
621
622 if (apply_chroot) {
623 if (context->root_directory)
624 if (chroot(context->root_directory) < 0) {
625 r = EXIT_CHROOT;
626 goto fail;
627 }
628
629 if (chdir(context->working_directory ? context->working_directory : "/") < 0) {
630 r = EXIT_CHDIR;
631 goto fail;
632 }
633 } else {
634
635 char *d;
636
637 if (asprintf(&d, "%s/%s",
638 context->root_directory ? context->root_directory : "",
639 context->working_directory ? context->working_directory : "") < 0) {
640 r = EXIT_MEMORY;
641 goto fail;
642 }
643
644 if (chdir(d) < 0) {
645 free(d);
646 r = EXIT_CHDIR;
647 goto fail;
648 }
649
650 free(d);
651 }
652
034c6ed7 653 if (close_fds(fds, n_fds) < 0 ||
47a71eed 654 shift_fds(fds, n_fds) < 0 ||
451a074f 655 flags_fds(fds, n_fds, context->non_blocking) < 0) {
034c6ed7
LP
656 r = EXIT_FDS;
657 goto fail;
658 }
659
81a2b7ce 660 if (apply_permissions) {
034c6ed7 661
81a2b7ce
LP
662 for (i = 0; i < RLIMIT_NLIMITS; i++) {
663 if (!context->rlimit[i])
664 continue;
665
666 if (setrlimit(i, context->rlimit[i]) < 0) {
667 r = EXIT_LIMITS;
668 goto fail;
669 }
034c6ed7 670 }
034c6ed7 671
81a2b7ce
LP
672 if (context->user)
673 if (enforce_user(context, uid) < 0) {
674 r = EXIT_USER;
675 goto fail;
676 }
677
693ced48
LP
678 /* PR_GET_SECUREBITS is not priviliged, while
679 * PR_SET_SECUREBITS is. So to suppress
680 * potential EPERMs we'll try not to call
681 * PR_SET_SECUREBITS unless necessary. */
682 if (prctl(PR_GET_SECUREBITS) != context->secure_bits)
683 if (prctl(PR_SET_SECUREBITS, context->secure_bits) < 0) {
684 r = EXIT_SECUREBITS;
685 goto fail;
686 }
81a2b7ce
LP
687
688 if (context->capabilities)
689 if (cap_set_proc(context->capabilities) < 0) {
690 r = EXIT_CAPABILITIES;
691 goto fail;
692 }
94f04347
LP
693 }
694
81a2b7ce
LP
695 if (!(our_env = new0(char*, 6))) {
696 r = EXIT_MEMORY;
697 goto fail;
698 }
034c6ed7 699
81a2b7ce
LP
700 if (n_fds > 0)
701 if (asprintf(our_env + n_env++, "LISTEN_PID=%llu", (unsigned long long) getpid()) < 0 ||
702 asprintf(our_env + n_env++, "LISTEN_FDS=%u", n_fds) < 0) {
703 r = EXIT_MEMORY;
704 goto fail;
705 }
034c6ed7 706
81a2b7ce
LP
707 if (home)
708 if (asprintf(our_env + n_env++, "HOME=%s", home) < 0) {
709 r = EXIT_MEMORY;
710 goto fail;
711 }
034c6ed7 712
81a2b7ce
LP
713 if (username)
714 if (asprintf(our_env + n_env++, "LOGNAME=%s", username) < 0 ||
715 asprintf(our_env + n_env++, "USER=%s", username) < 0) {
716 r = EXIT_MEMORY;
717 goto fail;
718 }
034c6ed7 719
81a2b7ce
LP
720 if (!(final_env = strv_env_merge(environ, our_env, context->environment, NULL))) {
721 r = EXIT_MEMORY;
722 goto fail;
723 }
034c6ed7 724
81a2b7ce 725 execve(command->path, command->argv, final_env);
034c6ed7
LP
726 r = EXIT_EXEC;
727
728 fail:
81a2b7ce
LP
729 strv_free(our_env);
730 strv_free(final_env);
731
034c6ed7
LP
732 _exit(r);
733 }
734
2da3263a 735
81a2b7ce 736 log_debug("Forked %s as %llu", command->path, (unsigned long long) pid);
2da3263a 737
034c6ed7 738 *ret = pid;
5cb5a6ff
LP
739 return 0;
740}
741
034c6ed7
LP
742void exec_context_init(ExecContext *c) {
743 assert(c);
744
745 c->umask = 0002;
034c6ed7 746 c->oom_adjust = 0;
9eba9da4 747 c->oom_adjust_set = false;
034c6ed7 748 c->nice = 0;
9eba9da4
LP
749 c->nice_set = false;
750 c->ioprio = IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE, 0);
751 c->ioprio_set = false;
94f04347
LP
752 c->cpu_sched_policy = SCHED_OTHER;
753 c->cpu_sched_priority = 0;
754 c->cpu_sched_set = false;
755 CPU_ZERO(&c->cpu_affinity);
756 c->cpu_affinity_set = false;
071830ff 757
94f04347 758 c->input = 0;
071830ff
LP
759 c->output = 0;
760 c->syslog_priority = LOG_DAEMON|LOG_INFO;
94f04347
LP
761
762 c->secure_bits = 0;
763 c->capability_bounding_set_drop = 0;
034c6ed7
LP
764}
765
766void exec_context_done(ExecContext *c) {
5cb5a6ff
LP
767 unsigned l;
768
769 assert(c);
770
771 strv_free(c->environment);
034c6ed7 772 c->environment = NULL;
5cb5a6ff 773
034c6ed7 774 for (l = 0; l < ELEMENTSOF(c->rlimit); l++) {
5cb5a6ff 775 free(c->rlimit[l]);
034c6ed7
LP
776 c->rlimit[l] = NULL;
777 }
778
9eba9da4
LP
779 free(c->working_directory);
780 c->working_directory = NULL;
781 free(c->root_directory);
782 c->root_directory = NULL;
5cb5a6ff 783
071830ff
LP
784 free(c->syslog_identifier);
785 c->syslog_identifier = NULL;
786
5cb5a6ff 787 free(c->user);
034c6ed7
LP
788 c->user = NULL;
789
5cb5a6ff 790 free(c->group);
034c6ed7
LP
791 c->group = NULL;
792
793 strv_free(c->supplementary_groups);
794 c->supplementary_groups = NULL;
94f04347
LP
795
796 if (c->capabilities) {
797 cap_free(c->capabilities);
798 c->capabilities = NULL;
799 }
5cb5a6ff
LP
800}
801
802void exec_command_free_list(ExecCommand *c) {
803 ExecCommand *i;
804
805 while ((i = c)) {
034c6ed7 806 LIST_REMOVE(ExecCommand, command, c, i);
5cb5a6ff
LP
807
808 free(i->path);
44d8db9e 809 strv_free(i->argv);
5cb5a6ff
LP
810 free(i);
811 }
812}
813
034c6ed7
LP
814void exec_command_free_array(ExecCommand **c, unsigned n) {
815 unsigned i;
816
817 for (i = 0; i < n; i++) {
818 exec_command_free_list(c[i]);
819 c[i] = NULL;
820 }
821}
822
5cb5a6ff 823void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) {
94f04347
LP
824 char ** e;
825 unsigned i;
9eba9da4 826
5cb5a6ff
LP
827 assert(c);
828 assert(f);
829
830 if (!prefix)
831 prefix = "";
832
833 fprintf(f,
94f04347
LP
834 "%sUMask: %04o\n"
835 "%sWorkingDirectory: %s\n"
451a074f
LP
836 "%sRootDirectory: %s\n"
837 "%sNonBlocking: %s\n",
5cb5a6ff 838 prefix, c->umask,
9eba9da4 839 prefix, c->working_directory ? c->working_directory : "/",
451a074f
LP
840 prefix, c->root_directory ? c->root_directory : "/",
841 prefix, yes_no(c->non_blocking));
fb33a393 842
94f04347
LP
843 if (c->environment)
844 for (e = c->environment; *e; e++)
845 fprintf(f, "%sEnvironment: %s\n", prefix, *e);
846
fb33a393
LP
847 if (c->nice_set)
848 fprintf(f,
849 "%sNice: %i\n",
850 prefix, c->nice);
851
852 if (c->oom_adjust_set)
853 fprintf(f,
854 "%sOOMAdjust: %i\n",
855 prefix, c->oom_adjust);
9eba9da4 856
94f04347
LP
857 for (i = 0; i < RLIM_NLIMITS; i++)
858 if (c->rlimit[i])
ea430986 859 fprintf(f, "%s%s: %llu\n", prefix, rlimit_to_string(i), (unsigned long long) c->rlimit[i]->rlim_max);
94f04347 860
9eba9da4
LP
861 if (c->ioprio_set)
862 fprintf(f,
863 "%sIOSchedulingClass: %s\n"
864 "%sIOPriority: %i\n",
94f04347 865 prefix, ioprio_class_to_string(IOPRIO_PRIO_CLASS(c->ioprio)),
9eba9da4 866 prefix, (int) IOPRIO_PRIO_DATA(c->ioprio));
94f04347
LP
867
868 if (c->cpu_sched_set)
869 fprintf(f,
870 "%sCPUSchedulingPolicy: %s\n"
38b48754
LP
871 "%sCPUSchedulingPriority: %i\n"
872 "%sCPUSchedulingResetOnFork: %s\n",
94f04347 873 prefix, sched_policy_to_string(c->cpu_sched_policy),
38b48754
LP
874 prefix, c->cpu_sched_priority,
875 prefix, yes_no(c->cpu_sched_reset_on_fork));
94f04347
LP
876
877 if (c->cpu_affinity_set) {
878 fprintf(f, "%sCPUAffinity:", prefix);
879 for (i = 0; i < CPU_SETSIZE; i++)
880 if (CPU_ISSET(i, &c->cpu_affinity))
881 fprintf(f, " %i", i);
882 fputs("\n", f);
883 }
884
885 if (c->timer_slack_ns_set)
886 fprintf(f, "%sTimerSlackNS: %lu\n", prefix, c->timer_slack_ns);
887
888 fprintf(f,
889 "%sInput: %s\n"
890 "%sOutput: %s\n",
891 prefix, exec_input_to_string(c->input),
892 prefix, exec_output_to_string(c->output));
893
894 if (c->output == EXEC_OUTPUT_SYSLOG || c->output == EXEC_OUTPUT_KERNEL)
895 fprintf(f,
896 "%sSyslogFacility: %s\n"
897 "%sSyslogLevel: %s\n",
898 prefix, log_facility_to_string(LOG_FAC(c->syslog_priority)),
899 prefix, log_level_to_string(LOG_PRI(c->syslog_priority)));
900
901 if (c->capabilities) {
902 char *t;
903 if ((t = cap_to_text(c->capabilities, NULL))) {
904 fprintf(f, "%sCapabilities: %s\n",
905 prefix, t);
906 cap_free(t);
907 }
908 }
909
910 if (c->secure_bits)
911 fprintf(f, "%sSecure Bits:%s%s%s%s%s%s\n",
912 prefix,
913 (c->secure_bits & SECURE_KEEP_CAPS) ? " keep-caps" : "",
914 (c->secure_bits & SECURE_KEEP_CAPS_LOCKED) ? " keep-caps-locked" : "",
915 (c->secure_bits & SECURE_NO_SETUID_FIXUP) ? " no-setuid-fixup" : "",
916 (c->secure_bits & SECURE_NO_SETUID_FIXUP_LOCKED) ? " no-setuid-fixup-locked" : "",
917 (c->secure_bits & SECURE_NOROOT) ? " noroot" : "",
918 (c->secure_bits & SECURE_NOROOT_LOCKED) ? "noroot-locked" : "");
919
920 if (c->capability_bounding_set_drop) {
921 fprintf(f, "%sCapabilityBoundingSetDrop:", prefix);
922
923 for (i = 0; i <= CAP_LAST_CAP; i++)
924 if (c->capability_bounding_set_drop & (1 << i)) {
925 char *t;
926
927 if ((t = cap_to_name(i))) {
928 fprintf(f, " %s", t);
929 free(t);
930 }
931 }
932
933 fputs("\n", f);
934 }
935
936 if (c->user)
937 fprintf(f, "%sUser: %s", prefix, c->user);
938 if (c->group)
939 fprintf(f, "%sGroup: %s", prefix, c->group);
940
941 if (c->supplementary_groups) {
942 char **g;
943
944 fprintf(f, "%sSupplementaryGroups:", prefix);
945
946 STRV_FOREACH(g, c->supplementary_groups)
947 fprintf(f, " %s", *g);
948
949 fputs("\n", f);
950 }
5cb5a6ff
LP
951}
952
034c6ed7
LP
953void exec_status_fill(ExecStatus *s, pid_t pid, int code, int status) {
954 assert(s);
5cb5a6ff 955
034c6ed7
LP
956 s->pid = pid;
957 s->code = code;
958 s->status = status;
959 s->timestamp = now(CLOCK_REALTIME);
5cb5a6ff 960}
44d8db9e
LP
961
962char *exec_command_line(ExecCommand *c) {
963 size_t k;
964 char *n, *p, **a;
965 bool first = true;
966
967 assert(c);
968 assert(c->argv);
969
9164977d 970 k = 1;
44d8db9e
LP
971 STRV_FOREACH(a, c->argv)
972 k += strlen(*a)+3;
973
974 if (!(n = new(char, k)))
975 return NULL;
976
977 p = n;
978 STRV_FOREACH(a, c->argv) {
979
980 if (!first)
981 *(p++) = ' ';
982 else
983 first = false;
984
985 if (strpbrk(*a, WHITESPACE)) {
986 *(p++) = '\'';
987 p = stpcpy(p, *a);
988 *(p++) = '\'';
989 } else
990 p = stpcpy(p, *a);
991
992 }
993
9164977d
LP
994 *p = 0;
995
44d8db9e
LP
996 /* FIXME: this doesn't really handle arguments that have
997 * spaces and ticks in them */
998
999 return n;
1000}
1001
1002void exec_command_dump(ExecCommand *c, FILE *f, const char *prefix) {
1003 char *cmd;
1004
1005 assert(c);
1006 assert(f);
1007
1008 if (!prefix)
1009 prefix = "";
1010
1011 cmd = exec_command_line(c);
1012
1013 fprintf(f,
1014 "%sCommand Line: %s\n",
1015 prefix, cmd ? cmd : strerror(ENOMEM));
1016
1017 free(cmd);
1018}
1019
1020void exec_command_dump_list(ExecCommand *c, FILE *f, const char *prefix) {
1021 assert(f);
1022
1023 if (!prefix)
1024 prefix = "";
1025
1026 LIST_FOREACH(command, c, c)
1027 exec_command_dump(c, f, prefix);
1028}
94f04347 1029
a6a80b4f
LP
1030void exec_command_append_list(ExecCommand **l, ExecCommand *e) {
1031 ExecCommand *end;
1032
1033 assert(l);
1034 assert(e);
1035
1036 if (*l) {
1037 /* It's kinda important that we keep the order here */
1038 LIST_FIND_TAIL(ExecCommand, command, *l, end);
1039 LIST_INSERT_AFTER(ExecCommand, command, *l, end, e);
1040 } else
1041 *l = e;
1042}
1043
94f04347
LP
1044static const char* const exec_output_table[_EXEC_OUTPUT_MAX] = {
1045 [EXEC_OUTPUT_CONSOLE] = "console",
1046 [EXEC_OUTPUT_NULL] = "null",
1047 [EXEC_OUTPUT_SYSLOG] = "syslog",
1048 [EXEC_OUTPUT_KERNEL] = "kernel"
1049};
1050
1051DEFINE_STRING_TABLE_LOOKUP(exec_output, ExecOutput);
1052
1053static const char* const exec_input_table[_EXEC_INPUT_MAX] = {
1054 [EXEC_INPUT_NULL] = "null",
1055 [EXEC_INPUT_CONSOLE] = "console"
1056};
1057
1058DEFINE_STRING_TABLE_LOOKUP(exec_input, ExecInput);