/*
 * nasd_edrfs_mq.c
 *
 * RPC layer for message queue colocation
 *
 * Author: Marc Unangst, Mathew Monroe
 */
/*
 * Copyright (c) of Carnegie Mellon University, 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>

#ifndef KERNEL
#include <nasd/nasd_types.h>
#include <nasd/nasd_edrfs_types.h>
#include <nasd/nasd_itypes.h>
#include <nasd/nasd_mem.h>
#include <nasd/nasd_common.h>
#include <nasd/nasd_edrfs_server_internal.h>
#include <nasd/nasd_timer.h>
#include <nasd/nasd_edrfs_rpc.h>
#include <nasd/nasd_timeout.h>
#include <nasd/nasd_udppipe.h>
#include <nasd/nasd_pipe_shm.h>
#include <nasd/nasd_edrfs_server.h>
#include <nasd/nasd_marshall.h>
#include <nasd/nasd_edrfs_mq.h>
#include <nasd/nasd_msg.h>
#include <nasd/nasd_edrfs_pipe.h>

#include <errno.h>
#include <sys/ipc.h>

/* Don't use __FILE__ here to avoid getting kernel-mangled names */
#define NASD_EDRFS_RPC_RETURN(_statval_) { \
  *op_status = _statval_; \
  return; \
}
#define DOBEGIN(_opname_) NASD_EDRFS_RPC_DOBEGIN(_opname_,"nasd_edrfs_mq.c")
#define DORETURN(_opname_) NASD_EDRFS_RPC_DORETURN(_opname_,"nasd_edrfs_mq.c")

int nasd_edrfs_mq_ignore_eexist;
nasd_threadgroup_t nasd_edrfs_mq_listener_tg;
int nasd_edrfs_mq_mqid = -1;
int nasd_edrfs_mq_service_threads;

/*
 * RPC callbacks
 */

void
nasd_edrfs_mq_op_null(
  nasd_status_t     *status,
  nasd_rpc_status_t *op_status)
{
  NASD_EDRFS_RPC_OP_STATS_DECL

  DOBEGIN(null);
  *status = NASD_SUCCESS;
  DORETURN(null);
}

void
nasd_edrfs_mq_op_mount(
  nasd_edrfs_mount_args_t   *in_args,
  nasd_edrfs_mount_res_t    *out_res,
  nasd_rpc_status_t         *op_status)
{
  NASD_EDRFS_RPC_OP_STATS_DECL

  DOBEGIN(mount);

  in_args->in_dirpath[NASD_EDRFS_MAX_NAME_LEN-1] = '\0';
  nasd_edrfs_real_mount(in_args->in_credential,
                        (char *)in_args->in_dirpath,
                        NASD_EDRFS_DRIVE_LIST_LEN,
                        out_res->out_drivelist,
                        &out_res->out_listlen,
                        &out_res->out_cookie,
                        &out_res->out_identifier,
                        &out_res->nasd_status);
  DORETURN(mount);
}

void
nasd_edrfs_mq_op_fsstat(
  nasd_edrfs_fsstat_args_t   *in_args,
  nasd_edrfs_fsstat_res_t    *out_res,
  nasd_rpc_status_t          *op_status)
{
  NASD_EDRFS_RPC_OP_STATS_DECL

  DOBEGIN(fsstat);

  nasd_edrfs_real_fsstat(in_args->in_cookie,
                         in_args->in_identifier,
                         in_args->in_credential,
                         &out_res->out_stat,
                         &out_res->out_attribute,
                         &out_res->nasd_status);

  DORETURN(fsstat);
}

void
nasd_edrfs_mq_op_fsinfo(
  nasd_edrfs_fsinfo_args_t   *in_args,
  nasd_edrfs_fsinfo_res_t    *out_res,
  nasd_rpc_status_t          *op_status)
{
  NASD_EDRFS_RPC_OP_STATS_DECL

  DOBEGIN(fsinfo);

  nasd_edrfs_real_fsinfo(in_args->in_cookie,
                         in_args->in_identifier,
                         in_args->in_credential,
                         &out_res->out_info,
                         &out_res->out_attribute,
                         &out_res->nasd_status);

  DORETURN(fsinfo);
}

