]> asedeno.scripts.mit.edu Git - 1ts-debian.git/blob - zhm/zhm.c
Fix 100465
[1ts-debian.git] / 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.61 2000/04/05 14:57:36 ghudson 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.61 2000/04/05 14:57:36 ghudson 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/run/"
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] [server]\n", argv[0]);
111         exit(2);
112     }
113
114     numserv = 0;
115
116     /* Override server argument? */
117     if (optind < argc) {
118         if ((hp = gethostbyname(argv[optind++])) == NULL) {
119             printf("Unknown server name: %s\n", argv[optind-1]);
120         } else {
121             strncpy(prim_serv, hp->h_name, sizeof(prim_serv));
122             prim_serv[sizeof(prim_serv) - 1] = '\0';
123         }
124
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");
129             exit(-5);
130         }
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]);
135                 continue;
136             }
137              serv_list[numserv++] = strsave(hp->h_name);
138         }
139         serv_list[numserv] = NULL;
140     }
141 #ifdef HAVE_HESIOD
142     else
143         use_hesiod = 1;
144 #endif
145
146     choose_server();
147     if (*prim_serv == '\0') {
148         printf("No valid primary server found, exiting.\n");
149         exit(ZERR_SERVNAK);
150     }
151     init_hm();
152
153     DPR2("zephyr server port: %u\n", ntohs(serv_sin.sin_port));
154     DPR2("zephyr client port: %u\n", ntohs(cli_port));
155   
156     /* Main loop */
157     for ever {
158         /* Wait for incoming packets or queue timeouts. */
159         DPR("Waiting for a packet...");
160         fd = ZGetFD();
161         FD_ZERO(&readers);
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");
166             die_gracefully();
167         }
168
169         if (terminating)
170             die_gracefully();
171
172         if (deactivating) {
173             deactivating = 0;
174             if (dieflag) {
175                 die_gracefully();
176             } else {
177                 choose_server();
178                 send_flush_notice(HM_FLUSH);
179                 deactivated = 1;
180             }
181         }
182
183         timer_process();
184
185         if (count > 0) {
186             ret = ZReceivePacket(packet, &pak_len, &from);
187             if ((ret != ZERR_NONE) && (ret != EINTR)){
188                 Zperr(ret);
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, &notice))
193                     != ZERR_NONE) {
194                     Zperr(ret);
195                     com_err("hm", ret, "parsing notice");
196                 } else {
197                     DPR("Got a packet.\n");
198                     DPR("notice:\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(&notice);
213                     } else {
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... */
220                             if (deactivated) {
221                                 send_boot_notice(HM_BOOT);
222                                 deactivated = 0;
223                             }
224                             transmission_tower(&notice, packet, pak_len);
225                             DPR2("Pending = %d\n", ZPending());
226                         } else {
227                             if (notice.z_kind == STAT) {
228                                 send_stats(&notice, &from);
229                             } else {
230                                 syslog(LOG_INFO,
231                                        "Unknown notice type: %d",
232                                        notice.z_kind);
233                             }
234                         }       
235                     }
236                 }
237             }
238         }
239     }
240 }
241
242 static void choose_server()
243 {
244     int i = 0;
245     char **clust_info, **cpp;
246
247 #ifdef HAVE_HESIOD
248     if (use_hesiod) {
249
250         /* Free up any previously used resources */
251         if (prim_serv[0]) 
252             i = 1;
253         while (i < numserv)
254             free(serv_list[i++]);
255         if (serv_list)
256             free(serv_list);
257         
258         numserv = 0;
259         prim_serv[0] = '\0';
260         
261         if ((clust_info = hes_resolve(hostname, "CLUSTER")) == NULL) {
262             zcluster = NULL;
263         } else {
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
267                  * server
268                  */
269                 if (!strncasecmp("ZEPHYR", *cpp, 6)) {
270                     register char *c;
271                 
272                     if ((c = strchr(*cpp, ' ')) == 0) {
273                         printf("Hesiod error getting primary server info.\n");
274                     } else {
275                         strncpy(prim_serv, c+1, sizeof(prim_serv));
276                         prim_serv[sizeof(prim_serv) - 1] = '\0';
277                     }
278                     break;
279                 }
280                 if (!strncasecmp("ZCLUSTER", *cpp, 9)) {
281                     register char *c;
282                 
283                     if ((c = strchr(*cpp, ' ')) == 0) {
284                         printf("Hesiod error getting zcluster info.\n");
285                     } else {
286                         if ((zcluster = malloc((unsigned)(strlen(c+1)+1)))
287                             != NULL) {
288                             strcpy(zcluster, c+1);
289                         } else {
290                             printf("Out of memory.\n");
291                             exit(-5);
292                         }
293                     }
294                     break;
295                 }
296             }
297             for (cpp = clust_info; *cpp; cpp++)
298                 free(*cpp);
299         }
300
301         if (zcluster == NULL) {
302             if ((zcluster = malloc((unsigned)(strlen("zephyr")+1))) != NULL)
303                 strcpy(zcluster, "zephyr");
304             else {
305                 printf("Out of memory.\n");
306                 exit(-5);
307             }
308         }
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 */
312             sleep(30);
313         }
314         cpp = (char **) malloc(2 * sizeof(char *));
315         if (cpp == NULL) {
316             printf("Out of memory.\n");
317             exit(-5);
318         }
319         if (prim_serv[0])
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 *));
326                 if (cpp == NULL) {
327                     printf("Out of memory.\n");
328                     exit(-5);
329                 }
330                 cpp[numserv++] = strsave(serv_list[i]);
331             }
332         }
333         for (i = 0; serv_list[i]; i++)
334             free(serv_list[i]);
335         cpp[numserv] = NULL;
336         serv_list = cpp;
337     }
338 #endif
339     
340     if (!prim_serv[0] && numserv) {
341         srandom(time(NULL));
342         strncpy(prim_serv, serv_list[random() % numserv], sizeof(prim_serv));
343         prim_serv[sizeof(prim_serv) - 1] = '\0';
344     }
345 }
346
347 static void init_hm()
348 {
349      struct servent *sp;
350      Code_t ret;
351      FILE *fp;
352 #ifdef _POSIX_VERSION
353      struct sigaction sa;
354 #endif
355
356      starttime = time((time_t *)0);
357      OPENLOG("hm", LOG_PID, LOG_DAEMON);
358   
359      ZSetServerState(1);        /* Aargh!!! */
360      if ((ret = ZInitialize()) != ZERR_NONE) {
361          Zperr(ret);
362          com_err("hm", ret, "initializing");
363          closelog();
364          exit(-1);
365      }
366      init_queue();
367
368      if (*prim_serv == '\0') {
369          strncpy(prim_serv, *serv_list, sizeof(prim_serv));
370          prim_serv[sizeof(prim_serv) - 1] = '\0';
371      }
372   
373      loopback[0] = 127;
374      loopback[1] = 0;
375      loopback[2] = 0;
376      loopback[3] = 1;
377       
378      if (inetd) {
379          ZSetFD(0);             /* fd 0 is on the socket, thanks to inetd */
380      } else {
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;
384       
385          if ((ret = ZOpenPort(&cli_port)) != ZERR_NONE) {
386              Zperr(ret);
387              com_err("hm", ret, "opening port");
388              exit(ret);
389          }
390      }
391      cli_sin = ZGetDestAddr();
392
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;
396       
397 #ifndef DEBUG
398      if (!inetd && !nofork)
399          detach();
400   
401      /* Write pid to file */
402      fp = fopen(PidFile, "w");
403      if (fp != NULL) {
404          fprintf(fp, "%d\n", getpid());
405          fclose(fp);
406      }
407 #endif /* DEBUG */
408
409      if (hmdebug) {
410           syslog(LOG_INFO, "Debugging on.");
411      }
412
413      /* Set up communications with server */
414      /* target is SERVER_SVCNAME port on server machine */
415
416      serv_sin.sin_family = AF_INET;
417   
418      /* who to talk to */
419      if ((hp = gethostbyname(prim_serv)) == NULL) {
420           DPR("gethostbyname failed\n");
421           find_next_server(NULL);
422      } else {
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);
427      }
428
429      send_boot_notice(HM_BOOT);
430      deactivated = 0;
431
432 #ifdef _POSIX_VERSION
433      sigemptyset(&sa.sa_mask);
434      sa.sa_flags = 0;
435      sa.sa_handler = deactivate;
436      sigaction(SIGHUP, &sa, (struct sigaction *)0);
437      sa.sa_handler = terminate; 
438      sigaction(SIGTERM, &sa, (struct sigaction *)0);
439 #else
440      signal(SIGHUP, deactivate);
441      signal(SIGTERM, terminate);
442 #endif
443 }
444
445 static void detach()
446 {
447      /* detach from terminal and fork. */
448      register int i, x = ZGetFD();
449      register long size;
450   
451      if (i = fork()) {
452           if (i < 0)
453                perror("fork");
454           exit(0);
455      }
456 #ifdef _POSIX_VERSION
457      size = sysconf(_SC_OPEN_MAX);
458 #else
459      size = getdtablesize();
460 #endif
461      for (i = 0; i < size; i++)
462           if (i != x)
463                close(i);
464
465      if ((i = open("/dev/tty", O_RDWR, 0666)) < 0)
466           ;             /* Can't open tty, but don't flame about it. */
467      else {
468 #ifdef TIOCNOTTY
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);
475 #endif
476           close(i);
477      }
478 #ifdef _POSIX_VERSION
479      setsid();
480 #endif
481 }
482
483 static char version[BUFSIZ];
484
485 static void send_stats(notice, sin)
486      ZNotice_t *notice;
487      struct sockaddr_in *sin;
488 {
489      ZNotice_t newnotice;
490      Code_t ret;
491      char *bfr;
492      char *list[20];
493      int len, i, nitems = 10;
494      unsigned long size;
495
496      newnotice = *notice;
497      
498      if ((ret = ZSetDestAddr(sin)) != ZERR_NONE) {
499           Zperr(ret);
500           com_err("hm", ret, "setting destination");
501      }
502      newnotice.z_kind = HMACK;
503
504      list[0] = (char *) malloc(MAXHOSTNAMELEN);
505      if (list[0] == NULL) {
506        printf("Out of memory.\n");
507        exit(-5);
508      }
509      strcpy(list[0], cur_serv);
510      list[1] = (char *) malloc(64);
511      if (list[1] == NULL) {
512        printf("Out of memory.\n");
513        exit(-5);
514      }
515      sprintf(list[1], "%d", queue_len());
516      list[2] = (char *) malloc(64);
517      if (list[2] == NULL) {
518        printf("Out of memory.\n");
519        exit(-5);
520      }
521      sprintf(list[2], "%d", nclt);
522      list[3] = (char *) malloc(64);
523      if (list[3] == NULL) {
524        printf("Out of memory.\n");
525        exit(-5);
526      }
527      sprintf(list[3], "%d", nserv);
528      list[4] = (char *) malloc(64);
529      if (list[4] == NULL) {
530        printf("Out of memory.\n");
531        exit(-5);
532      }
533      sprintf(list[4], "%d", nservchang);
534      list[5] = (char *) malloc(64);
535      if (list[5] == NULL) {
536        printf("Out of memory.\n");
537        exit(-5);
538      }
539      strncpy(list[5], rcsid_hm_c, 64);
540      list[5][63] = '\0';
541      
542      list[6] = (char *) malloc(64);
543      if (list[6] == NULL) {
544        printf("Out of memory.\n");
545        exit(-5);
546      }
547      if (no_server)
548           sprintf(list[6], "yes");
549      else
550           sprintf(list[6], "no");
551      list[7] = (char *) malloc(64);
552      if (list[7] == NULL) {
553        printf("Out of memory.\n");
554        exit(-5);
555      }
556      sprintf(list[7], "%ld", time((time_t *)0) - starttime);
557 #ifdef adjust_size
558      size = (unsigned long)sbrk(0);
559      adjust_size (size);
560 #else
561      size = -1;
562 #endif
563      list[8] = (char *)malloc(64);
564      if (list[8] == NULL) {
565        printf("Out of memory.\n");
566        exit(-5);
567      }
568      sprintf(list[8], "%ld", size);
569      list[9] = (char *)malloc(32);
570      if (list[9] == NULL) {
571        printf("Out of memory.\n");
572        exit(-5);
573      }
574      strncpy(list[9], MACHINE_TYPE, 32);
575      list[9][31] = '\0';
576
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 */
580      if (!*version)
581              sprintf(version, "%s%d.%d", ZVERSIONHDR, ZVERSIONMAJOR,
582                      ZVERSIONMINOR);
583      newnotice.z_version = version;
584
585      if ((ret = ZFormatRawNoticeList(&newnotice, list, nitems, &bfr,
586                                      &len)) != ZERR_NONE) {
587          syslog(LOG_INFO, "Couldn't format stats packet");
588      } else {
589          if ((ret = ZSendPacket(bfr, len, 0)) != ZERR_NONE) {
590              Zperr(ret);
591              com_err("hm", ret, "sending stats");
592          }
593      }
594      free(bfr);
595      for(i=0;i<nitems;i++)
596           free(list[i]);
597 }
598
599 void die_gracefully()
600 {
601      syslog(LOG_INFO, "Terminate signal caught...");
602      send_flush_notice(HM_FLUSH);
603      unlink(PidFile);
604      closelog();
605      exit(0);
606 }
607
608 static char *strsave(sp)
609     const char *sp;
610 {
611     register char *ret;
612
613     if((ret = malloc((unsigned) strlen(sp)+1)) == NULL) {
614             abort();
615     }
616     strcpy(ret,sp);
617     return(ret);
618 }