/*
 * nasd_od_mq.c
 *
 * Drive side of a cheesy shared-memory and message queue based
 * "colocation" interface.
 *
 * Author: Marc Unangst
 */
/*
 * Copyright (c) of Carnegie Mellon University, 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_drive_options.h>
#ifndef KERNEL
#include <nasd/nasd_types.h>
#include <nasd/nasd_freelist.h>
#include <nasd/nasd_itypes.h>
#include <nasd/nasd_mem.h>
#include <nasd/nasd_cache.h>
#include <nasd/nasd_common.h>
#include <nasd/nasd_mq.h>
#include <nasd/nasd_od_rpc.h>
#include <nasd/nasd_msg.h>
#include <nasd/nasd_drive_types.h>
#include <nasd/nasd_keymgmt_dr.h>
#include <nasd/nasd_udppipe.h>
#include <nasd/nasd_pipe_shm.h>
#include <nasd/nasd_security.h>
#include <nasd/nasd_security_dr.h>

#include <errno.h>

#define NASD_OD_RPC_RETURN(_statval_) { \
  *status = _statval_; \
  return; \
}
#define NASD_OD_RPC_NASD_RET res->nasd_status
#define DOBEGIN(_opname_) NASD_OD_RPC_DOBEGIN(_opname_,"nasd_od_mq.c")
#define DORETURN(_opname_) NASD_OD_RPC_DORETURN(_opname_,"nasd_od_mq.c")

#define NASD_MSGQ_SEC_DECL \
  nasd_capability_t capability; \
  nasd_timespec_t now; \
  nasd_key_t op_key; \
  nasd_key_t integrity_key;

#define NASD_MSGQ_OP_DECL \
  NASD_MSGQ_SEC_DECL \
  NASD_OD_RPC_OP_STATS_DECL

#define NASD_MSGQ_NOSEC_OP_DECL \
  NASD_OD_RPC_OP_STATS_DECL

#define NASD_OD_MQ_DEBUG		0
#define NASD_OD_MQ_LISTENER_THREADS	5

#define NASD_MSGQ_INVALID_MASK		(~(NASD_INTEGRITY_DATA|NASD_PRIVACY_DATA|NASD_PRIVACY_ARGS))

int nasd_od_mq_ignore_eexist = 0;

nasd_threadgroup_t nasd_od_mq_listener_tg;

int nasd_od_mq_mqid;
int nasd_od_mq_count = 0;
int nasd_od_mq_running = 0;
int nasd_od_mq_in_rpc = 0;

nasd_status_t
nasd_od_mq_verify_digest(nasd_security_param_t *sec_param,
			 nasd_capability_otw_t capability_otw,
			 nasd_key_t op_key,
			 nasd_key_t integrity_key,
			 void *args,
			 int args_len,
			 nasd_digest_nonce_otw_t digest_otw,
			 nasd_capability_t *capability)
{
  nasd_status_t rc;
  nasd_digest_nonce_t digest;
  nasd_key_t pkey;

  if(sec_param->actual_protection == NASD_NO_PROTECTION)
    return NASD_SUCCESS;


  if(NASD_SEC_IS_CAPABILITY(sec_param->type)) {
    if(!capability_otw)
      return NASD_BAD_KEYTYPE;
    nasd_capability_t_unmarshall(capability_otw, capability);
  }

  rc = nasd_sec_get_capability_keys(sec_param, capability,
				   capability_otw, op_key, integrity_key,
				    pkey);
  if(rc) {
    sec_param->actual_protection = NASD_NO_PROTECTION;
    return rc;
  }

  nasd_digest_nonce_t_unmarshall(digest_otw, &digest);

  rc = nasd_sec_verify_args(sec_param, capability, digest_otw,
			    &digest, args, args_len, op_key, integrity_key,
			    NULL);
  if(rc) {
    sec_param->actual_protection = NASD_NO_PROTECTION;
    return rc;
  }

  /* Generic security checks.  We do these here (after verifying args
     and capability) so as to not expose information about the drive
     if the MAC was incorrect. */
  /* make sure client has specified a valid partition number */
  if(!NASD_OD_PARTNUM_VALID(sec_param->partnum)) {
    return NASD_BAD_PARTITION;
  }
  /* if we got a capability, do some capability-specific checks */
  if(NASD_SEC_IS_CAPABILITY(sec_param->type)) {
    /* make sure this capability is for a valid partition (we don't
       trust the FM) */
    if(!NASD_OD_PARTNUM_VALID(capability->partnum)) {
      return NASD_BAD_PARTITION;
    }
    /* make sure the client is at least as secure as the FM wants it to be.
       we need to mask off the bits that we turned off earlier to avoid
       triggering false alarms. */
    if((sec_param->actual_protection &
	(capability->min_protection & NASD_MSGQ_INVALID_MASK)) !=
       (capability->min_protection & NASD_MSGQ_INVALID_MASK)) {
      return NASD_CAP_PROTECTION;
    }
    if((sec_param->actual_protection &
	(PART(capability->partnum).min_protection & NASD_MSGQ_INVALID_MASK)) !=
       (PART(capability->partnum).min_protection & NASD_MSGQ_INVALID_MASK)) {
      return NASD_PART_PROTECTION;
    }
  }

  return NASD_SUCCESS;
}

