0
|
1 /* Distributed Checksum Clearinghouse |
|
2 * |
|
3 * server |
|
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.297 $Revision$ |
|
40 */ |
|
41 |
|
42 #include "dccd_defs.h" |
|
43 #include <signal.h> /* for Linux and SunOS */ |
|
44 #include <sys/uio.h> |
|
45 #include <sys/wait.h> |
|
46 #include "dcc_ifaddrs.h" |
|
47 |
|
48 DCC_EMSG dcc_emsg; |
|
49 |
|
50 static const char *homedir; |
|
51 static char *aargs[10]; |
|
52 static char **aarg = &aargs[0]; |
|
53 static DCC_PATH dbclean_def = DCC_LIBEXECDIR"/dbclean"; |
|
54 static char *dbclean = dbclean_def; |
|
55 static pid_t dbclean_pid = -1; |
|
56 static char *addr_str; /* an IP address for dbclean */ |
|
57 static char dbclean_str[] = "dbclean"; |
|
58 #define MAX_DBCLEAN_FLAGS 50 |
|
59 static char dbclean_flags[MAX_DBCLEAN_FLAGS+1] = "-Pq"; |
|
60 static int dbclean_flags_len = LITZ("-Pq"); |
|
61 static const char *dbclean_argv[20] = {dbclean_str, dbclean_flags}; |
|
62 static int dbclean_argc = 2; |
|
63 static char dbclean_args_str[200]; |
|
64 static int dbclean_args_str_len; |
|
65 |
|
66 /* do not try to run dbclean too often */ |
|
67 time_t dbclean_limit_secs = DBCLEAN_LIMIT_SECS; |
|
68 static time_t dbclean_failed; |
|
69 |
|
70 static double wfix_quiet_rate = 1.0; /* max load that allows window fixing */ |
|
71 static double wfix_busy_rate; /* measured active rate */ |
|
72 static double wfix_rate_change = 0.4; /* rate reduction to this allows fix */ |
|
73 static DB_PTR wfix_size; |
|
74 static u_char wfix_size_set = 0; |
|
75 DBCLEAN_WFIX_STATE dbclean_wfix_state = WFIX_DELAY; |
|
76 static double wfix_ops; |
|
77 static time_t wfix_check_start; |
|
78 static time_t dbclean_wfix_secs; /* window fixing timer */ |
|
79 #define WFIX_POST_CLEAN_SECS (6*60*60) /* do not fix it soon after cleaning */ |
|
80 #define WFIX_PRE_CLEAN_SECS (2*60*60) /* or just before cleaning */ |
|
81 #define WFIX_RECHECK_SECS (15*60) /* between checks for window overflow */ |
|
82 #define WFIX_QUIET_SECS (5*60) /* waiting for clients to flee */ |
|
83 #define WFIX_MEASURE_SECS (5*60) /* counting clients */ |
|
84 #define WFIX_MAX_SECS (WFIX_PRE_CLEAN_SECS+WFIX_POST_CLEAN_SECS) |
|
85 |
|
86 const char *need_del_dbclean; |
|
87 time_t del_dbclean_next; |
|
88 time_t dbclean_limit; |
|
89 static time_t clean_fake_secs; /* timer for missing cron job */ |
|
90 static time_t clean_last_secs; |
|
91 |
|
92 u_long dccd_tracemask = DCC_TRACE_ON_DEF_BITS; |
|
93 |
|
94 u_char background = 1; |
|
95 int stopint; /* !=0 if stopping or received signal */ |
|
96 |
|
97 static time_t flods_ck_secs = FLODS_CK_SECS; |
|
98 |
|
99 const char *brand = ""; |
|
100 |
|
101 static char *my_srvr_id_str; |
|
102 DCC_SRVR_ID my_srvr_id; |
|
103 |
|
104 u_char use_ipv6 = 0; |
|
105 u_int16_t def_port; /* default port #, network byte order */ |
|
106 typedef struct port_list { |
|
107 struct port_list *fwd; |
|
108 u_int16_t port; /* network byte order */ |
|
109 } PORT_LIST; |
|
110 static PORT_LIST *ports; |
|
111 SRVR_SOC *srvr_socs; |
|
112 static SRVR_SOC **srvr_socs_end = &srvr_socs; |
|
113 int srvr_rcvbuf = 1024*1024; |
|
114 |
|
115 #ifdef USE_DBCLEAN_F |
|
116 static u_char db_mode = DB_OPEN_MMAP_WRITE; |
|
117 #else |
|
118 static u_char db_mode = 0; |
|
119 #endif |
|
120 |
|
121 int grey_embargo = DEF_GREY_EMBARGO; /* seconds to delay new traffic */ |
|
122 int grey_window = DEF_GREY_WINDOW; /* wait as long as this */ |
|
123 int grey_white = DEF_GREY_WHITE; /* remember this long */ |
|
124 |
|
125 char our_hostname[MAXHOSTNAMELEN]; |
|
126 DCC_SUM host_id_sum; /* advertised with our server-ID */ |
|
127 time_t host_id_next, host_id_last; /* when to advertise */ |
|
128 |
|
129 u_char anon_off; /* turn off anonymous access */ |
|
130 time_t anon_delay_us = DCC_ANON_DELAY_US_DEF; /* delay anonymous clients */ |
|
131 u_int anon_delay_inflate = DCC_ANON_INFLATE_OFF; |
|
132 |
|
133 u_char stop_mode; /* 0=normal 1=reboot 2=with/DB clean */ |
|
134 |
|
135 static int total_ops; |
|
136 static QUEUE *queue_free; |
|
137 static QUEUE *queue_head; |
|
138 |
|
139 /* assume or hope we can handle 500 requests/second */ |
|
140 int queue_max = 500*DCC_MAX_RTT_SECS; /* ultimate bound on queue size */ |
|
141 static int queue_max_cur; /* current upper bound */ |
|
142 static int queue_cur; /* current queue size */ |
|
143 |
|
144 struct timeval wake_time; /* when we awoke from select() */ |
|
145 struct timeval req_recv_time; /* when request arrived */ |
|
146 |
|
147 DCC_TS future; /* timestamp sanity */ |
|
148 |
|
149 |
|
150 static DB_NOKEEP_CKS set_new_nokeep_cks, reset_new_nokeep_cks; |
|
151 |
|
152 DCC_TGTS flod_tholds[DCC_DIM_CKS]; /* flood at or after these thresholds */ |
|
153 |
|
154 static const char *parse_rl_rate(RL_RATE *, float, const char *, const char *); |
|
155 static void add_dbclean_flag(char); |
|
156 static void add_dbclean_arg(const char *); |
|
157 static void check_dbclean(int); |
|
158 static void run_dbclean(const char *, const char *); |
|
159 static void sigterm(int); |
|
160 static void sighup(int); |
|
161 static void stop_children(void); |
|
162 static u_char get_if_changes(u_char); |
|
163 static SRVR_SOC *add_srvr_soc(u_char, int, const void *, u_int16_t); |
|
164 static int open_srvr_socs(int); |
|
165 static void wfix_later(time_t); |
|
166 static void recv_job(void) NRATTRIB; |
|
167 static u_char new_job(SRVR_SOC *); |
|
168 static void NRATTRIB dccd_quit(int, const char *, ...) PATTRIB(2,3); |
|
169 |
|
170 static void |
|
171 usage(const char* barg) |
|
172 { |
|
173 static const char str[] = { |
|
174 "usage: [-64dVbfFQ] -i server-ID [-n brand] [-h homedir]\n" |
|
175 " [-I [host-ID][,user]] [-a [server-addr][,server-port]]" |
|
176 " [-q qsize]\n" |
|
177 " [-G [on,][weak-body,][weak-IP,]embargo],[window],[white]]\n" |
|
178 " [-W [rate][,chg][,dbsize]] [-K [no-]type] [-T tracemode]\n" |
|
179 " [-u anon-delay[,inflate] [-C dbclean]" |
|
180 " [-L ltype,facility.level]\n" |
|
181 " [-R [RL_SUB],[RL_FREE],[RL_ALL_FREE],[RL_BUGS]]" |
|
182 }; |
|
183 static u_char complained; |
|
184 |
|
185 if (!complained) { |
|
186 if (barg) |
|
187 dcc_error_msg("unrecognized \"%s\"\nusage: %s\n..." |
|
188 " continuing", |
|
189 barg, str); |
|
190 else |
|
191 dcc_error_msg("%s\n... continuing", str); |
|
192 complained = 1; |
|
193 } |
|
194 } |
|
195 |
|
196 |
|
197 int NRATTRIB |
|
198 main(int argc, char **argv) |
|
199 { |
|
200 char *p; |
|
201 const char *rest; |
|
202 u_char print_version = 0; |
|
203 DCC_CK_TYPES type; |
|
204 DCC_SOCKU *sup; |
|
205 u_int16_t port; |
|
206 int new_embargo, new_window, new_white; |
|
207 int error, i; |
|
208 const char *cp; |
|
209 double d1, d2; |
|
210 u_long l; |
|
211 |
|
212 dcc_syslog_init(1, argv[0], 0); |
|
213 |
|
214 if (DCC_DIM_CKS != DCC_COMP_DIM_CKS) |
|
215 dcc_logbad(EX_SOFTWARE, |
|
216 "DCC_DIM_CKS != DCC_COMP_DIM_CKS;" |
|
217 " check uses of both"); |
|
218 |
|
219 /* get first bytes of our hostname to name our server-ID */ |
|
220 memset(our_hostname, 0, sizeof(our_hostname)); |
|
221 if (0 > gethostname(our_hostname, sizeof(our_hostname)-1)) |
|
222 dcc_logbad(EX_NOHOST, "gethostname(): %s", ERROR_STR()); |
|
223 our_hostname[sizeof(our_hostname)-1] = '\0'; |
|
224 if (our_hostname[0] == '\0') |
|
225 dcc_logbad(EX_NOHOST, "null hostname from gethostname()"); |
|
226 strncpy((char *)host_id_sum, our_hostname, sizeof(host_id_sum)); |
|
227 |
|
228 parse_rl_rate(&rl_sub_rate, 0.5, "RL_SUB", "400"); |
|
229 parse_rl_rate(&rl_anon_rate, RL_AVG_SECS, "RL_ANON", "50"); |
|
230 parse_rl_rate(&rl_all_anon_rate, 0.5, "RL_ALL_ANON", "600"); |
|
231 parse_rl_rate(&rl_bugs_rate, RL_AVG_SECS, "RL_BUGS", "0.1"); |
|
232 |
|
233 /* this must match DCCD_GETOPTS in cron-dccd.in */ |
|
234 while ((i = getopt(argc, argv, |
|
235 "64dVbfFQi:n:h:a:I:q:G:t:W:K:T:u:C:L:R:")) != -1) { |
|
236 switch (i) { |
|
237 case '6': |
|
238 #ifndef NO_IPV6 |
|
239 use_ipv6 = 2; |
|
240 #endif |
|
241 break; |
|
242 case '4': |
|
243 use_ipv6 = 0; |
|
244 break; |
|
245 |
|
246 case 'd': |
|
247 add_dbclean_flag('d'); |
|
248 ++db_debug; |
|
249 break; |
|
250 |
|
251 case 'V': |
|
252 fprintf(stderr, DCC_VERSION"\n"); |
|
253 print_version = 1; |
|
254 break; |
|
255 |
|
256 case 'b': |
|
257 background = 0; |
|
258 break; |
|
259 |
|
260 case 'f': |
|
261 db_mode &= ~DB_OPEN_MMAP_WRITE; |
|
262 add_dbclean_flag('f'); |
|
263 break; |
|
264 |
|
265 case 'F': |
|
266 db_mode |= DB_OPEN_MMAP_WRITE; |
|
267 add_dbclean_flag('F'); |
|
268 break; |
|
269 |
|
270 case 'Q': |
|
271 query_only = 1; |
|
272 break; |
|
273 |
|
274 case 'i': |
|
275 my_srvr_id_str = optarg; |
|
276 if (!dcc_get_srvr_id(dcc_emsg, &my_srvr_id, |
|
277 my_srvr_id_str, 0, 0, 0)) |
|
278 dcc_logbad(dcc_ex_code, "%s", dcc_emsg); |
|
279 add_dbclean_arg("-i"); |
|
280 add_dbclean_arg(my_srvr_id_str); |
|
281 break; |
|
282 |
|
283 case 'n': |
|
284 /* RFC 2822 says "values between 33 and 126" */ |
|
285 cp = optarg; |
|
286 while (*cp >= 33 && *cp <= 126 && *cp != ':') |
|
287 ++cp; |
|
288 if (cp == optarg |
|
289 || (cp - optarg) > ISZ(DCC_BRAND) |
|
290 || *cp != '\0') { |
|
291 dcc_error_msg("invalid brand name \"-n %s\"", |
|
292 optarg); |
|
293 } else { |
|
294 brand = optarg; |
|
295 } |
|
296 break; |
|
297 |
|
298 case 'h': |
|
299 homedir = optarg; |
|
300 /* tell dbclean "-h ." because we will already |
|
301 * be there and that allows our -h to be relative */ |
|
302 add_dbclean_arg("-h."); |
|
303 break; |
|
304 |
|
305 case 'I': |
|
306 p = strchr(optarg, ','); |
|
307 if (p) { |
|
308 *p++ = '\0'; |
|
309 dcc_daemon_su(p); |
|
310 if (*optarg == '\0') |
|
311 break; |
|
312 } |
|
313 if (*optarg != '\0') |
|
314 strncpy((char *)host_id_sum, optarg, |
|
315 sizeof(host_id_sum)); |
|
316 break; |
|
317 |
|
318 case 'a': |
|
319 /* postpone checking host names until we know -6 */ |
|
320 if (aarg > LAST(aargs)) { |
|
321 dcc_error_msg("too many -a args"); |
|
322 break; |
|
323 } |
|
324 optarg += strspn(optarg, DCC_WHITESPACE); |
|
325 *aarg++ = optarg; |
|
326 break; |
|
327 |
|
328 case 'q': |
|
329 l = strtoul(optarg, &p, 10); |
|
330 if (*p != '\0' || l < 2 || l > 1000) { |
|
331 dcc_error_msg("invalid queue length \"%s\"", |
|
332 optarg); |
|
333 } else { |
|
334 queue_max = l; |
|
335 } |
|
336 break; |
|
337 |
|
338 case 'G': |
|
339 grey_on = 1; |
|
340 dcc_syslog_init(1, argv[0], " grey"); |
|
341 add_dbclean_arg("-Gon"); |
|
342 /* handle leading "on" "weak-body", and "weak-IP" */ |
|
343 rest = optarg; |
|
344 while (*rest) { |
|
345 if (dcc_ck_word_comma(&rest, "weak-body") |
|
346 || dcc_ck_word_comma(&rest, "weak_body") |
|
347 || dcc_ck_word_comma(&rest, "weak")) { |
|
348 grey_weak_body = 1; |
|
349 continue; |
|
350 } |
|
351 if (dcc_ck_word_comma(&rest, "weak-IP") |
|
352 || dcc_ck_word_comma(&rest, "weak_IP")) { |
|
353 grey_weak_ip = 1; |
|
354 continue; |
|
355 } |
|
356 if (!dcc_ck_word_comma(&rest, "on")) |
|
357 break; |
|
358 } |
|
359 new_embargo = dcc_get_secs(rest, &rest, |
|
360 0, MAX_GREY_EMBARGO, |
|
361 grey_embargo); |
|
362 if (new_embargo < 0) { |
|
363 dcc_error_msg("invalid greylist embargo" |
|
364 " \"-G %s\"", optarg); |
|
365 break; |
|
366 } |
|
367 new_window = dcc_get_secs(rest, &rest, |
|
368 new_embargo, MAX_GREY_WINDOW, |
|
369 max(new_embargo,grey_window)); |
|
370 if (new_window < 0) { |
|
371 dcc_error_msg("invalid greylist wait time" |
|
372 " \"-G %s\"", optarg); |
|
373 break; |
|
374 } |
|
375 new_white = dcc_get_secs(rest, &rest, |
|
376 new_window, MAX_GREY_WHITE, |
|
377 max(new_window, grey_white)); |
|
378 if (new_white < 0 || *rest != '\0') { |
|
379 dcc_error_msg("invalid greylist whitelist time" |
|
380 " \"-G %s\"", optarg); |
|
381 break; |
|
382 } |
|
383 grey_embargo = new_embargo; |
|
384 grey_window = new_window; |
|
385 grey_white = new_white; |
|
386 break; |
|
387 |
|
388 case 't': /* obsolete */ |
|
389 break; |
|
390 |
|
391 case 'W': |
|
392 p = optarg; |
|
393 if (*p == '\0') { |
|
394 dcc_error_msg("unrecognized" |
|
395 " \"-W %s\"", optarg); |
|
396 break; |
|
397 } |
|
398 d1 = wfix_quiet_rate; |
|
399 if (*p != '\0' && *p != ',') { |
|
400 d1 = strtod(p, &p); |
|
401 if ((*p != '\0' && *p != ',') |
|
402 || d1 < 0.0 || d1 > 1000*1000.0) { |
|
403 dcc_error_msg("bad quiet rate in" |
|
404 " \"-W %s\"", optarg); |
|
405 break; |
|
406 } |
|
407 } |
|
408 if (*p == ',') |
|
409 ++p; |
|
410 d2 = wfix_rate_change; |
|
411 if (*p != '\0' && *p != ',') { |
|
412 d1 = strtod(p, &p); |
|
413 if ((*p != '\0' && *p != ',') |
|
414 || d1 < 0.0 || d1 > 1000*1000.0) { |
|
415 dcc_error_msg("bad rate change in" |
|
416 " \"-W %s\"", optarg); |
|
417 break; |
|
418 } |
|
419 } |
|
420 if (*p == ',') |
|
421 ++p; |
|
422 l = wfix_size/(1024*1024); |
|
423 if (*p != '\0') { |
|
424 l = strtoul(p, &p, 10); |
|
425 if ((*p != '\0' || l < DB_MIN_MIN_MBYTE |
|
426 || l > MAX_MAX_DB_MBYTE) |
|
427 && l != wfix_size) { |
|
428 dcc_error_msg("bad database size in" |
|
429 " \"-W %s\"", optarg); |
|
430 break; |
|
431 } |
|
432 } |
|
433 wfix_quiet_rate = d1; |
|
434 wfix_rate_change = d2; |
|
435 if (wfix_size/(1024*1024) != l) { |
|
436 wfix_size = ((DB_PTR)l)*(1024*1024); |
|
437 wfix_size_set = 1; |
|
438 } |
|
439 break; |
|
440 |
|
441 case 'K': |
|
442 if (!strcasecmp(optarg, "all")) { |
|
443 reset_new_nokeep_cks = -1; |
|
444 break; |
|
445 } |
|
446 if (!CLITCMP(optarg, "no-")) { |
|
447 optarg += LITZ("no-"); |
|
448 i = 0; |
|
449 } else { |
|
450 i = 1; |
|
451 } |
|
452 type = dcc_str2type_db(optarg, -1); |
|
453 if (type == DCC_CK_INVALID) { |
|
454 dcc_error_msg("bad checksum type in" |
|
455 " \"-K %s\"", optarg); |
|
456 break; |
|
457 } |
|
458 if (i) |
|
459 DB_SET_NOKEEP(reset_new_nokeep_cks, type); |
|
460 else |
|
461 DB_SET_NOKEEP(set_new_nokeep_cks, type); |
|
462 break; |
|
463 |
|
464 case 'T': |
|
465 if (!strcasecmp(optarg, "ADMN")) { |
|
466 dccd_tracemask |= DCC_TRACE_ADMN_BIT; |
|
467 } else if (!strcasecmp(optarg, "ANON")) { |
|
468 dccd_tracemask |= DCC_TRACE_ANON_BIT; |
|
469 } else if (!strcasecmp(optarg, "CLNT")) { |
|
470 dccd_tracemask |= DCC_TRACE_CLNT_BIT; |
|
471 } else if (!strcasecmp(optarg, "RLIM")) { |
|
472 dccd_tracemask |= DCC_TRACE_RLIM_BIT; |
|
473 } else if (!strcasecmp(optarg, "QUERY")) { |
|
474 dccd_tracemask |= DCC_TRACE_QUERY_BIT; |
|
475 } else if (!strcasecmp(optarg, "RIDC")) { |
|
476 dccd_tracemask |= DCC_TRACE_RIDC_BIT; |
|
477 } else if (!strcasecmp(optarg, "FLOOD")) { |
|
478 dccd_tracemask |= DCC_TRACE_FLOD_BIT; |
|
479 } else if (!strcasecmp(optarg, "FLOOD2")) { |
|
480 dccd_tracemask |= DCC_TRACE_FLOD2_BIT; |
|
481 } else if (!strcasecmp(optarg, "IDS")) { |
|
482 dccd_tracemask |= DCC_TRACE_IDS_BIT; |
|
483 } else if (!strcasecmp(optarg, "BL")) { |
|
484 dccd_tracemask |= DCC_TRACE_BL_BIT; |
|
485 } else if (!strcasecmp(optarg, "DB")) { |
|
486 dccd_tracemask |= DCC_TRACE_DB_BIT; |
|
487 } else if (!strcasecmp(optarg, "WLIST")) { |
|
488 dccd_tracemask |= DCC_TRACE_WLIST_BIT; |
|
489 } else { |
|
490 dcc_error_msg("invalid trace mode \"%s\"", |
|
491 optarg); |
|
492 } |
|
493 break; |
|
494 |
|
495 case 'u': |
|
496 i = parse_dccd_delay(dcc_emsg, &anon_delay_us, |
|
497 &anon_delay_inflate, optarg, |
|
498 0, 0); |
|
499 if (!i) { |
|
500 dcc_error_msg("%s", dcc_emsg); |
|
501 } else if (i == 2) { |
|
502 anon_off = 1; |
|
503 } else { |
|
504 anon_off = 0; |
|
505 } |
|
506 break; |
|
507 |
|
508 case 'C': |
|
509 if (*optarg == '\0') { |
|
510 dcc_error_msg("no path to dbclean \"-C %s\"", |
|
511 optarg); |
|
512 break; |
|
513 } |
|
514 /* capture the path to the dbclean program */ |
|
515 dbclean = optarg; |
|
516 /* capture any args following the program */ |
|
517 for (p = strpbrk(optarg, DCC_WHITESPACE); |
|
518 p != 0; |
|
519 p = strpbrk(p, DCC_WHITESPACE)) { |
|
520 *p++ = '\0'; |
|
521 p += strspn(p, DCC_WHITESPACE); |
|
522 if (*p == '\0') |
|
523 break; |
|
524 add_dbclean_arg(p); |
|
525 } |
|
526 break; |
|
527 |
|
528 case 'L': |
|
529 if (dcc_parse_log_opt(optarg)) { |
|
530 add_dbclean_arg("-L"); |
|
531 add_dbclean_arg(optarg); |
|
532 } |
|
533 break; |
|
534 |
|
535 case 'R': |
|
536 rest = parse_rl_rate(&rl_sub_rate, -1.0, |
|
537 "RL_SUB", optarg); |
|
538 rest = parse_rl_rate(&rl_anon_rate, -1.0, |
|
539 "RL_ANON", rest); |
|
540 rest = parse_rl_rate(&rl_all_anon_rate, -1.0, |
|
541 "RL_ALL_ANON", rest); |
|
542 rest = parse_rl_rate(&rl_bugs_rate, -1.0, |
|
543 "RL_BUGS", rest); |
|
544 break; |
|
545 |
|
546 default: |
|
547 usage(optopt2str(optopt)); |
|
548 } |
|
549 } |
|
550 argc -= optind; |
|
551 argv += optind; |
|
552 if (argc != 0) |
|
553 usage(argv[0]); |
|
554 |
|
555 if (my_srvr_id == DCC_ID_INVALID) { |
|
556 if (print_version) |
|
557 exit(EX_OK); |
|
558 dcc_logbad(EX_USAGE, "server-ID not set with -i"); |
|
559 } |
|
560 |
|
561 if (grey_on) { |
|
562 anon_off = 1; |
|
563 dccd_tracemask |= DCC_TRACE_IDS_BIT; |
|
564 } |
|
565 |
|
566 /* parse addresses after we know whether -6 was among the args */ |
|
567 for (aarg = &aargs[0]; aarg <= LAST(aargs) && *aarg; ++aarg) { |
|
568 char hostname[DCC_MAXDOMAINLEN]; |
|
569 |
|
570 addr_str = *aarg; |
|
571 rest = dcc_parse_nm_port(dcc_emsg, *aarg, 0, |
|
572 hostname, sizeof(hostname), |
|
573 &port, 0, 0, 0, 0); |
|
574 if (!rest) { |
|
575 dcc_error_msg("%s", dcc_emsg); |
|
576 continue; |
|
577 } |
|
578 rest += strspn(rest, DCC_WHITESPACE); |
|
579 if (*rest != '\0') |
|
580 dcc_error_msg("unrecognized port number in" |
|
581 "\"-a %s\"", *aarg); |
|
582 if (hostname[0] == '\0') { |
|
583 PORT_LIST *pport = dcc_malloc(sizeof(*pport)); |
|
584 pport->port = port; |
|
585 pport->fwd = ports; |
|
586 ports = pport; |
|
587 continue; |
|
588 } |
|
589 if (!strcmp(hostname, "@")) { |
|
590 ++addr_str; /* "" but not a const */ |
|
591 add_srvr_soc(SRVR_SOC_ADDR, AF_UNSPEC, 0, port); |
|
592 continue; |
|
593 } |
|
594 dcc_host_lock(); |
|
595 if (!dcc_get_host(hostname, use_ipv6 ? 2 : 0, &error)) { |
|
596 dcc_host_unlock(); |
|
597 dcc_error_msg("%s: %s", |
|
598 *aarg, DCC_HSTRERROR(error)); |
|
599 continue; |
|
600 } |
|
601 for (sup = dcc_hostaddrs; sup < dcc_hostaddrs_end; ++sup) { |
|
602 if (sup->sa.sa_family == AF_INET) |
|
603 add_srvr_soc(SRVR_SOC_ADDR, AF_INET, |
|
604 &sup->ipv4.sin_addr, port); |
|
605 else |
|
606 add_srvr_soc(SRVR_SOC_ADDR, AF_INET6, |
|
607 &sup->ipv6.sin6_addr, port); |
|
608 } |
|
609 dcc_host_unlock(); |
|
610 } |
|
611 if (addr_str) { |
|
612 /* tell dbclean about one "-a addr" */ |
|
613 add_dbclean_arg("-a"); |
|
614 add_dbclean_arg(addr_str); |
|
615 } |
|
616 |
|
617 dcc_clnt_unthread_init(); |
|
618 if (!dcc_cdhome(dcc_emsg, homedir, 0)) |
|
619 dcc_logbad(dcc_ex_code, "%s", dcc_emsg); |
|
620 |
|
621 i = check_load_ids(1); |
|
622 if (i < 0) |
|
623 dcc_logbad(dcc_ex_code, "%s", dcc_emsg); |
|
624 else if (!i) |
|
625 dcc_error_msg("%s", dcc_emsg); |
|
626 |
|
627 if (!def_port) |
|
628 def_port = DCC_GREY2PORT(grey_on); |
|
629 if (!srvr_socs && !ports) { |
|
630 ports = dcc_malloc(sizeof(*ports)); |
|
631 ports->fwd = 0; |
|
632 ports->port = def_port; |
|
633 } |
|
634 get_if_changes(db_debug != 0); |
|
635 |
|
636 /* make initial attempt to open the server UDP sockets |
|
637 * This also sets use_ipv6 to 0 or 1 if it is still 2 */ |
|
638 if (open_srvr_socs(45) <= 0) |
|
639 dcc_logbad(EX_OSERR, "failed to open any server sockets"); |
|
640 |
|
641 add_dbclean_flag(use_ipv6 == 0 ? '4' : '6'); |
|
642 |
|
643 if (background) { |
|
644 if (0 > daemon(1, 0)) |
|
645 dcc_logbad(EX_OSERR, "daemon(): %s", ERROR_STR()); |
|
646 } |
|
647 |
|
648 if (!background) |
|
649 signal(SIGHUP, sigterm); /* SIGHUP fatal during debugging */ |
|
650 else |
|
651 signal(SIGHUP, sighup); /* speed configuration check */ |
|
652 signal(SIGTERM, sigterm); |
|
653 signal(SIGINT, sigterm); |
|
654 signal(SIGPIPE, SIG_IGN); |
|
655 #ifdef SIGXFSZ |
|
656 signal(SIGXFSZ, SIG_IGN); |
|
657 #endif |
|
658 |
|
659 atexit(stop_children); |
|
660 |
|
661 gettimeofday(&db_time, 0); |
|
662 wake_time = db_time; |
|
663 |
|
664 flod_mmap_path_set(); |
|
665 |
|
666 /* open the database, and try once to fix it */ |
|
667 if (!dccd_db_open(DB_OPEN_LOCK_NOWAIT)) { |
|
668 dcc_error_msg("%s", dcc_emsg); |
|
669 run_dbclean("SRbad", "database initially broken"); |
|
670 check_dbclean(0); /* stall until dbclean finishes */ |
|
671 if (!dccd_db_open(DB_OPEN_LOCK_NOWAIT)) { |
|
672 dcc_error_msg("%s", dcc_emsg); |
|
673 dcc_logbad(EX_NOINPUT, |
|
674 "could not start database %s", db_nm); |
|
675 } |
|
676 } |
|
677 /* do not start if dbclean is running */ |
|
678 if (!lock_dbclean(dcc_emsg, db_nm)) |
|
679 dcc_logbad(dcc_ex_code, "%s: dbclean already running?", |
|
680 dcc_emsg); |
|
681 unlock_dbclean(); |
|
682 |
|
683 flod_trace_gen = db_time.tv_sec; |
|
684 host_id_next = db_time.tv_sec + DCC_SRVR_ID_SECS_ST; |
|
685 check_blacklist_file(); |
|
686 flods_init(); |
|
687 clients_load(); |
|
688 stats_clear(); |
|
689 if (flod_mmaps != 0 |
|
690 && flod_mmaps->dccd_stats.reset.tv_sec != 0) { |
|
691 memcpy(&dccd_stats, &flod_mmaps->dccd_stats, |
|
692 sizeof(dccd_stats)); |
|
693 } |
|
694 dcc_trace_msg(DCC_VERSION" listening to port %d %s %s", |
|
695 ntohs(srvr_socs->arg_port), |
|
696 dcc_homedir, db_window_size_str); |
|
697 |
|
698 recv_job(); |
|
699 } |
|
700 |
|
701 |
|
702 |
|
703 static SRVR_SOC * /* 0 or new entry */ |
|
704 add_srvr_soc(u_char flags, |
|
705 int family, /* AF_UNSPEC or 0 if addrp==0 */ |
|
706 const void *addrp, /* 0, *in_addr, or *in6_addr */ |
|
707 u_int16_t port) |
|
708 { |
|
709 DCC_SOCKU su; |
|
710 SRVR_SOC *sp; |
|
711 |
|
712 dcc_mk_su(&su, family, addrp, port); |
|
713 for (sp = srvr_socs; sp; sp = sp->fwd) { |
|
714 if (sp->arg_family == family |
|
715 && sp->arg_port == port |
|
716 && !memcmp(&sp->arg_addr, addrp, |
|
717 ((family == AF_INET) |
|
718 ? sizeof(sp->arg_addr.in4) |
|
719 : sizeof(sp->arg_addr.in6)))) |
|
720 return sp; |
|
721 } |
|
722 |
|
723 sp = dcc_malloc(sizeof(*sp)); |
|
724 memset(sp, 0, sizeof(*sp)); |
|
725 sp->flags = flags; |
|
726 sp->udp = -1; |
|
727 sp->listen = -1; |
|
728 sp->su = su; |
|
729 sp->arg_family = family; |
|
730 if (addrp) |
|
731 memcpy(&sp->arg_addr, addrp, |
|
732 ((family == AF_INET) |
|
733 ? sizeof(sp->arg_addr.in4) |
|
734 : sizeof(sp->arg_addr.in6))); |
|
735 sp->arg_port = port; |
|
736 |
|
737 *srvr_socs_end = sp; |
|
738 srvr_socs_end = &sp->fwd; |
|
739 |
|
740 return sp; |
|
741 } |
|
742 |
|
743 |
|
744 |
|
745 static int /* # of sockets opened */ |
|
746 open_srvr_socs(int retry_secs) |
|
747 { |
|
748 static u_char srvr_rcvbuf_set = 0; |
|
749 int *retry_secsp; |
|
750 u_char family; |
|
751 SRVR_SOC *sp; |
|
752 int i; |
|
753 int num_socs = 0; |
|
754 |
|
755 if (stopint) |
|
756 return -1; |
|
757 |
|
758 retry_secsp = retry_secs ? &retry_secs : 0; |
|
759 |
|
760 for (sp = srvr_socs; sp; sp = sp->fwd) { |
|
761 if (sp->udp >= 0) { |
|
762 ++num_socs; |
|
763 continue; |
|
764 } |
|
765 |
|
766 /* resolve default port number if we finally know it */ |
|
767 if (!sp->arg_port) |
|
768 sp->arg_port = def_port; |
|
769 |
|
770 family = sp->arg_family; |
|
771 |
|
772 /* create the UDP socket |
|
773 * If we are using INADDR_ANY |
|
774 * and do not yet know if IPv6 works, just try it */ |
|
775 if (family == AF_UNSPEC |
|
776 && use_ipv6 == 2) { |
|
777 dcc_mk_su(&sp->su, AF_INET6, |
|
778 &sp->arg_addr, sp->arg_port); |
|
779 i = dcc_udp_bind(dcc_emsg, &sp->udp, |
|
780 &sp->su, retry_secsp); |
|
781 if (i == 0) { |
|
782 dcc_error_msg("%s", dcc_emsg); |
|
783 /* still don't know about IPv6 */ |
|
784 continue; |
|
785 } |
|
786 if (i > 0) { |
|
787 /* we finished an INADDR_ANY socket |
|
788 * and learned that IPv6 works */ |
|
789 use_ipv6 = 1; |
|
790 continue; |
|
791 } |
|
792 /* we know or guess that IPv6 does not work */ |
|
793 use_ipv6 = 0; |
|
794 } |
|
795 |
|
796 if (family == AF_UNSPEC) { |
|
797 /* using INADDR_ANY but now know whether IPv6 works */ |
|
798 family = use_ipv6 ? AF_INET6 : AF_INET; |
|
799 } else if (family == AF_INET6 && use_ipv6 == 2) { |
|
800 /* don't know if IPv6 works but have an IPv6 address */ |
|
801 use_ipv6 = 1; |
|
802 } |
|
803 |
|
804 dcc_mk_su(&sp->su, family, &sp->arg_addr, sp->arg_port); |
|
805 if (0 >= dcc_udp_bind(dcc_emsg, &sp->udp, |
|
806 &sp->su, retry_secsp)) { |
|
807 dcc_error_msg("%s", dcc_emsg); |
|
808 continue; |
|
809 } |
|
810 |
|
811 /* set socket receive buffer size as large as possible */ |
|
812 for (;;) { |
|
813 if (!setsockopt(sp->udp, SOL_SOCKET, SO_RCVBUF, |
|
814 &srvr_rcvbuf, sizeof(srvr_rcvbuf))) |
|
815 break; |
|
816 if (srvr_rcvbuf_set || srvr_rcvbuf <= 4096) { |
|
817 dcc_error_msg("setsockopt(%s,SO_RCVBUF=%d): %s", |
|
818 dcc_su2str_err(&sp->su), |
|
819 srvr_rcvbuf, |
|
820 ERROR_STR()); |
|
821 break; |
|
822 } |
|
823 srvr_rcvbuf -= 4096; |
|
824 } |
|
825 srvr_rcvbuf_set = 1; |
|
826 |
|
827 ++num_socs; |
|
828 } |
|
829 |
|
830 /* Finally decide the IPv6 issue if we found no sign of IPv6 */ |
|
831 if (use_ipv6 == 2) |
|
832 use_ipv6 = 0; |
|
833 |
|
834 return num_socs; |
|
835 } |
|
836 |
|
837 |
|
838 |
|
839 /* get ready to bind to all local IP addreses */ |
|
840 static u_char /* 1=added an interface */ |
|
841 add_ifs(u_char not_quiet) |
|
842 { |
|
843 u_char added; |
|
844 SRVR_SOC *sp; |
|
845 PORT_LIST *pport; |
|
846 #ifdef HAVE_GETIFADDRS |
|
847 struct ifaddrs *ifap0, *ifap; |
|
848 int num_ifs; |
|
849 #endif |
|
850 |
|
851 if (!ports) |
|
852 return 0; |
|
853 |
|
854 added = 0; |
|
855 |
|
856 #ifdef HAVE_GETIFADDRS |
|
857 if (0 > getifaddrs(&ifap0)) { |
|
858 dcc_error_msg("getifaddrs(): %s", ERROR_STR()); |
|
859 ifap0 = 0; |
|
860 } |
|
861 |
|
862 num_ifs = 0; |
|
863 for (pport = ports; pport; pport = pport->fwd) { |
|
864 const SRVR_SOC *listener = 0; |
|
865 |
|
866 for (ifap = ifap0; ifap; ifap = ifap->ifa_next) { |
|
867 if (!(ifap->ifa_flags & IFF_UP)) |
|
868 continue; |
|
869 if (!ifap->ifa_addr) |
|
870 continue; |
|
871 switch (ifap->ifa_addr->sa_family) { |
|
872 case AF_INET: |
|
873 ++num_ifs; |
|
874 sp = add_srvr_soc(SRVR_SOC_IF | SRVR_SOC_NEW, |
|
875 ifap->ifa_addr->sa_family, |
|
876 &((struct sockaddr_in *)ifap |
|
877 ->ifa_addr)->sin_addr, |
|
878 pport->port); |
|
879 break; |
|
880 case AF_INET6: |
|
881 if (use_ipv6 == 0) |
|
882 continue; |
|
883 if (use_ipv6 == 2) |
|
884 use_ipv6 = 1; |
|
885 ++num_ifs; |
|
886 sp = add_srvr_soc(SRVR_SOC_IF | SRVR_SOC_NEW, |
|
887 ifap->ifa_addr->sa_family, |
|
888 &((struct sockaddr_in6*)ifap |
|
889 ->ifa_addr)->sin6_addr, |
|
890 pport->port); |
|
891 break; |
|
892 default: |
|
893 continue; |
|
894 } |
|
895 if (sp->flags & SRVR_SOC_NEW) { |
|
896 added = 1; |
|
897 if (not_quiet) |
|
898 dcc_trace_msg("start listening on %s", |
|
899 dcc_su2str_err(&sp->su)); |
|
900 } |
|
901 sp->flags &= ~(SRVR_SOC_MARK | SRVR_SOC_NEW); |
|
902 |
|
903 /* interfaces can have duplicate addresses */ |
|
904 if (listener == sp) |
|
905 continue; |
|
906 if (!listener) { |
|
907 listener = sp; |
|
908 if (!(sp->flags & SRVR_SOC_LISTEN)) { |
|
909 sp->flags |= SRVR_SOC_LISTEN; |
|
910 added = 1; |
|
911 } |
|
912 } else { |
|
913 if (sp->flags & SRVR_SOC_LISTEN) { |
|
914 sp->flags &= ~SRVR_SOC_LISTEN; |
|
915 added = 1; |
|
916 } |
|
917 } |
|
918 } |
|
919 } |
|
920 #ifdef HAVE_FREEIFADDRS |
|
921 /* since this is done only a few times when HAVE_FREEIFADDRS is not |
|
922 * defined, don't worry if we cannot release the list of interfaces */ |
|
923 freeifaddrs(ifap0); |
|
924 #endif |
|
925 |
|
926 if (num_ifs > 0) |
|
927 return added; |
|
928 #endif /* HAVE_GETIFADDRS */ |
|
929 |
|
930 /* if we got no joy from getifaddrs(), use INADDR_ANY */ |
|
931 for (pport = ports; pport; pport = pport->fwd) { |
|
932 sp = add_srvr_soc(SRVR_SOC_IF | SRVR_SOC_LISTEN | SRVR_SOC_NEW, |
|
933 AF_UNSPEC, 0, pport->port); |
|
934 if (sp->flags & SRVR_SOC_NEW) { |
|
935 added = 1; |
|
936 if (not_quiet) |
|
937 dcc_trace_msg("fallback listen on %s", |
|
938 dcc_su2str_err(&sp->su)); |
|
939 } |
|
940 sp->flags &= ~(SRVR_SOC_MARK | SRVR_SOC_NEW); |
|
941 } |
|
942 |
|
943 return added; |
|
944 } |
|
945 |
|
946 |
|
947 |
|
948 /* deal with changes to network interfaces */ |
|
949 static u_char /* 1=something changed */ |
|
950 get_if_changes(u_char not_quiet) |
|
951 { |
|
952 SRVR_SOC *sp, **spp; |
|
953 u_char changed; |
|
954 |
|
955 for (sp = srvr_socs; sp; sp = sp->fwd) { |
|
956 if (sp->flags & SRVR_SOC_IF) |
|
957 sp->flags |= SRVR_SOC_MARK; |
|
958 } |
|
959 |
|
960 changed = add_ifs(not_quiet); |
|
961 |
|
962 spp = &srvr_socs; |
|
963 while ((sp = *spp) != 0) { |
|
964 /* an interface recognized by add_srvr_soc() will have |
|
965 * its SRVR_SOC_MARK cleared */ |
|
966 if (!(sp->flags & SRVR_SOC_MARK)) { |
|
967 spp = &sp->fwd; |
|
968 continue; |
|
969 } |
|
970 |
|
971 /* forget interfaces that have disappeared */ |
|
972 dcc_trace_msg("stop listening on %s", dcc_su2str_err(&sp->su)); |
|
973 changed = 1; |
|
974 if (srvr_socs_end == &sp->fwd) |
|
975 srvr_socs_end = spp; |
|
976 *spp = sp->fwd; |
|
977 if (sp->udp >= 0) |
|
978 close(sp->udp); |
|
979 if (sp->listen >= 0) |
|
980 close(sp->listen); |
|
981 dcc_free(sp); |
|
982 } |
|
983 |
|
984 return changed; |
|
985 } |
|
986 |
|
987 |
|
988 |
|
989 static const char * |
|
990 parse_rl_rate(RL_RATE *rate, float penalty_secs, |
|
991 const char *label, const char *arg) |
|
992 { |
|
993 char *p; |
|
994 int per_sec, hi; |
|
995 |
|
996 if (penalty_secs >= 0.0) |
|
997 rate->penalty_secs = penalty_secs; |
|
998 |
|
999 if (*arg == '\0') |
|
1000 return arg; |
|
1001 |
|
1002 if (*arg == ',') |
|
1003 return ++arg; |
|
1004 |
|
1005 per_sec = strtod(arg, &p) * RL_SCALE; |
|
1006 hi = per_sec*RL_AVG_SECS; |
|
1007 if ((*p != '\0' && *p != ',') |
|
1008 || hi < RL_SCALE || per_sec > RL_MAX_CREDITS) { |
|
1009 dcc_error_msg("invalid %s value in \"%s\"", |
|
1010 label, arg); |
|
1011 return ""; |
|
1012 } |
|
1013 |
|
1014 /* maximum events/second * RL_SCALE */ |
|
1015 rate->per_sec = per_sec; |
|
1016 |
|
1017 /* maximum allowed accumulated credits */ |
|
1018 rate->hi = hi; |
|
1019 |
|
1020 /* minimum credit account balance */ |
|
1021 rate->lo = -per_sec * rate->penalty_secs; |
|
1022 |
|
1023 return (*p == ',') ? p+1 : p; |
|
1024 } |
|
1025 |
|
1026 |
|
1027 |
|
1028 static void |
|
1029 add_dbclean_flag(char flag) |
|
1030 { |
|
1031 if (dbclean_flags_len >= MAX_DBCLEAN_FLAGS) |
|
1032 dcc_logbad(EX_SOFTWARE, "too many flags for dbclean"); |
|
1033 dbclean_flags[dbclean_flags_len++] = flag; |
|
1034 } |
|
1035 |
|
1036 |
|
1037 |
|
1038 static void |
|
1039 add_dbclean_arg(const char *arg) |
|
1040 { |
|
1041 int i; |
|
1042 |
|
1043 if (dbclean_argc >= DIM(dbclean_argv)-2) |
|
1044 dcc_logbad(EX_SOFTWARE, "too many args for dbclean"); |
|
1045 dbclean_argv[dbclean_argc++] = arg; |
|
1046 i = snprintf(dbclean_args_str+dbclean_args_str_len, |
|
1047 sizeof(dbclean_args_str)-dbclean_args_str_len, |
|
1048 " %s", arg); |
|
1049 dbclean_args_str_len += i; |
|
1050 if (dbclean_args_str_len >= ISZ(dbclean_args_str)-2) |
|
1051 dcc_logbad(EX_SOFTWARE, "too many args for dbclean"); |
|
1052 } |
|
1053 |
|
1054 |
|
1055 |
|
1056 /* check effort to repair database */ |
|
1057 static void |
|
1058 check_dbclean(int options) |
|
1059 { |
|
1060 int status; |
|
1061 pid_t pid; |
|
1062 u_char ok; |
|
1063 |
|
1064 if (dbclean_pid < 0) |
|
1065 return; |
|
1066 |
|
1067 pid = waitpid(dbclean_pid, &status, options); |
|
1068 if (pid != dbclean_pid) |
|
1069 return; |
|
1070 |
|
1071 dbclean_pid = -1; |
|
1072 |
|
1073 /* do not try failing dbclean too often */ |
|
1074 #if defined(WIFEXITED) && defined(WEXITSTATUS) && defined(WTERMSIG) && defined(WIFSIGNALED) |
|
1075 ok = 1; |
|
1076 if (WIFSIGNALED(status)) { |
|
1077 dcc_error_msg("dbclean exited with signal %d", |
|
1078 WTERMSIG(status)); |
|
1079 ok = 0; |
|
1080 } else if (WIFEXITED(status)) { |
|
1081 status = WEXITSTATUS(status); |
|
1082 if (status != EX_OK) { |
|
1083 if (status > 100 && status < 130) |
|
1084 dcc_error_msg("dbclean stopped after signal %d", |
|
1085 status-100); |
|
1086 else |
|
1087 dcc_error_msg("dbclean exited with status %d", |
|
1088 status); |
|
1089 ok = 0; |
|
1090 } |
|
1091 } |
|
1092 #else |
|
1093 ok = (status == EX_OK); |
|
1094 #endif |
|
1095 if (ok) { |
|
1096 dbclean_failed = 0; |
|
1097 dbclean_limit_secs = DBCLEAN_LIMIT_SECS; |
|
1098 } else { |
|
1099 dbclean_failed = db_time.tv_sec; |
|
1100 dbclean_limit_secs *= 2; |
|
1101 if (dbclean_limit_secs > DEL_DBCLEAN_SECS) |
|
1102 dbclean_limit_secs = DEL_DBCLEAN_SECS; |
|
1103 } |
|
1104 |
|
1105 /* don't restart dbclean until after it has stopped running |
|
1106 * and cooled for a while */ |
|
1107 dbclean_limit = db_time.tv_sec + dbclean_limit_secs; |
|
1108 } |
|
1109 |
|
1110 |
|
1111 |
|
1112 /* try to repair the database */ |
|
1113 static void |
|
1114 run_dbclean(const char *mode, /* combination of '', R, S, and W */ |
|
1115 const char *reason) |
|
1116 { |
|
1117 int i; |
|
1118 |
|
1119 check_dbclean(0); /* wait until previous ends */ |
|
1120 |
|
1121 wfix_later(WFIX_RECHECK_SECS); |
|
1122 |
|
1123 i = snprintf(&dbclean_flags[dbclean_flags_len], |
|
1124 ISZ(dbclean_flags)-dbclean_flags_len, "%s", |
|
1125 mode); |
|
1126 if (i+dbclean_flags_len >= ISZ(dbclean_flags)) |
|
1127 dcc_logbad(EX_SOFTWARE, "too many flags for dbclean"); |
|
1128 |
|
1129 dbclean_pid = fork(); |
|
1130 if (dbclean_pid < 0) { |
|
1131 dcc_error_msg("dbclean fork(): %s", ERROR_STR()); |
|
1132 } else if (dbclean_pid == 0) { |
|
1133 dcc_trace_msg("%s; starting `%s %s%s`", |
|
1134 reason, dbclean, dbclean_flags, dbclean_args_str); |
|
1135 execv(dbclean, (char **)dbclean_argv); |
|
1136 dcc_error_msg("execv(%s %s%s): %s", |
|
1137 dbclean, dbclean_flags, dbclean_args_str, |
|
1138 ERROR_STR()); |
|
1139 exit(-1); |
|
1140 } |
|
1141 |
|
1142 need_del_dbclean = 0; |
|
1143 dbclean_limit = db_time.tv_sec + dbclean_limit_secs; |
|
1144 } |
|
1145 |
|
1146 |
|
1147 |
|
1148 static void |
|
1149 close_srvr_socs(void) |
|
1150 { |
|
1151 SRVR_SOC *sp; |
|
1152 |
|
1153 for (sp = srvr_socs; sp; sp = sp->fwd) { |
|
1154 if (sp->udp >= 0) { |
|
1155 close(sp->udp); |
|
1156 sp->udp = -1; |
|
1157 } |
|
1158 iflod_listen_close(sp); |
|
1159 } |
|
1160 } |
|
1161 |
|
1162 |
|
1163 |
|
1164 /* close files and otherwise clean up after being forked as a helper */ |
|
1165 void |
|
1166 after_fork(void) |
|
1167 { |
|
1168 IFLOD_INFO *ifp; |
|
1169 OFLOD_INFO *ofp; |
|
1170 |
|
1171 resolve_hosts_pid = -1; |
|
1172 dbclean_pid = -1; |
|
1173 |
|
1174 close_srvr_socs(); |
|
1175 for (ifp = iflods.infos; ifp <= LAST(iflods.infos); ++ifp) { |
|
1176 if (ifp->soc >= 0) |
|
1177 close(ifp->soc); |
|
1178 } |
|
1179 for (ofp = oflods.infos; ofp <= LAST(oflods.infos); ++ofp) { |
|
1180 if (ofp->soc >= 0) |
|
1181 close(ofp->soc); |
|
1182 } |
|
1183 |
|
1184 signal(SIGHUP, SIG_DFL); |
|
1185 signal(SIGTERM, SIG_DFL); |
|
1186 signal(SIGINT, SIG_DFL); |
|
1187 signal(SIGPIPE, SIG_DFL); |
|
1188 #ifdef SIGXFSZ |
|
1189 signal(SIGXFSZ, SIG_DFL); |
|
1190 #endif |
|
1191 } |
|
1192 |
|
1193 |
|
1194 |
|
1195 /* do not worry about cleaning to fix a window overflow for a while */ |
|
1196 static void |
|
1197 wfix_later(time_t delay) |
|
1198 { |
|
1199 dbclean_wfix_state = WFIX_DELAY; |
|
1200 dbclean_wfix_secs = db_time.tv_sec + delay; |
|
1201 } |
|
1202 |
|
1203 |
|
1204 |
|
1205 static double |
|
1206 wfix_measure(u_char force) |
|
1207 { |
|
1208 double secs; |
|
1209 |
|
1210 secs = db_time.tv_sec - wfix_check_start; |
|
1211 if (force || secs <= 0.0 || secs > WFIX_MEASURE_SECS*2 |
|
1212 || total_ops < wfix_ops) { |
|
1213 wfix_check_start = db_time.tv_sec; |
|
1214 dbclean_wfix_secs = db_time.tv_sec + WFIX_MEASURE_SECS; |
|
1215 wfix_ops = total_ops; |
|
1216 return -1.0; |
|
1217 } |
|
1218 return (total_ops - wfix_ops) / secs; |
|
1219 } |
|
1220 |
|
1221 |
|
1222 |
|
1223 static u_char /* 1=need dbclean now */ |
|
1224 wfix(char *reason, u_int reason_len) |
|
1225 { |
|
1226 double rate; |
|
1227 struct timeval sn; |
|
1228 time_t next_clean; |
|
1229 |
|
1230 /* stop everything if dbclean is running */ |
|
1231 if (db_minimum_map || wfix_quiet_rate <= 0.0) { |
|
1232 wfix_later(WFIX_RECHECK_SECS); |
|
1233 return 0; |
|
1234 } |
|
1235 |
|
1236 switch (dbclean_wfix_state) { |
|
1237 case WFIX_DELAY: /* just checking */ |
|
1238 if (!DB_IS_TIME(dbclean_wfix_secs, WFIX_MAX_SECS)) |
|
1239 return 0; |
|
1240 |
|
1241 /* no quick cleaning soon after the database was created, |
|
1242 * cleaned or repaired */ |
|
1243 dcc_ts2timeval(&sn, &db_parms.sn); |
|
1244 if (sn.tv_sec > db_time.tv_sec) |
|
1245 sn.tv_sec = 0; |
|
1246 if (sn.tv_sec < dbclean_failed |
|
1247 && dbclean_failed <= db_time.tv_sec) |
|
1248 sn.tv_sec = dbclean_failed; |
|
1249 if (sn.tv_sec <= db_time.tv_sec) { |
|
1250 dbclean_wfix_secs = sn.tv_sec + WFIX_POST_CLEAN_SECS; |
|
1251 if (!DB_IS_TIME(dbclean_wfix_secs, WFIX_MAX_SECS)) |
|
1252 return 0; |
|
1253 } |
|
1254 |
|
1255 /* check later if dbclean might run soon */ |
|
1256 next_clean = clean_fake_secs; |
|
1257 if (db_time.tv_sec >= next_clean - WFIX_PRE_CLEAN_SECS) { |
|
1258 dbclean_wfix_secs = next_clean + WFIX_POST_CLEAN_SECS; |
|
1259 if (!DB_IS_TIME(dbclean_wfix_secs, WFIX_MAX_SECS)) |
|
1260 return 0; |
|
1261 } |
|
1262 next_clean = clean_last_secs + 24*60*60; |
|
1263 if (db_time.tv_sec >= next_clean - WFIX_PRE_CLEAN_SECS) { |
|
1264 dbclean_wfix_secs = next_clean + WFIX_POST_CLEAN_SECS; |
|
1265 if (!DB_IS_TIME(dbclean_wfix_secs, WFIX_MAX_SECS)) |
|
1266 return 0; |
|
1267 } |
|
1268 |
|
1269 /* check later if the database is not too large now */ |
|
1270 if (db_fsize + db_hash_fsize |
|
1271 < (wfix_size_set ? wfix_size : db_max_rss)) { |
|
1272 wfix_later(WFIX_RECHECK_SECS); |
|
1273 break; |
|
1274 } |
|
1275 /* the database is too big, so try to chase the clients |
|
1276 * to the other DCC server (if any) */ |
|
1277 dbclean_wfix_state = WFIX_BUSY; |
|
1278 wfix_measure(1); |
|
1279 break; |
|
1280 |
|
1281 case WFIX_BUSY: |
|
1282 rate = wfix_measure(0); |
|
1283 if (rate >= 0.0) { |
|
1284 wfix_busy_rate = rate; |
|
1285 dbclean_wfix_state = WFIX_QUIET; |
|
1286 dbclean_wfix_secs = db_time.tv_sec + WFIX_QUIET_SECS; |
|
1287 } |
|
1288 break; |
|
1289 |
|
1290 case WFIX_QUIET: /* waiting for clients to flee */ |
|
1291 dbclean_wfix_state = WFIX_CHECK; |
|
1292 wfix_measure(1); |
|
1293 break; |
|
1294 |
|
1295 case WFIX_CHECK: /* counting clients */ |
|
1296 rate = wfix_measure(0); |
|
1297 if (rate < 0.0) |
|
1298 break; |
|
1299 if (rate <= wfix_quiet_rate |
|
1300 || rate <= wfix_busy_rate * wfix_rate_change) { |
|
1301 snprintf(reason, reason_len, |
|
1302 "database size "L_DPAT">"L_DPAT |
|
1303 "; load changed from %0.1f to %0.1f", |
|
1304 db_fsize + db_hash_fsize, |
|
1305 wfix_size_set ? wfix_size : db_max_rss, |
|
1306 wfix_busy_rate, rate); |
|
1307 return 1; |
|
1308 } |
|
1309 /* Other DCC servers did not take over the load. Maybe later */ |
|
1310 if (db_debug) |
|
1311 dcc_trace_msg("database size "L_DPAT" > "L_DPAT |
|
1312 ", but load changed from %0.1f to %0.1f", |
|
1313 db_fsize + db_hash_fsize, |
|
1314 wfix_size_set ? wfix_size : db_max_rss, |
|
1315 wfix_busy_rate, rate); |
|
1316 wfix_later(WFIX_RECHECK_SECS); |
|
1317 break; |
|
1318 } |
|
1319 |
|
1320 return 0; |
|
1321 } |
|
1322 |
|
1323 |
|
1324 |
|
1325 /* check for changes or other interesting events when the flood timer expires */ |
|
1326 static time_t /* microseconds to wait */ |
|
1327 check_changes(void) |
|
1328 { |
|
1329 static time_t misc_timer, files_timer; |
|
1330 time_t secs; |
|
1331 DB_HADDR hash_free; |
|
1332 const char *mode; |
|
1333 const char *reason; |
|
1334 char reason_buf[100]; |
|
1335 |
|
1336 reason = 0; |
|
1337 mode = 0; |
|
1338 |
|
1339 if (db_failed_line) { |
|
1340 snprintf(reason_buf, sizeof(reason_buf), |
|
1341 "database broken at line %d in %s "DCC_VERSION, |
|
1342 db_failed_line, db_failed_file); |
|
1343 reason = reason_buf; |
|
1344 mode = "Rbad"; |
|
1345 |
|
1346 } else if ((hash_free |
|
1347 = db_hash_len - db_hash_used) < MIN_CLEAN_HASH_ENTRIES |
|
1348 || hash_free < db_hash_len/20) { |
|
1349 /* try to expand the hash table when there are only |
|
1350 * a few free slots or the load factor rises above .95 */ |
|
1351 if (hash_free < MIN_HASH_ENTRIES) |
|
1352 snprintf(reason_buf, sizeof(reason_buf), |
|
1353 "%d free hash entries", |
|
1354 hash_free); |
|
1355 else |
|
1356 snprintf(reason_buf, sizeof(reason_buf), |
|
1357 "%d free hash entries among %d total", |
|
1358 hash_free, db_hash_len); |
|
1359 reason = reason_buf; |
|
1360 mode = "Rhash"; |
|
1361 |
|
1362 } else { |
|
1363 /* check nothing else if it is not yet time */ |
|
1364 secs = next_flods_ck - db_time.tv_sec; |
|
1365 if (secs > 0 && secs <= flods_ck_secs) |
|
1366 return secs * DCC_US; |
|
1367 } |
|
1368 |
|
1369 next_flods_ck = db_time.tv_sec + flods_ck_secs; |
|
1370 |
|
1371 /* do not make some checks too often even when flood checking is |
|
1372 * being rushed */ |
|
1373 if (DB_IS_TIME(misc_timer, MISC_CK_SECS)) { |
|
1374 misc_timer = db_time.tv_sec + MISC_CK_SECS; |
|
1375 |
|
1376 /* shrink our memory footprint |
|
1377 * if we are so idle that we don't have anything to flush */ |
|
1378 if (db_need_flush_secs == 0) { |
|
1379 if (db_unload(0, 1) == 1) { |
|
1380 rel_db_states(); |
|
1381 db_unload(0, 1); |
|
1382 } |
|
1383 } |
|
1384 |
|
1385 if (DB_IS_TIME(files_timer, 30)) { |
|
1386 files_timer = db_time.tv_sec + 30; |
|
1387 |
|
1388 if (0 >= check_load_ids(0)) |
|
1389 dcc_error_msg("check/reload: %s", dcc_emsg); |
|
1390 |
|
1391 check_blacklist_file(); |
|
1392 |
|
1393 #ifdef HAVE_GETIFADDRS |
|
1394 /* check for network interface changes, but only |
|
1395 * if we can release the result of getifaddrs() */ |
|
1396 if (get_if_changes(1)) { |
|
1397 int socs = open_srvr_socs(0); |
|
1398 if (!socs) |
|
1399 bad_stop("no server sockets"); |
|
1400 else if (socs > 0) |
|
1401 flods_restart("new network interfaces", |
|
1402 1); |
|
1403 } |
|
1404 #endif |
|
1405 } |
|
1406 |
|
1407 if (DB_IS_TIME(need_clients_save, CLIENTS_SAVE_SECS)) |
|
1408 clients_save(); |
|
1409 |
|
1410 /* sound a claim to our server-ID if the database is locked */ |
|
1411 if (DB_IS_TIME(host_id_next, DCC_SRVR_ID_SECS) |
|
1412 && DB_IS_LOCKED() |
|
1413 &&!db_failed_line ) { |
|
1414 refresh_srvr_rcd(host_id_sum, my_srvr_id, |
|
1415 "adding server-ID claim"); |
|
1416 |
|
1417 if (!db_failed_line ) { |
|
1418 host_id_last = db_time.tv_sec; |
|
1419 host_id_next = host_id_last + DCC_SRVR_ID_SECS; |
|
1420 } |
|
1421 } |
|
1422 } |
|
1423 |
|
1424 /* note when hash expansion finishes and collect a zombie */ |
|
1425 check_dbclean(WNOHANG); |
|
1426 |
|
1427 if (reason != 0) { |
|
1428 ; |
|
1429 |
|
1430 } else if (DB_IS_TIME(clean_fake_secs, 3*24*60*60) |
|
1431 && (!db_minimum_map |
|
1432 || DB_IS_TIME(clean_last_secs+2*24*60*60, 4*24*60*60))) { |
|
1433 reason = "work around broken cron job"; |
|
1434 mode = "Rcron"; |
|
1435 |
|
1436 } else if (need_del_dbclean != 0 |
|
1437 && DB_IS_TIME(del_dbclean_next, DEL_DBCLEAN_SECS)) { |
|
1438 /* the deletion of a report needs to be cleaned up */ |
|
1439 reason = need_del_dbclean; |
|
1440 mode = "Rdel"; |
|
1441 |
|
1442 } else if (DB_IS_TIME(dbclean_wfix_secs, WFIX_MAX_SECS) |
|
1443 && wfix(reason_buf, sizeof(reason_buf))) { |
|
1444 reason = reason_buf; |
|
1445 mode = "Rquick"; |
|
1446 } |
|
1447 |
|
1448 if (reason) { |
|
1449 if (!DB_IS_TIME(dbclean_limit, dbclean_limit_secs)) { |
|
1450 if (next_flods_ck > dbclean_limit) |
|
1451 next_flods_ck = dbclean_limit; |
|
1452 } else if (dbclean_pid < 0) { |
|
1453 run_dbclean(mode, reason); |
|
1454 } else { |
|
1455 RUSH_NEXT_FLODS_CK(); |
|
1456 } |
|
1457 } else { |
|
1458 flods_ck(0); |
|
1459 } |
|
1460 |
|
1461 /* we probably delayed */ |
|
1462 gettimeofday(&db_time, 0); |
|
1463 |
|
1464 secs = next_flods_ck - db_time.tv_sec; |
|
1465 return secs >= 0 ? secs*DCC_US : 0; |
|
1466 } |
|
1467 |
|
1468 |
|
1469 |
|
1470 static void NRATTRIB |
|
1471 recv_job(void) |
|
1472 { |
|
1473 fd_set rfds, *prfds, wfds, *pwfds; |
|
1474 # define PFD_SET(_fd0,_fds) {int _fd = _fd0; \ |
|
1475 p##_fds = &_fds; FD_SET(_fd,p##_fds); \ |
|
1476 if (max_fd < _fd) max_fd = _fd;} |
|
1477 int max_fd, nfds; |
|
1478 IFLOD_INFO *ifp; |
|
1479 OFLOD_INFO *ofp; |
|
1480 struct timeval delay; |
|
1481 time_t delay_us, slept_us, was_too_busy, us; |
|
1482 struct timeval iflods_read, busy_time, extra_time; |
|
1483 u_char db_has_failed; |
|
1484 # define QUANTUM (DCC_US/10) /* try to use only ~50% of the system */ |
|
1485 SRVR_SOC *sp; |
|
1486 QUEUE *q; |
|
1487 int bad_select; |
|
1488 u_char worked; |
|
1489 int fd, i; |
|
1490 |
|
1491 bad_select = 3; |
|
1492 was_too_busy = 0; |
|
1493 gettimeofday(&db_time, 0); |
|
1494 iflods_read = db_time; |
|
1495 busy_time = db_time; |
|
1496 extra_time = db_time; |
|
1497 delay_us = flods_ck_secs*DCC_US; |
|
1498 db_has_failed = 0; |
|
1499 for (;;) { |
|
1500 dcc_emsg[0] = '\0'; |
|
1501 |
|
1502 if (stopint) { |
|
1503 flods_ck_secs = SHUTDOWN_DELAY; |
|
1504 if (flods_off < 100000) { |
|
1505 flods_off = 100000; |
|
1506 flods_stop("server stopping", 0); |
|
1507 check_dbclean(WNOHANG); |
|
1508 if (dbclean_pid > 0) { |
|
1509 kill(dbclean_pid, SIGINT); |
|
1510 usleep(100*1000); |
|
1511 } |
|
1512 /* get started flushing the database while |
|
1513 * we wait for flooding to stop */ |
|
1514 rel_db_states(); |
|
1515 db_minimum_map = 1; |
|
1516 db_unload(0, 2); |
|
1517 } |
|
1518 /* get serious when the floods have stopped */ |
|
1519 if (oflods.total == 0) { |
|
1520 if (stopint < 0) |
|
1521 dccd_quit(0, "gracefully stopping%s", |
|
1522 stop_mode == 1 |
|
1523 ? " for reboot" |
|
1524 : stop_mode == 2 |
|
1525 ? " cleanly" |
|
1526 : ""); |
|
1527 dccd_quit(stopint | 128, |
|
1528 "exiting with signal %d", stopint); |
|
1529 } |
|
1530 } |
|
1531 |
|
1532 if (db_has_failed != db_failed_line) { |
|
1533 db_has_failed = db_failed_line; |
|
1534 if (db_failed_line) { |
|
1535 ++flods_off; |
|
1536 flods_stop("database corrupt", 1); |
|
1537 rel_db_states(); |
|
1538 db_unload(0, 2); |
|
1539 db_unlock(); |
|
1540 } |
|
1541 } |
|
1542 |
|
1543 FD_ZERO(&rfds); |
|
1544 prfds = 0; |
|
1545 FD_ZERO(&wfds); |
|
1546 pwfds = 0; |
|
1547 max_fd = -1; |
|
1548 |
|
1549 /* look for client requests */ |
|
1550 for (sp = srvr_socs; sp; sp = sp->fwd) { |
|
1551 if (sp->udp >= 0) |
|
1552 PFD_SET(sp->udp, rfds); |
|
1553 } |
|
1554 |
|
1555 if (was_too_busy > 0) |
|
1556 was_too_busy = QUANTUM - tv_diff2us(&db_time, |
|
1557 &busy_time); |
|
1558 if (was_too_busy > 0 |
|
1559 && (tv_diff2us(&db_time, &extra_time) |
|
1560 < min(30, min(KEEPALIVE_IN, KEEPALIVE_OUT)/2)) |
|
1561 && FLODS_OK()) { |
|
1562 /* if we have been too busy, |
|
1563 * then do nothing extra for a while */ |
|
1564 if (delay_us > was_too_busy) |
|
1565 delay_us = was_too_busy; |
|
1566 } else { |
|
1567 extra_time = db_time; |
|
1568 |
|
1569 /* Accept new incoming flood connections |
|
1570 * if flooding is on |
|
1571 * and we don't already have too many floods. */ |
|
1572 if (iflods.open < DCCD_MAX_FLOODS) { |
|
1573 for (sp = srvr_socs; sp; sp = sp->fwd) { |
|
1574 if (sp->listen >= 0) |
|
1575 PFD_SET(sp->listen, rfds); |
|
1576 } |
|
1577 } |
|
1578 |
|
1579 /* pump floods out */ |
|
1580 for (ofp = oflods.infos, i = 0; |
|
1581 i < oflods.open; |
|
1582 ++ofp) { |
|
1583 if (ofp->soc < 0) |
|
1584 continue; |
|
1585 ++i; |
|
1586 if (ofp->flags & OFLOD_FG_CONNECTED) { |
|
1587 PFD_SET(ofp->soc, rfds); |
|
1588 if (!(ofp->flags & OFLOD_FG_EAGAIN) |
|
1589 && ofp->obuf_len != 0) |
|
1590 PFD_SET(ofp->soc, wfds); |
|
1591 } else { |
|
1592 PFD_SET(ofp->soc, wfds); |
|
1593 } |
|
1594 } |
|
1595 |
|
1596 /* pump floods in */ |
|
1597 for (ifp = iflods.infos, i = 0; |
|
1598 i < iflods.open; |
|
1599 ++ifp) { |
|
1600 if (ifp->soc < 0) |
|
1601 continue; |
|
1602 ++i; |
|
1603 if (ifp->flags & IFLOD_FG_CONNECTED) { |
|
1604 PFD_SET(ifp->soc, rfds); |
|
1605 } else { |
|
1606 PFD_SET(ifp->soc, wfds); |
|
1607 } |
|
1608 } |
|
1609 } |
|
1610 |
|
1611 /* push data to the disk */ |
|
1612 if (db_need_flush_secs != 0) { |
|
1613 if (DB_IS_TIME(db_need_flush_secs, |
|
1614 max(DB_URGENT_NEED_FLUSH_SECS, |
|
1615 DB_NEED_FLUSH_SECS))) { |
|
1616 db_flush_needed(); |
|
1617 gettimeofday(&db_time, 0); |
|
1618 } |
|
1619 us = db_need_flush_secs - db_time.tv_sec; |
|
1620 if (us >= 0) { |
|
1621 us *= DCC_US; |
|
1622 if (delay_us > us) |
|
1623 delay_us = us; |
|
1624 } |
|
1625 } |
|
1626 |
|
1627 /* let dbclean run if we have run out of work |
|
1628 * or if we have been holding the lock for 0.1 seconds*/ |
|
1629 if (db_minimum_map |
|
1630 && DB_IS_LOCKED() |
|
1631 && (delay_us != 0 |
|
1632 || tv_diff2us(&db_time, &db_locked) >= DCC_US/10)) { |
|
1633 db_unlock(); |
|
1634 } |
|
1635 |
|
1636 /* delay until it is time to answer the oldest anonymous |
|
1637 * request or something else that needs doing */ |
|
1638 delay.tv_sec = delay_us/DCC_US; |
|
1639 delay.tv_usec = delay_us%DCC_US; |
|
1640 nfds = select(max_fd+1, prfds, pwfds, 0, &delay); |
|
1641 if (nfds < 0) { |
|
1642 if (errno != EINTR) { |
|
1643 if (--bad_select < 0) |
|
1644 bad_stop("give up after select(): %s", |
|
1645 ERROR_STR()); |
|
1646 else |
|
1647 dcc_error_msg("select(): %s", |
|
1648 ERROR_STR()); |
|
1649 } |
|
1650 /* ignore EINTR but recompute timers */ |
|
1651 FD_ZERO(&rfds); |
|
1652 FD_ZERO(&wfds); |
|
1653 } else { |
|
1654 bad_select = 3; |
|
1655 } |
|
1656 |
|
1657 gettimeofday(&wake_time, 0); |
|
1658 slept_us = tv_diff2us(&wake_time, &db_time); |
|
1659 if (slept_us >= 500) { |
|
1660 /* If select() paused for at least 0.5 millisecond, |
|
1661 * then the waiting request has just now arrived. */ |
|
1662 req_recv_time = wake_time; |
|
1663 } else { |
|
1664 /* If select() did not pause, then assume the waiting |
|
1665 * requests arrived when we were half finished working |
|
1666 * on flooding and other work besides ordinary requests |
|
1667 * before calling select(). */ |
|
1668 tv_add_us(&req_recv_time, |
|
1669 tv_diff2us(&wake_time, &req_recv_time) / 2); |
|
1670 } |
|
1671 db_time = wake_time; |
|
1672 |
|
1673 worked = 0; |
|
1674 for (sp = srvr_socs; sp; sp = sp->fwd) { |
|
1675 /* queue a new anonymous request |
|
1676 * or answer a new authenticated request */ |
|
1677 fd = sp->udp; |
|
1678 if (fd >= 0 && FD_ISSET(fd, &rfds)) { |
|
1679 --nfds; |
|
1680 worked = 1; |
|
1681 while (new_job(sp)) |
|
1682 continue; |
|
1683 } |
|
1684 |
|
1685 /* start a new incoming flood */ |
|
1686 fd = sp->listen; |
|
1687 if (fd >= 0 && FD_ISSET(fd, &rfds)) { |
|
1688 --nfds; |
|
1689 worked = 1; |
|
1690 iflod_start(sp); |
|
1691 } |
|
1692 } |
|
1693 if (worked) |
|
1694 gettimeofday(&db_time, 0); |
|
1695 /* reset request receipt clock for next time */ |
|
1696 req_recv_time = db_time; |
|
1697 |
|
1698 /* Accept new flood data or start new SOCKS floods. |
|
1699 * Listen to all peers to prevent starvation */ |
|
1700 worked = 0; |
|
1701 for (ifp = iflods.infos, i = 0; |
|
1702 nfds > 0 && i < iflods.open; |
|
1703 ++ifp) { |
|
1704 if (ifp->soc < 0) |
|
1705 continue; |
|
1706 ++i; |
|
1707 if (FD_ISSET(ifp->soc, &rfds) |
|
1708 || FD_ISSET(ifp->soc, &wfds)) { |
|
1709 --nfds; |
|
1710 iflod_read(ifp); |
|
1711 worked = 1; |
|
1712 } |
|
1713 } |
|
1714 if (worked) { |
|
1715 gettimeofday(&db_time, 0); |
|
1716 iflods_read = db_time; |
|
1717 } else if (was_too_busy <= 0) { |
|
1718 /* if incoming floods have been quiet for |
|
1719 * awhile, then assume flooding has caught up |
|
1720 * after having been turned off */ |
|
1721 if (tv_diff2us(&wake_time, &iflods_read) > 2*DCC_US) |
|
1722 iflods_ok_timer = db_time.tv_sec; |
|
1723 } |
|
1724 |
|
1725 /* pump output flood data and receive confirmations |
|
1726 * talk to all peers to prevent starvation */ |
|
1727 worked = 0; |
|
1728 for (ofp = oflods.infos, i = 0; |
|
1729 nfds > 0 && i < oflods.open; |
|
1730 ++ofp) { |
|
1731 if (ofp->soc < 0) |
|
1732 continue; |
|
1733 ++i; |
|
1734 if (FD_ISSET(ofp->soc, &rfds)) { |
|
1735 --nfds; |
|
1736 oflod_read(ofp); |
|
1737 if (ofp->soc < 0) |
|
1738 continue; |
|
1739 } |
|
1740 if (FD_ISSET(ofp->soc, &wfds)) { |
|
1741 --nfds; |
|
1742 oflod_write(ofp); |
|
1743 worked = 1; |
|
1744 } |
|
1745 } |
|
1746 if (worked) |
|
1747 gettimeofday(&db_time, 0); |
|
1748 |
|
1749 /* process delayed jobs when their times arrive */ |
|
1750 worked = 0; |
|
1751 for (;;) { |
|
1752 q = queue_head; |
|
1753 if (!q) { |
|
1754 delay_us = flods_ck_secs*DCC_US; |
|
1755 break; |
|
1756 } |
|
1757 |
|
1758 /* decide whether this job's time has come |
|
1759 * while defending against time jumps */ |
|
1760 delay_us = tv_diff2us(&q->answer, &db_time); |
|
1761 if (delay_us >= 1000 |
|
1762 && delay_us <= DCC_MAX_RTT |
|
1763 && !stopint) |
|
1764 break; /* not yet time for next job */ |
|
1765 |
|
1766 queue_head = q->later; |
|
1767 if (queue_head) |
|
1768 queue_head->earlier = 0; |
|
1769 --queue_cur; |
|
1770 do_work(q); |
|
1771 worked = 1; |
|
1772 free_q(q); |
|
1773 } |
|
1774 if (worked) |
|
1775 gettimeofday(&db_time, 0); |
|
1776 |
|
1777 /* check configuration changes etc. */ |
|
1778 us = check_changes(); |
|
1779 if (delay_us >= us) |
|
1780 delay_us = us; |
|
1781 |
|
1782 us = tv_diff2us(&db_time, &wake_time); |
|
1783 if (us >= QUANTUM && !stopint) { |
|
1784 gettimeofday(&db_time, 0); |
|
1785 busy_time = db_time; |
|
1786 was_too_busy = QUANTUM; |
|
1787 } |
|
1788 } |
|
1789 } |
|
1790 |
|
1791 |
|
1792 |
|
1793 static void |
|
1794 add_queue(QUEUE *q) |
|
1795 { |
|
1796 QUEUE *qnext, **qp; |
|
1797 |
|
1798 TMSG1(QUERY, "received %s", op_id_ip(q)); |
|
1799 if (!ck_clnt_id(q)) { |
|
1800 free_q(q); |
|
1801 return; |
|
1802 } |
|
1803 |
|
1804 ++total_ops; |
|
1805 |
|
1806 /* immediately process requests from authenticated clients |
|
1807 * if flooding is working */ |
|
1808 if (q->delay_us == 0) { |
|
1809 do_work(q); |
|
1810 free_q(q); |
|
1811 return; |
|
1812 } |
|
1813 |
|
1814 /* don't let the queue of delayed requests get too large */ |
|
1815 if (queue_cur >= queue_max) { |
|
1816 clnt_msg(q, "drop excess queued %s", op_id_ip(q)); |
|
1817 free_q(q); |
|
1818 return; |
|
1819 } |
|
1820 |
|
1821 tv_add_us(&q->answer, q->delay_us); |
|
1822 |
|
1823 /* add the new job to the queue */ |
|
1824 ++queue_cur; |
|
1825 qp = &queue_head; |
|
1826 for (;;) { |
|
1827 qnext = *qp; |
|
1828 if (!qnext) { |
|
1829 *qp = q; |
|
1830 break; |
|
1831 } |
|
1832 if (qnext->answer.tv_sec > q->answer.tv_sec |
|
1833 || (qnext->answer.tv_sec == q->answer.tv_sec |
|
1834 && qnext->answer.tv_usec > q->answer.tv_usec)) { |
|
1835 q->later = qnext; |
|
1836 qnext->earlier = q; |
|
1837 *qp = q; |
|
1838 break; |
|
1839 } |
|
1840 q->earlier = qnext; |
|
1841 qp = &qnext->later; |
|
1842 } |
|
1843 } |
|
1844 |
|
1845 |
|
1846 |
|
1847 /* get a new job in a datagram */ |
|
1848 static u_char /* 1=call again */ |
|
1849 new_job(SRVR_SOC *sp) |
|
1850 { |
|
1851 QUEUE *q; |
|
1852 static struct iovec iov = {0, sizeof(q->pkt)}; |
|
1853 static struct msghdr msg; |
|
1854 int i, j; |
|
1855 |
|
1856 /* Find a free queue entry for the job. |
|
1857 * Because we don't check for incoming jobs unless we think the |
|
1858 * queue is not full, there must always be a free entry or |
|
1859 * permission to make more entries. */ |
|
1860 q = queue_free; |
|
1861 if (q) { |
|
1862 queue_free = q->later; |
|
1863 } else { |
|
1864 i = 16; |
|
1865 q = dcc_malloc(i * sizeof(*q)); |
|
1866 if (!q) |
|
1867 dcc_logbad(EX_UNAVAILABLE, |
|
1868 "malloc(%d queue entries) failed", i); |
|
1869 queue_max_cur += i; |
|
1870 /* put all but the last new queue entry on the free list */ |
|
1871 while (--i > 0) { |
|
1872 q->later = queue_free; |
|
1873 queue_free = q; |
|
1874 ++q; |
|
1875 } |
|
1876 } |
|
1877 |
|
1878 memset(q, 0, sizeof(*q)); |
|
1879 q->sp = sp; |
|
1880 iov.iov_base = (char *)&q->pkt; |
|
1881 msg.msg_name = (void *)&q->clnt_su; |
|
1882 msg.msg_namelen = sizeof(q->clnt_su); |
|
1883 msg.msg_iov = &iov; |
|
1884 msg.msg_iovlen = 1; |
|
1885 i = recvmsg(sp->udp, &msg, 0); |
|
1886 if (i < 0) { |
|
1887 /* ignore some results of ICMP unreachables for UDP |
|
1888 * retransmissions seen on some platforms */ |
|
1889 if (DCC_BLOCK_ERROR()) { |
|
1890 ; |
|
1891 } else if (UNREACHABLE_ERRORS()) { |
|
1892 TMSG2(QUERY, "recvmsg(%s): %s", |
|
1893 dcc_su2str_err(&sp->su), ERROR_STR()); |
|
1894 } else { |
|
1895 dcc_error_msg("recvmsg(%s): %s", |
|
1896 dcc_su2str_err(&sp->su), ERROR_STR()); |
|
1897 } |
|
1898 free_q(q); |
|
1899 return 0; |
|
1900 } |
|
1901 if (q->clnt_su.sa.sa_family != sp->su.sa.sa_family |
|
1902 && !dcc_ipv4sutoipv6(&q->clnt_su, &q->clnt_su)) { |
|
1903 dcc_error_msg("recvmsg address family %d instead of %d", |
|
1904 q->clnt_su.sa.sa_family, |
|
1905 sp->su.sa.sa_family); |
|
1906 free_q(q); |
|
1907 return 1; |
|
1908 } |
|
1909 |
|
1910 if (DCC_SU_PORT(&q->clnt_su) == 0) { |
|
1911 drop_msg(q, "source port 0"); |
|
1912 free_q(q); |
|
1913 return 1; |
|
1914 } |
|
1915 |
|
1916 q->pkt_len = i; |
|
1917 if (i < ISZ(DCC_HDR)) { |
|
1918 drop_msg(q, "short request of %d bytes", i); |
|
1919 free_q(q); |
|
1920 return 1; |
|
1921 } |
|
1922 j = ntohs(q->pkt.hdr.len); |
|
1923 if (j != i) { |
|
1924 drop_msg(q, "request with header length %d instead of %d", |
|
1925 j, i); |
|
1926 free_q(q); |
|
1927 return 1; |
|
1928 } |
|
1929 |
|
1930 if (q->pkt.hdr.pkt_vers > DCC_PKT_VERSION_MAX_VALID |
|
1931 || q->pkt.hdr.pkt_vers < DCC_PKT_VERSION_MIN_VALID |
|
1932 || ((q->pkt.hdr.pkt_vers > DCC_PKT_VERSION_MAX |
|
1933 || q->pkt.hdr.pkt_vers < DCC_PKT_VERSION_MIN) |
|
1934 && q->pkt.hdr.op != DCC_OP_NOP)) { |
|
1935 drop_msg(q, "%s in unrecognized protocol version #%d", |
|
1936 qop2str(q), q->pkt.hdr.pkt_vers); |
|
1937 free_q(q); |
|
1938 return 1; |
|
1939 } |
|
1940 |
|
1941 q->answer = req_recv_time; |
|
1942 |
|
1943 switch ((DCC_OPS)q->pkt.hdr.op) { |
|
1944 case DCC_OP_NOP: |
|
1945 do_nop(q); |
|
1946 free_q(q); |
|
1947 return 1; |
|
1948 |
|
1949 case DCC_OP_REPORT: |
|
1950 if (db_parms.flags & DB_PARM_FG_GREY) |
|
1951 break; /* not valid for greylist servers */ |
|
1952 add_queue(q); |
|
1953 return 1; |
|
1954 |
|
1955 case DCC_OP_QUERY: |
|
1956 add_queue(q); |
|
1957 return 1; |
|
1958 |
|
1959 case DCC_OP_ADMN: |
|
1960 do_admn(q); |
|
1961 free_q(q); |
|
1962 return 1; |
|
1963 |
|
1964 case DCC_OP_DELETE: |
|
1965 do_delete(q); |
|
1966 free_q(q); |
|
1967 return 1; |
|
1968 |
|
1969 case DCC_OP_GREY_REPORT: |
|
1970 case DCC_OP_GREY_QUERY: |
|
1971 case DCC_OP_GREY_WHITE: |
|
1972 if (!(db_parms.flags & DB_PARM_FG_GREY)) |
|
1973 break; /* valid only for greylist servers */ |
|
1974 do_grey(q); |
|
1975 free_q(q); |
|
1976 return 1; |
|
1977 |
|
1978 case DCC_OP_GREY_SPAM: |
|
1979 if (!(db_parms.flags & DB_PARM_FG_GREY)) |
|
1980 break; /* valid only for greylist servers */ |
|
1981 do_grey_spam(q); |
|
1982 free_q(q); |
|
1983 return 1; |
|
1984 |
|
1985 case DCC_OP_INVALID: |
|
1986 case DCC_OP_ANSWER: |
|
1987 case DCC_OP_OK: |
|
1988 case DCC_OP_ERROR: |
|
1989 break; |
|
1990 } |
|
1991 |
|
1992 drop_msg(q, "invalid %s", op_id_ip(q)); |
|
1993 free_q(q); |
|
1994 return 1; |
|
1995 } |
|
1996 |
|
1997 |
|
1998 |
|
1999 void |
|
2000 free_q(QUEUE *q) |
|
2001 { |
|
2002 if (q->rl) |
|
2003 --q->rl->ref_cnt; |
|
2004 q->later = queue_free; |
|
2005 queue_free = q; |
|
2006 } |
|
2007 |
|
2008 |
|
2009 |
|
2010 u_char |
|
2011 dccd_db_open(u_char lock_mode) |
|
2012 { |
|
2013 DCC_CK_TYPES type; |
|
2014 time_t clean_secs; |
|
2015 DCC_TGTS tgts; |
|
2016 int i; |
|
2017 |
|
2018 if (!db_open(dcc_emsg, -1, 0, 0, lock_mode | db_mode)) |
|
2019 return 0; |
|
2020 |
|
2021 if (grey_on) { |
|
2022 /* for greylisting, ignore the args an silently impose our |
|
2023 * notion of which checksums to keep and flooding thresholds */ |
|
2024 db_parms.nokeep_cks = def_nokeep_cks(); |
|
2025 if (grey_weak_ip) |
|
2026 DB_RESET_NOKEEP(db_parms.nokeep_cks, DCC_CK_IP); |
|
2027 |
|
2028 for (type = DCC_CK_TYPE_FIRST; |
|
2029 type <= DCC_CK_TYPE_LAST; |
|
2030 ++type) { |
|
2031 if (type == DCC_CK_SRVR_ID) { |
|
2032 flod_tholds[type] = 1; |
|
2033 continue; |
|
2034 } |
|
2035 |
|
2036 if (DB_TEST_NOKEEP(db_parms.nokeep_cks, type)) |
|
2037 flod_tholds[type] = DCC_TGTS_INVALID; |
|
2038 else |
|
2039 flod_tholds[type] = 1; |
|
2040 |
|
2041 if (DCC_CK_IS_GREY_TRIPLE(grey_on,type) |
|
2042 || type == DCC_CK_IP) { |
|
2043 db_parms.ex_secs[type].all = grey_window; |
|
2044 db_parms.ex_secs[type].spam = grey_white; |
|
2045 } else if (type == DCC_CK_BODY |
|
2046 || DCC_CK_IS_GREY_MSG(grey_on,type)) { |
|
2047 db_parms.ex_secs[type].all = grey_window; |
|
2048 db_parms.ex_secs[type].spam = grey_window; |
|
2049 } else { |
|
2050 db_parms.ex_secs[type].all = 1; |
|
2051 db_parms.ex_secs[type].spam = 1; |
|
2052 } |
|
2053 } |
|
2054 |
|
2055 summarize_delay_secs = grey_embargo - FLODS_CK_SECS*2; |
|
2056 |
|
2057 } else { |
|
2058 /* impose our notion of which normal checksums to keep */ |
|
2059 DB_SET_NOKEEP(set_new_nokeep_cks, DCC_CK_FLOD_PATH); |
|
2060 DB_SET_NOKEEP(set_new_nokeep_cks, DCC_CK_INVALID); |
|
2061 DB_SET_NOKEEP(set_new_nokeep_cks, DCC_CK_REP_TOTAL); |
|
2062 DB_SET_NOKEEP(set_new_nokeep_cks, DCC_CK_REP_BULK); |
|
2063 db_parms.nokeep_cks = ((def_nokeep_cks() |
|
2064 & ~reset_new_nokeep_cks) |
|
2065 | set_new_nokeep_cks); |
|
2066 |
|
2067 for (type = DCC_CK_TYPE_FIRST; |
|
2068 type <= DCC_CK_TYPE_LAST; |
|
2069 ++type) { |
|
2070 if (type == DCC_CK_SRVR_ID) { |
|
2071 flod_tholds[type] = 1; |
|
2072 continue; |
|
2073 } |
|
2074 |
|
2075 if (type == DCC_CK_REP_TOTAL) |
|
2076 tgts = DCC_TGTS_INVALID; |
|
2077 if (DB_TEST_NOKEEP(db_parms.nokeep_cks, type)) |
|
2078 tgts = DCC_TGTS_INVALID; |
|
2079 else if (DCC_CK_IS_REP_CMN(0, type)) |
|
2080 tgts = DCC_TGTS_INVALID; |
|
2081 else |
|
2082 tgts = BULK_THRESHOLD; |
|
2083 flod_tholds[type] = tgts; |
|
2084 } |
|
2085 |
|
2086 /* We should not delay reports or summaries so much that |
|
2087 * dbclean might expire them before we can summarize them. */ |
|
2088 summarize_delay_secs = DCC_OLD_SPAM_SECS; |
|
2089 for (type = DCC_CK_TYPE_FIRST; |
|
2090 type <= DCC_CK_TYPE_LAST; |
|
2091 ++type) { |
|
2092 if (DB_TEST_NOKEEP(db_parms.nokeep_cks, type)) |
|
2093 continue; |
|
2094 i = db_parms.ex_secs[type].spam; |
|
2095 if (i != 0 && summarize_delay_secs > i) |
|
2096 summarize_delay_secs = i; |
|
2097 } |
|
2098 } |
|
2099 if (summarize_delay_secs < 1) |
|
2100 summarize_delay_secs = 1; |
|
2101 |
|
2102 /* adjust the thresholds after possible changes to kept checksums */ |
|
2103 set_db_tholds(db_parms.nokeep_cks); |
|
2104 |
|
2105 /* If we instead of cron asked for the last cleaning, make a note |
|
2106 * to clean the database during the graveyard shift. |
|
2107 * Otherwise the database will bloat while the cron job is broken. |
|
2108 * |
|
2109 * Compute 1 day + 45 minutes after cron should have last cleaned the |
|
2110 * database, if it has been cleaned by cron within the last 3 days |
|
2111 * or the quarter hour when it was last cleaned by this mechanism |
|
2112 * provided that was between local midnight and 05:00 |
|
2113 * or 3 minutes past a random quarter hour beteen midnight and 05:00 */ |
|
2114 if (db_parms.cleaned_cron >= db_time.tv_sec - 3*24*60*60 |
|
2115 && db_parms.cleaned_cron <= db_time.tv_sec) { |
|
2116 clean_last_secs = db_parms.cleaned_cron; |
|
2117 /* failsafe cleaning for the greylist database starts 15 minutes |
|
2118 * before the main database */ |
|
2119 clean_secs = clean_last_secs + 45*60 - grey_on*15*60; |
|
2120 } else { |
|
2121 struct tm tm; |
|
2122 |
|
2123 clean_last_secs = db_parms.cleaned; |
|
2124 if (clean_last_secs >= db_time.tv_sec) |
|
2125 clean_last_secs = 0; |
|
2126 clean_secs = clean_last_secs; |
|
2127 |
|
2128 /* if the previous time for this mechanism is not good, |
|
2129 * pick a new random time */ |
|
2130 dcc_localtime(clean_secs, &tm); |
|
2131 if (clean_secs == 0 || tm.tm_hour < 1 || tm.tm_hour >= 5) { |
|
2132 int rnum = (u_int)(db_time.tv_sec + db_time.tv_usec |
|
2133 + my_srvr_id - grey_on) % 23; |
|
2134 tm.tm_hour = ((rnum / 4) % 4) + 1; |
|
2135 tm.tm_min = (rnum % 4) * 15; |
|
2136 clean_secs = mktime(&tm); |
|
2137 } |
|
2138 } |
|
2139 /* round down to a quarter hour to prevent creep due to inevitiable |
|
2140 * delays in cron or this mechanism starting dbclean */ |
|
2141 clean_secs -= clean_secs % (15*60); |
|
2142 clean_secs %= (24*60*60); /* failsafe cleaning target minute */ |
|
2143 |
|
2144 /* compute the next scheduled failsafe cleaning. */ |
|
2145 clean_fake_secs = db_time.tv_sec - (db_time.tv_sec % (24*60*60)); |
|
2146 clean_fake_secs += clean_secs; |
|
2147 if (clean_fake_secs <= db_time.tv_sec) |
|
2148 clean_fake_secs += 24*60*60; |
|
2149 |
|
2150 /* The next failsafe cleaning should happen a day after the |
|
2151 * most recent cron or failsafe cleaning, modulo the delay before |
|
2152 * dbclean starts */ |
|
2153 while (clean_fake_secs <= clean_last_secs + 24*60*60 - 60*60) |
|
2154 clean_fake_secs += 24*60*60; |
|
2155 |
|
2156 /* Do not failsafe clean during the first 48 hours after the |
|
2157 * database was created to give the cron job a chance. |
|
2158 * We do not want failsafe cleaning to ever be running when the |
|
2159 * cron job tries to start. */ |
|
2160 while (clean_fake_secs <= db_parms.cleared + 2*24*60*60 |
|
2161 && db_parms.cleared <= db_time.tv_sec) |
|
2162 clean_fake_secs += 24*60*60; |
|
2163 |
|
2164 total_ops = 0; |
|
2165 |
|
2166 /* push our thresholds and flags to the file */ |
|
2167 return db_flush_parms(0); |
|
2168 } |
|
2169 |
|
2170 |
|
2171 |
|
2172 /* clean shut down */ |
|
2173 static void NRATTRIB |
|
2174 dccd_quit(int exitcode, const char *p, ...) |
|
2175 { |
|
2176 va_list args; |
|
2177 |
|
2178 if (stop_mode == 1) |
|
2179 db_stop(); |
|
2180 else if (stop_mode == 2) |
|
2181 make_clean(2); |
|
2182 db_unlock(); |
|
2183 |
|
2184 va_start(args, p); |
|
2185 if (exitcode) |
|
2186 dcc_verror_msg(p, args); |
|
2187 else |
|
2188 dcc_vtrace_msg(p, args); |
|
2189 va_end(args); |
|
2190 |
|
2191 /* db_close() can take a long time, so close some things early. */ |
|
2192 stop_children(); |
|
2193 check_dbclean(WNOHANG); |
|
2194 clients_save(); |
|
2195 |
|
2196 #ifdef HAVE_COHERENT_MMAP |
|
2197 /* If mmap() is not coherent, do not call close_srvr_socs() but |
|
2198 * keep the UDP sockets open to prevent another server from starting |
|
2199 * until we have flushed our buffers to prevent problems on systems |
|
2200 * that lack inter-process coherent mmap() */ |
|
2201 if (!(db_mode & DB_OPEN_MMAP_WRITE)) |
|
2202 close_srvr_socs(); |
|
2203 #endif |
|
2204 db_close(1); |
|
2205 |
|
2206 if (exitcode) |
|
2207 dcc_error_msg("stopped"); |
|
2208 else |
|
2209 dcc_trace_msg("stopped"); |
|
2210 exit(exitcode); |
|
2211 } |
|
2212 |
|
2213 |
|
2214 |
|
2215 /* watch for fatal signals */ |
|
2216 static void |
|
2217 sigterm(int sig) |
|
2218 { |
|
2219 stopint = sig; |
|
2220 stop_mode = 1; |
|
2221 next_flods_ck = 0; |
|
2222 (void)signal(sig, SIG_DFL); /* catch it only once */ |
|
2223 } |
|
2224 |
|
2225 |
|
2226 |
|
2227 /* SIGHUP hurries checking the configuration files */ |
|
2228 static void |
|
2229 sighup(int sig UATTRIB) |
|
2230 { |
|
2231 next_flods_ck = 0; |
|
2232 } |
|
2233 |
|
2234 |
|
2235 |
|
2236 /* emergency shutdown but close the database cleanly */ |
|
2237 void |
|
2238 bad_stop(const char *pat, ...) |
|
2239 { |
|
2240 va_list args; |
|
2241 |
|
2242 if (stopint) |
|
2243 return; |
|
2244 |
|
2245 va_start(args, pat); |
|
2246 dcc_verror_msg(pat, args); |
|
2247 va_end(args); |
|
2248 |
|
2249 stopint = -1; |
|
2250 next_flods_ck = 0; |
|
2251 } |
|
2252 |
|
2253 |
|
2254 |
|
2255 static void |
|
2256 stop_children(void) |
|
2257 { |
|
2258 if (resolve_hosts_pid > 0) |
|
2259 kill(resolve_hosts_pid, SIGKILL); |
|
2260 |
|
2261 if (dbclean_pid > 0) |
|
2262 kill(dbclean_pid, SIGKILL); |
|
2263 } |