/*
 r m t f i o 
*/

#ifdef DOCUMENTATION

title	rmtfio	block mode remote file access routines
index	rmtfio	block mode remote file access routines

synopsis
	.s.nf.
	_#include <stdio.h>
	_#include <cx.h>
	_#include <stdnet.h>
	.s
	NFILE 
	*rmt_opn(ndp, flp, options, inisiz, recsiz)
	char *ndp, *flp, *options;
	int inisiz, recsiz;
	.s
	rgetb(bp, siz, lbn, rfi)
	char *bp;
	int siz;
	int lbn;
	NFILE *rfi;
	.s
	rputb(bp, siz, lbn, rfi)
	char *bp;
	int siz;
	int lbn;
	NFILE *rfi;
	.s
	rclose(rfi)
	NFILE *rfi;
	.s.f
description

	Remote file access server for block mode file access.
	Must be used with the special AP nfar routines provided
	in the CN library. See fopnxn(), rgetb(), rputb().

bugs

	There is a race condition possible in the rclose() exchange
	where a nt_abo will be received when the task using the
	rfa exits before the close completes. Note that rclose()
	does not retrun the real close status.

#endif

#include <stdio.h>
#include <cx.h>
#include <stdnet.h>

#define RFAOBJ	128		/* object # of RFA task */

#define IE_NNT	0242

#define C_OPEN	1
#define C_CLOSE	2
#define C_GET	3
#define C_PUT	4

OPNSTS $rstat;
static OPNSTS *rfs = &$rstat;

struct r_fopnp {	/* remote fopen packet */
	int code;		/* function code */
	char filnam[32];
	char mode[16];
	int inisiz;
	int recsiz;
};

static struct r_fopnp ropnpkt;
static struct r_fopnp *rfp = &ropnpkt;

struct r_xfrp {		/* remote transfer req packet */
	int code;		/* function code */
	int siz;
  	int lbn;
};

extern int $$ferr;
extern int $$nerr;

static NFILE rf_head;
static int first = 1;

/*
  functions
*/

NFILE 
*rmt_opn(ndp, flp, options, inisiz, recsiz)
char *ndp;
char *flp;
char *options;
int inisiz;
int recsiz;
{
  NFILE *rfi;
  NFILE *con_rfa();

  if (first) {		/* on first call, set rf_head */
    zero(&rf_head, (sizeof (NFILE)));
    first = NULL;
  }

  if (!(rfi = con_rfa(ndp))) return(NULL);

  zero(rfp, (sizeof (struct r_fopnp)));

  rfp->code = C_OPEN;		/* set up an open packet */
  strcpy(rfp->filnam, flp);
  strcpy(rfp->mode, options);
  rfp->inisiz = inisiz;
  rfp->recsiz = recsiz;

  if (!sndrfa(rfp, (sizeof (struct r_fopnp)), rfi)) {
    tmsg('E', "can't sent rmt opn pkt err %o \n", $$nerr);
    return(NULL);
  }
  if (!getrfa(rfs, (sizeof (OPNSTS)), rfi)) {
    tmsg('E', "can't rcv rmt opn sts pkt err %o \n", $$nerr);
    return(NULL);
  }

  if (($$ferr = rfs->$$ferr) != 1) return(NULL);
  else return(rfi);
}

/*


*/

rgetb(bp, siz, lbn, rfi)
char *bp;
int siz;
int lbn;
NFILE *rfi;
{
  int errcod;
  struct r_xfrp *rqp;

  zero(rfp, (sizeof (struct r_fopnp)));
  rqp = rfp;

  rqp->code = C_GET;		/* set up get req packet */
  rqp->siz = siz;
  rqp->lbn = lbn;

  if (!sndrfa(rqp, (sizeof (struct r_xfrp)), rfi)) return(NULL);
  if (!getrfa(&errcod, 2, rfi)) return(NULL);
  if (($$ferr = errcod) != 1) return(NULL);

  return(getrfa(bp, siz, rfi));
}

/*


*/

