| We finally got a DECnet program working. It turned into a general purpose
message mover (I just can't stop :-) for network testing... so it may be
useful to people outside of the ``FS-II'' domain.
The program (in its simplest form) allows two people to connect by issuing
the following commands:
Person 1: % fs2
SERVER: Waiting for a connection...
Person 2: % fs2 child (node name of other user)
CLIENT: Connecting to server......
Both then get CONNECTED messages. At this point you are up and running. The
terminals have been switched into raw binary mode and FS-II can do it's bit.
When you wish to terminate a link, a string of '@@@@@' (5 at signs) on either
side of the link will break off connections for both parties.
The program has ALOT of switches. Here is a summary:
Usage: fs2 [options] [client-node]
Default: fs2 -p -g (if no switches but -a or -e)
-a = ASCII mode (instead of binary default)
-e = ECHO mode (instead of noecho default)
-l = local loopback
-r = remote loop back
-p = local input => remote output
-o fil = local input => file
-m fil = local,remote input => file
-g = local output <= remote input
-t fil = file <= remote input
-i fil = file replaces local input
-s fil = file replaces remote input
The options possible can be shown graphically:
I ---------- ------> O
\ /
\ /
------------> ---------- P -----> --------->
| \ ^
T | \ |
E | \ | S
R | \ | O
M \ C
I L ---> M <---- R K
N \ E
A | \ | T
L | \ |
V \ |
<------------ <--------- G ----- <---------
/ \
/ \
T <------ ------- S
For example, to just test the program you could use:
% fs2 -a -l
which would create an ASCII local loopback (echo typing).
% fs2 -r
would create a server that would echo anything comming over the DECnet link.
% fs2 -o foo.log
would create a log of a flight.
% fs2 -o foo2.log -s foo.log -g
would create a log (foo2) of this flight and at the same time play back
the previous log (foo) to my simulator.
There are LOTS of other possibilities. Note the following:
1) When pick any option (other than -a or -e) all defaults
will be turned off (-g and -p) so you must turn them back
on if you want them (like the last example).
2) The program is smart enough to recognize FS-II packets and
tries to write DECnet packets that are groups of FS-II
packets (if possible).
3) If you input from a file (-i or -s) at EOF both ends of
the link will terminate.
4) THE BIGGEE: This ONLY runs on VMS. Brad Morgan said he'd
look into doing a VMS version (schedule permitting) but
anyone who wants to take a crack at it is welcome to try.
5) Also note that the program uses SEQUENTAL STREAM protocol
and will probably have to change to SEQUENTIAL PACKET for
VMS.
Enjoy!
dave
/***************************************************************************
*
* FS2 - flight simulator host program (general DECnet interchange program)
*
* V1.0 880102 DBW - First hack
* V2.0 880109 DBW - First REAL version
*
* Switches and descriptors (descs[10]):
*
* -a = ASCII mode (as opposed to binary (default))
* -e = ECHO mode (as opposed to no echo (default))
*
* I ---------- ------> O (2)
* (8) \ /
* \ /
* ------------> ---------- P (0) -----> --------->
* | \ ^
* T | \ |
* E | \ | S
* R | \ | O
* M \ C
* I L (1) ---> M <---- R (5) K
* N (3,7) \ E
* A | \ | T
* L | \ |
* V \ |
* <------------ <--------- G (4) ----- <---------
* / \
* / \ (9)
* (6) T <------ ------- S
*
**************************************************************************/
#include<stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <sys/file.h>
#include <netdnet/dn.h>
#include <netdnet/dnetdb.h>
#include <fcntl.h>
#include <signal.h>
#include <errno.h>
#include <sgtty.h>
#define BUFFER_SIZE 1024
#define HIGHWATER (BUFFER_SIZE>>2)
#define no_eintr(x) while ((x) == -1 && errno == EINTR)
extern int errno;
extern char *optarg;
extern int optind;
int s = -1, ns = -1, acclen, dosock = 0, bin = 0, block = 0;
char buf1[BUFFER_SIZE],buf2[BUFFER_SIZE];
struct sockaddr_dn sockaddr, accsockaddr;
struct sgttyb ostate,nstate;
int descs[10];
int lflg = 0,
rflg = 0,
pflg = 0,
gflg = 0,
aflg = 0,
eflg = 0;
char *oflg = NULL,
*sflg = NULL,
*iflg = NULL,
*tflg = NULL,
*mflg = NULL;
ERROR(name)
char *name;
{
if (block) {
if (descs[8] != -1)
fcntl(descs[8],F_SETFL,fcntl(descs[8],F_GETFL,0) & ~FNDELAY);
if (descs[9] != -1)
fcntl(descs[9],F_SETFL,fcntl(descs[9],F_GETFL,0) & ~FNDELAY);
}
if (bin) stty(0, &ostate);
if (descs[2] != -1) close(descs[2]);
if (descs[3] != -1) close(descs[3]);
if (descs[6] != -1) close(descs[6]);
if (descs[8] != -1) close(descs[8]);
if (sflg) close(descs[9]);
if (s != -1) close(s);
if (ns != -1) close(ns);
if (name) {
perror(name);
/* freopen("fs2.debug","w",stderr);
* perror(name);
* fclose(stderr);
*/
exit(1);
}
exit(0);
}
msg(str)
char *str;
{
if (lflg || gflg) {
write(1,str,strlen(str));
if (!aflg) write(1,"\r",1);
}
}
main(argc,argv)
char **argv;
{
int i,opt,dodefault = 1;
for (i=0;i<10;i++) descs[i] = -1;
while ((opt=getopt(argc,argv,"aepglri:o:s:t:m:")) != EOF) {
if (opt != 'a' && opt != 'e') dodefault = 0;
switch (opt) {
case 'a': aflg++; break;
case 'e': eflg++; break;
case 'p': pflg++; break;
case 'g': gflg++; break;
case 'l': lflg++; break;
case 'r': rflg++; break;
case 'i': iflg = optarg; break;
case 'o': oflg = optarg; break;
case 's': sflg = optarg; break;
case 't': tflg = optarg; break;
case 'm': mflg = optarg; break;
default:
printf("Usage: fs2 [options] [client-node]\n");
printf("Default: fs2 -p -g (if no switches but -a or -e)\n\n");
printf(" -a = ASCII mode (instead of binary default)\n");
printf(" -e = ECHO mode (instead of noecho default)\n");
printf(" -l = local loopback\n");
printf(" -r = remote loop back\n");
printf(" -p = local input => remote output\n");
printf(" -o fil = local input => file\n");
printf(" -m fil = local,remote input => file\n");
printf(" -g = local output <= remote input\n");
printf(" -t fil = file <= remote input\n");
printf(" -i fil = file replaces local input\n");
printf(" -s fil = file replaces remote input\n\n");
ERROR("BAD COMMAND LINE");
}
}
if (dodefault) {
pflg++;
gflg++;
}
if (iflg)
if ((descs[8] = open(iflg,O_RDONLY,0644)) < 0) ERROR("bad -i file");
if (sflg)
if ((descs[9] = open(sflg,O_RDONLY,0644)) < 0) ERROR("bad -s file");
if (oflg)
if ((descs[2] = open(oflg,O_WRONLY|O_CREAT|O_TRUNC,0644)) < 0)
ERROR("bad -o file");
if (tflg)
if ((descs[6] = open(tflg,O_WRONLY|O_CREAT|O_TRUNC,0644)) < 0)
ERROR("bad -t file");
if (mflg)
if ((descs[3] = open(mflg,O_WRONLY|O_CREAT|O_TRUNC,0644)) < 0)
ERROR("bad -m file");
descs[7] = descs[3];
if (lflg) descs[1] = 1;
if (gflg) descs[4] = 1;
if (!iflg) descs[8] = 0;
if (pflg || rflg) dosock = 1;
if (!sflg && (gflg || mflg || tflg)) dosock = 1;
gtty(0, &ostate);
gtty(0, &nstate);
if (!aflg) {
nstate.sg_flags |= RAW;
nstate.sg_flags &= ~CRMOD;
}
if (!eflg)
nstate.sg_flags &= ~ECHO;
if (pflg || lflg || gflg || (oflg && !iflg)) {
stty(0, &nstate);
bin = 1;
}
else if (!iflg) descs[8] = -1;
if (dosock) {
if ((s = socket(AF_DECnet,SOCK_STREAM,0)) <= 0) ERROR(argv[0]);
sockaddr.sdn_family = AF_DECnet;
sockaddr.sdn_objnum = 173;
if (optind < argc) client(argv[optind]);
msg("SERVER: Waiting for client to connect\n");
if (bind(s,&sockaddr,sizeof(struct sockaddr_dn)) < 0) ERROR(argv[0]);
if (listen(s,1) < 0) ERROR(argv[0]);
no_eintr(ns=accept(s,&accsockaddr,&acclen));
msg("SERVER: Connected!\n");
if (pflg) descs[0] = ns;
if (rflg) descs[5] = ns;
if (!sflg) descs[9] = ns;
}
transfer();
if (dosock) msg("SERVER: Exiting...\n");
ERROR(NULL);
}
client(nodename)
char *nodename;
{
struct nodeent *nodep, *getnodebyname();
msg("CLIENT: Waiting for server to respond\n");
if ((nodep = getnodebyname(nodename)) == NULL) ERROR("fs2: Node unknown");
bcopy(nodep->n_addr,sockaddr.sdn_nodeaddr,nodep->n_length);
sockaddr.sdn_nodeaddrl = nodep->n_length;
if (connect(s,&sockaddr,sizeof(struct sockaddr_dn)) < 0) ERROR("fs2");
msg("CLIENT: Connected!\n");
if (pflg) descs[0] = s;
if (rflg) descs[5] = s;
if (!sflg) descs[9] = s;
transfer();
msg("CLIENT: Exiting...\n");
ERROR(NULL);
}
transfer() {
int i,j,k,a,flag,base1,base2,len1,len2,eof1,eof2;
void mysleep();
eof1 = eof2 = 0;
base1 = base2 = 0;
if (descs[8] != -1)
fcntl(descs[8],F_SETFL,fcntl(descs[8],F_GETFL,0)|FNDELAY);
if (descs[9] != -1)
fcntl(descs[9],F_SETFL,fcntl(descs[9],F_GETFL,0)|FNDELAY);
block = 1;
while (1) {
flag = 0;
if (descs[8] == -1) flag |= 1;
if (descs[9] == -1) flag |= 2;
while (flag != 3 && base1 < HIGHWATER && base2 < HIGHWATER &&
eof1 < 5 && eof2 < 5) {
if (descs[8] != -1) {
len1 = read(descs[8],&buf1[base1],BUFFER_SIZE-base1);
if (len1 == -1) {
if (errno != EWOULDBLOCK) ERROR("fs2");
flag |= 1;
}
if (len1 == 0) eof1 = 5;
for (i=0; i<len1; i++) {
if (buf1[base1+i] == '@') {
if (++eof1 >= 5) break;
}
else eof1 = 0;
}
if (len1 > 0) base1 += len1;
}
if (descs[9] != -1) {
len2 = read(descs[9],&buf2[base2],BUFFER_SIZE-base2);
if (len2 == -1) {
if (errno != EWOULDBLOCK) ERROR("fs2");
flag |= 2;
}
if (len2 == 0) eof2 = 5;
for (i=0; i<len2; i++) {
if (buf2[base2+i] == '@') {
if (++eof2 >= 5) break;
}
else eof2 = 0;
}
if (len2 > 0) base2 += len2;
}
}
if (base1) {
for (j=base1-2; j>=0; j--)
if (buf1[j] == 229 && buf1[j+1] < 10) break;
if (j<=0 || eof1 >= 5) j = base1;
for (i=0; i<4; i++)
if (descs[i] != -1) {
for(k=j; k; k -= a) {
a = write(descs[i],&buf1[j-k],k);
if (a == -1) {
if (errno != EWOULDBLOCK) ERROR("fs2");
mysleep(0,200000);
a = 0;
}
}
}
for (i=0; j<base1; j++,i++) buf1[i] = buf1[j];
base1 = i;
}
if (base2) {
for (j=base2-2; j>=0; j--)
if (buf2[j] == 229 && buf2[j+1] < 10) break;
if (j<=0 || eof2 >= 5) j = base2;
for (i=4; i<8; i++)
if (descs[i] != -1) {
for(k=j; k; k -= a) {
a = write(descs[i],&buf2[j-k],k);
if (a == -1) {
if (errno != EWOULDBLOCK) ERROR("fs2");
mysleep(0,200000);
a = 0;
}
}
}
for (i=0; j<base2; j++,i++) buf2[i] = buf2[j];
base2 = i;
}
if (eof1 >= 5 || eof2 >= 5) return;
if (flag == 3) {
if (iflg||sflg) return;
if (dosock && pflg && dnet_eof(descs[0])) return;
if (dosock && rflg && dnet_eof(descs[5])) return;
if (dosock && !sflg && dnet_eof(descs[9])) return;
mysleep(0,200000);
}
}
}
#define mask(s) (1<<((s)-1))
#define setvec(vec, a) vec.sv_handler = a; vec.sv_mask = vec.sv_onstack = 0
static int ringring;
void mysleep(n,n2)
unsigned n,n2;
{
int mysleepx(), omask;
struct itimerval itv, oitv;
register struct itimerval *itp = &itv;
struct sigvec vec, ovec;
if (n == 0 && n2 == 0) return;
timerclear(&itp->it_interval);
timerclear(&itp->it_value);
if (setitimer(ITIMER_REAL, itp, &oitv) < 0) return;
setvec(ovec, SIG_DFL);
omask = sigblock(0);
itp->it_value.tv_sec = n;
itp->it_value.tv_usec = n2;
if (timerisset(&oitv.it_value)) {
if (timercmp(&oitv.it_value, &itp->it_value, >)) {
oitv.it_value.tv_sec -= itp->it_value.tv_sec;
oitv.it_value.tv_usec -= itp->it_value.tv_usec;
if (oitv.it_value.tv_usec < 0) {
oitv.it_value.tv_usec += 1000000;
oitv.it_value.tv_sec--;
}
}
else {
itp->it_value = oitv.it_value;
oitv.it_value.tv_sec = 0;
oitv.it_value.tv_usec = 10000;
}
}
setvec(vec, mysleepx);
(void) sigvec(SIGALRM, &vec, &ovec);
ringring = 0;
(void) setitimer(ITIMER_REAL, itp, (struct itimerval *)0);
while (!ringring) sigpause(omask &~ mask(SIGALRM));
(void) sigvec(SIGALRM, &ovec, (struct sigvec *)0);
(void) setitimer(ITIMER_REAL, &oitv, (struct itimerval *)0);
}
static mysleepx()
{
ringring = 1;
}
|