ubik: Introduce ubik_CallRock
[openafs.git] / src / ubik / ubikclient.c
index 78131e5..186c620 100644 (file)
@@ -1,52 +1,33 @@
 /*
  * Copyright 2000, International Business Machines Corporation and others.
  * All Rights Reserved.
- * 
+ *
  * This software has been released under the terms of the IBM Public
  * License.  For details, see the LICENSE file in the top-level source
  * directory or online at http://www.openafs.org/dl/license10.html
  */
 
 #include <afsconfig.h>
-#if defined(UKERNEL)
-#include "afs/param.h"
-#else
 #include <afs/param.h>
+#include <afs/stds.h>
+
+#include <roken.h>
+#include <afs/opr.h>
+#ifdef AFS_PTHREAD_ENV
+# include <opr/lock.h>
+#endif
+
+#ifdef IGNORE_SOME_GCC_WARNINGS
+# pragma GCC diagnostic warning "-Wstrict-prototypes"
 #endif
 
-RCSID
-    ("$Header$");
-
-#if defined(UKERNEL)
-#include "afs/sysincludes.h"
-#include "afsincludes.h"
-#include "afs/stds.h"
-#include "rx/xdr.h"
-#include "rx/rx.h"
-#include "afs/lock.h"
-#include "afs/rxgen_consts.h"
-#define UBIK_LEGACY_CALLITER 1
-#include "ubik.h"
-#include "afs/pthread_glock.h"
-#else /* defined(UKERNEL) */
-#include <afs/stds.h>
 #include <afs/pthread_glock.h>
-#include <stdio.h>
-#include <string.h>
 #include <rx/xdr.h>
 #include <rx/rx.h>
 #include <lock.h>
-#ifdef AFS_NT40_ENV
-#include <winsock2.h>
-#else
-#include <unistd.h>
-#include <netdb.h>
-#include <netinet/in.h>
-#endif
 #include <afs/rxgen_consts.h>
+#define UBIK_LEGACY_CALLITER
 #include "ubik.h"
-#endif /* defined(UKERNEL) */
-
 
 short ubik_initializationState;        /*!< initial state is zero */
 
@@ -55,12 +36,13 @@ short ubik_initializationState;     /*!< initial state is zero */
  * \brief Parse list for clients.
  */
 int
-ubik_ParseClientList(int argc, char **argv, afs_int32 * aothers)
+ubik_ParseClientList(int argc, char **argv, afs_uint32 * aothers)
 {
-    register afs_int32 i;
-    register char *tp;
-    register struct hostent *th;
-    afs_int32 temp, counter;
+    afs_int32 i;
+    char *tp;
+    struct hostent *th;
+    afs_uint32 temp;
+    afs_int32 counter;
     int inServer;
 
     inServer = 0;              /* haven't seen -servers yet */
@@ -103,7 +85,6 @@ ubik_ParseClientList(int argc, char **argv, afs_int32 * aothers)
 
 #ifdef AFS_PTHREAD_ENV
 #include <pthread.h>
-#include <assert.h>
 
 static pthread_once_t random_once = PTHREAD_ONCE_INIT;
 static int called_afs_random_once;
@@ -112,19 +93,18 @@ static pthread_key_t random_number_key;
 static void
 afs_random_once(void)
 {
-    assert(pthread_key_create(&random_number_key, NULL) == 0);
+    opr_Verify(pthread_key_create(&random_number_key, NULL) == 0);
     called_afs_random_once = 1;
 }
 
 #endif
 
-#if !defined(UKERNEL)
-/*! 
+/*!
  * \brief use time and pid to try to get some initial randomness.
  */
 #define        ranstage(x)     (x)= (afs_uint32) (3141592621U*((afs_uint32)x)+1)
 
-/*! 
+/*!
  * \brief Random number generator and constants from KnuthV2 2d ed, p170
  *
  * Rules: \n
@@ -135,15 +115,15 @@ afs_random_once(void)
  * c is more or less immaterial.  1 or a is suggested. \n
  *
  * NB:  LOW ORDER BITS are not very random.  To get small random numbers,
- *      treat result as <1, with implied binary point, and multiply by 
+ *      treat result as <1, with implied binary point, and multiply by
  *      desired modulus.
  *
  * NB:  Has to be unsigned, since shifts on signed quantities may preserve
  *      the sign bit.
- * 
+ *
  * In this case, m == 2^32, the mod operation is implicit. a == pi, which
  * is used because it has some interesting characteristics (lacks any
- * interesting bit-patterns).   
+ * interesting bit-patterns).
  */
 unsigned int
 afs_random(void)
@@ -151,8 +131,10 @@ afs_random(void)
 #ifdef AFS_PTHREAD_ENV
     afs_uint32 state;
 
-    (called_afs_random_once || pthread_once(&random_once, afs_random_once));
-    state = (afs_uint32) pthread_getspecific(random_number_key);
+    if (!called_afs_random_once)
+       pthread_once(&random_once, afs_random_once);
+
+    state = (uintptr_t) pthread_getspecific(random_number_key);
 #else
     static afs_uint32 state = 0;
 #endif
@@ -167,7 +149,7 @@ afs_random(void)
 
     ranstage(state);
 #ifdef AFS_PTHREAD_ENV
-    pthread_setspecific(random_number_key, (const void *)state);
+    pthread_setspecific(random_number_key, (const void *)(uintptr_t)state);
 #endif
     return (state);
 
@@ -176,10 +158,10 @@ afs_random(void)
 /*!
  * \brief Returns int 0..14 using the high bits of a pseudo-random number instead of
  * the low bits, as the low bits are "less random" than the high ones...
- * 
+ *
  * \todo Slight roundoff error exists, an excercise for the reader.
  *
- * Need to multiply by something with lots of ones in it, so multiply by 
+ * Need to multiply by something with lots of ones in it, so multiply by
  * 8 or 16 is right out.
  */
 static unsigned int
@@ -192,20 +174,19 @@ afs_randomMod15(void)
 
     return temp;
 }
