1 /*
2 * cec — coraid ethernet console
3 * Copyright © Coraid, Inc. 2006-2008.
4 * All Rights Reserved.
5 */
6 #include <u.h>
7 #include <libc.h>
8 #include <ip.h> /* really! */
9 #include <ctype.h>
10 #include "cec.h"
11
12 enum {
13 Tinita = 0,
14 Tinitb,
15 Tinitc,
16 Tdata,
17 Tack,
18 Tdiscover,
19 Toffer,
20 Treset,
21
22 Hdrsz = 18,
23 Eaddrlen = 6,
24 };
25
26 typedef struct{
27 uchar ea[Eaddrlen];
28 int major;
29 char name[28];
30 } Shelf;
31
32 int conn(int);
33 void gettingkilled(int);
34 int pickone(void);
35 void probe(void);
36 void sethdr(Pkt *, int);
37 int shelfidx(void);
38
39 Shelf *con;
40 Shelf tab[1000];
41
42 char *host;
43 char *srv;
44 char *svc;
45
46 char pflag;
47
48 int ntab;
49 int shelf = -1;
50
51 uchar bcast[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
52 uchar contag;
53 uchar esc = '';
54 uchar ea[Eaddrlen];
55 uchar unsetea[Eaddrlen];
56
57 extern int fd; /* set in netopen */
58
59 void
post(char * srv,int fd)60 post(char *srv, int fd)
61 {
62 char buf[32];
63 int f;
64
65 if((f = create(srv, OWRITE, 0666)) == -1)
66 sysfatal("create %s: %r", srv);
67 snprint(buf, sizeof buf, "%d", fd);
68 if(write(f, buf, strlen(buf)) != strlen(buf))
69 sysfatal("write %s: %r", srv);
70 close(f);
71 }
72
73 void
dosrv(char * s)74 dosrv(char *s)
75 {
76 int p[2];
77
78 if(pipe(p) < 0)
79 sysfatal("pipe: %r");
80 if (srv[0] != '/')
81 svc = smprint("/srv/%s", s);
82 else
83 svc = smprint("%s", s);
84 post(svc, p[0]);
85 close(p[0]);
86 dup(p[1], 0);
87 dup(p[1], 1);
88
89 switch(rfork(RFFDG|RFPROC|RFNAMEG|RFNOTEG)){
90 case -1:
91 sysfatal("fork: %r");
92 case 0:
93 break;
94 default:
95 exits("");
96 }
97 close(2);
98 }
99
100 void
usage(void)101 usage(void)
102 {
103 fprint(2, "usage: cec [-dp] [-c esc] [-e ea] [-h host] [-s shelf] "
104 "[-S srv] interface\n");
105 exits0("usage");
106 }
107
108 void
catch(void *,char * note)109 catch(void*, char *note)
110 {
111 if(strcmp(note, "alarm") == 0)
112 noted(NCONT);
113 noted(NDFLT);
114 }
115
116 int
nilea(uchar * ea)117 nilea(uchar *ea)
118 {
119 return memcmp(ea, unsetea, Eaddrlen) == 0;
120 }
121
122 void
main(int argc,char ** argv)123 main(int argc, char **argv)
124 {
125 int r, n;
126
127 ARGBEGIN{
128 case 'S':
129 srv = EARGF(usage());
130 break;
131 case 'c':
132 esc = tolower(*(EARGF(usage()))) - 'a' + 1;
133 if(esc == 0 || esc >= ' ')
134 usage();
135 break;
136 case 'd':
137 debug++;
138 break;
139 case 'e':
140 if(parseether(ea, EARGF(usage())) == -1)
141 usage();
142 pflag = 1;
143 break;
144 case 'h':
145 host = EARGF(usage());
146 break;
147 case 'p':
148 pflag = 1;
149 break;
150 case 's':
151 shelf = atoi(EARGF(usage()));
152 break;
153 default:
154 usage();
155 }ARGEND
156 if(argc == 0)
157 *argv = "/net/ether0";
158 else if(argc != 1)
159 usage();
160
161 fmtinstall('E', eipfmt);
162 if(srv != nil)
163 dosrv(srv);
164 r = netopen(*argv);
165 if(r == -1){
166 fprint(2, "cec: can't netopen %s\n", *argv);
167 exits0("open");
168 }
169 notify(catch);
170 probe();
171 for(;;){
172 n = 0;
173 if(shelf == -1 && host == 0 && nilea(ea))
174 n = pickone();
175 rawon();
176 conn(n);
177 rawoff();
178 if(pflag == 0){
179 if(shelf != -1)
180 exits0("shelf not found");
181 if(host)
182 exits0("host not found");
183 if(!nilea(ea))
184 exits0("ea not found");
185 } else if(shelf != -1 || host || !nilea(ea))
186 exits0("");
187 }
188 }
189
190 void
timewait(int ms)191 timewait(int ms)
192 {
193 alarm(ms);
194 }
195
196 int
didtimeout(void)197 didtimeout(void)
198 {
199 char buf[ERRMAX];
200
201 rerrstr(buf, sizeof buf);
202 if(strcmp(buf, "interrupted") == 0){
203 werrstr(buf, 0);
204 return 1;
205 }
206 return 0;
207 }
208
209 ushort
htons(ushort h)210 htons(ushort h)
211 {
212 ushort n;
213 uchar *p;
214
215 p = (uchar*)&n;
216 p[0] = h >> 8;
217 p[1] = h;
218 return n;
219 }
220
221 ushort
ntohs(int h)222 ntohs(int h)
223 {
224 ushort n;
225 uchar *p;
226
227 n = h;
228 p = (uchar*)&n;
229 return p[0] << 8 | p[1];
230 }
231
232 int
tcmp(void * a,void * b)233 tcmp(void *a, void *b)
234 {
235 Shelf *s, *t;
236 int d;
237
238 s = a;
239 t = b;
240 d = s->major - t->major;
241 if(d == 0)
242 d = strcmp(s->name, t->name);
243 if(d == 0)
244 d = memcmp(s->ea, t->ea, Eaddrlen);
245 return d;
246 }
247
248 void
probe(void)249 probe(void)
250 {
251 char *sh, *other;
252 int n;
253 Pkt q;
254 Shelf *p;
255
256 do {
257 ntab = 0;
258 memset(q.dst, 0xff, Eaddrlen);
259 memset(q.src, 0, Eaddrlen);
260 q.etype = htons(Etype);
261 q.type = Tdiscover;
262 q.len = 0;
263 q.conn = 0;
264 q.seq = 0;
265 netsend(&q, 60);
266 timewait(Iowait);
267 while((n = netget(&q, sizeof q)) >= 0){
268 if((n <= 0 && didtimeout()) || ntab == nelem(tab))
269 break;
270 if(n < 60 || q.len == 0 || q.type != Toffer)
271 continue;
272 q.data[q.len] = 0;
273 sh = strtok((char *)q.data, " \t");
274 if(sh == nil)
275 continue;
276 if(!nilea(ea) && memcmp(ea, q.src, Eaddrlen) != 0)
277 continue;
278 if(shelf != -1 && atoi(sh) != shelf)
279 continue;
280 other = strtok(nil, "\x1");
281 if(other == 0)
282 other = "";
283 if(host && strcmp(host, other) != 0)
284 continue;
285 p = tab + ntab++;
286 memcpy(p->ea, q.src, Eaddrlen);
287 p->major = atoi(sh);
288 p->name[0] = 0;
289 if(p->name)
290 snprint(p->name, sizeof p->name, "%s", other);
291 }
292 alarm(0);
293 } while (ntab == 0 && pflag);
294 if(ntab == 0){
295 fprint(2, "none found.\n");
296 exits0("none found");
297 }
298 qsort(tab, ntab, sizeof tab[0], tcmp);
299 }
300
301 void
showtable(void)302 showtable(void)
303 {
304 int i;
305
306 for(i = 0; i < ntab; i++)
307 print("%2d %5d %E %s\n", i, tab[i].major, tab[i].ea, tab[i].name);
308 }
309
310 int
pickone(void)311 pickone(void)
312 {
313 char buf[80];
314 int n, i;
315
316 for(;;){
317 showtable();
318 print("[#qp]: ");
319 switch(n = read(0, buf, sizeof buf)){
320 case 1:
321 if(buf[0] == '\n')
322 continue;
323 /* fall through */
324 case 2:
325 if(buf[0] == 'p'){
326 probe();
327 break;
328 }
329 if(buf[0] == 'q')
330 /* fall through */
331 case 0:
332 case -1:
333 exits0(0);
334 break;
335 }
336 if(isdigit(buf[0])){
337 buf[n] = 0;
338 i = atoi(buf);
339 if(i >= 0 && i < ntab)
340 break;
341 }
342 }
343 return i;
344 }
345
346 void
sethdr(Pkt * pp,int type)347 sethdr(Pkt *pp, int type)
348 {
349 memmove(pp->dst, con->ea, Eaddrlen);
350 memset(pp->src, 0, Eaddrlen);
351 pp->etype = htons(Etype);
352 pp->type = type;
353 pp->len = 0;
354 pp->conn = contag;
355 }
356
357 void
ethclose(void)358 ethclose(void)
359 {
360 static Pkt msg;
361
362 sethdr(&msg, Treset);
363 timewait(Iowait);
364 netsend(&msg, 60);
365 alarm(0);
366 con = 0;
367 }
368
369 int
ethopen(void)370 ethopen(void)
371 {
372 Pkt tpk, rpk;
373 int i, n;
374
375 contag = (getpid() >> 8) ^ (getpid() & 0xff);
376 sethdr(&tpk, Tinita);
377 sethdr(&rpk, 0);
378 for(i = 0; i < 3 && rpk.type != Tinitb; i++){
379 netsend(&tpk, 60);
380 timewait(Iowait);
381 n = netget(&rpk, 1000);
382 alarm(0);
383 if(n < 0)
384 return -1;
385 }
386 if(rpk.type != Tinitb)
387 return -1;
388 sethdr(&tpk, Tinitc);
389 netsend(&tpk, 60);
390 return 0;
391 }
392
393 char
escape(void)394 escape(void)
395 {
396 char buf[64];
397 int r;
398
399 for(;;){
400 fprint(2, ">>> ");
401 buf[0] = '.';
402 rawoff();
403 r = read(0, buf, sizeof buf - 1);
404 rawon();
405 if(r == -1)
406 exits0("kbd: %r");
407 switch(buf[0]){
408 case 'i':
409 case 'q':
410 case '.':
411 return buf[0];
412 }
413 fprint(2, " (q)uit, (i)nterrupt, (.)continue\n");
414 }
415 }
416
417 /*
418 * this is a bit too aggressive. it really needs to replace only \n\r with \n.
419 */
420 static uchar crbuf[256];
421
422 void
nocrwrite(int fd,uchar * buf,int n)423 nocrwrite(int fd, uchar *buf, int n)
424 {
425 int i, j, c;
426
427 j = 0;
428 for(i = 0; i < n; i++)
429 if((c = buf[i]) != '\r')
430 crbuf[j++] = c;
431 write(fd, crbuf, j);
432 }
433
434 int
doloop(void)435 doloop(void)
436 {
437 int unacked, retries, set[2];
438 uchar c, tseq, rseq;
439 uchar ea[Eaddrlen];
440 Pkt tpk, spk;
441 Mux *m;
442
443 memmove(ea, con->ea, Eaddrlen);
444 retries = 0;
445 unacked = 0;
446 tseq = 0;
447 rseq = -1;
448 set[0] = 0;
449 set[1] = fd;
450 top:
451 if ((m = mux(set)) == 0)
452 exits0("mux: %r");
453 for (; ; )
454 switch (muxread(m, &spk)) {
455 case -1:
456 if (unacked == 0)
457 break;
458 if (retries-- == 0) {
459 fprint(2, "Connection timed out\n");
460 muxfree(m);
461 return 0;
462 }
463 netsend(&tpk, Hdrsz + unacked);
464 break;
465 case Fkbd:
466 c = spk.data[0];
467 if (c == esc) {
468 muxfree(m);
469 switch (escape()) {
470 case 'q':
471 tpk.len = 0;
472 tpk.type = Treset;
473 netsend(&tpk, 60);
474 return 0;
475 case '.':
476 goto top;
477 case 'i':
478 if ((m = mux(set)) == 0)
479 exits0("mux: %r");
480 break;
481 }
482 }
483 sethdr(&tpk, Tdata);
484 memcpy(tpk.data, spk.data, spk.len);
485 tpk.len = spk.len;
486 tpk.seq = ++tseq;
487 unacked = spk.len;
488 retries = 2;
489 netsend(&tpk, Hdrsz + spk.len);
490 break;
491 case Fcec:
492 if (memcmp(spk.src, ea, Eaddrlen) != 0 ||
493 ntohs(spk.etype) != Etype)
494 continue;
495 if (spk.type == Toffer &&
496 memcmp(spk.dst, bcast, Eaddrlen) != 0) {
497 muxfree(m);
498 return 1;
499 }
500 if (spk.conn != contag)
501 continue;
502 switch (spk.type) {
503 case Tdata:
504 if (spk.seq == rseq)
505 break;
506 nocrwrite(1, spk.data, spk.len);
507 memmove(spk.dst, spk.src, Eaddrlen);
508 memset(spk.src, 0, Eaddrlen);
509 spk.type = Tack;
510 spk.len = 0;
511 rseq = spk.seq;
512 netsend(&spk, 60);
513 break;
514 case Tack:
515 if (spk.seq == tseq)
516 unacked = 0;
517 break;
518 case Treset:
519 muxfree(m);
520 return 1;
521 }
522 break;
523 case Ffatal:
524 muxfree(m);
525 fprint(2, "kbd read error\n");
526 exits0("fatal");
527 }
528 }
529
530 int
conn(int n)531 conn(int n)
532 {
533 int r;
534
535 for(;;){
536 if(con)
537 ethclose();
538 con = tab + n;
539 if(ethopen() < 0){
540 fprint(2, "connection failed\n");
541 return 0;
542 }
543 r = doloop();
544 if(r <= 0)
545 return r;
546 }
547 }
548
549 void
exits0(char * s)550 exits0(char *s)
551 {
552 if(con != nil)
553 ethclose();
554 rawoff();
555 if(svc != nil)
556 remove(svc);
557 exits(s);
558 }
559