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;
} }
SECURITY_ATTRIBUTES sa;
// Set up the security attributes struct.
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.bInheritHandle = TRUE; // this is the critical bit
sa.lpSecurityDescriptor = NULL;
if (has_output) { 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 // 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 // ONLY IMPORTANT CHANGE
// set inheritance flag to TRUE in CreateProcess // set inheritance flag to TRUE in CreateProcess
// you need this for the client to inherit the handles // you need this for the client to inherit the handles
SECURITY_ATTRIBUTES sa;
// Set up the security attributes struct.
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.bInheritHandle = TRUE; // this is the critical bit
sa.lpSecurityDescriptor = NULL;
// 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) {
printError("preparePipes: GetStdHandle(stdout)");
goto errout;
}
if (!DuplicateHandle(GetCurrentProcess(), hstd,
GetCurrentProcess(), &hOutputWrite,
0, TRUE,
DUPLICATE_SAME_ACCESS)) {
printError("preparePipes: DuplicateHandle(stdout)");
goto errout;
}
} }
// we're going to give the same pipe for stderr, so duplicate for that: // Have to take the give stderr output file instead
// Create a duplicate of the output write handle for the std error if (!stderrFile.empty()) {
// write handle. This is necessary in case the child application // Open the file set up the child handle: TBD
// closes one of its std output handles. } else {
if (!DuplicateHandle(GetCurrentProcess(), hOutputWrite, // Let the child inherit our standard input
GetCurrentProcess(), &hErrorWrite, HANDLE hstd = GetStdHandle(STD_ERROR_HANDLE);
0, TRUE, DUPLICATE_SAME_ACCESS)) { if (hstd == INVALID_HANDLE_VALUE) {
goto errout; printError("preparePipes: GetStdHandle(stderr)");
goto errout;
}
if (!DuplicateHandle(GetCurrentProcess(), hstd,
GetCurrentProcess(), &hErrorWrite,
0, TRUE,
DUPLICATE_SAME_ACCESS)) {
printError("preparePipes: DuplicateHandle(stderr)");
goto errout;
}
} }
#endif
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,41 +312,35 @@ 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;
} }
/** /**
This routine appends the given argument to a command line such This routine appends the given argument to a command line such
that CommandLineToArgvW will return the argument string unchanged. that CommandLineToArgvW will return the argument string unchanged.
Arguments in a command line should be separated by spaces; this Arguments in a command line should be separated by spaces; this
function does not add these spaces. The caller must append spaces function does not add these spaces. The caller must append spaces
between calls. between calls.
@param arg Supplies the argument to encode. @param arg Supplies the argument to encode.
@param cmdLine Supplies the command line to which we append @param cmdLine Supplies the command line to which we append
the encoded argument string. the encoded argument string.
@force Supplies an indication of whether we should quote @force Supplies an indication of whether we should quote
the argument even if it does not contain any characters that the argument even if it does not contain any characters that
would ordinarily require quoting. would ordinarily require quoting.
*/ */
static void argQuote(const string& arg, static void argQuote(const string& arg,
string& cmdLine, string& cmdLine,
@ -318,28 +396,27 @@ static string argvToCmdLine(const string& cmd, const vector<string>& args)
// Create a child process // Create a child process
int ExecCmd::startExec(const string &cmd, const vector<string>& args, int ExecCmd::startExec(const string &cmd, const vector<string>& args,
bool has_input, bool has_output) bool has_input, bool has_output)
{ {
{ // Debug and logging { // Debug and logging
string command = cmd + " "; string command = cmd + " ";
for (vector<string>::const_iterator it = args.begin(); for (vector<string>::const_iterator it = args.begin();
it != args.end(); it++) { it != args.end(); it++) {
command += "{" + *it + "} "; command += "{" + *it + "} ";
} }
LOGDEB(("ExecCmd::startExec: (%d|%d) %s\n", LOGDEB(("ExecCmd::startExec: (%d|%d) %s\n",
has_input, has_output, command.c_str())); has_input, has_output, command.c_str()));
} }
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,96 +500,116 @@ 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; }
return -1;
} else if (waitRes == Timeout) { } else if (waitRes == Timeout) {
if (!CancelIo(hInputWrite)) if (!CancelIo(m->hInputWrite)) {
ErrorExit("CancelIo"); printError("CancelIo");
return; }
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); WaitResult waitRes = Wait(m->oOutputRead.hEvent, 5000);
if (waitRes == Ok) { if (waitRes == Ok) {
if (!GetOverlappedResult(hOutputRead, &oOutputRead, &dwRead, TRUE)) { if (!GetOverlappedResult(m->hOutputRead, &m->oOutputRead, &dwRead, TRUE)) {
ErrorExit("GetOverlappedResult"); 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;
} }
int ExecCmd::doexec(const string &cmd, const vector<string>& args, int ExecCmd::doexec(const string &cmd, const vector<string>& args,
const string *input, string *output) const string *input, string *output)
{ {
if (input && output) { if (input && output) {
LOGERR(("ExecCmd::doexec: can't do both input and output\n")); LOGERR(("ExecCmd::doexec: can't do both input and output\n"));
return -1; return -1;
} }
if (startExec(cmd, args, input != 0, output != 0) < 0) { if (startExec(cmd, args, input != 0, output != 0) < 0) {
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>
@ -31,8 +32,8 @@ bool exercise_mhexecm(const string& cmdstr, const string& mimetype,
vector<string> myparams; vector<string> myparams;
if (cmd.startExec(cmdstr, myparams, 1, 1) < 0) { if (cmd.startExec(cmdstr, myparams, 1, 1) < 0) {
cerr << "startExec " << cmdstr << " failed. Missing command?\n"; cerr << "startExec " << cmdstr << " failed. Missing command?\n";
return false; return false;
} }
for (vector<string>::const_iterator it = files.begin(); for (vector<string>::const_iterator it = files.begin();
@ -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);
} }
@ -148,17 +149,17 @@ static int op_flags;
class MEAdv : public ExecCmdAdvise { class MEAdv : public ExecCmdAdvise {
public: public:
void newData(int cnt) { void newData(int cnt) {
if (op_flags & OPT_c) { if (op_flags & OPT_c) {
static int callcnt; static int callcnt;
if (callcnt++ == 10) { if (callcnt++ == 10) {
// Just sets the cancellation flag // Just sets the cancellation flag
CancelCheck::instance().setCancel(); CancelCheck::instance().setCancel();
// Would be called from somewhere else and throws an // Would be called from somewhere else and throws an
// exception. We call it here for simplicity // exception. We call it here for simplicity
CancelCheck::instance().checkCancel(); CancelCheck::instance().checkCancel();
} }
} }
cerr << "newData(" << cnt << ")" << endl; cerr << "newData(" << cnt << ")" << endl;
} }
}; };
@ -168,21 +169,21 @@ public:
FILE *m_fp; FILE *m_fp;
string *m_input; string *m_input;
MEPv(string *i) MEPv(string *i)
: m_input(i) : m_input(i)
{ {
m_fp = fopen("/etc/group", "r"); m_fp = fopen("/etc/group", "r");
} }
~MEPv() { ~MEPv() {
if (m_fp) if (m_fp)
fclose(m_fp); fclose(m_fp);
} }
void newData() { void newData() {
char line[1024]; char line[1024];
if (m_fp && fgets(line, 1024, m_fp)) { if (m_fp && fgets(line, 1024, m_fp)) {
m_input->assign((const char *)line); m_input->assign((const char *)line);
} else { } else {
m_input->erase(); m_input->erase();
} }
} }
}; };
@ -195,14 +196,14 @@ int main(int argc, char *argv[])
reexec.init(argc, argv); reexec.init(argc, argv);
if (0) { if (0) {
// Disabled: For testing reexec arg handling // Disabled: For testing reexec arg handling
vector<string> newargs; vector<string> newargs;
newargs.push_back("newarg"); newargs.push_back("newarg");
newargs.push_back("newarg1"); newargs.push_back("newarg1");
newargs.push_back("newarg2"); newargs.push_back("newarg2");
newargs.push_back("newarg3"); newargs.push_back("newarg3");
newargs.push_back("newarg4"); newargs.push_back("newarg4");
reexec.insertArgs(newargs, 2); reexec.insertArgs(newargs, 2);
} }
#endif #endif
@ -210,30 +211,31 @@ int main(int argc, char *argv[])
argc--; argv++; argc--; argv++;
while (argc > 0 && **argv == '-') { while (argc > 0 && **argv == '-') {
(*argv)++; (*argv)++;
if (!(**argv)) if (!(**argv))
/* Cas du "adb - core" */ /* Cas du "adb - core" */
Usage(); Usage();
while (**argv) while (**argv)
switch (*(*argv)++) { switch (*(*argv)++) {
case 'c': op_flags |= OPT_c; break; case 'c': op_flags |= OPT_c; break;
case 'r': op_flags |= OPT_r; break; case 'r': op_flags |= OPT_r; break;
case 'w': op_flags |= OPT_w; break; case 'w': op_flags |= OPT_w; break;
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;
default: Usage(); break; case'h': Usage(stdout);
} default: Usage(); break;
}
b1: argc--; argv++; b1: argc--; argv++;
} }
if (argc < 1) if (argc < 1)
Usage(); Usage();
string arg1 = *argv++; argc--; string arg1 = *argv++; argc--;
vector<string> l; vector<string> l;
while (argc > 0) { while (argc > 0) {
l.push_back(*argv++); argc--; l.push_back(*argv++); argc--;
} }
DebugLog::getdbl()->setloglevel(DEBDEB1); DebugLog::getdbl()->setloglevel(DEBDEB1);
@ -243,19 +245,19 @@ int main(int argc, char *argv[])
#endif #endif
if (op_flags & OPT_w) { if (op_flags & OPT_w) {
// Test "which" method // Test "which" method
string path; string path;
if (ExecCmd::which(arg1, path)) { if (ExecCmd::which(arg1, path)) {
cout << path << endl; cout << path << endl;
return 0; return 0;
} }
return 1; return 1;
} else if (op_flags & OPT_m) { } else if (op_flags & OPT_m) {
if (l.size() < 2) if (l.size() < 2)
Usage(); Usage();
string mimetype = l[0]; string mimetype = l[0];
l.erase(l.begin()); l.erase(l.begin());
return exercise_mhexecm(arg1, mimetype, l) ? 0 : 1; return exercise_mhexecm(arg1, mimetype, l) ? 0 : 1;
} else { } else {
// Default: execute command line arguments // Default: execute command line arguments
ExecCmd mexec; ExecCmd mexec;
@ -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);
} }