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