/*
 * 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
 */

/*
 * afs_callback.c:
 *	Exported routines (and their private support) to implement
 *	the callback RPC interface.
 */

#include <afsconfig.h>
#include "afs/param.h"


#include "afs/sysincludes.h"	/*Standard vendor system headers */
#include "afsincludes.h"	/*AFS-based standard headers */
#define FSINT_COMMON_XG
#include "afs/afscbint.h"
#include "afs/afs_stats.h"	/*Cache Manager stats */
#include "afs/opr.h"
#include "afs/afs_args.h"

afs_int32 afs_allCBs = 0;	/*Break callbacks on all objects */
afs_int32 afs_oddCBs = 0;	/*Break callbacks on dirs */
afs_int32 afs_evenCBs = 0;	/*Break callbacks received on files */
afs_int32 afs_allZaps = 0;	/*Objects entries deleted */
afs_int32 afs_oddZaps = 0;	/*Dir cache entries deleted */
afs_int32 afs_evenZaps = 0;	/*File cache entries deleted */
afs_int32 afs_connectBacks = 0;

/*
 * Some debugging aids.
 */
static struct ltable {
    char *name;
    char *addr;
} ltable[] = {
    {
    "afs_xvcache", (char *)&afs_xvcache}, {
    "afs_xdcache", (char *)&afs_xdcache}, {
    "afs_xserver", (char *)&afs_xserver}, {
    "afs_xvcb", (char *)&afs_xvcb}, {
    "afs_xbrs", (char *)&afs_xbrs}, {
    "afs_xcell", (char *)&afs_xcell}, {
    "afs_xconn", (char *)&afs_xconn}, {
    "afs_xuser", (char *)&afs_xuser}, {
    "afs_xvolume", (char *)&afs_xvolume}, {
    "puttofile", (char *)&afs_puttofileLock}, {
    "afs_ftf", (char *)&afs_ftf}, {
    "afs_xcbhash", (char *)&afs_xcbhash}, {
    "afs_xaxs", (char *)&afs_xaxs}, {
    "afs_xinterface", (char *)&afs_xinterface},
    {
      "afs_xsrvAddr", (char *)&afs_xsrvAddr},
    {
      "afs_xvreclaim", (char *)&afs_xvreclaim},
    { "afsdb_client_lock", (char *)&afsdb_client_lock},
    { "afsdb_req_lock", (char *)&afsdb_req_lock},
    { "afs_discon_lock", (char *)&afs_discon_lock},
    { "afs_disconDirtyLock", (char *)&afs_disconDirtyLock},
    { "afs_discon_vc_dirty", (char *)&afs_xvcdirty},
    { "afs_dynrootDirLock", (char *)&afs_dynrootDirLock},
    { "afs_dynSymlinkLock", (char *)&afs_dynSymlinkLock},
};
unsigned long lastCallBack_vnode;
unsigned int lastCallBack_dv;
osi_timeval32_t lastCallBack_time;

/* these are for storing alternate interface addresses */
struct interfaceAddr afs_cb_interface;

/*------------------------------------------------------------------------
 * EXPORTED SRXAFSCB_GetCE
 *
 * Description:
 *	Routine called by the server-side callback RPC interface to
 *	implement pulling out the contents of the i'th cache entry.
 *
 * Arguments:
 *	a_call   : Ptr to Rx call on which this request came in.
 *	a_index  : Index of desired cache entry.
 *	a_result : Ptr to a buffer for the given cache entry.
 *
 * Returns:
 *	0 if everything went fine,
 *	1 if we were given a bad index.
 *
 * Environment:
 *	Nothing interesting.
 *
 * Side Effects:
 *	As advertised.
 *------------------------------------------------------------------------*/

int
SRXAFSCB_GetCE(struct rx_call *a_call, afs_int32 a_index,
	       struct AFSDBCacheEntry *a_result)
{

    int i;		/*Loop variable */
    struct vcache *tvc;	/*Ptr to current cache entry */
    int code;			/*Return code */
    XSTATS_DECLS;

    RX_AFS_GLOCK();

    XSTATS_START_CMTIME(AFS_STATS_CM_RPCIDX_GETCE);

    AFS_STATCNT(SRXAFSCB_GetCE);
    for (i = 0; i < VCSIZE; i++) {
	for (tvc = afs_vhashT[i]; tvc; tvc = tvc->hnext) {
	    if (a_index == 0)
		goto searchDone;
	    a_index--;
	}			/*Zip through current hash chain */
    }				/*Zip through hash chains */

  searchDone:
    if (tvc == NULL) {
	/*Past EOF */
	code = 1;
	goto fcnDone;
    }

    /*
     * Copy out the located entry.
     */
    a_result->addr = afs_data_pointer_to_int32(tvc);
    a_result->cell = tvc->f.fid.Cell;
    a_result->netFid.Volume = tvc->f.fid.Fid.Volume;
    a_result->netFid.Vnode = tvc->f.fid.Fid.Vnode;
    a_result->netFid.Unique = tvc->f.fid.Fid.Unique;
    a_result->lock.waitStates = tvc->lock.wait_states;
    a_result->lock.exclLocked = tvc->lock.excl_locked;
    a_result->lock.readersReading = tvc->lock.readers_reading;
    a_result->lock.numWaiting = tvc->lock.num_waiting;
#if defined(INSTRUMENT_LOCKS)
    a_result->lock.pid_last_reader = MyPidxx2Pid(tvc->lock.pid_last_reader);
    a_result->lock.pid_writer = MyPidxx2Pid(tvc->lock.pid_writer);
    a_result->lock.src_indicator = tvc->lock.src_indicator;
#else
    /* On osf20 , the vcache does not maintain these three fields */
    a_result->lock.pid_last_reader = 0;
    a_result->lock.pid_writer = 0;
    a_result->lock.src_indicator = 0;
#endif /* INSTRUMENT_LOCKS */
#ifdef AFS_64BIT_CLIENT
    a_result->Length = (afs_int32) tvc->f.m.Length & 0xffffffff;
#else /* AFS_64BIT_CLIENT */
    a_result->Length = tvc->f.m.Length;
#endif /* AFS_64BIT_CLIENT */
    a_result->DataVersion = hgetlo(tvc->f.m.DataVersion);
    a_result->callback = afs_data_pointer_to_int32(tvc->callback);	/* XXXX Now a pointer; change it XXXX */
    a_result->cbExpires = tvc->cbExpires;
    if (tvc->f.states & CVInit) {
        a_result->refCount = 1;
    } else {
#ifdef AFS_DARWIN80_ENV
    a_result->refCount = vnode_isinuse(AFSTOV(tvc),0)?1:0; /* XXX fix */
#else
    a_result->refCount = VREFCOUNT(tvc);
#endif
    }
    a_result->opens = tvc->opens;
    a_result->writers = tvc->execsOrWriters;
    a_result->mvstat = tvc->mvstat;
    a_result->states = tvc->f.states;
    code = 0;

