Changed: New functions launchProgramArray to pass an array of strings as arguments (easier to escape)

This commit is contained in:
kervala 2016-02-27 18:38:58 +01:00
parent bc2c147722
commit 1704030c38
3 changed files with 121 additions and 8 deletions

View file

@ -366,6 +366,9 @@ std::string formatThousands(const std::string& s);
/// The program will be launched in the current directory
bool launchProgram (const std::string &programName, const std::string &arguments, bool log = true);
/// Same but with an array of strings for arguments
bool launchProgramArray (const std::string &programName, const std::vector<std::string> &arguments, bool log = true);
/// This function executes a program and wait for result (used for example for crash report).
/// The program will be launched in the current directory
sint launchProgramAndWaitForResult (const std::string &programName, const std::string &arguments, bool log = true);

View file

@ -831,6 +831,7 @@ bool launchProgram(const std::string &programName, const std::string &arguments,
#endif
static bool firstLaunchProgram = true;
if (firstLaunchProgram)
{
// The aim of this is to avoid defunct process.
@ -896,6 +897,110 @@ bool launchProgram(const std::string &programName, const std::string &arguments,
return false;
}
bool launchProgramArray (const std::string &programName, const std::vector<std::string> &arguments, bool log)
{
#ifdef NL_OS_WINDOWS
PROCESS_INFORMATION pi;
std::string argumentsJoined = joinArguments(arguments);
if (!createProcess(programName, argumentsJoined, log, pi)) return false;
//nldebug("LAUNCH: Successful launch '%s' with arg '%s'", programName.c_str(), arguments.c_str());
CloseHandle( pi.hProcess );
CloseHandle( pi.hThread );
return true;
#else
#ifdef NL_OS_MAC
// special OS X case with bundles
if (toLower(programName).find(".app") != std::string::npos)
{
// we need to open bundles with "open" command
std::string command = NLMISC::toString("open \"%s\"", programName.c_str());
// append arguments if any
if (!arguments.empty())
{
command += NLMISC::toString(" --args %s", joinArguments(arguments).c_str());
}
int res = system(command.c_str());
if (!res) return true;
if (log)
{
nlwarning ("LAUNCH: Failed launched '%s' with arg '%s' return code %d", programName.c_str(), arguments.c_str(), res);
}
return false;
}
#endif
static bool firstLaunchProgram = true;
if (firstLaunchProgram)
{
// The aim of this is to avoid defunct process.
//
// From "man signal":
//------
// According to POSIX (3.3.1.3) it is unspecified what happens when SIGCHLD is set to SIG_IGN. Here
// the BSD and SYSV behaviours differ, causing BSD software that sets the action for SIGCHLD to
// SIG_IGN to fail on Linux.
//------
//
// But it works fine on my GNU/Linux so I do this because it's easier :) and I don't know exactly
// what to do to be portable.
signal(SIGCHLD, SIG_IGN);
firstLaunchProgram = false;
}
// Store the size of each arg
vector<char *> argv(arguments.size()+2);
uint i = 0;
argv[i] = (char *)programName.c_str();
for (; i < arguments.size(); i++)
{
argv[i+1] = (char *) arguments[i].c_str();
}
argv[i+1] = NULL;
int status = vfork ();
/////////////////////////////////////////////////////////
/// WARNING : NO MORE INSTRUCTION AFTER VFORK !
/// READ VFORK manual
/////////////////////////////////////////////////////////
if (status == -1)
{
char *err = strerror (errno);
if (log)
nlwarning("LAUNCH: Failed launched '%s' with arg '%s' err %d: '%s'", programName.c_str(), arguments.c_str(), errno, err);
}
else if (status == 0)
{
// Exec (the only allowed instruction after vfork)
status = execvp(programName.c_str(), &argv.front());
if (status == -1)
{
perror("Failed launched");
_exit(EXIT_FAILURE);
}
}
else
{
//nldebug("LAUNCH: Successful launch '%s' with arg '%s'", programName.c_str(), arguments.c_str());
return true;
}
#endif
return false;
}
sint launchProgramAndWaitForResult(const std::string &programName, const std::string &arguments, bool log)
{
#ifdef NL_OS_WINDOWS

View file

@ -1022,32 +1022,37 @@ void CPatchManager::executeBatchFile()
// make script executable
CFile::setRWAccess(batchFilename);
std::string arguments;
std::vector<std::string> arguments;
// 3 first parameters are Ryzom client full path, patch directory full path and client root directory full path
#ifdef NL_OS_WINDOWS
arguments += "\"" + CPath::standardizeDosPath(RyzomFilename) + "\" \"" + CPath::standardizeDosPath(ClientPatchPath) + "\" \"" + CPath::standardizeDosPath(ClientRootPath) + "\"";
arguments.push_back(CPath::standardizeDosPath(RyzomFilename));
arguments.push_back(CPath::standardizeDosPath(ClientPatchPath));
arguments.push_back(CPath::standardizeDosPath(ClientRootPath));
#else
arguments += "\"" + RyzomFilename + "\" \"" + ClientPatchPath + "\" " + ClientRootPath + "\"";
arguments.push_back(RyzomFilename);
arguments.push_back(ClientPatchPath);
arguments.push_back(ClientRootPath);
#endif
// append login, password and shard
if (!LoginLogin.empty())
{
arguments += " " + LoginLogin;
arguments.push_back(LoginLogin);
if (!LoginPassword.empty())
{
arguments += " " + LoginPassword;
arguments.push_back(LoginPassword);
if (!r2Mode)
{
arguments += " " + toString(LoginShardId);
arguments.push_back(toString(LoginShardId));
}
}
}
if (!launchProgram(batchFilename, arguments, false))
// launchProgram with array of strings as argument will escape arguments with spaces
if (!launchProgramArray(batchFilename, arguments, false))
{
// error occurs during the launch
string str = toString("Can't execute '%s': code=%d %s (error code 30)", batchFilename.c_str(), errno, strerror(errno));