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

Revision 13837, 16.8 KB checked in by ghudson, 24 years ago (diff)
Need time.h for ctime() prototype.
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.5 1999-11-01 19:51:08 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    display_help();
130  else
131    display_clusters(p);
132
133  return 0;
134}
135
136/* Display printer status in a user-readable format. */
137static void display_printers(void)
138{
139  struct printerfile file;
140  struct printer *printer;
141  int i;
142  char *ct;
143
144  read_printers(&file);
145  ct = ctime(&file.mtime);
146  ct[strlen(ct) - 1] = 0;
147
148  printf("            --  Printer status as of %s:  --\n", ct);
149  printf("PRINTER     CLUSTER  STATUS   JOBS          "
150         "PRINTER     CLUSTER  STATUS   JOBS\n");
151  printf("--------------------------------------------"
152         "----------------------------------\n");
153  for (i = 0; i < file.nprinters; i++)
154    {
155      printer = &file.printers[i];
156      printf("%-11.11s %7.7s   %-7.7s %3d", printer->name, printer->cluster,
157             (printer->up) ? "up" : "down", printer->jobs);
158
159      /* Drop to the next line or display separating spaces as appropriate. */
160      if (i % 2)
161        printf("\n");
162      else
163        printf("           ");
164    }
165  if (i % 2)
166    printf("\n");
167}
168
169/* Display cluster phone numbers in a user-readable format. */
170static void display_phones(void)
171{
172  struct clusterfile file;
173  int i;
174
175  read_clusters(&file);
176  printf("CLUSTER   PHONE NUMBER\n");
177  printf("----------------------\n");
178  for (i = 0; i < file.nclusters; i++)
179    {
180      if (file.clusters[i].public)
181        printf("%-8.8s  %s\n", file.clusters[i].name, file.clusters[i].phone);
182    }
183}
184
185/* Display a list of free cluster machines in the format:
186 *      <clustername> {<free> <busy>} ...
187 * At most five columns of free and busy numbers are given.
188 */
189static void display_cluster_ints(void)
190{
191  struct clusterfile file;
192  struct cluster *cluster;
193  int i, j;
194
195  read_clusters(&file);
196  for (i = 0; i < file.nclusters; i++)
197    {
198      cluster = &file.clusters[i];
199      printf("%-8.8s", cluster->name);
200      for (j = 0; j < file.narch && j < 5; j++)
201        {
202          printf((j == 0) ? " " : "   ");
203          printf("%3d %3d", cluster->stats[j].nfree,
204                 cluster->stats[j].total - cluster->stats[j].nfree);
205        }
206      printf("\n");
207    }
208}
209
210/* Display a list of free cluster machines and printer statuses
211 * in the formats:
212 *      cluster <clustername> {<free> <busy>} ...
213 *      printer <printername> {up|down} <jobs>
214 * If limit is set, display at most five column-pairs of free and
215 * busy numbers.
216 */
217static void display_ints(int limit)
218{
219  struct clusterfile cfile;
220  struct printerfile pfile;
221  struct cluster *cluster;
222  struct printer *printer;
223  int i, j;
224
225  read_clusters(&cfile);
226  read_printers(&pfile);
227  for (i = 0; i < cfile.nclusters; i++)
228    {
229      cluster = &cfile.clusters[i];
230      printf("cluster %-8.8s", cluster->name);
231      for (j = 0; j < cfile.narch && (!limit || j < 5); j++)
232        {
233          printf((j == 0) ? " " : "   ");
234          printf("%3d %3d", cluster->stats[j].nfree,
235                 cluster->stats[j].total - cluster->stats[j].nfree);
236        }
237      printf("\n");
238    }
239  for (i = 0; i < pfile.nprinters; i++)
240    {
241      printer = &pfile.printers[i];
242      printf("printer %-11.11s    %-7.7s %3d\n", printer->name,
243             (printer->up) ? "up" : "down", printer->jobs);
244    }
245}
246
247/* Display configuration information.  The first line returned
248 * has the form:
249 *      <number of architectures> <archname> ...
250 * The remaining lines come in groups of four, with the formats:
251 *      <number> <cluster group name>
252 *      {<cluster> <phone number>} ... XXXXX
253 *      <x coordinate> <y coordinate>
254 *      <printer> ... XXXXX
255 */
256static void display_cgroups(void)
257{
258  struct clusterfile cfile;
259  struct cgroupfile cgfile;
260  struct cgroup *cgroup;
261  int i, j;
262
263  read_clusters(&cfile);
264  read_cgroups(&cgfile);
265
266  printf("%d", cfile.narch);
267  for (i = 0; i < cfile.narch; i++)
268    printf(" %s", cfile.archnames[i]);
269  printf("\n");
270
271  for (i = 0; i < cgfile.ncgroups; i++)
272    {
273      /* Line 1: cluster group number and name */
274      cgroup = &cgfile.cgroups[i];
275      printf("%d\t%s\n", i + 1, cgroup->name);
276
277      /* Line 2: cluster names */
278      for (j = 0; j < cgroup->nclusters; j++)
279        printf("%s %s ", cgroup->clusters[j].name, cgroup->clusters[j].phone);
280      printf("XXXXX\n");
281
282      /* Line 3: X and Y coordinates */
283      printf("%d\t%d\n", cgroup->x, cgroup->y);
284
285      /* Line 4: printer names */
286      for (j = 0; j < cgroup->nprinters; j++)
287        printf("%s ", cgroup->printers[j]);
288      printf("XXXXX\n");
289    }
290}
291
292static void display_clusters(const char *s)
293{
294  struct clusterfile file;
295  struct cluster *cluster;
296  struct outof coltotals[6], rowtotal, *entry;
297  int i, j;
298  char *ct;
299
300  read_clusters(&file);
301  ct = ctime(&file.mtime);
302  ct[strlen(ct) - 1] = 0;
303
304  /* We can handle at most six architecture columns in 80 columns. */
305  if (file.narch > 6)
306    file.narch = 6;
307
308  /* Display the first header line. */
309  printf("             --  Cluster status as of %s:  --\n", ct);
310
311  /* Display the architecture names. */
312  printf("          ");
313  for (i = 0; i < file.narch; i++)
314    {
315      printf("%-9.9s ", file.archnames[i]);
316      coltotals[i].nfree = 0;
317      coltotals[i].total = 0;
318    }
319  printf("TOTAL\n");
320
321  /* Display the fre/tot column headers. */
322  printf("CLUSTER");
323  for (i = 0; i < file.narch + 1; i++)
324    printf("   fre/tot");
325  printf("\n");
326
327  /* Display the divider line. */
328  printf("--------");
329  for (i = 0; i < file.narch + 1; i++)
330    printf("----------");
331  printf("\n");
332
333  /* Display the stats. */
334  for (i = 0; i < file.nclusters; i++)
335    {
336      cluster = &file.clusters[i];
337      if (!cluster_matches(s, cluster))
338        continue;
339      printf("%-8.8s ", cluster->name);
340      rowtotal.nfree = 0;
341      rowtotal.total = 0;
342      for (j = 0; j < file.narch; j++)
343        {
344          entry = &cluster->stats[j];
345          if (entry->total == 0)
346            printf("    -     ");
347          else
348            printf("%3d / %-3d ", entry->nfree, entry->total);
349          coltotals[j].nfree += entry->nfree;
350          coltotals[j].total += entry->total;
351          rowtotal.nfree += entry->nfree;
352          rowtotal.total += entry->total;
353        }
354      printf("%3d / %-3d\n", rowtotal.nfree, rowtotal.total);
355    }
356
357  /* Display the column totals. */
358  rowtotal.nfree = 0;
359  rowtotal.total = 0;
360  printf("TOTALS   ");
361  for (i = 0; i < file.narch; i++)
362    {
363      printf("%3d / %-3d ", coltotals[i].nfree, coltotals[i].total);
364      rowtotal.nfree += coltotals[i].nfree;
365      rowtotal.total += coltotals[i].total;
366    }
367  printf("%3d / %-3d\n", rowtotal.nfree, rowtotal.total);
368}
369
370static void display_help(void)
371{
372  printf("Usage:\tcview [cluster ...]\n");
373  printf("\tcview printers\n");
374  printf("\tcview phones\n");
375}
376
377static void read_clusters(struct clusterfile *file)
378{
379  FILE *fp;
380  char *line = NULL;
381  int linesize, i;
382  struct cluster *cluster;
383  const char *p, *q;
384  struct stat statbuf;
385
386  fp = fopen(LARVNET_PATH_CLUSTERS, "r");
387  if (!fp)
388    {
389      fprintf(stderr, "Sorry, no cluster status information available.\n");
390      exit(1);
391    }
392
393  fstat(fileno(fp), &statbuf);
394  file->mtime = statbuf.st_mtime;
395
396  /* Read in the architecture names. */
397  file->archnames = NULL;
398  file->narch = 0;
399  while (read_line(fp, &line, &linesize) == 0)
400    {
401      if (*line == '-')
402        break;
403
404      /* Add an architecture name. */
405      file->narch++;
406      file->archnames = erealloc(file->archnames,
407                                 file->narch * sizeof(char *));
408      p = skip_spaces(line);
409      q = skip_nonspaces(p);
410      file->archnames[file->narch - 1] = estrndup(p, q - p);
411    }
412
413  /* Read in the cluster information. */
414  file->clusters = NULL;
415  file->nclusters = 0;
416  while (read_line(fp, &line, &linesize) == 0)
417    {
418      /* Make a new cluster entry. */
419      file->nclusters++;
420      file->clusters = erealloc(file->clusters,
421                                file->nclusters * sizeof(struct cluster));
422      cluster = &file->clusters[file->nclusters - 1];
423
424      /* Read the name, public/private field, and phone. */
425      p = skip_spaces(line);
426      q = skip_nonspaces(p);
427      cluster->name = estrndup(p, q - p);
428      p = skip_spaces(q);
429      q = skip_nonspaces(p);
430      cluster->public = first_field_matches(p, "public");
431      p = skip_spaces(q);
432      q = skip_nonspaces(p);
433      cluster->phone = estrndup(p, q - p);
434
435      /* Read the stats. */
436      cluster->stats = emalloc(file->narch * sizeof(struct outof));
437      for (i = 0; i < file->narch; i++)
438        {
439          p = skip_spaces(q);
440          q = skip_nonspaces(p);
441          cluster->stats[i].nfree = atoi(p);
442          p = skip_spaces(q);
443          q = skip_nonspaces(p);
444          cluster->stats[i].total = atoi(p);
445        }
446    }
447
448  fclose(fp);
449}
450
451static void read_printers(struct printerfile *file)
452{
453  FILE *fp;
454  char *line = NULL;
455  int linesize;
456  struct printer *printer;
457  const char *p, *q;
458  struct stat statbuf;
459
460  fp = fopen(LARVNET_PATH_PRINTERS, "r");
461  if (!fp)
462    {
463      fprintf(stderr, "Sorry, no printer status information available.\n");
464      exit(1);
465    }
466
467  fstat(fileno(fp), &statbuf);
468  file->mtime = statbuf.st_mtime;
469
470  file->printers = NULL;
471  file->nprinters = 0;
472  while (read_line(fp, &line, &linesize) == 0)
473    {
474      /* Make a new printer entry. */
475      file->nprinters++;
476      file->printers = erealloc(file->printers,
477                                file->nprinters * sizeof(struct printer));
478      printer = &file->printers[file->nprinters - 1];
479
480      /* Read in the name, cluster name, status, and jobs. */
481      p = skip_spaces(line);
482      q = skip_nonspaces(p);
483      printer->name = estrndup(p, q - p);
484      p = skip_spaces(q);
485      q = skip_nonspaces(p);
486      printer->cluster = estrndup(p, q - p);
487      p = skip_spaces(q);
488      q = skip_nonspaces(p);
489      printer->up = first_field_matches(p, "up");
490      p = skip_spaces(q);
491      printer->jobs = atoi(p);
492    }
493}
494
495static void read_cgroups(struct cgroupfile *file)
496{
497  FILE *fp;
498  char *line = NULL;
499  int linesize;
500  struct cgroup *cgroup;
501  struct cgcluster *cgcluster;
502  const char *p, *q;
503  struct stat statbuf;
504
505  fp = fopen(LARVNET_PATH_CGROUPS, "r");
506  if (!fp)
507    {
508      fprintf(stderr, "No cluster group information available.\n");
509      exit(1);
510    }
511
512  fstat(fileno(fp), &statbuf);
513  file->mtime = statbuf.st_mtime;
514
515  file->cgroups = NULL;
516  file->ncgroups = 0;
517  while (read_line(fp, &line, &linesize) == 0)
518    {
519      /* Make a new cluster group entry. */
520      file->ncgroups++;
521      file->cgroups = erealloc(file->cgroups,
522                               file->ncgroups * sizeof(struct cgroup));
523      cgroup = &file->cgroups[file->ncgroups - 1];
524
525      /* Read in the cluster group name and coordinates. */
526      p = skip_spaces(line);
527      q = skip_nonspaces(p);
528      cgroup->name = estrndup(p, q - p);
529      p = skip_spaces(q);
530      q = skip_nonspaces(p);
531      cgroup->x = atoi(p);
532      p = skip_spaces(q);
533      q = skip_nonspaces(p);
534      cgroup->y = atoi(p);
535
536      /* Read in the cluster names. */
537      cgroup->clusters = NULL;
538      cgroup->nclusters = 0;
539      while (1)
540        {
541          p = skip_spaces(q);
542          q = skip_nonspaces(p);
543          if (!*p || *p == '-')
544            break;
545          cgroup->nclusters++;
546          cgroup->clusters = erealloc(cgroup->clusters, cgroup->nclusters
547                                      * sizeof(struct cgcluster));
548          cgcluster = &cgroup->clusters[cgroup->nclusters - 1];
549          cgcluster->name = estrndup(p, q - p);
550          p = skip_spaces(q);
551          q = skip_nonspaces(p);
552          cgcluster->phone = estrndup(p, q - p);
553        }
554
555      /* Read in the printer names. */
556      cgroup->printers = NULL;
557      cgroup->nprinters = 0;
558      p = skip_spaces(q);
559      while (*p)
560        {
561          q = skip_nonspaces(p);
562          cgroup->nprinters++;
563          cgroup->printers = erealloc(cgroup->printers,
564                                      cgroup->nprinters * sizeof(char *));
565          cgroup->printers[cgroup->nprinters - 1] = estrndup(p, q - p);
566          p = skip_spaces(q);
567        }
568    }
569}
570
571static int first_field_matches(const char *s, const char *word)
572{
573  int len = strlen(word);
574
575  return (strncasecmp(s, word, len) == 0 &&
576          (isspace((unsigned char)s[len]) || !s[len]));
577}
578
579static int cluster_matches(const char *s, struct cluster *cluster)
580{
581  const char *p, *q;
582
583  /* If no cluster names specified, match public clusters. */
584  if (!*s)
585    return cluster->public;
586
587  p = skip_spaces(s);
588  while (*p)
589    {
590      if (first_field_matches(p, "public") && cluster->public)
591        return 1;
592      if (first_field_matches(p, "all")
593          || first_field_matches(p, cluster->name))
594        return 1;
595      q = skip_nonspaces(p);
596      p = skip_spaces(q);
597    }
598
599  return 0;
600}
601
602/* Read a line from a file into a dynamically allocated buffer,
603 * zeroing the trailing newline if there is one.  The calling routine
604 * may call read_line multiple times with the same buf and bufsize
605 * pointers; *buf will be reallocated and *bufsize adjusted as
606 * appropriate.  The initial value of *buf should be NULL.  After the
607 * calling routine is done reading lines, it should free *buf.  This
608 * function returns 0 if a line was successfully read, 1 if the file
609 * ended, and -1 if there was an I/O error.
610 */
611
612static int read_line(FILE *fp, char **buf, int *bufsize)
613{
614  char *newbuf;
615  int offset = 0, len;
616
617  if (*buf == NULL)
618    {
619      *buf = emalloc(128);
620      *bufsize = 128;
621    }
622
623  while (1)
624    {
625      if (!fgets(*buf + offset, *bufsize - offset, fp))
626        return (offset != 0) ? 0 : (ferror(fp)) ? -1 : 1;
627      len = offset + strlen(*buf + offset);
628      if ((*buf)[len - 1] == '\n')
629        {
630          (*buf)[len - 1] = 0;
631          return 0;
632        }
633      offset = len;
634
635      /* Allocate more space. */
636      newbuf = erealloc(*buf, *bufsize * 2);
637      *buf = newbuf;
638      *bufsize *= 2;
639    }
640}
641
642static const char *skip_spaces(const char *p)
643{
644  while (isspace((unsigned char)*p))
645    p++;
646  return p;
647}
648
649static const char *skip_nonspaces(const char *p)
650{
651  while (*p && !isspace((unsigned char)*p))
652    p++;
653  return p;
654}
655
656static void *emalloc(size_t size)
657{
658  void *ptr;
659
660  ptr = malloc(size);
661  if (!ptr)
662    {
663      fprintf(stderr, "Ran out of memory!\n");
664      exit(1);
665    }
666  return ptr;
667}
668
669static void *erealloc(void *ptr, size_t size)
670{
671  ptr = realloc(ptr, size);
672  if (!ptr)
673    {
674      fprintf(stderr, "Ran out of memory!\n");
675      exit(1);
676    }
677  return ptr;
678}
679
680static char *estrndup(const char *s, size_t n)
681{
682  char *new_s;
683
684  new_s = emalloc(n + 1);
685  memcpy(new_s, s, n);
686  new_s[n] = 0;
687  return new_s;
688}
Note: See TracBrowser for help on using the repository browser.