    /*
     * Return our results.
     */
  fcnDone:
    XSTATS_END_TIME;

    RX_AFS_GUNLOCK();

    return (code);

}				/*SRXAFSCB_GetCE */

int
SRXAFSCB_GetCE64(struct rx_call *a_call, afs_int32 a_index,
		 struct AFSDBCacheEntry64 *a_result)
{
    int i;		/*Loop variable */
    struct vcache *tvc;	/*Ptr to current cache entry */
    int code;			/*Return code */
    XSTATS_DECLS;

    RX_AFS_GLOCK();

    XSTATS_START_CMTIME(AFS_STATS_CM_RPCIDX_GETCE);

    AFS_STATCNT(SRXAFSCB_GetCE64);
    for (i = 0; i < VCSIZE; i++) {
	for (tvc = afs_vhashT[i]; tvc; tvc = tvc->hnext) {
	    if (a_index == 0)
		goto searchDone;
	    a_index--;
	}			/*Zip through current hash chain */
    }				/*Zip through hash chains */

  searchDone:
    if (tvc == NULL) {
	/*Past EOF */
	code = 1;
	goto fcnDone;
    }

    /*
     * Copy out the located entry.
     */
    a_result->addr = afs_data_pointer_to_int32(tvc);
    a_result->cell = tvc->f.fid.Cell;
    a_result->netFid.Volume = tvc->f.fid.Fid.Volume;
    a_result->netFid.Vnode = tvc->f.fid.Fid.Vnode;
    a_result->netFid.Unique = tvc->f.fid.Fid.Unique;
    a_result->lock.waitStates = tvc->lock.wait_states;
    a_result->lock.exclLocked = tvc->lock.excl_locked;
    a_result->lock.readersReading = tvc->lock.readers_reading;
    a_result->lock.numWaiting = tvc->lock.num_waiting;
#if defined(INSTRUMENT_LOCKS)
    a_result->lock.pid_last_reader = MyPidxx2Pid(tvc->lock.pid_last_reader);
    a_result->lock.pid_writer = MyPidxx2Pid(tvc->lock.pid_writer);
    a_result->lock.src_indicator = tvc->lock.src_indicator;
#else
    /* On osf20 , the vcache does not maintain these three fields */
    a_result->lock.pid_last_reader = 0;
    a_result->lock.pid_writer = 0;
    a_result->lock.src_indicator = 0;
#endif /* INSTRUMENT_LOCKS */
    a_result->Length = tvc->f.m.Length;
    a_result->DataVersion = hgetlo(tvc->f.m.DataVersion);
    a_result->callback = afs_data_pointer_to_int32(tvc->callback);	/* XXXX Now a pointer; change it XXXX */
    a_result->cbExpires = tvc->cbExpires;
    if (tvc->f.states & CVInit) {
        a_result->refCount = 1;
    } else {
#ifdef AFS_DARWIN80_ENV
    a_result->refCount = vnode_isinuse(AFSTOV(tvc),0)?1:0; /* XXX fix */
#else
    a_result->refCount = VREFCOUNT(tvc);
#endif
    }
    a_result->opens = tvc->opens;
    a_result->writers = tvc->execsOrWriters;
    a_result->mvstat = tvc->mvstat;
    a_result->states = tvc->f.states;
    code = 0;

    /*
     * Return our results.
     */
  fcnDone:
    XSTATS_END_TIME;

    RX_AFS_GUNLOCK();

    return (code);

}				/*SRXAFSCB_GetCE64 */


/*------------------------------------------------------------------------
 * EXPORTED SRXAFSCB_GetLock
 *
 * Description:
 *	Routine called by the server-side callback RPC interface to
 *	implement pulling out the contents of a lock in the lock
 *	table.
 *
 * Arguments:
 *	a_call   : Ptr to Rx call on which this request came in.
 *	a_index  : Index of desired lock.
 *	a_result : Ptr to a buffer for the given lock.
 *
 * Returns:
 *	0 if everything went fine,
 *	1 if we were given a bad index.
 *
 * Environment:
 *	Nothing interesting.
 *
 * Side Effects:
 *	As advertised.
 *------------------------------------------------------------------------*/

int
SRXAFSCB_GetLock(struct rx_call *a_call, afs_int32 a_index,
		 struct AFSDBLock *a_result)
{
    struct ltable *tl;		/*Ptr to lock table entry */
    int nentries;		/*Num entries in table */
    int code;			/*Return code */
    XSTATS_DECLS;

    RX_AFS_GLOCK();

    XSTATS_START_CMTIME(AFS_STATS_CM_RPCIDX_GETLOCK);

    AFS_STATCNT(SRXAFSCB_GetLock);
    memset(a_result, 0, sizeof(*a_result));
    nentries = sizeof(ltable) / sizeof(struct ltable);
    if (a_index < 0 || a_index >= nentries+afs_cellindex) {
	/*
	 * Past EOF
	 */
	code = 1;
    } else if (a_index >= nentries) {
	struct cell *tc = afs_GetCellByIndex(a_index-nentries, 0);
	strlcpy(a_result->name, tc->cellName, sizeof(a_result->name));
	a_result->lock.waitStates =
	    ((struct afs_lock *)&(tc->lock))->wait_states;
	a_result->lock.exclLocked =
	    ((struct afs_lock *)&(tc->lock))->excl_locked;
	a_result->lock.readersReading =
	    ((struct afs_lock *)&(tc->lock))->readers_reading;
	a_result->lock.numWaiting =
	    ((struct afs_lock *)&(tc->lock))->num_waiting;
#ifdef INSTRUMENT_LOCKS
	a_result->lock.pid_last_reader =
	    MyPidxx2Pid(((struct afs_lock *)&(tc->lock))->pid_last_reader);
	a_result->lock.pid_writer =
	    MyPidxx2Pid(((struct afs_lock *)&(tc->lock))->pid_writer);
	a_result->lock.src_indicator =
	    ((struct afs_lock *)&(tc->lock))->src_indicator;
#else
	a_result->lock.pid_last_reader = 0;
	a_result->lock.pid_writer = 0;
	a_result->lock.src_indicator = 0;
#endif
	code = 0;
    } else {
	/*
	 * Found it - copy out its contents.
	 */
	tl = &ltable[a_index];
	strlcpy(a_result->name, tl->name, sizeof(a_result->name));
	a_result->lock.waitStates =
	    ((struct afs_lock *)(tl->addr))->wait_states;
	a_result->lock.exclLocked =
	    ((struct afs_lock *)(tl->addr))->excl_locked;
	a_result->lock.readersReading =
	    ((struct afs_lock *)(tl->addr))->readers_reading;
	a_result->lock.numWaiting =
	    ((struct afs_lock *)(tl->addr))->num_waiting;
#ifdef INSTRUMENT_LOCKS
	a_result->lock.pid_last_reader =
	    MyPidxx2Pid(((struct afs_lock *)(tl->addr))->pid_last_reader);
	a_result->lock.pid_writer =
	    MyPidxx2Pid(((struct afs_lock *)(tl->addr))->pid_writer);
	a_result->lock.src_indicator =
	    ((struct afs_lock *)(tl->addr))->src_indicator;
#else
	a_result->lock.pid_last_reader = 0;
	a_result->lock.pid_writer = 0;
	a_result->lock.src_indicator = 0;
#endif
	code = 0;
    }

