Blame libdaemon/dexec.c

Packit ab56a9
/***
Packit ab56a9
  This file is part of libdaemon.
Packit ab56a9
Packit ab56a9
  Copyright 2003-2008 Lennart Poettering
Packit ab56a9
Packit ab56a9
  libdaemon is free software; you can redistribute it and/or modify
Packit ab56a9
  it under the terms of the GNU Lesser General Public License as
Packit ab56a9
  published by the Free Software Foundation, either version 2.1 of the
Packit ab56a9
  License, or (at your option) any later version.
Packit ab56a9
Packit ab56a9
  libdaemon is distributed in the hope that it will be useful, but
Packit ab56a9
  WITHOUT ANY WARRANTY; without even the implied warranty of
Packit ab56a9
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Packit ab56a9
  Lesser General Public License for more details.
Packit ab56a9
Packit ab56a9
  You should have received a copy of the GNU Lesser General Public
Packit ab56a9
  License along with libdaemon. If not, see
Packit ab56a9
  <http://www.gnu.org/licenses/>.
Packit ab56a9
***/
Packit ab56a9
Packit ab56a9
#ifdef HAVE_CONFIG_H
Packit ab56a9
#include <config.h>
Packit ab56a9
#endif
Packit ab56a9
Packit ab56a9
#include <sys/types.h>
Packit ab56a9
#include <unistd.h>
Packit ab56a9
#include <string.h>
Packit ab56a9
#include <errno.h>
Packit ab56a9
#include <sys/stat.h>
Packit ab56a9
#include <stdlib.h>
Packit ab56a9
#include <signal.h>
Packit ab56a9
#include <sys/wait.h>
Packit ab56a9
#include <limits.h>
Packit ab56a9
#include <fcntl.h>
Packit ab56a9
#include <stdio.h>
Packit ab56a9
#include <stdarg.h>
Packit ab56a9
#include <assert.h>
Packit ab56a9
Packit ab56a9
#include "dlog.h"
Packit ab56a9
#include "dsignal.h"
Packit ab56a9
#include "dfork.h"
Packit ab56a9
#include "dexec.h"
Packit ab56a9
Packit ab56a9
#define MAX_ARGS 64
Packit ab56a9
Packit ab56a9
int daemon_execv(const char *dir, int *ret, const char *prog, va_list ap) {
Packit ab56a9
    pid_t pid;
Packit ab56a9
    int p[2];
Packit ab56a9
    unsigned n = 0;
Packit ab56a9
    static char buf[256];
Packit ab56a9
    int sigfd, r;
Packit ab56a9
    fd_set fds;
Packit ab56a9
    int saved_errno;
Packit ab56a9
Packit ab56a9
    assert(daemon_signal_fd() >= 0);
Packit ab56a9
Packit ab56a9
    if (pipe(p) < 0) {
Packit ab56a9
        daemon_log(LOG_ERR, "pipe() failed: %s", strerror(errno));
Packit ab56a9
        return -1;
Packit ab56a9
    }
Packit ab56a9
Packit ab56a9
    if ((pid = fork()) < 0) {
Packit ab56a9
        daemon_log(LOG_ERR, "fork() failed: %s", strerror(errno));
Packit ab56a9
Packit ab56a9
        saved_errno = errno;
Packit ab56a9
        close(p[0]);
Packit ab56a9
        close(p[1]);
Packit ab56a9
        errno = saved_errno;
Packit ab56a9
Packit ab56a9
        return -1;
Packit ab56a9
Packit ab56a9
    } else if (pid == 0) {
Packit ab56a9
        char *args[MAX_ARGS];
Packit ab56a9
        int i;
Packit ab56a9
Packit ab56a9
        if (p[1] != 1)
Packit ab56a9
            if (dup2(p[1], 1) < 0) {
Packit ab56a9
                daemon_log(LOG_ERR, "dup2: %s", strerror(errno));
Packit ab56a9
                goto fail;
Packit ab56a9
            }
Packit ab56a9
Packit ab56a9
        if (p[1] != 2)
Packit ab56a9
            if (dup2(p[1], 2) < 0) {
Packit ab56a9
                daemon_log(LOG_ERR, "dup2: %s", strerror(errno));
Packit ab56a9
                goto fail;
Packit ab56a9
            }
Packit ab56a9
Packit ab56a9
Packit ab56a9
        if (p[0] > 2)
Packit ab56a9
            close(p[0]);
Packit ab56a9
Packit ab56a9
        if (p[1] > 2)
Packit ab56a9
            close(p[1]);
Packit ab56a9
Packit ab56a9
        close(0);
Packit ab56a9
Packit ab56a9
        if (open("/dev/null", O_RDONLY) != 0) {
Packit ab56a9
            daemon_log(LOG_ERR, "Unable to open /dev/null as STDIN");
Packit ab56a9
            goto fail;
Packit ab56a9
        }
Packit ab56a9
Packit ab56a9
        daemon_close_all(-1);
Packit ab56a9
        daemon_reset_sigs(-1);
Packit ab56a9
        daemon_unblock_sigs(-1);
Packit ab56a9
Packit ab56a9
        umask(0022); /* Set up a sane umask */
Packit ab56a9
Packit ab56a9
        if (dir && chdir(dir) < 0) {
Packit ab56a9
            daemon_log(LOG_WARNING, "Failed to change to directory '%s'", dir);
Packit ab56a9
            chdir("/");
Packit ab56a9
        }
Packit ab56a9
Packit ab56a9
        for (i = 0; i < MAX_ARGS-1; i++)
Packit ab56a9
            if (!(args[i] = va_arg(ap, char*)))
Packit ab56a9
                break;
Packit ab56a9
        args[i] = NULL;
Packit ab56a9
Packit ab56a9
        execv(prog, args);
Packit ab56a9
Packit ab56a9
        daemon_log(LOG_ERR, "execv(%s) failed: %s", prog, strerror(errno));
Packit ab56a9
Packit ab56a9
    fail:
Packit ab56a9
Packit ab56a9
        _exit(EXIT_FAILURE);
Packit ab56a9
    }
Packit ab56a9
Packit ab56a9
    close(p[1]);
Packit ab56a9
Packit ab56a9
    FD_ZERO(&fds);
Packit ab56a9
    FD_SET(p[0], &fds);
Packit ab56a9
    sigfd = daemon_signal_fd();
Packit ab56a9
    FD_SET(sigfd, &fds);
Packit ab56a9
Packit ab56a9
    n = 0;
Packit ab56a9
Packit ab56a9
    for (;;) {
Packit ab56a9
        fd_set qfds = fds;
Packit ab56a9
Packit ab56a9
        if (select(FD_SETSIZE, &qfds, NULL, NULL, NULL) < 0) {
Packit ab56a9
Packit ab56a9
            if (errno == EINTR)
Packit ab56a9
                continue;
Packit ab56a9
Packit ab56a9
            daemon_log(LOG_ERR, "select() failed: %s", strerror(errno));
Packit ab56a9
Packit ab56a9
            saved_errno = errno;
Packit ab56a9
            close(p[0]);
Packit ab56a9
            errno = saved_errno;
Packit ab56a9
            return -1;
Packit ab56a9
        }
Packit ab56a9
Packit ab56a9
        if (FD_ISSET(p[0], &qfds)) {
Packit ab56a9
            char c;
Packit ab56a9
Packit ab56a9
            if (read(p[0], &c, 1) != 1)
Packit ab56a9
                break;
Packit ab56a9
Packit ab56a9
            buf[n] = c;
Packit ab56a9
Packit ab56a9
            if (c == '\n' || n >= sizeof(buf) - 2) {
Packit ab56a9
                if (c != '\n') n++;
Packit ab56a9
                buf[n] = 0;
Packit ab56a9
Packit ab56a9
                if (buf[0])
Packit ab56a9
                    daemon_log(LOG_INFO, "client: %s", buf);
Packit ab56a9
Packit ab56a9
                n = 0;
Packit ab56a9
            } else
Packit ab56a9
                n++;
Packit ab56a9
        }
Packit ab56a9
Packit ab56a9
        if (FD_ISSET(sigfd, &qfds)) {
Packit ab56a9
            int sig;
Packit ab56a9
Packit ab56a9
            if ((sig = daemon_signal_next()) < 0) {
Packit ab56a9
                saved_errno = errno;
Packit ab56a9
                close(p[0]);
Packit ab56a9
                errno = saved_errno;
Packit ab56a9
                return -1;
Packit ab56a9
            }
Packit ab56a9
Packit ab56a9
            if (sig != SIGCHLD) {
Packit ab56a9
                daemon_log(LOG_WARNING, "Killing child.");
Packit ab56a9
                kill(pid, SIGTERM);
Packit ab56a9
            }
Packit ab56a9
        }
Packit ab56a9
    }
Packit ab56a9
Packit ab56a9
    if (n > 0) {
Packit ab56a9
        buf[n] = 0;
Packit ab56a9
        daemon_log(LOG_WARNING, "client: %s", buf);
Packit ab56a9
    }
Packit ab56a9
Packit ab56a9
    close(p[0]);
Packit ab56a9
Packit ab56a9
    for (;;) {
Packit ab56a9
        if (waitpid(pid, &r, 0) < 0) {
Packit ab56a9
Packit ab56a9
            if (errno == EINTR)
Packit ab56a9
                continue;
Packit ab56a9
Packit ab56a9
            daemon_log(LOG_ERR, "waitpid(): %s", strerror(errno));
Packit ab56a9
            return -1;
Packit ab56a9
        } else {
Packit ab56a9
            if (!WIFEXITED(r)) {
Packit ab56a9
                errno = ECANCELED;
Packit ab56a9
                return -1;
Packit ab56a9
            }
Packit ab56a9
Packit ab56a9
            if (ret)
Packit ab56a9
                *ret = WEXITSTATUS(r);
Packit ab56a9
Packit ab56a9
            return 0;
Packit ab56a9
        }
Packit ab56a9
    }
Packit ab56a9
}
Packit ab56a9
Packit ab56a9
int daemon_exec(const char *dir, int *ret, const char *prog, ...) {
Packit ab56a9
    va_list ap;
Packit ab56a9
    int r;
Packit ab56a9
Packit ab56a9
    va_start(ap, prog);
Packit ab56a9
    r = daemon_execv(dir, ret, prog, ap);
Packit ab56a9
    va_end(ap);
Packit ab56a9
Packit ab56a9
    return r;
Packit ab56a9
}