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