    XSTATS_END_TIME;

    RX_AFS_GUNLOCK();

    return (code);

}				/*SRXAFSCB_GetLock */


/*------------------------------------------------------------------------
 * static ClearCallBack
 *
 * Description:
 *	Clear out callback information for the specified file, or
 *	even a whole volume.  Used to worry about callback was from
 *      within the particular cell or not.  Now we don't bother with
 *      that anymore; it's not worth the time.
 *
 * Arguments:
 *	a_conn : Ptr to Rx connection involved.
 *	a_fid  : Ptr to AFS fid being cleared.
 *
 * Returns:
 *	0 (always)
 *
 * Environment:
 *	Nothing interesting.
 *
 * Side Effects:
 *	As advertised.

Appears to need to be called with GLOCK held, as the icl_Event4 stuff asserts otherwise

 *------------------------------------------------------------------------*/

static int
ClearCallBack(struct rx_connection *a_conn,
	      struct AFSFid *a_fid)
{
    struct vcache *tvc;
    int i;
    struct VenusFid localFid;
    struct volume *tv;
#ifdef AFS_DARWIN80_ENV
    vnode_t vp;
#endif

    AFS_STATCNT(ClearCallBack);

    AFS_ASSERT_GLOCK();

    /*
     * XXXX Don't hold any server locks here because of callback protocol XXX
     */
    localFid.Cell = 0;
    localFid.Fid.Volume = a_fid->Volume;
    localFid.Fid.Vnode = a_fid->Vnode;
    localFid.Fid.Unique = a_fid->Unique;

    /*
     * Volume ID of zero means don't do anything.
     */
    if (a_fid->Volume != 0) {
	if (a_fid->Vnode == 0) {
		struct afs_q *tq, *uq;
	    /*
	     * Clear callback for the whole volume.  Zip through the
	     * hash chain, nullifying entries whose volume ID matches.
	     */
loop1:
		ObtainReadLock(&afs_xvcache);
		i = VCHashV(&localFid);
		for (tq = afs_vhashTV[i].prev; tq != &afs_vhashTV[i]; tq = uq) {
		    uq = QPrev(tq);
		    tvc = QTOVH(tq);
		    if (tvc->f.fid.Fid.Volume == a_fid->Volume) {
			tvc->callback = NULL;
			if (!localFid.Cell)
			    localFid.Cell = tvc->f.fid.Cell;
			tvc->dchint = NULL;	/* invalidate hints */
			if (tvc->f.states & CVInit) {
			    ReleaseReadLock(&afs_xvcache);
			    afs_osi_Sleep(&tvc->f.states);
			    goto loop1;
			}
#ifdef AFS_DARWIN80_ENV
			if (tvc->f.states & CDeadVnode) {
			    ReleaseReadLock(&afs_xvcache);
			    afs_osi_Sleep(&tvc->f.states);
			    goto loop1;
			}
			vp = AFSTOV(tvc);
			if (vnode_get(vp))
			    continue;
			if (vnode_ref(vp)) {
			    AFS_GUNLOCK();
			    vnode_put(vp);
			    AFS_GLOCK();
			    continue;
			}
#else
			if (osi_vnhold(tvc) != 0) {
			    continue;
			}
#endif
			ReleaseReadLock(&afs_xvcache);
			afs_StaleVCacheFlags(tvc, 0, CUnique | CBulkFetching);
			afs_allCBs++;
			if (tvc->f.fid.Fid.Vnode & 1)
			    afs_oddCBs++;
			else
			    afs_evenCBs++;
			afs_Trace3(afs_iclSetp, CM_TRACE_CALLBACK,
				   ICL_TYPE_POINTER, tvc, ICL_TYPE_INT32,
				   tvc->f.states, ICL_TYPE_INT32,
				   a_fid->Volume);
#ifdef AFS_DARWIN80_ENV
			vnode_put(AFSTOV(tvc));
#endif
			ObtainReadLock(&afs_xvcache);
			uq = QPrev(tq);
			AFS_FAST_RELE(tvc);
		    } else if ((tvc->f.states & CMValid)
			       && (tvc->mvid.target_root->Fid.Volume == a_fid->Volume)) {
			tvc->f.states &= ~CMValid;
			if (!localFid.Cell)
			    localFid.Cell = tvc->mvid.target_root->Cell;
		    }
		}
		ReleaseReadLock(&afs_xvcache);

	    /*
	     * XXXX Don't hold any locks here XXXX
	     */
	    tv = afs_FindVolume(&localFid, 0);
	    if (tv) {
		afs_ResetVolumeInfo(tv);
		afs_PutVolume(tv, 0);
		/* invalidate mtpoint? */
	    }
	} /*Clear callbacks for whole volume */
	else {
	    /*
	     * Clear callbacks just for the one file.
	     */
	    struct vcache *uvc;
	    afs_allCBs++;
	    if (a_fid->Vnode & 1)
		afs_oddCBs++;	/*Could do this on volume basis, too */
	    else
		afs_evenCBs++;	/*A particular fid was specified */
loop2:
	    ObtainReadLock(&afs_xvcache);
	    i = VCHash(&localFid);
	    for (tvc = afs_vhashT[i]; tvc; tvc = uvc) {
		uvc = tvc->hnext;
		if (tvc->f.fid.Fid.Vnode == a_fid->Vnode
		    && tvc->f.fid.Fid.Volume == a_fid->Volume
		    && tvc->f.fid.Fid.Unique == a_fid->Unique) {
		    tvc->callback = NULL;
		    tvc->dchint = NULL;	/* invalidate hints */
		    if (tvc->f.states & CVInit) {
			ReleaseReadLock(&afs_xvcache);
			afs_osi_Sleep(&tvc->f.states);
			goto loop2;
		    }
#ifdef AFS_DARWIN80_ENV
		    if (tvc->f.states & CDeadVnode) {
			ReleaseReadLock(&afs_xvcache);
			afs_osi_Sleep(&tvc->f.states);
			goto loop2;
		    }
		    vp = AFSTOV(tvc);
		    if (vnode_get(vp))
			continue;
		    if (vnode_ref(vp)) {
			AFS_GUNLOCK();
			vnode_put(vp);
			AFS_GLOCK();
			continue;
		    }
#else
		    if (osi_vnhold(tvc) != 0) {
			continue;
		    }
#endif
		    ReleaseReadLock(&afs_xvcache);
		    afs_StaleVCacheFlags(tvc, 0, CUnique | CBulkFetching);
		    afs_Trace3(afs_iclSetp, CM_TRACE_CALLBACK,
			       ICL_TYPE_POINTER, tvc, ICL_TYPE_INT32,
			       tvc->f.states, ICL_TYPE_LONG, 0);
#ifdef CBDEBUG
		    lastCallBack_vnode = afid->Vnode;
		    lastCallBack_dv = tvc->mstat.DataVersion.low;
		    osi_GetuTime(&lastCallBack_time);
#endif /* CBDEBUG */
#ifdef AFS_DARWIN80_ENV
		    vnode_put(AFSTOV(tvc));
#endif
		    ObtainReadLock(&afs_xvcache);
		    uvc = tvc->hnext;
		    AFS_FAST_RELE(tvc);
		}
	    }			/*Walk through hash table */
	    ReleaseReadLock(&afs_xvcache);
	}			/*Clear callbacks for one file */
    }