void
nasd_edrfs_mq_op_lookup(
  nasd_edrfs_lookup_args_t   *in_args,
  nasd_edrfs_lookup_res_t    *out_res,
  nasd_rpc_status_t          *op_status)
{
  nasd_status_t rc;
  nasd_edrfs_lookupstuff_t *lus;
  NASD_EDRFS_RPC_OP_STATS_DECL

  DOBEGIN(lookup);

  lus = nasd_edrfs_get_lookupstuff();
  if (lus == NULL) {
    out_res->nasd_status = NASD_NO_MEM;
    goto really_done_lookup;
  }

  bcopy(in_args, &lus->in_args, sizeof(nasd_edrfs_lookup_args_t));

  lus->in_args.in_dirpath[NASD_EDRFS_MAX_NAME_LEN-1] = '\0';

  nasd_edrfs_real_lookup(lus->in_args.in_cookie,
                         lus->in_args.in_identifier,
                         lus->in_args.in_credential,
                         (char *)lus->in_args.in_dirpath,
                         &lus->out_res.out_cookie,
                         &lus->out_res.out_identifier,
                         &lus->out_res.out_attribute,
                         &lus->out_res.post_attribute,
                         &rc);
  lus->out_res.nasd_status = rc;

  bcopy(&lus->out_res, out_res, sizeof(nasd_edrfs_lookup_res_t));

really_done_lookup:
#if (DEBUG_LOOKUP_DETAIL > 0) || (DEBUG_RPC_ERR_DETAIL > 0)
  if (lus == NULL) {
    printf("lookup complete lus NULL\n");
  }
  else if (rc) {
    printf("lookup returning nasd status 0x%x (%s) for in_dirpath=\"%s\"\n", rc, nasd_error_string(rc), (char *)lus->in_args.in_dirpath);
  }
#if DEBUG_LOOKUP_DETAIL > 0
  else if (lus->out_res.out_attribute.valid != NASD_EDRFS_ATTRIBUTE_VALID) {
    printf("lookup Successful with invalid attribute for in_dirpath=\"%s\" nasdid 0x%"
           NASD_ID_FMT "\n",
      (char *)lus->in_args.in_dirpath,
      lus->out_res.out_identifier.nasd_identifier);
    if (lus->out_res.out_attribute.valid == NASD_EDRFS_ATTRIBUTE_INVALID) {
      printf("lus->out_res.out_attribute.valid = INVALID\n");
    }
    else {
      printf("lus->out_res.out_attribute.valid = 0x%x\n", ((int)lus->out_res.out_attribute.valid)&0xff);
    }
  }
  else {
    printf("lookup_otw Successful for in_dirpath=\"%s\" nasdid 0x%"
           NASD_ID_FMT "\n%s attributes: ",
           (char *)lus->in_args.in_dirpath,
           lus->out_res.out_identifier.nasd_identifier,
           (char *)lus->in_args.in_dirpath);
    nasd_dump_edrfs_attributes_otw((nasd_otw_base_t *)lus->out_res.out_attribute.attribute.fs_specific);
    printf("\n");
#ifndef KERNEL
    fflush(stdout);
#endif /* !KERNEL */
  }
#endif /* DEBUG_LOOKUP_DETAIL > 0 */
#endif /* (DEBUG_LOOKUP_DETAIL > 0) || (DEBUG_RPC_ERR_DETAIL > 0) */

  if (lus) {
    nasd_edrfs_free_lookupstuff(lus);
  }

  if(rc)
    out_res->nasd_status = rc;
  DORETURN(lookup);
}

