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