Shut down the unix-adapter when the child process exits.
- In the agent, poll for the process exit at the same time we pull for output. Once the child exits, record the exit code and close the data pipe. - In the unix-adapter, shut the program down once the input or output handlers abort. Before exiting, query the agent for the exit code. - Also: in the unix-adapter, apparently receiving the SIGWINCH signal can interrupt both select and the InputHandler's read system call. I hope it doesn't affect the blocking Win32 APIs, but I'm not really sure.
This commit is contained in:
parent
90164c34a9
commit
00a6c3b90e
@ -5,7 +5,8 @@ struct AgentMsg
|
||||
{
|
||||
enum Type {
|
||||
StartProcess,
|
||||
SetSize
|
||||
SetSize,
|
||||
GetExitCode
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -26,7 +26,8 @@ Agent::Agent(const QString &controlPipeName,
|
||||
QObject(parent),
|
||||
m_terminal(NULL),
|
||||
m_timer(NULL),
|
||||
m_autoShutDown(false),
|
||||
m_childProcess(NULL),
|
||||
m_childExitCode(-1),
|
||||
m_syncCounter(0)
|
||||
{
|
||||
m_bufferData = new CHAR_INFO[BUFFER_LINE_COUNT][MAX_CONSOLE_WIDTH];
|
||||
@ -44,6 +45,7 @@ Agent::Agent(const QString &controlPipeName,
|
||||
resetConsoleTracking(false);
|
||||
|
||||
connect(m_controlSocket, SIGNAL(readyRead()), SLOT(controlSocketReadyRead()));
|
||||
connect(m_controlSocket, SIGNAL(disconnected()), SLOT(socketDisconnected()));
|
||||
connect(m_dataSocket, SIGNAL(readyRead()), SLOT(dataSocketReadyRead()));
|
||||
|
||||
m_timer = new QTimer(this);
|
||||
@ -68,7 +70,6 @@ QLocalSocket *Agent::makeSocket(const QString &pipeName)
|
||||
if (!socket->waitForConnected())
|
||||
qFatal("Could not connect to %s", pipeName.toStdString().c_str());
|
||||
socket->setReadBufferSize(64 * 1024);
|
||||
connect(socket, SIGNAL(disconnected()), SLOT(socketDisconnected()));
|
||||
return socket;
|
||||
}
|
||||
|
||||
@ -108,18 +109,25 @@ void Agent::controlSocketReadyRead()
|
||||
void Agent::handlePacket(ReadBuffer &packet)
|
||||
{
|
||||
int type = packet.getInt();
|
||||
int32_t result = -1;
|
||||
switch (type) {
|
||||
case AgentMsg::StartProcess:
|
||||
handleStartProcessPacket(packet);
|
||||
result = handleStartProcessPacket(packet);
|
||||
break;
|
||||
case AgentMsg::SetSize:
|
||||
handleSetSizePacket(packet);
|
||||
result = handleSetSizePacket(packet);
|
||||
break;
|
||||
case AgentMsg::GetExitCode:
|
||||
packet.assertEof();
|
||||
result = m_childExitCode;
|
||||
}
|
||||
m_controlSocket->write((char*)&result, sizeof(result));
|
||||
}
|
||||
|
||||
void Agent::handleStartProcessPacket(ReadBuffer &packet)
|
||||
int Agent::handleStartProcessPacket(ReadBuffer &packet)
|
||||
{
|
||||
assert(m_childProcess == NULL);
|
||||
|
||||
std::wstring program = packet.getWString();
|
||||
std::wstring cmdline = packet.getWString();
|
||||
std::wstring cwd = packet.getWString();
|
||||
@ -149,15 +157,23 @@ void Agent::handleStartProcessPacket(ReadBuffer &packet)
|
||||
(LPVOID)envArg, cwdArg, &sui, &pi);
|
||||
|
||||
Trace("cp: %s %d", (ret ? "success" : "fail"), (int)pi.dwProcessId);
|
||||
|
||||
if (ret) {
|
||||
CloseHandle(pi.hThread);
|
||||
m_childProcess = pi.hProcess;
|
||||
}
|
||||
|
||||
// TODO: report success/failure to client
|
||||
return ret ? 0 : GetLastError();
|
||||
}
|
||||
|
||||
void Agent::handleSetSizePacket(ReadBuffer &packet)
|
||||
int Agent::handleSetSizePacket(ReadBuffer &packet)
|
||||
{
|
||||
int cols = packet.getInt();
|
||||
int rows = packet.getInt();
|
||||
packet.assertEof();
|
||||
resizeWindow(cols, rows);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Agent::dataSocketReadyRead()
|
||||
@ -189,7 +205,19 @@ void Agent::socketDisconnected()
|
||||
|
||||
void Agent::pollTimeout()
|
||||
{
|
||||
scrapeOutput();
|
||||
if (m_dataSocket->state() == QLocalSocket::ConnectedState)
|
||||
scrapeOutput();
|
||||
|
||||
if (m_childProcess != NULL) {
|
||||
if (WaitForSingleObject(m_childProcess, 0) == WAIT_OBJECT_0) {
|
||||
DWORD exitCode;
|
||||
if (GetExitCodeProcess(m_childProcess, &exitCode))
|
||||
m_childExitCode = exitCode;
|
||||
CloseHandle(m_childProcess);
|
||||
m_childProcess = NULL;
|
||||
m_dataSocket->disconnectFromServer();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Detect window movement. If the window moves down (presumably as a
|
||||
|
@ -33,8 +33,8 @@ signals:
|
||||
private slots:
|
||||
void controlSocketReadyRead();
|
||||
void handlePacket(ReadBuffer &packet);
|
||||
void handleStartProcessPacket(ReadBuffer &packet);
|
||||
void handleSetSizePacket(ReadBuffer &packet);
|
||||
int handleStartProcessPacket(ReadBuffer &packet);
|
||||
int handleSetSizePacket(ReadBuffer &packet);
|
||||
void dataSocketReadyRead();
|
||||
void socketDisconnected();
|
||||
void pollTimeout();
|
||||
@ -56,8 +56,8 @@ private:
|
||||
QLocalSocket *m_dataSocket;
|
||||
Terminal *m_terminal;
|
||||
QTimer *m_timer;
|
||||
|
||||
bool m_autoShutDown;
|
||||
HANDLE m_childProcess;
|
||||
int m_childExitCode;
|
||||
|
||||
int m_syncRow;
|
||||
int m_syncCounter;
|
||||
|
@ -257,6 +257,15 @@ static void writePacket(pconsole_t *pc, const WriteBuffer &packet)
|
||||
assert(success && actual == payloadSize);
|
||||
}
|
||||
|
||||
static int32_t readInt32(pconsole_t *pc)
|
||||
{
|
||||
int32_t result;
|
||||
DWORD actual;
|
||||
BOOL success = ReadFile(pc->controlPipe, &result, sizeof(int32_t), &actual, NULL);
|
||||
assert(success && actual == sizeof(int32_t));
|
||||
return result;
|
||||
}
|
||||
|
||||
PCONSOLE_API int pconsole_start_process(pconsole_t *pc,
|
||||
const wchar_t *appname,
|
||||
const wchar_t *cmdline,
|
||||
@ -279,7 +288,15 @@ PCONSOLE_API int pconsole_start_process(pconsole_t *pc,
|
||||
}
|
||||
packet.putWString(envStr);
|
||||
writePacket(pc, packet);
|
||||
// TODO: return success/fail...
|
||||
return readInt32(pc);
|
||||
}
|
||||
|
||||
PCONSOLE_API int pconsole_get_exit_code(pconsole_t *pc)
|
||||
{
|
||||
WriteBuffer packet;
|
||||
packet.putInt(AgentMsg::GetExitCode);
|
||||
writePacket(pc, packet);
|
||||
return readInt32(pc);
|
||||
}
|
||||
|
||||
PCONSOLE_API HANDLE pconsole_get_data_pipe(pconsole_t *pc)
|
||||
@ -294,6 +311,7 @@ PCONSOLE_API int pconsole_set_size(pconsole_t *pc, int cols, int rows)
|
||||
packet.putInt(cols);
|
||||
packet.putInt(rows);
|
||||
writePacket(pc, packet);
|
||||
return readInt32(pc);
|
||||
}
|
||||
|
||||
PCONSOLE_API void pconsole_close(pconsole_t *pc)
|
||||
|
@ -14,6 +14,7 @@
|
||||
|
||||
|
||||
static int signalWriteFd;
|
||||
static volatile bool ioHandlerDied;
|
||||
|
||||
|
||||
// Put the input terminal into non-blocking non-canonical mode.
|
||||
@ -125,12 +126,19 @@ void *OutputHandler::threadProc(void *pvthis)
|
||||
if (!ret || amount == 0)
|
||||
break;
|
||||
// TODO: partial writes?
|
||||
int written = write(STDOUT_FILENO, buffer, amount);
|
||||
// I don't know if this write can be interrupted or not, but handle it
|
||||
// just in case.
|
||||
int written;
|
||||
do {
|
||||
written = write(STDOUT_FILENO, buffer, amount);
|
||||
} while (written == -1 && errno == EINTR);
|
||||
if (written != amount)
|
||||
break;
|
||||
}
|
||||
delete [] buffer;
|
||||
CloseHandle(event);
|
||||
ioHandlerDied = true;
|
||||
writeToSignalFd();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -159,6 +167,11 @@ void *InputHandler::threadProc(void *pvthis)
|
||||
char *buffer = new char[bufferSize];
|
||||
while (true) {
|
||||
int amount = read(STDIN_FILENO, buffer, bufferSize);
|
||||
if (amount == -1 && errno == EINTR) {
|
||||
// Apparently, this read is interrupted on Cygwin 1.7 by a SIGWINCH
|
||||
// signal even though I set the SA_RESTART flag on the handler.
|
||||
continue;
|
||||
}
|
||||
if (amount <= 0)
|
||||
break;
|
||||
DWORD written;
|
||||
@ -177,6 +190,8 @@ void *InputHandler::threadProc(void *pvthis)
|
||||
}
|
||||
delete [] buffer;
|
||||
CloseHandle(event);
|
||||
ioHandlerDied = true;
|
||||
writeToSignalFd();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -223,11 +238,22 @@ int main()
|
||||
fd_set readfds;
|
||||
FD_ZERO(&readfds);
|
||||
FD_SET(signalReadFd, &readfds);
|
||||
if (select(signalReadFd + 1, &readfds, NULL, NULL, NULL) < 0) {
|
||||
if (select(signalReadFd + 1, &readfds, NULL, NULL, NULL) < 0 &&
|
||||
errno != EINTR) {
|
||||
perror("select failed");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Discard any data in the signal pipe.
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
// Check for terminal resize.
|
||||
{
|
||||
winsize sz2;
|
||||
@ -238,17 +264,14 @@ int main()
|
||||
}
|
||||
}
|
||||
|
||||
// Discard any data in the signal pipe.
|
||||
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);
|
||||
}
|
||||
// Check for an I/O handler shutting down (possibly indicating that the
|
||||
// child process has exited).
|
||||
if (ioHandlerDied)
|
||||
break;
|
||||
}
|
||||
|
||||
// TODO: Get the pconsole child exit code and exit with it.
|
||||
int exitCode = pconsole_get_exit_code(pconsole);
|
||||
|
||||
restoreTerminalMode(mode);
|
||||
return 0;
|
||||
return exitCode;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user