void
nasd_edrfs_mq_op_readdir(
  int                         shmid,
  nasd_edrfs_readdir_args_t   *in_args,
  nasd_edrfs_readdir_res_t    *out_res,
  nasd_rpc_status_t           *op_status)
{
  nasd_edrfs_dirent_t dirent;
  nasd_procpipe_t procpipe;
  nasd_status_t rc;
  nasd_edrfs_readdir_pipe_state_t state;

  NASD_EDRFS_RPC_OP_STATS_DECL

  DOBEGIN(readdir);

  /* yes, this is sort of an abuse of the "client-side" shmpipe setup routine */
  rc = nasd_shmpipe_setup_cs(shmid, (nasd_byte_t **) &state.dir_buf,
                             op_status);
  if(rc) {
    out_res->nasd_status = rc;
  } else {
    state.dir_buf_ents = in_args->in_count;
    state.dir_buf_ents_filled = 0;
    state.partial_buf_offset = 0;

    procpipe.state = &state;
    procpipe.push = nasd_copipe_readdir_push;
    procpipe.pull = NULL;

    nasd_edrfs_real_readdir(in_args->in_cookie,
                            in_args->in_identifier,
                            in_args->in_credential,
                            in_args->in_marker,
                            in_args->in_markerv,
                            in_args->in_count,
                            &out_res->out_marker,
                            &out_res->out_markerv,
                            &out_res->out_count,
                            &out_res->out_eof,
                            &procpipe,
                            &out_res->post_attribute,
                            &out_res->nasd_status);

    procpipe.push(procpipe.state, &dirent, 0, NULL, NULL, NULL);

    nasd_shmpipe_complete((nasd_byte_t *) state.dir_buf);
  }
  DORETURN(readdir);
}

void
nasd_edrfs_mq_op_access(
  nasd_edrfs_access_args_t   *in_args,
  nasd_edrfs_access_res_t    *out_res,
  nasd_rpc_status_t          *op_status)
{
  NASD_EDRFS_RPC_OP_STATS_DECL

  DOBEGIN(access);

  nasd_edrfs_real_access(in_args->in_cookie,
                         in_args->in_identifier,
                         in_args->in_credential,
                         in_args->in_access,
                         &out_res->out_access,
                         &out_res->post_attribute,
                         &out_res->nasd_status);

  DORETURN(access);
}

void
nasd_edrfs_mq_op_setattr(
  nasd_edrfs_setattr_args_t   *in_args,
  nasd_edrfs_setattr_res_t    *out_res,
  nasd_rpc_status_t           *op_status)
{
  NASD_EDRFS_RPC_OP_STATS_DECL

  DOBEGIN(setattr);

  nasd_edrfs_real_setattr(in_args->in_cookie,
                          in_args->in_identifier,
                          in_args->in_credential,
                          in_args->in_attribute,
                          in_args->in_fieldmask,
                          in_args->in_guard,
                          &out_res->out_attribute,
                          &out_res->nasd_status);

  DORETURN(setattr);
}

void
nasd_edrfs_mq_op_create(
  nasd_edrfs_create_args_t   *in_args,
  nasd_edrfs_create_res_t    *out_res,
  nasd_rpc_status_t          *op_status)
{
  NASD_EDRFS_RPC_OP_STATS_DECL

  DOBEGIN(create);

  in_args->in_dirpath[NASD_EDRFS_MAX_NAME_LEN-1] = '\0';

  nasd_edrfs_real_create(in_args->in_cookie,
                         in_args->in_directory,
                         in_args->in_credential,
                         (char *)in_args->in_dirpath,
                         in_args->in_attribute,
                         in_args->in_fieldmask,
                         &out_res->out_cookie,
                         &out_res->out_identifier,
                         &out_res->out_attribute,
                         &out_res->post_attribute,
                         &out_res->nasd_status);

  DORETURN(create);
}

void
nasd_edrfs_mq_op_symlink(
  nasd_edrfs_symlink_args_t   *in_args,
  nasd_edrfs_symlink_res_t    *out_res,
  nasd_rpc_status_t           *op_status)
{
  NASD_EDRFS_RPC_OP_STATS_DECL

  DOBEGIN(symlink);

  in_args->in_dirpath[NASD_EDRFS_MAX_NAME_LEN-1] = '\0';
  in_args->in_symlink[NASD_EDRFS_MAX_NAME_LEN-1] = '\0';

  nasd_edrfs_real_symlink(in_args->in_cookie,
                          in_args->in_directory,
                          in_args->in_credential,
                          (char *)in_args->in_dirpath,
                          (char *)in_args->in_symlink,
                          in_args->in_attribute,
                          &out_res->out_cookie,
                          &out_res->out_identifier,
                          &out_res->out_attribute,
                          &out_res->post_attribute,
                          &out_res->nasd_status);

  DORETURN(symlink);
}

