ubik: Don't leak memory if mutex init fails
[openafs.git] / src / ubik / ubikclient.c
index fcbe3ac..f39ba1e 100644 (file)
@@ -1,80 +1,77 @@
-/* Copyright (C) 1991 Transarc Corporation - All rights reserved. */
 /*
- * For copyright information, see IPL which you accepted in order to
- * download this software.
+ * 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
  */
 
-#if defined(UKERNEL)
-#include "../afs/param.h"
-#include "../afs/sysincludes.h"
-#include "../afs/afsincludes.h"
-#include "../afs/stds.h"
-#include "../rx/xdr.h"
-#include "../rx/rx.h"
-#include "../afs/lock.h"
-#include "../afs/rxgen_consts.h"
-#include "../afs/ubik.h"
-#include "../afs/pthread_glock.h"
-#else /* defined(UKERNEL) */
+#include <afsconfig.h>
 #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
+
+#ifdef UKERNEL
+#include "afsincludes.h"
+#endif
+
 #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 <netdb.h>
-#include <netinet/in.h>
-#endif
 #include <afs/rxgen_consts.h>
+#define UBIK_LEGACY_CALLITER
 #include "ubik.h"
-#endif /* defined(UKERNEL) */
-
 
-afs_int32 ubik_CallIter();
-short ubik_initializationState; /* initial state is zero */
+short ubik_initializationState;        /*!< initial state is zero */
 
 
-/*
- * parse list for clients
+/*!
+ * \brief Parse list for clients.
  */
-int ubik_ParseClientList(
-  int argc,
-  char **argv,
-  afs_int32 *aothers)
+int
+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 */
+
+    inServer = 0;              /* haven't seen -servers yet */
     counter = 0;
