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