]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/cec/cec.c
merge
[plan9front.git] / sys / src / cmd / cec / cec.c
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 = '\1c';
54 uchar   ea[Eaddrlen];
55 uchar   unsetea[Eaddrlen];
56
57 extern  int fd;         /* set in netopen */
58
59 void
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
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
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
109 catch(void*, char *note)
110 {
111         if(strcmp(note, "alarm") == 0)
112                 noted(NCONT);
113         noted(NDFLT);
114 }
115
116 int
117 nilea(uchar *ea)
118 {
119         return memcmp(ea, unsetea, Eaddrlen) == 0;
120 }
121
122 void
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
191 timewait(int ms)
192 {
193         alarm(ms);
194 }
195
196 int
197 didtimeout(void)
198 {
199         char err[ERRMAX];
200         int rv;
201
202         *err = 0;
203         errstr(err, sizeof err);
204         rv = strcmp(err, "interrupted") == 0;
205         errstr(err, sizeof err);
206         return rv;
207 }
208
209 ushort
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
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
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
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
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
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
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
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
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
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
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
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
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
550 exits0(char *s)
551 {
552         if(con != nil)
553                 ethclose();
554         rawoff();
555         if(svc != nil)
556                 remove(svc);
557         exits(s);
558 }