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 |
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/cmd/cec')
-rwxr-xr-x | sys/src/cmd/cec/LICENSE | 31 | ||||
-rwxr-xr-x | sys/src/cmd/cec/Protocol | 93 | ||||
-rwxr-xr-x | sys/src/cmd/cec/cec.c | 558 | ||||
-rwxr-xr-x | sys/src/cmd/cec/cec.h | 38 | ||||
-rwxr-xr-x | sys/src/cmd/cec/mkfile | 14 | ||||
-rwxr-xr-x | sys/src/cmd/cec/mux.c | 110 | ||||
-rwxr-xr-x | sys/src/cmd/cec/plan9.c | 89 | ||||
-rwxr-xr-x | sys/src/cmd/cec/utils.c | 52 |
8 files changed, 985 insertions, 0 deletions
diff --git a/sys/src/cmd/cec/LICENSE b/sys/src/cmd/cec/LICENSE new file mode 100755 index 000000000..40f3d2c68 --- /dev/null +++ b/sys/src/cmd/cec/LICENSE @@ -0,0 +1,31 @@ +The FreeBSD License + +Copyright © 2006-8 Coraid, Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + +This software is provided by Coraid “as is” and any express or +implied warranties, including, but not limited to, the implied +warranties of merchantability and fitness for a particular purpose are +disclaimed. In no event shall Coraid or contributors be liable for +any direct, indirect, incidental, special, exemplary, or consequential +damages (including, but not limited to, procurement of substitute +goods or services; loss of use, data, or profits; or business +interruption) however caused and on any theory of liability, whether +in contract, strict liability, or tort (including negligence or +otherwise) arising in any way out of the use of this software, even if +advised of the possibility of such damage. + +The views and conclusions contained in the software and documentation +are those of the authors and should not be interpreted as representing +official policies, either expressed or implied, of Coraid. diff --git a/sys/src/cmd/cec/Protocol b/sys/src/cmd/cec/Protocol new file mode 100755 index 000000000..196bc5db4 --- /dev/null +++ b/sys/src/cmd/cec/Protocol @@ -0,0 +1,93 @@ +Coraid Ethernet Console (cec) + +Abstract + +The Coraid Ethernet Console (cec) implements a bidirectional conversation +over raw ethernet frames with provisions for retransmission and discovery. +Like serial consoles, each machine has a single shared interface per service +type, but any number connections can be made from any machine connected to +the same physical network. The communications from the various connections +will be interleaved. The first implementation of cec is for console communications +to Coraid appliances. + +Outline + +1. Packet format +2. The Tdiscover packet and Toffer reply. +3. Initializing a connection. Tinit[abc] +4. The connection. Tdata and Tack +5. Closing the connection. Treset + +1. Packet Format + +All cec packets are follow the layout implied by the following structure + + struct Pkt { + uchar dst[6]; // destination ethernet address + uchar src[6]; // source ethernet address + uchar etype[2]; // service type + uchar type; // type of packet. + uchar conn; // unique connection id. + uchar seq; // packet sequence number + uchar len; // data length. + uchar data[1500]; + }; + +The packet types are as follows: + + enum { + Tinita, + Tinitb, + Tinitc, + Tdata, + Tack, + Tdiscover, + Toffer, + Treset, + }; + +2. The Tdiscover packet and Toffer reply. + +The Tdiscover packet is used to discover the avaliable cec devices on the local +network. The packet sent is of the form + + Tdiscover = { + [dest] 0xffffffff, + [src] mac of local interface, + [etype] service type, + [type] Tdiscover, + [seq] 0, + [len] 0, + [data] 0, + } + +The reply to the Tdiscover packet is of type Toffer which differes from +Tdiscover in that data and len may be set. The contents of data is application +specific. + +3. Initializing a connection. Tinit[abc] + +A connection is initialized by the following conversation: In addition +to the fields set for the Tdiscover packet, the client sends a packet +of type Tinita with the conntag of its choice. The server responds to +Tinita with a packet of type Tinitb. And finally the client sents a +Tinitc packet back to the server, completing the connection. + +4. The connection. Tdata and Tack + +Data is sent from the client to the console server with the Tdata packet. +The seq field is incremented for each data packet sent. Thus data packets +may be transmitted if lost. The data is whatever data the client has to +send to the server, up to 255 bytes. Typically, however, each keystroke +is sent in a seperate packet. The len field is the length of the +data. + +The server responds to a Tdata message with a Tack message with the +same seq sequence number. + +5. Closing the connection. Treset + +Either the server of the client may send a Treset message to close the +connection. There is no response to a Treset message, however any +information associated with that connection (conntag) is discarded +when the Treset message is recieved. 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); +} diff --git a/sys/src/cmd/cec/cec.h b/sys/src/cmd/cec/cec.h new file mode 100755 index 000000000..c5b388d93 --- /dev/null +++ b/sys/src/cmd/cec/cec.h @@ -0,0 +1,38 @@ +typedef struct { + uchar dst[6]; + uchar src[6]; + ushort etype; + uchar type; + uchar conn; + uchar seq; + uchar len; + uchar data[1500]; +} Pkt; + +enum { + Fkbd, + Fcec, + Ffatal, +}; + +typedef struct Mux Mux; +#pragma incomplete Mux; + +enum{ + Iowait = 2000, + Etype = 0xbcbc, +}; +int debug; + +Mux *mux(int fd[2]); +void muxfree(Mux*); +int muxread(Mux*, Pkt*); + +int netget(void *, int); +int netopen(char *name); +int netsend(void *, int); + +void dump(uchar*, int); +void exits0(char*); +void rawoff(void); +void rawon(void); diff --git a/sys/src/cmd/cec/mkfile b/sys/src/cmd/cec/mkfile new file mode 100755 index 000000000..0181f5f34 --- /dev/null +++ b/sys/src/cmd/cec/mkfile @@ -0,0 +1,14 @@ +</$objtype/mkfile + +TARG=cec + +OFILES=\ + cec.$O\ + plan9.$O\ + mux.$O\ + utils.$O\ + +HFILES=cec.h + +BIN=/$objtype/bin +</sys/src/cmd/mkone diff --git a/sys/src/cmd/cec/mux.c b/sys/src/cmd/cec/mux.c new file mode 100755 index 000000000..66f4fa6df --- /dev/null +++ b/sys/src/cmd/cec/mux.c @@ -0,0 +1,110 @@ +#include <u.h> +#include <libc.h> +#include "cec.h" + +typedef struct { + char type; + char pad[3]; + Pkt p; +} Muxmsg; + +typedef struct { + int fd; + int type; + int pid; +} Muxproc; + +struct Mux { + Muxmsg m; + Muxproc p[2]; + int pfd[2]; + int inuse; +}; + +static Mux smux = { +.inuse = -1, +}; + +void +muxcec(int, int cfd) +{ + Muxmsg m; + int l; + + m.type = Fcec; + while((l = netget(&m.p, sizeof m.p)) > 0) + if(write(cfd, &m, l+4) != l+4) + break; + exits(""); +} + +void +muxkbd(int kfd, int cfd) +{ + Muxmsg m; + + m.type = Fkbd; + while((m.p.len = read(kfd, m.p.data, sizeof m.p.data)) > 0) + if(write(cfd, &m, m.p.len+22) != m.p.len+22) + break; + m.type = Ffatal; + write(cfd, &m, 4); + exits(""); +} + +int +muxproc(Mux *m, Muxproc *p, int fd, void (*f)(int, int), int type) +{ + memset(p, 0, sizeof p); + p->type = -1; + switch(p->pid = rfork(RFPROC|RFFDG)){ + case -1: + return -1; + case 0: + close(m->pfd[0]); + f(fd, m->pfd[1]); + default: + p->fd = fd; + p->type = type; + return p->pid; + } +} + +void +muxfree(Mux *m) +{ + close(m->pfd[0]); + close(m->pfd[1]); + postnote(PNPROC, m->p[0].pid, "this note goes to 11"); + postnote(PNPROC, m->p[1].pid, "this note goes to 11"); + waitpid(); + waitpid(); + memset(m, 0, sizeof *m); + m->inuse = -1; +} + +Mux* +mux(int fd[2]) +{ + Mux *m; + + if(smux.inuse != -1) + sysfatal("mux in use"); + m = &smux; + m->inuse = 1; + if(pipe(m->pfd) == -1) + sysfatal("pipe: %r"); + muxproc(m, m->p+0, fd[0], muxkbd, Fkbd); + muxproc(m, m->p+1, fd[1], muxcec, Fcec); + close(m->pfd[1]); + return m; +} + +int +muxread(Mux *m, Pkt *p) +{ + if(read(m->pfd[0], &m->m, sizeof m->m) == -1) + return -1; + memcpy(p, &m->m.p, sizeof *p); + return m->m.type; +} diff --git a/sys/src/cmd/cec/plan9.c b/sys/src/cmd/cec/plan9.c new file mode 100755 index 000000000..62079e297 --- /dev/null +++ b/sys/src/cmd/cec/plan9.c @@ -0,0 +1,89 @@ +/* Copyright © Coraid, Inc. 2006. All rights reserved. */ +#include <u.h> +#include <libc.h> +#include "cec.h" + +int fd = -1; +int cfd = -1; +int efd = -1; + +int +netopen0(char *e) +{ + char buf[128], ctl[13]; + int n; + + snprint(buf, sizeof buf, "%s/clone", e); + if((efd = open(buf, ORDWR)) == -1) + return -1; + memset(ctl, 0, sizeof ctl); + if(read(efd, ctl, sizeof ctl) < 0) + return -1; + n = atoi(ctl); + snprint(buf, sizeof buf, "connect %d", Etype); + if(write(efd, buf, strlen(buf)) != strlen(buf)) + return -1; + snprint(buf, sizeof buf, "%s/%d/ctl", e, n); + if((cfd = open(buf, ORDWR)) < 0) + return -1; + snprint(buf, sizeof buf, "nonblocking"); + if(write(cfd, buf, strlen(buf)) != strlen(buf)) + return -1; + snprint(buf, sizeof buf, "%s/%d/data", e, n); + fd = open(buf, ORDWR); + return fd; +} + +void +netclose(void) +{ + close(efd); + close(cfd); + close(fd); + efd = -1; + cfd = -1; + fd = -1; +} + +int +netopen(char *e) +{ + int r; + + if((r = netopen0(e)) >= 0) + return r; + perror("netopen"); + netclose(); + return -1; +} + +/* what if len < netlen? */ +int +netget(void *v, int len) +{ + int l; + + l = read(fd, v, len); + if(debug && l > 0){ + fprint(2, "read %d bytes\n", l); + dump((uchar*)v, l); + } + if (l <= 0) + return 0; + return l; +} + +int +netsend(void *v, int len) +{ + uchar *p; + + p = v; + if (debug) { + fprint(2, "sending %d bytes\n", len); + dump(p, len); + } + if (len < 60) + len = 60; /* mintu */ + return write(fd, p, len); +} diff --git a/sys/src/cmd/cec/utils.c b/sys/src/cmd/cec/utils.c new file mode 100755 index 000000000..483b41d32 --- /dev/null +++ b/sys/src/cmd/cec/utils.c @@ -0,0 +1,52 @@ +#include <u.h> +#include <libc.h> +#include "cec.h" + +static int fd = -1; +extern char *svc; + +void +rawon(void) +{ + if(svc) + return; + if((fd = open("/dev/consctl", OWRITE)) == -1 || + write(fd, "rawon", 5) != 5) + fprint(2, "Can't make console raw\n"); +} + +void +rawoff(void) +{ + if(svc) + return; + close(fd); +} + +enum { + Perline = 16, + Perch = 3, +}; + +char line[Perch*Perline+1]; + +static void +format(uchar *buf, int n, int t) +{ + int i, r; + + for(i = 0; i < n; i++){ + r = (i + t) % Perline; + if(r == 0 && i + t > 0) + fprint(2, "%s\n", line); + sprint(line + r*Perch, "%.2x ", buf[i]); + } +} + +void +dump(uchar *p, int n) +{ + format(p, n, 0); + if(n % 16 > 0) + print("%s\n", line); +} |