/* r e x h s t - host network remote execution daemon */

#ifdef DOCUMENTATION

Title netrexdmn - remote execute system daemon - host side
Index netrexdmn - remote execute system daemon - host side

Synopsis
	.nf
	ins rexhst/task=<taskname>/cmd="<rmt node> <account> <passwd> <mode>"
	.f

Description

	This daemon connects to a remote node REX object providing
	a path for data to an from the task running on the remote
	node virtual terminal.

	REX starts this daemon with a unique task name used for future
	communication. The remote node name, and account name on the
	remote node and a password must also be passed. The daemon
	will use this to log in on the remote node.

	Further commands to the remote node are passed via S/R packets
	from REX.

	Four packet types are defined:
	.list
	.le ;SPAWN_CMD
	.le ;EXEC_CMD
	.le ;BYE
        .le ;QBYE
	.els

	SPAWN_CMD packets are used to run a task on a remote node
	and return exit status. REX connects to the daemon and the
	daemon emits the exit status of the remote task.

	EXEC_CMD packets are similar to spawn but the caller does
	not wait for the requested task to complete.

        BYE and QBYE log out the remote task; if BYE is used the
        remote logoff message is displayed. 

	The packet type is the 1st word of the received SR packet.

Network protocol

	The host side sends three types of network packets: a HELLO
	packet; a CMD_SPAWN packet; and a BYE packet.

	The HELLO packet is used to log in the virtual terminal on the
	remote side. See structure definition $RX_HELLO.

	The CMD_SPAWN packet passes a command line to be spawned
	on the remote VT. See $RX_SPAWN.

	The BYE and QBYE packets tell the remote side to log out 
	the VT and drop the connection.

	Packets received from the remote are: exit status; tty
	output.

	Exit status packets are converted to emit() status directives.

	Tty out packets carry the length and vfc from a VT: output. 
	Since the actual data output can be up to 8K, the data 
	portion of a VT: output is sent in the following packet.

	Incoming SR packets and network packets normally trigger AST's.
	During the startup HELLO processing, AST's are not used.
Bugs

	Interactive remote tasks can't be run yet.

#endif

#include <stdio.h>
#include <cx.h>
#include <stdnet.h>
#include <qiofun.h>
#include "rex.h"
                                                      
#define SIG_EFN  29	/* efn used to signal packet availible */
#define EXIT_EFN  28	/* efn signals diconnect complete */

#define AST_EFN  27	/* efn used for net qio's at ast state */

static struct $RX_TTYO net_pkt;	/* remote receive packet */
static struct $RX_EXSTAT *n_exsp;	/* ex stat pointer*/
static struct $RX_TTYO *n_ttyop;	/* iosb pointer */

static struct $vsrb {		/* received packet buffer */
      rad50 sndtsk[2];		/* sending task */
      int type;			/* packet type */
      word packet[MAXSRL-3];	/* packet content */
      } srbuf;

extern int $dsw;
extern int $$uic;
extern int $$nerr;
extern int $$exst;

static int inlogout = 0;	/* set to 1 to during logout */

static NIOV *rex_np;		/* network iov pointer */
static NIOV ast_niov;		/* niov for ast state calls */
static NIOV *ast_np;		/* niov pointer for ast's */

