1 | /***************************************
2 | $Revision: 1.19 $
3 |
4 | Example code: A thread.
5 |
6 | Status: NOT REVUED, NOT TESTED
7 |
8 | Authors: Chris Ottrey
9 | Joao Damas
10 |
11 | +html+ <DL COMPACT>
12 | +html+ <DT>Online References:
13 | +html+ <DD><UL>
14 | +html+ </UL>
15 | +html+ </DL>
16 |
17 | ******************/ /******************
18 | Modification History:
19 | ottrey (02/03/1999) Created.
20 | ottrey (08/03/1999) Modified.
21 | ottrey (17/06/1999) Stripped down.
22 | joao (22/06/1999) Redid thread startup
23 | ******************/ /******************
24 | Copyright (c) 1999 RIPE NCC
25 |
26 | All Rights Reserved
27 |
28 | Permission to use, copy, modify, and distribute this software and its
29 | documentation for any purpose and without fee is hereby granted,
30 | provided that the above copyright notice appear in all copies and that
31 | both that copyright notice and this permission notice appear in
32 | supporting documentation, and that the name of the author not be
33 | used in advertising or publicity pertaining to distribution of the
34 | software without specific, written prior permission.
35 |
36 | THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
37 | ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS; IN NO EVENT SHALL
38 | AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
39 | DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
40 | AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
41 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
42 | ***************************************/
43 | #include <pthread.h> /* Posix thread library */
44 | #include <stdio.h>
45 | #include <strings.h>
46 |
47 | #include "thread.h"
48 | #include "socket.h"
49 | #include "protocol_whois.h"
50 | #include "protocol_config.h"
51 | #include "protocol_mirror.h"
52 | #include "constants.h"
53 | #include "server.h"
54 | #include "memwrap.h"
55 |
56 | /*+ String sizes +*/
57 | #define STR_S 63
58 | #define STR_M 255
59 | #define STR_L 1023
60 | #define STR_XL 4095
61 | #define STR_XXL 16383
62 |
63 | /*+ Mutex lock. Used for synchronizing changes. +*/
64 | pthread_mutex_t Whois_thread_count_lock;
65 | pthread_mutex_t Config_thread_count_lock;
66 | pthread_mutex_t Mirror_thread_count_lock;
67 |
68 | /*+ The number of threads. +*/
69 | int Whois_thread_count;
70 | int Config_thread_count;
71 | int Mirror_thread_count;
72 |
73 | typedef struct th_args {
74 | void *function;
75 | int sock;
76 | } th_args;
77 |
78 | /* Some static declarations */
79 |
80 | /* This is a watchdog function/thread */
81 | /* It is started by TH_watchdog function */
82 | static void do_watchdog(void *arg);
83 |
84 |
85 | /* Logging results */
86 | static void log_print(const char *arg) {
87 | FILE *logf;
88 |
89 | if (CO_get_thread_logging() == 1) {
90 | if (strcmp(CO_get_thread_logfile(), "stdout") == 0) {
91 | printf(arg);
92 | }
93 | else {
94 | logf = fopen(CO_get_thread_logfile(), "a");
95 | fprintf(logf, arg);
96 | fclose(logf);
97 | }
98 | }
99 |
100 | } /* log_print() */
101 |
102 | /* TH_acquire_read_lock() */
103 | /*++++++++++++++++++++++++++++++++++++++
104 |
105 | Aquire a readers lock.
106 |
107 | rw_lock_t *prw_lock Readers writers lock.
108 |
109 | Reference: "Multithreaded Programming Techniques - Prasad p.192"
110 | More:
111 | +html+ <PRE>
112 | Author:
113 | ottrey
114 | +html+ </PRE>
115 | ++++++++++++++++++++++++++++++++++++++*/
116 | void TH_acquire_read_lock(rw_lock_t *prw_lock) {
117 | pthread_mutex_lock(&prw_lock->rw_mutex);
118 |
119 | while (prw_lock->rw_count < 0) {
120 | pthread_cond_wait(&prw_lock->rw_cond, &prw_lock->rw_mutex);
121 | }
122 |
123 | ++prw_lock->rw_count;
124 | pthread_mutex_unlock(&prw_lock->rw_mutex);
125 |
126 | } /* TH_acquire_read_lock() */
127 |
128 | /* TH_release_read_lock() */
129 | /*++++++++++++++++++++++++++++++++++++++
130 |
131 | Release a readers lock.
132 |
133 | rw_lock_t *prw_lock Readers writers lock.
134 |
135 | Reference: "Multithreaded Programming Techniques - Prasad p.192"
136 | More:
137 | +html+ <PRE>
138 | Author:
139 | ottrey
140 | +html+ </PRE>
141 | ++++++++++++++++++++++++++++++++++++++*/
142 | void TH_release_read_lock(rw_lock_t *prw_lock) {
143 | pthread_mutex_lock(&prw_lock->rw_mutex);
144 |
145 | --prw_lock->rw_count;
146 |
147 | if (!prw_lock->rw_count) {
148 | pthread_cond_signal(&prw_lock->rw_cond);
149 | }
150 |
151 | pthread_mutex_unlock(&prw_lock->rw_mutex);
152 |
153 | } /* TH_release_read_lock() */
154 |
155 | /* TH_acquire_write_lock() */
156 | /*++++++++++++++++++++++++++++++++++++++
157 |
158 | Aquire a writers lock.
159 |
160 | rw_lock_t *prw_lock Readers writers lock.
161 |
162 | Reference: "Multithreaded Programming Techniques - Prasad p.192"
163 | More:
164 | +html+ <PRE>
165 | Author:
166 | ottrey
167 | +html+ </PRE>
168 | ++++++++++++++++++++++++++++++++++++++*/
169 | void TH_acquire_write_lock(rw_lock_t *prw_lock) {
170 | pthread_mutex_lock(&prw_lock->rw_mutex);
171 |
172 | while (prw_lock->rw_count != 0) {
173 | pthread_cond_wait(&prw_lock->rw_cond, &prw_lock->rw_mutex);
174 | }
175 |
176 | prw_lock->rw_count = -1;
177 | pthread_mutex_unlock(&prw_lock->rw_mutex);
178 |
179 | } /* TH_acquire_write_lock() */
180 |
181 | /* TH_release_write_lock() */
182 | /*++++++++++++++++++++++++++++++++++++++
183 |
184 | Release a writers lock.
185 |
186 | rw_lock_t *prw_lock Readers writers lock.
187 |
188 | Reference: "Multithreaded Programming Techniques - Prasad p.192"
189 | More:
190 | +html+ <PRE>
191 | Author:
192 | ottrey
193 | +html+ </PRE>
194 | ++++++++++++++++++++++++++++++++++++++*/
195 | void TH_release_write_lock(rw_lock_t *prw_lock) {
196 | pthread_mutex_lock(&prw_lock->rw_mutex);
197 | prw_lock->rw_count = 0;
198 | pthread_mutex_unlock(&prw_lock->rw_mutex);
199 | pthread_cond_broadcast(&prw_lock->rw_cond);
200 |
201 | } /* TH_release_write_lock() */
202 |
203 | /* TH_init_read_write_lock() */
204 | /*++++++++++++++++++++++++++++++++++++++
205 |
206 | Initialize a readers/writers lock.
207 |
208 | rw_lock_t *prw_lock Readers writers lock.
209 |
210 | Side effect: the lock is set to open(?)
211 |
212 | Reference: "Multithreaded Programming Techniques - Prasad p.192"
213 | More:
214 | +html+ <PRE>
215 | Author:
216 | ottrey
217 | +html+ </PRE>
218 | ++++++++++++++++++++++++++++++++++++++*/
219 | void TH_init_read_write_lock(rw_lock_t *prw_lock) {
220 | pthread_mutex_init(&prw_lock->rw_mutex, NULL);
221 | pthread_cond_init(&prw_lock->rw_cond, NULL);
222 | prw_lock->rw_count = 0;
223 |
224 | } /* TH_init_read_write_lock() */
225 |
226 | int TH_get_id(void) {
227 |
228 | return (int)pthread_self();
229 |
230 | } /* TH_get_id() */
231 |
232 | /* TH_to_string() */
233 | char *TH_to_string(void) {
234 | char *thread_info;
235 | char tmp[STR_L];
236 | char thread_info_buffer[STR_XL];
237 |
238 | strcpy(thread_info_buffer, "Thread = { ");
239 |
240 | sprintf(tmp, "[pthread_self] = \"%d\" ", pthread_self());
241 | strcat(thread_info_buffer, tmp);
242 |
243 | /*
244 | thread_name = (char *)pthread_getspecific(Name);
245 |
246 | if (thread_name == NULL ) {
247 | sprintf(tmp, "[Name] = \"%s\" ", "didn't work!");
248 | }
249 | else {
250 | sprintf(tmp, "[Name] = \"%s\" ", thread_name);
251 | }
252 | strcat(thread_info_buffer, tmp);
253 | */
254 |
255 | strcat(thread_info_buffer, "}");
256 |
257 | dieif( wr_malloc((void **)&thread_info,
258 | strlen(thread_info_buffer)+1) != UT_OK);
259 |
260 | strcpy(thread_info, thread_info_buffer);
261 |
262 | return thread_info;
263 | } /* TH_to_string() */
264 |
265 | /* TH_do_whois() */
266 | /*++++++++++++++++++++++++++++++++++++++
267 |
268 | Handle whois connections.
269 |
270 | void *arg The socket to connect to. (It has to be passed in this way for this thread routine.)
271 |
272 | More:
273 | +html+ <PRE>
274 | Author:
275 | joao
276 | +html+ </PRE>
277 | ++++++++++++++++++++++++++++++++++++++*/
278 | void TH_do_whois(void *arg) {
279 | int sock = (int)arg;
280 |
281 | ER_dbg_va(FAC_TH, ASP_TH_NEW,
282 | "Whois: Child thread [%d]: Socket number = %d",
283 | pthread_self(), sock);
284 |
285 | /* Use a mutex to update the global whois thread counter. */
286 | pthread_mutex_lock(&Whois_thread_count_lock);
287 | Whois_thread_count++;
288 | ER_dbg_va(FAC_TH, ASP_TH_NEW,
289 | "Whois_thread_count++=%d", Whois_thread_count);
290 |
291 | pthread_mutex_unlock(&Whois_thread_count_lock);
292 |
293 | PW_interact(sock);
294 |
295 | /* Use a mutex to update the global whois thread counter. */
296 | pthread_mutex_lock(&Whois_thread_count_lock);
297 | Whois_thread_count--;
298 | ER_dbg_va(FAC_TH, ASP_TH_NEW,
299 | "Whois_thread_count--=%d", Whois_thread_count);
300 | pthread_mutex_unlock(&Whois_thread_count_lock);
301 |
302 | pthread_exit((void *)0);
303 |
304 | } /* TH_do_whois() */
305 |
306 | /* TH_do_mirror() */
307 | /*++++++++++++++++++++++++++++++++++++++
308 |
309 | Handle NRTM connections.
310 |
311 | void *arg The socket to connect to. (It has to be passed in this way for this thread routine.)
312 |
313 | More:
314 | +html+ <PRE>
315 | Author:
316 | joao
317 | +html+ </PRE>
318 | ++++++++++++++++++++++++++++++++++++++*/
319 | void TH_do_mirror(void *arg) {
320 | int sock = (int)arg;
321 | char print_buf[STR_M];
322 |
323 | sprintf(print_buf, "NRTM: Child thread [%d]: Socket number = %d\n", pthread_self(), sock); log_print(print_buf); strcpy(print_buf, "");
324 |
325 | /* Use a mutex to update the global mirror thread counter. */
326 | pthread_mutex_lock(&Mirror_thread_count_lock);
327 | Mirror_thread_count++;
328 | sprintf(print_buf, "Mirror_thread_count++=%d\n", Mirror_thread_count); log_print(print_buf); strcpy(print_buf, "");
329 | pthread_mutex_unlock(&Mirror_thread_count_lock);
330 |
331 | PM_interact(sock);
332 |
333 | /* Use a mutex to update the global mirror thread counter. */
334 | pthread_mutex_lock(&Mirror_thread_count_lock);
335 | Mirror_thread_count--;
336 | sprintf(print_buf, "Mirror_thread_count--=%d\n", Mirror_thread_count); log_print(print_buf); strcpy(print_buf, "");
337 | pthread_mutex_unlock(&Mirror_thread_count_lock);
338 |
339 | pthread_exit((void *)0);
340 |
341 | } /* TH_do_mirror() */
342 |
343 | /* TH_do_config() */
344 | /*++++++++++++++++++++++++++++++++++++++
345 |
346 | Handle config connections.
347 |
348 | void *arg The socket to connect to. (It has to be passed in this way for this
349 | thread routine.)
350 |
351 | More:
352 | +html+ <PRE>
353 | Author:
354 | joao
355 | +html+ </PRE>
356 | ++++++++++++++++++++++++++++++++++++++*/
357 | void TH_do_config(void *arg) {
358 | int sock = (int)arg;
359 | char print_buf[STR_M];
360 |
361 | sprintf(print_buf, "Config: Child thread [%d]: Socket number = %d\n", pthread_self(), sock); log_print(print_buf); strcpy(print_buf, "");
362 |
363 | /*
364 | printf("Hi there, there is nothing to configure yet\nBye..... :-)\n");
365 | fflush(NULL);
366 |
367 | SK_close(sock);
368 | */
369 | PC_interact(sock);
370 |
371 | pthread_exit((void *)0);
372 |
373 | } /* TH_do_config() */
374 |
375 | /* TH_hdl_signal() */
376 | /*++++++++++++++++++++++++++++++++++++++
377 |
378 | Handle signals.
379 |
380 | Changes the flags:
381 | do_nrtm
382 | do_update
383 | do_whoisd
384 |
385 | More:
386 | +html+ <PRE>
387 | Author:
388 | andrei
389 | +html+ </PRE>
390 | ++++++++++++++++++++++++++++++++++++++*/
391 | void TH_hdl_signal() {
392 | char print_buf[STR_M];
393 | sigset_t sset;
394 | int sigReceived;
395 | int do_update;
396 |
397 | sigemptyset(&sset);
398 | sigaddset(&sset, SIGTERM);
399 | sigaddset(&sset, SIGINT);
400 | /* This is a bit confusing, but is needed */
401 | /* For more information on signal handling in */
402 | /* threads see for example "Multithreading Programming */
403 | /* Techniques" by Shashi Prasad, ISBN 0-07-912250-7, pp. 94-101 */
404 | pthread_sigmask(SIG_BLOCK, &sset, NULL);
405 | /* fprintf(stderr, "Signal handler installed\n");*/
406 |
407 | for(;;)
408 | {
409 | sigwait(&sset, &sigReceived);
410 | sprintf(print_buf, "Signal received [%d]\n", sigReceived);
411 | log_print(print_buf); strcpy(print_buf, "");
412 | /* fprintf(stderr, "Signal received [%d]\n", sigReceived); */
413 | switch (sigReceived)
414 | {
415 | case SIGINT:
416 | /* SIGINT stops all servers */
417 | SV_shutdown();
418 | pthread_exit((void *)0);
419 | break;
420 |
421 | case SIGTERM:
422 | /* SIGTERM will switch the updates on and off */
423 | do_update=CO_get_do_update();
424 | if(do_update)do_update=0; else do_update=1;
425 | sprintf(print_buf, "%d", do_update);
426 | CO_set_const("UD.do_update", print_buf);
427 | if(do_update)
428 | sprintf(print_buf, "Starting updates\n");
429 | else
430 | sprintf(print_buf, "Stopping updates\n");
431 | log_print(print_buf); strcpy(print_buf, "");
432 | /* fprintf(stderr, "Stopping updates (SIGTERM received)\n"); */
433 | break;
434 | }
435 | }
436 | } /* TH_hdl_signal() */
437 |
438 |
439 |
440 |
441 | /* main_thread() */
442 | /*++++++++++++++++++++++++++++++++++++++
443 |
444 | Waits for an incoming connection on the and spawns a new thread to handle it.
445 |
446 | void *arg Pointer to a struct containing the socket to talk to the client and
447 | the function to call depending on the incoming connection.
448 |
449 | More:
450 | +html+ <PRE>
451 | Author:
452 | ottrey
453 | joao
454 | andrei (do_server)
455 | +html+ </PRE>
456 | ++++++++++++++++++++++++++++++++++++++*/
457 | static void *main_thread(void *arg) {
458 | th_args *args = (th_args *)arg;
459 | pthread_t tid;
460 | pthread_attr_t attr;
461 | int connected_socket;
462 | int do_server;
463 |
464 | while(do_server=CO_get_do_server()) {
465 |
466 | connected_socket = SK_accept_connection(args->sock);
467 | if(connected_socket==-1) break;
468 |
469 |
470 | ER_dbg_va(FAC_TH, ASP_TH_NEW, "Starting a new thread");
471 |
472 | /* Start a new thread. */
473 |
474 | pthread_attr_init(&attr); /* initialize attr with default attributes */
475 | pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
476 | pthread_create(&tid, &attr, (void *(*)(void *))(args->function), (void *)connected_socket);
477 | }
478 |
479 | ER_dbg_va(FAC_TH, ASP_TH_NEW, "Exiting from the main thread");
480 |
481 | } /* main_thread() */
482 |
483 | /* TH_run() */
484 | /*++++++++++++++++++++++++++++++++++++++
485 |
486 | This is the routine that creates the main threads.
487 |
488 | int sock The socket to connect to.
489 | void * do_function The function to call for each type of service
490 |
491 | More:
492 | +html+ <PRE>
493 | Author:
494 | ottrey
495 | joao
496 | +html+ </PRE>
497 | ++++++++++++++++++++++++++++++++++++++*/
498 | void TH_run(int sock, void *do_function(void *)) {
499 | th_args *args;
500 | pthread_t tid;
501 | pthread_attr_t attr;
502 |
503 | dieif( wr_calloc((void **)&args,1,sizeof(th_args)) != UT_OK);
504 |
505 | args->function=(void *)do_function;
506 | args->sock=sock;
507 |
508 | /* pthread_mutex_init(&Whois_thread_count_lock,NULL); */
509 |
510 |
511 | /* Start a new thread. */
512 | pthread_attr_init(&attr); /* initialize attr with default attributes */
513 | pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
514 | pthread_create(&tid, &attr, main_thread, (void *)args);
515 |
516 | } /* TH_run() */
517 |
518 |
519 | /*++++++++++++++++++++++++++++++++++++++
520 |
521 | This is the routine that creates 1 main thread.
522 |
523 | int sock The socket to listen to.
524 | void * do_function The function to call for each type of service
525 |
526 | More:
527 | +html+ <PRE>
528 | Author:
529 | ottrey
530 | joao
531 | andrei
532 | +html+ </PRE>
533 | ++++++++++++++++++++++++++++++++++++++*/
534 | void TH_run1(int sock, void *do_function(void *) ) {
535 | pthread_t tid;
536 | pthread_attr_t attr;
537 |
538 | /* Start a new thread. */
539 | pthread_attr_init(&attr); /* initialize attr with default attributes */
540 | pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
541 | pthread_create(&tid, &attr, (void *(*)(void *))do_function, (void *)sock);
542 |
543 | } /* TH_run() */
544 |
545 |
546 | void TH_run2(void *function(void *)) {
547 | pthread_t tid;
548 | pthread_attr_t attr;
549 |
550 | /* Start a new thread. */
551 | pthread_attr_init(&attr); /* initialize attr with default attributes */
552 | pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
553 | pthread_create(&tid, &attr, (void *(*)(void *))function, (void *)0);
554 |
555 | } /* TH_run2() */
556 |
557 |
558 |
559 | /*++++++++++++++++++++++++++++++++++++++
560 |
561 | This is the routine that creates a watchdog thread.
562 |
563 | The watchdog will cancel (pthread_cancel()) the calling thread in case the
564 | socket is closed by the client (its read-half is closed). The calling
565 | thread should make necessaruy preparations when calling the watchdog:
566 |
567 | - the socket should be connected
568 | - cancellation points and cleanup routines should be defined
569 |
570 | In case the connection is closed by the calling thread itself, the
571 | watchdog just exits and no action against the calling thread is performed.
572 |
573 | wd_args - a pointer to wd_args_t structure containing
574 | data about socket and thread ID
575 |
576 | More:
577 | +html+ <PRE>
578 | Author:
579 | ottrey
580 | joao
581 | andrei
582 | +html+ </PRE>
583 | ++++++++++++++++++++++++++++++++++++++*/
584 |
585 | void TH_watchdog(wd_args_t *wd_args) {
586 | pthread_t tid;
587 | pthread_attr_t attr;
588 |
589 | /* Start a new thread. */
590 | pthread_attr_init(&attr); /* initialize attr with default attributes */
591 | pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
592 | pthread_create(&tid, &attr, (void *(*)(void *))do_watchdog, (void *)wd_args);
593 |
594 | }
595 |
596 |
597 | /*++++++++++++++++++++++++++++++++++++++
598 |
599 | The watchdog thread itself
600 |
601 | The watchdog thread makes select() on the connected socket waiting until it
602 | becomes readable. If this happens as a result of some input, it'll simply
603 | dump it. Otherwise, this indicates that the client has closed the
604 | connection. In this case watchdog will cancel (pthread_cancel()) the whois
605 | thread (which in its turn will kill (mysql_kill()) mysql thread as part of
606 | its cleanup routine).
607 |
608 | More:
609 | +html+ <PRE>
610 | Author:
611 | andrei
612 | +html+ </PRE>
613 | ++++++++++++++++++++++++++++++++++++++*/
614 | static void do_watchdog(void *arg) {
615 | wd_args_t *wd_args = (wd_args_t *)arg;
616 | int socket;
617 | pthread_t tid;
618 | int nready;
619 | int n;
620 | fd_set rset;
621 | char buff[STR_S];
622 |
623 | socket = wd_args->connected_socket;
624 | tid = wd_args->tid;
625 |
626 |
627 | FD_ZERO(&rset);
628 | FD_SET(socket, &rset);
629 |
630 | while ((nready=select(socket+1, &rset, NULL, NULL, NULL))!=-1) {
631 |
632 | /* There was some input or client half of connection was closed */
633 | /* Check for the latter */
634 | if (( n=read(socket, buff, sizeof(buff))) == 0) {
635 | /* Connection was closed by client */
636 | /* Now send a cancellation request to the whois thread. */
637 | /* mysql thread will be terminated by thread cleanup routine */
638 |
639 | /* The only possible error is ESRCH, so we do not care about */
640 | pthread_cancel(tid);
641 |
642 | /* Exit the watchdog thread, passing NULL as we don't expect pthread_join() */
643 | pthread_exit(NULL);
644 | }
645 |
646 | /* Otherwise dump input and continue */
647 | }
648 |
649 | /* the only reason that we are here is that the socket has been */
650 | /* closed by the whois thread and not valid. Just exit the watchdog, */
651 | /* passing NULL as we don't expect pthread_join() */
652 | pthread_exit(NULL);
653 |
654 | }