Process.cpp 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. // Copyright (C) 2009-2022, 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. #if !ANKI_OS_ANDROID
  8. # include <ThirdParty/Reproc/reproc/include/reproc/reproc.h>
  9. #endif
  10. namespace anki {
  11. Process::~Process()
  12. {
  13. destroy();
  14. }
  15. void Process::destroy()
  16. {
  17. #if !ANKI_OS_ANDROID
  18. if(m_handle)
  19. {
  20. ProcessStatus status;
  21. [[maybe_unused]] const Error err = getStatus(status);
  22. if(status == ProcessStatus::kRunning)
  23. {
  24. ANKI_UTIL_LOGE("Process is still running. Forgot to wait for it");
  25. }
  26. m_handle = reproc_destroy(m_handle);
  27. }
  28. #endif
  29. }
  30. Error Process::start(CString executable, ConstWeakArray<CString> arguments, ConstWeakArray<CString> environment)
  31. {
  32. #if !ANKI_OS_ANDROID
  33. ANKI_ASSERT(m_handle == nullptr && "Already started");
  34. // Set args and env
  35. Array<const Char*, 64> args;
  36. Array<const Char*, 32> env;
  37. args[0] = executable.cstr();
  38. for(U32 i = 0; i < arguments.getSize(); ++i)
  39. {
  40. args[i + 1] = arguments[i].cstr();
  41. ANKI_ASSERT(args[i + 1]);
  42. }
  43. args[arguments.getSize() + 1] = nullptr;
  44. for(U32 i = 0; i < environment.getSize(); ++i)
  45. {
  46. env[i] = environment[i].cstr();
  47. ANKI_ASSERT(env[i]);
  48. }
  49. env[environment.getSize()] = nullptr;
  50. // Start process
  51. m_handle = reproc_new();
  52. reproc_options options = {};
  53. options.env.behavior = REPROC_ENV_EXTEND;
  54. if(environment.getSize())
  55. {
  56. options.env.extra = &env[0];
  57. }
  58. options.nonblocking = true;
  59. options.redirect.err.type = REPROC_REDIRECT_PIPE;
  60. I32 ret = reproc_start(m_handle, &args[0], options);
  61. if(ret < 0)
  62. {
  63. ANKI_UTIL_LOGE("reproc_start() failed: %s", reproc_strerror(ret));
  64. m_handle = reproc_destroy(m_handle);
  65. return Error::kUserData;
  66. }
  67. ret = reproc_close(m_handle, REPROC_STREAM_IN);
  68. if(ret < 0)
  69. {
  70. ANKI_UTIL_LOGE("reproc_close() failed: %s. Ignoring", reproc_strerror(ret));
  71. m_handle = reproc_destroy(m_handle);
  72. return Error::kUserData;
  73. }
  74. #endif
  75. return Error::kNone;
  76. }
  77. Error Process::wait(Second timeout, ProcessStatus* pStatus, I32* pExitCode)
  78. {
  79. #if !ANKI_OS_ANDROID
  80. ANKI_ASSERT(m_handle);
  81. ProcessStatus status;
  82. I32 exitCode;
  83. // Compute timeout in ms
  84. I32 rtimeout;
  85. if(timeout < 0.0)
  86. {
  87. rtimeout = REPROC_INFINITE;
  88. }
  89. else
  90. {
  91. // Cap the timeout to 1h to avoid overflows when converting to ms
  92. if(timeout > 1.0_hour)
  93. {
  94. ANKI_UTIL_LOGW("Timeout unreasonably high (%f sec). Will cap it to 1h", timeout);
  95. timeout = 1.0_hour;
  96. }
  97. rtimeout = I32(timeout * 1000.0);
  98. }
  99. // Wait
  100. const I32 ret = reproc_wait(m_handle, rtimeout);
  101. if(ret == REPROC_ETIMEDOUT)
  102. {
  103. status = ProcessStatus::kRunning;
  104. exitCode = 0;
  105. }
  106. else
  107. {
  108. status = ProcessStatus::kNotRunning;
  109. exitCode = ret;
  110. }
  111. if(pStatus)
  112. {
  113. *pStatus = status;
  114. }
  115. if(pExitCode)
  116. {
  117. *pExitCode = exitCode;
  118. }
  119. ANKI_ASSERT(!(status == ProcessStatus::kRunning && timeout < 0.0));
  120. #endif
  121. return Error::kNone;
  122. }
  123. Error Process::getStatus(ProcessStatus& status)
  124. {
  125. #if !ANKI_OS_ANDROID
  126. ANKI_ASSERT(m_handle);
  127. ANKI_CHECK(wait(0.0, &status, nullptr));
  128. #endif
  129. return Error::kNone;
  130. }
  131. Error Process::kill(ProcessKillSignal k)
  132. {
  133. #if !ANKI_OS_ANDROID
  134. ANKI_ASSERT(m_handle);
  135. I32 ret;
  136. CString funcName;
  137. if(k == ProcessKillSignal::kNormal)
  138. {
  139. ret = reproc_terminate(m_handle);
  140. funcName = "reproc_terminate";
  141. }
  142. else
  143. {
  144. ANKI_ASSERT(k == ProcessKillSignal::kForce);
  145. ret = reproc_kill(m_handle);
  146. funcName = "reproc_kill";
  147. }
  148. if(ret < 0)
  149. {
  150. ANKI_UTIL_LOGE("%s() failed: %s", funcName.cstr(), reproc_strerror(ret));
  151. return Error::kFunctionFailed;
  152. }
  153. #endif
  154. return Error::kNone;
  155. }
  156. Error Process::readFromStdout(StringAuto& text)
  157. {
  158. #if !ANKI_OS_ANDROID
  159. return readCommon(REPROC_STREAM_OUT, text);
  160. #else
  161. return Error::kNone;
  162. #endif
  163. }
  164. Error Process::readFromStderr(StringAuto& text)
  165. {
  166. #if !ANKI_OS_ANDROID
  167. return readCommon(REPROC_STREAM_ERR, text);
  168. #else
  169. return Error::kNone;
  170. #endif
  171. }
  172. #if !ANKI_OS_ANDROID
  173. Error Process::readCommon(I32 reprocStream, StringAuto& text)
  174. {
  175. ANKI_ASSERT(m_handle);
  176. // Limit the iterations in case the process writes to FD constantly
  177. U32 maxIterations = 16;
  178. while(maxIterations--)
  179. {
  180. Array<Char, 256> buff;
  181. const I32 ret =
  182. reproc_read(m_handle, REPROC_STREAM(reprocStream), reinterpret_cast<U8*>(&buff[0]), buff.getSize() - 1);
  183. if(ret == 0 || ret == REPROC_EPIPE || ret == REPROC_EWOULDBLOCK)
  184. {
  185. // No data or all data have bee read or something that I don't get
  186. break;
  187. }
  188. if(ret < 0)
  189. {
  190. ANKI_UTIL_LOGE("reproc_read() failed: %s", reproc_strerror(ret));
  191. return Error::kFunctionFailed;
  192. }
  193. buff[ret] = '\0';
  194. text.append(&buff[0]);
  195. }
  196. return Error::kNone;
  197. }
  198. #endif
  199. } // end namespace anki