/* r e x r m t -- rex remote server */ 

#ifdef DOCUMENTATION

title	rexrmt	remote object for rex (Remote EXecute) system
index	rexrmt	remote object for rex (Remote EXecute) system

synopsis

	  ins rexrmt/task=rex$$$
	  ncp set object 129 name rex$$$ copies 4

description

	Remote side of REX facility. Provides a VT: to run tasks
	for a daemon on another node.
bugs


edit history
	.s.nf
	 06 01/13/86 hjj	Allocated fixed buffer for vt: qio's
				rather than malloc(). May cure parent
				aborts on child aborts.
         05 1/3/85		brake out rex.h stuff and eliminated
				protocol version check
         04 1/2/86 gwm		added QUIET and VERBOSE modes for login/logout
	 03 11/26/85 hjj	changed to quiet logout
	 02 11/26/85 hjj	changed to quiet login
	 01 11/17/85 hjj	moved esb's off stack for spawn requests
	.s.f
#endif

#include <stdio.h>
#include <cx.h>
#include <stdnet.h>
#include <qiofun.h>
#include <qiottdrv.h>
#include <timbuf.h>
#include "rex.h"

#define IE_DAO	0363	/* transfer rejected */
#define IE_REJ	0250	/* transfer rejected */
#define IE_DSQ	0246	/* disk quota exceed, tried to put too much */
#define IE_EOF	0366	/* end of file */                                     

#define SIG_EFN  29	/* efn used to signal packet availible */
#define SPW_EFN  28	/* efn used for spawn */
#define EXE_EFN  27	/* efn used for execute spawn */

#define AST_EFN  26	/* efn used during ast's for net fcns */

#define VTB_LEN	255	/* vt buffer len */

static struct $RX_SPAWN net_pkt;	/* host receive packet */

extern int $dsw;
extern int $$nerr;
extern int $$nlun;	/* number of luns available */

static NIOV *np;
static NIOV ast_niov;		/* niov for ast state calls */
static NIOV *ast_np;		/* niov pointer for ast's */

static struct timbuf timstamp;

extern char $$task[];

static int vt_lun;		/* lun for vt */
static int vt_unit;		/* unit number to use */
static word vt = 052126;		/* "VT" */
static word stopper = 0;

static word spwn_stat[8];	/* est for RXSPWN */
static word exe_stat[8];	/* esb for RXEXEC */

static int tty_on = QUIET;	/* set to VERBOSE when vt output allowed */

static char vt_buf[VTB_LEN];	/* buffer for vt qio's */

int $$nargs = 1;

main()
{
  int netmsg();                                               
  int len;

  if(!opn_net(netmsg)) {
    tmsg('F', "open net failed nerr %d\n", $$nerr);
    exits(4);
  }

  wtse(10);	/* wait for attach from other task */

  while ((len = get_net(&net_pkt, sizeof net_pkt, np)) != NULL) {

    switch(net_pkt.type) {	/* whats the message code */
	case RXQHELLO:	hello(&net_pkt, QUIET); break;
	case RXHELLO:	hello(&net_pkt, VERBOSE); break;
	case RXSPAWN:	spwn_cmd(1, &net_pkt); break;
	case RXBYE:	bye(VERBOSE); break;
	case RXQBYE:	bye(QUIET); break;
	case RXEXEC:	spwn_cmd(0, &net_pkt); break;
	default:	badmsg(net_pkt.type);
    }
    zero(&net_pkt, sizeof net_pkt);
    $$nerr = 0; len = 1;
  }
  finish(1);
}

static hello(hel_p, mode)
struct $RX_HELLO *hel_p;
int mode;				/* quiet or verbose */
{
  char cmd[132];
  int i;

  /* log in */
                        
  tty_on = mode;
  sprintf(cmd, "HELLO %s/%s\n", hel_p->acnt, hel_p->pswd);
  for (i = 0; i < strlen(cmd); i++) cmd[i] = toupper(cmd[i]);

  if (vtlogin("...HEL", cmd) != IS_SUC) {
    hostmsg('F', "couldn\'t log in\n");
    r_emst(EX$SEV);	/* tell host login failed */
    finish(EX$SEV);
  }
  r_emst(EX$SUC);	/* tell host login ok */
  tty_on = VERBOSE;
}

static spwn_cmd(wait_flag, spw_p)
int wait_flag;		/* if 1, wait for task to emit status */
struct $RX_SPAWN *spw_p;
{
  rad50 tname[2];	/* name of task to spawn */
  int status;
  char tasknm[8];	/* task name to spawn */

