1 /* This file is part of the Project Athena Zephyr Notification System.
2 * It contains the hostmanager client program.
4 * Created by: David C. Jedlinsky
6 * $Id: zhm.c,v 1.61 2000/04/05 14:57:36 ghudson Exp $
8 * Copyright (c) 1987,1991 by the Massachusetts Institute of Technology.
9 * For copying and distribution information, see the file
15 static const char rcsid_hm_c[] = "$Id: zhm.c,v 1.61 2000/04/05 14:57:36 ghudson Exp $";
22 #define srandom srand48
25 #define PIDDIR "/var/athena/"
27 int hmdebug, rebootflag, noflushflag, errflg, dieflag, inetd, oldpid, nofork;
28 int no_server = 1, nservchang, nserv, nclt;
29 int booting = 1, timeout_type, deactivated = 1;
32 struct sockaddr_in cli_sin, serv_sin, from;
34 char **serv_list = NULL;
35 char prim_serv[MAXHOSTNAMELEN], cur_serv[MAXHOSTNAMELEN];
40 char hostname[MAXHOSTNAMELEN], loopback[4];
43 static RETSIGTYPE deactivate __P((void));
44 static RETSIGTYPE terminate __P((void));
45 static void choose_server __P((void));
46 static void init_hm __P((void));
47 static void detach __P((void));
48 static void send_stats __P((ZNotice_t *, struct sockaddr_in *));
49 static char *strsave __P((const char *));
52 static RETSIGTYPE deactivate()
57 static RETSIGTYPE terminate()
68 int opt, pak_len, i, j = 0, fd, count;
72 sprintf(PidFile, "%szhm.pid", PIDDIR);
74 if (gethostname(hostname, MAXHOSTNAMELEN) < 0) {
75 printf("Can't find my hostname?!\n");
79 while ((opt = getopt(argc, argv, "drhinf")) != EOF)
89 /* Reboot host -- send boot notice -- and exit */
93 /* inetd operation: don't do bind ourselves, fd 0 is
94 already connected to a socket. Implies -h */
110 fprintf(stderr, "Usage: %s [-d] [-h] [-r] [-n] [server]\n", argv[0]);
116 /* Override server argument? */
118 if ((hp = gethostbyname(argv[optind++])) == NULL) {
119 printf("Unknown server name: %s\n", argv[optind-1]);
121 strncpy(prim_serv, hp->h_name, sizeof(prim_serv));
122 prim_serv[sizeof(prim_serv) - 1] = '\0';
125 /* argc-optind is the # of other servers on the command line */
126 serv_list = (char **) malloc((argc - optind + 2) * sizeof(char *));
127 if (serv_list == NULL) {
128 printf("Out of memory.\n");
131 serv_list[numserv++] = prim_serv;
132 for (; optind < argc; optind++) {
133 if ((hp = gethostbyname(argv[optind])) == NULL) {
134 printf("Unknown server name '%s', ignoring\n", argv[optind]);
137 serv_list[numserv++] = strsave(hp->h_name);
139 serv_list[numserv] = NULL;
147 if (*prim_serv == '\0') {
148 printf("No valid primary server found, exiting.\n");
153 DPR2("zephyr server port: %u\n", ntohs(serv_sin.sin_port));
154 DPR2("zephyr client port: %u\n", ntohs(cli_port));
158 /* Wait for incoming packets or queue timeouts. */
159 DPR("Waiting for a packet...");
162 FD_SET(fd, &readers);
163 count = select(fd + 1, &readers, NULL, NULL, timer_timeout(&tv));
164 if (count == -1 && errno != EINTR) {
165 syslog(LOG_CRIT, "select() failed: %m");
178 send_flush_notice(HM_FLUSH);
186 ret = ZReceivePacket(packet, &pak_len, &from);
187 if ((ret != ZERR_NONE) && (ret != EINTR)){
189 com_err("hm", ret, "receiving notice");
190 } else if (ret != EINTR) {
191 /* Where did it come from? */
192 if ((ret = ZParseNotice(packet, pak_len, ¬ice))
195 com_err("hm", ret, "parsing notice");
197 DPR("Got a packet.\n");
199 DPR2("\tz_kind: %d\n", notice.z_kind);
200 DPR2("\tz_port: %u\n", ntohs(notice.z_port));
201 DPR2("\tz_class: %s\n", notice.z_class);
202 DPR2("\tz_class_inst: %s\n", notice.z_class_inst);
203 DPR2("\tz_opcode: %s\n", notice.z_opcode);
204 DPR2("\tz_sender: %s\n", notice.z_sender);
205 DPR2("\tz_recip: %s\n", notice.z_recipient);
206 DPR2("\tz_def_format: %s\n", notice.z_default_format);
207 DPR2("\tz_message: %s\n", notice.z_message);
208 if (memcmp(loopback, &from.sin_addr, 4) &&
209 ((notice.z_kind == SERVACK) ||
210 (notice.z_kind == SERVNAK) ||
211 (notice.z_kind == HMCTL))) {
212 server_manager(¬ice);
214 if (!memcmp(loopback, &from.sin_addr, 4) &&
215 ((notice.z_kind == UNSAFE) ||
216 (notice.z_kind == UNACKED) ||
217 (notice.z_kind == ACKED) ||
218 (notice.z_kind == HMCTL))) {
219 /* Client program... */
221 send_boot_notice(HM_BOOT);
224 transmission_tower(¬ice, packet, pak_len);
225 DPR2("Pending = %d\n", ZPending());
227 if (notice.z_kind == STAT) {
228 send_stats(¬ice, &from);
231 "Unknown notice type: %d",
242 static void choose_server()
245 char **clust_info, **cpp;
250 /* Free up any previously used resources */
254 free(serv_list[i++]);
261 if ((clust_info = hes_resolve(hostname, "CLUSTER")) == NULL) {
264 for (cpp = clust_info; *cpp; cpp++) {
265 /* Remove the following check once we have changed over to
266 * new Hesiod format (i.e. ZCLUSTER.sloc lookup, no primary
269 if (!strncasecmp("ZEPHYR", *cpp, 6)) {
272 if ((c = strchr(*cpp, ' ')) == 0) {
273 printf("Hesiod error getting primary server info.\n");
275 strncpy(prim_serv, c+1, sizeof(prim_serv));
276 prim_serv[sizeof(prim_serv) - 1] = '\0';
280 if (!strncasecmp("ZCLUSTER", *cpp, 9)) {
283 if ((c = strchr(*cpp, ' ')) == 0) {
284 printf("Hesiod error getting zcluster info.\n");
286 if ((zcluster = malloc((unsigned)(strlen(c+1)+1)))
288 strcpy(zcluster, c+1);
290 printf("Out of memory.\n");
297 for (cpp = clust_info; *cpp; cpp++)
301 if (zcluster == NULL) {
302 if ((zcluster = malloc((unsigned)(strlen("zephyr")+1))) != NULL)
303 strcpy(zcluster, "zephyr");
305 printf("Out of memory.\n");
309 while ((serv_list = hes_resolve(zcluster, "sloc")) == (char **)NULL) {
310 syslog(LOG_ERR, "No servers or no hesiod");
311 /* wait a bit, and try again */
314 cpp = (char **) malloc(2 * sizeof(char *));
316 printf("Out of memory.\n");
320 cpp[numserv++] = prim_serv;
321 for (i = 0; serv_list[i]; i++) {
322 /* copy in non-duplicates */
323 /* assume the names returned in the sloc are full domain names */
324 if (!prim_serv[0] || strcasecmp(prim_serv, serv_list[i])) {
325 cpp = (char **) realloc(cpp, (numserv+2) * sizeof(char *));
327 printf("Out of memory.\n");
330 cpp[numserv++] = strsave(serv_list[i]);
333 for (i = 0; serv_list[i]; i++)
340 if (!prim_serv[0] && numserv) {
342 strncpy(prim_serv, serv_list[random() % numserv], sizeof(prim_serv));
343 prim_serv[sizeof(prim_serv) - 1] = '\0';
347 static void init_hm()
352 #ifdef _POSIX_VERSION
356 starttime = time((time_t *)0);
357 OPENLOG("hm", LOG_PID, LOG_DAEMON);
359 ZSetServerState(1); /* Aargh!!! */
360 if ((ret = ZInitialize()) != ZERR_NONE) {
362 com_err("hm", ret, "initializing");
368 if (*prim_serv == '\0') {
369 strncpy(prim_serv, *serv_list, sizeof(prim_serv));
370 prim_serv[sizeof(prim_serv) - 1] = '\0';
379 ZSetFD(0); /* fd 0 is on the socket, thanks to inetd */
381 /* Open client socket, for receiving client and server notices */
382 sp = getservbyname(HM_SVCNAME, "udp");
383 cli_port = (sp) ? sp->s_port : HM_SVC_FALLBACK;
385 if ((ret = ZOpenPort(&cli_port)) != ZERR_NONE) {
387 com_err("hm", ret, "opening port");
391 cli_sin = ZGetDestAddr();
393 sp = getservbyname(SERVER_SVCNAME, "udp");
394 memset(&serv_sin, 0, sizeof(struct sockaddr_in));
395 serv_sin.sin_port = (sp) ? sp->s_port : SERVER_SVC_FALLBACK;
398 if (!inetd && !nofork)
401 /* Write pid to file */
402 fp = fopen(PidFile, "w");
404 fprintf(fp, "%d\n", getpid());
410 syslog(LOG_INFO, "Debugging on.");
413 /* Set up communications with server */
414 /* target is SERVER_SVCNAME port on server machine */
416 serv_sin.sin_family = AF_INET;
419 if ((hp = gethostbyname(prim_serv)) == NULL) {
420 DPR("gethostbyname failed\n");
421 find_next_server(NULL);
423 DPR2("Server = %s\n", prim_serv);
424 strncpy(cur_serv, prim_serv, sizeof(cur_serv));
425 cur_serv[sizeof(cur_serv) - 1] = '\0';
426 memcpy(&serv_sin.sin_addr, hp->h_addr, 4);
429 send_boot_notice(HM_BOOT);
432 #ifdef _POSIX_VERSION
433 sigemptyset(&sa.sa_mask);
435 sa.sa_handler = deactivate;
436 sigaction(SIGHUP, &sa, (struct sigaction *)0);
437 sa.sa_handler = terminate;
438 sigaction(SIGTERM, &sa, (struct sigaction *)0);
440 signal(SIGHUP, deactivate);
441 signal(SIGTERM, terminate);
447 /* detach from terminal and fork. */
448 register int i, x = ZGetFD();
456 #ifdef _POSIX_VERSION
457 size = sysconf(_SC_OPEN_MAX);
459 size = getdtablesize();
461 for (i = 0; i < size; i++)
465 if ((i = open("/dev/tty", O_RDWR, 0666)) < 0)
466 ; /* Can't open tty, but don't flame about it. */
469 /* Necessary for old non-POSIX systems which automatically assign
470 * an opened tty as the controlling terminal of a process which
471 * doesn't already have one. POSIX systems won't include
472 * <sys/ioctl.h> (see ../h/sysdep.h); if TIOCNOTTY is defined anyway,
473 * this is unnecessary but won't hurt. */
474 ioctl(i, TIOCNOTTY, (caddr_t) 0);
478 #ifdef _POSIX_VERSION
483 static char version[BUFSIZ];
485 static void send_stats(notice, sin)
487 struct sockaddr_in *sin;
493 int len, i, nitems = 10;
498 if ((ret = ZSetDestAddr(sin)) != ZERR_NONE) {
500 com_err("hm", ret, "setting destination");
502 newnotice.z_kind = HMACK;
504 list[0] = (char *) malloc(MAXHOSTNAMELEN);
505 if (list[0] == NULL) {
506 printf("Out of memory.\n");
509 strcpy(list[0], cur_serv);
510 list[1] = (char *) malloc(64);
511 if (list[1] == NULL) {
512 printf("Out of memory.\n");
515 sprintf(list[1], "%d", queue_len());
516 list[2] = (char *) malloc(64);
517 if (list[2] == NULL) {
518 printf("Out of memory.\n");
521 sprintf(list[2], "%d", nclt);
522 list[3] = (char *) malloc(64);
523 if (list[3] == NULL) {
524 printf("Out of memory.\n");
527 sprintf(list[3], "%d", nserv);
528 list[4] = (char *) malloc(64);
529 if (list[4] == NULL) {
530 printf("Out of memory.\n");
533 sprintf(list[4], "%d", nservchang);
534 list[5] = (char *) malloc(64);
535 if (list[5] == NULL) {
536 printf("Out of memory.\n");
539 strncpy(list[5], rcsid_hm_c, 64);
542 list[6] = (char *) malloc(64);
543 if (list[6] == NULL) {
544 printf("Out of memory.\n");
548 sprintf(list[6], "yes");
550 sprintf(list[6], "no");
551 list[7] = (char *) malloc(64);
552 if (list[7] == NULL) {
553 printf("Out of memory.\n");
556 sprintf(list[7], "%ld", time((time_t *)0) - starttime);
558 size = (unsigned long)sbrk(0);
563 list[8] = (char *)malloc(64);
564 if (list[8] == NULL) {
565 printf("Out of memory.\n");
568 sprintf(list[8], "%ld", size);
569 list[9] = (char *)malloc(32);
570 if (list[9] == NULL) {
571 printf("Out of memory.\n");
574 strncpy(list[9], MACHINE_TYPE, 32);
577 /* Since ZFormatRaw* won't change the version number on notices,
578 we need to set the version number explicitly. This code is taken
579 from Zinternal.c, function Z_FormatHeader */
581 sprintf(version, "%s%d.%d", ZVERSIONHDR, ZVERSIONMAJOR,
583 newnotice.z_version = version;
585 if ((ret = ZFormatRawNoticeList(&newnotice, list, nitems, &bfr,
586 &len)) != ZERR_NONE) {
587 syslog(LOG_INFO, "Couldn't format stats packet");
589 if ((ret = ZSendPacket(bfr, len, 0)) != ZERR_NONE) {
591 com_err("hm", ret, "sending stats");
595 for(i=0;i<nitems;i++)
599 void die_gracefully()
601 syslog(LOG_INFO, "Terminate signal caught...");
602 send_flush_notice(HM_FLUSH);
608 static char *strsave(sp)
613 if((ret = malloc((unsigned) strlen(sp)+1)) == NULL) {