chiark / gitweb /
implement recursive_stop/stop_when_unneeded unit flags
[elogind.git] / execute.c
1 /*-*- Mode: C; c-basic-offset: 8 -*-*/
2
3 #include <assert.h>
4 #include <dirent.h>
5 #include <errno.h>
6 #include <fcntl.h>
7 #include <unistd.h>
8 #include <string.h>
9 #include <signal.h>
10 #include <sys/socket.h>
11 #include <sys/un.h>
12
13 #include "execute.h"
14 #include "strv.h"
15 #include "macro.h"
16 #include "util.h"
17 #include "log.h"
18 #include "ioprio.h"
19
20 static int close_fds(int except[], unsigned n_except) {
21         DIR *d;
22         struct dirent *de;
23         int r = 0;
24
25         /* Modifies the fds array! (sorts it) */
26
27         if (!(d = opendir("/proc/self/fd")))
28                 return -errno;
29
30         while ((de = readdir(d))) {
31                 int fd;
32
33                 if (de->d_name[0] == '.')
34                         continue;
35
36                 if ((r = safe_atoi(de->d_name, &fd)) < 0)
37                         goto finish;
38
39                 if (fd < 3)
40                         continue;
41
42                 if (fd == dirfd(d))
43                         continue;
44
45                 if (except) {
46                         bool found;
47                         unsigned i;
48
49                         found = false;
50                         for (i = 0; i < n_except; i++)
51                                 if (except[i] == fd) {
52                                         found = true;
53                                         break;
54                                 }
55
56                         if (found)
57                                 continue;
58                 }
59
60                 if ((r = close_nointr(fd)) < 0)
61                         goto finish;
62         }
63
64 finish:
65         closedir(d);
66         return r;
67 }
68
69 static int shift_fds(int fds[], unsigned n_fds) {
70         int start, restart_from;
71
72         if (n_fds <= 0)
73                 return 0;
74
75         assert(fds);
76
77         start = 0;
78         for (;;) {
79                 int i;
80
81                 restart_from = -1;
82
83                 for (i = start; i < (int) n_fds; i++) {
84                         int nfd;
85
86                         /* Already at right index? */
87                         if (fds[i] == i+3)
88                                 continue;
89
90                         if ((nfd = fcntl(fds[i], F_DUPFD, i+3)) < 0)
91                                 return -errno;
92
93                         assert_se(close_nointr(fds[i]) == 0);
94                         fds[i] = nfd;
95
96                         /* Hmm, the fd we wanted isn't free? Then
97                          * let's remember that and try again from here*/
98                         if (nfd != i+3 && restart_from < 0)
99                                 restart_from = i;
100                 }
101
102                 if (restart_from < 0)
103                         break;
104
105                 start = restart_from;
106         }
107
108         return 0;
109 }
110
111 static int flags_fds(int fds[], unsigned n_fds) {
112         unsigned i;
113
114         if (n_fds <= 0)
115                 return 0;
116
117         assert(fds);
118
119         /* Drops O_NONBLOCK and FD_CLOEXEC from the file flags */
120
121         for (i = 0; i < n_fds; i++) {
122                 int flags;
123
124                 if ((flags = fcntl(fds[i], F_GETFL, 0)) < 0)
125                         return -errno;
126
127                 /* Since we are at it, let's make sure that nobody
128                  * forgot setting O_NONBLOCK for all our fds */
129
130                 if (fcntl(fds[i], F_SETFL, flags &~O_NONBLOCK) < 0)
131                         return -errno;
132
133                 if ((flags = fcntl(fds[i], F_GETFD, 0)) < 0)
134                         return -errno;
135
136                 /* Also make sure nobody forgot O_CLOEXEC for all our
137                  * fds */
138                 if (fcntl(fds[i], F_SETFD, flags &~FD_CLOEXEC) < 0)
139                         return -errno;
140         }
141
142         return 0;
143 }
144
145 static int replace_null_fd(int fd, int flags) {
146         int nfd;
147         assert(fd >= 0);
148
149         close_nointr(fd);
150
151         if ((nfd = open("/dev/null", flags|O_NOCTTY)) < 0)
152                 return -errno;
153
154         if (nfd != fd) {
155                 close_nointr_nofail(nfd);
156                 return -EIO;
157         }
158
159         return 0;
160 }
161
162 static int setup_output(const ExecContext *context, const char *ident) {
163         int r;
164
165         assert(context);
166
167         switch (context->output) {
168
169         case EXEC_CONSOLE:
170                 return 0;
171
172         case EXEC_NULL:
173
174                 if ((r = replace_null_fd(STDIN_FILENO, O_RDONLY)) < 0 ||
175                     (r = replace_null_fd(STDOUT_FILENO, O_WRONLY)) < 0 ||
176                     (r = replace_null_fd(STDERR_FILENO, O_WRONLY)) < 0)
177                         return r;
178
179                 return 0;
180
181         case EXEC_KERNEL:
182         case EXEC_SYSLOG: {
183
184                 int fd;
185                 union {
186                         struct sockaddr sa;
187                         struct sockaddr_un un;
188                 } sa;
189
190                 if ((r = replace_null_fd(STDIN_FILENO, O_RDONLY)) < 0)
191                         return r;
192
193                 close_nointr(STDOUT_FILENO);
194                 close_nointr(STDERR_FILENO);
195
196                 if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
197                         return -errno;
198
199                 if (fd != STDOUT_FILENO) {
200                         close_nointr_nofail(fd);
201                         return -EIO;
202                 }
203
204                 zero(sa);
205                 sa.sa.sa_family = AF_UNIX;
206                 strncpy(sa.un.sun_path+1, LOGGER_SOCKET, sizeof(sa.un.sun_path)-1);
207
208                 if (connect(fd, &sa.sa, sizeof(sa)) < 0) {
209                         close_nointr_nofail(fd);
210                         return -errno;
211                 }
212
213                 if (shutdown(fd, SHUT_RD) < 0) {
214                         close_nointr_nofail(fd);
215                         return -errno;
216                 }
217
218                 if ((fd = dup(fd)) < 0) {
219                         close_nointr_nofail(fd);
220                         return -errno;
221                 }
222
223                 if (fd != STDERR_FILENO) {
224                         close_nointr_nofail(fd);
225                         return -EIO;
226                 }
227
228                 /* We speak a very simple protocol between log server
229                  * and client: one line for the log destination (kmsg
230                  * or syslog), followed by the priority field,
231                  * followed by the process name. Since we replaced
232                  * stdin/stderr we simple use stdio to write to
233                  * it. Note that we use stderr, to minimize buffer
234                  * flushing issues. */
235
236                 fprintf(stderr,
237                         "%s\n"
238                         "%i\n"
239                         "%s\n",
240                         context->output == EXEC_KERNEL ? "kmsg" : "syslog",
241                         context->syslog_priority,
242                         context->syslog_identifier ? context->syslog_identifier : ident);
243
244                 return 0;
245         }
246         }
247
248         assert_not_reached("Unknown logging type");
249 }
250
251 int exec_spawn(const ExecCommand *command, const ExecContext *context, int *fds, unsigned n_fds, pid_t *ret) {
252         pid_t pid;
253
254         assert(command);
255         assert(context);
256         assert(ret);
257         assert(fds || n_fds <= 0);
258
259         log_debug("about to execute %s", command->path);
260
261         if ((pid = fork()) < 0)
262                 return -errno;
263
264         if (pid == 0) {
265                 char **e, **f = NULL;
266                 int i, r;
267                 sigset_t ss;
268
269                 /* child */
270
271                 if (sigemptyset(&ss) < 0 ||
272                     sigprocmask(SIG_SETMASK, &ss, NULL) < 0) {
273                         r = EXIT_SIGNAL_MASK;
274                         goto fail;
275                 }
276
277                 if (setpgid(0, 0) < 0) {
278                         r = EXIT_PGID;
279                         goto fail;
280                 }
281
282                 umask(context->umask);
283
284                 if (setup_output(context, file_name_from_path(command->path)) < 0) {
285                         r = EXIT_OUTPUT;
286                         goto fail;
287                 }
288
289                 if (context->oom_adjust_set) {
290                         char t[16];
291
292                         snprintf(t, sizeof(t), "%i", context->oom_adjust);
293                         char_array_0(t);
294
295                         if (write_one_line_file("/proc/self/oom_adj", t) < 0) {
296                                 r = EXIT_OOM_ADJUST;
297                                 goto fail;
298                         }
299                 }
300
301                 if (context->root_directory)
302                         if (chroot(context->root_directory) < 0) {
303                                 r = EXIT_CHROOT;
304                                 goto fail;
305                         }
306
307                 if (chdir(context->working_directory ? context->working_directory : "/") < 0) {
308                         r = EXIT_CHDIR;
309                         goto fail;
310                 }
311
312                 if (context->nice_set)
313                         if (setpriority(PRIO_PROCESS, 0, context->nice) < 0) {
314                                 r = EXIT_NICE;
315                                 goto fail;
316                         }
317
318                 if (context->ioprio_set)
319                         if (ioprio_set(IOPRIO_WHO_PROCESS, 0, context->ioprio) < 0) {
320                                 r = EXIT_IOPRIO;
321                                 goto fail;
322                         }
323
324                 if (close_fds(fds, n_fds) < 0 ||
325                     shift_fds(fds, n_fds) < 0 ||
326                     flags_fds(fds, n_fds) < 0) {
327                         r = EXIT_FDS;
328                         goto fail;
329                 }
330
331                 for (i = 0; i < RLIMIT_NLIMITS; i++) {
332                         if (!context->rlimit[i])
333                                 continue;
334
335                         if (setrlimit(i, context->rlimit[i]) < 0) {
336                                 r = EXIT_LIMITS;
337                                 goto fail;
338                         }
339                 }
340
341                 if (n_fds > 0) {
342                         char a[64], b[64];
343                         char *listen_env[3] = {
344                                 a,
345                                 b,
346                                 NULL
347                         };
348
349                         snprintf(a, sizeof(a), "LISTEN_PID=%llu", (unsigned long long) getpid());
350                         snprintf(b, sizeof(b), "LISTEN_FDS=%u", n_fds);
351
352                         a[sizeof(a)-1] = 0;
353                         b[sizeof(b)-1] = 0;
354
355                         if (context->environment) {
356                                 if (!(f = strv_merge(listen_env, context->environment))) {
357                                         r = EXIT_MEMORY;
358                                         goto fail;
359                                 }
360                                 e = f;
361                         } else
362                                 e = listen_env;
363
364                 } else
365                         e = context->environment;
366
367                 execve(command->path, command->argv, e);
368                 r = EXIT_EXEC;
369
370         fail:
371                 strv_free(f);
372                 _exit(r);
373         }
374
375
376         log_debug("executed %s as %llu", command->path, (unsigned long long) pid);
377
378         *ret = pid;
379         return 0;
380 }
381
382 void exec_context_init(ExecContext *c) {
383         assert(c);
384
385         c->umask = 0002;
386         cap_clear(c->capabilities);
387         c->capabilities_set = false;
388         c->oom_adjust = 0;
389         c->oom_adjust_set = false;
390         c->nice = 0;
391         c->nice_set = false;
392         c->ioprio = IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE, 0);
393         c->ioprio_set = false;
394
395         c->output = 0;
396         c->syslog_priority = LOG_DAEMON|LOG_INFO;
397 }
398
399 void exec_context_done(ExecContext *c) {
400         unsigned l;
401
402         assert(c);
403
404         strv_free(c->environment);
405         c->environment = NULL;
406
407         for (l = 0; l < ELEMENTSOF(c->rlimit); l++) {
408                 free(c->rlimit[l]);
409                 c->rlimit[l] = NULL;
410         }
411
412         free(c->working_directory);
413         c->working_directory = NULL;
414         free(c->root_directory);
415         c->root_directory = NULL;
416
417         free(c->syslog_identifier);
418         c->syslog_identifier = NULL;
419
420         free(c->user);
421         c->user = NULL;
422
423         free(c->group);
424         c->group = NULL;
425
426         strv_free(c->supplementary_groups);
427         c->supplementary_groups = NULL;
428 }
429
430 void exec_command_free_list(ExecCommand *c) {
431         ExecCommand *i;
432
433         while ((i = c)) {
434                 LIST_REMOVE(ExecCommand, command, c, i);
435
436                 free(i->path);
437                 strv_free(i->argv);
438                 free(i);
439         }
440 }
441
442 void exec_command_free_array(ExecCommand **c, unsigned n) {
443         unsigned i;
444
445         for (i = 0; i < n; i++) {
446                 exec_command_free_list(c[i]);
447                 c[i] = NULL;
448         }
449 }
450
451 void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) {
452
453         static const char * const table[] = {
454                 [IOPRIO_CLASS_NONE] = "none",
455                 [IOPRIO_CLASS_RT] = "realtime",
456                 [IOPRIO_CLASS_BE] = "best-effort",
457                 [IOPRIO_CLASS_IDLE] = "idle"
458         };
459
460         assert(c);
461         assert(f);
462
463         if (!prefix)
464                 prefix = "";
465
466         fprintf(f,
467                 "%sUmask: %04o\n"
468                 "%sWorking Directory: %s\n"
469                 "%sRoot Directory: %s\n",
470                 prefix, c->umask,
471                 prefix, c->working_directory ? c->working_directory : "/",
472                 prefix, c->root_directory ? c->root_directory : "/");
473
474         if (c->nice_set)
475                 fprintf(f,
476                         "%sNice: %i\n",
477                         prefix, c->nice);
478
479         if (c->oom_adjust_set)
480                 fprintf(f,
481                         "%sOOMAdjust: %i\n",
482                         prefix, c->oom_adjust);
483
484         if (c->ioprio_set)
485                 fprintf(f,
486                         "%sIOSchedulingClass: %s\n"
487                         "%sIOPriority: %i\n",
488                         prefix, table[IOPRIO_PRIO_CLASS(c->ioprio)],
489                         prefix, (int) IOPRIO_PRIO_DATA(c->ioprio));
490 }
491
492 void exec_status_fill(ExecStatus *s, pid_t pid, int code, int status) {
493         assert(s);
494
495         s->pid = pid;
496         s->code = code;
497         s->status = status;
498         s->timestamp = now(CLOCK_REALTIME);
499 }
500
501 char *exec_command_line(ExecCommand *c) {
502         size_t k;
503         char *n, *p, **a;
504         bool first = true;
505
506         assert(c);
507         assert(c->argv);
508
509         k = 1;
510         STRV_FOREACH(a, c->argv)
511                 k += strlen(*a)+3;
512
513         if (!(n = new(char, k)))
514                 return NULL;
515
516         p = n;
517         STRV_FOREACH(a, c->argv) {
518
519                 if (!first)
520                         *(p++) = ' ';
521                 else
522                         first = false;
523
524                 if (strpbrk(*a, WHITESPACE)) {
525                         *(p++) = '\'';
526                         p = stpcpy(p, *a);
527                         *(p++) = '\'';
528                 } else
529                         p = stpcpy(p, *a);
530
531         }
532
533         *p = 0;
534
535         /* FIXME: this doesn't really handle arguments that have
536          * spaces and ticks in them */
537
538         return n;
539 }
540
541 void exec_command_dump(ExecCommand *c, FILE *f, const char *prefix) {
542         char *cmd;
543
544         assert(c);
545         assert(f);
546
547         if (!prefix)
548                 prefix = "";
549
550         cmd = exec_command_line(c);
551
552         fprintf(f,
553                 "%sCommand Line: %s\n",
554                 prefix, cmd ? cmd : strerror(ENOMEM));
555
556         free(cmd);
557 }
558
559 void exec_command_dump_list(ExecCommand *c, FILE *f, const char *prefix) {
560         assert(f);
561
562         if (!prefix)
563                 prefix = "";
564
565         LIST_FOREACH(command, c, c)
566                 exec_command_dump(c, f, prefix);
567 }