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