Code sync
[external/libdaemon.git] / libdaemon / dexec.c
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
46 int 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
218 int 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 }