/*
 * nasd_edrfs_client_switch.c
 *
 * RPC wrappers, dispatch to correct binding type
 *
 * Authors: Jim Zelenka, Nat Lanza
 */
/*
 * Copyright (c) of Carnegie Mellon University, 1997,1998,1999.
 *
 * Permission to reproduce, use, and prepare derivative works of
 * this software for internal use is granted provided the copyright
 * and "No Warranty" statements are included with all reproductions
 * and derivative works. This software may also be redistributed
 * without charge provided that the copyright and "No Warranty"
 * statements are included in all redistributions.
 *
 * NO WARRANTY. THIS SOFTWARE IS FURNISHED ON AN "AS IS" BASIS.
 * CARNEGIE MELLON UNIVERSITY MAKES NO WARRANTIES OF ANY KIND, EITHER
 * EXPRESSED OR IMPLIED AS TO THE MATTER INCLUDING, BUT NOT LIMITED
 * TO: WARRANTY OF FITNESS FOR PURPOSE OR MERCHANTABILITY, EXCLUSIVITY
 * OF RESULTS OR RESULTS OBTAINED FROM USE OF THIS SOFTWARE. CARNEGIE
 * MELLON UNIVERSITY DOES NOT MAKE ANY WARRANTY OF ANY KIND WITH RESPECT
 * TO FREEDOM FROM PATENT, TRADEMARK, OR COPYRIGHT INFRINGEMENT.
 */


#include <nasd/nasd_options.h>
#include <nasd/nasd_mem.h>
#include <nasd/nasd_edrfs_client.h>
#include <nasd/nasd_edrfs_client_kpdev.h>

#include <nasd/nasd_pipe.h>
#include <nasd/nasd_pdev.h>

nasd_edrfscli_rpcmod_tab_t *nasd_edrfscli_rpcmod_tabs = NULL;
int nasd_edrfscli_rpcmod_max_specsize = 1;
struct nasd_edrfs_handle_s nasd_edrfscli_handles;

nasd_edrfs_default_bindings_t *nasd_edrfs_default_bindings = NULL;

NASD_DECLARE_ONCE(nasd_edrfscli_init_once)
NASD_DECLARE_MUTEX(nasd_edrfscli_use_counter_lock)
int nasd_edrfscli_use_counter = 0;
nasd_shutdown_list_t *nasd_edrfscli_shutdown_list = NULL;

#define INIT_COUNTER_LOCK_ONCE() \
  nasd_once(&nasd_edrfscli_init_once, nasd_edrfscli_sys_init)
#define LOCK_COUNTER_LOCK()   NASD_LOCK_MUTEX(nasd_edrfscli_use_counter_lock)
#define UNLOCK_COUNTER_LOCK() NASD_UNLOCK_MUTEX(nasd_edrfscli_use_counter_lock)

/*
 * nasd_edrfscli_real_init
 *
 * Called when the system comes into use after not being
 * used (start-of-day call to nasd_edrfscli_init(), or first
 * such call after the last call to nasd_edrfscli_shutdown()
 * which actually deactivated the system).
 */
nasd_status_t
nasd_edrfscli_real_init()
{
  nasd_status_t rc;

  /* already did threads_init() */ 

  rc = nasd_mem_init();
  if (rc) {
    nasd_threads_shutdown();
    return(rc);
  }

  rc = nasd_shutdown_sys_init();
  if (rc) {
    nasd_mem_shutdown();
    nasd_threads_shutdown();
    return(rc);
  }

  rc = nasd_shutdown_list_init(&nasd_edrfscli_shutdown_list);
  if (rc) {
    nasd_shutdown_cleanup();
    nasd_mem_shutdown();
    nasd_threads_shutdown();
    return(rc);
  }

  nasd_edrfscli_handles.prev = &nasd_edrfscli_handles;
  nasd_edrfscli_handles.next = &nasd_edrfscli_handles;

  rc = nasd_edrfscli_mods_init(nasd_edrfscli_shutdown_list);
  if (rc)
    goto bad;

  return(NASD_SUCCESS);

bad:
  rc = nasd_shutdown_list_shutdown(nasd_edrfscli_shutdown_list,
    NASD_SHUTDOWN_ANNOUNCE_NONE);
  if (rc) {
    NASD_PANIC();
  }
  nasd_shutdown_cleanup();
  nasd_mem_shutdown();
  nasd_threads_shutdown();
  return(rc);
}