void
nasd_od_mq_op_null(nasd_res_t *res,
		   nasd_rpc_status_t *status)
{
  NASD_OD_RPC_OP_STATS_DECL

  DOBEGIN(null);
  res->nasd_status = NASD_SUCCESS;
  DORETURN(null);
}

void
nasd_od_mq_op_sync(nasd_res_t *res,
		   nasd_rpc_status_t *status)
{
  NASD_OD_RPC_OP_STATS_DECL

  DOBEGIN(sync);
  res->nasd_status = nasd_odc_flush_dirty(1);
  DORETURN(sync);
}

void
nasd_od_mq_op_part_creat(nasd_security_param_t *sec_param,
			 nasd_capability_otw_t capability_otw,
			 nasd_digest_nonce_otw_t in_digest_otw,
			 nasd_p_part_creat_dr_args_t *args,
			 nasd_p_part_creat_dr_res_t *res,
			 nasd_digest_nonce_otw_t out_digest_otw,
			 nasd_rpc_status_t *status)
{
  nasd_identifier_t nid;
  NASD_MSGQ_OP_DECL

  DOBEGIN(part_creat);
  res->nasd_status = nasd_od_mq_verify_digest(sec_param, capability_otw,
					      op_key, integrity_key, args,
					      sizeof(*args), in_digest_otw,
					      &capability);
  if(!res->nasd_status) {
    NASD_SEC_POLICY_PART_CREAT(*sec_param, capability, *args,
			       res->nasd_status);
    if(!res->nasd_status) {
      res->nasd_status = nasd_od_create_partition(args->in_partnum,
						  args->in_blkcnt,
						  args->in_min_protection,
						  args->in_partition_key,
						  args->in_red_key,
						  args->in_black_key, &nid);
    }
  }
  nasd_gettime(&now);
  nasd_sec_fill_nonce(sec_param, op_key, integrity_key, NULL, 1, res,
		      sizeof(*res), &now, out_digest_otw,
		      NULL);
  DORETURN(part_creat);
}

void
nasd_od_mq_op_create(nasd_security_param_t *sec_param,
		     nasd_capability_otw_t capability_otw,
		     nasd_digest_nonce_otw_t in_digest_otw,
		     nasd_p_create_dr_args_t *args,
		     nasd_p_create_dr_res_t *res,
		     nasd_digest_nonce_otw_t out_digest_otw,
		     nasd_rpc_status_t *status)
{
  NASD_MSGQ_OP_DECL

  DOBEGIN(create);
  res->nasd_status = nasd_od_mq_verify_digest(sec_param, capability_otw,
					      op_key, integrity_key, args,
					      sizeof(*args), in_digest_otw,
					      &capability);
  if(!res->nasd_status) {
    NASD_SEC_POLICY_CREATE(*sec_param, capability, *args,
			       res->nasd_status);
    if(!res->nasd_status) {
      res->nasd_status = nasd_obj_create(args->in_partnum, &args->in_attribute,
					 args->in_fieldmask,
					 &res->out_identifier,
					 &res->out_attribute, 0);
    }
  }
  nasd_gettime(&now);
  nasd_sec_fill_nonce(sec_param, op_key, integrity_key, NULL, 1, res,
		      sizeof(*res), &now, out_digest_otw,
		      NULL);
  DORETURN(create);
}

void
nasd_od_mq_op_getattr(nasd_security_param_t *sec_param,
		      nasd_capability_otw_t capability_otw,
		      nasd_digest_nonce_otw_t in_digest_otw,
		      nasd_p_getattr_dr_args_t *args,
		      nasd_p_getattr_dr_res_t *res,
		      nasd_digest_nonce_otw_t out_digest_otw,
		      nasd_rpc_status_t *status)
{
  NASD_MSGQ_OP_DECL

  DOBEGIN(getattr);
  res->nasd_status = nasd_od_mq_verify_digest(sec_param, capability_otw,
					      op_key, integrity_key, args,
					      sizeof(*args), in_digest_otw,
					      &capability);
  if(!res->nasd_status) {
    NASD_SEC_POLICY_GETATTR(*sec_param, capability, *args,
			       res->nasd_status);
    if(!res->nasd_status) {

      res->nasd_status = nasd_obj_getattr(args->in_partnum,
					  args->in_identifier,
					  &res->out_attribute);
    }
  }
  nasd_gettime(&now);
  nasd_sec_fill_nonce(sec_param, op_key, integrity_key, NULL, 1, res,
		      sizeof(*res), &now, out_digest_otw,
		      NULL);
  DORETURN(getattr);
}