void
nasd_edrfs_mq_op_remove(
  nasd_edrfs_remove_args_t   *in_args,
  nasd_edrfs_remove_res_t    *out_res,
  nasd_rpc_status_t          *op_status)
{
  NASD_EDRFS_RPC_OP_STATS_DECL

  DOBEGIN(remove);

  in_args->in_dirpath[NASD_EDRFS_MAX_NAME_LEN-1] = '\0';

  nasd_edrfs_real_remove(in_args->in_cookie,
                         in_args->in_directory,
                         in_args->in_credential,
                         (char *)in_args->in_dirpath,
                         &out_res->nasd_status);

  DORETURN(remove);
}

void
nasd_edrfs_mq_op_mkdir(
  nasd_edrfs_mkdir_args_t   *in_args,
  nasd_edrfs_mkdir_res_t    *out_res,
  nasd_rpc_status_t         *op_status)
{
  NASD_EDRFS_RPC_OP_STATS_DECL

  DOBEGIN(mkdir);

  in_args->in_dirpath[NASD_EDRFS_MAX_NAME_LEN-1] = '\0';

  nasd_edrfs_real_mkdir(in_args->in_cookie,
                        in_args->in_directory, 
                        in_args->in_credential,
                        (char *)in_args->in_dirpath,
                        in_args->in_attribute,
                        in_args->in_fieldmask,
                        &out_res->out_cookie,
                        &out_res->out_identifier,
                        &out_res->out_attribute,
                        &out_res->post_attribute,
                        &out_res->nasd_status);

  DORETURN(mkdir);
}

void
nasd_edrfs_mq_op_rmdir(
  nasd_edrfs_rmdir_args_t   *in_args,
  nasd_edrfs_rmdir_res_t    *out_res,
  nasd_rpc_status_t         *op_status)
{
  NASD_EDRFS_RPC_OP_STATS_DECL

  DOBEGIN(rmdir);

  in_args->in_dirpath[NASD_EDRFS_MAX_NAME_LEN-1] = '\0';

  nasd_edrfs_real_rmdir(in_args->in_cookie,
                        in_args->in_directory,
                        in_args->in_credential,
                        (char *)in_args->in_dirpath,
                        &out_res->nasd_status);

  DORETURN(rmdir);
}

void
nasd_edrfs_mq_op_newcookie(
  nasd_edrfs_newcookie_args_t   *in_args,
  nasd_edrfs_newcookie_res_t    *out_res,
  nasd_rpc_status_t             *op_status)
{
  NASD_EDRFS_RPC_OP_STATS_DECL

  DOBEGIN(newcookie);
  
  nasd_edrfs_real_newcookie(in_args->in_identifier,
                            in_args->in_credential,
                            &out_res->out_cookie,
                            &out_res->nasd_status);
  DORETURN(newcookie);
}

void
nasd_edrfs_mq_op_rename(
  nasd_edrfs_rename_args_t   *in_args,
  nasd_edrfs_rename_res_t    *out_res,
  nasd_rpc_status_t          *op_status)
{
  NASD_EDRFS_RPC_OP_STATS_DECL

  DOBEGIN(rename);

  in_args->in_from_path[NASD_EDRFS_MAX_NAME_LEN-1] = '\0';
  in_args->in_to_path[NASD_EDRFS_MAX_NAME_LEN-1] = '\0';

  nasd_edrfs_real_rename(in_args->in_cookie,
                         in_args->in_from_directory,
                         in_args->in_credential,
                         (char *)in_args->in_from_path,
                         in_args->in_to_directory,
                         (char *)in_args->in_to_path,
                         &out_res->post_from_attribute,
                         &out_res->post_to_attribute,
                         &out_res->nasd_status);

  DORETURN(rename);
}

void
nasd_edrfs_mq_op_getstats(
  nasd_edrfs_getstats_res_t    *out_res,
  nasd_rpc_status_t            *op_status)
{
  NASD_EDRFS_RPC_OP_STATS_DECL

  DOBEGIN(getstats);

  nasd_edrfs_real_getstats(&out_res->out_opstats,
                           &out_res->out_cachestats,
                           &out_res->out_opdepths,
                           &out_res->nasd_status);

  DORETURN(getstats);
}

