summaryrefslogtreecommitdiff
path: root/sys/src/cmd/con/con.c
diff options
context:
space:
mode:
authorTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
committerTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
commite5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch)
treed8d51eac403f07814b9e936eed0c9a79195e2450 /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-xsys/src/cmd/con/con.c683
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;
+}