/*
 * nasd_edrfscli_real_shutdown
 *
 * Called when last user of the edrfscli system calls nasd_edrfscli_shutdown().
 * Clean up and deallocate resources.
 */
void
nasd_edrfscli_real_shutdown()
{
  nasd_status_t rc;

  rc = nasd_shutdown_list_shutdown(nasd_edrfscli_shutdown_list,
    NASD_SHUTDOWN_ANNOUNCE_NONE);
  if (rc) {
    NASD_PANIC();
  }
  nasd_edrfscli_shutdown_list = NULL;
  nasd_shutdown_cleanup();
  nasd_mem_shutdown();
  nasd_threads_shutdown();
}

/*
 * nasd_edrfscli_sys_init
 *
 * Executed exactly once, the first time nasd_edrfscli_init() is
 * called. Initialize counter tracking number of times system
 * is initted.
 */
void
nasd_edrfscli_sys_init()
{
  nasd_status_t rc;

  rc = nasd_mutex_init(&nasd_edrfscli_use_counter_lock);
  if (rc) {
    NASD_PANIC();
  }
  nasd_edrfscli_use_counter = 0;

  LOCK_COUNTER_LOCK();
  nasd_edrfs_default_bindings = NULL;
  nasd_edrfscli_mods_load();
  UNLOCK_COUNTER_LOCK();
}

/*
 * nasd_edrfscli_init
 *
 * Keep a counter of the number of times we're initted and
 * shutdown. When the last shutdown arrives, really deallocate.
 * This lets multiple subsystems use us without knowing about
 * one another.
 */
nasd_status_t
nasd_edrfscli_init()
{
  nasd_status_t rc;

  rc = nasd_threads_init();
  if (rc)
    return(rc);

  INIT_COUNTER_LOCK_ONCE();

  LOCK_COUNTER_LOCK();
  nasd_edrfscli_use_counter++;
  if (nasd_edrfscli_use_counter == 1) {
    rc = nasd_edrfscli_real_init();
    if (rc) {
      nasd_edrfscli_use_counter = 0;
    }
  }
  else {
    /* get rid of extra threads init above */
    nasd_threads_shutdown();
    rc = NASD_SUCCESS;
  }
  UNLOCK_COUNTER_LOCK();

  return(rc);
}

/*
 * nasd_edrfscli_shutdown
 *
 * Previous caller of nasd_edrfscli_init() not using the edrfscli
 * subsystem any more. Deallocate and cleanup iff necessary.
 */
void
nasd_edrfscli_shutdown()
{
  NASD_ASSERT(nasd_edrfscli_use_counter != 0);

  LOCK_COUNTER_LOCK();
  nasd_edrfscli_use_counter--;
  if (nasd_edrfscli_use_counter == 0) {
    nasd_edrfscli_real_shutdown();
  }
  UNLOCK_COUNTER_LOCK();
}

