strlcpy restricted to array length.
[openafs.git] / src / scout / scout.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  *
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 /*
11  * Scout: A quick and (semi-)dirty attempt at the old CMU vopcon.
12  *------------------------------------------------------------------------*/
13
14 #include <afsconfig.h>
15 #include <afs/param.h>
16
17 #include <roken.h>
18 #include <opr/softsig.h>
19
20 #undef  IN
21 #include <afs/gtxwindows.h>             /*Generic window package */
22 #include <afs/gtxobjects.h>             /*Object definitions */
23 #include <afs/gtxtextobj.h>             /*Text object interface */
24 #include <afs/gtxlightobj.h>    /*Light object interface */
25 #include <afs/gtxcurseswin.h>   /*Curses window package */
26 #include <afs/gtxdumbwin.h>             /*Dumb terminal window package */
27 #include <afs/gtxX11win.h>              /*X11 window package */
28 #include <afs/gtxframe.h>               /*Frame package */
29 #include <afs/gtxinput.h>
30 #include <afs/cmd.h>            /*Command interpretation library */
31 #include <afs/fsprobe.h>                /*Interface for fsprobe module */
32 #include <afs/afsutil.h>
33
34 #include <string.h>
35
36 /*
37  * Command line parameter indicies.
38  */
39 #define P_SERVER    0
40 #define P_BASE      1
41 #define P_FREQ      2
42 #define P_HOST      3
43 #define P_ATTENTION 4
44 #define P_DEBUG     5
45 #define P_WIDTHS    6
46
47 /*
48   * Define the default width in chars for each light object on the mini-line.
49   */
50 #define LIGHTOBJ_CONN_WIDTH      5
51 #define LIGHTOBJ_FETCH_WIDTH    10
52 #define LIGHTOBJ_STORE_WIDTH    10
53 #define LIGHTOBJ_WK_WIDTH        5
54 #define LIGHTOBJ_SRVNAME_WIDTH  13
55 #define LIGHTOBJ_DISK_WIDTH     12
56
57 /*
58  * Define column width indices.
59  */
60 #define COL_CONN        0
61 #define COL_FETCH       1
62 #define COL_STORE       2
63 #define COL_WK          3
64 #define COL_SRVNAME     4
65 #define COL_DISK        5
66
67 /*
68  * Define the types of justification we can perform.
69  */
70 #define SCOUT_RIGHT_JUSTIFY     0
71 #define SCOUT_LEFT_JUSTIFY      1
72 #define SCOUT_CENTER            2
73
74 /*
75  * Define the types of truncation we can perform.
76  */
77 #define SCOUT_LEFT_TRUNC        0
78 #define SCOUT_RIGHT_TRUNC       1
79
80 /*
81  * Define whether the value passed is a labeled disk quantity.
82  */
83 #define SCOUT_ISNT_LDISK        0
84 #define SCOUT_IS_LDISK          1
85
86 /*
87   * We sometimes use index-base pointers, so we need a distinguished
88   * NIL value.
89   */
90 #define SCOUT_NIL  (-1)
91
92 /*
93   * Structure describing everything you want to know about a FileServer
94   * disk.
95   */
96 struct scout_disk {
97     int prev;                   /*Index of previous list entry */
98     int next;                   /*Index of next list entry */
99     int active;                 /*Is this disk known to exist? */
100     char *name;                 /*Single-letter disk name */
101     struct onode *disk_lp;      /*Ptr to disk light object */
102 };
103
104 /*
105   * Structure defining all the objects in the Scout line (or lines)
106   * for each server being watched.  Note that scout_disk linked list
107   * for used disks is ordered alphabetically.
108   */
109 struct mini_line {
110     /*
111      * Information on server location & configuration.
112      */
113     struct sockaddr_in skt;     /*Server's socket info */
114     int numDisks;               /*Number of disks used */
115     /*
116      * Screen location info.
117      */
118     int base_line;              /*Line number on the screen */
119     int num_lines;              /*Number of lines of info */
120     /*
121      * Associated light objects.
122      */
123     struct onode *currConns_lp; /*Number of current connections */
124     struct onode *fetches_lp;   /*Number of data fetches */
125     struct onode *stores_lp;    /*Number of data stores */
126     struct onode *workstations_lp;      /*Workstation info */
127     struct onode *srvName_lp;   /*Server name */
128     struct scout_disk disks[VOLMAXPARTS];       /*Info on all the disks */
129     int used_head;              /*Index of first used disk struct */
130     int used_tail;              /*Index of last used disk struct */
131     int free_head;              /*Index of first free disk struct */
132     int free_tail;              /*Index of last free disk struct */
133 };
134
135 /*
136   * Structure defining the contents of the Scout screen.
137   */
138 struct mini_screen {
139     int numServers;             /*Number of servers being watched */
140     int base_line_num;          /*Base mini-line number */
141     struct mini_line *line;     /*Array of screen lines */
142 };
143
144 static char pn[] = "scout";     /*Program name */
145 static int scout_debug = 0;     /*Is debugging turned on? */
146 static FILE *scout_debugfd;     /*Debugging file descriptor */
147 static int scout_gtx_initialized = 0;   /*Has gtx been initialized? */
148 static struct mini_screen scout_screen; /*Mini-screen itself */
149 static int scout_probefreq;     /*Probe frequency in seconds */
150 static char scout_basename[64]; /*Base server name */
151 static char scout_hostname[128];        /*Name of machine we're running on */
152 static int scout_showhostname = 0;      /*Show name of machine we're on? */
153 static char scout_blankline[256];       /*Blank line */
154 static struct gwin *scout_gwin; /*Window to use */
155 static struct gtx_frame *scout_frame;   /*Frame to use */
156 static struct onode *scout_banner0_lp;  /*Banner light, line 0 */
157 static struct onode *scout_banner1_lp;  /*Banner light, line 1 */
158 static struct onode *scout_banner2_lp;  /*Banner light, line 2 */
159 static int scout_DiskLightLeftCol = 0;  /*Column for leftmost disk light */
160 static struct gwin_sizeparams scout_frameDims;  /*Frame dimensions */
161
162 static int scout_col_width[] = { LIGHTOBJ_CONN_WIDTH,
163     LIGHTOBJ_FETCH_WIDTH,
164     LIGHTOBJ_STORE_WIDTH,
165     LIGHTOBJ_WK_WIDTH,
166     LIGHTOBJ_SRVNAME_WIDTH,
167     LIGHTOBJ_DISK_WIDTH
168 };
169
170 /*
171   * Attention thresholds & modes.
172   */
173 #define SCOUT_ATTN_NOTUSED  (-1)
174 #define SCOUT_DISKM_PCUSED    0
175 #define SCOUT_DISKM_MINFREE   1
176
177 static int scout_attn_conn = SCOUT_ATTN_NOTUSED;
178 static int scout_attn_fetch = SCOUT_ATTN_NOTUSED;
179 static int scout_attn_store = SCOUT_ATTN_NOTUSED;
180 static int scout_attn_workstations = SCOUT_ATTN_NOTUSED;
181 static int scout_attn_disk_mode = SCOUT_DISKM_PCUSED;
182 static int scout_attn_disk_minfree = 1000;
183 static float scout_attn_disk_pcused = 0.95;
184 static char scout_attn_disk_pcusedstr[8] = "95";
185
186 /*
187   * Some strings we'll be using over and over again.
188   */
189 static char scout_Banner[256];
190 static const char *scout_label[] =
191     { "Conn", "Fetch", "Store", "Ws", "", "Disk attn" };
192 static const char *scout_underline[] =
193     { "----", "--------", "--------", "-----", "", "----------" };
194
195
196 /*------------------------------------------------------------------------
197  * scout_CleanExit
198  *
199  * Description:
200  *      Exits cleanly from the Scout.  If gtx has not yet been initialized,
201  *      then we simply call exit() with the value provided.  Otherwise,
202  *      we call the appropriate gtx routine to exit cleanly from gtx, which
203  *      must reset the terminal or window.  We also close the debugging
204  *      file descriptor, if one has been opened.
205  *
206  * Arguments:
207  *      int a_exitval : Value with which to exit the program.
208  *
209  * Returns:
210  *      Void.
211  *
212  * Environment:
213  *      Actions depend on scout_gtx_initialized.
214  *
215  * Side Effects:
216  *      This routine will always exit Scout.
217  *------------------------------------------------------------------------*/
218
219 static void
220 scout_CleanExit(int a_exitval)
221 {                               /*scout_CleanExit */
222
223     static char rn[] = "scout_CleanExit";       /*Routine name */
224
225     if (scout_debugfd != (FILE *) 0) {
226         fprintf(scout_debugfd, "[%s] Closing debugging file\n", rn);
227         fclose(scout_debugfd);
228     }
229
230     if (scout_gtx_initialized) {
231         gtxframe_exitValue = a_exitval;
232         gtxframe_ExitCmd((void *)(&gtxframe_exitValue), NULL);
233     } else
234         exit(a_exitval);
235
236 }                               /*scout_CleanExit */
237
238 /*------------------------------------------------------------------------
239  * mini_initLightObject
240  *
241  * Description:
242  *      Create and initialize a light onode according to the given
243  *      parameters.
244  *
245  * Arguments:
246  *      char *a_name       : Ptr to the light's string name.
247  *      int a_x            : X offset.
248  *      int a_y            : Y offset.
249  *      int a_width        : Width in chars.
250  *      struct gwin *a_win : Ptr to window structure.
251  *
252  * Returns:
253  *      Ptr to new light onode on success,
254  *      A null pointer otherwise.
255  *
256  * Environment:
257  *      See above.
258  *
259  * Side Effects:
260  *      As advertised.
261  *------------------------------------------------------------------------*/
262
263 static struct onode *
264 mini_initLightObject(char *a_name, int a_x, int a_y, int a_width, struct gwin *a_win)
265 {                               /*mini_initLightObject */
266
267     static char rn[] = "mini_initLightObject";  /*Routine name */
268     struct onode *newlightp;    /*Ptr to new light onode */
269     /*We only support curses right now */
270     struct gator_light_crparams light_crparams; /*Light creation params */
271     int name_len;               /*True length of name */
272
273     if (scout_debug) {
274         fprintf(scout_debugfd,
275                 "[%s] Called for name '%s', [%d, %d], %d-char field\n", rn,
276                 a_name, a_x, a_y, a_width);
277         fflush(scout_debugfd);
278     }
279     newlightp = NULL;
280
281     /*
282      * Set up the creation parameters according to the information we've
283      * received.
284      */
285     light_crparams.onode_params.cr_type = GATOR_OBJ_LIGHT;
286     name_len = strlen(a_name);
287     if (scout_debug)
288         fprintf(scout_debugfd, "[%s] Name '%s' has %d chars\n", rn, a_name,
289                 name_len);
290
291     if (a_width >= sizeof(light_crparams.onode_params.cr_name))
292         a_width = sizeof(light_crparams.onode_params.cr_name) - 1;
293
294     if (strlcpy(light_crparams.onode_params.cr_name, a_name, a_width + 1) >= a_width + 1)
295         /* The name is truncated, put a '*' at the end to note */
296         light_crparams.onode_params.cr_name[a_width - 1] = '*';
297
298     light_crparams.onode_params.cr_x = a_x;
299     light_crparams.onode_params.cr_y = a_y;
300     light_crparams.onode_params.cr_width = a_width;
301     light_crparams.onode_params.cr_height = 1;
302     light_crparams.onode_params.cr_window = a_win;
303     light_crparams.onode_params.cr_home_obj = NULL;
304     light_crparams.onode_params.cr_prev_obj = NULL;
305     light_crparams.onode_params.cr_parent_obj = NULL;
306     light_crparams.onode_params.cr_helpstring = NULL;
307
308     light_crparams.appearance = 0;
309     light_crparams.flashfreq = 0;
310     sprintf(light_crparams.label, "%s", a_name);
311     light_crparams.label_x = 0;
312     light_crparams.label_y = 0;
313
314     newlightp =
315         gator_objects_create((struct onode_createparams *)(&light_crparams));
316
317     /*
318      * Return the news, happy or not.
319      */
320     return (newlightp);
321
322 }                               /*mini_initLightObject */
323
324 /*------------------------------------------------------------------------
325  * scout_initDiskLightObjects
326  *
327  * Description:
328  *      Create and initialize all Scout light objects for a server's
329  *      disks.
330  *
331  * Arguments:
332  *      struct scout_disk *a_line : Ptr to the server object line.
333  *      struct gwin *a_win        : Ptr to window structure.
334  *
335  * Returns:
336  *      0 on success,
337  *      -1 otherwise.
338  *
339  * Environment:
340  *      See above.
341  *
342  * Side Effects:
343  *      As advertised.
344  *------------------------------------------------------------------------*/
345
346 static int
347 scout_initDiskLightObjects(struct mini_line *a_line, struct gwin *a_win)
348 {                               /*scout_initDiskLightObjects */
349
350     static char rn[] = "scout_initDiskLightObjects";    /*Routine name */
351     struct scout_disk *curr_disk;       /*Ptr to current disk being set up */
352     int i;                      /*Loop variable */
353
354     if (scout_debug) {
355         fprintf(scout_debugfd, "[%s] Called\n", rn);
356         fflush(scout_debugfd);
357     }
358
359     /*
360      * Set up the base linked list fields.
361      */
362     a_line->used_head = SCOUT_NIL;
363     a_line->used_tail = SCOUT_NIL;
364     a_line->free_head = 0;
365     a_line->free_tail = VOLMAXPARTS - 1;
366
367     /*
368      * Sweep through the disk structures, creating the light objects and
369      * marking them all as free.
370      */
371     curr_disk = a_line->disks;
372     for (i = 0; i < VOLMAXPARTS; i++) {
373         /*
374          * Create the disk light object.
375          */
376         if ((curr_disk->disk_lp = mini_initLightObject("Disk",  /*Object name */
377                                                        0,       /*X value */
378                                                        0,       /*Y value */
379                                                        scout_col_width[COL_DISK],       /*Width */
380                                                        a_win))  /*Window */
381             == NULL) {
382             fprintf(stderr, "[%s:%s] Can't create disk %d light object\n", pn,
383                     rn, i);
384             return (-1);
385         }
386
387         /*
388          * Set the other fields in the disk records; Note that in the
389          * fencepost cases, the prev and next pointers will have to be
390          * fixed.
391          */
392         curr_disk->prev = i - 1;
393         curr_disk->next = i + 1;
394         curr_disk->active = 0;
395         curr_disk->name = "";
396
397         /*
398          * Bump up to the next disk structure.
399          */
400         curr_disk++;
401
402     }                           /*for each disk structure */
403
404     /*
405      * We made it all the way through.  Fix the fencepost pointers, set
406      * the overall pointers, then return success.
407      */
408     a_line->disks[0].prev = SCOUT_NIL;
409     a_line->disks[VOLMAXPARTS - 1].next = SCOUT_NIL;
410
411     return (0);
412
413 }                               /*scout_initDiskLightObjects */
414
415
416 /*------------------------------------------------------------------------
417  * mini_justify
418  *
419  * Description:
420  *      Place the chars in the source buffer into the target buffer
421  *      with the desired justification, either centered, left-justified
422  *      or right-justified.  Also, support inidication of truncation
423  *      with a star (*), either on the left or right of the string,
424  *      and whether we're justifying a labeled disk quantity.
425  *
426  * Arguments:
427  *      char *a_srcbuff     : Ptr to source char buffer.
428  *      char *a_dstbuff     : Ptr to dest char buffer.
429  *      int a_dstwidth      : Width of dest buffer in chars.
430  *      int a_justification : Kind of justification.
431  *      int a_rightTrunc    : If non-zero, place the truncation char
432  *                            on the right of the string.  Otherwise,
433  *                            place it on the left.
434  *      int a_isLabeledDisk : Is this a labeled disk quantity?
435  *
436  * Returns:
437  *      0 on success,
438  *      -1 otherwise.
439  *
440  * Environment:
441  *      All it needs to know is exported by the fsprobe module, namely
442  *      the data structure where the probe results are stored.
443  *
444  * Side Effects:
445  *      As advertised.
446  *------------------------------------------------------------------------*/
447
448 int
449 mini_justify(char *a_srcbuff, char *a_dstbuff, int a_dstwidth,
450              int a_justification, int a_rightTrunc,
451              int a_isLabeledDisk)
452 {                               /*mini_justify */
453
454     static char rn[] = "mini_justify";  /*Routine name */
455     int leftpad_chars;          /*# of chars for left-padding */
456     int num_src_chars;          /*# of chars in source */
457     int true_num_src_chars;     /*# src chars before truncation */
458     int trunc_needed;           /*Is truncation needed? */
459     char diskChar = 0;          /*Disk name prefix */
460
461     if (scout_debug) {
462         fprintf(scout_debugfd, "[%s] Called with '%s', dest width=%d\n", rn,
463                 a_srcbuff, a_dstwidth);
464         fflush(scout_debugfd);
465     }
466
467     /*
468      * Remember the disk label, if we've been passed such a thing.
469      */
470     if (a_isLabeledDisk)
471         diskChar = *a_srcbuff;
472
473     /*
474      * If the destination width will overrun the gtx string storage,
475      * we automatically shorten up.
476      */
477     if (a_dstwidth > GATOR_LABEL_CHARS) {
478         if (scout_debug) {
479             fprintf(scout_debugfd,
480                     "[%s] Dest width (%d) > gtx buflen (%d), shrinking dest width\n",
481                     rn, a_dstwidth, GATOR_LABEL_CHARS);
482             fflush(scout_debugfd);
483         }
484         a_dstwidth = GATOR_LABEL_CHARS;
485     }
486
487     /*
488      * If our source string is too long, prepare for truncation.
489      */
490     true_num_src_chars = strlen(a_srcbuff);
491     if (true_num_src_chars >= a_dstwidth) {
492         trunc_needed = 1;
493         num_src_chars = a_dstwidth - 1;
494         leftpad_chars = 0;
495         if (!a_rightTrunc)
496             a_srcbuff += (true_num_src_chars - num_src_chars);
497     } else {
498         trunc_needed = 0;
499         num_src_chars = true_num_src_chars;
500
501         /*
502          * Compute the necessary left-padding.
503          */
504         switch (a_justification) {
505
506         case SCOUT_RIGHT_JUSTIFY:
507             leftpad_chars = (a_dstwidth - 1) - num_src_chars;
508             break;
509
510         case SCOUT_LEFT_JUSTIFY:
511             /*
512              * This is the really easy one.
513              */
514             leftpad_chars = 0;
515             break;
516
517         case SCOUT_CENTER:
518             leftpad_chars = ((a_dstwidth - 1) - num_src_chars) / 2;
519             break;
520
521         default:
522             fprintf(stderr, "[%s] Illegal justification command: %d", rn,
523                     a_justification);
524             return (-1);
525         }                       /*Switch on justification type */
526     }
527
528     /*
529      * Clear out the dest buffer, then place the source string at the
530      * appropriate padding location.  Remember to place a string
531      * terminator at the end of the dest buffer, plus whatever truncation
532      * may be needed.  If we're left-truncating, we've already shifted
533      * the src buffer appropriately.
534      */
535     strncpy(a_dstbuff, scout_blankline, a_dstwidth);
536     strncpy(a_dstbuff + leftpad_chars, a_srcbuff, num_src_chars);
537     *(a_dstbuff + a_dstwidth - 1) = '\0';
538     if (trunc_needed) {
539         if (a_rightTrunc)
540             *(a_dstbuff + a_dstwidth - 2) = '*';        /*Truncate on the right */
541         else {
542             if (a_isLabeledDisk) {
543                 *a_dstbuff = diskChar;
544                 *(a_dstbuff + 1) = ':';
545                 *(a_dstbuff + 2) = '*'; /*Truncate on the left, disk */
546             } else
547                 *a_dstbuff = '*';       /*Truncate on the left, non-disk */
548         }
549     }
550
551     /*Handle truncations */
552     /*
553      * Return the good news.
554      */
555     return (0);
556
557 }                               /*mini_justify */
558
559 /*------------------------------------------------------------------------
560  * scout_SetNonDiskLightLine
561  *
562  * Description:
563  *      Given a mini-line and a line number, place all non-disk lights
564  *      on that line.
565  *
566  * Arguments:
567  *      struct mini_line *a_srvline : Ptr to server descriptor.
568  *      int a_linenum               : Line number to move to.
569  *
570  * Returns:
571  *      Nothing.
572  *
573  * Environment:
574  *      The light's location is stored not only in the onode, but in
575  *      the light's rock, so we have to change both sets of value.
576  *
577  * Side Effects:
578  *      As described.
579  *------------------------------------------------------------------------*/
580
581 static void
582 scout_SetNonDiskLightLine(struct mini_line *a_srvline, int a_linenum)
583 {                               /*scout_SetNonDiskLightLine */
584
585     struct gator_lightobj *nondisk_lightdata;   /*Non-disk light data field */
586     struct gwin_strparams *nondisk_strparams;   /*Associated string params */
587
588     /*
589      * Do the exact same operation for each non-disk light in the record.
590      */
591     a_srvline->currConns_lp->o_y = a_linenum;
592     nondisk_lightdata =
593         (struct gator_lightobj *)(a_srvline->currConns_lp->o_data);
594     nondisk_strparams = (struct gwin_strparams *)(nondisk_lightdata->llrock);
595     nondisk_strparams->y = a_linenum;
596
597     a_srvline->fetches_lp->o_y = a_linenum;
598     nondisk_lightdata =
599         (struct gator_lightobj *)(a_srvline->fetches_lp->o_data);
600     nondisk_strparams = (struct gwin_strparams *)(nondisk_lightdata->llrock);
601     nondisk_strparams->y = a_linenum;
602
603     a_srvline->stores_lp->o_y = a_linenum;
604     nondisk_lightdata =
605         (struct gator_lightobj *)(a_srvline->stores_lp->o_data);
606     nondisk_strparams = (struct gwin_strparams *)(nondisk_lightdata->llrock);
607     nondisk_strparams->y = a_linenum;
608
609     a_srvline->workstations_lp->o_y = a_linenum;
610     nondisk_lightdata =
611         (struct gator_lightobj *)(a_srvline->workstations_lp->o_data);
612     nondisk_strparams = (struct gwin_strparams *)(nondisk_lightdata->llrock);
613     nondisk_strparams->y = a_linenum;
614
615     a_srvline->srvName_lp->o_y = a_linenum;
616     nondisk_lightdata =
617         (struct gator_lightobj *)(a_srvline->srvName_lp->o_data);
618     nondisk_strparams = (struct gwin_strparams *)(nondisk_lightdata->llrock);
619     nondisk_strparams->y = a_linenum;
620
621 }                               /*scout_SetNonDiskLightLine */
622
623 /*------------------------------------------------------------------------
624  * scout_RecomputeLightLocs
625  *
626  * Description:
627  *      Given a pointer to a server record, recompute its disk light
628  *      locations (and keep proper track of the number of screen lines
629  *      required for the server record).
630  *
631  * Arguments:
632  *      struct mini_line *a_srvline : Ptr to server descriptor.
633  *
634  * Returns:
635  *      0 if anything went wrong,
636  *      else the number of lines used by this server record.
637  *
638  * Environment:
639  *      One or more records have joined or left the used light list
640  *      for this server.  We're not sure which ones, so we recompute
641  *      them all.  We may also have had a width change in the gtx
642  *      frame.  The base_line field in the server record is guaranteed
643  *      to be correct at this point.
644  *
645  * Side Effects:
646  *      As described.
647  *------------------------------------------------------------------------*/
648
649 static int
650 scout_RecomputeLightLocs(struct mini_line *a_srvline)
651 {                               /*scout_RecomputeLightLocs */
652
653     static char rn[] = "scout_RecomputeLightLocs";      /*Routine name */
654     int lights_per_line;        /*# lights/line */
655     int lights_this_line;       /*# lights on cur line */
656     int curr_idx;               /*Current disk light idx */
657     struct scout_disk *sc_disk; /*Ptr to disk record array */
658     int lines_for_server;       /*Num lines in server record */
659     int curr_line;              /*Current line being filled */
660     int curr_x;                 /*Current x value for light */
661     struct gator_lightobj *disk_lightdata;      /*Disk light data field */
662     struct gwin_strparams *disk_strparams;      /*String params for disk light */
663
664     if (scout_debug) {
665         fprintf(scout_debugfd, "[%s] Called\n", rn);
666         fflush(scout_debugfd);
667     }
668
669     /*
670      * If we haven't yet computed the column for the leftmost disk light,
671      * do it now.
672      */
673     if (scout_DiskLightLeftCol == 0) {
674         int i;
675         int num_cols = sizeof(scout_col_width) / sizeof(*scout_col_width);
676         scout_DiskLightLeftCol = 0;
677         for (i = 0; i < (num_cols - 1); i++) {
678             scout_DiskLightLeftCol += scout_col_width[i] + 1;
679         }
680     }
681
682     /*
683      * Calculate how many disk light objects can fit in one line.
684      */
685     lights_per_line =
686         (scout_frameDims.maxx -
687          scout_DiskLightLeftCol) / (scout_col_width[COL_DISK] + 1);
688     if (scout_debug) {
689         fprintf(scout_debugfd, "[%s] %d lights per line\n", rn,
690                 lights_per_line);
691         fflush(scout_debugfd);
692     }
693
694     /*
695      * Sweep through the used disk light list, computing the location
696      * for each.
697      */
698     lights_this_line = 0;
699     curr_idx = a_srvline->used_head;
700     lines_for_server = 1;
701     curr_line = a_srvline->base_line;
702     curr_x = scout_DiskLightLeftCol;
703
704     while (curr_idx != SCOUT_NIL) {
705         /*
706          * Bump the number of lines for the server if we've already
707          * filled up the current line.
708          */
709         if (lights_this_line >= lights_per_line) {
710             lights_this_line = 0;
711             lines_for_server++;
712             curr_line++;
713             curr_x = scout_DiskLightLeftCol;
714         }
715
716         /*Current line filled up */
717         /*
718          * Set the current disk light's location.
719          */
720         sc_disk = a_srvline->disks;
721         sc_disk[curr_idx].disk_lp->o_x = curr_x;
722         sc_disk[curr_idx].disk_lp->o_y = curr_line;
723         disk_lightdata =
724             (struct gator_lightobj *)(sc_disk[curr_idx].disk_lp->o_data);
725         disk_strparams = (struct gwin_strparams *)(disk_lightdata->llrock);
726         disk_strparams->x = curr_x;
727         disk_strparams->y = curr_line;
728         if (scout_debug) {
729             fprintf(scout_debugfd,
730                     "[%s] Disk light at index %d located at [%d, %d]\n", rn,
731                     curr_idx, curr_x, curr_line);
732             fflush(scout_debugfd);
733         }
734
735         /*
736          * Record the inclusion of the light and move on to the next
737          * light, if any.
738          */
739         lights_this_line++;
740         curr_x += scout_col_width[COL_DISK] + 1;
741         curr_idx = sc_disk[curr_idx].next;
742
743     }                           /*Update each used disk light */
744
745     /*
746      * Remember to record the (possibly new) number of lines in the
747      * server record before returning the value of that field.
748      */
749     if (a_srvline->num_lines != lines_for_server) {
750         if (scout_debug) {
751             fprintf(scout_debugfd,
752                     "[%s] Server previously had %d screen lines; now changed\n",
753                     rn, a_srvline->num_lines);
754             fflush(scout_debugfd);
755         }
756         a_srvline->num_lines = lines_for_server;
757     }
758
759     if (scout_debug) {
760         fprintf(scout_debugfd, "[%s] Server has %d screen lines\n", rn,
761                 a_srvline->num_lines);
762         fflush(scout_debugfd);
763     }
764     return (a_srvline->num_lines);
765
766 }                               /*scout_RecomputeLightLocs */
767
768 /*------------------------------------------------------------------------
769  * scout_FindUsedDisk
770  *
771  * Description:
772  *      Given a single-letter disk name and a pointer to the current server
773  *      record, look for a used disk record with that name within the server.
774  *      If we can't find one, we create and incorporate one, and return that
775  *      fact to our caller.
776  *
777  * Arguments:
778  *      char a_diskname             : Single-char disk name.
779  *      struct mini_line *a_srvline : Ptr to server descriptor.
780  *      int *a_record_added         : Was a new record added?
781  *
782  * Returns:
783  *      Index of matching used disk record,
784  *      SCOUT_NIL otherwise.
785  *
786  * Return via parameter:
787  *      a_record_added set to 1 iff record was added to the used disk list.
788  *
789  * Environment:
790  *      Used disk records are kept in alphabetical order by the single-char
791  *      disk name.  Should a matching used disk record not be found, one is
792  *      pulled from the free pool.
793  *
794  * Side Effects:
795  *      An entry may be pulled off the free list and inserted into the
796  *      used list.  This entry is placed in the update list for the
797  *      current gtx frame (at least not by this routine).
798  *------------------------------------------------------------------------*/
799
800 static int
801 scout_FindUsedDisk(char *a_diskname, struct mini_line *a_srvline, int *a_record_added)
802 {                               /*scout_FindUsedDisk */
803
804     static char rn[] = "scout_FindUsedDisk";    /*Routine name */
805     int curr_idx;               /*Disk record index */
806     int append_idx;             /*Index to append after */
807     int new_idx;                /*Index of new used record */
808     struct scout_disk *sc_disk; /*Ptr to disk record */
809     int code;                   /*Function return value */
810
811     if (scout_debug) {
812         fprintf(scout_debugfd, "[%s] Called\n", rn);
813         fflush(scout_debugfd);
814     }
815
816     /*
817      * Sweep through the used disk records, looking for a match.
818      */
819     curr_idx = a_srvline->used_head;
820     append_idx = SCOUT_NIL;
821     sc_disk = a_srvline->disks;
822     if (scout_debug) {
823         fprintf(scout_debugfd,
824                 "[%s] Scanning existing used disk entries for disk '%s'\n",
825                 rn, a_diskname);
826         fflush(scout_debugfd);
827     }
828     while (curr_idx != SCOUT_NIL) {
829         if (scout_debug) {
830             fprintf(scout_debugfd, "[%s] Disk %d is named '%s'\n", rn,
831                     curr_idx, sc_disk[curr_idx].name);
832             fflush(scout_debugfd);
833         }
834         if (strcmp(sc_disk[curr_idx].name, a_diskname) == 0) {
835             /*
836              * We found it!  Bug out.
837              */
838             if (scout_debug) {
839                 fprintf(scout_debugfd, "[%s] Match found\n", rn);
840                 fflush(scout_debugfd);
841             }
842             return (curr_idx);
843         }
844
845         /*
846          * If we are alphabetically past the given disk name, we
847          * know that we won't find it in the used disk list; we
848          * also have the append index set correctly.
849          */
850         if (strcmp(a_diskname, sc_disk[curr_idx].name) < 0) {
851             if (scout_debug) {
852                 fprintf(scout_debugfd, "[%s] Disk '%s' can't be here\n", rn,
853                         a_diskname);
854                 fflush(scout_debugfd);
855             }
856             break;
857         }
858
859         /*
860          * There's still hope we'll find it.  Move on to the next used
861          * disk record, keeping this index as the best candidate so far
862          * for appending a missing entry.
863          */
864         append_idx = curr_idx;
865         curr_idx = sc_disk[curr_idx].next;
866     }                           /*Look for match */
867
868     /*
869      * We didn't find the record we wanted, which means we'll pull a
870      * record out of the free pool for it.  If we didn't find a place
871      * to append it, we then insert it at the beginning of the queue.
872      */
873     if (a_srvline->free_head == SCOUT_NIL)
874         return (SCOUT_NIL);
875     *a_record_added = 1;
876     new_idx = a_srvline->free_head;
877     if (scout_debug) {
878         fprintf(scout_debugfd, "[%s] Choosing free index %d for new entry\n",
879                 rn, new_idx);
880         fflush(scout_debugfd);
881     }
882     a_srvline->free_head = sc_disk[new_idx].next;
883     if (a_srvline->free_head == SCOUT_NIL)
884         a_srvline->free_tail = SCOUT_NIL;
885
886     /*
887      * Fill in the new record.
888      */
889     sc_disk[new_idx].active = 0;
890     sc_disk[new_idx].name = a_diskname;
891
892     /*
893      * Insert the new record where it belongs on the used disk list.
894      */
895     if (append_idx == SCOUT_NIL) {
896         /*
897          * It belongs at the beginning of the list.
898          */
899         if (scout_debug) {
900             fprintf(scout_debugfd, "[%s] Inserted at used disk head\n", rn);
901             fflush(scout_debugfd);
902         }
903         sc_disk[new_idx].next = a_srvline->used_head;
904         sc_disk[new_idx].prev = SCOUT_NIL;
905         a_srvline->used_head = new_idx;
906         if (a_srvline->used_tail == SCOUT_NIL)
907             a_srvline->used_tail = new_idx;
908     } else {
909         if (scout_debug) {
910             fprintf(scout_debugfd, "[%s] Entry appended after index %d\n", rn,
911                     append_idx);
912             fflush(scout_debugfd);
913         }
914         sc_disk[new_idx].prev = append_idx;
915         sc_disk[new_idx].next = sc_disk[append_idx].next;
916         sc_disk[append_idx].next = new_idx;
917         if (sc_disk[new_idx].next == SCOUT_NIL)
918             a_srvline->used_tail = new_idx;
919         else
920             sc_disk[sc_disk[new_idx].next].prev = new_idx;
921     };
922
923     /*
924      * Add the new used disk light object to the display list for
925      * the scout frame.
926      */
927     if (scout_debug) {
928         fprintf(scout_debugfd,
929                 "[%s] Adding disk light at index %d to display list\n", rn,
930                 new_idx);
931         fflush(scout_debugfd);
932     }
933     code = gtxframe_AddToList(scout_frame, sc_disk[new_idx].disk_lp);
934     if (code) {
935         if (scout_debug) {
936             fprintf(scout_debugfd,
937                     "[%s] Can't add to display list, code is %d\n", rn, code);
938             fflush(scout_debugfd);
939         }
940     }
941     return (new_idx);
942
943 }                               /*scout_FindUsedDisk */
944
945 /*------------------------------------------------------------------------
946  * scout_RemoveInactiveDisk
947  *
948  * Description:
949  *      Given a server record and a used disk index, remove the disk
950  *      record from the used list, put it on the free list, and remove
951  *      it from the gtx frame update list.
952  *
953  * Arguments:
954  *      struct mini_line *a_srvline     : Ptr to server descriptor.
955  *      int a_used_idx                  : Index of used disk record.
956  *
957  * Returns:
958  *      Nothing.
959  *
960  * Environment:
961  *      Formerly-used disk records are returned to the free pool.
962  *
963  * Side Effects:
964  *      Free and used disk record lists are modified for this server.
965  *      The disk record in question is pulled off the gtx update list
966  *      for the frame.
967  *------------------------------------------------------------------------*/
968
969 static void
970 scout_RemoveInactiveDisk(struct mini_line *a_srvline, int a_used_idx)
971 {                               /*scout_RemoveInactiveDisk */
972
973     static char rn[] = "scout_RemoveInactiveDisk";      /*Routine name */
974
975     if (scout_debug) {
976         fprintf(scout_debugfd, "[%s] Called\n", rn);
977         fflush(scout_debugfd);
978     }
979
980     /*code = gtxframe_RemoveFromList(scout_frame->window, lightobj); */
981
982 }                               /*scout_RemoveInactiveDisk */
983
984 /*------------------------------------------------------------------------
985  * mini_PrintDiskStats
986  *
987  * Description:
988  *      Given server indexing and light object information, a pointer
989  *      to a set of statistics, and whether the probe that produced these
990  *      stats succeeded or not, print out the stats in a nice way.
991  *
992  * Arguments:
993  *      struct mini_line *a_srvline     : Ptr to server descriptor.
994  *      struct ProbeViceStatistics *a_stats     : Ptr to current stats.
995  *      int a_probeOK                   : Was the probe OK?
996  *      int a_width_changed             : Has the frame width changed?
997  *      int a_fix_line_num              : Is the line number wrong?
998  *      int a_delta_line_num            : Change in line number.
999  *
1000  * Returns:
1001  *      0 if something went wrong,
1002  *      else the number of lines taken up by this server record.
1003  *
1004  * Environment:
1005  *      Nothing interesting.
1006  *
1007  * Side Effects:
1008  *      As advertised.
1009  *------------------------------------------------------------------------*/
1010
1011 static int
1012 mini_PrintDiskStats(struct mini_line *a_srvline,
1013                     struct ProbeViceStatistics *a_stats,
1014                     int a_probeOK, int a_width_changed,
1015                     int a_fix_line_num, int a_delta_line_num)
1016 {                               /*mini_PrintDiskStats */
1017
1018     static char rn[] = "mini_PrintDiskStats";   /*Routine name */
1019     char s[128];                /*String buffer */
1020     struct onode *curr_disklight;       /*Ptr to current disk light */
1021     struct onode *srvname_light;        /*Ptr to server name light */
1022     ViceDisk *curr_diskstat;    /*Ptr to current disk stat */
1023     int curr_disk;              /*Current disk stat number */
1024     int used_disk_idx;          /*Used disk index */
1025     int next_used_idx;          /*Ditto */
1026     int pastthreshold;          /*Was disk past threshold? */
1027     struct gator_lightobj *diskdata;    /*Private light data */
1028     struct gwin_strparams *disk_strparams;      /*String params for disk light */
1029     char *diskname = 0;         /*Name of disk */
1030     int found_idx;              /*Idx of matching disk */
1031     char *srv_name;             /*Server name */
1032     struct scout_disk *sc_disk; /*Ptr to scout disk desc */
1033     int fix_light_locs;         /*Recompute disk light locs? */
1034
1035     if (scout_debug) {
1036         fprintf(scout_debugfd, "[%s] Called\n", rn);
1037         fflush(scout_debugfd);
1038     }
1039
1040     /*
1041      * Remember the current server's print name, don't recompute light
1042      * locations yet.
1043      */
1044     srvname_light = a_srvline->srvName_lp;
1045     srv_name = ((struct gator_lightobj *)(srvname_light->o_data))->label;
1046     fix_light_locs = 0;
1047     if (scout_debug) {
1048         fprintf(scout_debugfd, "[%s] Value of a_delta_line_num is %d\n", rn,
1049                 a_delta_line_num);
1050         fflush(scout_debugfd);
1051     }
1052
1053     /*
1054      * If the probe failed, we simply blank out all the used disk
1055      * objects.  Note: a NON-ZERO value of a_probeOK implies failure.
1056      */
1057     if (a_probeOK) {
1058         used_disk_idx = a_srvline->used_head;
1059         while (used_disk_idx != SCOUT_NIL) {
1060             /*
1061              * Point to the current used disk's light, blank out its
1062              * contents, and make sure highlighting is turned off.  We
1063              * also take this opportunity to fix the line numbers if
1064              * needed.
1065              */
1066             curr_disklight = a_srvline->disks[used_disk_idx].disk_lp;
1067             diskdata = (struct gator_lightobj *)(curr_disklight->o_data);
1068             if (scout_debug) {
1069                 fprintf(scout_debugfd,
1070                         "[%s] Prev value of disk light %d: '%s'\n", rn,
1071                         used_disk_idx, diskdata->label);
1072                 fflush(scout_debugfd);
1073             }
1074             mini_justify(" ",   /*Src buffer */
1075                          diskdata->label,       /*Dest buffer */
1076                          scout_col_width[COL_DISK],     /*Dest's width */
1077                          SCOUT_RIGHT_JUSTIFY,   /*Right-justify */
1078                          SCOUT_LEFT_TRUNC,      /*Left-truncate */
1079                          SCOUT_ISNT_LDISK);     /*Not a labeled disk */
1080             gator_light_set(curr_disklight, 0);
1081             if (a_fix_line_num) {
1082                 curr_disklight->o_y += a_delta_line_num;
1083                 disk_strparams = (struct gwin_strparams *)(diskdata->llrock);
1084                 disk_strparams->y += a_delta_line_num;
1085             }
1086
1087             /*
1088              * Advance to next used disk, if any.
1089              */
1090             used_disk_idx = a_srvline->disks[used_disk_idx].next;
1091
1092         }                       /*Blank out disk name field */
1093
1094         /*
1095          * If the frame width has changed, we have to recompute all disk
1096          * light locations.  After that, the number of lines in the server
1097          * record will be accurate, and we return them.
1098          */
1099         if (a_width_changed)
1100             scout_RecomputeLightLocs(a_srvline);
1101
1102         return (a_srvline->num_lines);
1103
1104     }
1105
1106     /*Probe failed for the server */
1107     /*
1108      * Probe was successful.  Sweep through the statistics records,
1109      * and put up all values having to do with AFS partitions.  First,
1110      * mark all used disk objects for this server as inactive and fix
1111      * their line numbers if needed.
1112      */
1113     used_disk_idx = a_srvline->used_head;
1114     while (used_disk_idx != SCOUT_NIL) {
1115         if (scout_debug) {
1116             fprintf(scout_debugfd, "[%s] Marking used disk %d inactive\n", rn,
1117                     used_disk_idx);
1118             fflush(scout_debugfd);
1119         }
1120         sc_disk = (a_srvline->disks) + used_disk_idx;
1121         sc_disk->active = 0;
1122         used_disk_idx = sc_disk->next;
1123         if (a_fix_line_num) {
1124             sc_disk->disk_lp->o_y += a_delta_line_num;
1125             diskdata = (struct gator_lightobj *)(sc_disk->disk_lp->o_data);
1126             disk_strparams = (struct gwin_strparams *)(diskdata->llrock);
1127             disk_strparams->y += a_delta_line_num;
1128         }
1129     }                           /*Mark used disks inactive */
1130
1131     curr_diskstat = (ViceDisk *) a_stats->Disk;
1132     for (curr_disk = 0; curr_disk < VOLMAXPARTS; curr_disk++) {
1133         /*
1134          * An AFS partition name must be prefixed by `/vicep`.
1135          */
1136         if (scout_debug) {
1137             fprintf(scout_debugfd, "[%s] Disk stats at %p for disk '%s'\n",
1138                     rn, curr_diskstat, curr_diskstat->Name);
1139             fflush(scout_debugfd);
1140         }
1141         if (strncmp("/vice", curr_diskstat->Name, 5) == 0) {
1142             /*
1143              * Pull out the single-letter name (actually, abbreviation)
1144              * of the disk and look for such an entry in the used disks.
1145              */
1146             diskname = &curr_diskstat->Name[6];
1147             found_idx = scout_FindUsedDisk(diskname,    /*1-char name */
1148                                            a_srvline,   /*Server record */
1149                                            &fix_light_locs);    /*Recompute? */
1150             if (found_idx == SCOUT_NIL) {
1151                 fprintf(stderr,
1152                         "[%s] Can't display /vicep%s on server '%s'\n", rn,
1153                         diskname, srv_name);
1154             } else {
1155                 /*
1156                  * Found (or created) record for this disk.  Fill in the single-
1157                  * letter name of the disk followed by the number of free blocks.
1158                  * Turn the disk light on if the number of free blocks exceeds
1159                  * the threshold the user set, either % used or min free blocks.
1160                  */
1161                 sprintf(s, "%s:%d", diskname, curr_diskstat->BlocksAvailable);
1162                 if (scout_attn_disk_mode == SCOUT_DISKM_PCUSED) {
1163                     if ((float)
1164                         (curr_diskstat->TotalBlocks -
1165                          curr_diskstat->BlocksAvailable) /
1166                         (float)(curr_diskstat->TotalBlocks) >
1167                         scout_attn_disk_pcused)
1168                         pastthreshold = 1;
1169                     else
1170                         pastthreshold = 0;
1171                 } else
1172                     pastthreshold =
1173                         (curr_diskstat->BlocksAvailable <
1174                          scout_attn_disk_minfree) ? 1 : 0;
1175                 sc_disk = (a_srvline->disks) + found_idx;
1176                 sc_disk->active = 1;
1177                 diskdata =
1178                     (struct gator_lightobj *)(sc_disk->disk_lp->o_data);
1179                 if (scout_debug) {
1180                     fprintf(scout_debugfd,
1181                             "[%s] Justifying %s for disk idx %d (prev value: '%s')\n",
1182                             rn, s, found_idx, diskdata->label);
1183                     fflush(scout_debugfd);
1184                 }
1185                 mini_justify(s, /*Src buffer */
1186                              diskdata->label,   /*Dest buffer */
1187                              scout_col_width[COL_DISK], /*Dest's width */
1188                              SCOUT_LEFT_JUSTIFY,        /*Left-justify */
1189                              SCOUT_LEFT_TRUNC,  /*Left-truncate */
1190                              SCOUT_IS_LDISK);   /*Labeled disk */
1191
1192                 gator_light_set(sc_disk->disk_lp, pastthreshold);
1193
1194             }                   /*Found disk record */
1195         }
1196
1197         /*Found AFS disk name */
1198         /*
1199          * Advance to the next disk statistics record.
1200          */
1201         curr_diskstat++;
1202     }                           /*For each statistics record */
1203
1204     /*
1205      * We've now pulled out all the disk statistics from the probe.
1206      * See if any used disks that were there from the last time are
1207      * now gone.  If so, we remove them.
1208      */
1209     if (scout_debug) {
1210         fprintf(scout_debugfd,
1211                 "[%s] Scanning used disk records for inactive entries\n", rn);
1212         fflush(scout_debugfd);
1213     }
1214     used_disk_idx = a_srvline->used_head;
1215     while (used_disk_idx != SCOUT_NIL) {
1216         if (scout_debug) {
1217             fprintf(scout_debugfd, "[%s] Examining entry at index %d\n", rn,
1218                     used_disk_idx);
1219             fflush(scout_debugfd);
1220         }
1221         sc_disk = (a_srvline->disks) + used_disk_idx;
1222         next_used_idx = sc_disk->next;
1223         if (!(sc_disk->active)) {
1224             scout_RemoveInactiveDisk(a_srvline, /*Server record */
1225                                      used_disk_idx);    /*Disk index to nuke */
1226             fix_light_locs = 1;
1227         }
1228         used_disk_idx = next_used_idx;
1229
1230     }                           /*Remove inactive used disks */
1231
1232     /*
1233      * If we've had to add or remove disks to/from the used list,
1234      * or if the frame width has changed, we recompute the light
1235      * locations before returning.
1236      */
1237     if (fix_light_locs || a_width_changed)
1238         scout_RecomputeLightLocs(a_srvline);
1239
1240     /*
1241      * Return the (possibly new) size of the current server record.
1242      */
1243     return (a_srvline->num_lines);
1244
1245 }                               /*mini_PrintDiskStats */
1246
1247 /*------------------------------------------------------------------------
1248  * FS_Handler
1249  *
1250  * Description:
1251  *      Handler routine passed to the fsprobe module.  This handler is
1252  *      called immediately after a poll of all the FileServers has taken
1253  *      place.  Its job is to write out selected data to the scout
1254  *      screen.
1255  *
1256  * Arguments:
1257  *      None.
1258  *
1259  * Returns:
1260  *      0 on success,
1261  *      -1 otherwise.
1262  *
1263  * Environment:
1264  *      All it needs to know is exported by the fsprobe module, namely
1265  *      the data structure where the probe results are stored.
1266  *
1267  * Side Effects:
1268  *      Recomputes disk light locations in response to reshaping the
1269  *      scout window or from adding/deleting disk lights to/from
1270  *      individual servers.
1271  *------------------------------------------------------------------------*/
1272
1273 int
1274 FS_Handler(void)
1275 {                               /*FS_Handler */
1276
1277     static char rn[] = "FS_Handler";    /*Routine name */
1278     int code;                   /*Return code */
1279     struct ProbeViceStatistics *curr_stats;     /*Ptr to current stats */
1280     int *curr_probeOK;          /*Ptr to current probeOK field */
1281     int curr_srvidx;            /*Current server index */
1282     char s[128];                /*String buffer */
1283     static char sblank[] = " "; /*Blank string buffer */
1284     char *sp;                   /*Ptr to string buffer */
1285     struct mini_line *curr_line;        /*Current mini-line */
1286     int curr_line_num;          /*Current line number */
1287     struct gator_lightobj *lightdata;   /*Private light data */
1288     int setting;                /*Light setting (on or off) */
1289     int old_width;              /*Keep the old width value */
1290     int width_changed;          /*Has the frame width changed? */
1291     int fix_line_num;           /*Line number needs fixing */
1292     int delta_line_num;         /*Change in line number */
1293
1294     /*
1295      * See if the size of the scout frame has changed since the last
1296      * time.
1297      */
1298     old_width = scout_frameDims.maxx;
1299     if (scout_debug) {
1300         fprintf(scout_debugfd, "[%s] Calling wop_getdimensions\n", rn);
1301         fflush(scout_debugfd);
1302     }
1303     WOP_GETDIMENSIONS(scout_frame->window, &scout_frameDims);
1304     width_changed = (old_width == scout_frameDims.maxx) ? 0 : 1;
1305     if (scout_debug) {
1306         fprintf(scout_debugfd,
1307                 "[%s] Frame dimensions are %d rows, %d columns\n", rn,
1308                 scout_frameDims.maxy, scout_frameDims.maxx);
1309         if (width_changed)
1310             fprintf(scout_debugfd, "[%s] Width has changed from %d columns\n",
1311                     rn, old_width);
1312         fflush(scout_debugfd);
1313     }
1314
1315     /*
1316      * Print out the selected fields for each server.  We actually change
1317      * the light's label to the new data.
1318      */
1319     curr_line = scout_screen.line;
1320     curr_stats = fsprobe_Results.stats;
1321     curr_probeOK = fsprobe_Results.probeOK;
1322     curr_line_num = curr_line->base_line;
1323
1324     for (curr_srvidx = 0; curr_srvidx < scout_screen.numServers;
1325          curr_srvidx++) {
1326         /*
1327          * If the current server record is set up on the wrong line, fix
1328          * the non-disk light objects directly, and remember to fix the
1329          * disk light objects later on.
1330          */
1331         if (curr_line->base_line != curr_line_num) {
1332             fix_line_num = 1;
1333             delta_line_num = curr_line_num - curr_line->base_line;
1334             curr_line->base_line = curr_line_num;
1335             scout_SetNonDiskLightLine(curr_line, curr_line_num);
1336         } else {
1337             fix_line_num = 0;
1338             delta_line_num = 0;
1339         }
1340
1341         lightdata =
1342             (struct gator_lightobj *)(curr_line->currConns_lp->o_data);
1343         if (*curr_probeOK == 0) {
1344             sp = s;
1345             sprintf(sp, "%d", curr_stats->CurrentConnections);
1346         } else
1347             sp = sblank;
1348         mini_justify(sp,        /*Src buffer */
1349                      lightdata->label,  /*Dest buffer */
1350                      scout_col_width[COL_CONN], /*Dest's width */
1351                      SCOUT_RIGHT_JUSTIFY,       /*Right-justify */
1352                      SCOUT_LEFT_TRUNC,  /*Left-truncate */
1353                      SCOUT_ISNT_LDISK); /*Not a labeled disk */
1354         if (scout_attn_conn != SCOUT_ATTN_NOTUSED
1355             && curr_stats->CurrentConnections >= scout_attn_conn)
1356             setting = 1;
1357         else
1358             setting = 0;
1359         gator_light_set(curr_line->currConns_lp, setting);
1360
1361         lightdata = (struct gator_lightobj *)(curr_line->fetches_lp->o_data);
1362         if (*curr_probeOK == 0) {
1363             sp = s;
1364             sprintf(sp, "%u", curr_stats->TotalFetchs);
1365         } else
1366             sp = sblank;
1367         mini_justify(sp,        /*Src buffer */
1368                      lightdata->label,  /*Dest buffer */
1369                      scout_col_width[COL_FETCH],        /*Dest's width */
1370                      SCOUT_RIGHT_JUSTIFY,       /*Right-justify */
1371                      SCOUT_LEFT_TRUNC,  /*Left-truncate */
1372                      SCOUT_ISNT_LDISK); /*Not a labeled disk */
1373         if (scout_attn_fetch != SCOUT_ATTN_NOTUSED
1374             && curr_stats->TotalFetchs >= scout_attn_fetch)
1375             setting = 1;
1376         else
1377             setting = 0;
1378         gator_light_set(curr_line->fetches_lp, setting);
1379
1380         lightdata = (struct gator_lightobj *)(curr_line->stores_lp->o_data);
1381         if (*curr_probeOK == 0) {
1382             sp = s;
1383             sprintf(sp, "%u", curr_stats->TotalStores);
1384         } else
1385             sp = sblank;
1386         mini_justify(sp,        /*Src buffer */
1387                      lightdata->label,  /*Dest buffer */
1388                      scout_col_width[COL_STORE],        /*Dest's width */
1389                      SCOUT_RIGHT_JUSTIFY,       /*Right-justify */
1390                      SCOUT_LEFT_TRUNC,  /*Left-truncate */
1391                      SCOUT_ISNT_LDISK); /*Not a labeled disk */
1392         if (scout_attn_store != SCOUT_ATTN_NOTUSED
1393             && curr_stats->TotalStores >= scout_attn_store)
1394             setting = 1;
1395         else
1396             setting = 0;
1397         gator_light_set(curr_line->stores_lp, setting);
1398
1399         lightdata =
1400             (struct gator_lightobj *)(curr_line->workstations_lp->o_data);
1401         if (*curr_probeOK == 0) {
1402             sp = s;
1403             sprintf(sp, "%d", curr_stats->WorkStations);
1404         } else
1405             sp = sblank;
1406         mini_justify(sp,        /*Src buffer */
1407                      lightdata->label,  /*Dest buffer */
1408                      scout_col_width[COL_WK],   /*Dest's width */
1409                      SCOUT_RIGHT_JUSTIFY,       /*Right-justify */
1410                      SCOUT_LEFT_TRUNC,  /*Left-truncate */
1411                      SCOUT_ISNT_LDISK); /*Not a labeled disk */
1412         if (scout_attn_workstations != SCOUT_ATTN_NOTUSED
1413             && curr_stats->WorkStations >= scout_attn_workstations)
1414             setting = 1;
1415         else
1416             setting = 0;
1417         gator_light_set(curr_line->workstations_lp, setting);
1418
1419         /*
1420          * We turn the server light on if there was an error in the
1421          * current probe (e.g., if the curr_probeOK field is non-zero.
1422          * (Don't forget to fix the light's line if it needs it).
1423          */
1424         setting = (*curr_probeOK) ? 1 : 0;
1425         gator_light_set(curr_line->srvName_lp, setting);
1426
1427         /*
1428          * Print out the disk statistics.  The value returned is the
1429          * number of lines taken up by the server record (or 0 if
1430          * something went wrong).
1431          */
1432         code = mini_PrintDiskStats(curr_line,   /*Ptr to server line */
1433                                    curr_stats,  /*Fresh disk stats */
1434                                    *curr_probeOK,       /*Was probe OK? */
1435                                    width_changed,       /*Has the width changed? */
1436                                    fix_line_num,        /*Fix the line number? */
1437                                    delta_line_num);     /*Change in line number */
1438         if (code == 0) {
1439             fprintf(stderr, "[%s] Error in printing out disk statistics\n",
1440                     rn);
1441             return (-1);
1442         } else
1443             curr_line_num += code;
1444
1445         /*
1446          * Advance the current mini_line, stats source, and probe success
1447          * indication.
1448          */
1449         curr_line++;
1450         curr_stats++;
1451         curr_probeOK++;
1452
1453     }                           /*for each server probed */
1454
1455     /*
1456      * Display the scout frame.
1457      */
1458     sprintf(s, "Probe %d results", fsprobe_Results.probeNum);
1459     gtxframe_DisplayString(scout_frame, s);
1460     WOP_DISPLAY(scout_gwin);
1461
1462     /*
1463      * Return the happy news.
1464      */
1465     return (0);
1466
1467 }                               /*FS_Handler */
1468
1469 /*------------------------------------------------------------------------
1470  * init_mini_line
1471  *
1472  * Description:
1473  *      Initialize each line in the mini_screen.
1474  *
1475  * Arguments:
1476  *      struct sockaddr_in *a_skt : Ptr to server socket info.
1477  *      int a_lineNum;            : Line number being created.
1478  *      struct mini_line *a_line  : Ptr to mini_line to set up.
1479  *      char *a_srvname           : Printable server name.
1480  *
1481  * Returns:
1482  *      0 on success,
1483  *      Error value otherwise.
1484  *
1485  * Environment:
1486  *      Nothing interesting.
1487  *
1488  * Side Effects:
1489  *      As advertised.
1490  *------------------------------------------------------------------------*/
1491
1492 static int
1493 init_mini_line(struct sockaddr_in *a_skt, int a_lineNum,
1494                struct mini_line *a_line, char *a_srvname)
1495 {                               /*init_mini_line */
1496
1497     static char rn[] = "init_mini_line";        /*Routine name */
1498     int curr_x;                 /*Current X position */
1499     int curr_y;                 /*Current Y position */
1500     char s[128];                /*Scratch buffer */
1501     int code;                   /*Return code */
1502     struct gator_lightobj *lightdata;   /*Private light data */
1503
1504     if (scout_debug) {
1505         fprintf(scout_debugfd, "[%s] Called for base line %d\n", rn,
1506                 a_lineNum);
1507         fflush(scout_debugfd);
1508     }
1509
1510     /*
1511      * Fill in the top fields (except the disk fields, which will be
1512      * done elsewhere), then create the light onodes.
1513      */
1514     memcpy((char *)&(a_line->skt), (char *)a_skt, sizeof(struct sockaddr_in));
1515     a_line->numDisks = 0;
1516     a_line->base_line = a_lineNum + scout_screen.base_line_num;
1517     a_line->num_lines = 1;
1518
1519     curr_x = 1;
1520     curr_y = a_line->base_line;
1521     if ((a_line->currConns_lp =
1522          mini_initLightObject("Conns", curr_x, curr_y, scout_col_width[COL_CONN],
1523                               scout_gwin))
1524         == NULL) {
1525         fprintf(stderr, "[%s:%s] Can't create currConns light object\n", pn,
1526                 rn);
1527         return (-1);
1528     }
1529     curr_x += scout_col_width[COL_CONN] + 1;
1530
1531     if ((a_line->fetches_lp =
1532          mini_initLightObject("Fetches", curr_x, curr_y, scout_col_width[COL_FETCH],
1533                               scout_frame->window))
1534         == NULL) {
1535         fprintf(stderr, "[%s:%s] Can't create fetches light object\n", pn,
1536                 rn);
1537         return (-1);
1538     }
1539     curr_x += scout_col_width[COL_FETCH] + 1;
1540
1541     if ((a_line->stores_lp =
1542          mini_initLightObject("Stores", curr_x, curr_y, scout_col_width[COL_STORE],
1543                               scout_frame->window))
1544         == NULL) {
1545         fprintf(stderr, "[%s:%s] Can't create stores light object\n", pn, rn);
1546         return (-1);
1547     }
1548     curr_x += scout_col_width[COL_STORE] + 1;
1549
1550     if ((a_line->workstations_lp =
1551          mini_initLightObject("WrkStn", curr_x, curr_y, scout_col_width[COL_WK],
1552                               scout_frame->window))
1553         == NULL) {
1554         fprintf(stderr, "[%s:%s] Can't create workstations light object\n",
1555                 pn, rn);
1556         return (-1);
1557     }
1558     curr_x += scout_col_width[COL_WK] + 1;
1559
1560     if ((a_line->srvName_lp =
1561          mini_initLightObject(a_srvname, curr_x, curr_y,
1562                               scout_col_width[COL_SRVNAME], scout_frame->window))
1563         == NULL) {
1564         fprintf(stderr, "[%s:%s] Can't create server name light object\n", pn,
1565                 rn);
1566         return (-1);
1567     }
1568     sprintf(s, "%s", a_srvname);
1569     lightdata = (struct gator_lightobj *)(a_line->srvName_lp->o_data);
1570     code = mini_justify(s,      /*Src buffer */
1571                         lightdata->label,       /*Dest buffer */
1572                         scout_col_width[COL_SRVNAME],   /*Dest's width */
1573                         SCOUT_CENTER,   /*Centered */
1574                         SCOUT_RIGHT_TRUNC,      /*Right-truncate */
1575                         SCOUT_ISNT_LDISK);      /*Not a labeled disk */
1576     if (code) {
1577         fprintf(stderr,
1578                 "[%s] Can't center server name inside of light object\n", rn);
1579         return (code);
1580     }
1581
1582     if (scout_initDiskLightObjects(a_line, scout_frame->window)) {
1583         fprintf(stderr, "[%s:%s] Can't create disk light objects\n", pn, rn);
1584         return (-1);
1585     }
1586
1587     /*
1588      * Finally, return the happy news.
1589      */
1590     return (0);
1591
1592 }                               /*init_mini_line */
1593
1594 /*------------------------------------------------------------------------
1595  * execute_scout
1596  *
1597  * Description:
1598  *      Workhorse routine that starts up the FileServer probe.
1599  *
1600  * Arguments:
1601  *      int a_numservers                : Length of above list.
1602  *      struct cmd_item *a_srvname      : List of FileServer machines to
1603  *                                        monitor.
1604  *      int a_pkg                       : Window package to use.
1605  *
1606  * Returns:
1607  *      0 on success,
1608  *      Error value otherwise.
1609  *
1610  * Environment:
1611  *      Nothing interesting.
1612  *
1613  * Side Effects:
1614  *      As advertised.
1615  *------------------------------------------------------------------------*/
1616
1617 static int
1618 execute_scout(int a_numservers, struct cmd_item *a_srvname, int a_pkg)
1619 {                               /*execute_scout */
1620
1621     static char rn[] = "execute_scout"; /*Routine name */
1622     static char fullsrvname[128];       /*Full server name */
1623     int code;           /*Return code */
1624     struct sockaddr_in *FSSktArray;     /*Server socket array */
1625     int sktbytes;               /*Num bytes in above */
1626     struct sockaddr_in *curr_skt;       /*Ptr to current socket */
1627     struct cmd_item *curr_item; /*Ptr to current cmd item */
1628     struct hostent *he;         /*Host entry */
1629     struct mini_line *mini_lines;       /*Ptr to all mini-lines */
1630     struct mini_line *curr_line;        /*Ptr to current line */
1631     int i;                      /*Generic loop variable */
1632     int mini_line_bytes;        /*Num bytes in mini_lines */
1633     int linenum;                /*Current mini-line number */
1634     struct gator_lightobj *lightdata;   /*Private light data */
1635
1636     if (scout_debug) {
1637         fprintf(scout_debugfd, "[%s] Called\n", rn);
1638         fflush(scout_debugfd);
1639     }
1640
1641     /*
1642      * We have to initialize thread support before we start up any of
1643      * our packages.
1644      */
1645     opr_softsig_Init();
1646
1647     /*
1648      * Initialize the gtx package.
1649      */
1650     scout_gwin = gtx_Init(0,    /*Don't start up InputServer yet */
1651                           -1);  /*Type of window package */
1652     if (scout_gwin == NULL) {
1653         fprintf(stderr, "[%s:%s] Call to gtx_Init() failed!\n", pn, rn);
1654         return (-1);
1655     }
1656
1657     /*
1658      * Remember we've set up gtx so we can exit cleanly from now on.
1659      */
1660     scout_gtx_initialized = 1;
1661
1662     /*
1663      * Create the frame everything will go into, set it up as our only
1664      * frame for this window.
1665      */
1666     scout_frame = gtxframe_Create();
1667     if (scout_frame == (struct gtx_frame *)0) {
1668         fprintf(stderr, "[%s:%s] Call to gtxframe_Create() failed!\n", pn,
1669                 rn);
1670         return (-1);
1671     }
1672     gtxframe_SetFrame(scout_gwin, scout_frame);
1673     WOP_GETDIMENSIONS(scout_frame->window, &scout_frameDims);
1674
1675     /*
1676      * Allocate an array of sockets to describe each FileServer we'll be
1677      * watching.
1678      */
1679     sktbytes = a_numservers * sizeof(struct sockaddr_in);
1680     FSSktArray = calloc(1, sktbytes);
1681     if (FSSktArray == (struct sockaddr_in *)0) {
1682         fprintf(stderr,
1683                 "[%s] Can't malloc() %d sockaddrs (%d bytes) for the given servers\n",
1684                 rn, a_numservers, sktbytes);
1685         scout_CleanExit(-1);
1686     }
1687
1688     /*
1689      * Sweep through the server names provided, filling in the socket
1690      * info for each.  Take into account the fact that we may have a
1691      * base name associated for each.
1692      */
1693     curr_item = a_srvname;
1694     curr_skt = FSSktArray;
1695     while (curr_item) {
1696         if (*scout_basename == '\0')
1697             sprintf(fullsrvname, "%s", curr_item->data);
1698         else
1699             sprintf(fullsrvname, "%s.%s", curr_item->data, scout_basename);
1700         he = hostutil_GetHostByName(fullsrvname);
1701         if (he == NULL) {
1702             fprintf(stderr, "[%s] Can't get host info for '%s'\n", rn,
1703                     fullsrvname);
1704             return (-1);
1705         }
1706         memcpy(&(curr_skt->sin_addr.s_addr), he->h_addr, 4);
1707         curr_skt->sin_family = AF_INET;
1708         curr_skt->sin_port = htons(7000);       /* FileServer port */
1709
1710         /*
1711          * Bump our pointers.
1712          */
1713         curr_item = curr_item->next;
1714         curr_skt++;
1715
1716     }                           /*Build socket entry for each server */
1717
1718     /*
1719      * Create the set of mini-lines, one per server.
1720      */
1721     mini_line_bytes = a_numservers * sizeof(struct mini_line);
1722     mini_lines = calloc(1, mini_line_bytes);
1723     if (mini_lines == (struct mini_line *)0) {
1724         fprintf(stderr, "[%s] Can't malloc() %d bytes for %d screen lines\n",
1725                 rn, mini_line_bytes, a_numservers);
1726         return (-1);
1727     }
1728
1729     /*
1730      * Set up each line in the mini_lines, creating and initializing
1731      * its light objects.
1732      */
1733     scout_screen.base_line_num = 4;
1734     curr_line = mini_lines;
1735     curr_skt = FSSktArray;
1736     linenum = 0;
1737     curr_item = a_srvname;
1738     gtxframe_ClearList(scout_frame);
1739
1740     /*
1741      * Create the light objects that server as banner lines.  Remember
1742      * to take into account the server basename, if any, and the name
1743      * of the host that scout if running on, if that's wanted.
1744      */
1745     if (scout_showhostname) {
1746         if (*scout_basename == '\0')
1747             sprintf(scout_Banner, "[%s] %s", scout_hostname, "Scout");
1748         else
1749             sprintf(scout_Banner, "[%s] Scout for %s", scout_hostname,
1750                     scout_basename);
1751     } else {
1752         if (*scout_basename == '\0')
1753             sprintf(scout_Banner, "%s", " Scout");
1754         else
1755             sprintf(scout_Banner, " Scout for %s", scout_basename);
1756     }
1757     scout_banner0_lp = mini_initLightObject("Banner 0", /*Light name */
1758                                             0, /*X*/ 0, /*Y*/ scout_frameDims.maxx,     /*Width */
1759                                             scout_gwin);        /*Window */
1760     if (scout_banner0_lp != NULL) {
1761         lightdata = (struct gator_lightobj *)(scout_banner0_lp->o_data);
1762         mini_justify(scout_Banner, lightdata->label, scout_frameDims.maxx,
1763                      SCOUT_CENTER, SCOUT_RIGHT_TRUNC, SCOUT_ISNT_LDISK);
1764         gator_light_set(scout_banner0_lp, 1);
1765         gtxframe_AddToList(scout_frame, scout_banner0_lp);
1766
1767         /*Debugging */
1768         if (scout_debug)
1769             fprintf(scout_debugfd, "[%s] Scout label is '%s', %" AFS_SIZET_FMT " chars\n", rn,
1770                     lightdata->label, strlen(lightdata->label));
1771     }
1772
1773     scout_banner1_lp = mini_initLightObject("Banner 1", /*Light name */
1774                                             0, /*X*/ 2, /*Y*/ scout_frameDims.maxx,     /*Width */
1775                                             scout_gwin);        /*Window */
1776     if (scout_banner1_lp != NULL) {
1777         char attn_label[256];
1778         if (scout_attn_disk_mode == SCOUT_DISKM_PCUSED) {
1779             snprintf(attn_label, sizeof(attn_label), "%s: > %s%% used",
1780                      scout_label[5], scout_attn_disk_pcusedstr);
1781         } else {
1782             snprintf(attn_label, sizeof(attn_label), "%s: < %d blocks free",
1783                      scout_label[5], scout_attn_disk_minfree);
1784         }
1785         code = snprintf(scout_Banner, sizeof(scout_Banner),
1786                         "%*s %*s %*s %*s %*s %s",
1787                         scout_col_width[0], scout_label[0],
1788                         scout_col_width[1], scout_label[1],
1789                         scout_col_width[2], scout_label[2],
1790                         scout_col_width[3], scout_label[3],
1791                         scout_col_width[4], scout_label[4],
1792                         attn_label);
1793         if (code < 0 || code >= sizeof(scout_Banner)) {
1794             fprintf(stderr, "[%s] Truncation while generating banner\n", rn);
1795             return -1;
1796         }
1797
1798         lightdata = (struct gator_lightobj *)(scout_banner1_lp->o_data);
1799         mini_justify(scout_Banner, lightdata->label, scout_frameDims.maxx,
1800                      SCOUT_LEFT_JUSTIFY, SCOUT_RIGHT_TRUNC,
1801                      SCOUT_ISNT_LDISK);
1802
1803         gtxframe_AddToList(scout_frame, scout_banner1_lp);
1804     }
1805
1806     scout_banner2_lp = mini_initLightObject("Banner 2", /*Light name */
1807                                             0, /*X*/ 3, /*Y*/ scout_frameDims.maxx,     /*Width */
1808                                             scout_gwin);        /*Window */
1809     if (scout_banner2_lp != NULL) {
1810         snprintf(scout_Banner, sizeof(scout_Banner),
1811                  "%*s %*s %*s %*s %*s %s",
1812                  scout_col_width[0], scout_underline[0],
1813                  scout_col_width[1], scout_underline[1],
1814                  scout_col_width[2], scout_underline[2],
1815                  scout_col_width[3], scout_underline[3],
1816                  scout_col_width[4], scout_underline[4],
1817                  scout_underline[5]);
1818
1819         lightdata = (struct gator_lightobj *)(scout_banner2_lp->o_data);
1820         mini_justify(scout_Banner, lightdata->label,
1821                      scout_frameDims.maxx, SCOUT_LEFT_JUSTIFY,
1822                      SCOUT_RIGHT_TRUNC, SCOUT_ISNT_LDISK);
1823         gtxframe_AddToList(scout_frame, scout_banner2_lp);
1824     }
1825
1826     for (i = 0; i < a_numservers; i++) {
1827         code = init_mini_line(curr_skt, linenum, curr_line, curr_item->data);
1828         if (code) {
1829             fprintf(stderr, "[%s] Can't initialize line for server %d\n", rn,
1830                     i);
1831             return (-1);
1832         }
1833         curr_skt++;
1834         curr_line++;
1835         linenum++;
1836         curr_item = curr_item->next;
1837     }
1838
1839     /*
1840      * Now that all lines have been set up, we feed in the light items
1841      * created.  Note: the disk lights are entered at a later time,
1842      * as they enter the used disk list for each server.
1843      */
1844     curr_line = mini_lines;
1845     for (i = 0; i < a_numservers; i++) {
1846         code = gtxframe_AddToList(scout_frame, curr_line->currConns_lp);
1847         if (code) {
1848             fprintf(stderr,
1849                     "[%s] Can't add client connections light to display list\n",
1850                     rn);
1851             return (code);
1852         }
1853
1854         code = gtxframe_AddToList(scout_frame, curr_line->fetches_lp);
1855         if (code) {
1856             fprintf(stderr,
1857                     "[%s] Can't add fetches light to frame display list\n",
1858                     rn);
1859             return (code);
1860         }
1861
1862         code = gtxframe_AddToList(scout_frame, curr_line->stores_lp);
1863         if (code) {
1864             fprintf(stderr,
1865                     "[%s] Can't add stores light to frame display list\n",
1866                     rn);
1867             return (code);
1868         }
1869
1870         code = gtxframe_AddToList(scout_frame, curr_line->workstations_lp);
1871         if (code) {
1872             fprintf(stderr,
1873                     "[%s] Can't add workstation light to display list\n", rn);
1874             return (code);
1875         }
1876
1877         code = gtxframe_AddToList(scout_frame, curr_line->srvName_lp);
1878         if (code) {
1879             fprintf(stderr,
1880                     "[%s] Can't add server name light to display list\n", rn);
1881             return (code);
1882         }
1883
1884         /*
1885          * Move down to the next server record.
1886          */
1887         curr_line++;
1888
1889     }                           /*Add lights in server records to display list */
1890
1891     /*
1892      * Finish setting up the overall mini_screen structure.
1893      */
1894     scout_screen.numServers = a_numservers;
1895     scout_screen.line = mini_lines;
1896     WOP_GETDIMENSIONS(scout_frame->window, &scout_frameDims);
1897
1898     /*
1899      * Start up the fsprobe package, which will gather FileServer
1900      * statistics for us on a regular basis.
1901      */
1902     gtxframe_DisplayString(scout_frame,
1903                            "Establishing File Server connection(s)...");
1904     code = fsprobe_Init(a_numservers,   /*Num FileServers to probe */
1905                         FSSktArray,     /*FileServer socket array */
1906                         scout_probefreq,        /*Probe frequency */
1907                         FS_Handler,     /*Handler routine */
1908                         0);     /*Turn debugging output off */
1909     if (code) {
1910         fprintf(stderr, "[%s] Error returned by fsprobe_Init: %d\n", rn,
1911                 code);
1912         return (-1);
1913     }
1914
1915     code = (int)(intptr_t)gtx_InputServer(scout_gwin);
1916     if (code) {
1917         fprintf(stderr,
1918                 "[%s] Error exit from gtx_InputServer(), error is %d\n", rn,
1919                 code);
1920         scout_CleanExit(code);
1921     }
1922
1923     fsprobe_Wait(0); /* sleep forever */
1924     return 0;
1925 }                               /*execute_scout */
1926
1927 /*------------------------------------------------------------------------
1928  * countServers
1929  *
1930  * Description:
1931  *      Given a pointer to the list of servers we'll be polling,
1932  *      compute the length of the list.
1933  *
1934  * Arguments:
1935  *      struct cmd_item *a_firstItem : Ptr to first item in list.
1936  *
1937  * Returns:
1938  *      Length of the above list.
1939  *
1940  * Environment:
1941  *      Nothing interesting.
1942  *
1943  * Side Effects:
1944  *      As advertised.
1945  *------------------------------------------------------------------------*/
1946
1947 static int countServers(struct cmd_item *a_firstItem)
1948 {                               /*countServers */
1949
1950     int list_len;               /*List length */
1951     struct cmd_item *curr_item; /*Ptr to current item */
1952
1953     list_len = 0;
1954     curr_item = a_firstItem;
1955
1956     /*
1957      * Count 'em up.
1958      */
1959     while (curr_item) {
1960         list_len++;
1961         curr_item = curr_item->next;
1962     }
1963
1964     /*
1965      * Return our tally.
1966      */
1967     return (list_len);
1968
1969 }                               /*countServers */
1970
1971 /*------------------------------------------------------------------------
1972  * scout_AdoptThresholds
1973  *
1974  * Description:
1975  *      Parse and adopt one or more threshold values, as read off the
1976  *      command line.
1977  *
1978  * Arguments:
1979  *      struct cmd_item *a_thresh_item : Ptr to item on command-line
1980  *                                       threshold list.
1981  *
1982  * Returns:
1983  *      Nothing (but may exit the entire program on error!)
1984  *
1985  * Environment:
1986  *      Valid keywords are:
1987  *          conn, disk, fetch, store, ws
1988  *      The disk value, if it has a % sign, signifies that attention
1989  *      will be triggered when the disk is more than that percentage
1990  *      full; otherwise, it will specify the minimum number of free
1991  *      blocks.
1992  *
1993  * Side Effects:
1994  *      As specified.
1995  *------------------------------------------------------------------------*/
1996
1997 static void scout_AdoptThresholds(struct cmd_item *a_thresh_item)
1998 {                               /*scout_AdoptThresholds */
1999
2000     static char rn[] = "scout_AdoptThresholds"; /*Routine name */
2001     struct cmd_item *curr_item; /*Current item */
2002     char *curr_name;            /*Current name half of pair */
2003     char *curr_value;           /*Current value half of pair */
2004     int diskval_len;            /*Length of disk attn value */
2005
2006     curr_item = a_thresh_item;
2007     while (curr_item) {
2008         /*
2009          * If there isn't a corresponding value for the current
2010          * attention field, bitch & die.
2011          */
2012         if (curr_item->next == (struct cmd_item *)0) {
2013             printf("[%s] No threshold value given for '%s'\n", rn,
2014                    curr_item->data);
2015             scout_CleanExit(-1);
2016         }
2017
2018         curr_name = curr_item->data;
2019         curr_value = curr_item->next->data;
2020
2021         if (strcmp(curr_name, "conn") == 0) {
2022             if (scout_debug) {
2023                 fprintf(scout_debugfd,
2024                         "[%s] Setting conn attn value to %d (default %d)\n",
2025                         rn, atoi(curr_value), scout_attn_conn);
2026                 fflush(scout_debugfd);
2027             }
2028             scout_attn_conn = atoi(curr_value);
2029         } else if (strcmp(curr_name, "disk") == 0) {
2030             /*
2031              * If there's a '%' as the last character in the value,
2032              * we use percentage mode.
2033              */
2034             diskval_len = strlen(curr_value);
2035             if (curr_value[diskval_len - 1] == '%') {
2036                 curr_value[diskval_len - 1] = '\0';
2037                 if (scout_debug) {
2038                     fprintf(scout_debugfd,
2039                             "[%s] New disk attn value: 0.%s used (default %f)\n",
2040                             rn, curr_value, scout_attn_disk_pcused);
2041                     fflush(scout_debugfd);
2042                 }
2043                 sprintf(scout_attn_disk_pcusedstr, "%s", curr_value);
2044                 scout_attn_disk_pcused =
2045                     ((float)(atoi(curr_value))) / ((float)(100));
2046             } /*Percentage mode */
2047             else {
2048                 if (scout_debug) {
2049                     fprintf(scout_debugfd,
2050                             "[%s] New disk attn value: %s min free (default %f)\n",
2051                             rn, curr_value, scout_attn_disk_pcused);
2052                     fflush(scout_debugfd);
2053                 }
2054                 scout_attn_disk_mode = SCOUT_DISKM_MINFREE;
2055                 scout_attn_disk_minfree = atoi(curr_value);
2056             }                   /*Min free blocks mode */
2057         } else if (strcmp(curr_name, "fetch") == 0) {
2058             if (scout_debug) {
2059                 fprintf(scout_debugfd,
2060                         "[%s] Setting fetch attn value to %d (default %d)\n",
2061                         rn, atoi(curr_value), scout_attn_fetch);
2062                 fflush(scout_debugfd);
2063             }
2064             scout_attn_fetch = atoi(curr_value);
2065         } else if (strcmp(curr_name, "store") == 0) {
2066             if (scout_debug) {
2067                 fprintf(scout_debugfd,
2068                         "[%s] Setting store attn value to %d (default %d)\n",
2069                         rn, atoi(curr_value), scout_attn_store);
2070                 fflush(scout_debugfd);
2071             }
2072             scout_attn_store = atoi(curr_value);
2073         } else if (strcmp(curr_name, "ws") == 0) {
2074             if (scout_debug) {
2075                 fprintf(scout_debugfd,
2076                         "[%s] Setting workstation attn value to %d (default %d)\n",
2077                         rn, atoi(curr_value), scout_attn_workstations);
2078                 fflush(scout_debugfd);
2079             }
2080             scout_attn_workstations = atoi(curr_value);
2081         } else {
2082             printf("[%s] Unknown attention item: '%s'\n", rn,
2083                    curr_item->data);
2084             scout_CleanExit(-1);
2085         }
2086
2087         /*
2088          * Advance past the just-processed pair.
2089          */
2090         curr_item = curr_item->next->next;
2091
2092     }                           /*Interpret each name-value pair */
2093
2094 }                               /*scout_AdoptThresholds */
2095
2096 /**
2097  * Setup the user specified column widths.
2098  *
2099  * The column widths specifies the number of digits which
2100  * will be displayed without truncation.  The column widths
2101  * are given in the same order in which they are displayed,
2102  * from left to right. Not all columns need to be specified
2103  * on the command line. The default column widths for those
2104  * columns not specified. Column widths can not be set smaller
2105  * than the heading underline.
2106  *
2107  * @param[in] a_width_item      command line width item list
2108  */
2109 void
2110 scout_SetColumnWidths(struct cmd_item *a_width_item)
2111 {
2112     int i;
2113     int width;
2114     int num_cols = sizeof(scout_col_width) / sizeof(*scout_col_width);
2115
2116     for (i = 0; a_width_item && i < num_cols; i++) {
2117         width = atoi(a_width_item->data);
2118         if (width > 0) {
2119             int min_width = strlen(scout_underline[i]);
2120             if (min_width) {
2121                width = max(width, min_width);
2122             }
2123             scout_col_width[i] = width + 1;
2124         }
2125         a_width_item = a_width_item->next;
2126     }
2127     if (a_width_item) {
2128         fprintf(stderr, "Too many values given for -columnwidths\n");
2129         scout_CleanExit(1);
2130     }
2131 }
2132
2133 /*------------------------------------------------------------------------
2134  * scoutInit
2135  *
2136  * Description:
2137  *      Routine called when Scout is invoked, responsible for basic
2138  *      initialization, command line parsing, and calling the
2139  *      routine that does all the work.
2140  *
2141  * Arguments:
2142  *      as      : Command syntax descriptor.
2143  *      arock   : Associated rock (not used here).
2144  *
2145  * Returns:
2146  *      Zero (but may exit the entire program on error!)
2147  *
2148  * Environment:
2149  *      Nothing interesting.
2150  *
2151  * Side Effects:
2152  *      Initializes this program.
2153  *------------------------------------------------------------------------*/
2154
2155 static int scoutInit(struct cmd_syndesc *as, void *arock)
2156 {                               /*scoutInit */
2157
2158     static char rn[] = "scoutInit";     /*Routine name */
2159     int code;                   /*Return code */
2160     int wpkg_to_use;            /*Window package to use */
2161     int server_count;           /*Number of servers to watch */
2162     char *debug_filename;       /*Name of debugging output file */
2163
2164     if (scout_debug) {
2165         fprintf(scout_debugfd, "[%s] Called\n", rn);
2166         fflush(scout_debugfd);
2167     }
2168
2169     if (as->parms[P_DEBUG].items != 0) {
2170         scout_debug = 1;
2171         debug_filename = as->parms[P_DEBUG].items->data;
2172         scout_debugfd = fopen(debug_filename, "w");
2173         if (scout_debugfd == (FILE *) 0) {
2174             printf("[%s] Can't open debugging file '%s'!\n", rn,
2175                    debug_filename);
2176             scout_CleanExit(-1);
2177         }
2178         fprintf(scout_debugfd, "[%s] Writing to Scout debugging file '%s'\n",
2179                 rn, debug_filename);
2180     }
2181     wpkg_to_use = 2;            /*Always use curses for now */
2182     if (as->parms[P_FREQ].items != 0)
2183         scout_probefreq = atoi(as->parms[P_FREQ].items->data);
2184     else
2185         scout_probefreq = 60;
2186
2187     /*
2188      * See if we've been fed a base server name.
2189      */
2190     if (as->parms[P_BASE].items != 0)
2191         sprintf(scout_basename, "%s", as->parms[P_BASE].items->data);
2192     else
2193         *scout_basename = '\0';
2194
2195     /*
2196      * Count the number of servers we've been asked to monitor.
2197      */
2198     server_count = countServers(as->parms[P_SERVER].items);
2199
2200     /*
2201      * Create a line of blanks, a generally-useful thing.
2202      */
2203     sprintf(scout_blankline, "%255s", " ");
2204
2205     /*
2206      * Pull in the name of the host we're executing on if we've been
2207      * asked to.  If we can't get the name, we provide a default.
2208      */
2209     if (as->parms[P_HOST].items != 0) {
2210         scout_showhostname = 1;
2211         *scout_hostname = '\0';
2212         code = gethostname(scout_hostname, 128);
2213         if (code)
2214             sprintf(scout_hostname, "%s", "*No Hostname*");
2215     }
2216
2217     /*
2218      * Pull in any and all attention/highlighting thresholds.
2219      */
2220     if (as->parms[P_ATTENTION].items != 0)
2221         scout_AdoptThresholds(as->parms[P_ATTENTION].items);
2222
2223     if (as->parms[P_WIDTHS].items != 0) {
2224         scout_SetColumnWidths(as->parms[P_WIDTHS].items);
2225     }
2226
2227     /*
2228      * Now, drive the sucker.
2229      */
2230     code = execute_scout(server_count,  /*Num servers */
2231                          as->parms[P_SERVER].items,     /*Ptr to srv names */
2232                          wpkg_to_use);  /*Graphics pkg */
2233     if (code) {
2234         fprintf(stderr, "[%s] Error executing scout: %d\n", rn, code);
2235         scout_CleanExit(-1);
2236     }
2237
2238     /*
2239      * We initialized (and ran) correctly, so return the good news.
2240      */
2241     return (0);
2242
2243 }                               /*scoutInit */
2244
2245 #include "AFS_component_version_number.c"
2246
2247 int
2248 main(int argc, char **argv)
2249 {                               /*main */
2250
2251     afs_int32 code;     /*Return code */
2252     struct cmd_syndesc *ts;     /*Ptr to cmd line syntax descriptor */
2253
2254 #ifdef  AFS_AIX32_ENV
2255     /*
2256      * The following signal action for AIX is necessary so that in case of a
2257      * crash (i.e. core is generated) we can include the user's data section
2258      * in the core dump. Unfortunately, by default, only a partial core is
2259      * generated which, in many cases, isn't too useful.
2260      */
2261     struct sigaction nsa;
2262
2263     sigemptyset(&nsa.sa_mask);
2264     nsa.sa_handler = SIG_DFL;
2265     nsa.sa_flags = SA_FULLDUMP;
2266     sigaction(SIGSEGV, &nsa, NULL);
2267 #endif
2268     /*
2269      * Set up the commands we understand.
2270      */
2271     ts = cmd_CreateSyntax("initcmd", scoutInit, NULL, 0, "initialize the program");
2272     cmd_AddParm(ts, "-server", CMD_LIST, CMD_REQUIRED,
2273                 "FileServer name(s) to monitor");
2274     cmd_AddParm(ts, "-basename", CMD_SINGLE, CMD_OPTIONAL,
2275                 "base server name");
2276     cmd_AddParm(ts, "-frequency", CMD_SINGLE, CMD_OPTIONAL,
2277                 "poll frequency, in seconds");
2278     cmd_AddParm(ts, "-host", CMD_FLAG, CMD_OPTIONAL,
2279                 "show name of host you're running on");
2280     cmd_AddParm(ts, "-attention", CMD_LIST, CMD_OPTIONAL,
2281                 "specify attention (highlighting) level");
2282     cmd_AddParm(ts, "-debug", CMD_SINGLE, CMD_OPTIONAL,
2283                 "turn debugging output on to the named file");
2284     cmd_AddParm(ts, "-columnwidths", CMD_LIST, CMD_OPTIONAL,
2285                 "specify column widths");
2286
2287     /*
2288      * Parse command-line switches & execute the test, then get the heck
2289      * out of here.
2290      */
2291     code = cmd_Dispatch(argc, argv);
2292     if (code) {
2293         scout_CleanExit(1);
2294     } else
2295         scout_CleanExit(0);
2296
2297     return 0; /* not reachable */
2298 }                               /*main */