xref: /plan9/sys/src/cmd/cec/cec.c (revision 68de9c9388a1b0174f2979e40f3c4361793bf05a)
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