• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
daemon_execv(const char * dir,int * ret,const char * prog,va_list ap)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 
daemon_exec(const char * dir,int * ret,const char * prog,...)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