source: trunk/athena/etc/cviewd/cviewd.c @ 15534

Revision 15534, 17.0 KB checked in by ghudson, 24 years ago (diff)
From tibbetts: add help options and update help.
Line 
1/* Copyright 1998 by the Massachusetts Institute of Technology.
2 *
3 * Permission to use, copy, modify, and distribute this
4 * software and its documentation for any purpose and without
5 * fee is hereby granted, provided that the above copyright
6 * notice appear in all copies and that both that copyright
7 * notice and this permission notice appear in supporting
8 * documentation, and that the name of M.I.T. not be used in
9 * advertising or publicity pertaining to distribution of the
10 * software without specific, written prior permission.
11 * M.I.T. makes no representations about the suitability of
12 * this software for any purpose.  It is provided "as is"
13 * without express or implied warranty.
14 */
15
16/* This program presents reports generated by larvnetd to stdout
17 * according to a command line from stdin.
18 */
19
20static const char rcsid[] = "$Id: cviewd.c,v 1.6 2001-01-22 21:21:05 ghudson Exp $";
21
22#include <sys/types.h>
23#include <sys/stat.h>
24#include <stdio.h>
25#include <stdlib.h>
26#include <string.h>
27#include <ctype.h>
28#include <time.h>
29#include "larvnet.h"
30
31struct outof {
32  int nfree;
33  int total;
34};
35
36struct cluster {
37  char *name;
38  char *phone;
39  int public;
40  struct outof *stats;
41};
42
43struct clusterfile {
44  char **archnames;
45  int narch;
46  struct cluster *clusters;
47  int nclusters;
48  time_t mtime;
49};
50
51struct printer {
52  char *name;
53  char *cluster;
54  int up;
55  int jobs;
56};
57
58struct printerfile {
59  struct printer *printers;
60  int nprinters;
61  time_t mtime;
62};
63
64struct cgcluster {
65  char *name;
66  char *phone;
67};
68
69struct cgroup {
70  char *name;
71  int x;
72  int y;
73  struct cgcluster *clusters;
74  int nclusters;
75  char **printers;
76  int nprinters;
77};
78
79struct cgroupfile {
80  struct cgroup *cgroups;
81  int ncgroups;
82  time_t mtime;
83};
84
85static void display_printers(void);
86static void display_phones(void);
87static void display_cluster_ints(void);
88static void display_ints(int limit);
89static void display_cgroups(void);
90static void display_clusters(const char *s);
91static void display_help(void);
92static void read_clusters(struct clusterfile *file);
93static void read_printers(struct printerfile *file);
94static void read_cgroups(struct cgroupfile *file);
95static int first_field_matches(const char *s, const char *word);
96static int cluster_matches(const char *s, struct cluster *cluster);
97static int read_line(FILE *fp, char **buf, int *bufsize);
98static const char *skip_spaces(const char *p);
99static const char *skip_nonspaces(const char *p);
100static void *emalloc(size_t size);
101static void *erealloc(void *ptr, size_t size);
102static char *estrndup(const char *s, size_t n);
103
104int main(int argc, char **argv)
105{
106  char *line = NULL;
107  const char *p;
108  int linesize;
109
110  if (read_line(stdin, &line, &linesize) != 0)
111    return 1;
112
113  p = skip_spaces(line);
114
115  if (first_field_matches(p, "printers"))
116    display_printers();
117  else if (first_field_matches(p, "phones"))
118    display_phones();
119  else if (first_field_matches(p, "intsonlyplease"))
120    display_cluster_ints();
121  else if (first_field_matches(p, "intsonlyplease2"))
122    display_ints(1);
123  else if (first_field_matches(p, "intsonlyplease3"))
124    display_ints(0);
125  else if (first_field_matches(p, "configplease2")
126           || first_field_matches(p, "configplease3"))
127    display_cgroups();
128  else if (first_field_matches(p, "help")
129           || first_field_matches(p, "-h")
130           || first_field_matches(p, "--help"))
131    display_help();
132  else
133    display_clusters(p);
134
135  return 0;
136}
137
138/* Display printer status in a user-readable format. */
139static void display_printers(void)
140{
141  struct printerfile file;
142  struct printer *printer;
143  int i;
144  char *ct;
145
146  read_printers(&file);
147  ct = ctime(&file.mtime);
148  ct[strlen(ct) - 1] = 0;
149
150  printf("            --  Printer status as of %s:  --\n", ct);
151  printf("PRINTER     CLUSTER  STATUS   JOBS          "
152         "PRINTER     CLUSTER  STATUS   JOBS\n");
153  printf("--------------------------------------------"
154         "----------------------------------\n");
155  for (i = 0; i < file.nprinters; i++)
156    {
157      printer = &file.printers[i];
158      printf("%-11.11s %7.7s   %-7.7s %3d", printer->name, printer->cluster,
159             (printer->up) ? "up" : "down", printer->jobs);
160
161      /* Drop to the next line or display separating spaces as appropriate. */
162      if (i % 2)
163        printf("\n");
164      else
165        printf("           ");
166    }
167  if (i % 2)
168    printf("\n");
169}
170
171/* Display cluster phone numbers in a user-readable format. */
172static void display_phones(void)
173{
174  struct clusterfile file;
175  int i;
176
177  read_clusters(&file);
178  printf("CLUSTER   PHONE NUMBER\n");
179  printf("----------------------\n");
180  for (i = 0; i < file.nclusters; i++)
181    {
182      if (file.clusters[i].public)
183        printf("%-8.8s  %s\n", file.clusters[i].name, file.clusters[i].phone);
184    }
185}
186
187/* Display a list of free cluster machines in the format:
188 *      <clustername> {<free> <busy>} ...
189 * At most five columns of free and busy numbers are given.
190 */
191static void display_cluster_ints(void)
192{
193  struct clusterfile file;
194  struct cluster *cluster;
195  int i, j;
196
197  read_clusters(&file);
198  for (i = 0; i < file.nclusters; i++)
199    {
200      cluster = &file.clusters[i];
201      printf("%-8.8s", cluster->name);
202      for (j = 0; j < file.narch && j < 5; j++)
203        {
204          printf((j == 0) ? " " : "   ");
205          printf("%3d %3d", cluster->stats[j].nfree,
206                 cluster->stats[j].total - cluster->stats[j].nfree);
207        }
208      printf("\n");
209    }
210}
211
212/* Display a list of free cluster machines and printer statuses
213 * in the formats:
214 *      cluster <clustername> {<free> <busy>} ...
215 *      printer <printername> {up|down} <jobs>
216 * If limit is set, display at most five column-pairs of free and
217 * busy numbers.
218 */
219static void display_ints(int limit)
220{
221  struct clusterfile cfile;
222  struct printerfile pfile;
223  struct cluster *cluster;
224  struct printer *printer;
225  int i, j;
226
227  read_clusters(&cfile);
228  read_printers(&pfile);
229  for (i = 0; i < cfile.nclusters; i++)
230    {
231      cluster = &cfile.clusters[i];
232      printf("cluster %-8.8s", cluster->name);
233      for (j = 0; j < cfile.narch && (!limit || j < 5); j++)
234        {
235          printf((j == 0) ? " " : "   ");
236          printf("%3d %3d", cluster->stats[j].nfree,
237                 cluster->stats[j].total - cluster->stats[j].nfree);
238        }
239      printf("\n");
240    }
241  for (i = 0; i < pfile.nprinters; i++)
242    {
243      printer = &pfile.printers[i];
244      printf("printer %-11.11s    %-7.7s %3d\n", printer->name,
245             (printer->up) ? "up" : "down", printer->jobs);
246    }
247}
248
249/* Display configuration information.  The first line returned
250 * has the form:
251 *      <number of architectures> <archname> ...
252 * The remaining lines come in groups of four, with the formats:
253 *      <number> <cluster group name>
254 *      {<cluster> <phone number>} ... XXXXX
255 *      <x coordinate> <y coordinate>
256 *      <printer> ... XXXXX
257 */
258static void display_cgroups(void)
259{
260  struct clusterfile cfile;
261  struct cgroupfile cgfile;
262  struct cgroup *cgroup;
263  int i, j;
264
265  read_clusters(&cfile);
266  read_cgroups(&cgfile);
267
268  printf("%d", cfile.narch);
269  for (i = 0; i < cfile.narch; i++)
270    printf(" %s", cfile.archnames[i]);
271  printf("\n");
272
273  for (i = 0; i < cgfile.ncgroups; i++)
274    {
275      /* Line 1: cluster group number and name */
276      cgroup = &cgfile.cgroups[i];
277      printf("%d\t%s\n", i + 1, cgroup->name);
278
279      /* Line 2: cluster names */
280      for (j = 0; j < cgroup->nclusters; j++)
281        printf("%s %s ", cgroup->clusters[j].name, cgroup->clusters[j].phone);
282      printf("XXXXX\n");
283
284      /* Line 3: X and Y coordinates */
285      printf("%d\t%d\n", cgroup->x, cgroup->y);
286
287      /* Line 4: printer names */
288      for (j = 0; j < cgroup->nprinters; j++)
289        printf("%s ", cgroup->printers[j]);
290      printf("XXXXX\n");
291    }
292}
293
294static void display_clusters(const char *s)
295{
296  struct clusterfile file;
297  struct cluster *cluster;
298  struct outof coltotals[6], rowtotal, *entry;
299  int i, j;
300  char *ct;
301
302  read_clusters(&file);
303  ct = ctime(&file.mtime);
304  ct[strlen(ct) - 1] = 0;
305
306  /* We can handle at most six architecture columns in 80 columns. */
307  if (file.narch > 6)
308    file.narch = 6;
309
310  /* Display the first header line. */
311  printf("             --  Cluster status as of %s:  --\n", ct);
312
313  /* Display the architecture names. */
314  printf("          ");
315  for (i = 0; i < file.narch; i++)
316    {
317      printf("%-9.9s ", file.archnames[i]);
318      coltotals[i].nfree = 0;
319      coltotals[i].total = 0;
320    }
321  printf("TOTAL\n");
322
323  /* Display the fre/tot column headers. */
324  printf("CLUSTER");
325  for (i = 0; i < file.narch + 1; i++)
326    printf("   fre/tot");
327  printf("\n");
328
329  /* Display the divider line. */
330  printf("--------");
331  for (i = 0; i < file.narch + 1; i++)
332    printf("----------");
333  printf("\n");
334
335  /* Display the stats. */
336  for (i = 0; i < file.nclusters; i++)
337    {
338      cluster = &file.clusters[i];
339      if (!cluster_matches(s, cluster))
340        continue;
341      printf("%-8.8s ", cluster->name);
342      rowtotal.nfree = 0;
343      rowtotal.total = 0;
344      for (j = 0; j < file.narch; j++)
345        {
346          entry = &cluster->stats[j];
347          if (entry->total == 0)
348            printf("    -     ");
349          else
350            printf("%3d / %-3d ", entry->nfree, entry->total);
351          coltotals[j].nfree += entry->nfree;
352          coltotals[j].total += entry->total;
353          rowtotal.nfree += entry->nfree;
354          rowtotal.total += entry->total;
355        }
356      printf("%3d / %-3d\n", rowtotal.nfree, rowtotal.total);
357    }
358
359  /* Display the column totals. */
360  rowtotal.nfree = 0;
361  rowtotal.total = 0;
362  printf("TOTALS   ");
363  for (i = 0; i < file.narch; i++)
364    {
365      printf("%3d / %-3d ", coltotals[i].nfree, coltotals[i].total);
366      rowtotal.nfree += coltotals[i].nfree;
367      rowtotal.total += coltotals[i].total;
368    }
369  printf("%3d / %-3d\n", rowtotal.nfree, rowtotal.total);
370}
371
372static void display_help(void)
373{
374  printf("Usage:\tcview [cluster ...]\n");
375  printf("\tcview all\n");
376  printf("\tcview public\n");
377  printf("\tcview printers\n");
378  printf("\tcview phones\n");
379}
380
381static void read_clusters(struct clusterfile *file)
382{
383  FILE *fp;
384  char *line = NULL;
385  int linesize, i;
386  struct cluster *cluster;
387  const char *p, *q;
388  struct stat statbuf;
389
390  fp = fopen(LARVNET_PATH_CLUSTERS, "r");
391  if (!fp)
392    {
393      fprintf(stderr, "Sorry, no cluster status information available.\n");
394      exit(1);
395    }
396
397  fstat(fileno(fp), &statbuf);
398  file->mtime = statbuf.st_mtime;
399
400  /* Read in the architecture names. */
401  file->archnames = NULL;
402  file->narch = 0;
403  while (read_line(fp, &line, &linesize) == 0)
404    {
405      if (*line == '-')
406        break;
407
408      /* Add an architecture name. */
409      file->narch++;
410      file->archnames = erealloc(file->archnames,
411                                 file->narch * sizeof(char *));
412      p = skip_spaces(line);
413      q = skip_nonspaces(p);
414      file->archnames[file->narch - 1] = estrndup(p, q - p);
415    }
416
417  /* Read in the cluster information. */
418  file->clusters = NULL;
419  file->nclusters = 0;
420  while (read_line(fp, &line, &linesize) == 0)
421    {
422      /* Make a new cluster entry. */
423      file->nclusters++;
424      file->clusters = erealloc(file->clusters,
425                                file->nclusters * sizeof(struct cluster));
426      cluster = &file->clusters[file->nclusters - 1];
427
428      /* Read the name, public/private field, and phone. */
429      p = skip_spaces(line);
430      q = skip_nonspaces(p);
431      cluster->name = estrndup(p, q - p);
432      p = skip_spaces(q);
433      q = skip_nonspaces(p);
434      cluster->public = first_field_matches(p, "public");
435      p = skip_spaces(q);
436      q = skip_nonspaces(p);
437      cluster->phone = estrndup(p, q - p);
438
439      /* Read the stats. */
440      cluster->stats = emalloc(file->narch * sizeof(struct outof));
441      for (i = 0; i < file->narch; i++)
442        {
443          p = skip_spaces(q);
444          q = skip_nonspaces(p);
445          cluster->stats[i].nfree = atoi(p);
446          p = skip_spaces(q);
447          q = skip_nonspaces(p);
448          cluster->stats[i].total = atoi(p);
449        }
450    }
451
452  fclose(fp);
453}
454
455static void read_printers(struct printerfile *file)
456{
457  FILE *fp;
458  char *line = NULL;
459  int linesize;
460  struct printer *printer;
461  const char *p, *q;
462  struct stat statbuf;
463
464  fp = fopen(LARVNET_PATH_PRINTERS, "r");
465  if (!fp)
466    {
467      fprintf(stderr, "Sorry, no printer status information available.\n");
468      exit(1);
469    }
470
471  fstat(fileno(fp), &statbuf);
472  file->mtime = statbuf.st_mtime;
473
474  file->printers = NULL;
475  file->nprinters = 0;
476  while (read_line(fp, &line, &linesize) == 0)
477    {
478      /* Make a new printer entry. */
479      file->nprinters++;
480      file->printers = erealloc(file->printers,
481                                file->nprinters * sizeof(struct printer));
482      printer = &file->printers[file->nprinters - 1];
483
484      /* Read in the name, cluster name, status, and jobs. */
485      p = skip_spaces(line);
486      q = skip_nonspaces(p);
487      printer->name = estrndup(p, q - p);
488      p = skip_spaces(q);
489      q = skip_nonspaces(p);
490      printer->cluster = estrndup(p, q - p);
491      p = skip_spaces(q);
492      q = skip_nonspaces(p);
493      printer->up = first_field_matches(p, "up");
494      p = skip_spaces(q);
495      printer->jobs = atoi(p);
496    }
497}
498
499static void read_cgroups(struct cgroupfile *file)
500{
501  FILE *fp;
502  char *line = NULL;
503  int linesize;
504  struct cgroup *cgroup;
505  struct cgcluster *cgcluster;
506  const char *p, *q;
507  struct stat statbuf;
508
509  fp = fopen(LARVNET_PATH_CGROUPS, "r");
510  if (!fp)
511    {
512      fprintf(stderr, "No cluster group information available.\n");
513      exit(1);
514    }
515
516  fstat(fileno(fp), &statbuf);
517  file->mtime = statbuf.st_mtime;
518
519  file->cgroups = NULL;
520  file->ncgroups = 0;
521  while (read_line(fp, &line, &linesize) == 0)
522    {
523      /* Make a new cluster group entry. */
524      file->ncgroups++;
525      file->cgroups = erealloc(file->cgroups,
526                               file->ncgroups * sizeof(struct cgroup));
527      cgroup = &file->cgroups[file->ncgroups - 1];
528
529      /* Read in the cluster group name and coordinates. */
530      p = skip_spaces(line);
531      q = skip_nonspaces(p);
532      cgroup->name = estrndup(p, q - p);
533      p = skip_spaces(q);
534      q = skip_nonspaces(p);
535      cgroup->x = atoi(p);
536      p = skip_spaces(q);
537      q = skip_nonspaces(p);
538      cgroup->y = atoi(p);
539
540      /* Read in the cluster names. */
541      cgroup->clusters = NULL;
542      cgroup->nclusters = 0;
543      while (1)
544        {
545          p = skip_spaces(q);
546          q = skip_nonspaces(p);
547          if (!*p || *p == '-')
548            break;
549          cgroup->nclusters++;
550          cgroup->clusters = erealloc(cgroup->clusters, cgroup->nclusters
551                                      * sizeof(struct cgcluster));
552          cgcluster = &cgroup->clusters[cgroup->nclusters - 1];
553          cgcluster->name = estrndup(p, q - p);
554          p = skip_spaces(q);
555          q = skip_nonspaces(p);
556          cgcluster->phone = estrndup(p, q - p);
557        }
558
559      /* Read in the printer names. */
560      cgroup->printers = NULL;
561      cgroup->nprinters = 0;
562      p = skip_spaces(q);
563      while (*p)
564        {
565          q = skip_nonspaces(p);
566          cgroup->nprinters++;
567          cgroup->printers = erealloc(cgroup->printers,
568                                      cgroup->nprinters * sizeof(char *));
569          cgroup->printers[cgroup->nprinters - 1] = estrndup(p, q - p);
570          p = skip_spaces(q);
571        }
572    }
573}
574
575static int first_field_matches(const char *s, const char *word)
576{
577  int len = strlen(word);
578
579  return (strncasecmp(s, word, len) == 0 &&
580          (isspace((unsigned char)s[len]) || !s[len]));
581}
582
583static int cluster_matches(const char *s, struct cluster *cluster)
584{
585  const char *p, *q;
586
587  /* If no cluster names specified, match public clusters. */
588  if (!*s)
589    return cluster->public;
590
591  p = skip_spaces(s);
592  while (*p)
593    {
594      if (first_field_matches(p, "public") && cluster->public)
595        return 1;
596      if (first_field_matches(p, "all")
597          || first_field_matches(p, cluster->name))
598        return 1;
599      q = skip_nonspaces(p);
600      p = skip_spaces(q);
601    }
602
603  return 0;
604}
605
606/* Read a line from a file into a dynamically allocated buffer,
607 * zeroing the trailing newline if there is one.  The calling routine
608 * may call read_line multiple times with the same buf and bufsize
609 * pointers; *buf will be reallocated and *bufsize adjusted as
610 * appropriate.  The initial value of *buf should be NULL.  After the
611 * calling routine is done reading lines, it should free *buf.  This
612 * function returns 0 if a line was successfully read, 1 if the file
613 * ended, and -1 if there was an I/O error.
614 */
615
616static int read_line(FILE *fp, char **buf, int *bufsize)
617{
618  char *newbuf;
619  int offset = 0, len;
620
621  if (*buf == NULL)
622    {
623      *buf = emalloc(128);
624      *bufsize = 128;
625    }
626
627  while (1)
628    {
629      if (!fgets(*buf + offset, *bufsize - offset, fp))
630        return (offset != 0) ? 0 : (ferror(fp)) ? -1 : 1;
631      len = offset + strlen(*buf + offset);
632      if ((*buf)[len - 1] == '\n')
633        {
634          (*buf)[len - 1] = 0;
635          return 0;
636        }
637      offset = len;
638
639      /* Allocate more space. */
640      newbuf = erealloc(*buf, *bufsize * 2);
641      *buf = newbuf;
642      *bufsize *= 2;
643    }
644}
645
646static const char *skip_spaces(const char *p)
647{
648  while (isspace((unsigned char)*p))
649    p++;
650  return p;
651}
652
653static const char *skip_nonspaces(const char *p)
654{
655  while (*p && !isspace((unsigned char)*p))
656    p++;
657  return p;
658}
659
660static void *emalloc(size_t size)
661{
662  void *ptr;
663
664  ptr = malloc(size);
665  if (!ptr)
666    {
667      fprintf(stderr, "Ran out of memory!\n");
668      exit(1);
669    }
670  return ptr;
671}
672
673static void *erealloc(void *ptr, size_t size)
674{
675  ptr = realloc(ptr, size);
676  if (!ptr)
677    {
678      fprintf(stderr, "Ran out of memory!\n");
679      exit(1);
680    }
681  return ptr;
682}
683
684static char *estrndup(const char *s, size_t n)
685{
686  char *new_s;
687
688  new_s = emalloc(n + 1);
689  memcpy(new_s, s, n);
690  new_s[n] = 0;
691  return new_s;
692}
Note: See TracBrowser for help on using the repository browser.