xref: /plan9-contrib/sys/src/cmd/cec/cec.c (revision 661d349634de036a14b1de6108b8b752958e81ff)
1 /*
2  * Copyright © Coraid, Inc. 2006, 2007.  All Rights Reserved.
3  * ethernet console for Coraid storage products.
4  *  simple command line version.
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 	HDRSIZ = 18,
23 	Eaddrlen = 6,
24 };
25 
26 typedef struct Shelf Shelf;
27 
28 struct Shelf {
29 	uchar	ea[Eaddrlen];
30 	int	shelfno;
31 	char	*str;
32 };
33 
34 void 	conn(int);
35 void	exits0(char *);
36 void 	gettingkilled(int);
37 int 	pickone(void);
38 void 	probe(void);
39 void	sethdr(Pkt *, int);
40 int	shelfidx(void);
41 
42 extern int errno;
43 extern int fd;			/* set in netopen */
44 
45 Shelf	tab[1000];
46 int	ntab;
47 uchar	contag;
48 int	shelf = -1;
49 Shelf	*connp;
50 char 	esc = '';
51 
52 void
53 usage(void)
54 {
55 	fprint(2, "usage: cec [-d] [-e esc] [-s shelf] interface\n");
56 	exits0("usage");
57 }
58 
59 void
60 catch(void*, char *note)
61 {
62 	if(strcmp(note, "alarm") == 0)
63 		noted(NCONT);
64 	noted(NDFLT);
65 }
66 
67 void
68 main(int argc, char **argv)
69 {
70 	int r, n;
71 
72 	ARGBEGIN{
73 	case 'd':
74 		debug++;
75 		break;
76 	case 's':
77 		shelf = atoi(EARGF(usage()));
78 		break;
79 	case 'e':
80 		esc = toupper(*(EARGF(usage()))) - 'A' + 1;
81 		if(esc <= 0 || esc >= ' ')
82 			usage();
83 		break;
84 	default:
85 		usage();
86 	}ARGEND
87 	if(debug)
88 		fprint(2, "debug is on\n");
89 	if(argc != 1)
90 		usage();
91 
92 	fmtinstall('E', eipfmt);
93 	r = netopen(*argv);
94 	if(r == -1){
95 		fprint(2, "cec: can't netopen %s\n", *argv);
96 		exits0("open");
97 	}
98 	notify(catch);
99 	probe();
100 	for(;;){
101 		n = 0;
102 		if(shelf == -1)
103 			n = pickone();
104 		rawon();
105 		conn(n);
106 		rawoff();
107 		if(shelf != -1)
108 			exits0("shelf not found");
109 	}
110 }
111 
112 void
113 timewait(int ms)  /* arrange for a sig_alarm signal after `ms' milliseconds */
114 {
115 	alarm(ms);
116 }
117 
118 int
119 didtimeout(void)
120 {
121 	char buf[ERRMAX];
122 
123 	rerrstr(buf, sizeof buf);
124 	if(strcmp(buf, "interrupted") == 0){
125 		werrstr(buf, 0);
126 		return 1;
127 	}
128 	return 0;
129 }
130 
131 ushort
132 htons(ushort h)
133 {
134 	ushort n;
135 	uchar *p;
136 
137 	p = (uchar*)&n;
138 	p[0] = h >> 8;
139 	p[1] = h;
140 	return n;
141 }
142 
143 ushort
144 ntohs(int h)
145 {
146 	ushort n;
147 	uchar *p;
148 
149 	n = h;
150 	p = (uchar*)&n;
151 	return p[0] << 8 | p[1];
152 }
153 
154 void
155 probe(void)
156 {
157 	int n;
158 	char *sh, *other;
159 	uchar buf[1500];
160 	Pkt q;
161 	Shelf *p;
162 
163 	ntab = 0;
164 	memset(buf, 0xff, Eaddrlen);
165 	memset(q.dst, 0xff, Eaddrlen);
166 	memset(q.src, 0, Eaddrlen);
167 	q.etype = htons(Etype);
168 	q.type = Tdiscover;
169 	q.len = 0;
170 	q.conn = 0;
171 	q.seq = 0;
172 	netsend(&q, 60);
173 //	fprint(2, "Probing for shelves ... ");
174 	timewait(Iowait);
175 	while((n = netget(&q, sizeof q)) >= 0) {
176 		if((n <= 0 && didtimeout()) || ntab == nelem(tab))
177 			break;
178 		if(n < 60 || q.len == 0 || q.type != Toffer)
179 			continue;
180 		q.data[q.len] = 0;
181 		sh = strtok((char *)q.data, " \t");
182 		if(sh == nil)
183 			continue;
184 		if(shelf != -1 && atoi(sh) != shelf)
185 			continue;
186 		other = strtok(nil, "\x1");
187 		p = tab + ntab++;
188 		memcpy(p->ea, q.src, Eaddrlen);
189 		p->shelfno = atoi(sh);
190 		p->str = other? strdup(other): "";
191 		if(shelf != -1) {
192 			fprint(2, "shelf %d found.\n", shelf);
193 			break;
194 		}
195 	}
196 	alarm(0);
197 	if(ntab == 0) {
198 		fprint(2, "none found.\n");
199 		exits0("none found");
200 	}
201 //	fprint(2, "done.\n");
202 }
203 
204 void
205 showtable(void)
206 {
207 	int i;
208 
209 	for(i = 0; i < ntab; i++)
210 		print("%2d   %5d %E %s\n", i,
211 			tab[i].shelfno, tab[i].ea, tab[i].str);
212 }
213 
214 int
215 pickone(void)
216 {
217 	char buf[80];
218 	int n, i;
219 
220 	for(;;){
221 		showtable();
222 		print("[#qp]: ");
223 		switch(n = read(0, buf, sizeof buf)){
224 		case 1:
225 			if(buf[0] == '\n')
226 				continue;
227 			/* fall through */
228 		case 2:
229 			if(buf[0] == 'p'){
230 				probe();
231 				break;
232 			}
233 			if(buf[0] == 'q')
234 			/* fall through */
235 		case 0:
236 		case -1:
237 				exits0(0);
238 		}
239 		if(isdigit(buf[0])){
240 			buf[n] = 0;
241 			i = atoi(buf);
242 			if(i >= 0 && i < ntab)
243 				break;
244 		}
245 	}
246 	return i;
247 }
248 
249 void
250 sethdr(Pkt *pp, int type)
251 {
252 	memmove(pp->dst, connp->ea, Eaddrlen);
253 	memset(pp->src, 0, Eaddrlen);
254 	pp->etype = htons(Etype);
255 	pp->type = type;
256 	pp->len = 0;
257 	pp->conn = contag;
258 }
259 
260 void
261 ethclose(void)
262 {
263 	static Pkt msg;
264 
265 	sethdr(&msg, Treset);
266 	timewait(Iowait);
267 	netsend(&msg, 60);
268 	alarm(0);
269 	connp = 0;
270 }
271 
272 int
273 ethopen(void)
274 {
275 	Pkt tpk, rpk;
276 	int i, n;
277 
278 	contag = (getpid() >> 8) ^ (getpid() & 0xff);
279 	sethdr(&tpk, Tinita);
280 	sethdr(&rpk, 0);
281 	for(i = 0; i < 3 && rpk.type != Tinitb; i++){
282 		netsend(&tpk, 60);
283 		timewait(Iowait);
284 		n = netget(&rpk, 1000);
285 		alarm(0);
286 		if(n < 0)
287 			return -1;
288 	}
289 	if(rpk.type != Tinitb)
290 		return -1;
291 	sethdr(&tpk, Tinitc);
292 	netsend(&tpk, 60);
293 	return 0;
294 }
295 
296 char
297 escape(void)
298 {
299 	char buf[64];
300 
301 	for(;;){
302 		fprint(2, ">>> ");
303 		buf[0] = '.';
304 		rawoff();
305 		read(0, buf, sizeof buf-1);
306 		rawon();
307 		switch(buf[0]){
308 		case 'i':
309 		case 'q':
310 		case '.':
311 			return buf[0];
312 		}
313 		fprint(2, "	(q)uit, (i)nterrupt, (.)continue\n");
314 	}
315 }
316 
317 /*
318  * this is a bit too agressive.  it really needs to replace only \n\r with \n.
319  */
320 static uchar crbuf[1514];
321 
322 void
323 nocrwrite(int fd, uchar *buf, int n)
324 {
325 	int i, j, c;
326 
327 	j = 0;
328 	for(i = 0; i < n; i++){
329 		if((c = buf[i]) == '\r')
330 			continue;
331 		crbuf[j++] = c;
332 	}
333 	write(fd, crbuf, j);
334 }
335 
336 int
337 doloop(void)
338 {
339 	int unacked, retries, set[2];
340 	uchar c, tseq, rseq;
341 	uchar ea[Eaddrlen];
342 	Mux * m;
343 	Pkt tpk, spk;
344 
345 	memmove(ea, connp->ea, Eaddrlen);
346 	retries = 0;
347 	unacked = 0;
348 	tseq = 0;
349 	rseq = -1;
350 	set[0] = 0;
351 	set[1] = fd;
352 top:
353 	if ((m = mux(set)) == 0)
354 		exits0("mux: %r");
355 	for (; ; )
356 		switch (muxread(m, &spk)) {
357 		case -1:
358 			if (unacked == 0)
359 				break;
360 			if (retries-- == 0) {
361 				fprint(2, "Connection timed out\n");
362 				muxfree(m);
363 				return 0;
364 			}
365 			netsend(&tpk, HDRSIZ + unacked);
366 			break;
367 		case 0:
368 			c = spk.data[0];
369 			if (c == esc) {
370 				muxfree(m);
371 				switch (escape()) {
372 				case 'q':
373 					tpk.len = 0;
374 					tpk.type = Treset;
375 					netsend(&tpk, 60);
376 					return 0;
377 				case '.':
378 					goto top;
379 				case 'i':
380 					if ((m = mux(set)) == 0)
381 						exits0("mux: %r");
382 					break;
383 				}
384 			}
385 			sethdr(&tpk, Tdata);
386 			memcpy(tpk.data, spk.data, spk.len);
387 			tpk.len = spk.len;
388 			tpk.seq = ++tseq;
389 			unacked = spk.len;
390 			retries = 2;
391 			netsend(&tpk, HDRSIZ + spk.len);
392 			break;
393 		default:
394 			if (memcmp(spk.src, ea, Eaddrlen) != 0 ||
395 			    ntohs(spk.etype) != Etype)
396 				continue;
397 			if (spk.type == Toffer) {
398 				muxfree(m);
399 				return 1;
400 			}
401 			if (spk.conn != contag)
402 				continue;
403 			switch (spk.type) {
404 			case Tdata:
405 				if (spk.seq == rseq)
406 					break;
407 				nocrwrite(1, spk.data, spk.len);
408 				if (0)
409 					write(1, spk.data, spk.len);
410 				memmove(spk.dst, spk.src, Eaddrlen);
411 				memset(spk.src, 0, Eaddrlen);
412 				spk.type = Tack;
413 				spk.len = 0;
414 				rseq = spk.seq;
415 				netsend(&spk, 60);
416 				break;
417 			case Tack:
418 				if (spk.seq == tseq)
419 					unacked = 0;
420 				break;
421 			case Treset:
422 				muxfree(m);
423 				return 1;
424 			}
425 		}
426 }
427 
428 void
429 conn(int n)
430 {
431 	do {
432 		if(connp)
433 			ethclose();
434 		connp = &tab[n];
435 		if(ethopen() < 0){
436 			fprint(2, "connection failed.\n");
437 			return;
438 		}
439 	} while(doloop());
440 }
441 
442 void
443 exits0(char *s)
444 {
445 	if(connp != nil)
446 		ethclose();
447 	rawoff();
448 	exits(s);
449 }
450