commit 3f30d4e3a7005d7e8b7a0c4e59fd56a401576c7b
Author: John Goerzen <jgoerzen@complete.org>
Date:   Mon Nov 15 16:02:12 2010 -0600

    Cherry pick upstream patch for kissnetd to support PTYs

diff --git a/kiss/kissnetd.8 b/kiss/kissnetd.8
index d069ac9..3b47bac 100644
--- a/kiss/kissnetd.8
+++ b/kiss/kissnetd.8
@@ -2,7 +2,7 @@
 .SH NAME
 kissnetd \- Create a virtual network.
 .SH SYNOPSIS
-.B kissnetd [-f size] [-v] tty...
+.B kissnetd [-f size] [-v] [-p <num> | tty... ]
 .SH DESCRIPTION
 .LP
 .B Kissnetd
@@ -12,6 +12,10 @@ received on tty is copied to the other ttys. This allows a number of AX.25
 systems to share the same packets.
 .SH OPTIONS
 .TP 10
+.BI "\-p num"
+Automaticaly allocate num Unix98 slave pty's via /dev/ptmx.
+These are written to stdout and could be parsed by your startup scripts.
+.TP 10
 .BI "\-f size"
 This sets the maximum KISS frame size that the program will handle. The
 default is 512 bytes which will be adequate under most circumstances.
diff --git a/kiss/kissnetd.c b/kiss/kissnetd.c
index 2000482..78bab0d 100644
--- a/kiss/kissnetd.c
+++ b/kiss/kissnetd.c
@@ -10,6 +10,7 @@
  */
  
 #include <stdio.h>
+#define __USE_XOPEN
 #include <stdlib.h>
 #include <sys/time.h>
 #include <sys/types.h>
@@ -20,6 +21,7 @@
 #include <errno.h>
 #include <syslog.h>
 #include <time.h>
+#include <limits.h>
 
 static char *Version = "1.5";
 static int VerboseMode = 0;
@@ -28,21 +30,26 @@ static int MaxFrameSize = 512;
 #define REOPEN_TIMEOUT	30	/* try tio reopen every 10 s */
 
 struct PortDescriptor {
-	char		Name[80];
+	char		Name[PATH_MAX];
 	int		Fd;
 	unsigned char	*FrameBuffer;
 	int		BufferIndex;
 	time_t		TimeLastOpen;
+	char		namepts[PATH_MAX];  /* name of the unix98 pts slaves, which
+				       * the client has to use */
+	int		is_active;
 };
 
 static struct PortDescriptor *PortList[FD_SETSIZE];
+
 static int NbPort = 0;
 
 static void Usage(void)
 {
-	fprintf(stderr, "\nUsage : kissnetd [-v] [-f size] /dev/pty?? [/dev/pty??]*\n");
+	fprintf(stderr, "\nUsage : kissnetd [-v] [-f size] [-p num | /dev/pty?? [/dev/pty??]* ]\n");
 	fprintf(stderr, " -v       : Verbose mode, trace on stdout\n");
 	fprintf(stderr, " -f size  : Set max frame size to size bytes (default 512)\n");
+	fprintf(stderr, " -p num   : Number of /dev/ptmx-master-devices has to open\n");
 	exit(1);
 } 
 
@@ -82,45 +89,107 @@ static void NewPort(char *Name)
 		exit(1);
 	}
 	
-	strncpy(MyPort->Name, Name, sizeof(MyPort->Name));
+	strncpy(MyPort->Name, Name, PATH_MAX-1);
+	MyPort->Name[PATH_MAX-1] = '\0';
 	MyPort->Fd = -1;
 	MyPort->FrameBuffer[0] = 0xC0;
 	MyPort->BufferIndex = 1;
