Windows execmd: use a wide char environment

This commit is contained in:
Jean-Francois Dockes 2020-06-02 08:53:11 +01:00
parent 84c0098f23
commit 7ccde15839

View File

@ -32,7 +32,6 @@
#include <psapi.h>
#include "smallut.h"
#include "pathut.h"
#include "transcode.h"
using namespace std;
@ -42,7 +41,7 @@ using namespace std;
static void printError(const string& text)
{
DWORD err = GetLastError();
LOGERR(text << " : err: " << err << "\n");
LOGERR(text << " : err: " << err << "\n");
}
/**
@ -108,37 +107,30 @@ static string argvToCmdLine(const string& cmd, const vector<string>& args)
return cmdline;
}
// Because we build with UNICODE defined, GetEnvironmentStrings is
// defined as GetEnvironmentStringsW. Because of a Windows problem teh
// GetEnvironmentStrings function is really a GetEnvironmentStringsA,
// which is what we want.
// See: https://devblogs.microsoft.com/oldnewthing/20130117-00/?p=5533
#undef GetEnvironmentStrings
// Merge the father environment with the variable specified in m_env
static char *mergeEnvironment(const std::unordered_map<string, string>& addenv)
static wchar_t *mergeEnvironment(
const std::unordered_map<string, string>& addenv)
{
// Parse existing environment.
char *envir = GetEnvironmentStrings();
char *cp0 = envir;
std::unordered_map<string, string> envirmap;
// Retrieve existing environment.
wchar_t *wenvir = GetEnvironmentStringsW();
const wchar_t *cp0 = wenvir;
std::unordered_map<string, string> envirmap;
string name, value;
for (char *cp1 = cp0;;cp1++) {
if (*cp1 == '=') {
name = string(cp0, cp1 - cp0);
for (const wchar_t *cp1 = cp0;;cp1++) {
if (*cp1 == L'=') {
wchartoutf8(cp0, name, cp1 - cp0);
cp0 = cp1 + 1;
} else if (*cp1 == 0) {
value = string(cp0, cp1 - cp0);
wchartoutf8(cp0, value, cp1 - cp0);
envirmap[name] = value;
LOGDEB1("mergeEnvir: [" << (name) << "] = [" << (value) << "]\n" );
LOGDEB1("mergeEnvir: [" << name << "] = [" << value << "]\n" );
cp0 = cp1 + 1;
if (*cp0 == 0)
break;
}
}
FreeEnvironmentStringsA(envir);
FreeEnvironmentStringsW(wenvir);
// Merge our values
for (auto it = addenv.begin(); it != addenv.end(); it++) {
@ -146,21 +138,27 @@ static char *mergeEnvironment(const std::unordered_map<string, string>& addenv)
}
// Create environment block
// Size computation. We could do an exact computation by
// converting the strings, but do worst case instead. one utf-8
// byte can't convert to more than one wchar
size_t sz = 0;
for (auto it = envirmap.begin(); it != envirmap.end(); it++) {
sz += it->first.size() + it->second.size() + 2; // =, 0
// the +2 is for '=' and '\0'
sz += sizeof(wchar_t) * (it->first.size() + it->second.size() + 2);
}
sz++; // final 0
char *nenvir = (char *)malloc(sz);
if (nenvir == 0)
sz += sizeof(wchar_t); // final 0
wchar_t *nenvir = (wchar_t *)malloc(sz+2);
if (nullptr == nenvir)
return nenvir;
char *cp = nenvir;
for (auto it = envirmap.begin(); it != envirmap.end(); it++) {
memcpy(cp, it->first.c_str(), it->first.size());
cp += it->first.size();
*cp++ = '=';
memcpy(cp, it->second.c_str(), it->second.size());
cp += it->second.size();
wchar_t *cp = nenvir;
for (const auto& entry : envirmap) {
utf8towchar(entry.first, cp, sz);
cp += wcslen(cp);
*cp++ = L'=';
utf8towchar(entry.second, cp, sz);
cp += wcslen(cp);
*cp++ = 0;
}
// Final double-zero
@ -267,8 +265,10 @@ static int getVMMBytes(HANDLE hProcess)
PROCESS_MEMORY_COUNTERS pmc;
const int MB = 1024 * 1024;
if (GetProcessMemoryInfo(hProcess, &pmc, sizeof(pmc))) {
LOGDEB2("ExecCmd: getVMMBytes paged Kbs " << (int(pmc.QuotaPagedPoolUsage/1024)) << " non paged " << (int(pmc.QuotaNonPagedPoolUsage/1024)) << " Kbs\n" );
return int(pmc.QuotaPagedPoolUsage /MB +
LOGDEB2("ExecCmd: getVMMBytes paged Kbs " <<
pmc.QuotaPagedPoolUsage/1024 << " non paged " <<
pmc.QuotaNonPagedPoolUsage/1024 << " Kbs\n");
return int(pmc.QuotaPagedPoolUsage / MB +
pmc.QuotaNonPagedPoolUsage / MB);
}
return -1;
@ -335,7 +335,7 @@ static bool sendIntr(int pid)
LOGDEB("execmd_w: sendIntr attaching console\n" );
if (!AttachConsole((unsigned int) pid)) {
int err = GetLastError();
LOGERR("execmd_w: sendIntr: AttachConsole failed: " << (err) << "\n" );
LOGERR("execmd_w: sendIntr: AttachConsole failed: " << err << "\n");
return false;
}
}
@ -345,7 +345,8 @@ static bool sendIntr(int pid)
// Disable Ctrl-C handling for our program
if (!SetConsoleCtrlHandler(NULL, true)) {
int err = GetLastError();
LOGERR("execmd_w:sendIntr:SetCons.Ctl.Hndlr.(NULL, true) failed: " << (err) << "\n" );
LOGERR("execmd_w:sendIntr:SetCons.Ctl.Hndlr.(NULL, true) failed: " <<
err << "\n");
return false;
}
#endif
@ -355,7 +356,7 @@ static bool sendIntr(int pid)
bool ret = GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, pid);
if (!ret) {
int err = GetLastError();
LOGERR("execmd_w:sendIntr:Gen.Cons.CtrlEvent failed: " << (err) << "\n" );
LOGERR("execmd_w:sendIntr:Gen.Cons.CtrlEvent failed: " << err << "\n");
}
#if 0
@ -367,7 +368,7 @@ static bool sendIntr(int pid)
LOGDEB("execmd_w: sendIntr detaching console\n" );
if (!FreeConsole()) {
int err = GetLastError();
LOGERR("execmd_w: sendIntr: FreeConsole failed: " << (err) << "\n" );
LOGERR("execmd_w: sendIntr: FreeConsole failed: " << err << "\n");
}
}
@ -388,7 +389,7 @@ public:
~ExecCmdRsrc() {
if (!m_active || !m_parent)
return;
LOGDEB1("~ExecCmdRsrc: working. mypid: " << ((int)getpid()) << "\n" );
LOGDEB1("~ExecCmdRsrc: working. mypid: " << ((int)getpid()) << "\n" );
if (m_parent->m_hOutputRead)
CloseHandle(m_parent->m_hOutputRead);
if (m_parent->m_hInputWrite)
@ -556,7 +557,8 @@ bool ExecCmd::Internal::tooBig()
return false;
int mbytes = getVMMBytes(m_piProcInfo.hProcess);
if (mbytes > m_rlimit_as_mbytes) {
LOGINFO("ExecCmd:: process mbytes " << (mbytes) << " > set limit " << (m_rlimit_as_mbytes) << "\n" );
LOGINFO("ExecCmd:: process mbytes " << mbytes << " > set limit " <<
m_rlimit_as_mbytes << "\n");
m_killRequest = true;
return true;
}
@ -738,7 +740,8 @@ int ExecCmd::startExec(const string &cmd, const vector<string>& args,
it != args.end(); it++) {
command += "{" + *it + "} ";
}
LOGDEB("ExecCmd::startExec: (" << (has_input) << "|" << (has_output) << ") " << (command) << "\n" );
LOGDEB("ExecCmd::startExec: (" << has_input << "|" << has_output <<
") " << command << "\n");
}
// What if we're called twice ? First make sure we're clean
@ -756,7 +759,7 @@ int ExecCmd::startExec(const string &cmd, const vector<string>& args,
HANDLE hErrorWrite;
if (!m->preparePipes(has_input, &hInputRead, has_output,
&hOutputWrite, &hErrorWrite)) {
LOGERR("ExecCmd::startExec: preparePipes failed\n" );
LOGERR("ExecCmd::startExec: preparePipes failed\n");
return false;
}
@ -786,21 +789,21 @@ int ExecCmd::startExec(const string &cmd, const vector<string>& args,
siStartInfo.hStdInput = hInputRead;
siStartInfo.hStdError = hErrorWrite;
char *envir = mergeEnvironment(m->m_env);
wchar_t *envir = mergeEnvironment(m->m_env);
int flags = CREATE_NEW_PROCESS_GROUP | CREATE_UNICODE_ENVIRONMENT;
// Create the child process.
LOGDEB("ExecCmd:startExec: cmdline [" << cmdline << "]\n");
SYSPATH(cmdline, wcmdline);
bSuccess = CreateProcessW(NULL,
wcmdline, // command line
NULL, // process security attributes
NULL, // primary thread security attrs
TRUE, // handles are inherited
CREATE_NEW_PROCESS_GROUP, // creation flags
envir, // Merged environment
NULL, // use parent's current directory
&siStartInfo, // STARTUPINFO pointer
&m->m_piProcInfo); // PROCESS_INFORMATION
bSuccess = CreateProcessW(NULL, // app name
wcmdline, // command line
NULL, // process security attributes
NULL, // primary thread security attrs
TRUE, // handles are inherited
flags, // creation flags
envir, // Merged environment
NULL, // use parent's current directory
&siStartInfo, // STARTUPINFO pointer
&m->m_piProcInfo); // PROCESS_INFORMATION
if (!bSuccess) {
printError("ExecCmd::doexec: CreateProcess");
}
@ -823,7 +826,7 @@ int ExecCmd::startExec(const string &cmd, const vector<string>& args,
// Send data to the child.
int ExecCmd::send(const string& data)
{
LOGDEB2("ExecCmd::send: cnt " << (int(data.size())) << "\n" );
LOGDEB2("ExecCmd::send: cnt " << data.size() << "\n");
BOOL bSuccess = WriteFile(m->m_hInputWrite, data.c_str(),
(DWORD)data.size(), NULL, &m->m_oInputWrite);
DWORD err = GetLastError();
@ -833,7 +836,7 @@ int ExecCmd::send(const string& data)
// and ERROR_IO_PENDING
// in the first case bytes read/written parameter can be used directly
if (!bSuccess && err != ERROR_IO_PENDING) {
LOGERR("ExecCmd::send: WriteFile: got err " << (err) << "\n" );
LOGERR("ExecCmd::send: WriteFile: got err " << err << "\n");
return -1;
}
@ -843,7 +846,7 @@ int ExecCmd::send(const string& data)
if (!GetOverlappedResult(m->m_hInputWrite,
&m->m_oInputWrite, &dwWritten, TRUE)) {
err = GetLastError();
LOGERR("ExecCmd::send: GetOverLappedResult: err " << (err) << "\n" );
LOGERR("ExecCmd::send: GetOverLappedResult: err " << err << "\n");
return -1;
}
} else if (waitRes == Quit) {
@ -859,7 +862,7 @@ int ExecCmd::send(const string& data)
}
return -1;
}
LOGDEB2("ExecCmd::send: returning " << (int(dwWritten)) << "\n" );
LOGDEB2("ExecCmd::send: returning " << (int(dwWritten)) << "\n");
return dwWritten;
}
@ -874,7 +877,7 @@ int ExecCmd::send(const string& data)
// 0 means read whatever comes back on the first read;
int ExecCmd::receive(string& data, int cnt)
{
LOGDEB1("ExecCmd::receive: cnt " << (cnt) << "\n" );
LOGDEB1("ExecCmd::receive: cnt " << cnt << "\n");
int totread = 0;
@ -896,10 +899,10 @@ int ExecCmd::receive(string& data, int cnt)
BOOL bSuccess = ReadFile(m->m_hOutputRead, chBuf, toread,
NULL, &m->m_oOutputRead);
DWORD err = GetLastError();
LOGDEB1("receive: ReadFile: success " << (int(bSuccess)) << " err " << (int(err)) << "\n" );
LOGDEB1("receive: ReadFile: success " <<bSuccess<<" err "<< err << "\n");
if (!bSuccess && err != ERROR_IO_PENDING) {
if (err != ERROR_BROKEN_PIPE)
LOGERR("ExecCmd::receive: ReadFile error: " << (int(err)) << "\n" );
LOGERR("ExecCmd::receive: ReadFile error: " << err << "\n");
break;
}
@ -911,7 +914,8 @@ int ExecCmd::receive(string& data, int cnt)
&dwRead, TRUE)) {
err = GetLastError();
if (err && err != ERROR_BROKEN_PIPE) {
LOGERR("ExecCmd::recv:GetOverlappedResult: err " << (err) << "\n" );
LOGERR("ExecCmd::recv:GetOverlappedResult: err " << err <<
"\n");
return -1;
}
}
@ -920,7 +924,7 @@ int ExecCmd::receive(string& data, int cnt)
data.append(chBuf, dwRead);
if (m->m_advise)
m->m_advise->newData(dwRead);
LOGDEB1("ExecCmd::recv: ReadFile: " << (int(dwRead)) << " bytes\n" );
LOGDEB1("ExecCmd::recv: ReadFile: " << dwRead << " bytes\n");
}
} else if (waitRes == Quit) {
if (!CancelIo(m->m_hOutputRead)) {
@ -928,7 +932,7 @@ int ExecCmd::receive(string& data, int cnt)
}
break;
} else if (waitRes == Timeout) {
LOGDEB0("ExecCmd::receive: timeout (" << (m->m_timeoutMs) << " mS)\n" );
LOGDEB0("ExecCmd::receive: timeout (" << m->m_timeoutMs << " mS)\n");
if (m->tooBig()) {
if (!CancelIo(m->m_hOutputRead)) {
printError("CancelIo");
@ -947,7 +951,7 @@ int ExecCmd::receive(string& data, int cnt)
}
}
if (m->m_killRequest) {
LOGINFO("ExecCmd::doexec: cancel request\n" );
LOGINFO("ExecCmd::doexec: cancel request\n");
if (!CancelIo(m->m_hOutputRead)) {
printError("CancelIo");
}
@ -958,13 +962,13 @@ int ExecCmd::receive(string& data, int cnt)
if ((cnt == 0 && totread > 0) || (cnt > 0 && totread == cnt))
break;
}
LOGDEB1("ExecCmd::receive: returning " << (totread) << " bytes\n" );
LOGDEB1("ExecCmd::receive: returning " << totread << " bytes\n");
return totread;
}
int ExecCmd::getline(string& data)
{
LOGDEB2("ExecCmd::getline: cnt " << (cnt) << ", timeo " << (timeo) << "\n" );
LOGDEB2("ExecCmd::getline: cnt " << cnt << ", timeo " << timeo << "\n");
data.erase();
if (m->m_buf.empty()) {
m->m_buf.reserve(4096);
@ -989,7 +993,7 @@ int ExecCmd::getline(string& data)
}
if (foundnl) {
LOGDEB2("ExecCmd::getline: ret: [" << (data) << "]\n" );
LOGDEB2("ExecCmd::getline: ret: [" << data << "]\n");
return int(data.size());
}
@ -999,7 +1003,7 @@ int ExecCmd::getline(string& data)
return -1;
}
if (m->m_buf.empty()) {
LOGDEB("ExecCmd::getline: eof? ret: [" << (data) << "]\n" );
LOGDEB("ExecCmd::getline: eof? ret: [" << data << "]\n");
return int(data.size());
}
m->m_bufoffs = 0;
@ -1019,7 +1023,7 @@ int ExecCmd::wait()
// Wait until child process exits.
while (WaitForSingleObject(m->m_piProcInfo.hProcess, m->m_timeoutMs)
== WAIT_TIMEOUT) {
LOGDEB("ExecCmd::wait: timeout (ok)\n" );
LOGDEB("ExecCmd::wait: timeout (ok)\n");
if (m->m_advise) {
m->m_advise->newData(0);
}
@ -1089,7 +1093,7 @@ int ExecCmd::doexec(const string &cmd, const vector<string>& args,
const string *input, string *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;
}
@ -1105,7 +1109,7 @@ int ExecCmd::doexec(const string &cmd, const vector<string>& args,
if (input) {
if (!input->empty()) {
if (send(*input) != (int)input->size()) {
LOGERR("ExecCmd::doexec: send failed\n" );
LOGERR("ExecCmd::doexec: send failed\n");
CloseHandle(m->m_hInputWrite);
m->m_hInputWrite = NULL;
return -1;
@ -1120,7 +1124,7 @@ int ExecCmd::doexec(const string &cmd, const vector<string>& args,
break;
}
if (send(*input) != (int)input->size()) {
LOGERR("ExecCmd::doexec: send failed\n" );
LOGERR("ExecCmd::doexec: send failed\n");
CloseHandle(m->m_hInputWrite);
m->m_hInputWrite = NULL;
break;
@ -1133,4 +1137,3 @@ int ExecCmd::doexec(const string &cmd, const vector<string>& args,
cleaner.inactivate();
return wait();
}