source: trunk/athena/bin/getcluster/getcluster.c @ 11392

Revision 11392, 11.2 KB checked in by danw, 26 years ago (diff)
Don't flame if the local file or fallback file doesn't exist
RevLine 
[649]1#include <stdio.h>
[8517]2#include <stdlib.h>
[649]3#include <ctype.h>
[10988]4#include <errno.h>
[10229]5#include <sys/types.h>
6#include <netinet/in.h>
[11312]7#include <limits.h>
8#include <string.h>
[10976]9#include <hesiod.h>
[649]10
[10229]11#ifndef INADDR_NONE
12#define INADDR_NONE ((unsigned long) -1)
13#endif
14
15/* Delay attaching new packs by up to four hours. */
16#define UPDATE_INTERVAL (3600 * 4)
17
[8517]18static void usage(void);
[10229]19static void shellenv(char **hp, char *ws_version, int bourneshell);
[8527]20static void output_var(const char *var, const char *val, int bourneshell);
[8517]21static void upper(char *v);
[11312]22static void free_list(char **list);
23static char **readcluster(FILE *f);
24static char **merge(char **l1, char **l2);
[8517]25static int vercmp(const char *v1, const char *v2);
[11312]26static void *emalloc(size_t size);
[8517]27
[10976]28/* Make a hesiod cluster query for the machine you are on and produce a
29 * set of environment variable assignments for the C shell or the Bourne
30 * shell, depending on the '-b' flag
[11312]31 * If a localfile or a fallbackfile is specified, read cluster information
32 * from it as well. Variables in localfile override varibles obtained from
33 * Hesiod, and variables obtained from Hesiod override in fallbackfile.
34 * "Override" means that the presence of any instances of variable "foo"
35 * in one source will prevent any instances from a source being overridden.
36 * Example 1:
37 *   localfile: lpr myprinter
38 *   Hesiod:    syslib random-syspack-1 9.0
39 *              syslib random-syspack-2 9.1
40 *              syslib old-random-syspack
41 *   fallback:  syslib normal-syspack
[649]42 *
[11312]43 * lpr would be myprinter, and syslib would be negotiated among the three
44 * Hesiod entries.
45 *
46 * Example 2:
47 *   localfile: lpr myprinter
48 *              syslib new-spiffy-syspack
49 *   Hesiod:    syslib random-syspack-1 9.0
50 *              syslib random-syspack-2 9.1
51 *              syslib old-random-syspack
52 *   fallback:  syslib normal-syspack
53 *
54 * lpr would be myprinter, and syslib would be new-spiffy-syspack, regardless
55 * of the given version.
56 *
57 *
[10976]58 * If any stdio errors, truncate standard output to 0 and return an exit
59 * status.
[649]60 */
61
[8517]62int main(int argc, char **argv)
[649]63{
[11312]64  char buf[256], **hp, **fp, **lp, **or1, **or2;
[10229]65  int debug = 0, bourneshell = 0, ch;
[11312]66  char *fallbackfile = NULL, *localfile = NULL;
67  FILE *f;
[10976]68  void *hescontext;
[11312]69  extern int optind;
70  extern char *optarg;
[649]71
[11312]72  while ((ch = getopt(argc, argv, "bdl:f:")) != -1)
[10976]73    {
74      switch (ch)
75        {
76        case 'd':
77          debug = 1;
78          break;
79        case 'b':
80          bourneshell = 1;
81          break;
[11312]82        case 'f':
83          fallbackfile = optarg;
84          break;
85        case 'l':
86          localfile = optarg;
87          break;
[10976]88        default:
89          usage();
90        }
[8402]91    }
[8517]92  argc -= optind;
93  argv += optind;
94
95  if (argc != 2)
96    usage();
97
[11312]98  fp = NULL;
99  if (fallbackfile != NULL)
[10976]100    {
[11312]101      f = fopen(fallbackfile, "r");
[11392]102      if (f)
[10976]103        {
[11312]104          fp = readcluster(f);
105          fclose(f);
106        }
[10976]107    }
[11312]108
109  lp = NULL;
110  if (localfile != NULL)
[10976]111    {
[11312]112      f = fopen(localfile, "r");
[11392]113      if (f)
[10976]114        {
[11312]115          lp = readcluster(f);
116          fclose(f);
[10976]117        }
[11312]118    }
119
120  hp = NULL;
121  if (debug)
122    {
123      /* Get clusterinfo records from standard input. */
124      hp = readcluster(stdin);
125    }
126  else
127    {
128      /* Get clusterinfo records from Hesiod. */
129      if (hesiod_init(&hescontext) != 0)
130        perror("hesiod_init");
131      else
[10976]132        {
[11312]133          hp = hesiod_resolve(hescontext, argv[0], "cluster");
134          if (hp == NULL && errno != ENOENT)
135            perror("hesiod_resolve");
[10976]136        }
137    }
[8517]138
[11312]139  if (hp == NULL && lp == NULL && fp == NULL)
140    {
141      fprintf(stderr, "No cluster information avaliable for %s\n", argv[0]);
142      return 2;
143    }
144
145  or1 = merge(lp, hp);
146  or2 = merge(or1, fp);
147  shellenv(or2, argv[1], bourneshell);
[10976]148  if (!debug)
149    {
[11312]150      if (hp != NULL)
151        hesiod_free_list(hescontext, hp);
[10976]152      hesiod_end(hescontext);
153    }
[11312]154  /* We don't bother to free memory we know we allocated just before exiting;
155   * it's not worth the trouble. */
[10229]156  return (ferror(stdout)) ? 1 : 0;
[649]157}
158
[11312]159static char **merge(char **l1, char **l2)
160{
161  int size, point, i, j, ret, sizefroml1;
162  char **lp, **nl;
163  char var[256], compvar[256], dummy[256];
164
165  if (l1 == NULL)
166    return l2;
167  if (l2 == NULL)
168    return l1;
169
170  size = 1;
171  for (i = 0; l1[i] != NULL; i++)
172    size++;
173  for (i = 0; l2[i] != NULL; i++)
174    size++;
175
176  nl = emalloc(sizeof(char *) * size);
177  point = 0;
178
179  /* Copy l1 to nl. */
180  for (i = 0; l1[i] != NULL; i++)
181    {
182      ret = sscanf(l1[i], "%s %s", var, dummy);
183      if (ret == 2) /* Ignore invalid lines. */
184        {
185          nl[point] = l1[i];
186          point++;
187        }
188    }
189  sizefroml1 = point;
190
191  /* For each entry in l2, add it to nl if nothing in l1 has that var. */
192  for (i = 0; l2[i] != NULL; i++)
193    {
194      ret = sscanf(l2[i], "%s %s", var, dummy);
195      if (ret < 2)
196        continue; /* Ignore invalid lines. */
197      for (j = 0; j < sizefroml1; j++)
198        {
199          sscanf(nl[j], "%s", compvar);
200          if (strcmp(var, compvar) == 0)
201              break;
202        }
203      if (j == sizefroml1)
204        {
205          nl[point] = l2[i];
206          point++;
207        }
208    }
209  nl[point] = NULL;
210  return nl;
211}
212
213static void free_list(char **list)
214{
215  char **c;
216
217  if (list == NULL)
218      return;
219
220  for (c = list; *c != NULL; c++)
221    free(*c);
222  free(list);
223}
224
[8517]225static void usage()
[649]226{
[11312]227  fprintf(stderr, "Usage: getcluster [-f fallbackfile] [-l localfile]"
228          " [-b] [-d] hostname version\n");
[8517]229  exit(1);
230}
[649]231
[10229]232/* Cluster records will come in entries of the form:
[8517]233 *
[10229]234 *      variable value [version [flags]]
[8517]235 *
[10229]236 * There may be multiple records for the same variable, but not
237 * multiple entries with the same variable and version.  There may be
238 * at most one entry for a given variable that does not list a
239 * version.
[8517]240 *
[10229]241 * Discard records if they have a version greater than the current
242 * workstation version and any of the following is true:
243 *      - They have 't' listed in the flags.
244 *      - AUTOUPDATE is false.
245 *      - The environment variable UPDATE_TIME doesn't exist or
246 *        specifies a time later than the current time.
247 * Set NEW_TESTING_RELEASE if any records are discarded for the first
248 * reason, NEW_PRODUCTION_RELEASE if any records are discarded for
249 * the second reason, and UPDATE_TIME if any entries specify a version
250 * greater than the current workstation version and are not discarded
251 * for the first two reasons.
252 *
253 * After discarding records, output the variable definition with the
254 * highest version number.  If there aren't any records left with
255 * version numbers and there's one with no version number, output
256 * that one. */
257static void shellenv(char **hp, char *ws_version, int bourneshell)
[8517]258{
[10229]259  int *seen, count, i, j, output_time = 0, autoupdate = 0;
[8517]260  char var[80], val[80], vers[80], flags[80], compvar[80], compval[80];
[8527]261  char compvers[80], defaultval[80], new_production[80], new_testing[80];
[10229]262  char timebuf[32], *envp;
263  time_t update_time = -1, now;
264  unsigned long ip = INADDR_NONE;
[8517]265
[10229]266  time(&now);
267
268  /* Gather information from the environment.  UPDATE_TIME comes from
269   * the clusterinfo file we wrote out last time; AUTOUPDATE and ADDR
270   * come from rc.conf.  If neither file has been sourced by the
271   * caller, we just use defaults. */
272  envp = getenv("UPDATE_TIME");
273  if (envp)
274    update_time = atoi(envp);
275  envp = getenv("AUTOUPDATE");
276  if (envp && strcmp(envp, "true") == 0)
277    autoupdate = 1;
278  envp = getenv("ADDR");
279  if (envp)
280    ip = inet_addr(envp);
281
[8517]282  count = 0;
283  while (hp[count])
284    count++;
285
286  seen = (int *) calloc(count, sizeof(int));
287  if (seen == NULL)
288    exit(1);
289
[8527]290  strcpy(new_production, "0.0");
291  strcpy(new_testing, "0.0");
292
[10229]293  /* The outer loop is for the purpose of "considering each variable."
294   * We skip entries for variables which had a previous entry. */
[8517]295  for (i = 0; i < count; i++)
[8402]296    {
[8517]297      if (seen[i])
298        continue;
299      sscanf(hp[i], "%s", var);
300
301      /* Consider each entry for this variable (including hp[i]). */
302      strcpy(vers, "0.0");
303      *defaultval = 0;
304      for (j = i; j < count; j++)
[8402]305        {
[8517]306          *compvers = *flags = 0;
307          sscanf(hp[j], "%s %s %s %s", compvar, compval, compvers, flags);
308          if (strcmp(compvar, var) != 0)
[8402]309            continue;
[8517]310          seen[j] = 1;
311
[10229]312          /* If there's no version, keep this as the default value in
313           * case we don't come up with anything else to print.  If
314           * there is a version, discard it if it doesn't match the
315           * current workstation version and (a) we're not autoupdate,
316           * or (b) it's a testing version.  If we do consider the
317           * entry, and its version is greater than the current best
318           * version we have, update the current best version and its
[8517]319           * value. */
320          if (!*compvers)
[11312]321            strcpy(defaultval, compval);
[8517]322          else if (((autoupdate && !strchr(flags, 't')) ||
[8527]323                    (vercmp(compvers, ws_version) == 0)))
[8517]324            {
[10229]325              if (vercmp(compvers, ws_version) > 0)
326                {
327                  /* We want to take this value, but not necessarily
328                   * right away.  Accept the record only if we have an
329                   * update time which has already passed.  Make
330                   * a note that we should output the time
331                   * whether or not we discard the entry, since the
332                   * workstation is out of date either way. */
333                  output_time = 1;
334                  if (update_time == -1 || now < update_time)
335                    continue;
336                }
337
[8527]338              if (vercmp(compvers, vers) >= 0)
339                {
340                  strcpy(val, compval);
341                  strcpy(vers, compvers);
342                }
[8517]343            }
[8527]344          else
345            {
346              /* Discard this entry, but record most recent testing and
347               * production releases. */
348              if (strchr(flags, 't') && vercmp(compvers, new_testing) > 0)
349                strcpy(new_testing, compvers);
350              if (!strchr(flags, 't') && vercmp(compvers, new_production) > 0)
351                strcpy(new_production, compvers);
352            }
[8517]353        }
354      if (*vers != '0' || *defaultval)
355        {
356          if (*vers == '0')
357            strcpy(val, defaultval);
[8402]358          upper(var);
[8527]359          output_var(var, val, bourneshell);
[1446]360        }
[8517]361    }
[8402]362
[8527]363  if (vercmp(new_testing, ws_version) > 0)
364    output_var("NEW_TESTING_RELEASE", new_testing, bourneshell);
365  if (vercmp(new_production, ws_version) > 0)
366    output_var("NEW_PRODUCTION_RELEASE", new_production, bourneshell);
[10229]367  if (output_time)
368    {
369      /* If we have no time from the environment, make up one
370       * between now and UPDATE_INTERVAL seconds in the future. */
371      if (update_time == -1)
372        {
373          srand(ntohl(ip));
374          update_time = now + rand() % UPDATE_INTERVAL;
375        }
376      sprintf(timebuf, "%lu", update_time);
377      output_var("UPDATE_TIME", timebuf, bourneshell);
378    }
[8527]379  free(seen);
[649]380}
381
[8527]382static void output_var(const char *var, const char *val, int bourneshell)
383{
384  if (bourneshell)
385    printf("%s=%s ; export %s\n", var, val, var);
386  else
387    printf("setenv %s %s\n", var, val);
388}
389
[8517]390static void upper(char *v)
[649]391{
[8402]392  while(*v)
393    {
394      *v = toupper(*v);
395      v++;
396    }
[649]397}
[8402]398
[8517]399static int vercmp(const char *v1, const char *v2)
[8402]400{
[8517]401  int major1 = 0, minor1 = 0, major2 =0, minor2 = 0;
402
403  sscanf(v1, "%d.%d", &major1, &minor1);
404  sscanf(v2, "%d.%d", &major2, &minor2);
[10976]405  return (major1 != major2) ? (major1 - major2) : (minor1 - minor2);
[8402]406}
[11312]407
408static char **readcluster(FILE *f)
409{
410  char line[1024];
411  char **lp;
412  int nl, al;
413
414  nl = 0;
415  al = 10;
416  lp = emalloc(al * sizeof(char *));
417
418  lp[0] = NULL;
419  while (fgets(line, 1024, f) != NULL)
420    {
421      if (nl + 1 == al)
422        {
423          al = al * 2;
424          lp = realloc(lp, al * sizeof(char *));
425          if (lp == NULL)
426            {
427              fprintf(stderr, "Out of memory.");
428              exit(1);
429            }
430        }
431      lp[nl] = strdup(line);
432      if (lp[nl] == NULL)
433        {
434          fprintf(stderr, "Out of memory.");
435          exit(1);
436        }
437      nl++;
438    }
439  lp[nl] = NULL;
440
441  return lp;
442}
443
444static void *emalloc(size_t size)
445{
446  void *p;
447
448  p = malloc(size);
449  if (p == NULL)
450    {
451      fprintf(stderr, "Out of memory.\n");
452      exit(1);
453    }
454  return p;
455}
Note: See TracBrowser for help on using the repository browser.