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