324 lines
		
	
	
		
			7.6 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			324 lines
		
	
	
		
			7.6 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
| /*	slip 1.1 - Serial line IP			Author: Kees J. Bot
 | |
|  *								19 Jul 1997
 | |
|  */
 | |
| #define nil 0
 | |
| #include <sys/types.h>
 | |
| #include <stdarg.h>
 | |
| #include <stdlib.h>
 | |
| #include <unistd.h>
 | |
| #include <fcntl.h>
 | |
| #include <string.h>
 | |
| #include <errno.h>
 | |
| #include <sys/asynchio.h>
 | |
| 
 | |
| #if __minix && !__minix_vmd
 | |
| #define HAS_ASYN	0	/* Standard Minix doesn't have async I/O. */
 | |
| #else
 | |
| #define HAS_ASYN	1	/* Everyone else does in some way. */
 | |
| #endif
 | |
| 
 | |
| #if !HAS_ASYN
 | |
| #include <signal.h>
 | |
| #endif
 | |
| 
 | |
| #define END		0300		/* End of packet. */
 | |
| #define ESC		0333		/* Byte stuffing escape. */
 | |
| #define ESC_END		0334		/* END -> ESC ESC_END -> END. */
 | |
| #define ESC_ESC		0335		/* ESC -> ESC ESC_ESC -> ESC. */
 | |
| 
 | |
| #define PACKLEN		2048		/* Max datagram size. */
 | |
| #define SLIPLEN   (1 + 2*PACKLEN + 1)	/* Max serial size when all escaped. */
 | |
| 
 | |
| /* Pathetic fprintf() clone to avoid dragging in the stdio library. */
 | |
| static int fprintf(int fd, const char *format, ...);
 | |
| #define stderr	2
 | |
| 
 | |
| int main(int argc, char **argv)
 | |