  /* spawn command line */

  strcpy(tasknm, spw_p->task);

  ascr50(6, "MCR...", tname);	/* convert to rad50 */

  if (wait_flag) 
    status = spwnx(&tname, 0, SPW_EFN, 0, spwn_stat, spw_p->cmdlin, 
  		strlen(spw_p->cmdlin), vt_unit, vt);
  else
    status = spwnx(&tname, 0, EXE_EFN, 0, exe_stat, spw_p->cmdlin, 
  		strlen(spw_p->cmdlin), vt_unit, vt);
  
  if (status != IS_SUC) {	/* should be good */
    hostmsg('F', "spwnx error %o task %s cmd <%s>\n", (status & 0377),
	tasknm, spw_p->cmdlin);
    r_emst(EX$SEV);
  }

  if (wait_flag) {
    wtse(SPW_EFN);
    r_emst(spwn_stat[0]);
  }
  else r_emst(EX$SUC);

  return;
}

static bye(mode)
int mode;			/* quiet or verbose */
{
  struct $RX_SPAWN bye_p;

  /* log out */

  strcpy(bye_p.task, "BYE");
  sprintf(bye_p.cmdlin, "BYE");

  tty_on = mode;		/* turn off logout messages if quiet */
  spwn_cmd(1, &bye_p); 

  finish(EX$SUC);
}

/* VT stuff ------------------------------------------------------ */

static vtlogin(task, cmdlin)
char *task;	/* task name to execute */
char *cmdlin;	/* command line for task */
{
  word tname[2];
  int status;

  if (cre_vt() != 1) {		/* create a virtual terminal */
    hostmsg('F', "cre_vt error %o\n", $dsw);
    finish(EX$SEV);
  }

  ascr50(6, task, tname);
  status = spwnx(&tname, 0, SPW_EFN, 0, spwn_stat, cmdlin, strlen(cmdlin),
		  vt_unit, vt);
  if (status != IS_SUC) {
    hostmsg('F', "spwnx error %o\n", (status & 0377));
    finish(EX$SEV);
  }

  status = wtse(SPW_EFN);

  return(spwn_stat[0]);
}


static cre_vt()		/* create a virtual terminal */
{
  int status;
  unsigned *lp, *a_lun();

  int in_ast(), out_ast(), at_ast();

  if ((lp = a_lun(&vt_lun)) != NULL) *lp = stdin;	/* mark in use */
  else {
    $dsw = IE_ILU;
    return(NULL);
  }

  if ((vt_unit = crvt(&in_ast, &out_ast, 0, VTB_LEN)) <= 0) {
    tmsg('F', "error %o creating virtual terminal\n", (vt_unit & 0377));
    return(0);
  }

  if ((status = alun(vt_lun, vt, vt_unit)) != IS_SUC) {
    tmsg('F', "alun failed for %s%o:, dsw = %o\n",
  	&vt, vt_unit, (status & 0377));
    return(0);
  }

  return(1);
}


static int out_ast()
{
  int status;
  word vtr_parms[6];
  int vt_iosb[2];

  astset();

  vtr_parms[1] = gtdp(1);	/* get VT requested byte count */

  if (vtr_parms[1] <= 0) {
	hostmsg('E', "VT req byte count %o\n", vtr_parms[1]);
/*	finish(EX$SEV);
*/
  astx(3);
  }

  if (vtr_parms[1] > 0) {	/* allocate a buffer */
    if (vtr_parms[1] > VTB_LEN) {
      hostmsg('F', "vt req. larger than buffer, %o bytes requested\n",
        vtr_parms[1]);
      vtr_parms[1] = VTB_LEN - 1;
    }
    vtr_parms[0] = vt_buf;
  }
  vtr_parms[2] = IS_SUC;	/* say xfr is sucessfull */
  vtr_parms[3] = vtr_parms[4] = vtr_parms[5] = 0;

  /* issue write request to controlling terminal */

  status = qiow(IO_RVB, vt_lun, vt_lun, &vt_iosb, 0, &vtr_parms);
  if (status != IS_SUC) {
    hostmsg('F', "VT read QIOW failed with error %o\n", (status & 0377));
/*	finish(EX$SEV);
*/
    astx(3);
  }
  if (vt_iosb[0] < 0) {
    hostmsg ('F', "I/O error %o reading VT:\n", (vt_iosb[0] & 0377));
/*	finish(EX$SEV);
*/
    astx(3);
  }

  if ((tty_on == VERBOSE) && (vt_iosb[1] > 0)) 
	ttynet(vt_buf, vt_iosb[1], gtdp(2)); /* send to host */

  astx(3);
}


