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/con.c |
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/cmd/con/con.c')
-rwxr-xr-x | sys/src/cmd/con/con.c | 683 |
1 files changed, 683 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; +} |