rputb(bp, siz, lbn, rfi)
char *bp;
int siz;
int lbn;
NFILE *rfi;
{
  int errcod;
  struct r_xfrp *rqp;

  zero(rfp, (sizeof (struct r_fopnp)));
  rqp = rfp;

  rqp->code = C_PUT;		/* set up put req packet */
  rqp->siz = siz;
  rqp->lbn = lbn;

  if (!sndrfa(rqp, (sizeof (struct r_xfrp)), rfi)) return(NULL);
  if (!getrfa(&errcod, 2, rfi)) return(NULL);

  if (($$ferr = errcod) != 1) {
    tmsg('E', "rputb - rfa return err %o \n", errcod);
    return(NULL);
  }
  if(!sndrfa(bp, siz, rfi)) return(NULL);;
  if (!getrfa(&errcod, 2, rfi)) return(NULL);
  return((errcod != 1)?0:1);
}

/*


*/

rclose(rfi)
NFILE *rfi;
{
  zero(rfp, (sizeof (struct r_fopnp)));
  rfp->code = C_CLOSE;		/* set up a close packet */
  if (!sndrfa(rfp, (sizeof (struct r_fopnp)), rfi)) return(NULL);

  return(retrfi(rfi));
}

/*

Internal routines

*/

static int sndrfa(pktp, pktsiz, rfi)
char *pktp;
int pktsiz;
NFILE *rfi;
{
  if (!put_net(pktp, pktsiz, rfi->np)) {
    rfi->$$ferr = $$ferr = $$nerr;
    tmsg('W', "sndrfa err %o iosb %o %o \n", rfi->$$ferr, rfi->np->iosb[0], 
	rfi->np->iosb[1]);
    return(NULL);
  }
  return(pktsiz);
}

static int getrfa(pktp, pktsiz, rfi)
char *pktp;
int pktsiz;
NFILE *rfi;
{
  int glen;

  if (!(glen = get_net(pktp, pktsiz, rfi->np))) {
    rfi->$$ferr = $$ferr = $$nerr;
    tmsg('W', "getrfa err %o iosb %o %o \n", rfi->$$ferr, rfi->np->iosb[0], 
	rfi->np->iosb[1]);
    return(NULL);
  }
  return(glen);
}

static NFILE *con_rfa(namp)
char *namp;	/* remote node name */
{
  NFILE *getrfi();
  NIOV *np, *try_con();

  if (!(np = try_con(namp))) return(NULL);

  return(getrfi(np));
}

static NFILE *getrfi(np)
NIOV *np;
{
  NFILE *rfi, *hp;

  if (!(rfi = malloc(sizeof (NFILE)))) {
    $$ferr = -39;	/* IE.NBF - no room */
    return(NULL);
  }
  zero(rfi, (sizeof (NFILE)));

  rfi->np = np;		/* logical link iov */

  for (hp = &rf_head; (hp->lp != 0); hp = hp->lp) ; 
  hp->lp = rfi;		/* link into list */
}

static int retrfi(rfi)
NFILE *rfi;
{
  NFILE *hp;

  for (hp = &rf_head; hp->lp != 0; hp = hp->lp) { /* find in chain */
    if (hp->lp == rfi) {		/* found it */
      hp->lp = rfi->lp;			/* remove from list */
      if (!dsc_net(rfi->np, 0)) 	/* return link structure */
	tmsg('W', "retrfi - dsc_net err %o \n", $$nerr);
      mfree(rfi);		/* give memory back */
      return(1);
    }
  }
  tmsg('E', "retrfi-rfi not in list addr %o \n", rfi);
  return(NULL);
}

static NIOV *try_con(ndp)
char *ndp;
{
  NIOV *np;

  if ((np = con_obj(ndp, RFAOBJ))) return(np);

  if ($$nerr != IE_NNT) {	/* only no net open allowed */
    $$ferr = $$nerr;	/* make net err a file err */
    return(NULL);	/* other connect errors just return */
  }

  /* if opn_net succeeds, call ourself again, else die */

  if(opn_net(0)) return(try_con(ndp));	/* try again */

  tmsg('F', "can't open network, err %o\n", $$nerr);
  exits(4);
}