    /*Fid has non-zero volume ID */
    /*
     * Always return a predictable value.
     */
    return (0);

}				/*ClearCallBack */


/*------------------------------------------------------------------------
 * EXPORTED SRXAFSCB_CallBack
 *
 * Description:
 *	Routine called by the server-side callback RPC interface to
 *	implement passing in callback information.
 *	table.
 *
 * Arguments:
 *	a_call      : Ptr to Rx call on which this request came in.
 *	a_fids      : Ptr to array of fids involved.
 *	a_callbacks : Ptr to matching callback info for the fids.
 *
 * Returns:
 *	0 (always).
 *
 * Environment:
 *	Nothing interesting.
 *
 * Side Effects:
 *	As advertised.
 *------------------------------------------------------------------------*/

int
SRXAFSCB_CallBack(struct rx_call *a_call, struct AFSCBFids *a_fids,
		  struct AFSCBs *a_callbacks)
{
    int i;		/*Loop variable */
    struct AFSFid *tfid;	/*Ptr to current fid */
    struct rx_connection *tconn;	/*Call's connection */
    int code = 0;
    XSTATS_DECLS;

    RX_AFS_GLOCK();

    XSTATS_START_CMTIME(AFS_STATS_CM_RPCIDX_CALLBACK);

    AFS_STATCNT(SRXAFSCB_CallBack);
    if (!(tconn = rx_ConnectionOf(a_call)))
	return (0);
    tfid = (struct AFSFid *)a_fids->AFSCBFids_val;

    /*
     * For now, we ignore callbacks, since the File Server only *breaks*
     * callbacks at present.
     */
    for (i = 0; i < a_fids->AFSCBFids_len; i++)
	ClearCallBack(tconn, &tfid[i]);

    XSTATS_END_TIME;

    RX_AFS_GUNLOCK();

    return (0);

}				/*SRXAFSCB_CallBack */


/*------------------------------------------------------------------------
 * EXPORTED SRXAFSCB_Probe
 *
 * Description:
 *	Routine called by the server-side callback RPC interface to
 *	implement ``probing'' the Cache Manager, just making sure it's
 *	still there.
 *
 * Arguments:
 *	a_call : Ptr to Rx call on which this request came in.
 *
 * Returns:
 *	0 (always).
 *
 * Environment:
 *	Nothing interesting.
 *
 * Side Effects:
 *	As advertised.
 *------------------------------------------------------------------------*/

int
SRXAFSCB_Probe(struct rx_call *a_call)
{
    int code = 0;
    XSTATS_DECLS;

    RX_AFS_GLOCK();
    AFS_STATCNT(SRXAFSCB_Probe);

    XSTATS_START_CMTIME(AFS_STATS_CM_RPCIDX_PROBE);
    XSTATS_END_TIME;

    RX_AFS_GUNLOCK();

    return (0);

}				/*SRXAFSCB_Probe */


/*------------------------------------------------------------------------
 * EXPORTED SRXAFSCB_InitCallBackState
 *
 * Description:
 *	Routine called by the server-side callback RPC interface to
 *	implement clearing all callbacks from this host.
 *
 * Arguments:
 *	a_call : Ptr to Rx call on which this request came in.
 *
 * Returns:
 *	0 (always).
 *
 * Environment:
 *	Nothing interesting.
 *
 * Side Effects:
 *	As advertised.
 *------------------------------------------------------------------------*/

int
SRXAFSCB_InitCallBackState(struct rx_call *a_call)
{
    int i;
    struct vcache *tvc;
    struct rx_connection *tconn;
    struct rx_peer *peer;
    struct server *ts;
    int code = 0;
    XSTATS_DECLS;

    RX_AFS_GLOCK();

    XSTATS_START_CMTIME(AFS_STATS_CM_RPCIDX_INITCALLBACKSTATE);
    AFS_STATCNT(SRXAFSCB_InitCallBackState);

    /*
     * Find the address of the host making this call
     */
    if ((tconn = rx_ConnectionOf(a_call)) && (peer = rx_PeerOf(tconn))) {

	afs_allCBs++;
	afs_oddCBs++;		/*Including any missed via create race */
	afs_evenCBs++;		/*Including any missed via create race */

	ts = afs_FindServer(rx_HostOf(peer), rx_PortOf(peer), (afsUUID *) 0,
			    0);
	if (ts) {
	    for (i = 0; i < VCSIZE; i++)
		for (tvc = afs_vhashT[i]; tvc; tvc = tvc->hnext) {
		    if (tvc->callback == ts) {
			afs_StaleVCacheFlags(tvc, AFS_STALEVC_NODNLC |
						  AFS_STALEVC_CLEARCB,
					     CUnique | CBulkFetching);
		    }
		}

	    /* capabilities need be requested again */
	    ts->flags &= ~SCAPS_KNOWN;
	}



	/* find any volumes residing on this server and flush their state */
	{
	    struct volume *tv;
	    int j;

	    for (i = 0; i < NVOLS; i++)
		for (tv = afs_volumes[i]; tv; tv = tv->next) {
		    for (j = 0; j < AFS_MAXHOSTS; j++)
			if (tv->serverHost[j] == ts)
			    afs_ResetVolumeInfo(tv);
		}
	}
	osi_dnlc_purge();	/* may be a little bit extreme */
    }

    XSTATS_END_TIME;

    RX_AFS_GUNLOCK();

    return (0);

}				/*SRXAFSCB_InitCallBackState */


