forked from ryd/chaosvpn
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdaemon.c
278 lines (241 loc) · 6.55 KB
/
daemon.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
#include <errno.h>
#include <signal.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#ifndef WIN32
#include <sys/wait.h>
#endif
#include "chaosvpn.h"
static void
fix_fds (void)
{
/* Bad Things Happen if stdin, stdout, and stderr have been closed
(as by the `sh incantation "attraction >&- 2>&-"). When you do
that, the X connection gets allocated to one of these fds, and
then some random library writes to stderr, and random bits get
stuffed down the X pipe, causing "Xlib: sequence lost" errors.
So, we cause the first three file descriptors to be open to
/dev/null if they aren't open to something else already. This
must be done before any other files are opened (or the closing
of that other file will again free up one of the "magic" first
three FDs.)
We do this by opening /dev/null three times, and then closing
those fds, *unless* any of them got allocated as #0, #1, or #2,
in which case we leave them open. Gag.
Really, this crap is technically required of *every* X program,
if you want it to be robust in the face of "2>&-".
*/
int fd0 = open ("/dev/null", O_RDWR);
int fd1 = open ("/dev/null", O_RDWR);
int fd2 = open ("/dev/null", O_RDWR);
if (fd0 > 2) close (fd0);
if (fd1 > 2) close (fd1);
if (fd2 > 2) close (fd2);
}
bool
daemonize(void)
{
#ifndef WIN32
pid_t pid, sid;
int rc;
/* Fork off the parent process */
pid = fork();
if (pid < 0) {
return false;
}
/* If we got a good PID, then we are the parent and can exit */
if (pid > 0) {
exit(EXIT_SUCCESS);
}
/* now in forked child */
/* Fork again, to become a real daemon */
pid = fork();
if (pid < 0) {
exit(1);
} else if (pid > 0) {
exit(EXIT_SUCCESS);
}
/* Change the file mode mask */
umask(0);
/* Create a new SID for the child process */
sid = setsid();
if (sid < 0) {
log_err("daemonize(): setsid() failed: %s", strerror(errno));
return false;
}
/* Change the current working directory */
rc = chdir("/");
if (rc != 0) {
log_warn("daemonize(): chdir(\"/\") failed: %s", strerror(errno));
/* warn, but ignore errors */
}
/* Close out the standard file descriptors */
close(STDIN_FILENO);
close(STDOUT_FILENO),
close(STDERR_FILENO);
/* Replace standard file descriptors with /dev/null */
fix_fds();
#endif
return true;
}
bool
daemon_init(struct daemon_info* di, const char* path, ...)
{
int i;
int numargs;
va_list ap;
di->di_stderr_fd[0] = -1;
di->di_stderr_fd[1] = -1;
di->di_stderr = NULL;
di->di_pid = -1;
di->di_path = strdup(path);
va_start(ap, path);
for (numargs = 0; va_arg(ap, char*) != NULL; ++numargs);
va_end(ap);
/* append one argument: NULL */
++numargs;
di->di_arguments = (char**)malloc(sizeof(char*) * numargs);
if (di->di_arguments == NULL) {
goto bail_out;
}
memset(di->di_arguments, 0, sizeof(char*) * numargs);
di->di_numarguments = numargs;
va_start(ap, path);
for (i = 0; i < numargs; i++) {
di->di_arguments[i] = va_arg(ap, char*);
if (di->di_arguments[i] != NULL) {
di->di_arguments[i] = strdup(di->di_arguments[i]);
if (di->di_arguments[i] == NULL) {
goto bail_out;
}
}
}
va_end(ap);
return true;
bail_out:
if (di->di_path != NULL) {
free(di->di_path);
di->di_path = NULL;
}
if (di->di_arguments != NULL) {
for(i = 0; di->di_arguments[i] != NULL; i++) {
free(di->di_arguments[i]);
}
free(di->di_arguments);
di->di_arguments = NULL;
}
return false;
}
bool
daemon_addparam(struct daemon_info* di, const char* param)
{
int numargs;
char** tmp;
char* dparam;
numargs = di->di_numarguments + 1;
tmp = (char**)realloc(di->di_arguments, sizeof(char*) * numargs);
if (tmp == NULL) return false;
dparam = strdup(param);
if (dparam == NULL) return false;
di->di_arguments = tmp;
++di->di_numarguments;
di->di_arguments[numargs - 2] = dparam;
di->di_arguments[numargs - 1] = NULL;
return true;
}
void
daemon_free(struct daemon_info* di)
{
int i;
free(di->di_path);
for (i = 0; di->di_arguments[i] != NULL; i++) {
free(di->di_arguments[i]);
}
free(di->di_arguments);
di->di_arguments = NULL;
di->di_pid = -1;
if (di->di_stderr_fd[0] != -1)
close(di->di_stderr_fd[0]);
if (di->di_stderr_fd[1] != -1)
close(di->di_stderr_fd[1]);
di->di_stderr_fd[0] = -1;
di->di_stderr_fd[1] = -1;
if (di->di_stderr)
fclose(di->di_stderr);
di->di_stderr = NULL;
}
bool
daemon_start(struct daemon_info* di)
{
#ifndef WIN32
pid_t pid;
if (di->di_stderr_fd[0] != -1)
close(di->di_stderr_fd[0]);
if (di->di_stderr_fd[1] != -1)
close(di->di_stderr_fd[1]);
if (pipe(di->di_stderr_fd) == -1) {
log_err("daemon_start(): creating stderr pipe failed: %s", strerror(errno));
return false;
}
fcntl(di->di_stderr_fd[0], O_NONBLOCK);
fcntl(di->di_stderr_fd[1], O_NONBLOCK);
di->di_stderr = fdopen(di->di_stderr_fd[0], "r");
switch(pid = fork()) {
case 0:
(void)setsid();
dup2(di->di_stderr_fd[1], STDERR_FILENO);
close(di->di_stderr_fd[0]);
close(di->di_stderr_fd[1]);
(void)execvp(di->di_path, di->di_arguments);
exit(EXIT_FAILURE);
case -1:
return false;
default:
close(di->di_stderr_fd[1]);
di->di_stderr_fd[1] = -1;
di->di_pid = pid;
return true;
}
#endif
return true;
}
void
daemon_stop(struct daemon_info* di, const unsigned int sleepdelay)
{
#ifndef WIN32
pid_t pgid;
pgid = di->di_pid; //__getpgid(di->di_pid);
if (pgid == -1) {
return;
}
if (kill(pgid, SIGTERM)) {
if (errno != ESRCH) {
return;
}
}
if (sleepdelay == 0) {
/* no sigkill at all */
return;
}
(void)sleep(sleepdelay);
(void)kill(pgid, SIGKILL);
#endif
return;
}
bool
daemon_sigchld(struct daemon_info* di, unsigned int waitbeforerestart)
{
#ifndef WIN32
int status;
waitpid(di->di_pid, &status, 0);
if (waitbeforerestart != 0) {
(void)sleep(waitbeforerestart);
}
#endif
return daemon_start(di);
}