llprocesslauncher.cpp
上传用户:king477883
上传日期:2021-03-01
资源大小:9553k
文件大小:7k
源码类别:

游戏引擎

开发平台:

C++ Builder

  1. /** 
  2.  * @file llprocesslauncher.cpp
  3.  * @brief Utility class for launching, terminating, and tracking the state of processes.
  4.  *
  5.  * $LicenseInfo:firstyear=2008&license=viewergpl$
  6.  * 
  7.  * Copyright (c) 2008-2010, Linden Research, Inc.
  8.  * 
  9.  * Second Life Viewer Source Code
  10.  * The source code in this file ("Source Code") is provided by Linden Lab
  11.  * to you under the terms of the GNU General Public License, version 2.0
  12.  * ("GPL"), unless you have obtained a separate licensing agreement
  13.  * ("Other License"), formally executed by you and Linden Lab.  Terms of
  14.  * the GPL can be found in doc/GPL-license.txt in this distribution, or
  15.  * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
  16.  * 
  17.  * There are special exceptions to the terms and conditions of the GPL as
  18.  * it is applied to this Source Code. View the full text of the exception
  19.  * in the file doc/FLOSS-exception.txt in this software distribution, or
  20.  * online at
  21.  * http://secondlifegrid.net/programs/open_source/licensing/flossexception
  22.  * 
  23.  * By copying, modifying or distributing this software, you acknowledge
  24.  * that you have read and understood your obligations described above,
  25.  * and agree to abide by those obligations.
  26.  * 
  27.  * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
  28.  * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
  29.  * COMPLETENESS OR PERFORMANCE.
  30.  * $/LicenseInfo$
  31.  */
  32.  
  33. #include "linden_common.h"
  34. #include "llprocesslauncher.h"
  35. #include <iostream>
  36. #if LL_DARWIN || LL_LINUX
  37. // not required or present on Win32
  38. #include <sys/wait.h>
  39. #endif
  40. LLProcessLauncher::LLProcessLauncher()
  41. {
  42. #if LL_WINDOWS
  43. mProcessHandle = 0;
  44. #else
  45. mProcessID = 0;
  46. #endif
  47. }
  48. LLProcessLauncher::~LLProcessLauncher()
  49. {
  50. kill();
  51. }
  52. void LLProcessLauncher::setExecutable(const std::string &executable)
  53. {
  54. mExecutable = executable;
  55. }
  56. void LLProcessLauncher::setWorkingDirectory(const std::string &dir)
  57. {
  58. mWorkingDir = dir;
  59. }
  60. void LLProcessLauncher::clearArguments()
  61. {
  62. mLaunchArguments.clear();
  63. }
  64. void LLProcessLauncher::addArgument(const std::string &arg)
  65. {
  66. mLaunchArguments.push_back(arg);
  67. }
  68. void LLProcessLauncher::addArgument(const char *arg)
  69. {
  70. mLaunchArguments.push_back(std::string(arg));
  71. }
  72. #if LL_WINDOWS
  73. int LLProcessLauncher::launch(void)
  74. {
  75. // If there was already a process associated with this object, kill it.
  76. kill();
  77. orphan();
  78. int result = 0;
  79. PROCESS_INFORMATION pinfo;
  80. STARTUPINFOA sinfo;
  81. memset(&sinfo, 0, sizeof(sinfo));
  82. std::string args = mExecutable;
  83. for(int i = 0; i < (int)mLaunchArguments.size(); i++)
  84. {
  85. args += " ";
  86. args += mLaunchArguments[i];
  87. }
  88. // So retarded.  Windows requires that the second parameter to CreateProcessA be a writable (non-const) string...
  89. char *args2 = new char[args.size() + 1];
  90. strcpy(args2, args.c_str());
  91. if( ! CreateProcessA( NULL, args2, NULL, NULL, FALSE, 0, NULL, NULL, &sinfo, &pinfo ) )
  92. {
  93. // TODO: do better than returning the OS-specific error code on failure...
  94. result = GetLastError();
  95. if(result == 0)
  96. {
  97. // Make absolutely certain we return a non-zero value on failure.
  98. result = -1;
  99. }
  100. }
  101. else
  102. {
  103. // foo = pinfo.dwProcessId; // get your pid here if you want to use it later on
  104. // CloseHandle(pinfo.hProcess); // stops leaks - nothing else
  105. mProcessHandle = pinfo.hProcess;
  106. CloseHandle(pinfo.hThread); // stops leaks - nothing else
  107. }
  108. delete[] args2;
  109. return result;
  110. }
  111. bool LLProcessLauncher::isRunning(void)
  112. {
  113. if(mProcessHandle != 0)
  114. {
  115. DWORD waitresult = WaitForSingleObject(mProcessHandle, 0);
  116. if(waitresult == WAIT_OBJECT_0)
  117. {
  118. // the process has completed.
  119. mProcessHandle = 0;
  120. }
  121. }
  122. return (mProcessHandle != 0);
  123. }
  124. bool LLProcessLauncher::kill(void)
  125. {
  126. bool result = true;
  127. if(mProcessHandle != 0)
  128. {
  129. TerminateProcess(mProcessHandle,0);
  130. if(isRunning())
  131. {
  132. result = false;
  133. }
  134. }
  135. return result;
  136. }
  137. void LLProcessLauncher::orphan(void)
  138. {
  139. // Forget about the process
  140. mProcessHandle = 0;
  141. }
  142. // static 
  143. void LLProcessLauncher::reap(void)
  144. {
  145. // No actions necessary on Windows.
  146. }
  147. #else // Mac and linux
  148. #include <signal.h>
  149. #include <fcntl.h>
  150. #include <errno.h>
  151. static std::list<pid_t> sZombies;
  152. // Attempt to reap a process ID -- returns true if the process has exited and been reaped, false otherwise.
  153. static bool reap_pid(pid_t pid)
  154. {
  155. bool result = false;
  156. pid_t wait_result = ::waitpid(pid, NULL, WNOHANG);
  157. if(wait_result == pid)
  158. {
  159. result = true;
  160. }
  161. else if(wait_result == -1)
  162. {
  163. if(errno == ECHILD)
  164. {
  165. // No such process -- this may mean we're ignoring SIGCHILD.
  166. result = true;
  167. }
  168. }
  169. return result;
  170. }
  171. int LLProcessLauncher::launch(void)
  172. {
  173. // If there was already a process associated with this object, kill it.
  174. kill();
  175. orphan();
  176. int result = 0;
  177. int current_wd = -1;
  178. // create an argv vector for the child process
  179. const char ** fake_argv = new const char *[mLaunchArguments.size() + 2];  // 1 for the executable path, 1 for the NULL terminator
  180. int i = 0;
  181. // add the executable path
  182. fake_argv[i++] = mExecutable.c_str();
  183. // and any arguments
  184. for(int j=0; j < mLaunchArguments.size(); j++)
  185. fake_argv[i++] = mLaunchArguments[j].c_str();
  186. // terminate with a null pointer
  187. fake_argv[i] = NULL;
  188. if(!mWorkingDir.empty())
  189. {
  190. // save the current working directory
  191. current_wd = ::open(".", O_RDONLY);
  192. // and change to the one the child will be executed in
  193. if (::chdir(mWorkingDir.c_str()))
  194. {
  195. // chdir failed
  196. }
  197. }
  198.   // flush all buffers before the child inherits them
  199.   ::fflush(NULL);
  200. pid_t id = vfork();
  201. if(id == 0)
  202. {
  203. // child process
  204. ::execv(mExecutable.c_str(), (char * const *)fake_argv);
  205. // If we reach this point, the exec failed.
  206. // Use _exit() instead of exit() per the vfork man page.
  207. _exit(0);
  208. }
  209. // parent process
  210. if(current_wd >= 0)
  211. {
  212. // restore the previous working directory
  213. if (::fchdir(current_wd))
  214. {
  215. // chdir failed
  216. }
  217. ::close(current_wd);
  218. }
  219. delete[] fake_argv;
  220. mProcessID = id;
  221. // At this point, the child process will have been created (since that's how vfork works -- the child borrowed our execution context until it forked)
  222. // If the process doesn't exist at this point, the exec failed.
  223. if(!isRunning())
  224. {
  225. result = -1;
  226. }
  227. return result;
  228. }
  229. bool LLProcessLauncher::isRunning(void)
  230. {
  231. if(mProcessID != 0)
  232. {
  233. // Check whether the process has exited, and reap it if it has.
  234. if(reap_pid(mProcessID))
  235. {
  236. // the process has exited.
  237. mProcessID = 0;
  238. }
  239. }
  240. return (mProcessID != 0);
  241. }
  242. bool LLProcessLauncher::kill(void)
  243. {
  244. bool result = true;
  245. if(mProcessID != 0)
  246. {
  247. // 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.
  248. (void)::kill(mProcessID, SIGTERM);
  249. // This will have the side-effect of reaping the zombie if the process has exited.
  250. if(isRunning())
  251. {
  252. result = false;
  253. }
  254. }
  255. return result;
  256. }
  257. void LLProcessLauncher::orphan(void)
  258. {
  259. // Disassociate the process from this object
  260. if(mProcessID != 0)
  261. {
  262. // We may still need to reap the process's zombie eventually
  263. sZombies.push_back(mProcessID);
  264. mProcessID = 0;
  265. }
  266. }
  267. // static 
  268. void LLProcessLauncher::reap(void)
  269. {
  270. // Attempt to real all saved process ID's.
  271. std::list<pid_t>::iterator iter = sZombies.begin();
  272. while(iter != sZombies.end())
  273. {
  274. if(reap_pid(*iter))
  275. {
  276. iter = sZombies.erase(iter);
  277. }
  278. else
  279. {
  280. iter++;
  281. }
  282. }
  283. }
  284. #endif