minix/commands/simple/slip.c
2009-11-09 10:26:00 +00:00

323 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;
}