+	MyPort->namepts [0] = '\0';
+	MyPort->is_active = 0;
 	PortList[NbPort++] = MyPort;
 }
 
 static void ReopenPort(int PortNumber)
 {
 	char MyString[80];
-
 	PortList[PortNumber]->TimeLastOpen = time(NULL);
 		
 	if (VerboseMode) {
 		printf("Reopening port %d\n", PortNumber);
 	}
 	
-	syslog(LOG_WARNING, "kissnetd : Opening port %s\n", PortList[PortNumber]->Name);
-	
-	PortList[PortNumber]->Fd = open(PortList[PortNumber]->Name, O_RDWR | O_NONBLOCK);
-	if (PortList[PortNumber]->Fd < 0) {
-		syslog(LOG_WARNING, "kissnetd : Error opening port %s : %s\n", 
-			PortList[PortNumber]->Name, strerror(errno));
-		if (VerboseMode) {
-			sprintf(MyString, "cannot reopen %s", PortList[PortNumber]->Name);
-			perror(MyString);
+	if (PortList[PortNumber]->namepts[0] == '\0') {
+		
+		syslog(LOG_WARNING, "kissnetd : Opening port %s\n", PortList[PortNumber]->Name);
+		PortList[PortNumber]->Fd = open(PortList[PortNumber]->Name, O_RDWR | O_NONBLOCK);
+		if (PortList[PortNumber]->Fd < 0) {
+			syslog(LOG_WARNING, "kissnetd : Error opening port %s : %s\n", 
+				PortList[PortNumber]->Name, strerror(errno));
+			if (VerboseMode) {
+				sprintf(MyString, "cannot reopen %s", PortList[PortNumber]->Name);
+				perror(MyString);
+			}
+			return;
 		}
+		PortList[PortNumber]->is_active = 1;
+		if (!strcmp(PortList[PortNumber]->Name, "/dev/ptmx")) {
+			char *npts;
+			/* get name of pts-device */
+			if ((npts = ptsname(PortList[PortNumber]->Fd)) == NULL) {
+				sprintf(MyString, "Cannot get name of pts-device.\n");
+				syslog(LOG_WARNING, "kissnetd : Cannot get name of pts-device\n"); 
+				exit(1);
+			}
+			strncpy(PortList[PortNumber]->namepts, npts, PATH_MAX-1);
+			PortList[PortNumber]->namepts[PATH_MAX-1] = '\0';
+
+			/* unlock pts-device */
+			if (unlockpt(PortList[PortNumber]->Fd) == -1) {
+				sprintf(MyString, "Cannot unlock pts-device %s\n", PortList[PortNumber]->namepts);
+				syslog(LOG_WARNING, "kissnetd : Cannot unlock pts-device %s\n", PortList[PortNumber]->namepts);
+				exit(1);
+			}
+			syslog(LOG_WARNING, "kissnetd : Using /dev/ptmx with slave pty %s\n", PortList[PortNumber]->namepts);
+		}
+	} else {
+		if (PortList[PortNumber]->Fd == -1) {
+			syslog(LOG_WARNING, "kissnetd : Cannot reopen port ptmx (slave %s) : not supported by ptmx-device\n", 
+		       	PortList[PortNumber]->namepts);
+			if (VerboseMode) {
+				sprintf(MyString, "cannot reopen ptmx (slave %s).", PortList[PortNumber]->namepts);
+				perror(MyString);
+			}
+			return;
+		}
+		syslog(LOG_WARNING, "kissnetd : Trying to poll port ptmx (slave %s).\n", 
+		       	PortList[PortNumber]->namepts);
+		PortList[PortNumber]->is_active = 1;
 	}
 }
 
 static void TickReopen(void)
 {
 	int i;
+	static int wrote_info = 0;
 	time_t CurrentTime = time(NULL);
 	
 	for (i=0; i<NbPort; i++) {
-		if (PortList[i]->Fd >= 0) continue;
+		if (PortList[i]->Fd >= 0 &&  PortList[i]->is_active == 1) continue;
 		if ( (CurrentTime - PortList[i]->TimeLastOpen) > REOPEN_TIMEOUT ) ReopenPort(i);
 	}
+	
+	if (!wrote_info) {
+		for (i=0; i<NbPort; i++) {
+			if (PortList[i]->namepts[0] != '\0') {
+				if (wrote_info == 0)
+					printf("\nAwaiting client connects on:\n");
+				else
+					printf(" ");
+				printf("%s", PortList[i]->namepts);
+				wrote_info = 1;
+			}
+		}
+		if (wrote_info > 0) {
+			printf("\n");
+			if (!VerboseMode) {
+				fflush(stdout);
+				fflush(stderr);
+				close(0);
+				close(1);
+				close(2);
+			}
+		}
+	}
 }
 
 static void Broadcast(int InputPort)
@@ -130,19 +199,52 @@ static void Broadcast(int InputPort)
 	
 	/* Broadcast only info frames */
 	
-	if (PortList[InputPort]->FrameBuffer[1] != 0x00) return;
+	if (PortList[InputPort]->FrameBuffer[1] != 0x00 && \
+	    PortList[InputPort]->FrameBuffer[1] != 0x20 && \
+	    PortList[InputPort]->FrameBuffer[1] != 0x80)
+		return;
 	
 	for (i=0; i<NbPort; i++) {
+		int offset = 0;
 		if (i == InputPort) continue;
-		if (PortList[i]->Fd < 0) continue;
+		if (PortList[i]->Fd < 0 || PortList[i]->is_active == 0) continue;
+again:
 		rc = write(PortList[i]->Fd, 
-			   PortList[InputPort]->FrameBuffer, 
-			   PortList[InputPort]->BufferIndex);
+			   PortList[InputPort]->FrameBuffer+offset, 
+			   PortList[InputPort]->BufferIndex-offset);
+		if (rc < 0) {
+			if (errno == EAGAIN) {
+				if (PortList[i]->namepts[0] == '\0')
+					syslog(LOG_WARNING, "kissnetd : write buffer full on port %s. dropping frame. %s", 
+						PortList[i]->Name, strerror(errno));
+				else
+					syslog(LOG_WARNING, "kissnetd : write buffer full on ptmx port %s. dropping frame. %s", 
+						PortList[i]->namepts, strerror(errno));
+				continue;
+			}
+			if (PortList[i]->namepts[0] == '\0')
+				syslog(LOG_WARNING, "kissnetd : Error writing to port %s : %s\n", 
+					PortList[i]->Name, strerror(errno));
+			else
+				syslog(LOG_WARNING, "kissnetd : Error writing to port ptmx (slave %s) : %s\n", 
+					PortList[i]->namepts, strerror(errno));
+			if (VerboseMode) perror("write");
+			PortList[i]->is_active = 0;
+			if (PortList[i]->namepts[0] == '\0') {
+				close(PortList[i]->Fd);
+				PortList[i]->Fd = -1;
+			}
+			continue;
+		}
 		if (VerboseMode) {
 			printf("Sending %d bytes on port %d : rc=%d\n",
 				PortList[InputPort]->BufferIndex,
 				i, rc);
 		}	   
+		if (rc < PortList[InputPort]->BufferIndex-offset) {
+			offset += rc;
+			goto again;
+		}
 	}
 }
 
