]> git.ipfire.org Git - people/ms/systemd.git/blame - execute.c
fixme: minor update
[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. */
464 if (uid != 0)
465 if (prctl(PR_SET_SECUREBITS, context->secure_bits|SECURE_KEEP_CAPS) < 0)
466 return -errno;
467
468 /* Second step: set the capabilites. This will reduce
469 * the capabilities to the minimum we need. */
470
471 if (!(d = cap_dup(context->capabilities)))
472 return -errno;
473
474 if (cap_set_flag(d, CAP_EFFECTIVE, ELEMENTSOF(bits), bits, CAP_SET) < 0 ||
475 cap_set_flag(d, CAP_PERMITTED, ELEMENTSOF(bits), bits, CAP_SET) < 0) {
476 r = -errno;
477 cap_free(d);
478 return r;
479 }
480
481 if (cap_set_proc(d) < 0) {
482 r = -errno;
483 cap_free(d);
484 return r;
485 }
486
487 cap_free(d);
488 }
489
490 /* Third step: actually set the uids */
491 if (setresuid(uid, uid, uid) < 0)
492 return -errno;
493
494 /* At this point we should have all necessary capabilities but
495 are otherwise a normal user. However, the caps might got
496 corrupted due to the setresuid() so we need clean them up
497 later. This is done outside of this call. */
498
499 return 0;
500}
501
502int exec_spawn(const ExecCommand *command,
503 const ExecContext *context,
504 int *fds, unsigned n_fds,
505 bool apply_permissions,
506 bool apply_chroot,
507 pid_t *ret) {
508
034c6ed7
LP
509 pid_t pid;
510
5cb5a6ff
LP
511 assert(command);
512 assert(context);
513 assert(ret);
034c6ed7
LP
514 assert(fds || n_fds <= 0);
515
81a2b7ce 516 log_debug("About to execute %s", command->path);
acbb0225 517
034c6ed7
LP
518 if ((pid = fork()) < 0)
519 return -errno;
520
521 if (pid == 0) {
034c6ed7 522 int i, r;
309bff19 523 sigset_t ss;
81a2b7ce
LP
524 const char *username = NULL, *home = NULL;
525 uid_t uid = (uid_t) -1;
526 gid_t gid = (gid_t) -1;
527 char **our_env = NULL, **final_env = NULL;
528 unsigned n_env = 0;
309bff19 529
034c6ed7 530 /* child */
5cb5a6ff 531
309bff19
LP
532 if (sigemptyset(&ss) < 0 ||
533 sigprocmask(SIG_SETMASK, &ss, NULL) < 0) {
534 r = EXIT_SIGNAL_MASK;
535 goto fail;
536 }
537
9eba9da4
LP
538 if (setpgid(0, 0) < 0) {
539 r = EXIT_PGID;
034c6ed7
LP
540 goto fail;
541 }
542
9eba9da4
LP
543 umask(context->umask);
544
94f04347
LP
545 if (setup_input(context) < 0) {
546 r = EXIT_INPUT;
547 goto fail;
548 }
549
071830ff
LP
550 if (setup_output(context, file_name_from_path(command->path)) < 0) {
551 r = EXIT_OUTPUT;
552 goto fail;
553 }
554
fb33a393
LP
555 if (context->oom_adjust_set) {
556 char t[16];
034c6ed7 557
fb33a393
LP
558 snprintf(t, sizeof(t), "%i", context->oom_adjust);
559 char_array_0(t);
034c6ed7 560
fb33a393
LP
561 if (write_one_line_file("/proc/self/oom_adj", t) < 0) {
562 r = EXIT_OOM_ADJUST;
563 goto fail;
564 }
034c6ed7
LP
565 }
566
fb33a393
LP
567 if (context->nice_set)
568 if (setpriority(PRIO_PROCESS, 0, context->nice) < 0) {
569 r = EXIT_NICE;
570 goto fail;
571 }
572
94f04347
LP
573 if (context->cpu_sched_set) {
574 struct sched_param param;
575
576 zero(param);
577 param.sched_priority = context->cpu_sched_priority;
578
38b48754
LP
579 if (sched_setscheduler(0, context->cpu_sched_policy |
580 (context->cpu_sched_reset_on_fork ? SCHED_RESET_ON_FORK : 0), &param) < 0) {
94f04347
LP
581 r = EXIT_SETSCHEDULER;
582 goto fail;
583 }
584 }
585
586 if (context->cpu_affinity_set)
587 if (sched_setaffinity(0, sizeof(context->cpu_affinity), &context->cpu_affinity) < 0) {
588 r = EXIT_CPUAFFINITY;
589 goto fail;
590 }
591
9eba9da4
LP
592 if (context->ioprio_set)
593 if (ioprio_set(IOPRIO_WHO_PROCESS, 0, context->ioprio) < 0) {
594 r = EXIT_IOPRIO;
595 goto fail;
596 }
597
94f04347
LP
598 if (context->timer_slack_ns_set)
599 if (prctl(PR_SET_TIMERSLACK, context->timer_slack_ns_set) < 0) {
600 r = EXIT_TIMERSLACK;
601 goto fail;
602 }
603
81a2b7ce
LP
604 if (context->user) {
605 username = context->user;
606 if (get_user_creds(&username, &uid, &gid, &home) < 0) {
607 r = EXIT_USER;
608 goto fail;
609 }
610 }
611
612 if (apply_permissions)
613 if (enforce_groups(context, username, uid) < 0) {
614 r = EXIT_GROUP;
615 goto fail;
616 }
617
618 if (apply_chroot) {
619 if (context->root_directory)
620 if (chroot(context->root_directory) < 0) {
621 r = EXIT_CHROOT;
622 goto fail;
623 }
624
625 if (chdir(context->working_directory ? context->working_directory : "/") < 0) {
626 r = EXIT_CHDIR;
627 goto fail;
628 }
629 } else {
630
631 char *d;
632
633 if (asprintf(&d, "%s/%s",
634 context->root_directory ? context->root_directory : "",
635 context->working_directory ? context->working_directory : "") < 0) {
636 r = EXIT_MEMORY;
637 goto fail;
638 }
639
640 if (chdir(d) < 0) {
641 free(d);
642 r = EXIT_CHDIR;
643 goto fail;
644 }
645
646 free(d);
647 }
648
034c6ed7 649 if (close_fds(fds, n_fds) < 0 ||
47a71eed 650 shift_fds(fds, n_fds) < 0 ||
451a074f 651 flags_fds(fds, n_fds, context->non_blocking) < 0) {
034c6ed7
LP
652 r = EXIT_FDS;
653 goto fail;
654 }
655
81a2b7ce 656 if (apply_permissions) {
034c6ed7 657
81a2b7ce
LP
658 for (i = 0; i < RLIMIT_NLIMITS; i++) {
659 if (!context->rlimit[i])
660 continue;
661
662 if (setrlimit(i, context->rlimit[i]) < 0) {
663 r = EXIT_LIMITS;
664 goto fail;
665 }
034c6ed7 666 }
034c6ed7 667
81a2b7ce
LP
668 if (context->user)
669 if (enforce_user(context, uid) < 0) {
670 r = EXIT_USER;
671 goto fail;
672 }
673
94f04347
LP
674 if (prctl(PR_SET_SECUREBITS, context->secure_bits) < 0) {
675 r = EXIT_SECUREBITS;
676 goto fail;
677 }
81a2b7ce
LP
678
679 if (context->capabilities)
680 if (cap_set_proc(context->capabilities) < 0) {
681 r = EXIT_CAPABILITIES;
682 goto fail;
683 }
94f04347
LP
684 }
685
81a2b7ce
LP
686 if (!(our_env = new0(char*, 6))) {
687 r = EXIT_MEMORY;
688 goto fail;
689 }
034c6ed7 690
81a2b7ce
LP
691 if (n_fds > 0)
692 if (asprintf(our_env + n_env++, "LISTEN_PID=%llu", (unsigned long long) getpid()) < 0 ||
693 asprintf(our_env + n_env++, "LISTEN_FDS=%u", n_fds) < 0) {
694 r = EXIT_MEMORY;
695 goto fail;
696 }
034c6ed7 697
81a2b7ce
LP
698 if (home)
699 if (asprintf(our_env + n_env++, "HOME=%s", home) < 0) {
700 r = EXIT_MEMORY;
701 goto fail;
702 }
034c6ed7 703
81a2b7ce
LP
704 if (username)
705 if (asprintf(our_env + n_env++, "LOGNAME=%s", username) < 0 ||
706 asprintf(our_env + n_env++, "USER=%s", username) < 0) {
707 r = EXIT_MEMORY;
708 goto fail;
709 }
034c6ed7 710
81a2b7ce
LP
711 if (!(final_env = strv_env_merge(environ, our_env, context->environment, NULL))) {
712 r = EXIT_MEMORY;
713 goto fail;
714 }
034c6ed7 715
81a2b7ce 716 execve(command->path, command->argv, final_env);
034c6ed7
LP
717 r = EXIT_EXEC;
718
719 fail:
81a2b7ce
LP
720 strv_free(our_env);
721 strv_free(final_env);
722
034c6ed7
LP
723 _exit(r);
724 }
725
2da3263a 726
81a2b7ce 727 log_debug("Forked %s as %llu", command->path, (unsigned long long) pid);
2da3263a 728
034c6ed7 729 *ret = pid;
5cb5a6ff
LP
730 return 0;
731}
732
034c6ed7
LP
733void exec_context_init(ExecContext *c) {
734 assert(c);
735
736 c->umask = 0002;
034c6ed7 737 c->oom_adjust = 0;
9eba9da4 738 c->oom_adjust_set = false;
034c6ed7 739 c->nice = 0;
9eba9da4
LP
740 c->nice_set = false;
741 c->ioprio = IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE, 0);
742 c->ioprio_set = false;
94f04347
LP
743 c->cpu_sched_policy = SCHED_OTHER;
744 c->cpu_sched_priority = 0;
745 c->cpu_sched_set = false;
746 CPU_ZERO(&c->cpu_affinity);
747 c->cpu_affinity_set = false;
071830ff 748
94f04347 749 c->input = 0;
071830ff
LP
750 c->output = 0;
751 c->syslog_priority = LOG_DAEMON|LOG_INFO;
94f04347
LP
752
753 c->secure_bits = 0;
754 c->capability_bounding_set_drop = 0;
034c6ed7
LP
755}
756
757void exec_context_done(ExecContext *c) {
5cb5a6ff
LP
758 unsigned l;
759
760 assert(c);
761
762 strv_free(c->environment);
034c6ed7 763 c->environment = NULL;
5cb5a6ff 764
034c6ed7 765 for (l = 0; l < ELEMENTSOF(c->rlimit); l++) {
5cb5a6ff 766 free(c->rlimit[l]);
034c6ed7
LP
767 c->rlimit[l] = NULL;
768 }
769
9eba9da4
LP
770 free(c->working_directory);
771 c->working_directory = NULL;
772 free(c->root_directory);
773 c->root_directory = NULL;
5cb5a6ff 774
071830ff
LP
775 free(c->syslog_identifier);
776 c->syslog_identifier = NULL;
777
5cb5a6ff 778 free(c->user);
034c6ed7
LP
779 c->user = NULL;
780
5cb5a6ff 781 free(c->group);
034c6ed7
LP
782 c->group = NULL;
783
784 strv_free(c->supplementary_groups);
785 c->supplementary_groups = NULL;
94f04347
LP
786
787 if (c->capabilities) {
788 cap_free(c->capabilities);
789 c->capabilities = NULL;
790 }
5cb5a6ff
LP
791}
792
793void exec_command_free_list(ExecCommand *c) {
794 ExecCommand *i;
795
796 while ((i = c)) {
034c6ed7 797 LIST_REMOVE(ExecCommand, command, c, i);
5cb5a6ff
LP
798
799 free(i->path);
44d8db9e 800 strv_free(i->argv);
5cb5a6ff
LP
801 free(i);
802 }
803}
804
034c6ed7
LP
805void exec_command_free_array(ExecCommand **c, unsigned n) {
806 unsigned i;
807
808 for (i = 0; i < n; i++) {
809 exec_command_free_list(c[i]);
810 c[i] = NULL;
811 }
812}
813
5cb5a6ff 814void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) {
94f04347
LP
815 char ** e;
816 unsigned i;
9eba9da4 817
5cb5a6ff
LP
818 assert(c);
819 assert(f);
820
821 if (!prefix)
822 prefix = "";
823
824 fprintf(f,
94f04347
LP
825 "%sUMask: %04o\n"
826 "%sWorkingDirectory: %s\n"
451a074f
LP
827 "%sRootDirectory: %s\n"
828 "%sNonBlocking: %s\n",
5cb5a6ff 829 prefix, c->umask,
9eba9da4 830 prefix, c->working_directory ? c->working_directory : "/",
451a074f
LP
831 prefix, c->root_directory ? c->root_directory : "/",
832 prefix, yes_no(c->non_blocking));
fb33a393 833
94f04347
LP
834 if (c->environment)
835 for (e = c->environment; *e; e++)
836 fprintf(f, "%sEnvironment: %s\n", prefix, *e);
837
fb33a393
LP
838 if (c->nice_set)
839 fprintf(f,
840 "%sNice: %i\n",
841 prefix, c->nice);
842
843 if (c->oom_adjust_set)
844 fprintf(f,
845 "%sOOMAdjust: %i\n",
846 prefix, c->oom_adjust);
9eba9da4 847
94f04347
LP
848 for (i = 0; i < RLIM_NLIMITS; i++)
849 if (c->rlimit[i])
ea430986 850 fprintf(f, "%s%s: %llu\n", prefix, rlimit_to_string(i), (unsigned long long) c->rlimit[i]->rlim_max);
94f04347 851
9eba9da4
LP
852 if (c->ioprio_set)
853 fprintf(f,
854 "%sIOSchedulingClass: %s\n"
855 "%sIOPriority: %i\n",
94f04347 856 prefix, ioprio_class_to_string(IOPRIO_PRIO_CLASS(c->ioprio)),
9eba9da4 857 prefix, (int) IOPRIO_PRIO_DATA(c->ioprio));
94f04347
LP
858
859 if (c->cpu_sched_set)
860 fprintf(f,
861 "%sCPUSchedulingPolicy: %s\n"
38b48754
LP
862 "%sCPUSchedulingPriority: %i\n"
863 "%sCPUSchedulingResetOnFork: %s\n",
94f04347 864 prefix, sched_policy_to_string(c->cpu_sched_policy),
38b48754
LP
865 prefix, c->cpu_sched_priority,
866 prefix, yes_no(c->cpu_sched_reset_on_fork));
94f04347
LP
867
868 if (c->cpu_affinity_set) {
869 fprintf(f, "%sCPUAffinity:", prefix);
870 for (i = 0; i < CPU_SETSIZE; i++)
871 if (CPU_ISSET(i, &c->cpu_affinity))
872 fprintf(f, " %i", i);
873 fputs("\n", f);
874 }
875
876 if (c->timer_slack_ns_set)
877 fprintf(f, "%sTimerSlackNS: %lu\n", prefix, c->timer_slack_ns);
878
879 fprintf(f,
880 "%sInput: %s\n"
881 "%sOutput: %s\n",
882 prefix, exec_input_to_string(c->input),
883 prefix, exec_output_to_string(c->output));
884
885 if (c->output == EXEC_OUTPUT_SYSLOG || c->output == EXEC_OUTPUT_KERNEL)
886 fprintf(f,
887 "%sSyslogFacility: %s\n"
888 "%sSyslogLevel: %s\n",
889 prefix, log_facility_to_string(LOG_FAC(c->syslog_priority)),
890 prefix, log_level_to_string(LOG_PRI(c->syslog_priority)));
891
892 if (c->capabilities) {
893 char *t;
894 if ((t = cap_to_text(c->capabilities, NULL))) {
895 fprintf(f, "%sCapabilities: %s\n",
896 prefix, t);
897 cap_free(t);
898 }
899 }
900
901 if (c->secure_bits)
902 fprintf(f, "%sSecure Bits:%s%s%s%s%s%s\n",
903 prefix,
904 (c->secure_bits & SECURE_KEEP_CAPS) ? " keep-caps" : "",
905 (c->secure_bits & SECURE_KEEP_CAPS_LOCKED) ? " keep-caps-locked" : "",
906 (c->secure_bits & SECURE_NO_SETUID_FIXUP) ? " no-setuid-fixup" : "",
907 (c->secure_bits & SECURE_NO_SETUID_FIXUP_LOCKED) ? " no-setuid-fixup-locked" : "",
908 (c->secure_bits & SECURE_NOROOT) ? " noroot" : "",
909 (c->secure_bits & SECURE_NOROOT_LOCKED) ? "noroot-locked" : "");
910
911 if (c->capability_bounding_set_drop) {
912 fprintf(f, "%sCapabilityBoundingSetDrop:", prefix);
913
914 for (i = 0; i <= CAP_LAST_CAP; i++)
915 if (c->capability_bounding_set_drop & (1 << i)) {
916 char *t;
917
918 if ((t = cap_to_name(i))) {
919 fprintf(f, " %s", t);
920 free(t);
921 }
922 }
923
924 fputs("\n", f);
925 }
926
927 if (c->user)
928 fprintf(f, "%sUser: %s", prefix, c->user);
929 if (c->group)
930 fprintf(f, "%sGroup: %s", prefix, c->group);
931
932 if (c->supplementary_groups) {
933 char **g;
934
935 fprintf(f, "%sSupplementaryGroups:", prefix);
936
937 STRV_FOREACH(g, c->supplementary_groups)
938 fprintf(f, " %s", *g);
939
940 fputs("\n", f);
941 }
5cb5a6ff
LP
942}
943
034c6ed7
LP
944void exec_status_fill(ExecStatus *s, pid_t pid, int code, int status) {
945 assert(s);
5cb5a6ff 946
034c6ed7
LP
947 s->pid = pid;
948 s->code = code;
949 s->status = status;
950 s->timestamp = now(CLOCK_REALTIME);
5cb5a6ff 951}
44d8db9e
LP
952
953char *exec_command_line(ExecCommand *c) {
954 size_t k;
955 char *n, *p, **a;
956 bool first = true;
957
958 assert(c);
959 assert(c->argv);
960
9164977d 961 k = 1;
44d8db9e
LP
962 STRV_FOREACH(a, c->argv)
963 k += strlen(*a)+3;
964
965 if (!(n = new(char, k)))
966 return NULL;
967
968 p = n;
969 STRV_FOREACH(a, c->argv) {
970
971 if (!first)
972 *(p++) = ' ';
973 else
974 first = false;
975
976 if (strpbrk(*a, WHITESPACE)) {
977 *(p++) = '\'';
978 p = stpcpy(p, *a);
979 *(p++) = '\'';
980 } else
981 p = stpcpy(p, *a);
982
983 }
984
9164977d
LP
985 *p = 0;
986
44d8db9e
LP
987 /* FIXME: this doesn't really handle arguments that have
988 * spaces and ticks in them */
989
990 return n;
991}
992
993void exec_command_dump(ExecCommand *c, FILE *f, const char *prefix) {
994 char *cmd;
995
996 assert(c);
997 assert(f);
998
999 if (!prefix)
1000 prefix = "";
1001
1002 cmd = exec_command_line(c);
1003
1004 fprintf(f,
1005 "%sCommand Line: %s\n",
1006 prefix, cmd ? cmd : strerror(ENOMEM));
1007
1008 free(cmd);
1009}
1010
1011void exec_command_dump_list(ExecCommand *c, FILE *f, const char *prefix) {
1012 assert(f);
1013
1014 if (!prefix)
1015 prefix = "";
1016
1017 LIST_FOREACH(command, c, c)
1018 exec_command_dump(c, f, prefix);
1019}
94f04347 1020
a6a80b4f
LP
1021void exec_command_append_list(ExecCommand **l, ExecCommand *e) {
1022 ExecCommand *end;
1023
1024 assert(l);
1025 assert(e);
1026
1027 if (*l) {
1028 /* It's kinda important that we keep the order here */
1029 LIST_FIND_TAIL(ExecCommand, command, *l, end);
1030 LIST_INSERT_AFTER(ExecCommand, command, *l, end, e);
1031 } else
1032 *l = e;
1033}
1034
94f04347
LP
1035static const char* const exec_output_table[_EXEC_OUTPUT_MAX] = {
1036 [EXEC_OUTPUT_CONSOLE] = "console",
1037 [EXEC_OUTPUT_NULL] = "null",
1038 [EXEC_OUTPUT_SYSLOG] = "syslog",
1039 [EXEC_OUTPUT_KERNEL] = "kernel"
1040};
1041
1042DEFINE_STRING_TABLE_LOOKUP(exec_output, ExecOutput);
1043
1044static const char* const exec_input_table[_EXEC_INPUT_MAX] = {
1045 [EXEC_INPUT_NULL] = "null",
1046 [EXEC_INPUT_CONSOLE] = "console"
1047};
1048
1049DEFINE_STRING_TABLE_LOOKUP(exec_input, ExecInput);