2 ==============================================================================
4 This file is part of the Water library.
5 Copyright (c) 2016 ROLI Ltd.
6 Copyright (C) 2017-2024 Filipe Coelho <falktx@falktx.com>
8 Permission is granted to use this software under the terms of the ISC license
9 http://www.isc.org/downloads/software-support-policy/isc-license/
11 Permission to use, copy, modify, and/or distribute this software for any
12 purpose with or without fee is hereby granted, provided that the above
13 copyright notice and this permission notice appear in all copies.
15 THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD
16 TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
17 FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
18 OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
19 USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
20 TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
23 ==============================================================================
26 #include "ChildProcess.h"
27 #include "../files/File.h"
28 #include "../misc/Time.h"
31 # include <crt_externs.h>
37 # include <sys/wait.h>
40 #include "CarlaProcessUtils.hpp"
45 //=====================================================================================================================
46 class ChildProcess::ActiveProcess
49 ActiveProcess (const String
& command
)
52 STARTUPINFOA startupInfo
;
53 carla_zeroStruct(startupInfo
);
54 startupInfo
.cb
= sizeof (startupInfo
);
56 ok
= CreateProcessA (nullptr, const_cast<LPSTR
>(command
.toRawUTF8()),
57 nullptr, nullptr, TRUE
, CREATE_NO_WINDOW
| CREATE_UNICODE_ENVIRONMENT
,
58 nullptr, nullptr, &startupInfo
, &processInfo
) != FALSE
;
66 void closeProcessInfo() noexcept
71 CloseHandle (processInfo
.hThread
);
72 CloseHandle (processInfo
.hProcess
);
76 bool isRunning() const noexcept
78 return WaitForSingleObject (processInfo
.hProcess
, 0) != WAIT_OBJECT_0
;
81 bool checkRunningAndUnsetPID() noexcept
87 CloseHandle (processInfo
.hThread
);
88 CloseHandle (processInfo
.hProcess
);
92 bool killProcess() const noexcept
94 return TerminateProcess (processInfo
.hProcess
, 0) != FALSE
;
97 bool terminateProcess() const noexcept
99 return TerminateProcess (processInfo
.hProcess
, 0) != FALSE
;
102 uint32
getExitCodeAndClearPID() noexcept
105 GetExitCodeProcess (processInfo
.hProcess
, &exitCode
);
107 return (uint32
) exitCode
;
110 int getPID() const noexcept
118 PROCESS_INFORMATION processInfo
;
120 CARLA_DECLARE_NON_COPYABLE (ActiveProcess
)
123 class ChildProcess::ActiveProcess
126 ActiveProcess (const StringArray
& arguments
, const Type type
)
129 String
exe (arguments
[0].unquoted());
131 // Looks like you're trying to launch a non-existent exe or a folder (perhaps on OSX
132 // you're trying to launch the .app folder rather than the actual binary inside it?)
133 wassert (File::getCurrentWorkingDirectory().getChildFile (exe
.toRawUTF8()).existsAsFile()
134 || ! exe
.containsChar (CARLA_OS_SEP
));
137 for (int i
= 0; i
< arguments
.size(); ++i
)
138 if (arguments
[i
].isNotEmpty())
139 argv
.add (const_cast<char*> (arguments
[i
].toRawUTF8()));
151 pref
= CPU_TYPE_ARM64
;
155 pref
= CPU_TYPE_X86_64
;
162 posix_spawnattr_t attr
;
163 posix_spawnattr_init(&attr
);
164 // posix_spawnattr_setflags(&attr, POSIX_SPAWN_USEVFORK);
165 CARLA_SAFE_ASSERT_RETURN(posix_spawnattr_setbinpref_np(&attr
, 1, &pref
, nullptr) == 0,);
166 char*** const environptr
= _NSGetEnviron();
167 CARLA_SAFE_ASSERT_RETURN(posix_spawn(&result
, exe
.toRawUTF8(), nullptr, &attr
,
168 argv
.getRawDataPointer(), environptr
!= nullptr ? *environptr
: nullptr) == 0,);
169 posix_spawnattr_destroy(&attr
);
171 const pid_t result
= vfork();
179 else if (result
== 0)
182 carla_terminateProcessOnParentExit(true);
184 if (execvp (exe
.toRawUTF8(), argv
.getRawDataPointer()))
190 // we're the parent process..
202 CARLA_SAFE_ASSERT_INT(childPID
== 0, childPID
);
205 bool isRunning() const noexcept
210 const int pid
= waitpid (childPID
, &childState
, WNOHANG
|WUNTRACED
);
211 return pid
== 0 || ! (WIFEXITED (childState
) || WIFSIGNALED (childState
) || WIFSTOPPED (childState
));
217 bool checkRunningAndUnsetPID() noexcept
222 const int pid
= waitpid (childPID
, &childState
, WNOHANG
|WUNTRACED
);
225 if ( ! (WIFEXITED (childState
) || WIFSIGNALED (childState
) || WIFSTOPPED (childState
)))
235 bool killProcess() noexcept
237 if (::kill (childPID
, SIGKILL
) == 0)
246 bool terminateProcess() const noexcept
248 return ::kill (childPID
, SIGTERM
) == 0;
251 uint32
getExitCodeAndClearPID() noexcept
256 const int pid
= waitpid (childPID
, &childState
, WNOHANG
);
259 if (pid
>= 0 && WIFEXITED (childState
))
260 return WEXITSTATUS (childState
);
266 int getPID() const noexcept
274 CARLA_DECLARE_NON_COPYABLE (ActiveProcess
)
278 //=====================================================================================================================
280 ChildProcess::ChildProcess() {}
281 ChildProcess::~ChildProcess() {}
283 bool ChildProcess::isRunning() const
285 return activeProcess
!= nullptr && activeProcess
->isRunning();
288 bool ChildProcess::kill()
290 return activeProcess
== nullptr || activeProcess
->killProcess();
293 bool ChildProcess::terminate()
295 return activeProcess
== nullptr || activeProcess
->terminateProcess();
298 uint32
ChildProcess::getExitCodeAndClearPID()
300 return activeProcess
!= nullptr ? activeProcess
->getExitCodeAndClearPID() : 0;
303 bool ChildProcess::waitForProcessToFinish (const int timeoutMs
)
305 const uint32 timeoutTime
= Time::getMillisecondCounter() + (uint32
) timeoutMs
;
309 if (activeProcess
== nullptr)
311 if (! activeProcess
->checkRunningAndUnsetPID())
316 while (timeoutMs
< 0 || Time::getMillisecondCounter() < timeoutTime
);
321 uint32
ChildProcess::getPID() const noexcept
323 return activeProcess
!= nullptr ? activeProcess
->getPID() : 0;
326 //=====================================================================================================================
329 bool ChildProcess::start (const String
& command
, Type
)
331 activeProcess
= new ActiveProcess (command
);
333 if (! activeProcess
->ok
)
334 activeProcess
= nullptr;
336 return activeProcess
!= nullptr;
339 bool ChildProcess::start (const StringArray
& args
, const Type type
)
343 for (int i
= 0, size
= args
.size(); i
< size
; ++i
)
345 String
arg (args
[i
]);
347 // If there are spaces, surround it with quotes. If there are quotes,
348 // replace them with \" so that CommandLineToArgv will correctly parse them.
349 if (arg
.containsAnyOf ("\" "))
350 arg
= arg
.replace ("\"", "\\\"").quoted();
358 return start (escaped
.trim(), type
);
361 bool ChildProcess::start (const String
& command
, const Type type
)
363 return start (StringArray::fromTokens (command
, true), type
);
366 bool ChildProcess::start (const StringArray
& args
, const Type type
)
368 if (args
.size() == 0)
371 activeProcess
= new ActiveProcess (args
, type
);
373 if (activeProcess
->childPID
== 0)
374 activeProcess
= nullptr;
376 return activeProcess
!= nullptr;