324 lines
7.6 KiB
C
324 lines
7.6 KiB
C
/* 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;
|
|
}
|