/* r e x - remote execution control task */

#ifdef DOCUMENTATION

Title rex - remote execute control task
Index rex - remote execute control task

Synopsis
	.nf
	rex -l <localname> <node> <login name> <password>
	.s
	rex -h <localname> <node> <login name> <password>
	.s
	rex -b <localname>
	.s
	rex -o <localname>
	.s
	rex -s <localname> <command line>
	.s
	rex -e <localname> <command line>
	.s
	.f

Description

	REX allows logging into an running tasks on a remote system.
	Unlike RMT, more than one system at a time can be accessed.
	REX can also be run from an indirect command file and passes
	back exit status from remote tasks.

	All commands are passed to a daemon task that actually
	links to the remote system. REX only serves to control
	the daemon. Each daemon has a unique name 
	which is also its task name.

	To startup a daemon and login on a remote system, use the
	"-l" or "-h" options. For example, the line:
	.s.nf
		rex -l hobbit devsys bilbo pasword
	.f.s

	will start a daemon named "hobbit" connected to remote
	node "devsys" and login using the login name "bilbo"
	and the password "pasword". The "-l" option is quiet;
        the logon message is not displayed. The "-h" option
        displays the message.

	On a login request, REX will exit with the status returned
	by the remotely spawned hello task. Other errors, such as
	an inability to connect to the remote node will return
	a fatal exit status.

	Command lines can be sent to an established daemon using the
	"-s" (spawn) switch. For example, to run a task build
	through daemon "hobbit" would use the following command:
	.s.nf
		rex -s hobbit tkb @build.tkb
	.f.s

	Upon completion, the remote TKB exit status becomes the
	exit status of REX.

	The "-e" switch is like "-s", however the rex task and daemon
	do not wait for the spawned task to exit.

	Note that there is currently no facility to run a remote
	interactive task. If the remote task issues a read to the
	daemon, the task will be aborted.

	The "-b" or "-o" options will log out of a remote system and
	shut down the associated daemon. The "-o" option is "quiet";
        the logoff message is not displayed. The "-b" option displays
        the logoff message.
        
Bugs

	No interactive remote tasks.
	Little or no protection.

#endif


#include <stdio.h>
#include <cx.h>
#include <qiofun.h>
#include <qioret.h>
#include "rex.h"

#define SD_RAL	1	/* pass all offsping control blocks (for spawncmd) */

#define SPWN_EFN 30


extern int $dsw;
extern int $$uic;

main(argc, argv)
int argc;
char *argv[];
{
  char cmd[80];			/* created command line */
  rad50 daemon[2];		/* daemon name in r50 */

  switch (parse(daemon, cmd, argc, argv)) {
  	case 'l': exits(login(argv[2], argv[3], argv[4], argv[5], QUIET));
  	case 'h': exits(login(argv[2], argv[3], argv[4], argv[5], VERBOSE));
  	case 'b': exits(bye(daemon, QUIET));
  	case 'o': exits(bye(daemon, VERBOSE));
  	case 's': exits(spwncm(SPWN_CMD, daemon, cmd));
  	case 'e': exits(spwncm(EXEC_CMD, daemon, cmd));
  	default:  usage();
  }
}

/* parse - extracts daemon name and command line from argv[].
  	Returns switch character.
*/
parse(daetsk, cmdp, argc, argv)
rad50 daetsk[];		/* rad50 daemon name to return */
char *cmdp;		/* pointer to rest of command line */
int argc;		/* arg count */
char *argv[];		/* arg list */
{
  int i;
  int argidx;		/* index of next arg in argv[] */
  int rtnval;

  if (argc < 2) return(NULL);	/* must have atleast 2 args */

  if (*argv[1] == '-') {
    argidx = 2;
    rtnval = *++argv[1];	/* get switch value */
  }
  else {
    argidx = 1;
    rtnval = ' ';	/* default to data line */
  }

  ascr50(6, argv[argidx], daetsk);	/* convert daemon name */
  argidx++;

  *cmdp = NULL;			/* assume no command line */
  if (argidx == argc) return(rtnval);

  strcpy(cmdp, argv[argidx]);
  for (i = argidx+1; i < argc; i++) {
    strcat(cmdp, " ");
    strcat(cmdp, argv[i]);
  }
  return(rtnval);
}