| {
 | |
|     char *ps_device;
 | |
|     int ps_fd;
 | |
|     int doing[2], discard;
 | |
|     ssize_t r;
 | |
| #if !HAS_ASYN
 | |
|     pid_t other_pid;
 | |
| #endif
 | |
|     size_t ps_len[2], sl_len[2];
 | |
|     unsigned char *sl_end;
 | |
|     unsigned char ps_buf[2][PACKLEN];
 | |
|     unsigned char sl_buf[2][SLIPLEN];
 | |
|     asynchio_t asyn;
 | |
| 
 | |
|     if (argc != 2) {
 | |
| 	fprintf(stderr, "Usage: slip psip-device\n");
 | |
| 	exit(1);
 | |
|     }
 | |
|     ps_device= argv[1];
 | |
| 
 | |
|     if ((ps_fd= open(ps_device, O_RDWR)) < 0) {
 | |
| 	fprintf(stderr, "slip: can't open %s: %s\n",
 | |
| 	    ps_device, strerror(errno));
 | |
| 	exit(1);
 | |
|     }
 | |
| 
 | |
|     doing[0]= 1;	/* We're doing serial -> psip. */
 | |
|     discard= 0;		/* No input error. */
 | |
|     sl_len[0]= 0;	/* Nothing read from serial line yet. */
 | |
|     sl_end= nil;	/* No END marker seen. */
 | |
|     ps_len[0]= 0;	/* Nothing to write to pseudo IP device. */
 | |
| 
 | |
|     doing[1]= 1;	/* We're doing psip -> serial. */
 | |
|     sl_len[1]= 0;	/* Nothing read from pseudo IP device yet. */
 | |
|     ps_len[1]= 0;	/* Nothing to write to serial line. */
 | |
| 
 | |
| #if !HAS_ASYN
 | |
|     /* Oops, standard Minix can't do asynchronous I/O.  Fork and let the parent
 | |
|      * do serial -> psip, and the child do psip -> serial.  (Note that we have
 | |
|      * to make sure that we're not reading and writing at the same time even
 | |
|      * for standard Minix.  For Minix-vmd we do fill an input buffer while an
 | |
|      * output buffer is waiting to be drained to improve performance a bit.)
 | |
|      */
 | |
|     switch ((other_pid= fork())) {
 | |
|     case -1:
 | |
| 	fprintf(stderr, "slip: can't fork: %s\n", strerror(errno));
 | |
| 	exit(1);
 | |
|     case 0:
 | |
| 	/* Child. */
 | |
| 	doing[0]= 0;	/* *Not* doing serial -> psip. */
 | |
| 	other_pid= getppid();
 | |
| 	break;
 | |
|     default:
 | |
| 	/* Parent. */
 | |
| 	doing[1]= 0;	/* *Not* doing psip -> serial. */
 | |
|     }
 | |
| #endif
 | |
| 
 | |
|     asyn_init(&asyn);
 | |
| 
 | |
|     for (;;) {
 | |
| 	if (doing[0]) {
 | |
| 	    /* If there is an END marker in the serial input then create
 | |
| 	     * an IP packet to be send to the TCP/IP task.
 | |
| 	     */
 | |
| 	    while (sl_end != nil && ps_len[0] == 0) {
 | |
| 		unsigned char *sp= sl_buf[0];
 | |
| 		unsigned char *pp= ps_buf[0];
 | |
| 
 | |
| 		while (sp < sl_end) {
 | |
| 		    int c= *sp++;
 | |
| 
 | |
| 		    if (c == ESC) {
 | |
| 			switch (*sp++) {
 | |
| 			case ESC_ESC:	/* ESC ESC_ESC -> ESC. */
 | |
| 			    c= ESC;
 | |
| 			    break;
 | |
| 			case ESC_END:	/* ESC ESC_END -> END. */
 | |
| 			    c= END;
 | |
| 			    break;
 | |
| 			default:
 | |
| 			    /* Protocol error. */
 | |
| 			    discard= 1;
 | |
| 			}
 | |
| 		    }
 | |
| 		    if (pp < ps_buf[0] + PACKLEN) {
 | |
| 			*pp++ = c;
 | |
| 		    } else {
 | |
| 			/* Packet too big, discard. */
 | |
| 			discard= 1;
 | |
| 		    }
 | |
| 		}
 | |
| 		if (discard) {
 | |
| 		    discard= 0;
 | |
| 		} else {
 | |
| 		    /* A new packet can be send to the TCP/IP server. */
 | |
| 		    ps_len[0]= (pp - ps_buf[0]);
 | |
| 		}
 | |
| 		/* Move what's beyond END to the front. */
 | |
| 		sl_end++;
 | |
| 		sl_len[0] -= (sl_end - sl_buf[0]);
 | |
| 		memmove(sl_buf[0], sl_end, sl_len[0]);
 | |
| 		sl_end= memchr(sl_buf[0], END, sl_len[0]);
 | |
| 	    }
 | |
| 
 | |
| 	    /* Reading from serial input. */
 | |
| 	    if (sl_end == nil && (HAS_ASYN || ps_len[0] == 0)) {
 | |
| 		r= asyn_read(&asyn, 0, sl_buf[0] + sl_len[0],
 | |
| 							SLIPLEN - sl_len[0]);
 | |
| 		if (r > 0) {
 | |
| 		    sl_end= memchr(sl_buf[0] + sl_len[0], END, r);
 | |
| 		    sl_len[0]+= r;
 | |
| 		    if (sl_end == nil && sl_len[0] == SLIPLEN) {
 | |
| 			/* Packet is idiotically big and no END in sight. */
 | |
| 			sl_len[0]= 0;
 | |
| 			discard= 1;
 | |
| 		    }
 | |
| 		} else
 | |
| 		if (r == 0) {
 | |
| 		    fprintf(stderr, "slip: EOF on serial input\n");
 | |
| 		    break;
 | |
| 		} else
 | |
| 		if (errno != ASYN_INPROGRESS) {
 | |
| 		    fprintf(stderr, "slip: serial input error: %s\n",
 | |
| 			strerror(errno));
 | |
| 		    break;
 | |
| 		}
 | |
| 	    }
 | |
| 
 | |
| 	    /* Writing to the psip device. */
 | |
| 	    if (ps_len[0] > 0) {
 | |
| 		r= asyn_write(&asyn, ps_fd, ps_buf[0], ps_len[0]);
 | |
| 		if (r == ps_len[0]) {
 | |
| 		    /* Packet written. */
 | |
| 		    ps_len[0]= 0;
 | |
| 		} else
 | |
| 		if (r >= 0) {
 | |
| 		    fprintf(stderr,
 | |
| 			"slip: odd write to %s, tried %u, wrote %d\n",
 | |
| 			ps_device, (unsigned) ps_len[0], (int) r);
 | |
| 		    break;
 | |
| 		} else
 | |
| 		if (errno != ASYN_INPROGRESS) {
 | |
| 		    fprintf(stderr, "slip: error writing %s: %s\n",
 | |
| 			ps_device, strerror(errno));
 | |
| 		    break;
 | |
| 		}
 | |
| 	    }
 | |
| 	}
 | |
| 
 | |
| 	if (doing[1]) {
 | |
| 	    /* Transform an IP packet to a "byte stuffed" serial packet. */
 | |
| 	    if (ps_len[1] > 0 && sl_len[1] == 0) {
 | |
| 		unsigned char *pp= ps_buf[1];
 | |
| 		unsigned char *sp= sl_buf[1];
 | |
| 
 | |
| 		*sp++ = END;
 | |
| 		while (ps_len[1] > 0) {
 | |
| 		    int c= *pp++;
 | |
| 		    ps_len[1]--;
 | |
| 		    switch (c) {
 | |
| 		    case ESC:		/* ESC -> ESC ESC_ESC. */
 | |
| 			*sp++ = ESC;
 | |
| 			c= ESC_ESC;
 | |
| 			break;
 | |
| 		    case END:		/* END -> ESC ESC_END. */
 | |
| 			*sp++ = ESC;
 | |
| 			c= ESC_END;
 | |
| 			break;
 | |
| 		    }
 | |
| 		    *sp++ = c;
 | |
| 		}
 | |
| 		*sp++ = END;
 | |
| 		sl_len[1]= (sp - sl_buf[1]);
 | |
| 	    }
 | |
| 
 | |
| 	    /* Reading from the psip device. */
 | |
| 	    if (ps_len[1] == 0 && (HAS_ASYN || sl_len[1] == 0)) {
 | |
| 		r= asyn_read(&asyn, ps_fd, ps_buf[1], PACKLEN);
 | |
| 		if (r > 0) {
 | |
| 		    /* One packet read. */
 | |
| 		    ps_len[1]= r;
 | |
| 		} else
 | |
| 		if (r == 0) {
 | |
| 		    fprintf(stderr, "slip: EOF on %s\n", ps_device);
 | |
| 		    break;
 | |
| 		} else
 | |
| 		if (errno != ASYN_INPROGRESS) {
 | |
| 		    fprintf(stderr, "slip: error reading %s: %s\n",
 | |
| 			ps_device, strerror(errno));
 | |
| 		    break;
 | |
| 		}
 | |
| 	    }
 | |
| 
 | |
| 	    /* Writing to serial output. */
 | |
| 	    if (sl_len[1] > 0) {
 | |
| 		r= asyn_write(&asyn, 1, sl_buf[1], sl_len[1]);
 | |
| 		if (r > 0) {
 | |
| 		    if ((sl_len[1]-= r) > 0) {
 | |
| 			memmove(sl_buf[1], sl_buf[1] + r, sl_len[1]);
 | |
| 		    }
 | |
| 		} else
 | |
| 		if (r == 0) {
 | |
| 		    fprintf(stderr, "slip: EOF on serial output\n");
 | |
| 		    break;
 | |
| 		} else
 | |
| 		if (errno != ASYN_INPROGRESS) {
 | |
| 		    fprintf(stderr, "slip: serial output error: %s\n",
 | |
| 			strerror(errno));
 | |
| 		    break;
 | |
| 		}
 | |
| 	    }
 | |
| 	}
 | |
| 
 | |
| 	/* Wait for something to happen. */
 | |
| 	if (asyn_wait(&asyn, 0, nil) < 0) {
 | |
| 	    fprintf(stderr,
 | |
| 		"slip: error while waiting for I/O to happen: %s\n",
 | |
| 		strerror(errno));
 | |
| 	    break;
 | |
| 	}
 | |
|     }
 | |
| #if !HAS_ASYN
 | |
|     /* Tell my alter ego that the game is over. */
 | |
|     kill(other_pid, SIGKILL);
 | |
| #endif
 | |
|     return 1;
 | |
| }
 | |
