Process.cpp 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374
  1. // Copyright (C) 2009-present, Panagiotis Christopoulos Charitos and contributors.
  2. // All rights reserved.
  3. // Code licensed under the BSD License.
  4. // http://www.anki3d.org/LICENSE
  5. #include <AnKi/Util/Process.h>
  6. #include <AnKi/Util/Array.h>
  7. #include <AnKi/Util/Thread.h>
  8. #if !ANKI_OS_ANDROID
  9. # include <ThirdParty/Reproc/reproc/include/reproc/reproc.h>
  10. # include <ThirdParty/Subprocess/subprocess.h>
  11. #endif
  12. #if ANKI_POSIX
  13. # include <unistd.h>
  14. #endif
  15. namespace anki {
  16. Process::~Process()
  17. {
  18. destroy();
  19. }
  20. void Process::destroy()
  21. {
  22. #if !ANKI_OS_ANDROID
  23. if(m_handle)
  24. {
  25. ProcessStatus status;
  26. [[maybe_unused]] const Error err = getStatus(status);
  27. if(status == ProcessStatus::kRunning)
  28. {
  29. ANKI_UTIL_LOGE("Process is still running. Forgot to wait for it");
  30. }
  31. m_handle = reproc_destroy(m_handle);
  32. }
  33. #endif
  34. }
  35. Error Process::start(CString executable, const DynamicArray<String>& arguments, const DynamicArray<String>& environment, ProcessOptions options)
  36. {
  37. // Set args and env
  38. Array<const Char*, kMaxArgs> args;
  39. Array<const Char*, kMaxEnv> env;
  40. args[0] = executable.cstr();
  41. for(U32 i = 0; i < arguments.getSize(); ++i)
  42. {
  43. args[i + 1] = arguments[i].cstr();
  44. ANKI_ASSERT(args[i + 1]);
  45. }
  46. args[arguments.getSize() + 1] = nullptr;
  47. for(U32 i = 0; i < environment.getSize(); ++i)
  48. {
  49. env[i] = environment[i].cstr();
  50. ANKI_ASSERT(env[i]);
  51. }
  52. env[environment.getSize()] = nullptr;
  53. return startInternal(&args[0], &env[0], options);
  54. }
  55. Error Process::start(CString executable, ConstWeakArray<CString> arguments, ConstWeakArray<CString> environment, ProcessOptions options)
  56. {
  57. // Set args and env
  58. Array<const Char*, kMaxArgs> args;
  59. Array<const Char*, kMaxEnv> env;
  60. args[0] = executable.cstr();
  61. for(U32 i = 0; i < arguments.getSize(); ++i)
  62. {
  63. args[i + 1] = arguments[i].cstr();
  64. ANKI_ASSERT(args[i + 1]);
  65. }
  66. args[arguments.getSize() + 1] = nullptr;
  67. for(U32 i = 0; i < environment.getSize(); ++i)
  68. {
  69. env[i] = environment[i].cstr();
  70. ANKI_ASSERT(env[i]);
  71. }
  72. env[environment.getSize()] = nullptr;
  73. return startInternal(&args[0], &env[0], options);
  74. }
  75. Error Process::startInternal(const Char* args[], const Char* env[], ProcessOptions options)
  76. {
  77. #if !ANKI_OS_ANDROID
  78. ANKI_ASSERT(m_handle == nullptr && "Already started");
  79. // Start process
  80. m_handle = reproc_new();
  81. reproc_options reprocOptions = {};
  82. reprocOptions.env.behavior = REPROC_ENV_EXTEND;
  83. if(env[0] != nullptr)
  84. {
  85. reprocOptions.env.extra = &env[0];
  86. }
  87. reprocOptions.nonblocking = true;
  88. reprocOptions.redirect.in.type = REPROC_REDIRECT_DISCARD;
  89. reprocOptions.redirect.err.type = (!!(options & ProcessOptions::kOpenStderr)) ? REPROC_REDIRECT_PIPE : REPROC_REDIRECT_DISCARD;
  90. reprocOptions.redirect.out.type = (!!(options & ProcessOptions::kOpenStdout)) ? REPROC_REDIRECT_PIPE : REPROC_REDIRECT_DISCARD;
  91. I32 ret = reproc_start(m_handle, &args[0], reprocOptions);
  92. if(ret < 0)
  93. {
  94. ANKI_UTIL_LOGE("reproc_start() failed: %s", reproc_strerror(ret));
  95. m_handle = reproc_destroy(m_handle);
  96. return Error::kUserData;
  97. }
  98. ret = reproc_close(m_handle, REPROC_STREAM_IN);
  99. if(ret < 0)
  100. {
  101. ANKI_UTIL_LOGE("reproc_close() failed: %s. Ignoring", reproc_strerror(ret));
  102. m_handle = reproc_destroy(m_handle);
  103. return Error::kUserData;
  104. }
  105. #else
  106. (void)args;
  107. (void)env;
  108. (void)options;
  109. #endif
  110. return Error::kNone;
  111. }
  112. Error Process::wait(Second timeout, ProcessStatus* pStatus, I32* pExitCode)
  113. {
  114. #if !ANKI_OS_ANDROID
  115. ANKI_ASSERT(m_handle);
  116. ProcessStatus status;
  117. I32 exitCode;
  118. // Compute timeout in ms
  119. I32 rtimeout;
  120. if(timeout < 0.0)
  121. {
  122. rtimeout = REPROC_INFINITE;
  123. }
  124. else
  125. {
  126. // Cap the timeout to 1h to avoid overflows when converting to ms
  127. if(timeout > 1.0_hour)
  128. {
  129. ANKI_UTIL_LOGW("Timeout unreasonably high (%f sec). Will cap it to 1h", timeout);
  130. timeout = 1.0_hour;
  131. }
  132. rtimeout = I32(timeout * 1000.0);
  133. }
  134. // Wait
  135. const I32 ret = reproc_wait(m_handle, rtimeout);
  136. if(ret == REPROC_ETIMEDOUT)
  137. {
  138. status = ProcessStatus::kRunning;
  139. exitCode = 0;
  140. }
  141. else
  142. {
  143. status = ProcessStatus::kNotRunning;
  144. exitCode = ret;
  145. }
  146. if(pStatus)
  147. {
  148. *pStatus = status;
  149. }
  150. if(pExitCode)
  151. {
  152. *pExitCode = exitCode;
  153. }
  154. ANKI_ASSERT(!(status == ProcessStatus::kRunning && timeout < 0.0));
  155. #else
  156. (void)timeout;
  157. (void)pStatus;
  158. (void)pExitCode;
  159. #endif
  160. return Error::kNone;
  161. }
  162. Error Process::getStatus(ProcessStatus& status)
  163. {
  164. #if !ANKI_OS_ANDROID
  165. ANKI_ASSERT(m_handle);
  166. ANKI_CHECK(wait(0.0, &status, nullptr));
  167. #else
  168. (void)status;
  169. #endif
  170. return Error::kNone;
  171. }
  172. Error Process::kill(ProcessKillSignal k)
  173. {
  174. #if !ANKI_OS_ANDROID
  175. ANKI_ASSERT(m_handle);
  176. I32 ret;
  177. CString funcName;
  178. if(k == ProcessKillSignal::kNormal)
  179. {
  180. ret = reproc_terminate(m_handle);
  181. funcName = "reproc_terminate";
  182. }
  183. else
  184. {
  185. ANKI_ASSERT(k == ProcessKillSignal::kForce);
  186. ret = reproc_kill(m_handle);
  187. funcName = "reproc_kill";
  188. }
  189. if(ret < 0)
  190. {
  191. ANKI_UTIL_LOGE("%s() failed: %s", funcName.cstr(), reproc_strerror(ret));
  192. return Error::kFunctionFailed;
  193. }
  194. #else
  195. (void)k;
  196. #endif
  197. return Error::kNone;
  198. }
  199. Error Process::readFromStdout(String& text)
  200. {
  201. #if !ANKI_OS_ANDROID
  202. return readCommon(REPROC_STREAM_OUT, text);
  203. #else
  204. (void)text;
  205. return Error::kNone;
  206. #endif
  207. }
  208. Error Process::readFromStderr(String& text)
  209. {
  210. #if !ANKI_OS_ANDROID
  211. return readCommon(REPROC_STREAM_ERR, text);
  212. #else
  213. (void)text;
  214. return Error::kNone;
  215. #endif
  216. }
  217. #if !ANKI_OS_ANDROID
  218. Error Process::readCommon(I32 reprocStream, String& text)
  219. {
  220. ANKI_ASSERT(m_handle);
  221. // Limit the iterations in case the process writes to FD constantly
  222. U32 maxIterations = 16;
  223. while(maxIterations--)
  224. {
  225. Array<Char, 256> buff;
  226. const I32 ret = reproc_read(m_handle, REPROC_STREAM(reprocStream), reinterpret_cast<U8*>(&buff[0]), buff.getSize() - 1);
  227. if(ret == 0 || ret == REPROC_EPIPE || ret == REPROC_EWOULDBLOCK)
  228. {
  229. // No data or all data have bee read or something that I don't get
  230. break;
  231. }
  232. if(ret < 0)
  233. {
  234. ANKI_UTIL_LOGE("reproc_read() failed: %s", reproc_strerror(ret));
  235. return Error::kFunctionFailed;
  236. }
  237. buff[ret] = '\0';
  238. text += &buff[0];
  239. }
  240. return Error::kNone;
  241. }
  242. #endif
  243. Error Process::callProcess(CString executable, ConstWeakArray<CString> arguments, String* stdOut, String* stdErr, I32& exitCode)
  244. {
  245. #if !ANKI_OS_ANDROID
  246. if(true)
  247. {
  248. ProcessOptions options = ProcessOptions::kNone;
  249. options |= (stdOut) ? ProcessOptions::kOpenStdout : ProcessOptions::kNone;
  250. options |= (stdErr) ? ProcessOptions::kOpenStderr : ProcessOptions::kNone;
  251. Process proc;
  252. ANKI_CHECK(proc.start(executable, arguments, {}, options));
  253. ANKI_CHECK(proc.wait(-1.0, nullptr, &exitCode));
  254. if(stdOut)
  255. {
  256. ANKI_CHECK(proc.readFromStdout(*stdOut));
  257. }
  258. if(stdErr)
  259. {
  260. ANKI_CHECK(proc.readFromStderr(*stdErr));
  261. }
  262. proc.destroy();
  263. }
  264. else
  265. {
  266. Array<const char*, 128> args;
  267. U32 count = 0;
  268. args[count++] = executable.cstr();
  269. for(U32 i = 0; i < arguments.getSize(); ++i)
  270. {
  271. args[count++] = arguments[i].cstr();
  272. }
  273. args[count] = nullptr;
  274. const int options = subprocess_option_inherit_environment | subprocess_option_no_window | subprocess_option_search_user_path;
  275. struct subprocess_s subprocess;
  276. int err = subprocess_create(&args[0], options, &subprocess);
  277. if(err)
  278. {
  279. ANKI_UTIL_LOGE("subprocess_create() failed");
  280. return Error::kFunctionFailed;
  281. }
  282. err = subprocess_join(&subprocess, &exitCode);
  283. if(err)
  284. {
  285. ANKI_UTIL_LOGE("subprocess_join() failed");
  286. subprocess_terminate(&subprocess);
  287. return Error::kFunctionFailed;
  288. }
  289. if(stdOut)
  290. {
  291. ANKI_ASSERT(!"TODO");
  292. }
  293. if(stdErr)
  294. {
  295. ANKI_ASSERT(!"TODO");
  296. }
  297. subprocess_destroy(&subprocess);
  298. }
  299. #else
  300. (void)executable;
  301. (void)arguments;
  302. (void)stdOut;
  303. (void)stdErr;
  304. (void)exitCode;
  305. #endif
  306. return Error::kNone;
  307. }
  308. U32 getCurrentProcessId()
  309. {
  310. #if ANKI_OS_WINDOWS
  311. return GetCurrentProcessId();
  312. #elif ANKI_POSIX
  313. return getpid();
  314. #endif
  315. }
  316. } // end namespace anki