2 * Copyright 2000, International Business Machines Corporation and others.
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
12 * Implementation of the client side of the AFS Cache Manager
13 * extended statistics facility.
15 *------------------------------------------------------------------------*/
17 #include <afsconfig.h>
18 #include <afs/param.h>
23 #include "xstat_cm.h" /*Interface for this module */
26 #include <afs/afsutil.h>
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 */
38 afs_int32 xstat_cmData[AFSCB_MAX_XSTAT_LONGS]; /*Buffer for collected data */
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 */
55 /*------------------------------------------------------------------------
56 * [private] xstat_cm_CleanupInit
59 * Set up for recovery after an error in initialization (i.e.,
60 * during a call to xstat_cm_Init.
67 * Error value otherwise.
70 * This routine is private to the module.
73 * Zeros out basic data structures.
74 *------------------------------------------------------------------------*/
77 xstat_cm_CleanupInit(void)
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;
92 /*------------------------------------------------------------------------
93 * [exported] xstat_cm_Cleanup
96 * Clean up our memory and connection state.
99 * int a_releaseMem : Should we free up malloc'ed areas?
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.
107 * xstat_cm_numServers should be properly set. We don't do anything
108 * unless xstat_cm_Init() has already been called.
111 * Shuts down Rx connections gracefully, frees allocated space
113 *------------------------------------------------------------------------*/
116 xstat_cm_Cleanup(int a_releaseMem)
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 */
124 * Assume the best, but check the worst.
126 if (!xstat_cm_initflag) {
127 fprintf(stderr, "[%s] Refused; module not initialized\n", rn);
133 * Take care of all Rx connections first. Check to see that the
134 * server count is a legal value.
136 if (xstat_cm_numServers <= 0) {
138 "[%s] Illegal number of servers (xstat_cm_numServers = %d)\n",
139 rn, xstat_cm_numServers);
142 if (xstat_cm_ConnInfo != (struct xstat_cm_ConnectionInfo *)0) {
144 * The xstat_cm connection structure array exists. Go through
145 * it and close up any Rx connections it holds.
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;
154 } /*for each xstat_cm connection */
155 } /*xstat_cm connection structure exists */
156 } /*Legal number of servers */
159 * If asked to, release the space we've allocated.
162 if (xstat_cm_ConnInfo != (struct xstat_cm_ConnectionInfo *)0)
163 free(xstat_cm_ConnInfo);
167 * Return the news, whatever it is.
173 /*------------------------------------------------------------------------
174 * [private] xstat_cm_LWP
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.
189 * Started by xstat_cm_Init(), uses global structures and the
190 * global private xstat_cm_oneShot variable.
194 *------------------------------------------------------------------------*/
196 xstat_cm_LWP(void *unused)
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 */
210 * Set up some numbers we'll need.
212 clientVersionNumber = AFSCB_XSTAT_VERSION;
214 while (1) { /*Service loop */
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.
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++;
226 for (conn_idx = 0; conn_idx < xstat_cm_numServers; conn_idx++) {
228 * Grab the statistics for the current Cache Manager, if the
229 * connection is valid.
232 printf("[%s] Getting collections from Cache Manager '%s'\n",
233 rn, curr_conn->hostName);
234 if (curr_conn->rxconn != (struct rx_connection *)0) {
236 printf("[%s] Connection OK, calling RXAFSCB_GetXStats\n",
240 * Probe the given CM for each desired collection.
242 currCollIDP = xstat_cm_collIDP;
243 for (numColls = 0; numColls < xstat_cm_numCollections;
244 numColls++, currCollIDP++) {
246 * Initialize the per-probe values.
249 printf("[%s] Asking for data collection %d\n", rn,
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);
257 xstat_cm_Results.connP = curr_conn;
259 if (xstat_cm_debug) {
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);
271 xstat_cm_Results.probeOK =
272 RXAFSCB_GetXStats(curr_conn->rxconn,
273 clientVersionNumber, *currCollIDP,
275 &(xstat_cm_Results.probeTime),
276 &(xstat_cm_Results.data));
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
286 printf("[%s] Calling handler routine.\n", rn);
287 code = xstat_cm_Handler();
290 "[%s] Handler routine got error code %d\n",
292 } /*For each collection */
295 /*Valid Rx connection */
297 * Advance the xstat_cm connection pointer.
301 } /*For each xstat_cm connection */
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.
309 printf("[%s] Polling complete for probe round %d.\n", rn,
310 xstat_cm_Results.probeNum);
312 if (xstat_cm_oneShot) {
314 * One-shot execution desired.
316 break; /*from the perpetual while loop */
317 } /*One-shot execution */
320 * Continuous execution desired. Sleep for the required
321 * number of seconds or wakeup sooner if forced.
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 */
339 /*------------------------------------------------------------------------
340 * [exported] xstat_cm_Init
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.
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.
359 * -2 for (at least one) connection error,
360 * thread process creation code, if it failed,
361 * -1 for other fatal errors.
364 * *** MUST BE THE FIRST ROUTINE CALLED FROM THIS PACKAGE ***
367 * Sets up just about everything.
368 *------------------------------------------------------------------------*/
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)
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 */
388 * If we've already been called, snicker at the bozo, gently
389 * remind him of his doubtful heritage, and return success.
391 if (xstat_cm_initflag) {
392 fprintf(stderr, "[%s] Called multiple times!\n", rn);
395 xstat_cm_initflag = 1;
397 opr_mutex_init(&xstat_cm_force_lock);
398 opr_cv_init(&xstat_cm_force_cv);
401 * Check the parameters for bogosities.
404 if (a_numServers <= 0) {
405 fprintf(stderr, "[%s] Illegal number of servers: %d\n", rn,
409 if (a_socketArray == (struct sockaddr_in *)0) {
410 fprintf(stderr, "[%s] Null server socket array argument\n", rn);
413 if (a_ProbeFreqInSecs <= 0) {
414 fprintf(stderr, "[%s] Illegal probe frequency: %d\n", rn,
418 if (a_ProbeHandler == NULL) {
419 fprintf(stderr, "[%s] Null probe handler function argument\n", rn);
422 if (a_numCollections <= 0) {
423 fprintf(stderr, "[%s] Illegal collection count argument: %d\n", rn,
427 if (a_collIDP == NULL) {
428 fprintf(stderr, "[%s] Null collection ID array argument\n", rn);
435 * Record our passed-in info.
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));
455 * Get ready in case we have to do a cleanup - basically, zero
458 code = xstat_cm_CleanupInit();
463 * Allocate the necessary data structures and initialize everything
466 xstat_cm_ConnInfo = malloc(a_numServers
467 * sizeof(struct xstat_cm_ConnectionInfo));
468 if (xstat_cm_ConnInfo == (struct xstat_cm_ConnectionInfo *)0) {
470 "[%s] Can't allocate %d connection info structs (%" AFS_SIZET_FMT " bytes)\n",
472 (a_numServers * sizeof(struct xstat_cm_ConnectionInfo)));
473 return (-1); /*No cleanup needs to be done yet */
477 * Initialize the Rx subsystem, just in case nobody's done it.
480 printf("[%s] Initializing Rx on port 0\n", rn);
481 code = rx_Init(htons(0));
483 fprintf(stderr, "[%s] Fatal error in rx_Init(), error=%d\n", rn,
489 printf("[%s] Rx initialized on port 0\n", rn);
492 * Create a null Rx client security object, to be used by the
495 secobj = rxnull_NewClientSecurityObject();
496 if (secobj == (struct rx_securityClass *)0) {
498 "[%s] Can't create probe thread client security object.\n", rn);
499 xstat_cm_Cleanup(1); /*Delete already-malloc'ed areas */
503 printf("[%s] Probe thread client security object created\n", rn);
505 curr_conn = xstat_cm_ConnInfo;
507 for (curr_srv = 0; curr_srv < a_numServers; curr_srv++) {
509 * Copy in the socket info for the current server, resolve its
510 * printable name if possible.
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));
518 memcpy(&(curr_conn->skt), a_socketArray + curr_srv,
519 sizeof(struct sockaddr_in));
522 hostutil_GetNameByINet(curr_conn->skt.sin_addr.s_addr);
523 if (hostNameFound == NULL) {
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';
529 strcpy(curr_conn->hostName, hostNameFound);
531 printf("[%s] Host name for server index %d is %s\n", rn,
532 curr_srv, curr_conn->hostName);
536 * Make an Rx connection to the current server.
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 */
548 if (curr_conn->rxconn == (struct rx_connection *)0) {
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));
555 printf("[%s] New connection at %p\n", rn, curr_conn->rxconn);
558 * Bump the current xstat_cm connection to set up.
565 * Start up the probe thread.
568 printf("[%s] Creating the probe thread\n", rn);
569 code = pthread_create(&xstat_cm_thread, NULL, xstat_cm_LWP, NULL);
571 fprintf(stderr, "[%s] Can't create xstat_cm thread! Error is %d\n", rn,
573 xstat_cm_Cleanup(1); /*Delete already-malloc'ed areas */
578 * Return the final results.
587 /*------------------------------------------------------------------------
588 * [exported] xstat_cm_ForceProbeNow
591 * Wake up the probe thread, forcing it to execute a probe immediately.
598 * Error value otherwise.
601 * The module must have been initialized.
605 *------------------------------------------------------------------------*/
608 xstat_cm_ForceProbeNow(void)
610 static char rn[] = "xstat_cm_ForceProbeNow"; /*Routine name */
613 * There isn't a prayer unless we've been initialized.
615 if (!xstat_cm_initflag) {
616 fprintf(stderr, "[%s] Must call xstat_cm_Init first!\n", rn);
621 * Kick the sucker in the side.
623 opr_mutex_enter(&xstat_cm_force_lock);
624 opr_cv_signal(&xstat_cm_force_cv);
625 opr_mutex_exit(&xstat_cm_force_lock);
628 * We did it, so report the happy news.
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.
638 * int sleep_secs : time to wait in seconds when running
639 * in continuous mode. 0 means wait forever.
645 xstat_cm_Wait(int sleep_secs)
647 static char rn[] = "xstat_cm_Wait"; /*Routine name */
649 struct timeval tv; /*Time structure */
651 if (xstat_cm_oneShot) {
653 * One-shot operation; just wait for the collection to be done.
656 printf("[%s] Calling pthread_join()\n", rn);
657 code = pthread_join(xstat_cm_thread, NULL);
659 printf("[%s] Returned from pthread_join()\n", rn);
662 "[%s] Error %d encountered by pthread_join()\n",
665 } else if (sleep_secs == 0) {
670 fprintf(stderr, "[%s] going to sleep ...\n", rn);
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 */
678 fprintf(stderr, "[%s] select() error %d\n", rn, errno);
683 /* Let's just fall asleep while. */
686 ("xstat_cm service started, main thread sleeping for %d secs.\n",
688 tv.tv_sec = sleep_secs;
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 */
696 fprintf(stderr, "[%s] select() error: %d\n", rn, errno);