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

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