main(argc, argv)
int argc;
char *argv[];	/* [1]- node [2]-login name [3]-password [4]-mode */
{
  struct $RX_HELLO hel_pkt;	/* remote VT: login packet */
  extern int net_ast();

  /* link to remote side */

  if (lnk_rmt(argv[1]) == NULL) {
    tmsg('F', "couldn\'t link to remote object on %s\n", argv[1]);
    exits(EX$SEV);
  }

  /* log into remote side */

  zero(&hel_pkt, sizeof (struct $RX_HELLO));

/* set verbose or quiet mode */
  if (atoi(argv[4]) == QUIET) hel_pkt.type = RXQHELLO;
    else hel_pkt.type = RXHELLO;

  hel_pkt.vers = RXVERSN;
  strcpy(hel_pkt.acnt, argv[2]);	/* login account name */
  strcpy(hel_pkt.pswd, argv[3]);	/* password */

  if (put_net(&hel_pkt, sizeof (struct $RX_HELLO), rex_np) == NULL) {
    tmsg('F', "couldn\'t send hello packet err %o\n", $$nerr);
    exits(EX$SEV);
  }

  /* startup network packet processing */  

  n_exsp = n_ttyop = &net_pkt;	/* set pointers */

  if (gta_net(&net_pkt, sizeof (struct $RX_TTYO), ast_np, &net_ast) == NULL) {
    tmsg('F',"main:gta_net failed $$nerr = %o\n", $$nerr);
    exits(EX$SEV);
  }

  /* process commands till BYE packet is received */

  clef(EXIT_EFN);	/* clear exit flag */

  sr_process();

  wtse(EXIT_EFN);	/* wait for disconnect */

  cls_net();
  exit();		/* note - exit status in $$exst */
}

/* SR stuff ------------------------------------------------------ */

sr_ast()		/* set SIG_EFN on delivery of SR packet */
{
  astset();
  setf(SIG_EFN);
  astx(0);
}

sr_process()	/* process SR packets */
{
  int srlen;		/* length of received sr buffer */
  int sr_ast();

  /* set up to receive SR packets from REX */

  clef(SIG_EFN);		/* clear packets waiting flag */
  if (srda(&sr_ast) != IS_SUC) {
    tmsg('F', "srda failed ?? err %o \n", $dsw);
    exits(EX$SEV);
  }

  for (;;) {	/* loop till BYE packet breaks us out */
    wtse(SIG_EFN);	/* wait for SR packet */
    clef(SIG_EFN);
    while ((srlen = vrcd(0, &srbuf, MAXSRL)) > 0) {	/* try receives */
      switch (srbuf.type) {
	  case SPWN_CMD: {
	  		spwn_cmd(srbuf.packet, srlen - PKTHDR, RXSPAWN);
	  		break;
	  		}              
	  case BYE:	{                       
	  		bye_cmd(VERBOSE);
	  		return;
	  		}
	  case QBYE:	{
	  		bye_cmd(QUIET);
	  		return;
	  		}
	  case EXEC_CMD: {
	  		spwn_cmd(srbuf.packet, srlen - PKTHDR, RXEXEC);
	  		break;
	  		}

	  default:	{	/* ignore others */
	  		break;
	  		}

      }
    }
    if ((srlen & 0377) != IE_ITS) {	/* make sure err is no more packets */
      tmsg('F', "vrcd failed ?? err %o \n", srlen);
      exits(EX$SEV);
    }

  } /* end for (;;) */
}

spwn_cmd(buf, len, code)	/* spawn a command and return status */
char buf[];		/* command line buffer */
int len;		/* len of command line in WORDS */
int code;		/* spawn or exec */
{
  char tasknm[8];	/* task name to spawn */
  struct $RX_SPAWN spw_pkt;

  buf[len*2] = NULL;	/* make sure buffer is null terminated */

  strcpy(tasknm, "MCR...");

  /* set up spawn packet */

  spw_pkt.type = code;
  spw_pkt.vers = RXVERSN;	/* version - in case protocol changes */
  strcpy(spw_pkt.task, tasknm);		/* task to spawn */
  strcpy(spw_pkt.cmdlin, buf);		/* command line */

  if (put_net(&spw_pkt, sizeof (struct $RX_SPAWN), rex_np) == NULL) {
    tmsg('F', "couldn\'t send spawn packet err %o\n", $$nerr);
    exits(EX$SEV);
  }
  return;
}

bye_cmd(mode)	/* send BYE packet to remote */

