]> asedeno.scripts.mit.edu Git - 1ts-debian.git/blob - zephyr/server/main.c
1472e1e1a5b1f882eb1fbd27baa421d1b39aa336
[1ts-debian.git] / zephyr / server / main.c
1 /* This file is part of the Project Athena Zephyr Notification System.
2  * It contains the main loop of the Zephyr server
3  *
4  *      Created by:     John T. Kohl
5  *
6  *      $Source: /afs/dev.mit.edu/source/repository/athena/lib/zephyr/server/main.c,v $
7  *      $Author$
8  *
9  *      Copyright (c) 1987,1988,1991 by the Massachusetts Institute of Technology.
10  *      For copying and distribution information, see the file
11  *      "mit-copyright.h". 
12  */
13
14 #include <zephyr/mit-copyright.h>
15 #include "zserver.h"
16 #include <sys/socket.h>
17 #include <sys/resource.h>
18
19 #ifndef lint
20 #ifndef SABER
21 static const char rcsid_main_c[] =
22     "$Id$";
23 #endif
24 #endif
25
26 /*
27  * Server loop for Zephyr.
28  */
29
30 /*
31   The Zephyr server maintains several linked lists of information.
32
33   There is an array of servers (otherservers) initialized and maintained
34   by server_s.c.
35
36   Each server descriptor contains a pointer to a linked list of hosts
37   which are ``owned'' by that server.  The first server is the ``limbo''
38   server which owns any host which was formerly owned by a dead server.
39
40   Each of these host list entries has an IP address and a pointer to a
41   linked list of clients on that host.
42
43   Each client has a sockaddr_in, a list of subscriptions, and possibly
44   a session key.
45
46   In addition, the class manager has copies of the pointers to the
47   clients which are registered with a particular class, the
48   not-yet-acknowledged list has copies of pointers to some clients,
49   and the hostm manager may have copies of pointers to some clients
50   (if the client has not acknowledged a packet after a given timeout).
51 */
52
53 #define EVER            (;;)            /* don't stop looping */
54
55 static int do_net_setup(void);
56 static int initialize(void);
57 static void usage(void);
58 static void do_reset(void);
59 static RETSIGTYPE bye(int);
60 static RETSIGTYPE dbug_on(int);
61 static RETSIGTYPE dbug_off(int);
62 static RETSIGTYPE sig_dump_db(int);
63 static RETSIGTYPE sig_dump_strings(int);
64 static RETSIGTYPE reset(int);
65 static RETSIGTYPE reap(int);
66 static void read_from_dump(char *dumpfile);
67 static void dump_db(void);
68 static void dump_strings(void);
69
70 #ifndef DEBUG
71 static void detach(void);
72 #endif
73
74 static short doreset = 0;               /* if it becomes 1, perform
75                                            reset functions */
76
77 int nfds;                               /* max file descriptor for select() */
78 int srv_socket;                         /* dgram socket for clients
79                                            and other servers */
80 int bdump_socket = -1;                  /* brain dump socket fd
81                                            (closed most of the time) */
82 fd_set interesting;                     /* the file descrips we are listening
83                                            to right now */
84 struct sockaddr_in srv_addr;            /* address of the socket */
85
86 Unacked *nacklist = NULL;               /* list of packets waiting for ack's */
87
88 unsigned short hm_port;                 /* host manager receiver port */
89 unsigned short hm_srv_port;             /* host manager server sending port */
90
91 char *programname;                      /* set to the basename of argv[0] */
92 char myname[MAXHOSTNAMELEN];            /* my host name */
93
94 char list_file[128];
95 #ifdef HAVE_KRB5
96 char keytab_file[128];
97 static char tkt5_file[256];
98 #endif
99 #ifdef HAVE_KRB4
100 char srvtab_file[128];
101 char my_realm[REALM_SZ];
102 static char tkt_file[128];
103 #endif
104 char acl_dir[128];
105 char subs_file[128];
106
107 int zdebug;
108 #ifdef DEBUG_MALLOC
109 int dump_malloc_stats = 0;
110 unsigned long m_size;
111 #endif
112 #ifdef DEBUG
113 int zalone;
114 #endif
115
116 struct timeval t_local;                 /* store current time for other uses */
117
118 static int dump_db_flag = 0;
119 static int dump_strings_flag = 0;
120
121 u_long npackets;                        /* number of packets processed */
122 time_t uptime;                          /* when we started operations */
123 static int nofork;
124 struct in_addr my_addr;
125 char *bdump_version = "1.2";
126
127 #ifdef HAVE_KRB5
128 int bdump_auth_proto = 5;
129 #else /* HAVE_KRB5 */
130 #ifdef HAVE_KRB4
131 int bdump_auth_proto = 4;
132 #else /* HAVE_KRB4 */
133 int bdump_auth_proto = 0;
134 #endif /* HAVE_KRB4 */
135 #endif /* HAVE_KRB5 */
136
137 #ifdef HAVE_KRB5
138 krb5_ccache Z_krb5_ccache;
139 krb5_keyblock *__Zephyr_keyblock;
140 #else
141 #ifdef HAVE_KRB4
142 C_Block __Zephyr_session;
143 #endif
144 #endif
145
146 int
147 main(int argc,
148      char **argv)
149 {
150     int nfound;                 /* #fildes ready on select */
151     fd_set readable;
152     struct timeval tv;
153     int init_from_dump = 0;
154     char *dumpfile;
155 #ifdef _POSIX_VERSION
156     struct sigaction action;
157 #endif
158     int optchar;                        /* option processing */
159     extern char *optarg;
160     extern int optind;
161
162     sprintf(list_file, "%s/zephyr/%s", SYSCONFDIR, SERVER_LIST_FILE);
163 #ifdef HAVE_KRB4
164     sprintf(srvtab_file, "%s/zephyr/%s", SYSCONFDIR, ZEPHYR_SRVTAB);
165     sprintf(tkt_file, "%s/zephyr/%s", SYSCONFDIR, ZEPHYR_TKFILE);
166 #endif
167 #ifdef HAVE_KRB5
168     sprintf(keytab_file, "%s/zephyr/%s", SYSCONFDIR, ZEPHYR_KEYTAB);
169     sprintf(tkt5_file, "FILE:%s/zephyr/%s", SYSCONFDIR, ZEPHYR_TK5FILE);
170 #endif
171     sprintf(acl_dir, "%s/zephyr/%s", SYSCONFDIR, ZEPHYR_ACL_DIR);
172     sprintf(subs_file, "%s/zephyr/%s", SYSCONFDIR, DEFAULT_SUBS_FILE);
173
174     /* set name */
175     programname = strrchr(argv[0],'/');
176     programname = (programname) ? programname + 1 : argv[0];
177
178     /* process arguments */
179     while ((optchar = getopt(argc, argv, "dsnv4f:k:")) != EOF) {
180         switch(optchar) {
181           case 'd':
182             zdebug = 1;
183             break;
184 #ifdef DEBUG
185           case 's':
186             zalone = 1;
187             break;
188 #endif
189           case 'n':
190             nofork = 1;
191             break;
192           case 'k':
193 #ifdef HAVE_KRB4
194             strncpy(my_realm, optarg, REALM_SZ);
195 #endif
196             break;
197           case 'v':
198             bdump_version = optarg;
199             break;
200           case 'f':
201             init_from_dump = 0;
202             dumpfile = optarg;
203             break;
204         case '4':
205             bdump_auth_proto = 4;
206             break;
207           case '?':
208           default:
209             usage();
210             /*NOTREACHED*/
211         }
212     }
213
214 #ifdef HAVE_KRB4
215     /* if there is no readable srvtab and we are not standalone, there
216        is no possible way we can succeed, so we exit */
217
218     if (access(srvtab_file, R_OK)
219 #ifdef DEBUG            
220         && !zalone
221 #endif /* DEBUG */
222         ) {
223         fprintf(stderr, "NO ZEPHYR SRVTAB (%s) available; exiting\n",
224                 srvtab_file);
225         exit(1);
226     }
227     /* Use local realm if not specified on command line. */
228     if (!*my_realm) {
229         if (krb_get_lrealm(my_realm, 1) != KSUCCESS) {
230             fputs("Couldn't get local Kerberos realm; exiting.\n", stderr);
231             exit(1);
232         }
233     }
234 #endif /* HAVE_KRB4 */
235
236 #ifndef DEBUG
237     if (!nofork)
238         detach();
239 #endif /* DEBUG */
240
241     /* open log */
242     OPENLOG(programname, LOG_PID, LOG_LOCAL6);
243
244 #if defined (DEBUG) && 0
245     if (zalone)
246         syslog(LOG_DEBUG, "standalone operation");
247 #endif
248 #if 0
249     if (zdebug)
250         syslog(LOG_DEBUG, "debugging on");
251 #endif
252
253     /* set up sockets & my_addr and myname, 
254        find other servers and set up server table, initialize queues
255        for retransmits, initialize error tables,
256        set up restricted classes */
257
258     /* Initialize t_local for other uses */
259     gettimeofday(&t_local, NULL);
260
261     if (initialize())
262         exit(1);
263
264     if (init_from_dump)
265         read_from_dump(dumpfile);
266
267     /* Seed random number set.  */
268     srandom(getpid() ^ time(0));
269
270     /* chdir to somewhere where a core dump will survive */
271     if (chdir(TEMP_DIRECTORY) != 0)
272         syslog(LOG_ERR, "chdir failed (%m) (execution continuing)");
273
274     FD_ZERO(&interesting);
275     FD_SET(srv_socket, &interesting);
276
277     nfds = srv_socket + 1;
278
279
280 #ifdef _POSIX_VERSION
281     action.sa_flags = 0;
282     sigemptyset(&action.sa_mask);
283
284     action.sa_handler = bye;
285     sigaction(SIGINT, &action, NULL);
286     sigaction(SIGTERM, &action, NULL);
287
288     action.sa_handler = dbug_on;
289     sigaction(SIGUSR1, &action, NULL);
290
291     action.sa_handler = dbug_off;
292     sigaction(SIGUSR2, &action, NULL);
293
294     action.sa_handler = reap;
295     sigaction(SIGCHLD, &action, NULL);
296
297     action.sa_handler = sig_dump_db;
298     sigaction(SIGFPE, &action, NULL);
299
300 #ifdef SIGEMT
301     action.sa_handler = sig_dump_strings;
302     sigaction(SIGEMT, &action, NULL);
303 #endif
304
305     action.sa_handler = reset;
306     sigaction(SIGHUP, &action, NULL);
307 #else /* !posix */
308     signal(SIGINT, bye);
309     signal(SIGTERM, bye);
310     signal(SIGUSR1, dbug_on);
311     signal(SIGUSR2, dbug_off);
312     signal(SIGCHLD, reap);
313     signal(SIGFPE, sig_dump_db);
314 #ifdef SIGEMT
315     signal(SIGEMT, sig_dump_strings);
316 #endif
317     signal(SIGHUP, reset);
318 #endif /* _POSIX_VERSION */
319
320     syslog(LOG_NOTICE, "Ready for action");
321
322     /* Reinitialize t_local now that initialization is done. */
323     gettimeofday(&t_local, NULL);
324     uptime = NOW;
325
326     realm_wakeup();
327 #ifdef DEBUG_MALLOC
328     malloc_inuse(&m_size);
329 #endif
330     for EVER {
331         if (doreset)
332             do_reset();
333
334         if (dump_db_flag)
335             dump_db();
336         if (dump_strings_flag)
337             dump_strings();
338
339         timer_process();
340
341         readable = interesting;
342         if (msgs_queued()) {
343             /* when there is input in the queue, we
344                artificially set up to pick up the input */
345             nfound = 1;
346             FD_ZERO(&readable);
347         } else  {
348             nfound = select(nfds, &readable, NULL, NULL, timer_timeout(&tv));
349         }
350
351         /* Initialize t_local for other uses */
352         gettimeofday(&t_local, (struct timezone *)0);
353                 
354         /* don't flame about EINTR, since a SIGUSR1 or SIGUSR2
355            can generate it by interrupting the select */
356         if (nfound < 0) {
357             if (errno != EINTR)
358                 syslog(LOG_WARNING, "select error: %m");
359 #ifdef DEBUG_MALLOC
360             if (dump_malloc_stats) {
361                 unsigned long foo,histid2;
362
363                 dump_malloc_stats = 0;
364                 foo = malloc_inuse(&histid2);
365                 printf("Total inuse: %d\n",foo);
366                 malloc_list(2,m_size,histid2);
367             }
368 #endif
369             continue;
370         }
371
372         if (nfound == 0) {
373             /* either we timed out or we were just
374                polling for input.  Either way we want to continue
375                the loop, and process the next timeout */
376             continue;
377         } else {
378             if (bdump_socket >= 0 && FD_ISSET(bdump_socket,&readable))
379                 bdump_send();
380             else if (msgs_queued() || FD_ISSET(srv_socket, &readable))
381                 handle_packet();
382             else
383                 syslog(LOG_ERR, "select weird?!?!");
384         }
385     }
386 }
387
388 /* Initialize net stuff.
389    Set up the server array.
390    Initialize the packet ack queues to be empty.
391    Initialize the error tables.
392    Restrict certain classes.
393    */
394
395 static int
396 initialize(void)
397 {
398     int zero = 0;
399     if (do_net_setup())
400         return(1);
401
402     server_init();
403
404 #ifdef HAVE_KRB4
405     krb_set_tkt_string(tkt_file);
406 #endif
407     realm_init();
408     
409     ZSetServerState(1);
410     ZInitialize();              /* set up the library */
411 #ifdef HAVE_KRB5
412     krb5_cc_resolve(Z_krb5_ctx, tkt5_file, &Z_krb5_ccache);
413 #ifdef HAVE_KRB5_CC_SET_DEFAULT_NAME
414     krb5_cc_set_default_name(Z_krb5_ctx, tkt5_file);
415 #else
416     {
417         /* Hack to make krb5_cc_default do something reasonable */
418         char *env=(char *)malloc(strlen(tkt5_file)+12);
419         if (!env) return(1);
420         sprintf(env, "KRB5CCNAME=%s", tkt5_file);
421         putenv(env);
422     }
423 #endif
424 #endif
425 #ifdef HAVE_KRB4
426     /* Override what Zinitialize set for ZGetRealm() */
427     if (*my_realm) 
428       strcpy(__Zephyr_realm, my_realm);
429 #endif
430     init_zsrv_err_tbl();        /* set up err table */
431
432     ZSetFD(srv_socket);         /* set up the socket as the input fildes */
433
434     /* set up default strings */
435
436     class_control = make_string(ZEPHYR_CTL_CLASS, 1);
437     class_admin = make_string(ZEPHYR_ADMIN_CLASS, 1);
438     class_hm = make_string(HM_CTL_CLASS, 1);
439     class_ulogin = make_string(LOGIN_CLASS, 1);
440     class_ulocate = make_string(LOCATE_CLASS, 1);
441     wildcard_instance = make_string(WILDCARD_INSTANCE, 1);
442     empty = make_string("", 0);
443
444     /* restrict certain classes */
445     access_init();
446     return 0;
447 }
448
449 /* 
450  * Set up the server and client sockets, and initialize my_addr and myname
451  */
452
453 static int
454 do_net_setup(void)
455 {
456     struct servent *sp;
457     struct hostent *hp;
458     char hostname[MAXHOSTNAMELEN+1];
459     int flags;
460
461     if (gethostname(hostname, MAXHOSTNAMELEN + 1)) {
462         syslog(LOG_ERR, "no hostname: %m");
463         return 1;
464     }
465     hp = gethostbyname(hostname);
466     if (!hp) {
467         syslog(LOG_ERR, "no gethostbyname repsonse");
468         strncpy(myname, hostname, MAXHOSTNAMELEN);
469         return 1;
470     }
471     strncpy(myname, hp->h_name, MAXHOSTNAMELEN);
472     memcpy(&my_addr, hp->h_addr, sizeof(hp->h_addr));
473
474     setservent(1);              /* keep file/connection open */
475
476     memset(&srv_addr, 0, sizeof(srv_addr));
477     srv_addr.sin_family = AF_INET;
478     sp = getservbyname(SERVER_SVCNAME, "udp");
479     srv_addr.sin_port = (sp) ? sp->s_port : SERVER_SVC_FALLBACK;
480
481     sp = getservbyname(HM_SVCNAME, "udp");
482     hm_port = (sp) ? sp->s_port : HM_SVC_FALLBACK;
483         
484     sp = getservbyname(HM_SRV_SVCNAME, "udp");
485     hm_srv_port = (sp) ? sp->s_port : HM_SRV_SVC_FALLBACK;
486         
487     srv_socket = socket(AF_INET, SOCK_DGRAM, 0);
488     if (srv_socket < 0) {
489         syslog(LOG_ERR, "client_sock failed: %m");
490         return 1;
491     } else {
492 #ifdef SO_BSDCOMPAT
493       int on = 1;
494
495       /* Prevent Linux from giving us socket errors we don't care about. */
496       setsockopt(srv_socket, SOL_SOCKET, SO_BSDCOMPAT, &on, sizeof(on));
497 #endif
498     }
499     if (bind(srv_socket, (struct sockaddr *) &srv_addr,
500              sizeof(srv_addr)) < 0) {
501         syslog(LOG_ERR, "client bind failed: %m");
502         return 1;
503     }
504
505     /* set not-blocking */
506 #ifdef _POSIX_VERSION
507     flags = fcntl(srv_socket, F_GETFL);
508     flags |= O_NONBLOCK;
509     fcntl(srv_socket, F_SETFL, flags);
510 #else
511     flags = 1;
512     ioctl(srv_socket, FIONBIO, &flags);
513 #endif
514
515     return 0;
516 }    
517
518
519 /*
520  * print out a usage message.
521  */
522
523 static void
524 usage(void)
525 {
526 #ifdef DEBUG
527         fprintf(stderr, "Usage: %s [-d] [-s] [-n] [-k realm] [-f dumpfile]\n",
528                 programname);
529 #else
530         fprintf(stderr, "Usage: %s [-d] [-n] [-k realm] [-f dumpfile]\n",
531                 programname);
532 #endif /* DEBUG */
533         exit(2);
534 }
535
536 int
537 packets_waiting(void)
538 {
539     fd_set readable, initial;
540     struct timeval tv;
541
542     if (msgs_queued())
543         return 1;
544     FD_ZERO(&initial);
545     FD_SET(srv_socket, &initial);
546     readable = initial;
547     tv.tv_sec = tv.tv_usec = 0;
548     return (select(srv_socket + 1, &readable, NULL, NULL, &tv) > 0);
549 }
550
551 static RETSIGTYPE
552 bye(int sig)
553 {
554     server_shutdown();          /* tell other servers */
555 #ifdef REALM_MGMT
556     realm_shutdown();           /* tell other realms */
557 #endif
558     hostm_shutdown();           /* tell our hosts */
559     kill_realm_pids();
560 #ifdef HAVE_KRB4
561     dest_tkt();
562 #endif
563     syslog(LOG_NOTICE, "goodbye (sig %d)", sig);
564     exit(0);
565 }
566
567 static RETSIGTYPE
568 dbug_on(int sig)
569 {
570     syslog(LOG_DEBUG, "debugging turned on");
571 #ifdef DEBUG_MALLOC
572     dump_malloc_stats = 1;
573 #endif
574     zdebug = 1;
575 }
576
577 static RETSIGTYPE
578 dbug_off(int sig)
579 {
580     syslog(LOG_DEBUG, "debugging turned off");
581 #ifdef DEBUG_MALLOC
582     malloc_inuse(&m_size);
583 #endif
584     zdebug = 0;
585 }
586
587 int fork_for_dump = 0;
588
589 static RETSIGTYPE
590 sig_dump_strings(int sig)
591 {
592     dump_strings_flag = 1;
593 }
594
595 static void dump_strings(void)
596 {
597     char filename[128];
598
599     FILE *fp;
600     int oerrno = errno;
601
602     sprintf(filename, "%szephyr.strings", TEMP_DIRECTORY);
603     fp = fopen (filename, "w");
604     if (!fp) {
605         syslog(LOG_ERR, "can't open strings dump file: %m");
606         errno = oerrno;
607         dump_strings_flag = 0;
608         return;
609     }
610     syslog(LOG_INFO, "dumping strings to disk");
611     print_string_table(fp);
612     if (fclose(fp) == EOF)
613         syslog(LOG_ERR, "error writing strings dump file");
614     else
615         syslog(LOG_INFO, "dump done");
616     oerrno = errno;
617     dump_strings_flag = 0;
618     return;
619 }
620
621 static RETSIGTYPE
622 sig_dump_db(int sig)
623 {
624     dump_db_flag = 1;
625 }
626
627 static void
628 dump_db(void)
629 {
630     /* dump the in-core database to human-readable form on disk */
631     FILE *fp;
632     int oerrno = errno;
633     int pid;
634     char filename[128];
635
636     pid = (fork_for_dump) ? fork() : -1;
637     if (pid > 0) {
638         dump_db_flag = 0;
639         return;
640     }
641     sprintf(filename, "%szephyr.db", TEMP_DIRECTORY);
642     fp = fopen(filename, "w");
643     if (!fp) {
644         syslog(LOG_ERR, "can't open dump database");
645         errno = oerrno;
646         dump_db_flag = 0;
647         return;
648     }
649     syslog(LOG_INFO, "dumping to disk");
650     server_dump_servers(fp);
651     uloc_dump_locs(fp);
652     client_dump_clients(fp);
653     triplet_dump_subs(fp);
654     realm_dump_realms(fp);
655     syslog(LOG_INFO, "dump done");
656     if (fclose(fp) == EOF)
657         syslog(LOG_ERR, "can't close dump db");
658     if (pid == 0)
659         exit(0);
660     errno = oerrno;
661     dump_db_flag = 0;
662 }
663
664 static RETSIGTYPE
665 reset(int sig)
666 {
667 #if 1
668     zdbug((LOG_DEBUG,"reset()"));
669 #endif
670     doreset = 1;
671 }
672
673 static RETSIGTYPE
674 reap(int sig)
675 {
676     int pid, i = 0;
677     int oerrno = errno;
678     ZRealm *rlm;
679 #ifdef _POSIX_VERSION
680     int waitb;
681 #else
682     union wait waitb;
683 #endif
684 #if 1
685     zdbug((LOG_DEBUG,"reap()"));
686 #endif
687 #ifdef _POSIX_VERSION
688     while ((pid = waitpid(-1, &waitb, WNOHANG)) == 0) 
689       { i++; if (i > 10) break; }
690 #else
691     while ((pid = wait3 (&waitb, WNOHANG, (struct rusage*) 0)) == 0) 
692       { i++; if (i > 10) break; }
693 #endif
694
695     errno = oerrno;
696  
697     if (pid) {
698       if (WIFSIGNALED(waitb) == 0) {
699         if (WIFEXITED(waitb) != 0) {
700           rlm = realm_get_realm_by_pid(pid);
701           if (rlm) {
702             rlm->child_pid = 0;
703             rlm->have_tkt = 1;
704           }
705         }
706       } else {
707         rlm = realm_get_realm_by_pid(pid);
708         if (rlm) {
709           rlm->child_pid = 0;
710         }
711       }
712     }
713 }
714
715 static void
716 do_reset(void)
717 {
718     int oerrno = errno;
719 #ifdef _POSIX_VERSION
720     sigset_t mask, omask;
721 #else
722     int omask;
723 #endif
724 #if 0
725     zdbug((LOG_DEBUG,"do_reset()"));
726 #endif
727 #ifdef _POSIX_VERSION
728     sigemptyset(&mask);
729     sigaddset(&mask, SIGHUP);
730     sigprocmask(SIG_BLOCK, &mask, &omask);
731 #else
732     omask = sigblock(sigmask(SIGHUP));
733 #endif
734
735     /* reset various things in the server's state */
736     subscr_reset();
737     server_reset();
738     access_reinit();
739     syslog(LOG_INFO, "restart completed");
740     doreset = 0;
741     errno = oerrno;
742 #ifdef _POSIX_VERSION
743     sigprocmask(SIG_SETMASK, &omask, (sigset_t *)0);
744 #else
745     sigsetmask(omask);
746 #endif
747 }
748
749 #ifndef DEBUG
750 /*
751  * detach from the terminal
752  */
753
754 static void
755 detach(void)
756 {
757     /* detach from terminal and fork. */
758     int i;
759     long size;
760
761 #ifdef _POSIX_VERSION
762     size = sysconf(_SC_OPEN_MAX);
763 #else
764     size = getdtablesize();
765 #endif
766     /* profiling seems to get confused by fork() */
767     i = fork ();
768     if (i) {
769         if (i < 0)
770             perror("fork");
771         exit(0);
772     }
773
774     for (i = 0; i < size; i++)
775         close(i);
776
777     i = open("/dev/tty", O_RDWR, 666);
778 #ifdef TIOCNOTTY /* Only necessary on old systems. */
779     ioctl(i, TIOCNOTTY, NULL);
780 #endif
781     close(i);
782 #ifdef _POSIX_VERSION
783     setsid();
784 #endif
785 }
786 #endif /* not DEBUG */
787
788 static void
789 read_from_dump(char *dumpfile)
790 {
791     /* Not yet implemented. */
792     return;
793 }
794