Listing 3: Remote DCL (rdcl) Program

/*====================================================
 AUTHOR: Dave Brillhart
 FILE:   rdcl.c
 -----------------------------------------------------
 This C program will allow a DECnet capable UNIX
 host to execute a remote DECnet object much like
 the "rsh" command available via TCP/IP. This code
 can execute a single DCL command (batch) or a series
 of interactive commands. In interactive mode, a
 blank line will terminate the program.
 -----------------------------------------------------
 Compile this program on an Ultrix host using:
   # cc rdcl.c -ldnet -o rdcl
 -----------------------------------------------------
 Use this program as follows (authenication depends
 on proxey setup):
   # rdcl <nodename>[/<username>/[<password>]] [cmd]
====================================================*/

/*--------------------------------------------------*/
#include <stdio.h>
#include <sys/types.h>        /* dnet_conn & select */
#include <sys/socket.h>                /* dnet_conn */
#include <netdnet/dn.h>                /* dnet_conn */
#include <sys/time.h>                     /* select */
/*--------------------------------------------------*/
#define BUFSIZE   2048    /* read/write buffer size */
#define READWAIT  3.0    /* read timeout in seconds */
#define OBJECT    "dnet_cmd"  /* DECnet object name */
#define NODESIZE  16   /* max DECnet node name size */
#define E_F       '\004'               /* EOF == ^D */
/*--------------------------------------------------*/
int main(argc,argv)
  int argc; char *argv[];
{
  int sock;                /* socket for connection */
  char node[NODESIZE];          /* DECnet node name */
  int i;                      /* local loop counter */
  char buff[BUFSIZE];            /* buffer for data */

  if (argc < 2) {
    printf("Usage: %s nodename [cmd]\n",argv[0]);
    exit(1);
  }
  /*------------------------------------------------*/
  /* Connect with the remote DECnet object. These   */
  /* 'dnet_conn' and 'nerror' calls are the only    */
  /* ones that depend on the "dnet" library         */
  /*------------------------------------------------*/
  strcpy(node,argv[1]);
  if ((sock=dnet_conn(node,OBJECT,0,0,0,0,0)) < 0) {
    nerror(argv[0]);         /* DECnet error mapper */
    exit(1);
  }
  /*------------------------------------------------*/
  /* If ARGC >=3 then a batch command was issued.   */
  /* If ARGC < 3 then interactive mode is desired.  */
  /*------------------------------------------------*/
  buff[0] = '\0';   /* initialize buffer for strcat */
  if (argc >= 3) {   /* construct the batch command */
    for (i=3; i<=argc; i++) {
      strcat(buff,argv[i-1]);      /* append tokens */
      if (i != argc) strcat(buff," "); /* insert WS */
    }
    execute_batch_cmd(sock,node,buff,'b');
  }
  else        /* prompt loop - interactive commands */
    while(1) {
      printf("\nEnter Command: "); gets(buff);
      if (execute_batch_cmd(sock,node,buff,'i') < 0)
        break;
    }
  /*------------------------------------------------*/
  /* Finished - Close network connection and exit   */
  /*------------------------------------------------*/
  puts("Exiting...\n");
  close(sock);
  exit(0);
}

/*--------------------------------------------------*/
/* Execute this single VMS command, close the       */
/* connection, and terminate.                       */
/*--------------------------------------------------*/
int execute_batch_cmd(sock,node,buff,mode)
  int sock; char *node,*buff,mode;
{
  int len;                        /* length of data */
  int selRtn;            /* return value for select */
  char EOFstr[2];             /* EOF str for socket */

  sprintf(EOFstr,"%c%c",E_F,'\0');

  if ((len=strlen(buff)) <= 0) { /* empty - snd EOF */
    strcpy(buff,EOFstr); len=1;
  }
  else
    printf("\nSENDING CMD: %s to %s\n",buff,node);
  if(write(sock,buff,len) < 0) {
    perror("Error: can't send cmd across network");
    return(-1);
  }
  if (mode == 'b') write(sock,EOFstr,1); /* snd EOF */

  /*------------------------------------------------*/
  /* Read data coming back from the VMS host        */
  /*------------------------------------------------*/
  do {
    selRtn = config_select(sock);
    switch(selRtn) {
      case -1:                     /* select failed */
        printf("Error: select failed.\n");
        return(-2);
      case 0:                   /* socket timed out */
        printf("Warning: select timed out.\n");
        return(-3);
      default:          /* socket ready for read... */
        if ((len=read(sock,buff,BUFSIZE)) < 0) {
          perror("Error: can't read from network");
          return(-4);
        }
        buff[len]='\0'; /* return string needs null */
        if (strcmp(buff,"EOC") == 0 && mode == 'i')
          return(0);     /* return for next command */
        if (strcmp(buff,"EOC") != 0 && len > 0)
          puts(buff);    /* print appropriate lines */
    }
  } while (len > 0);
  return(-5);
}

/*--------------------------------------------------*/
/* configure "select" for a short timeout and have  */
/* it watch the socket file descriptor for activity */
/*--------------------------------------------------*/
int config_select(int socket)
{
  struct timeval timer;  /* timeout struct - select */
  fd_set rset;    /* file descriptor set for select */

  timer.tv_sec = READWAIT; timer.tv_usec = 0.0;
  FD_ZERO(&rset); FD_SET(socket,&rset);

  return(select(getdtablesize(),&rset,0,0,&timer));
}

