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