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