/* * Copyright © Coraid, Inc. 2006, 2007. All Rights Reserved. * ethernet console for Coraid storage products. * simple command line version. */ #include #include #include /* really! */ #include #include "cec.h" enum { Tinita = 0, Tinitb, Tinitc, Tdata, Tack, Tdiscover, Toffer, Treset, HDRSIZ = 18, Eaddrlen = 6, }; typedef struct Shelf Shelf; struct Shelf { uchar ea[Eaddrlen]; int shelfno; char *str; }; void conn(int); void exits0(char *); void gettingkilled(int); int pickone(void); void probe(void); void sethdr(Pkt *, int); int shelfidx(void); extern int errno; extern int fd; /* set in netopen */ Shelf tab[1000]; int ntab; uchar contag; int shelf = -1; Shelf *connp; char esc = ''; void usage(void) { fprint(2, "usage: cec [-d] [-e esc] [-s shelf] interface\n"); exits0("usage"); } void catch(void*, char *note) { if(strcmp(note, "alarm") == 0) noted(NCONT); noted(NDFLT); } void main(int argc, char **argv) { int r, n; ARGBEGIN{ case 'd': debug++; break; case 's': shelf = atoi(EARGF(usage())); break; case 'e': esc = toupper(*(EARGF(usage()))) - 'A' + 1; if(esc <= 0 || esc >= ' ') usage(); break; default: usage(); }ARGEND if(debug) fprint(2, "debug is on\n"); if(argc != 1) usage(); fmtinstall('E', eipfmt); 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) n = pickone(); rawon(); conn(n); rawoff(); if(shelf != -1) exits0("shelf not found"); } } void timewait(int ms) /* arrange for a sig_alarm signal after `ms' milliseconds */ { 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]; } void probe(void) { int n; char *sh, *other; uchar buf[1500]; Pkt q; Shelf *p; ntab = 0; memset(buf, 0xff, Eaddrlen); 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); // fprint(2, "Probing for shelves ... "); 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(shelf != -1 && atoi(sh) != shelf) continue; other = strtok(nil, "\x1"); p = tab + ntab++; memcpy(p->ea, q.src, Eaddrlen); p->shelfno = atoi(sh); p->str = other? strdup(other): ""; if(shelf != -1) { fprint(2, "shelf %d found.\n", shelf); break; } } alarm(0); if(ntab == 0) { fprint(2, "none found.\n"); exits0("none found"); } // fprint(2, "done.\n"); } void showtable(void) { int i; for(i = 0; i < ntab; i++) print("%2d %5d %E %s\n", i, tab[i].shelfno, tab[i].ea, tab[i].str); } 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); } 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, connp->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); connp = 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]; for(;;){ fprint(2, ">>> "); buf[0] = '.'; rawoff(); read(0, buf, sizeof buf-1); rawon(); switch(buf[0]){ case 'i': case 'q': case '.': return buf[0]; } fprint(2, " (q)uit, (i)nterrupt, (.)continue\n"); } } /* * this is a bit too agressive. it really needs to replace only \n\r with \n. */ static uchar crbuf[1514]; 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') continue; crbuf[j++] = c; } write(fd, crbuf, j); } int doloop(void) { int unacked, retries, set[2]; uchar c, tseq, rseq; uchar ea[Eaddrlen]; Mux * m; Pkt tpk, spk; memmove(ea, connp->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, HDRSIZ + unacked); break; case 0: 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, HDRSIZ + spk.len); break; default: if (memcmp(spk.src, ea, Eaddrlen) != 0 || ntohs(spk.etype) != Etype) continue; if (spk.type == Toffer) { 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); if (0) write(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; } } } void conn(int n) { do { if(connp) ethclose(); connp = &tab[n]; if(ethopen() < 0){ fprint(2, "connection failed.\n"); return; } } while(doloop()); } void exits0(char *s) { if(connp != nil) ethclose(); rawoff(); exits(s); }