1/*** 2 This file is part of libdaemon. 3 4 Copyright 2003-2008 Lennart Poettering 5 6 libdaemon is free software; you can redistribute it and/or modify 7 it under the terms of the GNU Lesser General Public License as 8 published by the Free Software Foundation, either version 2.1 of the 9 License, or (at your option) any later version. 10 11 libdaemon is distributed in the hope that it will be useful, but 12 WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 Lesser General Public License for more details. 15 16 You should have received a copy of the GNU Lesser General Public 17 License along with libdaemon. If not, see 18 <http://www.gnu.org/licenses/>. 19***/ 20 21#ifdef HAVE_CONFIG_H 22#include <config.h> 23#endif 24 25#include <sys/types.h> 26#include <unistd.h> 27#include <string.h> 28#include <errno.h> 29#include <sys/stat.h> 30#include <stdlib.h> 31#include <signal.h> 32#include <sys/wait.h> 33#include <limits.h> 34#include <fcntl.h> 35#include <stdio.h> 36#include <stdarg.h> 37#include <assert.h> 38 39#include "dlog.h" 40#include "dsignal.h" 41#include "dfork.h" 42#include "dexec.h" 43 44#define MAX_ARGS 64 45 46int daemon_execv(const char *dir, int *ret, const char *prog, va_list ap) { 47 pid_t pid; 48 int p[2]; 49 unsigned n = 0; 50 static char buf[256]; 51 int sigfd, r; 52 fd_set fds; 53 int saved_errno; 54 55 assert(daemon_signal_fd() >= 0); 56 57 if (pipe(p) < 0) { 58 daemon_log(LOG_ERR, "pipe() failed: %s", strerror(errno)); 59 return -1; 60 } 61 62 if ((pid = fork()) < 0) { 63 daemon_log(LOG_ERR, "fork() failed: %s", strerror(errno)); 64 65 saved_errno = errno; 66 close(p[0]); 67 close(p[1]); 68 errno = saved_errno; 69 70 return -1; 71 72 } else if (pid == 0) { 73 char *args[MAX_ARGS]; 74 int i; 75 76 if (p[1] != 1) 77 if (dup2(p[1], 1) < 0) { 78 daemon_log(LOG_ERR, "dup2: %s", strerror(errno)); 79 goto fail; 80 } 81 82 if (p[1] != 2) 83 if (dup2(p[1], 2) < 0) { 84 daemon_log(LOG_ERR, "dup2: %s", strerror(errno)); 85 goto fail; 86 } 87 88 89 if (p[0] > 2) 90 close(p[0]); 91 92 if (p[1] > 2) 93 close(p[1]); 94 95 close(0); 96 97 if (open("/dev/null", O_RDONLY) != 0) { 98 daemon_log(LOG_ERR, "Unable to open /dev/null as STDIN"); 99 goto fail; 100 } 101 102 daemon_close_all(-1); 103 daemon_reset_sigs(-1); 104 daemon_unblock_sigs(-1); 105 106 umask(0022); /* Set up a sane umask */ 107 108 if (dir && chdir(dir) < 0) { 109 daemon_log(LOG_WARNING, "Failed to change to directory '%s'", dir); 110 chdir("/"); 111 } 112 113 for (i = 0; i < MAX_ARGS-1; i++) 114 if (!(args[i] = va_arg(ap, char*))) 115 break; 116 args[i] = NULL; 117 118 execv(prog, args); 119 120 daemon_log(LOG_ERR, "execv(%s) failed: %s", prog, strerror(errno)); 121 122 fail: 123 124 _exit(EXIT_FAILURE); 125 } 126 127 close(p[1]); 128 129 FD_ZERO(&fds); 130 FD_SET(p[0], &fds); 131 sigfd = daemon_signal_fd(); 132 FD_SET(sigfd, &fds); 133 134 n = 0; 135 136 for (;;) { 137 fd_set qfds = fds; 138 139 if (select(FD_SETSIZE, &qfds, NULL, NULL, NULL) < 0) { 140 141 if (errno == EINTR) 142 continue; 143 144 daemon_log(LOG_ERR, "select() failed: %s", strerror(errno)); 145 146 saved_errno = errno; 147 close(p[0]); 148 errno = saved_errno; 149 return -1; 150 } 151 152 if (FD_ISSET(p[0], &qfds)) { 153 char c; 154 155 if (read(p[0], &c, 1) != 1) 156 break; 157 158 buf[n] = c; 159 160 if (c == '\n' || n >= sizeof(buf) - 2) { 161 if (c != '\n') n++; 162 buf[n] = 0; 163 164 if (buf[0]) 165 daemon_log(LOG_INFO, "client: %s", buf); 166 167 n = 0; 168 } else 169 n++; 170 } 171 172 if (FD_ISSET(sigfd, &qfds)) { 173 int sig; 174 175 if ((sig = daemon_signal_next()) < 0) { 176 saved_errno = errno; 177 close(p[0]); 178 errno = saved_errno; 179 return -1; 180 } 181 182 if (sig != SIGCHLD) { 183 daemon_log(LOG_WARNING, "Killing child."); 184 kill(pid, SIGTERM); 185 } 186 } 187 } 188 189 if (n > 0) { 190 buf[n] = 0; 191 daemon_log(LOG_WARNING, "client: %s", buf); 192 } 193 194 close(p[0]); 195 196 for (;;) { 197 if (waitpid(pid, &r, 0) < 0) { 198 199 if (errno == EINTR) 200 continue; 201 202 daemon_log(LOG_ERR, "waitpid(): %s", strerror(errno)); 203 return -1; 204 } else { 205 if (!WIFEXITED(r)) { 206 errno = ECANCELED; 207 return -1; 208 } 209 210 if (ret) 211 *ret = WEXITSTATUS(r); 212 213 return 0; 214 } 215 } 216} 217 218int daemon_exec(const char *dir, int *ret, const char *prog, ...) { 219 va_list ap; 220 int r; 221 222 va_start(ap, prog); 223 r = daemon_execv(dir, ret, prog, ap); 224 va_end(ap); 225 226 return r; 227} 228