|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403 |
- /*
- ==============================================================================
-
- This file is part of the Water library.
- Copyright (c) 2016 ROLI Ltd.
- Copyright (C) 2017-2018 Filipe Coelho <falktx@falktx.com>
-
- Permission is granted to use this software under the terms of the ISC license
- http://www.isc.org/downloads/software-support-policy/isc-license/
-
- Permission to use, copy, modify, and/or distribute this software for any
- purpose with or without fee is hereby granted, provided that the above
- copyright notice and this permission notice appear in all copies.
-
- THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD
- TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
- FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
- OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
- USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
- TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
- OF THIS SOFTWARE.
-
- ==============================================================================
- */
-
- #include "ChildProcess.h"
- #include "../files/File.h"
- #include "../misc/Time.h"
- #include "../streams/MemoryOutputStream.h"
-
- #ifndef CARLA_OS_WIN
- # include <signal.h>
- # include <sys/wait.h>
- #endif
-
- namespace water {
-
- #ifdef CARLA_OS_WIN
- //=====================================================================================================================
- class ChildProcess::ActiveProcess
- {
- public:
- ActiveProcess (const String& command, int streamFlags)
- : ok (false), readPipe (0), writePipe (0)
- {
- SECURITY_ATTRIBUTES securityAtts;
- carla_zeroStruct(securityAtts);
- securityAtts.nLength = sizeof (securityAtts);
- securityAtts.bInheritHandle = TRUE;
-
- if (CreatePipe (&readPipe, &writePipe, &securityAtts, 0)
- && SetHandleInformation (readPipe, HANDLE_FLAG_INHERIT, 0))
- {
- STARTUPINFO startupInfo;
- carla_zeroStruct(startupInfo);
- startupInfo.cb = sizeof (startupInfo);
-
- startupInfo.hStdOutput = (streamFlags & wantStdOut) != 0 ? writePipe : 0;
- startupInfo.hStdError = (streamFlags & wantStdErr) != 0 ? writePipe : 0;
- startupInfo.dwFlags = STARTF_USESTDHANDLES;
-
- ok = CreateProcess (nullptr, const_cast<LPSTR>(command.toRawUTF8()),
- nullptr, nullptr, TRUE, CREATE_NO_WINDOW | CREATE_UNICODE_ENVIRONMENT,
- nullptr, nullptr, &startupInfo, &processInfo) != FALSE;
- }
- }
-
- ~ActiveProcess()
- {
- if (ok)
- {
- CloseHandle (processInfo.hThread);
- CloseHandle (processInfo.hProcess);
- }
-
- if (readPipe != 0)
- CloseHandle (readPipe);
-
- if (writePipe != 0)
- CloseHandle (writePipe);
- }
-
- bool isRunning() const noexcept
- {
- return WaitForSingleObject (processInfo.hProcess, 0) != WAIT_OBJECT_0;
- }
-
- int read (void* dest, int numNeeded) const noexcept
- {
- int total = 0;
-
- while (ok && numNeeded > 0)
- {
- DWORD available = 0;
-
- if (! PeekNamedPipe ((HANDLE) readPipe, nullptr, 0, nullptr, &available, nullptr))
- break;
-
- const int numToDo = jmin ((int) available, numNeeded);
-
- if (available == 0)
- {
- if (! isRunning())
- break;
-
- Sleep(5);
- }
- else
- {
- DWORD numRead = 0;
- if (! ReadFile ((HANDLE) readPipe, dest, numToDo, &numRead, nullptr))
- break;
-
- total += numRead;
- dest = addBytesToPointer (dest, numRead);
- numNeeded -= numRead;
- }
- }
-
- return total;
- }
-
- bool killProcess() const noexcept
- {
- return TerminateProcess (processInfo.hProcess, 0) != FALSE;
- }
-
- bool terminateProcess() const noexcept
- {
- return TerminateProcess (processInfo.hProcess, 0) != FALSE;
- }
-
- uint32 getExitCode() const noexcept
- {
- DWORD exitCode = 0;
- GetExitCodeProcess (processInfo.hProcess, &exitCode);
- return (uint32) exitCode;
- }
-
- int getPID() const noexcept
- {
- return 0;
- }
-
- bool ok;
-
- private:
- HANDLE readPipe, writePipe;
- PROCESS_INFORMATION processInfo;
-
- CARLA_DECLARE_NON_COPY_CLASS (ActiveProcess)
- };
- #else
- class ChildProcess::ActiveProcess
- {
- public:
- ActiveProcess (const StringArray& arguments, int streamFlags)
- : childPID (0), pipeHandle (0), readHandle (0)
- {
- String exe (arguments[0].unquoted());
-
- // Looks like you're trying to launch a non-existent exe or a folder (perhaps on OSX
- // you're trying to launch the .app folder rather than the actual binary inside it?)
- wassert (File::getCurrentWorkingDirectory().getChildFile (exe).existsAsFile()
- || ! exe.containsChar (File::separator));
-
- int pipeHandles[2] = { 0 };
-
- if (pipe (pipeHandles) == 0)
- {
- Array<char*> argv;
- for (int i = 0; i < arguments.size(); ++i)
- if (arguments[i].isNotEmpty())
- argv.add (const_cast<char*> (arguments[i].toRawUTF8()));
-
- argv.add (nullptr);
-
- const pid_t result = vfork();
-
- if (result < 0)
- {
- close (pipeHandles[0]);
- close (pipeHandles[1]);
- }
- else if (result == 0)
- {
- if (execvp (exe.toRawUTF8(), argv.getRawDataPointer()))
- _exit (-1);
- }
- else
- {
- // we're the parent process..
- childPID = result;
- pipeHandle = pipeHandles[0];
- close (pipeHandles[1]); // close the write handle
- }
-
- // FIXME
- (void)streamFlags;
- }
- }
-
- ~ActiveProcess()
- {
- if (readHandle != 0)
- fclose (readHandle);
-
- if (pipeHandle != 0)
- close (pipeHandle);
- }
-
- bool isRunning() const noexcept
- {
- if (childPID != 0)
- {
- int childState = 0;
- const int pid = waitpid (childPID, &childState, WNOHANG|WUNTRACED);
- return pid == 0 || ! (WIFEXITED (childState) || WIFSIGNALED (childState) || WIFSTOPPED (childState));
- }
-
- return false;
- }
-
- int read (void* const dest, const int numBytes) noexcept
- {
- wassert (dest != nullptr);
-
- #ifdef fdopen
- #error // the zlib headers define this function as NULL!
- #endif
-
- if (readHandle == 0 && childPID != 0)
- readHandle = fdopen (pipeHandle, "r");
-
- if (readHandle != 0)
- return (int) fread (dest, 1, (size_t) numBytes, readHandle);
-
- return 0;
- }
-
- bool killProcess() const noexcept
- {
- return ::kill (childPID, SIGKILL) == 0;
- }
-
- bool terminateProcess() const noexcept
- {
- return ::kill (childPID, SIGTERM) == 0;
- }
-
- uint32 getExitCode() const noexcept
- {
- if (childPID != 0)
- {
- int childState = 0;
- const int pid = waitpid (childPID, &childState, WNOHANG);
-
- if (pid >= 0 && WIFEXITED (childState))
- return WEXITSTATUS (childState);
- }
-
- return 0;
- }
-
- int getPID() const noexcept
- {
- return childPID;
- }
-
- int childPID;
-
- private:
- int pipeHandle;
- FILE* readHandle;
-
- CARLA_DECLARE_NON_COPY_CLASS (ActiveProcess)
- };
- #endif
-
- //=====================================================================================================================
-
- ChildProcess::ChildProcess() {}
- ChildProcess::~ChildProcess() {}
-
- bool ChildProcess::isRunning() const
- {
- return activeProcess != nullptr && activeProcess->isRunning();
- }
-
- int ChildProcess::readProcessOutput (void* dest, int numBytes)
- {
- return activeProcess != nullptr ? activeProcess->read (dest, numBytes) : 0;
- }
-
- bool ChildProcess::kill()
- {
- return activeProcess == nullptr || activeProcess->killProcess();
- }
-
- bool ChildProcess::terminate()
- {
- return activeProcess == nullptr || activeProcess->terminateProcess();
- }
-
- uint32 ChildProcess::getExitCode() const
- {
- return activeProcess != nullptr ? activeProcess->getExitCode() : 0;
- }
-
- bool ChildProcess::waitForProcessToFinish (const int timeoutMs) const
- {
- const uint32 timeoutTime = Time::getMillisecondCounter() + (uint32) timeoutMs;
-
- do
- {
- if (! isRunning())
- return true;
-
- carla_msleep(5);
- }
- while (timeoutMs < 0 || Time::getMillisecondCounter() < timeoutTime);
-
- return false;
- }
-
- String ChildProcess::readAllProcessOutput()
- {
- MemoryOutputStream result;
-
- for (;;)
- {
- char buffer [512];
- const int num = readProcessOutput (buffer, sizeof (buffer));
-
- if (num <= 0)
- break;
-
- result.write (buffer, (size_t) num);
- }
-
- return result.toString();
- }
-
- uint32 ChildProcess::getPID() const noexcept
- {
- return activeProcess != nullptr ? activeProcess->getPID() : 0;
- }
-
- //=====================================================================================================================
-
- #ifdef CARLA_OS_WIN
- bool ChildProcess::start (const String& command, int streamFlags)
- {
- activeProcess = new ActiveProcess (command, streamFlags);
-
- if (! activeProcess->ok)
- activeProcess = nullptr;
-
- return activeProcess != nullptr;
- }
-
- bool ChildProcess::start (const StringArray& args, int streamFlags)
- {
- String escaped;
-
- for (int i = 0, size = args.size(); i < size; ++i)
- {
- String arg (args[i]);
-
- // If there are spaces, surround it with quotes. If there are quotes,
- // replace them with \" so that CommandLineToArgv will correctly parse them.
- if (arg.containsAnyOf ("\" "))
- arg = arg.replace ("\"", "\\\"").quoted();
-
- escaped << arg;
-
- if (i+1 < size)
- escaped << ' ';
- }
-
- return start (escaped.trim(), streamFlags);
- }
- #else
- bool ChildProcess::start (const String& command, int streamFlags)
- {
- return start (StringArray::fromTokens (command, true), streamFlags);
- }
-
- bool ChildProcess::start (const StringArray& args, int streamFlags)
- {
- if (args.size() == 0)
- return false;
-
- activeProcess = new ActiveProcess (args, streamFlags);
-
- if (activeProcess->childPID == 0)
- activeProcess = nullptr;
-
- return activeProcess != nullptr;
- }
- #endif
-
- }
|