@@ -159,11 +261,20 @@ static void ProcessInput(int PortNumber)
 	}
 	if (!Length) return;
 	if (Length < 0) {
-		syslog(LOG_WARNING, "kissnetd : Error reading port %s : %s\n", 
-			PortList[PortNumber]->Name, strerror(errno));
+		if (errno == EAGAIN)
+			return;
+		if (MyPort->namepts[0] == '\0')
+			syslog(LOG_WARNING, "kissnetd : Error reading from port %s : %s\n", 
+				PortList[PortNumber]->Name, strerror(errno));
+		else
+			syslog(LOG_WARNING, "kissnetd : Error reading from port ptmx (slave %s) : %s\n", 
+				PortList[PortNumber]->namepts, strerror(errno));
 		if (VerboseMode) perror("read");
-		close(MyPort->Fd);
-		MyPort->Fd = -1;
+		MyPort->is_active = 0;
+		if (MyPort->namepts[0] == '\0') {
+			close(MyPort->Fd);
+			MyPort->Fd = -1;
+		}
 		return;
 	}
 	for (i=0; i<Length; i++) {
@@ -194,7 +305,7 @@ static void ProcessPortList(void)
 	
 	FD_ZERO(&MyFdSet);
 	for (i=0; i<NbPort; i++) {
-		if (PortList[i]->Fd >= 0) FD_SET(PortList[i]->Fd, &MyFdSet);
+		if (PortList[i]->Fd >= 0 && PortList[i]->is_active) FD_SET(PortList[i]->Fd, &MyFdSet);
 	}
 	rc = select(FD_SETSIZE, &MyFdSet, NULL, NULL, &Timeout);
 	
@@ -212,31 +323,42 @@ static void ProcessPortList(void)
 	}	
 }
 
-static void ProcessArgv(char *argv[])
+static void ProcessArgv(int argc, char *argv[])
 {
-	char *Opt;
-	int ArgvIndex = 0;
+	int opt;
+	int i=0;
+	int ptmxdevices = 0;
 	
-	while (argv[ArgvIndex]) {
-		if (argv[ArgvIndex][0] != '-') {
-			NewPort(argv[ArgvIndex++]);
-			continue;
-		}
-		for (Opt = &argv[ArgvIndex++][1]; *Opt; Opt++) {
-			switch (*Opt) {
-			case 'v':
-				VerboseMode = 1;
-				break;
-			case 'f':
-				MaxFrameSize = atoi(argv[ArgvIndex++]);
-				break;	
-			default:
-				fprintf(stderr, "Invalid option %c\n", *Opt);
-				Usage();
-				break;
+	while ((opt = getopt(argc, argv, "vf:p:")) != -1) {
+		switch (opt) {
+		case 'v':
+			VerboseMode = 1;
+			break;
+		case 'f':
+			MaxFrameSize = atoi(argv[optind]);
+			break;
+		case 'p':
+			ptmxdevices = atoi(optarg);
+			if (ptmxdevices < 1) {
+				fprintf(stderr, "error: too many devices\n");
+				exit(1);
 			}
+			for (i=0; i < ptmxdevices; i++)
+				NewPort("/dev/ptmx");
+			break;
+		default:
+			fprintf(stderr, "Invalid option %s\n", argv[optind]);
+			Usage();
+			exit(1);
 		}
+	}
 		
+	while (optind < argc)
+		NewPort(argv[optind++]);
+
+	if (NbPort < 2) {
+		fprintf(stderr, "This multiplexer needs at least two pty's\n");
+		exit(1);
 	}
 }
 
@@ -251,7 +373,7 @@ int main(int argc, char *argv[])
 		Banner(1);
 	}
 	
-	ProcessArgv(argv+1);
+	ProcessArgv(argc, argv);
 	while (1) ProcessPortList();
 	return 0;	
 }
