1 | /*************************************** 2 | $Revision: 1.50 $ 3 | 4 | Example code: A server for a client to connect to. 5 | 6 | Status: NOT REVUED, NOT TESTED 7 | 8 | Authors: Chris Ottrey, Joao Damas, 9 | heavy rewrite by Andrei Robachevsky, Marek Bukowy 10 | 11 | +html+ <DL COMPACT> 12 | +html+ <DT>Online References: 13 | +html+ <DD><UL> 14 | +html+ <LI>Based on <A HREF="http://iii.ripe.net/dbase/coding/new.code/progress/ottrey/code/java/src/DBServer.java">DBServer.java</A> 15 | +html+ </UL> 16 | +html+ </DL> 17 | 18 | ******************/ /****************** 19 | Modification History: 20 | ottrey (02/03/1999) Created. 21 | ottrey (08/03/1999) Modified. 22 | joao (22/06/1999) Modified. 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 | 44 | #include <ctype.h> 45 | 46 | #include <sys/types.h> 47 | #include <sys/stat.h> 48 | #include <sys/wait.h> 49 | #include <sys/socket.h> 50 | #include <netinet/in.h> 51 | 52 | #include "thread.h" 53 | #include "rxroutines.h" 54 | #include "sk.h" 55 | /* 56 | #include "objects.h" 57 | */ 58 | #include "constants.h" 59 | 60 | #include "ca_configFns.h" 61 | #include "ca_dictionary.h" 62 | #include "ca_macros.h" 63 | #include "ca_srcAttribs.h" 64 | 65 | #include "mysql_driver.h" 66 | #include "access_control.h" 67 | #include "ud.h" 68 | #include "server.h" 69 | 70 | #include "rp.h" 71 | #include "memwrap.h" 72 | 73 | #include "ta.h" 74 | 75 | #include "protocol_whois.h" 76 | #include "protocol_mirror.h" 77 | #include "protocol_config.h" 78 | 79 | /*+ String sizes +*/ 80 | #define STR_S 63 81 | #define STR_M 255 82 | #define STR_L 1023 83 | #define STR_XL 4095 84 | #define STR_XXL 16383 85 | 86 | /* Storage for descriptors of the read side of the pipe */ 87 | int sv_lockfd[MAX_LOCKS]; 88 | 89 | /* Listening sockets */ 90 | int SV_whois_sock; 91 | int SV_config_sock; 92 | int SV_mirror_sock; 93 | 94 | /* each updatable source has its own update thread and its own socket */ 95 | #define MAX_SOURCES 100 96 | int SV_update_sock[MAX_SOURCES]; 97 | 98 | 99 | /*+ Server starting time +*/ 100 | time_t SV_starttime; 101 | 102 | /* Logging results */ 103 | static void log_print(const char *arg) { 104 | 105 | printf(arg); 106 | 107 | } /* log_print() */ 108 | 109 | 110 | /* counters - by marek */ 111 | typedef struct { 112 | int count; 113 | pthread_mutex_t lock; /*+ Mutex lock.Used for synchronizing changes.+*/ 114 | pthread_cond_t cond; /*+ condition variable +*/ 115 | } svr_counter_t; 116 | 117 | 118 | /* structure passed to every running server */ 119 | typedef struct { 120 | void (*function)(int); 121 | int conn_sock; 122 | int accept_sock; 123 | int limit; /* limit for the number of concurrent connections */ 124 | svr_counter_t *counter; /* number of active clients */ 125 | char *name; 126 | } svr_args; 127 | 128 | 129 | /*++++++++++++++++++++++++++++++++++++++ 130 | function to operate on the counter structures - 131 | takes the increment (can be negative), changes the value 132 | using the locks and everything, 133 | 134 | int 135 | counter_add returns the new value. 136 | 137 | svr_counter_t *cst counter structure 138 | 139 | int incval increment value (can be negative) 140 | 141 | Author: 142 | marek 143 | ++++++++++++++++++++++++++++++++++++++*/ 144 | static 145 | int 146 | counter_add( svr_counter_t *cst, int incval ) 147 | { 148 | int newval; 149 | 150 | /* add under mutex */ 151 | pthread_mutex_lock( &(cst->lock) ); 152 | cst->count += incval; 153 | newval = cst->count; 154 | pthread_mutex_unlock(&(cst->lock) ); 155 | 156 | /* now - signal the change of value to the waiting thread */ 157 | pthread_cond_signal( &(cst->cond) ); 158 | 159 | return newval; 160 | } 161 | 162 | 163 | /*++++++++++++++++++++++++++++++++++++++ 164 | 165 | int 166 | counter_state returns the current value of a counter 167 | 168 | svr_counter_t *cst counter 169 | 170 | Author: 171 | marek 172 | 173 | ++++++++++++++++++++++++++++++++++++++*/ 174 | static 175 | int 176 | counter_state( svr_counter_t *cst ) 177 | { 178 | return counter_add( cst, 0 ); 179 | } 180 | 181 | 182 | /*++++++++++++++++++++++++++++++++++++++ 183 | waits until the counter is in the range [0-limit]. 184 | unless the limit is 0, in which case the check is disabled. 185 | 186 | int counter_wait returns the new value of the counter after wait 187 | 188 | svr_counter_t *cst counter 189 | 190 | int limit limit / range, or 0 to disable the check 191 | 192 | Author: 193 | marek 194 | ++++++++++++++++++++++++++++++++++++++*/ 195 | static 196 | int counter_wait(svr_counter_t *cst, int limit ) 197 | { 198 | int newval; 199 | 200 | pthread_mutex_lock( &(cst->lock) ); 201 | 202 | if( limit != 0 ) { 203 | while( cst->count >= limit ) { 204 | pthread_cond_wait( &(cst->cond), &(cst->lock)); 205 | } 206 | } 207 | 208 | newval = cst->count; 209 | pthread_mutex_unlock(&(cst->lock) ); 210 | 211 | return newval; 212 | } 213 | 214 | /*++++++++++++++++++++++++++++++++++++++ 215 | 216 | Loading the radix tree. Started as a separate thread. 217 | 218 | Author: 219 | marek 220 | ++++++++++++++++++++++++++++++++++++++*/ 221 | void radix_init(void){ 222 | int i; 223 | ca_dbSource_t *source_hdl; 224 | 225 | wr_log_set(0); 226 | /* this needs to be done in two loops, 227 | because the trees must be created asap (first loop) 228 | and then locked until they are populated in the second loop 229 | */ 230 | 231 | for(i=0; (source_hdl = ca_get_SourceHandleByPosition(i))!=NULL ; i++){ 232 | dieif( RP_init_trees( source_hdl ) != RP_OK ); 233 | } 234 | 235 | for(i=0; (source_hdl = ca_get_SourceHandleByPosition(i))!=NULL ; i++){ 236 | dieif( RP_sql_load_reg( source_hdl ) != RP_OK ); 237 | } 238 | 239 | wr_log_set(0); /* switch on/off the memory leak detector */ 240 | /* pthread_mutex_unlock( &radix_initializing_lock ); */ 241 | 242 | pthread_exit((void *)0); 243 | } 244 | 245 | 246 | /************************************************************ 247 | * int SV_sleep() * 248 | * * 249 | * sleeps till shutdown request comes * 250 | * but at most <delay> seconds * 251 | * * 252 | * Returns: * 253 | * 1 - timeout * 254 | * 0 - shutdown * 255 | * * 256 | ************************************************************/ 257 | 258 | int SV_sleep(int delay) 259 | { 260 | int do_server; 261 | int elapsed_time=0; 262 | 263 | while((do_server=CO_get_do_server()) && (elapsed_time<delay)) 264 | { 265 | sleep(TIME_SLICE); 266 | elapsed_time+=TIME_SLICE; 267 | } 268 | if(elapsed_time<delay)return(1); else return(0); 269 | } 270 | 271 | /*++++++++++++++++++++++++++++++++++++++ 272 | 273 | Handle signals. 274 | 275 | Changes the flags: 276 | do_nrtm 277 | do_update 278 | do_whoisd 279 | 280 | More: 281 | +html+ <PRE> 282 | Author: 283 | andrei 284 | +html+ </PRE> 285 | ++++++++++++++++++++++++++++++++++++++*/ 286 | void *SV_signal_thread() { 287 | char print_buf[STR_M]; 288 | sigset_t sset; 289 | int sigReceived; 290 | int do_update; 291 | 292 | sigemptyset(&sset); 293 | sigaddset(&sset, SIGTERM); 294 | sigaddset(&sset, SIGINT); 295 | sigaddset(&sset, SIGUSR1); 296 | /* This is a bit confusing, but is needed */ 297 | /* For more information on signal handling in */ 298 | /* threads see for example "Multithreading Programming */ 299 | /* Techniques" by Shashi Prasad, ISBN 0-07-912250-7, pp. 94-101 */ 300 | pthread_sigmask(SIG_BLOCK, &sset, NULL); 301 | /* fprintf(stderr, "Signal handler installed\n");*/ 302 | 303 | for(;;) 304 | { 305 | sigwait(&sset, &sigReceived); 306 | sprintf(print_buf, "Signal received [%d]\n", sigReceived); 307 | log_print(print_buf); strcpy(print_buf, ""); 308 | /* fprintf(stderr, "Signal received [%d]\n", sigReceived); */ 309 | switch (sigReceived) 310 | { 311 | case SIGINT: 312 | /* SIGINT stops all servers */ 313 | SV_shutdown(); 314 | pthread_exit((void *)0); 315 | break; 316 | 317 | case SIGTERM: 318 | /* SIGTERM will switch the updates on and off */ 319 | do_update=CO_get_do_update(); 320 | if(do_update)do_update=0; else do_update=1; 321 | sprintf(print_buf, "%d", do_update); 322 | CO_set_const("UD.do_update", print_buf); 323 | if(do_update) 324 | sprintf(print_buf, "Starting updates\n"); 325 | else 326 | sprintf(print_buf, "Stopping updates\n"); 327 | log_print(print_buf); strcpy(print_buf, ""); 328 | /* fprintf(stderr, "Stopping updates (SIGTERM received)\n"); */ 329 | break; 330 | } 331 | } 332 | } /* SV_signal_thread() */ 333 | 334 | 335 | /* SV_do_child() */ 336 | /*++++++++++++++++++++++++++++++++++++++ 337 | 338 | Handle whois/config/mirror connections. Takes a pointer to the 339 | service description structure, containing a connected socket, limit 340 | of active threads, pointer to the counter of them. Does not stop to 341 | obey the limits, assumes this to be checked and assumes that it is 342 | already counted. Decrements the counter on exit. 343 | 344 | Precondition: the counter must be incremented before this function is called. 345 | 346 | void *SV_do_child Actually, does not return anything useful. Just NULL. 347 | 348 | void *varg service description structure. 349 | 350 | Author: 351 | marek 352 | ++++++++++++++++++++++++++++++++++++++*/ 353 | void *SV_do_child(void *varg) 354 | { 355 | svr_args *args = (svr_args *) varg; 356 | int sock = args->conn_sock; 357 | int curclients; 358 | 359 | ER_dbg_va(FAC_TH, ASP_TH_NEW, 360 | ": Child thread [%d]: Socket number = %d", 361 | args->name, pthread_self(), sock); 362 | 363 | curclients = counter_state( args->counter ); /* already added */ 364 | ER_dbg_va(FAC_TH, ASP_TH_NEW, 365 | "%s threads++ = %d", args->name, curclients); 366 | 367 | TA_add(sock, args->name); 368 | 369 | args->function(sock); 370 | 371 | /* TA_delete must come first - otherwise the server would crash 372 | when trying to report address of a closed socket */ 373 | TA_delete(); 374 | close(sock); 375 | 376 | /* update the global thread counter. */ 377 | curclients = counter_add( args->counter, -1); 378 | ER_dbg_va(FAC_TH, ASP_TH_NEW, 379 | "%s threads-- = %d", args->name, curclients); 380 | 381 | free(args); 382 | 383 | return NULL; /* exit the thread */ 384 | } /* SV_do_child */ 385 | 386 | 387 | /* main_loop() */ 388 | /*++++++++++++++++++++++++++++++++++++++ 389 | 390 | Waits for an incoming connection on the and spawns a new thread to 391 | handle it. Takes a pointer to the service description structure 392 | containing the number of the listening socket, limit of active 393 | threads, pointer to the counter of them, and the function to call 394 | with a connected socket. Increments the counter before starting 395 | a client thread to run SV_do_child(). 396 | 397 | void *arg pointer to the service description structure. 398 | 399 | More: 400 | +html+ <PRE> 401 | Author: 402 | ottrey 403 | joao 404 | andrei (do_server) 405 | marek (rewritten/simplified/added limits) 406 | +html+ </PRE> 407 | ++++++++++++++++++++++++++++++++++++++*/ 408 | static void *main_loop(void *arg) { 409 | svr_args *argset = (svr_args *)arg; 410 | svr_args *argcopy; 411 | char loopname[32]; 412 | int children; 413 | char chnum[16]; 414 | 415 | snprintf(loopname, 32, "s-%s", argset->name); 416 | 417 | TA_add(0, loopname); 418 | 419 | while( CO_get_do_server() != 0 ) { 420 | /* check the number of clients, do not proceed until it's below limit */ 421 | children = counter_wait( argset->counter, argset->limit ); 422 | snprintf(chnum, 16, "%d", children); 423 | TA_setactivity(chnum); /* display the current number of children */ 424 | 425 | /* wait for new connections */ 426 | argset->conn_sock = SK_accept_connection(argset->accept_sock); 427 | if(argset->conn_sock == -1) { 428 | break; 429 | } 430 | 431 | ER_dbg_va(FAC_TH, ASP_TH_NEW, "%s: starting a new child thread", 432 | loopname); 433 | TA_increment(); 434 | /* incrementing argset->counter here - to avoid race condition and 435 | ensure a _more_correct_ value of current clients also for unlimited 436 | or infrequent connections. Does not really matter otherwise. 437 | 438 | NOTE: this architecture implies that higher values can be 439 | displayed for infrequent threads, because there's no way 440 | to change it when threads are exiting while this thread is 441 | blocked in call to accept(). If this call was in the child thread, 442 | the number would be an underestimation instead. I prefer over-e. 443 | */ 444 | counter_add( argset->counter, 1); 445 | 446 | /* Start a new thread. will decrement counter when exiting */ 447 | 448 | /* now. There's a race condition - argset must be copied in SV_do_child 449 | and can be reused here only afterwards. To avoid it, we make a copy 450 | and expect SV_do_child to free it after use. 451 | Caveat: the counter remains where it was, we just copy the pointer. 452 | */ 453 | argcopy = malloc( sizeof(svr_args) ); 454 | memcpy( argcopy, argset, sizeof(svr_args) ); 455 | TH_create( SV_do_child, (void *)argcopy ); 456 | } 457 | 458 | TA_delete(); 459 | ER_dbg_va(FAC_TH, ASP_TH_NEW, "Exiting from the main loop"); 460 | 461 | pthread_exit((void *)0); 462 | return NULL; /* stupid compilers. */ 463 | } /* main_loop() */ 464 | 465 | /* SV_concurrent_server() */ 466 | /*++++++++++++++++++++++++++++++++++++++ 467 | 468 | This is the routine that creates the main threads. 469 | 470 | int sock The socket to connect to. 471 | 472 | int limit Limit of active clients (0 == no limit) 473 | 474 | void * do_function The function to call for each type of service 475 | 476 | More: 477 | +html+ <PRE> 478 | Author: 479 | ottrey 480 | joao 481 | marek 482 | +html+ </PRE> 483 | ++++++++++++++++++++++++++++++++++++++*/ 484 | static 485 | void SV_concurrent_server(int sock, int limit, char *name, 486 | void do_function(int)) 487 | { 488 | svr_args *args; 489 | 490 | dieif( wr_calloc((void **)&args, 1, sizeof(svr_args)) != UT_OK); 491 | 492 | args->accept_sock=sock; 493 | args->limit=limit; 494 | args->name=name; 495 | args->function=do_function; 496 | 497 | dieif( wr_calloc((void **)&(args->counter),1,sizeof(svr_counter_t)) != UT_OK); 498 | pthread_mutex_init( &(args->counter->lock), NULL ); 499 | pthread_cond_init( &(args->counter->cond), NULL ); 500 | args->counter->count = 0; 501 | 502 | 503 | /* Start a new thread. */ 504 | 505 | TH_create(main_loop, (void *)args); 506 | 507 | } /* SV_concurrent_server() */ 508 | 509 | /* SV_start() */ 510 | /*++++++++++++++++++++++++++++++++++++++ 511 | 512 | Start the server. 513 | 514 | More: 515 | +html+ <PRE> 516 | Authors: 517 | ottrey 518 | joao 519 | +html+ </PRE> 520 | +html+ Starts up the server. 521 | +html+ <OL> 522 | +html+ <LI> Create sockets on the necessary ports (whois, config and mirror) 523 | +html+ <LI> Start new threads for each service. 524 | +html+ </OL> 525 | +html+ <A HREF=".DBrc">.properties</A> 526 | 527 | ++++++++++++++++++++++++++++++++++++++*/ 528 | void SV_start() { 529 | int whois_port = -1; 530 | int config_port = -1; 531 | int mirror_port = -1; 532 | int update_port = -1; 533 | int update_mode = 0; 534 | int fdes[2]; 535 | struct timeval tval; 536 | ca_dbSource_t *source_hdl; 537 | char *source_name; 538 | int source; 539 | char *db_host, *db_name, *db_user, *db_passwd; 540 | int db_port; 541 | SQ_connection_t *db_connection; 542 | 543 | /* Store the starting time */ 544 | gettimeofday(&tval, NULL); 545 | SV_starttime = tval.tv_sec;/* seconds since Jan. 1, 1970 */ 546 | 547 | /* Create interrupt pipe */ 548 | /* Writing to this pipe will cause sleeping threads */ 549 | /* to wake up */ 550 | fprintf(stderr, "Creating an interrupt pipe\n"); 551 | if(pipe(fdes)==-1) { 552 | printf("Cannot open interrupt pipe\n"); 553 | exit(-1); 554 | } 555 | /* Save the pipe descriptors in sv_lock array */ 556 | sv_lockfd[WLOCK_SHTDOWN]=fdes[0]; 557 | sv_lockfd[LOCK_SHTDOWN]=fdes[1]; 558 | 559 | /* Initialise modules */ 560 | SK_init(); 561 | 562 | /* Initialise the access control list. */ 563 | AC_build(); 564 | AC_acc_load(); 565 | /* explicitly start the decay thread */ 566 | TH_create((void *(*)(void *))AC_decay, NULL); 567 | 568 | 569 | 570 | /* Get port information for each service */ 571 | whois_port = ca_get_svwhois_port; 572 | ER_dbg_va(FAC_SV, ASP_SV_PORT, "whois port is %d", whois_port); 573 | 574 | config_port = ca_get_svconfig_port; 575 | ER_dbg_va(FAC_SV, ASP_SV_PORT, "config port is %d", config_port); 576 | 577 | mirror_port = ca_get_svmirror_port; 578 | ER_dbg_va(FAC_SV, ASP_SV_PORT, "mirror port is %d", mirror_port); 579 | 580 | 581 | /* 6. Create a socket on the necessary ports/addresses and bind to them. */ 582 | /* whois socket */ 583 | SV_whois_sock = SK_getsock(SOCK_STREAM, whois_port, 128, INADDR_ANY); 584 | /* Currently binds to INADDR_ANY. Will need to get specific address */ 585 | /* SV_whois_sock = SK_getsock(SOCK_STREAM,whois_port,whois_addr); */ 586 | /* config interface socket */ 587 | SV_config_sock = SK_getsock(SOCK_STREAM, config_port, 5, INADDR_ANY); 588 | /* nrt socket */ 589 | SV_mirror_sock = SK_getsock(SOCK_STREAM,mirror_port, 5, INADDR_ANY); 590 | 591 | /* Check every Database and create sockets */ 592 | /* we need first to create and bind all of them */ 593 | /* so that in case of failure we do not start any */ 594 | /* update thread */ 595 | fprintf(stderr, "Check the DB\n"); 596 | for(source=0; (source_hdl = ca_get_SourceHandleByPosition(source))!=NULL ; source++){ 597 | /* check for crash and recover if needed */ 598 | /* make a connection to a database */ 599 | db_host = ca_get_srcdbmachine(source_hdl); 600 | db_port = ca_get_srcdbport(source_hdl); 601 | db_name = ca_get_srcdbname(source_hdl); 602 | db_user = ca_get_srcdbuser(source_hdl); 603 | db_passwd = ca_get_srcdbpassword(source_hdl); 604 | db_connection=SQ_get_connection(db_host, db_port, db_name, db_user, db_passwd); 605 | /* now check TR record */ 606 | TR_recover(db_connection); 607 | /* free resources */ 608 | SQ_close_connection(db_connection); 609 | free(db_host); 610 | free(db_name); 611 | free(db_user); 612 | free(db_passwd); 613 | 614 | update_mode = ca_get_srcmode(source_hdl); 615 | if(IS_UPDATE(update_mode)) { 616 | /* update_port = SK_atoport(CO_get_update_port(), "tcp"); */ 617 | update_port = ca_get_srcupdateport(source_hdl); 618 | printf("XXX htons(update_port)=%d\n", update_port); 619 | /* XXX ask AMRM to change the name of the function */ 620 | 621 | SV_update_sock[source] = SK_getsock(SOCK_STREAM, update_port, 5, INADDR_ANY); 622 | } 623 | else SV_update_sock[source] = 0; 624 | } 625 | SV_update_sock[source+1]=-1; /* end of socket array */ 626 | 627 | /* Initialise the radix tree (separate thread[s]) 628 | already can allow socket connections, because the trees will 629 | be created locked, and will be unlocked when loaded */ 630 | 631 | /* pthread_mutex_lock( &radix_initializing_lock ); */ 632 | TH_create((void *(*)(void *))radix_init, NULL); 633 | /* pthread_mutex_lock( &radix_initializing_lock ); */ 634 | 635 | 636 | /* Now.... accept() calls block until they get a connection 637 | so to listen on more than one port we need more 638 | than one thread */ 639 | 640 | /* Create master thread for whois threads */ 641 | SV_concurrent_server(SV_whois_sock, 64, "whois", PW_interact); 642 | 643 | /* Create master thread for config threads */ 644 | SV_concurrent_server(SV_config_sock, 0, "config", PC_interact); 645 | /* Create master thread for mirror threads */ 646 | SV_concurrent_server(SV_mirror_sock, 0, "mirror", PM_interact); 647 | 648 | /* Walk through the sources and */ 649 | /* run update thread for every source with CANUPD == 'y' */ 650 | 651 | for(source=0; (source_hdl = ca_get_SourceHandleByPosition(source))!=NULL ; source++){ 652 | update_mode = ca_get_srcmode(source_hdl); 653 | source_name= ca_get_srcname(source_hdl); 654 | 655 | if(IS_UPDATE(update_mode)) { 656 | /* run RIPupdate thread */ 657 | fprintf(stderr,"Source [%s] Mode UPDATE\n", source_name); 658 | TH_create((void *(*)(void *))UD_do_updates, (void *)source); 659 | } 660 | else if(IS_NRTM_CLNT(update_mode)){ 661 | /* start NRTM client */ 662 | fprintf(stderr,"Source [%s] Mode NRTM\n", source_name); 663 | TH_create((void *(*)(void *))UD_do_nrtm, (void *)source); 664 | } 665 | else fprintf(stderr,"Source [%s] Mode STATIC\n", source_name); 666 | free(source_name); /* because ca_* functions return copies */ 667 | } 668 | 669 | pthread_exit(NULL); 670 | 671 | } /* SV_start() */ 672 | 673 | /* SV_shutdown() */ 674 | /*++++++++++++++++++++++++++++++++++++++ 675 | 676 | Shutdown the server. 677 | 678 | More: 679 | +html+ <PRE> 680 | Authors: 681 | andrei 682 | +html+ </PRE> 683 | +html+ Stops the server. 684 | +html+ <OL> 685 | +html+ <LI> Close listening sockets (whois, config, mirror and updates) 686 | +html+ <LI> Stop all threads by triggering do_server variable. 687 | +html+ </OL> 688 | +html+ <A HREF=".DBrc">.properties</A> 689 | 690 | ++++++++++++++++++++++++++++++++++++++*/ 691 | void SV_shutdown() { 692 | char print_buf[STR_M]; 693 | int source; 694 | 695 | sprintf(print_buf, "%d", 0); 696 | /* Stop updates */ 697 | CO_set_const("UD.do_update", print_buf); 698 | /* Stop all servers */ 699 | CO_set_const("SV.do_server", print_buf); 700 | sprintf(print_buf, "Stopping all servers\n"); 701 | fprintf(stderr, print_buf); 702 | /*log_print(print_buf); */ 703 | strcpy(print_buf, ""); 704 | 705 | /* Wake up all sleeping threads */ 706 | fprintf(stderr, "Going to wake sleeping threads up\n"); 707 | write(sv_lockfd[WLOCK_SHTDOWN], " ", 1); 708 | 709 | /* CLose all listening sockets, so accept call exits */ 710 | close(SV_whois_sock); 711 | close(SV_config_sock); 712 | close(SV_mirror_sock); 713 | for (source=0; SV_update_sock[source]!=-1; source++) 714 | if(SV_update_sock[source]!=0)close(SV_update_sock[source]); 715 | 716 | 717 | } /* SV_shutdown() */