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