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