diff --git a/code/nel/include/nel/misc/common.h b/code/nel/include/nel/misc/common.h index a654b703e..4ddc88076 100644 --- a/code/nel/include/nel/misc/common.h +++ b/code/nel/include/nel/misc/common.h @@ -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 &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); diff --git a/code/nel/src/misc/common.cpp b/code/nel/src/misc/common.cpp index 1050db638..424a1de20 100644 --- a/code/nel/src/misc/common.cpp +++ b/code/nel/src/misc/common.cpp @@ -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 &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 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 diff --git a/code/ryzom/client/src/login_patch.cpp b/code/ryzom/client/src/login_patch.cpp index c4a11a22a..8208d6e28 100644 --- a/code/ryzom/client/src/login_patch.cpp +++ b/code/ryzom/client/src/login_patch.cpp @@ -1022,32 +1022,37 @@ void CPatchManager::executeBatchFile() // make script executable CFile::setRWAccess(batchFilename); - std::string arguments; + std::vector 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 + // 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));