void
nasd_od_mq_op_write_simple(int shmid,
			   nasd_security_param_t *sec_param,
			   nasd_capability_otw_t capability_otw,
			   nasd_digest_nonce_otw_t in_digest_otw,
			   nasd_p_smpl_op_dr_args_t *args,
			   nasd_p_fastwrite_dr_res_t *res,
			   nasd_digest_nonce_otw_t out_digest_otw,
			   nasd_rpc_status_t *status)
{
  nasd_status_t rc;
  nasd_shm_pipe_state_t state;
  nasd_procpipe_t procpipe;
  NASD_MSGQ_OP_DECL

  DOBEGIN(write_simple);
  res->nasd_status = nasd_od_mq_verify_digest(sec_param, capability_otw,
					      op_key, integrity_key, args,
					      sizeof(*args), in_digest_otw,
					      &capability);
  if(!res->nasd_status) {
    NASD_SEC_POLICY_WRITE_SIMPLE(*sec_param, capability, *args,
			       res->nasd_status);
    if(!res->nasd_status) {

      rc = nasd_shmpipe_setup(shmid, args->in_len, &state);
      if(rc) {
	res->nasd_status = rc;
      } else {
	procpipe.state = &state;
	procpipe.push = NULL;
	procpipe.pull = nasd_shmpipe_pull;
	res->out_datalen = 0;
	res->nasd_status = nasd_obj_write_simple(args->in_partnum,
						 args->in_identifier,
						 args->in_offset, args->in_len,
						 &procpipe, NULL,
						 &res->out_datalen);
	nasd_shmpipe_complete(state.orig_buf);
      }
    }
  }
  nasd_gettime(&now);
  nasd_sec_fill_nonce(sec_param, op_key, integrity_key, NULL, 1, res,
		      sizeof(*res), &now, out_digest_otw,
		      NULL);
  DORETURN(write_simple);
}

void
nasd_od_mq_op_read_simple_internal(int shmid,
				   int is_read2,
				   nasd_security_param_t *sec_param,
				   nasd_capability_otw_t capability_otw,
				   nasd_digest_nonce_otw_t in_digest_otw,
				   nasd_p_smpl_op_dr_args_t *args,
				   nasd_p_fastread_dr_res_t *res,
				   nasd_digest_nonce_otw_t out_digest_otw,
				   nasd_rpc_status_t *status)
{
  nasd_status_t rc;
  nasd_shm_pipe_state_t state;
  nasd_procpipe_t procpipe;
  NASD_MSGQ_SEC_DECL

  res->nasd_status = nasd_od_mq_verify_digest(sec_param, capability_otw,
					      op_key, integrity_key, args,
					      sizeof(*args), in_digest_otw,
					      &capability);
  if(!res->nasd_status) {
    NASD_SEC_POLICY_READ_SIMPLE(*sec_param, capability, *args,
			       res->nasd_status);
    if(!res->nasd_status) {

      rc = nasd_shmpipe_setup(shmid, args->in_len, &state);
      if(rc) {
	res->nasd_status = rc;
	return;
      }

      procpipe.state = &state;
      procpipe.push = nasd_shmpipe_push;
      procpipe.pull = NULL;
      res->out_datalen = 0;
      res->nasd_status = nasd_obj_read_simple(args->in_partnum,
					      args->in_identifier,
					      args->in_offset, args->in_len,
					      NULL,
					      is_read2, 0,  &procpipe,
					      &res->out_datalen,
					      NULL);
      nasd_shmpipe_complete(state.orig_buf);
    }
  }
  nasd_gettime(&now);
  nasd_sec_fill_nonce(sec_param, op_key, integrity_key, NULL, 1, res,
		      sizeof(*res), &now, out_digest_otw,
		      NULL);
}


void
nasd_od_mq_op_read_simple(int shmid,
			  nasd_security_param_t *sec_param,
			  nasd_capability_otw_t capability_otw,
			  nasd_digest_nonce_otw_t in_digest_otw,
			  nasd_p_smpl_op_dr_args_t *args,
			  nasd_p_fastread_dr_res_t *res,
			  nasd_digest_nonce_otw_t out_digest_otw,
			  nasd_rpc_status_t *status)
{
  NASD_OD_RPC_OP_STATS_DECL

  DOBEGIN(read_simple);
  nasd_od_mq_op_read_simple_internal(shmid, 0, sec_param, capability_otw,
				     in_digest_otw, args, res, out_digest_otw,
				     status);
  DORETURN(read_simple);
}

void
nasd_od_mq_op_read2_simple(int shmid,
			   nasd_security_param_t *sec_param,
			   nasd_capability_otw_t capability_otw,
			   nasd_digest_nonce_otw_t in_digest_otw,
			   nasd_p_smpl_op_dr_args_t *args,
			   nasd_p_fastread_dr_res_t *res,
			   nasd_digest_nonce_otw_t out_digest_otw,
			   nasd_rpc_status_t *status)
{
  NASD_MSGQ_NOSEC_OP_DECL

  DOBEGIN(read2_simple);
  nasd_od_mq_op_read_simple_internal(shmid, 1, sec_param, capability_otw,
				     in_digest_otw, args, res, out_digest_otw,
				     status);
  DORETURN(read2_simple);
}

