prototypes-fixes-20020821
[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, ack = 1;
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_UPDATE);
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 #ifdef AFS_NT40_ENV
520             send(fd, &rc, 1, 0);
521 #else
522             write(fd, &rc, 1);
523 #endif
524             ack = 0;
525             if (V_BreakVolumeCallbacks) {
526                 Log("fssync: volume %u moved to %x; breaking all call backs\n",
527                     command.volume, command.reason);
528                 VOL_UNLOCK
529                 VATTACH_UNLOCK
530                 (*V_BreakVolumeCallbacks)(command.volume);
531                 VATTACH_LOCK
532                 VOL_LOCK
533             }
534             break;
535         case FSYNC_RESTOREVOLUME:
536             /* if the volume is being restored, break all callbacks on it*/
537 #ifdef AFS_NT40_ENV
538             send(fd, &rc, 1, 0);
539 #else
540             write(fd, &rc, 1);
541 #endif
542             ack = 0;
543             if (V_BreakVolumeCallbacks) {
544                 VOL_UNLOCK
545                 VATTACH_UNLOCK
546                 (*V_BreakVolumeCallbacks)(command.volume);
547                 VATTACH_LOCK
548                 VOL_LOCK
549             }
550             break;
551         default:
552             rc = FSYNC_DENIED;
553             break;
554     }
555     VOL_UNLOCK
556     VATTACH_UNLOCK
557     if (ack) {
558 #ifdef AFS_NT40_ENV
559         send(fd, &rc, 1, 0);
560 #else
561         write(fd, &rc, 1);
562 #endif
563     }
564 }
565
566 static void FSYNC_Drop(fd)
567     int fd;
568 {
569     struct offlineInfo *p;
570     register i;
571     Error error;
572     char tvolName[VMAXPATHLEN];
573
574     VATTACH_LOCK
575     VOL_LOCK
576     p = OfflineVolumes[FindHandler(fd)];
577     for (i = 0; i<MAXOFFLINEVOLUMES; i++) {
578         if (p[i].volumeID) {
579             Volume *vp;
580
581             tvolName[0] = '/';
582             sprintf(&tvolName[1], VFORMAT, p[i].volumeID);
583             vp = VAttachVolumeByName_r(&error, p[i].partName, tvolName, V_UPDATE);
584             if (vp)
585                 VPutVolume_r(vp);
586             p[i].volumeID = 0;
587         }
588     }
589     VOL_UNLOCK
590     VATTACH_UNLOCK
591     RemoveHandler(fd);
592 #ifdef AFS_NT40_ENV
593     closesocket(fd);
594 #else
595     close(fd);
596 #endif
597     AcceptOn();
598 }
599
600 static int AcceptHandler = -1;  /* handler id for accept, if turned on */
601
602 static void AcceptOn() {
603     if (AcceptHandler == -1) {
604         assert(AddHandler(AcceptSd, FSYNC_newconnection));
605         AcceptHandler = FindHandler(AcceptSd);      
606     }
607 }
608
609 static void AcceptOff() {
610     if (AcceptHandler != -1) {
611         assert(RemoveHandler(AcceptSd));
612         AcceptHandler = -1;
613     }
614 }
615
616 /* The multiple FD handling code. */
617
618 static int HandlerFD[MAXHANDLERS];
619 static int (*HandlerProc[MAXHANDLERS])();
620
621 static void InitHandler ()
622 {
623     register int i;
624     ObtainWriteLock(&FSYNC_handler_lock);
625     for(i=0;i<MAXHANDLERS;i++)
626         {HandlerFD[i] = -1;
627         HandlerProc[i] = 0;
628         }
629     ReleaseWriteLock(&FSYNC_handler_lock);
630 }
631
632 static void CallHandler(fd_set *fdsetp)
633 {
634     register int i;
635     ObtainReadLock(&FSYNC_handler_lock);
636     for(i=0;i<MAXHANDLERS;i++) {
637         if (HandlerFD[i] >= 0 && FD_ISSET(HandlerFD[i], fdsetp)) {
638             ReleaseReadLock(&FSYNC_handler_lock);
639             (*HandlerProc[i])(HandlerFD[i]);
640             ObtainReadLock(&FSYNC_handler_lock);
641         }
642     }
643     ReleaseReadLock(&FSYNC_handler_lock);
644 }
645
646 static int AddHandler (afd, aproc)
647     int afd;
648     int (*aproc)();
649 {
650     register int i;
651     ObtainWriteLock(&FSYNC_handler_lock);
652     for(i=0;i<MAXHANDLERS;i++)
653         if (HandlerFD[i] == -1) break;
654     if (i>=MAXHANDLERS) {
655         ReleaseWriteLock(&FSYNC_handler_lock);
656         return 0;
657     }
658     HandlerFD[i] = afd;
659     HandlerProc[i] = aproc;
660     ReleaseWriteLock(&FSYNC_handler_lock);
661     return 1;
662 }
663
664 static int FindHandler (afd)
665     register int afd;
666 {
667     register int i;
668     ObtainReadLock(&FSYNC_handler_lock);
669     for(i=0;i<MAXHANDLERS;i++)
670         if (HandlerFD[i] == afd) {
671         ReleaseReadLock(&FSYNC_handler_lock);
672         return i;
673     }
674     ReleaseReadLock(&FSYNC_handler_lock); /* just in case */
675     assert(1 == 2);
676     return -1; /* satisfy compiler */
677 }
678
679 static int FindHandler_r (afd)
680     register int afd;
681 {
682     register int i;
683     for(i=0;i<MAXHANDLERS;i++)
684         if (HandlerFD[i] == afd) {
685         return i;
686     }
687     assert(1 == 2);
688     return -1; /* satisfy compiler */
689 }
690
691 static int RemoveHandler (afd)
692     register int afd;
693 {
694     ObtainWriteLock(&FSYNC_handler_lock);
695     HandlerFD[FindHandler_r(afd)] = -1;
696     ReleaseWriteLock(&FSYNC_handler_lock);
697     return 1;
698 }
699
700 static void GetHandler (fd_set *fdsetp, int *maxfdp)
701 {
702     register int i;
703     register int maxfd = -1;
704     FD_ZERO(fdsetp);
705     ObtainReadLock(&FSYNC_handler_lock); /* just in case */
706     for(i=0;i<MAXHANDLERS;i++)
707         if (HandlerFD[i] != -1) {
708             FD_SET(HandlerFD[i], fdsetp);
709             if (maxfd < HandlerFD[i])
710                 maxfd = HandlerFD[i];
711         }
712     *maxfdp = maxfd;
713     ReleaseReadLock(&FSYNC_handler_lock); /* just in case */
714 }
715