void
nasd_edrfscli_mod_register(
  nasd_edrfscli_rpcmod_tab_t  *tab,
  int                          counter_lock_held)
{
  nasd_edrfscli_rpcmod_tab_t *r;

  if (counter_lock_held == 0) {
    INIT_COUNTER_LOCK_ONCE();
    LOCK_COUNTER_LOCK();
  }

  for(r=nasd_edrfscli_rpcmod_tabs;r;r=r->next) {
    if (r == tab) {
      nasd_printf("NASD EDRFS CLIENT: "
        "module 0x%lx (%s) already registered!\n",
        (unsigned long)tab, tab->name);
      if (counter_lock_held == 0) {
        UNLOCK_COUNTER_LOCK();
      }
      return;
    }
    if (!strcmp(r->name, tab->name)) {
      nasd_printf("NASD EDRFS CLIENT: "
        "new module 0x%lx has same name as module "
        "0x%lx (%s), rejecting registration\n",
        (unsigned long)tab, (unsigned long)r, r->name);
      if (counter_lock_held == 0) {
        UNLOCK_COUNTER_LOCK();
      }
      return;
    }
    if (r->binding_type == tab->binding_type) {
      nasd_printf("NASD EDRFS CLIENT: "
        "new module 0x%lx (%s) has same binding type"
        " as module 0x%lx (%s) (type %d), rejecting registration\n",
        (unsigned long)tab, tab->name,
        (unsigned long)r, r->name, r->binding_type);
      if (counter_lock_held == 0) {
        UNLOCK_COUNTER_LOCK();
      }
      return;
    }
  }

  tab->next = nasd_edrfscli_rpcmod_tabs;
  nasd_edrfscli_rpcmod_tabs = tab;

  if (tab->spec_size > nasd_edrfscli_rpcmod_max_specsize)
    nasd_edrfscli_rpcmod_max_specsize = tab->spec_size;

  if (nasd_edrfscli_use_counter) {
    if (r->init_status) {
#if NASD_EDRFSCLI_SWITCH_STANDALONE == 0
      r->init_status = r->init(nasd_edrfscli_shutdown_list);
#else /* NASD_EDRFSCLI_SWITCH_STANDALONE == 0 */
      r->init_status = r->init(NULL);
#endif /* NASD_EDRFSCLI_SWITCH_STANDALONE == 0 */
    }
    else {
      r->init_status = NASD_SUCCESS;
    }
  }

  if (counter_lock_held == 0) {
    UNLOCK_COUNTER_LOCK();
  }
}

/*
 * XXX deal with outstanding handles using the
 * mod being unloaded
 */
void
nasd_edrfscli_mod_unregister(
  nasd_edrfscli_rpcmod_tab_t  *tab,
  int                          counter_lock_held)
{
  nasd_edrfscli_rpcmod_tab_t *r, *p;

  if (counter_lock_held == 0) {
    INIT_COUNTER_LOCK_ONCE();
    LOCK_COUNTER_LOCK();
  }

  for(p=NULL,r=nasd_edrfscli_rpcmod_tabs;r;p=r,r=r->next) {
    if (r == tab) {
      if (p) {
        p->next = r->next;
      }
      else {
        nasd_edrfscli_rpcmod_tabs = r->next;
      }
      r->next = NULL;
      break;
    }
  }

  if (r == NULL) {
    nasd_printf("NASD EDRFS CLIENT: failed unregistry for module 0x%lx (%s),"
      " could not locate it!\n", (unsigned long)tab, tab->name);
  }

  if (counter_lock_held == 0) {
    UNLOCK_COUNTER_LOCK();
  }
}

/*
 * Caller holds counter lock
 */
nasd_status_t
nasd_edrfscli_mods_init(
  nasd_shutdown_list_t  *sl)
{
  nasd_edrfscli_rpcmod_tab_t *r;
  nasd_status_t rc;

  rc = NASD_SUCCESS;
  for(r=nasd_edrfscli_rpcmod_tabs;r;r=r->next) {
    if (r->init) {
      rc = r->init_status = r->init(sl);
      if (rc) {
        break;
      }
    }
    else {
      rc = r->init_status = NASD_SUCCESS;
    }
  }

  return(rc);
}

static void
nasd_edrfscli_handle_free(
  nasd_edrfs_handle_t  handle)
{
  NASD_Free(handle->rpc_specific_handle, handle->rpc_specific_handle_size);
  NASD_Free(handle, sizeof(struct nasd_edrfs_handle_s));
}

