xref: /minix/minix/commands/slip/slip.c (revision 045e0ed3)
1 /*	slip 1.1 - Serial line IP			Author: Kees J. Bot
2  *								19 Jul 1997
3  */
4 #define nil 0
5 #include <sys/types.h>
6 #include <stdarg.h>
7 #include <stdlib.h>
8 #include <unistd.h>
9 #include <fcntl.h>
10 #include <string.h>
11 #include <errno.h>
12 #include <sys/asynchio.h>
13 
14 #if __minix && !__minix_vmd
15 #define HAS_ASYN	0	/* Standard Minix doesn't have async I/O. */
16 #else
17 #define HAS_ASYN	1	/* Everyone else does in some way. */
18 #endif
19 
20 #if !HAS_ASYN
21 #include <signal.h>
22 #endif
23 
24 #define END		0300		/* End of packet. */
25 #define ESC		0333		/* Byte stuffing escape. */
26 #define ESC_END		0334		/* END -> ESC ESC_END -> END. */
27 #define ESC_ESC		0335		/* ESC -> ESC ESC_ESC -> ESC. */
28 
29 #define PACKLEN		2048		/* Max datagram size. */
30 #define SLIPLEN   (1 + 2*PACKLEN + 1)	/* Max serial size when all escaped. */
31 
32 /* Pathetic fprintf() clone to avoid dragging in the stdio library. */
33 static int fprintf(int fd, const char *format, ...);
34 #define stderr	2
35 
36 int main(int argc, char **argv)
37 {
38     char *ps_device;
39     int ps_fd;
40     int doing[2], discard;
41     ssize_t r;
42 #if !HAS_ASYN
43     pid_t other_pid;
44 #endif
45     size_t ps_len[2], sl_len[2];
46     unsigned char *sl_end;
47     unsigned char ps_buf[2][PACKLEN];
48     unsigned char sl_buf[2][SLIPLEN];
49     asynchio_t asyn;
50 
51     if (argc != 2) {
52 	fprintf(stderr, "Usage: slip psip-device\n");
53 	exit(1);
54     }
55     ps_device= argv[1];
56 
57     if ((ps_fd= open(ps_device, O_RDWR)) < 0) {
58 	fprintf(stderr, "slip: can't open %s: %s\n",
59 	    ps_device, strerror(errno));
60 	exit(1);
61     }
62 
63     doing[0]= 1;	/* We're doing serial -> psip. */
64     discard= 0;		/* No input error. */
65     sl_len[0]= 0;	/* Nothing read from serial line yet. */
66     sl_end= nil;	/* No END marker seen. */
67     ps_len[0]= 0;	/* Nothing to write to pseudo IP device. */
68 
69     doing[1]= 1;	/* We're doing psip -> serial. */
70     sl_len[1]= 0;	/* Nothing read from pseudo IP device yet. */
71     ps_len[1]= 0;	/* Nothing to write to serial line. */
72 
73 #if !HAS_ASYN
74     /* Oops, standard Minix can't do asynchronous I/O.  Fork and let the parent
75      * do serial -> psip, and the child do psip -> serial.  (Note that we have
76      * to make sure that we're not reading and writing at the same time even
77      * for standard Minix.  For Minix-vmd we do fill an input buffer while an
78      * output buffer is waiting to be drained to improve performance a bit.)
79      */
80     switch ((other_pid= fork())) {
81     case -1:
82 	fprintf(stderr, "slip: can't fork: %s\n", strerror(errno));
83 	exit(1);
84     case 0:
85 	/* Child. */
86 	doing[0]= 0;	/* *Not* doing serial -> psip. */
87 	other_pid= getppid();
88 	break;
89     default:
90 	/* Parent. */
91 	doing[1]= 0;	/* *Not* doing psip -> serial. */
92     }
93 #endif
94 
95     asyn_init(&asyn);
96 
97     for (;;) {
98 	if (doing[0]) {
99 	    /* If there is an END marker in the serial input then create
100 	     * an IP packet to be send to the TCP/IP task.
101 	     */
102 	    while (sl_end != nil && ps_len[0] == 0) {
103 		unsigned char *sp= sl_buf[0];
104 		unsigned char *pp= ps_buf[0];
105 
106 		while (sp < sl_end) {
107 		    int c= *sp++;
108 
109 		    if (c == ESC) {
110 			switch (*sp++) {
111 			case ESC_ESC:	/* ESC ESC_ESC -> ESC. */
112 			    c= ESC;
113 			    break;
114 			case ESC_END:	/* ESC ESC_END -> END. */
115 			    c= END;
116 			    break;
117 			default:
118 			    /* Protocol error. */
119 			    discard= 1;
120 			}
121 		    }
122 		    if (pp < ps_buf[0] + PACKLEN) {
123 			*pp++ = c;
124 		    } else {
125 			/* Packet too big, discard. */
126 			discard= 1;
127 		    }
128 		}
129 		if (discard) {
130 		    discard= 0;
131 		} else {
132 		    /* A new packet can be send to the TCP/IP server. */
133 		    ps_len[0]= (pp - ps_buf[0]);
134 		}
135 		/* Move what's beyond END to the front. */
136 		sl_end++;
137 		sl_len[0] -= (sl_end - sl_buf[0]);
138 		memmove(sl_buf[0], sl_end, sl_len[0]);
139 		sl_end= memchr(sl_buf[0], END, sl_len[0]);
140 	    }
141 
142 	    /* Reading from serial input. */
143 	    if (sl_end == nil && (HAS_ASYN || ps_len[0] == 0)) {
144 		r= asyn_read(&asyn, 0, sl_buf[0] + sl_len[0],
145 							SLIPLEN - sl_len[0]);
146 		if (r > 0) {
147 		    sl_end= memchr(sl_buf[0] + sl_len[0], END, r);
148 		    sl_len[0]+= r;
149 		    if (sl_end == nil && sl_len[0] == SLIPLEN) {
150 			/* Packet is idiotically big and no END in sight. */
151 			sl_len[0]= 0;
152 			discard= 1;
153 		    }
154 		} else
155 		if (r == 0) {
156 		    fprintf(stderr, "slip: EOF on serial input\n");
157 		    break;
158 		} else
159 		if (errno != ASYN_INPROGRESS) {
160 		    fprintf(stderr, "slip: serial input error: %s\n",
161 			strerror(errno));
162 		    break;
163 		}
164 	    }
165 
166 	    /* Writing to the psip device. */
167 	    if (ps_len[0] > 0) {
168 		r= asyn_write(&asyn, ps_fd, ps_buf[0], ps_len[0]);
169 		if (r == ps_len[0]) {
170 		    /* Packet written. */
171 		    ps_len[0]= 0;
172 		} else
173 		if (r >= 0) {
174 		    fprintf(stderr,
175 			"slip: odd write to %s, tried %u, wrote %d\n",
176 			ps_device, (unsigned) ps_len[0], (int) r);
177 		    break;
178 		} else
179 		if (errno != ASYN_INPROGRESS) {
180 		    fprintf(stderr, "slip: error writing %s: %s\n",
181 			ps_device, strerror(errno));
182 		    break;
183 		}
184 	    }
185 	}
186 
187 	if (doing[1]) {
188 	    /* Transform an IP packet to a "byte stuffed" serial packet. */
189 	    if (ps_len[1] > 0 && sl_len[1] == 0) {
190 		unsigned char *pp= ps_buf[1];
191 		unsigned char *sp= sl_buf[1];
192 
193 		*sp++ = END;
194 		while (ps_len[1] > 0) {
195 		    int c= *pp++;
196 		    ps_len[1]--;
197 		    switch (c) {
198 		    case ESC:		/* ESC -> ESC ESC_ESC. */
199 			*sp++ = ESC;
200 			c= ESC_ESC;
201 			break;
202 		    case END:		/* END -> ESC ESC_END. */
203 			*sp++ = ESC;
204 			c= ESC_END;
205 			break;
206 		    }
207 		    *sp++ = c;
208 		}
209 		*sp++ = END;
210 		sl_len[1]= (sp - sl_buf[1]);
211 	    }
212 
213 	    /* Reading from the psip device. */
214 	    if (ps_len[1] == 0 && (HAS_ASYN || sl_len[1] == 0)) {
215 		r= asyn_read(&asyn, ps_fd, ps_buf[1], PACKLEN);
216 		if (r > 0) {
217 		    /* One packet read. */
218 		    ps_len[1]= r;
219 		} else
220 		if (r == 0) {
221 		    fprintf(stderr, "slip: EOF on %s\n", ps_device);
222 		    break;
223 		} else
224 		if (errno != ASYN_INPROGRESS) {
225 		    fprintf(stderr, "slip: error reading %s: %s\n",
226 			ps_device, strerror(errno));
227 		    break;
228 		}
229 	    }
230 
231 	    /* Writing to serial output. */
232 	    if (sl_len[1] > 0) {
233 		r= asyn_write(&asyn, 1, sl_buf[1], sl_len[1]);
234 		if (r > 0) {
235 		    if ((sl_len[1]-= r) > 0) {
236 			memmove(sl_buf[1], sl_buf[1] + r, sl_len[1]);
237 		    }
238 		} else
239 		if (r == 0) {
240 		    fprintf(stderr, "slip: EOF on serial output\n");
241 		    break;
242 		} else
243 		if (errno != ASYN_INPROGRESS) {
244 		    fprintf(stderr, "slip: serial output error: %s\n",
245 			strerror(errno));
246 		    break;
247 		}
248 	    }
249 	}
250 
251 	/* Wait for something to happen. */
252 	if (asyn_wait(&asyn, 0, nil) < 0) {
253 	    fprintf(stderr,
254 		"slip: error while waiting for I/O to happen: %s\n",
255 		strerror(errno));
256 	    break;
257 	}
258     }
259 #if !HAS_ASYN
260     /* Tell my alter ego that the game is over. */
261     kill(other_pid, SIGKILL);
262 #endif
263     return 1;
264 }
265 
266 static int fprintf(int fd, const char *format, ...)
267 /* Simple fprintf() to save a few bytes by not using the stdio library. */
268 {
269     int len;
270     ssize_t r;
271     const char *fp0, *fp;
272     va_list ap;
273 
274     len= 0;
275     fp= fp0= format;
276     va_start(ap, format);
277 
278     while (*fp != 0) {
279 	if (*fp == '%' && memchr("sdu", fp[1], 3) != nil) {
280 	    if (fp > fp0) {
281 		if ((r= write(fd, fp0, (fp - fp0))) < 0) goto error;
282 		len+= r;
283 	    }
284 	    fp++;
285 	    fp0= fp+1;
286 
287 	    if (*fp == 's') {
288 		char *s= va_arg(ap, char *);
289 
290 		if ((r= write(fd, s, strlen(s))) < 0) goto error;
291 		len+= r;
292 	    } else {
293 		int d;
294 		unsigned u;
295 		char a[3 * sizeof(u) + 2];
296 		char *p;
297 
298 		if (*fp == 'd') {
299 		    u= d= va_arg(ap, int);
300 		    if (d < 0) u= -u;
301 		} else {
302 		    u= va_arg(ap, unsigned);
303 		    d= 0;
304 		}
305 
306 		p= a + sizeof(a);
307 		*--p= 0;
308 		do *--p= '0' + (u % 10); while ((u /= 10) > 0);
309 
310 		if (d < 0) *--p= '-';
311 		if ((r= write(fd, p, (a + sizeof(a)) - p)) < 0) goto error;
312 		len+= r;
313 	    }
314 	}
315 	fp++;
316     }
317     if (fp > fp0) {
318 	if ((r= write(fd, fp0, (fp - fp0))) < 0) goto error;
319 	len+= r;
320     }
321     va_end(ap);
322     return len;
323 error:
324     va_end(ap);
325     return -1;
326 }
327