void
nasd_edrfs_mq_op_resetstats(
  nasd_status_t      *out_res,
  nasd_rpc_status_t  *op_status)
{
  NASD_EDRFS_RPC_OP_STATS_DECL

  DOBEGIN(resetstats);

  nasd_edrfs_real_resetstats(out_res);

  DORETURN(resetstats);
}

/*
 * RPC mechanism
 */

nasd_status_t
nasd_edrfs_mq_specific_init()
{
  nasd_status_t rc;

  rc = nasd_init_threadgroup(&nasd_edrfs_mq_listener_tg);
  if(rc) {
    printf("EDRFS FILE MANAGER: cannot init threadgroup: ret=0x%x (%s)\n",
	   rc, nasd_error_string(rc));
    return rc;
  }

  return NASD_SUCCESS;
}

void
nasd_edrfs_mq_shutdown(
  void  *ignored)
{
  msgctl(nasd_edrfs_mq_mqid, IPC_RMID, NULL);
}

nasd_status_t
nasd_edrfs_mq_specific_startup()
{
  nasd_status_t rc;
  int flags;

  if(nasd_edrfs_mq_ignore_eexist)
    flags = IPC_CREAT | 0666;
  else
    flags = IPC_CREAT | IPC_EXCL | 0666;

  nasd_edrfs_mq_mqid = nasd_msg_msgget(NASD_EDRFS_MQ_KEY, flags);
  if(nasd_edrfs_mq_mqid < 0) {
    printf("EDRFS FILE MANAGER: cannot create nasd_edrfs_mq_mqid: ret=%d errno=%d (%s)\n",
	   nasd_edrfs_mq_mqid, errno, strerror(errno));
    return NASD_FAIL;
  }
  rc = nasd_shutdown_proc(nasd_edrfs_shutdown, nasd_edrfs_mq_shutdown, NULL);
  if (rc) {
    nasd_edrfs_mq_shutdown(NULL);
    return(rc);
  }

  return NASD_SUCCESS;
}

void
nasd_edrfs_mq_specific_stop()
{
  nasd_edrfs_mq_specific_listen_stop();
}

void
nasd_edrfs_mq_specific_listen_stop(void)
{
  int i;
  nasd_edrfs_mq_buf_t mb;
  nasd_status_t rc;

  mb.mtype = NASD_EDRFS_MQ_KICK;

  NASD_THREADGROUP_INDICATE_SHUTDOWN(&nasd_edrfs_mq_listener_tg);
  for(i = 0; i < nasd_edrfs_mq_service_threads; i++) {
    rc = nasd_msg_msgsnd(nasd_edrfs_mq_mqid, (struct msgbuf *) &mb,
                         NASD_EDRFS_MQ_MSGLEN, 0);
    if(rc) {
      printf("EDRFS FILE MANAGER: unable to send shutdown kick message to thread %d: errno=%d (%s)\n",
	     i, errno, strerror(errno));
      continue;
    }
  }
  NASD_THREADGROUP_WAIT_STOP(&nasd_edrfs_mq_listener_tg);
  return;
}

nasd_status_t
nasd_edrfs_mq_specific_listen(
  int  service_threads)
{
  int i;
  nasd_thread_t tid;
  nasd_status_t rc;

  nasd_edrfs_mq_service_threads = service_threads;

  for(i = 0; i < service_threads; i++) {
    rc = nasd_thread_create(&tid, nasd_edrfs_mq_listen, NULL);
    if(rc) {
      NASD_THREADGROUP_WAIT_START(&nasd_edrfs_mq_listener_tg);
      nasd_edrfs_mq_specific_listen_stop();
      printf("EDRFS FILE MANAGER: error creating message queue listener %d: rc=0x%x (%s)\n",
	     i, rc, nasd_error_string(rc));
    }
    NASD_THREADGROUP_STARTED(&nasd_edrfs_mq_listener_tg);
  }

  return NASD_SUCCESS;
}

nasd_status_t
nasd_edrfs_mq_specific_set_stacksize(
  int  stacksize)
{
  return(NASD_OP_NOT_SUPPORTED);
}