nasd_status_t
nasd_bind_to_edrfs_server(
  char                 *server_name,
  char                 *portnum,
  int                   in_binding_type,
  void                 *binding_param,
  int                   binding_param_len,
  nasd_edrfs_handle_t  *handlep)
{
  nasd_edrfscli_rpcmod_tab_t *r;
  nasd_edrfs_handle_t handle;
  int binding_type, nd;
  nasd_status_t rc;

  INIT_COUNTER_LOCK_ONCE();
  nd = 0;

  if ((in_binding_type == NASD_BIND_DEFAULT) && binding_param_len) {
    return(NASD_BAD_BINDING_PARAM_LEN);
  }

  LOCK_COUNTER_LOCK();
  if (nasd_edrfscli_use_counter == 0)
    return(NASD_SYS_NOT_INITIALIZED);
  nasd_edrfscli_use_counter++;
  UNLOCK_COUNTER_LOCK();

#ifndef KERNEL
{
  extern char *getenv();
  char *env;

  /* set this to something like "/dev/nasdkp0" */
  env = getenv("NASD_JIMZ_MONKEY_COLOCATE");
  /*
   * If we've already got a nasd_edrfs_param_kpdev_t
   * argument, we use it anyway.
   */
  if (env) {
    if (in_binding_type != NASD_BIND_COLOCATE) {
      if (in_binding_type != NASD_BIND_KPDEV)
      {
        static nasd_edrfs_param_kpdev_t kpdev_args;
        strcpy(kpdev_args.devname, env);
        binding_param = &kpdev_args;
        binding_param_len = sizeof(kpdev_args);
      }
      in_binding_type = NASD_BIND_COLOCATE;
    }
  }
}
#endif /* !KERNEL */

  if (in_binding_type == NASD_BIND_DEFAULT) {
    if ((nasd_edrfs_default_bindings == NULL) ||
      (nasd_edrfs_default_bindings->nbindings == 0))
    {
      nasd_edrfscli_shutdown();
      return(NASD_NO_DEFAULT_BINDING);
    }
    nd = 0;
  }

  *handlep = NULL;

  NASD_Malloc(handle, sizeof(struct nasd_edrfs_handle_s),
    (nasd_edrfs_handle_t));
  if (handle == NULL) {
    nasd_edrfscli_shutdown();
    return(NASD_NO_MEM);
  }
  bzero((char *)handle, sizeof(struct nasd_edrfs_handle_s));

  rc = NASD_SUCCESS;

  LOCK_COUNTER_LOCK();

  NASD_Malloc(handle->rpc_specific_handle, nasd_edrfscli_rpcmod_max_specsize,
    (void *));
  if (handle->rpc_specific_handle == NULL) {
    NASD_Free(handle, sizeof(struct nasd_edrfs_handle_s));
    UNLOCK_COUNTER_LOCK();
    nasd_edrfscli_shutdown();
    return(NASD_NO_MEM);
  }
  handle->rpc_specific_handle_size = nasd_edrfscli_rpcmod_max_specsize;
  bzero(handle->rpc_specific_handle, handle->rpc_specific_handle_size);

begin_binding_try:

  if (in_binding_type == NASD_BIND_DEFAULT) {
    if (nd >= nasd_drive_default_bindings->nbindings) {
      /* no more defaults to try */
      nasd_edrfscli_handle_free(handle);
      nasd_edrfscli_shutdown();
      /*
       * Return error from last binding try- more informative than
       * NASD_FAIL or somesuch, at least.
       */
      return(rc);
    }
    binding_type = nasd_drive_default_bindings->bindings[nd];
  }
  else {
    binding_type = in_binding_type;
  }

  for(r=nasd_edrfscli_rpcmod_tabs;r;r=r->next) {
    if (r->binding_type == binding_type)
      break;
  }

  handle->rpc_tab = r;
  handle->type = binding_type;

  if (handle->rpc_tab) {
    if (handle->rpc_tab->init_status != NASD_SUCCESS) {
      rc = NASD_RPCMOD_INIT_FAIL;
    }
    else {
      rc = handle->rpc_tab->bind(handle, server_name, portnum, binding_type,
        binding_param, binding_param_len);
    }
    if (rc == NASD_SUCCESS) {
      handle->next = nasd_edrfscli_handles.next;
      handle->prev = &nasd_edrfscli_handles;
      handle->prev->next = handle;
      handle->next->prev = handle;
    }
  }
  else {
    rc = NASD_BAD_HANDLE_TYPE;
  }

  if (rc && (binding_type == NASD_BIND_DEFAULT)) {
    /* retry next default type */
    nd++;
    goto begin_binding_try;
  }

  UNLOCK_COUNTER_LOCK();

  if (rc) {
    nasd_edrfscli_handle_free(handle);
    nasd_edrfscli_shutdown();
    return(rc);
  }

  *handlep = handle;
  return(NASD_SUCCESS);
}