/* login - spawns a new copy of the REXHST task with the requested name
 *	 - The command line passed to the daemon contains login data.
 *	 - The daemon emits status which is passed to REX's parent.
 */
login(daemon, node, acnt, pswd, mode)
char *daemon;	/* desired daemon name in ascii */
char *node;	/* remote node to connect to */
char *acnt;	/* login name */
char *pswd;	/* password */
int mode;	/* quiet or verbose */
{
  word exit_stat[8];	/* returned exit status */
  rad50 mcrtsk[2];	/* rad50 mcr... task name */
  char cmdlin[80];	/* buffer for daemon command line */
  
  sprintf(cmdlin, "run %s/task=%s/cmd=\"dmn %s %s %s %d\"\033", 
  		REX_HOST, daemon, node, acnt, pswd, mode);

  ascr50(6, "MCR...", mcrtsk);	/* convert MCR... name */
  if (spwn(&mcrtsk, $$uic, SPWN_EFN, 0, exit_stat, 
  			cmdlin, strlen(cmdlin)) != IS_SUC) {
    tmsg('F', "spwnx error %o\n", ($dsw & 0377));
    return(EX$SEV);
  }

  wtse(SPWN_EFN);	/* wait for daemon to emit status */
  return(exit_stat[0]);	/* return emitted status */
}

bye(daetsk, mode)
rad50 daetsk[];
int mode;		/* quiet or verbose */
{
  char temp[8];		/* used for err msg */
  struct $sr {		/* sr buffer */
      int type;
  } srbuf;

    zero(temp, sizeof temp);
    r50toa(temp, daetsk, 2);

/* select quiet or verbose mode */
  if (mode == QUIET) srbuf.type = BYE;
    else srbuf.type = QBYE;

  if (vsda(daetsk, &srbuf, sizeof (struct $sr), 0) != IS_SUC) {
    zero(temp, sizeof temp);
    r50toa(temp, daetsk, 2);
    tmsg('F', "can\'t send bye packet to %s, err %o\n",
  	temp, $dsw);
    return(EX$SEV);
  }

  return(EX$SUC);
}

spwncm(code, daetsk, cmdp)
int code;	/* command code, SPWN_CMD or EXEC_CMD */
rad50 daetsk[];
char *cmdp;
{
  word exit_stat[8];	/* returned exit status */
  char temp[8];		/* used for err msg */
  int stat;		/* send status */
  struct $sr {		/* sr buffer */
      int type;
      char cmdbuf[132];	/* space for command line */
  } srbuf;                                    

  zero(temp, sizeof temp);
  r50toa(temp, daetsk, 2);

  srbuf.type = code;
  strcpy(srbuf.cmdbuf, cmdp);

  if (code == SPWN_CMD)
    stat = vsrc(daetsk, &srbuf, (sizeof srbuf)/2, SPWN_EFN, 0, exit_stat);
  else
    stat = vsda(daetsk, &srbuf, (sizeof srbuf)/2, 0);

  if (stat != IS_SUC) {
    zero(temp, sizeof temp);
    r50toa(temp, daetsk, 2);
    tmsg('F', "can\'t send data packet to %s, err %o\n",
  	temp, $dsw);
    return(EX$SEV);
  }

  if (code == SPWN_CMD) {
    wtse(SPWN_EFN);	/* wait for daemon to emit status */
    return(exit_stat[0]);	/* return emitted status */
  }
  else return(EX$SUC);
}

usage()
{
  tmsg('F',"usage: rex [-lbse] command line\n");
  exits(EX$SEV);
}
