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