1 /* This file is part of the Project Athena Zephyr Notification System.
2 * It contains the main loop of the Zephyr server
4 * Created by: John T. Kohl
6 * $Source: /afs/dev.mit.edu/source/repository/athena/lib/zephyr/server/main.c,v $
9 * Copyright (c) 1987,1988,1991 by the Massachusetts Institute of Technology.
10 * For copying and distribution information, see the file
14 #include <zephyr/mit-copyright.h>
16 #include <sys/socket.h>
17 #include <sys/resource.h>
21 static const char rcsid_main_c[] =
27 * Server loop for Zephyr.
31 The Zephyr server maintains several linked lists of information.
33 There is an array of servers (otherservers) initialized and maintained
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.
40 Each of these host list entries has an IP address and a pointer to a
41 linked list of clients on that host.
43 Each client has a sockaddr_in, a list of subscriptions, and possibly
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).
53 #define EVER (;;) /* don't stop looping */
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));
71 static void detach __P((void));
74 static short doreset = 0; /* if it becomes 1, perform
77 int nfds; /* max file descriptor for select() */
78 int srv_socket; /* dgram socket for clients
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
84 struct sockaddr_in srv_addr; /* address of the socket */
86 Unacked *nacklist = NULL; /* list of packets waiting for ack's */
88 unsigned short hm_port; /* host manager receiver port */
89 unsigned short hm_srv_port; /* host manager server sending port */
91 char *programname; /* set to the basename of argv[0] */
92 char myname[MAXHOSTNAMELEN]; /* my host name */
96 char keytab_file[128];
97 static char tkt5_file[256];
100 char srvtab_file[128];
101 char my_realm[REALM_SZ];
102 static char tkt_file[128];
109 int dump_malloc_stats = 0;
110 unsigned long m_size;
116 struct timeval t_local; /* store current time for other uses */
118 static int dump_db_flag = 0;
119 static int dump_strings_flag = 0;
121 u_long npackets; /* number of packets processed */
122 time_t uptime; /* when we started operations */
124 struct in_addr my_addr;
125 char *bdump_version = "1.2";
128 int bdump_auth_proto = 5;
129 #else /* HAVE_KRB5 */
131 int bdump_auth_proto = 4;
132 #else /* HAVE_KRB4 */
133 int bdump_auth_proto = 0;
134 #endif /* HAVE_KRB4 */
135 #endif /* HAVE_KRB5 */
138 krb5_ccache Z_krb5_ccache;
139 krb5_keyblock *__Zephyr_keyblock;
142 C_Block __Zephyr_session;
151 int nfound; /* #fildes ready on select */
154 int init_from_dump = 0;
156 #ifdef _POSIX_VERSION
157 struct sigaction action;
159 int optchar; /* option processing */
163 sprintf(list_file, "%s/zephyr/%s", SYSCONFDIR, SERVER_LIST_FILE);
165 sprintf(srvtab_file, "%s/zephyr/%s", SYSCONFDIR, ZEPHYR_SRVTAB);
166 sprintf(tkt_file, "%s/zephyr/%s", SYSCONFDIR, ZEPHYR_TKFILE);
169 sprintf(keytab_file, "%s/zephyr/%s", SYSCONFDIR, ZEPHYR_KEYTAB);
170 sprintf(tkt5_file, "FILE:%s/zephyr/%s", SYSCONFDIR, ZEPHYR_TK5FILE);
172 sprintf(acl_dir, "%s/zephyr/%s", SYSCONFDIR, ZEPHYR_ACL_DIR);
173 sprintf(subs_file, "%s/zephyr/%s", SYSCONFDIR, DEFAULT_SUBS_FILE);
176 programname = strrchr(argv[0],'/');
177 programname = (programname) ? programname + 1 : argv[0];
179 /* process arguments */
180 while ((optchar = getopt(argc, argv, "dsnv4f:k:")) != EOF) {
195 strncpy(my_realm, optarg, REALM_SZ);
199 bdump_version = optarg;
206 bdump_auth_proto = 4;
216 /* if there is no readable srvtab and we are not standalone, there
217 is no possible way we can succeed, so we exit */
219 if (access(srvtab_file, R_OK)
224 fprintf(stderr, "NO ZEPHYR SRVTAB (%s) available; exiting\n",
228 /* Use local realm if not specified on command line. */
230 if (krb_get_lrealm(my_realm, 1) != KSUCCESS) {
231 fputs("Couldn't get local Kerberos realm; exiting.\n", stderr);
235 #endif /* HAVE_KRB4 */
243 OPENLOG(programname, LOG_PID, LOG_LOCAL6);
245 #if defined (DEBUG) && 0
247 syslog(LOG_DEBUG, "standalone operation");
251 syslog(LOG_DEBUG, "debugging on");
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 */
259 /* Initialize t_local for other uses */
260 gettimeofday(&t_local, NULL);
266 read_from_dump(dumpfile);
268 /* Seed random number set. */
269 srandom(getpid() ^ time(0));
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)");
275 FD_ZERO(&interesting);
276 FD_SET(srv_socket, &interesting);
278 nfds = srv_socket + 1;
281 #ifdef _POSIX_VERSION
283 sigemptyset(&action.sa_mask);
285 action.sa_handler = bye;
286 sigaction(SIGINT, &action, NULL);
287 sigaction(SIGTERM, &action, NULL);
289 action.sa_handler = dbug_on;
290 sigaction(SIGUSR1, &action, NULL);
292 action.sa_handler = dbug_off;
293 sigaction(SIGUSR2, &action, NULL);
295 action.sa_handler = reap;
296 sigaction(SIGCHLD, &action, NULL);
298 action.sa_handler = sig_dump_db;
299 sigaction(SIGFPE, &action, NULL);
302 action.sa_handler = sig_dump_strings;
303 sigaction(SIGEMT, &action, NULL);
306 action.sa_handler = reset;
307 sigaction(SIGHUP, &action, NULL);
310 signal(SIGTERM, bye);
311 signal(SIGUSR1, dbug_on);
312 signal(SIGUSR2, dbug_off);
313 signal(SIGCHLD, reap);
314 signal(SIGFPE, sig_dump_db);
316 signal(SIGEMT, sig_dump_strings);
318 signal(SIGHUP, reset);
319 #endif /* _POSIX_VERSION */
321 syslog(LOG_NOTICE, "Ready for action");
323 /* Reinitialize t_local now that initialization is done. */
324 gettimeofday(&t_local, NULL);
329 malloc_inuse(&m_size);
337 if (dump_strings_flag)
342 readable = interesting;
344 /* when there is input in the queue, we
345 artificially set up to pick up the input */
349 nfound = select(nfds, &readable, NULL, NULL, timer_timeout(&tv));
352 /* Initialize t_local for other uses */
353 gettimeofday(&t_local, (struct timezone *)0);
355 /* don't flame about EINTR, since a SIGUSR1 or SIGUSR2
356 can generate it by interrupting the select */
359 syslog(LOG_WARNING, "select error: %m");
361 if (dump_malloc_stats) {
362 unsigned long foo,histid2;
364 dump_malloc_stats = 0;
365 foo = malloc_inuse(&histid2);
366 printf("Total inuse: %d\n",foo);
367 malloc_list(2,m_size,histid2);
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 */
379 if (bdump_socket >= 0 && FD_ISSET(bdump_socket,&readable))
381 else if (msgs_queued() || FD_ISSET(srv_socket, &readable))
384 syslog(LOG_ERR, "select weird?!?!");
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.
406 krb_set_tkt_string(tkt_file);
411 ZInitialize(); /* set up the library */
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);
418 /* Hack to make krb5_cc_default do something reasonable */
419 char *env=(char *)malloc(strlen(tkt5_file)+12);
421 sprintf(env, "KRB5CCNAME=%s", tkt5_file);
427 /* Override what Zinitialize set for ZGetRealm() */
429 strcpy(__Zephyr_realm, my_realm);
431 init_zsrv_err_tbl(); /* set up err table */
433 ZSetFD(srv_socket); /* set up the socket as the input fildes */
435 /* set up default strings */
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);
445 /* restrict certain classes */
451 * Set up the server and client sockets, and initialize my_addr and myname
459 char hostname[MAXHOSTNAMELEN+1];
462 if (gethostname(hostname, MAXHOSTNAMELEN + 1)) {
463 syslog(LOG_ERR, "no hostname: %m");
466 hp = gethostbyname(hostname);
468 syslog(LOG_ERR, "no gethostbyname repsonse");
469 strncpy(myname, hostname, MAXHOSTNAMELEN);
472 strncpy(myname, hp->h_name, MAXHOSTNAMELEN);
473 memcpy(&my_addr, hp->h_addr, sizeof(hp->h_addr));
475 setservent(1); /* keep file/connection open */
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;
482 sp = getservbyname(HM_SVCNAME, "udp");
483 hm_port = (sp) ? sp->s_port : HM_SVC_FALLBACK;
485 sp = getservbyname(HM_SRV_SVCNAME, "udp");
486 hm_srv_port = (sp) ? sp->s_port : HM_SRV_SVC_FALLBACK;
488 srv_socket = socket(AF_INET, SOCK_DGRAM, 0);
489 if (srv_socket < 0) {
490 syslog(LOG_ERR, "client_sock failed: %m");
496 /* Prevent Linux from giving us socket errors we don't care about. */
497 setsockopt(srv_socket, SOL_SOCKET, SO_BSDCOMPAT, &on, sizeof(on));
500 if (bind(srv_socket, (struct sockaddr *) &srv_addr,
501 sizeof(srv_addr)) < 0) {
502 syslog(LOG_ERR, "client bind failed: %m");
506 /* set not-blocking */
507 #ifdef _POSIX_VERSION
508 flags = fcntl(srv_socket, F_GETFL);
510 fcntl(srv_socket, F_SETFL, flags);
513 ioctl(srv_socket, FIONBIO, &flags);
521 * print out a usage message.
528 fprintf(stderr, "Usage: %s [-d] [-s] [-n] [-k realm] [-f dumpfile]\n",
531 fprintf(stderr, "Usage: %s [-d] [-n] [-k realm] [-f dumpfile]\n",
540 fd_set readable, initial;
546 FD_SET(srv_socket, &initial);
548 tv.tv_sec = tv.tv_usec = 0;
549 return (select(srv_socket + 1, &readable, NULL, NULL, &tv) > 0);
556 server_shutdown(); /* tell other servers */
558 realm_shutdown(); /* tell other realms */
560 hostm_shutdown(); /* tell our hosts */
565 syslog(LOG_NOTICE, "goodbye (sig %d)", sig);
573 syslog(LOG_DEBUG, "debugging turned on");
575 dump_malloc_stats = 1;
584 syslog(LOG_DEBUG, "debugging turned off");
586 malloc_inuse(&m_size);
591 int fork_for_dump = 0;
594 sig_dump_strings(sig)
597 dump_strings_flag = 1;
600 static void dump_strings()
607 sprintf(filename, "%szephyr.strings", TEMP_DIRECTORY);
608 fp = fopen (filename, "w");
610 syslog(LOG_ERR, "can't open strings dump file: %m");
612 dump_strings_flag = 0;
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");
620 syslog(LOG_INFO, "dump done");
622 dump_strings_flag = 0;
633 static void dump_db()
635 /* dump the in-core database to human-readable form on disk */
641 pid = (fork_for_dump) ? fork() : -1;
646 sprintf(filename, "%szephyr.db", TEMP_DIRECTORY);
647 fp = fopen(filename, "w");
649 syslog(LOG_ERR, "can't open dump database");
654 syslog(LOG_INFO, "dumping to disk");
655 server_dump_servers(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");
674 zdbug((LOG_DEBUG,"reset()"));
686 #ifdef _POSIX_VERSION
692 zdbug((LOG_DEBUG,"reap()"));
694 #ifdef _POSIX_VERSION
695 while ((pid = waitpid(-1, &waitb, WNOHANG)) == 0)
696 { i++; if (i > 10) break; }
698 while ((pid = wait3 (&waitb, WNOHANG, (struct rusage*) 0)) == 0)
699 { i++; if (i > 10) break; }
705 if (WIFSIGNALED(waitb) == 0) {
706 if (WIFEXITED(waitb) != 0) {
707 rlm = realm_get_realm_by_pid(pid);
714 rlm = realm_get_realm_by_pid(pid);
726 #ifdef _POSIX_VERSION
727 sigset_t mask, omask;
732 zdbug((LOG_DEBUG,"do_reset()"));
734 #ifdef _POSIX_VERSION
736 sigaddset(&mask, SIGHUP);
737 sigprocmask(SIG_BLOCK, &mask, &omask);
739 omask = sigblock(sigmask(SIGHUP));
742 /* reset various things in the server's state */
746 syslog(LOG_INFO, "restart completed");
749 #ifdef _POSIX_VERSION
750 sigprocmask(SIG_SETMASK, &omask, (sigset_t *)0);
758 * detach from the terminal
764 /* detach from terminal and fork. */
768 #ifdef _POSIX_VERSION
769 size = sysconf(_SC_OPEN_MAX);
771 size = getdtablesize();
773 /* profiling seems to get confused by fork() */
781 for (i = 0; i < size; i++)
784 i = open("/dev/tty", O_RDWR, 666);
785 #ifdef TIOCNOTTY /* Only necessary on old systems. */
786 ioctl(i, TIOCNOTTY, NULL);
789 #ifdef _POSIX_VERSION
793 #endif /* not DEBUG */
796 read_from_dump(dumpfile)
799 /* Not yet implemented. */