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/cec/cec.c |
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/cmd/cec/cec.c')
-rwxr-xr-x | sys/src/cmd/cec/cec.c | 558 |
1 files changed, 558 insertions, 0 deletions
diff --git a/sys/src/cmd/cec/cec.c b/sys/src/cmd/cec/cec.c new file mode 100755 index 000000000..f962a037e --- /dev/null +++ b/sys/src/cmd/cec/cec.c @@ -0,0 +1,558 @@ +/* + * cec — coraid ethernet console + * Copyright © Coraid, Inc. 2006-2008. + * All Rights Reserved. + */ +#include <u.h> +#include <libc.h> +#include <ip.h> /* really! */ +#include <ctype.h> +#include "cec.h" + +enum { + Tinita = 0, + Tinitb, + Tinitc, + Tdata, + Tack, + Tdiscover, + Toffer, + Treset, + + Hdrsz = 18, + Eaddrlen = 6, +}; + +typedef struct{ + uchar ea[Eaddrlen]; + int major; + char name[28]; +} Shelf; + +int conn(int); +void gettingkilled(int); +int pickone(void); +void probe(void); +void sethdr(Pkt *, int); +int shelfidx(void); + +Shelf *con; +Shelf tab[1000]; + +char *host; +char *srv; +char *svc; + +char pflag; + +int ntab; +int shelf = -1; + +uchar bcast[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; +uchar contag; +uchar esc = ''; +uchar ea[Eaddrlen]; +uchar unsetea[Eaddrlen]; + +extern int fd; /* set in netopen */ + +void +post(char *srv, int fd) +{ + char buf[32]; + int f; + + if((f = create(srv, OWRITE, 0666)) == -1) + 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); +} + +void +dosrv(char *s) +{ + int p[2]; + + if(pipe(p) < 0) + sysfatal("pipe: %r"); + if (srv[0] != '/') + svc = smprint("/srv/%s", s); + else + svc = smprint("%s", s); + post(svc, p[0]); + close(p[0]); + dup(p[1], 0); + dup(p[1], 1); + + switch(rfork(RFFDG|RFPROC|RFNAMEG|RFNOTEG)){ + case -1: + sysfatal("fork: %r"); + case 0: + break; + default: + exits(""); + } + close(2); +} + +void +usage(void) +{ + fprint(2, "usage: cec [-dp] [-c esc] [-e ea] [-h host] [-s shelf] " + "[-S srv] interface\n"); + exits0("usage"); +} + +void +catch(void*, char *note) +{ + if(strcmp(note, "alarm") == 0) + noted(NCONT); + noted(NDFLT); +} + +int +nilea(uchar *ea) +{ + return memcmp(ea, unsetea, Eaddrlen) == 0; +} + +void +main(int argc, char **argv) +{ + int r, n; + + ARGBEGIN{ + case 'S': + srv = EARGF(usage()); + break; + case 'c': + esc = tolower(*(EARGF(usage()))) - 'a' + 1; + if(esc == 0 || esc >= ' ') + usage(); + break; + case 'd': + debug++; + break; + case 'e': + if(parseether(ea, EARGF(usage())) == -1) + usage(); + pflag = 1; + break; + case 'h': + host = EARGF(usage()); + break; + case 'p': + pflag = 1; + break; + case 's': + shelf = atoi(EARGF(usage())); + break; + default: + usage(); + }ARGEND + if(argc == 0) + *argv = "/net/ether0"; + else if(argc != 1) + usage(); + + fmtinstall('E', eipfmt); + if(srv != nil) + dosrv(srv); + r = netopen(*argv); + if(r == -1){ + fprint(2, "cec: can't netopen %s\n", *argv); + exits0("open"); + } + notify(catch); + probe(); + for(;;){ + n = 0; + if(shelf == -1 && host == 0 && nilea(ea)) + n = pickone(); + rawon(); + conn(n); + rawoff(); + if(pflag == 0){ + if(shelf != -1) + exits0("shelf not found"); + if(host) + exits0("host not found"); + if(!nilea(ea)) + exits0("ea not found"); + } else if(shelf != -1 || host || !nilea(ea)) + exits0(""); + } +} + +void +timewait(int ms) +{ + alarm(ms); +} + +int +didtimeout(void) +{ + char buf[ERRMAX]; + + rerrstr(buf, sizeof buf); + if(strcmp(buf, "interrupted") == 0){ + werrstr(buf, 0); + return 1; + } + return 0; +} + +ushort +htons(ushort h) +{ + ushort n; + uchar *p; + + p = (uchar*)&n; + p[0] = h >> 8; + p[1] = h; + return n; +} + +ushort +ntohs(int h) +{ + ushort n; + uchar *p; + + n = h; + p = (uchar*)&n; + return p[0] << 8 | p[1]; +} + +int +tcmp(void *a, void *b) +{ + Shelf *s, *t; + int d; + + s = a; + t = b; + d = s->major - t->major; + if(d == 0) + d = strcmp(s->name, t->name); + if(d == 0) + d = memcmp(s->ea, t->ea, Eaddrlen); + return d; +} + +void +probe(void) +{ + char *sh, *other; + int n; + Pkt q; + Shelf *p; + + do { + ntab = 0; + memset(q.dst, 0xff, Eaddrlen); + memset(q.src, 0, Eaddrlen); + q.etype = htons(Etype); + q.type = Tdiscover; + q.len = 0; + q.conn = 0; + q.seq = 0; + netsend(&q, 60); + timewait(Iowait); + while((n = netget(&q, sizeof q)) >= 0){ + if((n <= 0 && didtimeout()) || ntab == nelem(tab)) + break; + if(n < 60 || q.len == 0 || q.type != Toffer) + continue; + q.data[q.len] = 0; + sh = strtok((char *)q.data, " \t"); + if(sh == nil) + continue; + if(!nilea(ea) && memcmp(ea, q.src, Eaddrlen) != 0) + continue; + if(shelf != -1 && atoi(sh) != shelf) + continue; + other = strtok(nil, "\x1"); + if(other == 0) + other = ""; + if(host && strcmp(host, other) != 0) + continue; + p = tab + ntab++; + memcpy(p->ea, q.src, Eaddrlen); + p->major = atoi(sh); + p->name[0] = 0; + if(p->name) + snprint(p->name, sizeof p->name, "%s", other); + } + alarm(0); + } while (ntab == 0 && pflag); + if(ntab == 0){ + fprint(2, "none found.\n"); + exits0("none found"); + } + qsort(tab, ntab, sizeof tab[0], tcmp); +} + +void +showtable(void) +{ + int i; + + for(i = 0; i < ntab; i++) + print("%2d %5d %E %s\n", i, tab[i].major, tab[i].ea, tab[i].name); +} + +int +pickone(void) +{ + char buf[80]; + int n, i; + + for(;;){ + showtable(); + print("[#qp]: "); + switch(n = read(0, buf, sizeof buf)){ + case 1: + if(buf[0] == '\n') + continue; + /* fall through */ + case 2: + if(buf[0] == 'p'){ + probe(); + break; + } + if(buf[0] == 'q') + /* fall through */ + case 0: + case -1: + exits0(0); + break; + } + if(isdigit(buf[0])){ + buf[n] = 0; + i = atoi(buf); + if(i >= 0 && i < ntab) + break; + } + } + return i; +} + +void +sethdr(Pkt *pp, int type) +{ + memmove(pp->dst, con->ea, Eaddrlen); + memset(pp->src, 0, Eaddrlen); + pp->etype = htons(Etype); + pp->type = type; + pp->len = 0; + pp->conn = contag; +} + +void +ethclose(void) +{ + static Pkt msg; + + sethdr(&msg, Treset); + timewait(Iowait); + netsend(&msg, 60); + alarm(0); + con = 0; +} + +int +ethopen(void) +{ + Pkt tpk, rpk; + int i, n; + + contag = (getpid() >> 8) ^ (getpid() & 0xff); + sethdr(&tpk, Tinita); + sethdr(&rpk, 0); + for(i = 0; i < 3 && rpk.type != Tinitb; i++){ + netsend(&tpk, 60); + timewait(Iowait); + n = netget(&rpk, 1000); + alarm(0); + if(n < 0) + return -1; + } + if(rpk.type != Tinitb) + return -1; + sethdr(&tpk, Tinitc); + netsend(&tpk, 60); + return 0; +} + +char +escape(void) +{ + char buf[64]; + int r; + + for(;;){ + fprint(2, ">>> "); + buf[0] = '.'; + rawoff(); + r = read(0, buf, sizeof buf - 1); + rawon(); + if(r == -1) + exits0("kbd: %r"); + switch(buf[0]){ + case 'i': + case 'q': + case '.': + return buf[0]; + } + fprint(2, " (q)uit, (i)nterrupt, (.)continue\n"); + } +} + +/* + * this is a bit too aggressive. it really needs to replace only \n\r with \n. + */ +static uchar crbuf[256]; + +void +nocrwrite(int fd, uchar *buf, int n) +{ + int i, j, c; + + j = 0; + for(i = 0; i < n; i++) + if((c = buf[i]) != '\r') + crbuf[j++] = c; + write(fd, crbuf, j); +} + +int +doloop(void) +{ + int unacked, retries, set[2]; + uchar c, tseq, rseq; + uchar ea[Eaddrlen]; + Pkt tpk, spk; + Mux *m; + + memmove(ea, con->ea, Eaddrlen); + retries = 0; + unacked = 0; + tseq = 0; + rseq = -1; + set[0] = 0; + set[1] = fd; +top: + if ((m = mux(set)) == 0) + exits0("mux: %r"); + for (; ; ) + switch (muxread(m, &spk)) { + case -1: + if (unacked == 0) + break; + if (retries-- == 0) { + fprint(2, "Connection timed out\n"); + muxfree(m); + return 0; + } + netsend(&tpk, Hdrsz + unacked); + break; + case Fkbd: + c = spk.data[0]; + if (c == esc) { + muxfree(m); + switch (escape()) { + case 'q': + tpk.len = 0; + tpk.type = Treset; + netsend(&tpk, 60); + return 0; + case '.': + goto top; + case 'i': + if ((m = mux(set)) == 0) + exits0("mux: %r"); + break; + } + } + sethdr(&tpk, Tdata); + memcpy(tpk.data, spk.data, spk.len); + tpk.len = spk.len; + tpk.seq = ++tseq; + unacked = spk.len; + retries = 2; + netsend(&tpk, Hdrsz + spk.len); + break; + case Fcec: + if (memcmp(spk.src, ea, Eaddrlen) != 0 || + ntohs(spk.etype) != Etype) + continue; + if (spk.type == Toffer && + memcmp(spk.dst, bcast, Eaddrlen) != 0) { + muxfree(m); + return 1; + } + if (spk.conn != contag) + continue; + switch (spk.type) { + case Tdata: + if (spk.seq == rseq) + break; + nocrwrite(1, spk.data, spk.len); + memmove(spk.dst, spk.src, Eaddrlen); + memset(spk.src, 0, Eaddrlen); + spk.type = Tack; + spk.len = 0; + rseq = spk.seq; + netsend(&spk, 60); + break; + case Tack: + if (spk.seq == tseq) + unacked = 0; + break; + case Treset: + muxfree(m); + return 1; + } + break; + case Ffatal: + muxfree(m); + fprint(2, "kbd read error\n"); + exits0("fatal"); + } +} + +int +conn(int n) +{ + int r; + + for(;;){ + if(con) + ethclose(); + con = tab + n; + if(ethopen() < 0){ + fprint(2, "connection failed\n"); + return 0; + } + r = doloop(); + if(r <= 0) + return r; + } +} + +void +exits0(char *s) +{ + if(con != nil) + ethclose(); + rawoff(); + if(svc != nil) + remove(svc); + exits(s); +} |