xstat: add xstat_*_Wait functions
[openafs.git] / src / xstat / xstat_fs.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  *
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 /*
11  * Description:
12  *      Implementation of the client side of the AFS File Server extended
13  *      statistics facility.
14  *
15  *------------------------------------------------------------------------*/
16
17 #include <afsconfig.h>
18 #include <afs/param.h>
19
20 #include <roken.h>
21
22 #include "xstat_fs.h"           /*Interface for this module */
23 #include <lwp.h>                /*Lightweight process package */
24
25 #include <afs/afsutil.h>
26 #include <afs/afscbint.h>
27
28 #define LWP_STACK_SIZE  (16 * 1024)
29
30 /*
31  * Exported variables.
32  */
33 int xstat_fs_numServers;        /*Num connected servers */
34 struct xstat_fs_ConnectionInfo
35  *xstat_fs_ConnInfo;            /*Ptr to connection array */
36 int numCollections;             /*Number of data collections */
37 struct xstat_fs_ProbeResults xstat_fs_Results;  /*Latest probe results */
38 char terminationEvent;          /*One-shot termination event */
39
40 afs_int32 xstat_fsData[AFS_MAX_XSTAT_LONGS];    /*Buffer for collected data */
41
42 /*
43  * Private globals.
44  */
45 static int xstat_fs_ProbeFreqInSecs;    /*Probe freq. in seconds */
46 static int xstat_fs_initflag = 0;       /*Was init routine called? */
47 static int xstat_fs_debug = 0;  /*Debugging output enabled? */
48 static int xstat_fs_oneShot = 0;        /*One-shot operation? */
49 static int (*xstat_fs_Handler) (void);  /*Probe handler routine */
50 static PROCESS probeLWP_ID;     /*Probe LWP process ID */
51 static int xstat_fs_numCollections;     /*Number of desired collections */
52 static afs_int32 *xstat_fs_collIDP;     /*Ptr to collection IDs desired */
53
54
55 /*------------------------------------------------------------------------
56  * [private] xstat_fs_CleanupInit
57  *
58  * Description:
59  *      Set up for recovery after an error in initialization (i.e.,
60  *      during a call to xstat_fs_Init.
61  *
62  * Arguments:
63  *      None.
64  *
65  * Returns:
66  *      0 on success,
67  *      Error value otherwise.
68  *
69  * Environment:
70  *      This routine is private to the module.
71  *
72  * Side Effects:
73  *      Zeros out basic data structures.
74  *------------------------------------------------------------------------*/
75
76 static int
77 xstat_fs_CleanupInit(void)
78 {
79     afs_int32 code;             /*Return code from callback stubs */
80     struct rx_call *rxcall;     /*Bogus param */
81     AFSCBFids *Fids_Array;      /*Bogus param */
82     AFSCBs *CallBack_Array;     /*Bogus param */
83
84     xstat_fs_ConnInfo = (struct xstat_fs_ConnectionInfo *)0;
85     xstat_fs_Results.probeNum = 0;
86     xstat_fs_Results.probeTime = 0;
87     xstat_fs_Results.connP = (struct xstat_fs_ConnectionInfo *)0;
88     xstat_fs_Results.collectionNumber = 0;
89     xstat_fs_Results.data.AFS_CollData_len = AFS_MAX_XSTAT_LONGS;
90     xstat_fs_Results.data.AFS_CollData_val = (afs_int32 *) xstat_fsData;
91     xstat_fs_Results.probeOK = 0;
92
93     rxcall = (struct rx_call *)0;
94     Fids_Array = (AFSCBFids *) 0;
95     CallBack_Array = (AFSCBs *) 0;
96
97     /*
98      * Call each of the callback routines our module provides (in
99      * xstat_fs_callback.c) to make sure they're all there.
100      */
101     code = SRXAFSCB_CallBack(rxcall, Fids_Array, CallBack_Array);
102     if (code)
103         return (code);
104     code = SRXAFSCB_InitCallBackState3(rxcall, (afsUUID *) 0);
105     if (code)
106         return (code);
107     code = SRXAFSCB_Probe(rxcall);
108     return (code);
109 }
110
111
112 /*------------------------------------------------------------------------
113  * [exported] xstat_fs_Cleanup
114  *
115  * Description:
116  *      Clean up our memory and connection state.
117  *
118  * Arguments:
119  *      int a_releaseMem : Should we free up malloc'ed areas?
120  *
121  * Returns:
122  *      0 on total success,
123  *      -1 if the module was never initialized, or there was a problem
124  *              with the xstat_fs connection array.
125  *
126  * Environment:
127  *      xstat_fs_numServers should be properly set.  We don't do anything
128  *      unless xstat_fs_Init() has already been called.
129  *
130  * Side Effects:
131  *      Shuts down Rx connections gracefully, frees allocated space
132  *      (if so directed).
133  *------------------------------------------------------------------------*/
134
135 int
136 xstat_fs_Cleanup(int a_releaseMem)
137 {
138     static char rn[] = "xstat_fs_Cleanup";      /*Routine name */
139     int code;                   /*Return code */
140     int conn_idx;               /*Current connection index */
141     struct xstat_fs_ConnectionInfo *curr_conn;  /*Ptr to xstat_fs connection */
142
143     /*
144      * Assume the best, but check the worst.
145      */
146     if (!xstat_fs_initflag) {
147         fprintf(stderr, "[%s] Refused; module not initialized\n", rn);
148         return (-1);
149     } else
150         code = 0;
151
152     /*
153      * Take care of all Rx connections first.  Check to see that the
154      * server count is a legal value.
155      */
156     if (xstat_fs_numServers <= 0) {
157         fprintf(stderr,
158                 "[%s] Illegal number of servers (xstat_fs_numServers = %d)\n",
159                 rn, xstat_fs_numServers);
160         code = -1;
161     } else {
162         if (xstat_fs_ConnInfo != (struct xstat_fs_ConnectionInfo *)0) {
163             /*
164              * The xstat_fs connection structure array exists.  Go through
165              * it and close up any Rx connections it holds.
166              */
167             curr_conn = xstat_fs_ConnInfo;
168             for (conn_idx = 0; conn_idx < xstat_fs_numServers; conn_idx++) {
169                 if (curr_conn->rxconn != (struct rx_connection *)0) {
170                     rx_DestroyConnection(curr_conn->rxconn);
171                     curr_conn->rxconn = (struct rx_connection *)0;
172                 }
173                 curr_conn++;
174             }                   /*for each xstat_fs connection */
175         }                       /*xstat_fs connection structure exists */
176     }                           /*Legal number of servers */
177
178     /*
179      * If asked to, release the space we've allocated.
180      */
181     if (a_releaseMem) {
182         if (xstat_fs_ConnInfo != (struct xstat_fs_ConnectionInfo *)0)
183             free(xstat_fs_ConnInfo);
184     }
185
186     /*
187      * Return the news, whatever it is.
188      */
189     return (code);
190 }
191
192
193 /*------------------------------------------------------------------------
194  * [private] xstat_fs_LWP
195  *
196  * Description:
197  *      This LWP iterates over the server connections and gathers up
198  *      the desired statistics from each one on a regular basis.  When
199  *      the sweep is done, the associated handler function is called
200  *      to process the new data.
201  *
202  * Arguments:
203  *      None.
204  *
205  * Returns:
206  *      Nothing.
207  *
208  * Environment:
209  *      Started by xstat_fs_Init(), uses global structures and the
210  *      global private xstat_fs_oneShot variable.
211  *
212  * Side Effects:
213  *      Nothing interesting.
214  *------------------------------------------------------------------------*/
215
216 static void *
217 xstat_fs_LWP(void *unused)
218 {
219     static char rn[] = "xstat_fs_LWP";  /*Routine name */
220     afs_int32 code;     /*Results of calls */
221     int oneShotCode;            /*Result of one-shot signal */
222     struct timeval tv;          /*Time structure */
223     int conn_idx;               /*Connection index */
224     struct xstat_fs_ConnectionInfo *curr_conn;  /*Current connection */
225     afs_int32 srvVersionNumber; /*Xstat version # */
226     afs_int32 clientVersionNumber;      /*Client xstat version */
227     afs_int32 numColls;         /*Number of collections to get */
228     afs_int32 *currCollIDP;     /*Curr collection ID desired */
229
230     /*
231      * Set up some numbers we'll need.
232      */
233     clientVersionNumber = AFS_XSTAT_VERSION;
234
235     while (1) {                 /*Service loop */
236         /*
237          * Iterate through the server connections, gathering data.
238          * Don't forget to bump the probe count and zero the statistics
239          * areas before calling the servers.
240          */
241         if (xstat_fs_debug)
242             printf("[%s] Waking up, getting data from %d server(s)\n", rn,
243                    xstat_fs_numServers);
244         curr_conn = xstat_fs_ConnInfo;
245         xstat_fs_Results.probeNum++;
246
247         for (conn_idx = 0; conn_idx < xstat_fs_numServers; conn_idx++) {
248             /*
249              * Grab the statistics for the current File Server, if the
250              * connection is valid.
251              */
252             if (xstat_fs_debug)
253                 printf("[%s] Getting collections from File Server '%s'\n", rn,
254                        curr_conn->hostName);
255             if (curr_conn->rxconn != (struct rx_connection *)0) {
256                 if (xstat_fs_debug)
257                     printf("[%s] Connection OK, calling RXAFS_GetXStats\n",
258                            rn);
259
260                 currCollIDP = xstat_fs_collIDP;
261                 for (numColls = 0; numColls < xstat_fs_numCollections;
262                      numColls++, currCollIDP++) {
263                     /*
264                      * Initialize the per-probe values.
265                      */
266                     if (xstat_fs_debug)
267                         printf("[%s] Asking for data collection %d\n", rn,
268                                *currCollIDP);
269                     xstat_fs_Results.collectionNumber = *currCollIDP;
270                     xstat_fs_Results.data.AFS_CollData_len =
271                         AFS_MAX_XSTAT_LONGS;
272                     memset(xstat_fs_Results.data.AFS_CollData_val, 0,
273                            AFS_MAX_XSTAT_LONGS * 4);
274
275                     xstat_fs_Results.connP = curr_conn;
276
277                     if (xstat_fs_debug) {
278                         printf
279                             ("%s: Calling RXAFS_GetXStats, conn=%" AFS_PTR_FMT ", clientVersionNumber=%d, collectionNumber=%d, srvVersionNumberP=%" AFS_PTR_FMT ", timeP=%" AFS_PTR_FMT ", dataP=%" AFS_PTR_FMT "\n",
280                              rn, curr_conn->rxconn, clientVersionNumber,
281                              *currCollIDP, &srvVersionNumber,
282                              &(xstat_fs_Results.probeTime),
283                              &(xstat_fs_Results.data));
284                         printf("%s: [bufflen=%d, buffer at %" AFS_PTR_FMT "]\n", rn,
285                                xstat_fs_Results.data.AFS_CollData_len,
286                                xstat_fs_Results.data.AFS_CollData_val);
287                     }
288
289                     xstat_fs_Results.probeOK =
290                         RXAFS_GetXStats(curr_conn->rxconn,
291                                         clientVersionNumber, *currCollIDP,
292                                         &srvVersionNumber,
293                                         &(xstat_fs_Results.probeTime),
294                                         &(xstat_fs_Results.data));
295
296                     /*
297                      * Now that we (may) have the data for this connection,
298                      * call the associated handler function.  The handler does
299                      * not take any explicit parameters, but rather gets to the
300                      * goodies via some of the objects exported by this module.
301                      */
302                     if (xstat_fs_debug)
303                         printf("[%s] Calling handler routine.\n", rn);
304                     code = xstat_fs_Handler();
305                     if (code)
306                         fprintf(stderr,
307                                 "[%s] Handler returned error code %d\n", rn,
308                                 code);
309
310                 }               /*For each collection */
311             }
312
313             /*Valid Rx connection */
314             /*
315              * Advance the xstat_fs connection pointer.
316              */
317             curr_conn++;
318
319         }                       /*For each xstat_fs connection */
320
321         /*
322          * All (valid) connections have been probed.  Fall asleep for the
323          * prescribed number of seconds, unless we're a one-shot.  In
324          * that case, we need to signal our caller that we're done.
325          */
326         if (xstat_fs_debug)
327             printf("[%s] Polling complete for probe round %d.\n", rn,
328                    xstat_fs_Results.probeNum);
329
330         if (xstat_fs_oneShot) {
331             /*
332              * One-shot execution desired.  Signal our main procedure
333              * that we've finished our collection round.
334              */
335             if (xstat_fs_debug)
336                 printf("[%s] Signalling main process at %" AFS_PTR_FMT "\n", rn,
337                        &terminationEvent);
338             oneShotCode = LWP_SignalProcess(&terminationEvent);
339             if (oneShotCode)
340                 fprintf(stderr, "[%s] Error %d from LWP_SignalProcess()", rn,
341                         oneShotCode);
342             break;              /*from the perpetual while loop */
343         } /*One-shot execution */
344         else {
345             /*
346              * Continuous execution desired.  Sleep for the required
347              * number of seconds.
348              */
349             tv.tv_sec = xstat_fs_ProbeFreqInSecs;
350             tv.tv_usec = 0;
351             if (xstat_fs_debug)
352                 printf("[%s] Falling asleep for %d seconds\n", rn,
353                        xstat_fs_ProbeFreqInSecs);
354             code = IOMGR_Select(0,      /*Num fids */
355                                 0,      /*Descs ready for reading */
356                                 0,      /*Descs ready for writing */
357                                 0,      /*Descs w/exceptional conditions */
358                                 &tv);   /*Ptr to timeout structure */
359             if (code)
360                 fprintf(stderr, "[%s] IOMGR_Select returned code %d\n", rn,
361                         code);
362         }                       /*Continuous execution */
363     }                           /*Service loop */
364     return NULL;
365 }
366
367 /*------------------------------------------------------------------------
368  * [exported] xstat_fs_Init
369  *
370  * Description:
371  *      Initialize the xstat_fs module: set up Rx connections to the
372  *      given set of File Servers, start up the probe and callback LWPs,
373  *      and associate the routine to be called when a probe completes.
374  *      Also, let it know which collections you're interested in.
375  *
376  * Arguments:
377  *      int a_numServers                  : Num. servers to connect to.
378  *      struct sockaddr_in *a_socketArray : Array of server sockets.
379  *      int a_ProbeFreqInSecs             : Probe frequency in seconds.
380  *      int (*a_ProbeHandler)()           : Ptr to probe handler fcn.
381  *      int a_flags                       : Various flags.
382  *      int a_numCollections              : Number of collections desired.
383  *      afs_int32 *a_collIDP                      : Ptr to collection IDs.
384  *
385  * Returns:
386  *      0 on success,
387  *      -2 for (at least one) connection error,
388  *      LWP process creation code, if it failed,
389  *      -1 for other fatal errors.
390  *
391  * Environment:
392  *      *** MUST BE THE FIRST ROUTINE CALLED FROM THIS PACKAGE ***
393  *      Also, the server security object CBsecobj MUST be a static,
394  *      since it has to stick around after this routine exits.
395  *
396  * Side Effects:
397  *      Sets up just about everything.
398  *------------------------------------------------------------------------*/
399
400 int
401 xstat_fs_Init(int a_numServers, struct sockaddr_in *a_socketArray,
402               int a_ProbeFreqInSecs, int (*a_ProbeHandler) (void), int a_flags,
403               int a_numCollections, afs_int32 * a_collIDP)
404 {
405     static char rn[] = "xstat_fs_Init"; /*Routine name */
406     afs_int32 code;     /*Return value */
407     static struct rx_securityClass *CBsecobj;   /*Callback security object */
408     struct rx_securityClass *secobj;    /*Client security object */
409     struct rx_service *rxsrv_afsserver; /*Server for AFS */
410     int arg_errfound;           /*Argument error found? */
411     int curr_srv;               /*Current server idx */
412     struct xstat_fs_ConnectionInfo *curr_conn;  /*Ptr to current conn */
413     char *hostNameFound;        /*Ptr to returned host name */
414     int conn_err;               /*Connection error? */
415     int collIDBytes;            /*Num bytes in coll ID array */
416     char hoststr[16];
417
418     /*
419      * If we've already been called, snicker at the bozo, gently
420      * remind him of his doubtful heritage, and return success.
421      */
422     if (xstat_fs_initflag) {
423         fprintf(stderr, "[%s] Called multiple times!\n", rn);
424         return (0);
425     } else
426         xstat_fs_initflag = 1;
427
428     /*
429      * Check the parameters for bogosities.
430      */
431     arg_errfound = 0;
432     if (a_numServers <= 0) {
433         fprintf(stderr, "[%s] Illegal number of servers: %d\n", rn,
434                 a_numServers);
435         arg_errfound = 1;
436     }
437     if (a_socketArray == (struct sockaddr_in *)0) {
438         fprintf(stderr, "[%s] Null server socket array argument\n", rn);
439         arg_errfound = 1;
440     }
441     if (a_ProbeFreqInSecs <= 0) {
442         fprintf(stderr, "[%s] Illegal probe frequency: %d\n", rn,
443                 a_ProbeFreqInSecs);
444         arg_errfound = 1;
445     }
446     if (a_ProbeHandler == (int (*)())0) {
447         fprintf(stderr, "[%s] Null probe handler function argument\n", rn);
448         arg_errfound = 1;
449     }
450     if (a_numCollections <= 0) {
451         fprintf(stderr, "[%s] Illegal collection count argument: %d\n", rn,
452                 a_numServers);
453         arg_errfound = 1;
454     }
455     if (a_collIDP == NULL) {
456         fprintf(stderr, "[%s] Null collection ID array argument\n", rn);
457         arg_errfound = 1;
458     }
459     if (arg_errfound)
460         return (-1);
461
462     /*
463      * Record our passed-in info.
464      */
465     xstat_fs_debug = (a_flags & XSTAT_FS_INITFLAG_DEBUGGING);
466     xstat_fs_oneShot = (a_flags & XSTAT_FS_INITFLAG_ONE_SHOT);
467     xstat_fs_numServers = a_numServers;
468     xstat_fs_Handler = a_ProbeHandler;
469     xstat_fs_ProbeFreqInSecs = a_ProbeFreqInSecs;
470     xstat_fs_numCollections = a_numCollections;
471     collIDBytes = xstat_fs_numCollections * sizeof(afs_int32);
472     xstat_fs_collIDP = malloc(collIDBytes);
473     memcpy(xstat_fs_collIDP, a_collIDP, collIDBytes);
474     if (xstat_fs_debug) {
475         printf("[%s] Asking for %d collection(s): ", rn,
476                xstat_fs_numCollections);
477         for (curr_srv = 0; curr_srv < xstat_fs_numCollections; curr_srv++)
478             printf("%d ", *(xstat_fs_collIDP + curr_srv));
479         printf("\n");
480     }
481
482     /*
483      * Get ready in case we have to do a cleanup - basically, zero
484      * everything out.
485      */
486     code = xstat_fs_CleanupInit();
487     if (code)
488         return (code);
489
490     /*
491      * Allocate the necessary data structures and initialize everything
492      * else.
493      */
494     xstat_fs_ConnInfo = malloc(a_numServers
495                                * sizeof(struct xstat_fs_ConnectionInfo));
496     if (xstat_fs_ConnInfo == (struct xstat_fs_ConnectionInfo *)0) {
497         fprintf(stderr,
498                 "[%s] Can't allocate %d connection info structs (%" AFS_SIZET_FMT " bytes)\n",
499                 rn, a_numServers,
500                 (a_numServers * sizeof(struct xstat_fs_ConnectionInfo)));
501         return (-1);            /*No cleanup needs to be done yet */
502     }
503
504     /*
505      * Initialize the Rx subsystem, just in case nobody's done it.
506      */
507     if (xstat_fs_debug)
508         printf("[%s] Initializing Rx\n", rn);
509     code = rx_Init(0);
510     if (code) {
511         fprintf(stderr, "[%s] Fatal error in rx_Init()\n", rn);
512         return (-1);
513     }
514     if (xstat_fs_debug)
515         printf("[%s] Rx initialized\n", rn);
516
517     /*
518      * Create a null Rx server security object, to be used by the
519      * Callback listener.
520      */
521     CBsecobj = (struct rx_securityClass *)
522         rxnull_NewServerSecurityObject();
523     if (CBsecobj == (struct rx_securityClass *)0) {
524         fprintf(stderr,
525                 "[%s] Can't create callback listener's security object.\n",
526                 rn);
527         xstat_fs_Cleanup(1);    /*Delete already-malloc'ed areas */
528         return (-1);
529     }
530     if (xstat_fs_debug)
531         printf("[%s] Callback server security object created\n", rn);
532
533     /*
534      * Create a null Rx client security object, to be used by the
535      * probe LWP.
536      */
537     secobj = rxnull_NewClientSecurityObject();
538     if (secobj == (struct rx_securityClass *)0) {
539         fprintf(stderr,
540                 "[%s] Can't create probe LWP client security object.\n", rn);
541         xstat_fs_Cleanup(1);    /*Delete already-malloc'ed areas */
542         return (-1);
543     }
544     if (xstat_fs_debug)
545         printf("[%s] Probe LWP client security object created\n", rn);
546
547     curr_conn = xstat_fs_ConnInfo;
548     conn_err = 0;
549     for (curr_srv = 0; curr_srv < a_numServers; curr_srv++) {
550         /*
551          * Copy in the socket info for the current server, resolve its
552          * printable name if possible.
553          */
554         if (xstat_fs_debug) {
555             char hoststr[16];
556             printf("[%s] Copying in the following socket info:\n", rn);
557             printf("[%s] IP addr %s, port %d\n", rn,
558                    afs_inet_ntoa_r((a_socketArray + curr_srv)->sin_addr.s_addr,hoststr),
559                    ntohs((a_socketArray + curr_srv)->sin_port));
560         }
561         memcpy(&(curr_conn->skt), a_socketArray + curr_srv,
562                sizeof(struct sockaddr_in));
563
564         hostNameFound =
565             hostutil_GetNameByINet(curr_conn->skt.sin_addr.s_addr);
566         if (hostNameFound == NULL) {
567             fprintf(stderr,
568                     "[%s] Can't map Internet address %s to a string name\n",
569                     rn, afs_inet_ntoa_r(curr_conn->skt.sin_addr.s_addr,hoststr));
570             curr_conn->hostName[0] = '\0';
571         } else {
572             strcpy(curr_conn->hostName, hostNameFound);
573             if (xstat_fs_debug)
574                 printf("[%s] Host name for server index %d is %s\n", rn,
575                        curr_srv, curr_conn->hostName);
576         }
577
578         /*
579          * Make an Rx connection to the current server.
580          */
581         if (xstat_fs_debug)
582             printf
583                 ("[%s] Connecting to srv idx %d, IP addr %s, port %d, service 1\n",
584                  rn, curr_srv, afs_inet_ntoa_r(curr_conn->skt.sin_addr.s_addr,hoststr),
585                  ntohs(curr_conn->skt.sin_port));
586
587         curr_conn->rxconn = rx_NewConnection(curr_conn->skt.sin_addr.s_addr,    /*Server addr */
588                                              curr_conn->skt.sin_port,   /*Server port */
589                                              1, /*AFS service # */
590                                              secobj,    /*Security obj */
591                                              0);        /*# of above */
592         if (curr_conn->rxconn == (struct rx_connection *)0) {
593             fprintf(stderr,
594                     "[%s] Can't create Rx connection to server '%s' (%s)\n",
595                     rn, curr_conn->hostName, afs_inet_ntoa_r(curr_conn->skt.sin_addr.s_addr,hoststr));
596             conn_err = 1;
597         }
598         if (xstat_fs_debug)
599             printf("[%s] New connection at %" AFS_PTR_FMT "\n", rn, curr_conn->rxconn);
600
601         /*
602          * Bump the current xstat_fs connection to set up.
603          */
604         curr_conn++;
605
606     }                           /*for curr_srv */
607
608     /*
609      * Create the AFS callback service (listener).
610      */
611     if (xstat_fs_debug)
612         printf("[%s] Creating AFS callback listener\n", rn);
613     rxsrv_afsserver = rx_NewService(0,  /*Use default port */
614                                     1,  /*Service ID */
615                                     "afs",      /*Service name */
616                                     &CBsecobj,  /*Ptr to security object(s) */
617                                     1,  /*# of security objects */
618                                     RXAFSCB_ExecuteRequest);    /*Dispatcher */
619     if (rxsrv_afsserver == (struct rx_service *)0) {
620         fprintf(stderr, "[%s] Can't create callback Rx service/listener\n",
621                 rn);
622         xstat_fs_Cleanup(1);    /*Delete already-malloc'ed areas */
623         return (-1);
624     }
625     if (xstat_fs_debug)
626         printf("[%s] Callback listener created\n", rn);
627
628     /*
629      * Start up the AFS callback service.
630      */
631     if (xstat_fs_debug)
632         printf("[%s] Starting up callback listener.\n", rn);
633     rx_StartServer(0);          /*Don't donate yourself to LWP pool */
634
635     /*
636      * Start up the probe LWP.
637      */
638     if (xstat_fs_debug)
639         printf("[%s] Creating the probe LWP\n", rn);
640     code = LWP_CreateProcess(xstat_fs_LWP,      /*Function to start up */
641                              LWP_STACK_SIZE,    /*Stack size in bytes */
642                              1, /*Priority */
643                              (void *)0, /*Parameters */
644                              "xstat_fs Worker", /*Name to use */
645                              &probeLWP_ID);     /*Returned LWP process ID */
646     if (code) {
647         fprintf(stderr, "[%s] Can't create xstat_fs LWP!  Error is %d\n", rn,
648                 code);
649         xstat_fs_Cleanup(1);    /*Delete already-malloc'ed areas */
650         return (code);
651     }
652     if (xstat_fs_debug)
653         printf("[%s] Probe LWP process structure located at %" AFS_PTR_FMT "\n", rn,
654                probeLWP_ID);
655
656     /*
657      * Return the final results.
658      */
659     if (conn_err)
660         return (-2);
661     else
662         return (0);
663 }
664
665
666 /*------------------------------------------------------------------------
667  * [exported] xstat_fs_ForceProbeNow
668  *
669  * Description:
670  *      Wake up the probe LWP, forcing it to execute a probe immediately.
671  *
672  * Arguments:
673  *      None.
674  *
675  * Returns:
676  *      0 on success,
677  *      Error value otherwise.
678  *
679  * Environment:
680  *      The module must have been initialized.
681  *
682  * Side Effects:
683  *      As advertised.
684  *------------------------------------------------------------------------*/
685
686 int
687 xstat_fs_ForceProbeNow(void)
688 {
689     static char rn[] = "xstat_fs_ForceProbeNow";        /*Routine name */
690
691     /*
692      * There isn't a prayer unless we've been initialized.
693      */
694     if (!xstat_fs_initflag) {
695         fprintf(stderr, "[%s] Must call xstat_fs_Init first!\n", rn);
696         return (-1);
697     }
698
699     /*
700      * Kick the sucker in the side.
701      */
702     IOMGR_Cancel(probeLWP_ID);
703
704     /*
705      * We did it, so report the happy news.
706      */
707     return (0);
708 }
709
710 /**
711  * Fill the xstat full perf data structure from the data collection array.
712  *
713  * This function is a client-side decoding of the non-portable xstat_fs full
714  * performance data.  The full perf structure includes timeval structures,
715  * which have platform dependent size.
716  *
717  * To make things even more interesting, the word ordering of the time
718  * values on hosts with 64-bit time depend on endianess. The ordering
719  * within a given afs_int32 is handled by xdr.
720  *
721  * @param[out] aout an address to a stats structure pointer
722  * @param[in] ain array of int32s received
723  * @param[in] alen length of ain
724  * @param[inout] abuf a buffer provided by the caller
725  *
726  * @return 0 on success
727  */
728 int
729 xstat_fs_DecodeFullPerfStats(struct fs_stats_FullPerfStats **aout,
730                              afs_int32 * ain,
731                              afs_int32 alen,
732                              struct fs_stats_FullPerfStats *abuf)
733 {
734     int i;
735     afs_int32 *p;
736     int snbo = -2;      /* detected remote site has network-byte ordering */
737
738     static const int XSTAT_FPS_LEN = sizeof(struct fs_stats_FullPerfStats) / sizeof(afs_int32); /* local size of fps */
739     static const int XSTAT_FPS_SMALL = 424;     /**< fps size when sizeof(timeval) is 2*sizeof(afs_int32) */
740     static const int XSTAT_FPS_LARGE = 666;     /**< fps size when sizeof(timeval) is 2*sizeof(afs_int64) */
741
742 #define DECODE_TV(t) \
743     do { \
744         if (alen == XSTAT_FPS_SMALL) { \
745             (t).tv_sec = *p++; \
746             (t).tv_usec = *p++; \
747         } else { \
748             if (snbo) { \
749                 p++; \
750                 (t).tv_sec = *p++; \
751                 p++; \
752                 (t).tv_usec = *p++; \
753             } else { \
754                 (t).tv_sec = *p++; \
755                 p++; \
756                 (t).tv_usec = *p++; \
757                 p++; \
758             } \
759         } \
760     } while (0)
761
762     if (alen != XSTAT_FPS_SMALL && alen != XSTAT_FPS_LARGE) {
763         return -1;              /* unrecognized size */
764     }
765
766     if (alen == XSTAT_FPS_LEN && alen == XSTAT_FPS_SMALL) {
767         /* Same size, and xdr dealt with byte ordering; no decoding needed. */
768         *aout = (struct fs_stats_FullPerfStats *)ain;
769         return 0;
770     }
771
772     if (alen == XSTAT_FPS_LARGE) {
773         /* Attempt to detect the word ordering of the time values. */
774         struct fs_stats_FullPerfStats *fps =
775             (struct fs_stats_FullPerfStats *)ain;
776         afs_int32 *epoch = (afs_int32 *) & (fps->det.epoch);
777         if (epoch[0] == 0 && epoch[1] != 0) {
778             snbo = 1;
779         } else if (epoch[0] != 0 && epoch[1] == 0) {
780             snbo = 0;
781         } else {
782             return -2;          /* failed to detect server word ordering */
783         }
784     }
785
786     if (alen == XSTAT_FPS_LEN && alen == XSTAT_FPS_LARGE
787 #if defined(WORDS_BIGENDIAN)
788         && snbo
789 #else /* WORDS_BIGENDIAN */
790         && !snbo
791 #endif /* WORDS_BIGENDIAN */
792         ) {
793         /* Same size and order; no decoding needed. */
794         *aout = (struct fs_stats_FullPerfStats *)ain;
795         return 0;
796     }
797
798     /* Either different sizes, or different ordering, or both. Schlep over
799      * each field, decoding time values. The fields up to the first time value
800      * can be copied in bulk. */
801     if (xstat_fs_debug) {
802         printf("debug: Decoding xstat full perf stats; length=%d", alen);
803         if (alen == XSTAT_FPS_LARGE) {
804             printf(", order='%s'", (snbo ? "big-endian" : "little-endian"));
805         }
806         printf("\n");
807     }
808
809     p = ain;
810     memset(abuf, 0, sizeof(struct fs_stats_FullPerfStats));
811     memcpy(abuf, p, sizeof(struct afs_PerfStats));
812     p += sizeof(struct afs_PerfStats) / sizeof(afs_int32);
813
814     DECODE_TV(abuf->det.epoch);
815     for (i = 0; i < FS_STATS_NUM_RPC_OPS; i++) {
816         struct fs_stats_opTimingData *td = abuf->det.rpcOpTimes + i;
817         td->numOps = *p++;
818         td->numSuccesses = *p++;
819         DECODE_TV(td->sumTime);
820         DECODE_TV(td->sqrTime);
821         DECODE_TV(td->minTime);
822         DECODE_TV(td->maxTime);
823     }
824     for (i = 0; i < FS_STATS_NUM_XFER_OPS; i++) {
825         struct fs_stats_xferData *xd = abuf->det.xferOpTimes + i;
826         xd->numXfers = *p++;
827         xd->numSuccesses = *p++;
828         DECODE_TV(xd->sumTime);
829         DECODE_TV(xd->sqrTime);
830         DECODE_TV(xd->minTime);
831         DECODE_TV(xd->maxTime);
832         xd->sumBytes = *p++;
833         xd->minBytes = *p++;
834         xd->maxBytes = *p++;
835         memcpy((void *)xd->count, (void *)p,
836                sizeof(afs_int32) * FS_STATS_NUM_XFER_BUCKETS);
837         p += FS_STATS_NUM_XFER_BUCKETS;
838     }
839     *aout = abuf;
840     return 0;
841 #undef DECODE_TV
842 }
843
844 /*
845  * Wait for the collection to complete. Returns after one cycle if running in
846  * one-shot mode, otherwise wait for a given amount of time.
847  *
848  * Args:
849  *    int sleep_secs : time to wait in seconds when running
850  *                     in continuous mode. 0 means wait forever.
851  *
852  * Returns:
853  *    0 on success
854  */
855 int
856 xstat_fs_Wait(int sleep_secs)
857 {
858     static char rn[] = "xstat_fs_Wait"; /*Routine name */
859     int code;
860     struct timeval tv;          /*Time structure */
861
862     if (xstat_fs_oneShot) {
863         /*
864          * One-shot operation; just wait for the collection to be done.
865          */
866         if (xstat_fs_debug)
867             printf("[%s] Calling LWP_WaitProcess() on event %" AFS_PTR_FMT
868                    "\n", rn, &terminationEvent);
869         code = LWP_WaitProcess(&terminationEvent);
870         if (xstat_fs_debug)
871             printf("[%s] Returned from LWP_WaitProcess()\n", rn);
872         if (code) {
873             fprintf(stderr,
874                     "[%s] Error %d encountered by LWP_WaitProcess()\n",
875                     rn, code);
876         }
877     } else if (sleep_secs == 0) {
878         /* Sleep forever. */
879         tv.tv_sec = 24 * 60;
880         tv.tv_usec = 0;
881         if (xstat_fs_debug)
882             fprintf(stderr, "[ %s ] going to sleep ...\n", rn);
883         while (1) {
884             code = IOMGR_Select(0,      /*Num fds */
885                                 0,      /*Descriptors ready for reading */
886                                 0,      /*Descriptors ready for writing */
887                                 0,      /*Descriptors with exceptional conditions */
888                                 &tv);   /*Timeout structure */
889             if (code) {
890                 fprintf(stderr,
891                         "[ %s ] IOMGR_Select() returned non-zero value %d\n",
892                         rn, code);
893                 break;
894             }
895         }
896     } else {
897         /* Let's just fall asleep while.  */
898         if (xstat_fs_debug)
899             printf
900                 ("xstat_fs service started, main thread sleeping for %d secs.\n",
901                  sleep_secs);
902         tv.tv_sec = sleep_secs;
903         tv.tv_usec = 0;
904         code = IOMGR_Select(0,  /*Num fds */
905                             0,  /*Descriptors ready for reading */
906                             0,  /*Descriptors ready for writing */
907                             0,  /*Descriptors with exceptional conditions */
908                             &tv);       /*Timeout structure */
909         if (code)
910             fprintf(stderr,
911                     "[%s] IOMGR_Select() returned non-zero value: %d\n", rn,
912                     code);
913     }
914     return code;
915 }