threaded-volserver-progress-20031114
[openafs.git] / src / vol / fssync.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         System:         VICE-TWO
12         Module:         fssync.c
13         Institution:    The Information Technology Center, Carnegie-Mellon University
14
15  */
16 #ifdef notdef
17
18 /* All this is going away in early 1989 */
19 int newVLDB;                    /* Compatibility flag */
20
21 #endif
22 static int newVLDB = 1;
23
24 #ifndef AFS_PTHREAD_ENV
25 #define USUAL_PRIORITY (LWP_MAX_PRIORITY - 2)
26
27 /*
28  * stack size increased from 8K because the HP machine seemed to have trouble
29  * with the smaller stack
30  */
31 #define USUAL_STACK_SIZE        (24 * 1024)
32 #endif /* !AFS_PTHREAD_ENV */
33
34 /*
35    fsync.c
36    File server synchronization with external volume utilities.
37  */
38 #include <afsconfig.h>
39 #include <afs/param.h>
40
41 RCSID
42     ("$Header$");
43
44 #include <sys/types.h>
45 #include <stdio.h>
46 #ifdef AFS_NT40_ENV
47 #include <winsock2.h>
48 #include <time.h>
49 #else
50 #include <sys/param.h>
51 #include <sys/socket.h>
52 #include <netinet/in.h>
53 #include <netdb.h>
54 #include <sys/time.h>
55 #endif
56 #include <errno.h>
57 #ifdef AFS_PTHREAD_ENV
58 #include <assert.h>
59 #else /* AFS_PTHREAD_ENV */
60 #include <afs/assert.h>
61 #endif /* AFS_PTHREAD_ENV */
62 #include <signal.h>
63
64 #ifdef HAVE_STRING_H
65 #include <string.h>
66 #else
67 #ifdef HAVE_STRINGS_H
68 #include <strings.h>
69 #endif
70 #endif
71
72
73 #include <rx/xdr.h>
74 #include <afs/afsint.h>
75 #include "nfs.h"
76 #include <afs/errors.h>
77 #include "fssync.h"
78 #include "lwp.h"
79 #include "lock.h"
80 #include <afs/afssyscalls.h>
81 #include "ihandle.h"
82 #include "vnode.h"
83 #include "volume.h"
84 #include "partition.h"
85
86 /*@printflike@*/ extern void Log(const char *format, ...);
87
88 #ifdef osi_Assert
89 #undef osi_Assert
90 #endif
91 #define osi_Assert(e) (void)(e)
92
93 extern int LogLevel;            /* Vice loglevel */
94 int (*V_BreakVolumeCallbacks) ();
95
96 #define MAXHANDLERS     4       /* Up to 4 clients; must be at least 2, so that
97                                  * move = dump+restore can run on single server */
98 #define MAXOFFLINEVOLUMES 30    /* This needs to be as big as the maximum
99                                  * number that would be offline for 1 operation.
100                                  * Current winner is salvage, which needs all
101                                  * cloned read-only copies offline when salvaging
102                                  * a single read-write volume */
103
104 #define MAX_BIND_TRIES  5       /* Number of times to retry socket bind */
105
106
107 struct offlineInfo {
108     VolumeId volumeID;
109     char partName[16];
110 };
111
112 static struct offlineInfo OfflineVolumes[MAXHANDLERS][MAXOFFLINEVOLUMES];
113
114 static FS_sd = -1;              /* Client socket for talking to file server */
115 static AcceptSd = -1;           /* Socket used by server for accepting connections */
116
117 static int getport();
118
119 struct command {
120     bit32 command;
121     bit32 reason;
122     VolumeId volume;
123     char partName[16];          /* partition name, e.g. /vicepa */
124 };
125
126
127 /* Forward declarations */
128 static int getport();
129 static void FSYNC_sync();
130 static void FSYNC_newconnection();
131 static void FSYNC_com();
132 static void FSYNC_Drop();
133 static void AcceptOn();
134 static void AcceptOff();
135 static void InitHandler();
136 static void CallHandler(fd_set * fdsetp);
137 static int AddHandler();
138 static int FindHandler();
139 static int FindHandler_r();
140 static int RemoveHandler();
141 static void GetHandler(fd_set * fdsetp, int *maxfdp);
142
143 /*
144  * This lock controls access to the handler array. The overhead
145  * is minimal in non-preemptive environments.
146  */
147 struct Lock FSYNC_handler_lock;
148
149 int
150 FSYNC_clientInit(void)
151 {
152     struct sockaddr_in addr;
153     /* I can't believe the following is needed for localhost connections!! */
154     static time_t backoff[] =
155         { 3, 3, 3, 5, 5, 5, 7, 15, 16, 24, 32, 40, 48, 0 };
156     time_t *timeout = &backoff[0];
157
158     for (;;) {
159         FS_sd = getport(&addr);
160         if (connect(FS_sd, (struct sockaddr *)&addr, sizeof(addr)) >= 0)
161             return 1;
162 #if defined(AFS_SGI_ENV)
163         /* down with worthless error messages! */
164         if (!*timeout) {
165             perror("FSYNC_clientInit failed (after many retries)");
166             break;
167         }
168 #else
169         if (!*timeout)
170             break;
171         if (!(*timeout & 1))
172             perror("FSYNC_clientInit temporary failure (will retry)");
173 #endif
174         FSYNC_clientFinis();
175         sleep(*timeout++);
176     }
177     perror("FSYNC_clientInit failed (giving up!)");
178     return 0;
179 }
180
181 void
182 FSYNC_clientFinis(void)
183 {
184 #ifdef AFS_NT40_ENV
185     closesocket(FS_sd);
186 #else
187     close(FS_sd);
188 #endif
189     FS_sd = -1;
190     Lock_Destroy(&FSYNC_handler_lock);
191 }
192
193 int
194 FSYNC_askfs(VolumeId volume, char *partName, int com, int reason)
195 {
196     byte response;
197     struct command command;
198     int n;
199     command.volume = volume;
200     command.command = com;
201     command.reason = reason;
202     if (partName)
203         strcpy(command.partName, partName);
204     else
205         command.partName[0] = 0;
206     assert(FS_sd != -1);
207     VFSYNC_LOCK
208 #ifdef AFS_NT40_ENV
209     if (send(FS_sd, (char *)&command, sizeof(command), 0) != sizeof(command)) {
210         printf("FSYNC_askfs: write to file server failed\n");
211         response = FSYNC_DENIED;
212         goto done;
213     }
214     while ((n = recv(FS_sd, &response, 1, 0)) != 1) {
215         if (n == 0 || WSAEINTR != WSAGetLastError()) {
216             printf("FSYNC_askfs: No response from file server\n");
217             response = FSYNC_DENIED;
218             goto done;
219         }
220     }
221 #else
222     if (write(FS_sd, &command, sizeof(command)) != sizeof(command)) {
223         printf("FSYNC_askfs: write to file server failed\n");
224         response = FSYNC_DENIED;
225         goto done;
226     }
227     while ((n = read(FS_sd, &response, 1)) != 1) {
228         if (n == 0 || errno != EINTR) {
229             printf("FSYNC_askfs: No response from file server\n");
230             response = FSYNC_DENIED;
231             goto done;
232         }
233     }
234 #endif
235     if (response == 0) {
236         printf
237             ("FSYNC_askfs: negative response from file server; volume %u, command %d\n",
238              command.volume, (int)command.command);
239     }
240  done:
241     VFSYNC_UNLOCK
242
243     return response;
244 }
245
246 void
247 FSYNC_fsInit(void)
248 {
249 #ifdef AFS_PTHREAD_ENV
250     pthread_t tid;
251     pthread_attr_t tattr;
252     assert(pthread_attr_init(&tattr) == 0);
253     assert(pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED) == 0);
254     assert(pthread_create(&tid, &tattr, FSYNC_sync, NULL) == 0);
255 #else /* AFS_PTHREAD_ENV */
256     PROCESS pid;
257     assert(LWP_CreateProcess
258            (FSYNC_sync, USUAL_STACK_SIZE, USUAL_PRIORITY, (void *)0,
259             "FSYNC_sync", &pid) == LWP_SUCCESS);
260 #endif /* AFS_PTHREAD_ENV */
261 }
262
263 static int
264 getport(struct sockaddr_in *addr)
265 {
266     int sd;
267
268     memset(addr, 0, sizeof(*addr));
269     assert((sd = socket(AF_INET, SOCK_STREAM, 0)) >= 0);
270 #ifdef STRUCT_SOCKADDR_HAS_SA_LEN
271     addr->sin_len = sizeof(struct sockaddr_in);
272 #endif
273     addr->sin_addr.s_addr = htonl(0x7f000001);
274     addr->sin_family = AF_INET; /* was localhost->h_addrtype */
275     addr->sin_port = htons(2040);       /* XXXX htons not _really_ neccessary */
276
277     return sd;
278 }
279
280 static void
281 FSYNC_sync()
282 {
283     struct sockaddr_in addr;
284     int on = 1;
285     extern VInit;
286     int code;
287     int numTries;
288 #ifdef AFS_PTHREAD_ENV
289     int tid;
290 #endif
291
292 #ifndef AFS_NT40_ENV
293     (void)signal(SIGPIPE, SIG_IGN);
294 #endif
295
296 #ifdef AFS_PTHREAD_ENV
297     /* set our 'thread-id' so that the host hold table works */
298     MUTEX_ENTER(&rx_stats_mutex);       /* protects rxi_pthread_hinum */
299     tid = ++rxi_pthread_hinum;
300     MUTEX_EXIT(&rx_stats_mutex);
301     pthread_setspecific(rx_thread_id_key, (void *)tid);
302     Log("Set thread id %d for FSYNC_sync\n", tid);
303 #endif /* AFS_PTHREAD_ENV */
304
305     while (!VInit) {
306         /* Let somebody else run until level > 0.  That doesn't mean that 
307          * all volumes have been attached. */
308 #ifdef AFS_PTHREAD_ENV
309         pthread_yield();
310 #else /* AFS_PTHREAD_ENV */
311         LWP_DispatchProcess();
312 #endif /* AFS_PTHREAD_ENV */
313     }
314     AcceptSd = getport(&addr);
315     /* Reuseaddr needed because system inexplicably leaves crud lying around */
316     code =
317         setsockopt(AcceptSd, SOL_SOCKET, SO_REUSEADDR, (char *)&on,
318                    sizeof(on));
319     if (code)
320         Log("FSYNC_sync: setsockopt failed with (%d)\n", errno);
321
322     for (numTries = 0; numTries < MAX_BIND_TRIES; numTries++) {
323         if ((code =
324              bind(AcceptSd, (struct sockaddr *)&addr, sizeof(addr))) == 0)
325             break;
326         Log("FSYNC_sync: bind failed with (%d), will sleep and retry\n",
327             errno);
328         sleep(5);
329     }
330     assert(!code);
331     listen(AcceptSd, 100);
332     InitHandler();
333     AcceptOn();
334     for (;;) {
335         fd_set readfds;
336         int maxfd;
337         GetHandler(&readfds, &maxfd);
338         /* Note: check for >= 1 below is essential since IOMGR_select
339          * doesn't have exactly same semantics as select.
340          */
341 #ifdef AFS_PTHREAD_ENV
342         if (select(maxfd + 1, &readfds, NULL, NULL, NULL) >= 1)
343 #else /* AFS_PTHREAD_ENV */
344         if (IOMGR_Select(maxfd + 1, &readfds, NULL, NULL, NULL) >= 1)
345 #endif /* AFS_PTHREAD_ENV */
346             CallHandler(&readfds);
347     }
348 }
349
350 static void
351 FSYNC_newconnection(int afd)
352 {
353     struct sockaddr_in other;
354     int junk, fd;
355     junk = sizeof(other);
356     fd = accept(afd, (struct sockaddr *)&other, &junk);
357     if (fd == -1) {
358         Log("FSYNC_newconnection:  accept failed, errno==%d\n", errno);
359         assert(1 == 2);
360     } else if (!AddHandler(fd, FSYNC_com)) {
361         AcceptOff();
362         assert(AddHandler(fd, FSYNC_com));
363     }
364 }
365
366 /*
367 #define TEST2081
368 */
369
370 afs_int32 FS_cnt = 0;
371 static void
372 FSYNC_com(int fd)
373 {
374     byte rc = FSYNC_OK;
375     int n, i;
376     Error error;
377     struct command command;
378     int leaveonline;
379     register struct offlineInfo *volumes, *v;
380     Volume *vp;
381     char tvolName[VMAXPATHLEN];
382
383     FS_cnt++;
384 #ifdef AFS_NT40_ENV
385     n = recv(fd, &command, sizeof(command), 0);
386 #else
387     n = read(fd, &command, sizeof(command));
388 #endif
389     if (n <= 0) {
390         FSYNC_Drop(fd);
391         return;
392     }
393     if (n < sizeof(command)) {
394         Log("FSYNC_com:  partial read (%d instead of %d); dropping connection (cnt=%d)\n", n, sizeof(command), FS_cnt);
395         FSYNC_Drop(fd);
396         return;
397     }
398     VATTACH_LOCK VOL_LOCK volumes = OfflineVolumes[FindHandler(fd)];
399     for (v = 0, i = 0; i < MAXOFFLINEVOLUMES; i++) {
400         if (volumes[i].volumeID == command.volume
401             && strcmp(volumes[i].partName, command.partName) == 0) {
402             v = &volumes[i];
403             break;
404         }
405     }
406     switch (command.command) {
407     case FSYNC_DONE:
408         /* don't try to put online, this call is made only after deleting
409          * a volume, in which case we want to remove the vol # from the
410          * OfflineVolumes array only */
411         if (v)
412             v->volumeID = 0;
413         break;
414     case FSYNC_ON:
415
416 /*
417 This is where a detatched volume gets reattached. However in the
418 special case where the volume is merely busy, it is already
419 attatched and it is only necessary to clear the busy flag. See
420 defect #2080 for details.
421 */
422
423         /* is the volume already attatched? */
424 #ifdef  notdef
425 /*
426  * XXX With the following enabled we had bizarre problems where the backup id would
427  * be reset to 0; that was due to the interaction between fileserver/volserver in that they
428  * both keep volumes in memory and the changes wouldn't be made to the fileserver. Some of
429  * the problems were due to refcnt changes as result of VGetVolume/VPutVolume which would call
430  * VOffline, etc. when we don't want to; someday the whole #2080 issue should be revisited to
431  * be done right XXX
432  */
433         vp = VGetVolume_r(&error, command.volume);
434         if (vp) {
435             /* yep, is the BUSY flag set? */
436             if (vp->specialStatus == VBUSY) {
437 /* test harness for defect #2081 */
438
439 #ifdef TEST2081
440                 /*
441                  * test #2081 by releasing TEST.2081,
442                  * so leave it alone here, zap it after
443                  */
444
445                 if (strcmp(vp->header->diskstuff.name, "TEST.2081") == 0)
446                     break;
447 #endif
448                 /* yep, clear BUSY flag */
449
450                 vp->specialStatus = 0;
451                 /* make sure vol is online */
452                 if (v) {
453                     v->volumeID = 0;
454                     V_inUse(vp) = 1;    /* online */
455                 }
456                 VPutVolume_r(vp);
457                 break;
458             }
459             VPutVolume_r(vp);
460         }
461 #endif
462
463         /* so, we need to attach the volume */
464
465         if (v)
466             v->volumeID = 0;
467         tvolName[0] = '/';
468         sprintf(&tvolName[1], VFORMAT, command.volume);
469
470         vp = VAttachVolumeByName_r(&error, command.partName, tvolName,
471                                    V_VOLUPD);
472         if (vp)
473             VPutVolume_r(vp);
474         break;
475     case FSYNC_OFF:
476     case FSYNC_NEEDVOLUME:{
477             leaveonline = 0;
478             /* not already offline, we need to find a slot for newly offline volume */
479             if (!v) {
480                 for (i = 0; i < MAXOFFLINEVOLUMES; i++) {
481                     if (volumes[i].volumeID == 0) {
482                         v = &volumes[i];
483                         break;
484                     }
485                 }
486             }
487             if (!v) {
488                 rc = FSYNC_DENIED;
489                 break;
490             }
491             vp = VGetVolume_r(&error, command.volume);
492             if (vp) {
493                 if (command.partName[0] != 0
494                     && strcmp(command.partName, vp->partition->name) != 0) {
495                     /* volume on desired partition is not online, so we
496                      * should treat this as an offline volume.
497                      */
498                     VPutVolume_r(vp);
499                     vp = (Volume *) 0;
500                 }
501             }
502             if (vp) {
503                 leaveonline = (command.command == FSYNC_NEEDVOLUME
504                                && (command.reason == V_READONLY
505                                    || (!VolumeWriteable(vp)
506                                        && (command.reason == V_CLONE
507                                            || command.reason == V_DUMP))
508                                )
509                     );
510                 if (!leaveonline) {
511                     if (command.command == FSYNC_NEEDVOLUME
512                         && (command.reason == V_CLONE
513                             || command.reason == V_DUMP)) {
514                         vp->specialStatus = VBUSY;
515                     }
516                     /* remember what volume we got, so we can keep track of how
517                      * many volumes the volserver or whatever is using.  Note that
518                      * vp is valid since leaveonline is only set when vp is valid.
519                      */
520                     v->volumeID = command.volume;
521                     strcpy(v->partName, vp->partition->name);
522                     if (!V_inUse(vp)) {
523                         /* in this case, VOffline just returns sans decrementing
524                          * ref count.  We could try to fix it, but it has lots of
525                          * weird callers.
526                          */
527                         VPutVolume_r(vp);
528                     } else {
529                         VOffline_r(vp, "A volume utility is running.");
530                     }
531                     vp = 0;
532                 } else {
533                     VUpdateVolume_r(&error, vp);        /* At least get volume stats right */
534                     if (LogLevel) {
535                         Log("FSYNC: Volume %u (%s) was left on line for an external %s request\n", V_id(vp), V_name(vp), command.reason == V_CLONE ? "clone" : command.reason == V_READONLY ? "readonly" : command.reason == V_DUMP ? "dump" : "UNKNOWN");
536                     }
537                 }
538                 if (vp)
539                     VPutVolume_r(vp);
540             }
541             rc = FSYNC_OK;
542             break;
543         }
544     case FSYNC_MOVEVOLUME:
545         /* Yuch:  the "reason" for the move is the site it got moved to... */
546         /* still set specialStatus so we stop sending back VBUSY.
547          * also should still break callbacks.  Note that I don't know
548          * how to tell if we should break all or not, so we just do it
549          * since it doesn't matter much if we do an extra break
550          * volume callbacks on a volume move within the same server */
551         vp = VGetVolume_r(&error, command.volume);
552         if (vp) {
553             vp->specialStatus = VMOVED;
554             VPutVolume_r(vp);
555         }
556
557         if (V_BreakVolumeCallbacks) {
558             Log("fssync: volume %u moved to %x; breaking all call backs\n",
559                 command.volume, command.reason);
560             VOL_UNLOCK VATTACH_UNLOCK(*V_BreakVolumeCallbacks) (command.
561                                                                 volume);
562         VATTACH_LOCK VOL_LOCK}
563         break;
564     case FSYNC_RESTOREVOLUME:
565         /* if the volume is being restored, break all callbacks on it */
566         if (V_BreakVolumeCallbacks) {
567             Log("fssync: volume %u restored; breaking all call backs\n",
568                 command.volume);
569             VOL_UNLOCK VATTACH_UNLOCK(*V_BreakVolumeCallbacks) (command.
570                                                                 volume);
571         VATTACH_LOCK VOL_LOCK}
572         break;
573     default:
574         rc = FSYNC_DENIED;
575         break;
576     }
577     VOL_UNLOCK VATTACH_UNLOCK
578 #ifdef AFS_NT40_ENV
579       (void) send(fd, &rc, 1, 0);
580 #else
581       (void) write(fd, &rc, 1);
582 #endif
583 }
584
585 static void
586 FSYNC_Drop(int fd)
587 {
588     struct offlineInfo *p;
589     register i;
590     Error error;
591     char tvolName[VMAXPATHLEN];
592
593     VATTACH_LOCK VOL_LOCK p = OfflineVolumes[FindHandler(fd)];
594     for (i = 0; i < MAXOFFLINEVOLUMES; i++) {
595         if (p[i].volumeID) {
596             Volume *vp;
597
598             tvolName[0] = '/';
599             sprintf(&tvolName[1], VFORMAT, p[i].volumeID);
600             vp = VAttachVolumeByName_r(&error, p[i].partName, tvolName,
601                                        V_VOLUPD);
602             if (vp)
603                 VPutVolume_r(vp);
604             p[i].volumeID = 0;
605         }
606     }
607     VOL_UNLOCK VATTACH_UNLOCK RemoveHandler(fd);
608 #ifdef AFS_NT40_ENV
609     closesocket(fd);
610 #else
611     close(fd);
612 #endif
613     AcceptOn();
614 }
615
616 static int AcceptHandler = -1;  /* handler id for accept, if turned on */
617
618 static void
619 AcceptOn()
620 {
621     if (AcceptHandler == -1) {
622         assert(AddHandler(AcceptSd, FSYNC_newconnection));
623         AcceptHandler = FindHandler(AcceptSd);
624     }
625 }
626
627 static void
628 AcceptOff()
629 {
630     if (AcceptHandler != -1) {
631         assert(RemoveHandler(AcceptSd));
632         AcceptHandler = -1;
633     }
634 }
635
636 /* The multiple FD handling code. */
637
638 static int HandlerFD[MAXHANDLERS];
639 static int (*HandlerProc[MAXHANDLERS]) ();
640
641 static void
642 InitHandler()
643 {
644     register int i;
645     ObtainWriteLock(&FSYNC_handler_lock);
646     for (i = 0; i < MAXHANDLERS; i++) {
647         HandlerFD[i] = -1;
648         HandlerProc[i] = 0;
649     }
650     ReleaseWriteLock(&FSYNC_handler_lock);
651 }
652
653 static void
654 CallHandler(fd_set * fdsetp)
655 {
656     register int i;
657     ObtainReadLock(&FSYNC_handler_lock);
658     for (i = 0; i < MAXHANDLERS; i++) {
659         if (HandlerFD[i] >= 0 && FD_ISSET(HandlerFD[i], fdsetp)) {
660             ReleaseReadLock(&FSYNC_handler_lock);
661             (*HandlerProc[i]) (HandlerFD[i]);
662             ObtainReadLock(&FSYNC_handler_lock);
663         }
664     }
665     ReleaseReadLock(&FSYNC_handler_lock);
666 }
667
668 static int
669 AddHandler(int afd, int (*aproc) ())
670 {
671     register int i;
672     ObtainWriteLock(&FSYNC_handler_lock);
673     for (i = 0; i < MAXHANDLERS; i++)
674         if (HandlerFD[i] == -1)
675             break;
676     if (i >= MAXHANDLERS) {
677         ReleaseWriteLock(&FSYNC_handler_lock);
678         return 0;
679     }
680     HandlerFD[i] = afd;
681     HandlerProc[i] = aproc;
682     ReleaseWriteLock(&FSYNC_handler_lock);
683     return 1;
684 }
685
686 static int
687 FindHandler(register int afd)
688 {
689     register int i;
690     ObtainReadLock(&FSYNC_handler_lock);
691     for (i = 0; i < MAXHANDLERS; i++)
692         if (HandlerFD[i] == afd) {
693             ReleaseReadLock(&FSYNC_handler_lock);
694             return i;
695         }
696     ReleaseReadLock(&FSYNC_handler_lock);       /* just in case */
697     assert(1 == 2);
698     return -1;                  /* satisfy compiler */
699 }
700
701 static int
702 FindHandler_r(register int afd)
703 {
704     register int i;
705     for (i = 0; i < MAXHANDLERS; i++)
706         if (HandlerFD[i] == afd) {
707             return i;
708         }
709     assert(1 == 2);
710     return -1;                  /* satisfy compiler */
711 }
712
713 static int
714 RemoveHandler(register int afd)
715 {
716     ObtainWriteLock(&FSYNC_handler_lock);
717     HandlerFD[FindHandler_r(afd)] = -1;
718     ReleaseWriteLock(&FSYNC_handler_lock);
719     return 1;
720 }
721
722 static void
723 GetHandler(fd_set * fdsetp, int *maxfdp)
724 {
725     register int i;
726     register int maxfd = -1;
727     FD_ZERO(fdsetp);
728     ObtainReadLock(&FSYNC_handler_lock);        /* just in case */
729     for (i = 0; i < MAXHANDLERS; i++)
730         if (HandlerFD[i] != -1) {
731             FD_SET(HandlerFD[i], fdsetp);
732             if (maxfd < HandlerFD[i])
733                 maxfd = HandlerFD[i];
734         }
735     *maxfdp = maxfd;
736     ReleaseReadLock(&FSYNC_handler_lock);       /* just in case */
737 }