void
nasd_od_mq_op_tread_simple(int shmid,
			   nasd_security_param_t *sec_param,
			   nasd_capability_otw_t capability_otw,
			   nasd_digest_nonce_otw_t in_digest_otw,
			   nasd_p_thrtl_op_dr_args_t *args,
			   nasd_p_fastread_dr_res_t *res,
			   nasd_digest_nonce_otw_t out_digest_otw,
			   nasd_rpc_status_t *status)
{
  nasd_status_t rc;
  nasd_shm_pipe_state_t state;
  nasd_procpipe_t procpipe;
  NASD_MSGQ_SEC_DECL

  res->nasd_status = nasd_od_mq_verify_digest(sec_param, capability_otw,
					      op_key, integrity_key, args,
					      sizeof(*args), in_digest_otw,
					      &capability);
  if(!res->nasd_status) {
    NASD_SEC_POLICY_TREAD_SIMPLE(*sec_param, capability, *args,
				 res->nasd_status);
    if(!res->nasd_status) {

      rc = nasd_shmpipe_setup(shmid, args->in_len, &state);
      if(rc) {
	res->nasd_status = rc;
	return;
      }

      procpipe.state = &state;
      procpipe.push = nasd_shmpipe_push;
      procpipe.pull = NULL;
      res->out_datalen = 0;
      res->nasd_status = nasd_obj_read_simple(args->in_partnum,
					      args->in_identifier,
					      args->in_offset, args->in_len,
					      &args->in_bms_targ, 0,0,
					      &procpipe,
					      &res->out_datalen,
					      NULL);
      nasd_shmpipe_complete(state.orig_buf);
    }
  }
  nasd_gettime(&now);
  nasd_sec_fill_nonce(sec_param, op_key, integrity_key, NULL, 1, res,
		      sizeof(*res), &now, out_digest_otw,
		      NULL);
}

void
nasd_od_mq_op_setattr(nasd_security_param_t *sec_param,
		      nasd_capability_otw_t capability_otw,
		      nasd_digest_nonce_otw_t in_digest_otw,
		      nasd_p_setattr_dr_args_t *args,
		      nasd_p_setattr_dr_res_t *res,
		      nasd_digest_nonce_otw_t out_digest_otw,
		      nasd_rpc_status_t *status)
{
  NASD_MSGQ_OP_DECL

  DOBEGIN(setattr);
  res->nasd_status = nasd_od_mq_verify_digest(sec_param, capability_otw,
					      op_key, integrity_key, args,
					      sizeof(*args), in_digest_otw,
					      &capability);
  if(!res->nasd_status) {
    NASD_SEC_POLICY_SETATTR(*sec_param, capability, *args,
			       res->nasd_status);
    if(!res->nasd_status) {


      res->nasd_status = nasd_obj_setattr(args->in_partnum,
					  args->in_identifier,
					  &args->in_attribute,
					  args->in_fieldmask,
					  &res->out_attribute);
    }
  }
  nasd_gettime(&now);
  nasd_sec_fill_nonce(sec_param, op_key, integrity_key, NULL, 1, res,
		      sizeof(*res), &now, out_digest_otw,
		      NULL);
  DORETURN(setattr);
}

void
nasd_od_mq_op_flush_obj(nasd_security_param_t *sec_param,
			nasd_capability_otw_t capability_otw,
			nasd_digest_nonce_otw_t in_digest_otw,
			nasd_p_flush_obj_dr_args_t *args,
			nasd_p_flush_obj_dr_res_t *res,
			nasd_digest_nonce_otw_t out_digest_otw,
			nasd_rpc_status_t *status)
{
  NASD_MSGQ_OP_DECL

  DOBEGIN(flush_obj);
  res->nasd_status = nasd_od_mq_verify_digest(sec_param, capability_otw,
					      op_key, integrity_key, args,
					      sizeof(*args), in_digest_otw,
					      &capability);
  if(!res->nasd_status) {
    NASD_SEC_POLICY_FLUSH_OBJ(*sec_param, capability, *args,
			       res->nasd_status);
    if(!res->nasd_status) {
      res->nasd_status = nasd_obj_flush(args->in_partnum, args->in_identifier);
    }
  }
  nasd_gettime(&now);
  nasd_sec_fill_nonce(sec_param, op_key, integrity_key, NULL, 1, res,
		      sizeof(*res), &now, out_digest_otw,
		      NULL);
  DORETURN(flush_obj);
}

