0
|
1 /* Distributed Checksum Clearinghouse |
|
2 * |
|
3 * continually restart a daemon |
|
4 * |
|
5 * Copyright (c) 2008 by Rhyolite Software, LLC |
|
6 * |
|
7 * This agreement is not applicable to any entity which sells anti-spam |
|
8 * solutions to others or provides an anti-spam solution as part of a |
|
9 * security solution sold to other entities, or to a private network |
|
10 * which employs the DCC or uses data provided by operation of the DCC |
|
11 * but does not provide corresponding data to other users. |
|
12 * |
|
13 * Permission to use, copy, modify, and distribute this software without |
|
14 * changes for any purpose with or without fee is hereby granted, provided |
|
15 * that the above copyright notice and this permission notice appear in all |
|
16 * copies and any distributed versions or copies are either unchanged |
|
17 * or not called anything similar to "DCC" or "Distributed Checksum |
|
18 * Clearinghouse". |
|
19 * |
|
20 * Parties not eligible to receive a license under this agreement can |
|
21 * obtain a commercial license to use DCC by contacting Rhyolite Software |
|
22 * at sales@rhyolite.com. |
|
23 * |
|
24 * A commercial license would be for Distributed Checksum and Reputation |
|
25 * Clearinghouse software. That software includes additional features. This |
|
26 * free license for Distributed ChecksumClearinghouse Software does not in any |
|
27 * way grant permision to use Distributed Checksum and Reputation Clearinghouse |
|
28 * software |
|
29 * |
|
30 * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL |
|
31 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES |
|
32 * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC |
|
33 * BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES |
|
34 * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, |
|
35 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, |
|
36 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS |
|
37 * SOFTWARE. |
|
38 * |
|
39 * Rhyolite Software DCC 1.3.103-1.16 $Revision$ |
|
40 */ |
|
41 |
|
42 #include <sys/types.h> |
|
43 #include <sys/wait.h> |
|
44 #include <signal.h> |
|
45 #include <pwd.h> |
|
46 #include "dcc_clnt.h" |
|
47 |
|
48 |
|
49 static pid_t start_pid = -1; |
|
50 |
|
51 static void |
|
52 restart_sigterm(int sig) |
|
53 { |
|
54 if (start_pid > 0 |
|
55 && 0 <= kill(start_pid, sig)) |
|
56 return; |
|
57 exit(-1); |
|
58 } |
|
59 |
|
60 |
|
61 |
|
62 #define MIN_RESTART_DELAY 5 |
|
63 #define MAX_RESTART_DELAY (5*60) |
|
64 |
|
65 /* stay alive despite core dumps |
|
66 * Restart immediately, but not more often than every MINRESTART_DELAY |
|
67 * seconds. If the restarts are happening at the maximum rate, |
|
68 * halve the rate. */ |
|
69 void |
|
70 dcc_daemon_restart(const char *rundir, void(reconn)(void)) |
|
71 { |
|
72 #if defined(WIFEXITED) && defined(WTERMSIG) && defined(WIFSIGNALED) |
|
73 pid_t pid; |
|
74 time_t next_restart, restart_delay; |
|
75 int status; |
|
76 const char *etype; |
|
77 DCC_PATH pidpath; |
|
78 |
|
79 signal(SIGHUP, restart_sigterm); |
|
80 signal(SIGTERM, restart_sigterm); |
|
81 signal(SIGINT, restart_sigterm); |
|
82 |
|
83 restart_delay = MIN_RESTART_DELAY; |
|
84 next_restart = 0; |
|
85 for (;;) { |
|
86 start_pid = fork(); |
|
87 if (!start_pid) { |
|
88 #ifdef HAVE_SETPGID |
|
89 if (0 > setpgid(0, 0)) |
|
90 dcc_error_msg("setpgid(): %s", ERROR_STR()); |
|
91 #endif |
|
92 /* reconnect sockets or whatever except first time */ |
|
93 if (next_restart != 0 |
|
94 && reconn) |
|
95 reconn(); |
|
96 return; |
|
97 } |
|
98 |
|
99 if (start_pid < 0) |
|
100 dcc_logbad(EX_OSERR, "(re)start fork(): %s", |
|
101 ERROR_STR()); |
|
102 |
|
103 next_restart = time(0)+restart_delay; |
|
104 pid = waitpid(start_pid, &status, 0); |
|
105 if (pid < 0) { |
|
106 if (errno != EINTR) |
|
107 dcc_logbad(EX_OSERR, "restart waitpid(): %s", |
|
108 ERROR_STR()); |
|
109 exit(0); |
|
110 } |
|
111 |
|
112 start_pid = -1; |
|
113 if (WIFEXITED(status)) { |
|
114 status = WEXITSTATUS(status); |
|
115 etype = "exit "; |
|
116 if (status != EX_DCC_RESTART) { |
|
117 if (dcc_clnt_debug) |
|
118 dcc_error_msg("do not restart after" |
|
119 " exit(%d)", |
|
120 status); |
|
121 exit(status); |
|
122 } |
|
123 |
|
124 } else if (WIFSIGNALED(status)) { |
|
125 status = WTERMSIG(status); |
|
126 etype = "signal #"; |
|
127 if (status != SIGILL |
|
128 && status != SIGSEGV |
|
129 #ifdef SIGBUS |
|
130 && status != SIGBUS |
|
131 #endif |
|
132 #ifdef SIGXCPU |
|
133 && status != SIGXCPU |
|
134 #endif |
|
135 #ifdef SIGFPE |
|
136 && status != SIGFPE |
|
137 #endif |
|
138 #ifdef SIGSYS |
|
139 && status != SIGSYS |
|
140 #endif |
|
141 #ifdef SIGTRAP |
|
142 && status != SIGTRAP |
|
143 #endif |
|
144 && status != SIGQUIT |
|
145 && status != SIGABRT) { |
|
146 if (dcc_clnt_debug) |
|
147 dcc_error_msg("do not restart after" |
|
148 " signal %d", |
|
149 status); |
|
150 exit(0); |
|
151 } |
|
152 |
|
153 } else { |
|
154 dcc_logbad(EX_OSERR, "unknown exit status %#x", status); |
|
155 } |
|
156 |
|
157 next_restart -= time(0); |
|
158 if (next_restart > 0 && next_restart <= MAX_RESTART_DELAY) { |
|
159 restart_delay *= 2; |
|
160 if (restart_delay > MAX_RESTART_DELAY) |
|
161 restart_delay = MAX_RESTART_DELAY; |
|
162 |
|
163 /* while we wait, become the designated target */ |
|
164 dcc_error_msg("delay %d seconds to restart after %s%d", |
|
165 (int)next_restart, etype, status); |
|
166 dcc_pidfile(pidpath, rundir); |
|
167 sleep(next_restart); |
|
168 unlink(pidpath); |
|
169 |
|
170 } else { |
|
171 restart_delay -= next_restart-1; |
|
172 if (restart_delay < MIN_RESTART_DELAY) |
|
173 restart_delay = MIN_RESTART_DELAY; |
|
174 } |
|
175 dcc_error_msg("restart after %s%d", etype, status); |
|
176 } |
|
177 #endif /* not POSIX */ |
|
178 } |
|
179 |
|
180 |
|
181 |
|
182 /* change to the UID and GID of a daemon */ |
|
183 void |
|
184 dcc_daemon_su(const char *user) |
|
185 { |
|
186 struct passwd *pw; |
|
187 |
|
188 pw = getpwnam(user); |
|
189 if (!pw) { |
|
190 dcc_error_msg("getpwnam(%s): %s", user, ERROR_STR()); |
|
191 return; |
|
192 } |
|
193 |
|
194 if (0 > setgid(pw->pw_gid)) |
|
195 dcc_error_msg("setgid(%d %s): %s", |
|
196 (int)pw->pw_gid, user, ERROR_STR()); |
|
197 if (0 > setuid(pw->pw_uid)) |
|
198 dcc_error_msg("setuid(%d %s): %s", |
|
199 (int)pw->pw_uid, user, ERROR_STR()); |
|
200 } |
|
201 |
|
202 |
|
203 |
|
204 void |
|
205 dcc_pidfile(DCC_PATH pidpath, const char *rundir) |
|
206 { |
|
207 FILE *f; |
|
208 |
|
209 snprintf(pidpath, sizeof(DCC_PATH), "%s/%s.pid", |
|
210 rundir, dcc_progname); |
|
211 unlink(pidpath); |
|
212 f = fopen(pidpath, "w"); |
|
213 if (!f) { |
|
214 dcc_error_msg("fopen(%s): %s", pidpath, ERROR_STR()); |
|
215 } else { |
|
216 #ifdef linux |
|
217 /* Linux threads are broken. Signals given the |
|
218 * original process are delivered to only the |
|
219 * thread that happens to have that PID. The |
|
220 * sendmail libmilter thread that needs to hear |
|
221 * SIGINT and other signals is known only to the milter. |
|
222 * Unless you put its PID into the file, it will not hear |
|
223 * the signals. That breaks scripts that need to stop dccm. |
|
224 * However, signaling the process group works. */ |
|
225 fprintf(f, "-%d\n", (u_int)getpgrp()); |
|
226 #else |
|
227 fprintf(f, "%d\n", (u_int)getpid()); |
|
228 #endif |
|
229 fclose(f); |
|
230 } |
|
231 } |