modules/sv/server.c
/* [<][>][^][v][top][bottom][index][help] */
FUNCTIONS
This source file includes following functions.
- log_print
- counter_add
- counter_state
- counter_wait
- radix_init
- SV_sleep
- SV_signal_thread
- SV_do_child
- main_loop
- SV_concurrent_server
- SV_start
- SV_shutdown
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) {
/* [<][>][^][v][top][bottom][index][help] */
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 )
/* [<][>][^][v][top][bottom][index][help] */
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 )
/* [<][>][^][v][top][bottom][index][help] */
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 )
/* [<][>][^][v][top][bottom][index][help] */
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){
/* [<][>][^][v][top][bottom][index][help] */
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)
/* [<][>][^][v][top][bottom][index][help] */
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() {
/* [<][>][^][v][top][bottom][index][help] */
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)
/* [<][>][^][v][top][bottom][index][help] */
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) {
/* [<][>][^][v][top][bottom][index][help] */
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,
/* [<][>][^][v][top][bottom][index][help] */
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() {
/* [<][>][^][v][top][bottom][index][help] */
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() {
/* [<][>][^][v][top][bottom][index][help] */
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() */