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>
22 #include <afs/cellconfig.h>
23 #include <afs/afsint.h>
24 #include <afs/afsutil.h>
25 #include <afs/volser.h>
26 #include <afs/volser_prototypes.h>
27 #define FSINT_COMMON_XG
28 #include <afs/afscbint.h>
30 #include "fsprobe.h" /*Interface for this module */
35 int fsprobe_numServers; /*Num servers connected */
36 struct fsprobe_ConnectionInfo *fsprobe_ConnInfo; /*Ptr to connection array */
37 struct fsprobe_ProbeResults fsprobe_Results; /*Latest probe results */
38 int fsprobe_ProbeFreqInSecs; /*Probe freq. in seconds */
43 static int fsprobe_initflag = 0; /*Was init routine called? */
44 static int fsprobe_debug = 0; /*Debugging output enabled? */
45 static int (*fsprobe_Handler) (void); /*Probe handler routine */
46 static pthread_t fsprobe_thread; /*Probe thread */
47 static int fsprobe_statsBytes; /*Num bytes in stats block */
48 static int fsprobe_probeOKBytes; /*Num bytes in probeOK block */
49 static opr_mutex_t fsprobe_force_lock; /*Lock to force probe */
50 static opr_cv_t fsprobe_force_cv; /*Condvar to force probe */
52 /*------------------------------------------------------------------------
53 * [private] fsprobe_CleanupInit
56 * Set up for recovery after an error in initialization (i.e.,
57 * during a call to fsprobe_Init.
64 * Error value otherwise.
67 * This routine is private to the module.
70 * Zeros out basic data structures.
71 *------------------------------------------------------------------------*/
74 fsprobe_CleanupInit(void)
75 { /*fsprobe_CleanupInit */
77 afs_int32 code; /*Return code from callback stubs */
78 struct rx_call *rxcall; /*Bogus param */
79 AFSCBFids *Fids_Array; /*Bogus param */
80 AFSCBs *CallBack_Array; /*Bogus param */
81 struct interfaceAddr *interfaceAddr; /*Bogus param */
83 fsprobe_ConnInfo = (struct fsprobe_ConnectionInfo *)0;
84 memset(&fsprobe_Results, 0, sizeof(struct fsprobe_ProbeResults));
86 rxcall = (struct rx_call *)0;
87 Fids_Array = (AFSCBFids *) 0;
88 CallBack_Array = (AFSCBs *) 0;
91 code = SRXAFSCB_CallBack(rxcall, Fids_Array, CallBack_Array);
94 code = SRXAFSCB_InitCallBackState2(rxcall, interfaceAddr);
97 code = SRXAFSCB_Probe(rxcall);
100 } /*fsprobe_CleanupInit */
103 /*------------------------------------------------------------------------
104 * [exported] fsprobe_Cleanup
107 * Clean up our memory and connection state.
110 * int a_releaseMem : Should we free up malloc'ed areas?
113 * 0 on total success,
114 * -1 if the module was never initialized, or there was a problem
115 * with the fsprobe connection array.
118 * fsprobe_numServers should be properly set. We don't do anything
119 * unless fsprobe_Init() has already been called.
122 * Shuts down Rx connections gracefully, frees allocated space
124 *------------------------------------------------------------------------*/
127 fsprobe_Cleanup(int a_releaseMem)
128 { /*fsprobe_Cleanup */
130 static char rn[] = "fsprobe_Cleanup"; /*Routine name */
131 int code; /*Return code */
132 int conn_idx; /*Current connection index */
133 struct fsprobe_ConnectionInfo *curr_conn; /*Ptr to fsprobe connection */
136 * Assume the best, but check the worst.
138 if (!fsprobe_initflag) {
139 fprintf(stderr, "[%s] Refused; module not initialized\n", rn);
145 * Take care of all Rx connections first. Check to see that the
146 * server count is a legal value.
148 if (fsprobe_numServers <= 0) {
150 "[%s] Illegal number of servers to clean up (fsprobe_numServers = %d)\n",
151 rn, fsprobe_numServers);
154 if (fsprobe_ConnInfo != (struct fsprobe_ConnectionInfo *)0) {
156 * The fsprobe connection structure array exists. Go through it
157 * and close up any Rx connections it holds.
159 curr_conn = fsprobe_ConnInfo;
160 for (conn_idx = 0; conn_idx < fsprobe_numServers; conn_idx++) {
161 if (curr_conn->rxconn != (struct rx_connection *)0) {
162 rx_DestroyConnection(curr_conn->rxconn);
163 curr_conn->rxconn = (struct rx_connection *)0;
165 if (curr_conn->rxVolconn != (struct rx_connection *)0) {
166 rx_DestroyConnection(curr_conn->rxVolconn);
167 curr_conn->rxVolconn = (struct rx_connection *)0;
170 } /*for each fsprobe connection */
171 } /*fsprobe connection structure exists */
172 } /*Legal number of servers */
175 * Now, release all the space we've allocated, if asked to.
178 if (fsprobe_ConnInfo != (struct fsprobe_ConnectionInfo *)0)
179 free(fsprobe_ConnInfo);
180 if (fsprobe_Results.stats != NULL)
181 free(fsprobe_Results.stats);
182 if (fsprobe_Results.probeOK != (int *)0)
183 free(fsprobe_Results.probeOK);
187 * Return the news, whatever it is.
191 } /*fsprobe_Cleanup */
193 /*------------------------------------------------------------------------
194 * [private] fsprobe_LWP
197 * This thread iterates over the server connections and gathers up
198 * the desired statistics from each one on a regular basis. When
199 * the sweep is done, the associated handler function is called
200 * to process the new data.
209 * Started by fsprobe_Init(), uses global sturctures.
213 *------------------------------------------------------------------------*/
215 fsprobe_LWP(void *unused)
218 static char rn[] = "fsprobe_LWP"; /*Routine name */
219 afs_int32 code; /*Results of calls */
220 struct timeval tv; /*Time structure */
221 struct timespec wait; /*Time to wait */
222 int conn_idx; /*Connection index */
223 struct fsprobe_ConnectionInfo *curr_conn; /*Current connection */
224 struct ProbeViceStatistics *curr_stats; /*Current stats region */
225 int *curr_probeOK; /*Current probeOK field */
226 ViceStatistics64 stats64; /*Current stats region */
227 stats64.ViceStatistics64_val = malloc(STATS64_VERSION *
229 while (1) { /*Service loop */
231 * Iterate through the server connections, gathering data.
232 * Don't forget to bump the probe count and zero the statistics
233 * areas before calling the servers.
237 "[%s] Waking up, collecting data from %d connected servers\n",
238 rn, fsprobe_numServers);
239 curr_conn = fsprobe_ConnInfo;
240 curr_stats = fsprobe_Results.stats;
241 curr_probeOK = fsprobe_Results.probeOK;
242 fsprobe_Results.probeNum++;
243 memset(fsprobe_Results.stats, 0, fsprobe_statsBytes);
244 memset(fsprobe_Results.probeOK, 0, fsprobe_probeOKBytes);
246 for (conn_idx = 0; conn_idx < fsprobe_numServers; conn_idx++) {
248 * Grab the statistics for the current FileServer, if the
249 * connection is valid.
252 fprintf(stderr, "[%s] Contacting server %s\n", rn,
253 curr_conn->hostName);
254 if (curr_conn->rxconn != (struct rx_connection *)0) {
257 "[%s] Connection valid, calling RXAFS_GetStatistics\n",
260 RXAFS_GetStatistics64(curr_conn->rxconn, STATS64_VERSION, &stats64);
261 if (*curr_probeOK == RXGEN_OPCODE)
263 RXAFS_GetStatistics(curr_conn->rxconn, (ViceStatistics *)curr_stats);
264 else if (*curr_probeOK == 0) {
265 curr_stats->CurrentTime = RoundInt64ToInt32(stats64.ViceStatistics64_val[STATS64_CURRENTTIME]);
266 curr_stats->BootTime = RoundInt64ToInt32(stats64.ViceStatistics64_val[STATS64_BOOTTIME]);
267 curr_stats->StartTime = RoundInt64ToInt32(stats64.ViceStatistics64_val[STATS64_STARTTIME]);
268 curr_stats->CurrentConnections = RoundInt64ToInt32(stats64.ViceStatistics64_val[STATS64_CURRENTCONNECTIONS]);
269 curr_stats->TotalFetchs = RoundInt64ToInt32(stats64.ViceStatistics64_val[STATS64_TOTALFETCHES]);
270 curr_stats->TotalStores = RoundInt64ToInt32(stats64.ViceStatistics64_val[STATS64_TOTALSTORES]);
271 curr_stats->WorkStations = RoundInt64ToInt32(stats64.ViceStatistics64_val[STATS64_WORKSTATIONS]);
275 /*Valid Rx connection */
277 * Call the Volume Server too to get additional stats
280 fprintf(stderr, "[%s] Contacting volume server %s\n", rn,
281 curr_conn->hostName);
282 if (curr_conn->rxVolconn != (struct rx_connection *)0) {
285 struct diskPartition partition;
286 struct diskPartition64 *partition64p =
287 malloc(sizeof(struct diskPartition64));
291 "[%s] Connection valid, calling RXAFS_GetStatistics\n",
293 for (i = 0; i < curr_conn->partCnt; i++) {
294 if (curr_conn->partList.partFlags[i] & PARTVALID) {
295 MapPartIdIntoName(curr_conn->partList.partId[i],
298 AFSVolPartitionInfo64(curr_conn->rxVolconn, pname,
302 curr_stats->Disk[i].BlocksAvailable =
303 RoundInt64ToInt31(partition64p->free);
304 curr_stats->Disk[i].TotalBlocks =
305 RoundInt64ToInt31(partition64p->minFree);
306 strcpy(curr_stats->Disk[i].Name, pname);
308 if (code == RXGEN_OPCODE) {
310 AFSVolPartitionInfo(curr_conn->rxVolconn,
313 curr_stats->Disk[i].BlocksAvailable =
315 curr_stats->Disk[i].TotalBlocks =
317 strcpy(curr_stats->Disk[i].Name, pname);
322 "Could not get information on server %s partition %s\n",
323 curr_conn->hostName, pname);
332 * Advance the fsprobe connection pointer & stats pointer.
338 } /*For each fsprobe connection */
341 * All (valid) connections have been probed. Now, call the
342 * associated handler function. The handler does not take
343 * any explicit parameters, rather gets to the goodies via
344 * some of the objects exported by this module.
348 "[%s] Polling complete, calling associated handler routine.\n",
350 code = fsprobe_Handler();
352 fprintf(stderr, "[%s] Handler routine returned error code %d\n",
356 * Fall asleep for the prescribed number of seconds or wakeup
359 gettimeofday(&tv, NULL);
360 wait.tv_sec = tv.tv_sec + fsprobe_ProbeFreqInSecs;
361 wait.tv_nsec = tv.tv_usec * 1000;
362 opr_mutex_enter(&fsprobe_force_lock);
363 code = opr_cv_timedwait(&fsprobe_force_cv, &fsprobe_force_lock, &wait);
364 opr_Assert(code == 0 || code == ETIMEDOUT);
365 opr_mutex_exit(&fsprobe_force_lock);
367 AFS_UNREACHED(free(stats64.ViceStatistics64_val));
368 AFS_UNREACHED(return(NULL));
371 /*list all the partitions on <aserver> */
372 static int newvolserver = 0;
375 XListPartitions(struct rx_connection *aconn, struct partList *ptrPartList,
379 struct partEntries partEnts;
383 if (newvolserver == 1) {
384 for (i = 0; i < 26; i++)
385 partIds.partIds[i] = -1;
387 code = AFSVolListPartitions(aconn, &partIds);
389 for (i = 0; i < 26; i++) {
390 if ((partIds.partIds[i]) != -1) {
391 ptrPartList->partId[j] = partIds.partIds[i];
392 ptrPartList->partFlags[j] = PARTVALID;
395 ptrPartList->partFlags[i] = 0;
401 partEnts.partEntries_len = 0;
402 partEnts.partEntries_val = NULL;
403 code = AFSVolXListPartitions(aconn, &partEnts);
405 if (code == RXGEN_OPCODE) {
406 newvolserver = 1; /* Doesn't support new interface */
413 *cntp = partEnts.partEntries_len;
414 if (*cntp > VOLMAXPARTS) {
416 "Warning: number of partitions on the server too high %d (process only %d)\n",
420 for (i = 0; i < *cntp; i++) {
421 ptrPartList->partId[i] = partEnts.partEntries_val[i];
422 ptrPartList->partFlags[i] = PARTVALID;
424 free(partEnts.partEntries_val);
429 "Could not fetch the list of partitions from the server\n");
434 /*------------------------------------------------------------------------
435 * [exported] fsprobe_Init
438 * Initialize the fsprobe module: set up Rx connections to the
439 * given set of servers, start up the probe and callback threads,
440 * and associate the routine to be called when a probe completes.
443 * int a_numServers : Num. servers to connect to.
444 * struct sockaddr_in *a_socketArray : Array of server sockets.
445 * int a_ProbeFreqInSecs : Probe frequency in seconds.
446 * int (*a_ProbeHandler)() : Ptr to probe handler fcn.
447 * int a_debug; : Turn debugging output on?
451 * -2 for (at least one) connection error,
452 * thread process creation code, if it failed,
453 * -1 for other fatal errors.
456 * *** MUST BE THE FIRST ROUTINE CALLED FROM THIS PACKAGE ***
457 * Also, the server security object CBsecobj MUST be a static,
458 * since it has to stick around after this routine exits.
461 * Sets up just about everything.
462 *------------------------------------------------------------------------*/
465 fsprobe_Init(int a_numServers, struct sockaddr_in *a_socketArray,
466 int a_ProbeFreqInSecs, int (*a_ProbeHandler)(void),
470 static char rn[] = "fsprobe_Init"; /*Routine name */
471 afs_int32 code; /*Return value */
472 static struct rx_securityClass *CBsecobj; /*Callback security object */
473 struct rx_securityClass *secobj; /*Client security object */
474 struct rx_service *rxsrv_afsserver; /*Server for AFS */
475 int arg_errfound; /*Argument error found? */
476 int curr_srv; /*Current server idx */
477 struct fsprobe_ConnectionInfo *curr_conn; /*Ptr to current conn */
478 char *hostNameFound; /*Ptr to returned host name */
479 int conn_err; /*Connection error? */
482 * If we've already been called, snicker at the bozo, gently
483 * remind him of his doubtful heritage, and return success.
485 if (fsprobe_initflag) {
486 fprintf(stderr, "[%s] Called multiple times!\n", rn);
489 fsprobe_initflag = 1;
491 opr_mutex_init(&fsprobe_force_lock);
492 opr_cv_init(&fsprobe_force_cv);
495 * Check the parameters for bogosities.
498 if (a_numServers <= 0) {
499 fprintf(stderr, "[%s] Illegal number of servers: %d\n", rn,
503 if (a_socketArray == (struct sockaddr_in *)0) {
504 fprintf(stderr, "[%s] Null server socket array argument\n", rn);
507 if (a_ProbeFreqInSecs <= 0) {
508 fprintf(stderr, "[%s] Illegal probe frequency: %d\n", rn,
512 if (a_ProbeHandler == (int (*)())0) {
513 fprintf(stderr, "[%s] Null probe handler function argument\n", rn);
520 * Record our passed-in info.
522 fsprobe_debug = a_debug;
523 fsprobe_numServers = a_numServers;
524 fsprobe_Handler = a_ProbeHandler;
525 fsprobe_ProbeFreqInSecs = a_ProbeFreqInSecs;
528 * Get ready in case we have to do a cleanup - basically, zero
531 fsprobe_CleanupInit();
534 * Allocate the necessary data structures and initialize everything
537 fsprobe_ConnInfo = (struct fsprobe_ConnectionInfo *)
538 malloc(a_numServers * sizeof(struct fsprobe_ConnectionInfo));
539 if (fsprobe_ConnInfo == (struct fsprobe_ConnectionInfo *)0) {
541 "[%s] Can't allocate %d connection info structs (%"AFS_SIZET_FMT" bytes)\n",
543 (a_numServers * sizeof(struct fsprobe_ConnectionInfo)));
544 return (-1); /*No cleanup needs to be done yet */
547 fsprobe_statsBytes = a_numServers * sizeof(struct ProbeViceStatistics);
548 fsprobe_Results.stats = (struct ProbeViceStatistics *)
549 malloc(fsprobe_statsBytes);
550 if (fsprobe_Results.stats == NULL) {
552 "[%s] Can't allocate %d statistics structs (%d bytes)\n", rn,
553 a_numServers, fsprobe_statsBytes);
554 fsprobe_Cleanup(1); /*Delete already-malloc'ed areas */
556 } else if (fsprobe_debug)
557 fprintf(stderr, "[%s] fsprobe_Results.stats allocated (%d bytes)\n",
558 rn, fsprobe_statsBytes);
560 fsprobe_probeOKBytes = a_numServers * sizeof(int);
561 fsprobe_Results.probeOK = malloc(fsprobe_probeOKBytes);
562 if (fsprobe_Results.probeOK == (int *)0) {
564 "[%s] Can't allocate %d probeOK array entries (%d bytes)\n",
565 rn, a_numServers, fsprobe_probeOKBytes);
566 fsprobe_Cleanup(1); /*Delete already-malloc'ed areas */
568 } else if (fsprobe_debug)
569 fprintf(stderr, "[%s] fsprobe_Results.probeOK allocated (%d bytes)\n",
570 rn, fsprobe_probeOKBytes);
572 fsprobe_Results.probeNum = 0;
573 fsprobe_Results.probeTime = 0;
574 memset(fsprobe_Results.stats, 0,
575 (a_numServers * sizeof(struct ProbeViceStatistics)));
578 * Initialize the Rx subsystem, just in case nobody's done it.
581 fprintf(stderr, "[%s] Initializing Rx\n", rn);
584 fprintf(stderr, "[%s] Fatal error in rx_Init()\n", rn);
588 fprintf(stderr, "[%s] Rx initialized.\n", rn);
591 * Create a null Rx server security object, to be used by the
594 CBsecobj = rxnull_NewServerSecurityObject();
595 if (CBsecobj == (struct rx_securityClass *)0) {
597 "[%s] Can't create null security object for the callback listener.\n",
599 fsprobe_Cleanup(1); /*Delete already-malloc'ed areas */
603 fprintf(stderr, "[%s] Callback server security object created\n", rn);
606 * Create a null Rx client security object, to be used by the
609 secobj = rxnull_NewClientSecurityObject();
610 if (secobj == (struct rx_securityClass *)0) {
612 "[%s] Can't create client security object for probe thread.\n",
614 fsprobe_Cleanup(1); /*Delete already-malloc'ed areas */
618 fprintf(stderr, "[%s] Probe thread client security object created\n",
621 curr_conn = fsprobe_ConnInfo;
623 for (curr_srv = 0; curr_srv < a_numServers; curr_srv++) {
625 * Copy in the socket info for the current server, resolve its
626 * printable name if possible.
629 fprintf(stderr, "[%s] Copying in the following socket info:\n",
631 fprintf(stderr, "[%s] IP addr 0x%x, port %d\n", rn,
632 (a_socketArray + curr_srv)->sin_addr.s_addr,
633 (a_socketArray + curr_srv)->sin_port);
635 memcpy(&(curr_conn->skt), a_socketArray + curr_srv,
636 sizeof(struct sockaddr_in));
639 hostutil_GetNameByINet(curr_conn->skt.sin_addr.s_addr);
640 if (hostNameFound == NULL) {
642 "[%s] Can't map Internet address %u to a string name\n",
643 rn, curr_conn->skt.sin_addr.s_addr);
644 curr_conn->hostName[0] = '\0';
646 strcpy(curr_conn->hostName, hostNameFound);
648 fprintf(stderr, "[%s] Host name for server index %d is %s\n",
649 rn, curr_srv, curr_conn->hostName);
653 * Make an Rx connection to the current server.
657 "[%s] Connecting to srv idx %d, IP addr 0x%x, port %d, service 1\n",
658 rn, curr_srv, curr_conn->skt.sin_addr.s_addr,
659 curr_conn->skt.sin_port);
660 curr_conn->rxconn = rx_NewConnection(curr_conn->skt.sin_addr.s_addr, /*Server addr */
661 curr_conn->skt.sin_port, /*Server port */
662 1, /*AFS service num */
663 secobj, /*Security object */
664 0); /*Number of above */
665 if (curr_conn->rxconn == (struct rx_connection *)0) {
667 "[%s] Can't create Rx connection to server %s (%u)\n",
668 rn, curr_conn->hostName, curr_conn->skt.sin_addr.s_addr);
672 fprintf(stderr, "[%s] New connection at %p\n", rn,
676 * Make an Rx connection to the current volume server.
680 "[%s] Connecting to srv idx %d, IP addr 0x%x, port %d, service 1\n",
681 rn, curr_srv, curr_conn->skt.sin_addr.s_addr,
683 curr_conn->rxVolconn = rx_NewConnection(curr_conn->skt.sin_addr.s_addr, /*Server addr */
684 htons(AFSCONF_VOLUMEPORT), /*Volume Server port */
685 VOLSERVICE_ID, /*AFS service num */
686 secobj, /*Security object */
687 0); /*Number of above */
688 if (curr_conn->rxVolconn == (struct rx_connection *)0) {
690 "[%s] Can't create Rx connection to volume server %s (%u)\n",
691 rn, curr_conn->hostName, curr_conn->skt.sin_addr.s_addr);
696 memset(&curr_conn->partList, 0, sizeof(struct partList));
697 curr_conn->partCnt = 0;
698 i = XListPartitions(curr_conn->rxVolconn, &curr_conn->partList,
701 curr_conn->partCnt = cnt;
705 fprintf(stderr, "[%s] New connection at %p\n", rn,
706 curr_conn->rxVolconn);
710 * Bump the current fsprobe connection to set up.
717 * Create the AFS callback service (listener).
720 fprintf(stderr, "[%s] Creating AFS callback listener\n", rn);
721 rxsrv_afsserver = rx_NewService(0, /*Use default port */
723 "afs", /*Service name */
724 &CBsecobj, /*Ptr to security object(s) */
725 1, /*Number of security objects */
726 RXAFSCB_ExecuteRequest); /*Dispatcher */
727 if (rxsrv_afsserver == (struct rx_service *)0) {
728 fprintf(stderr, "[%s] Can't create callback Rx service/listener\n",
730 fsprobe_Cleanup(1); /*Delete already-malloc'ed areas */
734 fprintf(stderr, "[%s] Callback listener created\n", rn);
737 * Start up the AFS callback service.
740 fprintf(stderr, "[%s] Starting up callback listener.\n", rn);
741 rx_StartServer(0 /*Don't donate yourself to thread pool */ );
744 * Start up the probe thread.
747 fprintf(stderr, "[%s] Creating the probe thread\n", rn);
748 code = pthread_create(&fsprobe_thread, NULL, fsprobe_LWP, NULL);
750 fprintf(stderr, "[%s] Can't create fsprobe thread! Error is %d\n", rn,
752 fsprobe_Cleanup(1); /*Delete already-malloc'ed areas */
757 * Return the final results.
767 /*------------------------------------------------------------------------
768 * [exported] fsprobe_ForceProbeNow
771 * Wake up the probe thread, forcing it to execute a probe immediately.
778 * Error value otherwise.
781 * The module must have been initialized.
785 *------------------------------------------------------------------------*/
788 fsprobe_ForceProbeNow(void)
789 { /*fsprobe_ForceProbeNow */
791 static char rn[] = "fsprobe_ForceProbeNow"; /*Routine name */
794 * There isn't a prayer unless we've been initialized.
796 if (!fsprobe_initflag) {
797 fprintf(stderr, "[%s] Must call fsprobe_Init first!\n", rn);
802 * Kick the sucker in the side.
804 opr_mutex_enter(&fsprobe_force_lock);
805 opr_cv_signal(&fsprobe_force_cv);
806 opr_mutex_exit(&fsprobe_force_lock);
809 * We did it, so report the happy news.
813 } /*fsprobe_ForceProbeNow */
815 /*------------------------------------------------------------------------
816 * [exported] fsprobe_Wait
819 * Wait for the collection to complete.
822 * int sleep_secs : time to wait in seconds. 0 means sleep forever.
826 * Error value otherwise.
829 * The module must have been initialized.
833 *------------------------------------------------------------------------*/
835 fsprobe_Wait(int sleep_secs)
840 if (sleep_secs == 0) {
844 code = select(0, 0, 0, 0, &tv);
849 tv.tv_sec = sleep_secs;
851 code = select(0, 0, 0, 0, &tv);