/***
|
This file is part of libdaemon.
|
|
Copyright 2003-2008 Lennart Poettering
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
of this software and associated documentation files (the "Software"), to deal
|
in the Software without restriction, including without limitation the rights
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
copies of the Software, and to permit persons to whom the Software is
|
furnished to do so, subject to the following conditions:
|
|
The above copyright notice and this permission notice shall be included in
|
all copies or substantial portions of the Software.
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
SOFTWARE.
|
|
***/
|
|
#ifdef HAVE_CONFIG_H
|
#include <config.h>
|
#endif
|
|
#include <sys/types.h>
|
#include <unistd.h>
|
#include <string.h>
|
#include <errno.h>
|
#include <sys/stat.h>
|
#include <stdlib.h>
|
#include <signal.h>
|
#include <sys/wait.h>
|
#include <limits.h>
|
#include <fcntl.h>
|
#include <stdio.h>
|
#include <stdarg.h>
|
#include <assert.h>
|
|
#include "dlog.h"
|
#include "dsignal.h"
|
#include "dfork.h"
|
#include "dexec.h"
|
|
#define MAX_ARGS 64
|
|
int daemon_execv(const char *dir, int *ret, const char *prog, va_list ap) {
|
pid_t pid;
|
int p[2];
|
unsigned n = 0;
|
static char buf[256];
|
int sigfd, r;
|
fd_set fds;
|
int saved_errno;
|
|
assert(daemon_signal_fd() >= 0);
|
|
if (pipe(p) < 0) {
|
daemon_log(LOG_ERR, "pipe() failed: %s", strerror(errno));
|
return -1;
|
}
|
|
if ((pid = fork()) < 0) {
|
daemon_log(LOG_ERR, "fork() failed: %s", strerror(errno));
|
|
saved_errno = errno;
|
close(p[0]);
|
close(p[1]);
|
errno = saved_errno;
|
|
return -1;
|
|
} else if (pid == 0) {
|
char *args[MAX_ARGS];
|
int i;
|
|
if (p[1] != 1)
|
if (dup2(p[1], 1) < 0) {
|
daemon_log(LOG_ERR, "dup2: %s", strerror(errno));
|
goto fail;
|
}
|
|
if (p[1] != 2)
|
if (dup2(p[1], 2) < 0) {
|
daemon_log(LOG_ERR, "dup2: %s", strerror(errno));
|
goto fail;
|
}
|
|
|
if (p[0] > 2)
|
close(p[0]);
|
|
if (p[1] > 2)
|
close(p[1]);
|
|
close(0);
|
|
if (open("/dev/null", O_RDONLY) != 0) {
|
daemon_log(LOG_ERR, "Unable to open /dev/null as STDIN");
|
goto fail;
|
}
|
|
daemon_close_all(-1);
|
daemon_reset_sigs(-1);
|
daemon_unblock_sigs(-1);
|
|
umask(0022); /* Set up a sane umask */
|
|
if (dir && chdir(dir) < 0) {
|
daemon_log(LOG_WARNING, "Failed to change to directory '%s'", dir);
|
chdir("/");
|
}
|
|
for (i = 0; i < MAX_ARGS-1; i++)
|
if (!(args[i] = va_arg(ap, char*)))
|
break;
|
args[i] = NULL;
|
|
execv(prog, args);
|
|
daemon_log(LOG_ERR, "execv(%s) failed: %s", prog, strerror(errno));
|
|
fail:
|
|
_exit(EXIT_FAILURE);
|
}
|
|
close(p[1]);
|
|
FD_ZERO(&fds);
|
FD_SET(p[0], &fds);
|
sigfd = daemon_signal_fd();
|
FD_SET(sigfd, &fds);
|
|
n = 0;
|
|
for (;;) {
|
fd_set qfds = fds;
|
|
if (select(FD_SETSIZE, &qfds, NULL, NULL, NULL) < 0) {
|
|
if (errno == EINTR)
|
continue;
|
|
daemon_log(LOG_ERR, "select() failed: %s", strerror(errno));
|
|
saved_errno = errno;
|
close(p[0]);
|
errno = saved_errno;
|
return -1;
|
}
|
|
if (FD_ISSET(p[0], &qfds)) {
|
char c;
|
|
if (read(p[0], &c, 1) != 1)
|
break;
|
|
buf[n] = c;
|
|
if (c == '\n' || n >= sizeof(buf) - 2) {
|
if (c != '\n') n++;
|
buf[n] = 0;
|
|
if (buf[0])
|
daemon_log(LOG_INFO, "client: %s", buf);
|
|
n = 0;
|
} else
|
n++;
|
}
|
|
if (FD_ISSET(sigfd, &qfds)) {
|
int sig;
|
|
if ((sig = daemon_signal_next()) < 0) {
|
saved_errno = errno;
|
close(p[0]);
|
errno = saved_errno;
|
return -1;
|
}
|
|
if (sig != SIGCHLD) {
|
daemon_log(LOG_WARNING, "Killing child.");
|
kill(pid, SIGTERM);
|
}
|
}
|
}
|
|
if (n > 0) {
|
buf[n] = 0;
|
daemon_log(LOG_WARNING, "client: %s", buf);
|
}
|
|
close(p[0]);
|
|
for (;;) {
|
if (waitpid(pid, &r, 0) < 0) {
|
|
if (errno == EINTR)
|
continue;
|
|
daemon_log(LOG_ERR, "waitpid(): %s", strerror(errno));
|
return -1;
|
} else {
|
if (!WIFEXITED(r)) {
|
errno = ECANCELED;
|
return -1;
|
}
|
|
if (ret)
|
*ret = WEXITSTATUS(r);
|
|
return 0;
|
}
|
}
|
}
|
|
int daemon_exec(const char *dir, int *ret, const char *prog, ...) {
|
va_list ap;
|
int r;
|
|
va_start(ap, prog);
|
r = daemon_execv(dir, ret, prog, ap);
|
va_end(ap);
|
|
return r;
|
}
|