9850d43f4837fb421974602b6a3e0db61b3c3939
[openafs.git] / src / xstat / xstat_cm.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 Cache Manager
13  *      extended 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_cm.h"           /*Interface for this module */
24 #include <pthread.h>
25
26 #include <afs/afsutil.h>
27
28 /*
29  * Exported variables.
30  */
31 int xstat_cm_numServers;        /*Num connected servers */
32 struct xstat_cm_ConnectionInfo
33  *xstat_cm_ConnInfo;            /*Ptr to connection array */
34 struct xstat_cm_ProbeResults xstat_cm_Results;  /*Latest probe results */
35
36 afs_int32 xstat_cmData[AFSCB_MAX_XSTAT_LONGS];  /*Buffer for collected data */
37
38 /*
39  * Private globals.
40  */
41 static int xstat_cm_ProbeFreqInSecs;    /*Probe freq. in seconds */
42 static int xstat_cm_initflag = 0;       /*Was init routine called? */
43 static int xstat_cm_debug = 0;  /*Debugging output enabled? */
44 static int xstat_cm_oneShot = 0;        /*One-shot operation? */
45 static int (*xstat_cm_Handler) (void);  /*Probe handler routine */
46 static pthread_t xstat_cm_thread;       /*Probe thread */
47 static int xstat_cm_numCollections;     /*Number of desired collections */
48 static afs_int32 *xstat_cm_collIDP;     /*Ptr to collection IDs desired */
49 static opr_mutex_t xstat_cm_force_lock; /*Lock to wakeup probe */
50 static opr_cv_t xstat_cm_force_cv;      /*Condvar to wakeup probe */
51
52
53 /*------------------------------------------------------------------------
54  * [private] xstat_cm_CleanupInit
55  *
56  * Description:
57  *      Set up for recovery after an error in initialization (i.e.,
58  *      during a call to xstat_cm_Init.
59  *
60  * Arguments:
61  *      None.
62  *
63  * Returns:
64  *      0 on success,
65  *      Error value otherwise.
66  *
67  * Environment:
68  *      This routine is private to the module.
69  *
70  * Side Effects:
71  *      Zeros out basic data structures.
72  *------------------------------------------------------------------------*/
73
74 static int
75 xstat_cm_CleanupInit(void)
76 {
77     xstat_cm_ConnInfo = (struct xstat_cm_ConnectionInfo *)0;
78     xstat_cm_Results.probeNum = 0;
79     xstat_cm_Results.probeTime = 0;
80     xstat_cm_Results.connP = (struct xstat_cm_ConnectionInfo *)0;
81     xstat_cm_Results.collectionNumber = 0;
82     xstat_cm_Results.data.AFSCB_CollData_len = AFSCB_MAX_XSTAT_LONGS;
83     xstat_cm_Results.data.AFSCB_CollData_val = (afs_int32 *) xstat_cmData;
84     xstat_cm_Results.probeOK = 0;
85
86     return (0);
87 }
88
89
90 /*------------------------------------------------------------------------
91  * [exported] xstat_cm_Cleanup
92  *
93  * Description:
94  *      Clean up our memory and connection state.
95  *
96  * Arguments:
97  *      int a_releaseMem : Should we free up malloc'ed areas?
98  *
99  * Returns:
100  *      0 on total success,
101  *      -1 if the module was never initialized, or there was a problem
102  *              with the xstat_cm connection array.
103  *
104  * Environment:
105  *      xstat_cm_numServers should be properly set.  We don't do anything
106  *      unless xstat_cm_Init() has already been called.
107  *
108  * Side Effects:
109  *      Shuts down Rx connections gracefully, frees allocated space
110  *      (if so directed).
111  *------------------------------------------------------------------------*/
112
113 int
114 xstat_cm_Cleanup(int a_releaseMem)
115 {
116     static char rn[] = "xstat_cm_Cleanup";      /*Routine name */
117     int code;                   /*Return code */
118     int conn_idx;               /*Current connection index */
119     struct xstat_cm_ConnectionInfo *curr_conn;  /*Ptr to xstat_cm connection */
120
121     /*
122      * Assume the best, but check the worst.
123      */
124     if (!xstat_cm_initflag) {
125         fprintf(stderr, "[%s] Refused; module not initialized\n", rn);
126         return (-1);
127     } else
128         code = 0;
129
130     /*
131      * Take care of all Rx connections first.  Check to see that the
132      * server count is a legal value.
133      */
134     if (xstat_cm_numServers <= 0) {
135         fprintf(stderr,
136                 "[%s] Illegal number of servers (xstat_cm_numServers = %d)\n",
137                 rn, xstat_cm_numServers);
138         code = -1;
139     } else {
140         if (xstat_cm_ConnInfo != (struct xstat_cm_ConnectionInfo *)0) {
141             /*
142              * The xstat_cm connection structure array exists.  Go through
143              * it and close up any Rx connections it holds.
144              */
145             curr_conn = xstat_cm_ConnInfo;
146             for (conn_idx = 0; conn_idx < xstat_cm_numServers; conn_idx++) {
147                 if (curr_conn->rxconn != (struct rx_connection *)0) {
148                     rx_DestroyConnection(curr_conn->rxconn);
149                     curr_conn->rxconn = (struct rx_connection *)0;
150                 }
151                 curr_conn++;
152             }                   /*for each xstat_cm connection */
153         }                       /*xstat_cm connection structure exists */
154     }                           /*Legal number of servers */
155
156     /*
157      * If asked to, release the space we've allocated.
158      */
159     if (a_releaseMem) {
160         if (xstat_cm_ConnInfo != (struct xstat_cm_ConnectionInfo *)0)
161             free(xstat_cm_ConnInfo);
162     }
163
164     /*
165      * Return the news, whatever it is.
166      */
167     return (code);
168 }
169
170
171 /*------------------------------------------------------------------------
172  * [private] xstat_cm_LWP
173  *
174  * Description:
175  *      This thread iterates over the server connections and gathers up
176  *      the desired statistics from each one on a regular basis, for
177  *      all known data collections.  The associated handler function
178  *      is called each time a new data collection is received.
179  *
180  * Arguments:
181  *      None.
182  *
183  * Returns:
184  *      Nothing.
185  *
186  * Environment:
187  *      Started by xstat_cm_Init(), uses global structures and the
188  *      global private xstat_cm_oneShot variable.
189  *
190  * Side Effects:
191  *      As advertised.
192  *------------------------------------------------------------------------*/
193 static void *
194 xstat_cm_LWP(void *unused)
195 {
196     static char rn[] = "xstat_cm_LWP";  /*Routine name */
197     afs_int32 code;     /*Results of calls */
198     struct timeval tv;          /*Time structure */
199     struct timespec wait;       /*Time to wait */
200     int conn_idx;               /*Connection index */
201     struct xstat_cm_ConnectionInfo *curr_conn;  /*Current connection */
202     afs_int32 srvVersionNumber; /*Xstat version # */
203     afs_int32 clientVersionNumber;      /*Client xstat version */
204     afs_int32 numColls;         /*Number of collections to get */
205     afs_int32 *currCollIDP;     /*Curr collection ID desired */
206
207     /*
208      * Set up some numbers we'll need.
209      */
210     clientVersionNumber = AFSCB_XSTAT_VERSION;
211
212     while (1) {                 /*Service loop */
213         /*
214          * Iterate through the server connections, gathering data.
215          * Don't forget to bump the probe count and zero the statistics
216          * areas before calling the servers.
217          */
218         if (xstat_cm_debug)
219             printf("[%s] Waking up, getting data from %d server(s)\n", rn,
220                    xstat_cm_numServers);
221         curr_conn = xstat_cm_ConnInfo;
222         xstat_cm_Results.probeNum++;
223
224         for (conn_idx = 0; conn_idx < xstat_cm_numServers; conn_idx++) {
225             /*
226              * Grab the statistics for the current Cache Manager, if the
227              * connection is valid.
228              */
229             if (xstat_cm_debug)
230                 printf("[%s] Getting collections from Cache Manager '%s'\n",
231                        rn, curr_conn->hostName);
232             if (curr_conn->rxconn != (struct rx_connection *)0) {
233                 if (xstat_cm_debug)
234                     printf("[%s] Connection OK, calling RXAFSCB_GetXStats\n",
235                            rn);
236
237                 /*
238                  * Probe the given CM for each desired collection.
239                  */
240                 currCollIDP = xstat_cm_collIDP;
241                 for (numColls = 0; numColls < xstat_cm_numCollections;
242                      numColls++, currCollIDP++) {
243                     /*
244                      * Initialize the per-probe values.
245                      */
246                     if (xstat_cm_debug)
247                         printf("[%s] Asking for data collection %d\n", rn,
248                                *currCollIDP);
249                     xstat_cm_Results.collectionNumber = *currCollIDP;
250                     xstat_cm_Results.data.AFSCB_CollData_len =
251                         AFSCB_MAX_XSTAT_LONGS;
252                     memset(xstat_cm_Results.data.AFSCB_CollData_val, 0,
253                            AFSCB_MAX_XSTAT_LONGS * 4);
254
255                     xstat_cm_Results.connP = curr_conn;
256
257                     if (xstat_cm_debug) {
258                         printf
259                             ("%s: Calling RXAFSCB_GetXStats, conn=%p, clientVersionNumber=%d, collectionNumber=%d, srvVersionNumberP=%p, timeP=%p, dataP=%p\n",
260                              rn, curr_conn->rxconn, clientVersionNumber,
261                              *currCollIDP, &srvVersionNumber,
262                              &(xstat_cm_Results.probeTime),
263                              &(xstat_cm_Results.data));
264                         printf("%s: [bufflen=%d, buffer at %p]\n", rn,
265                                xstat_cm_Results.data.AFSCB_CollData_len,
266                                xstat_cm_Results.data.AFSCB_CollData_val);
267                     }
268
269                     xstat_cm_Results.probeOK =
270                         RXAFSCB_GetXStats(curr_conn->rxconn,
271                                           clientVersionNumber, *currCollIDP,
272                                           &srvVersionNumber,
273                                           &(xstat_cm_Results.probeTime),
274                                           &(xstat_cm_Results.data));
275
276                     /*
277                      * Now that we (may) have the data for this connection,
278                      * call the associated handler function.  The handler
279                      * does not take any explicit parameters, but rather
280                      * gets to the goodies via some of the objects exported
281                      * by this module.
282                      */
283                     if (xstat_cm_debug)
284                         printf("[%s] Calling handler routine.\n", rn);
285                     code = xstat_cm_Handler();
286                     if (code)
287                         fprintf(stderr,
288                                 "[%s] Handler routine got error code %d\n",
289                                 rn, code);
290                 }               /*For each collection */
291             }
292
293             /*Valid Rx connection */
294             /*
295              * Advance the xstat_cm connection pointer.
296              */
297             curr_conn++;
298
299         }                       /*For each xstat_cm connection */
300
301         /*
302          * All (valid) connections have been probed.  Fall asleep for the
303          * prescribed number of seconds, unless we're a one-shot.  In
304          * that case, we need to signal our caller that we're done.
305          */
306         if (xstat_cm_debug)
307             printf("[%s] Polling complete for probe round %d.\n", rn,
308                    xstat_cm_Results.probeNum);
309
310         if (xstat_cm_oneShot) {
311             /*
312              * One-shot execution desired.
313              */
314             break;              /*from the perpetual while loop */
315         } /*One-shot execution */
316         else {
317             /*
318              * Continuous execution desired.  Sleep for the required
319              * number of seconds or wakeup sooner if forced.
320              */
321             if (xstat_cm_debug)
322                 printf("[%s] Falling asleep for %d seconds\n", rn,
323                        xstat_cm_ProbeFreqInSecs);
324             gettimeofday(&tv, NULL);
325             wait.tv_sec = tv.tv_sec + xstat_cm_ProbeFreqInSecs;
326             wait.tv_nsec = tv.tv_usec * 1000;
327             opr_mutex_enter(&xstat_cm_force_lock);
328             code = opr_cv_timedwait(&xstat_cm_force_cv, &xstat_cm_force_lock, &wait);
329             opr_Assert(code == 0 || code == ETIMEDOUT);
330             opr_mutex_exit(&xstat_cm_force_lock);
331         }                       /*Continuous execution */
332     }                           /*Service loop */
333     return NULL;
334 }
335
336
337 /*------------------------------------------------------------------------
338  * [exported] xstat_cm_Init
339  *
340  * Description:
341  *      Initialize the xstat_cm module: set up Rx connections to the
342  *      given set of Cache Managers, start up the probe thread, and
343  *      associate the routine to be called when a probe completes.
344  *      Also, let it know which collections you're interested in.
345  *
346  * Arguments:
347  *      int a_numServers                  : Num. servers to connect to.
348  *      struct sockaddr_in *a_socketArray : Array of server sockets.
349  *      int a_ProbeFreqInSecs             : Probe frequency in seconds.
350  *      int (*a_ProbeHandler)()           : Ptr to probe handler fcn.
351  *      int a_flags;                      : Various flags.
352  *      int a_numCollections              : Number of collections desired.
353  *      afs_int32 *a_collIDP                      : Ptr to collection IDs.
354  *
355  * Returns:
356  *      0 on success,
357  *      -2 for (at least one) connection error,
358  *      thread process creation code, if it failed,
359  *      -1 for other fatal errors.
360  *
361  * Environment:
362  *      *** MUST BE THE FIRST ROUTINE CALLED FROM THIS PACKAGE ***
363  *
364  * Side Effects:
365  *      Sets up just about everything.
366  *------------------------------------------------------------------------*/
367
368 int
369 xstat_cm_Init(int a_numServers, struct sockaddr_in *a_socketArray,
370               int a_ProbeFreqInSecs, int (*a_ProbeHandler) (void), int a_flags,
371               int a_numCollections, afs_int32 * a_collIDP)
372 {
373
374     static char rn[] = "xstat_cm_Init"; /*Routine name */
375     afs_int32 code;     /*Return value */
376     struct rx_securityClass *secobj;    /*Client security object */
377     int arg_errfound;           /*Argument error found? */
378     int curr_srv;               /*Current server idx */
379     struct xstat_cm_ConnectionInfo *curr_conn;  /*Ptr to current conn */
380     char *hostNameFound;        /*Ptr to returned host name */
381     int conn_err;               /*Connection error? */
382     int collIDBytes;            /*Num bytes in coll ID array */
383     char hoststr[16];
384
385     /*
386      * If we've already been called, snicker at the bozo, gently
387      * remind him of his doubtful heritage, and return success.
388      */
389     if (xstat_cm_initflag) {
390         fprintf(stderr, "[%s] Called multiple times!\n", rn);
391         return (0);
392     } else
393         xstat_cm_initflag = 1;
394
395     opr_mutex_init(&xstat_cm_force_lock);
396     opr_cv_init(&xstat_cm_force_cv);
397
398     /*
399      * Check the parameters for bogosities.
400      */
401     arg_errfound = 0;
402     if (a_numServers <= 0) {
403         fprintf(stderr, "[%s] Illegal number of servers: %d\n", rn,
404                 a_numServers);
405         arg_errfound = 1;
406     }
407     if (a_socketArray == (struct sockaddr_in *)0) {
408         fprintf(stderr, "[%s] Null server socket array argument\n", rn);
409         arg_errfound = 1;
410     }
411     if (a_ProbeFreqInSecs <= 0) {
412         fprintf(stderr, "[%s] Illegal probe frequency: %d\n", rn,
413                 a_ProbeFreqInSecs);
414         arg_errfound = 1;
415     }
416     if (a_ProbeHandler == NULL) {
417         fprintf(stderr, "[%s] Null probe handler function argument\n", rn);
418         arg_errfound = 1;
419     }
420     if (a_numCollections <= 0) {
421         fprintf(stderr, "[%s] Illegal collection count argument: %d\n", rn,
422                 a_numServers);
423         arg_errfound = 1;
424     }
425     if (a_collIDP == NULL) {
426         fprintf(stderr, "[%s] Null collection ID array argument\n", rn);
427         arg_errfound = 1;
428     }
429     if (arg_errfound)
430         return (-1);
431
432     /*
433      * Record our passed-in info.
434      */
435     xstat_cm_debug = (a_flags & XSTAT_CM_INITFLAG_DEBUGGING);
436     xstat_cm_oneShot = (a_flags & XSTAT_CM_INITFLAG_ONE_SHOT);
437     xstat_cm_numServers = a_numServers;
438     xstat_cm_Handler = a_ProbeHandler;
439     xstat_cm_ProbeFreqInSecs = a_ProbeFreqInSecs;
440     xstat_cm_numCollections = a_numCollections;
441     collIDBytes = xstat_cm_numCollections * sizeof(afs_int32);
442     xstat_cm_collIDP = malloc(collIDBytes);
443     memcpy(xstat_cm_collIDP, a_collIDP, collIDBytes);
444     if (xstat_cm_debug) {
445         printf("[%s] Asking for %d collection(s): ", rn,
446                xstat_cm_numCollections);
447         for (curr_srv = 0; curr_srv < xstat_cm_numCollections; curr_srv++)
448             printf("%d ", *(xstat_cm_collIDP + curr_srv));
449         printf("\n");
450     }
451
452     /*
453      * Get ready in case we have to do a cleanup - basically, zero
454      * everything out.
455      */
456     code = xstat_cm_CleanupInit();
457     if (code)
458         return (code);
459
460     /*
461      * Allocate the necessary data structures and initialize everything
462      * else.
463      */
464     xstat_cm_ConnInfo = malloc(a_numServers
465                                * sizeof(struct xstat_cm_ConnectionInfo));
466     if (xstat_cm_ConnInfo == (struct xstat_cm_ConnectionInfo *)0) {
467         fprintf(stderr,
468                 "[%s] Can't allocate %d connection info structs (%" AFS_SIZET_FMT " bytes)\n",
469                 rn, a_numServers,
470                 (a_numServers * sizeof(struct xstat_cm_ConnectionInfo)));
471         return (-1);            /*No cleanup needs to be done yet */
472     }
473
474     /*
475      * Initialize the Rx subsystem, just in case nobody's done it.
476      */
477     if (xstat_cm_debug)
478         printf("[%s] Initializing Rx on port 0\n", rn);
479     code = rx_Init(htons(0));
480     if (code) {
481         fprintf(stderr, "[%s] Fatal error in rx_Init(), error=%d\n", rn,
482                 code);
483         return (-1);
484     }
485
486     if (xstat_cm_debug)
487         printf("[%s] Rx initialized on port 0\n", rn);
488
489     /*
490      * Create a null Rx client security object, to be used by the
491      * probe thread.
492      */
493     secobj = rxnull_NewClientSecurityObject();
494     if (secobj == (struct rx_securityClass *)0) {
495         fprintf(stderr,
496                 "[%s] Can't create probe thread client security object.\n", rn);
497         xstat_cm_Cleanup(1);    /*Delete already-malloc'ed areas */
498         return (-1);
499     }
500     if (xstat_cm_debug)
501         printf("[%s] Probe thread client security object created\n", rn);
502
503     curr_conn = xstat_cm_ConnInfo;
504     conn_err = 0;
505     for (curr_srv = 0; curr_srv < a_numServers; curr_srv++) {
506         /*
507          * Copy in the socket info for the current server, resolve its
508          * printable name if possible.
509          */
510         if (xstat_cm_debug) {
511             printf("[%s] Copying in the following socket info:\n", rn);
512             printf("[%s] IP addr %s, port %d\n", rn,
513                    afs_inet_ntoa_r((a_socketArray + curr_srv)->sin_addr.s_addr,hoststr),
514                    ntohs((a_socketArray + curr_srv)->sin_port));
515         }
516         memcpy(&(curr_conn->skt), a_socketArray + curr_srv,
517                sizeof(struct sockaddr_in));
518
519         hostNameFound =
520             hostutil_GetNameByINet(curr_conn->skt.sin_addr.s_addr);
521         if (hostNameFound == NULL) {
522             fprintf(stderr,
523                     "[%s] Can't map Internet address %s to a string name\n",
524                     rn, afs_inet_ntoa_r(curr_conn->skt.sin_addr.s_addr,hoststr));
525             curr_conn->hostName[0] = '\0';
526         } else {
527             strcpy(curr_conn->hostName, hostNameFound);
528             if (xstat_cm_debug)
529                 printf("[%s] Host name for server index %d is %s\n", rn,
530                        curr_srv, curr_conn->hostName);
531         }
532
533         /*
534          * Make an Rx connection to the current server.
535          */
536         if (xstat_cm_debug)
537             printf
538                 ("[%s] Connecting to srv idx %d, IP addr %s, port %d, service 1\n",
539                  rn, curr_srv, afs_inet_ntoa_r(curr_conn->skt.sin_addr.s_addr,hoststr),
540                  ntohs(curr_conn->skt.sin_port));
541         curr_conn->rxconn = rx_NewConnection(curr_conn->skt.sin_addr.s_addr,    /*Server addr */
542                                              curr_conn->skt.sin_port,   /*Server port */
543                                              1, /*AFS service # */
544                                              secobj,    /*Security obj */
545                                              0);        /*# of above */
546         if (curr_conn->rxconn == (struct rx_connection *)0) {
547             fprintf(stderr,
548                     "[%s] Can't create Rx connection to server '%s' (%s)\n",
549                     rn, curr_conn->hostName, afs_inet_ntoa_r(curr_conn->skt.sin_addr.s_addr,hoststr));
550             conn_err = 1;
551         }
552         if (xstat_cm_debug)
553             printf("[%s] New connection at %p\n", rn, curr_conn->rxconn);
554
555         /*
556          * Bump the current xstat_cm connection to set up.
557          */
558         curr_conn++;
559
560     }                           /*for curr_srv */
561
562     /*
563      * Start up the probe thread.
564      */
565     if (xstat_cm_debug)
566         printf("[%s] Creating the probe thread\n", rn);
567     code = pthread_create(&xstat_cm_thread, NULL, xstat_cm_LWP, NULL);
568     if (code) {
569         fprintf(stderr, "[%s] Can't create xstat_cm thread!  Error is %d\n", rn,
570                 code);
571         xstat_cm_Cleanup(1);    /*Delete already-malloc'ed areas */
572         return (code);
573     }
574
575     /*
576      * Return the final results.
577      */
578     if (conn_err)
579         return (-2);
580     else
581         return (0);
582 }
583
584
585 /*------------------------------------------------------------------------
586  * [exported] xstat_cm_ForceProbeNow
587  *
588  * Description:
589  *      Wake up the probe thread, forcing it to execute a probe immediately.
590  *
591  * Arguments:
592  *      None.
593  *
594  * Returns:
595  *      0 on success,
596  *      Error value otherwise.
597  *
598  * Environment:
599  *      The module must have been initialized.
600  *
601  * Side Effects:
602  *      As advertised.
603  *------------------------------------------------------------------------*/
604
605 int
606 xstat_cm_ForceProbeNow(void)
607 {
608     static char rn[] = "xstat_cm_ForceProbeNow";        /*Routine name */
609
610     /*
611      * There isn't a prayer unless we've been initialized.
612      */
613     if (!xstat_cm_initflag) {
614         fprintf(stderr, "[%s] Must call xstat_cm_Init first!\n", rn);
615         return (-1);
616     }
617
618     /*
619      * Kick the sucker in the side.
620      */
621     opr_mutex_enter(&xstat_cm_force_lock);
622     opr_cv_signal(&xstat_cm_force_cv);
623     opr_mutex_exit(&xstat_cm_force_lock);
624
625     /*
626      * We did it, so report the happy news.
627      */
628     return (0);
629 }
630
631 /*
632  * Wait for the collection to complete. Returns after one cycle if running in
633  * one-shot mode, otherwise wait for a given amount of time.
634  *
635  * Args:
636  *    int sleep_secs : time to wait in seconds when running
637  *                     in continuous mode. 0 means wait forever.
638  *
639  * Returns:
640  *    0 on success
641  */
642 int
643 xstat_cm_Wait(int sleep_secs)
644 {
645     static char rn[] = "xstat_cm_Wait"; /*Routine name */
646     int code;
647     struct timeval tv;          /*Time structure */
648
649     if (xstat_cm_oneShot) {
650         /*
651          * One-shot operation; just wait for the collection to be done.
652          */
653         if (xstat_cm_debug)
654             printf("[%s] Calling pthread_join()\n", rn);
655         code = pthread_join(xstat_cm_thread, NULL);
656         if (xstat_cm_debug)
657             printf("[%s] Returned from pthread_join()\n", rn);
658         if (code) {
659             fprintf(stderr,
660                     "[%s] Error %d encountered by pthread_join()\n",
661                     rn, code);
662         }
663     } else if (sleep_secs == 0) {
664         /* Sleep forever. */
665         if (xstat_cm_debug)
666             fprintf(stderr, "[%s] going to sleep ...\n", rn);
667         while (1) {
668             code = select(0,    /*Num fds */
669                           0,    /*Descriptors ready for reading */
670                           0,    /*Descriptors ready for writing */
671                           0,    /*Descriptors with exceptional conditions */
672                           NULL);        /* NULL timeout means "forever" */
673             if (code < 0) {
674                 fprintf(stderr, "[%s] select() error %d\n", rn, errno);
675                 break;
676             }
677         }
678     } else {
679         /* Let's just fall asleep while.  */
680         if (xstat_cm_debug)
681             printf
682                 ("xstat_cm service started, main thread sleeping for %d secs.\n",
683                  sleep_secs);
684         tv.tv_sec = sleep_secs;
685         tv.tv_usec = 0;
686         code = select(0,        /*Num fds */
687                       0,        /*Descriptors ready for reading */
688                       0,        /*Descriptors ready for writing */
689                       0,        /*Descriptors with exceptional conditions */
690                       &tv);     /*Timeout structure */
691         if (code < 0)
692             fprintf(stderr, "[%s] select() error: %d\n", rn, errno);
693     }
694     return code;
695 }