-    for(i=1; i<argc; i++) {
+    for (i = 1; i < argc; i++) {
        /* look for -servers argument */
        tp = argv[i];
-       
+
        if (inServer) {
-           if (*tp == '-') break;  /* done */
+           if (*tp == '-')
+               break;          /* done */
            /* otherwise this is a new host name */
-           LOCK_GLOBAL_MUTEX
+           LOCK_GLOBAL_MUTEX;
            th = gethostbyname(tp);
            if (!th) {
-               UNLOCK_GLOBAL_MUTEX
+               UNLOCK_GLOBAL_MUTEX;
                return UBADHOST;
            }
-           memmove((void *) &temp, (const void *) th->h_addr, sizeof(afs_int32));
-           UNLOCK_GLOBAL_MUTEX
-           if (counter++ >= MAXSERVERS) return UNHOSTS;
+           memmove((void *)&temp, (const void *)th->h_addr,
+                   sizeof(afs_int32));
+           UNLOCK_GLOBAL_MUTEX;
+           if (counter++ >= MAXSERVERS)
+               return UNHOSTS;
            *aothers++ = temp;
-       }
-       else {
+       } else {
            /* haven't seen a -server yet */
            if (!strcmp(tp, "-servers")) {
                inServer = 1;
@@ -85,60 +82,64 @@ int ubik_ParseClientList(
        /* never saw a -server */
        return UNOENT;
     }
-    if (counter        < MAXSERVERS) *aothers++ = 0;    /* null terminate if room */
+    if (counter < MAXSERVERS)
+       *aothers++ = 0;         /* null terminate if room */
     return 0;
 }
 
 #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;
-static pthread_key_t random_number_key; 
+static pthread_key_t random_number_key;
 
-static void afs_random_once(void)
+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
-/* 
- * Random number generator and constants from KnuthV2 2d ed, p170
+
+#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:
- * X = (aX + c) % m
- * m is a power of two 
- * a % 8 is 5
- * a is 0.73m  should be 0.01m .. 0.99m
- * c is more or less immaterial.  1 or a is suggested.
+ * Rules: \n
+ * X = (aX + c) % m \n
+ * m is a power of two \n
+ * a % 8 is 5 \n
+ * a is 0.73m  should be 0.01m .. 0.99m \n
+ * 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).   
- * 
- */
-
-/* 
- * use time and pid to try to get some initial randomness.
+ * interesting bit-patterns).
  */
-#if !defined(UKERNEL)
-#define        ranstage(x)     (x)= (afs_uint32) (3141592621*((afs_uint32)x)+1)
-
-unsigned int afs_random(void)
+unsigned int
+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
@@ -146,33 +147,35 @@ unsigned int afs_random(void)
     if (!state) {
        int i;
        state = time(0) + getpid();
-       for (i=0;i<15;i++) {
+       for (i = 0; i < 15; i++) {
            ranstage(state);
        }
     }
 
     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);
 
 }
 
-/*
- * returns int 0..14 using the high bits of a pseudo-random number instead of
+/*!
+ * \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...
- * slight roundoff error exists, an excercise for the reader.
- * need to multiply by something with lots of ones in it, so multiply by 
+ *
+ * \todo Slight roundoff error exists, an excercise for the reader.
+ *
+ * Need to multiply by something with lots of ones in it, so multiply by
  * 8 or 16 is right out.
  */
-
-static unsigned int afs_randomMod15(void)
+static unsigned int
+afs_randomMod15(void)
 {
     afs_uint32 temp;
 
     temp = afs_random() >> 4;
-    temp = (temp *15) >> 28;
+    temp = (temp * 15) >> 28;
 
     return temp;
 }
@@ -182,47 +185,50 @@ static unsigned int afs_randomMod15(void)
 #undef abs
 #endif /* abs */
 #define abs(a) ((a) < 0 ? -1*(a) : (a))
-int ubik_ClientInit(
-  register struct rx_connection **serverconns,
-  struct ubik_client **aclient)
+int
+ubik_ClientInit(struct rx_connection **serverconns,
+               struct ubik_client **aclient)
 {
-    int i,j;
+    int i, j;
     int count;
     int offset;
-    register struct ubik_client *tc;
+    struct ubik_client *tc;
 
-    initialize_u_error_table();
+    initialize_U_error_table();
 
-    if ( *aclient ) {  /* the application is doing a re-initialization*/
-       LOCK_UBIK_CLIENT((*aclient))
+    if (*aclient) {            /* the application is doing a re-initialization */
+       LOCK_UBIK_CLIENT((*aclient));
        /* this is an important defensive check */
-       if ( ! ((*aclient)->initializationState) ) {
-           UNLOCK_UBIK_CLIENT((*aclient))
+       if (!((*aclient)->initializationState)) {
+           UNLOCK_UBIK_CLIENT((*aclient));
            return UREINITIALIZE;
        }
 
        /* release all existing connections */
-       for (tc = *aclient, i=0; i<MAXSERVERS; i++) 
-       {
-           struct rx_connection *rxConn = ubik_GetRPCConn(tc,i);
-           if (rxConn == 0) break;
+       for (tc = *aclient, i = 0; i < MAXSERVERS; i++) {
+           struct rx_connection *rxConn = ubik_GetRPCConn(tc, i);
+           if (rxConn == 0)
+               break;
 #ifdef AFS_PTHREAD_ENV
            rx_ReleaseCachedConnection(rxConn);
 #else
-           rx_DestroyConnection (rxConn);
+           rx_DestroyConnection(rxConn);
 #endif
        }
-       UNLOCK_UBIK_CLIENT((*aclient))
+       UNLOCK_UBIK_CLIENT((*aclient));
 #ifdef AFS_PTHREAD_ENV
-       if (pthread_mutex_destroy(&((*aclient)->cm))) return UMUTEXDESTROY;
+       if (pthread_mutex_destroy(&((*aclient)->cm)))
+           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));
+    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)) {
+    if (pthread_mutex_init(&(tc->cm), (const pthread_mutexattr_t *)0)) {
+       free(tc);
        return UMUTEXINIT;
     }
 #endif
@@ -230,19 +236,20 @@ int ubik_ClientInit(
 
     /* first count the # of server conns so we can randomize properly */
     count = 0;
-    for(i=0;i<MAXSERVERS;i++) {
-       if (serverconns[i] == (struct rx_connection *) 0) break;
+    for (i = 0; i < MAXSERVERS; i++) {
+       if (serverconns[i] == (struct rx_connection *)0)
+           break;
        count++;
     }
 
     /* here count is the # of servers we're actually passed in.  Compute
      * offset, a number between 0..count-1, where we'll start copying from the
      * client-provided array. */
-    for (i=0; i< count; i++) {
+    for (i = 0; i < count; i++) {
        offset = afs_randomMod15() % count;
-       for (j=abs(offset); j<2*count; j++) {
-           if (!tc->conns[abs(j%count)]) {
-               tc->conns[abs(j%count)] = serverconns[i];
+       for (j = abs(offset); j < 2 * count; j++) {
+           if (!tc->conns[abs(j % count)]) {
+               tc->conns[abs(j % count)] = serverconns[i];
                break;
            }
        }
@@ -252,42 +259,46 @@ int ubik_ClientInit(
     return 0;
 }
 
-/* 
- * ubik_ClientDestroy - destroys a ubik connection.  It calls rx to destroy the
- * component rx connections, then frees the ubik connection structure.
+/*!
+ * \brief Destroy an ubik connection.
+ *
+ * It calls rx to destroy the component rx connections, then frees the ubik
+ * connection structure.
  */
-
-afs_int32 ubik_ClientDestroy(struct ubik_client *aclient)
+afs_int32
+ubik_ClientDestroy(struct ubik_client * aclient)
 {
-    register int c;
+    int c;
 
-    if (aclient == 0) return 0;
+    if (aclient == 0)
+       return 0;
     LOCK_UBIK_CLIENT(aclient);
-    for (c=0; c<MAXSERVERS; c++) {
-       struct rx_connection *rxConn = ubik_GetRPCConn(aclient,c);
-       if (rxConn == 0) break;
+    for (c = 0; c < MAXSERVERS; c++) {
+       struct rx_connection *rxConn = ubik_GetRPCConn(aclient, c);
+       if (rxConn == 0)
+           break;
 #ifdef AFS_PTHREAD_ENV
        rx_ReleaseCachedConnection(rxConn);
 #else
-       rx_DestroyConnection (rxConn);
+       rx_DestroyConnection(rxConn);
 #endif
     }
-    aclient->initializationState = 0; /* client in not initialized*/
+    aclient->initializationState = 0;  /* client in not initialized */
     UNLOCK_UBIK_CLIENT(aclient);
 #ifdef AFS_PTHREAD_ENV
-    pthread_mutex_destroy(&(aclient->cm)); /* ignore failure */
+    pthread_mutex_destroy(&(aclient->cm));     /* ignore failure */
 #endif
-    free (aclient);
+    free(aclient);
     return 0;
 }
 
-/*
- * RefreshConn -- So that intermittent failures that cause connections to die
+/*!
+ * \brief So that intermittent failures that cause connections to die
  *     don't kill whole ubik connection, refresh them when the connection is in
  *     error.
  */
-
-static struct rx_connection *RefreshConn(struct rx_connection *tc)
+struct rx_connection *
+ubik_RefreshConn(struct rx_connection *tc)
 {
     afs_uint32 host;
     u_short port;
@@ -306,8 +317,8 @@ static struct rx_connection *RefreshConn(struct rx_connection *tc)
      * destroy old one after creating new one so that refCount on security
      * object cannot reach zero.
      */
-    newTc = rx_NewConnection (host, port, service, sc, si);
-    rx_DestroyConnection (tc);
+    newTc = rx_NewConnection(host, port, service, sc, si);
+    rx_DestroyConnection(tc);
     return newTc;
 }
 
@@ -315,13 +326,16 @@ static struct rx_connection *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);
-
-void ubik_client_init_mutex() {
-    assert(pthread_mutex_init(&ubik_client_mutex, NULL) == 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)
+{
+    MUTEX_INIT(&ubik_client_mutex, "client init", MUTEX_DEFAULT, 0);
 }
 
 #else
@@ -332,168 +346,23 @@ void ubik_client_init_mutex() {
 #endif
 
 #define SYNCCOUNT 10
-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.
- * in the future, we should also put in a protocol to find the sync site
- */
-afs_int32 ubik_Call(aproc, aclient, aflags, p1, p2, p3, p4, p5, p6, p7, p8, p9,
-               p10, p11, p12, p13, p14, p15, p16)
-  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 = 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 = 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;
-}
+static int *calls_needsync[SYNCCOUNT]; /* proc calls that need the sync site */
+static int synccount = 0;
 
 
 
-/*
- * call this after getting back a UNOTSYNC 
- * note that getting a UNOTSYNC error code back does *not* guarantee
+/*!
+ * \brief Call this after getting back a #UNOTSYNC.
+ *
+ * \note Getting a #UNOTSYNC error code back does \b not guarantee
  * that there is a sync site yet elected.  However, if there is a sync
  * site out there somewhere, and you're trying an operation that
- * requires a sync site, ubik will return UNOTSYNC, indicating the
+ * requires a sync site, ubik will return #UNOTSYNC, indicating the
  * operation won't work until you find a sync site
  */
-static int try_GetSyncSite(register struct ubik_client *aclient, afs_int32 apos) {
+static int
+try_GetSyncSite(struct ubik_client *aclient, afs_int32 apos)
+{
     struct rx_peer *rxp;
     afs_int32 code;
     int i;
@@ -505,8 +374,8 @@ static int try_GetSyncSite(register struct ubik_client *aclient, afs_int32 apos)
 
     /* get this conn */
     tc = aclient->conns[apos];
-    if (tc && rx_ConnError (tc)) {
-       aclient->conns[apos] = (tc = RefreshConn (tc));
+    if (tc && rx_ConnError(tc)) {
+       aclient->conns[apos] = (tc = ubik_RefreshConn(tc));
     }
     if (!tc) {
        return -1;
@@ -514,25 +383,24 @@ static int try_GetSyncSite(register struct ubik_client *aclient, afs_int32 apos)
 
     /* now see if we can find the sync site host */
     code = VOTE_GetSyncSite(tc, &newHost);
-    if ( aclient->initializationState != origLevel) {
-       return -1; /* somebody did a ubik_ClientInit */
+    if (aclient->initializationState != origLevel) {
+       return -1;              /* somebody did a ubik_ClientInit */
     }
 
-    if ( !code && newHost ) {
-       newHost = htonl(newHost); /* convert back to network order */
+    if (!code && newHost) {
+       newHost = htonl(newHost);       /* convert back to network order */
 
        /*
         * 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;i++) {
+       for (i = 0; i < MAXSERVERS; i++) {
            rxp = rx_PeerOf(aclient->conns[i]);
            thisHost = rx_HostOf(rxp);
            if (!thisHost) {
                return -1;
-           }
-           else if (thisHost == newHost) {
+           } else if (thisHost == newHost) {
                return i;       /* we were told to use this one */
            }
        }
@@ -540,130 +408,99 @@ static int try_GetSyncSite(register struct ubik_client *aclient, afs_int32 apos)
     return -1;
 }
 
-/* 
- * Create an internal version of ubik_CallIter that takes an additional
- * parameter - to indicate whether the ubik client handle has already
- * been locked.
- */
-
 #define NEED_LOCK 1
 #define NO_LOCK 0
 
-static afs_int32 CallIter(aproc, aclient, aflags, apos, p1, p2, p3, p4, p5, p6,
-                     p7, p8, p9, p10, p11, p12, p13, p14, p15, p16, needlock)
-  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;
-  long p12;
-  long p13;
-  long p14;
-  long p15;
-  long p16;
-  int needlock;
+/*!
+ * \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) (), 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;
 
     if (needlock) {
-       LOCK_UBIK_CLIENT(aclient)
+       LOCK_UBIK_CLIENT(aclient);
     }
     origLevel = aclient->initializationState;
 
     code = UNOSERVERS;
 
-    while (*apos < MAXSERVERS)
-    {
+    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 = RefreshConn (tc);
+       if (rx_ConnError(tc)) {
+           tc = ubik_RefreshConn(tc);
            aclient->conns[*apos] = tc;
        }
 
        if ((aflags & UPUBIKONLY) && (aclient->states[*apos] & CFLastFailed)) {
-           (*apos)++;  /* try another one if this server is down */
-       }
-       else {
-           break;      /* this is the desired path */
-       }
-    }
-    if (*apos >= MAXSERVERS) {
-       if (needlock) {
-           UNLOCK_UBIK_CLIENT(aclient)
+           (*apos)++;          /* try another one if this server is down */
+       } else {
+           break;              /* this is the desired path */
        }
-       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 */
-    }
+    code =
+       (*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 */
+       goto errout;
 
     /* what should I do in case of UNOQUORUM ? */
     if (code < 0) {
-       aclient->states[*apos] |= CFLastFailed;           /* network errors */
-    }
-    else {
+       aclient->states[*apos] |= CFLastFailed; /* network errors */
+    } else {
        /* either misc ubik code, or misc application code or success. */
-       aclient->states[*apos] &= ~CFLastFailed;          /* operation worked */
+       aclient->states[*apos] &= ~CFLastFailed;        /* operation worked */
     }
 
     (*apos)++;
+errout:
     if (needlock) {
-       UNLOCK_UBIK_CLIENT(aclient)
+       UNLOCK_UBIK_CLIENT(aclient);
     }
     return code;
 }
 
-/* 
- * call this instead of stub and we'll guarantee to find a host that's up.
- * in the future, we should also put in a protocol to find the sync site
+/*!
+ * \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(aproc, aclient, aflags, p1, p2, p3, p4, p5, p6, p7, p8,
-                   p9, p10, p11, p12, p13, p14, p15, p16)
-  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
+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;
     afs_int32 count;
     afs_int32 temp;
@@ -671,74 +508,189 @@ afs_int32 ubik_Call_New(aproc, aclient, aflags, p1, p2, p3, p4, p5, p6, p7, p8,
     int stepBack;
     short origLevel;
 
-    LOCK_UBIK_CLIENT(aclient)
-restart:
+    LOCK_UBIK_CLIENT(aclient);
+  restart:
     rcode = UNOSERVERS;
     origLevel = aclient->initializationState;
 
     /* Do two passes. First pass only checks servers known running */
-    for (aflags |= UPUBIKONLY, pass=0; pass<2; pass++, aflags &= ~UPUBIKONLY) {
+    for (aflags |= UPUBIKONLY, pass = 0; pass < 2;
+        pass++, aflags &= ~UPUBIKONLY) {
        stepBack = 0;
        count = 0;
        while (1) {
-           code = CallIter(aproc, aclient, aflags, &count, p1,p2,p3,p4,
-                           p5,p6,p7,p8,p9,p10,p11,p12,p13,p14,p15,p16,NO_LOCK);
-           if ( code && ( aclient->initializationState != origLevel)) {
+           code =
+               CallIter(aproc, aclient, aflags, &count, p1, p2, p3, p4, p5,
+                        p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, p16,
+                        NO_LOCK);
+           if (code && (aclient->initializationState != origLevel)) {
                goto restart;
            }
            if (code == UNOSERVERS) {
                break;
            }
-           rcode = code;        /* remember code from last good call */
+           rcode = code;       /* remember code from last good call */
 
-           if (code == UNOTSYNC) {     /* means this requires a sync site */
-               if (aclient->conns[3]) { /* don't bother unless 4 or more srv */
+           if (code == UNOTSYNC) {     /* means this requires a sync site */
+               if (aclient->conns[3]) {        /* don't bother unless 4 or more srv */
                    temp = try_GetSyncSite(aclient, count);
-                   if ( aclient->initializationState != origLevel) {
-                       goto restart; /* somebody did a ubik_ClientInit */
+                   if (aclient->initializationState != origLevel) {
+                       goto restart;   /* somebody did a ubik_ClientInit */
                    }
                    if ((temp >= 0) && ((temp > count) || (stepBack++ <= 2))) {
-                       count = temp;       /* generally try to make progress */
+                       count = temp;   /* generally try to make progress */
                    }
                }
-           }
-           else if ((code >= 0) && (code != UNOQUORUM)) {
-               UNLOCK_UBIK_CLIENT(aclient)
-               return code;  /* success or global error condition */
+           } else if ((code >= 0) && (code != UNOQUORUM)) {
+               UNLOCK_UBIK_CLIENT(aclient);
+               return code;    /* success or global error condition */
            }
        }
     }
-    UNLOCK_UBIK_CLIENT(aclient)
+    UNLOCK_UBIK_CLIENT(aclient);
     return rcode;
 }
 
-/* 
- * 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(aproc, aclient, aflags, apos, p1, p2, p3, p4, p5, p6, p7,
-                   p8, p9, p10, p11, p12, p13, p14, p15, p16)
-  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;
-  long p12;
-  long p13;
-  long p14;
-  long p15;
-  long p16;
+afs_int32
+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;
 }