/*
 * nasd_remote.h
 *
 * Basic remote execution prototype for NASD drive.
 * 
 * Note: You shouldn't need this header file outside of the drive
 *
 * Authors: David Rochberg, Erik Riedel
 */

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


/* 

This is a second implementation 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. In this
version, there are three new RPCs: remote_attach, remote_detach, and
remote_invoke.

remote_attach takes a nasdid, an operation name, and some arguments.

If the named operation is supported by the drive, the attach call
stores the arguments and calls the "attach" function specific to the
named operation.

After an operation has been attached to an object, remote_invoke
acts just like "read" except that the remote operation interposes
on the data sent by the drive.

Once a user is finished with an operation, "remote_detach" detaches
the function from the object.

In a production implementation, one would want to add a way to name a
specific invocation so that one could have multiple operations
attached to a given object.


More nitty-gritty:

First of all:  there is a sample application that performs Caesar
cipher "encryption" in nasd_remote.c  I recommend reading that as you
read this documentation.

Each operation is described as a nasd_remote_function_t, which has 6
elements: a name, and five functions associated with the operation.

attach: This gets called when an operation is attached to an object.
  It's passed a nasd_odc_ent_t with an invocation structure
  (nasd_remote_invocation_t) attached to it.  The following fields in
  the invocation structure are valid:
  
     f:  the nasd_remote_function_t that's being attached.
     args_otw:  a pointer to the arguments passed by the caller
     args_otw_len: length of args_otw;
     partnum: partition number of object being attached to
     nid: nasdid of object being attached to.
  
  Attach time is probably the right time to unmarshall the otw_args into
  the "args" field of the invocation structure.  (You probably also want
  to set args_len so you'll have something to pass to NASD_Free later
  on).  If you return a value other than NASD_SUCCESS from your attach
  function, the attach operation will be aborted and your return value
  passed back to the user.  If your function will need state or a mutex, 
  this is also probably the right place to initialize the state and
  mutex fields.  
  
  
detach:  This gets called when an operation is detached from an
  object.
  
  You should free all resources from attach here.


start:
  This is called at the beginning of a user call to remote invoke, and 
  is an opportunity to:
    1. replace the original NASD pipe operations with your own push 
       function
    2. lock any mutexes.

finish:
  This is an opportunity to:
     1.  Flush any data stored in your invocation state that must be
         sent to the client at end-of-call.  (For example, consider a
         frequent-sets style operation:  As data is pushed through the 
         push function, the operation accumulates state, and then, at
         the very end of the read, it sends a summary of that state
         over the wire).

push:  
   This is where the work happens.  If installed during the "start"
   function, your push function is called by the regular read function 
   every time it has a block to send to the client.  You can push data 
   to the client by calling invocation->original->push.
 */



#ifndef _NASD_REMOTE_H_
#define _NASD_REMOTE_H_ 1
#include <nasd/nasd_cache.h>
#include <nasd/nasd_types.h>
#include <nasd/nasd_pdrive.h>
#include <nasd/nasd_threadstuff.h>
#include <nasd/nasd_edrfs_types.h>
#include <nasd/nasd_pipe.h>

nasd_status_t nasd_remote_sysinit();

struct nasd_odc_ent_s;

typedef struct nasd_remote_invocation_s nasd_remote_invocation_t;

typedef struct nasd_remote_function_s nasd_remote_function_t;
struct nasd_remote_function_s {
  char			function_name[NASD_REMOTE_FUNCTION_NAME_SIZE];
  nasd_status_t		(*attach)(struct nasd_odc_ent_s *ne);
  nasd_status_t		(*detach)(struct nasd_odc_ent_s *ne);
  nasd_status_t		(*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);
  nasd_status_t		(*call_start)(struct nasd_odc_ent_s *ne);
  nasd_status_t		(*call_finish)(struct nasd_odc_ent_s *ne,nasd_len_t *out_len);
};



extern nasd_remote_function_t nasd_remote_dispatch_table[];

struct nasd_remote_invocation_s {
  nasd_procpipe_t		  pipe;
  nasd_procpipe_t		  *original;
  nasd_remote_function_t	  *f;
/* If we can be assured that nasd_marshall_* work in-place, then we can
   take out the args_otw stuff */
  nasd_byte_t			  *args_otw; 
  nasd_len_t			  args_otw_len; 
  nasd_byte_t			  *args;
  nasd_len_t                      args_len;
  nasd_byte_t			  *state;
  nasd_offset_t			  offset;
  nasd_partnum_t		  partnum;
  nasd_identifier_t		  nid;
  NASD_DECLARE_MUTEX(		  mutex);
  nasd_len_t			  bytes_written;
  struct nasd_remote_invocation_s *next;
};
  
nasd_freelist_t *remote_invocation_freelist;

nasd_status_t nasd_caesar_attach(struct nasd_odc_ent_s *ne);
nasd_status_t nasd_caesar_detach(struct nasd_odc_ent_s *ne);
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);
nasd_status_t nasd_caesar_start(struct nasd_odc_ent_s *ne);
nasd_status_t nasd_caesar_finish(struct nasd_odc_ent_s *ne,nasd_len_t *out_len);


#define NASD_REMOTE_DEBUG 1
#endif /* _NASD_REMOTE_H_ */



