| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374 |
- // Copyright (C) 2009-present, Panagiotis Christopoulos Charitos and contributors.
- // All rights reserved.
- // Code licensed under the BSD License.
- // http://www.anki3d.org/LICENSE
- #include <AnKi/Util/Process.h>
- #include <AnKi/Util/Array.h>
- #include <AnKi/Util/Thread.h>
- #if !ANKI_OS_ANDROID
- # include <ThirdParty/Reproc/reproc/include/reproc/reproc.h>
- # include <ThirdParty/Subprocess/subprocess.h>
- #endif
- #if ANKI_POSIX
- # include <unistd.h>
- #endif
- namespace anki {
- Process::~Process()
- {
- destroy();
- }
- void Process::destroy()
- {
- #if !ANKI_OS_ANDROID
- if(m_handle)
- {
- ProcessStatus status;
- [[maybe_unused]] const Error err = getStatus(status);
- if(status == ProcessStatus::kRunning)
- {
- ANKI_UTIL_LOGE("Process is still running. Forgot to wait for it");
- }
- m_handle = reproc_destroy(m_handle);
- }
- #endif
- }
- Error Process::start(CString executable, const DynamicArray<String>& arguments, const DynamicArray<String>& environment, ProcessOptions options)
- {
- // Set args and env
- Array<const Char*, kMaxArgs> args;
- Array<const Char*, kMaxEnv> env;
- args[0] = executable.cstr();
- for(U32 i = 0; i < arguments.getSize(); ++i)
- {
- args[i + 1] = arguments[i].cstr();
- ANKI_ASSERT(args[i + 1]);
- }
- args[arguments.getSize() + 1] = nullptr;
- for(U32 i = 0; i < environment.getSize(); ++i)
- {
- env[i] = environment[i].cstr();
- ANKI_ASSERT(env[i]);
- }
- env[environment.getSize()] = nullptr;
- return startInternal(&args[0], &env[0], options);
- }
- Error Process::start(CString executable, ConstWeakArray<CString> arguments, ConstWeakArray<CString> environment, ProcessOptions options)
- {
- // Set args and env
- Array<const Char*, kMaxArgs> args;
- Array<const Char*, kMaxEnv> env;
- args[0] = executable.cstr();
- for(U32 i = 0; i < arguments.getSize(); ++i)
- {
- args[i + 1] = arguments[i].cstr();
- ANKI_ASSERT(args[i + 1]);
- }
- args[arguments.getSize() + 1] = nullptr;
- for(U32 i = 0; i < environment.getSize(); ++i)
- {
- env[i] = environment[i].cstr();
- ANKI_ASSERT(env[i]);
- }
- env[environment.getSize()] = nullptr;
- return startInternal(&args[0], &env[0], options);
- }
- Error Process::startInternal(const Char* args[], const Char* env[], ProcessOptions options)
- {
- #if !ANKI_OS_ANDROID
- ANKI_ASSERT(m_handle == nullptr && "Already started");
- // Start process
- m_handle = reproc_new();
- reproc_options reprocOptions = {};
- reprocOptions.env.behavior = REPROC_ENV_EXTEND;
- if(env[0] != nullptr)
- {
- reprocOptions.env.extra = &env[0];
- }
- reprocOptions.nonblocking = true;
- reprocOptions.redirect.in.type = REPROC_REDIRECT_DISCARD;
- reprocOptions.redirect.err.type = (!!(options & ProcessOptions::kOpenStderr)) ? REPROC_REDIRECT_PIPE : REPROC_REDIRECT_DISCARD;
- reprocOptions.redirect.out.type = (!!(options & ProcessOptions::kOpenStdout)) ? REPROC_REDIRECT_PIPE : REPROC_REDIRECT_DISCARD;
- I32 ret = reproc_start(m_handle, &args[0], reprocOptions);
- if(ret < 0)
- {
- ANKI_UTIL_LOGE("reproc_start() failed: %s", reproc_strerror(ret));
- m_handle = reproc_destroy(m_handle);
- return Error::kUserData;
- }
- ret = reproc_close(m_handle, REPROC_STREAM_IN);
- if(ret < 0)
- {
- ANKI_UTIL_LOGE("reproc_close() failed: %s. Ignoring", reproc_strerror(ret));
- m_handle = reproc_destroy(m_handle);
- return Error::kUserData;
- }
- #else
- (void)args;
- (void)env;
- (void)options;
- #endif
- return Error::kNone;
- }
- Error Process::wait(Second timeout, ProcessStatus* pStatus, I32* pExitCode)
- {
- #if !ANKI_OS_ANDROID
- ANKI_ASSERT(m_handle);
- ProcessStatus status;
- I32 exitCode;
- // Compute timeout in ms
- I32 rtimeout;
- if(timeout < 0.0)
- {
- rtimeout = REPROC_INFINITE;
- }
- else
- {
- // Cap the timeout to 1h to avoid overflows when converting to ms
- if(timeout > 1.0_hour)
- {
- ANKI_UTIL_LOGW("Timeout unreasonably high (%f sec). Will cap it to 1h", timeout);
- timeout = 1.0_hour;
- }
- rtimeout = I32(timeout * 1000.0);
- }
- // Wait
- const I32 ret = reproc_wait(m_handle, rtimeout);
- if(ret == REPROC_ETIMEDOUT)
- {
- status = ProcessStatus::kRunning;
- exitCode = 0;
- }
- else
- {
- status = ProcessStatus::kNotRunning;
- exitCode = ret;
- }
- if(pStatus)
- {
- *pStatus = status;
- }
- if(pExitCode)
- {
- *pExitCode = exitCode;
- }
- ANKI_ASSERT(!(status == ProcessStatus::kRunning && timeout < 0.0));
- #else
- (void)timeout;
- (void)pStatus;
- (void)pExitCode;
- #endif
- return Error::kNone;
- }
- Error Process::getStatus(ProcessStatus& status)
- {
- #if !ANKI_OS_ANDROID
- ANKI_ASSERT(m_handle);
- ANKI_CHECK(wait(0.0, &status, nullptr));
- #else
- (void)status;
- #endif
- return Error::kNone;
- }
- Error Process::kill(ProcessKillSignal k)
- {
- #if !ANKI_OS_ANDROID
- ANKI_ASSERT(m_handle);
- I32 ret;
- CString funcName;
- if(k == ProcessKillSignal::kNormal)
- {
- ret = reproc_terminate(m_handle);
- funcName = "reproc_terminate";
- }
- else
- {
- ANKI_ASSERT(k == ProcessKillSignal::kForce);
- ret = reproc_kill(m_handle);
- funcName = "reproc_kill";
- }
- if(ret < 0)
- {
- ANKI_UTIL_LOGE("%s() failed: %s", funcName.cstr(), reproc_strerror(ret));
- return Error::kFunctionFailed;
- }
- #else
- (void)k;
- #endif
- return Error::kNone;
- }
- Error Process::readFromStdout(String& text)
- {
- #if !ANKI_OS_ANDROID
- return readCommon(REPROC_STREAM_OUT, text);
- #else
- (void)text;
- return Error::kNone;
- #endif
- }
- Error Process::readFromStderr(String& text)
- {
- #if !ANKI_OS_ANDROID
- return readCommon(REPROC_STREAM_ERR, text);
- #else
- (void)text;
- return Error::kNone;
- #endif
- }
- #if !ANKI_OS_ANDROID
- Error Process::readCommon(I32 reprocStream, String& text)
- {
- ANKI_ASSERT(m_handle);
- // Limit the iterations in case the process writes to FD constantly
- U32 maxIterations = 16;
- while(maxIterations--)
- {
- Array<Char, 256> buff;
- const I32 ret = reproc_read(m_handle, REPROC_STREAM(reprocStream), reinterpret_cast<U8*>(&buff[0]), buff.getSize() - 1);
- if(ret == 0 || ret == REPROC_EPIPE || ret == REPROC_EWOULDBLOCK)
- {
- // No data or all data have bee read or something that I don't get
- break;
- }
- if(ret < 0)
- {
- ANKI_UTIL_LOGE("reproc_read() failed: %s", reproc_strerror(ret));
- return Error::kFunctionFailed;
- }
- buff[ret] = '\0';
- text += &buff[0];
- }
- return Error::kNone;
- }
- #endif
- Error Process::callProcess(CString executable, ConstWeakArray<CString> arguments, String* stdOut, String* stdErr, I32& exitCode)
- {
- #if !ANKI_OS_ANDROID
- if(true)
- {
- ProcessOptions options = ProcessOptions::kNone;
- options |= (stdOut) ? ProcessOptions::kOpenStdout : ProcessOptions::kNone;
- options |= (stdErr) ? ProcessOptions::kOpenStderr : ProcessOptions::kNone;
- Process proc;
- ANKI_CHECK(proc.start(executable, arguments, {}, options));
- ANKI_CHECK(proc.wait(-1.0, nullptr, &exitCode));
- if(stdOut)
- {
- ANKI_CHECK(proc.readFromStdout(*stdOut));
- }
- if(stdErr)
- {
- ANKI_CHECK(proc.readFromStderr(*stdErr));
- }
- proc.destroy();
- }
- else
- {
- Array<const char*, 128> args;
- U32 count = 0;
- args[count++] = executable.cstr();
- for(U32 i = 0; i < arguments.getSize(); ++i)
- {
- args[count++] = arguments[i].cstr();
- }
- args[count] = nullptr;
- const int options = subprocess_option_inherit_environment | subprocess_option_no_window | subprocess_option_search_user_path;
- struct subprocess_s subprocess;
- int err = subprocess_create(&args[0], options, &subprocess);
- if(err)
- {
- ANKI_UTIL_LOGE("subprocess_create() failed");
- return Error::kFunctionFailed;
- }
- err = subprocess_join(&subprocess, &exitCode);
- if(err)
- {
- ANKI_UTIL_LOGE("subprocess_join() failed");
- subprocess_terminate(&subprocess);
- return Error::kFunctionFailed;
- }
- if(stdOut)
- {
- ANKI_ASSERT(!"TODO");
- }
- if(stdErr)
- {
- ANKI_ASSERT(!"TODO");
- }
- subprocess_destroy(&subprocess);
- }
- #else
- (void)executable;
- (void)arguments;
- (void)stdOut;
- (void)stdErr;
- (void)exitCode;
- #endif
- return Error::kNone;
- }
- U32 getCurrentProcessId()
- {
- #if ANKI_OS_WINDOWS
- return GetCurrentProcessId();
- #elif ANKI_POSIX
- return getpid();
- #endif
- }
- } // end namespace anki
|