Add a Unix pty<->pconsole adapter.

This commit is contained in:
Ryan Prichard 2012-01-28 14:23:31 -08:00
parent 1aaf102905
commit 19d9c1832c
3 changed files with 216 additions and 0 deletions

View File

@ -1,7 +1,9 @@
all :
cd agent && $(MAKE)
cd libpconsole && $(MAKE)
cd unix-adapter && $(MAKE)
clean :
cd agent && $(MAKE) clean
cd libpconsole && $(MAKE) clean
cd unix-adapter && $(MAKE) clean

18
unix-adapter/Makefile Normal file
View File

@ -0,0 +1,18 @@
include ../config.mk
PROGRAM = pconsole
OBJECTS = main.o
CXXFLAGS += -I../include
LDFLAGS += ../libpconsole/pconsole.dll
all : $(PROGRAM)
$(PROGRAM) : $(OBJECTS)
@echo Linking $@
@$(CXX) -o $@ $^ $(LDFLAGS)
.PHONY : clean
clean:
rm -f $(PROGRAM) *.o *.d
-include $(OBJECTS:.o=.d)

196
unix-adapter/main.cc Normal file
View File

@ -0,0 +1,196 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <sys/ioctl.h>
#include <sys/select.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <errno.h>
#include <pconsole.h>
static int signalWriteFd;
// Put the input terminal into non-blocking non-canonical mode.
static termios setRawTerminalMode()
{
if (!isatty(STDIN_FILENO)) {
fprintf(stderr, "input is not a tty\n");
exit(1);
}
if (!isatty(STDOUT_FILENO)) {
fprintf(stderr, "output is not a tty\n");
exit(1);
}
// This code makes the terminal output non-blocking.
int flags = fcntl(STDOUT_FILENO, F_GETFL, 0);
if (flags == -1) {
perror("fcntl F_GETFL on stdout failed");
exit(1);
}
if (fcntl(STDOUT_FILENO, F_SETFL, flags | O_NONBLOCK) == -1) {
perror("fcntl F_SETFL on stdout failed");
exit(1);
}
termios buf;
if (tcgetattr(STDIN_FILENO, &buf) < 0) {
perror("tcgetattr failed");
exit(1);
}
termios saved = buf;
buf.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
buf.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
buf.c_cflag &= ~(CSIZE | PARENB);
buf.c_cflag |= CS8;
buf.c_oflag &= ~OPOST;
buf.c_cc[VMIN] = 0;
buf.c_cc[VTIME] = 0;
if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &buf) < 0) {
fprintf(stderr, "tcsetattr failed\n");
exit(1);
}
return saved;
}
static void restoreTerminalMode(termios original)
{
if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &original) < 0) {
perror("error restoring terminal mode");
exit(1);
}
}
static void terminalResized(int signo)
{
char dummy = 0;
write(signalWriteFd, &dummy, 1);
}
static void pconsoleIo(pconsole_t *pconsole)
{
char dummy = 0;
write(signalWriteFd, &dummy, 1);
}
int main()
{
winsize sz;
ioctl(STDIN_FILENO, TIOCGWINSZ, &sz);
pconsole_t *pconsole = pconsole_open(sz.ws_col, sz.ws_row);
if (pconsole == NULL) {
fprintf(stderr, "Error creating pconsole.\n");
exit(1);
}
pconsole_set_io_cb(pconsole, pconsoleIo);
{
struct sigaction resizeSigAct;
memset(&resizeSigAct, 0, sizeof(resizeSigAct));
resizeSigAct.sa_handler = terminalResized;
resizeSigAct.sa_flags = SA_RESTART;
sigaction(SIGWINCH, &resizeSigAct, NULL);
}
termios mode = setRawTerminalMode();
int signalReadFd;
const int bufSize = 4096;
char writeBuf[bufSize];
int writeBufAmount;
char buf[bufSize];
int amount;
{
int pipeFd[2];
if (pipe2(pipeFd, O_NONBLOCK) != 0) {
perror("Could not create pipe");
exit(1);
}
signalReadFd = pipeFd[0];
signalWriteFd = pipeFd[1];
}
while (true) {
fd_set readfds;
fd_set writefds;
FD_ZERO(&readfds);
FD_ZERO(&writefds);
FD_SET(signalReadFd, &readfds);
if (pconsole_get_output_queue_size(pconsole) < bufSize)
FD_SET(STDIN_FILENO, &readfds);
if (writeBufAmount > 0)
FD_SET(STDOUT_FILENO, &writefds);
if (select(signalReadFd + 1, &readfds, &writefds, NULL, NULL) < 0) {
perror("select failed");
exit(1);
}
// Check for terminal resize.
{
winsize sz2;
ioctl(STDIN_FILENO, TIOCGWINSZ, &sz2);
if (memcmp(&sz, &sz2, sizeof(sz)) != 0) {
sz = sz2;
pconsole_set_size(pconsole, sz.ws_col, sz.ws_row);
}
}
// Discard any data in the signal pipe.
amount = read(signalReadFd, buf, bufSize);
if (amount == 0 || amount < 0 && errno != EAGAIN) {
perror("error reading internal signal fd");
exit(1);
}
// Read from the pty and write to the pconsole.
amount = bufSize - pconsole_get_output_queue_size(pconsole);
if (amount > 0) {
amount = read(STDIN_FILENO, buf, amount);
if (amount == 0) {
break;
} else if (amount < 0 && errno != EAGAIN) {
perror("error reading from pty");
exit(1);
} else if (amount > 0) {
pconsole_write(pconsole, buf, amount);
}
}
// Read from the pconsole.
amount = bufSize - writeBufAmount;
if (amount > 0) {
amount = pconsole_read(pconsole, buf + writeBufAmount, amount);
if (amount == 0)
break;
else if (amount > 0)
writeBufAmount += amount;
}
// Write to the pty.
if (writeBufAmount > 0) {
amount = write(STDOUT_FILENO, writeBuf, writeBufAmount);
if (amount == 0) {
break;
} else if (amount < 0 && errno != EAGAIN) {
perror("error writing to pty");
} else if (amount > 0) {
int remaining = writeBufAmount - amount;
if (amount < writeBufAmount)
memmove(writeBuf, writeBuf + amount, remaining);
writeBufAmount = remaining;
}
}
}
// TODO: Get the pconsole child exit code and exit with it.
restoreTerminalMode(mode);
return 0;
}