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