/*------------------------------------------------------------------------
 * EXPORTED SRXAFSCB_XStatsVersion
 *
 * Description:
 *	Routine called by the server-side callback RPC interface to
 *	implement pulling out the xstat version number for the Cache
 *	Manager.
 *
 * Arguments:
 *	a_versionP : Ptr to the version number variable to set.
 *
 * Returns:
 *	0 (always)
 *
 * Environment:
 *	Nothing interesting.
 *
 * Side Effects:
 *	As advertised.
 *------------------------------------------------------------------------*/

int
SRXAFSCB_XStatsVersion(struct rx_call *a_call, afs_int32 * a_versionP)
{
    int code = 0;

    XSTATS_DECLS;

    RX_AFS_GLOCK();
    XSTATS_START_CMTIME(AFS_STATS_CM_RPCIDX_XSTATSVERSION);

    *a_versionP = AFSCB_XSTAT_VERSION;

    XSTATS_END_TIME;

    RX_AFS_GUNLOCK();

    return (0);
}				/*SRXAFSCB_XStatsVersion */


/*------------------------------------------------------------------------
 * EXPORTED SRXAFSCB_GetXStats
 *
 * Description:
 *	Routine called by the server-side callback RPC interface to
 *	implement getting the given data collection from the extended
 *	Cache Manager statistics.
 *
 * Arguments:
 *	a_call		    : Ptr to Rx call on which this request came in.
 *	a_clientVersionNum  : Client version number.
 *	a_opCode	    : Desired operation.
 *	a_serverVersionNumP : Ptr to version number to set.
 *	a_timeP		    : Ptr to time value (seconds) to set.
 *	a_dataArray	    : Ptr to variable array structure to return
 *			      stuff in.
 *
 * Returns:
 *	0 (always).
 *
 * Environment:
 *	Nothing interesting.
 *
 * Side Effects:
 *	As advertised.
 *------------------------------------------------------------------------*/

int
SRXAFSCB_GetXStats(struct rx_call *a_call, afs_int32 a_clientVersionNum,
		   afs_int32 a_collectionNumber, afs_int32 * a_srvVersionNumP,
		   afs_int32 * a_timeP, AFSCB_CollData * a_dataP)
{
    int code;		/*Return value */
    afs_int32 *dataBuffP;	/*Ptr to data to be returned */
    afs_int32 dataBytes;	/*Bytes in data buffer */
    XSTATS_DECLS;

    opr_StaticAssert(sizeof(osi_timeval32_t) == SIZEOF_OSI_TIMEVAL32_T);

    RX_AFS_GLOCK();

    XSTATS_START_CMTIME(AFS_STATS_CM_RPCIDX_GETXSTATS);

    /*
     * Record the time of day and the server version number.
     */
    *a_srvVersionNumP = AFSCB_XSTAT_VERSION;
    *a_timeP = osi_Time();

    /*
     * Stuff the appropriate data in there (assume victory)
     */
    code = 0;

#ifdef AFS_NOSTATS
    /*
     * We're not keeping stats, so just return successfully with
     * no data.
     */
    a_dataP->AFSCB_CollData_len = 0;
    a_dataP->AFSCB_CollData_val = NULL;
#else
    switch (a_collectionNumber) {
    case AFSCB_XSTATSCOLL_CALL_INFO:
	/*
	 * Pass back all the call-count-related data.
	 *
	 * >>> We are forced to allocate a separate area in which to
	 * >>> put this stuff in by the RPC stub generator, since it
	 * >>> will be freed at the tail end of the server stub code.
	 */
	dataBytes = sizeof(struct afs_CMStats);
	dataBuffP = (afs_int32 *) afs_osi_Alloc(dataBytes);
	osi_Assert(dataBuffP != NULL);
	memcpy((char *)dataBuffP, (char *)&afs_cmstats, dataBytes);
	a_dataP->AFSCB_CollData_len = dataBytes >> 2;
	a_dataP->AFSCB_CollData_val = dataBuffP;
	break;

    case AFSCB_XSTATSCOLL_PERF_INFO:
	/*
	 * Update and then pass back all the performance-related data.
	 * Note: the only performance fields that need to be computed
	 * at this time are the number of accesses for this collection
	 * and the current server record info.
	 *
	 * >>> We are forced to allocate a separate area in which to
	 * >>> put this stuff in by the RPC stub generator, since it
	 * >>> will be freed at the tail end of the server stub code.
	 */
	afs_stats_cmperf.numPerfCalls++;
	afs_CountServers();
	dataBytes = sizeof(afs_stats_cmperf);
	dataBuffP = (afs_int32 *) afs_osi_Alloc(dataBytes);
	osi_Assert(dataBuffP != NULL);
	memcpy((char *)dataBuffP, (char *)&afs_stats_cmperf, dataBytes);
	a_dataP->AFSCB_CollData_len = dataBytes >> 2;
	a_dataP->AFSCB_CollData_val = dataBuffP;
	break;

    case AFSCB_XSTATSCOLL_FULL_PERF_INFO:
	/*
	 * Pass back the full range of performance and statistical
	 * data available.  We have to bring the normal performance
	 * data collection up to date, then copy that data into
	 * the full collection.
	 *
	 * >>> We are forced to allocate a separate area in which to
	 * >>> put this stuff in by the RPC stub generator, since it
	 * >>> will be freed at the tail end of the server stub code.
	 */
	afs_stats_cmperf.numPerfCalls++;
	afs_CountServers();
	memcpy((char *)(&(afs_stats_cmfullperf.perf)),
	       (char *)(&afs_stats_cmperf), sizeof(struct afs_stats_CMPerf));
	afs_stats_cmfullperf.numFullPerfCalls++;

	dataBytes = sizeof(afs_stats_cmfullperf);
	dataBuffP = (afs_int32 *) afs_osi_Alloc(dataBytes);
	osi_Assert(dataBuffP != NULL);
	memcpy((char *)dataBuffP, (char *)(&afs_stats_cmfullperf), dataBytes);
	a_dataP->AFSCB_CollData_len = dataBytes >> 2;
	a_dataP->AFSCB_CollData_val = dataBuffP;
	break;

    default:
	/*
	 * Illegal collection number.
	 */
	a_dataP->AFSCB_CollData_len = 0;
	a_dataP->AFSCB_CollData_val = NULL;
	code = 1;
    }				/*Switch on collection number */
#endif /* AFS_NOSTATS */

    XSTATS_END_TIME;

    RX_AFS_GUNLOCK();

    return (code);

}				/*SRXAFSCB_GetXStats */


