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