Windows: 'trexecmd -o env' sort of works, but waits for timeout at end of input

--HG--
branch : WINDOWSPORT
This commit is contained in:
Jean-Francois Dockes 2015-09-08 15:49:13 +02:00
parent 6dc0817406
commit ef4bb2f4ca
6 changed files with 340 additions and 209 deletions

View File

@ -16,6 +16,7 @@
*/ */
#ifndef _EXECMD_H_INCLUDED_ #ifndef _EXECMD_H_INCLUDED_
#define _EXECMD_H_INCLUDED_ #define _EXECMD_H_INCLUDED_
#include "autoconfig.h"
#include <string> #include <string>
#include <vector> #include <vector>

View File

@ -116,7 +116,7 @@ string path_tchartoutf8(TCHAR *text)
} }
return string(&buffer[0]); return string(&buffer[0]);
#else #else
path = text; return text;
#endif #endif
} }

View File

@ -42,7 +42,7 @@
<ConfigurationType>StaticLibrary</ConfigurationType> <ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries> <UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v140</PlatformToolset> <PlatformToolset>v140</PlatformToolset>
<CharacterSet>Unicode</CharacterSet> <CharacterSet>MultiByte</CharacterSet>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType> <ConfigurationType>StaticLibrary</ConfigurationType>
@ -236,6 +236,7 @@
<ClCompile Include="..\utils\wipedir.cpp" /> <ClCompile Include="..\utils\wipedir.cpp" />
<ClCompile Include="..\xaposix\safe.cc" /> <ClCompile Include="..\xaposix\safe.cc" />
<ClCompile Include="dirent.c" /> <ClCompile Include="dirent.c" />
<ClCompile Include="execmd_w.cpp" />
<ClCompile Include="fnmatch.c" /> <ClCompile Include="fnmatch.c" />
<ClCompile Include="strptime.cpp" /> <ClCompile Include="strptime.cpp" />
</ItemGroup> </ItemGroup>

View File

@ -293,6 +293,9 @@
<ClCompile Include="..\utils\cpuconf.cpp"> <ClCompile Include="..\utils\cpuconf.cpp">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="execmd_w.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="ClassDiagram.cd" /> <None Include="ClassDiagram.cd" />

View File

