comparison thrlib/clnt_threaded.c @ 0:c7f6b056b673

First import of vendor version
author Peter Gervai <grin@grin.hu>
date Tue, 10 Mar 2009 13:49:58 +0100
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:c7f6b056b673
1 /* Distributed Checksum Clearinghouse
2 *
3 * threaded version of client locking
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.73 $Revision$
40 */
41
42 #include "dcc_ck.h"
43 #include "dcc_clnt.h"
44 #ifdef HAVE_PTHREAD_H
45 #include <pthread.h>
46 #else
47 #include <sys/pthread.h>
48 #endif
49 #include <signal.h>
50
51 u_char grey_on;
52 u_char grey_query_only;
53
54 DCC_WF cmn_wf, cmn_tmp_wf;
55
56
57 /* many POSIX thread implementations have unexpected side effects on
58 * ordinary system calls, so don't use the threaded version unless
59 * necessary */
60
61 /* protect the links among contexts and the miscellaneous global
62 * variables in the DCC client library */
63 static pthread_mutex_t ctxts_mutex;
64 #ifdef DCC_DEBUG_CLNT_LOCK
65 static pthread_t ctxts_owner;
66 #endif
67
68 /* make syslog() thread-safe */
69 static pthread_mutex_t syslog_mutex;
70 static u_char syslog_threaded;
71
72 #ifdef DCC_DEBUG_HEAP
73 static pthread_mutex_t malloc_mutex;
74 static u_char malloc_threaded;
75 #endif
76
77 /* make gethostbyname() thread-safe */
78 static pthread_mutex_t host_mutex;
79
80 static pthread_t clnt_resolve_tid;
81 static pthread_cond_t clnt_resolve_cond;
82 static u_char clnt_resolve_stopping;
83
84 /* The threaded DNS blacklist support uses fork() to create helper processes
85 * to wait for the typical single-threaded DNS resolver library. */
86 static pthread_mutex_t helper_mutex;
87
88 /* create user logs in a burst while holding a lock
89 * this reduces the total number of file descriptors needed
90 * at a cost of stopping everything while copying from the main
91 * log file to the per-user log files */
92 pthread_mutex_t user_log_mutex;
93 pthread_t user_log_owner;
94
95
96 /* this is used only in the threaded DCC clients */
97 void
98 clnt_sigs_off(sigset_t *sigsold)
99 {
100 sigset_t sigsnew;
101 int error;
102
103 sigemptyset(&sigsnew);
104 sigaddset(&sigsnew, SIGHUP);
105 sigaddset(&sigsnew, SIGINT);
106 sigaddset(&sigsnew, SIGTERM);
107 sigaddset(&sigsnew, SIGALRM);
108 error = pthread_sigmask(SIG_BLOCK, &sigsnew, sigsold);
109 if (error)
110 dcc_logbad(EX_SOFTWARE, "pthread_sigmask(): %s",
111 ERROR_STR1(error));
112 }
113
114
115
116 void
117 dcc_ctxts_lock(void)
118 {
119 int error;
120
121 #ifdef DCC_DEBUG_CLNT_LOCK
122 if (ctxts_owner == pthread_self())
123 dcc_logbad(EX_SOFTWARE, "already have ctxts lock");
124 #endif
125
126 error = pthread_mutex_lock(&ctxts_mutex);
127 if (error)
128 dcc_logbad(EX_SOFTWARE, "pthread_mutex_lock(ctxts): %s",
129 ERROR_STR1(error));
130 #ifdef DCC_DEBUG_CLNT_LOCK
131 ctxts_owner = pthread_self();
132 #endif
133 }
134
135
136
137 void
138 dcc_ctxts_unlock(void)
139 {
140 int error;
141
142 #ifdef DCC_DEBUG_CLNT_LOCK
143 ctxts_owner = 0;
144 #endif
145 error = pthread_mutex_unlock(&ctxts_mutex);
146 if (error)
147 dcc_logbad(EX_SOFTWARE, "pthread_mutex_unlock(ctxts): %s",
148 ERROR_STR1(error));
149 }
150
151
152
153 #ifdef DCC_DEBUG_CLNT_LOCK
154 void
155 assert_ctxts_locked(void)
156 {
157 if (ctxts_owner != pthread_self())
158 dcc_logbad(EX_SOFTWARE, "don't have ctxts lock");
159 }
160
161
162
163 void
164 assert_ctxts_unlocked(void)
165 {
166 if (ctxts_owner == pthread_self())
167 dcc_logbad(EX_SOFTWARE, "have ctxts lock");
168 }
169 #endif
170
171
172
173 void
174 dcc_syslog_lock(void)
175 {
176 int error;
177
178 if (!syslog_threaded)
179 return;
180 error = pthread_mutex_lock(&syslog_mutex);
181 if (error)
182 dcc_logbad(EX_SOFTWARE, "pthread_mutex_lock(syslog): %s",
183 ERROR_STR1(error));
184 }
185
186
187
188 void
189 dcc_syslog_unlock(void)
190 {
191 int error;
192
193 if (!syslog_threaded)
194 return;
195 error = pthread_mutex_unlock(&syslog_mutex);
196 if (error)
197 dcc_logbad(EX_SOFTWARE, "pthread_mutex_unlock(syslog): %s",
198 ERROR_STR1(error));
199 }
200
201
202
203 /* gethostbyname() etc. are usually not reentrant */
204 u_char dcc_host_locked = 1;
205
206 /* do not worry about locking gethostbyname() until the locks have
207 * been initialized */
208 static u_char dcc_host_threaded = 0;
209
210 /* This function is mentioned in dccifd/dccif-test/dccif-test.c
211 * and so cannot change lightly. */
212 void
213 dcc_host_lock(void)
214 {
215 int error;
216
217 if (!dcc_host_threaded)
218 return;
219
220 error = pthread_mutex_lock(&host_mutex);
221 if (error)
222 dcc_logbad(EX_SOFTWARE, "pthread_mutex_lock(host): %s",
223 ERROR_STR1(error));
224 dcc_host_locked = 1;
225 }
226
227
228
229 /* This function is mentioned in dccifd/dccif-test/dccif-test.c
230 * and so cannot change lightly. */
231 void
232 dcc_host_unlock(void)
233 {
234 int error;
235
236 if (!dcc_host_threaded)
237 return;
238
239 dcc_host_locked = 0;
240 error = pthread_mutex_unlock(&host_mutex);
241 if (error)
242 dcc_logbad(EX_SOFTWARE, "pthread_mutex_unlock(host): %s",
243 ERROR_STR1(error));
244 }
245
246
247
248 #ifdef DCC_DEBUG_HEAP
249 void
250 dcc_malloc_lock(void)
251 {
252 int error;
253
254 if (!malloc_threaded) /* no locking until locks created */
255 return;
256 error = pthread_mutex_lock(&malloc_mutex);
257 if (error)
258 dcc_logbad(EX_SOFTWARE, "pthread_mutex_lock(malloc): %s",
259 ERROR_STR1(error));
260 }
261
262
263 void
264 dcc_malloc_unlock(void)
265 {
266 int error;
267
268 if (!malloc_threaded) /* no locking until locks created */
269 return;
270 error = pthread_mutex_unlock(&malloc_mutex);
271 if (error)
272 dcc_logbad(EX_SOFTWARE, "pthread_mutex_unlock(malloc): %s",
273 ERROR_STR1(error));
274 }
275 #endif /* DCC_DEBUG_HEAP */
276
277
278
279 #ifdef HAVE_LOCALTIME_R
280 /* make localtime() thread safe */
281 static pthread_mutex_t localtime_mutex;
282 static u_char localtime_threaded;
283
284 void
285 dcc_localtime_lock(void)
286 {
287 int error;
288
289 if (!localtime_threaded)
290 return;
291 error = pthread_mutex_lock(&localtime_mutex);
292 if (error)
293 dcc_logbad(EX_SOFTWARE, "pthread_mutex_lock(localtime): %s",
294 ERROR_STR1(error));
295 }
296
297
298
299 void
300 dcc_localtime_unlock(void)
301 {
302 int error;
303
304 if (!localtime_threaded)
305 return;
306 error = pthread_mutex_unlock(&localtime_mutex);
307 if (error)
308 dcc_logbad(EX_SOFTWARE, "pthread_mutex_unlock(localtime): %s",
309 ERROR_STR1(error));
310 }
311 #endif /* HAVE_LOCALTIME_R */
312
313
314
315 const char *main_white_nm;
316 const char *mapfile_nm = DCC_MAP_NM_DEF;
317
318 /* resolve things */
319 DCC_CLNT_CTXT *
320 resolve_sub(DCC_CLNT_CTXT *ctxt, /* 0=allocate and initialize */
321 DCC_WF *wf, DCC_WF *tmp_wf)
322 {
323 DCC_EMSG emsg;
324
325 if (!ctxt) {
326 dcc_wf_init(wf, 0);
327 if (main_white_nm)
328 dcc_new_white_nm(emsg, wf, main_white_nm);
329
330 emsg[0] = '\0';
331 if (!dcc_map_info(emsg, mapfile_nm, -1))
332 dcc_logbad(EX_USAGE, "%s", emsg);
333
334 ctxt = dcc_alloc_ctxt();
335 }
336
337 if (wf->ascii_nm[0] != '\0') {
338 if (clnt_resolve_stopping)
339 return ctxt;
340 dcc_ctxts_unlock();
341 switch (dcc_rdy_white(emsg, wf, tmp_wf)) {
342 case DCC_WHITE_OK:
343 case DCC_WHITE_NOFILE:
344 case DCC_WHITE_SILENT:
345 break;
346 case DCC_WHITE_CONTINUE:
347 case DCC_WHITE_COMPLAIN:
348 dcc_error_msg("%s", emsg);
349 break;
350 }
351 dcc_ctxts_lock();
352
353 /* Tell the other threads that the hash table
354 * in the disk file has changed.
355 * This kludge lets this thread use its own
356 * wf structure without hogging the lock
357 * on cmn_wf. */
358 if (wf->closed) {
359 wf->closed = 0;
360 cmn_wf.need_reopen = 1;
361 }
362 }
363
364 return ctxt;
365 }
366
367
368
369 static void * NRATTRIB
370 clnt_resolve_thread(void *arg UATTRIB)
371 {
372 DCC_WF wf, tmp_wf;
373 DCC_CLNT_CTXT *ctxt;
374 DCC_EMSG emsg;
375 int error;
376
377 /* let the thread in charge of signals deal with them */
378 clnt_sigs_off(0);
379
380 ctxt = 0;
381 dcc_ctxts_lock();
382 for (;;) {
383 if (clnt_resolve_stopping) {
384 dcc_ctxts_unlock();
385 pthread_exit(0);
386 }
387
388 ctxt = resolve_sub(ctxt, &wf, &tmp_wf);
389
390 if (clnt_resolve_stopping) {
391 dcc_ctxts_unlock();
392 pthread_exit(0);
393 }
394 emsg[0] = '\0';
395 if (!dcc_clnt_rdy(emsg, ctxt, DCC_CLNT_FG_NO_FAIL))
396 dcc_error_msg("%s", emsg);
397 else if (!dcc_info_unlock(emsg))
398 dcc_logbad(dcc_ex_code, "%s", emsg);
399
400 if (grey_on) {
401 if (clnt_resolve_stopping) {
402 dcc_ctxts_unlock();
403 pthread_exit(0);
404 }
405 emsg[0] = '\0';
406 if (!dcc_clnt_rdy(emsg, ctxt, (DCC_CLNT_FG_GREY
407 | DCC_CLNT_FG_NO_FAIL)))
408 dcc_error_msg("%s", emsg);
409 else if (!dcc_info_unlock(emsg))
410 dcc_logbad(dcc_ex_code, "%s", emsg);
411 }
412
413 #ifdef DCC_DEBUG_CLNT_LOCK
414 ctxts_owner = 0;
415 #endif
416 error = pthread_cond_wait(&clnt_resolve_cond, &ctxts_mutex);
417 if (error != 0)
418 dcc_logbad(EX_SOFTWARE,
419 "pthread_cond_wait(resolve): %s",
420 ERROR_STR1(error));
421 #ifdef DCC_DEBUG_CLNT_LOCK
422 ctxts_owner = pthread_self();
423 #endif
424 }
425 }
426
427
428
429 u_char /* 1=awoke the resolver thread */
430 dcc_clnt_wake_resolve(void)
431 {
432 int error;
433
434 /* we cannot awaken ourself or awaken the thread before it starts */
435 if (clnt_resolve_tid == 0
436 || pthread_equal(pthread_self(), clnt_resolve_tid))
437 return 0;
438
439 error = pthread_cond_signal(&clnt_resolve_cond);
440 if (error != 0)
441 dcc_logbad(EX_SOFTWARE, "pthread_cond_signal(resolve): %s",
442 ERROR_STR1(error));
443 return 1;
444 }
445
446
447
448 void
449 dcc_clnt_stop_resolve(void)
450 {
451 if (clnt_resolve_stopping)
452 return;
453 clnt_resolve_stopping = 1;
454 if (pthread_equal(pthread_self(), clnt_resolve_tid))
455 return;
456 pthread_cond_signal(&clnt_resolve_cond);
457 }
458
459
460
461 /* some pthreads implementations (e.g. AIX) don't like static
462 * initializations */
463 static void
464 dcc_mutex_init(void *mutex, const char *nm)
465 {
466 int error = pthread_mutex_init(mutex, 0);
467 if (error)
468 dcc_logbad(EX_SOFTWARE, "pthread_mutex_init(%s): %s",
469 nm, ERROR_STR1(error));
470 }
471
472
473
474 static pthread_mutex_t work_mutex;
475 static pthread_t cwf_owner;
476
477 void
478 lock_work(void)
479 {
480 int result = pthread_mutex_lock(&work_mutex);
481 if (result)
482 dcc_logbad(EX_SOFTWARE, "pthread_mutex_lock(work_free): %s",
483 ERROR_STR1(result));
484 }
485
486
487
488 void
489 unlock_work(void)
490 {
491 int result = pthread_mutex_unlock(&work_mutex);
492 if (result)
493 dcc_logbad(EX_SOFTWARE, "pthread_mutex_unlock(work_free): %s",
494 ERROR_STR1(result));
495 }
496
497
498
499 /* lock all CWF structures as well as the cmn_wf structure */
500 static pthread_mutex_t cwf_mutex;
501
502 void
503 lock_wf(void)
504 {
505 int error = pthread_mutex_lock(&cwf_mutex);
506 if (error)
507 dcc_logbad(EX_SOFTWARE, "pthread_mutex_lock(cwf): %s",
508 ERROR_STR1(error));
509 cwf_owner = pthread_self();
510 }
511
512
513
514 void
515 unlock_wf(void)
516 {
517 int error;
518 cwf_owner = 0;
519 error = pthread_mutex_unlock(&cwf_mutex);
520 if (error)
521 dcc_logbad(EX_SOFTWARE, "pthread_mutex_unlock(cwf): %s",
522 ERROR_STR1(error));
523 }
524
525
526
527 #ifdef DCC_DEBUG_CLNT_LOCK
528 void
529 assert_cwf_locked(void)
530 {
531 if (cwf_owner != pthread_self())
532 dcc_logbad(EX_SOFTWARE, "don't have cwf lock");
533 }
534 #endif
535
536
537
538 void
539 dcc_clnt_thread_init(void)
540 {
541 DCC_CLNT_CTXT *ctxt;
542 int error;
543
544 /* Some pthreads implementations (e.g. AIX) don't like static
545 * POSIX thread initializations */
546
547 dcc_mutex_init(&ctxts_mutex, "ctxt");
548 dcc_mutex_init(&syslog_mutex, "syslog");
549 syslog_threaded = 1;
550 #ifdef DCC_DEBUG_HEAP
551 dcc_mutex_init(&malloc_mutex, "heap");
552 malloc_threaded = 1;
553 #endif
554 #ifndef HAVE_LOCALTIME_R
555 dcc_mutex_init(&localtime_mutex, "localtime");
556 localtime_threaded = 1;
557 #endif
558 dcc_mutex_init(&host_mutex, "host");
559 dcc_host_threaded = 1;
560 dcc_host_locked = 0;
561
562 dcc_mutex_init(&user_log_mutex, "user_log");
563
564 dcc_mutex_init(&work_mutex, "wf_mutex");
565 dcc_mutex_init(&cwf_mutex, "cwf_mutex");
566
567 /* prevent race between resolver thread and other threads to
568 * initialize things by doing it before starting the thread */
569 lock_wf();
570 dcc_ctxts_lock();
571 ctxt = resolve_sub(0, &cmn_wf, &cmn_tmp_wf);
572 dcc_rel_ctxt(ctxt);
573 dcc_ctxts_unlock();
574 unlock_wf();
575
576 error = pthread_cond_init(&clnt_resolve_cond, 0);
577 if (error)
578 dcc_logbad(EX_SOFTWARE, "phtread_cond_init(resolve): %s",
579 ERROR_STR1(error));
580 error = pthread_create(&clnt_resolve_tid, 0, clnt_resolve_thread, 0);
581 if (error)
582 dcc_logbad(EX_SOFTWARE, "pthread_create(resolve): %s",
583 ERROR_STR1(error));
584 error = pthread_detach(clnt_resolve_tid);
585 if (error)
586 dcc_logbad(EX_SOFTWARE, "pthread_detach(resolve): %s",
587 ERROR_STR1(error));
588 }
589
590
591
592 /* protect DNS blacklist helper channels */
593 u_char
594 helper_lock_init(void)
595 {
596 dcc_mutex_init(&helper_mutex, "helper");
597 return 1;
598 }
599
600
601
602 void
603 helper_lock(void)
604 {
605 int error;
606
607 error = pthread_mutex_lock(&helper_mutex);
608 if (error)
609 dcc_logbad(EX_SOFTWARE,
610 "pthread_mutex_lock(helper counter): %s",
611 ERROR_STR1(error));
612 }
613
614
615
616 void
617 helper_unlock(void)
618 {
619 int error;
620
621 error = pthread_mutex_unlock(&helper_mutex);
622 if (error)
623 dcc_logbad(EX_SOFTWARE,
624 "pthread_mutex_unlock(helper counter): %s",
625 ERROR_STR1(error));
626 }