/*
 * nasd_remote.c
 *
 * Basic remote execution prototype for user-level drive
 *
 * Authors: Erik Riedel, David Rochberg
 */

/*
 * 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_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_pdrive.h>
#include <nasd/nasd_timer.h>
#include <nasd/nasd_remote.h>
#include <nasd/nasd_remote_types.h>
#include <nasd/nasd_remote_types_marshall.h>
#include <nasd/nasd_shutdown.h>

/*  This is a second version of the NASD remote execution prototype.
    The first version (upon which Riedel's thesis results are based)
    was for an older version of the NASD code base.  

    This version exports the same basic functionality as the first
    one, namely a means to interpose functionality along the data path
    of a read call.  

    However, the implementation is substantially different.  See
    nasd_remote.h for more documentation. */


nasd_remote_function_t nasd_remote_dispatch_table[] = {
  {"caesar_test",
   nasd_caesar_attach,nasd_caesar_detach,nasd_caesar_push,nasd_caesar_start,nasd_caesar_finish},
  {"\0",
   NULL,NULL,NULL,NULL,NULL}
};



void
nasd_remote_stop(void *ignored)
{
  NASD_FREELIST_DESTROY(remote_invocation_freelist,next,(nasd_remote_invocation_t *));
  nasd_printf("DRIVE: nasd_remote_stop:  remote invocation subsystem stopped\n");
}



nasd_status_t
nasd_remote_sysinit() 
{
  nasd_status_t		rc;
  NASD_FREELIST_CREATE(remote_invocation_freelist,4,2,sizeof(nasd_remote_invocation_t));
  if (remote_invocation_freelist == NULL) {
    return(NASD_NO_MEM);
  }
  NASD_FREELIST_PRIME(remote_invocation_freelist, 2 ,next,
		      (nasd_remote_invocation_t *));
  
  rc = nasd_shutdown_proc(nasd_odc_shutdown,nasd_remote_stop, NULL);
  if (rc) 
    return(rc);
  
  nasd_printf("DRIVE: active disk subsystem initialized \n"); /*DB*/
  
  return(NASD_SUCCESS);
}



nasd_status_t nasd_remote_pull_error(void *state_arg,
               void *buf,
               nasd_len_t len,
               nasd_len_t *out_len,
               nasd_byte_t *out_digest,
               int *digest_valid,
               void (*commit)(void *, nasd_offset_t, nasd_len_t),
               void *commit_rock)
{
  nasd_printf("nasd_remote_pull_error called.  aborting.");
  NASD_ASSERT(0);
  return NASD_FAIL;
}



/* nasd_caesar remote "application" */

nasd_status_t nasd_caesar_attach(struct nasd_odc_ent_s		 *ne)
{
  nasd_remote_caesar_args_t		*args;
  nasd_debug_breakpoint();
  ne->invocation->args_len = sizeof(nasd_remote_caesar_args_t);
  NASD_Malloc(ne->invocation->args,ne->invocation->args_len,(nasd_byte_t *));
  args = (nasd_remote_caesar_args_t *)ne->invocation->args;
  /* We'd love to cast to nasd_remote_caesar_args_otw_t, but that's an array type, so void* it is */
  nasd_remote_caesar_args_t_unmarshall((void*)ne->invocation->args_otw,
				       (nasd_remote_caesar_args_t *)ne->invocation->args);
  
  if (args->marshall1000 != 1000) {
    return NASD_REMOTE_BAD_MARSHALL;
  }
  nasd_mutex_init(&ne->invocation->mutex);
  return NASD_SUCCESS;
}

nasd_status_t nasd_caesar_detach(struct nasd_odc_ent_s *ne)
{
  nasd_mutex_destroy(&ne->invocation->mutex);
  NASD_Free(ne->invocation->args,ne->invocation->args_len);
  return NASD_SUCCESS;
}

nasd_status_t nasd_caesar_push (void *state_arg,
				void *input_buffer,
				nasd_len_t input_len,
				nasd_byte_t *in_digest,
				nasd_byte_t *out_digest,
				int *digest_valid)
{
#define NASD_CAESAR_BUF_SIZE 8192
  nasd_remote_invocation_t    *invocation = (nasd_remote_invocation_t *)state_arg;
  nasd_remote_caesar_args_t   *args=(nasd_remote_caesar_args_t *)invocation->args;
  nasd_byte_t		       buf[8192];
  nasd_len_t		       output_len=0;
  char			       *in = input_buffer;
  char			       *out = buf;
  char			       base;

  /* I believe this to be true.  If not, the right thing to do is to
     split big pushes into a series of smaller pushes */
  NASD_ASSERT(input_len <= NASD_CAESAR_BUF_SIZE); 

  while (input_len-- > 0) {
    base = 0;
    if (*in >= 'a' && *in <= 'z') {
      base = 'a';
    } else if (*in >= 'A' && *in <= 'Z') {
      base = 'A';
    }
    *out = base ? base + ((*in - base + args->key) % 26) : *in;
/*CO    *out = base ? 'a' : *in;*/
    out++;in++;output_len++;
  }
  invocation->bytes_written += output_len;
  
  return invocation->original->push(invocation->original->state,
				    buf,
				    output_len,
				    in_digest,
				    out_digest,
				    digest_valid);
}
nasd_status_t nasd_caesar_start(struct nasd_odc_ent_s *ne)
{
  nasd_remote_invocation_t	*invocation=ne->invocation;
  invocation->pipe.push = nasd_caesar_push;
  invocation->pipe.pull = nasd_remote_pull_error;
  invocation->pipe.state = invocation;
  NASD_LOCK_MUTEX(invocation->mutex);
  return NASD_SUCCESS;
}
nasd_status_t nasd_caesar_finish(struct nasd_odc_ent_s *ne,nasd_len_t *len)
{
  nasd_remote_invocation_t	*invocation=ne->invocation;
  invocation->pipe.push = NULL;
  invocation->pipe.pull = NULL;
  invocation->pipe.state = NULL;
  NASD_UNLOCK_MUTEX(invocation->mutex);

  /* this particular app doesn't need to do this; this is just an
     example of what to do if the app returns a different number of bytes
     than were requested */
  *len = invocation->bytes_written;		
  return NASD_SUCCESS; 
}