void
nasd_od_mq_op_eject_obj(nasd_security_param_t *sec_param,
			nasd_capability_otw_t capability_otw,
			nasd_digest_nonce_otw_t in_digest_otw,
			nasd_p_eject_obj_dr_args_t *args,
			nasd_p_eject_obj_dr_res_t *res,
			nasd_digest_nonce_otw_t out_digest_otw,
			nasd_rpc_status_t *status)
{
  NASD_MSGQ_OP_DECL

  DOBEGIN(eject_obj);
  res->nasd_status = nasd_od_mq_verify_digest(sec_param, capability_otw,
					      op_key, integrity_key, args,
					      sizeof(*args), in_digest_otw,
					      &capability);
  if(!res->nasd_status) {
    NASD_SEC_POLICY_EJECT_OBJ(*sec_param, capability, *args,
			       res->nasd_status);
    if(!res->nasd_status) {
      res->nasd_status = nasd_obj_eject(args->in_partnum, args->in_identifier);
    }
  }
  nasd_gettime(&now);
  nasd_sec_fill_nonce(sec_param, op_key, integrity_key, NULL, 1, res,
		      sizeof(*res), &now, out_digest_otw,
		      NULL);
  DORETURN(eject_obj);
}

void
nasd_od_mq_op_remove(nasd_security_param_t *sec_param,
		     nasd_capability_otw_t capability_otw,
		     nasd_digest_nonce_otw_t in_digest_otw,
		     nasd_p_remove_dr_args_t *args,
		     nasd_p_remove_dr_res_t *res,
		     nasd_digest_nonce_otw_t out_digest_otw,
		     nasd_rpc_status_t *status)
{
  NASD_MSGQ_OP_DECL

  DOBEGIN(remove);
  res->nasd_status = nasd_od_mq_verify_digest(sec_param, capability_otw,
					      op_key, integrity_key, args,
					      sizeof(*args), in_digest_otw,
					      &capability);
  if(!res->nasd_status) {
    NASD_SEC_POLICY_REMOVE(*sec_param, capability, *args,
			       res->nasd_status);
    if(!res->nasd_status) {
      res->nasd_status = nasd_obj_remove(args->in_partnum,
					 args->in_identifier);
    }
  }
  nasd_gettime(&now);
  nasd_sec_fill_nonce(sec_param, op_key, integrity_key, NULL, 1, res,
		      sizeof(*res), &now, out_digest_otw,
		      NULL);
  DORETURN(remove);
}

void
nasd_od_mq_op_initialize(nasd_p_initialize_dr_args_t *args,
			 nasd_p_initialize_dr_res_t *res,
			 nasd_rpc_status_t *status)
{
  NASD_MSGQ_NOSEC_OP_DECL

  DOBEGIN(initialize);
  res->nasd_status = nasd_sec_initialize_drive(args->in_master_key,
					       args->in_drive_key);
  DORETURN(initialize);
}

void
nasd_od_mq_op_strt_iread(nasd_security_param_t *sec_param,
			 nasd_capability_otw_t capability_otw,
			 nasd_digest_nonce_otw_t in_digest_otw,
			 nasd_p_strt_iread_dr_args_t *args,
			 nasd_p_strt_iread_dr_res_t *res,
			 nasd_digest_nonce_otw_t out_digest_otw,
			 nasd_rpc_status_t *status)
{
  NASD_MSGQ_OP_DECL

  DOBEGIN(strt_iread);
  res->nasd_status = nasd_od_mq_verify_digest(sec_param, capability_otw,
					      op_key, integrity_key, args,
					      sizeof(*args), in_digest_otw,
					      &capability);
  if(!res->nasd_status) {
    NASD_SEC_POLICY_STRT_IREAD(*sec_param, capability, *args,
			       res->nasd_status);
    if(!res->nasd_status) {
      res->nasd_status = nasd_obj_start_iread(args->in_partnum,
					      args->in_index_identifier,
					      args->in_data_identifier,
					      args->in_interval,
					      args->in_offset,
					      args->in_flownum,
					      args->in_earliest_start,
					      args->in_latest_start,
					      args->in_client_addr,
					      &res->out_stream_id);
    }
  }
  nasd_gettime(&now);
  nasd_sec_fill_nonce(sec_param, op_key, integrity_key, NULL, 1, res,
		      sizeof(*res), &now, out_digest_otw,
		      NULL);
  DORETURN(strt_iread);
}

void
nasd_od_mq_op_stop_iread(nasd_security_param_t *sec_param,
			 nasd_capability_otw_t capability_otw,
			 nasd_digest_nonce_otw_t in_digest_otw,
			 nasd_p_stop_iread_dr_args_t *args,
			 nasd_p_stop_iread_dr_res_t *res,
			 nasd_digest_nonce_otw_t out_digest_otw,
			 nasd_rpc_status_t *status)
{
  NASD_MSGQ_OP_DECL

  DOBEGIN(stop_iread);
  res->nasd_status = nasd_od_mq_verify_digest(sec_param, capability_otw,
					      op_key, integrity_key, args,
					      sizeof(*args), in_digest_otw,
					      &capability);
  if(!res->nasd_status) {
    NASD_SEC_POLICY_STOP_IREAD(*sec_param, capability, *args,
			       res->nasd_status);
    if(!res->nasd_status) {
      res->nasd_status = nasd_obj_stop_iread(args->in_stream_id);
    }
  }
  nasd_gettime(&now);
  nasd_sec_fill_nonce(sec_param, op_key, integrity_key, NULL, 1, res,
		      sizeof(*res), &now, out_digest_otw,
		      NULL);
  DORETURN(stop_iread);
}

