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 * $Id: main.c,v 1.68 1999/01/22 23:19:45 ghudson Exp $
8 * Copyright (c) 1987,1988,1991 by the Massachusetts Institute of Technology.
9 * For copying and distribution information, see the file
13 #include <zephyr/mit-copyright.h>
15 #include <sys/socket.h>
16 #include <sys/resource.h>
20 static const char rcsid_main_c[] =
21 "$Id: main.c,v 1.68 1999/01/22 23:19:45 ghudson Exp $";
26 * Server loop for Zephyr.
30 The Zephyr server maintains several linked lists of information.
32 There is an array of servers (otherservers) initialized and maintained
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.
39 Each of these host list entries has an IP address and a pointer to a
40 linked list of clients on that host.
42 Each client has a sockaddr_in, a list of subscriptions, and possibly
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).
52 #define EVER (;;) /* don't stop looping */
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));
70 static void detach __P((void));
73 static short doreset = 0; /* if it becomes 1, perform
76 int nfds; /* max file descriptor for select() */
77 int srv_socket; /* dgram socket for clients
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
83 struct sockaddr_in srv_addr; /* address of the socket */
85 Unacked *nacklist = NULL; /* list of packets waiting for ack's */
87 unsigned short hm_port; /* host manager receiver port */
88 unsigned short hm_srv_port; /* host manager server sending port */
90 char *programname; /* set to the basename of argv[0] */
91 char myname[MAXHOSTNAMELEN]; /* my host name */
97 char srvtab_file[128];
98 char my_realm[REALM_SZ];
99 static char tkt_file[128];
106 int dump_malloc_stats = 0;
107 unsigned long m_size;
113 struct timeval t_local; /* store current time for other uses */
115 static int dump_db_flag = 0;
116 static int dump_strings_flag = 0;
118 u_long npackets; /* number of packets processed */
119 time_t uptime; /* when we started operations */
121 struct in_addr my_addr;
122 char *bdump_version = "1.2";
129 int nfound; /* #fildes ready on select */
132 int init_from_dump = 0;
134 #ifdef _POSIX_VERSION
135 struct sigaction action;
137 int optchar; /* option processing */
142 sprintf(list_file, "%s/zephyr/%s", SYSCONFDIR, SERVER_LIST_FILE);
145 sprintf(srvtab_file, "%s/zephyr/%s", SYSCONFDIR, ZEPHYR_SRVTAB);
146 sprintf(tkt_file, "%s/zephyr/%s", SYSCONFDIR, ZEPHYR_TKFILE);
148 sprintf(acl_dir, "%s/zephyr/%s", SYSCONFDIR, ZEPHYR_ACL_DIR);
149 sprintf(subs_file, "%s/zephyr/%s", SYSCONFDIR, DEFAULT_SUBS_FILE);
152 programname = strrchr(argv[0],'/');
153 programname = (programname) ? programname + 1 : argv[0];
155 /* process arguments */
156 while ((optchar = getopt(argc, argv, "dsnv:f:k:")) != EOF) {
171 strncpy(my_realm, optarg, REALM_SZ);
175 bdump_version = optarg;
189 /* if there is no readable srvtab and we are not standalone, there
190 is no possible way we can succeed, so we exit */
192 if (access(srvtab_file, R_OK)
197 fprintf(stderr, "NO ZEPHYR SRVTAB (%s) available; exiting\n",
201 /* Use local realm if not specified on command line. */
203 if (krb_get_lrealm(my_realm, 1) != KSUCCESS) {
204 fputs("Couldn't get local Kerberos realm; exiting.\n", stderr);
208 #endif /* HAVE_KRB4 */
216 OPENLOG(programname, LOG_PID, LOG_LOCAL6);
218 #if defined (DEBUG) && 0
220 syslog(LOG_DEBUG, "standalone operation");
224 syslog(LOG_DEBUG, "debugging on");
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 */
232 /* Initialize t_local for other uses */
233 gettimeofday(&t_local, NULL);
239 read_from_dump(dumpfile);
241 /* Seed random number set. */
242 srandom(getpid() ^ time(0));
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)");
248 FD_ZERO(&interesting);
249 FD_SET(srv_socket, &interesting);
251 nfds = srv_socket + 1;
254 #ifdef _POSIX_VERSION
256 sigemptyset(&action.sa_mask);
258 action.sa_handler = bye;
259 sigaction(SIGINT, &action, NULL);
260 sigaction(SIGTERM, &action, NULL);
262 action.sa_handler = dbug_on;
263 sigaction(SIGUSR1, &action, NULL);
265 action.sa_handler = dbug_off;
266 sigaction(SIGUSR2, &action, NULL);
268 action.sa_handler = reap;
269 sigaction(SIGCHLD, &action, NULL);
271 action.sa_handler = sig_dump_db;
272 sigaction(SIGFPE, &action, NULL);
275 action.sa_handler = sig_dump_strings;
276 sigaction(SIGEMT, &action, NULL);
279 action.sa_handler = reset;
280 sigaction(SIGHUP, &action, NULL);
283 signal(SIGTERM, bye);
284 signal(SIGUSR1, dbug_on);
285 signal(SIGUSR2, dbug_off);
286 signal(SIGCHLD, reap);
287 signal(SIGFPE, sig_dump_db);
289 signal(SIGEMT, sig_dump_strings);
291 signal(SIGHUP, reset);
292 #endif /* _POSIX_VERSION */
294 syslog(LOG_NOTICE, "Ready for action");
296 /* Reinitialize t_local now that initialization is done. */
297 gettimeofday(&t_local, NULL);
300 timer_set_rel(SWEEP_INTERVAL, sweep_ticket_hash_table, NULL);
304 malloc_inuse(&m_size);
312 if (dump_strings_flag)
317 readable = interesting;
319 /* when there is input in the queue, we
320 artificially set up to pick up the input */
324 nfound = select(nfds, &readable, NULL, NULL, timer_timeout(&tv));
327 /* Initialize t_local for other uses */
328 gettimeofday(&t_local, (struct timezone *)0);
330 /* don't flame about EINTR, since a SIGUSR1 or SIGUSR2
331 can generate it by interrupting the select */
334 syslog(LOG_WARNING, "select error: %m");
336 if (dump_malloc_stats) {
337 unsigned long foo,histid2;
339 dump_malloc_stats = 0;
340 foo = malloc_inuse(&histid2);
341 printf("Total inuse: %d\n",foo);
342 malloc_list(2,m_size,histid2);
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 */
354 if (bdump_socket >= 0 && FD_ISSET(bdump_socket,&readable))
356 else if (msgs_queued() || FD_ISSET(srv_socket, &readable))
359 syslog(LOG_ERR, "select weird?!?!");
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.
380 krb_set_tkt_string(tkt_file);
385 ZInitialize(); /* set up the library */
386 init_zsrv_err_tbl(); /* set up err table */
388 ZSetFD(srv_socket); /* set up the socket as the input fildes */
390 /* set up default strings */
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);
400 /* restrict certain classes */
406 * Set up the server and client sockets, and initialize my_addr and myname
414 char hostname[MAXHOSTNAMELEN+1];
417 if (gethostname(hostname, MAXHOSTNAMELEN + 1)) {
418 syslog(LOG_ERR, "no hostname: %m");
421 hp = gethostbyname(hostname);
423 syslog(LOG_ERR, "no gethostbyname repsonse");
424 strncpy(myname, hostname, MAXHOSTNAMELEN);
427 strncpy(myname, hp->h_name, MAXHOSTNAMELEN);
428 memcpy(&my_addr, hp->h_addr, sizeof(hp->h_addr));
430 setservent(1); /* keep file/connection open */
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;
437 sp = getservbyname(HM_SVCNAME, "udp");
438 hm_port = (sp) ? sp->s_port : HM_SVC_FALLBACK;
440 sp = getservbyname(HM_SRV_SVCNAME, "udp");
441 hm_srv_port = (sp) ? sp->s_port : HM_SRV_SVC_FALLBACK;
443 srv_socket = socket(AF_INET, SOCK_DGRAM, 0);
444 if (srv_socket < 0) {
445 syslog(LOG_ERR, "client_sock failed: %m");
448 if (bind(srv_socket, (struct sockaddr *) &srv_addr,
449 sizeof(srv_addr)) < 0) {
450 syslog(LOG_ERR, "client bind failed: %m");
454 /* set not-blocking */
455 #ifdef _POSIX_VERSION
456 flags = fcntl(srv_socket, F_GETFL);
458 fcntl(srv_socket, F_SETFL, flags);
461 ioctl(srv_socket, FIONBIO, &flags);
469 * print out a usage message.
476 fprintf(stderr, "Usage: %s [-d] [-s] [-n] [-k realm] [-f dumpfile]\n",
479 fprintf(stderr, "Usage: %s [-d] [-n] [-k realm] [-f dumpfile]\n",
488 fd_set readable, initial;
494 FD_SET(srv_socket, &initial);
496 tv.tv_sec = tv.tv_usec = 0;
497 return (select(srv_socket + 1, &readable, NULL, NULL, &tv) > 0);
504 server_shutdown(); /* tell other servers */
505 hostm_shutdown(); /* tell our hosts */
509 syslog(LOG_NOTICE, "goodbye (sig %d)", sig);
517 syslog(LOG_DEBUG, "debugging turned on");
519 dump_malloc_stats = 1;
528 syslog(LOG_DEBUG, "debugging turned off");
530 malloc_inuse(&m_size);
535 int fork_for_dump = 0;
538 sig_dump_strings(sig)
541 dump_strings_flag = 1;
544 static void dump_strings()
551 sprintf(filename, "%szephyr.strings", TEMP_DIRECTORY);
552 fp = fopen (filename, "w");
554 syslog(LOG_ERR, "can't open strings dump file: %m");
556 dump_strings_flag = 0;
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");
564 syslog(LOG_INFO, "dump done");
566 dump_strings_flag = 0;
577 static void dump_db()
579 /* dump the in-core database to human-readable form on disk */
585 pid = (fork_for_dump) ? fork() : -1;
590 sprintf(filename, "%szephyr.db", TEMP_DIRECTORY);
591 fp = fopen(filename, "w");
593 syslog(LOG_ERR, "can't open dump database");
598 syslog(LOG_INFO, "dumping to disk");
599 server_dump_servers(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");
618 zdbug((LOG_DEBUG,"reset()"));
629 #ifdef _POSIX_VERSION
631 while (waitpid(-1, &waitb, WNOHANG) == 0) ;
634 while (wait3 (&waitb, WNOHANG, (struct rusage*) 0) == 0) ;
644 #ifdef _POSIX_VERSION
645 sigset_t mask, omask;
650 zdbug((LOG_DEBUG,"do_reset()"));
652 #ifdef _POSIX_VERSION
654 sigaddset(&mask, SIGHUP);
655 sigprocmask(SIG_BLOCK, &mask, &omask);
657 omask = sigblock(sigmask(SIGHUP));
660 /* reset various things in the server's state */
664 syslog(LOG_INFO, "restart completed");
667 #ifdef _POSIX_VERSION
668 sigprocmask(SIG_SETMASK, &omask, (sigset_t *)0);
676 * detach from the terminal
682 /* detach from terminal and fork. */
686 #ifdef _POSIX_VERSION
687 size = sysconf(_SC_OPEN_MAX);
689 size = getdtablesize();
691 /* profiling seems to get confused by fork() */
699 for (i = 0; i < size; i++)
702 i = open("/dev/tty", O_RDWR, 666);
703 #ifdef TIOCNOTTY /* Only necessary on old systems. */
704 ioctl(i, TIOCNOTTY, NULL);
707 #ifdef _POSIX_VERSION
711 #endif /* not DEBUG */
714 read_from_dump(dumpfile)
717 /* Not yet implemented. */