diff options
author | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
---|---|---|
committer | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
commit | e5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch) | |
tree | d8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/cmd/con |
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/cmd/con')
-rwxr-xr-x | sys/src/cmd/con/con.c | 683 | ||||
-rwxr-xr-x | sys/src/cmd/con/hayes.c | 240 | ||||
-rwxr-xr-x | sys/src/cmd/con/mkfile | 18 | ||||
-rwxr-xr-x | sys/src/cmd/con/xmr.c | 182 | ||||
-rwxr-xr-x | sys/src/cmd/con/xms.c | 215 |
5 files changed, 1338 insertions, 0 deletions
diff --git a/sys/src/cmd/con/con.c b/sys/src/cmd/con/con.c new file mode 100755 index 000000000..a8b697700 --- /dev/null +++ b/sys/src/cmd/con/con.c @@ -0,0 +1,683 @@ +#include <u.h> +#include <libc.h> + +int debug; /* true if debugging */ +int ctl = -1; /* control fd (for break's) */ +int raw; /* true if raw is on */ +int consctl = -1; /* control fd for cons */ +int ttypid; /* pid's if the 2 processes (used to kill them) */ +int outfd = 1; /* local output file descriptor */ +int cooked; /* non-zero forces cooked mode */ +int returns; /* non-zero forces carriage returns not to be filtered out */ +int crtonl; /* non-zero forces carriage returns to be converted to nls coming from net */ +int strip; /* strip off parity bits */ +char firsterr[2*ERRMAX]; +char transerr[2*ERRMAX]; +int limited; +char *remuser; /* for BSD rlogin authentication */ +int verbose; +int baud; +int notkbd; +int nltocr; /* translate kbd nl to cr and vice versa */ + +static char *srv; + +#define MAXMSG (2*8192) + +int dodial(char*, char*, char*); +void fromkbd(int); +void fromnet(int); +long iread(int, void*, int); +long iwrite(int, void*, int); +int menu(int); +void notifyf(void*, char*); +void pass(int, int, int); +void rawoff(void); +void rawon(void); +void stdcon(int); +char* system(int, char*); +void dosystem(int, char*); +int wasintr(void); +void punt(char*); +char* syserr(void); +void seterr(char*); + +/* protocols */ +void device(char*, char*); +void rlogin(char*, char*); +void simple(char*, char*); + +void +usage(void) +{ + punt("usage: con [-CdnrRsTv] [-b baud] [-l [user]] [-c cmd] [-S svc] " + "net!host[!service]"); +} + +void +main(int argc, char *argv[]) +{ + char *dest; + char *cmd = 0; + + returns = 1; + ARGBEGIN{ + case 'b': + baud = atoi(EARGF(usage())); + break; + case 'C': + cooked = 1; + break; + case 'c': + cmd = EARGF(usage()); + break; + case 'd': + debug = 1; + break; + case 'l': + limited = 1; + if(argv[1] != nil && argv[1][0] != '-') + remuser = EARGF(usage()); + break; + case 'n': + notkbd = 1; + break; + case 'r': + returns = 0; + break; + case 's': + strip = 1; + break; + case 'S': + srv = EARGF(usage()); + break; + case 'R': + nltocr = 1; + break; + case 'T': + crtonl = 1; + break; + case 'v': + verbose = 1; + break; + default: + usage(); + }ARGEND + + if(argc != 1){ + if(remuser == 0) + usage(); + dest = remuser; + remuser = 0; + } else + dest = argv[0]; + if(*dest == '/' && strchr(dest, '!') == 0) + device(dest, cmd); + else if(limited){ + simple(dest, cmd); /* doesn't return if dialout succeeds */ + rlogin(dest, cmd); /* doesn't return if dialout succeeds */ + } else { + rlogin(dest, cmd); /* doesn't return if dialout succeeds */ + simple(dest, cmd); /* doesn't return if dialout succeeds */ + } + punt(firsterr); +} + +/* + * just dial and use as a byte stream with remote echo + */ +void +simple(char *dest, char *cmd) +{ + int net; + + net = dodial(dest, 0, 0); + if(net < 0) + return; + + if(cmd) + dosystem(net, cmd); + + if(!cooked) + rawon(); + stdcon(net); + exits(0); +} + +/* + * dial, do UCB authentication, use as a byte stream with local echo + * + * return if dial failed + */ +void +rlogin(char *dest, char *cmd) +{ + int net; + char buf[128]; + char *p; + char *localuser; + + /* only useful on TCP */ + if(strchr(dest, '!') + && (strncmp(dest, "tcp!", 4)!=0 && strncmp(dest, "net!", 4)!=0)) + return; + + net = dodial(dest, "tcp", "login"); + if(net < 0) + return; + + /* + * do UCB rlogin authentication + */ + localuser = getuser(); + if(remuser == 0){ + if(limited) + remuser = ":"; + else + remuser = localuser; + } + p = getenv("TERM"); + if(p == 0) + p = "p9"; + if(write(net, "", 1)<0 + || write(net, localuser, strlen(localuser)+1)<0 + || write(net, remuser, strlen(remuser)+1)<0 + || write(net, p, strlen(p)+1)<0){ + close(net); + punt("BSD authentication failed"); + } + if(read(net, buf, 1) != 1) + punt("BSD authentication failed1"); + if(buf[0] != 0){ + fprint(2, "con: remote error: "); + while(read(net, buf, 1) == 1){ + write(2, buf, 1); + if(buf[0] == '\n') + break; + } + exits("read"); + } + + if(cmd) + dosystem(net, cmd); + + if(!cooked) + rawon(); + nltocr = 1; + stdcon(net); + exits(0); +} + +/* + * just open a device and use it as a connection + */ +void +device(char *dest, char *cmd) +{ + int net; + char cname[128]; + + net = open(dest, ORDWR); + if(net < 0) { + fprint(2, "con: cannot open %s: %r\n", dest); + exits("open"); + } + snprint(cname, sizeof cname, "%sctl", dest); + ctl = open(cname, ORDWR); + if (baud > 0) { + if(ctl >= 0){ + /* set speed and use fifos if available */ + fprint(ctl, "b%d i1", baud); + } + else + fprint(2, "con: cannot open %s: %r\n", cname); + } + + if(cmd) + dosystem(net, cmd); + + if(!cooked) + rawon(); + stdcon(net); + exits(0); +} + +/* + * ignore interrupts + */ +void +notifyf(void *a, char *msg) +{ + USED(a); + + if(strstr(msg, "yankee")) + noted(NDFLT); + if(strstr(msg, "closed pipe") + || strcmp(msg, "interrupt") == 0 + || strcmp(msg, "hangup") == 0) + noted(NCONT); + noted(NDFLT); +} + +/* + * turn keyboard raw mode on + */ +void +rawon(void) +{ + if(debug) + fprint(2, "rawon\n"); + if(raw) + return; + if(consctl < 0) + consctl = open("/dev/consctl", OWRITE); + if(consctl < 0){ +// fprint(2, "can't open consctl\n"); + return; + } + write(consctl, "rawon", 5); + raw = 1; +} + +/* + * turn keyboard raw mode off + */ +void +rawoff(void) +{ + if(debug) + fprint(2, "rawoff\n"); + if(raw == 0) + return; + if(consctl < 0) + consctl = open("/dev/consctl", OWRITE); + if(consctl < 0){ +// fprint(2, "can't open consctl\n"); + return; + } + write(consctl, "rawoff", 6); + raw = 0; +} + +/* + * control menu + */ +#define STDHELP "\t(b)reak, (q)uit, (i)nterrupt, toggle printing (r)eturns, (.)continue, (!cmd)\n" + +int +menu(int net) +{ + char buf[MAXMSG]; + long n; + int done; + int wasraw = raw; + + if(wasraw) + rawoff(); + + fprint(2, ">>> "); + for(done = 0; !done; ){ + n = read(0, buf, sizeof(buf)-1); + if(n <= 0) + return -1; + buf[n] = 0; + switch(buf[0]){ + case '!': + print(buf); + system(net, buf+1); + print("!\n"); + done = 1; + break; + case '.': + done = 1; + break; + case 'q': + return -1; + case 'i': + buf[0] = 0x1c; + write(net, buf, 1); + done = 1; + break; + case 'b': + if(ctl >= 0) + write(ctl, "k", 1); + done = 1; + break; + case 'r': + returns = 1-returns; + done = 1; + break; + default: + fprint(2, STDHELP); + break; + } + if(!done) + fprint(2, ">>> "); + } + + if(wasraw) + rawon(); + else + rawoff(); + return 0; +} + +void +post(char *srv, int fd) +{ + int f; + char buf[32]; + + f = create(srv, OWRITE /* |ORCLOSE */ , 0666); + if(f < 0) + sysfatal("create %s: %r", srv); + snprint(buf, sizeof buf, "%d", fd); + if(write(f, buf, strlen(buf)) != strlen(buf)) + sysfatal("write %s: %r", srv); + close(f); +} + +/* + * the real work. two processes pass bytes back and forth between the + * terminal and the network. + */ +void +stdcon(int net) +{ + int netpid; + int p[2]; + char *svc; + + svc = nil; + if (srv) { + if(pipe(p) < 0) + sysfatal("pipe: %r"); + if (srv[0] != '/') + svc = smprint("/srv/%s", srv); + else + svc = srv; + post(svc, p[0]); + close(p[0]); + dup(p[1], 0); + dup(p[1], 1); + /* pipe is now std in & out */ + } + ttypid = getpid(); + switch(netpid = rfork(RFMEM|RFPROC)){ + case -1: + perror("con"); + exits("fork"); + case 0: + notify(notifyf); + fromnet(net); + if (svc) + remove(svc); + postnote(PNPROC, ttypid, "die yankee dog"); + exits(0); + default: + notify(notifyf); + fromkbd(net); + if (svc) + remove(svc); + if(notkbd) + for(;;) + sleep(0); + postnote(PNPROC, netpid, "die yankee dog"); + exits(0); + } +} + +/* + * Read the keyboard and write it to the network. '^\' gets us into + * the menu. + */ +void +fromkbd(int net) +{ + long n; + char buf[MAXMSG]; + char *p, *ep; + int eofs; + + eofs = 0; + for(;;){ + n = read(0, buf, sizeof(buf)); + if(n < 0){ + if(wasintr()){ + if(!raw){ + buf[0] = 0x7f; + n = 1; + } else + continue; + } else + return; + } + if(n == 0){ + if(++eofs > 32) + return; + } else + eofs = 0; + if(n && memchr(buf, 0x1c, n)){ + if(menu(net) < 0) + return; + }else{ + if(!raw && n==0){ + buf[0] = 0x4; + n = 1; + } + if(nltocr){ + ep = buf+n; + for(p = buf; p < ep; p++) + switch(*p){ + case '\r': + *p = '\n'; + break; + case '\n': + *p = '\r'; + break; + } + } + if(iwrite(net, buf, n) != n) + return; + } + } +} + +/* + * Read from the network and write to the screen. + * Filter out spurious carriage returns. + */ +void +fromnet(int net) +{ + long n; + char buf[MAXMSG]; + char *cp, *ep; + + for(;;){ + n = iread(net, buf, sizeof(buf)); + if(n < 0) + return; + if(n == 0) + continue; + + if (strip) + for (cp=buf; cp<buf+n; cp++) + *cp &= 0177; + + if(crtonl) { + /* convert cr's to nl's */ + for (cp = buf; cp < buf + n; cp++) + if (*cp == '\r') + *cp = '\n'; + } + else if(!returns){ + /* convert cr's to null's */ + cp = buf; + ep = buf + n; + while(cp < ep && (cp = memchr(cp, '\r', ep-cp))){ + memmove(cp, cp+1, ep-cp-1); + ep--; + n--; + } + } + + if(n > 0 && iwrite(outfd, buf, n) != n){ + if(outfd == 1) + return; + outfd = 1; + if(iwrite(1, buf, n) != n) + return; + } + } +} + +/* + * dial and return a data connection + */ +int +dodial(char *dest, char *net, char *service) +{ + char name[128]; + char devdir[128]; + int data; + + devdir[0] = 0; + strcpy(name, netmkaddr(dest, net, service)); + data = dial(name, 0, devdir, &ctl); + if(data < 0){ + seterr(name); + return -1; + } + fprint(2, "connected to %s on %s\n", name, devdir); + return data; +} + +void +dosystem(int fd, char *cmd) +{ + char *p; + + p = system(fd, cmd); + if(p){ + print("con: %s terminated with %s\n", cmd, p); + exits(p); + } +} + +/* + * run a command with the network connection as standard IO + */ +char * +system(int fd, char *cmd) +{ + int pid; + int p; + static Waitmsg msg; + int pfd[2]; + int n; + char buf[4096]; + + if(pipe(pfd) < 0){ + perror("pipe"); + return "pipe failed"; + } + outfd = pfd[1]; + + close(consctl); + consctl = -1; + switch(pid = fork()){ + case -1: + perror("con"); + return "fork failed"; + case 0: + close(pfd[1]); + dup(pfd[0], 0); + dup(fd, 1); + close(ctl); + close(fd); + close(pfd[0]); + if(*cmd) + execl("/bin/rc", "rc", "-c", cmd, nil); + else + execl("/bin/rc", "rc", nil); + perror("con"); + exits("exec"); + break; + default: + close(pfd[0]); + while((n = read(pfd[1], buf, sizeof(buf))) > 0) + if(write(fd, buf, n) != n) + break; + p = waitpid(); + outfd = 1; + close(pfd[1]); + if(p < 0 || p != pid) + return "lost child"; + break; + } + return msg.msg; +} + +int +wasintr(void) +{ + return strcmp(syserr(), "interrupted") == 0; +} + +void +punt(char *msg) +{ + if(*msg == 0) + msg = transerr; + fprint(2, "con: %s\n", msg); + exits(msg); +} + +char* +syserr(void) +{ + static char err[ERRMAX]; + errstr(err, sizeof err); + return err; +} + +void +seterr(char *addr) +{ + char *se = syserr(); + + if(verbose) + fprint(2, "'%s' calling %s\n", se, addr); + if(firsterr[0] && (strstr(se, "translate") || + strstr(se, "file does not exist") || + strstr(se, "unknown address") || + strstr(se, "directory entry not found"))) + return; + strcpy(firsterr, se); +} + + +long +iread(int f, void *a, int n) +{ + long m; + + for(;;){ + m = read(f, a, n); + if(m >= 0 || !wasintr()) + break; + } + return m; +} + +long +iwrite(int f, void *a, int n) +{ + long m; + + m = write(f, a, n); + if(m < 0 && wasintr()) + return n; + return m; +} diff --git a/sys/src/cmd/con/hayes.c b/sys/src/cmd/con/hayes.c new file mode 100755 index 000000000..6ade8be86 --- /dev/null +++ b/sys/src/cmd/con/hayes.c @@ -0,0 +1,240 @@ +#include <u.h> +#include <libc.h> + +void setspeed(int, int); +int getspeed(char*, int); +void godial(int, int, char*); +int readmsg(int, int); +void punt(char*, ...); + +int pulsed; +int verbose; +char msgbuf[128]; /* last message read */ + +enum +{ + Ok, + Success, + Failure, + Noise, +}; + +typedef struct Msg Msg; +struct Msg +{ + char *text; + int type; +}; + + +Msg msgs[] = +{ + { "OK", Ok, }, + { "NO CARRIER", Failure, }, + { "ERROR", Failure, }, + { "NO DIALTONE", Failure, }, + { "BUSY", Failure, }, + { "NO ANSWER", Failure, }, + { "CONNECT", Success, }, + { 0, 0 }, +}; + +void +usage(void) +{ + punt("usage: hayes [-p] telno [device]"); +} + +void +main(int argc, char **argv) +{ + int data = -1; + int ctl = -1; + char *cname; + + ARGBEGIN{ + case 'p': + pulsed = 1; + break; + case 'v': + verbose = 1; + break; + default: + usage(); + }ARGEND + + switch(argc){ + case 1: + data = 1; + break; + case 2: + data = open(argv[1], ORDWR); + if(data < 0){ + fprint(2, "hayes: %r opening %s\n", argv[1]); + exits("hayes"); + } + cname = malloc(strlen(argv[1])+4); + sprint(cname, "%sctl", argv[1]); + ctl = open(cname, ORDWR); + free(cname); + break; + default: + usage(); + } + godial(data, ctl, argv[0]); + exits(0); +} + +int +send(int fd, char *x) +{ + return write(fd, x, strlen(x)); +} + +void +godial(int data, int ctl, char *number) +{ + char *dialstr; + int m; + int baud; + + /* get the modem's attention */ + if(send(data, "\r+++\r") < 0) + punt("failed write"); + readmsg(data, 2); + sleep(1000); + + /* initialize */ + if(send(data, "ATZ\r") < 0) + punt("failed write"); + m = readmsg(data, 2); + if(m < 0) + punt("can't get modem's attention"); + + /* + * Q0 = report result codes + * V1 = full word result codes + * W1 = negotiation progress codes enabled + * E1 = echo commands + * M1 = speaker on until on-line + */ + if(send(data, "ATQ0V1E1M1\r") < 0) + punt("failed write"); + m = readmsg(data, 2); + if(m != Ok) + punt("can't get modem's attention"); + if(send(data, "ATW1\r") < 0) + punt("failed write"); + readmsg(data, 2); + sleep(1000); + + /* godial */ + dialstr = malloc(6+strlen(number)); + sprint(dialstr, "ATD%c%s\r", pulsed ? 'P' : 'T', number); + if(send(data, dialstr) < 0) { + free(dialstr); + punt("failed write"); + } + free(dialstr); + m = readmsg(data, 60); + if(m != Success) + punt("dial failed: %s", msgbuf); + baud = getspeed(msgbuf, 9600); + setspeed(ctl, baud); + fprint(2, "hayes: connected at %d baud\n", baud); +} + +/* + * read until we see a message or we time out + */ +int +readmsg(int f, int secs) +{ + ulong start; + char *p; + int len; + Dir *d; + Msg *pp; + + p = msgbuf; + len = sizeof(msgbuf) - 1; + for(start = time(0); time(0) <= start+secs;){ + if((d = dirfstat(f)) == nil) + punt("failed read"); + if(d->length == 0){ + free(d); + sleep(100); + continue; + } + free(d); + if(read(f, p, 1) <= 0) + punt("failed read"); + if(*p == '\n' || *p == '\r' || len == 0){ + *p = 0; + if(verbose && p != msgbuf) + fprint(2, "%s\n", msgbuf); + for(pp = msgs; pp->text; pp++) + if(strncmp(pp->text, msgbuf, strlen(pp->text))==0) + return pp->type; + start = time(0); + p = msgbuf; + len = sizeof(msgbuf) - 1; + continue; + } + len--; + p++; + } + strcpy(msgbuf, "No response from modem"); + return Noise; +} + +/* + * get baud rate from a connect message + */ +int +getspeed(char *msg, int speed) +{ + char *p; + int s; + + p = msg + sizeof("CONNECT") - 1; + while(*p == ' ' || *p == '\t') + p++; + s = atoi(p); + if(s <= 0) + return speed; + else + return s; +} + +/* + * set speed and RTS/CTS modem flow control + */ +void +setspeed(int ctl, int baud) +{ + char buf[32]; + + if(ctl < 0) + return; + sprint(buf, "b%d", baud); + write(ctl, buf, strlen(buf)); + write(ctl, "m1", 2); +} + + +void +punt(char *fmt, ...) +{ + char buf[256]; + va_list arg; + int n; + + strcpy(buf, "hayes: "); + va_start(arg, fmt); + n = vseprint(buf+strlen(buf), buf+sizeof(buf), fmt, arg) - buf; + va_end(arg); + buf[n] = '\n'; + write(2, buf, n+1); + exits("hayes"); +} diff --git a/sys/src/cmd/con/mkfile b/sys/src/cmd/con/mkfile new file mode 100755 index 000000000..2f04039f4 --- /dev/null +++ b/sys/src/cmd/con/mkfile @@ -0,0 +1,18 @@ +</$objtype/mkfile + +TARG=con\ + xms\ + xmr\ + hayes\ + +BIN=/$objtype/bin + +UPDATE=\ + mkfile\ + $HFILES\ + ${TARG:%=%.c}\ + /sys/man/1/con\ + +</sys/src/cmd/mkmany + + diff --git a/sys/src/cmd/con/xmr.c b/sys/src/cmd/con/xmr.c new file mode 100755 index 000000000..3f57610ef --- /dev/null +++ b/sys/src/cmd/con/xmr.c @@ -0,0 +1,182 @@ +#include <u.h> +#include <libc.h> + +enum { + Soh= 0x1, + Eot= 0x4, + Ack= 0x6, + Nak= 0x15, + Cancel= 0x18, +}; + +int notifyf(void*, char*); +int readupto(uchar*, int); +int receive(int, uchar); +void send(int); + +int debug, dfd; + +void +main(int argc, char **argv) +{ + int fd; + uchar seqno; + ulong bytes; + + ARGBEGIN { + case 'd': + dfd = 2; + debug = 1; + break; + } ARGEND + + if(argc != 1){ + fprint(2, "usage: xmr filename\n"); + exits("usage"); + } + fd = open("/dev/consctl", OWRITE); + if(fd >= 0) + write(fd, "rawon", 5); + fd = create(argv[0], ORDWR, 0666); + if(fd < 0){ + perror("xmr: create"); + exits("create"); + } + + atnotify(notifyf, 1); + send(Nak); + + /* + * keep receiving till the other side gives up + */ + bytes = 0; + for(seqno = 1; ; seqno++){ + if(receive(fd, seqno) == -1) + break; + bytes += 128; + } + fprint(2, "xmr: received %ld bytes\n", bytes); + exits(0); +} + +void +send(int byte) +{ + uchar c; + + c = byte; + if(write(1, &c, 1) != 1){ + fprint(2, "xmr: hungup\n"); + exits("hangup"); + } +} + +int +readupto(uchar *a, int len) +{ + int n; + int sofar; + + for(sofar = 0; sofar < len; sofar += n){ + n = read(0, a, len-sofar); + if(n <= 0){ + send(Nak); + return sofar; + } + if(*a == Eot || *a == Cancel) + return sofar + n; + a += n; + } + return sofar; + +} + +int +receive(int fd, uchar seqno) +{ + uchar buf[128+4]; + uchar sum; + uchar *p; + int n; + int tries; + int have; + + for(have = 0, tries = 0;; tries++){ + if(debug) + fprint(dfd, "have == %d\n", have); + if(tries > 10){ + fprint(2, "xmr: timed out\n"); + if(debug) + close(dfd); + exits("timeout"); + } + + /* try to gather up a block */ + alarm(15*1000); + n = readupto(&buf[have], 132-have); + alarm(0); + have += n; + if(have){ + switch(buf[0]){ + case Eot: + send(Ack); + return -1; + case Cancel: + fprint(2, "xmr: transfer aborted by sender\n"); + exits("cancel"); + } + } + if(have != 132) + continue; + + /* checksum */ + for(p = buf, sum = 0; p < &buf[128+3]; p++) + sum += *p; + + /* If invalid block, resynchronize */ + if(buf[0] != Soh || buf[2] != (255-buf[1]) || sum != buf[131]){ + if(debug){ + fprint(dfd, "resync %2.2ux %d %d %ux %ux\n", buf[0], + buf[1], buf[2], sum, buf[131]); + write(dfd, (char*)buf+3, 128); + fprint(dfd, "\n"); + } + p = memchr(buf+1, Soh, 131); + if(p){ + have = 132-(p-buf); + memmove(buf, p, have); + } else + have = 0; + continue; + } + + /* it's probably a real block, so dump it if there's an error */ + have = 0; + + /* if this is the last block, ack */ + if(buf[1] == seqno-1){ + tries = 0; + send(Ack); + }else if(buf[1] == seqno){ + if(debug) + fprint(dfd, "Ack\n"); + send(Ack); + if(write(fd, buf+3, 128) != 128){ + fprint(2, "xmr: abort, error writing file\n"); + exits("write"); + } + return 0; + } else { + send(Nak); + } + } +} + +int +notifyf(void *a, char *msg) +{ + USED(a); + if(strcmp(msg, "alarm") == 0) + return 1; + return 0; +} diff --git a/sys/src/cmd/con/xms.c b/sys/src/cmd/con/xms.c new file mode 100755 index 000000000..2d46f845a --- /dev/null +++ b/sys/src/cmd/con/xms.c @@ -0,0 +1,215 @@ +#include <u.h> +#include <libc.h> +#include <ctype.h> + +enum { + Soh= 0x1, + Stx= 0x2, + Eot= 0x4, + Ack= 0x6, + Nak= 0x15, + Cancel= 0x18, +}; + +int send(uchar*, int); +int notifyf(void*, char*); + +int debug, progress, onek; + +void +errorout(int ctl, int bytes) +{ + uchar buf[2]; + + buf[0] = Cancel; + write(1, buf, 1); + fprint(2, "\nxms: gave up after %d bytes\n", bytes); + write(ctl, "rawoff", 6); + exits("cancel"); +} + +ushort +updcrc(int c, ushort crc) +{ + int count; + + for (count=8; --count>=0;) { + if (crc & 0x8000) { + crc <<= 1; + crc += (((c<<=1) & 0400) != 0); + crc ^= 0x1021; + } + else { + crc <<= 1; + crc += (((c<<=1) & 0400) != 0); + } + } + return crc; +} + +void +main(int argc, char **argv) +{ + uchar c; + uchar buf[1024+5]; + uchar seqno; + int fd, ctl; + long n; + int sum; + uchar *p; + int bytes; + int crcmode; + + ARGBEGIN{ + case 'd': + debug = 1; + break; + case 'p': + progress = 1; + break; + case '1': + onek = 1; + break; + }ARGEND + + if(argc != 1){ + fprint(2, "usage: xms filename\n"); + exits("usage"); + } + fd = open(argv[0], OREAD); + if(fd < 0){ + perror("xms"); + exits("open"); + } + + ctl = open("/dev/consctl", OWRITE); + if(ctl < 0){ + perror("xms"); + exits("consctl"); + } + write(ctl, "rawon", 5); + + /* give the other side a 30 seconds to signal ready */ + atnotify(notifyf, 1); + alarm(30*1000); + crcmode = 0; + for(;;){ + if(read(0, &c, 1) != 1){ + fprint(2, "xms: timeout\n"); + exits("timeout"); + } + c = c & 0x7f; + if(c == Nak) + break; + if(c == 'C') { + if (debug) + fprint(2, "crc mode engaged\n"); + crcmode = 1; + break; + } + } + alarm(0); + + /* send the file in 128/1024 byte chunks */ + for(bytes = 0, seqno = 1; ; bytes += n, seqno++){ + n = read(fd, buf+3, onek ? 1024 : 128); + if(n < 0) + exits("read"); + if(n == 0) + break; + if(n < (onek ? 1024 : 128)) + memset(&buf[n+3], 0, (onek ? 1024 : 128)-n); + buf[0] = onek ? Stx : Soh; + buf[1] = seqno; + buf[2] = 255 - seqno; + + /* calculate checksum and stuff into last byte */ + if (crcmode) { + unsigned short crc; + crc = 0; + for(p = buf + 3; p < &buf[(onek ? 1024 : 128)+3]; p++) + crc = updcrc(*p, crc); + crc = updcrc(0, crc); + crc = updcrc(0, crc); + buf[(onek ? 1024 : 128) + 3] = crc >> 8; + buf[(onek ? 1024 : 128) + 4] = crc; + } + else { + sum = 0; + for(p = buf + 3; p < &buf[(onek ? 1024 : 128)+3]; p++) + sum += *p; + buf[(onek ? 1024 : 128) + 3] = sum; + } + + if(send(buf, (onek ? 1024 : 128) + 4 + crcmode) < 0) + errorout(ctl, bytes); + if (progress && bytes % 10240 == 0) + fprint(2, "%dK\n", bytes / 1024); + } + + /* tell other side we're done */ + buf[0] = Eot; + if(send(buf, 1) < 0) + errorout(ctl, bytes); + + fprint(2, "xms: %d bytes transmitted\n", bytes); + write(ctl, "rawoff", 6); + exits(0); +} + +/* + * send a message till it's acked or we give up + */ +int +send(uchar *buf, int len) +{ + int tries; + int n; + uchar c; + + for(tries = 0;; tries++, sleep(2*1000)){ + if(tries == 10) + return -1; + if(write(1, buf, len) != len) + return -1; + + alarm(30*1000); + n = read(0, &c, 1); + alarm(0); + if(debug) switch(c){ + case Soh: + fprint(2, " Soh"); + break; + case Eot: + fprint(2, " Eot"); + break; + case Ack: + fprint(2, " Ack"); + break; + case Nak: + fprint(2, " Nak"); + break; + case Cancel: + fprint(2, "\nremote Cancel\n"); + return -1; + default: + if(isprint(c)) + fprint(2, "%c", c); + else + fprint(2, " \\0%o", c); + } + c = c & 0x7f; + if(n == 1 && c == Ack) + break; + } + return 0; +} + +int +notifyf(void *a, char *msg) +{ + USED(a); + if(strcmp(msg, "alarm") == 0) + return 1; + return 0; +} |