Get pconsole.exe and pconsole.dll building simultaneously.
* Remove obsolete comments and APIs. * In pconsole.exe, use two threads to handle pconsole<->pty communication. I use blocking I/O for the pty. For the Win32 pipe to the pconsole agent, I use overlapped I/O and emulate blocking I/O. I'm not sure I *have* to open the pipe in overlapped mode. When I last tried using non-overlapped I/O, I had a problem where a pending read would block writes (or vice versa). Maybe setting the overlapped parameter to {Read,Write}File would be sufficient.
This commit is contained in:
parent
40c7e87528
commit
27f0c4d156
@ -15,29 +15,9 @@ extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct pconsole_s pconsole_t;
|
||||
typedef void (*pconsole_io_cb)(pconsole_t *pconsole);
|
||||
typedef void (*pconsole_process_exit_cb)(pconsole_t *pconsole,
|
||||
int pid,
|
||||
int exitcode);
|
||||
|
||||
/*
|
||||
* pconsole API.
|
||||
*
|
||||
* This library provides non-blocking I/O APIs that communicate with a pconsole
|
||||
* agent process over Win32 named pipes.
|
||||
*
|
||||
* Most I/O is transferred with the pconsole_read and pconsole_write functions.
|
||||
* The format is the same as a Unix pty (i.e. escape sequences).
|
||||
*
|
||||
* A pconsole_t instance has an internal write buffer that grows on-demand to
|
||||
* an arbitrary size. A pconsole client should use the
|
||||
* pconsole_get_output_queue_size API to limit the size of the output buffer.
|
||||
* (TODO: This isn't a practical concern, though, is it?) The internal read
|
||||
* buffer is statically-sized -- if the client does not read available data
|
||||
* using pconsole_read, then the pipe will eventually block.
|
||||
*
|
||||
* The library uses callbacks to notify the client. A callback function is
|
||||
* always called on a worker thread that the pconsole library handles.
|
||||
*/
|
||||
|
||||
/*
|
||||
|
@ -14,7 +14,6 @@
|
||||
#define AGENT_EXE L"pconsole-agent.exe"
|
||||
|
||||
static volatile LONG consoleCounter;
|
||||
const int bufSize = 4096;
|
||||
|
||||
struct pconsole_s {
|
||||
pconsole_s();
|
||||
@ -186,9 +185,9 @@ static void startAgentProcess(std::wstring &controlPipeName,
|
||||
CloseWindowStation(station);
|
||||
}
|
||||
|
||||
PCONSOLE_API pconsole_s *pconsole_open(int cols, int rows)
|
||||
PCONSOLE_API pconsole_t *pconsole_open(int cols, int rows)
|
||||
{
|
||||
pconsole_s *pc = new pconsole_s;
|
||||
pconsole_t *pc = new pconsole_t;
|
||||
|
||||
// Start pipes.
|
||||
std::wstringstream pipeName;
|
||||
@ -220,7 +219,7 @@ PCONSOLE_API pconsole_s *pconsole_open(int cols, int rows)
|
||||
return pc;
|
||||
}
|
||||
|
||||
static void writePacket(pconsole_s *pc, const WriteBuffer &packet)
|
||||
static void writePacket(pconsole_t *pc, const WriteBuffer &packet)
|
||||
{
|
||||
std::string payload = packet.str();
|
||||
int payloadSize = payload.size();
|
||||
@ -253,7 +252,12 @@ PCONSOLE_API int pconsole_start_process(pconsole_t *pc,
|
||||
writePacket(pc, packet);
|
||||
}
|
||||
|
||||
PCONSOLE_API int pconsole_set_size(pconsole_s *pc, int cols, int rows)
|
||||
PCONSOLE_API HANDLE pconsole_get_data_pipe(pconsole_t *pc)
|
||||
{
|
||||
return pc->dataPipe;
|
||||
}
|
||||
|
||||
PCONSOLE_API int pconsole_set_size(pconsole_t *pc, int cols, int rows)
|
||||
{
|
||||
WriteBuffer packet;
|
||||
packet.putInt(AgentMsg::SetSize);
|
||||
@ -262,7 +266,7 @@ PCONSOLE_API int pconsole_set_size(pconsole_s *pc, int cols, int rows)
|
||||
writePacket(pc, packet);
|
||||
}
|
||||
|
||||
PCONSOLE_API void pconsole_close(pconsole_s *pc)
|
||||
PCONSOLE_API void pconsole_close(pconsole_t *pc)
|
||||
{
|
||||
CloseHandle(pc->controlPipe);
|
||||
CloseHandle(pc->dataPipe);
|
||||
|
@ -8,8 +8,10 @@
|
||||
#include <fcntl.h>
|
||||
#include <signal.h>
|
||||
#include <errno.h>
|
||||
#include <pthread.h>
|
||||
#include <pconsole.h>
|
||||
|
||||
|
||||
static int signalWriteFd;
|
||||
|
||||
|
||||
@ -25,6 +27,7 @@ static termios setRawTerminalMode()
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/*
|
||||
// This code makes the terminal output non-blocking.
|
||||
int flags = fcntl(STDOUT_FILENO, F_GETFL, 0);
|
||||
if (flags == -1) {
|
||||
@ -35,6 +38,7 @@ static termios setRawTerminalMode()
|
||||
perror("fcntl F_SETFL on stdout failed");
|
||||
exit(1);
|
||||
}
|
||||
*/
|
||||
|
||||
termios buf;
|
||||
if (tcgetattr(STDIN_FILENO, &buf) < 0) {
|
||||
@ -47,7 +51,7 @@ static termios setRawTerminalMode()
|
||||
buf.c_cflag &= ~(CSIZE | PARENB);
|
||||
buf.c_cflag |= CS8;
|
||||
buf.c_oflag &= ~OPOST;
|
||||
buf.c_cc[VMIN] = 0;
|
||||
buf.c_cc[VMIN] = 1; // blocking read
|
||||
buf.c_cc[VTIME] = 0;
|
||||
if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &buf) < 0) {
|
||||
fprintf(stderr, "tcsetattr failed\n");
|
||||
@ -64,18 +68,118 @@ static void restoreTerminalMode(termios original)
|
||||
}
|
||||
}
|
||||
|
||||
static void terminalResized(int signo)
|
||||
static void writeToSignalFd()
|
||||
{
|
||||
char dummy = 0;
|
||||
write(signalWriteFd, &dummy, 1);
|
||||
}
|
||||
|
||||
static void pconsoleIo(pconsole_t *pconsole)
|
||||
static void terminalResized(int signo)
|
||||
{
|
||||
char dummy = 0;
|
||||
write(signalWriteFd, &dummy, 1);
|
||||
writeToSignalFd();
|
||||
}
|
||||
|
||||
// Create a manual reset, initially unset event.
|
||||
static HANDLE createEvent()
|
||||
{
|
||||
return CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
}
|
||||
|
||||
|
||||
// Connect pconsole overlapped I/O to Cygwin blocking STDOUT_FILENO.
|
||||
class OutputHandler {
|
||||
public:
|
||||
OutputHandler(HANDLE pconsole);
|
||||
pthread_t getThread() { return thread; }
|
||||
private:
|
||||
static void *threadProc(void *pvthis);
|
||||
HANDLE pconsole;
|
||||
pthread_t thread;
|
||||
};
|
||||
|
||||
OutputHandler::OutputHandler(HANDLE pconsole) : pconsole(pconsole)
|
||||
{
|
||||
pthread_create(&thread, NULL, OutputHandler::threadProc, this);
|
||||
}
|
||||
|
||||
// TODO: See whether we can make the pipe non-overlapped if we still use
|
||||
// an OVERLAPPED structure in the ReadFile/WriteFile calls.
|
||||
void *OutputHandler::threadProc(void *pvthis)
|
||||
{
|
||||
OutputHandler *pthis = (OutputHandler*)pvthis;
|
||||
HANDLE event = createEvent();
|
||||
OVERLAPPED over;
|
||||
const int bufferSize = 4096;
|
||||
char *buffer = new char[bufferSize];
|
||||
while (true) {
|
||||
DWORD amount;
|
||||
memset(&over, 0, sizeof(over));
|
||||
over.hEvent = event;
|
||||
BOOL ret = ReadFile(pthis->pconsole,
|
||||
buffer, bufferSize,
|
||||
&amount,
|
||||
&over);
|
||||
if (!ret && GetLastError() == ERROR_IO_PENDING)
|
||||
ret = GetOverlappedResult(pthis->pconsole, &over, &amount, TRUE);
|
||||
if (!ret || amount == 0)
|
||||
break;
|
||||
// TODO: partial writes?
|
||||
int written = write(STDOUT_FILENO, buffer, amount);
|
||||
if (written != amount)
|
||||
break;
|
||||
}
|
||||
delete [] buffer;
|
||||
CloseHandle(event);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
// Connect Cygwin non-blocking STDIN_FILENO to pconsole overlapped I/O.
|
||||
class InputHandler {
|
||||
public:
|
||||
InputHandler(HANDLE pconsole);
|
||||
pthread_t getThread() { return thread; }
|
||||
private:
|
||||
static void *threadProc(void *pvthis);
|
||||
HANDLE pconsole;
|
||||
pthread_t thread;
|
||||
};
|
||||
|
||||
InputHandler::InputHandler(HANDLE pconsole) : pconsole(pconsole)
|
||||
{
|
||||
pthread_create(&thread, NULL, InputHandler::threadProc, this);
|
||||
}
|
||||
|
||||
void *InputHandler::threadProc(void *pvthis)
|
||||
{
|
||||
InputHandler *pthis = (InputHandler*)pvthis;
|
||||
HANDLE event = createEvent();
|
||||
const int bufferSize = 4096;
|
||||
char *buffer = new char[bufferSize];
|
||||
while (true) {
|
||||
int amount = read(STDIN_FILENO, buffer, bufferSize);
|
||||
if (amount <= 0)
|
||||
break;
|
||||
DWORD written;
|
||||
OVERLAPPED over;
|
||||
memset(&over, 0, sizeof(over));
|
||||
over.hEvent = event;
|
||||
BOOL ret = WriteFile(pthis->pconsole,
|
||||
buffer, amount,
|
||||
&written,
|
||||
&over);
|
||||
if (!ret && GetLastError() == ERROR_IO_PENDING)
|
||||
ret = GetOverlappedResult(pthis->pconsole, &over, &written, TRUE);
|
||||
// TODO: partial writes?
|
||||
if (!ret || written != amount)
|
||||
break;
|
||||
}
|
||||
delete [] buffer;
|
||||
CloseHandle(event);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
int main()
|
||||
{
|
||||
winsize sz;
|
||||
@ -87,8 +191,6 @@ int main()
|
||||
exit(1);
|
||||
}
|
||||
|
||||
pconsole_set_io_cb(pconsole, pconsoleIo);
|
||||
|
||||
{
|
||||
struct sigaction resizeSigAct;
|
||||
memset(&resizeSigAct, 0, sizeof(resizeSigAct));
|
||||
@ -100,12 +202,6 @@ int main()
|
||||
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) {
|
||||
@ -116,18 +212,14 @@ int main()
|
||||
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);
|
||||
OutputHandler outputHandler(pconsole_get_data_pipe(pconsole));
|
||||
InputHandler inputHandler(pconsole_get_data_pipe(pconsole));
|
||||
|
||||
if (select(signalReadFd + 1, &readfds, &writefds, NULL, NULL) < 0) {
|
||||
while (true) {
|
||||
fd_set readfds;
|
||||
FD_ZERO(&readfds);
|
||||
FD_SET(signalReadFd, &readfds);
|
||||
if (select(signalReadFd + 1, &readfds, NULL, NULL, NULL) < 0) {
|
||||
perror("select failed");
|
||||
exit(1);
|
||||
}
|
||||
@ -143,50 +235,12 @@ int main()
|
||||
}
|
||||
|
||||
// Discard any data in the signal pipe.
|
||||
amount = read(signalReadFd, buf, bufSize);
|
||||
char tmpBuf[256];
|
||||
int amount = read(signalReadFd, tmpBuf, sizeof(tmpBuf));
|
||||
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.
|
||||
|
Loading…
Reference in New Issue
Block a user