/*------------------------------------------------------------------------
 * EXPORTED afs_RXCallBackServer
 *
 * Description:
 *	Body of the thread supporting callback services.
 *
 * Arguments:
 *	None.
 *
 * Returns:
 *	0 (always).
 *
 * Environment:
 *	Nothing interesting.
 *
 * Side Effects:
 *	As advertised.
 *------------------------------------------------------------------------*/

int
afs_RXCallBackServer(void)
{
    AFS_STATCNT(afs_RXCallBackServer);

    while (1) {
	if (afs_server)
	    break;
	afs_osi_Sleep(&afs_server);
    }

    /*
     * Donate this process to Rx.
     */
    rx_ServerProc(NULL);
    return (0);

}				/*afs_RXCallBackServer */


/*------------------------------------------------------------------------
 * EXPORTED shutdown_CB
 *
 * Description:
 *	Zero out important Cache Manager data structures.
 *
 * Arguments:
 *	None.
 *
 * Returns:
 *	0 (always).
 *
 * Environment:
 *	Nothing interesting.
 *
 * Side Effects:
 *	As advertised.
 *------------------------------------------------------------------------*/

int
shutdown_CB(void)
{
    AFS_STATCNT(shutdown_CB);

    if (afs_cold_shutdown) {
	afs_oddCBs = afs_evenCBs = afs_allCBs = afs_allZaps = afs_oddZaps =
	    afs_evenZaps = afs_connectBacks = 0;
    }

    return (0);

}				/*shutdown_CB */

/*------------------------------------------------------------------------
 * EXPORTED SRXAFSCB_InitCallBackState2
 *
 * Description:
 *      This routine was used in the AFS 3.5 beta release, but not anymore.
 *      It has since been replaced by SRXAFSCB_InitCallBackState3.
 *
 * Arguments:
 *      a_call : Ptr to Rx call on which this request came in.
 *
 * Returns:
 *      RXGEN_OPCODE (always).
 *
 * Environment:
 *      Nothing interesting.
 *
 * Side Effects:
 *      None
 *------------------------------------------------------------------------*/

int
SRXAFSCB_InitCallBackState2(struct rx_call *a_call,
			    struct interfaceAddr *addr)
{
    return RXGEN_OPCODE;
}

/*------------------------------------------------------------------------
 * EXPORTED SRXAFSCB_WhoAreYou
 *
 * Description:
 *      Routine called by the server-side callback RPC interface to
 *      obtain a unique identifier for the client. The server uses
 *      this identifier to figure out whether or not two RX connections
 *      are from the same client, and to find out which addresses go
 *      with which clients.
 *
 * Arguments:
 *      a_call : Ptr to Rx call on which this request came in.
 *      addr: Ptr to return the list of interfaces for this client.
 *
 * Returns:
 *      0 (Always)
 *
 * Environment:
 *      Nothing interesting.
 *
 * Side Effects:
 *      As advertised.
 *------------------------------------------------------------------------*/

int
SRXAFSCB_WhoAreYou(struct rx_call *a_call, struct interfaceAddr *addr)
{
    int i;
    int code = 0;

    RX_AFS_GLOCK();

    AFS_STATCNT(SRXAFSCB_WhoAreYou);

    memset(addr, 0, sizeof(*addr));

    ObtainReadLock(&afs_xinterface);

    /* return all network interface addresses */
    addr->numberOfInterfaces = afs_cb_interface.numberOfInterfaces;
    addr->uuid = afs_cb_interface.uuid;
    for (i = 0; i < afs_cb_interface.numberOfInterfaces; i++) {
	addr->addr_in[i] = ntohl(afs_cb_interface.addr_in[i]);
	addr->subnetmask[i] = ntohl(afs_cb_interface.subnetmask[i]);
	addr->mtu[i] = ntohl(afs_cb_interface.mtu[i]);
    }

    ReleaseReadLock(&afs_xinterface);

    RX_AFS_GUNLOCK();

    return code;
}


/*------------------------------------------------------------------------
 * EXPORTED SRXAFSCB_InitCallBackState3
 *
 * Description:
 *	Routine called by the server-side callback RPC interface to
 *	implement clearing all callbacks from this host.
 *
 * Arguments:
 *	a_call : Ptr to Rx call on which this request came in.
 *
 * Returns:
 *	0 (always).
 *
 * Environment:
 *	Nothing interesting.
 *
 * Side Effects:
 *	As advertised.
 *------------------------------------------------------------------------*/

int
SRXAFSCB_InitCallBackState3(struct rx_call *a_call, afsUUID * a_uuid)
{
    int code;

    /*
     * TBD: Lookup the server by the UUID instead of its IP address.
     */
    code = SRXAFSCB_InitCallBackState(a_call);

    return code;
}


/*------------------------------------------------------------------------
 * EXPORTED SRXAFSCB_ProbeUuid
 *
 * Description:
 *	Routine called by the server-side callback RPC interface to
 *	implement ``probing'' the Cache Manager, just making sure it's
 *	still there is still the same client it used to be.
 *
 * Arguments:
 *	a_call : Ptr to Rx call on which this request came in.
 *	a_uuid : Ptr to UUID that must match the client's UUID.
 *
 * Returns:
 *	0 if a_uuid matches the UUID for this client
 *      Non-zero otherwize
 *
 * Environment:
 *	Nothing interesting.
 *
 * Side Effects:
 *	As advertised.
 *------------------------------------------------------------------------*/

