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 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 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 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 } 559