Initial IBM OpenAFS 1.0 tree
[openafs.git] / src / ubik / beacon.c
1 /*
2  * (C) COPYRIGHT IBM CORPORATION 1987, 1988
3  * LICENSED MATERIALS - PROPERTY OF IBM
4  */
5 #include <afs/param.h>
6 #include <sys/types.h>
7 #ifdef AFS_NT40_ENV
8 #include <winsock2.h>
9 #include <time.h>
10 #else
11 #include <sys/file.h>
12 #include <sys/time.h>
13 #include <sys/socket.h>
14 #include <netinet/in.h>
15 #include <netdb.h>
16 #endif
17 #include <errno.h>
18 #include <lock.h>
19 #include <rx/xdr.h>
20 #include <rx/rx.h>
21 #include <rx/rx_multi.h>
22 #ifndef AFS_NT40_ENV
23 #include <afs/afsutil.h>
24 #include <afs/netutils.h>
25 #endif
26
27 #define UBIK_INTERNALS
28 #include "ubik.h"
29 #include "ubik_int.h"
30
31 /* statics used to determine if we're the sync site */
32 static afs_int32 syncSiteUntil = 0;         /* valid only if amSyncSite */
33 int ubik_amSyncSite = 0;            /* flag telling if I'm sync site */
34 static nServers;                    /* total number of servers */
35 static char amIMagic=0;             /* is this host the magic host */
36 extern struct rx_securityClass *rxnull_NewClientSecurityObject();
37 int (*ubik_CRXSecurityProc)();
38 char *ubik_CRXSecurityRock;
39 afs_int32 ubikSecIndex;
40 struct rx_securityClass     *ubikSecClass;
41
42
43 /* Module responsible for both deciding if we're currently the sync site,
44  * and keeping collecting votes so as to stay sync site.
45  *
46  * The basic module contacts all of the servers it can, trying to get them to vote
47  * for this server for sync site.  The vote request message (called a beacon message)
48  * also specifies until which time this site claims to be the sync site, if at all, thus enabling
49  * receiving sites to know how long the sync site guarantee is made for.
50  *
51  * Each  of these beacon messages is thus both a declaration of how long this site will
52  * remain sync site, and an attempt to extend that time by collecting votes for a later
53  * sync site extension.
54  *
55  * The voting module is responsible for choosing a reasonable time until which it promises
56  * not to vote for someone else.  This parameter (BIG seconds) is not actually passed in
57  * the interface (perhaps it should be?) but is instead a compile time constant that both
58  * sides know about.
59  
60  * The beacon and vote modules work intimately together; the vote module decides how long
61  * it should promise the beacon module its vote, and the beacon module takes all of these
62  * votes and decides for how long it is the synchronization site.
63  */
64
65 /* procedure called from debug rpc call to get this module's state for debugging */
66 ubeacon_Debug(aparm)
67 register struct ubik_debug *aparm; {
68     /* fill in beacon's state fields in the ubik_debug structure */
69     aparm->syncSiteUntil = syncSiteUntil;
70     aparm->nServers = nServers;
71 }
72
73 /* procedure that determines whether this site has enough current votes to remain sync site.
74  *  called from higher-level modules (everything but the vote module).
75  *
76  * If we're the sync site, check that our guarantees, obtained by the ubeacon_Interact
77  * light-weight process, haven't expired.  We're sync site as long as a majority of the
78  * servers in existence have promised us unexpired guarantees.  The variable ubik_syncSiteUntil
79  * contains the time at which the latest of the majority of the sync site guarantees expires
80  * (if the variable ubik_amSyncSite is true)
81  * This module also calls up to the recovery module if it thinks that the recovery module
82  * may have to pick up a new database (which offucr sif we lose the sync site votes).
83  */
84 ubeacon_AmSyncSite() {
85     register afs_int32 now;
86     register afs_int32 rcode;
87     
88     /* special case for fast startup */
89     if (nServers == 1) {
90         return 1;       /* one guy is always the sync site */
91     }
92
93     if (ubik_amSyncSite == 0) rcode = 0;  /* if I don't think I'm the sync site, say so */
94     else {
95         now = FT_ApproxTime();
96         if (syncSiteUntil <= now) {         /* if my votes have expired, say so */
97             if (ubik_amSyncSite) ubik_dprint("Ubik: I am no longer the sync site\n");
98             ubik_amSyncSite = 0;
99             rcode = 0;
100         }
101         else {
102             rcode = 1;              /* otherwise still have the required votes */
103         }
104     }
105     if (rcode == 0) urecovery_ResetState(); /* force recovery to re-execute */
106     ubik_dprint("beacon: amSyncSite is %d\n", rcode);
107     return rcode;
108 }
109
110 /* setup server list; called with two parms, first is my address, second is list of other servers
111  * called only at initialization to set up the list of servers to contact for votes.  Just creates
112  * the server structure.  Note that there are two connections in every server structure, one for
113  * vote calls (which must always go through quickly) and one for database operations, which
114  * are subject to waiting for locks.  If we used only one, the votes would sometimes get
115  * held up behind database operations, and the sync site guarantees would timeout
116  * even though the host would be up for communication.
117  *
118  * The "magic" host is the one with the lowest internet address.  It is
119  * magic because its vote counts epsilon more than the others.  This acts
120  * as a tie-breaker when we have an even number of hosts in the system.
121  * For example, if the "magic" host is up in a 2 site system, then it
122  * is sync site.  Without the magic host hack, if anyone crashed in a 2
123  * site system, we'd be out of business.
124  */
125 ubeacon_InitServerList(ame, aservers)
126     afs_int32 ame;
127     register afs_int32 aservers[]; {
128     register struct ubik_server *ts;
129     register afs_int32 servAddr;
130     register afs_int32 i, code;
131     afs_int32 magicHost;
132     struct ubik_server *magicServer;
133
134     /* verify that the addresses passed in are correct */
135     if ( code = verifyInterfaceAddress(&ame, aservers ))
136         return code;
137
138     /* get the security index to use, if we can */
139     if (ubik_CRXSecurityProc) {
140         i = (*ubik_CRXSecurityProc)(ubik_CRXSecurityRock, &ubikSecClass, &ubikSecIndex);
141     }
142     else i = 1;
143     if (i) {
144         /* don't have sec module yet */
145         ubikSecIndex = 0;
146         ubikSecClass = rxnull_NewClientSecurityObject();
147     }
148     i = 0;
149     magicHost = ntohl(ame);     /* do comparisons in host order */
150     magicServer = (struct ubik_server *) 0;
151     while (servAddr = *aservers++) {
152         if (i >= MAXSERVERS) return UNHOSTS;        /* too many hosts */
153         ts = (struct ubik_server *) malloc(sizeof(struct ubik_server));
154         bzero(ts, sizeof(struct ubik_server));
155         ts->next = ubik_servers;
156         ubik_servers = ts;
157         ts->addr[0] = servAddr; /* primary address in  net byte order */
158         ts->vote_rxcid = rx_NewConnection(servAddr, ubik_callPortal, VOTE_SERVICE_ID, ubikSecClass, ubikSecIndex);      /* for vote reqs */
159         ts->disk_rxcid = rx_NewConnection(servAddr, ubik_callPortal, DISK_SERVICE_ID, ubikSecClass, ubikSecIndex);      /* for disk reqs */
160         ts->up = 1;
161         if (ntohl((afs_uint32) servAddr) < (afs_uint32) magicHost) {
162             magicHost = ntohl(servAddr);
163             magicServer = ts;
164         }
165         i++;
166     }
167     if (magicServer) magicServer->magic = 1;    /* remember for when counting votes */
168     else amIMagic = 1;
169     nServers = i+1;     /* count this server as well as the remotes */
170     ubik_quorum = (nServers>>1)+1;      /* compute the majority figure */
171                                         /* send addrs to all other servers */
172     code = updateUbikNetworkAddress(ubik_host);
173     if ( code )
174         return code;            
175
176 /* Shoud we set some defaults for RX??
177     r_retryInterval = 2;        
178     r_nRetries = (RPCTIMEOUT/r_retryInterval);
179 */
180     if (nServers == 1) {    /* special case 1 server */
181         if (!ubik_amSyncSite) ubik_dprint("Ubik: I am the sync site - 1 server\n");
182         ubik_amSyncSite = 1;
183         syncSiteUntil = 0x7fffffff; /* quite a while */
184     }
185     return 0;
186 }
187
188 /* main lwp loop for code that sends out beacons.  This code only runs while
189  * we're sync site or we want to be the sync site.  It runs in its very own light-weight
190  * process.
191  */
192 ubeacon_Interact() { 
193     register afs_int32 code;
194     struct timeval tt;
195     struct rx_connection *connections[MAXSERVERS];
196     struct ubik_server *servers[MAXSERVERS];
197     register afs_int32 i;
198     register struct ubik_server *ts;
199     afs_int32 temp, yesVotes, lastWakeupTime, oldestYesVote, syncsite;
200     struct ubik_tid ttid;
201     afs_int32 startTime;
202
203     /* loop forever getting votes */
204     lastWakeupTime = 0;     /* keep track of time we last started a vote collection */
205     while (1) {
206
207         /* don't wakeup more than every POLLTIME seconds */
208         temp = (lastWakeupTime + POLLTIME) - FT_ApproxTime();
209         /* don't sleep if last collection phase took too long (probably timed someone out ) */
210         if (temp > 0) {
211             if (temp > POLLTIME) temp = POLLTIME;
212             tt.tv_sec = temp;
213             tt.tv_usec = 0;
214             code = IOMGR_Select(0, 0, 0, 0, &tt);
215         }
216         else code = 0;
217
218         lastWakeupTime = FT_ApproxTime();   /* started a new collection phase */
219
220         if (nServers == 1) continue;    /* special-case 1 server for speedy startup */
221
222         if (!uvote_ShouldIRun()) continue;  /* if voter has heard from a better candidate than us, don't bother running */
223
224         /* otherwise we should run for election, or we're the sync site (and have already won);
225             send out the beacon packets */
226         /* build list of all up hosts (noticing dead hosts are running again
227             is a task for the recovery module, not the beacon module), and
228             prepare to send them an r multi-call containing the beacon message */
229         i = 0;      /* collect connections */
230         for(ts = ubik_servers; ts; ts=ts->next) {
231             if (ts->up && ts->addr[0] != ubik_host[0]) {
232                 servers[i] = ts;
233                 connections[i++] = ts->vote_rxcid;
234             }
235         }
236         servers[i] = (struct ubik_server *) 0;  /* end of list */
237         /* note that we assume in the vote module that we'll always get at least BIGTIME 
238             seconds of vote from anyone who votes for us, which means we can conservatively
239             assume we'll be fine until SMALLTIME seconds after we start collecting votes */
240         /* this next is essentially an expansion of rgen's ServBeacon routine */
241
242         ttid.epoch = ubik_epochTime;
243         if (ubik_dbase->flags & DBWRITING) {
244             /*
245              * if a write is in progress, we have to send the writeTidCounter
246              * which holds the tid counter of the write transaction , and not
247              * send the tidCounter value which holds the tid counter of the
248              * last transaction.
249              */
250             ttid.counter = ubik_dbase->writeTidCounter;
251           }
252         else
253             ttid.counter = ubik_dbase->tidCounter+1;
254
255         /* now analyze return codes, counting up our votes */
256         yesVotes = 0;               /* count how many to ensure we have quorum */
257         oldestYesVote = 0x3fffffff; /* time quorum expires */
258         syncsite= ubeacon_AmSyncSite();
259         startTime = FT_ApproxTime();
260         /*
261          * Don't waste time using mult Rx calls if there are no connections out there
262          */
263         if (i > 0) {
264             multi_Rx(connections, i) {
265                 multi_VOTE_Beacon(syncsite, startTime, &ubik_dbase->version, &ttid);
266                 temp = FT_ApproxTime();     /* now, more or less */
267                 ts = servers[multi_i];
268                 ts->lastBeaconSent = temp;
269                 code = multi_error;
270                 /* note that the vote time (the return code) represents the time
271                    the vote was computed, *not* the time the vote expires.  We compute
272                    the latter down below if we got enough votes to go with */
273                 if (code > 0) {
274                     ts->lastVoteTime = code;
275                     if (code < oldestYesVote) oldestYesVote = code;
276                     ts->lastVote = 1;
277                     yesVotes += 2;
278                     if (ts->magic) yesVotes++;  /* the extra epsilon */
279                     ts->up = 1; /* server is up (not really necessary: recovery does this for real) */
280                     ts->beaconSinceDown = 1;
281                     ubik_dprint("yes vote from host %s\n",afs_inet_ntoa(ts->addr[0]));
282                 }
283                 else if (code == 0) {
284                     ts->lastVoteTime = temp;
285                     ts->lastVote = 0;
286                     ts->beaconSinceDown = 1;
287                     ubik_dprint("no vote from %s\n", afs_inet_ntoa(ts->addr[0]));
288                 }
289                 else if (code < 0) {
290                     ts->up = 0;
291                     ts->beaconSinceDown = 0;
292                     urecovery_LostServer();
293                     ubik_dprint("time out from %s\n", afs_inet_ntoa(ts->addr[0]));
294                 }
295             } multi_End;
296         }
297         /* now call our own voter module to see if we'll vote for ourself.  Note that
298             the same restrictions apply for our voting for ourself as for our voting
299             for anyone else. */
300         i = SVOTE_Beacon((struct rx_connection *) 0, ubeacon_AmSyncSite(), startTime, &ubik_dbase->version, &ttid);
301         if (i) {
302             yesVotes += 2;
303             if (amIMagic) yesVotes++;   /* extra epsilon */
304             if (i < oldestYesVote) oldestYesVote = i;
305         }
306
307         /* now decide if we have enough votes to become sync site.
308             Note that we can still get enough votes even if we didn't for ourself. */
309         if (yesVotes > nServers) {  /* yesVotes is bumped by 2 or 3 for each site */
310             if (!ubik_amSyncSite) ubik_dprint("Ubik: I am the sync site\n");
311             ubik_amSyncSite = 1;
312             syncSiteUntil = oldestYesVote + SMALLTIME;
313             LWP_NoYieldSignal(&ubik_amSyncSite);
314         }
315         else {
316             if (ubik_amSyncSite) ubik_dprint("Ubik: I am no longer the sync site\n");
317             ubik_amSyncSite = 0;
318             urecovery_ResetState(); /* tell recovery we're no longer the sync site */
319         }
320
321     }   /* while loop */
322 }
323
324 /* 
325 * Input Param   : ame is the pointer to my IP address specified in the
326 *                 CellServDB file. aservers is an array containing IP 
327 *                 addresses of remote ubik servers. The array is 
328 *                 terminated by a zero address.
329 *
330 * Algorithm     : Verify that my IP addresses 'ame' does actually exist
331 *                 on this machine.  If any of my IP addresses are there 
332 *                 in the remote server list 'aserver', remove them from 
333 *                 this list.  Update global variable ubik_host[] with 
334 *                 my IP addresses.
335 *
336 * Return Values : 0 on success, non-zero on failure
337 */
338 verifyInterfaceAddress(ame, aservers)
339 afs_uint32 *ame;                /* one of my interface addr in net byte order */
340 afs_uint32 aservers[];  /* list of all possible server addresses */
341 {
342     afs_uint32  myAddr[UBIK_MAX_INTERFACE_ADDR], *servList;
343     int         count, index, found, i, j, totalServers, start, end;
344
345     /* count the number of servers */
346     for ( totalServers=0, servList = aservers; *servList; servList++)
347         totalServers++;
348
349 #ifdef AFS_NT40_ENV 
350     /* for now use getaddr(). use getAllAddr when implemented */
351     myAddr[0] =  rxi_getaddr();
352     count = (myAddr[0] != 0);
353 #else
354     if(AFSDIR_SERVER_NETRESTRICT_FILEPATH || AFSDIR_SERVER_NETINFO_FILEPATH) {
355       /*
356        * Find addresses we are supposed to register as per the netrestrict file
357        * if it exists, else just register all the addresses we find on this 
358        * host as returned by rx_getAllAddr (in NBO)
359        */
360       char reason[1024];
361       count=parseNetFiles(myAddr,NULL,NULL,UBIK_MAX_INTERFACE_ADDR,
362                           reason,AFSDIR_SERVER_NETINFO_FILEPATH,
363                           AFSDIR_SERVER_NETRESTRICT_FILEPATH);
364       if(count<0) {
365         ubik_print("ubik: Can't register any valid addresses:%s\n",reason);
366         ubik_print("Aborting..\n");
367         return UBADHOST;
368       }
369     }
370     else {
371       /* get all my interface addresses in net byte order */
372       count = rx_getAllAddr(myAddr, UBIK_MAX_INTERFACE_ADDR); 
373     }
374 #endif
375
376     if ( count <= 0 )           /* no address found */
377     {
378         ubik_print("ubik: No network addresses found, aborting..");
379         return UBADHOST;
380     }
381
382     /* verify that the My-address passed in by ubik is correct */
383     for ( j=0, found = 0; j < count; j++)
384     {
385         if ( *ame == myAddr[j] ) /* both in net byte order */
386         {
387             found = 1;
388             break;
389         }
390     }
391          
392     if ( !found )
393     {
394         ubik_print("ubik: primary address %s does not exist\n",
395                         afs_inet_ntoa(*ame));
396         return UBADHOST;
397     }
398
399     /* if any of my addresses are there in serverList, then
400     ** use that as my primary addresses : the higher level 
401     ** application screwed up in dealing with multihomed concepts
402     */
403     for ( j=0, found = 0; j < count; j++)
404     {
405         for ( i=0; i < totalServers; i++)
406             if ( myAddr[j] == aservers[i] )     
407             {
408                 *ame = aservers[i];
409                 aservers[i]  = 0 ; 
410                 found = 1;
411             }
412     }
413     if ( found )
414         ubik_print("Using %s as my primary address\n", afs_inet_ntoa(*ame) );
415
416     /* get rid of servers which were purged because all 
417     ** those interface addresses are myself 
418     */
419     for ( start=0, end=totalServers-1; (start<end) ; start++, end--)
420     {
421         /* find the first zero entry from the beginning */
422         for ( ; (start < end) && ( aservers[start] ); start++);
423
424         /* find the last non-zero entry from the end */
425         for ( ; (end >= 0) && ( !aservers[end] ); end-- );
426
427         /* if there is nothing more to purge, exit from loop */
428         if ( start >= end ) break;
429
430         /* move the entry */
431         aservers[start] = aservers[end];
432         aservers[end]   = 0;            /* this entry was moved */
433     }
434         
435     /* update all my addresses in ubik_host in such a way 
436     ** that ubik_host[0] has the primary address 
437     */
438     ubik_host[0] = *ame;        
439     for ( j=0, i=1; j < count; j++)
440         if ( *ame != myAddr[j] )
441             ubik_host[i++] =  myAddr[j];
442
443     return 0;   /* return success */
444 }       
445
446
447 /* 
448 * Input Param   : ubik_host is an array containing all my IP addresses.
449 *
450 * Algorithm     : Do an RPC to all remote ubik servers infroming them 
451 *                 about my IP addresses. Get their IP addresses and
452 *                 update my linked list of ubik servers 'ubik_servers'
453 *
454 * Return Values : 0 on success, non-zero on failure
455 */
456 int
457 updateUbikNetworkAddress(ubik_host)
458 afs_uint32 ubik_host[UBIK_MAX_INTERFACE_ADDR];
459 {
460     int                 j, count, found, index, code = 0;
461     UbikInterfaceAddr   inAddr, outAddr;
462     struct rx_connection *conns[MAXSERVERS];
463     struct ubik_server  *ts, *server[MAXSERVERS];
464     char                buffer[32];
465
466     for ( count = 0, ts=ubik_servers; ts; count++, ts = ts->next )
467     {
468         conns[count]  = ts->disk_rxcid;
469         server[count] = ts;
470     }
471
472
473     /* inform all other servers only if there are more than one
474        database servers in the cell */
475
476     if ( count > 0) {
477         
478         for ( j=0; j < UBIK_MAX_INTERFACE_ADDR; j++)
479             inAddr.hostAddr[j] = ntohl(ubik_host[j]);
480
481     
482         /* do the multi-RX RPC to all other servers */
483         multi_Rx(conns, count) {
484             multi_DISK_UpdateInterfaceAddr(&inAddr, &outAddr);
485             ts = server[multi_i]; /* reply received from this server */
486             if ( !multi_error ) {
487                 if ( ts->addr[0] != htonl(outAddr.hostAddr[0]) ) {
488                     code = UBADHOST;
489                     strcpy(buffer, (char*)afs_inet_ntoa(ts->addr[0]));
490                     ubik_print("ubik:Two primary addresses for same server \
491                     %s %s\n", buffer, afs_inet_ntoa(htonl(outAddr.hostAddr[0])));
492                 }
493                 else {
494                     for ( j=1; j < UBIK_MAX_INTERFACE_ADDR; j++)
495                         ts->addr[j] = htonl(outAddr.hostAddr[j]);
496                 }
497             }
498             else if ( multi_error == RXGEN_OPCODE ) {/* pre 3.5 remote server */
499                 ubik_print("ubik server %s does not support UpdateInterfaceAddr RPC\n", afs_inet_ntoa(ts->addr[0]));
500             }
501             else if ( multi_error == UBADHOST ) {
502                 code = UBADHOST; /* remote CellServDB inconsistency */
503                 ubik_print("Inconsistent Cell Info on server: ");
504                 for ( j=0; j < UBIK_MAX_INTERFACE_ADDR && ts->addr[j]; j++)
505                     printf("%s ", afs_inet_ntoa(ts->addr[j]));
506                 printf("\n");
507                 fflush(stdout); fflush(stderr);     
508             }
509             else {
510                 ts->up= 0;      /* mark the remote server as down */
511             }
512         } multi_End;
513     }
514     return code;
515 }
516