int mode;	/* quiet or verbose */
{
  struct $RX_BYE bye_pkt;

/* set quiet or verbose mode */
  if (mode == QUIET) bye_pkt.type = RXQBYE;
    else bye_pkt.type = RXBYE;

  bye_pkt.vers = RXVERSN;

  if (put_net(&bye_pkt, sizeof (struct $RX_BYE), rex_np) == NULL) {
    tmsg('F', "couldn\'t send bye packet err %o\n", $$nerr);
    exits(EX$SEV);
  }
  inlogout = 1;
  return;
}

/* network stuff ------------------------------------------------- */

/* NOTE:
	a separate NIOV structure is set up for network functions
	that execute at AST state. This prevents interference between
	iosb's and efn's.
*/

lnk_rmt(node_name)
char *node_name;
{
  NIOV *con_obj();
  int netmsg();

  if(!opn_net(netmsg)) {	/* open network */
    tmsg('F',"lnk_rmt:opn_net failed $$nerr = %o\n", $$nerr);
    return(NULL);
  }

  if (!(rex_np = con_obj(node_name, REXOBJ))) {
    tmsg('F',"lnk_rmt:con_obj failed $$nerr = %o\n", $$nerr);
    return(NULL);
  }

  ast_np = &ast_niov;	/* set up alternate pointer */
  copy(ast_np, rex_np, sizeof (struct niov));
  ast_np->efn = AST_EFN;

  return(rex_np);
}

net_ast()	/* process NET packets */
{
  astset();

  switch (n_exsp->type) {
	  case RXEMIT: {
  			emst(0, n_exsp->ex_stat); /* report exit stat */
			if (inlogout) astx(1);	/* quit exit in logout */
	  		break;
	  		}
	  case RXTTYO: {
	  		ttyxfr(n_ttyop);
  			break;
	  		}

	  default:	{	/* ignore others */
	  		break;
	  		}
    }

/* issue network read for next packet */

  if (gta_net(&net_pkt, sizeof (struct $RX_TTYO), ast_np, &net_ast) == NULL) {
    tmsg('F',"main:gta_net failed $$nerr = %o\n", $$nerr);
    exits(EX$SEV);
  }
  astx(1);	/* pop addr of iosb */
}                              

ttyxfr(np_p)	/* handle tty pkt - CALLED AT AST STATE */
struct $RX_TTYO *np_p;	/* poinetr to rcvd network packet */
{
  char *ttyo_p;		/* pointer to tty buffer */
  word qio_parms[6];
  word ttiosb[2];
  int status;

/* allocate a buffer for tty data */

  if (np_p->buflen <= 0) {	/* make sure size ok */
    tmsg('E', "TTYO pkt with buflen = %d, vfc %o, vers %d\n",
    	np_p->buflen, np_p->vfc, np_p->vers);
    return;              
  } 

  if ((ttyo_p = malloc(np_p->buflen)) == NULL) {
    tmsg('E', "ttyxfr-no memory buflen = %d",	np_p->buflen);
    return;
  }

/* get buffer from remote */

  if (get_net(ttyo_p, np_p->buflen, ast_np) <= NULL) {
    tmsg('E', "ttyxfr-get_net err %o",	$$nerr);
    return;
  }

/* output buffer to ti; */

  qio_parms[0] = ttyo_p;		/* point to buffer */
  qio_parms[1] = np_p->buflen;		/* set length */
  qio_parms[2] = np_p->vfc;		/* set vertical format char */
  qio_parms[3] = qio_parms[4] = qio_parms[5] = 0;

  status = qiow(IO_WVB, fileno(stdout), 2, &ttiosb, 0, &qio_parms);
  if (status != IS_SUC) {
	tmsg('F', "ttyxfr tty QIOW failed err %o\n", (status & 0377));
	return;
  }

  free(ttyo_p);		/* give memory back */
  return;
}

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

  return(1);
}

finish(stat)	/* close everything and exit with status = stat */
int stat;
{
  $$exst = stat;
  if (inlogout) {
    setf(EXIT_EFN);
    return;
  }
  exit();	/* must be error */
}
