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