void
nasd_od_mq_op_rshutdown(nasd_security_param_t *sec_param,
			nasd_capability_otw_t capability_otw,
			nasd_digest_nonce_otw_t in_digest_otw,
			nasd_p_rshutdown_dr_args_t *args,
			nasd_p_rshutdown_dr_res_t *res,
			nasd_digest_nonce_otw_t out_digest_otw,
			nasd_rpc_status_t *status)
{
  NASD_MSGQ_OP_DECL

  DOBEGIN(rshutdown);
  res->nasd_status = nasd_od_mq_verify_digest(sec_param, capability_otw,
					      op_key, integrity_key, args,
					      sizeof(*args), in_digest_otw,
					      &capability);
  if(!res->nasd_status) {
    NASD_SEC_POLICY_RSHUTDOWN(*sec_param, capability, *args,
			       res->nasd_status);
    if(!res->nasd_status) {
      res->nasd_status = nasd_drive_rshutdown(args->in_flags);
    }
  }
  nasd_gettime(&now);
  nasd_sec_fill_nonce(sec_param, op_key, integrity_key, NULL, 1, res,
		      sizeof(*res), &now, out_digest_otw,
		      NULL);
  DORETURN(rshutdown);
}

void
nasd_od_mq_op_getinfo(
			 nasd_p_getinfo_dr_res_t *res,
			 nasd_rpc_status_t *status)
{
  NASD_MSGQ_NOSEC_OP_DECL

  DOBEGIN(getinfo);

  res->nasd_status = nasd_drive_getinfo(&res->info);

  DORETURN(getinfo);
}

