opr: Assert opr_cv_timedwait return codes
[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_mutex_exit(&xstat_cm_force_lock);
330         }                       /*Continuous execution */
331     }                           /*Service loop */
332     return NULL;
333 }
334
335
336 /*------------------------------------------------------------------------
337  * [exported] xstat_cm_Init
338  *
339  * Description:
340  *      Initialize the xstat_cm module: set up Rx connections to the
341  *      given set of Cache Managers, start up the probe thread, and
342  *      associate the routine to be called when a probe completes.
343  *      Also, let it know which collections you're interested in.
344  *
345  * Arguments:
346  *      int a_numServers                  : Num. servers to connect to.
347  *      struct sockaddr_in *a_socketArray : Array of server sockets.
348  *      int a_ProbeFreqInSecs             : Probe frequency in seconds.
349  *      int (*a_ProbeHandler)()           : Ptr to probe handler fcn.
350  *      int a_flags;                      : Various flags.
351  *      int a_numCollections              : Number of collections desired.
352  *      afs_int32 *a_collIDP                      : Ptr to collection IDs.
353  *
354  * Returns:
355  *      0 on success,
356  *      -2 for (at least one) connection error,
357  *      thread process creation code, if it failed,
358  *      -1 for other fatal errors.
359  *
360  * Environment:
361  *      *** MUST BE THE FIRST ROUTINE CALLED FROM THIS PACKAGE ***
362  *
363  * Side Effects:
364  *      Sets up just about everything.
365  *------------------------------------------------------------------------*/
366
367 int
368 xstat_cm_Init(int a_numServers, struct sockaddr_in *a_socketArray,
369               int a_ProbeFreqInSecs, int (*a_ProbeHandler) (void), int a_flags,
370               int a_numCollections, afs_int32 * a_collIDP)
371 {
372
373     static char rn[] = "xstat_cm_Init"; /*Routine name */
374     afs_int32 code;     /*Return value */
375     struct rx_securityClass *secobj;    /*Client security object */
376     int arg_errfound;           /*Argument error found? */
377     int curr_srv;               /*Current server idx */
378     struct xstat_cm_ConnectionInfo *curr_conn;  /*Ptr to current conn */
379     char *hostNameFound;        /*Ptr to returned host name */
380     int conn_err;               /*Connection error? */
381     int collIDBytes;            /*Num bytes in coll ID array */
382     char hoststr[16];
383
384     /*
385      * If we've already been called, snicker at the bozo, gently
386      * remind him of his doubtful heritage, and return success.
387      */
388     if (xstat_cm_initflag) {
389         fprintf(stderr, "[%s] Called multiple times!\n", rn);
390         return (0);
391     } else
392         xstat_cm_initflag = 1;
393
394     opr_mutex_init(&xstat_cm_force_lock);
395     opr_cv_init(&xstat_cm_force_cv);
396
397     /*
398      * Check the parameters for bogosities.
399      */
400     arg_errfound = 0;
401     if (a_numServers <= 0) {
402         fprintf(stderr, "[%s] Illegal number of servers: %d\n", rn,
403                 a_numServers);
404         arg_errfound = 1;
405     }
406     if (a_socketArray == (struct sockaddr_in *)0) {
407         fprintf(stderr, "[%s] Null server socket array argument\n", rn);
408         arg_errfound = 1;
409     }
410     if (a_ProbeFreqInSecs <= 0) {
411         fprintf(stderr, "[%s] Illegal probe frequency: %d\n", rn,
412                 a_ProbeFreqInSecs);
413         arg_errfound = 1;
414     }
415     if (a_ProbeHandler == NULL) {
416         fprintf(stderr, "[%s] Null probe handler function argument\n", rn);
417         arg_errfound = 1;
418     }
419     if (a_numCollections <= 0) {
420         fprintf(stderr, "[%s] Illegal collection count argument: %d\n", rn,
421                 a_numServers);
422         arg_errfound = 1;
423     }
424     if (a_collIDP == NULL) {
425         fprintf(stderr, "[%s] Null collection ID array argument\n", rn);
426         arg_errfound = 1;
427     }
428     if (arg_errfound)
429         return (-1);
430
431     /*
432      * Record our passed-in info.
433      */
434     xstat_cm_debug = (a_flags & XSTAT_CM_INITFLAG_DEBUGGING);
435     xstat_cm_oneShot = (a_flags & XSTAT_CM_INITFLAG_ONE_SHOT);
436     xstat_cm_numServers = a_numServers;
437     xstat_cm_Handler = a_ProbeHandler;
438     xstat_cm_ProbeFreqInSecs = a_ProbeFreqInSecs;
439     xstat_cm_numCollections = a_numCollections;
440     collIDBytes = xstat_cm_numCollections * sizeof(afs_int32);
441     xstat_cm_collIDP = malloc(collIDBytes);
442     memcpy(xstat_cm_collIDP, a_collIDP, collIDBytes);
443     if (xstat_cm_debug) {
444         printf("[%s] Asking for %d collection(s): ", rn,
445                xstat_cm_numCollections);
446         for (curr_srv = 0; curr_srv < xstat_cm_numCollections; curr_srv++)
447             printf("%d ", *(xstat_cm_collIDP + curr_srv));
448         printf("\n");
449     }
450
451     /*
452      * Get ready in case we have to do a cleanup - basically, zero
453      * everything out.
454      */
455     code = xstat_cm_CleanupInit();
456     if (code)
457         return (code);
458
459     /*
460      * Allocate the necessary data structures and initialize everything
461      * else.
462      */
463     xstat_cm_ConnInfo = malloc(a_numServers
464                                * sizeof(struct xstat_cm_ConnectionInfo));
465     if (xstat_cm_ConnInfo == (struct xstat_cm_ConnectionInfo *)0) {
466         fprintf(stderr,
467                 "[%s] Can't allocate %d connection info structs (%" AFS_SIZET_FMT " bytes)\n",
468                 rn, a_numServers,
469                 (a_numServers * sizeof(struct xstat_cm_ConnectionInfo)));
470         return (-1);            /*No cleanup needs to be done yet */
471     }
472
473     /*
474      * Initialize the Rx subsystem, just in case nobody's done it.
475      */
476     if (xstat_cm_debug)
477         printf("[%s] Initializing Rx on port 0\n", rn);
478     code = rx_Init(htons(0));
479     if (code) {
480         fprintf(stderr, "[%s] Fatal error in rx_Init(), error=%d\n", rn,
481                 code);
482         return (-1);
483     }
484
485     if (xstat_cm_debug)
486         printf("[%s] Rx initialized on port 0\n", rn);
487
488     /*
489      * Create a null Rx client security object, to be used by the
490      * probe thread.
491      */
492     secobj = rxnull_NewClientSecurityObject();
493     if (secobj == (struct rx_securityClass *)0) {
494         fprintf(stderr,
495                 "[%s] Can't create probe thread client security object.\n", rn);
496         xstat_cm_Cleanup(1);    /*Delete already-malloc'ed areas */
497         return (-1);
498     }
499     if (xstat_cm_debug)
500         printf("[%s] Probe thread client security object created\n", rn);
501
502     curr_conn = xstat_cm_ConnInfo;
503     conn_err = 0;
504     for (curr_srv = 0; curr_srv < a_numServers; curr_srv++) {
505         /*
506          * Copy in the socket info for the current server, resolve its
507          * printable name if possible.
508          */
509         if (xstat_cm_debug) {
510             printf("[%s] Copying in the following socket info:\n", rn);
511             printf("[%s] IP addr %s, port %d\n", rn,
512                    afs_inet_ntoa_r((a_socketArray + curr_srv)->sin_addr.s_addr,hoststr),
513                    ntohs((a_socketArray + curr_srv)->sin_port));
514         }
515         memcpy(&(curr_conn->skt), a_socketArray + curr_srv,
516                sizeof(struct sockaddr_in));
517
518         hostNameFound =
519             hostutil_GetNameByINet(curr_conn->skt.sin_addr.s_addr);
520         if (hostNameFound == NULL) {
521             fprintf(stderr,
522                     "[%s] Can't map Internet address %s to a string name\n",
523                     rn, afs_inet_ntoa_r(curr_conn->skt.sin_addr.s_addr,hoststr));
524             curr_conn->hostName[0] = '\0';
525         } else {
526             strcpy(curr_conn->hostName, hostNameFound);
527             if (xstat_cm_debug)
528                 printf("[%s] Host name for server index %d is %s\n", rn,
529                        curr_srv, curr_conn->hostName);
530         }
531
532         /*
533          * Make an Rx connection to the current server.
534          */
535         if (xstat_cm_debug)
536             printf
537                 ("[%s] Connecting to srv idx %d, IP addr %s, port %d, service 1\n",
538                  rn, curr_srv, afs_inet_ntoa_r(curr_conn->skt.sin_addr.s_addr,hoststr),
539                  ntohs(curr_conn->skt.sin_port));
540         curr_conn->rxconn = rx_NewConnection(curr_conn->skt.sin_addr.s_addr,    /*Server addr */
541                                              curr_conn->skt.sin_port,   /*Server port */
542                                              1, /*AFS service # */
543                                              secobj,    /*Security obj */
544                                              0);        /*# of above */
545         if (curr_conn->rxconn == (struct rx_connection *)0) {
546             fprintf(stderr,
547                     "[%s] Can't create Rx connection to server '%s' (%s)\n",
548                     rn, curr_conn->hostName, afs_inet_ntoa_r(curr_conn->skt.sin_addr.s_addr,hoststr));
549             conn_err = 1;
550         }
551         if (xstat_cm_debug)
552             printf("[%s] New connection at %p\n", rn, curr_conn->rxconn);
553
554         /*
555          * Bump the current xstat_cm connection to set up.
556          */
557         curr_conn++;
558
559     }                           /*for curr_srv */
560
561     /*
562      * Start up the probe thread.
563      */
564     if (xstat_cm_debug)
565         printf("[%s] Creating the probe thread\n", rn);
566     code = pthread_create(&xstat_cm_thread, NULL, xstat_cm_LWP, NULL);
567     if (code) {
568         fprintf(stderr, "[%s] Can't create xstat_cm thread!  Error is %d\n", rn,
569                 code);
570         xstat_cm_Cleanup(1);    /*Delete already-malloc'ed areas */
571         return (code);
572     }
573
574     /*
575      * Return the final results.
576      */
577     if (conn_err)
578         return (-2);
579     else
580         return (0);
581 }
582
583
584 /*------------------------------------------------------------------------
585  * [exported] xstat_cm_ForceProbeNow
586  *
587  * Description:
588  *      Wake up the probe thread, forcing it to execute a probe immediately.
589  *
590  * Arguments:
591  *      None.
592  *
593  * Returns:
594  *      0 on success,
595  *      Error value otherwise.
596  *
597  * Environment:
598  *      The module must have been initialized.
599  *
600  * Side Effects:
601  *      As advertised.
602  *------------------------------------------------------------------------*/
603
604 int
605 xstat_cm_ForceProbeNow(void)
606 {
607     static char rn[] = "xstat_cm_ForceProbeNow";        /*Routine name */
608
609     /*
610      * There isn't a prayer unless we've been initialized.
611      */
612     if (!xstat_cm_initflag) {
613         fprintf(stderr, "[%s] Must call xstat_cm_Init first!\n", rn);
614         return (-1);
615     }
616
617     /*
618      * Kick the sucker in the side.
619      */
620     opr_mutex_enter(&xstat_cm_force_lock);
621     opr_cv_signal(&xstat_cm_force_cv);
622     opr_mutex_exit(&xstat_cm_force_lock);
623
624     /*
625      * We did it, so report the happy news.
626      */
627     return (0);
628 }
629
630 /*
631  * Wait for the collection to complete. Returns after one cycle if running in
632  * one-shot mode, otherwise wait for a given amount of time.
633  *
634  * Args:
635  *    int sleep_secs : time to wait in seconds when running
636  *                     in continuous mode. 0 means wait forever.
637  *
638  * Returns:
639  *    0 on success
640  */
641 int
642 xstat_cm_Wait(int sleep_secs)
643 {
644     static char rn[] = "xstat_cm_Wait"; /*Routine name */
645     int code;
646     struct timeval tv;          /*Time structure */
647
648     if (xstat_cm_oneShot) {
649         /*
650          * One-shot operation; just wait for the collection to be done.
651          */
652         if (xstat_cm_debug)
653             printf("[%s] Calling pthread_join()\n", rn);
654         code = pthread_join(xstat_cm_thread, NULL);
655         if (xstat_cm_debug)
656             printf("[%s] Returned from pthread_join()\n", rn);
657         if (code) {
658             fprintf(stderr,
659                     "[%s] Error %d encountered by pthread_join()\n",
660                     rn, code);
661         }
662     } else if (sleep_secs == 0) {
663         /* Sleep forever. */
664         if (xstat_cm_debug)
665             fprintf(stderr, "[%s] going to sleep ...\n", rn);
666         while (1) {
667             code = select(0,    /*Num fds */
668                           0,    /*Descriptors ready for reading */
669                           0,    /*Descriptors ready for writing */
670                           0,    /*Descriptors with exceptional conditions */
671                           NULL);        /* NULL timeout means "forever" */
672             if (code < 0) {
673                 fprintf(stderr, "[%s] select() error %d\n", rn, errno);
674                 break;
675             }
676         }
677     } else {
678         /* Let's just fall asleep while.  */
679         if (xstat_cm_debug)
680             printf
681                 ("xstat_cm service started, main thread sleeping for %d secs.\n",
682                  sleep_secs);
683         tv.tv_sec = sleep_secs;
684         tv.tv_usec = 0;
685         code = select(0,        /*Num fds */
686                       0,        /*Descriptors ready for reading */
687                       0,        /*Descriptors ready for writing */
688                       0,        /*Descriptors with exceptional conditions */
689                       &tv);     /*Timeout structure */
690         if (code < 0)
691             fprintf(stderr, "[%s] select() error: %d\n", rn, errno);
692     }
693     return code;
694 }