chiark / gitweb /
fixme: minor updates
[elogind.git] / execute.c
1 /*-*- Mode: C; c-basic-offset: 8 -*-*/
2
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
22 #include <assert.h>
23 #include <dirent.h>
24 #include <errno.h>
25 #include <fcntl.h>
26 #include <unistd.h>
27 #include <string.h>
28 #include <signal.h>
29 #include <sys/socket.h>
30 #include <sys/un.h>
31 #include <sys/prctl.h>
32 #include <linux/sched.h>
33 #include <sys/types.h>
34 #include <sys/stat.h>
35 #include <grp.h>
36 #include <pwd.h>
37
38 #include "execute.h"
39 #include "strv.h"
40 #include "macro.h"
41 #include "util.h"
42 #include "log.h"
43 #include "ioprio.h"
44 #include "securebits.h"
45
46 static 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
90 finish:
91         closedir(d);
92         return r;
93 }
94
95 static 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
119                         assert_se(close_nointr(fds[i]) == 0);
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
137 static int flags_fds(int fds[], unsigned n_fds, bool nonblock) {
138         unsigned i;
139
140         if (n_fds <= 0)
141                 return 0;
142
143         assert(fds);
144
145         /* Drops/Sets O_NONBLOCK and FD_CLOEXEC from the file flags */
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
153                 if (nonblock)
154                         flags |= O_NONBLOCK;
155                 else
156                         flags &= ~O_NONBLOCK;
157
158                 if (fcntl(fds[i], F_SETFL, flags) < 0)
159                         return -errno;
160
161                 /* We unconditionally drop FD_CLOEXEC from the fds,
162                  * since after all we want to pass these fds to our
163                  * children */
164                 if ((flags = fcntl(fds[i], F_GETFD, 0)) < 0)
165                         return -errno;
166
167                 if (fcntl(fds[i], F_SETFD, flags &~FD_CLOEXEC) < 0)
168                         return -errno;
169         }
170
171         return 0;
172 }
173
174 static 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
191 static int setup_output(const ExecContext *context, const char *ident) {
192         int r;
193
194         assert(context);
195
196         switch (context->output) {
197
198         case EXEC_OUTPUT_CONSOLE:
199                 return 0;
200
201         case EXEC_OUTPUT_NULL:
202
203                 if ((r = replace_null_fd(STDOUT_FILENO, O_WRONLY)) < 0 ||
204                     (r = replace_null_fd(STDERR_FILENO, O_WRONLY)) < 0)
205                         return r;
206
207                 return 0;
208
209         case EXEC_OUTPUT_KERNEL:
210         case EXEC_OUTPUT_SYSLOG: {
211
212                 int fd;
213                 union {
214                         struct sockaddr sa;
215                         struct sockaddr_un un;
216                 } sa;
217
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",
265                         context->output == EXEC_OUTPUT_KERNEL ? "kmsg" : "syslog",
266                         context->syslog_priority,
267                         context->syslog_identifier ? context->syslog_identifier : ident);
268
269                 return 0;
270         }
271
272         default:
273                 assert_not_reached("Unknown output type");
274         }
275 }
276
277 static int setup_input(const ExecContext *context) {
278         int r;
279
280         assert(context);
281
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         }
296 }
297
298 static 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
328 static 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
374 static 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
447 static 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
502 int 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
509         pid_t pid;
510
511         assert(command);
512         assert(context);
513         assert(ret);
514         assert(fds || n_fds <= 0);
515
516         log_debug("About to execute %s", command->path);
517
518         if ((pid = fork()) < 0)
519                 return -errno;
520
521         if (pid == 0) {
522                 int i, r;
523                 sigset_t ss;
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;
529
530                 /* child */
531
532                 if (sigemptyset(&ss) < 0 ||
533                     sigprocmask(SIG_SETMASK, &ss, NULL) < 0) {
534                         r = EXIT_SIGNAL_MASK;
535                         goto fail;
536                 }
537
538                 if (setpgid(0, 0) < 0) {
539                         r = EXIT_PGID;
540                         goto fail;
541                 }
542
543                 umask(context->umask);
544
545                 if (setup_input(context) < 0) {
546                         r = EXIT_INPUT;
547                         goto fail;
548                 }
549
550                 if (setup_output(context, file_name_from_path(command->path)) < 0) {
551                         r = EXIT_OUTPUT;
552                         goto fail;
553                 }
554
555                 if (context->oom_adjust_set) {
556                         char t[16];
557
558                         snprintf(t, sizeof(t), "%i", context->oom_adjust);
559                         char_array_0(t);
560
561                         if (write_one_line_file("/proc/self/oom_adj", t) < 0) {
562                                 r = EXIT_OOM_ADJUST;
563                                 goto fail;
564                         }
565                 }
566
567                 if (context->nice_set)
568                         if (setpriority(PRIO_PROCESS, 0, context->nice) < 0) {
569                                 r = EXIT_NICE;
570                                 goto fail;
571                         }
572
573                 if (context->cpu_sched_set) {
574                         struct sched_param param;
575
576                         zero(param);
577                         param.sched_priority = context->cpu_sched_priority;
578
579                         if (sched_setscheduler(0, context->cpu_sched_policy |
580                                                (context->cpu_sched_reset_on_fork ? SCHED_RESET_ON_FORK : 0), &param) < 0) {
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
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
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
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
649                 if (close_fds(fds, n_fds) < 0 ||
650                     shift_fds(fds, n_fds) < 0 ||
651                     flags_fds(fds, n_fds, context->non_blocking) < 0) {
652                         r = EXIT_FDS;
653                         goto fail;
654                 }
655
656                 if (apply_permissions) {
657
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                                 }
666                         }
667
668                         if (context->user)
669                                 if (enforce_user(context, uid) < 0) {
670                                         r = EXIT_USER;
671                                         goto fail;
672                                 }
673
674                         if (prctl(PR_SET_SECUREBITS, context->secure_bits) < 0) {
675                                 r = EXIT_SECUREBITS;
676                                 goto fail;
677                         }
678
679                         if (context->capabilities)
680                                 if (cap_set_proc(context->capabilities) < 0) {
681                                         r = EXIT_CAPABILITIES;
682                                         goto fail;
683                                 }
684                 }
685
686                 if (!(our_env = new0(char*, 6))) {
687                         r = EXIT_MEMORY;
688                         goto fail;
689                 }
690
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                         }
697
698                 if (home)
699                         if (asprintf(our_env + n_env++, "HOME=%s", home) < 0) {
700                                 r = EXIT_MEMORY;
701                                 goto fail;
702                         }
703
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                         }
710
711                 if (!(final_env = strv_env_merge(environ, our_env, context->environment, NULL))) {
712                         r = EXIT_MEMORY;
713                         goto fail;
714                 }
715
716                 execve(command->path, command->argv, final_env);
717                 r = EXIT_EXEC;
718
719         fail:
720                 strv_free(our_env);
721                 strv_free(final_env);
722
723                 _exit(r);
724         }
725
726
727         log_debug("Forked %s as %llu", command->path, (unsigned long long) pid);
728
729         *ret = pid;
730         return 0;
731 }
732
733 void exec_context_init(ExecContext *c) {
734         assert(c);
735
736         c->umask = 0002;
737         c->oom_adjust = 0;
738         c->oom_adjust_set = false;
739         c->nice = 0;
740         c->nice_set = false;
741         c->ioprio = IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE, 0);
742         c->ioprio_set = false;
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;
748
749         c->input = 0;
750         c->output = 0;
751         c->syslog_priority = LOG_DAEMON|LOG_INFO;
752
753         c->secure_bits = 0;
754         c->capability_bounding_set_drop = 0;
755 }
756
757 void exec_context_done(ExecContext *c) {
758         unsigned l;
759
760         assert(c);
761
762         strv_free(c->environment);
763         c->environment = NULL;
764
765         for (l = 0; l < ELEMENTSOF(c->rlimit); l++) {
766                 free(c->rlimit[l]);
767                 c->rlimit[l] = NULL;
768         }
769
770         free(c->working_directory);
771         c->working_directory = NULL;
772         free(c->root_directory);
773         c->root_directory = NULL;
774
775         free(c->syslog_identifier);
776         c->syslog_identifier = NULL;
777
778         free(c->user);
779         c->user = NULL;
780
781         free(c->group);
782         c->group = NULL;
783
784         strv_free(c->supplementary_groups);
785         c->supplementary_groups = NULL;
786
787         if (c->capabilities) {
788                 cap_free(c->capabilities);
789                 c->capabilities = NULL;
790         }
791 }
792
793 void exec_command_free_list(ExecCommand *c) {
794         ExecCommand *i;
795
796         while ((i = c)) {
797                 LIST_REMOVE(ExecCommand, command, c, i);
798
799                 free(i->path);
800                 strv_free(i->argv);
801                 free(i);
802         }
803 }
804
805 void 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
814 void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) {
815         char ** e;
816         unsigned i;
817
818         assert(c);
819         assert(f);
820
821         if (!prefix)
822                 prefix = "";
823
824         fprintf(f,
825                 "%sUMask: %04o\n"
826                 "%sWorkingDirectory: %s\n"
827                 "%sRootDirectory: %s\n"
828                 "%sNonBlocking: %s\n",
829                 prefix, c->umask,
830                 prefix, c->working_directory ? c->working_directory : "/",
831                 prefix, c->root_directory ? c->root_directory : "/",
832                 prefix, yes_no(c->non_blocking));
833
834         if (c->environment)
835                 for (e = c->environment; *e; e++)
836                         fprintf(f, "%sEnvironment: %s\n", prefix, *e);
837
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);
847
848         for (i = 0; i < RLIM_NLIMITS; i++)
849                 if (c->rlimit[i])
850                         fprintf(f, "%s%s: %llu\n", prefix, rlimit_to_string(i), (unsigned long long) c->rlimit[i]->rlim_max);
851
852         if (c->ioprio_set)
853                 fprintf(f,
854                         "%sIOSchedulingClass: %s\n"
855                         "%sIOPriority: %i\n",
856                         prefix, ioprio_class_to_string(IOPRIO_PRIO_CLASS(c->ioprio)),
857                         prefix, (int) IOPRIO_PRIO_DATA(c->ioprio));
858
859         if (c->cpu_sched_set)
860                 fprintf(f,
861                         "%sCPUSchedulingPolicy: %s\n"
862                         "%sCPUSchedulingPriority: %i\n"
863                         "%sCPUSchedulingResetOnFork: %s\n",
864                         prefix, sched_policy_to_string(c->cpu_sched_policy),
865                         prefix, c->cpu_sched_priority,
866                         prefix, yes_no(c->cpu_sched_reset_on_fork));
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         }
942 }
943
944 void exec_status_fill(ExecStatus *s, pid_t pid, int code, int status) {
945         assert(s);
946
947         s->pid = pid;
948         s->code = code;
949         s->status = status;
950         s->timestamp = now(CLOCK_REALTIME);
951 }
952
953 char *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
961         k = 1;
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
985         *p = 0;
986
987         /* FIXME: this doesn't really handle arguments that have
988          * spaces and ticks in them */
989
990         return n;
991 }
992
993 void 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
1011 void 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 }
1020
1021 void 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
1035 static 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
1042 DEFINE_STRING_TABLE_LOOKUP(exec_output, ExecOutput);
1043
1044 static const char* const exec_input_table[_EXEC_INPUT_MAX] = {
1045         [EXEC_INPUT_NULL] = "null",
1046         [EXEC_INPUT_CONSOLE] = "console"
1047 };
1048
1049 DEFINE_STRING_TABLE_LOOKUP(exec_input, ExecInput);