/* master listener/dispatcher thread */
void
nasd_od_mq_listen(nasd_threadarg_t ignored)
{
  int ret;
  int replyq;
  nasd_msgq_buf_t mb;
  nasd_msgq_buf_t mr;

#if NASD_OD_MQ_DEBUG > 0
  fprintf(stderr, "mq: mq_listen started\n");
#endif /* NASD_OD_MQ_DEBUG > 0 */

  NASD_THREADGROUP_RUNNING(&nasd_od_mq_listener_tg);

  while(!NASD_THREADGROUP_SHUTDOWNP(&nasd_od_mq_listener_tg)) {
    ret = msgrcv(nasd_od_mq_mqid, &mb, NASD_MSGQ_MSGLEN, 0, 0);
    if(ret < 0) {
      if(errno == EINTR)
	continue;
      else if(errno == EIDRM) {
#if NASD_OD_MQ_DEBUG > 0
	nasd_printf("nasd_od_mq: message queue removed; returning\n");
#endif /* NASD_OD_MQ_DEBUG > 0 */
	return;
      } else if(errno == E2BIG) {
	nasd_printf("nasd_od_mq: WARNING: received oversize message\n");
	continue;
      } else {
	nasd_printf("nasd_od_mq: msgrcv returned %d; errno=%d (%s)\n",
	       ret, errno, strerror(errno));
	fflush(stdout);
	NASD_PANIC();
      }
    }

#if NASD_OD_MQ_DEBUG > 0
    nasd_printf("nasd_od_mq: received message type %ld\n", mb.mtype);
#endif /* NASD_OD_MQ_DEBUG > 0 */

    /* need to save this, since it shares space with the reply struct */
    replyq = MSG(&mb).replyq;
    mr.mtype = MSG(&mb).seqnum;

    /* these are meaningless with message queues */
    mb.sec_param.actual_protection &= NASD_MSGQ_INVALID_MASK;

    /* dispatch to appropriate handler function */
    switch(mb.mtype) {
    case NASD_MSGQ_KICK:
      break;

    case NASD_MSGQ_NULL:
      nasd_od_mq_op_null((nasd_res_t *) &mr.op.nasd_status,
			 &REP(&mr).op_status);
      break;

    case NASD_MSGQ_SYNC:
      nasd_od_mq_op_sync((nasd_res_t *) &mr.op.nasd_status,
			 &REP(&mr).op_status);
      break;

    case NASD_MSGQ_PCRPART:
      nasd_od_mq_op_part_creat(&mb.sec_param, (mb.capability_valid ?
					       mb.capability_otw : NULL),
			       mb.digest_otw, &mb.op.part_creat_args,
			       (nasd_p_part_creat_dr_res_t *) &mr.op.nasd_status,
			       mr.digest_otw, &REP(&mr).op_status);
      break;

    case NASD_MSGQ_PCREATE:
      nasd_od_mq_op_create(&mb.sec_param, (mb.capability_valid ?
					   mb.capability_otw : NULL),
			   mb.digest_otw, &mb.op.create_args,
			   &mr.op.create_res, mr.digest_otw, &REP(&mr).op_status);
      break;

    case NASD_MSGQ_PGETATTR:
      nasd_od_mq_op_getattr(&mb.sec_param, (mb.capability_valid ?
					    mb.capability_otw : NULL),
			    mb.digest_otw, &mb.op.getattr_args,
			    (nasd_p_getattr_dr_res_t *) &mr.op.nasd_status,
			    mr.digest_otw, &REP(&mr).op_status);
      break;

    case NASD_MSGQ_PWRITE_SIMPLE:
      nasd_od_mq_op_write_simple(MSG(&mb).shmid, &mb.sec_param,
				 (mb.capability_valid ?
				  mb.capability_otw : NULL),
				 mb.digest_otw, &mb.op.smpl_op_args,
				 &mr.op.fastwrite_res, mr.digest_otw,
				 &REP(&mr).op_status);
      break;

    case NASD_MSGQ_PREAD_SIMPLE:
      nasd_od_mq_op_read_simple(MSG(&mb).shmid, &mb.sec_param,
				(mb.capability_valid ?
				 mb.capability_otw : NULL),
				mb.digest_otw, &mb.op.smpl_op_args,
				&mr.op.fastread_res, mr.digest_otw,
				&REP(&mr).op_status);
      break;

    case NASD_MSGQ_PREAD2_SIMPLE:
      nasd_od_mq_op_read2_simple(MSG(&mb).shmid, &mb.sec_param,
				 (mb.capability_valid ?
				  mb.capability_otw : NULL),
				 mb.digest_otw, &mb.op.smpl_op_args,
				 &mr.op.fastread_res, mr.digest_otw,
				 &REP(&mr).op_status);
      break;

    case NASD_MSGQ_PTREAD_SIMPLE:
      nasd_od_mq_op_tread_simple(MSG(&mb).shmid, &mb.sec_param,
				 (mb.capability_valid ?
				  mb.capability_otw : NULL),
				 mb.digest_otw, &mb.op.thrtl_op_args,
				 &mr.op.fastread_res, mr.digest_otw,
				 &REP(&mr).op_status);
      break;

    case NASD_MSGQ_PSETATTR: 
      nasd_od_mq_op_setattr(&mb.sec_param, (mb.capability_valid ?
					    mb.capability_otw : NULL),
			    mb.digest_otw, &mb.op.setattr_args,
			    &mr.op.setattr_res, mr.digest_otw,
			    &REP(&mr).op_status);
      break;

    case NASD_MSGQ_PFLUSH_OBJ:
      nasd_od_mq_op_flush_obj(&mb.sec_param, (mb.capability_valid ?
					      mb.capability_otw : NULL),
			      mb.digest_otw, &mb.op.flush_obj_args,
			      (nasd_p_flush_obj_dr_res_t *) &mr.op.nasd_status,
			      mr.digest_otw, &REP(&mr).op_status);
      break;

    case NASD_MSGQ_PEJECT_OBJ:
      nasd_od_mq_op_eject_obj(&mb.sec_param, (mb.capability_valid ?
					      mb.capability_otw : NULL),
			      mb.digest_otw, &mb.op.eject_obj_args,
			      (nasd_p_eject_obj_dr_res_t *) &mr.op.nasd_status,
			      mr.digest_otw, &REP(&mr).op_status);
      break;

    case NASD_MSGQ_PREMOVE:
      nasd_od_mq_op_remove(&mb.sec_param, (mb.capability_valid ?
					   mb.capability_otw : NULL),
			   mb.digest_otw, &mb.op.remove_args,
			   (nasd_p_remove_dr_res_t *) &mr.op.nasd_status,
			   mr.digest_otw, &REP(&mr).op_status);
      break;

    case NASD_MSGQ_PINITIALIZE:
      nasd_od_mq_op_initialize(&mb.op.initialize_args,
			       (nasd_p_initialize_dr_res_t *) &mr.op.nasd_status,
			       &REP(&mr).op_status);
      break;

    case NASD_MSGQ_PSTRT_IREAD:
      nasd_od_mq_op_strt_iread(&mb.sec_param, (mb.capability_valid ?
					       mb.capability_otw : NULL),
			       mb.digest_otw, &mb.op.strt_iread_args,
			       &mr.op.strt_iread_res, mr.digest_otw,
			       &REP(&mr).op_status);
      break;

    case NASD_MSGQ_PSTOP_IREAD:
      nasd_od_mq_op_stop_iread(&mb.sec_param, (mb.capability_valid ?
					       mb.capability_otw : NULL),
			       mb.digest_otw, &mb.op.stop_iread_args,
			       (nasd_p_stop_iread_dr_res_t *) &mr.op.nasd_status,
			       mr.digest_otw, &REP(&mr).op_status);
      break;

    case NASD_MSGQ_PRSHUTDOWN:
      nasd_od_mq_op_rshutdown(&mb.sec_param, (mb.capability_valid ?
					      mb.capability_otw : NULL),
			      mb.digest_otw, &mb.op.rshutdown_args,
			      (nasd_p_rshutdown_dr_res_t *) &mr.op.nasd_status,
			      mr.digest_otw, &REP(&mr).op_status);
      break;

    case NASD_MSGQ_PGETINFO:
      nasd_od_mq_op_getinfo(&mr.op.getinfo_res, &REP(&mr).op_status);
      break;

    default:
      mr.op.nasd_status = NASD_OP_NOT_SUPPORTED;
      break;
    }

    /* send reply */
    msgsnd(replyq, (struct msgbuf *) &mr, NASD_MSGQ_MSGLEN, 0);
  }

  NASD_THREADGROUP_DONE(&nasd_od_mq_listener_tg);
  return;
}

