|
- /*
- ==============================================================================
-
- 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(0);
- }
- 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;
- }
-
- 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?)
- jassert (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
- {
- jassert (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;
- }
-
- 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();
- }
-
- 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;
- }
- 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
-
- }
|