nasd_status_t
nasd_unbind_edrfs_server(
  nasd_edrfs_handle_t  *handlep)
{
  nasd_edrfs_handle_t handle;
  nasd_status_t rc;

  INIT_COUNTER_LOCK_ONCE();

  handle = *handlep;

  *handlep = NULL;

  if (handle == NULL) {
    return(NASD_BAD_HANDLE);
  }

  if (handle->rpc_tab) {
    rc = handle->rpc_tab->unbind(handle);
  }
  else {
    rc = NASD_SUCCESS;
  }

  LOCK_COUNTER_LOCK();
  handle->prev->next = handle->next;
  handle->next->prev = handle->prev;
  UNLOCK_COUNTER_LOCK();

  nasd_edrfscli_handle_free(handle);

  nasd_edrfscli_shutdown();

  return(rc);
}

/*
 * Operation wrappers
 */

void
nasd_edrfscli_null(
  nasd_edrfs_handle_t     handle,
  nasd_status_t          *nasd_status,
  nasd_rpc_status_t      *op_status)
{
  nasd_res_t res;

  if (handle == NULL) {
    *nasd_status = NASD_BAD_HANDLE;
    *op_status = 0;
    return;
  }
  if (handle->rpc_tab == NULL) {
    *nasd_status = NASD_BINDING_INVALIDATED;
    *op_status = 0;
    return;
  }
  if (handle->rpc_tab->null == NULL) {
    *nasd_status = NASD_BINDING_NOIMPL;
    *op_status = 0;
    return;
  }

  handle->rpc_tab->null(handle, &res, op_status);
  *nasd_status = res.nasd_status;
}

void
nasd_edrfscli_mount(
  nasd_edrfs_handle_t       handle,
  nasd_edrfs_mount_args_t  *args,
  nasd_edrfs_mount_res_t   *res,
  nasd_rpc_status_t        *op_status)
{
  if (handle == NULL) {
    res->nasd_status = NASD_BAD_HANDLE;
    *op_status = 0;
    return;
  }
  if (handle->rpc_tab == NULL) {
    res->nasd_status = NASD_BINDING_INVALIDATED;
    *op_status = 0;
    return;
  }
  if (handle->rpc_tab->mount == NULL) {
    res->nasd_status = NASD_BINDING_NOIMPL;
    *op_status = 0;
    return;
  }

  handle->rpc_tab->mount(handle, args, res, op_status);
}

void
nasd_edrfscli_fsstat(
  nasd_edrfs_handle_t        handle,
  nasd_edrfs_fsstat_args_t  *args,
  nasd_edrfs_fsstat_res_t   *res,
  nasd_rpc_status_t         *op_status)
{
  if (handle == NULL) {
    res->nasd_status = NASD_BAD_HANDLE;
    *op_status = 0;
    return;
  }
  if (handle->rpc_tab == NULL) {
    res->nasd_status = NASD_BINDING_INVALIDATED;
    *op_status = 0;
    return;
  }
  if (handle->rpc_tab->fsstat == NULL) {
    res->nasd_status = NASD_BINDING_NOIMPL;
    *op_status = 0;
    return;
  }

  handle->rpc_tab->fsstat(handle, args, res, op_status);
}

void
nasd_edrfscli_fsinfo(
  nasd_edrfs_handle_t        handle,
  nasd_edrfs_fsinfo_args_t  *args,
  nasd_edrfs_fsinfo_res_t   *res,
  nasd_rpc_status_t         *op_status)
{
  if (handle == NULL) {
    res->nasd_status = NASD_BAD_HANDLE;
    *op_status = 0;
    return;
  }
  if (handle->rpc_tab == NULL) {
    res->nasd_status = NASD_BINDING_INVALIDATED;
    *op_status = 0;
    return;
  }
  if (handle->rpc_tab->fsinfo == NULL) {
    res->nasd_status = NASD_BINDING_NOIMPL;
    *op_status = 0;
    return;
  }

  handle->rpc_tab->fsinfo(handle, args, res, op_status);
}

void
nasd_edrfscli_lookup_otw_provided(
  nasd_edrfs_handle_t        handle,
  nasd_edrfs_lookup_args_t  *args,
  nasd_edrfs_lookup_res_t   *res,
  nasd_otw_base_t           *args_otw_p,
  nasd_otw_base_t           *res_otw_p,
  nasd_rpc_status_t         *op_status)
{
  if (handle == NULL) {
    res->nasd_status = NASD_BAD_HANDLE;
    *op_status = 0;
    return;
  }
  if (handle->rpc_tab == NULL) {
    res->nasd_status = NASD_BINDING_INVALIDATED;
    *op_status = 0;
    return;
  }
  if (handle->rpc_tab->lookup_otw_provided == NULL) {
    res->nasd_status = NASD_BINDING_NOIMPL;
    *op_status = 0;
    return;
  }

  handle->rpc_tab->lookup_otw_provided(handle, args, res,
    args_otw_p, res_otw_p, op_status);
}

