Home | History | Annotate | Download | only in libdaemon
      1 /***
      2   This file is part of libdaemon.
      3 
      4   Copyright 2003-2008 Lennart Poettering
      5 
      6   Permission is hereby granted, free of charge, to any person obtaining a copy
      7   of this software and associated documentation files (the "Software"), to deal
      8   in the Software without restriction, including without limitation the rights
      9   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     10   copies of the Software, and to permit persons to whom the Software is
     11   furnished to do so, subject to the following conditions:
     12 
     13   The above copyright notice and this permission notice shall be included in
     14   all copies or substantial portions of the Software.
     15 
     16   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     17   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     18   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     19   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     20   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     21   OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
     22   SOFTWARE.
     23 
     24 ***/
     25 
     26 #ifdef HAVE_CONFIG_H
     27 #include <config.h>
     28 #endif
     29 
     30 #include <sys/types.h>
     31 #include <unistd.h>
     32 #include <string.h>
     33 #include <errno.h>
     34 #include <sys/stat.h>
     35 #include <stdlib.h>
     36 #include <signal.h>
     37 #include <sys/wait.h>
     38 #include <limits.h>
     39 #include <fcntl.h>
     40 #include <stdio.h>
     41 #include <stdarg.h>
     42 #include <assert.h>
     43 
     44 #include "dlog.h"
     45 #include "dsignal.h"
     46 #include "dfork.h"
     47 #include "dexec.h"
     48 
     49 #define MAX_ARGS 64
     50 
     51 int daemon_execv(const char *dir, int *ret, const char *prog, va_list ap) {
     52     pid_t pid;
     53     int p[2];
     54     unsigned n = 0;
     55     static char buf[256];
     56     int sigfd, r;
     57     fd_set fds;
     58     int saved_errno;
     59 
     60     assert(daemon_signal_fd() >= 0);
     61 
     62     if (pipe(p) < 0) {
     63         daemon_log(LOG_ERR, "pipe() failed: %s", strerror(errno));
     64         return -1;
     65     }
     66 
     67     if ((pid = fork()) < 0) {
     68         daemon_log(LOG_ERR, "fork() failed: %s", strerror(errno));
     69 
     70         saved_errno = errno;
     71         close(p[0]);
     72         close(p[1]);
     73         errno = saved_errno;
     74 
     75         return -1;
     76 
     77     } else if (pid == 0) {
     78         char *args[MAX_ARGS];
     79         int i;
     80 
     81         if (p[1] != 1)
     82             if (dup2(p[1], 1) < 0) {
     83                 daemon_log(LOG_ERR, "dup2: %s", strerror(errno));
     84                 goto fail;
     85             }
     86 
     87         if (p[1] != 2)
     88             if (dup2(p[1], 2) < 0) {
     89                 daemon_log(LOG_ERR, "dup2: %s", strerror(errno));
     90                 goto fail;
     91             }
     92 
     93 
     94         if (p[0] > 2)
     95             close(p[0]);
     96 
     97         if (p[1] > 2)
     98             close(p[1]);
     99 
    100         close(0);
    101 
    102         if (open("/dev/null", O_RDONLY) != 0) {
    103             daemon_log(LOG_ERR, "Unable to open /dev/null as STDIN");
    104             goto fail;
    105         }
    106 
    107         daemon_close_all(-1);
    108         daemon_reset_sigs(-1);
    109         daemon_unblock_sigs(-1);
    110 
    111         umask(0022); /* Set up a sane umask */
    112 
    113         if (dir && chdir(dir) < 0) {
    114             daemon_log(LOG_WARNING, "Failed to change to directory '%s'", dir);
    115             chdir("/");
    116         }
    117 
    118         for (i = 0; i < MAX_ARGS-1; i++)
    119             if (!(args[i] = va_arg(ap, char*)))
    120                 break;
    121         args[i] = NULL;
    122 
    123         execv(prog, args);
    124 
    125         daemon_log(LOG_ERR, "execv(%s) failed: %s", prog, strerror(errno));
    126 
    127     fail:
    128 
    129         _exit(EXIT_FAILURE);
    130     }
    131 
    132     close(p[1]);
    133 
    134     FD_ZERO(&fds);
    135     FD_SET(p[0], &fds);
    136     sigfd = daemon_signal_fd();
    137     FD_SET(sigfd, &fds);
    138 
    139     n = 0;
    140 
    141     for (;;) {
    142         fd_set qfds = fds;
    143 
    144         if (select(FD_SETSIZE, &qfds, NULL, NULL, NULL) < 0) {
    145 
    146             if (errno == EINTR)
    147                 continue;
    148 
    149             daemon_log(LOG_ERR, "select() failed: %s", strerror(errno));
    150 
    151             saved_errno = errno;
    152             close(p[0]);
    153             errno = saved_errno;
    154             return -1;
    155         }
    156 
    157         if (FD_ISSET(p[0], &qfds)) {
    158             char c;
    159 
    160             if (read(p[0], &c, 1) != 1)
    161                 break;
    162 
    163             buf[n] = c;
    164 
    165             if (c == '\n' || n >= sizeof(buf) - 2) {
    166                 if (c != '\n') n++;
    167                 buf[n] = 0;
    168 
    169                 if (buf[0])
    170                     daemon_log(LOG_INFO, "client: %s", buf);
    171 
    172                 n = 0;
    173             } else
    174                 n++;
    175         }
    176 
    177         if (FD_ISSET(sigfd, &qfds)) {
    178             int sig;
    179 
    180             if ((sig = daemon_signal_next()) < 0) {
    181                 saved_errno = errno;
    182                 close(p[0]);
    183                 errno = saved_errno;
    184                 return -1;
    185             }
    186 
    187             if (sig != SIGCHLD) {
    188                 daemon_log(LOG_WARNING, "Killing child.");
    189                 kill(pid, SIGTERM);
    190             }
    191         }
    192     }
    193 
    194     if (n > 0) {
    195         buf[n] = 0;
    196         daemon_log(LOG_WARNING, "client: %s", buf);
    197     }
    198 
    199     close(p[0]);
    200 
    201     for (;;) {
    202         if (waitpid(pid, &r, 0) < 0) {
    203 
    204             if (errno == EINTR)
    205                 continue;
    206 
    207             daemon_log(LOG_ERR, "waitpid(): %s", strerror(errno));
    208             return -1;
    209         } else {
    210             if (!WIFEXITED(r)) {
    211                 errno = ECANCELED;
    212                 return -1;
    213             }
    214 
    215             if (ret)
    216                 *ret = WEXITSTATUS(r);
    217 
    218             return 0;
    219         }
    220     }
    221 }
    222 
    223 int daemon_exec(const char *dir, int *ret, const char *prog, ...) {
    224     va_list ap;
    225     int r;
    226 
    227     va_start(ap, prog);
    228     r = daemon_execv(dir, ret, prog, ap);
    229     va_end(ap);
    230 
    231     return r;
    232 }
    233