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