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

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