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.

383 lines
10.0KB

  1. /*
  2. ==============================================================================
  3. This file is part of the Water library.
  4. Copyright (c) 2016 ROLI Ltd.
  5. Copyright (C) 2017-2025 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. #ifdef CARLA_OS_MAC
  24. # include <crt_externs.h>
  25. # include <spawn.h>
  26. #endif
  27. #ifndef CARLA_OS_WIN
  28. # include <signal.h>
  29. # include <sys/wait.h>
  30. #endif
  31. #include "CarlaProcessUtils.hpp"
  32. #include "extra/Sleep.hpp"
  33. namespace water {
  34. #ifdef CARLA_OS_WIN
  35. //=====================================================================================================================
  36. class ChildProcess::ActiveProcess
  37. {
  38. public:
  39. ActiveProcess (const String& command)
  40. : ok (false)
  41. {
  42. STARTUPINFOA startupInfo;
  43. carla_zeroStruct(startupInfo);
  44. startupInfo.cb = sizeof (startupInfo);
  45. ok = CreateProcessA (nullptr, const_cast<LPSTR>(command.toRawUTF8()),
  46. nullptr, nullptr, TRUE, CREATE_NO_WINDOW | CREATE_UNICODE_ENVIRONMENT,
  47. nullptr, nullptr, &startupInfo, &processInfo) != FALSE;
  48. }
  49. ~ActiveProcess()
  50. {
  51. closeProcessInfo();
  52. }
  53. void closeProcessInfo() noexcept
  54. {
  55. if (ok)
  56. {
  57. ok = false;
  58. CloseHandle (processInfo.hThread);
  59. CloseHandle (processInfo.hProcess);
  60. }
  61. }
  62. bool isRunning() const noexcept
  63. {
  64. return WaitForSingleObject (processInfo.hProcess, 0) != WAIT_OBJECT_0;
  65. }
  66. bool checkRunningAndUnsetPID() noexcept
  67. {
  68. if (isRunning())
  69. return true;
  70. ok = false;
  71. CloseHandle (processInfo.hThread);
  72. CloseHandle (processInfo.hProcess);
  73. return false;
  74. }
  75. bool killProcess() const noexcept
  76. {
  77. return TerminateProcess (processInfo.hProcess, 0) != FALSE;
  78. }
  79. bool terminateProcess() const noexcept
  80. {
  81. return TerminateProcess (processInfo.hProcess, 0) != FALSE;
  82. }
  83. uint32 getExitCodeAndClearPID() noexcept
  84. {
  85. DWORD exitCode = 0;
  86. GetExitCodeProcess (processInfo.hProcess, &exitCode);
  87. closeProcessInfo();
  88. return (uint32) exitCode;
  89. }
  90. int getPID() const noexcept
  91. {
  92. return 0;
  93. }
  94. bool ok;
  95. private:
  96. PROCESS_INFORMATION processInfo;
  97. CARLA_DECLARE_NON_COPYABLE (ActiveProcess)
  98. };
  99. #else
  100. class ChildProcess::ActiveProcess
  101. {
  102. public:
  103. ActiveProcess (const StringArray& arguments, const Type type)
  104. : childPID (0)
  105. {
  106. String exe (arguments[0].unquoted());
  107. // Looks like you're trying to launch a non-existent exe or a folder (perhaps on OSX
  108. // you're trying to launch the .app folder rather than the actual binary inside it?)
  109. wassert (File::getCurrentWorkingDirectory().getChildFile (exe.toRawUTF8()).existsAsFile()
  110. || ! exe.containsChar (CARLA_OS_SEP));
  111. Array<char*> argv;
  112. for (int i = 0; i < arguments.size(); ++i)
  113. if (arguments[i].isNotEmpty())
  114. argv.add (const_cast<char*> (arguments[i].toRawUTF8()));
  115. argv.add (nullptr);
  116. #ifdef CARLA_OS_MAC
  117. cpu_type_t pref;
  118. pid_t result = -1;
  119. switch (type)
  120. {
  121. # ifdef __MAC_10_12
  122. case TypeARM:
  123. pref = CPU_TYPE_ARM64;
  124. break;
  125. # endif
  126. case TypeIntel:
  127. pref = CPU_TYPE_X86_64;
  128. break;
  129. default:
  130. pref = CPU_TYPE_ANY;
  131. break;
  132. }
  133. posix_spawnattr_t attr;
  134. posix_spawnattr_init(&attr);
  135. // posix_spawnattr_setflags(&attr, POSIX_SPAWN_USEVFORK);
  136. CARLA_SAFE_ASSERT_RETURN(posix_spawnattr_setbinpref_np(&attr, 1, &pref, nullptr) == 0,);
  137. char*** const environptr = _NSGetEnviron();
  138. CARLA_SAFE_ASSERT_RETURN(posix_spawn(&result, exe.toRawUTF8(), nullptr, &attr,
  139. argv.getRawDataPointer(), environptr != nullptr ? *environptr : nullptr) == 0,);
  140. posix_spawnattr_destroy(&attr);
  141. #else
  142. const pid_t result = vfork();
  143. #endif
  144. if (result < 0)
  145. {
  146. // error
  147. }
  148. #ifndef CARLA_OS_MAC
  149. else if (result == 0)
  150. {
  151. // child process
  152. carla_terminateProcessOnParentExit(true);
  153. if (execvp (exe.toRawUTF8(), argv.getRawDataPointer()))
  154. _exit (-1);
  155. }
  156. #endif
  157. else
  158. {
  159. // we're the parent process..
  160. childPID = result;
  161. }
  162. #ifndef CARLA_OS_MAC
  163. // unused
  164. (void)type;
  165. #endif
  166. }
  167. ~ActiveProcess()
  168. {
  169. CARLA_SAFE_ASSERT_INT(childPID == 0, childPID);
  170. }
  171. bool isRunning() const noexcept
  172. {
  173. if (childPID != 0)
  174. {
  175. int childState = 0;
  176. const int pid = waitpid (childPID, &childState, WNOHANG|WUNTRACED);
  177. return pid == 0 || ! (WIFEXITED (childState) || WIFSIGNALED (childState) || WIFSTOPPED (childState));
  178. }
  179. return false;
  180. }
  181. bool checkRunningAndUnsetPID() noexcept
  182. {
  183. if (childPID != 0)
  184. {
  185. int childState = 0;
  186. const int pid = waitpid (childPID, &childState, WNOHANG|WUNTRACED);
  187. if (pid == 0)
  188. return true;
  189. if ( ! (WIFEXITED (childState) || WIFSIGNALED (childState) || WIFSTOPPED (childState)))
  190. return true;
  191. childPID = 0;
  192. return false;
  193. }
  194. return false;
  195. }
  196. bool killProcess() noexcept
  197. {
  198. if (::kill (childPID, SIGKILL) == 0)
  199. {
  200. childPID = 0;
  201. return true;
  202. }
  203. return false;
  204. }
  205. bool terminateProcess() const noexcept
  206. {
  207. return ::kill (childPID, SIGTERM) == 0;
  208. }
  209. uint32 getExitCodeAndClearPID() noexcept
  210. {
  211. if (childPID != 0)
  212. {
  213. int childState = 0;
  214. const int pid = waitpid (childPID, &childState, WNOHANG);
  215. childPID = 0;
  216. if (pid >= 0 && WIFEXITED (childState))
  217. return WEXITSTATUS (childState);
  218. }
  219. return 0;
  220. }
  221. int getPID() const noexcept
  222. {
  223. return childPID;
  224. }
  225. int childPID;
  226. private:
  227. CARLA_DECLARE_NON_COPYABLE (ActiveProcess)
  228. };
  229. #endif
  230. //=====================================================================================================================
  231. ChildProcess::ChildProcess() {}
  232. ChildProcess::~ChildProcess() {}
  233. bool ChildProcess::isRunning() const
  234. {
  235. return activeProcess != nullptr && activeProcess->isRunning();
  236. }
  237. bool ChildProcess::kill()
  238. {
  239. return activeProcess == nullptr || activeProcess->killProcess();
  240. }
  241. bool ChildProcess::terminate()
  242. {
  243. return activeProcess == nullptr || activeProcess->terminateProcess();
  244. }
  245. uint32 ChildProcess::getExitCodeAndClearPID()
  246. {
  247. return activeProcess != nullptr ? activeProcess->getExitCodeAndClearPID() : 0;
  248. }
  249. bool ChildProcess::waitForProcessToFinish (const int timeoutMs)
  250. {
  251. const uint32 timeoutTime = Time::getMillisecondCounter() + (uint32) timeoutMs;
  252. do
  253. {
  254. if (activeProcess == nullptr)
  255. return true;
  256. if (! activeProcess->checkRunningAndUnsetPID())
  257. return true;
  258. d_msleep(5);
  259. }
  260. while (timeoutMs < 0 || Time::getMillisecondCounter() < timeoutTime);
  261. return false;
  262. }
  263. uint32 ChildProcess::getPID() const noexcept
  264. {
  265. return activeProcess != nullptr ? activeProcess->getPID() : 0;
  266. }
  267. //=====================================================================================================================
  268. #ifdef CARLA_OS_WIN
  269. bool ChildProcess::start (const String& command, Type)
  270. {
  271. activeProcess = new ActiveProcess (command);
  272. if (! activeProcess->ok)
  273. activeProcess = nullptr;
  274. return activeProcess != nullptr;
  275. }
  276. bool ChildProcess::start (const StringArray& args, const Type type)
  277. {
  278. String escaped;
  279. for (int i = 0, size = args.size(); i < size; ++i)
  280. {
  281. String arg (args[i]);
  282. // If there are spaces, surround it with quotes. If there are quotes,
  283. // replace them with \" so that CommandLineToArgv will correctly parse them.
  284. if (arg.containsAnyOf ("\" "))
  285. arg = arg.replace ("\"", "\\\"").quoted();
  286. escaped << arg;
  287. if (i+1 < size)
  288. escaped << ' ';
  289. }
  290. return start (escaped.trim(), type);
  291. }
  292. #else
  293. bool ChildProcess::start (const String& command, const Type type)
  294. {
  295. return start (StringArray::fromTokens (command, true), type);
  296. }
  297. bool ChildProcess::start (const StringArray& args, const Type type)
  298. {
  299. if (args.size() == 0)
  300. return false;
  301. activeProcess = new ActiveProcess (args, type);
  302. if (activeProcess->childPID == 0)
  303. activeProcess = nullptr;
  304. return activeProcess != nullptr;
  305. }
  306. #endif
  307. }