-#endif /* !defined(UKERNEL) */
 
 #ifdef abs
 #undef abs
 #endif /* abs */
 #define abs(a) ((a) < 0 ? -1*(a) : (a))
 int
-ubik_ClientInit(register struct rx_connection **serverconns,
+ubik_ClientInit(struct rx_connection **serverconns,
                struct ubik_client **aclient)
 {
     int i, j;
     int count;
     int offset;
-    register struct ubik_client *tc;
+    struct ubik_client *tc;
 
     initialize_U_error_table();
 
@@ -234,13 +215,14 @@ ubik_ClientInit(register struct rx_connection **serverconns,
            return UMUTEXDESTROY;
 #endif
     } else {
-       tc = (struct ubik_client *)malloc(sizeof(struct ubik_client));
+       tc = malloc(sizeof(struct ubik_client));
     }
     if (tc == NULL)
        return UNOMEM;
     memset((void *)tc, 0, sizeof(*tc));
 #ifdef AFS_PTHREAD_ENV
     if (pthread_mutex_init(&(tc->cm), (const pthread_mutexattr_t *)0)) {
+       free(tc);
        return UMUTEXINIT;
     }
 #endif
@@ -271,7 +253,7 @@ ubik_ClientInit(register struct rx_connection **serverconns,
     return 0;
 }
 
-/*! 
+/*!
  * \brief Destroy an ubik connection.
  *
  * It calls rx to destroy the component rx connections, then frees the ubik
@@ -280,7 +262,7 @@ ubik_ClientInit(register struct rx_connection **serverconns,
 afs_int32
 ubik_ClientDestroy(struct ubik_client * aclient)
 {
-    register int c;
+    int c;
 
     if (aclient == 0)
        return 0;
@@ -338,15 +320,16 @@ ubik_RefreshConn(struct rx_connection *tc)
 
 pthread_once_t ubik_client_once = PTHREAD_ONCE_INIT;
 pthread_mutex_t ubik_client_mutex;
-#define LOCK_UCLNT_CACHE \
-    assert(pthread_once(&ubik_client_once, ubik_client_init_mutex) == 0 && \
-          pthread_mutex_lock(&ubik_client_mutex)==0)
-#define UNLOCK_UCLNT_CACHE assert(pthread_mutex_unlock(&ubik_client_mutex)==0)
+#define LOCK_UCLNT_CACHE do { \
+    opr_Verify(pthread_once(&ubik_client_once, ubik_client_init_mutex) == 0); \
+    MUTEX_ENTER(&ubik_client_mutex); \
+  } while (0)
+#define UNLOCK_UCLNT_CACHE MUTEX_EXIT(&ubik_client_mutex)
 
 void
 ubik_client_init_mutex(void)
 {
-    assert(pthread_mutex_init(&ubik_client_mutex, NULL) == 0);
+    MUTEX_INIT(&ubik_client_mutex, "client init", MUTEX_DEFAULT, 0);
 }
 
 #else
@@ -360,145 +343,6 @@ ubik_client_init_mutex(void)
 static int *calls_needsync[SYNCCOUNT]; /* proc calls that need the sync site */
 static int synccount = 0;
 
-/*!
- * call this instead of stub and we'll guarantee to find a host that's up.
- * 
- * \todo In the future, we should also put in a protocol to find the sync site.
- */
-afs_int32
-ubik_Call(int (*aproc) (), register struct ubik_client *aclient, 
-         afs_int32 aflags, long p1, long p2, long p3, long p4, 
-         long p5, long p6, long p7, long p8, long p9, long p10,
-         long p11, long p12, long p13, long p14, long p15, long p16)
-{
-    afs_int32 rcode, code, newHost, thisHost, i, count;
-    int chaseCount, pass, needsync, inlist, j;
-    struct rx_connection *tc;
-    struct rx_peer *rxp;
-    short origLevel;
-
-    if (!aclient)
-       return UNOENT;
-    LOCK_UBIK_CLIENT(aclient);
-
-  restart:
-    origLevel = aclient->initializationState;
-    rcode = UNOSERVERS;
-    chaseCount = inlist = needsync = 0;
-
-    LOCK_UCLNT_CACHE;
-    for (j = 0; ((j < SYNCCOUNT) && calls_needsync[j]); j++) {
-       if (calls_needsync[j] == (int *)aproc) {
-           inlist = needsync = 1;
-           break;
-       }
-    }
-    UNLOCK_UCLNT_CACHE;
-    /* 
-     * First  pass, we try all servers that are up.
-     * Second pass, we try all servers.
-     */
-    for (pass = 0; pass < 2; pass++) { /*p */
-       /* For each entry in our servers list */
-       for (count = 0;; count++) {     /*s */
-
-           if (needsync) {
-               /* Need a sync site. Lets try to quickly find it */
-               if (aclient->syncSite) {
-                   newHost = aclient->syncSite;        /* already in network order */
-                   aclient->syncSite = 0;      /* Will reset if it works */
-               } else if (aclient->conns[3]) {
-                   /* If there are fewer than four db servers in a cell,
-                    * there's no point in making the GetSyncSite call.
-                    * At best, it's a wash. At worst, it results in more
-                    * RPCs than you would otherwise make.
-                    */
-                   tc = aclient->conns[count];
-                   if (tc && rx_ConnError(tc)) {
-                       aclient->conns[count] = tc = ubik_RefreshConn(tc);
-                   }
-                   if (!tc)
-                       break;
-                   code = VOTE_GetSyncSite(tc, &newHost);
-                   if (aclient->initializationState != origLevel)
-                       goto restart;   /* somebody did a ubik_ClientInit */
-                   if (code)
-                       newHost = 0;
-                   newHost = htonl(newHost);   /* convert to network order */
-               } else {
-                   newHost = 0;
-               }
-               if (newHost) {
-                   /* position count at the appropriate slot in the client
-                    * structure and retry. If we can't find in slot, we'll
-                    * just continue through the whole list 
-                    */
-                   for (i = 0; i < MAXSERVERS && aclient->conns[i]; i++) {
-                       rxp = rx_PeerOf(aclient->conns[i]);
-                       thisHost = rx_HostOf(rxp);
-                       if (!thisHost)
-                           break;
-                       if (thisHost == newHost) {
-                           if (chaseCount++ > 2)
-                               break;  /* avoid loop asking */
-                           count = i;  /* this index is the sync site */
-                           break;
-                       }
-                   }
-               }
-           }
-           /*needsync */
-           tc = aclient->conns[count];
-           if (tc && rx_ConnError(tc)) {
-               aclient->conns[count] = tc = ubik_RefreshConn(tc);
-           }
-           if (!tc)
-               break;
-
-           if ((pass == 0) && (aclient->states[count] & CFLastFailed)) {
-               continue;       /* this guy's down */
-           }
-
-           rcode =
-               (*aproc) (tc, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11,
-                         p12, p13, p14, p15, p16);
-           if (aclient->initializationState != origLevel) {
-               /* somebody did a ubik_ClientInit */
-               if (rcode)
-                   goto restart;       /* call failed */
-               else
-                   goto done;  /* call suceeded */
-           }
-           if (rcode < 0) {    /* network errors */
-               aclient->states[count] |= CFLastFailed; /* Mark serer down */
-           } else if (rcode == UNOTSYNC) {
-               needsync = 1;
-           } else if (rcode != UNOQUORUM) {
-               /* either misc ubik code, or misc appl code, or success. */
-               aclient->states[count] &= ~CFLastFailed;        /* mark server up */
-               goto done;      /* all done */
-           }
-       }                       /*s */
-    }                          /*p */
-
-  done:
-    if (needsync) {
-       if (!inlist) {          /* Remember proc call that needs sync site */
-           LOCK_UCLNT_CACHE;
-           calls_needsync[synccount % SYNCCOUNT] = (int *)aproc;
-           synccount++;
-           UNLOCK_UCLNT_CACHE;
-           inlist = 1;
-       }
-       if (!rcode) {           /* Remember the sync site - cmd successful */
-           rxp = rx_PeerOf(aclient->conns[count]);
-           aclient->syncSite = rx_HostOf(rxp);
-       }
-    }
-    UNLOCK_UBIK_CLIENT(aclient);
-    return rcode;
-}
-
 
 
 /*!
@@ -511,7 +355,7 @@ ubik_Call(int (*aproc) (), register struct ubik_client *aclient,
  * operation won't work until you find a sync site
  */
 static int
-try_GetSyncSite(register struct ubik_client *aclient, afs_int32 apos)
+try_GetSyncSite(struct ubik_client *aclient, afs_int32 apos)
 {
     struct rx_peer *rxp;
     afs_int32 code;
@@ -561,18 +405,18 @@ try_GetSyncSite(register struct ubik_client *aclient, afs_int32 apos)
 #define NEED_LOCK 1
 #define NO_LOCK 0
 
-/*! 
+/*!
  * \brief Create an internal version of ubik_CallIter that takes an additional
  * parameter - to indicate whether the ubik client handle has already
  * been locked.
  */
 static afs_int32
-CallIter(int (*aproc) (), register struct ubik_client *aclient, 
-        afs_int32 aflags, int *apos, long p1, long p2, long p3, long p4, 
-        long p5, long p6, long p7, long p8, long p9, long p10, long p11, 
+CallIter(int (*aproc) (), struct ubik_client *aclient,
+        afs_int32 aflags, int *apos, long p1, long p2, long p3, long p4,
+        long p5, long p6, long p7, long p8, long p9, long p10, long p11,
         long p12, long p13, long p14, long p15, long p16, int needlock)
 {
-    register afs_int32 code;
+    afs_int32 code;
     struct rx_connection *tc;
     short origLevel;
 
@@ -586,12 +430,8 @@ CallIter(int (*aproc) (), register struct ubik_client *aclient,
     while (*apos < MAXSERVERS) {
        /* tc is the next conn to try */
        tc = aclient->conns[*apos];
-       if (!tc) {
-           if (needlock) {
-               UNLOCK_UBIK_CLIENT(aclient);
-           }
-           return UNOSERVERS;
-       }
+       if (!tc)
+           goto errout;
 
        if (rx_ConnError(tc)) {
            tc = ubik_RefreshConn(tc);
@@ -604,22 +444,15 @@ CallIter(int (*aproc) (), register struct ubik_client *aclient,
            break;              /* this is the desired path */
        }
     }
-    if (*apos >= MAXSERVERS) {
-       if (needlock) {
-           UNLOCK_UBIK_CLIENT(aclient);
-       }
-       return UNOSERVERS;
-    }
+    if (*apos >= MAXSERVERS)
+       goto errout;
 
     code =
        (*aproc) (tc, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13,
                  p14, p15, p16);
-    if (aclient->initializationState != origLevel) {
-       if (needlock) {
-           UNLOCK_UBIK_CLIENT(aclient);
-       }
-       return code;            /* somebody did a ubik_ClientInit */
-    }
+    if (aclient->initializationState != origLevel)
+       /* somebody did a ubik_ClientInit */
+       goto errout;
 
     /* what should I do in case of UNOQUORUM ? */
     if (code < 0) {
@@ -630,21 +463,36 @@ CallIter(int (*aproc) (), register struct ubik_client *aclient,
     }
 
     (*apos)++;
+errout:
     if (needlock) {
        UNLOCK_UBIK_CLIENT(aclient);
     }
     return code;
 }
 
-/*! 
+/*!
+ * \brief This is part of an iterator.  It doesn't handle finding sync sites.
+ */
+afs_int32
+ubik_CallIter(int (*aproc) (), struct ubik_client *aclient,
+                              afs_int32 aflags, int *apos, long p1, long p2,
+                              long p3, long p4, long p5, long p6, long p7,
+                              long p8, long p9, long p10, long p11, long p12,
+                              long p13, long p14, long p15, long p16)
+{
+    return CallIter(aproc, aclient, aflags, apos, p1, p2, p3, p4, p5, p6, p7,
+                   p8, p9, p10, p11, p12, p13, p14, p15, p16, NEED_LOCK);
+}
+
+/*!
  * \brief Call this instead of stub and we'll guarantee to find a host that's up.
  *
  * \todo In the future, we should also put in a protocol to find the sync site.
  */
 afs_int32
-ubik_Call_New(int (*aproc) (), register struct ubik_client *aclient, 
-             afs_int32 aflags, long p1, long p2, long p3, long p4, long p5, 
-             long p6, long p7, long p8, long p9, long p10, long p11, 
+ubik_Call_New(int (*aproc) (), struct ubik_client *aclient,
+             afs_int32 aflags, long p1, long p2, long p3, long p4, long p5,
+             long p6, long p7, long p8, long p9, long p10, long p11,
              long p12, long p13, long p14, long p15, long p16)
 {
     afs_int32 code, rcode;
@@ -698,15 +546,265 @@ ubik_Call_New(int (*aproc) (), register struct ubik_client *aclient,
 }
 
 /*!
- * \brief This is part of an iterator.  It doesn't handle finding sync sites.
+ * call this instead of stub and we'll guarantee to find a host that's up.
+ *
+ * \todo In the future, we should also put in a protocol to find the sync site.
  */
 afs_int32
-ubik_CallIter(int (*aproc) (), struct ubik_client *aclient,
-                              afs_int32 aflags, int *apos, long p1, long p2,
-                              long p3, long p4, long p5, long p6, long p7,
-                              long p8, long p9, long p10, long p11, long p12,
-                              long p13, long p14, long p15, long p16)
+ubik_Call(int (*aproc) (), struct ubik_client *aclient,
+         afs_int32 aflags, long p1, long p2, long p3, long p4,
+         long p5, long p6, long p7, long p8, long p9, long p10,
+         long p11, long p12, long p13, long p14, long p15, long p16)
 {
-    return CallIter(aproc, aclient, aflags, apos, p1, p2, p3, p4, p5, p6, p7,
-                   p8, p9, p10, p11, p12, p13, p14, p15, p16, NEED_LOCK);
+    afs_int32 rcode, code, newHost, thisHost, i, count;
+    int chaseCount, pass, needsync, inlist, j;
+    struct rx_connection *tc;
+    struct rx_peer *rxp;
+    short origLevel;
+
+    if (aflags & UBIK_CALL_NEW)
+       return ubik_Call_New(aproc, aclient, aflags, p1, p2, p3, p4,
+                            p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15,
+                            p16);
+
+    if (!aclient)
+       return UNOENT;
+    LOCK_UBIK_CLIENT(aclient);
+
+  restart:
+    origLevel = aclient->initializationState;
+    rcode = UNOSERVERS;
+    chaseCount = inlist = needsync = 0;
+
+    LOCK_UCLNT_CACHE;
+    for (j = 0; ((j < SYNCCOUNT) && calls_needsync[j]); j++) {
+       if (calls_needsync[j] == (int *)aproc) {
+           inlist = needsync = 1;
+           break;
+       }
+    }
+    UNLOCK_UCLNT_CACHE;
+    /*
+     * First  pass, we try all servers that are up.
+     * Second pass, we try all servers.
+     */
+    for (pass = 0; pass < 2; pass++) { /*p */
+       /* For each entry in our servers list */
+       for (count = 0;; count++) {     /*s */
+
+           if (needsync) {
+               /* Need a sync site. Lets try to quickly find it */
+               if (aclient->syncSite) {
+                   newHost = aclient->syncSite;        /* already in network order */
+                   aclient->syncSite = 0;      /* Will reset if it works */
+               } else if (aclient->conns[3]) {
+                   /* If there are fewer than four db servers in a cell,
+                    * there's no point in making the GetSyncSite call.
+                    * At best, it's a wash. At worst, it results in more
+                    * RPCs than you would otherwise make.
+                    */
+                   tc = aclient->conns[count];
+                   if (tc && rx_ConnError(tc)) {
+                       aclient->conns[count] = tc = ubik_RefreshConn(tc);
+                   }
+                   if (!tc)
+                       break;
+                   code = VOTE_GetSyncSite(tc, &newHost);
+                   if (aclient->initializationState != origLevel)
+                       goto restart;   /* somebody did a ubik_ClientInit */
+                   if (code)
+                       newHost = 0;
+                   newHost = htonl(newHost);   /* convert to network order */
+               } else {
+                   newHost = 0;
+               }
+               if (newHost) {
+                   /* position count at the appropriate slot in the client
+                    * structure and retry. If we can't find in slot, we'll
+                    * just continue through the whole list
+                    */
+                   for (i = 0; i < MAXSERVERS && aclient->conns[i]; i++) {
+                       rxp = rx_PeerOf(aclient->conns[i]);
+                       thisHost = rx_HostOf(rxp);
+                       if (!thisHost)
+                           break;
+                       if (thisHost == newHost) {
+                           if (chaseCount++ > 2)
+                               break;  /* avoid loop asking */
+                           count = i;  /* this index is the sync site */
+                           break;
+                       }
+                   }
+               }
+           }
+           /*needsync */
+           tc = aclient->conns[count];
+           if (tc && rx_ConnError(tc)) {
+               aclient->conns[count] = tc = ubik_RefreshConn(tc);
+           }
+           if (!tc)
+               break;
+
+           if ((pass == 0) && (aclient->states[count] & CFLastFailed)) {
+               continue;       /* this guy's down */
+           }
+
+           rcode =
+               (*aproc) (tc, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11,
+                         p12, p13, p14, p15, p16);
+           if (aclient->initializationState != origLevel) {
+               /* somebody did a ubik_ClientInit */
+               if (rcode)
+                   goto restart;       /* call failed */
+               else
+                   goto done;  /* call suceeded */
+           }
+           if (rcode < 0) {    /* network errors */
+               aclient->states[count] |= CFLastFailed; /* Mark serer down */
+           } else if (rcode == UNOTSYNC) {
+               needsync = 1;
+           } else if (rcode != UNOQUORUM) {
+               /* either misc ubik code, or misc appl code, or success. */
+               aclient->states[count] &= ~CFLastFailed;        /* mark server up */
+               goto done;      /* all done */
+           }
+       }                       /*s */
+    }                          /*p */
+
+  done:
+    if (needsync) {
+       if (!inlist) {          /* Remember proc call that needs sync site */
+           LOCK_UCLNT_CACHE;
+           calls_needsync[synccount % SYNCCOUNT] = (int *)aproc;
+           synccount++;
+           UNLOCK_UCLNT_CACHE;
+           inlist = 1;
+       }
+       if (!rcode) {           /* Remember the sync site - cmd successful */
+           rxp = rx_PeerOf(aclient->conns[count]);
+           aclient->syncSite = rx_HostOf(rxp);
+       }
+    }
+    UNLOCK_UBIK_CLIENT(aclient);
+    return rcode;
+}
+
+afs_int32
+ubik_CallRock(struct ubik_client *aclient, afs_int32 aflags,
+             ubik_callrock_func proc, void *rock)
+{
+    afs_int32 rcode, code, newHost, thisHost, i, _ucount;
+    int chaseCount, pass, needsync;
+    struct rx_connection *tc;
+    struct rx_peer *rxp;
+    short origLevel;
+
+    if (!aclient)
+       return UNOENT;
+    LOCK_UBIK_CLIENT(aclient);
+
+ restart:
+    origLevel = aclient->initializationState;
+    rcode = UNOSERVERS;
+    chaseCount = needsync = 0;
+
+    /*
+     * First  pass, we try all servers that are up.
+     * Second pass, we try all servers.
+     */
+    for (pass = 0; pass < 2; pass++) {
+       /* For each entry in our servers list */
+       for (_ucount = 0;; _ucount++) {
+           struct ubik_callrock_info info;
+           if (needsync) {
+               /* Need a sync site. Lets try to quickly find it */
+               if (aclient->syncSite) {
+                   newHost = aclient->syncSite;        /* already in network order */
+                   aclient->syncSite = 0;      /* Will reset if it works */
+               } else if (aclient->conns[3]) {
+                   /*
+                    * If there are fewer than four db servers in a cell,
+                    * there's no point in making the GetSyncSite call.
+                    * At best, it's a wash. At worst, it results in more
+                    * RPCs than you would otherwise make.
+                    */
+                   tc = aclient->conns[_ucount];
+                   if (tc && rx_ConnError(tc)) {
+                       aclient->conns[_ucount] = tc = ubik_RefreshConn(tc);
+                   }
+                   if (!tc)
+                       break;
+                   code = VOTE_GetSyncSite(tc, &newHost);
+                   if (aclient->initializationState != origLevel)
+                       goto restart;   /* somebody did a ubik_ClientInit */
+                   if (code)
+                       newHost = 0;
+                   newHost = htonl(newHost);   /* convert to network order */
+               } else {
+                   newHost = 0;
+               }
+               if (newHost) {
+                   /*
+                    * position count at the appropriate slot in the client
+                    * structure and retry. If we can't find in slot, we'll
+                    * just continue through the whole list
+                    */
+                   for (i = 0; i < MAXSERVERS && aclient->conns[i]; i++) {
+                       rxp = rx_PeerOf(aclient->conns[i]);
+                       thisHost = rx_HostOf(rxp);
+                       if (!thisHost)
+                           break;
+                       if (thisHost == newHost) {
+                           if (chaseCount++ > 2)
+                               break;  /* avoid loop asking */
+                           _ucount = i;  /* this index is the sync site */
+                           break;
+                       }
+                   }
+               }
+           }
+           /*needsync */
+           tc = aclient->conns[_ucount];
+           if (tc && rx_ConnError(tc)) {
+               aclient->conns[_ucount] = tc = ubik_RefreshConn(tc);
+           }
+           if (!tc)
+               break;
+
+           if ((pass == 0) && (aclient->states[_ucount] & CFLastFailed)) {
+               continue;       /* this guy's down */
+           }
+
+           memset(&info, 0, sizeof(info));
+           info.conn = tc;
+           rcode = (*proc)(&info, rock);
+
+           if (aclient->initializationState != origLevel) {
+               /* somebody did a ubik_ClientInit */
+               if (rcode)
+                   goto restart;       /* call failed */
+               else
+                   goto done;  /* call suceeded */
+           }
+           if (rcode < 0) {    /* network errors */
+               aclient->states[_ucount] |= CFLastFailed; /* Mark server down */
+           } else if (rcode == UNOTSYNC) {
+               needsync = 1;
+           } else if (rcode != UNOQUORUM) {
+               /* either misc ubik code, or misc appl code, or success. */
+               aclient->states[_ucount] &= ~CFLastFailed;      /* mark server up*/
+               goto done;      /* all done */
+           }
+       }
+    }
+
+ done:
+    if (needsync) {
+       if (!rcode) {           /* Remember the sync site - cmd successful */
+           rxp = rx_PeerOf(aclient->conns[_ucount]);
+           aclient->syncSite = rx_HostOf(rxp);
+       }
+    }
+    UNLOCK_UBIK_CLIENT(aclient);
+    return rcode;
 }