int
SRXAFSCB_ProbeUuid(struct rx_call *a_call, afsUUID * a_uuid)
{
    int code = 0;
    XSTATS_DECLS;

    RX_AFS_GLOCK();
    AFS_STATCNT(SRXAFSCB_Probe);

    XSTATS_START_CMTIME(AFS_STATS_CM_RPCIDX_PROBE);
    if (!afs_uuid_equal(a_uuid, &afs_cb_interface.uuid))
	code = 1;		/* failure */
    XSTATS_END_TIME;

    RX_AFS_GUNLOCK();

    return code;
}


/*------------------------------------------------------------------------
 * EXPORTED SRXAFSCB_GetServerPrefs
 *
 * Description:
 *	Routine to list server preferences used by this client.
 *
 * Arguments:
 *	a_call  : Ptr to Rx call on which this request came in.
 *	a_index : Input server index
 *	a_srvr_addr  : Output server address in host byte order
 *                     (0xffffffff on last server)
 *	a_srvr_rank  : Output server rank
 *
 * Returns:
 *	0 on success
 *
 * Environment:
 *	Nothing interesting.
 *
 * Side Effects:
 *	As advertised.
 *------------------------------------------------------------------------*/

int
SRXAFSCB_GetServerPrefs(struct rx_call *a_call, afs_int32 a_index,
			afs_int32 * a_srvr_addr, afs_int32 * a_srvr_rank)
{
    int i, j;
    struct srvAddr *sa;

    RX_AFS_GLOCK();
    AFS_STATCNT(SRXAFSCB_GetServerPrefs);

    ObtainReadLock(&afs_xserver);

    /* Search the hash table for the server with this index */
    *a_srvr_addr = 0xffffffff;
    *a_srvr_rank = 0xffffffff;
    for (i = 0, j = 0; j < NSERVERS && i <= a_index; j++) {
	for (sa = afs_srvAddrs[j]; sa && i <= a_index; sa = sa->next_bkt, i++) {
	    if (i == a_index) {
		*a_srvr_addr = ntohl(sa->sa_ip);
		*a_srvr_rank = sa->sa_iprank;
	    }
	}
    }

    ReleaseReadLock(&afs_xserver);

    RX_AFS_GUNLOCK();

    return 0;
}


/*------------------------------------------------------------------------
 * EXPORTED SRXAFSCB_GetCellServDB
 *
 * Description:
 *	Routine to list cells configured for this client
 *
 * Arguments:
 *	a_call  : Ptr to Rx call on which this request came in.
 *	a_index : Input cell index
 *	a_name  : Output cell name ("" on last cell)
 *	a_hosts : Output cell database servers in host byte order.
 *
 * Returns:
 *	0 on success
 *
 * Environment:
 *	Nothing interesting.
 *
 * Side Effects:
 *	As advertised.
 *------------------------------------------------------------------------*/

int
SRXAFSCB_GetCellServDB(struct rx_call *a_call, afs_int32 a_index,
		       char **a_name, serverList * a_hosts)
{
    afs_int32 i, j = 0;
    struct cell *tcell;
    char *t_name, *p_name = NULL;

    RX_AFS_GLOCK();
    AFS_STATCNT(SRXAFSCB_GetCellServDB);

    tcell = afs_GetCellByIndex(a_index, READ_LOCK);

    if (!tcell) {
	i = 0;
	a_hosts->serverList_val = 0;
	a_hosts->serverList_len = 0;
    } else {
	p_name = tcell->cellName;
	for (j = 0; j < AFSMAXCELLHOSTS && tcell->cellHosts[j]; j++);
	i = strlen(p_name);
	a_hosts->serverList_val = afs_osi_Alloc(j * sizeof(afs_int32));
	osi_Assert(a_hosts->serverList_val != NULL);
	a_hosts->serverList_len = j;
	for (j = 0; j < AFSMAXCELLHOSTS && tcell->cellHosts[j]; j++)
	    a_hosts->serverList_val[j] =
		ntohl(tcell->cellHosts[j]->addr->sa_ip);
	afs_PutCell(tcell, READ_LOCK);
    }

    t_name = afs_osi_Alloc(i + 1);
    if (t_name == NULL) {
	if (tcell != NULL)
	    afs_osi_Free(a_hosts->serverList_val, (j * sizeof(afs_int32)));
	RX_AFS_GUNLOCK();
	return ENOMEM;
    }

    t_name[i] = '\0';
    if (p_name)
	memcpy(t_name, p_name, i);

    RX_AFS_GUNLOCK();

    *a_name = t_name;
    return 0;
}


/*------------------------------------------------------------------------
 * EXPORTED SRXAFSCB_GetLocalCell
 *
 * Description:
 *	Routine to return name of client's local cell
 *
 * Arguments:
 *	a_call  : Ptr to Rx call on which this request came in.
 *	a_name  : Output cell name
 *
 * Returns:
 *	0 on success
 *
 * Environment:
 *	Nothing interesting.
 *
 * Side Effects:
 *	As advertised.
 *------------------------------------------------------------------------*/

int
SRXAFSCB_GetLocalCell(struct rx_call *a_call, char **a_name)
{
    int plen;
    struct cell *tcell;
    char *t_name, *p_name = NULL;

    RX_AFS_GLOCK();
    AFS_STATCNT(SRXAFSCB_GetLocalCell);

    /* Search the list for the primary cell. Cell number 1 is only
     * the primary cell is when no other cell is explicitly marked as
     * the primary cell.  */
    tcell = afs_GetPrimaryCell(READ_LOCK);
    if (tcell)
	p_name = tcell->cellName;
    if (p_name)
	plen = strlen(p_name);
    else
	plen = 0;
    t_name = afs_osi_Alloc(plen + 1);
    if (t_name == NULL) {
	if (tcell)
	    afs_PutCell(tcell, READ_LOCK);
	RX_AFS_GUNLOCK();
	return ENOMEM;
    }

    t_name[plen] = '\0';
    if (p_name)
	memcpy(t_name, p_name, plen);

    RX_AFS_GUNLOCK();

    *a_name = t_name;
    if (tcell)
	afs_PutCell(tcell, READ_LOCK);
    return 0;
}


/*
 * afs_MarshallCacheConfig - marshall client cache configuration
 *
 * PARAMETERS
 *
 * IN callerVersion - the rpc stat version of the caller.
 *
 * IN config - client cache configuration.
 *
 * OUT ptr - buffer where configuration is marshalled.
 *
 * RETURN CODES
 *
 * Returns void.
 */
