comparison src/lwwire.c @ 0:bef2801ac83e

Initial checkin with reference implementation of core protocol Initial checkin. Has the initial version of the protocol documentation along with a reference implementation of the core protocol.
author William Astle <lost@l-w.ca>
date Sun, 08 May 2016 12:56:39 -0600
parents
children 2f2cbd2d2561
comparison
equal deleted inserted replaced
-1:000000000000 0:bef2801ac83e
1 /*
2 This program implements the lwwire protocol. It expects STDIN and STDOUT
3 to be connected to an appropriately configured communication channel.
4
5 The following timeouts are specified by the protocol and are listed here
6 for convenience:
7
8 Between bytes in a request: 10 milliseconds
9 Between bytes reading a response (client): 10ms <= timeout <= 1000ms
10
11 Server receiving bad request: >= 1100 milliseconds
12
13 Implementation notes:
14
15 This implementation uses low level I/O calls (read()/write()) on STDIN
16 and STDOUT because we MUST have fully unbuffered I/O for the protocol
17 to function properly and we really do not want the stdio overhead for
18 that.
19
20 The complexity of the lwwire_readdata() and lwwire_writedata() functions
21 is required to handle some possible corner cases that would otherwise
22 completely bollix everything up.
23
24 Command line options:
25
26 drive=N,PATH
27
28 Specify that drive #N should reference the file at PATH. Note that the
29 file at PATH will be created if it doesn't exist, but only if it is actually
30 accessed. N is a decimal number from 0 to 255. If N is prefixed with "C",
31 the drive is treated as read-only.
32
33
34 By default, no drives are associated with files. Also, it is unspecified
35 what happens if multiple protocol instances access the same drive.
36
37 */
38
39 // for nanosleep
40 #define _POSIX_C_SOURCE 199309L
41
42 #include <errno.h>
43 #include <fcntl.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <string.h>
47 #include <sys/select.h>
48 #include <time.h>
49 #include <unistd.h>
50
51 #define LWERR_NONE 0
52 #define LWERR_CHECKSUM 0xF3
53 #define LWERR_READ 0xF4
54 #define LWERR_WRITE 0xF5
55 #define LWERR_NOTREADY 0xF6
56
57 struct lwwire_driveinfo
58 {
59 char *path;
60 FILE *fp;
61 int isconst;
62 };
63
64 struct lwwire_driveinfo drivedata[256];
65
66 void lwwire_protoerror(void);
67 int lwwire_readdata(void *, int, int);
68 int lwwire_writedata(void *, int);
69 void lwwire_write(void *, int);
70 int lwwire_read(void *, int);
71 int lwwire_read2(void *, int, int);
72 void lwwire_reset(void);
73 int lwwire_fetch_sector(int dn, int lsn, void *);
74 int lwwire_save_sector(int dn, int lsn, void *);
75 int nonblock(int);
76
77 void lwwire_proto_read(void);
78 void lwwire_proto_write(void);
79 void lwwire_proto_readex(void);
80 void lwwire_proto_requestextension(void);
81 void lwwire_proto_disableextension(void);
82 void lwwire_proto_extensionop(void);
83
84 int main(int argc, char **argv)
85 {
86 unsigned char buf[32];
87 time_t curtime;
88 struct tm *tmval;
89 int rv;
90 int i;
91
92 // make stdin and stdout non-blocking
93 if (nonblock(0) < 0)
94 {
95 fprintf(stderr, "Cannot make stdin non-blocking: %s\n", strerror(errno));
96 exit(1);
97 }
98 if (nonblock(1) < 0)
99 {
100 fprintf(stderr, "Cannot make stdout non-blocking: %s\n", strerror(errno));
101 exit(1);
102 }
103
104 memset(&drivedata, 0, sizeof(drivedata));
105
106 for (i = 1; i < argc; i++)
107 {
108 if (strncmp("drive=", argv[i], 6) == 0)
109 {
110 int dn=0;
111 int isconst = 0;
112 char *ptr;
113 ptr = argv[i] + 6;
114 if (*ptr == 'C')
115 {
116 isconst = 1;
117 ptr++;
118 }
119 while (*ptr >= '0' && *ptr <= '9')
120 {
121 dn = dn * 10 + (*ptr - '0');
122 ptr++;
123 }
124 if (*ptr != ',' || dn > 255)
125 {
126 fprintf(stderr, "Ignoring invalid drive specification: %s\n", argv[i]);
127 continue;
128 }
129 ptr++;
130 drivedata[dn].path = ptr;
131 drivedata[dn].isconst = isconst;
132 }
133 }
134
135 fprintf(stderr, "Running with the following disk images:\n");
136 for (i = 0; i < 256; i++)
137 {
138 if (drivedata[i].path)
139 {
140 fprintf(stderr, "[%d%s] %s\n", i, drivedata[i].isconst ? "C" : "", drivedata[i].path);
141 }
142 }
143
144 // main loop reading operations and dispatching
145 for (;;)
146 {
147 rv = lwwire_readdata(buf, 1, 0);
148 if (rv < 0)
149 {
150 fprintf(stderr, "Error or timeout reading operation code.\n");
151 lwwire_protoerror();
152 continue;
153 }
154 if (rv == 0)
155 {
156 fprintf(stderr, "EOF on comm channel. Exiting.\n");
157 exit(0);
158 }
159 fprintf(stderr, "Handling opcode %02X\n", buf[0]);
160
161 // we have an opcode here
162 switch (buf[0])
163 {
164 case 0x00: // NOOP
165 break;
166
167 case 0x23: // TIME
168 curtime = time(NULL);
169 tmval = localtime(&curtime);
170 buf[0] = tmval -> tm_year;
171 buf[1] = tmval -> tm_mon;
172 buf[2] = tmval -> tm_mday;
173 buf[3] = tmval -> tm_hour;
174 buf[4] = tmval -> tm_min;
175 buf[5] = tmval -> tm_sec;
176 buf[6] = tmval -> tm_wday;
177 lwwire_write(buf, 7);
178 break;
179
180 case 0x46: // PRINTFLUSH
181 // no printer is supported by this implemention so NO-OP
182 break;
183
184 case 0x47: // GETSTAT (useless dw3 operation)
185 case 0x53: // SETSTAT (useless dw3 operation)
186 // burn two bytes from the client and do nothing
187 lwwire_read(buf, 2);
188 break;
189
190 case 0x49: // INIT (old style INIT call)
191 case 0x54: // TERM (old DW3 op treated same as INIT)
192 case 0xF8: // RESET3 (junk on the line during reset)
193 case 0xFE: // RESET1 (junk on the line during reset)
194 case 0xFF: // RESET2 (junk on the line during reset)
195 lwwire_reset();
196 break;
197
198 case 0x50: // PRINT
199 // burn a byte because we don't support any printers
200 lwwire_read(buf, 1);
201 break;
202
203 case 0x52: // READ
204 case 0x72: // REREAD (same semantics as READ)
205 fprintf(stderr, "DWPROTO: read()\n");
206 lwwire_proto_read();
207 break;
208
209 case 0x57: // WRITE
210 case 0x77: // REWRITE (same semantics as WRITE)
211 fprintf(stderr, "DWPROTO: write()\n");
212 lwwire_proto_write();
213 break;
214
215 case 0x5A: // DWINIT (new style init)
216 lwwire_reset();
217 if (lwwire_read(buf, 1) < 0)
218 break;
219 fprintf(stderr, "DWINIT: client drive code %02X\n", buf[0]);
220 // tell the client we speak lwwire protocol
221 buf[0] = 0x80;
222 lwwire_write(buf, 1);
223 break;
224
225 case 0xD2: // READEX (improved reading operation)
226 case 0xF2: // REREADEX (same semantics as READEX)
227 fprintf(stderr, "DWPROTO: readex()\n");
228 lwwire_proto_readex();
229 break;
230
231 case 0xF0: // REQUESTEXTENSION
232 lwwire_proto_requestextension();
233 break;
234
235 case 0xF1: // DISABLEEXTENSION
236 lwwire_proto_disableextension();
237 break;
238
239 case 0xF3: // EXTENSIONOP
240 lwwire_proto_extensionop();
241 break;
242
243 default:
244 fprintf(stderr, "Unrecognized operation code %02X. Doing error state.\n", buf[0]);
245 lwwire_protoerror();
246 break;
247 }
248 }
249 }
250
251 // protocol handling functions
252 void lwwire_proto_read(void)
253 {
254 unsigned char buf[259];
255 int ec;
256 int lsn;
257 int i;
258
259 if (lwwire_read(buf, 4) < 0)
260 return;
261
262 lsn = (buf[1] << 16) | (buf[2] << 8) | buf[3];
263
264 ec = lwwire_fetch_sector(buf[0], lsn, buf + 1);
265 buf[0] = ec;
266 lwwire_write(buf, 1);
267 if (ec)
268 return;
269 // all this futzing around here is probably a long enough
270 // delay but testing on real hardware is needed here
271 ec = 0;
272 for (i = 1; i < 257; i++)
273 ec += buf[i];
274 buf[257] = (ec >> 8) & 0xff;
275 buf[258] = ec & 0xff;
276 lwwire_write(buf + 1, 258);
277 }
278
279 void lwwire_proto_write(void)
280 {
281 unsigned char buf[262];
282 int lsn;
283 int ec;
284 int i;
285 if (lwwire_read(buf, 262) < 0)
286 return;
287
288 lsn = (buf[1] << 16) | (buf[2] << 8) | buf[3];
289 for (ec = 0, i = 4; i < 260; i++)
290 ec += buf[i];
291 if (ec != ((buf[260] << 8) | buf[261]))
292 {
293 buf[0] = LWERR_CHECKSUM;
294 }
295 else
296 {
297 ec = lwwire_save_sector(buf[0], lsn, buf + 4);
298 buf[0] = ec;
299 }
300 lwwire_write(buf, 1);
301 }
302
303 void lwwire_proto_readex(void)
304 {
305 unsigned char buf[256];
306 int lsn;
307 int ec;
308 int i;
309 int csum;
310 if (lwwire_read(buf, 4) < 0)
311 return;
312 lsn = (buf[1] << 16) | (buf[2] << 8) | buf[3];
313 ec = lwwire_fetch_sector(buf[0], lsn, buf);
314 if (ec)
315 memset(buf, 0, 256);
316 for (i = 0, csum = 0; i < 256; i++)
317 csum += buf[i];
318 lwwire_write(buf, 256);
319 if ((i = lwwire_read2(buf, 2, 5)) < 0)
320 {
321 fprintf(stderr, "Error reading protocol bytes: %d, %s\n", i, strerror(errno));
322 return;
323 }
324 i = (buf[0] << 8) | buf[1];
325 if (i != csum)
326 ec = LWERR_CHECKSUM;
327 buf[0] = ec;
328 lwwire_write(buf, 1);
329 }
330
331 void lwwire_proto_requestextension(void)
332 {
333 unsigned char buf[1];
334
335 if (lwwire_read(buf, 1) < 0)
336 return;
337 // NAK the request
338 buf[1] = 0x55;
339 lwwire_write(buf, 1);
340 }
341
342 void lwwire_proto_disableextension(void)
343 {
344 unsigned char buf[1];
345
346 if (lwwire_read(buf, 1) < 0)
347 return;
348 // ACK disabling any unsupported extensions
349 buf[1] = 0x42;
350 lwwire_write(buf, 1);
351 }
352
353 void lwwire_proto_extensionop(void)
354 {
355 unsigned char buf[1];
356 if (lwwire_read(buf, 1) < 0)
357 return;
358 // we don't currently support any extensions so treat as unknown
359 lwwire_protoerror();
360 }
361
362 // Various infrastructure things follow here.
363 int nonblock(int fd)
364 {
365 int flags;
366
367 flags = fcntl(fd, F_GETFL);
368 if (flags < 0)
369 return -1;
370 flags |= O_NONBLOCK;
371 return fcntl(fd, F_SETFL, flags);
372 }
373
374 /*
375 Read len bytes from the input. If no bytes are available after
376 10 ms, return error.
377
378 This *may* allow a timeout longer than 10ms. However, it will
379 eventually time out. In the worse case, it is more permissive
380 than the specification. It will not time out before 10ms elapses.
381
382 If "itimeout" is 0, then it will wait forever for the first
383 byte. Otherwise, it will time out even on the first one.
384
385 */
386 int lwwire_readdata(void *buf, int len, int itimeout)
387 {
388 int toread = len;
389 int rv;
390 fd_set fdset;
391 struct timeval timeout;
392
393 if (itimeout == 0)
394 {
395 for (;;)
396 {
397 // now wait for the descriptor to be readable
398 FD_ZERO(&fdset);
399 FD_SET(0, &fdset);
400
401 rv = select(1, &fdset, NULL, NULL, NULL);
402 if (rv < 0)
403 {
404 // this is a last ditch effort to not break completely
405 // in the face of a signal; it should occur only rarely
406 // and it is not clear what the correct behaviour should
407 // be.
408 if (errno == EINTR)
409 continue;
410 return -1;
411 }
412 // if we actually have something to read, move on
413 if (rv > 0)
414 break;
415 }
416 }
417 while (toread > 0)
418 {
419 rv = read(0, buf, toread);
420 if (rv == toread)
421 break;
422 if (rv == 0)
423 {
424 // flag EOF so the caller knows to bail
425 return 0;
426 }
427 if (rv < 0)
428 {
429 if (errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR)
430 {
431 return -1;
432 }
433 rv = 0;
434 }
435 // now rv is the number of bytes read
436 buf += rv;
437 toread -= rv;
438
439 // now wait for the descriptor to be readable
440 FD_ZERO(&fdset);
441 FD_SET(0, &fdset);
442 timeout.tv_sec = 0;
443 timeout.tv_usec = 10000 * itimeout;
444
445 rv = select(1, &fdset, NULL, NULL, &timeout);
446 if (rv < 0)
447 {
448 // this is a last ditch effort to not break completely
449 // in the face of a signal; it should occur only rarely
450 // and it is not clear what the correct behaviour should
451 // be.
452 if (errno == EINTR)
453 continue;
454 return -1;
455 }
456 // timeout condition
457 if (rv == 0)
458 {
459 errno = ETIMEDOUT;
460 return -1;
461 }
462 // anything else here means we have more bytes to read
463 }
464 fprintf(stderr, "Protocol bytes read (%d):", len);
465 for (rv = 0; rv < len; rv++)
466 fprintf(stderr, " %02X ", ((char *)(buf))[rv] & 0xff);
467 fprintf(stderr, "\n");
468 return len;
469 }
470
471 /*
472 Write data to the output. This will time out after 10 seconds. The timeout
473 is only there in case the underlying communication channel goes out to lunch.
474
475 It returns -1 on error or len on success.
476
477 The timeout requires the file descriptor to be non-blocking.
478
479 */
480 int lwwire_writedata(void *buf, int len)
481 {
482 int towrite = len;
483 int rv;
484 fd_set fdset;
485 struct timeval timeout;
486
487 while (towrite > 0)
488 {
489 rv = write(0, buf, towrite);
490 if (rv == towrite)
491 break;
492 if (rv < 0)
493 {
494 if (errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR)
495 {
496 return -1;
497 }
498 rv = 0;
499 }
500 // now rv is the number of bytes read
501 buf += rv;
502 towrite -= rv;
503
504 // now wait for the descriptor to be writable
505 FD_ZERO(&fdset);
506 FD_SET(1, &fdset);
507 timeout.tv_sec = 10;
508 timeout.tv_usec = 0;
509
510 rv = select(2, NULL, &fdset, NULL, &timeout);
511 if (rv < 0)
512 {
513 // this is a last ditch effort to not break completely
514 // in the face of a signal; it should occur only rarely
515 // and it is not clear what the correct behaviour should
516 // be.
517 if (errno == EINTR)
518 continue;
519 return -1;
520 }
521 // timeout condition
522 if (rv == 0)
523 {
524 errno = ETIMEDOUT;
525 return -1;
526 }
527 // anything else here means we have more bytes to write
528 }
529 fprintf(stderr, "Protocol bytes written (%d):", len);
530 for (rv = 0; rv < len; rv++)
531 fprintf(stderr, " %02X ", ((char *)(buf))[rv] & 0xff);
532 fprintf(stderr, "\n");
533 return len;
534 }
535
536 // like lwwire_writedata() except it bails the program on error.
537 void lwwire_write(void *buf, int len)
538 {
539 if (lwwire_writedata(buf, len) < 0)
540 {
541 fprintf(stderr, "Error writing %d bytes to client: %s\n", len, strerror(errno));
542 exit(1);
543 }
544 }
545
546 // like lwwire_readdata() except it bails on EOF and bounces to
547 // error state on errors, and always does the timeout for initial
548 // bytes. It returns -1 on a timeout. It does not return on EOF.
549 // It does not return on random errors.
550 int lwwire_read2(void *buf, int len, int toscale)
551 {
552 int rv;
553 rv = lwwire_readdata(buf, len, toscale);
554 if (rv < 0)
555 {
556 if (errno == ETIMEDOUT)
557 {
558 lwwire_protoerror();
559 return -1;
560 }
561 fprintf(stderr, "Error reading %d bytes from client: %s\n", len, strerror(errno));
562 exit(1);
563 }
564 if (rv == 0)
565 {
566 fprintf(stderr, "EOF reading %d bytes from client.", len);
567 exit(0);
568 }
569 return 0;
570 }
571
572 int lwwire_read(void *buf, int len)
573 {
574 return lwwire_read2(buf, len, 1);
575 }
576
577 /*
578 Handle a protocol error by maintaining radio silence for
579 at least 1100 ms. The pause must be *at least* 1100ms so
580 it's no problem if the messing about takes longer. Do a
581 state reset after the error.
582 */
583 void lwwire_protoerror(void)
584 {
585 struct timespec sltime;
586 struct timespec rtime;
587
588 sltime.tv_sec = 1;
589 sltime.tv_nsec = 100000000;
590
591 while (nanosleep(&sltime, &rtime) < 0)
592 {
593 // anything other than EINTR indicates something seriously messed up
594 if (errno != EINTR)
595 break;
596 sltime = rtime;
597 }
598 lwwire_reset();
599 }
600
601 /* fetch a file pointer for the specified drive number */
602 FILE *lwwire_fetch_drive_fp(int dn)
603 {
604 if (drivedata[dn].path == NULL)
605 return NULL;
606 if (drivedata[dn].fp)
607 {
608 if (ferror(drivedata[dn].fp))
609 fclose(drivedata[dn].fp);
610 else
611 return drivedata[dn].fp;
612 }
613
614 drivedata[dn].fp = fopen(drivedata[dn].path, "r+");
615 if (!drivedata[dn].fp)
616 {
617 if (errno == ENOENT && !drivedata[dn].isconst)
618 {
619 drivedata[dn].fp = fopen(drivedata[dn].path, "w+");
620 }
621 }
622 return drivedata[dn].fp;
623 }
624
625 /* read a sector from a disk image */
626 int lwwire_fetch_sector(int dn, int lsn, void *buf)
627 {
628 FILE *fp;
629 int rc;
630
631 fp = lwwire_fetch_drive_fp(dn);
632 if (!fp)
633 return LWERR_NOTREADY;
634
635 if (fseek(fp, lsn * 256, SEEK_SET) < 0)
636 return LWERR_READ;
637 rc = fread(buf, 1, 256, fp);
638 if (rc < 256)
639 {
640 memset(buf + rc, 0, 256 - rc);
641 }
642 return 0;
643 }
644
645 int lwwire_save_sector(int dn, int lsn, void *buf)
646 {
647 FILE *fp;
648 int rc;
649
650 fp = lwwire_fetch_drive_fp(dn);
651 if (!fp)
652 return LWERR_NOTREADY;
653 if (drivedata[dn].isconst)
654 return LWERR_WRITE;
655 if (fseek(fp, lsn * 256, SEEK_SET) < 0)
656 return LWERR_WRITE;
657 rc = fwrite(buf, 1, 256, fp);
658 if (rc < 256)
659 return LWERR_WRITE;
660 return 0;
661 }
662
663
664 /*
665 Reset the protocol state to "base protocol" mode.
666 */
667 void lwwire_reset(void)
668 {
669 }