void
nasd_edrfscli_lookup(
  nasd_edrfs_handle_t        handle,
  nasd_edrfs_lookup_args_t  *args,
  nasd_edrfs_lookup_res_t   *res,
  nasd_rpc_status_t         *op_status)
{
  if (handle == NULL) {
    res->nasd_status = NASD_BAD_HANDLE;
    *op_status = 0;
    return;
  }
  if (handle->rpc_tab == NULL) {
    res->nasd_status = NASD_BINDING_INVALIDATED;
    *op_status = 0;
    return;
  }
  if (handle->rpc_tab->lookup == NULL) {
    res->nasd_status = NASD_BINDING_NOIMPL;
    *op_status = 0;
    return;
  }

  handle->rpc_tab->lookup(handle, args, res, op_status);
}

void
nasd_edrfscli_readdir(
  nasd_edrfs_handle_t         handle,
  nasd_edrfs_readdir_args_t  *args,
  nasd_edrfs_dirent_t        *out_entries,
  nasd_edrfs_readdir_res_t   *res,
  nasd_rpc_status_t          *op_status)
{
  if (handle == NULL) {
    res->nasd_status = NASD_BAD_HANDLE;
    *op_status = 0;
    return;
  }
  if (handle->rpc_tab == NULL) {
    res->nasd_status = NASD_BINDING_INVALIDATED;
    *op_status = 0;
    return;
  }
  if (handle->rpc_tab->readdir == NULL) {
    res->nasd_status = NASD_BINDING_NOIMPL;
    *op_status = 0;
    return;
  }

  handle->rpc_tab->readdir(handle, args, out_entries, res, op_status);
}

void
nasd_edrfscli_access(
  nasd_edrfs_handle_t        handle,
  nasd_edrfs_access_args_t  *args,
  nasd_edrfs_access_res_t   *res,
  nasd_rpc_status_t         *op_status)
{
  if (handle == NULL) {
    res->nasd_status = NASD_BAD_HANDLE;
    *op_status = 0;
    return;
  }
  if (handle->rpc_tab == NULL) {
    res->nasd_status = NASD_BINDING_INVALIDATED;
    *op_status = 0;
    return;
  }
  if (handle->rpc_tab->access == NULL) {
    res->nasd_status = NASD_BINDING_NOIMPL;
    *op_status = 0;
    return;
  }

  handle->rpc_tab->access(handle, args, res, op_status);
}

void
nasd_edrfscli_setattr(
  nasd_edrfs_handle_t         handle,
  nasd_edrfs_setattr_args_t  *args,
  nasd_edrfs_setattr_res_t   *res,
  nasd_rpc_status_t          *op_status)
{
  if (handle == NULL) {
    res->nasd_status = NASD_BAD_HANDLE;
    *op_status = 0;
    return;
  }
  if (handle->rpc_tab == NULL) {
    res->nasd_status = NASD_BINDING_INVALIDATED;
    *op_status = 0;
    return;
  }
  if (handle->rpc_tab->setattr == NULL) {
    res->nasd_status = NASD_BINDING_NOIMPL;
    *op_status = 0;
    return;
  }

  handle->rpc_tab->setattr(handle, args, res, op_status);
}

void
nasd_edrfscli_create_otw_provided(
  nasd_edrfs_handle_t        handle,
  nasd_edrfs_create_args_t  *args,
  nasd_edrfs_create_res_t   *res,
  nasd_otw_base_t           *args_otw_p,
  nasd_otw_base_t           *res_otw_p,
  nasd_rpc_status_t         *op_status)
{
  if (handle == NULL) {
    res->nasd_status = NASD_BAD_HANDLE;
    *op_status = 0;
    return;
  }
  if (handle->rpc_tab == NULL) {
    res->nasd_status = NASD_BINDING_INVALIDATED;
    *op_status = 0;
    return;
  }
  if (handle->rpc_tab->create_otw_provided == NULL) {
    res->nasd_status = NASD_BINDING_NOIMPL;
    *op_status = 0;
    return;
  }

  handle->rpc_tab->create_otw_provided(handle, args, res,
    args_otw_p, res_otw_p, op_status);
}

