]> asedeno.scripts.mit.edu Git - 1ts-debian.git/blob - zephyr/server/main.c
d83940d50127f15e4ffee27aefb974ba8226423c
[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: kcr $
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: main.c 2345 2009-03-24 03:59:25Z kcr $";
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 reset(int);
64 static RETSIGTYPE reap(int);
65 static void read_from_dump(char *dumpfile);
66 static void dump_db(void);
67 static void dump_strings(void);
68
69 #ifndef DEBUG
70 static void detach(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 char list_file[128];
94 #ifdef HAVE_KRB5
95 char keytab_file[128];
96 static char tkt5_file[256];
97 #endif
98 #ifdef HAVE_KRB4
99 char srvtab_file[128];
100 static char tkt_file[128];
101 #endif
102 #if defined(HAVE_KRB4) || defined(HAVE_KRB5)
103 char my_realm[REALM_SZ];
104 #endif
105 char acl_dir[128];
106 char subs_file[128];
107
108 int zdebug;
109 #ifdef DEBUG_MALLOC
110 int dump_malloc_stats = 0;
111 unsigned long m_size;
112 #endif
113 #ifdef DEBUG
114 int zalone;
115 #endif
116
117 struct timeval t_local;                 /* store current time for other uses */
118
119 static int dump_db_flag = 0;
120 static int dump_strings_flag = 0;
121
122 u_long npackets;                        /* number of packets processed */
123 time_t uptime;                          /* when we started operations */
124 static int nofork;
125 struct in_addr my_addr;
126 char *bdump_version = "1.2";
127
128 #ifdef HAVE_KRB5
129 int bdump_auth_proto = 5;
130 #else /* HAVE_KRB5 */
131 #ifdef HAVE_KRB4
132 int bdump_auth_proto = 4;
133 #else /* HAVE_KRB4 */
134 int bdump_auth_proto = 0;
135 #endif /* HAVE_KRB4 */
136 #endif /* HAVE_KRB5 */
137
138 #ifdef HAVE_KRB5
139 krb5_ccache Z_krb5_ccache;
140 krb5_keyblock *__Zephyr_keyblock;
141 #else
142 #ifdef HAVE_KRB4
143 C_Block __Zephyr_session;
144 #endif
145 #endif
146
147 int
148 main(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 #if defined(HAVE_KRB4) || defined(HAVE_KRB5)
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 (zdebug)
250         syslog(LOG_DEBUG, "debugging on");
251
252     /* set up sockets & my_addr and myname, 
253        find other servers and set up server table, initialize queues
254        for retransmits, initialize error tables,
255        set up restricted classes */
256
257     /* Initialize t_local for other uses */
258     gettimeofday(&t_local, NULL);
259
260     if (initialize())
261         exit(1);
262
263     if (init_from_dump)
264         read_from_dump(dumpfile);
265
266     /* Seed random number set.  */
267     srandom(getpid() ^ time(0));
268
269     /* chdir to somewhere where a core dump will survive */
270     if (chdir(TEMP_DIRECTORY) != 0)
271         syslog(LOG_ERR, "chdir failed (%m) (execution continuing)");
272
273     FD_ZERO(&interesting);
274     FD_SET(srv_socket, &interesting);
275
276     nfds = srv_socket + 1;
277
278
279 #ifdef _POSIX_VERSION
280     action.sa_flags = 0;
281     sigemptyset(&action.sa_mask);
282
283     action.sa_handler = bye;
284     sigaction(SIGINT, &action, NULL);
285     sigaction(SIGTERM, &action, NULL);
286
287     action.sa_handler = dbug_on;
288     sigaction(SIGUSR1, &action, NULL);
289
290     action.sa_handler = dbug_off;
291     sigaction(SIGUSR2, &action, NULL);
292
293     action.sa_handler = reap;
294     sigaction(SIGCHLD, &action, NULL);
295
296     action.sa_handler = sig_dump_db;
297     sigaction(SIGFPE, &action, NULL);
298
299     action.sa_handler = reset;
300     sigaction(SIGHUP, &action, NULL);
301 #else /* !posix */
302     signal(SIGINT, bye);
303     signal(SIGTERM, bye);
304     signal(SIGUSR1, dbug_on);
305     signal(SIGUSR2, dbug_off);
306     signal(SIGCHLD, reap);
307     signal(SIGFPE, sig_dump_db);
308     signal(SIGHUP, reset);
309 #endif /* _POSIX_VERSION */
310
311     syslog(LOG_NOTICE, "Ready for action");
312
313     /* Reinitialize t_local now that initialization is done. */
314     gettimeofday(&t_local, NULL);
315     uptime = NOW;
316
317     realm_wakeup();
318 #ifdef DEBUG_MALLOC
319     malloc_inuse(&m_size);
320 #endif
321     for EVER {
322         if (doreset)
323             do_reset();
324
325         if (dump_db_flag)
326             dump_db();
327         if (dump_strings_flag)
328             dump_strings();
329
330         timer_process();
331
332         readable = interesting;
333         if (msgs_queued()) {
334             /* when there is input in the queue, we
335                artificially set up to pick up the input */
336             nfound = 1;
337             FD_ZERO(&readable);
338         } else  {
339             nfound = select(nfds, &readable, NULL, NULL, timer_timeout(&tv));
340         }
341
342         /* Initialize t_local for other uses */
343         gettimeofday(&t_local, (struct timezone *)0);
344                 
345         /* don't flame about EINTR, since a SIGUSR1 or SIGUSR2
346            can generate it by interrupting the select */
347         if (nfound < 0) {
348             if (errno != EINTR)
349                 syslog(LOG_WARNING, "select error: %m");
350 #ifdef DEBUG_MALLOC
351             if (dump_malloc_stats) {
352                 unsigned long foo,histid2;
353
354                 dump_malloc_stats = 0;
355                 foo = malloc_inuse(&histid2);
356                 printf("Total inuse: %d\n",foo);
357                 malloc_list(2,m_size,histid2);
358             }
359 #endif
360             continue;
361         }
362
363         if (nfound == 0) {
364             /* either we timed out or we were just
365                polling for input.  Either way we want to continue
366                the loop, and process the next timeout */
367             continue;
368         } else {
369             if (bdump_socket >= 0 && FD_ISSET(bdump_socket,&readable))
370                 bdump_send();
371             else if (msgs_queued() || FD_ISSET(srv_socket, &readable))
372                 handle_packet();
373             else
374                 syslog(LOG_ERR, "select weird?!?!");
375         }
376     }
377 }
378
379 /* Initialize net stuff.
380    Set up the server array.
381    Initialize the packet ack queues to be empty.
382    Initialize the error tables.
383    Restrict certain classes.
384    */
385
386 static int
387 initialize(void)
388 {
389     if (do_net_setup())
390         return(1);
391
392     server_init();
393
394 #ifdef HAVE_KRB4
395     krb_set_tkt_string(tkt_file);
396 #endif
397     realm_init();
398     
399     ZSetServerState(1);
400     ZInitialize();              /* set up the library */
401 #ifdef HAVE_KRB5
402     krb5_cc_resolve(Z_krb5_ctx, tkt5_file, &Z_krb5_ccache);
403 #ifdef HAVE_KRB5_CC_SET_DEFAULT_NAME
404     krb5_cc_set_default_name(Z_krb5_ctx, tkt5_file);
405 #else
406     {
407         /* Hack to make krb5_cc_default do something reasonable */
408         char *env=(char *)malloc(strlen(tkt5_file)+12);
409         if (!env) return(1);
410         sprintf(env, "KRB5CCNAME=%s", tkt5_file);
411         putenv(env);
412     }
413 #endif
414 #endif
415 #if defined(HAVE_KRB4) || defined(HAVE_KRB5)
416     /* Override what Zinitialize set for ZGetRealm() */
417     if (*my_realm) 
418       strcpy(__Zephyr_realm, my_realm);
419 #endif
420     init_zsrv_err_tbl();        /* set up err table */
421
422     ZSetFD(srv_socket);         /* set up the socket as the input fildes */
423
424     /* set up default strings */
425
426     class_control = make_string(ZEPHYR_CTL_CLASS, 1);
427     class_admin = make_string(ZEPHYR_ADMIN_CLASS, 1);
428     class_hm = make_string(HM_CTL_CLASS, 1);
429     class_ulogin = make_string(LOGIN_CLASS, 1);
430     class_ulocate = make_string(LOCATE_CLASS, 1);
431     wildcard_instance = make_string(WILDCARD_INSTANCE, 1);
432     empty = make_string("", 0);
433
434     /* restrict certain classes */
435     access_init();
436     return 0;
437 }
438
439 /* 
440  * Set up the server and client sockets, and initialize my_addr and myname
441  */
442
443 static int
444 do_net_setup(void)
445 {
446     struct servent *sp;
447     struct hostent *hp;
448     char hostname[MAXHOSTNAMELEN+1];
449     int flags;
450
451     if (gethostname(hostname, MAXHOSTNAMELEN + 1)) {
452         syslog(LOG_ERR, "no hostname: %m");
453         return 1;
454     }
455     hp = gethostbyname(hostname);
456     if (!hp) {
457         syslog(LOG_ERR, "no gethostbyname repsonse");
458         strncpy(myname, hostname, MAXHOSTNAMELEN);
459         return 1;
460     }
461     strncpy(myname, hp->h_name, MAXHOSTNAMELEN);
462     memcpy(&my_addr, hp->h_addr, sizeof(hp->h_addr));
463
464     setservent(1);              /* keep file/connection open */
465
466     memset(&srv_addr, 0, sizeof(srv_addr));
467     srv_addr.sin_family = AF_INET;
468     sp = getservbyname(SERVER_SVCNAME, "udp");
469     srv_addr.sin_port = (sp) ? sp->s_port : SERVER_SVC_FALLBACK;
470
471     sp = getservbyname(HM_SVCNAME, "udp");
472     hm_port = (sp) ? sp->s_port : HM_SVC_FALLBACK;
473         
474     sp = getservbyname(HM_SRV_SVCNAME, "udp");
475     hm_srv_port = (sp) ? sp->s_port : HM_SRV_SVC_FALLBACK;
476         
477     srv_socket = socket(AF_INET, SOCK_DGRAM, 0);
478     if (srv_socket < 0) {
479         syslog(LOG_ERR, "client_sock failed: %m");
480         return 1;
481     } 
482     if (bind(srv_socket, (struct sockaddr *) &srv_addr,
483              sizeof(srv_addr)) < 0) {
484         syslog(LOG_ERR, "client bind failed: %m");
485         return 1;
486     }
487
488     /* set not-blocking */
489 #ifdef _POSIX_VERSION
490     flags = fcntl(srv_socket, F_GETFL);
491     flags |= O_NONBLOCK;
492     fcntl(srv_socket, F_SETFL, flags);
493 #else
494     flags = 1;
495     ioctl(srv_socket, FIONBIO, &flags);
496 #endif
497
498     return 0;
499 }    
500
501
502 /*
503  * print out a usage message.
504  */
505
506 static void
507 usage(void)
508 {
509 #ifdef DEBUG
510         fprintf(stderr, "Usage: %s [-d] [-s] [-n] [-k realm] [-f dumpfile]\n",
511                 programname);
512 #else
513         fprintf(stderr, "Usage: %s [-d] [-n] [-k realm] [-f dumpfile]\n",
514                 programname);
515 #endif /* DEBUG */
516         exit(2);
517 }
518
519 int
520 packets_waiting(void)
521 {
522     fd_set readable, initial;
523     struct timeval tv;
524
525     if (msgs_queued())
526         return 1;
527     FD_ZERO(&initial);
528     FD_SET(srv_socket, &initial);
529     readable = initial;
530     tv.tv_sec = tv.tv_usec = 0;
531     return (select(srv_socket + 1, &readable, NULL, NULL, &tv) > 0);
532 }
533
534 static RETSIGTYPE
535 bye(int sig)
536 {
537     server_shutdown();          /* tell other servers */
538 #ifdef REALM_MGMT
539     realm_shutdown();           /* tell other realms */
540 #endif
541     hostm_shutdown();           /* tell our hosts */
542     kill_realm_pids();
543 #ifdef HAVE_KRB4
544     dest_tkt();
545 #endif
546     syslog(LOG_NOTICE, "goodbye (sig %d)", sig);
547     exit(0);
548 }
549
550 static RETSIGTYPE
551 dbug_on(int sig)
552 {
553     syslog(LOG_DEBUG, "debugging turned on");
554 #ifdef DEBUG_MALLOC
555     dump_malloc_stats = 1;
556 #endif
557     zdebug = 1;
558 }
559
560 static RETSIGTYPE
561 dbug_off(int sig)
562 {
563     syslog(LOG_DEBUG, "debugging turned off");
564 #ifdef DEBUG_MALLOC
565     malloc_inuse(&m_size);
566 #endif
567     zdebug = 0;
568 }
569
570 int fork_for_dump = 0;
571
572 static void dump_strings(void)
573 {
574     char filename[128];
575
576     FILE *fp;
577     int oerrno = errno;
578
579     sprintf(filename, "%szephyr.strings", TEMP_DIRECTORY);
580     fp = fopen (filename, "w");
581     if (!fp) {
582         syslog(LOG_ERR, "can't open strings dump file: %m");
583         errno = oerrno;
584         dump_strings_flag = 0;
585         return;
586     }
587     syslog(LOG_INFO, "dumping strings to disk");
588     print_string_table(fp);
589     if (fclose(fp) == EOF)
590         syslog(LOG_ERR, "error writing strings dump file");
591     else
592         syslog(LOG_INFO, "dump done");
593     oerrno = errno;
594     dump_strings_flag = 0;
595     return;
596 }
597
598 static RETSIGTYPE
599 sig_dump_db(int sig)
600 {
601     dump_db_flag = 1;
602 }
603
604 static void
605 dump_db(void)
606 {
607     /* dump the in-core database to human-readable form on disk */
608     FILE *fp;
609     int oerrno = errno;
610     int pid;
611     char filename[128];
612
613     pid = (fork_for_dump) ? fork() : -1;
614     if (pid > 0) {
615         dump_db_flag = 0;
616         return;
617     }
618     sprintf(filename, "%szephyr.db", TEMP_DIRECTORY);
619     fp = fopen(filename, "w");
620     if (!fp) {
621         syslog(LOG_ERR, "can't open dump database");
622         errno = oerrno;
623         dump_db_flag = 0;
624         return;
625     }
626     syslog(LOG_INFO, "dumping to disk");
627     server_dump_servers(fp);
628     uloc_dump_locs(fp);
629     client_dump_clients(fp);
630     triplet_dump_subs(fp);
631     realm_dump_realms(fp);
632     syslog(LOG_INFO, "dump done");
633     if (fclose(fp) == EOF)
634         syslog(LOG_ERR, "can't close dump db");
635     if (pid == 0)
636         exit(0);
637     errno = oerrno;
638     dump_db_flag = 0;
639 }
640
641 static RETSIGTYPE
642 reset(int sig)
643 {
644     zdbug((LOG_DEBUG,"reset()"));
645     doreset = 1;
646 }
647
648 static RETSIGTYPE
649 reap(int sig)
650 {
651     int pid, i = 0;
652     int oerrno = errno;
653     ZRealm *rlm;
654 #ifdef _POSIX_VERSION
655     int waitb;
656 #else
657     union wait waitb;
658 #endif
659
660     zdbug((LOG_DEBUG,"reap()"));
661 #ifdef _POSIX_VERSION
662     while ((pid = waitpid(-1, &waitb, WNOHANG)) == 0) 
663       { i++; if (i > 10) break; }
664 #else
665     while ((pid = wait3 (&waitb, WNOHANG, (struct rusage*) 0)) == 0) 
666       { i++; if (i > 10) break; }
667 #endif
668
669     errno = oerrno;
670  
671     if (pid) {
672       if (WIFSIGNALED(waitb) == 0) {
673         if (WIFEXITED(waitb) != 0) {
674           rlm = realm_get_realm_by_pid(pid);
675           if (rlm) {
676             rlm->child_pid = 0;
677             rlm->have_tkt = 1;
678           }
679         }
680       } else {
681         rlm = realm_get_realm_by_pid(pid);
682         if (rlm) {
683           rlm->child_pid = 0;
684         }
685       }
686     }
687 }
688
689 static void
690 do_reset(void)
691 {
692     int oerrno = errno;
693 #ifdef _POSIX_VERSION
694     sigset_t mask, omask;
695 #else
696     int omask;
697 #endif
698 #ifdef _POSIX_VERSION
699     sigemptyset(&mask);
700     sigaddset(&mask, SIGHUP);
701     sigprocmask(SIG_BLOCK, &mask, &omask);
702 #else
703     omask = sigblock(sigmask(SIGHUP));
704 #endif
705
706     /* reset various things in the server's state */
707     subscr_reset();
708     server_reset();
709     access_reinit();
710     syslog(LOG_INFO, "restart completed");
711     doreset = 0;
712     errno = oerrno;
713 #ifdef _POSIX_VERSION
714     sigprocmask(SIG_SETMASK, &omask, (sigset_t *)0);
715 #else
716     sigsetmask(omask);
717 #endif
718 }
719
720 #ifndef DEBUG
721 /*
722  * detach from the terminal
723  */
724
725 static void
726 detach(void)
727 {
728     /* detach from terminal and fork. */
729     int i;
730     long size;
731
732 #ifdef _POSIX_VERSION
733     size = sysconf(_SC_OPEN_MAX);
734 #else
735     size = getdtablesize();
736 #endif
737     /* profiling seems to get confused by fork() */
738     i = fork ();
739     if (i) {
740         if (i < 0)
741             perror("fork");
742         exit(0);
743     }
744
745     for (i = 0; i < size; i++)
746         close(i);
747
748     i = open("/dev/tty", O_RDWR, 666);
749 #ifdef TIOCNOTTY /* Only necessary on old systems. */
750     ioctl(i, TIOCNOTTY, NULL);
751 #endif
752     close(i);
753 #ifdef _POSIX_VERSION
754     setsid();
755 #endif
756 }
757 #endif /* not DEBUG */
758
759 static void
760 read_from_dump(char *dumpfile)
761 {
762     /* Not yet implemented. */
763     return;
764 }
765