#define DECLARE_VARS(msgtype, msgfield, reptype, repfield) \
        msgtype *msg = &(mb.un.msgfield);\
        reptype *rep = &(mr.un.repfield);
#define DO_CALL(callname) callname(&msg->args, &rep->res, &rep->op_status)
#define SETUP_REPLY()   mr.mtype = msg->seqnum; replyq = msg->replyq;

void
nasd_edrfs_mq_listen(nasd_threadarg_t ignored)
{
  int ret;
  int replyq;
  nasd_edrfs_mq_buf_t mb;
  nasd_edrfs_mq_buf_t mr;

  NASD_THREADGROUP_RUNNING(&nasd_edrfs_mq_listener_tg);

  while(!NASD_THREADGROUP_SHUTDOWNP(&nasd_edrfs_mq_listener_tg)) {
    ret = msgrcv(nasd_edrfs_mq_mqid, &mb, NASD_EDRFS_MQ_MSGLEN, 0, 0);
    if(ret < 0) {
      if(errno == EINTR)
        continue;
      else if(errno == EIDRM) {
        printf("nasd_edrfs_mq_listen: message queue removed; returning\n");
        return;
      } else if(errno == E2BIG) {
        printf("nasd_edrfs_mq_listen: WARNING: received oversize message\n");
        continue;
      } else {
        printf("nasd_edrfs_mq_listen: msgrcv returned %d; errno=%d (%s)\n",
               ret, errno, strerror(errno));
        fflush(stdout);
        NASD_PANIC();
      }
    }

    /* dispatch to appropriate handler function */
    switch(mb.mtype) {
    case NASD_EDRFS_MQ_KICK:
      replyq = -1;
      break;

    case NASD_EDRFS_MQ_NULL: {
      DECLARE_VARS(NASDMQ_edrfs_null_msg_t, null_msg, NASDMQ_edrfs_null_rep_t, null_rep);
      nasd_edrfs_mq_op_null(&rep->nasd_status, &rep->op_status);
      SETUP_REPLY();
      break; }

    case NASD_EDRFS_MQ_MOUNT: {
      DECLARE_VARS(NASDMQ_edrfs_mount_msg_t, mount_msg, NASDMQ_edrfs_mount_rep_t, mount_rep);
      DO_CALL(nasd_edrfs_mq_op_mount);
      SETUP_REPLY();
      break; }

    case NASD_EDRFS_MQ_FSSTAT: {
      DECLARE_VARS(NASDMQ_edrfs_fsstat_msg_t, fsstat_msg, NASDMQ_edrfs_fsstat_rep_t, fsstat_rep);
      DO_CALL(nasd_edrfs_mq_op_fsstat);
      SETUP_REPLY();
      break; }

    case NASD_EDRFS_MQ_FSINFO: {
      DECLARE_VARS(NASDMQ_edrfs_fsinfo_msg_t, fsinfo_msg, NASDMQ_edrfs_fsinfo_rep_t, fsinfo_rep);
      DO_CALL(nasd_edrfs_mq_op_fsinfo);
      SETUP_REPLY();
      break; }

    case NASD_EDRFS_MQ_LOOKUP: {
      DECLARE_VARS(NASDMQ_edrfs_lookup_msg_t, lookup_msg, NASDMQ_edrfs_lookup_rep_t, lookup_rep);
      DO_CALL(nasd_edrfs_mq_op_lookup);
      SETUP_REPLY();
      break; }

    case NASD_EDRFS_MQ_READDIR: {
      DECLARE_VARS(NASDMQ_edrfs_readdir_msg_t, readdir_msg, NASDMQ_edrfs_readdir_rep_t, readdir_rep);
      nasd_edrfs_mq_op_readdir(msg->shmid, &msg->args, &rep->res, &rep->op_status);
      SETUP_REPLY();
      break; }

    case NASD_EDRFS_MQ_ACCESS: {
      DECLARE_VARS(NASDMQ_edrfs_access_msg_t, access_msg, NASDMQ_edrfs_access_rep_t, access_rep);
      DO_CALL(nasd_edrfs_mq_op_access);
      SETUP_REPLY();
      break; }

    case NASD_EDRFS_MQ_SETATTR: {
      DECLARE_VARS(NASDMQ_edrfs_setattr_msg_t, setattr_msg, NASDMQ_edrfs_setattr_rep_t, setattr_rep);
      DO_CALL(nasd_edrfs_mq_op_setattr);
      SETUP_REPLY();
      break; }

    case NASD_EDRFS_MQ_CREATE: {
      DECLARE_VARS(NASDMQ_edrfs_create_msg_t, create_msg, NASDMQ_edrfs_create_rep_t, create_rep);
      DO_CALL(nasd_edrfs_mq_op_create);
      SETUP_REPLY();
      break; }

    case NASD_EDRFS_MQ_SYMLINK: {
      DECLARE_VARS(NASDMQ_edrfs_symlink_msg_t, symlink_msg, NASDMQ_edrfs_symlink_rep_t, symlink_rep);
      DO_CALL(nasd_edrfs_mq_op_symlink);
      SETUP_REPLY();
      break; }

    case NASD_EDRFS_MQ_REMOVE: {
      DECLARE_VARS(NASDMQ_edrfs_remove_msg_t, remove_msg, NASDMQ_edrfs_remove_rep_t, remove_rep);
      DO_CALL(nasd_edrfs_mq_op_remove);
      SETUP_REPLY();
      break; }

    case NASD_EDRFS_MQ_MKDIR: {
      DECLARE_VARS(NASDMQ_edrfs_mkdir_msg_t, mkdir_msg, NASDMQ_edrfs_mkdir_rep_t, mkdir_rep);
      DO_CALL(nasd_edrfs_mq_op_mkdir);
      SETUP_REPLY();
      break; }

    case NASD_EDRFS_MQ_RMDIR: {
      DECLARE_VARS(NASDMQ_edrfs_rmdir_msg_t, rmdir_msg, NASDMQ_edrfs_rmdir_rep_t, rmdir_rep);
      DO_CALL(nasd_edrfs_mq_op_rmdir);
      SETUP_REPLY();
      break; }

    case NASD_EDRFS_MQ_NEWCOOKIE: {
      DECLARE_VARS(NASDMQ_edrfs_newcookie_msg_t, newcookie_msg, NASDMQ_edrfs_newcookie_rep_t, newcookie_rep);
      DO_CALL(nasd_edrfs_mq_op_newcookie);
      SETUP_REPLY();
      break; }

    case NASD_EDRFS_MQ_RENAME: {
      DECLARE_VARS(NASDMQ_edrfs_rename_msg_t, rename_msg, NASDMQ_edrfs_rename_rep_t, rename_rep);
      DO_CALL(nasd_edrfs_mq_op_rename);
      SETUP_REPLY();
      break; }

    case NASD_EDRFS_MQ_GETSTATS: {
      DECLARE_VARS(NASDMQ_edrfs_getstats_msg_t, getstats_msg, NASDMQ_edrfs_getstats_rep_t, getstats_rep);
      nasd_edrfs_mq_op_getstats(&rep->res, &rep->op_status);
      SETUP_REPLY();
      break; }

    case NASD_EDRFS_MQ_RESETSTATS: {
      DECLARE_VARS(NASDMQ_edrfs_resetstats_msg_t, resetstats_msg, NASDMQ_edrfs_resetstats_rep_t, resetstats_rep);
      nasd_edrfs_mq_op_resetstats(&rep->nasd_status, &rep->op_status);
      SETUP_REPLY();
      break; }

    default:
      replyq = (-1);
      printf("nasd_edrfs_mq_listen: received unknown message type %ld\n",
             mb.mtype);
      break;
    }

    if(replyq >= 0) {
      ret = nasd_msg_msgsnd(replyq, (struct msgbuf *) &mr, NASD_EDRFS_MQ_MSGLEN, 0);
      if(ret) {
        printf("nasd_edrfs_mq_listen: couldn't send message reply: ret=%d errno=%d (%s)\n",
               ret, errno, strerror(errno));
      }
    }
  }

  NASD_THREADGROUP_DONE(&nasd_edrfs_mq_listener_tg);
}

#undef DECLARE_VARS
#undef DO_CALL
#undef SETUP_REPLY

#endif /* !KERNEL */

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