void
nasd_edrfscli_create(
  nasd_edrfs_handle_t        handle,
  nasd_edrfs_create_args_t  *args,
  nasd_edrfs_create_res_t   *res,
  nasd_rpc_status_t         *op_status)
{
  if (handle == NULL) {
    res->nasd_status = NASD_BAD_HANDLE;
    *op_status = 0;
    return;
  }
  if (handle->rpc_tab == NULL) {
    res->nasd_status = NASD_BINDING_INVALIDATED;
    *op_status = 0;
    return;
  }
  if (handle->rpc_tab->create == NULL) {
    res->nasd_status = NASD_BINDING_NOIMPL;
    *op_status = 0;
    return;
  }

  handle->rpc_tab->create(handle, args, res, op_status);
}

void
nasd_edrfscli_symlink(
  nasd_edrfs_handle_t         handle,
  nasd_edrfs_symlink_args_t  *args,
  nasd_edrfs_symlink_res_t   *res,
  nasd_rpc_status_t          *op_status)
{
  if (handle == NULL) {
    res->nasd_status = NASD_BAD_HANDLE;
    *op_status = 0;
    return;
  }
  if (handle->rpc_tab == NULL) {
    res->nasd_status = NASD_BINDING_INVALIDATED;
    *op_status = 0;
    return;
  }
  if (handle->rpc_tab->symlink == NULL) {
    res->nasd_status = NASD_BINDING_NOIMPL;
    *op_status = 0;
    return;
  }

  handle->rpc_tab->symlink(handle, args, res, op_status);
}

void
nasd_edrfscli_remove(
  nasd_edrfs_handle_t        handle,
  nasd_edrfs_remove_args_t  *args,
  nasd_edrfs_remove_res_t   *res,
  nasd_rpc_status_t         *op_status)
{
  if (handle == NULL) {
    res->nasd_status = NASD_BAD_HANDLE;
    *op_status = 0;
    return;
  }
  if (handle->rpc_tab == NULL) {
    res->nasd_status = NASD_BINDING_INVALIDATED;
    *op_status = 0;
    return;
  }
  if (handle->rpc_tab->remove == NULL) {
    res->nasd_status = NASD_BINDING_NOIMPL;
    *op_status = 0;
    return;
  }

  handle->rpc_tab->remove(handle, args, res, op_status);
}

void
nasd_edrfscli_mkdir(
  nasd_edrfs_handle_t       handle,
  nasd_edrfs_mkdir_args_t  *args,
  nasd_edrfs_mkdir_res_t   *res,
  nasd_rpc_status_t        *op_status)
{
  if (handle == NULL) {
    res->nasd_status = NASD_BAD_HANDLE;
    *op_status = 0;
    return;
  }
  if (handle->rpc_tab == NULL) {
    res->nasd_status = NASD_BINDING_INVALIDATED;
    *op_status = 0;
    return;
  }
  if (handle->rpc_tab->mkdir == NULL) {
    res->nasd_status = NASD_BINDING_NOIMPL;
    *op_status = 0;
    return;
  }

  handle->rpc_tab->mkdir(handle, args, res, op_status);
}

void
nasd_edrfscli_rmdir(
  nasd_edrfs_handle_t       handle,
  nasd_edrfs_rmdir_args_t  *args,
  nasd_edrfs_rmdir_res_t   *res,
  nasd_rpc_status_t        *op_status)
{
  if (handle == NULL) {
    res->nasd_status = NASD_BAD_HANDLE;
    *op_status = 0;
    return;
  }
  if (handle->rpc_tab == NULL) {
    res->nasd_status = NASD_BINDING_INVALIDATED;
    *op_status = 0;
    return;
  }
  if (handle->rpc_tab->rmdir == NULL) {
    res->nasd_status = NASD_BINDING_NOIMPL;
    *op_status = 0;
    return;
  }

  handle->rpc_tab->rmdir(handle, args, res, op_status);
}

