]> asedeno.scripts.mit.edu Git - 1ts-debian.git/blob - zephyr/zhm/zhm.c
a53badaa21a52d8e3d6f53b516477772c1f76fa3
[1ts-debian.git] / zephyr / zhm / zhm.c
1 /* This file is part of the Project Athena Zephyr Notification System.
2  * It contains the hostmanager client program.
3  *
4  *      Created by:     David C. Jedlinsky
5  *
6  *      $Id: zhm.c,v 1.62 2000/05/19 23:19:21 zacheiss Exp $
7  *
8  *      Copyright (c) 1987,1991 by the Massachusetts Institute of Technology.
9  *      For copying and distribution information, see the file
10  *      "mit-copyright.h". 
11  */
12
13 #include "zhm.h"
14
15 static const char rcsid_hm_c[] = "$Id: zhm.c,v 1.62 2000/05/19 23:19:21 zacheiss Exp $";
16
17 #ifdef HAVE_HESIOD
18 int use_hesiod = 0;
19 #endif
20
21 #ifdef macII
22 #define srandom srand48
23 #endif
24
25 #define PIDDIR "/var/athena/"
26
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;
30 long starttime;
31 u_short cli_port;
32 struct sockaddr_in cli_sin, serv_sin, from;
33 int numserv;
34 char **serv_list = NULL;
35 char prim_serv[MAXHOSTNAMELEN], cur_serv[MAXHOSTNAMELEN];
36 char *zcluster;
37 int deactivating = 0;
38 int terminating = 0;
39 struct hostent *hp;
40 char hostname[MAXHOSTNAMELEN], loopback[4];
41 char PidFile[128];
42
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 *));
50 extern int optind;
51
52 static RETSIGTYPE deactivate()
53 {
54     deactivating = 1;
55 }
56
57 static RETSIGTYPE terminate()
58 {
59     terminating = 1;
60 }
61
62 main(argc, argv)
63 char *argv[];
64 {
65     ZNotice_t notice;
66     ZPacket_t packet;
67     Code_t ret;
68     int opt, pak_len, i, j = 0, fd, count;
69     fd_set readers;
70     struct timeval tv;
71
72     sprintf(PidFile, "%szhm.pid", PIDDIR);
73
74     if (gethostname(hostname, MAXHOSTNAMELEN) < 0) {
75         printf("Can't find my hostname?!\n");
76         exit(-1);
77     }
78     prim_serv[0] = '\0';
79     while ((opt = getopt(argc, argv, "drhinf")) != EOF)
80         switch(opt) {
81           case 'd':
82             hmdebug = 1;
83             break;
84           case 'h':
85             /* Die on SIGHUP */
86             dieflag = 1;
87             break;
88           case 'r':
89             /* Reboot host -- send boot notice -- and exit */
90             rebootflag= 1;
91             break;
92           case 'i':
93             /* inetd operation: don't do bind ourselves, fd 0 is
94                already connected to a socket. Implies -h */
95             inetd = 1;
96             dieflag = 1;
97             break;
98           case 'n':
99             nofork = 1;
100             break;
101           case 'f':
102             noflushflag = 1;
103             break;
104           case '?':
105           default:
106             errflg++;
107             break;
108         }
109     if (errflg) {
110         fprintf(stderr, "Usage: %s [-d] [-h] [-r] [-n] [-f] [server]\n", 
111                 argv[0]);       
112         exit(2);
113     }
114
115     numserv = 0;
116
117     /* Override server argument? */
118     if (optind < argc) {
119         if ((hp = gethostbyname(argv[optind++])) == NULL) {
120             printf("Unknown server name: %s\n", argv[optind-1]);
121         } else {
122             strncpy(prim_serv, hp->h_name, sizeof(prim_serv));
123             prim_serv[sizeof(prim_serv) - 1] = '\0';
124         }
125
126         /* argc-optind is the # of other servers on the command line */
127         serv_list = (char **) malloc((argc - optind + 2) * sizeof(char *));
128         if (serv_list == NULL) {
129             printf("Out of memory.\n");
130             exit(-5);
131         }
132         serv_list[numserv++] = prim_serv;
133         for (; optind < argc; optind++) {
134             if ((hp = gethostbyname(argv[optind])) == NULL) {
135                 printf("Unknown server name '%s', ignoring\n", argv[optind]);
136                 continue;
137             }
138              serv_list[numserv++] = strsave(hp->h_name);
139         }
140         serv_list[numserv] = NULL;
141     }
142 #ifdef HAVE_HESIOD
143     else
144         use_hesiod = 1;
145 #endif
146
147     choose_server();
148     if (*prim_serv == '\0') {
149         printf("No valid primary server found, exiting.\n");
150         exit(ZERR_SERVNAK);
151     }
152     init_hm();
153
154     DPR2("zephyr server port: %u\n", ntohs(serv_sin.sin_port));
155     DPR2("zephyr client port: %u\n", ntohs(cli_port));
156   
157     /* Main loop */
158     for ever {
159         /* Wait for incoming packets or queue timeouts. */
160         DPR("Waiting for a packet...");
161         fd = ZGetFD();
162         FD_ZERO(&readers);
163         FD_SET(fd, &readers);
164         count = select(fd + 1, &readers, NULL, NULL, timer_timeout(&tv));
165         if (count == -1 && errno != EINTR) {
166             syslog(LOG_CRIT, "select() failed: %m");
167             die_gracefully();
168         }
169
170         if (terminating)
171             die_gracefully();
172
173         if (deactivating) {
174             deactivating = 0;
175             if (dieflag) {
176                 die_gracefully();
177             } else {
178                 choose_server();
179                 send_flush_notice(HM_FLUSH);
180                 deactivated = 1;
181             }
182         }
183
184         timer_process();
185
186         if (count > 0) {
187             ret = ZReceivePacket(packet, &pak_len, &from);
188             if ((ret != ZERR_NONE) && (ret != EINTR)){
189                 Zperr(ret);
190                 com_err("hm", ret, "receiving notice");
191             } else if (ret != EINTR) {
192                 /* Where did it come from? */
193                 if ((ret = ZParseNotice(packet, pak_len, &notice))
194                     != ZERR_NONE) {
195                     Zperr(ret);
196                     com_err("hm", ret, "parsing notice");
197                 } else {
198                     DPR("Got a packet.\n");
199                     DPR("notice:\n");
200                     DPR2("\tz_kind: %d\n", notice.z_kind);
201                     DPR2("\tz_port: %u\n", ntohs(notice.z_port));
202                     DPR2("\tz_class: %s\n", notice.z_class);
203                     DPR2("\tz_class_inst: %s\n", notice.z_class_inst);
204                     DPR2("\tz_opcode: %s\n", notice.z_opcode);
205                     DPR2("\tz_sender: %s\n", notice.z_sender);
206                     DPR2("\tz_recip: %s\n", notice.z_recipient);
207                     DPR2("\tz_def_format: %s\n", notice.z_default_format);
208                     DPR2("\tz_message: %s\n", notice.z_message);
209                     if (memcmp(loopback, &from.sin_addr, 4) &&
210                         ((notice.z_kind == SERVACK) ||
211                          (notice.z_kind == SERVNAK) ||
212                          (notice.z_kind == HMCTL))) {
213                         server_manager(&notice);
214                     } else {
215                         if (!memcmp(loopback, &from.sin_addr, 4) &&
216                             ((notice.z_kind == UNSAFE) ||
217                              (notice.z_kind == UNACKED) ||
218                              (notice.z_kind == ACKED) ||
219                              (notice.z_kind == HMCTL))) {
220                             /* Client program... */
221                             if (deactivated) {
222                                 send_boot_notice(HM_BOOT);
223                                 deactivated = 0;
224                             }
225                             transmission_tower(&notice, packet, pak_len);
226                             DPR2("Pending = %d\n", ZPending());
227                         } else {
228                             if (notice.z_kind == STAT) {
229                                 send_stats(&notice, &from);
230                             } else {
231                                 syslog(LOG_INFO,
232                                        "Unknown notice type: %d",
233                                        notice.z_kind);
234                             }
235                         }       
236                     }
237                 }
238             }
239         }
240     }
241 }
242
243 static void choose_server()
244 {
245     int i = 0;
246     char **clust_info, **cpp;
247
248 #ifdef HAVE_HESIOD
249     if (use_hesiod) {
250
251         /* Free up any previously used resources */
252         if (prim_serv[0]) 
253             i = 1;
254         while (i < numserv)
255             free(serv_list[i++]);
256         if (serv_list)
257             free(serv_list);
258         
259         numserv = 0;
260         prim_serv[0] = '\0';
261         
262         if ((clust_info = hes_resolve(hostname, "CLUSTER")) == NULL) {
263             zcluster = NULL;
264         } else {
265             for (cpp = clust_info; *cpp; cpp++) {
266                 /* Remove the following check once we have changed over to
267                  * new Hesiod format (i.e. ZCLUSTER.sloc lookup, no primary
268                  * server
269                  */
270                 if (!strncasecmp("ZEPHYR", *cpp, 6)) {
271                     register char *c;
272                 
273                     if ((c = strchr(*cpp, ' ')) == 0) {
274                         printf("Hesiod error getting primary server info.\n");
275                     } else {
276                         strncpy(prim_serv, c+1, sizeof(prim_serv));
277                         prim_serv[sizeof(prim_serv) - 1] = '\0';
278                     }
279                     break;
280                 }
281                 if (!strncasecmp("ZCLUSTER", *cpp, 9)) {
282                     register char *c;
283                 
284                     if ((c = strchr(*cpp, ' ')) == 0) {
285                         printf("Hesiod error getting zcluster info.\n");
286                     } else {
287                         if ((zcluster = malloc((unsigned)(strlen(c+1)+1)))
288                             != NULL) {
289                             strcpy(zcluster, c+1);
290                         } else {
291                             printf("Out of memory.\n");
292                             exit(-5);
293                         }
294                     }
295                     break;
296                 }
297             }
298             for (cpp = clust_info; *cpp; cpp++)
299                 free(*cpp);
300         }
301
302         if (zcluster == NULL) {
303             if ((zcluster = malloc((unsigned)(strlen("zephyr")+1))) != NULL)
304                 strcpy(zcluster, "zephyr");
305             else {
306                 printf("Out of memory.\n");
307                 exit(-5);
308             }
309         }
310         while ((serv_list = hes_resolve(zcluster, "sloc")) == (char **)NULL) {
311             syslog(LOG_ERR, "No servers or no hesiod");
312             /* wait a bit, and try again */
313             sleep(30);
314         }
315         cpp = (char **) malloc(2 * sizeof(char *));
316         if (cpp == NULL) {
317             printf("Out of memory.\n");
318             exit(-5);
319         }
320         if (prim_serv[0])
321             cpp[numserv++] = prim_serv;
322         for (i = 0; serv_list[i]; i++) {
323             /* copy in non-duplicates */
324             /* assume the names returned in the sloc are full domain names */
325             if (!prim_serv[0] || strcasecmp(prim_serv, serv_list[i])) {
326                 cpp = (char **) realloc(cpp, (numserv+2) * sizeof(char *));
327                 if (cpp == NULL) {
328                     printf("Out of memory.\n");
329                     exit(-5);
330                 }
331                 cpp[numserv++] = strsave(serv_list[i]);
332             }
333         }
334         for (i = 0; serv_list[i]; i++)
335             free(serv_list[i]);
336         cpp[numserv] = NULL;
337         serv_list = cpp;
338     }
339 #endif
340     
341     if (!prim_serv[0] && numserv) {
342         srandom(time(NULL));
343         strncpy(prim_serv, serv_list[random() % numserv], sizeof(prim_serv));
344         prim_serv[sizeof(prim_serv) - 1] = '\0';
345     }
346 }
347
348 static void init_hm()
349 {
350      struct servent *sp;
351      Code_t ret;
352      FILE *fp;
353 #ifdef _POSIX_VERSION
354      struct sigaction sa;
355 #endif
356
357      starttime = time((time_t *)0);
358      OPENLOG("hm", LOG_PID, LOG_DAEMON);
359   
360      ZSetServerState(1);        /* Aargh!!! */
361      if ((ret = ZInitialize()) != ZERR_NONE) {
362          Zperr(ret);
363          com_err("hm", ret, "initializing");
364          closelog();
365          exit(-1);
366      }
367      init_queue();
368
369      if (*prim_serv == '\0') {
370          strncpy(prim_serv, *serv_list, sizeof(prim_serv));
371          prim_serv[sizeof(prim_serv) - 1] = '\0';
372      }
373   
374      loopback[0] = 127;
375      loopback[1] = 0;
376      loopback[2] = 0;
377      loopback[3] = 1;
378       
379      if (inetd) {
380          ZSetFD(0);             /* fd 0 is on the socket, thanks to inetd */
381      } else {
382          /* Open client socket, for receiving client and server notices */
383          sp = getservbyname(HM_SVCNAME, "udp");
384          cli_port = (sp) ? sp->s_port : HM_SVC_FALLBACK;
385       
386          if ((ret = ZOpenPort(&cli_port)) != ZERR_NONE) {
387              Zperr(ret);
388              com_err("hm", ret, "opening port");
389              exit(ret);
390          }
391      }
392      cli_sin = ZGetDestAddr();
393
394      sp = getservbyname(SERVER_SVCNAME, "udp");
395      memset(&serv_sin, 0, sizeof(struct sockaddr_in));
396      serv_sin.sin_port = (sp) ? sp->s_port : SERVER_SVC_FALLBACK;
397       
398 #ifndef DEBUG
399      if (!inetd && !nofork)
400          detach();
401   
402      /* Write pid to file */
403      fp = fopen(PidFile, "w");
404      if (fp != NULL) {
405          fprintf(fp, "%d\n", getpid());
406          fclose(fp);
407      }
408 #endif /* DEBUG */
409
410      if (hmdebug) {
411           syslog(LOG_INFO, "Debugging on.");
412      }
413
414      /* Set up communications with server */
415      /* target is SERVER_SVCNAME port on server machine */
416
417      serv_sin.sin_family = AF_INET;
418   
419      /* who to talk to */
420      if ((hp = gethostbyname(prim_serv)) == NULL) {
421           DPR("gethostbyname failed\n");
422           find_next_server(NULL);
423      } else {
424           DPR2("Server = %s\n", prim_serv);
425           strncpy(cur_serv, prim_serv, sizeof(cur_serv));
426           cur_serv[sizeof(cur_serv) - 1] = '\0';        
427           memcpy(&serv_sin.sin_addr, hp->h_addr, 4);
428      }
429
430      send_boot_notice(HM_BOOT);
431      deactivated = 0;
432
433 #ifdef _POSIX_VERSION
434      sigemptyset(&sa.sa_mask);
435      sa.sa_flags = 0;
436      sa.sa_handler = deactivate;
437      sigaction(SIGHUP, &sa, (struct sigaction *)0);
438      sa.sa_handler = terminate; 
439      sigaction(SIGTERM, &sa, (struct sigaction *)0);
440 #else
441      signal(SIGHUP, deactivate);
442      signal(SIGTERM, terminate);
443 #endif
444 }
445
446 static void detach()
447 {
448      /* detach from terminal and fork. */
449      register int i, x = ZGetFD();
450      register long size;
451   
452      if (i = fork()) {
453           if (i < 0)
454                perror("fork");
455           exit(0);
456      }
457 #ifdef _POSIX_VERSION
458      size = sysconf(_SC_OPEN_MAX);
459 #else
460      size = getdtablesize();
461 #endif
462      for (i = 0; i < size; i++)
463           if (i != x)
464                close(i);
465
466      if ((i = open("/dev/tty", O_RDWR, 0666)) < 0)
467           ;             /* Can't open tty, but don't flame about it. */
468      else {
469 #ifdef TIOCNOTTY
470           /* Necessary for old non-POSIX systems which automatically assign
471            * an opened tty as the controlling terminal of a process which
472            * doesn't already have one.  POSIX systems won't include
473            * <sys/ioctl.h> (see ../h/sysdep.h); if TIOCNOTTY is defined anyway,
474            * this is unnecessary but won't hurt. */
475           ioctl(i, TIOCNOTTY, (caddr_t) 0);
476 #endif
477           close(i);
478      }
479 #ifdef _POSIX_VERSION
480      setsid();
481 #endif
482 }
483
484 static char version[BUFSIZ];
485
486 static void send_stats(notice, sin)
487      ZNotice_t *notice;
488      struct sockaddr_in *sin;
489 {
490      ZNotice_t newnotice;
491      Code_t ret;
492      char *bfr;
493      char *list[20];
494      int len, i, nitems = 10;
495      unsigned long size;
496
497      newnotice = *notice;
498      
499      if ((ret = ZSetDestAddr(sin)) != ZERR_NONE) {
500           Zperr(ret);
501           com_err("hm", ret, "setting destination");
502      }
503      newnotice.z_kind = HMACK;
504
505      list[0] = (char *) malloc(MAXHOSTNAMELEN);
506      if (list[0] == NULL) {
507        printf("Out of memory.\n");
508        exit(-5);
509      }
510      strcpy(list[0], cur_serv);
511      list[1] = (char *) malloc(64);
512      if (list[1] == NULL) {
513        printf("Out of memory.\n");
514        exit(-5);
515      }
516      sprintf(list[1], "%d", queue_len());
517      list[2] = (char *) malloc(64);
518      if (list[2] == NULL) {
519        printf("Out of memory.\n");
520        exit(-5);
521      }
522      sprintf(list[2], "%d", nclt);
523      list[3] = (char *) malloc(64);
524      if (list[3] == NULL) {
525        printf("Out of memory.\n");
526        exit(-5);
527      }
528      sprintf(list[3], "%d", nserv);
529      list[4] = (char *) malloc(64);
530      if (list[4] == NULL) {
531        printf("Out of memory.\n");
532        exit(-5);
533      }
534      sprintf(list[4], "%d", nservchang);
535      list[5] = (char *) malloc(64);
536      if (list[5] == NULL) {
537        printf("Out of memory.\n");
538        exit(-5);
539      }
540      strncpy(list[5], rcsid_hm_c, 64);
541      list[5][63] = '\0';
542      
543      list[6] = (char *) malloc(64);
544      if (list[6] == NULL) {
545        printf("Out of memory.\n");
546        exit(-5);
547      }
548      if (no_server)
549           sprintf(list[6], "yes");
550      else
551           sprintf(list[6], "no");
552      list[7] = (char *) malloc(64);
553      if (list[7] == NULL) {
554        printf("Out of memory.\n");
555        exit(-5);
556      }
557      sprintf(list[7], "%ld", time((time_t *)0) - starttime);
558 #ifdef adjust_size
559      size = (unsigned long)sbrk(0);
560      adjust_size (size);
561 #else
562      size = -1;
563 #endif
564      list[8] = (char *)malloc(64);
565      if (list[8] == NULL) {
566        printf("Out of memory.\n");
567        exit(-5);
568      }
569      sprintf(list[8], "%ld", size);
570      list[9] = (char *)malloc(32);
571      if (list[9] == NULL) {
572        printf("Out of memory.\n");
573        exit(-5);
574      }
575      strncpy(list[9], MACHINE_TYPE, 32);
576      list[9][31] = '\0';
577
578      /* Since ZFormatRaw* won't change the version number on notices,
579         we need to set the version number explicitly.  This code is taken
580         from Zinternal.c, function Z_FormatHeader */
581      if (!*version)
582              sprintf(version, "%s%d.%d", ZVERSIONHDR, ZVERSIONMAJOR,
583                      ZVERSIONMINOR);
584      newnotice.z_version = version;
585
586      if ((ret = ZFormatRawNoticeList(&newnotice, list, nitems, &bfr,
587                                      &len)) != ZERR_NONE) {
588          syslog(LOG_INFO, "Couldn't format stats packet");
589      } else {
590          if ((ret = ZSendPacket(bfr, len, 0)) != ZERR_NONE) {
591              Zperr(ret);
592              com_err("hm", ret, "sending stats");
593          }
594      }
595      free(bfr);
596      for(i=0;i<nitems;i++)
597           free(list[i]);
598 }
599
600 void die_gracefully()
601 {
602      syslog(LOG_INFO, "Terminate signal caught...");
603      send_flush_notice(HM_FLUSH);
604      unlink(PidFile);
605      closelog();
606      exit(0);
607 }
608
609 static char *strsave(sp)
610     const char *sp;
611 {
612     register char *ret;
613
614     if((ret = malloc((unsigned) strlen(sp)+1)) == NULL) {
615             abort();
616     }
617     strcpy(ret,sp);
618     return(ret);
619 }