PageRenderTime 127ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 0ms

/indra/llcommon/llprocesslauncher.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 357 lines | 233 code | 64 blank | 60 comment | 41 complexity | b42c52ce55cd54cd6bf87476de6959f8 MD5 | raw file
Possible License(s): LGPL-2.1
  1. /**
  2. * @file llprocesslauncher.cpp
  3. * @brief Utility class for launching, terminating, and tracking the state of processes.
  4. *
  5. * $LicenseInfo:firstyear=2008&license=viewerlgpl$
  6. * Second Life Viewer Source Code
  7. * Copyright (C) 2010, Linden Research, Inc.
  8. *
  9. * This library is free software; you can redistribute it and/or
  10. * modify it under the terms of the GNU Lesser General Public
  11. * License as published by the Free Software Foundation;
  12. * version 2.1 of the License only.
  13. *
  14. * This library is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  17. * Lesser General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU Lesser General Public
  20. * License along with this library; if not, write to the Free Software
  21. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  22. *
  23. * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
  24. * $/LicenseInfo$
  25. */
  26. #include "linden_common.h"
  27. #include "llprocesslauncher.h"
  28. #include <iostream>
  29. #if LL_DARWIN || LL_LINUX
  30. // not required or present on Win32
  31. #include <sys/wait.h>
  32. #endif
  33. LLProcessLauncher::LLProcessLauncher()
  34. {
  35. #if LL_WINDOWS
  36. mProcessHandle = 0;
  37. #else
  38. mProcessID = 0;
  39. #endif
  40. }
  41. LLProcessLauncher::~LLProcessLauncher()
  42. {
  43. kill();
  44. }
  45. void LLProcessLauncher::setExecutable(const std::string &executable)
  46. {
  47. mExecutable = executable;
  48. }
  49. void LLProcessLauncher::setWorkingDirectory(const std::string &dir)
  50. {
  51. mWorkingDir = dir;
  52. }
  53. const std::string& LLProcessLauncher::getExecutable() const
  54. {
  55. return mExecutable;
  56. }
  57. void LLProcessLauncher::clearArguments()
  58. {
  59. mLaunchArguments.clear();
  60. }
  61. void LLProcessLauncher::addArgument(const std::string &arg)
  62. {
  63. mLaunchArguments.push_back(arg);
  64. }
  65. void LLProcessLauncher::addArgument(const char *arg)
  66. {
  67. mLaunchArguments.push_back(std::string(arg));
  68. }
  69. #if LL_WINDOWS
  70. int LLProcessLauncher::launch(void)
  71. {
  72. // If there was already a process associated with this object, kill it.
  73. kill();
  74. orphan();
  75. int result = 0;
  76. PROCESS_INFORMATION pinfo;
  77. STARTUPINFOA sinfo;
  78. memset(&sinfo, 0, sizeof(sinfo));
  79. std::string args = mExecutable;
  80. for(int i = 0; i < (int)mLaunchArguments.size(); i++)
  81. {
  82. args += " ";
  83. args += mLaunchArguments[i];
  84. }
  85. // So retarded. Windows requires that the second parameter to CreateProcessA be a writable (non-const) string...
  86. char *args2 = new char[args.size() + 1];
  87. strcpy(args2, args.c_str());
  88. const char * working_directory = 0;
  89. if(!mWorkingDir.empty()) working_directory = mWorkingDir.c_str();
  90. if( ! CreateProcessA( NULL, args2, NULL, NULL, FALSE, 0, NULL, working_directory, &sinfo, &pinfo ) )
  91. {
  92. result = GetLastError();
  93. LPTSTR error_str = 0;
  94. if(
  95. FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
  96. NULL,
  97. result,
  98. 0,
  99. (LPTSTR)&error_str,
  100. 0,
  101. NULL)
  102. != 0)
  103. {
  104. char message[256];
  105. wcstombs(message, error_str, 256);
  106. message[255] = 0;
  107. llwarns << "CreateProcessA failed: " << message << llendl;
  108. LocalFree(error_str);
  109. }
  110. if(result == 0)
  111. {
  112. // Make absolutely certain we return a non-zero value on failure.
  113. result = -1;
  114. }
  115. }
  116. else
  117. {
  118. // foo = pinfo.dwProcessId; // get your pid here if you want to use it later on
  119. // CloseHandle(pinfo.hProcess); // stops leaks - nothing else
  120. mProcessHandle = pinfo.hProcess;
  121. CloseHandle(pinfo.hThread); // stops leaks - nothing else
  122. }
  123. delete[] args2;
  124. return result;
  125. }
  126. bool LLProcessLauncher::isRunning(void)
  127. {
  128. if(mProcessHandle != 0)
  129. {
  130. DWORD waitresult = WaitForSingleObject(mProcessHandle, 0);
  131. if(waitresult == WAIT_OBJECT_0)
  132. {
  133. // the process has completed.
  134. mProcessHandle = 0;
  135. }
  136. }
  137. return (mProcessHandle != 0);
  138. }
  139. bool LLProcessLauncher::kill(void)
  140. {
  141. bool result = true;
  142. if(mProcessHandle != 0)
  143. {
  144. TerminateProcess(mProcessHandle,0);
  145. if(isRunning())
  146. {
  147. result = false;
  148. }
  149. }
  150. return result;
  151. }
  152. void LLProcessLauncher::orphan(void)
  153. {
  154. // Forget about the process
  155. mProcessHandle = 0;
  156. }
  157. // static
  158. void LLProcessLauncher::reap(void)
  159. {
  160. // No actions necessary on Windows.
  161. }
  162. #else // Mac and linux
  163. #include <signal.h>
  164. #include <fcntl.h>
  165. #include <errno.h>
  166. static std::list<pid_t> sZombies;
  167. // Attempt to reap a process ID -- returns true if the process has exited and been reaped, false otherwise.
  168. static bool reap_pid(pid_t pid)
  169. {
  170. bool result = false;
  171. pid_t wait_result = ::waitpid(pid, NULL, WNOHANG);
  172. if(wait_result == pid)
  173. {
  174. result = true;
  175. }
  176. else if(wait_result == -1)
  177. {
  178. if(errno == ECHILD)
  179. {
  180. // No such process -- this may mean we're ignoring SIGCHILD.
  181. result = true;
  182. }
  183. }
  184. return result;
  185. }
  186. int LLProcessLauncher::launch(void)
  187. {
  188. // If there was already a process associated with this object, kill it.
  189. kill();
  190. orphan();
  191. int result = 0;
  192. int current_wd = -1;
  193. // create an argv vector for the child process
  194. const char ** fake_argv = new const char *[mLaunchArguments.size() + 2]; // 1 for the executable path, 1 for the NULL terminator
  195. int i = 0;
  196. // add the executable path
  197. fake_argv[i++] = mExecutable.c_str();
  198. // and any arguments
  199. for(int j=0; j < mLaunchArguments.size(); j++)
  200. fake_argv[i++] = mLaunchArguments[j].c_str();
  201. // terminate with a null pointer
  202. fake_argv[i] = NULL;
  203. if(!mWorkingDir.empty())
  204. {
  205. // save the current working directory
  206. current_wd = ::open(".", O_RDONLY);
  207. // and change to the one the child will be executed in
  208. if (::chdir(mWorkingDir.c_str()))
  209. {
  210. // chdir failed
  211. }
  212. }
  213. // flush all buffers before the child inherits them
  214. ::fflush(NULL);
  215. pid_t id = vfork();
  216. if(id == 0)
  217. {
  218. // child process
  219. ::execv(mExecutable.c_str(), (char * const *)fake_argv);
  220. // If we reach this point, the exec failed.
  221. // Use _exit() instead of exit() per the vfork man page.
  222. _exit(0);
  223. }
  224. // parent process
  225. if(current_wd >= 0)
  226. {
  227. // restore the previous working directory
  228. if (::fchdir(current_wd))
  229. {
  230. // chdir failed
  231. }
  232. ::close(current_wd);
  233. }
  234. delete[] fake_argv;
  235. mProcessID = id;
  236. return result;
  237. }
  238. bool LLProcessLauncher::isRunning(void)
  239. {
  240. if(mProcessID != 0)
  241. {
  242. // Check whether the process has exited, and reap it if it has.
  243. if(reap_pid(mProcessID))
  244. {
  245. // the process has exited.
  246. mProcessID = 0;
  247. }
  248. }
  249. return (mProcessID != 0);
  250. }
  251. bool LLProcessLauncher::kill(void)
  252. {
  253. bool result = true;
  254. if(mProcessID != 0)
  255. {
  256. // Try to kill the process. We'll do approximately the same thing whether the kill returns an error or not, so we ignore the result.
  257. (void)::kill(mProcessID, SIGTERM);
  258. // This will have the side-effect of reaping the zombie if the process has exited.
  259. if(isRunning())
  260. {
  261. result = false;
  262. }
  263. }
  264. return result;
  265. }
  266. void LLProcessLauncher::orphan(void)
  267. {
  268. // Disassociate the process from this object
  269. if(mProcessID != 0)
  270. {
  271. // We may still need to reap the process's zombie eventually
  272. sZombies.push_back(mProcessID);
  273. mProcessID = 0;
  274. }
  275. }
  276. // static
  277. void LLProcessLauncher::reap(void)
  278. {
  279. // Attempt to real all saved process ID's.
  280. std::list<pid_t>::iterator iter = sZombies.begin();
  281. while(iter != sZombies.end())
  282. {
  283. if(reap_pid(*iter))
  284. {
  285. iter = sZombies.erase(iter);
  286. }
  287. else
  288. {
  289. iter++;
  290. }
  291. }
  292. }
  293. #endif