Audio plugin host https://kx.studio/carla
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

ChildProcess.cpp 11KB

7 years ago
7 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386
  1. /*
  2. ==============================================================================
  3. This file is part of the Water library.
  4. Copyright (c) 2016 ROLI Ltd.
  5. Copyright (C) 2017-2018 Filipe Coelho <falktx@falktx.com>
  6. Permission is granted to use this software under the terms of the ISC license
  7. http://www.isc.org/downloads/software-support-policy/isc-license/
  8. Permission to use, copy, modify, and/or distribute this software for any
  9. purpose with or without fee is hereby granted, provided that the above
  10. copyright notice and this permission notice appear in all copies.
  11. THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD
  12. TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
  13. FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
  14. OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
  15. USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
  16. TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
  17. OF THIS SOFTWARE.
  18. ==============================================================================
  19. */
  20. #include "ChildProcess.h"
  21. #include "../files/File.h"
  22. #include "../misc/Time.h"
  23. #include "../streams/MemoryOutputStream.h"
  24. #ifndef CARLA_OS_WIN
  25. # include <signal.h>
  26. # include <sys/wait.h>
  27. #endif
  28. namespace water {
  29. #ifdef CARLA_OS_WIN
  30. //=====================================================================================================================
  31. class ChildProcess::ActiveProcess
  32. {
  33. public:
  34. ActiveProcess (const String& command, int streamFlags)
  35. : ok (false), readPipe (0), writePipe (0)
  36. {
  37. SECURITY_ATTRIBUTES securityAtts;
  38. carla_zeroStruct(securityAtts);
  39. securityAtts.nLength = sizeof (securityAtts);
  40. securityAtts.bInheritHandle = TRUE;
  41. if (CreatePipe (&readPipe, &writePipe, &securityAtts, 0)
  42. && SetHandleInformation (readPipe, HANDLE_FLAG_INHERIT, 0))
  43. {
  44. STARTUPINFO startupInfo;
  45. carla_zeroStruct(startupInfo);
  46. startupInfo.cb = sizeof (startupInfo);
  47. startupInfo.hStdOutput = (streamFlags & wantStdOut) != 0 ? writePipe : 0;
  48. startupInfo.hStdError = (streamFlags & wantStdErr) != 0 ? writePipe : 0;
  49. startupInfo.dwFlags = STARTF_USESTDHANDLES;
  50. ok = CreateProcess (nullptr, const_cast<LPSTR>(command.toRawUTF8()),
  51. nullptr, nullptr, TRUE, CREATE_NO_WINDOW | CREATE_UNICODE_ENVIRONMENT,
  52. nullptr, nullptr, &startupInfo, &processInfo) != FALSE;
  53. }
  54. }
  55. ~ActiveProcess()
  56. {
  57. if (ok)
  58. {
  59. CloseHandle (processInfo.hThread);
  60. CloseHandle (processInfo.hProcess);
  61. }
  62. if (readPipe != 0)
  63. CloseHandle (readPipe);
  64. if (writePipe != 0)
  65. CloseHandle (writePipe);
  66. }
  67. bool isRunning() const noexcept
  68. {
  69. return WaitForSingleObject (processInfo.hProcess, 0) != WAIT_OBJECT_0;
  70. }
  71. int read (void* dest, int numNeeded) const noexcept
  72. {
  73. int total = 0;
  74. while (ok && numNeeded > 0)
  75. {
  76. DWORD available = 0;
  77. if (! PeekNamedPipe ((HANDLE) readPipe, nullptr, 0, nullptr, &available, nullptr))
  78. break;
  79. const int numToDo = jmin ((int) available, numNeeded);
  80. if (available == 0)
  81. {
  82. if (! isRunning())
  83. break;
  84. Sleep(0);
  85. }
  86. else
  87. {
  88. DWORD numRead = 0;
  89. if (! ReadFile ((HANDLE) readPipe, dest, numToDo, &numRead, nullptr))
  90. break;
  91. total += numRead;
  92. dest = addBytesToPointer (dest, numRead);
  93. numNeeded -= numRead;
  94. }
  95. }
  96. return total;
  97. }
  98. bool killProcess() const noexcept
  99. {
  100. return TerminateProcess (processInfo.hProcess, 0) != FALSE;
  101. }
  102. uint32 getExitCode() const noexcept
  103. {
  104. DWORD exitCode = 0;
  105. GetExitCodeProcess (processInfo.hProcess, &exitCode);
  106. return (uint32) exitCode;
  107. }
  108. int getPID() const noexcept
  109. {
  110. return 0;
  111. }
  112. bool ok;
  113. private:
  114. HANDLE readPipe, writePipe;
  115. PROCESS_INFORMATION processInfo;
  116. CARLA_DECLARE_NON_COPY_CLASS (ActiveProcess)
  117. };
  118. #else
  119. class ChildProcess::ActiveProcess
  120. {
  121. public:
  122. ActiveProcess (const StringArray& arguments, int streamFlags)
  123. : childPID (0), pipeHandle (0), readHandle (0)
  124. {
  125. String exe (arguments[0].unquoted());
  126. // Looks like you're trying to launch a non-existent exe or a folder (perhaps on OSX
  127. // you're trying to launch the .app folder rather than the actual binary inside it?)
  128. jassert (File::getCurrentWorkingDirectory().getChildFile (exe).existsAsFile()
  129. || ! exe.containsChar (File::separator));
  130. int pipeHandles[2] = { 0 };
  131. if (pipe (pipeHandles) == 0)
  132. {
  133. Array<char*> argv;
  134. for (int i = 0; i < arguments.size(); ++i)
  135. if (arguments[i].isNotEmpty())
  136. argv.add (const_cast<char*> (arguments[i].toRawUTF8()));
  137. argv.add (nullptr);
  138. const pid_t result = vfork();
  139. if (result < 0)
  140. {
  141. close (pipeHandles[0]);
  142. close (pipeHandles[1]);
  143. }
  144. else if (result == 0)
  145. {
  146. if (execvp (exe.toRawUTF8(), argv.getRawDataPointer()))
  147. _exit (-1);
  148. }
  149. else
  150. {
  151. // we're the parent process..
  152. childPID = result;
  153. pipeHandle = pipeHandles[0];
  154. close (pipeHandles[1]); // close the write handle
  155. }
  156. // FIXME
  157. (void)streamFlags;
  158. }
  159. }
  160. ~ActiveProcess()
  161. {
  162. if (readHandle != 0)
  163. fclose (readHandle);
  164. if (pipeHandle != 0)
  165. close (pipeHandle);
  166. }
  167. bool isRunning() const noexcept
  168. {
  169. if (childPID != 0)
  170. {
  171. int childState = 0;
  172. const int pid = waitpid (childPID, &childState, WNOHANG|WUNTRACED);
  173. return pid == 0 || ! (WIFEXITED (childState) || WIFSIGNALED (childState) || WIFSTOPPED (childState));
  174. }
  175. return false;
  176. }
  177. int read (void* const dest, const int numBytes) noexcept
  178. {
  179. jassert (dest != nullptr);
  180. #ifdef fdopen
  181. #error // the zlib headers define this function as NULL!
  182. #endif
  183. if (readHandle == 0 && childPID != 0)
  184. readHandle = fdopen (pipeHandle, "r");
  185. if (readHandle != 0)
  186. return (int) fread (dest, 1, (size_t) numBytes, readHandle);
  187. return 0;
  188. }
  189. bool killProcess() const noexcept
  190. {
  191. return ::kill (childPID, SIGKILL) == 0;
  192. }
  193. uint32 getExitCode() const noexcept
  194. {
  195. if (childPID != 0)
  196. {
  197. int childState = 0;
  198. const int pid = waitpid (childPID, &childState, WNOHANG);
  199. if (pid >= 0 && WIFEXITED (childState))
  200. return WEXITSTATUS (childState);
  201. }
  202. return 0;
  203. }
  204. int getPID() const noexcept
  205. {
  206. return childPID;
  207. }
  208. int childPID;
  209. private:
  210. int pipeHandle;
  211. FILE* readHandle;
  212. CARLA_DECLARE_NON_COPY_CLASS (ActiveProcess)
  213. };
  214. #endif
  215. //=====================================================================================================================
  216. ChildProcess::ChildProcess() {}
  217. ChildProcess::~ChildProcess() {}
  218. bool ChildProcess::isRunning() const
  219. {
  220. return activeProcess != nullptr && activeProcess->isRunning();
  221. }
  222. int ChildProcess::readProcessOutput (void* dest, int numBytes)
  223. {
  224. return activeProcess != nullptr ? activeProcess->read (dest, numBytes) : 0;
  225. }
  226. bool ChildProcess::kill()
  227. {
  228. return activeProcess == nullptr || activeProcess->killProcess();
  229. }
  230. uint32 ChildProcess::getExitCode() const
  231. {
  232. return activeProcess != nullptr ? activeProcess->getExitCode() : 0;
  233. }
  234. bool ChildProcess::waitForProcessToFinish (const int timeoutMs) const
  235. {
  236. const uint32 timeoutTime = Time::getMillisecondCounter() + (uint32) timeoutMs;
  237. do
  238. {
  239. if (! isRunning())
  240. return true;
  241. }
  242. while (timeoutMs < 0 || Time::getMillisecondCounter() < timeoutTime);
  243. return false;
  244. }
  245. String ChildProcess::readAllProcessOutput()
  246. {
  247. MemoryOutputStream result;
  248. for (;;)
  249. {
  250. char buffer [512];
  251. const int num = readProcessOutput (buffer, sizeof (buffer));
  252. if (num <= 0)
  253. break;
  254. result.write (buffer, (size_t) num);
  255. }
  256. return result.toString();
  257. }
  258. uint32 ChildProcess::getPID() const noexcept
  259. {
  260. return activeProcess != nullptr ? activeProcess->getPID() : 0;
  261. }
  262. //=====================================================================================================================
  263. #ifdef CARLA_OS_WIN
  264. bool ChildProcess::start (const String& command, int streamFlags)
  265. {
  266. activeProcess = new ActiveProcess (command, streamFlags);
  267. if (! activeProcess->ok)
  268. activeProcess = nullptr;
  269. return activeProcess != nullptr;
  270. }
  271. bool ChildProcess::start (const StringArray& args, int streamFlags)
  272. {
  273. String escaped;
  274. for (int i = 0, size = args.size(); i < size; ++i)
  275. {
  276. String arg (args[i]);
  277. // If there are spaces, surround it with quotes. If there are quotes,
  278. // replace them with \" so that CommandLineToArgv will correctly parse them.
  279. if (arg.containsAnyOf ("\" "))
  280. arg = arg.replace ("\"", "\\\"").quoted();
  281. escaped << arg;
  282. if (i+1 < size)
  283. escaped << ' ';
  284. }
  285. return start (escaped.trim(), streamFlags);
  286. }
  287. #else
  288. bool ChildProcess::start (const String& command, int streamFlags)
  289. {
  290. return start (StringArray::fromTokens (command, true), streamFlags);
  291. }
  292. bool ChildProcess::start (const StringArray& args, int streamFlags)
  293. {
  294. if (args.size() == 0)
  295. return false;
  296. activeProcess = new ActiveProcess (args, streamFlags);
  297. if (activeProcess->childPID == 0)
  298. activeProcess = nullptr;
  299. return activeProcess != nullptr;
  300. }
  301. #endif
  302. }