V10/cmd/cu.c
#include <stdio.h>
#include <signal.h>
#include <sys/ttyio.h>
#include <sys/filio.h>
#include <ctype.h>
/*
* cu telno [ class ]
* Escape with `~' at beginning of line.
* Ordinary diversions are ~<, ~> and ~>>.
* Silent output diversions are ~>: and ~>>:.
* Terminate output diversion with ~> alone.
* Output command requests are ~! and ~:! (silent).
* Quit is ~. and ~! gives local command or shell.
* Also ~$ for canned procedure pumping remote.
* ~%put from [to] and ~%take from [to] invoke builtins
*/
char CRLF[2] = {'\r', '\n'};
#define equal(s1,s2) (strcmp(s1, s2)==0)
char *cunfile;
int ln; /* fd for comm line */
char tkill, terase; /* current input kill & erase */
char c;
int intr;
int nhup;
int nflag;
int tandm;
int hduplx;
int errflg;
int speed = B9600; /* used only for direct */
int parity = 0;
int chan[2]; /* interprocess channel */
extern int optind, opterr;
extern char *optarg;
struct sgttyb realtty;
struct tchars realtch;
int sig2();
char *connmsg[] = {
"",
"ACU busy",
"call dropped",
"no carrier",
"can't fork",
"acu access",
"tty access",
"tty hung",
"usage: cu [-hnt] telno [ class ]",
"unknown service class",
"stuff dk error message here", /* hack */
};
extern char *errstr;
struct dial {
char *telno;
char *dialtype;
char *comment;
};
char partab[] = {
0001,0201,0201,0001,0201,0001,0001,0201,
0202,0004,0003,0201,0005,0206,0201,0001,
0201,0001,0001,0201,0001,0201,0201,0001,
0001,0201,0201,0001,0201,0001,0001,0201,
0200,0000,0000,0200,0000,0200,0200,0000,
0000,0200,0200,0000,0200,0000,0000,0200,
0000,0200,0200,0000,0200,0000,0000,0200,
0200,0000,0000,0200,0000,0200,0200,0000,
0200,0000,0000,0200,0000,0200,0200,0000,
0000,0200,0200,0000,0200,0000,0000,0200,
0000,0200,0200,0000,0200,0000,0000,0200,
0200,0000,0000,0200,0000,0200,0200,0000,
0000,0200,0200,0000,0200,0000,0000,0200,
0200,0000,0000,0200,0000,0200,0200,0000,
0200,0000,0000,0200,0000,0200,0200,0000,
0000,0200,0200,0000,0200,0000,0000,0201
};
/*
* spawn child to invoke rd to read from line, output to fd 1
* main line invokes wr to read tty, write to line
*/
main(ac, av)
char *av[];
{
int fk;
struct sgttyb stbuf;
struct ttydevb tdbuf;
struct dial d;
signal(SIGPIPE, SIG_IGN);
d.telno = NULL;
d.dialtype = NULL;
d.comment = "";
options(ac, av);
if (optind >= ac || errflg) {
prf(connmsg[8]);
exit(8);
}
ioctl(0, TIOCGETP, &realtty);
ioctl(0, TIOCGETC, &realtch);
gettelno(av[optind], &d);
if (optind+1 < ac)
d.dialtype = av[optind+1];
if (d.dialtype==NULL || *d.dialtype=='\0')
d.dialtype = "1200";
if (nflag) {
printf("%s %s %s\n", d.telno, d.dialtype, d.comment);
exit(0);
}
if (equal(d.dialtype, "direct")) {
ln = open(d.telno, 2);
if (ln>=0) {
struct tchars tcr;
ioctl(ln, TIOCGETP, &stbuf);
stbuf.sg_flags &= ~ECHO;
stbuf.sg_flags |= RAW|EVENP|ODDP;
stbuf.sg_ispeed = speed; /* obsolete */
stbuf.sg_ospeed = speed; /* obsolete */
ioctl(ln, TIOCGETC, &tcr);
tcr.t_stopc = '\027';
tcr.t_startc = '\031';
ioctl(ln, TIOCGDEV, &tdbuf);
tdbuf.ispeed = tdbuf.ospeed = speed;
tdbuf.flags |= F8BIT|EVENP|ODDP;
ioctl(ln, TIOCSETP, &stbuf);
ioctl(ln, TIOCSETC, &tcr);
ioctl(ln, TIOCSDEV, &tdbuf);
ioctl(ln, TIOCHPCL, 0);
ioctl(ln, TIOCEXCL, 0);
}
} else{
ln = dialout(d.telno, d.dialtype);
if(ln==-1 && errstr){
ln=-10;
connmsg[10]=errstr;
}
}
if (ln < 0) {
prf("Connect failed: %s",connmsg[-ln]);
exit(-ln);
}
ioctl(ln, TIOCGETP, &stbuf);
prf("Connected");
if (d.comment && *d.comment && *d.comment!='\n')
prf(d.comment);
if (tandm) {
ioctl(ln, TIOCGETP, &stbuf);
stbuf.sg_flags = ODDP+EVENP+TANDEM+CBREAK;
ioctl(ln, TIOCSETN, &stbuf);
}
if(pipe(chan)<0) {
prf("no pipe");
exit(1);
}
fk = fork();
nhup = (int)signal(SIGINT, SIG_IGN);
if (fk == 0) {
close(chan[1]);
rd();
prf("\007Lost carrier");
exit(3);
}
close(chan[0]);
mode(1);
wr();
mode(0);
kill(fk, SIGKILL);
stbuf.sg_ispeed = 0;
stbuf.sg_ospeed = 0;
ioctl(ln, TIOCSETN, &stbuf);
tdbuf.ispeed = tdbuf.ospeed = 0;
ioctl(ln, TIOCSDEV, &tdbuf);
prf("Disconnected");
exit(0);
}
/*
* wr: write to remote: 0 -> line.
* ~. terminate
* ~<file send file
* ~! local login-style shell
* ~!cmd execute cmd locally
* ~$proc execute proc locally, send output to line
* ~%cmd execute builtin cmd (put and take)
*/
wr()
{
int ds, fk, lcl, x, nc;
char *p, b[600];
for (;;) {
p = b;
while (rdc(0) >= 1) {
if (p == b)
lcl=(c == '~');
if (p == b+1 && b[0] == '~')
lcl=(c!='~');
if (c == 0)
c = 0177;
if (!lcl) {
if (c==0177)
ioctl(ln, TIOCFLUSH, 0);
if (wrc(ln, c, 1) <= 0) {
prf("line gone");
return;
}
if (c==0177)
ioctl(0, TIOCFLUSH, 0);
}
if (lcl) {
if (c == 0177)
c = tkill;
if (c == '\r' || c == '\n')
goto A;
if (!hduplx)
wrc(1, c, 1);
}
*p++ = c;
if (c == terase) {
p = p-2;
if (p<b)
p = b;
}
if (c == tkill || c == 0177 || c == '\r' || c == '\n')
p = b;
}
return;
A:
if (!hduplx || realtty.sg_flags&CRMOD)
echo("");
*p = 0;
switch (b[1]) {
case '.':
case '\004':
return;
case 'b':
sendbreak();
break;
case '!':
case '$':
fk = fork();
if (fk == 0) {
close(1);
dup(b[1] == '$'? ln:2);
close(ln);
mode(0);
if (!nhup)
signal(SIGINT, SIG_DFL);
if (b[2] == 0)
execl("/bin/sh","sh",0);
else
execl("/bin/sh","sh","-c",b+2,(char *)0);
prf("Can't execute shell");
exit(1);
}
if (fk!=(-1)) {
while (wait((int *)0)!=fk)
;
}
mode(1);
if (b[1] == '!')
echo("!");
break;
case '<':
if (b[2] == 0) break;
if ((ds = open(b+2,0))<0) {
prf("Can't divert %s",b+1);
break;
}
intr = x = 0;
mode(2);
if (!nhup)
signal(SIGINT, sig2);
while (!intr && (nc = rdc(ds)) >= 1) {
if (wrc(ln, c, nc==1) <= 0) {
x = 1;
break;
}
}
signal(SIGINT, SIG_IGN);
close(ds);
mode(1);
if (x)
return;
break;
case '%':
dopercen(&b[2]);
break;
default:
prf("Use `~~' to start line with `~'");
}
continue;
}
}
dopercen(line)
register char *line;
{
char *args[10];
register narg, f;
int rcount, nc;
for (narg = 0; narg < 10;) {
while(*line == ' ' || *line == '\t')
line++;
if (*line == '\0')
break;
args[narg++] = line;
while(*line != '\0' && *line != ' ' && *line != '\t')
line++;
if (*line == '\0')
break;
*line++ = '\0';
}
if (equal(args[0], "break")) {
sendbreak();
return;
} else if (equal(args[0], "take")) {
if (narg < 2) {
prf("usage: ~%%take from [to]");
return;
}
if (narg < 3)
args[2] = args[1];
write(chan[1],args[2],strlen(args[2]));
wrln("echo '~>:'");
wrln(args[2]);
wrln(";tee /dev/null <");
wrln(args[1]);
wrln(";echo '~>'\n");
return;
} else if (equal(args[0], "put")) {
if (narg < 2) {
prf("usage: ~%%put from [to]");
return;
}
if (narg < 3)
args[2] = args[1];
if ((f = open(args[1], 0)) < 0) {
prf("cannot open: %s", args[1]);
return;
}
wrln("stty -echo;cat >");
wrln(args[2]);
wrln(";stty echo\n");
sleep(5);
intr = 0;
if (!nhup)
signal(SIGINT, sig2);
mode(2);
rcount = 0;
while(!intr && (nc = rdc(f)) >= 1) {
rcount++;
if (c == tkill || c == terase)
wrc(ln, '\\', 0);
if (wrc(ln, c, nc == 1) <= 0)
intr = 1;
}
signal(SIGINT, SIG_IGN);
close(f);
if (intr) {
wrc(ln, '\n', 1);
prf("stopped after %d bytes", rcount);
}
wrc(ln, '\004', 1);
sleep(5);
mode(1);
return;
}
prf("~%%%s unknown\n", args[0]);
}
wrln(s)
register char *s;
{
register n = strlen(s);
write(ln, s, n);
}
/*
* rd: read from remote: line -> 1
* catch:
* ~>[>][:][file]
* stuff from file...
* ~> (ends diversion)
* ways for remote to run local command:
* ~!command (run command locally)
* ~:!command (run silently locally)
*/
rd()
{
int ds, slnt, pid, hold=0, nc;
char *p, *q, b[600];
char name[100], len;
p = b;
ds = -1;
while ((nc = rdc(ln)) >= 1) {
if (ds < 0 && hold==0)
slnt = 0;
if (p==b && c=='~')
hold= ++slnt;
if (hold && slnt && p==b+1 && c!=':') {
wrc(1, '~', 1);
slnt--;
hold = 0;
}
if (!slnt)
wrc(1, c, nc==1);
*p++ = c;
if (c!='\n' && p < &b[599])
continue;
q = p;
p = b;
hold = 0;
/* impossibly insecure
if (strncmp(b, "~:!",3)==0||strncmp(b, "~!", 2)==0) {
*--q= '\0';
if (*--q == '\r')
*q= '\0';
mode(0);
if ((pid=fork())==0) {
p = b+2;
if (*p=='!')
p++;
execl("/bin/sh", "sh", "-c", p, (char *)0);
exit(0);
}
while (wait((int *)0)!=pid)
;
mode(1);
continue;
}
*/
if (b[0]!='~' || b[1]!='>') {
if (*(q-2) == '\r') {
q--;
*(q-1)=(*q);
}
if (ds>=0)
write(ds, b, q-b);
continue;
}
if (ds>=0)
close(ds);
if (slnt) {
write(1, b, q - b);
write(1, CRLF, sizeof(CRLF));
}
if (*(q-2) == '\r')
q--;
*(q-1) = 0;
slnt = 0;
q = b+2;
if (*q == '>')
q++;
if (*q == ':') {
slnt = 1;
q++;
}
if (*q == 0) {
ds = -1;
continue;
}
len = read(chan[0],name,sizeof(name));
name[len>=0?len:0] = 0;
if(strcmp(name,q)) {
prf("masquerading ~%take");
return;
}
if (b[2]!='>' || (ds = open(q,1))<0)
ds = creat(q, 0644);
lseek(ds, (long)0, 2);
if (ds<0)
prf("Can't divert %s",b+1);
}
}
mode(f)
{
struct sgttyb stbuf;
static struct tchars nochars = { -1, -1, -1, -1, -1, -1};
ioctl(0, TIOCGETP, &stbuf);
tkill = stbuf.sg_kill;
terase = stbuf.sg_erase;
if (f == 0) {
ioctl(0, TIOCSETP, &realtty);
ioctl(0, TIOCSETC, &realtch);
return;
}
if (f == 1) {
stbuf.sg_flags |= CBREAK;
stbuf.sg_flags &= ~CRMOD;
if (!hduplx)
stbuf.sg_flags &= ~ECHO;
ioctl(0, TIOCSETP, &stbuf);
ioctl(0, TIOCSETC, &nochars);
return;
}
if (f == 2) {
stbuf.sg_flags &= ~(ECHO|CRMOD);
ioctl(0, TIOCSETP, &stbuf);
ioctl(0, TIOCSETC, &realtch);
return;
}
}
echo(s)
char *s;
{
register n = strlen(s);
if (n>0)
write(1, s, n);
write(1, CRLF, sizeof(CRLF));
}
/* VARARGS1 */
prf(f, s)
char *f;
char *s;
{
printf(f, s);
printf(CRLF);
fflush(stdout);
}
sendbreak()
{
struct sgttyb b;
int olds;
#ifdef TIOCSBRK
ioctl(ln, TIOCSBRK, 0);
#else
ioctl(ln, TIOCGETP, &b);
olds = b.sg_ispeed;
b.sg_ispeed = B50;
b.sg_ospeed = B50;
ioctl(ln, TIOCSETP, &b);
write(ln, "\0\0\0", 3);
b.sg_ispeed = olds;
b.sg_ospeed = olds;
ioctl(ln, TIOCSETP, &b);
#endif
}
/*
* Symbolic phone numbers
*/
gettelno(np, dp)
char *np;
register struct dial *dp;
{
char cunumber[128];
char *hp;
register char *xnp;
char *getenv();
if (cunfile) {
if (look(np, dp, cunfile))
return;
} else {
hp = getenv("HOME");
if (hp) {
strcpy(cunumber, hp);
strcat(cunumber, "/lib/cunumber");
if (look(np, dp, cunumber))
return;
}
if (look(np, dp, "/usr/lib/cunumber"))
return;
}
xnp = np;
if (*np != '/')
while (*xnp) {
if (*xnp!=';' && *xnp!=':' && *xnp!='-' && *xnp!='*'
&& *xnp!='#' && !isdigit(*xnp)) {
prf("Symbolic number not found");
exit(1);
}
xnp++;
}
dp->telno = np;
}
look(np, dp, fnp)
register char *np;
register struct dial *dp;
char *fnp;
{
FILE *fp;
static char line[128];
register char *lp;
register i;
char *opts[8];
register char **optp;
char *w[4];
if ((fp = fopen(fnp, "r")) == NULL)
return(0);
while (fgets(line, sizeof(line), fp)) {
lp = line;
optp = opts;
for (i = 0; i<4; i++) {
while (isspace(*lp))
lp++;
if (i==1 && *lp=='-') {
*optp++ = lp;
i--;
} else
w[i] = lp;
while ((!isspace(*lp) || i==3) && *lp)
lp++;
if (*lp)
*lp++ = '\0';
}
if (strcmp(w[0], np))
continue;
i = optind;
optind = 0;
options(optp-opts, opts);
optind = i;
dp->telno = w[1];
dp->dialtype = w[2];
dp->comment = w[3];
fclose(fp);
return(1);
}
fclose(fp);
return(0);
}
options(ac, av)
char **av;
{
register o;
opterr = 0;
while ((o = getopt(ac, av, "hntf:s:p:")) != EOF) {
switch(o) {
case '?':
errflg++;
continue;
case 'h':
hduplx++;
continue;
case 'f':
cunfile = optarg;
continue;
case 't':
tandm++;
continue;
case 'n':
nflag++;
continue;
case 's':
if ((speed = getspeed(optarg)) < 0) {
fprintf(stderr, "-s %s: illegal speed\n");
errflg++;
}
continue;
case 'p':
if ((parity = getpar(optarg)) < 0) {
fprintf(stderr, "-p %s: illegal parity\n");
errflg++;
}
continue;
}
}
}
struct speeds{
char *s_name;
int s_define;
} speeds[] = {
"0", B0,
"50", B50,
"75", B75,
"110", B110,
"134", B134,
"150", B150,
"200", B200,
"300", B300,
"600", B600,
"1200", B1200,
"1800", B1800,
"2400", B2400,
"4800", B4800,
"9600", B9600,
"exta", EXTA,
"extb", EXTB,
"19200", EXTA,
0
};
getspeed(s)
char *s;
{
register struct speeds *sp;
for (sp = speeds; sp->s_name; sp++)
if (strcmp(sp->s_name, s) == 0)
return (sp->s_define);
return (-1);
}
getpar(s)
char *s;
{
switch (s[0]) {
case '0':
return (0);
case '1':
return (EVENP|ODDP);
case 'e':
return (EVENP);
case 'o':
return (ODDP);
}
return (-1);
}
wrc(f, c, flush)
register c;
{
static char buf[64];
static char *bp = buf;
register r;
c &= 0177;
if (f==ln) {
switch (parity) {
case EVENP:
c |= (partab[c] & 0200);
break;
case ODDP:
c |= (partab[c] & 0200) ^ 0200;
break;
case EVENP|ODDP:
c |= 0200;
break;
}
}
*bp++ = c;
r = 1;
if (flush || bp >= &buf[64]) {
r = write(f, buf, bp-buf);
bp = buf;
}
return(r);
}
rdc(ds)
{
static char buf[64];
static nc = 0;
static char *bp;
if (nc <= 0) {
nc = read(ds, buf, 64);
bp = buf;
}
if (nc <= 0)
return(nc);
nc--;
c = *bp++ & 0177;
return(nc+1);
}
sig2()
{
signal(SIGINT, SIG_IGN);
intr = 1;
}