static void
afs_MarshallCacheConfig(afs_uint32 callerVersion, cm_initparams_v1 * config,
			afs_uint32 * ptr)
{
    AFS_STATCNT(afs_MarshallCacheConfig);
    /*
     * We currently only support version 1.
     */
    *(ptr++) = config->nChunkFiles;
    *(ptr++) = config->nStatCaches;
    *(ptr++) = config->nDataCaches;
    *(ptr++) = config->nVolumeCaches;
    *(ptr++) = config->firstChunkSize;
    *(ptr++) = config->otherChunkSize;
    *(ptr++) = config->cacheSize;
    *(ptr++) = config->setTime;
    *(ptr++) = config->memCache;
}


/*------------------------------------------------------------------------
 * EXPORTED SRXAFSCB_GetCacheConfig
 *
 * Description:
 *	Routine to return parameters used to initialize client cache.
 *      Client may request any format version. Server may not return
 *      format version greater than version requested by client.
 *
 * Arguments:
 *	a_call:        Ptr to Rx call on which this request came in.
 *	callerVersion: Data format version desired by the client.
 *	serverVersion: Data format version of output data.
 *      configCount:   Number bytes allocated for output data.
 *      config:        Client cache configuration.
 *
 * Returns:
 *	0 on success
 *
 * Environment:
 *	Nothing interesting.
 *
 * Side Effects:
 *	As advertised.
 *------------------------------------------------------------------------*/

int
SRXAFSCB_GetCacheConfig(struct rx_call *a_call, afs_uint32 callerVersion,
			afs_uint32 * serverVersion, afs_uint32 * configCount,
			cacheConfig * config)
{
    afs_uint32 *t_config;
    size_t allocsize;
    cm_initparams_v1 cm_config;

    RX_AFS_GLOCK();
    AFS_STATCNT(SRXAFSCB_GetCacheConfig);

    /*
     * Currently only support version 1
     */
    allocsize = sizeof(cm_initparams_v1);
    t_config = afs_osi_Alloc(allocsize);
    if (t_config == NULL) {
	RX_AFS_GUNLOCK();
	return ENOMEM;
    }

    cm_config.nChunkFiles = cm_initParams.cmi_nChunkFiles;
    cm_config.nStatCaches = cm_initParams.cmi_nStatCaches;
    cm_config.nDataCaches = cm_initParams.cmi_nDataCaches;
    cm_config.nVolumeCaches = cm_initParams.cmi_nVolumeCaches;
    cm_config.firstChunkSize = cm_initParams.cmi_firstChunkSize;
    cm_config.otherChunkSize = cm_initParams.cmi_otherChunkSize;
    cm_config.cacheSize = cm_initParams.cmi_cacheSize;
    cm_config.setTime = cm_initParams.cmi_setTime;
    cm_config.memCache = cm_initParams.cmi_memCache;

    afs_MarshallCacheConfig(callerVersion, &cm_config, t_config);

    *serverVersion = AFS_CLIENT_RETRIEVAL_FIRST_EDITION;
    *configCount = allocsize;
    config->cacheConfig_val = t_config;
    config->cacheConfig_len = allocsize / sizeof(afs_uint32);

    RX_AFS_GUNLOCK();

    return 0;
}

/*------------------------------------------------------------------------
 * EXPORTED SRXAFSCB_GetCellByNum
 *
 * Description:
 *	Routine to get information about a cell specified by its
 *	cell number (returned by GetCE/GetCE64).
 *
 * Arguments:
 *	a_call    : Ptr to Rx call on which this request came in.
 *	a_cellnum : Input cell number
 *	a_name    : Output cell name (one zero byte when no such cell).
 *	a_hosts   : Output cell database servers in host byte order.
 *
 * Returns:
 *	0 on success
 *
 * Environment:
 *	Nothing interesting.
 *
 * Side Effects:
 *	As advertised.
 *------------------------------------------------------------------------*/

int
SRXAFSCB_GetCellByNum(struct rx_call *a_call, afs_int32 a_cellnum,
		      char **a_name, serverList * a_hosts)
{
    afs_int32 i, sn;
    struct cell *tcell;

    RX_AFS_GLOCK();
    AFS_STATCNT(SRXAFSCB_GetCellByNum);

    a_hosts->serverList_val = 0;
    a_hosts->serverList_len = 0;

    tcell = afs_GetCellStale(a_cellnum, READ_LOCK);
    if (!tcell) {
	*a_name = afs_strdup("");
	RX_AFS_GUNLOCK();
	return 0;
    }

    ObtainReadLock(&tcell->lock);
    *a_name = afs_strdup(tcell->cellName);

    for (sn = 0; sn < AFSMAXCELLHOSTS && tcell->cellHosts[sn]; sn++);
    a_hosts->serverList_len = sn;
    a_hosts->serverList_val = afs_osi_Alloc(sn * sizeof(afs_int32));
    osi_Assert(a_hosts->serverList_val != NULL);

    for (i = 0; i < sn; i++)
	a_hosts->serverList_val[i] = ntohl(tcell->cellHosts[i]->addr->sa_ip);
    ReleaseReadLock(&tcell->lock);
    afs_PutCell(tcell, READ_LOCK);

    RX_AFS_GUNLOCK();
    return 0;
}

int
SRXAFSCB_TellMeAboutYourself(struct rx_call *a_call,
			     struct interfaceAddr *addr,
			     Capabilities * capabilities)
{
    int i;
    int code = 0;
    afs_uint32 *dataBuffP;
    afs_int32 dataBytes;

    RX_AFS_GLOCK();

    AFS_STATCNT(SRXAFSCB_WhoAreYou);

    ObtainReadLock(&afs_xinterface);

    /* return all network interface addresses */
    memset(addr, 0, sizeof(*addr));
    addr->numberOfInterfaces = afs_cb_interface.numberOfInterfaces;
    addr->uuid = afs_cb_interface.uuid;
    for (i = 0; i < afs_cb_interface.numberOfInterfaces; i++) {
	addr->addr_in[i] = ntohl(afs_cb_interface.addr_in[i]);
	addr->subnetmask[i] = ntohl(afs_cb_interface.subnetmask[i]);
	addr->mtu[i] = ntohl(afs_cb_interface.mtu[i]);
    }

    ReleaseReadLock(&afs_xinterface);

    RX_AFS_GUNLOCK();

    dataBytes = 1 * sizeof(afs_uint32);
    dataBuffP = afs_osi_Alloc(dataBytes);
    osi_Assert(dataBuffP != NULL);
    dataBuffP[0] = CLIENT_CAPABILITY_ERRORTRANS;
    capabilities->Capabilities_len = dataBytes / sizeof(afs_uint32);
    capabilities->Capabilities_val = dataBuffP;

    return code;
}
