afsmonitor: add fs callback xstats collection
[openafs.git] / src / afsmonitor / afsmonitor.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  * Portions Copyright (c) 2003 Apple Computer, Inc.
10  */
11
12 /*
13  * Afsmonitor: An AFS Performance Monitoring Tool
14  *
15  *-------------------------------------------------------------------------*/
16
17
18 #include <afsconfig.h>
19 #include <afs/param.h>
20
21
22 #include <stdio.h>
23 #include <math.h>
24 #include <string.h>
25 #include <errno.h>
26 #include <afs/cmd.h>
27 #include <signal.h>
28 #undef IN
29 #include <sys/types.h>
30 #include <netinet/in.h>
31 #include <sys/socket.h>
32 #include <netdb.h>
33 #include <ctype.h>
34 #ifdef HAVE_STDINT_H
35 # include <stdint.h>
36 #endif
37
38 #include <afs/gtxwindows.h>             /*Generic window package */
39 #include <afs/gtxobjects.h>             /*Object definitions */
40 #include <afs/gtxlightobj.h>    /*Light object interface */
41 #include <afs/gtxcurseswin.h>   /*Curses window package */
42 #include <afs/gtxdumbwin.h>             /*Dumb terminal window package */
43 #include <afs/gtxX11win.h>              /*X11 window package */
44 #include <afs/gtxframe.h>               /*Frame package */
45 #include <afs/gtxinput.h>
46
47 #include <afs/xstat_fs.h>
48 #include <afs/xstat_cm.h>
49
50 #include "afsmonitor.h"
51
52 /* command line parameter indices */
53
54 #define P_CONFIG        0
55 #define P_FREQUENCY     1
56 #define P_OUTPUT        2
57 #define P_DETAILED      3
58 /* #define P_PACKAGE    X */
59 #define P_DEBUG         4
60 #define P_FSHOSTS       5
61 #define P_CMHOSTS       6
62 #define P_BUFFERS       7
63
64
65 int afsmon_debug = 0;           /* debug info to file ? */
66 FILE *debugFD;                  /* debugging file descriptor */
67 static int afsmon_output = 0;   /* output to file ? */
68 static int afsmon_detOutput = 0;        /* detailed output ? */
69 static int afsmon_onceOnly = 0; /* probe once only ? (not implemented) */
70 int afsmon_probefreq;           /* probe frequency */
71 static int wpkg_to_use;         /* graphics package to use */
72 static char output_filename[80];        /* output filename */
73 char errMsg[256];               /* buffers used to print error messages after */
74 char errMsg1[256];              /* gtx is initialized (stderr/stdout gone !) */
75 int num_bufSlots = 0;           /* number of slots in fs & cm circular buffers */
76
77 /* Flags used to process "show" directives in config file */
78 short fs_showFlags[NUM_FS_STAT_ENTRIES];
79 short cm_showFlags[NUM_CM_STAT_ENTRIES];
80
81
82 /* afsmonitor misc definitions */
83
84 #define DEFAULT_FREQUENCY 60    /* default proble frequency in seconds */
85 #define DEFAULT_BUFSLOTS  0     /* default number of buffer slots */
86 #define CFG_STR_LEN     80      /* max length of config file fields */
87 #define FS 1                    /* for misc. use */
88 #define CM 2                    /* for misc. use */
89
90
91 #define NUM_XSTAT_FS_AFS_PERFSTATS_LONGS 66     /* number of fields (longs) in struct afs_PerfStats that we display */
92 #define NUM_AFS_STATS_CMPERF_LONGS 40   /* number of longs in struct afs_stats_CMPerf excluding up/down stats and fields we dont display */
93
94
95 /* variables used for exec'ing user provided threshold handlers */
96 char *fsHandler_argv[20];       /* *argv[] for the handler */
97 char fsHandler_args[20][256];   /* buffer space for arguments */
98 int exec_fsThreshHandler = 0;   /* execute fs threshold handler ? */
99
100
101 /* THRESHOLD STRUCTURE DEFINITIONS */
102
103 /* flag to indicate that threshold entries apply to all hosts. these will
104    be turned off when the first fs or cm host entry is processed */
105 static int global_ThreshFlag = 1;
106 static int global_fsThreshCount = 0;    /* number of global fs thresholds */
107 static int global_cmThreshCount = 0;    /* number of global cm thresholds */
108
109
110
111 /* Linked lists of file server and cache manager host names are made from
112 the entries in the config file. Head pointers to FS and CM server name lists. */
113 static struct afsmon_hostEntry *FSnameList;
114 static struct afsmon_hostEntry *CMnameList;
115
116 /* number of fileservers and cache managers to monitor */
117 int numFS = 0;
118 int numCM = 0;
119
120 /* number of xstat collection ids */
121 #define MAX_NUM_FS_COLLECTIONS 2
122 #define MAX_NUM_CM_COLLECTIONS 1
123 int num_fs_collections = 0;
124 int num_cm_collections = 0;
125
126 /* variables used for processing config file */
127 /* ptr to the hostEntry structure of the last "fs" or "cm" entry processed
128 in the config file */
129 static struct afsmon_hostEntry *last_hostEntry;
130 /* names of the last host processed in the config file */
131 static char last_fsHost[HOST_NAME_LEN];
132 static char last_cmHost[HOST_NAME_LEN];
133 static int lastHostType = 0;    /* 0 = no host entries processed
134                                  * 1 = last host was file server
135                                  * 2 = last host was cache manager. */
136
137
138 /* FILE SERVER CIRCULAR BUFFER VARIABLES  */
139
140 struct afsmon_fs_Results_list {
141     struct xstat_fs_ProbeResults *fsResults[MAX_NUM_FS_COLLECTIONS];
142     int empty[MAX_NUM_FS_COLLECTIONS];
143     struct afsmon_fs_Results_list *next;
144 };
145
146 struct afsmon_fs_Results_CBuffer {
147     int probeNum;               /* probe number of entries in this slot */
148     struct afsmon_fs_Results_list *list;        /* ptr to list of results */
149 };
150
151 int afsmon_fs_results_length[] =
152     { XSTAT_FS_FULLPERF_RESULTS_LEN, XSTAT_FS_CBSTATS_RESULTS_LEN };
153
154 /* buffer for FS probe results */
155 struct afsmon_fs_Results_CBuffer *afsmon_fs_ResultsCB;
156
157 int afsmon_fs_curr_CBindex = 0; /* current fs CB slot */
158
159 /* Probe number variables. The current probe number is incremented 
160 when the first probe from a new probe cycle is received. The prev probe
161 number is incremented when the last probe of the current cycle is
162 received. This difference is because of the purpose for which these
163 counters are used */
164
165 int afsmon_fs_curr_probeNum = 1;        /* current fs probe number */
166 int afsmon_fs_prev_probeNum = 0;        /* previous fs probe number */
167
168
169 /* CACHE MANAGER CIRCULAR BUFFER VARIABLES  */
170
171 struct afsmon_cm_Results_list {
172     struct xstat_cm_ProbeResults *cmResults[MAX_NUM_CM_COLLECTIONS];
173     int empty[MAX_NUM_CM_COLLECTIONS];
174     struct afsmon_cm_Results_list *next;
175 };
176
177 struct afsmon_cm_Results_CBuffer {
178     int probeNum;               /* probe number of entries in this slot */
179     struct afsmon_cm_Results_list *list;        /* ptr to list of results */
180 };
181
182 int afsmon_cm_results_length[] = { XSTAT_CM_FULLPERF_RESULTS_LEN };
183
184 /* buffer for CM probe results */
185 struct afsmon_cm_Results_CBuffer *afsmon_cm_ResultsCB;
186
187 int afsmon_cm_curr_CBindex = 0; /* current cm CB slot */
188
189
190 /* Probe number variables. The current probe number is incremented 
191 when the first probe from a new probe cycle is received. The prev probe
192 number is incremented when the last probe of the current cycle is
193 received. This difference is because of the purpose for which these
194 counters are used */
195
196 int afsmon_cm_curr_probeNum = 1;        /* current cm probe number */
197 int afsmon_cm_prev_probeNum = 0;        /* previous cm probe number */
198
199
200 /* Structures to hold FS & CM results in string format(suitable for display ) */
201
202 /* ptr to array holding the results of FS probes in ascii format */
203         /* for current probe cycle */
204 struct fs_Display_Data *curr_fsData = (struct fs_Display_Data *)0;
205         /* for previous probe cycle */
206 struct fs_Display_Data *prev_fsData = (struct fs_Display_Data *)0;
207
208
209 /* ptr to array holding the results of CM probes in ascii format */
210         /* for current probe cycle */
211 struct cm_Display_Data *curr_cmData = (struct cm_Display_Data *)0;
212         /* for previous probe cycle */
213 struct cm_Display_Data *prev_cmData = (struct cm_Display_Data *)0;
214
215 /* EXTERN DEFINITIONS */
216
217 /* file server and cache manager variable names (from afsmon_labels.h) */
218 extern char *fs_varNames[];
219 extern char *cm_varNames[];
220
221 /* GTX & MISC VARIABLES */
222
223 /* afsmonitor window */
224 extern struct gwin *afsmon_win;
225
226 /* current page number in the overview frame */
227 extern int ovw_currPage;
228
229 /* number of FS alerts and number of hosts on FS alerts */
230 int num_fs_alerts;
231 int numHosts_onfs_alerts;
232
233 /* number of CM alerts and number of hosts on FS alerts */
234 int num_cm_alerts;
235 int numHosts_oncm_alerts;
236
237 /* flag to indicate that atleast one probe cycle has completed and 
238 data is available for updating the display */
239 extern int fs_Data_Available;
240 extern int cm_Data_Available;
241
242 extern int gtx_initialized;     /* gtx initialized ? */
243
244 /* This array contains the indices of the file server data items that
245 are to be displayed on the File Servers screen. For example, suppose the
246 user wishes to display only the vcache statistics then the following array
247 will contain indices 2 to 14 corresponding to the position of the
248 vcache data items in the fs_varNames[] array. If the config file contains
249 no "show fs .." directives, it will contain the indices of all the 
250 items in the fs_varNames[] array */
251
252 short fs_Display_map[NUM_FS_STAT_ENTRIES];
253 int fs_DisplayItems_count = 0;  /* number of items to display */
254 int fs_showDefault = 1;         /* show all of FS data ? */
255
256
257 /* same use as above for Cache Managers  */
258 short cm_Display_map[NUM_CM_STAT_ENTRIES];
259 int cm_DisplayItems_count = 0;  /* number of items to display */
260 int cm_showDefault = 1;         /* show all of CM data ? */
261
262 extern int fs_currPage;         /* current page number in the File Servers frame */
263 extern int fs_curr_LCol;        /* current leftmost column on display on FS frame */
264
265 extern int cm_currPage;         /* current page number in the Cache Managers frame */
266 extern int cm_curr_LCol;        /* current leftmost column on display on CM frame */
267
268 /* File server and Cache manager data is classified into sections & 
269 groups to help the user choose what he wants displayed */
270 extern char *fs_categories[];   /* file server data category names */
271 extern char *cm_categories[];   /* cache manager data category names */
272
273
274 static int fs_FullPerfs_ltoa(struct fs_Display_Data *a_fsData,
275                              struct xstat_fs_ProbeResults *a_fsResults);
276 static int fs_CallBackStats_ltoa(struct fs_Display_Data *a_fsData,
277                                  struct xstat_fs_ProbeResults *a_fsResults);
278
279 #ifdef HAVE_STRCASESTR
280 extern char * strcasestr(const char *, const char *);
281 #else
282 /*      
283         strcasestr(): Return first occurence of pattern s2 in s1, case 
284         insensitive. 
285
286         This routine is required since I made pattern matching of the
287         config file to be case insensitive. 
288 */
289
290 char *
291 strcasestr(s1, s2)
292      char *s1;
293      char *s2;
294 {
295     char *ptr;
296     int len1, len2;
297
298     len1 = strlen(s1);
299     len2 = strlen(s2);
300
301     if (len1 < len2)
302         return ((char *)NULL);
303
304     ptr = s1;
305
306     while (len1 >= len2 && len1 > 0) {
307         if ((strncasecmp(ptr, s2, len2)) == 0)
308             return (ptr);
309         ptr++;
310         len1--;
311     }
312     return ((char *)NULL);
313 }
314 #endif
315
316 struct hostent *
317 GetHostByName(char *name)
318 {
319     struct hostent *he;
320 #ifdef AFS_SUN5_ENV
321     char ip_addr[32];
322 #endif
323
324     he = gethostbyname(name);
325 #ifdef AFS_SUN5_ENV
326     /* On solaris the above does not resolve hostnames to full names */
327     if (he != NULL) {
328         memcpy(ip_addr, he->h_addr, he->h_length);
329         he = gethostbyaddr(ip_addr, he->h_length, he->h_addrtype);
330     }
331 #endif
332     return (he);
333 }
334
335
336 /*-----------------------------------------------------------------------
337  * afsmon_Exit()
338  *
339  * Description
340  *      Exit gracefully from the afsmonitor. Frees memory where appropriate,
341  *      cleans up after gtx and closes all open file descriptors. If a user 
342  *      provided threshold handler is to be exec'ed then gtx cleanup is
343  *      not performed and an exec() is made instead of an exit(). 
344  *
345  * Returns
346  *      Nothing.
347  *
348  * Comments 
349  *      This function is called to execute a user handler only 
350  *      by a child process.
351  * 
352  *----------------------------------------------------------------------*/
353
354 int
355 afsmon_Exit(int a_exitVal)      /* exit code */
356 {                               /* afsmon_Exit */
357     static char rn[] = "afsmon_Exit";
358     struct afsmon_fs_Results_list *tmp_fslist;
359     struct afsmon_fs_Results_list *next_fslist;
360     struct xstat_fs_ProbeResults *tmp_xstat_fsPR;
361     struct afsmon_cm_Results_list *tmp_cmlist;
362     struct afsmon_cm_Results_list *next_cmlist;
363     struct xstat_cm_ProbeResults *tmp_xstat_cmPR;
364     struct afsmon_hostEntry *curr_hostEntry;
365     struct afsmon_hostEntry *prev_hostEntry;
366     int i;
367     int j;
368     int bufslot;
369     int code;
370
371     if (afsmon_debug) {
372         fprintf(debugFD, "[ %s ] Called with exit code %d\n", rn, a_exitVal);
373         fflush(debugFD);
374     }
375
376     /* get out of curses first, but not if we are here to exec a threshold
377      * handler. If we do, the screen gets messed up  */
378     if (gtx_initialized && !exec_fsThreshHandler)
379         gator_cursesgwin_cleanup(afsmon_win);
380
381     /* print the error message buffer */
382     if (errMsg[0] != '\0')
383         fprintf(stderr, "%s", errMsg);
384     if (errMsg1[0] != '\0')
385         fprintf(stderr, "%s", errMsg1);
386
387     /* deallocate file server circular buffers */
388     if (numFS && num_bufSlots) {
389         if (afsmon_debug) {
390             fprintf(debugFD, "freeing FS circular buffers ");
391             fflush(debugFD);
392         }
393
394         for (bufslot = 0; bufslot < num_bufSlots; bufslot++) {
395             if (afsmon_debug)
396                 fprintf(debugFD, " %d) ", bufslot);
397             if (afsmon_fs_ResultsCB[bufslot].list !=
398                 (struct afsmon_fs_Results_list *)0) {
399                 tmp_fslist = afsmon_fs_ResultsCB[bufslot].list;
400                 j = numFS;
401                 while (tmp_fslist) {
402                     /* make sure we do not go astray */
403                     if (--j < 0) {
404                         if (afsmon_debug)
405                             fprintf(debugFD,
406                                     "[ %s ] error in deallocating fs CB\n",
407                                     rn);
408                         break;
409                     }
410                     next_fslist = tmp_fslist->next;
411                     for (i = 0; i < MAX_NUM_FS_COLLECTIONS; i++) {
412                         tmp_xstat_fsPR = tmp_fslist->fsResults[i];
413
414                         if (afsmon_debug)
415                             fprintf(debugFD, "%d ", numFS - j);
416
417                         /* free xstat_fs_Results data */
418                         free(tmp_xstat_fsPR->data.AFS_CollData_val);
419                         free(tmp_xstat_fsPR->connP);
420                         free(tmp_xstat_fsPR);
421                     }
422
423                     /* free the fs list item */
424                     free(tmp_fslist);
425                     tmp_fslist = next_fslist;
426
427                 }               /* while fs list items in this slot */
428             }                   /* if entries in this buffer slot */
429         }                       /* for each fs buffer slot */
430         if (afsmon_debug)
431             fprintf(debugFD, "\n");
432     }
433
434     if (afsmon_debug)
435         fflush(debugFD);
436     /* deallocate cache manager curcular buffers */
437     if (numCM && num_bufSlots) {
438         if (afsmon_debug)
439             fprintf(debugFD, "freeing CM curcular buffers ");
440         for (bufslot = 0; bufslot < num_bufSlots; bufslot++) {
441             if (afsmon_debug)
442                 fprintf(debugFD, " %d) ", bufslot);
443             if (afsmon_cm_ResultsCB[bufslot].list !=
444                 (struct afsmon_cm_Results_list *)0) {
445                 tmp_cmlist = afsmon_cm_ResultsCB[bufslot].list;
446                 j = numCM;
447                 while (tmp_cmlist) {
448                     /* make sure we do not go astray */
449                     if (--j < 0) {
450                         if (afsmon_debug)
451                             fprintf(debugFD,
452                                     "[ %s ] error in deallocating cm CB\n",
453                                     rn);
454                         break;
455                     }
456                     next_cmlist = tmp_cmlist->next;
457                     for (i = 0; i < MAX_NUM_CM_COLLECTIONS; i++) {
458                         tmp_xstat_cmPR = tmp_cmlist->cmResults[i];
459
460                         if (afsmon_debug)
461                             fprintf(debugFD, "%d ", numCM - j);
462                         /* make sure data is ok */
463                         /* Print_cm_FullPerfInfo(tmp_xstat_cmPR); */
464
465                         /* free xstat_cm_Results data */
466                         free(tmp_xstat_cmPR->data.AFSCB_CollData_val);
467                         free(tmp_xstat_cmPR->connP);
468                     }
469                     free(tmp_cmlist->cmResults);
470
471                     /* free the cm list item */
472                     free(tmp_cmlist);
473                     tmp_cmlist = next_cmlist;
474
475                 }               /* while cm list items in this slot */
476             }                   /* if entries in this buffer slot */
477         }                       /* for each cm buffer slot */
478         if (afsmon_debug)
479             fprintf(debugFD, "\n");
480     }
481
482
483     /* deallocate FS & CM Print buffers */
484     if (curr_fsData != (struct fs_Display_Data *)0) {
485         if (afsmon_debug)
486             fprintf(debugFD, "Deallocating FS Print Buffers .... curr");
487         free(curr_fsData);
488     }
489     if (prev_fsData != (struct fs_Display_Data *)0) {
490         if (afsmon_debug)
491             fprintf(debugFD, ", prev \n");
492         free(prev_fsData);
493     }
494     if (prev_cmData != (struct cm_Display_Data *)0) {
495         if (afsmon_debug)
496             fprintf(debugFD, "Deallocating CM Print Buffers .... curr");
497         free(curr_cmData);
498     }
499     if (prev_cmData != (struct cm_Display_Data *)0) {
500         if (afsmon_debug)
501             fprintf(debugFD, ", prev \n");
502         free(prev_cmData);
503     }
504
505     /* deallocate hostEntry lists */
506     if (numFS) {
507         if (afsmon_debug)
508             fprintf(debugFD, "Deallocating FS hostEntries ..");
509         curr_hostEntry = FSnameList;
510         for (i = 0; i < numFS; i++) {
511             prev_hostEntry = curr_hostEntry;
512             if (curr_hostEntry->thresh != NULL)
513                 free(curr_hostEntry->thresh);
514             free(curr_hostEntry);
515             if (afsmon_debug)
516                 fprintf(debugFD, " %d", i);
517             curr_hostEntry = prev_hostEntry->next;
518         }
519         if (afsmon_debug)
520             fprintf(debugFD, "\n");
521     }
522     if (numCM) {
523         if (afsmon_debug)
524             fprintf(debugFD, "Deallocating CM hostEntries ..");
525         curr_hostEntry = CMnameList;
526         for (i = 0; i < numCM; i++) {
527             prev_hostEntry = curr_hostEntry;
528             if (curr_hostEntry->thresh != NULL)
529                 free(curr_hostEntry->thresh);
530             free(curr_hostEntry);
531             if (afsmon_debug)
532                 fprintf(debugFD, " %d", i);
533             curr_hostEntry = prev_hostEntry->next;
534         }
535         if (afsmon_debug)
536             fprintf(debugFD, "\n");
537     }
538
539     /* close debug file */
540     if (afsmon_debug) {
541         fflush(debugFD);
542         fclose(debugFD);
543     }
544
545     if (exec_fsThreshHandler) {
546         code = execvp(fsHandler_argv[0], fsHandler_argv);
547         if (code == -1) {
548             fprintf(stderr, "execvp() of %s returned %d, errno %d\n",
549                     fsHandler_argv[0], code, errno);
550             exit(-1);
551         }
552     }
553
554     exit(a_exitVal);
555 }                               /* afsmon_Exit */
556
557 /*-----------------------------------------------------------------------
558  * insert_FS()
559  *
560  * Description:
561  *      Insert a hostname in the file server names list.
562  *
563  * Returns:
564  *      Success: 0
565  *      Failure: -1
566  *----------------------------------------------------------------------*/
567
568 int
569 insert_FS(char *a_hostName)             /* name of cache manager to be inserted in list */
570 {                               /* insert_FS() */
571     static struct afsmon_hostEntry *curr_item;
572     static struct afsmon_hostEntry *prev_item;
573
574     if (*a_hostName == '\0')
575         return (-1);
576     curr_item = (struct afsmon_hostEntry *)
577         malloc(sizeof(struct afsmon_hostEntry));
578     if (curr_item == (struct afsmon_hostEntry *)0) {
579         fprintf(stderr, "Failed to allocate space for FS nameList\n");
580         return (-1);
581     }
582
583     strncpy(curr_item->hostName, a_hostName, CFG_STR_LEN);
584     curr_item->next = (struct afsmon_hostEntry *)0;
585     curr_item->numThresh = 0;
586     curr_item->thresh = NULL;
587
588     if (FSnameList == (struct afsmon_hostEntry *)0)
589         FSnameList = curr_item;
590     else
591         prev_item->next = curr_item;
592
593     prev_item = curr_item;
594     /*  record the address of this entry so that its threshold
595      * count can be incremented during  the first pass of the config file */
596     last_hostEntry = curr_item;
597
598     return (0);
599 }
600
601 /*-----------------------------------------------------------------------
602  * print_FS()
603  *
604  * Description:
605  *      Debug routine.
606  *      Prints the file server names linked list.
607  *
608  * Returns:
609  *      Nothing.
610  *----------------------------------------------------------------------*/
611 void
612 print_FS(void)
613 {                               /* print_FS() */
614     static char rn[] = "print_FS";
615     struct afsmon_hostEntry *tempFS;
616     struct Threshold *threshP;
617     int i;
618
619     if (afsmon_debug) {
620         fprintf(debugFD, "[ %s ] Called\n", rn);
621         fflush(debugFD);
622     }
623
624     if (afsmon_debug) {
625         tempFS = FSnameList;
626         fprintf(debugFD, "No of File Servers: %d\n", numFS);
627         if (numFS) {
628             do {
629                 fprintf(debugFD, "\t %s threshCount = %d\n", tempFS->hostName,
630                         tempFS->numThresh);
631                 threshP = tempFS->thresh;
632                 for (i = 0; i < tempFS->numThresh; i++, threshP++)
633                     fprintf(debugFD, "\t thresh (%2d) %s %s %s\n",
634                             threshP->index, threshP->itemName,
635                             threshP->threshVal, threshP->handler);
636             } while ((tempFS = tempFS->next) != (struct afsmon_hostEntry *)0);
637         }
638         fprintf(debugFD, "\t\t-----End of List-----\n");
639         fflush(debugFD);
640     }
641
642 }
643
644 /*-----------------------------------------------------------------------
645  * insert_CM()
646  *
647  * Description:
648  *      Insert a hostname in the cache manager names list.
649  *
650  * Returns:
651  *      Success: 0
652  *      Failure: -1
653  *----------------------------------------------------------------------*/
654
655 int
656 insert_CM(char *a_hostName)             /* name of cache manager to be inserted in list */
657 {                               /* insert_CM */
658     static struct afsmon_hostEntry *curr_item;
659     static struct afsmon_hostEntry *prev_item;
660
661     if (*a_hostName == '\0')
662         return (-1);
663     curr_item = (struct afsmon_hostEntry *)
664         malloc(sizeof(struct afsmon_hostEntry));
665     if (curr_item == (struct afsmon_hostEntry *)0) {
666         fprintf(stderr, "Failed to allocate space for CM nameList\n");
667         return (-1);
668     }
669
670     strncpy(curr_item->hostName, a_hostName, CFG_STR_LEN);
671     curr_item->next = (struct afsmon_hostEntry *)0;
672     curr_item->numThresh = 0;
673     curr_item->thresh = NULL;
674
675     if (CMnameList == (struct afsmon_hostEntry *)0)
676         CMnameList = curr_item;
677     else
678         prev_item->next = curr_item;
679
680     prev_item = curr_item;
681     /* side effect. note the address of this entry so that its threshold
682      * count can be incremented during  the first pass of the config file */
683     last_hostEntry = curr_item;
684
685     return (0);
686 }
687
688
689 /*-----------------------------------------------------------------------
690  * print_CM()
691  *
692  * Description:
693  *      Debug routine.
694  *      Prints the cache manager names linked list.
695  *
696  * Returns:
697  *      Nothing.
698  *----------------------------------------------------------------------*/
699 int
700 print_CM(void)
701 {                               /* print_CM() */
702     static char rn[] = "print_CM";
703     struct afsmon_hostEntry *tempCM;
704     struct Threshold *threshP;
705     int i;
706
707     if (afsmon_debug) {
708         fprintf(debugFD, "[ %s ] Called\n", rn);
709         fflush(debugFD);
710     }
711
712     if (afsmon_debug) {
713         tempCM = CMnameList;
714         fprintf(debugFD, "No of Cache Managers: %d\n", numCM);
715         if (numCM) {
716             do {
717                 fprintf(debugFD, "\t %s threshCount = %d\n", tempCM->hostName,
718                         tempCM->numThresh);
719                 threshP = tempCM->thresh;
720                 for (i = 0; i < tempCM->numThresh; i++, threshP++)
721                     fprintf(debugFD, "\t thresh (%2d) %s %s %s\n",
722                             threshP->index, threshP->itemName,
723                             threshP->threshVal, threshP->handler);
724             } while ((tempCM = tempCM->next) != (struct afsmon_hostEntry *)0);
725         }
726         fprintf(debugFD, "\t\t-----End of List-----\n");
727     }
728     return (0);
729 }                               /* print_CM() */
730
731
732
733 /*-----------------------------------------------------------------------
734  * parse_hostEntry()
735  *
736  * Description:
737  *      Parse the host entry line in the config file. Check the syntax,
738  *      and inserts the host name in the FS ot CM linked list. Also
739  *      remember if this entry was an fs or cm & the ptr to its hostEntry
740  *      structure. The threshold entries in the config file are dependent
741  *      on their position relative to the hostname entries. Hence it is
742  *      required to remember the names of the last file server and cache
743  *      manager entries that were processed.
744  *
745  * Returns:
746  *      Success: 0
747  *      Failure: -1
748  *
749  *----------------------------------------------------------------------*/
750
751 int
752 parse_hostEntry(char *a_line)
753 {                               /* parse_hostEntry */
754
755     static char rn[] = "parse_hostEntry";       /* routine name */
756     char opcode[CFG_STR_LEN];   /* specifies type of config entry */
757     char arg1[CFG_STR_LEN];     /* hostname or qualifier (fs/cm?)  */
758     char arg2[CFG_STR_LEN];     /* threshold variable */
759     char arg3[CFG_STR_LEN];     /* threshold value */
760     char arg4[CFG_STR_LEN];     /* user's handler  */
761     struct hostent *he;         /* host entry */
762
763     if (afsmon_debug) {
764         fprintf(debugFD, "[ %s ] Called, a_line = %s\n", rn, a_line);
765         fflush(debugFD);
766     }
767
768     /* break it up */
769     opcode[0] = 0;
770     arg1[0] = 0;
771     arg2[0] = 0;
772     arg3[0] = 0;
773     arg4[0] = 0;
774     sscanf(a_line, "%s %s %s %s %s", opcode, arg1, arg2, arg3, arg4);
775     /* syntax is "opcode hostname" */
776     if ((strlen(arg2)) != 0) {
777         fprintf(stderr, "[ %s ] Extraneous characters at end of line\n", rn);
778         return (-1);
779     }
780
781     /* good host ? */
782     he = GetHostByName(arg1);
783     if (he == NULL) {
784         fprintf(stderr, "[ %s ] Unable to resolve hostname %s\n", rn, arg1);
785         return (-1);
786     }
787
788     if ((strcasecmp(opcode, "fs")) == 0) {
789         /* use the complete host name to insert in the file server names list */
790         insert_FS(he->h_name);
791         /* note that last host entry in the config file was fs */
792         lastHostType = 1;
793         numFS++;
794         /* threholds are not global anymore */
795         if (global_ThreshFlag)
796             global_ThreshFlag = 0;
797     } else if ((strcasecmp(opcode, "cm")) == 0) {
798         /* use the complete host name to insert in the CM names list */
799         insert_CM(he->h_name);
800         /* last host entry in the config file was cm */
801         lastHostType = 2;
802         numCM++;
803         /* threholds are not global anymore */
804         if (global_ThreshFlag)
805             global_ThreshFlag = 0;
806     } else
807         return (-1);
808
809     return (0);
810 }
811
812 /*-----------------------------------------------------------------------
813  * parse_threshEntry()
814  *
815  * Description
816  *      Parse the threshold entry line in the config file. This function is
817  *      called in the the first pass of the config file. It checks the syntax 
818  *      of the config lines and verifies their positional validity - eg.,
819  *      a cm threshold cannot appear after a fs hostname entry, etc.
820  *      It also counts the thresholds applicable to each host.
821  *
822  * Returns
823  *      Success: 0
824  *      Failure: -1
825  *
826  *----------------------------------------------------------------------*/
827
828 int
829 parse_threshEntry(char *a_line)
830 {                               /* parse_threshEntry */
831     static char rn[] = "parse_threshEntry";     /* routine name */
832     char opcode[CFG_STR_LEN];   /* specifies type of config entry */
833     char arg1[CFG_STR_LEN];     /* hostname or qualifier (fs/cm?)  */
834     char arg2[CFG_STR_LEN];     /* threshold variable */
835     char arg3[CFG_STR_LEN];     /* threshold value */
836     char arg4[CFG_STR_LEN];     /* user's handler  */
837     char arg5[CFG_STR_LEN];     /* junk characters */
838
839     if (afsmon_debug) {
840         fprintf(debugFD, "[ %s ] Called, a_line = %s\n", rn, a_line);
841         fflush(debugFD);
842     }
843
844     /* break it up */
845     opcode[0] = 0;
846     arg1[0] = 0;
847     arg2[0] = 0;
848     arg3[0] = 0;
849     arg4[0] = 0;
850     arg5[0] = 0;
851     sscanf(a_line, "%s %s %s %s %s %s", opcode, arg1, arg2, arg3, arg4, arg5);
852
853     /* syntax is "thresh fs/cm variable_name threshold_value [handler] " */
854     if (((strlen(arg1)) == 0) || ((strlen(arg2)) == 0)
855         || ((strlen(arg3)) == 0)) {
856         fprintf(stderr, "[ %s ] Incomplete line\n", rn);
857         return (-1);
858     }
859     if (strlen(arg3) > THRESH_VAR_LEN - 2) {
860         fprintf(stderr, "[%s ] threshold value too long\n", rn);
861         return (-1);
862     }
863
864     if ((strcasecmp(arg1, "fs")) == 0) {
865         switch (lastHostType) {
866         case 0:         /* its a global threshold */
867             global_fsThreshCount++;
868             break;
869         case 1:         /* inc thresh count of last file server */
870             last_hostEntry->numThresh++;
871             break;
872         case 2:
873             fprintf(stderr,
874                     "[ %s ] A threshold for a File Server cannot be placed after a Cache Manager host entry in the config file \n",
875                     rn);
876             return (-1);
877         default:
878             fprintf(stderr, "[ %s ] Programming error 1\n", rn);
879             return (-1);
880         }
881     } else if ((strcasecmp(arg1, "cm")) == 0) {
882         switch (lastHostType) {
883         case 0:         /* its a global threshold */
884             global_cmThreshCount++;
885             break;
886         case 2:         /* inc thresh count of last cache manager */
887             last_hostEntry->numThresh++;
888             break;
889         case 1:
890             fprintf(stderr,
891                     "[ %s ] A threshold for a Cache Manager cannot be placed after a File Server host entry in the config file \n",
892                     rn);
893             return (-1);
894         default:
895             fprintf(stderr, "[ %s ] Programming error 2\n", rn);
896             return (-1);
897         }
898     } else {
899         fprintf(stderr,
900                 "[ %s ] Syntax error. Second argument should be \"fs\" or \"cm\" \n",
901                 rn);
902         return (-1);
903     }
904
905     return (0);
906 }                               /* parse_threshEntry */
907
908
909 /*-----------------------------------------------------------------------
910  * store_threshold()
911  *
912  * Description
913  *      The thresholds applicable to each host machine are stored in the
914  *      FSnameList and CMnameList. Threshold entries in the config file are
915  *      context sensitive. The host to which this threshold is applicable
916  *      is pointed to by last_fsHost (for file servers) and last_cmHost
917  *      for cache managers. For global thresholds the info is recorded for
918  *      all the hosts. This function is called in the second pass of the
919  *      config file. In the first pass a count of the number of global
920  *      thresholds is determined and this information is used in this 
921  *      routine. If threshold entries are duplicated the first entry is
922  *      overwritten.
923  *      Each threshold entry also has an index field. This is a positional
924  *      index to the corresponding variable in the prev_[fs/cm]Data arrays.
925  *      This makes it easy to check the threshold for overflow.
926  *
927  * Returns:
928  *      Success: 0
929  *      Failure: -1
930  *----------------------------------------------------------------------*/
931
932 int
933 store_threshold(int a_type,             /* 1 = fs , 2 = cm */
934                 char *a_varName,        /* threshold name */
935                 char *a_value,          /* threshold value */
936                 char *a_handler)        /* threshold overflow handler */
937 {                               /* store_thresholds */
938
939     static char rn[] = "store_thresholds";      /* routine name */
940     struct afsmon_hostEntry *tmp_host;  /* tmp ptr to hostEntry */
941     struct afsmon_hostEntry *Header;    /* tmp ptr to hostEntry list header */
942     struct Threshold *threshP;  /* tmp ptr to threshold list */
943     char *hostname;
944     int index;                  /* index to fs_varNames or cm_varNames */
945     int found;
946     int done;
947     int srvCount;               /* tmp count of host names */
948     int *global_TC;             /* ptr to global_xxThreshCount */
949     int i, j;
950
951     if (afsmon_debug) {
952         fprintf(debugFD,
953                 "[ %s ] Called, a_type= %d, a_varName= %s, a_value= %s, a_handler=%s\n",
954                 rn, a_type, a_varName, a_value, a_handler);
955         fflush(debugFD);
956     }
957
958     /* resolve the threshold variable name */
959     found = 0;
960     if (a_type == 1) {          /* fs threshold */
961         for (index = 0; index < NUM_FS_STAT_ENTRIES; index++) {
962             if (strcasecmp(a_varName, fs_varNames[index]) == 0) {
963                 found = 1;
964                 break;
965             }
966         }
967         if (!found) {
968             fprintf(stderr, "[ %s ] Unknown FS threshold variable name %s\n",
969                     rn, a_varName);
970             return (-1);
971         }
972         Header = FSnameList;
973         srvCount = numFS;
974         hostname = last_fsHost;
975         global_TC = &global_fsThreshCount;
976     } else if (a_type == 2) {   /* cm threshold */
977         for (index = 0; index < NUM_CM_STAT_ENTRIES; index++) {
978             if (strcasecmp(a_varName, cm_varNames[index]) == 0) {
979                 found = 1;
980                 break;
981             }
982         }
983         if (!found) {
984             fprintf(stderr, "[ %s ] Unknown CM threshold variable name %s\n",
985                     rn, a_varName);
986             return (-1);
987         }
988         Header = CMnameList;
989         srvCount = numCM;
990         hostname = last_cmHost;
991         global_TC = &global_cmThreshCount;
992     } else
993         return (-1);
994
995
996
997     /* if the global thresh count is not zero, place this threshold on
998      * all the host entries  */
999
1000     if (*global_TC) {
1001         tmp_host = Header;
1002         for (i = 0; i < srvCount; i++) {
1003             threshP = tmp_host->thresh;
1004             done = 0;
1005             for (j = 0; j < tmp_host->numThresh; j++) {
1006                 if ((threshP->itemName[0] == '\0')
1007                     || (strcasecmp(threshP->itemName, a_varName) == 0)) {
1008                     strncpy(threshP->itemName, a_varName,
1009                             THRESH_VAR_NAME_LEN);
1010                     strncpy(threshP->threshVal, a_value, THRESH_VAR_LEN);
1011                     strcpy(threshP->handler, a_handler);
1012                     threshP->index = index;
1013                     done = 1;
1014                     break;
1015                 }
1016                 threshP++;
1017             }
1018             if (!done) {
1019                 fprintf(stderr, "[ %s ] Could not insert threshold entry",
1020                         rn);
1021                 fprintf(stderr, "for %s in thresh list of host %s \n",
1022                         a_varName, tmp_host->hostName);
1023                 return (-1);
1024             }
1025             tmp_host = tmp_host->next;
1026         }
1027         (*global_TC)--;
1028         return (0);
1029     }
1030
1031     /* it is not a global threshold, insert it in the thresh list of this
1032      * host only. We overwrite the global threshold if it was alread set */
1033
1034     if (*hostname == '\0') {
1035         fprintf(stderr, "[ %s ] Programming error 3\n", rn);
1036         return (-1);
1037     }
1038
1039     /* get the hostEntry that this threshold belongs to */
1040     tmp_host = Header;
1041     found = 0;
1042     for (i = 0; i < srvCount; i++) {
1043         if (strcasecmp(tmp_host->hostName, hostname) == 0) {
1044             found = 1;
1045             break;
1046         }
1047         tmp_host = tmp_host->next;
1048     }
1049     if (!found) {
1050         fprintf(stderr, "[ %s ] Unable to find host %s in %s hostEntry list",
1051                 rn, hostname, (a_type - 1) ? "CM" : "FS");
1052         return (-1);
1053     }
1054
1055     /* put this entry on the thresh list of this host, overwrite global value
1056      * if needed */
1057
1058     threshP = tmp_host->thresh;
1059     done = 0;
1060     for (i = 0; i < tmp_host->numThresh; i++) {
1061         if ((threshP->itemName[0] == '\0')
1062             || (strcasecmp(threshP->itemName, a_varName) == 0)) {
1063             strncpy(threshP->itemName, a_varName, THRESH_VAR_NAME_LEN);
1064             strncpy(threshP->threshVal, a_value, THRESH_VAR_LEN);
1065             strcpy(threshP->handler, a_handler);
1066             threshP->index = index;
1067             done = 1;
1068             break;
1069         }
1070         threshP++;
1071     }
1072
1073     if (!done) {
1074         fprintf(stderr,
1075                 "[ %s ] Unable to insert threshold %s for %s host %s\n", rn,
1076                 a_varName, (a_type - 1) ? "CM" : "FS", tmp_host->hostName);
1077         return (-1);
1078     }
1079
1080     return (0);
1081
1082 }                               /* store_thresholds */
1083
1084
1085 /*-----------------------------------------------------------------------
1086  * parse_showEntry()
1087  *
1088  * Description:
1089  *      This function process a "show" entry in the config file. A "show"
1090  *      entry specifies what statistics the user wants to see. File
1091  *      server and Cache Manager data is divided into sections. Each section
1092  *      is made up of one or more groups. If a group name is specified only
1093  *      those statistics under that group are shown. If a section name is
1094  *      specified all the groups under this section are shown.
1095  *      Data as obtained from the xstat probes is considered to be ordered.
1096  *      This data is mapped to the screen thru fs_Display_map[] and
1097  *      cm_Display_map[]. This routine parses the "show" entry against the
1098  *      section/group names in the [fs/cm]_categories[] array. If there is
1099  *      no match it tries to match it against a variable name in 
1100  *      [fs/cm]_varNames[] array. In each case the corresponding indices to
1101  *      the data is the [fs/cm]_displayInfo[] is recorded. 
1102  *
1103  * Returns:
1104  *      Success: 0
1105  *      Failure: -1 (invalid entry)
1106  *               > -1 (programming error)
1107  *----------------------------------------------------------------------*/
1108
1109 int
1110 parse_showEntry(char *a_line)
1111 {                               /* parse_showEntry */
1112     static char rn[] = "parse_showEntry";
1113     char opcode[CFG_STR_LEN];   /* specifies type of config entry */
1114     char arg1[CFG_STR_LEN];     /* show fs or cm entry ? */
1115     char arg2[CFG_STR_LEN];     /* what we gotta show  */
1116     char arg3[CFG_STR_LEN];     /* junk */
1117     char catName[CFG_STR_LEN];  /* for category names */
1118     int numGroups;              /* number of groups in a section */
1119     int fromIdx;
1120     int toIdx;
1121     int found;
1122     int idx = 0;                /* index to fs_categories[] */
1123     int i;
1124     int j;
1125
1126
1127     if (afsmon_debug) {
1128         fprintf(debugFD, "[ %s ] Called, a_line= %s\n", rn, a_line);
1129         fflush(debugFD);
1130     }
1131     opcode[0] = 0;
1132     arg1[0] = 0;
1133     arg2[0] = 0;
1134     arg3[0] = 0;
1135     sscanf(a_line, "%s %s %s %s", opcode, arg1, arg2, arg3);
1136
1137     if (arg3[0] != '\0') {
1138         fprintf(stderr, "[ %s ] Extraneous characters at end of line\n", rn);
1139         return (-1);
1140     }
1141
1142     if ((strcasecmp(arg1, "fs") != 0) && (strcasecmp(arg1, "cm") != 0)) {
1143         fprintf(stderr,
1144                 "[ %s ] Second argument of \"show\" directive should be \"fs\" or \"cm\" \n",
1145                 rn);
1146         return (-1);
1147     }
1148
1149     /* Each entry can either be a variable name or a section/group name. Variable
1150      * names are listed in xx_varNames[] and section/group names in xx_categories[].
1151      * The section/group names in xx_categiries[] also give the starting/ending
1152      * indices of the variables belonging to that section/group. These indices
1153      * are stored in order in xx_Display_map[] and displayed to the screen in that
1154      * order. */
1155
1156     /* To handle duplicate "show" entries we keep track of what what we have
1157      * already marked to show in the xx_showFlags[] */
1158
1159     if (strcasecmp(arg1, "fs") == 0) {  /* its a File Server entry */
1160
1161         /* mark that we have to show only what the user wants */
1162         fs_showDefault = 0;
1163
1164         /* if it is a section/group name, find it in the fs_categories[] array */
1165
1166         found = 0;
1167         if (strcasestr(arg2, "_section") != (char *)NULL
1168             || strcasestr(arg2, "_group") != (char *)NULL) {
1169             idx = 0;
1170             while (idx < FS_NUM_DATA_CATEGORIES) {
1171                 sscanf(fs_categories[idx], "%s %d %d", catName, &fromIdx,
1172                        &toIdx);
1173                 idx++;
1174                 if (strcasecmp(arg2, catName) == 0) {
1175                     found = 1;
1176                     break;
1177                 }
1178             }
1179
1180             if (!found) {       /* typo in section/group name */
1181                 fprintf(stderr,
1182                         "[ %s ] Could not find section/group name %s\n", rn,
1183                         arg2);
1184                 return (-1);
1185             }
1186         }
1187
1188         /* if it is a group name, read its start/end indices and fill in the
1189          * fs_Display_map[]. */
1190
1191         if (strcasestr(arg2, "_group") != (char *)NULL) {
1192
1193             if (fromIdx < 0 || toIdx < 0 || fromIdx > NUM_FS_STAT_ENTRIES
1194                 || toIdx > NUM_FS_STAT_ENTRIES)
1195                 return (-2);
1196             for (j = fromIdx; j <= toIdx; j++) {
1197                 if (!fs_showFlags[j]) {
1198                     fs_Display_map[fs_DisplayItems_count] = j;
1199                     fs_DisplayItems_count++;
1200                     fs_showFlags[j] = 1;
1201                 }
1202                 if (fs_DisplayItems_count > NUM_FS_STAT_ENTRIES) {
1203                     fprintf(stderr, "[ %s ] fs_DisplayItems_count ovf\n", rn);
1204                     return (-3);
1205                 }
1206             }
1207         } else
1208             /* if it is a section name, get the count of number of groups in it and
1209              * for each group fill in the start/end indices in the fs_Display_map[] */
1210
1211         if (strcasestr(arg2, "_section") != (char *)NULL) {
1212             /* fromIdx is actually the number of groups in thi section */
1213             numGroups = fromIdx;
1214             /* for each group in section */
1215             while (idx < FS_NUM_DATA_CATEGORIES && numGroups) {
1216                 sscanf(fs_categories[idx], "%s %d %d", catName, &fromIdx,
1217                        &toIdx);
1218
1219                 if (strcasestr(catName, "_group") != NULL) {
1220                     if (fromIdx < 0 || toIdx < 0
1221                         || fromIdx > NUM_FS_STAT_ENTRIES
1222                         || toIdx > NUM_FS_STAT_ENTRIES)
1223                         return (-4);
1224                     for (j = fromIdx; j <= toIdx; j++) {
1225                         if (!fs_showFlags[j]) {
1226                             fs_Display_map[fs_DisplayItems_count] = j;
1227                             fs_DisplayItems_count++;
1228                             fs_showFlags[j] = 1;
1229                         }
1230                         if (fs_DisplayItems_count > NUM_FS_STAT_ENTRIES) {
1231                             fprintf(stderr,
1232                                     "[ %s ] fs_DisplayItems_count ovf\n", rn);
1233                             return (-5);
1234                         }
1235                     }
1236                 } else {
1237                     fprintf(stderr, "[ %s ] Error parsing groups for %s\n",
1238                             rn, arg2);
1239                     return (-6);
1240                 }
1241                 idx++;
1242                 numGroups--;
1243             }                   /* for each group in section */
1244
1245
1246         } else {                /* it is a variable name */
1247
1248             for (i = 0; i < NUM_FS_STAT_ENTRIES; i++) {
1249                 if (strcasecmp(arg2, fs_varNames[i]) == 0) {
1250                     if (!fs_showFlags[i]) {
1251                         fs_Display_map[fs_DisplayItems_count] = i;
1252                         fs_DisplayItems_count++;
1253                         fs_showFlags[i] = 1;
1254                     }
1255                     if (fs_DisplayItems_count >= NUM_FS_STAT_ENTRIES) {
1256                         fprintf(stderr, "[ %s ] fs_DisplayItems_count ovf\n",
1257                                 rn);
1258                         return (-25);
1259                     }
1260                     found = 1;
1261                 }
1262             }
1263             if (!found) {       /* typo in section/group name */
1264                 fprintf(stderr, "[ %s ] Could not find variable name %s\n",
1265                         rn, arg2);
1266                 return (-1);
1267             }
1268         }                       /* its a variable name */
1269
1270     }
1271
1272     /* it is an fs entry */
1273     if (strcasecmp(arg1, "cm") == 0) {  /* its a Cache Manager entry */
1274
1275
1276         /* mark that we have to show only what the user wants */
1277         cm_showDefault = 0;
1278
1279         /* if it is a section/group name, find it in the cm_categories[] array */
1280
1281         found = 0;
1282         if (strcasestr(arg2, "_section") != (char *)NULL
1283             || strcasestr(arg2, "_group") != (char *)NULL) {
1284             idx = 0;
1285             while (idx < CM_NUM_DATA_CATEGORIES) {
1286                 sscanf(cm_categories[idx], "%s %d %d", catName, &fromIdx,
1287                        &toIdx);
1288                 idx++;
1289                 if (strcasecmp(arg2, catName) == 0) {
1290                     found = 1;
1291                     break;
1292                 }
1293             }
1294
1295             if (!found) {       /* typo in section/group name */
1296                 fprintf(stderr,
1297                         "[ %s ] Could not find section/group name %s\n", rn,
1298                         arg2);
1299                 return (-1);
1300             }
1301         }
1302
1303         /* if it is a group name, read its start/end indices and fill in the
1304          * cm_Display_map[]. */
1305
1306         if (strcasestr(arg2, "_group") != (char *)NULL) {
1307
1308             if (fromIdx < 0 || toIdx < 0 || fromIdx > NUM_CM_STAT_ENTRIES
1309                 || toIdx > NUM_CM_STAT_ENTRIES)
1310                 return (-10);
1311             for (j = fromIdx; j <= toIdx; j++) {
1312                 if (!cm_showFlags[j]) {
1313                     cm_Display_map[cm_DisplayItems_count] = j;
1314                     cm_DisplayItems_count++;
1315                     cm_showFlags[j] = 1;
1316                 }
1317                 if (cm_DisplayItems_count > NUM_CM_STAT_ENTRIES) {
1318                     fprintf(stderr, "[ %s ] cm_DisplayItems_count ovf\n", rn);
1319                     return (-11);
1320                 }
1321             }
1322         } else
1323             /* if it is a section name, get the count of number of groups in it and
1324              * for each group fill in the start/end indices in the cm_Display_map[] */
1325
1326         if (strcasestr(arg2, "_section") != (char *)NULL) {
1327             /* fromIdx is actually the number of groups in thi section */
1328             numGroups = fromIdx;
1329             /* for each group in section */
1330             while (idx < CM_NUM_DATA_CATEGORIES && numGroups) {
1331                 sscanf(cm_categories[idx], "%s %d %d", catName, &fromIdx,
1332                        &toIdx);
1333
1334                 if (strcasestr(catName, "_group") != NULL) {
1335                     if (fromIdx < 0 || toIdx < 0
1336                         || fromIdx > NUM_CM_STAT_ENTRIES
1337                         || toIdx > NUM_CM_STAT_ENTRIES)
1338                         return (-12);
1339                     for (j = fromIdx; j <= toIdx; j++) {
1340                         if (!cm_showFlags[j]) {
1341                             cm_Display_map[cm_DisplayItems_count] = j;
1342                             cm_DisplayItems_count++;
1343                             cm_showFlags[j] = 1;
1344                         }
1345                         if (cm_DisplayItems_count > NUM_CM_STAT_ENTRIES) {
1346                             fprintf(stderr,
1347                                     "[ %s ] cm_DisplayItems_count ovf\n", rn);
1348                             return (-13);
1349                         }
1350                     }
1351                 } else {
1352                     fprintf(stderr, "[ %s ] Error parsing groups for %s\n",
1353                             rn, arg2);
1354                     return (-15);
1355                 }
1356                 idx++;
1357                 numGroups--;
1358             }                   /* for each group in section */
1359         } else {                /* it is a variable name */
1360
1361             for (i = 0; i < NUM_CM_STAT_ENTRIES; i++) {
1362                 if (strcasecmp(arg2, cm_varNames[i]) == 0) {
1363                     if (!cm_showFlags[i]) {
1364                         cm_Display_map[cm_DisplayItems_count] = i;
1365                         cm_DisplayItems_count++;
1366                         cm_showFlags[i] = 1;
1367                     }
1368                     if (cm_DisplayItems_count >= NUM_CM_STAT_ENTRIES) {
1369                         fprintf(stderr, "[ %s ] cm_DisplayItems_count ovf\n",
1370                                 rn);
1371                         return (-20);
1372                     }
1373                     found = 1;
1374                 }
1375             }
1376             if (!found) {       /* typo in section/group name */
1377                 fprintf(stderr, "[ %s ] Could not find variable name %s\n",
1378                         rn, arg2);
1379                 return (-1);
1380             }
1381         }                       /* its a variable name */
1382
1383     }
1384     /* it is an cm entry */
1385     return (0);
1386 }                               /* parse_showEntry */
1387
1388
1389 /*-----------------------------------------------------------------------
1390  * process_config_file()
1391  *
1392  * Description:
1393  *      Parse config file entries in two passes. In the first pass: 
1394  *              - the syntax of all the entries is checked
1395  *              - host names are noted and the FSnamesList and CMnamesList 
1396  *                constructed. 
1397  *              - a count of the global thresholds and local thresholds of 
1398  *                each host are counted. 
1399  *              - "show" entries are processed.
1400  *      In the second pass:
1401  *              - thresholds are stored 
1402  *
1403  * Returns:
1404  *      Success: 0
1405  *      Failure: Exits afsmonitor showing error and line.
1406  *----------------------------------------------------------------------*/
1407
1408 int
1409 process_config_file(char *a_config_filename)
1410 {                               /* process_config_file() */
1411     static char rn[] = "process_config_file";   /* routine name */
1412     FILE *configFD;             /* config file descriptor */
1413     char line[4 * CFG_STR_LEN]; /* a line of config file */
1414     char opcode[CFG_STR_LEN];   /* specifies type of config entry */
1415     char arg1[CFG_STR_LEN];     /* hostname or qualifier (fs/cm?)  */
1416     char arg2[CFG_STR_LEN];     /* threshold variable */
1417     char arg3[CFG_STR_LEN];     /* threshold value */
1418     char arg4[CFG_STR_LEN];     /* user's handler  */
1419     struct afsmon_hostEntry *curr_host;
1420     struct hostent *he;         /* hostentry to resolve host name */
1421     char *handlerPtr;           /* ptr to pass theresh handler string */
1422     int code = 0;               /* error code */
1423     int linenum = 0;            /* config file line number */
1424     int threshCount;            /* count of thresholds for each server */
1425     int error_in_config;        /* syntax errors in config file  ?? */
1426     int i;
1427     int numBytes;
1428
1429     if (afsmon_debug) {
1430         fprintf(debugFD, "[ %s ] Called, a_config_filename= %s\n", rn,
1431                 a_config_filename);
1432         fflush(debugFD);
1433     }
1434
1435     /* open config file */
1436
1437     configFD = fopen(a_config_filename, "r");
1438     if (configFD == (FILE *) 0) {
1439         fprintf(stderr, "Failed to open config file %s \n",
1440                 a_config_filename);
1441         if (afsmon_debug) {
1442             fprintf(debugFD, "[ %s ] Failed to open config file %s \n", rn,
1443                     a_config_filename);
1444         }
1445         afsmon_Exit(5);
1446     }
1447
1448
1449     /* parse config file */
1450
1451     /* We process the config file in two passes. In the first pass we check
1452      * for correct syntax and for valid entries and also keep count of the
1453      * number of servers and thresholds to monitor. This the data strctures
1454      * can be arrays instead of link lists since we would know their sizes. */
1455
1456     /* First Pass */
1457
1458     numFS = 0;
1459     numCM = 0;
1460     threshCount = 0;
1461     error_in_config = 0;        /* flag to note if config file has syntax errors */
1462
1463     while ((fgets(line, CFG_STR_LEN, configFD)) != NULL) {
1464         opcode[0] = 0;
1465         arg1[0] = 0;
1466         arg2[0] = 0;
1467         arg3[0] = 0;
1468         arg4[0] = 0;
1469         sscanf(line, "%s %s %s %s %s", opcode, arg1, arg2, arg3, arg4);
1470         linenum++;
1471         /* skip blank lines and comment lines */
1472         if ((strlen(opcode) == 0) || line[0] == '#')
1473             continue;
1474
1475         if ((strcasecmp(opcode, "fs") == 0)
1476             || (strcasecmp(opcode, "cm")) == 0) {
1477             code = parse_hostEntry(line);
1478         } else if ((strcasecmp(opcode, "thresh")) == 0) {
1479             code = parse_threshEntry(line);
1480         } else if ((strcasecmp(opcode, "show")) == 0) {
1481             code = parse_showEntry(line);
1482         } else {
1483             fprintf(stderr, "[ %s ] Unknown opcode %s\n", rn, opcode);
1484             code = 1;
1485         }
1486
1487         if (code) {
1488             fprintf(stderr, "[ %s ] Error in line:\n %d: %s\n", rn, linenum,
1489                     line);
1490             error_in_config = 1;
1491         }
1492     }
1493
1494     if (error_in_config)
1495         afsmon_Exit(10);
1496
1497     if (afsmon_debug) {
1498         fprintf(debugFD, "Global FS thresholds count = %d\n",
1499                 global_fsThreshCount);
1500         fprintf(debugFD, "Global CM thresholds count = %d\n",
1501                 global_cmThreshCount);
1502         fflush(debugFD);
1503     }
1504
1505     /* the threshold count of all hosts in increased by 1 for each global
1506      * threshold. If one of the hosts has a local threshold for the same 
1507      * variable it would end up being counted twice. whats a few bytes of memory
1508      * wasted anyway ? */
1509
1510     if (global_fsThreshCount) {
1511         curr_host = FSnameList;
1512         for (i = 0; i < numFS; i++) {
1513             curr_host->numThresh += global_fsThreshCount;
1514             curr_host = curr_host->next;
1515         }
1516     }
1517     if (global_cmThreshCount) {
1518         curr_host = CMnameList;
1519         for (i = 0; i < numCM; i++) {
1520             curr_host->numThresh += global_cmThreshCount;
1521             curr_host = curr_host->next;
1522         }
1523     }
1524
1525
1526     /* make sure we have something to monitor */
1527     if (numFS == 0 && numCM == 0) {
1528         fprintf(stderr,
1529                 "\nConfig file must specify atleast one File Server or Cache Manager host to monitor.\n");
1530         fclose(configFD);
1531         afsmon_Exit(15);
1532     }
1533
1534     /* Second Pass */
1535
1536     fseek(configFD, 0, 0);      /* seek to the beginning */
1537
1538
1539     /* allocate memory for threshold lists */
1540     curr_host = FSnameList;
1541     for (i = 0; i < numFS; i++) {
1542         if (curr_host->hostName[0] == '\0') {
1543             fprintf(stderr, "[ %s ] Programming error 4\n", rn);
1544             afsmon_Exit(20);
1545         }
1546         if (curr_host->numThresh) {
1547             numBytes = curr_host->numThresh * sizeof(struct Threshold);
1548             curr_host->thresh = (struct Threshold *)malloc(numBytes);
1549             if (curr_host->thresh == NULL) {
1550                 fprintf(stderr, "[ %s ] Memory Allocation error 1", rn);
1551                 afsmon_Exit(25);
1552             }
1553             memset(curr_host->thresh, 0, numBytes);
1554         }
1555         curr_host = curr_host->next;;
1556     }
1557
1558     curr_host = CMnameList;
1559     for (i = 0; i < numCM; i++) {
1560         if (curr_host->hostName[0] == '\0') {
1561             fprintf(stderr, "[ %s ] Programming error 5\n", rn);
1562             afsmon_Exit(30);
1563         }
1564         if (curr_host->numThresh) {
1565             numBytes = curr_host->numThresh * sizeof(struct Threshold);
1566             curr_host->thresh = (struct Threshold *)malloc(numBytes);
1567             if (curr_host->thresh == NULL) {
1568                 fprintf(stderr, "[ %s ] Memory Allocation error 2", rn);
1569                 afsmon_Exit(35);
1570             }
1571             memset(curr_host->thresh, 0, numBytes);
1572         }
1573         curr_host = curr_host->next;;
1574     }
1575
1576
1577     opcode[0] = 0;
1578     arg1[0] = 0;
1579     arg2[0] = 0;
1580     arg3[0] = 0;
1581     arg4[0] = 0;
1582     last_fsHost[0] = '\0';
1583     last_cmHost[0] = '\0';
1584     linenum = 0;
1585     while ((fgets(line, CFG_STR_LEN, configFD)) != NULL) {
1586         opcode[0] = 0;
1587         arg1[0] = 0;
1588         arg2[0] = 0;
1589         arg3[0] = 0;
1590         arg4[0] = 0;
1591         sscanf(line, "%s %s %s %s %s", opcode, arg1, arg2, arg3, arg4);
1592         linenum++;
1593
1594         /* if we have a host entry, remember the host name */
1595         if (strcasecmp(opcode, "fs") == 0) {
1596             he = GetHostByName(arg1);
1597             strncpy(last_fsHost, he->h_name, HOST_NAME_LEN);
1598         } else if (strcasecmp(opcode, "cm") == 0) {
1599             he = GetHostByName(arg1);
1600             strncpy(last_cmHost, he->h_name, HOST_NAME_LEN);
1601         } else if (strcasecmp(opcode, "thresh") == 0) {
1602             /* if we have a threshold handler it may have arguments
1603              * and the sscanf() above would not get them, so do the 
1604              * following */
1605             if (strlen(arg4)) {
1606                 handlerPtr = line;
1607                 /* now skip over 4 words - this is done by first
1608                  * skipping leading blanks then skipping a word */
1609                 for (i = 0; i < 4; i++) {
1610                     while (isspace(*handlerPtr))
1611                         handlerPtr++;
1612                     while (!isspace(*handlerPtr))
1613                         handlerPtr++;
1614                 }
1615                 while (isspace(*handlerPtr))
1616                     handlerPtr++;
1617                 /* we how have a pointer to the start of the handler
1618                  * name & args */
1619             } else
1620                 handlerPtr = arg4;      /* empty string */
1621
1622
1623             if (strcasecmp(arg1, "fs") == 0)
1624                 code = store_threshold(1,       /* 1 = fs */
1625                                        arg2, arg3, handlerPtr);
1626
1627             else if (strcasecmp(arg1, "cm") == 0)
1628                 code = store_threshold(2,       /* 2 = fs */
1629                                        arg2, arg3, handlerPtr);
1630
1631             else {
1632                 fprintf(stderr, "[ %s ] Programming error 6\n", rn);
1633                 afsmon_Exit(40);
1634             }
1635             if (code) {
1636                 fprintf(stderr, "[ %s ] Failed to store threshold\n", rn);
1637                 fprintf(stderr, "[ %s ] Error processing line:\n%d: %s", rn,
1638                         linenum, line);
1639                 afsmon_Exit(45);
1640             }
1641         }
1642     }
1643
1644
1645     fclose(configFD);
1646     return (0);
1647 }
1648
1649 /*-----------------------------------------------------------------------
1650  * Print_FS_CB
1651  *
1652  * Description:
1653  *      Debug routine.
1654  *      Print the File Server circular buffer.
1655  *
1656  * Returns:
1657  *      Nothing.
1658  *----------------------------------------------------------------------*/
1659
1660 void
1661 Print_FS_CB(void)
1662 {                               /* Print_FS_CB() */
1663
1664     struct afsmon_fs_Results_list *fslist;
1665     int i;
1666     int j;
1667     int k;
1668
1669     /* print valid info in the fs CB */
1670
1671     if (afsmon_debug) {
1672         fprintf(debugFD,
1673                 "==================== FS Buffer ========================\n");
1674         fprintf(debugFD, "afsmon_fs_curr_CBindex = %d\n",
1675                 afsmon_fs_curr_CBindex);
1676         fprintf(debugFD, "afsmon_fs_curr_probeNum = %d\n\n",
1677                 afsmon_fs_curr_probeNum);
1678
1679         for (i = 0; i < num_bufSlots; i++) {
1680             fprintf(debugFD, "\t--------- slot %d ----------\n", i);
1681             fslist = afsmon_fs_ResultsCB[i].list;
1682             j = 0;
1683             while (j < numFS) {
1684                 for (k = 0; k < MAX_NUM_FS_COLLECTIONS; k++) {
1685                     if (!(fslist->empty[k])) {
1686                         fprintf(debugFD, "\t %d) probeNum = %d host = %s cn = %d",
1687                                 j,
1688                                 fslist->fsResults[k]->probeNum,
1689                                 fslist->fsResults[k]->connP->hostName,
1690                                 fslist->fsResults[k]->collectionNumber);
1691                         if (fslist->fsResults[k]->probeOK)
1692                             fprintf(debugFD, " NOTOK\n");
1693                         else
1694                             fprintf(debugFD, " OK\n");
1695                     } else
1696                         fprintf(debugFD, "\t %d) -- empty --\n", j);
1697                 }
1698                 fslist = fslist->next;
1699                 j++;
1700             }
1701             if (fslist != (struct afsmon_fs_Results_list *)0)
1702                 fprintf(debugFD, "dangling last next ptr fs CB\n");
1703         }
1704     }
1705 }                               /* Print_FS_CB() */
1706
1707 /*-----------------------------------------------------------------------
1708  * save_FS_results_inCB()
1709  *
1710  * Description:
1711  *      Saves the results of the latest FS probe in the fs circular
1712  *      buffers. If the current probe cycle is in progress the contents
1713  *      of xstat_fs_Results are copied to the end of the list of results
1714  *      in the current slot (pointed to by afsmon_fs_curr_CBindex). If
1715  *      a new probe cycle has started the next slot in the circular buffer
1716  *      is initialized and the results copied. Note that the Rx related
1717  *      information available in xstat_fs_Results is not copied.
1718  *
1719  * Returns:
1720  *      Success: 0
1721  *      Failure: Exits afsmonitor.
1722  *----------------------------------------------------------------------*/
1723 int
1724 save_FS_results_inCB(int a_newProbeCycle)       /* start of a new probe cycle ? */
1725 {                               /* save_FS_results_inCB() */
1726     static char rn[] = "save_FS_results_inCB";  /* routine name */
1727     struct afsmon_fs_Results_list *tmp_fslist_item;     /* temp fs list item */
1728     struct xstat_fs_ProbeResults *tmp_fsPR;     /* temp ptr */
1729     int i;
1730     int index;
1731
1732     if (afsmon_debug) {
1733         fprintf(debugFD, "[ %s ] Called, a_newProbeCycle= %d\n", rn,
1734                 a_newProbeCycle);
1735         fflush(debugFD);
1736     }
1737
1738     switch (xstat_fs_Results.collectionNumber) {
1739     case AFS_XSTATSCOLL_FULL_PERF_INFO:
1740         index = 0;
1741         break;
1742     case AFS_XSTATSCOLL_CBSTATS:
1743         index = 1;
1744     default:
1745         if (index < 0) {
1746             fprintf(stderr, "[ %s ] collection number %d is out of range.\n",
1747                     rn, xstat_fs_Results.collectionNumber);
1748             afsmon_Exit(51);
1749         }
1750     }
1751
1752     /* If a new probe cycle started, mark the list in the current buffer
1753      * slot empty for resuse. Note that afsmon_fs_curr_CBindex was appropriately
1754      * incremented in afsmon_FS_Handler() */
1755
1756     if (a_newProbeCycle) {
1757         tmp_fslist_item = afsmon_fs_ResultsCB[afsmon_fs_curr_CBindex].list;
1758         for (i = 0; i < numFS; i++) {
1759             tmp_fslist_item->empty[index] = 1;
1760             tmp_fslist_item = tmp_fslist_item->next;
1761         }
1762     }
1763
1764     /* locate last unused item in list */
1765     tmp_fslist_item = afsmon_fs_ResultsCB[afsmon_fs_curr_CBindex].list;
1766     for (i = 0; i < numFS; i++) {
1767         if (tmp_fslist_item->empty[index])
1768             break;
1769         tmp_fslist_item = tmp_fslist_item->next;
1770     }
1771
1772     /* if we could not find one we have an inconsistent list */
1773     if (!tmp_fslist_item->empty[index]) {
1774         fprintf(stderr,
1775                 "[ %s ] list inconsistency 1. unable to find an empty slot to store results of probenum %d of %s\n",
1776                 rn, xstat_fs_Results.probeNum,
1777                 xstat_fs_Results.connP->hostName);
1778         afsmon_Exit(50);
1779     }
1780
1781     tmp_fsPR = tmp_fslist_item->fsResults[index];
1782
1783     /* copy hostname and probe number and probe time and probe status.
1784      * if the probe failed return now */
1785
1786     memcpy(tmp_fsPR->connP->hostName, xstat_fs_Results.connP->hostName,
1787            sizeof(xstat_fs_Results.connP->hostName));
1788     tmp_fsPR->probeNum = xstat_fs_Results.probeNum;
1789     tmp_fsPR->probeTime = xstat_fs_Results.probeTime;
1790     tmp_fsPR->probeOK = xstat_fs_Results.probeOK;
1791     if (xstat_fs_Results.probeOK) {     /* probeOK = 1 => notOK */
1792         /* we have a nonempty results structure so mark the list item used */
1793         tmp_fslist_item->empty[index] = 0;
1794         return (0);
1795     }
1796
1797     /* copy connection information */
1798     memcpy(&(tmp_fsPR->connP->skt), &(xstat_fs_Results.connP->skt),
1799            sizeof(struct sockaddr_in));
1800
1801     memcpy(tmp_fsPR->connP->hostName, xstat_fs_Results.connP->hostName,
1802            sizeof(xstat_fs_Results.connP->hostName));
1803     tmp_fsPR->collectionNumber = xstat_fs_Results.collectionNumber;
1804
1805     /* copy the probe data information */
1806     tmp_fsPR->data.AFS_CollData_len =
1807         min(xstat_fs_Results.data.AFS_CollData_len,
1808             afsmon_fs_results_length[index]);
1809     memcpy(tmp_fsPR->data.AFS_CollData_val,
1810            xstat_fs_Results.data.AFS_CollData_val,
1811            tmp_fsPR->data.AFS_CollData_len * sizeof(afs_int32));
1812
1813
1814     /* we have a valid results structure so mark the list item used */
1815     tmp_fslist_item->empty[index] = 0;
1816
1817     /* Print the fs circular buffer */
1818     Print_FS_CB();
1819
1820     return (0);
1821 }                               /* save_FS_results_inCB() */
1822
1823
1824 /*-----------------------------------------------------------------------
1825  * fs_Results_ltoa()
1826  *
1827  * Description:
1828  *      The results of xstat probes are stored in a string format in 
1829  *      the arrays curr_fsData and prev_fsData. The information stored in
1830  *      prev_fsData is copied to the screen. 
1831  *      This function converts xstat FS results from longs to strings and 
1832  *      place them in the given buffer (a pointer to an item in curr_fsData).
1833  *      When a probe cycle completes, curr_fsData is copied to prev_fsData
1834  *      in afsmon_FS_Hnadler().
1835  *
1836  * Returns:
1837  *      Always returns 0.
1838  *----------------------------------------------------------------------*/
1839
1840 int
1841 fs_Results_ltoa(struct fs_Display_Data *a_fsData,       /* target buffer */
1842                 struct xstat_fs_ProbeResults *a_fsResults)      /* ptr to xstat fs Results */
1843 {                               /* fs_Results_ltoa */
1844
1845     static char rn[] = "fs_Results_ltoa";       /* routine name */
1846
1847     if (afsmon_debug) {
1848         fprintf(debugFD, "[ %s ] Called, a_fsData= %p, a_fsResults= %p\n", rn,
1849                 a_fsData, a_fsResults);
1850         fflush(debugFD);
1851     }
1852
1853     switch (a_fsResults->collectionNumber) {
1854     case AFS_XSTATSCOLL_FULL_PERF_INFO:
1855         fs_FullPerfs_ltoa(a_fsData, a_fsResults);
1856         break;
1857     case AFS_XSTATSCOLL_CBSTATS:
1858         fs_CallBackStats_ltoa(a_fsData, a_fsResults);
1859         break;
1860     default:
1861         if (afsmon_debug) {
1862             fprintf(debugFD, "[ %s ] Unexpected collection id %d\n",
1863                     rn, a_fsResults->collectionNumber);
1864         }
1865     }
1866
1867     return (0);
1868 }                               /* fs_Results_ltoa */
1869
1870 /*-----------------------------------------------------------------------
1871  * fs_FullPerfs_ltoa()
1872  *
1873  * Description:
1874  *      Convert the full perf xstat collection from int32s to strings.
1875  *
1876  * Returns:
1877  *      Always returns 0.
1878  *----------------------------------------------------------------------*/
1879 static int
1880 fs_FullPerfs_ltoa(struct fs_Display_Data *a_fsData,
1881                 struct xstat_fs_ProbeResults *a_fsResults)
1882 {
1883     afs_int32 *srcbuf;
1884     struct fs_stats_FullPerfStats *fullPerfP;
1885     int idx;
1886     int i, j;
1887     afs_int32 *tmpbuf;
1888     afs_int32 numInt32s;
1889
1890     fullPerfP = (struct fs_stats_FullPerfStats *)
1891         (a_fsResults->data.AFS_CollData_val);
1892
1893     /* there are two parts to the xstat FS statistics 
1894      * - fullPerfP->overall which give the overall performance statistics, and
1895      * - fullPerfP->det which gives detailed info about file server operation
1896      * execution times */
1897
1898     /*
1899      * Unfortunately, the full perf stats contain timeval structures which
1900      * do not have the same size everywhere. Avoid displaying gargbage,
1901      * but at least try to show the overall stats.
1902      */
1903     numInt32s = a_fsResults->data.AFS_CollData_len;
1904     if (numInt32s !=
1905         (sizeof(struct fs_stats_FullPerfStats) / sizeof(afs_int32))) {
1906         srcbuf = a_fsResults->data.AFS_CollData_val;
1907         for (i = 0; i < NUM_FS_STAT_ENTRIES; i++) {
1908             if (i < numInt32s && i < NUM_XSTAT_FS_AFS_PERFSTATS_LONGS) {
1909                 sprintf(a_fsData->data[i], "%d", srcbuf[i]);
1910             } else {
1911                 sprintf(a_fsData->data[i], "%s", "--");
1912             }
1913         }
1914         return 0;
1915     }
1916
1917     /* copy overall performance statistics */
1918     srcbuf = (afs_int32 *) & (fullPerfP->overall);
1919     idx = 0;
1920     for (i = 0; i < NUM_XSTAT_FS_AFS_PERFSTATS_LONGS; i++) {
1921         sprintf(a_fsData->data[idx], "%d", *srcbuf);
1922         idx++;
1923         srcbuf++;
1924     }
1925
1926     /* copy epoch */
1927     srcbuf = (afs_int32 *) & (fullPerfP->det.epoch);
1928     sprintf(a_fsData->data[idx], "%d", *srcbuf);        /* epoch */
1929     idx++;
1930
1931     /* copy fs operation timing */
1932
1933     srcbuf = (afs_int32 *) (fullPerfP->det.rpcOpTimes);
1934
1935     for (i = 0; i < FS_STATS_NUM_RPC_OPS; i++) {
1936         sprintf(a_fsData->data[idx], "%d", *srcbuf);    /* numOps */
1937         idx++;
1938         srcbuf++;
1939         sprintf(a_fsData->data[idx], "%d", *srcbuf);    /* numSuccesses */
1940         idx++;
1941         srcbuf++;
1942         tmpbuf = srcbuf++;      /* sum time */
1943         sprintf(a_fsData->data[idx], "%d.%06d", *tmpbuf, *srcbuf);
1944         idx++;
1945         srcbuf++;
1946         tmpbuf = srcbuf++;      /* sqr time */
1947         sprintf(a_fsData->data[idx], "%d.%06d", *tmpbuf, *srcbuf);
1948         idx++;
1949         srcbuf++;
1950         tmpbuf = srcbuf++;      /* min time */
1951         sprintf(a_fsData->data[idx], "%d.%06d", *tmpbuf, *srcbuf);
1952         idx++;
1953         srcbuf++;
1954         tmpbuf = srcbuf++;      /* max time */
1955         sprintf(a_fsData->data[idx], "%d.%06d", *tmpbuf, *srcbuf);
1956         idx++;
1957         srcbuf++;
1958     }
1959
1960     /* copy fs transfer timings */
1961
1962     srcbuf = (afs_int32 *) (fullPerfP->det.xferOpTimes);
1963     for (i = 0; i < FS_STATS_NUM_XFER_OPS; i++) {
1964         sprintf(a_fsData->data[idx], "%d", *srcbuf);    /* numOps */
1965         idx++;
1966         srcbuf++;
1967         sprintf(a_fsData->data[idx], "%d", *srcbuf);    /* numSuccesses */
1968         idx++;
1969         srcbuf++;
1970         tmpbuf = srcbuf++;      /* sum time */
1971         sprintf(a_fsData->data[idx], "%d.%06d", *tmpbuf, *srcbuf);
1972         idx++;
1973         srcbuf++;
1974         tmpbuf = srcbuf++;      /* sqr time */
1975         sprintf(a_fsData->data[idx], "%d.%06d", *tmpbuf, *srcbuf);
1976         idx++;
1977         srcbuf++;
1978         tmpbuf = srcbuf++;      /* min time */
1979         sprintf(a_fsData->data[idx], "%d.%06d", *tmpbuf, *srcbuf);
1980         idx++;
1981         srcbuf++;
1982         tmpbuf = srcbuf++;      /* max time */
1983         sprintf(a_fsData->data[idx], "%d.%06d", *tmpbuf, *srcbuf);
1984         idx++;
1985         srcbuf++;
1986         sprintf(a_fsData->data[idx], "%d", *srcbuf);    /* sum bytes */
1987         idx++;
1988         srcbuf++;
1989         sprintf(a_fsData->data[idx], "%d", *srcbuf);    /* min bytes */
1990         idx++;
1991         srcbuf++;
1992         sprintf(a_fsData->data[idx], "%d", *srcbuf);    /* max bytes */
1993         idx++;
1994         srcbuf++;
1995         for (j = 0; j < FS_STATS_NUM_XFER_BUCKETS; j++) {
1996             sprintf(a_fsData->data[idx], "%d", *srcbuf);        /* bucket[j] */
1997             idx++;
1998             srcbuf++;
1999         }
2000     }
2001
2002     return (0);
2003 }
2004
2005 /*-----------------------------------------------------------------------
2006  * fs_CallBackStats_ltoa()
2007  *
2008  * Description:
2009  *      Convert the callback counter xstat collection from
2010  *      int32s to strings.
2011  *
2012  * Returns:
2013  *      Always returns 0.
2014  *----------------------------------------------------------------------*/
2015
2016 static int
2017 fs_CallBackStats_ltoa(struct fs_Display_Data *a_fsData,
2018                       struct xstat_fs_ProbeResults *a_fsResults)
2019 {
2020     int idx;
2021     int i;
2022     int len = a_fsResults->data.AFS_CollData_len;
2023     afs_int32 *val = a_fsResults->data.AFS_CollData_val;
2024
2025     /* place callback stats after the full perf stats */
2026     idx = NUM_FS_FULLPERF_ENTRIES;
2027     for (i=0; i < len && i < NUM_FS_CB_ENTRIES; i++) {
2028         sprintf(a_fsData->data[idx++], "%u", val[i]);
2029     }
2030     return 0;
2031 }
2032
2033 /*-----------------------------------------------------------------------
2034  * execute_thresh_handler()
2035  *
2036  * Description:
2037  *      Execute a threshold handler. An agrv[] array of pointers is 
2038  *      constructed from the given data. A child process is forked 
2039  *      which immediately calls afsmon_Exit() with indication that a
2040  *      threshold handler is to be exec'ed insted of exiting. 
2041  *
2042  * Returns:
2043  *      Success: 0
2044  *      Failure: Afsmonitor exits if threshold handler has more than 20 args.
2045  *----------------------------------------------------------------------*/
2046
2047 int
2048 execute_thresh_handler(char *a_handler,         /* ptr to handler function + args */
2049                        char *a_hostName,        /* host name for which threshold crossed */
2050                        int a_hostType,          /* fs or cm ? */
2051                        char *a_threshName,      /* threshold variable name */
2052                        char *a_threshValue,     /* threshold value */
2053                        char *a_actValue)        /* actual value */
2054 {                               /* execute_thresh_handler */
2055
2056     static char rn[] = "execute_thresh_handler";
2057     char fileName[256];         /* file name to execute */
2058     int i;
2059     char *ch;
2060     int code;
2061     int argNum;
2062     int anotherArg;             /* boolean used to flag if another arg is available */
2063
2064     if (afsmon_debug) {
2065         fprintf(debugFD,
2066                 "[ %s ] Called, a_handler= %s, a_hostName= %s, a_hostType= %d, a_threshName= %s, a_threshValue= %s, a_actValue= %s\n",
2067                 rn, a_handler, a_hostName, a_hostType, a_threshName,
2068                 a_threshValue, a_actValue);
2069         fflush(debugFD);
2070     }
2071
2072
2073     /* get the filename to execute - the first argument */
2074     sscanf(a_handler, "%s", fileName);
2075
2076     /* construct the contents of *argv[] */
2077
2078     strncpy(fsHandler_args[0], fileName, 256);
2079     strncpy(fsHandler_args[1], a_hostName, HOST_NAME_LEN);
2080     if (a_hostType == FS)
2081         strcpy(fsHandler_args[2], "fs");
2082     else
2083         strcpy(fsHandler_args[2], "cm");
2084     strncpy(fsHandler_args[3], a_threshName, THRESH_VAR_NAME_LEN);
2085     strncpy(fsHandler_args[4], a_threshValue, THRESH_VAR_LEN);
2086     strncpy(fsHandler_args[5], a_actValue, THRESH_VAR_LEN);
2087
2088
2089     argNum = 6;
2090     anotherArg = 1;
2091     ch = a_handler;
2092
2093     /* we have already extracted the file name so skip to the 1st arg */
2094     while (isspace(*ch))        /* leading blanks */
2095         ch++;
2096     while (!isspace(*ch) && *ch != '\0')        /* handler filename */
2097         ch++;
2098
2099     while (*ch != '\0') {
2100         if (isspace(*ch)) {
2101             anotherArg = 1;
2102         } else if (anotherArg) {
2103             anotherArg = 0;
2104             sscanf(ch, "%s", fsHandler_args[argNum]);
2105             argNum++;
2106         }
2107         ch++;
2108         if (argNum >= 20) {
2109             sprintf(errMsg,
2110                     "Threshold handlers cannot have more than 20 arguments\n");
2111             afsmon_Exit(55);
2112         }
2113
2114     }
2115
2116     fsHandler_argv[argNum] = NULL;
2117     for (i = 0; i < argNum; i++)
2118         fsHandler_argv[i] = fsHandler_args[i];
2119
2120
2121     /* exec the threshold handler */
2122
2123     if (fork() == 0) {
2124         exec_fsThreshHandler = 1;
2125         code = afsmon_Exit(60);
2126     }
2127
2128     return (0);
2129 }                               /* execute_thresh_handler */
2130
2131
2132
2133 /*-----------------------------------------------------------------------
2134  * check_fs_thresholds()
2135  *
2136  * Description:
2137  *      Checks the thresholds and sets the overflow flag. Recall that the
2138  *      thresholds for each host are stored in the hostEntry lists
2139  *      [fs/cm]nameList arrays. The probe results are passed to this 
2140  *      function in the display-ready format - ie., as strings. Though
2141  *      this looks stupid the overhead incurred in converting the strings
2142  *      back to floats and comparing them is insignificant and 
2143  *      programming is easier this way. 
2144  *      The threshold flags are a part of the display structures
2145  *      curr_[fs/cm]Data.
2146  *
2147  * Returns:
2148  *      0
2149  *----------------------------------------------------------------------*/
2150
2151 int
2152 check_fs_thresholds(struct afsmon_hostEntry *a_hostEntry, /* ptr to hostEntry */
2153                     struct fs_Display_Data *a_Data)       /* ptr to fs data to be displayed */
2154 {                               /* check_fs_thresholds */
2155
2156     static char rn[] = "check_fs_thresholds";
2157     struct Threshold *threshP;
2158     double tValue;              /* threshold value */
2159     double pValue;              /* probe value */
2160     int i;
2161     int idx;
2162     int count;                  /* number of thresholds exceeded */
2163
2164     if (afsmon_debug) {
2165         fprintf(debugFD, "[ %s ] Called, a_hostEntry= %p, a_Data= %p\n", rn,
2166                 a_hostEntry, a_Data);
2167         fflush(debugFD);
2168     }
2169
2170     if (a_hostEntry->numThresh == 0) {
2171         /* store in ovf count ?? */
2172         return (0);
2173     }
2174
2175     count = 0;
2176     threshP = a_hostEntry->thresh;
2177     for (i = 0; i < a_hostEntry->numThresh; i++) {
2178         if (threshP->itemName[0] == '\0') {
2179             threshP++;
2180             continue;
2181         }
2182         idx = threshP->index;   /* positional index to the data array */
2183         tValue = atof(threshP->threshVal);      /* threshold value */
2184         pValue = atof(a_Data->data[idx]);       /* probe value */
2185         if (pValue > tValue) {
2186
2187             if (afsmon_debug) {
2188                 fprintf(debugFD,
2189                         "[ %s ] fs = %s, thresh ovf for %s, threshold= %s, probevalue= %s\n",
2190                         rn, a_hostEntry->hostName, threshP->itemName,
2191                         threshP->threshVal, a_Data->data[idx]);
2192                 fflush(debugFD);
2193             }
2194             /* if the threshold is crossed, call the handler function
2195              * only if this was a transition -ie, if the threshold was
2196              * crossed in the last probe too just count & keep quite! */
2197
2198             if (!a_Data->threshOvf[idx]) {
2199                 a_Data->threshOvf[idx] = 1;
2200                 /* call the threshold handler if provided */
2201                 if (threshP->handler[0] != '\0') {
2202                     if (afsmon_debug) {
2203                         fprintf(debugFD, "[ %s ] Calling ovf handler %s\n",
2204                                 rn, threshP->handler);
2205                         fflush(debugFD);
2206                     }
2207                     execute_thresh_handler(threshP->handler, a_Data->hostName,
2208                                            FS, threshP->itemName,
2209                                            threshP->threshVal,
2210                                            a_Data->data[idx]);
2211                 }
2212             }
2213
2214             count++;
2215         } else
2216             /* in case threshold was previously crossed, blank it out */
2217             a_Data->threshOvf[idx] = 0;
2218         threshP++;
2219     }
2220     /* store the overflow count */
2221     a_Data->ovfCount = count;
2222
2223     return (0);
2224 }                               /* check_fs_thresholds */
2225
2226
2227 /*-----------------------------------------------------------------------
2228  * save_FS_data_forDisplay()
2229  *
2230  * Description:
2231  *      Does the following:
2232  *      - if the probe number changed (ie, a cycle completed) curr_fsData
2233  *      is copied to prev_fsData, curr_fsData zeroed and refresh the 
2234  *      overview screen and file server screen with the new data.
2235  *      - store the results of the current probe from xstat_fs_Results into
2236  *      curr_fsData. ie., convert longs to strings.
2237  *      - check the thresholds 
2238  *
2239  * Returns:
2240  *      Success: 0
2241  *      Failure: Exits afsmonitor.
2242  *----------------------------------------------------------------------*/
2243
2244 int
2245 save_FS_data_forDisplay(struct xstat_fs_ProbeResults *a_fsResults)
2246 {                               /* save_FS_data_forDisplay */
2247
2248     static char rn[] = "save_FS_data_forDisplay";       /* routine name */
2249     struct fs_Display_Data *curr_fsDataP;       /* tmp ptr to curr_fsData */
2250     struct fs_Display_Data *prev_fsDataP;       /* tmp ptr to prev_fsData */
2251     struct afsmon_hostEntry *curr_host;
2252     static int results_Received = 0;    /* number of probes reveived in
2253                                          * the current cycle. If this is equal to numFS we got all
2254                                          * the data we want in this cycle and can now display it */
2255     int numBytes;
2256     int okay;
2257     int i;
2258     int code;
2259     int done;
2260
2261
2262     if (afsmon_debug) {
2263         fprintf(debugFD, "[ %s ] Called, a_fsResults= %p\n", rn, a_fsResults);
2264         fflush(debugFD);
2265     }
2266
2267     /* store results in the display array */
2268
2269     okay = 0;
2270     curr_fsDataP = curr_fsData;
2271     for (i = 0; i < numFS; i++) {
2272         if ((strcasecmp(curr_fsDataP->hostName, a_fsResults->connP->hostName))
2273             == 0) {
2274             okay = 1;
2275             break;
2276         }
2277         curr_fsDataP++;
2278     }
2279
2280     if (!okay) {
2281         fprintf(stderr,
2282                 "[ %s ] Could not insert FS probe results for host %s in fs display array\n",
2283                 rn, a_fsResults->connP->hostName);
2284         afsmon_Exit(65);
2285     }
2286
2287     /*  Check the status of the probe. If it succeeded, we store its
2288      * results in the display data structure. If it failed we only mark 
2289      * the failed status in the display data structure. */
2290
2291     if (a_fsResults->probeOK) { /* 1 => notOK the xstat results */
2292         curr_fsDataP->probeOK = 0;
2293
2294         /* print the probe status */
2295         if (afsmon_debug) {
2296             fprintf(debugFD, "\n\t\t ----- fs display data ------\n");
2297             fprintf(debugFD, "HostName = %s  PROBE FAILED \n",
2298                     curr_fsDataP->hostName);
2299             fflush(debugFD);
2300         }
2301
2302     } else {                    /* probe succeeded, update display data structures */
2303         curr_fsDataP->probeOK = 1;
2304
2305         /* convert longs to strings and place them in curr_fsDataP */
2306         fs_Results_ltoa(curr_fsDataP, a_fsResults);
2307
2308         /* compare with thresholds and set the overflow flags.
2309          * note that the threshold information is in the hostEntry structure and 
2310          * each threshold item has a positional index associated with it */
2311
2312         /* locate the hostEntry for this host */
2313         done = 0;
2314         curr_host = FSnameList;
2315         for (i = 0; i < numFS; i++) {
2316             if (strcasecmp(curr_host->hostName, a_fsResults->connP->hostName)
2317                 == 0) {
2318                 done = 1;
2319                 break;
2320             }
2321             curr_host = curr_host->next;;
2322         }
2323         if (!done)
2324             afsmon_Exit(70);
2325
2326         code = check_fs_thresholds(curr_host, curr_fsDataP);
2327         if (code) {
2328             fprintf(stderr, "[ %s ] Error in checking thresholds\n", rn);
2329             afsmon_Exit(75);
2330         }
2331
2332         /* print the info we just saved */
2333
2334         if (afsmon_debug) {
2335             fprintf(debugFD, "\n\t\t ----- fs display data ------\n");
2336             fprintf(debugFD, "HostName = %s\n", curr_fsDataP->hostName);
2337             for (i = 0; i < NUM_FS_STAT_ENTRIES; i++)
2338                 fprintf(debugFD, "%20s  %30s  %s\n", curr_fsDataP->data[i],
2339                         fs_varNames[i],
2340                         curr_fsDataP->threshOvf[i] ? "(ovf)" : "");
2341
2342             fprintf(debugFD, "\t\t--------------------------------\n\n");
2343             fflush(debugFD);
2344         }
2345
2346     }                           /* the probe succeeded, so we store the data in the display structure */
2347
2348
2349     /* if we have received a reply from all the hosts for this probe cycle,
2350      * it is time to display the data */
2351
2352     results_Received++;
2353     if (results_Received == numFS * num_fs_collections) {
2354         results_Received = 0;
2355
2356         if (afsmon_fs_curr_probeNum != afsmon_fs_prev_probeNum + 1) {
2357             sprintf(errMsg, "[ %s ] Probe number %d missed! \n", rn,
2358                     afsmon_fs_prev_probeNum + 1);
2359             afsmon_Exit(80);
2360         } else
2361             afsmon_fs_prev_probeNum++;
2362
2363         /* backup the display data of the probe cycle that just completed -
2364          * ie., store curr_fsData in prev_fsData */
2365
2366         memcpy((char *)prev_fsData, (char *)curr_fsData,
2367                (numFS * sizeof(struct fs_Display_Data)));
2368
2369
2370         /* initialize curr_fsData but retain the threshold flag information.
2371          * The previous state of threshold flags is used in check_fs_thresholds() */
2372
2373         numBytes = NUM_FS_STAT_ENTRIES * FS_STAT_STRING_LEN;
2374         curr_fsDataP = curr_fsData;
2375         for (i = 0; i < numFS; i++) {
2376             curr_fsDataP->probeOK = 0;
2377             curr_fsDataP->ovfCount = 0;
2378             memset(curr_fsDataP->data, 0, numBytes);
2379             curr_fsDataP++;
2380         }
2381
2382
2383         /* prev_fsData now contains all the information for the probe cycle
2384          * that just completed. Now count the number of threshold overflows for
2385          * use in the overview screen */
2386
2387         prev_fsDataP = prev_fsData;
2388         num_fs_alerts = 0;
2389         numHosts_onfs_alerts = 0;
2390         for (i = 0; i < numFS; i++) {
2391             if (!prev_fsDataP->probeOK) {       /* if probe failed */
2392                 num_fs_alerts++;
2393                 numHosts_onfs_alerts++;
2394             }
2395             if (prev_fsDataP->ovfCount) {       /* overflows ?? */
2396                 num_fs_alerts += prev_fsDataP->ovfCount;
2397                 numHosts_onfs_alerts++;
2398             }
2399             prev_fsDataP++;
2400         }
2401         if (afsmon_debug)
2402             fprintf(debugFD, "Number of FS alerts = %d (on %d hosts)\n",
2403                     num_fs_alerts, numHosts_onfs_alerts);
2404
2405         /* flag that the data is now ready to be displayed */
2406         fs_Data_Available = 1;
2407
2408         /* call the Overview frame update routine (update only FS info) */
2409         ovw_refresh(ovw_currPage, OVW_UPDATE_FS);
2410
2411         /* call the File Servers frame update routine */
2412         fs_refresh(fs_currPage, fs_curr_LCol);
2413
2414     }
2415     /* display data */
2416     return (0);
2417 }                               /* save_FS_data_forDisplay */
2418
2419
2420
2421
2422 /*-----------------------------------------------------------------------
2423  * afsmon_FS_Handler()
2424  *
2425  * Description:
2426  *      This is the File Server probe Handler. It updates the afsmonitor
2427  *      probe counts, fs circular buffer indices and calls the functions
2428  *      to process the results of this probe.
2429  *
2430  * Returns:
2431  *      Success: 0
2432  *      Failure: Exits afsmonitor.
2433  *----------------------------------------------------------------------*/
2434
2435 int
2436 afsmon_FS_Handler(void)
2437 {                               /* afsmon_FS_Handler() */
2438     static char rn[] = "afsmon_FS_Handler";     /* routine name */
2439     int newProbeCycle;          /* start of new probe cycle ? */
2440     int code;                   /* return status */
2441
2442
2443     if (afsmon_debug) {
2444         fprintf(debugFD,
2445                 "[ %s ] Called, hostName= %s, probeNum= %d, status=%s, collection=%d\n", rn,
2446                 xstat_fs_Results.connP->hostName, xstat_fs_Results.probeNum,
2447                 xstat_fs_Results.probeOK ? "FAILED" : "OK",
2448                 xstat_fs_Results.collectionNumber);
2449         fflush(debugFD);
2450     }
2451
2452
2453     /* print the probe results to output file */
2454     if (afsmon_output) {
2455         code = afsmon_fsOutput(output_filename, afsmon_detOutput);
2456         if (code) {
2457             fprintf(stderr,
2458                     "[ %s ] output to file %s returned error code=%d\n", rn,
2459                     output_filename, code);
2460         }
2461     }
2462
2463     /* Update current probe number and circular buffer index. if current 
2464      * probenum changed make sure it is only by 1 */
2465
2466     newProbeCycle = 0;
2467     if (xstat_fs_Results.probeNum != afsmon_fs_curr_probeNum) {
2468         if (xstat_fs_Results.probeNum == afsmon_fs_curr_probeNum + 1) {
2469             afsmon_fs_curr_probeNum++;
2470             newProbeCycle = 1;
2471             if (num_bufSlots)
2472                 afsmon_fs_curr_CBindex =
2473                     (afsmon_fs_curr_probeNum - 1) % num_bufSlots;
2474         } else {
2475             fprintf(stderr, "[ %s ] probe number %d-1 missed\n", rn,
2476                     xstat_fs_Results.probeNum);
2477             afsmon_Exit(85);
2478         }
2479     }
2480
2481
2482     /* store the results of this probe in the FS circular buffer */
2483     if (num_bufSlots)
2484         save_FS_results_inCB(newProbeCycle);
2485
2486
2487     /* store the results of the current probe in the fs data display structure.
2488      * if the current probe number changed, swap the current and previous display
2489      * structures. note that the display screen is updated from these structures 
2490      * and should start showing the data of the just completed probe cycle */
2491
2492     save_FS_data_forDisplay(&xstat_fs_Results);
2493
2494     return (0);
2495 }
2496
2497
2498
2499 /*----------------------------------------------------------------------- * 
2500  * Print_CM_CB()     
2501  *
2502  * Description:
2503  *      Debug routine.
2504  *      Prints the  Cache Manager circular buffer 
2505  *----------------------------------------------------------------------*/
2506
2507 void
2508 Print_CM_CB(void)
2509 {                               /* Print_CM_CB() */
2510
2511     struct afsmon_cm_Results_list *cmlist;
2512     int i;
2513     int j;
2514     int k;
2515
2516     /* print valid info in the cm CB */
2517
2518     if (afsmon_debug) {
2519         fprintf(debugFD,
2520                 "==================== CM Buffer ========================\n");
2521         fprintf(debugFD, "afsmon_cm_curr_CBindex = %d\n",
2522                 afsmon_cm_curr_CBindex);
2523         fprintf(debugFD, "afsmon_cm_curr_probeNum = %d\n\n",
2524                 afsmon_cm_curr_probeNum);
2525
2526         for (i = 0; i < num_bufSlots; i++) {
2527             fprintf(debugFD, "\t--------- slot %d ----------\n", i);
2528             cmlist = afsmon_cm_ResultsCB[i].list;
2529             j = 0;
2530             while (j < numCM) {
2531                 for (k = 0; k < MAX_NUM_CM_COLLECTIONS; k++) {
2532                     if (!cmlist->empty[k]) {
2533                         fprintf(debugFD,
2534                                 "\t %d) probeNum = %d host = %s cn = %d",
2535                                 j,
2536                                 cmlist->cmResults[k]->probeNum,
2537                                 cmlist->cmResults[k]->connP->hostName,
2538                                 cmlist->cmResults[k]->collectionNumber);
2539                         if (cmlist->cmResults[k]->probeOK)
2540                             fprintf(debugFD, " NOTOK\n");
2541                         else
2542                             fprintf(debugFD, " OK\n");
2543                     } else
2544                         fprintf(debugFD, "\t %d) -- empty --\n", j);
2545                 }
2546                 cmlist = cmlist->next;
2547                 j++;
2548             }
2549             if (cmlist != (struct afsmon_cm_Results_list *)0)
2550                 fprintf(debugFD, "dangling last next ptr cm CB\n");
2551         }
2552     }
2553 }
2554
2555
2556 /*-----------------------------------------------------------------------
2557  * save_CM_results_inCB()
2558  *
2559  * Description:
2560  *      Saves the results of the latest CM probe in the cm circular
2561  *      buffers. If the current probe cycle is in progress the contents
2562  *      of xstat_cm_Results are copied to the end of the list of results
2563  *      in the current slot (pointed to by afsmon_cm_curr_CBindex). If
2564  *      a new probe cycle has started the next slot in the circular buffer
2565  *      is initialized and the results copied. Note that the Rx related
2566  *      information available in xstat_cm_Results is not copied.
2567  *
2568  * Returns:
2569  *      Success: 0
2570  *      Failure: Exits afsmonitor.
2571  *----------------------------------------------------------------------*/
2572
2573 int
2574 save_CM_results_inCB(int a_newProbeCycle)       /* start of new probe cycle ? */
2575 {                               /* save_CM_results_inCB() */
2576     static char rn[] = "save_CM_results_inCB";  /* routine name */
2577     struct afsmon_cm_Results_list *tmp_cmlist_item;     /* temp cm list item */
2578     struct xstat_cm_ProbeResults *tmp_cmPR;     /* temp ptr */
2579     int i;
2580     int index;
2581
2582
2583     if (afsmon_debug) {
2584         fprintf(debugFD, "[ %s ] Called, a_newProbeCycle= %d\n", rn,
2585                 a_newProbeCycle);
2586         fflush(debugFD);
2587     }
2588
2589     if (xstat_cm_Results.collectionNumber == AFSCB_XSTATSCOLL_FULL_PERF_INFO) {
2590         index = 0;
2591     } else {
2592         fprintf(stderr, "[ %s ] collection number %d is out of range.\n",
2593                 rn, xstat_cm_Results.collectionNumber);
2594         afsmon_Exit(91);
2595     }
2596
2597     /* If a new probe cycle started, mark the list in the current buffer
2598      * slot empty for resuse. Note that afsmon_cm_curr_CBindex was appropriately
2599      * incremented in afsmon_CM_Handler() */
2600
2601     if (a_newProbeCycle) {
2602         tmp_cmlist_item = afsmon_cm_ResultsCB[afsmon_cm_curr_CBindex].list;
2603         for (i = 0; i < numCM; i++) {
2604             tmp_cmlist_item->empty[index] = 1;
2605             tmp_cmlist_item = tmp_cmlist_item->next;
2606         }
2607     }
2608
2609     /* locate last unused item in list */
2610     tmp_cmlist_item = afsmon_cm_ResultsCB[afsmon_cm_curr_CBindex].list;
2611     for (i = 0; i < numCM; i++) {
2612         if (tmp_cmlist_item->empty[index])
2613             break;
2614         tmp_cmlist_item = tmp_cmlist_item->next;
2615     }
2616
2617     /* if we could not find one we have an inconsistent list */
2618     if (!tmp_cmlist_item->empty[index]) {
2619         fprintf(stderr,
2620                 "[ %s ] list inconsistency 1. unable to find an empty slot to store results of probenum %d of %s\n",
2621                 rn, xstat_cm_Results.probeNum,
2622                 xstat_cm_Results.connP->hostName);
2623         afsmon_Exit(90);
2624     }
2625
2626     tmp_cmPR = tmp_cmlist_item->cmResults[index];
2627
2628     /* copy hostname and probe number and probe time and probe status.
2629      * if the probe failed return now */
2630
2631     memcpy(tmp_cmPR->connP->hostName, xstat_cm_Results.connP->hostName,
2632            sizeof(xstat_cm_Results.connP->hostName));
2633     tmp_cmPR->probeNum = xstat_cm_Results.probeNum;
2634     tmp_cmPR->probeTime = xstat_cm_Results.probeTime;
2635     tmp_cmPR->probeOK = xstat_cm_Results.probeOK;
2636     if (xstat_cm_Results.probeOK) {     /* probeOK = 1 => notOK */
2637         /* we have a nonempty results structure so mark the list item used */
2638         tmp_cmlist_item->empty[index] = 0;
2639         return (0);
2640     }
2641
2642
2643     /* copy connection information */
2644     memcpy(&(tmp_cmPR->connP->skt), &(xstat_cm_Results.connP->skt),
2645            sizeof(struct sockaddr_in));
2646
2647    /**** NEED TO COPY rx_connection INFORMATION HERE ******/
2648
2649     memcpy(tmp_cmPR->connP->hostName, xstat_cm_Results.connP->hostName,
2650            sizeof(xstat_cm_Results.connP->hostName));
2651     tmp_cmPR->collectionNumber = xstat_cm_Results.collectionNumber;
2652
2653     /* copy the probe data information */
2654     tmp_cmPR->data.AFSCB_CollData_len =
2655         min(xstat_cm_Results.data.AFSCB_CollData_len,
2656             afsmon_cm_results_length[index]);
2657     memcpy(tmp_cmPR->data.AFSCB_CollData_val,
2658            xstat_cm_Results.data.AFSCB_CollData_val,
2659            tmp_cmPR->data.AFSCB_CollData_len * sizeof(afs_int32));
2660
2661
2662     /* we have a valid results structure so mark the list item used */
2663     tmp_cmlist_item->empty[index] = 0;
2664
2665     /* print the stored info - to make sure we copied it right */
2666     /*   Print_cm_FullPerfInfo(tmp_cmPR);        */
2667     /* Print the cm circular buffer */
2668     Print_CM_CB();
2669     return (0);
2670 }                               /* save_CM_results_inCB */
2671
2672
2673
2674 /*-----------------------------------------------------------------------
2675  * cm_Results_ltoa()
2676  *
2677  * Description:
2678  *      The results of xstat probes are stored in a string format in 
2679  *      the arrays curr_cmData and prev_cmData. The information stored in
2680  *      prev_cmData is copied to the screen. 
2681  *      This function converts xstat FS results from longs to strings and 
2682  *      places them in the given buffer (a pointer to an item in curr_cmData).
2683  *      When a probe cycle completes, curr_cmData is copied to prev_cmData
2684  *      in afsmon_CM_Handler().
2685  *
2686  * Returns:
2687  *      Always returns 0.
2688  *----------------------------------------------------------------------*/
2689
2690 int
2691 cm_Results_ltoa(struct cm_Display_Data *a_cmData,       /* target buffer */
2692                 struct xstat_cm_ProbeResults *a_cmResults)      /* ptr to xstat cm Results */
2693 {                               /* cm_Results_ltoa */
2694
2695     static char rn[] = "cm_Results_ltoa";       /* routine name */
2696     struct afs_stats_CMFullPerf *fullP; /* ptr to complete CM stats */
2697     afs_int32 *srcbuf;
2698     afs_int32 *tmpbuf;
2699     int i, j;
2700     int idx;
2701     afs_int32 numLongs;
2702
2703     if (afsmon_debug) {
2704         fprintf(debugFD, "[ %s ] Called, a_cmData= %p, a_cmResults= %p\n", rn,
2705                 a_cmData, a_cmResults);
2706         fflush(debugFD);
2707     }
2708
2709
2710     fullP = (struct afs_stats_CMFullPerf *)
2711         (a_cmResults->data.AFSCB_CollData_val);
2712
2713     /* There are 4 parts to CM statistics
2714      * - Overall performance statistics (including up/down statistics)
2715      * - This CMs FS RPC operations info 
2716      * - This CMs FS RPC errors info
2717      * - This CMs FS transfers info
2718      * - Authentication info
2719      * - [Un]Replicated access info
2720      */
2721
2722     /* copy overall performance statistics */
2723     srcbuf = (afs_int32 *) & (fullP->perf);
2724     idx = 0;
2725     /* we skip the 19 entry, ProtServAddr, so the index must account for this */
2726     for (i = 0; i < NUM_AFS_STATS_CMPERF_LONGS + 1; i++) {
2727         if (i == 19) {
2728             srcbuf++;
2729             continue;           /* skip ProtServerAddr */
2730         }
2731         sprintf(a_cmData->data[idx], "%d", *srcbuf);
2732         idx++;
2733         srcbuf++;
2734     }
2735
2736     /*printf("Ending index value = %d\n",idx-1); */
2737
2738     /* server up/down statistics */
2739     /* copy file server up/down stats */
2740     srcbuf = (afs_int32 *) (fullP->perf.fs_UpDown);
2741     numLongs =
2742         2 * (sizeof(struct afs_stats_SrvUpDownInfo) / sizeof(afs_int32));
2743     for (i = 0; i < numLongs; i++) {
2744         sprintf(a_cmData->data[idx], "%d", *srcbuf);
2745         idx++;
2746         srcbuf++;
2747     }
2748
2749     /*printf("Ending index value = %d\n",idx-1); */
2750
2751     /* copy volume location  server up/down stats */
2752     srcbuf = (afs_int32 *) (fullP->perf.vl_UpDown);
2753     numLongs =
2754         2 * (sizeof(struct afs_stats_SrvUpDownInfo) / sizeof(afs_int32));
2755     for (i = 0; i < numLongs; i++) {
2756         sprintf(a_cmData->data[idx], "%d", *srcbuf);
2757         idx++;
2758         srcbuf++;
2759     }
2760
2761     /*printf("Ending index value = %d\n",idx-1); */
2762
2763     /* copy CMs individual FS RPC operations info */
2764     srcbuf = (afs_int32 *) (fullP->rpc.fsRPCTimes);
2765     for (i = 0; i < AFS_STATS_NUM_FS_RPC_OPS; i++) {
2766         sprintf(a_cmData->data[idx], "%d", *srcbuf);    /* numOps */
2767         idx++;
2768         srcbuf++;
2769         sprintf(a_cmData->data[idx], "%d", *srcbuf);    /* numSuccesses */
2770         idx++;
2771         srcbuf++;
2772         tmpbuf = srcbuf++;      /* sum time */
2773         sprintf(a_cmData->data[idx], "%d.%06d", *tmpbuf, *srcbuf);
2774         idx++;
2775         srcbuf++;
2776         tmpbuf = srcbuf++;      /* sqr time */
2777         sprintf(a_cmData->data[idx], "%d.%06d", *tmpbuf, *srcbuf);
2778         idx++;
2779         srcbuf++;
2780         tmpbuf = srcbuf++;      /* min time */
2781         sprintf(a_cmData->data[idx], "%d.%06d", *tmpbuf, *srcbuf);
2782         idx++;
2783         srcbuf++;
2784         tmpbuf = srcbuf++;      /* max time */
2785         sprintf(a_cmData->data[idx], "%d.%06d", *tmpbuf, *srcbuf);
2786         idx++;
2787         srcbuf++;
2788     }
2789
2790     /*printf("Ending index value = %d\n",idx-1); */
2791
2792     /* copy CMs individual FS RPC errors info */
2793
2794     srcbuf = (afs_int32 *) (fullP->rpc.fsRPCErrors);
2795     for (i = 0; i < AFS_STATS_NUM_FS_RPC_OPS; i++) {
2796         sprintf(a_cmData->data[idx], "%d", *srcbuf);    /* server */
2797         idx++;
2798         srcbuf++;
2799         sprintf(a_cmData->data[idx], "%d", *srcbuf);    /* network */
2800         idx++;
2801         srcbuf++;
2802         sprintf(a_cmData->data[idx], "%d", *srcbuf);    /* prot */
2803         idx++;
2804         srcbuf++;
2805         sprintf(a_cmData->data[idx], "%d", *srcbuf);    /* vol */
2806         idx++;
2807         srcbuf++;
2808         sprintf(a_cmData->data[idx], "%d", *srcbuf);    /* busies */
2809         idx++;
2810         srcbuf++;
2811         sprintf(a_cmData->data[idx], "%d", *srcbuf);    /* other */
2812         idx++;
2813         srcbuf++;
2814     }
2815
2816     /*printf("Ending index value = %d\n",idx-1); */
2817
2818     /* copy CMs individual RPC transfers info */
2819
2820     srcbuf = (afs_int32 *) (fullP->rpc.fsXferTimes);
2821     for (i = 0; i < AFS_STATS_NUM_FS_XFER_OPS; i++) {
2822         sprintf(a_cmData->data[idx], "%d", *srcbuf);    /* numOps */
2823         idx++;
2824         srcbuf++;
2825         sprintf(a_cmData->data[idx], "%d", *srcbuf);    /* numSuccesses */
2826         idx++;
2827         srcbuf++;
2828         tmpbuf = srcbuf++;      /* sum time */
2829         sprintf(a_cmData->data[idx], "%d.%06d", *tmpbuf, *srcbuf);
2830         idx++;
2831         srcbuf++;
2832         tmpbuf = srcbuf++;      /* sqr time */
2833         sprintf(a_cmData->data[idx], "%d.%06d", *tmpbuf, *srcbuf);
2834         idx++;
2835         srcbuf++;
2836         tmpbuf = srcbuf++;      /* min time */
2837         sprintf(a_cmData->data[idx], "%d.%06d", *tmpbuf, *srcbuf);
2838         idx++;
2839         srcbuf++;
2840         tmpbuf = srcbuf++;      /* max time */
2841         sprintf(a_cmData->data[idx], "%d.%06d", *tmpbuf, *srcbuf);
2842         idx++;
2843         srcbuf++;
2844         sprintf(a_cmData->data[idx], "%d", *srcbuf);    /* sum bytes */
2845         idx++;
2846         srcbuf++;
2847         sprintf(a_cmData->data[idx], "%d", *srcbuf);    /* min bytes */
2848         idx++;
2849         srcbuf++;
2850         sprintf(a_cmData->data[idx], "%d", *srcbuf);    /* max bytes */
2851         idx++;
2852         srcbuf++;
2853         for (j = 0; j < AFS_STATS_NUM_XFER_BUCKETS; j++) {
2854             sprintf(a_cmData->data[idx], "%d", *srcbuf);        /* bucket[j] */
2855             idx++;
2856             srcbuf++;
2857         }
2858     }
2859
2860     /*printf("Ending index value = %d\n",idx-1); */
2861
2862     /* copy CM operations timings */
2863
2864     srcbuf = (afs_int32 *) (fullP->rpc.cmRPCTimes);
2865     for (i = 0; i < AFS_STATS_NUM_CM_RPC_OPS; i++) {
2866         sprintf(a_cmData->data[idx], "%d", *srcbuf);    /* numOps */
2867         idx++;
2868         srcbuf++;
2869         sprintf(a_cmData->data[idx], "%d", *srcbuf);    /* numSuccesses */
2870         idx++;
2871         srcbuf++;
2872         tmpbuf = srcbuf++;      /* sum time */
2873         sprintf(a_cmData->data[idx], "%d.%06d", *tmpbuf, *srcbuf);
2874         idx++;
2875         srcbuf++;
2876         tmpbuf = srcbuf++;      /* sqr time */
2877         sprintf(a_cmData->data[idx], "%d.%06d", *tmpbuf, *srcbuf);
2878         idx++;
2879         srcbuf++;
2880         tmpbuf = srcbuf++;      /* min time */
2881         sprintf(a_cmData->data[idx], "%d.%06d", *tmpbuf, *srcbuf);
2882         idx++;
2883         srcbuf++;
2884         tmpbuf = srcbuf++;      /* max time */
2885         sprintf(a_cmData->data[idx], "%d.%06d", *tmpbuf, *srcbuf);
2886         idx++;
2887         srcbuf++;
2888     }
2889
2890     /*printf("Ending index value = %d\n",idx-1); */
2891
2892     /* copy authentication info */
2893
2894     srcbuf = (afs_int32 *) & (fullP->authent);
2895     numLongs = sizeof(struct afs_stats_AuthentInfo) / sizeof(afs_int32);
2896     for (i = 0; i < numLongs; i++) {
2897         sprintf(a_cmData->data[idx], "%d", *srcbuf);
2898         idx++;
2899         srcbuf++;
2900     }
2901
2902     /*printf("Ending index value = %d\n",idx-1); */
2903
2904     /* copy CM [un]replicated access info */
2905
2906     srcbuf = (afs_int32 *) & (fullP->accessinf);
2907     numLongs = sizeof(struct afs_stats_AccessInfo) / sizeof(afs_int32);
2908     for (i = 0; i < numLongs; i++) {
2909         sprintf(a_cmData->data[idx], "%d", *srcbuf);
2910         idx++;
2911         srcbuf++;
2912     }
2913
2914     /*printf("Ending index value = %d\n",idx-1); */
2915     return (0);
2916
2917 }                               /* cm_Results_ltoa */
2918
2919
2920 /*-----------------------------------------------------------------------
2921  * Function:    check_cm_thresholds()
2922  *
2923  * Description:
2924  *      Checks the thresholds and sets the overflow flag. Recall that the
2925  *      thresholds for each host are stored in the hostEntry lists
2926  *      [fs/cm]nameList arrays. The probe results are passed to this 
2927  *      function in the display-ready format - ie., as strings. Though
2928  *      this looks stupid the overhead incurred in converting the strings
2929  *      back to floats and comparing them is insignificant and 
2930  *      programming is easier this way. 
2931  *      The threshold flags are a part of the display structures
2932  *      curr_[fs/cm]Data.
2933  *
2934  * Returns:
2935  *      0
2936  *----------------------------------------------------------------------*/
2937
2938 int
2939 check_cm_thresholds(struct afsmon_hostEntry *a_hostEntry,       /* ptr to hostEntry */
2940                     struct cm_Display_Data *a_Data)             /* ptr to cm data to be displayed */
2941 {                               /* check_cm_thresholds */
2942
2943     static char rn[] = "check_cm_thresholds";
2944     struct Threshold *threshP;
2945     double tValue;              /* threshold value */
2946     double pValue;              /* probe value */
2947     int i;
2948     int idx;
2949     int count;                  /* number of thresholds exceeded */
2950
2951     if (afsmon_debug) {
2952         fprintf(debugFD, "[ %s ] Called, a_hostEntry= %p, a_Data= %p\n", rn,
2953                 a_hostEntry, a_Data);
2954         fflush(debugFD);
2955     }
2956
2957     if (a_hostEntry->numThresh == 0) {
2958         /* store in ovf count ?? */
2959         return (0);
2960     }
2961
2962     count = 0;
2963     threshP = a_hostEntry->thresh;
2964     for (i = 0; i < a_hostEntry->numThresh; i++) {
2965         if (threshP->itemName[0] == '\0') {
2966             threshP++;
2967             continue;
2968         }
2969         idx = threshP->index;   /* positional index to the data array */
2970         tValue = atof(threshP->threshVal);      /* threshold value */
2971         pValue = atof(a_Data->data[idx]);       /* probe value */
2972         if (pValue > tValue) {
2973
2974             if (afsmon_debug) {
2975                 fprintf(debugFD,
2976                         "[ %s ] cm = %s, thresh ovf for %s, threshold= %s, probevalue= %s\n",
2977                         rn, a_hostEntry->hostName, threshP->itemName,
2978                         threshP->threshVal, a_Data->data[idx]);
2979                 fflush(debugFD);
2980             }
2981
2982             /* if the threshold is crossed, call the handler function
2983              * only if this was a transition -ie, if the threshold was
2984              * crossed in the last probe too just count & keep quite! */
2985
2986             if (!a_Data->threshOvf[idx]) {
2987                 a_Data->threshOvf[idx] = 1;
2988                 /* call the threshold handler if provided */
2989                 if (threshP->handler[0] != '\0') {
2990                     if (afsmon_debug) {
2991                         fprintf(debugFD, "[ %s ] Calling ovf handler %s\n",
2992                                 rn, threshP->handler);
2993                         fflush(debugFD);
2994                     }
2995                     execute_thresh_handler(threshP->handler, a_Data->hostName,
2996                                            CM, threshP->itemName,
2997                                            threshP->threshVal,
2998                                            a_Data->data[idx]);
2999                 }
3000             }
3001
3002             count++;
3003         } else
3004             /* in case threshold was previously crossed, blank it out */
3005             a_Data->threshOvf[idx] = 0;
3006         threshP++;
3007     }
3008     /* store the overflow count */
3009     a_Data->ovfCount = count;
3010
3011     return (0);
3012 }                               /* check_cm_thresholds */
3013
3014
3015 /*-----------------------------------------------------------------------
3016  * save_CM_data_forDisplay()
3017  *
3018  * Description:
3019  *      Does the following:
3020  *      - if the probe number changed (ie, a cycle completed) curr_cmData
3021  *      is copied to prev_cmData, curr_cmData zeroed and refresh the 
3022  *      overview screen and file server screen with the new data.
3023  *      - store the results of the current probe from xstat_cm_Results into
3024  *      curr_cmData. ie., convert longs to strings.
3025  *      - check the thresholds 
3026  *
3027  * Returns:
3028  *      Success: 0
3029  *      Failure: Exits afsmonitor.
3030  *
3031  *----------------------------------------------------------------------*/
3032
3033 int
3034 save_CM_data_forDisplay(struct xstat_cm_ProbeResults *a_cmResults)
3035 {                               /* save_CM_data_forDisplay */
3036
3037     static char rn[] = "save_CM_data_forDisplay";       /* routine name */
3038     struct cm_Display_Data *curr_cmDataP;
3039     struct cm_Display_Data *prev_cmDataP;
3040     struct afsmon_hostEntry *curr_host;
3041     static int results_Received = 0;    /* number of probes reveived in
3042                                          * the current cycle. If this is equal to numFS we got all
3043                                          * the data we want in this cycle and can now display it */
3044     int numBytes;
3045     int done;
3046     int code;
3047     int okay;
3048     int i;
3049
3050     if (afsmon_debug) {
3051         fprintf(debugFD, "[ %s ] Called, a_cmResults= %p\n", rn, a_cmResults);
3052         fflush(debugFD);
3053     }
3054
3055     /* store results in the display array */
3056
3057     okay = 0;
3058     curr_cmDataP = curr_cmData;
3059     for (i = 0; i < numCM; i++) {
3060         if ((strcasecmp(curr_cmDataP->hostName, a_cmResults->connP->hostName))
3061             == 0) {
3062             okay = 1;
3063             break;
3064         }
3065         curr_cmDataP++;
3066     }
3067
3068     if (!okay) {
3069         fprintf(stderr,
3070                 "[ %s ] Could not insert CM probe results for host %s in cm display array\n",
3071                 rn, a_cmResults->connP->hostName);
3072         afsmon_Exit(95);
3073     }
3074
3075     /*  Check the status of the probe. If it succeeded, we store its
3076      * results in the display data structure. If it failed we only mark 
3077      * the failed status in the display data structure. */
3078
3079
3080     if (a_cmResults->probeOK) { /* 1 => notOK the xstat results */
3081         curr_cmDataP->probeOK = 0;
3082
3083         /* print the probe status */
3084         if (afsmon_debug) {
3085             fprintf(debugFD, "\n\t\t ----- cm display data ------\n");
3086             fprintf(debugFD, "HostName = %s  PROBE FAILED \n",
3087                     curr_cmDataP->hostName);
3088             fflush(debugFD);
3089         }
3090
3091     } else {                    /* probe succeeded, update display data structures */
3092         curr_cmDataP->probeOK = 1;
3093
3094
3095         /* covert longs to strings and place them in curr_cmDataP */
3096         cm_Results_ltoa(curr_cmDataP, a_cmResults);
3097
3098         /* compare with thresholds and set the overflow flags.
3099          * note that the threshold information is in the hostEntry structure and 
3100          * each threshold item has a positional index associated with it */
3101
3102         /* locate the hostEntry for this host */
3103         done = 0;
3104         curr_host = CMnameList;
3105         for (i = 0; i < numCM; i++) {
3106             if (strcasecmp(curr_host->hostName, a_cmResults->connP->hostName)
3107                 == 0) {
3108                 done = 1;
3109                 break;
3110             }
3111             curr_host = curr_host->next;
3112         }
3113         if (!done)
3114             afsmon_Exit(100);
3115
3116         code = check_cm_thresholds(curr_host, curr_cmDataP);
3117         if (code) {
3118             fprintf(stderr, "[ %s ] Error in checking thresholds\n", rn);
3119             afsmon_Exit(105);
3120         }
3121
3122         /* print the info we just saved */
3123         if (afsmon_debug) {
3124             fprintf(debugFD, "\n\t\t ----- CM display data ------\n");
3125             fprintf(debugFD, "HostName = %s\n", curr_cmDataP->hostName);
3126             for (i = 0; i < NUM_CM_STAT_ENTRIES; i++) {
3127                 switch (i) {
3128                 case 0:
3129                     fprintf(debugFD, "\t -- Overall Perf Info --\n");
3130                     break;
3131                 case 39:
3132                     fprintf(debugFD,
3133                             "\t -- File Server up/down stats - same cell --\n");
3134                     break;
3135                 case 64:
3136                     fprintf(debugFD,
3137                             "\t -- File Server up/down stats - diff cell --\n");
3138                     break;
3139                 case 89:
3140                     fprintf(debugFD,
3141                             "\t -- VL server up/down stats - same cell --\n");
3142                     break;
3143                 case 114:
3144                     fprintf(debugFD,
3145                             "\t -- VL server up/down stats - diff cell --\n");
3146                     break;
3147                 case 139:
3148                     fprintf(debugFD, "\t -- FS Operation Timings --\n");
3149                     break;
3150                 case 279:
3151                     fprintf(debugFD, "\t -- FS Error Info --\n");
3152                     break;
3153                 case 447:
3154                     fprintf(debugFD, "\t -- FS Transfer Timings --\n");
3155                     break;
3156                 case 475:
3157                     fprintf(debugFD, "\t -- CM Operations Timings --\n");
3158                     break;
3159                 case 510:
3160                     fprintf(debugFD, "\t -- Authentication Info --\n");
3161                     break;
3162                 case 522:
3163                     fprintf(debugFD, "\t -- Access Info --\n");
3164                     break;
3165                 default:
3166                     break;
3167                 }
3168
3169                 fprintf(debugFD, "%20s  %30s %s\n", curr_cmDataP->data[i],
3170                         cm_varNames[i],
3171                         curr_cmDataP->threshOvf[i] ? "(ovf)" : "");
3172             }
3173             fprintf(debugFD, "\t\t--------------------------------\n\n");
3174         }
3175
3176     }                           /* if the probe succeeded, update the display data structures */
3177
3178     /* if we have received a reply from all the hosts for this probe cycle,
3179      * it is time to display the data */
3180
3181     results_Received++;
3182     if (results_Received == numCM * num_cm_collections) {
3183         results_Received = 0;
3184
3185         if (afsmon_cm_curr_probeNum != afsmon_cm_prev_probeNum + 1) {
3186             sprintf(errMsg, "[ %s ] Probe number %d missed! \n", rn,
3187                     afsmon_cm_prev_probeNum + 1);
3188             afsmon_Exit(110);
3189         } else
3190             afsmon_cm_prev_probeNum++;
3191
3192
3193         /* backup the display data of the probe cycle that just completed -
3194          * ie., store curr_cmData in prev_cmData */
3195
3196         memcpy((char *)prev_cmData, (char *)curr_cmData,
3197                (numCM * sizeof(struct cm_Display_Data)));
3198
3199
3200         /* initialize curr_cmData but retain the threshold flag information.
3201          * The previous state of threshold flags is used in check_cm_thresholds() */
3202
3203         curr_cmDataP = curr_cmData;
3204         numBytes = NUM_CM_STAT_ENTRIES * CM_STAT_STRING_LEN;
3205         for (i = 0; i < numCM; i++) {
3206             curr_cmDataP->probeOK = 0;
3207             curr_cmDataP->ovfCount = 0;
3208             memset(curr_cmDataP->data, 0, numBytes);
3209             curr_cmDataP++;
3210         }
3211
3212         /* prev_cmData now contains all the information for the probe cycle
3213          * that just completed. Now count the number of threshold overflows for
3214          * use in the overview screen */
3215
3216         prev_cmDataP = prev_cmData;
3217         num_cm_alerts = 0;
3218         numHosts_oncm_alerts = 0;
3219         for (i = 0; i < numCM; i++) {
3220             if (!prev_cmDataP->probeOK) {       /* if probe failed */
3221                 num_cm_alerts++;
3222                 numHosts_oncm_alerts++;
3223             } else if (prev_cmDataP->ovfCount) {        /* overflows ?? */
3224                 num_cm_alerts += prev_cmDataP->ovfCount;
3225                 numHosts_oncm_alerts++;
3226             }
3227             prev_cmDataP++;
3228         }
3229         if (afsmon_debug)
3230             fprintf(debugFD, "Number of CM alerts = %d (on %d hosts)\n",
3231                     num_cm_alerts, numHosts_oncm_alerts);
3232
3233
3234         /* flag that the data is now ready to be displayed */
3235         cm_Data_Available = 1;
3236
3237         /* update the Overview frame (only CM info) */
3238         ovw_refresh(ovw_currPage, OVW_UPDATE_CM);
3239
3240         /* update the Cache Managers frame */
3241         cm_refresh(cm_currPage, cm_curr_LCol);
3242
3243     }
3244
3245
3246     return (0);
3247 }                               /* save_CM_data_forDisplay */
3248
3249
3250
3251 /*-----------------------------------------------------------------------
3252  * afsmon_CM_Handler()
3253  *
3254  * Description:
3255  *      This is the Cache Manager probe Handler. It updates the afsmonitor
3256  *      probe counts, cm circular buffer indices and calls the functions
3257  *      to process the results of this probe.
3258  *
3259  * Returns:
3260  *      Success: 0
3261  *      Failure: Exits afsmonitor.
3262  *----------------------------------------------------------------------*/
3263
3264 int
3265 afsmon_CM_Handler(void)
3266 {                               /* afsmon_CM_Handler() */
3267     static char rn[] = "afsmon_CM_Handler";     /* routine name */
3268     int code;                   /* return status */
3269     int newProbeCycle;          /* start of new probe cycle ? */
3270
3271     if (afsmon_debug) {
3272         fprintf(debugFD,
3273                 "[ %s ] Called, hostName= %s, probeNum= %d, status= %s\n", rn,
3274                 xstat_cm_Results.connP->hostName, xstat_cm_Results.probeNum,
3275                 xstat_cm_Results.probeOK ? "FAILED" : "OK");
3276         fflush(debugFD);
3277     }
3278
3279
3280     /* print the probe results to output file */
3281     if (afsmon_output) {
3282         code = afsmon_cmOutput(output_filename, afsmon_detOutput);
3283         if (code) {
3284             fprintf(stderr,
3285                     "[ %s ] output to file %s returned error code=%d\n", rn,
3286                     output_filename, code);
3287         }
3288     }
3289
3290     /* Update current probe number and circular buffer index. if current 
3291      * probenum changed make sure it is only by 1 */
3292
3293     newProbeCycle = 0;
3294     if (xstat_cm_Results.probeNum != afsmon_cm_curr_probeNum) {
3295         if (xstat_cm_Results.probeNum == afsmon_cm_curr_probeNum + 1) {
3296             afsmon_cm_curr_probeNum++;
3297             newProbeCycle = 1;
3298             if (num_bufSlots)
3299                 afsmon_cm_curr_CBindex =
3300                     (afsmon_cm_curr_probeNum - 1) % num_bufSlots;
3301         } else {
3302             fprintf(stderr, "[ %s ] probe number %d-1 missed\n", rn,
3303                     xstat_cm_Results.probeNum);
3304             afsmon_Exit(115);
3305         }
3306     }
3307
3308     /* save the results of this probe in the CM buffer */
3309     if (num_bufSlots)
3310         save_CM_results_inCB(newProbeCycle);
3311
3312     /* store the results of the current probe in the cm data display structure.
3313      * if the current probe number changed, swap the current and previous display
3314      * structures. note that the display screen is updated from these structures 
3315      * and should start showing the data of the just completed probe cycle */
3316
3317     save_CM_data_forDisplay(&xstat_cm_Results);
3318
3319     return (0);
3320 }
3321
3322 /*-----------------------------------------------------------------------
3323  * init_fs_buffers()
3324  *
3325  * Description:
3326  *      Allocate and Initialize circular buffers for file servers.
3327  *
3328  * Returns:
3329  *      Success: 0
3330  *      Failure to allocate memory: exits afsmonitor.
3331  *----------------------------------------------------------------------*/
3332
3333 int
3334 init_fs_buffers(void)
3335 {                               /* init_fs_buffers() */
3336     static char rn[] = "init_fs_buffers";       /* routine name */
3337     struct afsmon_fs_Results_list *new_fslist_item;     /* ptr for new struct */
3338     struct afsmon_fs_Results_list *tmp_fslist_item;     /* temp ptr */
3339     struct xstat_fs_ProbeResults *new_fsPR;     /* ptr for new struct  */
3340     int i, j;
3341     int bufslot;
3342     int numfs;
3343
3344
3345     if (afsmon_debug) {
3346         fprintf(debugFD, "[ %s ] Called\n", rn);
3347         fflush(debugFD);
3348     }
3349
3350     /* allocate memory for the circular buffer of pointers */
3351
3352     afsmon_fs_ResultsCB = (struct afsmon_fs_Results_CBuffer *)
3353         malloc(sizeof(struct afsmon_fs_Results_CBuffer) * num_bufSlots);
3354
3355     /* initialize the fs circular buffer */
3356     for (i = 0; i < num_bufSlots; i++) {
3357         afsmon_fs_ResultsCB[i].list = (struct afsmon_fs_Results_list *)0;
3358         afsmon_fs_ResultsCB[i].probeNum = 0;
3359     }
3360
3361     /* create  a list of numFS items to store fs probe results for 
3362      * each slot in CB */
3363
3364     if (numFS) {                /* if we have file servers to monitor */
3365         for (bufslot = 0; bufslot < num_bufSlots; bufslot++) {
3366             numfs = numFS;      /* get the number of servers */
3367             while (numfs--) {
3368
3369                 /* if any of these mallocs fail we only need to free the memory we
3370                  * have allocated in this iteration. the rest of it which is in a 
3371                  * proper linked list will be freed in afsmon_Exit */
3372
3373                 /* allocate memory for an fs list item */
3374                 new_fslist_item = (struct afsmon_fs_Results_list *)
3375                     malloc(sizeof(struct afsmon_fs_Results_list));
3376                 if (new_fslist_item == (struct afsmon_fs_Results_list *)0)
3377                     return (-1);
3378
3379                 for (i = 0; i < MAX_NUM_FS_COLLECTIONS; i++) {
3380                     /* allocate memory to store xstat_fs_Results */
3381                     new_fsPR = (struct xstat_fs_ProbeResults *)
3382                         malloc(sizeof(struct xstat_fs_ProbeResults));
3383                     if (!new_fsPR) {
3384                         free(new_fslist_item);
3385                         return (-1);
3386                     }
3387
3388                     new_fsPR->connP = (struct xstat_fs_ConnectionInfo *)
3389                         malloc(sizeof(struct xstat_fs_ConnectionInfo));
3390                     if (new_fsPR->connP == (struct xstat_fs_ConnectionInfo *)0) {
3391                         free(new_fslist_item);
3392                         free(new_fsPR);
3393                         return (-1);
3394                     }
3395
3396                     /* >>>  need to allocate rx connection info structure here <<< */
3397                     new_fsPR->data.AFS_CollData_val = (afs_int32 *)
3398                        malloc(afsmon_fs_results_length[i] * sizeof(afs_int32));
3399                     if (new_fsPR->data.AFS_CollData_val == NULL) {
3400                        free(new_fslist_item);
3401                        free(new_fsPR->connP);
3402                        free(new_fsPR);
3403                        return (-1);
3404                     }
3405                     new_fslist_item->fsResults[i] = new_fsPR;
3406                     new_fslist_item->empty[i] = 1;
3407                 }
3408
3409                 /* initialize this list entry */
3410                 new_fslist_item->next = (struct afsmon_fs_Results_list *)0;
3411
3412                 /* store it at the end of the fs list in the current CB slot */
3413                 if (afsmon_fs_ResultsCB[bufslot].list ==
3414                     (struct afsmon_fs_Results_list *)0)
3415                     afsmon_fs_ResultsCB[bufslot].list = new_fslist_item;
3416                 else {
3417                     tmp_fslist_item = afsmon_fs_ResultsCB[bufslot].list;
3418                     j = 0;
3419                     while (tmp_fslist_item !=
3420                            (struct afsmon_fs_Results_list *)0) {
3421                         if (tmp_fslist_item->next ==
3422 &nbs