@ -5,39 +5,60 @@
#include <iostream> #include <iostream>
#include <string> #include <string>
#include "debuglog.h"
using namespace std; using namespace std;
static void printError(const string& text)
{
DWORD err = GetLastError();
LOGERR(("%s : err: %d\n", text.c_str(), err));
}
class ExecCmd::Internal { class ExecCmd::Internal {
public: public:
Internal() Internal()
: m_advise(0), m_provide(0), m_timeoutMs(1000), : advise(0), provide(0), timeoutMs(1000),
m_pid(-1), m_hOutputRead(NULL), m_hInputWrite(NULL) { hOutputRead(NULL), hInputWrite(NULL) {
} }
std::vector<std::string> m_env; std::vector<std::string> m_env;
ExecCmdAdvise *m_advise; ExecCmdAdvise *advise;
ExecCmdProvide *m_provide; ExecCmdProvide *provide;
bool m_killRequest; bool killRequest;
int m_timeoutMs; int timeoutMs;
string m_stderrFile; string stderrFile;
// Subprocess id // Subprocess id
pid_t m_pid; HANDLE hOutputRead;
HANDLE m_hOutputRead; HANDLE hInputWrite;
HANDLE m_hInputWrite; OVERLAPPED oOutputRead; // Do these need resource control?
OVERLAPPED m_oOutputRead; // Do these need resource control? OVERLAPPED oInputWrite;
OVERLAPPED m_oInputWrite; PROCESS_INFORMATION piProcInfo;
PROCESS_INFORMATION m_piProcInfo;
// Reset internal state indicators. Any resources should have been // Reset internal state indicators. Any resources should have been
// previously freed // previously freed
void reset() { void reset() {
m_killRequest = false; killRequest = false;
m_hOutputRead = NULL; hOutputRead = NULL;
m_hInputWrite = NULL; hInputWrite = NULL;
memset(&m_oOutputRead, 0, sizeof(m_oOutputRead)); memset(&oOutputRead, 0, sizeof(oOutputRead));
memset(&m_oInputWrite, 0, sizeof(m_oInputWrite)); memset(&oInputWrite, 0, sizeof(oInputWrite));
m_pid = -1; ZeroMemory(&piProcInfo, sizeof(PROCESS_INFORMATION));
} }
void releaseResources() {
if (hOutputRead)
CloseHandle(hOutputRead);
if (hInputWrite)
CloseHandle(hInputWrite);
if (oOutputRead.hEvent)
CloseHandle(oOutputRead.hEvent);
if (oInputWrite.hEvent)
CloseHandle(oInputWrite.hEvent);
reset();
}
bool PreparePipes(bool has_input, HANDLE *hChildInput,
bool has_output, HANDLE *hChildOutput,
HANDLE *hChildError);
}; };
ExecCmd::ExecCmd() ExecCmd::ExecCmd()
@ -47,38 +68,57 @@ ExecCmd::ExecCmd()
m->reset(); m->reset();
} }
} }
ExecCmd::~ExecCmd()
{
if (m)
m->releaseResources();
}
bool ExecCmd::which(const string& cmd, string& exe, const char* path)
{
return false;
}
void ExecCmd::setAdvise(ExecCmdAdvise *adv) void ExecCmd::setAdvise(ExecCmdAdvise *adv)
{ {
m->m_advise = adv; m->advise = adv;
} }
void ExecCmd::setProvide(ExecCmdProvide *p) void ExecCmd::setProvide(ExecCmdProvide *p)
{ {
m->m_provide = p; m->provide = p;
} }
void ExecCmd::setTimeout(int mS) void ExecCmd::setTimeout(int mS)
{ {
if (mS > 30) { if (mS > 30) {
m->m_timeoutMs = mS; m->timeoutMs = mS;
} }
} }
void ExecCmd::setStderr(const std::string& stderrFile) void ExecCmd::setStderr(const std::string& stderrFile)
{ {
m->m_stderrFile = stderrFile; m->stderrFile = stderrFile;
} }
pid_t ExecCmd::getChildPid() pid_t ExecCmd::getChildPid()
{ {
return m->m_pid; return m->piProcInfo.dwProcessId;
} }
void ExecCmd::setKill() void ExecCmd::setKill()
{ {
m->m_killRequest = true; m->killRequest = true;
} }
void ExecCmd::zapChild() void ExecCmd::zapChild()
{ {
setKill(); setKill();
(void)wait(); (void)wait();
} }
void ExecCmd::putenv(const string &envassign)
{
m->m_env.push_back(envassign);
}
void ExecCmd::putenv(const string &name, const string& value)
{
string ea = name + "=" + value;
putenv(ea);
}
bool ExecCmd::Internal::PreparePipes(bool has_input,HANDLE *hChildInput, bool ExecCmd::Internal::PreparePipes(bool has_input,HANDLE *hChildInput,
@ -90,43 +130,48 @@ bool ExecCmd::Internal::PreparePipes(bool has_input,HANDLE *hChildInput,
HANDLE hOutputWrite = NULL; HANDLE hOutputWrite = NULL;
HANDLE hErrorWrite = NULL; HANDLE hErrorWrite = NULL;
HANDLE hInputRead = NULL; HANDLE hInputRead = NULL;
m_hOutputRead = NULL; hOutputRead = NULL;
m_hInputWrite = NULL; hInputWrite = NULL;
memset(&m_oOutputRead, 0, sizeof(m_oOutputRead)); memset(&oOutputRead, 0, sizeof(oOutputRead));
memset(&m_oInputWrite, 0, sizeof(m_oInputWrite)); memset(&oInputWrite, 0, sizeof(oInputWrite));
// manual reset event // manual reset event
m_oOutputRead.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); oOutputRead.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (m_oOutputRead.hEvent == INVALID_HANDLE_VALUE) { if (oOutputRead.hEvent == INVALID_HANDLE_VALUE) {
LOGERR(("ExecCmd::PreparePipes: CreateEvent failed\n"));
goto errout; goto errout;
} }
// manual reset event // manual reset event
m_oInputWrite.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); oInputWrite.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (m_oInputWrite.hEvent == INVALID_HANDLE_VALUE) { if (oInputWrite.hEvent == INVALID_HANDLE_VALUE) {
LOGERR(("ExecCmd::PreparePipes: CreateEvent failed\n"));
goto errout; goto errout;
} }
if (has_output) {
// src for this code: https://www.daniweb.com/software-development/cpp/threads/295780/using-named-pipes-with-asynchronous-io-redirection-to-winapi
// ONLY IMPORTANT CHANGE
// set inheritance flag to TRUE in CreateProcess
// you need this for the client to inherit the handles
SECURITY_ATTRIBUTES sa; SECURITY_ATTRIBUTES sa;
// Set up the security attributes struct. // Set up the security attributes struct.
sa.nLength = sizeof(SECURITY_ATTRIBUTES); sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.bInheritHandle = TRUE; // this is the critical bit sa.bInheritHandle = TRUE; // this is the critical bit
sa.lpSecurityDescriptor = NULL; sa.lpSecurityDescriptor = NULL;
if (has_output) {
LOGDEB(("ExecCmd::preparePipes: has output\n"));
// src for this code: https://www.daniweb.com/software-development/cpp/threads/295780/using-named-pipes-with-asynchronous-io-redirection-to-winapi
// ONLY IMPORTANT CHANGE
// set inheritance flag to TRUE in CreateProcess
// you need this for the client to inherit the handles
// Create the child output named pipe. // Create the child output named pipe.
// This creates a inheritable, one-way handle for the server to read // This creates a inheritable, one-way handle for the server to read
hOutputReadTmp = CreateNamedPipe( hOutputReadTmp = CreateNamedPipe(
TEXT("\\\\.\\pipe\\outstreamPipe"), TEXT("\\\\.\\pipe\\outstreamPipe"),
PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED, PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED,
PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, PIPE_WAIT,
1, 4096, 4096, 0, &sa); 1, 4096, 4096, 0, &sa);
if (hOutputReadTmp == INVALID_HANDLE_VALUE) { if (hOutputReadTmp == INVALID_HANDLE_VALUE) {
printError("preparePipes: CreateNamedPipe(out tmp)");
goto errout; goto errout;
} }
@ -143,7 +188,10 @@ bool ExecCmd::Internal::PreparePipes(bool has_input,HANDLE *hChildInput,
0, &sa, OPEN_EXISTING, // very important flag! 0, &sa, OPEN_EXISTING, // very important flag!
FILE_ATTRIBUTE_NORMAL, 0 // no template file for OPEN_EXISTING FILE_ATTRIBUTE_NORMAL, 0 // no template file for OPEN_EXISTING
); );
if (hOutputWrite == INVALID_HANDLE_VALUE) {
printError("preparePipes: CreateFile(outputWrite)");
goto errout;
}
// All is well? not quite. Our main server-side handle was // All is well? not quite. Our main server-side handle was
// created shareable. // created shareable.
@ -156,9 +204,10 @@ bool ExecCmd::Internal::PreparePipes(bool has_input,HANDLE *hChildInput,
// the child inherits the properties and, as a result, // the child inherits the properties and, as a result,
// non-closeable handles to the pipes are created. // non-closeable handles to the pipes are created.
if (!DuplicateHandle(GetCurrentProcess(), hOutputReadTmp, if (!DuplicateHandle(GetCurrentProcess(), hOutputReadTmp,
GetCurrentProcess(), &m_hOutputRead, GetCurrentProcess(), &hOutputRead,
0, FALSE, // Make it uninheritable. 0, FALSE, // Make it uninheritable.
DUPLICATE_SAME_ACCESS)) { DUPLICATE_SAME_ACCESS)) {
printError("preparePipes: DuplicateHandle(readtmp->outputread)");
goto errout; goto errout;
} }
// now we kill the original, inheritable server-side handle. That // now we kill the original, inheritable server-side handle. That
@ -166,30 +215,44 @@ bool ExecCmd::Internal::PreparePipes(bool has_input,HANDLE *hChildInput,
// getting it. Child-proofing. Close inheritable copies of the // getting it. Child-proofing. Close inheritable copies of the
// handles you do not want to be inherited. // handles you do not want to be inherited.
if (!CloseHandle(hOutputReadTmp)) { if (!CloseHandle(hOutputReadTmp)) {
printError("preparePipes: CloseHandle(readtmp)");
goto errout; goto errout;
} }
hOutputReadTmp = NULL; hOutputReadTmp = NULL;
}
#if 0 // Nope, can't do this, we certainly don't want to mix stdout and stderr
// Have to take the give stderr output file instead
if (!m->m_stderrFile.empty()) {
// Open the file and make the handle inheritable
} else { } else {
// Duplicate our own stderr. Does passing NULL work for this ? // Not using child output. Let the child have our standard output.
} HANDLE hstd = GetStdHandle(STD_OUTPUT_HANDLE);
if (hstd == INVALID_HANDLE_VALUE) {
// we're going to give the same pipe for stderr, so duplicate for that: printError("preparePipes: GetStdHandle(stdout)");
// Create a duplicate of the output write handle for the std error
// write handle. This is necessary in case the child application
// closes one of its std output handles.
if (!DuplicateHandle(GetCurrentProcess(), hOutputWrite,
GetCurrentProcess(), &hErrorWrite,
0, TRUE, DUPLICATE_SAME_ACCESS)) {
goto errout; goto errout;
} }
#endif if (!DuplicateHandle(GetCurrentProcess(), hstd,
GetCurrentProcess(), &hOutputWrite,
0, TRUE,
DUPLICATE_SAME_ACCESS)) {
printError("preparePipes: DuplicateHandle(stdout)");
goto errout;
}
}
// Have to take the give stderr output file instead
if (!stderrFile.empty()) {
// Open the file set up the child handle: TBD
} else {
// Let the child inherit our standard input
HANDLE hstd = GetStdHandle(STD_ERROR_HANDLE);
if (hstd == INVALID_HANDLE_VALUE) {
printError("preparePipes: GetStdHandle(stderr)");
goto errout;
}
if (!DuplicateHandle(GetCurrentProcess(), hstd,
GetCurrentProcess(), &hErrorWrite,
0, TRUE,
DUPLICATE_SAME_ACCESS)) {
printError("preparePipes: DuplicateHandle(stderr)");
goto errout;
}
}
if (has_input) { if (has_input) {
// now same procedure for input pipe // now same procedure for input pipe
@ -200,6 +263,7 @@ bool ExecCmd::Internal::PreparePipes(bool has_input,HANDLE *hChildInput,
PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
1, 4096, 4096, 0, &sa); 1, 4096, 4096, 0, &sa);
if (hInputWriteTmp == INVALID_HANDLE_VALUE) { if (hInputWriteTmp == INVALID_HANDLE_VALUE) {
printError("preparePipes: CreateNamedPipe(inputWTmp)");
goto errout; goto errout;
} }
@ -209,17 +273,37 @@ bool ExecCmd::Internal::PreparePipes(bool has_input,HANDLE *hChildInput,
0, &sa, OPEN_EXISTING, // very important flag! 0, &sa, OPEN_EXISTING, // very important flag!
FILE_ATTRIBUTE_NORMAL, 0 // no template file for OPEN_EXISTING FILE_ATTRIBUTE_NORMAL, 0 // no template file for OPEN_EXISTING
); );
if (hInputRead == INVALID_HANDLE_VALUE) {
printError("preparePipes: CreateFile(inputRead)");
goto errout;
}
if (!DuplicateHandle(GetCurrentProcess(), hInputWriteTmp, if (!DuplicateHandle(GetCurrentProcess(), hInputWriteTmp,
GetCurrentProcess(), &m_hInputWrite, GetCurrentProcess(), &hInputWrite,
0, FALSE, // Make it uninheritable. 0, FALSE, // Make it uninheritable.
DUPLICATE_SAME_ACCESS)) { DUPLICATE_SAME_ACCESS)) {
printError("preparePipes: DuplicateHandle(inputWTmp->inputW)");
goto errout; goto errout;
} }
if (!CloseHandle(hInputWriteTmp)) { if (!CloseHandle(hInputWriteTmp)) {
printError("preparePipes: CloseHandle(inputWTmp)");
goto errout; goto errout;
} }
hInputWriteTmp = NULL; hInputWriteTmp = NULL;
} else {
// Let the child inherit our standard input
HANDLE hstd = GetStdHandle(STD_INPUT_HANDLE);
if (hstd == INVALID_HANDLE_VALUE) {
printError("preparePipes: GetStdHandle(stdin)");
goto errout;
}
if (!DuplicateHandle(GetCurrentProcess(), hstd,
GetCurrentProcess(), &hInputRead,
0, TRUE,
DUPLICATE_SAME_ACCESS)) {
printError("preparePipes: DuplicateHandle(stdin)");
goto errout;
}
} }
*hChildInput = hInputRead; *hChildInput = hInputRead;
@ -228,24 +312,18 @@ bool ExecCmd::Internal::PreparePipes(bool has_input,HANDLE *hChildInput,
return true; return true;
errout: errout:
if (hInputWriteTmp) releaseResources();
CloseHandle(hInputWriteTmp);
if (hOutputReadTmp)
CloseHandle(hOutputReadTmp);
if (m_hOutputRead)
CloseHandle(m_hOutputRead);
if (m_hInputWrite)
CloseHandle(m_hInputWrite);
if (m_oOutputRead.hEvent)
CloseHandle(m_oOutputRead.hEvent);
if (m_oInputWrite.hEvent)
CloseHandle(m_oInputWrite.hEvent);
if (hOutputWrite) if (hOutputWrite)
CloseHandle(hOutputWrite); CloseHandle(hOutputWrite);
if (hInputRead) if (hInputRead)
CloseHandle(hInputRead); CloseHandle(hInputRead);
if (hErrorWrite) if (hErrorWrite)
CloseHandle(hErrorWrite); CloseHandle(hErrorWrite);
if (hInputWriteTmp)
CloseHandle(hInputWriteTmp);
if (hOutputReadTmp)
CloseHandle(hOutputReadTmp);
return false;
} }
/** /**
@ -332,14 +410,13 @@ int ExecCmd::startExec(const string &cmd, const vector<string>& args,
string cmdline = argvToCmdLine(cmd, args); string cmdline = argvToCmdLine(cmd, args);
// The resource manager ensures resources are freed if we return early
//ExecCmdRsrc e(this->m);
HANDLE hInputRead; HANDLE hInputRead;
HANDLE hOutputWrite; HANDLE hOutputWrite;
HANDLE hErrorWrite; HANDLE hErrorWrite;
if (!m->PreparePipes(has_input, &hInputRead, has_output, if (!m->PreparePipes(has_input, &hInputRead, has_output,
&hOutputWrite, &hErrorWrite)) { &hOutputWrite, &hErrorWrite)) {
LOGERR(("ExecCmd::startExec: preparePipes failed\n"));
m->releaseResources();
return false; return false;
} }
@ -347,12 +424,10 @@ int ExecCmd::startExec(const string &cmd, const vector<string>& args,
BOOL bSuccess = FALSE; BOOL bSuccess = FALSE;
// Set up members of the PROCESS_INFORMATION structure. // Set up members of the PROCESS_INFORMATION structure.
ZeroMemory(&m->piProcInfo, sizeof(PROCESS_INFORMATION));
ZeroMemory(&piProcInfo, sizeof(PROCESS_INFORMATION));
// Set up members of the STARTUPINFO structure. // Set up members of the STARTUPINFO structure.
// This structure specifies the STDIN and STDOUT handles for redirection. // This structure specifies the STDIN and STDOUT handles for redirection.
ZeroMemory(&siStartInfo, sizeof(STARTUPINFO)); ZeroMemory(&siStartInfo, sizeof(STARTUPINFO));
siStartInfo.cb = sizeof(STARTUPINFO); siStartInfo.cb = sizeof(STARTUPINFO);
siStartInfo.dwFlags |= STARTF_USESTDHANDLES; siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
@ -361,8 +436,12 @@ int ExecCmd::startExec(const string &cmd, const vector<string>& args,
siStartInfo.hStdError = hErrorWrite; siStartInfo.hStdError = hErrorWrite;
// Create the child process. // Create the child process.
// Need a writable buffer for the command line, for some reason.
LPSTR buf = (LPSTR)malloc(cmdline.size() + 1);
memcpy(buf, cmdline.c_str(), cmdline.size());
buf[cmdline.size()] = 0;
bSuccess = CreateProcess(NULL, bSuccess = CreateProcess(NULL,
cmdline.c_str(), // command line buf, // command line
NULL, // process security attributes NULL, // process security attributes
NULL, // primary thread security attrs NULL, // primary thread security attrs
TRUE, // handles are inherited TRUE, // handles are inherited
@ -370,25 +449,50 @@ int ExecCmd::startExec(const string &cmd, const vector<string>& args,
NULL, // use parent's environment NULL, // use parent's environment
NULL, // use parent's current directory NULL, // use parent's current directory
&siStartInfo, // STARTUPINFO pointer &siStartInfo, // STARTUPINFO pointer
&piProcInfo); // receives PROCESS_INFORMATION &m->piProcInfo); // receives PROCESS_INFORMATION
free(buf);
if (!bSuccess) { if (!bSuccess) {
printError("ExecCmd::doexec: CreateProcess");
m->releaseResources();
return false; return false;
} }
m->pid = piProcInfo.dwProcessId;
return true; return true;
} }
enum WaitResult {
Ok, Quit, Timeout
};
static WaitResult Wait(HANDLE hdl, int timeout)
{
//HANDLE hdls[2] = { hdl, eQuit };
HANDLE hdls[1] = { hdl};
LOGDEB(("ExecCmd::Wait\n"));
DWORD res = WaitForMultipleObjects(1, hdls, FALSE, timeout);
if (res == WAIT_OBJECT_0) {
LOGDEB(("ExecCmd::Wait: returning Ok\n"));
return Ok;
} else if (res == (WAIT_OBJECT_0 + 1)) {
LOGDEB(("ExecCmd::Wait: returning Quit\n"));
return Quit;
} else if (res == WAIT_TIMEOUT) {
LOGDEB(("ExecCmd::Wait: returning Timeout\n"));
return Timeout;
}
printError("Wait: WaitForMultipleObjects: unknown, returning Timout\n");
return Timeout;
}
// Read from a file and write its contents to the pipe for the child's // Read from a file and write its contents to the pipe for the child's
// STDIN. Stop when there is no more data. // STDIN. Stop when there is no more data.
int ExecCmd::send(const string& data) int ExecCmd::send(const string& data)
{ {
DWORD dwRead, dwWritten; DWORD dwWritten;
BOOL bSuccess = false; BOOL bSuccess = false;
bSuccess = WriteFile(m->m_hInputWrite, data.c_str(), data.size(), bSuccess = WriteFile(m->hInputWrite, data.c_str(), (DWORD)data.size(),
NULL, &m->m_oInputWrite); NULL, &m->oInputWrite);
DWORD err = GetLastError(); DWORD err = GetLastError();
// TODO: some more decision, either the operation completes immediately // TODO: some more decision, either the operation completes immediately
@ -396,78 +500,98 @@ int ExecCmd::send(const string& data)
// and ERROR_IO_PENDING // and ERROR_IO_PENDING
// in the first case bytes read/written parameter can be used directly // in the first case bytes read/written parameter can be used directly
if (!bSuccess && err != ERROR_IO_PENDING) { if (!bSuccess && err != ERROR_IO_PENDING) {
return; return -1;
} }
WaitResult waitRes = Wait(oInputWrite.hEvent, 5000); WaitResult waitRes = Wait(m->oInputWrite.hEvent, 5000);
if (waitRes == Ok) { if (waitRes == Ok) {
if (!GetOverlappedResult(m->m_hInputWrite, if (!GetOverlappedResult(m->hInputWrite,
&m->m_oInputWrite, &dwWritten, TRUE)) { &m->oInputWrite, &dwWritten, TRUE)) {
ErrorExit("GetOverlappedResult"); printError("GetOverlappedResult");
return -1;
} }
} else if (waitRes == Quit) { } else if (waitRes == Quit) {
if (!CancelIo(hInputWrite)) if (!CancelIo(m->hInputWrite)) {
ErrorExit("CancelIo"); printError("CancelIo");
return;
} else if (waitRes == Timeout) {
if (!CancelIo(hInputWrite))
ErrorExit("CancelIo");
return;
} }
return -1;
} else if (waitRes == Timeout) {
if (!CancelIo(m->hInputWrite)) {
printError("CancelIo");
}
return -1;
}
return dwWritten;
} }
#ifndef MIN
#define MIN(A,B) ((A)<(B)?(A):(B))
#endif
// Read output from the child process's pipe for STDOUT // Read output from the child process's pipe for STDOUT
// and write to cout in this programme // and write to cout in this programme
// Stop when there is no more data. // Stop when there is no more data.
// @arg cnt count to read, -1 means read to end of data. // @arg cnt count to read, -1 means read to end of data.
int ExecCmd::receive(const string& data, int cnt) int ExecCmd::receive(string& data, int cnt)
{ {
DWORD dwRead; DWORD dwRead;
const int BUFSIZE = 8192; const int BUFSIZE = 8192;
CHAR chBuf[BUFSIZE]; CHAR chBuf[BUFSIZE];
BOOL bSuccess = FALSE; BOOL bSuccess = FALSE;
int totread = 0; int totread = 0;
LOGDEB(("ExecCmd::receive: cnt %d\n", cnt));
while (true) { while (true) {
int toread = cnt > 0 ? MIN(cnt - totread, BUFSIZE) : BUFSIZE; int toread = cnt > 0 ? MIN(cnt - totread, BUFSIZE) : BUFSIZE;
bSuccess = ReadFile(m->m_hOutputRead, chBuf, toread, LOGDEB(("ExecCmd::receive: calling ReadFile with cnt %d\n", toread))
NULL, &m->m_oOutputRead); bSuccess = ReadFile(m->hOutputRead, chBuf, toread,
NULL, &m->oOutputRead);
DWORD err = GetLastError(); DWORD err = GetLastError();
if (!bSuccess && err != ERROR_IO_PENDING) if (!bSuccess && err != ERROR_IO_PENDING) {
LOGERR(("ExecCmd::receive: ReadFile error: %d\n", int(err)));
break; break;
WaitResult waitRes = Wait(oOutputRead.hEvent, 5000);
if (waitRes == Ok) {
if (!GetOverlappedResult(hOutputRead, &oOutputRead, &dwRead, TRUE)) {
ErrorExit("GetOverlappedResult");
} }
WaitResult waitRes = Wait(m->oOutputRead.hEvent, 5000);
if (waitRes == Ok) {
if (!GetOverlappedResult(m->hOutputRead, &m->oOutputRead, &dwRead, TRUE)) {
printError("GetOverlappedResult");
return -1;
}
LOGDEB(("ExecCmd::receive: got %d bytes\n", int(dwRead)));
totread += dwRead; totread += dwRead;
data.append(buf, dwRead); data.append(chBuf, dwRead);
} else if (waitRes == Quit) { } else if (waitRes == Quit) {
if (!CancelIo(hOutputRead)) if (!CancelIo(m->hOutputRead)) {
ErrorExit("CancelIo"); printError("CancelIo");
}
break; break;
} else if (waitRes == Timeout) { } else if (waitRes == Timeout) {
if (!CancelIo(hOutputRead)) if (!CancelIo(m->hOutputRead)) {
ErrorExit("CancelIo"); printError("CancelIo");
}
break; break;
} }
} }
return totread; return totread;
} }
int ExecCmd::getline(std::string& data)
{
return -1;
}
int ExecCmd::wait() int ExecCmd::wait()
{ {
// Wait until child process exits. // Wait until child process exits.
WaitForSingleObject(m->m_piProcInfo.hProcess, INFINITE); WaitForSingleObject(m->piProcInfo.hProcess, INFINITE);
// Get exit code // Get exit code
DWORD exit_code = 0; DWORD exit_code = 0;
GetExitCodeProcess(m->m_piProcInfo.hProcess, &exit_code); GetExitCodeProcess(m->piProcInfo.hProcess, &exit_code);
// Close handles to the child process and its primary thread. // Close handles to the child process and its primary thread.
CloseHandle(m->m_piProcInfo.hProcess); CloseHandle(m->piProcInfo.hProcess);
CloseHandle(m->m_piProcInfo.hThread); CloseHandle(m->piProcInfo.hThread);
return (int)exit_code; return (int)exit_code;
} }
@ -483,9 +607,9 @@ int ExecCmd::doexec(const string &cmd, const vector<string>& args,
return -1; return -1;
} }
if (input) { if (input) {
receive(*input); send(*input);
} else if (output) { } else if (output) {
send(*output); receive(*output);
} }
return wait(); return wait();
} }

View File

@ -1,11 +1,12 @@
#include "autoconfig.h"
#include "execmd.h" #include "execmd.h"
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h> #include "safeunistd.h"
#include <string.h> #include <string.h>
#include <signal.h>
#include <string> #include <string>
#include <iostream> #include <iostream>
@ -115,22 +116,22 @@ bool exercise_mhexecm(const string& cmdstr, const string& mimetype,
static char *thisprog; static char *thisprog;
static char usage [] = static char usage [] =
"trexecmd [-c -r -i -o] cmd [arg1 arg2 ...]\n" "trexecmd [-c -r -i -o] cmd [arg1 arg2 ...]\n"
" -c : test cancellation (ie: trexecmd -c sleep 1000)\n" " -c : test cancellation (ie: trexecmd -c sleep 1000)\n"
" -r : run reexec. Must be separate option.\n" " -r : run reexec. Must be separate option.\n"
" -i : command takes input\n" " -i : command takes input\n"
" -o : command produces output\n" " -o : command produces output\n"
" If -i is set, we send /etc/group contents to whatever command is run\n" " If -i is set, we send /etc/group contents to whatever command is run\n"
" If -o is set, we print whatever comes out\n" " If -o is set, we print whatever comes out\n"
"trexecmd -m <filter> <mimetype> <file> [file ...]: test execm:\n" "trexecmd -m <filter> <mimetype> <file> [file ...]: test execm:\n"
" <filter> should be the path to an execm filter\n" " <filter> should be the path to an execm filter\n"
" <mimetype> the type of the file parameters\n" " <mimetype> the type of the file parameters\n"
"trexecmd -w cmd : do the 'which' thing\n" "trexecmd -w cmd : do the 'which' thing\n"
; ;
static void Usage(void) static void Usage(FILE *fp = stderr)
{ {
fprintf(stderr, "%s: usage:\n%s", thisprog, usage); fprintf(fp, "%s: usage:\n%s", thisprog, usage);
exit(1); exit(1);
} }
@ -222,6 +223,7 @@ int main(int argc, char *argv[])
case 'm': op_flags |= OPT_m; break; case 'm': op_flags |= OPT_m; break;
case 'i': op_flags |= OPT_i; break; case 'i': op_flags |= OPT_i; break;
case 'o': op_flags |= OPT_o; break; case 'o': op_flags |= OPT_o; break;
case'h': Usage(stdout);
default: Usage(); break; default: Usage(); break;
} }
b1: argc--; argv++; b1: argc--; argv++;
@ -263,13 +265,13 @@ int main(int argc, char *argv[])
// Set callback to be called whenever there is new data // Set callback to be called whenever there is new data
// available and at a periodic interval, to check for // available and at a periodic interval, to check for
// cancellation // cancellation
#ifdef LATER
MEAdv adv; MEAdv adv;
mexec.setAdvise(&adv); mexec.setAdvise(&adv);
mexec.setTimeout(5); mexec.setTimeout(5);
// Stderr output goes there // Stderr output goes there
mexec.setStderr("/tmp/trexecStderr"); mexec.setStderr("/tmp/trexecStderr");
#endif
// A few environment variables. Check with trexecmd env // A few environment variables. Check with trexecmd env
mexec.putenv("TESTVARIABLE1=TESTVALUE1"); mexec.putenv("TESTVARIABLE1=TESTVALUE1");
mexec.putenv("TESTVARIABLE2=TESTVALUE2"); mexec.putenv("TESTVARIABLE2=TESTVALUE2");
@ -297,7 +299,7 @@ int main(int argc, char *argv[])
fprintf(stderr, "Status: 0x%x\n", status); fprintf(stderr, "Status: 0x%x\n", status);
if (op_flags & OPT_o) { if (op_flags & OPT_o) {
cout << output; cout << "data received: [" << output <<"]\n";
} }
exit (status >> 8); exit (status >> 8);
} }