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() */