| 
 | |
| static int fprintf(int fd, const char *format, ...)
 | |
| /* Simple fprintf() to save a few bytes by not using the stdio library. */
 | |
| {
 | |
|     int len;
 | |
|     ssize_t r;
 | |
|     const char *fp0, *fp;
 | |
|     va_list ap;
 | |
| 
 | |
|     len= 0;
 | |
|     fp= fp0= format;
 | |
|     va_start(ap, format);
 | |
| 
 | |
|     while (*fp != 0) {
 | |
| 	if (*fp == '%' && memchr("sdu", fp[1], 3) != nil) {
 | |
| 	    if (fp > fp0) {
 | |
| 		if ((r= write(fd, fp0, (fp - fp0))) < 0) return -1;
 | |
| 		len+= r;
 | |
| 	    }
 | |
| 	    fp++;
 | |
| 	    fp0= fp+1;
 | |
| 
 | |
| 	    if (*fp == 's') {
 | |
| 		char *s= va_arg(ap, char *);
 | |
| 
 | |
| 		if ((r= write(fd, s, strlen(s))) < 0) return -1;
 | |
| 		len+= r;
 | |
| 	    } else {
 | |
| 		int d;
 | |
| 		unsigned u;
 | |
| 		char a[3 * sizeof(u) + 2];
 | |
| 		char *p;
 | |
| 
 | |
| 		if (*fp == 'd') {
 | |
| 		    u= d= va_arg(ap, int);
 | |
| 		    if (d < 0) u= -u;
 | |
| 		} else {
 | |
| 		    u= va_arg(ap, unsigned);
 | |
| 		    d= 0;
 | |
| 		}
 | |
| 
 | |
| 		p= a + sizeof(a);
 | |
| 		*--p= 0;
 | |
| 		do *--p= '0' + (u % 10); while ((u /= 10) > 0);
 | |
| 
 | |
| 		if (d < 0) *--p= '-';
 | |
| 		if ((r= write(fd, p, (a + sizeof(a)) - p)) < 0) return -1;
 | |
| 		len+= r;
 | |
| 	    }
 | |
| 	}
 | |
| 	fp++;
 | |
|     }
 | |
|     if (fp > fp0) {
 | |
| 	if ((r= write(fd, fp0, (fp - fp0))) < 0) return -1;
 | |
| 	len+= r;
 | |
|     }
 | |
|     va_end(ap);
 | |
|     return len;
 | |
| }
 | 