static int at_ast()
{
astset();
astx(3);
}


static int in_ast()	/* vt wants input. get buffer and transfer down */
{

  int status;
  word in_parms[6];		/* in_ast qio param block */
  int vt_iosb[2];		/* io status block */

  astset();

  in_parms[0] = 1;	/* cb = 1, return read status */
  in_parms[1] = 0;	/* sw2, 0 byte count */
  in_parms[2] = IE_EOF; /* say ^z to VT */
  
  status = qiow(IO_STC, vt_lun, vt_lun, &vt_iosb, 0, &in_parms);
  if (status != IS_SUC) {
    tmsg('F', "VT STC QIOW failed with error %o\n", (status & 0377));
    finish(EX$SEV);
  }
  if (vt_iosb[0] < 0) {
    tmsg ('F', "I/O error %o writing VT:\n", (vt_iosb[0] & 0377));
    finish(EX$SEV);
  }
  astx(3);
}

static unsigned *a_lun(lnum)
int *lnum;
{
  unsigned *lp;		/* lun address pointer */
  int lcnt;

  lp = $$luns;
  for (lcnt = 0; lcnt < $$nlun; lcnt++, lp++) 
    if (*lp == NULL) {
      *lnum = lcnt + 2;		/* free luns start at lun 2 */
      return(lp);
    }

  return(NULL);
}

/* Network stuff ---------------------------------------------------- */

static r_emst(exstat)	/* retrun exit stat to host */
int exstat;
{
  struct $RX_EXSTAT exst_p;	/* initial packet */

  dsar();

  exst_p.vers = RXVERSN;
  exst_p.type = RXEMIT;
  exst_p.ex_stat = exstat;

  if (put_net(&exst_p, sizeof exst_p, np) == NULL) {
    tmsg('F', "couldn\'t send exst packet err %o\n", $$nerr);
    finish(EX$SEV);
  }
  enar();
}

static hostmsg(svr, argl)
int svr;	/* severity char */
{
  char outbuf[132];

  $$rtime(&timstamp);

  sprintf(outbuf, "\n%02d:%02d:%02d %s-%c-%r", timstamp.g_tihr, 
  	timstamp.g_timi, timstamp.g_tisc, $$task, svr, &argl);

  ttynet(outbuf, strlen(outbuf), " ");	/* return message to host */
}

static ttynet(tbuf, len, vfc) /* return tty buf to host */
char *tbuf;
int len;
char vfc;
{
  struct $RX_TTYO ttyo_p;	/* initial packet */

  dsar();		/* let code finish */

  ttyo_p.vers = RXVERSN;
  ttyo_p.type = RXTTYO;
  ttyo_p.buflen = len;
  ttyo_p.vfc = vfc;

  if (put_net(&ttyo_p, sizeof ttyo_p, ast_np) == NULL) {
    tmsg('F', "couldn\'t send ttyo packet err %o\n", ast_np->iosb[0]);
    finish(EX$SEV);
  }

  if (put_net(tbuf, len, ast_np) == NULL) {
    tmsg('F', "couldn\'t send tbuf packet err %o\n", ast_np->iosb[0]);
    finish(EX$SEV);
  }
  enar();		/* let asts in */
}

badmsg(p)
int *p;
{
  tmsg('M', "bad message code %o \n", *p);
}

netmsg(code, acp)
char code[4];		/* message type code */
struct a_conblk *acp;	/* pointer to received message */
{
  switch (code[1]) { 
      case NT_CON:	{
		accept(acp);
		setf(10);
	      break;
	      }    
      case NT_INT:	{
	      break;
	      }    
      case NT_DSC:	{
	      finish(4);
	      }    
      case NT_ABT:	{
	      finish(4);
	      }    
      case NT_ABO:	{
	      finish(4);
	      }    
    
      default: {
	      tmsg('W', "unexpected msg %d msglen %d lun %d\n",
			code[1], code[2], code[3]);
	      }
      }

  return(1);
}

accept(ap)
struct a_conblk *ap;	/* pointer to received message */
{
  NIOV *acc_net();

  if ((np = acc_net(ap)) == NULL) {
    tmsg('E', "accnt failed, err %o\n", $$nerr);
    return(0);
  }
  ast_np = &ast_niov;	/* set up alternate pointer */
  copy(ast_np, np, sizeof (struct niov));
  ast_np->efn = AST_EFN;

}

finish(stat)	/* close everything and exit with status = stat */
int stat;
{
  dsc_net(np);
  cls_net();
  exits(stat);
}
