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.

408 lines
11KB

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