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