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