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  | }