nasd_status_t
nasd_drive_startup_msgq(void)
{
  int rc;
  nasd_thread_t mq_listen[NASD_OD_MQ_LISTENER_THREADS];
  int i;

#if NASD_OD_MQ_DEBUG > 0
  fprintf(stderr, "mq: nasd_startup_msgq\n");
#endif /* NASD_OD_MQ_DEBUG > 0 */

  for(i = 0; i < NASD_OD_MQ_LISTENER_THREADS; i++) {
    rc = nasd_thread_create(&mq_listen[i], nasd_od_mq_listen, NULL);
    if(rc) {
      NASD_THREADGROUP_WAIT_START(&nasd_od_mq_listener_tg);
      nasd_od_mq_stop_listeners();
      nasd_printf("DRIVE: error creating message queue listener %d: rc=0x%x (%s)\n",
	     i, rc, nasd_error_string(rc));
      return rc;
    }
    NASD_THREADGROUP_STARTED(&nasd_od_mq_listener_tg);
  }

  NASD_THREADGROUP_WAIT_START(&nasd_od_mq_listener_tg);

  return NASD_SUCCESS;
}

nasd_status_t
nasd_od_mq_stop_listeners(void)
{
  int i;
  nasd_msgq_buf_t mb;
  int rc;

  if (nasd_od_mq_count == 0) {
    /*
     * I put this check in because this was running even if
     * the system was never initted. It looks to me like
     * this should be registered as a shutdown proc before
     * launching the threads. --jimz
     */
    return(NASD_SUCCESS);
  }

  mb.mtype = NASD_MSGQ_KICK;

  NASD_THREADGROUP_INDICATE_SHUTDOWN(&nasd_od_mq_listener_tg);
  /* need to kick listener threads, since they're all blocked in
     msgrcv() -- and if not, this message will be ignored */
  for(i = 0; i < NASD_OD_MQ_LISTENER_THREADS; i++) {
    rc = nasd_msg_msgsnd(nasd_od_mq_mqid, (struct msgbuf *) &mb,
			 NASD_MSGQ_MSGLEN, IPC_NOWAIT);
    if(rc) {
      nasd_printf("DRIVE: unable to send shutdown kick message to thread %d: errno=%d (%s)\n",
	     i, errno, strerror(errno));
      continue;
    }
  }
  NASD_THREADGROUP_WAIT_STOP(&nasd_od_mq_listener_tg);
  /* might want to use a timeout mechanism like DCE does, but we're
     less likely than DCE to get hung up with a dead/crashed client. */
  return NASD_SUCCESS;
}

nasd_status_t
nasd_shutdown_msgq(void)
{
  return nasd_od_mq_stop_listeners();
}

nasd_status_t
nasd_od_mq_init(void)
{
  nasd_status_t rc;

#if NASD_OD_MQ_DEBUG > 0
  fprintf(stderr, "mq: nasd_od_mq_init\n");
#endif /* NASD_OD_MQ_DEBUG > 0 */

  if(nasd_od_mq_count == 0) {
    int flags;

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

    nasd_od_mq_mqid = msgget(NASD_MSGQ_KEY, flags);
#if NASD_OD_MQ_DEBUG > 0
    fprintf(stderr, "mq: queue created, id %d\n", nasd_od_mq_mqid);
#endif /* NASD_OD_MQ_DEBUG > 0 */
    if(nasd_od_mq_mqid < 0) {
      nasd_printf("DRIVE ERROR: cannot create nasd_od_mq_mqid (got %d errno %d)\n",
        nasd_od_mq_mqid, errno);
      return(NASD_FAIL);
    }

    rc = nasd_init_threadgroup(&nasd_od_mq_listener_tg);
    if(rc) {
      return(rc);
    }
    rc = nasd_shutdown_proc(nasd_odc_shutdown, nasd_od_mq_shutdown,
				(void *)((unsigned long)nasd_od_mq_mqid));
    if (rc) {
      nasd_od_mq_shutdown(NULL);
      return(rc);
    }
  }

  nasd_od_mq_count++;

  return NASD_SUCCESS;
}

void
nasd_od_mq_shutdown(void *arg)
{
  int m = (int)((unsigned long)arg);

  nasd_od_mq_count--;

  if(nasd_od_mq_count == 0) {
    nasd_destroy_threadgroup(&nasd_od_mq_listener_tg);
    msgctl(m, IPC_RMID, NULL);
  }

  return;
}


#endif /* !KERNEL */
