include-afsconfig-before-param-h-20010712
[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     bzero(addr, sizeof(*addr));
238     assert((sd = socket(AF_INET, SOCK_STREAM, 0)) >= 0);
239     addr->sin_addr.s_addr = htonl(0x7f000001);
240     addr->sin_family = AF_INET; /* was localhost->h_addrtype */
241     addr->sin_port = htons(2040);  /* XXXX htons not _really_ neccessary */
242     return sd;
243 }
244
245 static void FSYNC_sync() {
246     struct sockaddr_in addr;
247     int on = 1;
248     extern VInit;
249     int code;
250     int numTries;
251
252 #ifndef AFS_NT40_ENV
253     signal(SIGPIPE, SIG_IGN);
254 #endif
255     while (!VInit) {
256       /* Let somebody else run until level > 0.  That doesn't mean that 
257        * all volumes have been attached. */
258 #ifdef AFS_PTHREAD_ENV
259         pthread_yield();
260 #else /* AFS_PTHREAD_ENV */
261         LWP_DispatchProcess();
262 #endif /* AFS_PTHREAD_ENV */
263       }
264     AcceptSd = getport(&addr);
265     /* Reuseaddr needed because system inexplicably leaves crud lying around */
266     code = setsockopt(AcceptSd, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof(on));
267     if (code) Log("FSYNC_sync: setsockopt failed with (%d)\n",errno);
268
269     for (numTries=0; numTries < MAX_BIND_TRIES; numTries++) {
270     if ((code = bind(AcceptSd, (struct sockaddr *) &addr, sizeof(addr))) == 0) break;
271     Log("FSYNC_sync: bind failed with (%d), will sleep and retry\n",errno);
272     sleep(5);
273     }
274     assert(!code);
275     listen(AcceptSd,100);
276     InitHandler();
277     AcceptOn();
278     for(;;) {
279         fd_set readfds;
280         int maxfd;
281         GetHandler(&readfds, &maxfd);
282         /* Note: check for >= 1 below is essential since IOMGR_select
283          * doesn't have exactly same semantics as select.
284          */
285 #ifdef AFS_PTHREAD_ENV
286         if (select(maxfd+1, &readfds, NULL, NULL, NULL) >= 1)
287 #else /* AFS_PTHREAD_ENV */
288         if (IOMGR_Select(maxfd+1, &readfds, NULL, NULL, NULL) >= 1)
289 #endif /* AFS_PTHREAD_ENV */
290             CallHandler(&readfds);
291     }
292 }
293
294 static void FSYNC_newconnection(afd)
295     int afd;
296 {
297     struct sockaddr_in other;
298     int junk, fd;
299     junk = sizeof(other);
300     fd = accept(afd, (struct sockaddr *) &other, &junk);
301     if (fd == -1) {
302         Log("FSYNC_newconnection:  accept failed, errno==%d\n", errno);
303         assert(1==2);
304     }
305     else if (!AddHandler(fd, FSYNC_com)) {
306         AcceptOff();
307         assert(AddHandler(fd, FSYNC_com));
308     }
309 }
310
311 /*
312 #define TEST2081
313 */
314
315 afs_int32 FS_cnt = 0;
316 static void FSYNC_com(fd)
317     int fd;
318 {
319     byte rc = FSYNC_OK;
320     int n, i;
321     Error error;
322     struct command command;
323     int leaveonline;
324     register struct offlineInfo *volumes, *v;
325     Volume *vp;
326     char tvolName[VMAXPATHLEN];
327
328     FS_cnt++;
329 #ifdef AFS_NT40_ENV
330     n = recv(fd, &command, sizeof (command), 0);
331 #else    
332     n = read(fd, &command, sizeof (command));
333 #endif
334     if (n <= 0) {
335         FSYNC_Drop(fd);
336         return;
337     }
338     if (n < sizeof(command)) {
339         Log("FSYNC_com:  partial read (%d instead of %d); dropping connection (cnt=%d)\n", n, sizeof(command), FS_cnt);
340         FSYNC_Drop(fd);
341         return;
342     }
343     VATTACH_LOCK
344     VOL_LOCK
345     volumes = OfflineVolumes[FindHandler(fd)];
346     for (v = 0, i = 0; i<MAXOFFLINEVOLUMES; i++) {
347         if (volumes[i].volumeID == command.volume
348             && strcmp(volumes[i].partName, command.partName)==0) {
349             v = &volumes[i];
350             break;
351         }
352     }
353     switch (command.command) {
354         case FSYNC_DONE:
355             /* don't try to put online, this call is made only after deleting
356                a volume, in which case we want to remove the vol # from the
357                OfflineVolumes array only */
358             if (v) v->volumeID = 0;
359             break;
360         case FSYNC_ON:
361
362 /*
363 This is where a detatched volume gets reattached. However in the
364 special case where the volume is merely busy, it is already
365 attatched and it is only necessary to clear the busy flag. See
366 defect #2080 for details.
367 */
368
369                 /* is the volume already attatched? */
370 #ifdef  notdef
371 /*
372  * XXX With the following enabled we had bizarre problems where the backup id would
373  * be reset to 0; that was due to the interaction between fileserver/volserver in that they
374  * both keep volumes in memory and the changes wouldn't be made to the fileserver. Some of
375  * the problems were due to refcnt changes as result of VGetVolume/VPutVolume which would call
376  * VOffline, etc. when we don't want to; someday the whole #2080 issue should be revisited to
377  * be done right XXX
378  */
379                 vp=VGetVolume_r(&error,command.volume);
380                 if(vp) {
381                     /* yep, is the BUSY flag set? */
382                     if(vp->specialStatus==VBUSY) {
383 /* test harness for defect #2081 */
384
385 #ifdef TEST2081
386                         /*
387                           test #2081 by releasing TEST.2081,
388                           so leave it alone here, zap it after
389                           */
390
391                         if(strcmp(vp->header->diskstuff.name,"TEST.2081")==0)
392                             break;
393 #endif
394                         /* yep, clear BUSY flag */
395
396                         vp->specialStatus=0;
397                         /* make sure vol is online */
398                         if(v) {
399                             v->volumeID=0;
400                             V_inUse(vp)=1;      /* online */
401                         }
402                         VPutVolume_r(vp);
403                         break;
404                     }
405                     VPutVolume_r(vp); 
406                 }
407 #endif
408
409                 /* so, we need to attach the volume */
410
411             if (v)
412                 v->volumeID = 0;
413             tvolName[0] = '/';
414             sprintf(&tvolName[1], VFORMAT, command.volume);
415
416             vp = VAttachVolumeByName_r(&error, command.partName, tvolName, V_UPDATE);
417             if (vp)
418                 VPutVolume_r(vp);
419             break;
420         case FSYNC_OFF:
421         case FSYNC_NEEDVOLUME: {
422             leaveonline = 0;
423             /* not already offline, we need to find a slot for newly offline volume */
424             if (!v) {
425                 for (i = 0; i<MAXOFFLINEVOLUMES; i++) {
426                     if (volumes[i].volumeID == 0) {
427                         v = &volumes[i];
428                         break;
429                     }
430                 }
431             }
432             if (!v) {
433                 rc = FSYNC_DENIED;
434                 break;
435             }
436             vp = VGetVolume_r(&error, command.volume);
437             if (vp) {
438                 if (command.partName[0] != 0
439                     && strcmp(command.partName, vp->partition->name) != 0) {
440                     /* volume on desired partition is not online, so we
441                      * should treat this as an offline volume.
442                      */
443                     VPutVolume_r(vp);
444                     vp = (Volume *) 0;
445                 }
446             }
447             if (vp) {
448                 leaveonline = (
449                     command.command==FSYNC_NEEDVOLUME
450                     && (command.reason==V_READONLY
451                        || (!VolumeWriteable(vp)
452                           && (command.reason==V_CLONE || command.reason==V_DUMP))
453                     )
454                 );
455                 if (!leaveonline) {
456                     if (command.command==FSYNC_NEEDVOLUME
457                         && (command.reason==V_CLONE || command.reason==V_DUMP)) {
458                             vp->specialStatus = VBUSY;
459                         }
460                     /* remember what volume we got, so we can keep track of how
461                      * many volumes the volserver or whatever is using.  Note that
462                      * vp is valid since leaveonline is only set when vp is valid.
463                      */
464                     v->volumeID = command.volume;
465                     strcpy(v->partName, vp->partition->name);
466                     if (!V_inUse(vp)) {
467                         /* in this case, VOffline just returns sans decrementing
468                          * ref count.  We could try to fix it, but it has lots of
469                          * weird callers.
470                          */
471                         VPutVolume_r(vp);
472                     }
473                     else {
474                         VOffline_r(vp, "A volume utility is running.");
475                     }
476                     vp = 0;
477                 }
478                 else {
479                     VUpdateVolume_r(&error, vp);        /* At least get volume stats right */
480                     if (LogLevel) {
481                         Log("FSYNC: Volume %u (%s) was left on line for an external %s request\n",
482                             V_id(vp), V_name(vp),
483                             command.reason == V_CLONE? "clone":
484                             command.reason == V_READONLY? "readonly":
485                             command.reason == V_DUMP? "dump" : "UNKNOWN");
486                     }
487                 }
488                 if (vp)
489                     VPutVolume_r(vp);
490             }
491             rc = FSYNC_OK;
492             break;
493         }
494         case FSYNC_MOVEVOLUME:
495             /* Yuch:  the "reason" for the move is the site it got moved to... */
496             /* still set specialStatus so we stop sending back VBUSY.
497                also should still break callbacks.  Note that I don't know
498                how to tell if we should break all or not, so we just do it
499                since it doesn't matter much if we do an extra break
500                volume callbacks on a volume move within the same server */
501             vp = VGetVolume_r(&error, command.volume);
502             if (vp) {
503                 vp->specialStatus = VMOVED;
504                 VPutVolume_r(vp);
505             }
506             if (V_BreakVolumeCallbacks) {
507                 Log("fssync: volume %u moved to %x; breaking all call backs\n",
508                     command.volume, command.reason);
509                 VOL_UNLOCK
510                 VATTACH_UNLOCK
511                 (*V_BreakVolumeCallbacks)(command.volume);
512                 VATTACH_LOCK
513                 VOL_LOCK
514             }
515             break;
516         case FSYNC_RESTOREVOLUME:
517             /* if the volume is being restored, break all callbacks on it*/
518             if (V_BreakVolumeCallbacks) {
519                 VOL_UNLOCK
520                 VATTACH_UNLOCK
521                 (*V_BreakVolumeCallbacks)(command.volume);
522                 VATTACH_LOCK
523                 VOL_LOCK
524             }
525             break;
526         default:
527             rc = FSYNC_DENIED;
528             break;
529     }
530     VOL_UNLOCK
531     VATTACH_UNLOCK
532 #ifdef AFS_NT40_ENV
533     send(fd, &rc, 1, 0);
534 #else
535     write(fd, &rc, 1);
536 #endif
537 }
538
539 static void FSYNC_Drop(fd)
540     int fd;
541 {
542     struct offlineInfo *p;
543     register i;
544     Error error;
545     char tvolName[VMAXPATHLEN];
546
547     VATTACH_LOCK
548     VOL_LOCK
549     p = OfflineVolumes[FindHandler(fd)];
550     for (i = 0; i<MAXOFFLINEVOLUMES; i++) {
551         if (p[i].volumeID) {
552             Volume *vp;
553
554             tvolName[0] = '/';
555             sprintf(&tvolName[1], VFORMAT, p[i].volumeID);
556             vp = VAttachVolumeByName_r(&error, p[i].partName, tvolName, V_UPDATE);
557             if (vp)
558                 VPutVolume_r(vp);
559             p[i].volumeID = 0;
560         }
561     }
562     VOL_UNLOCK
563     VATTACH_UNLOCK
564     RemoveHandler(fd);
565 #ifdef AFS_NT40_ENV
566     closesocket(fd);
567 #else
568     close(fd);
569 #endif
570     AcceptOn();
571 }
572
573 static int AcceptHandler = -1;  /* handler id for accept, if turned on */
574
575 static void AcceptOn() {
576     if (AcceptHandler == -1) {
577         assert(AddHandler(AcceptSd, FSYNC_newconnection));
578         AcceptHandler = FindHandler(AcceptSd);      
579     }
580 }
581
582 static void AcceptOff() {
583     if (AcceptHandler != -1) {
584         assert(RemoveHandler(AcceptSd));
585         AcceptHandler = -1;
586     }
587 }
588
589 /* The multiple FD handling code. */
590
591 static int HandlerFD[MAXHANDLERS];
592 static int (*HandlerProc[MAXHANDLERS])();
593
594 static void InitHandler ()
595 {
596     register int i;
597     ObtainWriteLock(&FSYNC_handler_lock);
598     for(i=0;i<MAXHANDLERS;i++)
599         {HandlerFD[i] = -1;
600         HandlerProc[i] = 0;
601         }
602     ReleaseWriteLock(&FSYNC_handler_lock);
603 }
604
605 static void CallHandler(fd_set *fdsetp)
606 {
607     register int i;
608     ObtainReadLock(&FSYNC_handler_lock);
609     for(i=0;i<MAXHANDLERS;i++) {
610         if (HandlerFD[i] >= 0 && FD_ISSET(HandlerFD[i], fdsetp)) {
611             ReleaseReadLock(&FSYNC_handler_lock);
612             (*HandlerProc[i])(HandlerFD[i]);
613             ObtainReadLock(&FSYNC_handler_lock);
614         }
615     }
616     ReleaseReadLock(&FSYNC_handler_lock);
617 }
618
619 static int AddHandler (afd, aproc)
620     int afd;
621     int (*aproc)();
622 {
623     register int i;
624     ObtainWriteLock(&FSYNC_handler_lock);
625     for(i=0;i<MAXHANDLERS;i++)
626         if (HandlerFD[i] == -1) break;
627     if (i>=MAXHANDLERS) {
628         ReleaseWriteLock(&FSYNC_handler_lock);
629         return 0;
630     }
631     HandlerFD[i] = afd;
632     HandlerProc[i] = aproc;
633     ReleaseWriteLock(&FSYNC_handler_lock);
634     return 1;
635 }
636
637 static int FindHandler (afd)
638     register int afd;
639 {
640     register int i;
641     ObtainReadLock(&FSYNC_handler_lock);
642     for(i=0;i<MAXHANDLERS;i++)
643         if (HandlerFD[i] == afd) {
644         ReleaseReadLock(&FSYNC_handler_lock);
645         return i;
646     }
647     ReleaseReadLock(&FSYNC_handler_lock); /* just in case */
648     assert(1 == 2);
649     return -1; /* satisfy compiler */
650 }
651
652 static int FindHandler_r (afd)
653     register int afd;
654 {
655     register int i;
656     for(i=0;i<MAXHANDLERS;i++)
657         if (HandlerFD[i] == afd) {
658         return i;
659     }
660     assert(1 == 2);
661     return -1; /* satisfy compiler */
662 }
663
664 static int RemoveHandler (afd)
665     register int afd;
666 {
667     ObtainWriteLock(&FSYNC_handler_lock);
668     HandlerFD[FindHandler_r(afd)] = -1;
669     ReleaseWriteLock(&FSYNC_handler_lock);
670     return 1;
671 }
672
673 static void GetHandler (fd_set *fdsetp, int *maxfdp)
674 {
675     register int i;
676     register int maxfd = -1;
677     FD_ZERO(fdsetp);
678     ObtainReadLock(&FSYNC_handler_lock); /* just in case */
679     for(i=0;i<MAXHANDLERS;i++)
680         if (HandlerFD[i] != -1) {
681             FD_SET(HandlerFD[i], fdsetp);
682             if (maxfd < HandlerFD[i])
683                 maxfd = HandlerFD[i];
684         }
685     *maxfdp = maxfd;
686     ReleaseReadLock(&FSYNC_handler_lock); /* just in case */
687 }
688