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