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 AFS FileServer probe facility.
14 *------------------------------------------------------------------------*/
16 #include <afsconfig.h>
17 #include <afs/param.h>
23 #include <fsprobe.h> /*Interface for this module */
24 #include <lwp.h> /*Lightweight process package */
25 #include <afs/cellconfig.h>
26 #include <afs/afsint.h>
27 #include <afs/afsutil.h>
29 #define LWP_STACK_SIZE (16 * 1024)
32 * Routines we need that don't have explicit include file definitions.
34 extern int RXAFSCB_ExecuteRequest(struct rx_call *); /*AFS callback dispatcher */
37 * Help out the linker by explicitly importing the callback routines.
39 extern afs_int32 SRXAFSCB_CallBack(struct rx_call *, AFSCBFids *, AFSCBs *);
40 extern afs_int32 SRXAFSCB_InitCallBackState2(struct rx_call *,
41 struct interfaceAddr *);
42 extern afs_int32 SRXAFSCB_Probe(struct rx_call *);
47 int fsprobe_numServers; /*Num servers connected */
48 struct fsprobe_ConnectionInfo *fsprobe_ConnInfo; /*Ptr to connection array */
49 struct fsprobe_ProbeResults fsprobe_Results; /*Latest probe results */
50 int fsprobe_ProbeFreqInSecs; /*Probe freq. in seconds */
55 static int fsprobe_initflag = 0; /*Was init routine called? */
56 static int fsprobe_debug = 0; /*Debugging output enabled? */
57 static int (*fsprobe_Handler) (void); /*Probe handler routine */
58 static PROCESS probeLWP_ID; /*Probe LWP process ID */
59 static int fsprobe_statsBytes; /*Num bytes in stats block */
60 static int fsprobe_probeOKBytes; /*Num bytes in probeOK block */
63 * We have to pass a port to Rx to start up our callback listener
64 * service, but 7001 is already taken up by the Cache Manager. So,
67 #define FSPROBE_CBPORT 7101
70 /*------------------------------------------------------------------------
71 * [private] fsprobe_CleanupInit
74 * Set up for recovery after an error in initialization (i.e.,
75 * during a call to fsprobe_Init.
82 * Error value otherwise.
85 * This routine is private to the module.
88 * Zeros out basic data structures.
89 *------------------------------------------------------------------------*/
92 fsprobe_CleanupInit(void)
93 { /*fsprobe_CleanupInit */
95 afs_int32 code; /*Return code from callback stubs */
96 struct rx_call *rxcall; /*Bogus param */
97 AFSCBFids *Fids_Array; /*Bogus param */
98 AFSCBs *CallBack_Array; /*Bogus param */
99 struct interfaceAddr *interfaceAddr; /*Bogus param */
101 fsprobe_ConnInfo = (struct fsprobe_ConnectionInfo *)0;
102 memset(&fsprobe_Results, 0, sizeof(struct fsprobe_ProbeResults));
104 rxcall = (struct rx_call *)0;
105 Fids_Array = (AFSCBFids *) 0;
106 CallBack_Array = (AFSCBs *) 0;
107 interfaceAddr = NULL;
109 code = SRXAFSCB_CallBack(rxcall, Fids_Array, CallBack_Array);
112 code = SRXAFSCB_InitCallBackState2(rxcall, interfaceAddr);
115 code = SRXAFSCB_Probe(rxcall);
118 } /*fsprobe_CleanupInit */
121 /*------------------------------------------------------------------------
122 * [exported] fsprobe_Cleanup
125 * Clean up our memory and connection state.
128 * int a_releaseMem : Should we free up malloc'ed areas?
131 * 0 on total success,
132 * -1 if the module was never initialized, or there was a problem
133 * with the fsprobe connection array.
136 * fsprobe_numServers should be properly set. We don't do anything
137 * unless fsprobe_Init() has already been called.
140 * Shuts down Rx connections gracefully, frees allocated space
142 *------------------------------------------------------------------------*/
145 fsprobe_Cleanup(int a_releaseMem)
146 { /*fsprobe_Cleanup */
148 static char rn[] = "fsprobe_Cleanup"; /*Routine name */
149 int code; /*Return code */
150 int conn_idx; /*Current connection index */
151 struct fsprobe_ConnectionInfo *curr_conn; /*Ptr to fsprobe connection */
154 * Assume the best, but check the worst.
156 if (!fsprobe_initflag) {
157 fprintf(stderr, "[%s] Refused; module not initialized\n", rn);
163 * Take care of all Rx connections first. Check to see that the
164 * server count is a legal value.
166 if (fsprobe_numServers <= 0) {
168 "[%s] Illegal number of servers to clean up (fsprobe_numServers = %d)\n",
169 rn, fsprobe_numServers);
172 if (fsprobe_ConnInfo != (struct fsprobe_ConnectionInfo *)0) {
174 * The fsprobe connection structure array exists. Go through it
175 * and close up any Rx connections it holds.
177 curr_conn = fsprobe_ConnInfo;
178 for (conn_idx = 0; conn_idx < fsprobe_numServers; conn_idx++) {
179 if (curr_conn->rxconn != (struct rx_connection *)0) {
180 rx_DestroyConnection(curr_conn->rxconn);
181 curr_conn->rxconn = (struct rx_connection *)0;
183 if (curr_conn->rxVolconn != (struct rx_connection *)0) {
184 rx_DestroyConnection(curr_conn->rxVolconn);
185 curr_conn->rxVolconn = (struct rx_connection *)0;
188 } /*for each fsprobe connection */
189 } /*fsprobe connection structure exists */
190 } /*Legal number of servers */
193 * Now, release all the space we've allocated, if asked to.
196 if (fsprobe_ConnInfo != (struct fsprobe_ConnectionInfo *)0)
197 free(fsprobe_ConnInfo);
198 if (fsprobe_Results.stats != NULL)
199 free(fsprobe_Results.stats);
200 if (fsprobe_Results.probeOK != (int *)0)
201 free(fsprobe_Results.probeOK);
205 * Return the news, whatever it is.
209 } /*fsprobe_Cleanup */
211 /*------------------------------------------------------------------------
212 * [private] fsprobe_LWP
215 * This LWP iterates over the server connections and gathers up
216 * the desired statistics from each one on a regular basis. When
217 * the sweep is done, the associated handler function is called
218 * to process the new data.
227 * Started by fsprobe_Init(), uses global sturctures.
231 *------------------------------------------------------------------------*/
233 fsprobe_LWP(void *unused)
236 static char rn[] = "fsprobe_LWP"; /*Routine name */
237 register afs_int32 code; /*Results of calls */
238 struct timeval tv; /*Time structure */
239 int conn_idx; /*Connection index */
240 struct fsprobe_ConnectionInfo *curr_conn; /*Current connection */
241 struct ProbeViceStatistics *curr_stats; /*Current stats region */
242 int *curr_probeOK; /*Current probeOK field */
243 ViceStatistics64 stats64; /*Current stats region */
244 stats64.ViceStatistics64_val = (afs_uint64 *)malloc(STATS64_VERSION *
246 while (1) { /*Service loop */
248 * Iterate through the server connections, gathering data.
249 * Don't forget to bump the probe count and zero the statistics
250 * areas before calling the servers.
254 "[%s] Waking up, collecting data from %d connected servers\n",
255 rn, fsprobe_numServers);
256 curr_conn = fsprobe_ConnInfo;
257 curr_stats = fsprobe_Results.stats;
258 curr_probeOK = fsprobe_Results.probeOK;
259 fsprobe_Results.probeNum++;
260 memset(fsprobe_Results.stats, 0, fsprobe_statsBytes);
261 memset(fsprobe_Results.probeOK, 0, fsprobe_probeOKBytes);
263 for (conn_idx = 0; conn_idx < fsprobe_numServers; conn_idx++) {
265 * Grab the statistics for the current FileServer, if the
266 * connection is valid.
269 fprintf(stderr, "[%s] Contacting server %s\n", rn,
270 curr_conn->hostName);
271 if (curr_conn->rxconn != (struct rx_connection *)0) {
274 "[%s] Connection valid, calling RXAFS_GetStatistics\n",
277 RXAFS_GetStatistics64(curr_conn->rxconn, STATS64_VERSION, &stats64);
278 if (*curr_probeOK == RXGEN_OPCODE)
280 RXAFS_GetStatistics(curr_conn->rxconn, curr_stats);
281 else if (*curr_probeOK == 0) {
282 curr_stats->CurrentTime = RoundInt64ToInt32(stats64.ViceStatistics64_val[STATS64_CURRENTTIME]);
283 curr_stats->BootTime = RoundInt64ToInt32(stats64.ViceStatistics64_val[STATS64_BOOTTIME]);
284 curr_stats->StartTime = RoundInt64ToInt32(stats64.ViceStatistics64_val[STATS64_STARTTIME]);
285 curr_stats->CurrentConnections = RoundInt64ToInt32(stats64.ViceStatistics64_val[STATS64_CURRENTCONNECTIONS]);
286 curr_stats->TotalFetchs = RoundInt64ToInt32(stats64.ViceStatistics64_val[STATS64_TOTALFETCHES]);
287 curr_stats->TotalStores = RoundInt64ToInt32(stats64.ViceStatistics64_val[STATS64_TOTALSTORES]);
288 curr_stats->WorkStations = RoundInt64ToInt32(stats64.ViceStatistics64_val[STATS64_WORKSTATIONS]);
292 /*Valid Rx connection */
294 * Call the Volume Server too to get additional stats
297 fprintf(stderr, "[%s] Contacting volume server %s\n", rn,
298 curr_conn->hostName);
299 if (curr_conn->rxVolconn != (struct rx_connection *)0) {
302 struct diskPartition partition;
303 struct diskPartition64 *partition64p =
304 (struct diskPartition64 *)malloc(sizeof(struct diskPartition64));
308 "[%s] Connection valid, calling RXAFS_GetStatistics\n",
310 for (i = 0; i < curr_conn->partCnt; i++) {
311 if (curr_conn->partList.partFlags[i] & PARTVALID) {
312 MapPartIdIntoName(curr_conn->partList.partId[i],
315 AFSVolPartitionInfo64(curr_conn->rxVolconn, pname,
319 curr_stats->Disk[i].BlocksAvailable =
320 RoundInt64ToInt31(partition64p->free);
321 curr_stats->Disk[i].TotalBlocks =
322 RoundInt64ToInt31(partition64p->minFree);
323 strcpy(curr_stats->Disk[i].Name, pname);
325 if (code == RXGEN_OPCODE) {
327 AFSVolPartitionInfo(curr_conn->rxVolconn,
330 curr_stats->Disk[i].BlocksAvailable =
332 curr_stats->Disk[i].TotalBlocks =
334 strcpy(curr_stats->Disk[i].Name, pname);
339 "Could not get information on server %s partition %s\n",
340 curr_conn->hostName, pname);
349 * Advance the fsprobe connection pointer & stats pointer.
355 } /*For each fsprobe connection */
358 * All (valid) connections have been probed. Now, call the
359 * associated handler function. The handler does not take
360 * any explicit parameters, rather gets to the goodies via
361 * some of the objects exported by this module.
365 "[%s] Polling complete, calling associated handler routine.\n",
367 code = fsprobe_Handler();
369 fprintf(stderr, "[%s] Handler routine returned error code %d\n",
373 * Fall asleep for the prescribed number of seconds.
375 tv.tv_sec = fsprobe_ProbeFreqInSecs;
378 fprintf(stderr, "[%s] Falling asleep for %d seconds\n", rn,
379 fsprobe_ProbeFreqInSecs);
380 code = IOMGR_Select(0, /*Num fids */
381 0, /*Descriptors ready for reading */
382 0, /*Descriptors ready for writing */
383 0, /*Descriptors w/exceptional conditions */
384 &tv); /*Ptr to timeout structure */
386 fprintf(stderr, "[%s] IOMGR_Select returned code %d\n", rn, code);
388 free(stats64.ViceStatistics64_val);
393 /*list all the partitions on <aserver> */
394 static int newvolserver = 0;
397 XListPartitions(struct rx_connection *aconn, struct partList *ptrPartList,
401 struct partEntries partEnts;
402 register int i, j = 0, code;
405 if (newvolserver == 1) {
406 for (i = 0; i < 26; i++)
407 partIds.partIds[i] = -1;
409 code = AFSVolListPartitions(aconn, &partIds);
411 for (i = 0; i < 26; i++) {
412 if ((partIds.partIds[i]) != -1) {
413 ptrPartList->partId[j] = partIds.partIds[i];
414 ptrPartList->partFlags[j] = PARTVALID;
417 ptrPartList->partFlags[i] = 0;
423 partEnts.partEntries_len = 0;
424 partEnts.partEntries_val = NULL;
425 code = AFSVolXListPartitions(aconn, &partEnts);
427 if (code == RXGEN_OPCODE) {
428 newvolserver = 1; /* Doesn't support new interface */
435 *cntp = partEnts.partEntries_len;
436 if (*cntp > VOLMAXPARTS) {
438 "Warning: number of partitions on the server too high %d (process only %d)\n",
442 for (i = 0; i < *cntp; i++) {
443 ptrPartList->partId[i] = partEnts.partEntries_val[i];
444 ptrPartList->partFlags[i] = PARTVALID;
446 free(partEnts.partEntries_val);
451 "Could not fetch the list of partitions from the server\n");
456 /*------------------------------------------------------------------------
457 * [exported] fsprobe_Init
460 * Initialize the fsprobe module: set up Rx connections to the
461 * given set of servers, start up the probe and callback LWPs,
462 * and associate the routine to be called when a probe completes.
465 * int a_numServers : Num. servers to connect to.
466 * struct sockaddr_in *a_socketArray : Array of server sockets.
467 * int a_ProbeFreqInSecs : Probe frequency in seconds.
468 * int (*a_ProbeHandler)() : Ptr to probe handler fcn.
469 * int a_debug; : Turn debugging output on?
473 * -2 for (at least one) connection error,
474 * LWP process creation code, if it failed,
475 * -1 for other fatal errors.
478 * *** MUST BE THE FIRST ROUTINE CALLED FROM THIS PACKAGE ***
479 * Also, the server security object CBsecobj MUST be a static,
480 * since it has to stick around after this routine exits.
483 * Sets up just about everything.
484 *------------------------------------------------------------------------*/
487 fsprobe_Init(int a_numServers, struct sockaddr_in *a_socketArray,
488 int a_ProbeFreqInSecs, int (*a_ProbeHandler)(void),
492 static char rn[] = "fsprobe_Init"; /*Routine name */
493 register afs_int32 code; /*Return value */
494 static struct rx_securityClass *CBsecobj; /*Callback security object */
495 struct rx_securityClass *secobj; /*Client security object */
496 struct rx_service *rxsrv_afsserver; /*Server for AFS */
497 int arg_errfound; /*Argument error found? */
498 int curr_srv; /*Current server idx */
499 struct fsprobe_ConnectionInfo *curr_conn; /*Ptr to current conn */
500 char *hostNameFound; /*Ptr to returned host name */
501 int conn_err; /*Connection error? */
502 int PortToUse; /*Callback port to use */
505 * If we've already been called, snicker at the bozo, gently
506 * remind him of his doubtful heritage, and return success.
508 if (fsprobe_initflag) {
509 fprintf(stderr, "[%s] Called multiple times!\n", rn);
512 fsprobe_initflag = 1;
515 * Check the parameters for bogosities.
518 if (a_numServers <= 0) {
519 fprintf(stderr, "[%s] Illegal number of servers: %d\n", rn,
523 if (a_socketArray == (struct sockaddr_in *)0) {
524 fprintf(stderr, "[%s] Null server socket array argument\n", rn);
527 if (a_ProbeFreqInSecs <= 0) {
528 fprintf(stderr, "[%s] Illegal probe frequency: %d\n", rn,
532 if (a_ProbeHandler == (int (*)())0) {
533 fprintf(stderr, "[%s] Null probe handler function argument\n", rn);
540 * Record our passed-in info.
542 fsprobe_debug = a_debug;
543 fsprobe_numServers = a_numServers;
544 fsprobe_Handler = a_ProbeHandler;
545 fsprobe_ProbeFreqInSecs = a_ProbeFreqInSecs;
548 * Get ready in case we have to do a cleanup - basically, zero
551 fsprobe_CleanupInit();
554 * Allocate the necessary data structures and initialize everything
557 fsprobe_ConnInfo = (struct fsprobe_ConnectionInfo *)
558 malloc(a_numServers * sizeof(struct fsprobe_ConnectionInfo));
559 if (fsprobe_ConnInfo == (struct fsprobe_ConnectionInfo *)0) {
561 "[%s] Can't allocate %d connection info structs (%lu bytes)\n",
563 (a_numServers * sizeof(struct fsprobe_ConnectionInfo)));
564 return (-1); /*No cleanup needs to be done yet */
568 fprintf(stderr, "[%s] fsprobe_ConnInfo allocated (%d bytes)\n", rn,
569 a_numServers * sizeof(struct fsprobe_ConnectionInfo));
572 fsprobe_statsBytes = a_numServers * sizeof(struct ProbeViceStatistics);
573 fsprobe_Results.stats = (struct ProbeViceStatistics *)
574 malloc(fsprobe_statsBytes);
575 if (fsprobe_Results.stats == NULL) {
577 "[%s] Can't allocate %d statistics structs (%d bytes)\n", rn,
578 a_numServers, fsprobe_statsBytes);
579 fsprobe_Cleanup(1); /*Delete already-malloc'ed areas */
581 } else if (fsprobe_debug)
582 fprintf(stderr, "[%s] fsprobe_Results.stats allocated (%d bytes)\n",
583 rn, fsprobe_statsBytes);
585 fsprobe_probeOKBytes = a_numServers * sizeof(int);
586 fsprobe_Results.probeOK = (int *)malloc(fsprobe_probeOKBytes);
587 if (fsprobe_Results.probeOK == (int *)0) {
589 "[%s] Can't allocate %d probeOK array entries (%d bytes)\n",
590 rn, a_numServers, fsprobe_probeOKBytes);
591 fsprobe_Cleanup(1); /*Delete already-malloc'ed areas */
593 } else if (fsprobe_debug)
594 fprintf(stderr, "[%s] fsprobe_Results.probeOK allocated (%d bytes)\n",
595 rn, fsprobe_probeOKBytes);
597 fsprobe_Results.probeNum = 0;
598 fsprobe_Results.probeTime = 0;
599 memset(fsprobe_Results.stats, 0,
600 (a_numServers * sizeof(struct ProbeViceStatistics)));
603 * Initialize the Rx subsystem, just in case nobody's done it.
606 fprintf(stderr, "[%s] Initializing Rx\n", rn);
607 PortToUse = FSPROBE_CBPORT;
609 code = rx_Init(htons(PortToUse));
611 if (code == RX_ADDRINUSE) {
614 "[%s] Callback port %d in use, advancing\n", rn,
618 fprintf(stderr, "[%s] Fatal error in rx_Init()\n", rn);
624 fprintf(stderr, "[%s] Rx initialized on port %d\n", rn, PortToUse);
627 * Create a null Rx server security object, to be used by the
630 CBsecobj = rxnull_NewServerSecurityObject();
631 if (CBsecobj == (struct rx_securityClass *)0) {
633 "[%s] Can't create null security object for the callback listener.\n",
635 fsprobe_Cleanup(1); /*Delete already-malloc'ed areas */
639 fprintf(stderr, "[%s] Callback server security object created\n", rn);
642 * Create a null Rx client security object, to be used by the
645 secobj = rxnull_NewClientSecurityObject();
646 if (secobj == (struct rx_securityClass *)0) {
648 "[%s] Can't create client security object for probe LWP.\n",
650 fsprobe_Cleanup(1); /*Delete already-malloc'ed areas */
654 fprintf(stderr, "[%s] Probe LWP client security object created\n",
657 curr_conn = fsprobe_ConnInfo;
659 for (curr_srv = 0; curr_srv < a_numServers; curr_srv++) {
661 * Copy in the socket info for the current server, resolve its
662 * printable name if possible.
665 fprintf(stderr, "[%s] Copying in the following socket info:\n",
667 fprintf(stderr, "[%s] IP addr 0x%x, port %d\n", rn,
668 (a_socketArray + curr_srv)->sin_addr.s_addr,
669 (a_socketArray + curr_srv)->sin_port);
671 memcpy(&(curr_conn->skt), a_socketArray + curr_srv,
672 sizeof(struct sockaddr_in));
675 hostutil_GetNameByINet(curr_conn->skt.sin_addr.s_addr);
676 if (hostNameFound == NULL) {
678 "[%s] Can't map Internet address %u to a string name\n",
679 rn, curr_conn->skt.sin_addr.s_addr);
680 curr_conn->hostName[0] = '\0';
682 strcpy(curr_conn->hostName, hostNameFound);
684 fprintf(stderr, "[%s] Host name for server index %d is %s\n",
685 rn, curr_srv, curr_conn->hostName);
689 * Make an Rx connection to the current server.
693 "[%s] Connecting to srv idx %d, IP addr 0x%x, port %d, service 1\n",
694 rn, curr_srv, curr_conn->skt.sin_addr.s_addr,
695 curr_conn->skt.sin_port);
696 curr_conn->rxconn = rx_NewConnection(curr_conn->skt.sin_addr.s_addr, /*Server addr */
697 curr_conn->skt.sin_port, /*Server port */
698 1, /*AFS service num */
699 secobj, /*Security object */
700 0); /*Number of above */
701 if (curr_conn->rxconn == (struct rx_connection *)0) {
703 "[%s] Can't create Rx connection to server %s (%u)\n",
704 rn, curr_conn->hostName, curr_conn->skt.sin_addr.s_addr);
708 fprintf(stderr, "[%s] New connection at %p\n", rn,
712 * Make an Rx connection to the current volume server.
716 "[%s] Connecting to srv idx %d, IP addr 0x%x, port %d, service 1\n",
717 rn, curr_srv, curr_conn->skt.sin_addr.s_addr,
719 curr_conn->rxVolconn = rx_NewConnection(curr_conn->skt.sin_addr.s_addr, /*Server addr */
720 htons(AFSCONF_VOLUMEPORT), /*Volume Server port */
721 VOLSERVICE_ID, /*AFS service num */
722 secobj, /*Security object */
723 0); /*Number of above */
724 if (curr_conn->rxVolconn == (struct rx_connection *)0) {
726 "[%s] Can't create Rx connection to volume server %s (%u)\n",
727 rn, curr_conn->hostName, curr_conn->skt.sin_addr.s_addr);
732 memset(&curr_conn->partList, 0, sizeof(struct partList));
733 curr_conn->partCnt = 0;
734 i = XListPartitions(curr_conn->rxVolconn, &curr_conn->partList,
737 curr_conn->partCnt = cnt;
741 fprintf(stderr, "[%s] New connection at %p\n", rn,
742 curr_conn->rxVolconn);
746 * Bump the current fsprobe connection to set up.
753 * Create the AFS callback service (listener).
756 fprintf(stderr, "[%s] Creating AFS callback listener\n", rn);
757 rxsrv_afsserver = rx_NewService(0, /*Use default port */
759 "afs", /*Service name */
760 &CBsecobj, /*Ptr to security object(s) */
761 1, /*Number of security objects */
762 RXAFSCB_ExecuteRequest); /*Dispatcher */
763 if (rxsrv_afsserver == (struct rx_service *)0) {
764 fprintf(stderr, "[%s] Can't create callback Rx service/listener\n",
766 fsprobe_Cleanup(1); /*Delete already-malloc'ed areas */
770 fprintf(stderr, "[%s] Callback listener created\n", rn);
773 * Start up the AFS callback service.
776 fprintf(stderr, "[%s] Starting up callback listener.\n", rn);
777 rx_StartServer(0 /*Don't donate yourself to LWP pool */ );
780 * Start up the probe LWP.
783 fprintf(stderr, "[%s] Creating the probe LWP\n", rn);
784 code = LWP_CreateProcess(fsprobe_LWP, /*Function to start up */
785 LWP_STACK_SIZE, /*Stack size in bytes */
787 (void *)0, /*Parameters */
788 "fsprobe Worker", /*Name to use */
789 &probeLWP_ID); /*Returned LWP process ID */
791 fprintf(stderr, "[%s] Can't create fsprobe LWP! Error is %d\n", rn,
793 fsprobe_Cleanup(1); /*Delete already-malloc'ed areas */
797 fprintf(stderr, "[%s] Probe LWP process structure located at %p\n",
802 * Do I need to do this?
805 fprintf(stderr, "[%s] Calling osi_Wakeup()\n", rn);
806 osi_Wakeup(&rxsrv_afsserver); /*Wake up anyone waiting for it */
810 * Return the final results.
820 /*------------------------------------------------------------------------
821 * [exported] fsprobe_ForceProbeNow
824 * Wake up the probe LWP, forcing it to execute a probe immediately.
831 * Error value otherwise.
834 * The module must have been initialized.
838 *------------------------------------------------------------------------*/
841 fsprobe_ForceProbeNow(void)
842 { /*fsprobe_ForceProbeNow */
844 static char rn[] = "fsprobe_ForceProbeNow"; /*Routine name */
847 * There isn't a prayer unless we've been initialized.
849 if (!fsprobe_initflag) {
850 fprintf(stderr, "[%s] Must call fsprobe_Init first!\n", rn);
855 * Kick the sucker in the side.
857 IOMGR_Cancel(probeLWP_ID);
860 * We did it, so report the happy news.
864 } /*fsprobe_ForceProbeNow */