void
nasd_edrfscli_newcookie(
  nasd_edrfs_handle_t           handle,
  nasd_edrfs_newcookie_args_t  *args,
  nasd_edrfs_newcookie_res_t   *res,
  nasd_rpc_status_t            *op_status)
{
  if (handle == NULL) {
    res->nasd_status = NASD_BAD_HANDLE;
    *op_status = 0;
    return;
  }
  if (handle->rpc_tab == NULL) {
    res->nasd_status = NASD_BINDING_INVALIDATED;
    *op_status = 0;
    return;
  }
  if (handle->rpc_tab->newcookie == NULL) {
    res->nasd_status = NASD_BINDING_NOIMPL;
    *op_status = 0;
    return;
  }

  handle->rpc_tab->newcookie(handle, args, res, op_status);
}

void
nasd_edrfscli_rename(
  nasd_edrfs_handle_t        handle,
  nasd_edrfs_rename_args_t  *args,
  nasd_edrfs_rename_res_t   *res,
  nasd_rpc_status_t         *op_status)
{
  if (handle == NULL) {
    res->nasd_status = NASD_BAD_HANDLE;
    *op_status = 0;
    return;
  }
  if (handle->rpc_tab == NULL) {
    res->nasd_status = NASD_BINDING_INVALIDATED;
    *op_status = 0;
    return;
  }
  if (handle->rpc_tab->rename == NULL) {
    res->nasd_status = NASD_BINDING_NOIMPL;
    *op_status = 0;
    return;
  }

  handle->rpc_tab->rename(handle, args, res, op_status);
}

void
nasd_edrfscli_getstats(
  nasd_edrfs_handle_t          handle,
  nasd_edrfs_getstats_res_t   *res,
  nasd_rpc_status_t           *op_status)
{
  if (handle == NULL) {
    res->nasd_status = NASD_BAD_HANDLE;
    *op_status = 0;
    return;
  }
  if (handle->rpc_tab == NULL) {
    res->nasd_status = NASD_BINDING_INVALIDATED;
    *op_status = 0;
    return;
  }
  if (handle->rpc_tab->getstats == NULL) {
    res->nasd_status = NASD_BINDING_NOIMPL;
    *op_status = 0;
    return;
  }

  handle->rpc_tab->getstats(handle, res, op_status);
}

void
nasd_edrfscli_resetstats(
  nasd_edrfs_handle_t   handle,
  nasd_res_t           *res,
  nasd_rpc_status_t    *op_status)
{
  if (handle == NULL) {
    res->nasd_status = NASD_BAD_HANDLE;
    *op_status = 0;
    return;
  }
  if (handle->rpc_tab == NULL) {
    res->nasd_status = NASD_BINDING_INVALIDATED;
    *op_status = 0;
    return;
  }
  if (handle->rpc_tab->resetstats == NULL) {
    res->nasd_status = NASD_BINDING_NOIMPL;
    *op_status = 0;
    return;
  }

  handle->rpc_tab->resetstats(handle, res, op_status);
}

char *
_nasd_edrfscli_error_string(
  nasd_edrfs_handle_t            handle,
  nasd_rpc_status_t              status,
  nasd_edrfscli_error_string_t   str,
  char                          *in_file,
  int                            line)
{
  char *file;
  int len;

  /*
   * Be paranoid about fitting filename in string. Truncate to 80
   * characters. Use the last 80 characters instead of the first
   * to try to be maximally informitive.
   */
  len = strlen(in_file);
  if (len > 80) {
    file = &in_file[len-80];
  }
  else {
    file = in_file;
  }

  if (handle == NULL) {
    sprintf(str,
      "NULL handle passed to nasd_edrfscli_error_string file [%s] line %d",
      file, line);
  }
  else if (status == 999) {
    /*
     * !!! Nasty hack. See nasd_edrfs_rpc.h for this
     * in action.
     */
    strcpy(str, "File manager offline");
  }
  else {
    if (handle->rpc_tab == NULL) {
      sprintf(str,
        "Invalidated handle passed to nasd_edrfscli_error_string file [%s] line %d",
        file, line);
    }
    else {
      handle->rpc_tab->xlate_error(handle, status, str, file, line);
    }
  }

  return(str);
}

/* Local Variables:  */
/* indent-tabs-mode: nil */
/* tab-width: 2 */
/* End: */
