diff options
46 files changed, 316 insertions, 3529 deletions
diff --git a/eval/src/tests/apps/analyze_onnx_model/analyze_onnx_model_test.cpp b/eval/src/tests/apps/analyze_onnx_model/analyze_onnx_model_test.cpp index 72ef0346ea3..dc4527dd5c8 100644 --- a/eval/src/tests/apps/analyze_onnx_model/analyze_onnx_model_test.cpp +++ b/eval/src/tests/apps/analyze_onnx_model/analyze_onnx_model_test.cpp @@ -97,4 +97,4 @@ TEST_F("test error: symbolic size mismatch", ServerCmd(probe_cmd, ServerCmd::cap //----------------------------------------------------------------------------- -TEST_MAIN_WITH_PROCESS_PROXY() { TEST_RUN_ALL(); } +TEST_MAIN() { TEST_RUN_ALL(); } diff --git a/eval/src/tests/apps/eval_expr/eval_expr_test.cpp b/eval/src/tests/apps/eval_expr/eval_expr_test.cpp index fd103829618..d7979c6dbea 100644 --- a/eval/src/tests/apps/eval_expr/eval_expr_test.cpp +++ b/eval/src/tests/apps/eval_expr/eval_expr_test.cpp @@ -4,7 +4,6 @@ #include <vespa/vespalib/testkit/time_bomb.h> #include <vespa/vespalib/util/stringfmt.h> #include <vespa/vespalib/data/slime/slime.h> -#include <vespa/vespalib/util/child_process.h> #include <vespa/vespalib/data/input.h> #include <vespa/vespalib/data/output.h> #include <vespa/vespalib/data/simple_buffer.h> @@ -168,4 +167,4 @@ TEST_F("require that type issues produces error", Server()) { //----------------------------------------------------------------------------- -TEST_MAIN_WITH_PROCESS_PROXY() { TEST_RUN_ALL(); } +TEST_MAIN() { TEST_RUN_ALL(); } diff --git a/eval/src/vespa/eval/eval/test/test_io.cpp b/eval/src/vespa/eval/eval/test/test_io.cpp index 044b6779431..5512ecf0c1c 100644 --- a/eval/src/vespa/eval/eval/test/test_io.cpp +++ b/eval/src/vespa/eval/eval/test/test_io.cpp @@ -66,59 +66,6 @@ StdOut::commit(size_t bytes) //----------------------------------------------------------------------------- -ChildIn::ChildIn(ChildProcess &child) - : _child(child), - _output() -{ -} - -WritableMemory -ChildIn::reserve(size_t bytes) -{ - return _output.reserve(bytes); -} - -Output & -ChildIn::commit(size_t bytes) -{ - _output.commit(bytes); - Memory buf = _output.obtain(); - REQUIRE(_child.write(buf.data, buf.size)); - _output.evict(buf.size); - return *this; -} - -//----------------------------------------------------------------------------- - -ChildOut::ChildOut(ChildProcess &child) - : _child(child), - _input() -{ - REQUIRE(_child.running()); - REQUIRE(!_child.failed()); -} - -Memory -ChildOut::obtain() -{ - if ((_input.get().size == 0) && !_child.eof()) { - WritableMemory buf = _input.reserve(4_Ki); - uint32_t res = _child.read(buf.data, buf.size); - REQUIRE((res > 0) || _child.eof()); - _input.commit(res); - } - return _input.obtain(); -} - -Input & -ChildOut::evict(size_t bytes) -{ - _input.evict(bytes); - return *this; -} - -//----------------------------------------------------------------------------- - void ServerCmd::maybe_close() { @@ -132,10 +79,8 @@ void ServerCmd::maybe_exit() { if (!_exited) { - read_until_eof(_child_stdout); - assert(_child.wait()); - assert(!_child.running()); - _exit_code = _child.getExitCode(); + read_until_eof(_child); + _exit_code = _child.join(); _exited = true; } } @@ -156,9 +101,7 @@ ServerCmd::dump_message(const char *prefix, const Slime &slime) } ServerCmd::ServerCmd(vespalib::string cmd) - : _child(cmd.c_str()), - _child_stdin(_child), - _child_stdout(_child), + : _child(cmd), _basename(fs::path(cmd).filename()), _closed(false), _exited(false), @@ -167,9 +110,7 @@ ServerCmd::ServerCmd(vespalib::string cmd) } ServerCmd::ServerCmd(vespalib::string cmd, capture_stderr_tag) - : _child(cmd.c_str(), ChildProcess::capture_stderr_tag()), - _child_stdin(_child), - _child_stdout(_child), + : _child(cmd, true), _basename(fs::path(cmd).filename()), _closed(false), _exited(false), @@ -187,9 +128,9 @@ Slime ServerCmd::invoke(const Slime &req) { dump_message("request --> ", req); - write_compact(req, _child_stdin); + write_compact(req, _child); Slime reply; - REQUIRE(JsonFormat::decode(_child_stdout, reply)); + REQUIRE(JsonFormat::decode(_child, reply)); dump_message("reply <-- ", reply); return reply; } @@ -199,12 +140,12 @@ ServerCmd::write_then_read_all(const vespalib::string &input) { vespalib::string result; dump_string("input --> ", input); - memcpy(_child_stdin.reserve(input.size()).data, input.data(), input.size()); - _child_stdin.commit(input.size()); + memcpy(_child.reserve(input.size()).data, input.data(), input.size()); + _child.commit(input.size()); maybe_close(); - for (auto mem = _child_stdout.obtain(); mem.size > 0; mem = _child_stdout.obtain()) { + for (auto mem = _child.obtain(); mem.size > 0; mem = _child.obtain()) { result.append(mem.data, mem.size); - _child_stdout.evict(mem.size); + _child.evict(mem.size); } dump_string("output <-- ", result); return result; diff --git a/eval/src/vespa/eval/eval/test/test_io.h b/eval/src/vespa/eval/eval/test/test_io.h index 62fd6588780..26fe260c857 100644 --- a/eval/src/vespa/eval/eval/test/test_io.h +++ b/eval/src/vespa/eval/eval/test/test_io.h @@ -9,7 +9,7 @@ #include <vespa/vespalib/data/simple_buffer.h> #include <vespa/vespalib/data/slime/slime.h> #include <vespa/vespalib/util/size_literals.h> -#include <vespa/vespalib/util/child_process.h> +#include <vespa/vespalib/process/process.h> #include <functional> namespace vespalib::eval::test { @@ -40,38 +40,12 @@ public: }; /** - * Output adapter used to write to stdin of a child process. - **/ -class ChildIn : public Output { - ChildProcess &_child; - SimpleBuffer _output; -public: - ChildIn(ChildProcess &child); - WritableMemory reserve(size_t bytes) override; - Output &commit(size_t bytes) override; -}; - -/** - * Input adapter used to read from stdout of a child process. - **/ -class ChildOut : public Input { - ChildProcess &_child; - SimpleBuffer _input; -public: - ChildOut(ChildProcess &child); - Memory obtain() override; - Input &evict(size_t bytes) override; -}; - -/** * A command run as a child process that acts as a server reading json * from stdin and writing json to stdout. **/ class ServerCmd { private: - ChildProcess _child; - ChildIn _child_stdin; - ChildOut _child_stdout; + Process _child; vespalib::string _basename; bool _closed; bool _exited; diff --git a/fastos/src/tests/CMakeLists.txt b/fastos/src/tests/CMakeLists.txt index dce59650973..54341ff8e9a 100644 --- a/fastos/src/tests/CMakeLists.txt +++ b/fastos/src/tests/CMakeLists.txt @@ -1,10 +1,4 @@ # Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -vespa_add_executable(fastos_processtest_app TEST - SOURCES - processtest.cpp - DEPENDS - fastos -) vespa_add_executable(fastos_filetest_app TEST SOURCES filetest.cpp diff --git a/fastos/src/tests/filetest.cpp b/fastos/src/tests/filetest.cpp index 80045269c9e..a3470810e2e 100644 --- a/fastos/src/tests/filetest.cpp +++ b/fastos/src/tests/filetest.cpp @@ -40,8 +40,6 @@ bool createFile(const char* fileName, class FileTest : public BaseTest { -private: - virtual bool useProcessStarter() const override { return true; } public: const std::string srcDir = getenv("SOURCE_DIRECTORY") ? getenv("SOURCE_DIRECTORY") : "."; const std::string roFilename = srcDir + "/hello.txt"; diff --git a/fastos/src/tests/processtest.cpp b/fastos/src/tests/processtest.cpp deleted file mode 100644 index 204b87938de..00000000000 --- a/fastos/src/tests/processtest.cpp +++ /dev/null @@ -1,292 +0,0 @@ -// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -#include "tests.h" -#include <vespa/fastos/process.h> -#include <thread> - -using namespace std::chrono_literals; -using namespace std::chrono; - -class MyListener : public FastOS_ProcessRedirectListener -{ -private: - MyListener(const MyListener&); - MyListener& operator=(const MyListener&); - - const char *_title; - int _receivedBytes; - -public: - static int _allocCount; - static int _successCount; - static int _failCount; - static std::mutex *_counterLock; - - MyListener (const char *title) - : _title(title), - _receivedBytes(0) - { - std::lock_guard<std::mutex> guard(*_counterLock); - _allocCount++; - } - - virtual ~MyListener () - { - bool isStdout = (strcmp(_title, "STDOUT") == 0); - - const int correctByteCount = 16; - - std::lock_guard<std::mutex> guard(*_counterLock); - if(_receivedBytes == (isStdout ? correctByteCount : 0)) - _successCount++; - else - _failCount++; - - _allocCount--; - } - - void OnReceiveData (const void *data, size_t length) override - { - _receivedBytes += length; - if(data != nullptr) - { - } - else - delete(this); - } -}; - -int MyListener::_allocCount = 0; -int MyListener::_successCount = 0; -int MyListener::_failCount = 0; -std::mutex *MyListener::_counterLock = nullptr; - -class ProcessTest : public BaseTest -{ -private: - bool useProcessStarter() const override { return true; } - bool useIPCHelper() const override { return true; } - ProcessTest(const ProcessTest&); - ProcessTest& operator=(const ProcessTest&); - int GetLastError () const { return errno; } - - // Flag which indicates whether an IPC message is received - // or not. - bool _gotMessage; - int _receivedMessages; - std::mutex *_counterLock; - bool _isChild; -public: - ProcessTest () - : _gotMessage(false), - _receivedMessages(0), - _counterLock(nullptr), - _isChild(true) - { - } - - void PollWaitTest () - { - TestHeader("PollWait Test"); - - FastOS_Process *xproc = new FastOS_Process("sort"); - - if(xproc->Create()) - { - int i; - for(i=0; i<10; i++) - { - bool stillRunning; - int returnCode; - - if(!xproc->PollWait(&returnCode, &stillRunning)) - { - Progress(false, "PollWait failure: %d", - GetLastError()); - break; - } - - if(i <= 5) - Progress(stillRunning, "StillRunning = %s", - stillRunning ? "true" : "false"); - - if(!stillRunning) - { - Progress(returnCode == 0, "Process exit code: %d", - returnCode); - break; - } - - if(i == 5) - { - // Make sort quit - xproc->WriteStdin(nullptr, 0); - } - - std::this_thread::sleep_for(1s); - } - - if(i == 10) - { - Progress(false, "Timeout"); - xproc->Kill(); - } - } - delete xproc; - - PrintSeparator(); - } - - void ProcessTests (bool doKill, bool stdinPre, bool waitKill) - { - const int numLoops = 100; - const int numEachTime = 40; - - MyListener::_counterLock = new std::mutex; - - char testHeader[200]; - strcpy(testHeader, "Process Test"); - if(doKill) - strcat(testHeader, " w/Kill"); - if(!stdinPre) - strcat(testHeader, " w/open stdin"); - if(waitKill) - strcat(testHeader, " w/Wait timeout"); - - TestHeader(testHeader); - - MyListener::_allocCount = 0; - MyListener::_successCount = 0; - MyListener::_failCount = 0; - - Progress(true, "Starting processes..."); - - for(int i=0; i<numLoops; i++) - { - FastOS_ProcessInterface *procs[numEachTime]; - - int j; - for(j=0; j<numEachTime; j++) - { - FastOS_ProcessInterface *xproc = - new FastOS_Process("sort", - new MyListener("STDOUT"), - new MyListener("STDERR")); - - if(xproc->Create()) - { - const char *str = "Peter\nPaul\nMary\n"; - - if(!waitKill && stdinPre) - { - xproc->WriteStdin(str, strlen(str)); - xproc->WriteStdin(nullptr, 0); - } - - if(doKill) - { - if(!xproc->Kill()) - Progress(false, "Kill failure %d", GetLastError()); - } - - if(!waitKill && !stdinPre) - { - xproc->WriteStdin(str, strlen(str)); - xproc->WriteStdin(nullptr, 0); - } - } - else - { - Progress(false, "Process.CreateWithShell failure %d", - GetLastError()); - delete xproc; - xproc = nullptr; - } - procs[j] = xproc; - } - - for(j=0; j<numEachTime; j++) - { - FastOS_ProcessInterface *xproc = procs[j]; - if(xproc == nullptr) - continue; - - int timeOut = -1; - if(waitKill) - timeOut = 1; - - steady_clock::time_point start = steady_clock::now(); - - int returnCode; - if(!xproc->Wait(&returnCode, timeOut)) - Progress(false, "Process.Wait failure %d", GetLastError()); - else - { - int checkReturnCode = 0; - if(doKill || waitKill) - checkReturnCode = FastOS_Process::KILL_EXITCODE; - if(returnCode != checkReturnCode) - Progress(false, "returnCode = %d", returnCode); - } - - if (waitKill) { - nanoseconds elapsed = steady_clock::now() - start; - if((elapsed < 900ms) || - (elapsed > 3500ms)) - { - Progress(false, "WaitKill time = %d", duration_cast<milliseconds>(elapsed).count()); - } - } - - delete xproc; - - if(waitKill) - Progress(true, "Started %d processes", i * numEachTime + j + 1); - } - - if(!waitKill && ((i % 10) == 9)) - Progress(true, "Started %d processes", (i+1) * numEachTime); - - if(waitKill && (((i+1) * numEachTime) > 50)) - break; - } - - Progress(MyListener::_allocCount == 0, "MyListener alloc count = %d", MyListener::_allocCount); - - if (!doKill && !waitKill) { - Progress(MyListener::_successCount == (2 * numLoops * numEachTime), - "MyListener _successCount = %d", MyListener::_successCount); - - Progress(MyListener::_failCount == 0, - "MyListener _failCount = %d", MyListener::_failCount); - } - - delete MyListener::_counterLock; - MyListener::_counterLock = nullptr; - - PrintSeparator(); - } - - int Main () override - { - _isChild = false; - - printf("grep for the string '%s' to detect failures.\n\n", failString); - - PollWaitTest(); - ProcessTests(false, true, false); - ProcessTests(true, true, false); - ProcessTests(true, false, false); - ProcessTests(false, true, true); - - printf("END OF TEST (%s)\n", _argv[0]); - - return allWasOk() ? 0 : 1; - } -}; - -int main (int argc, char **argv) -{ - ProcessTest app; - setvbuf(stdout, nullptr, _IOLBF, 8192); - return app.Entry(argc, argv); -} diff --git a/fastos/src/vespa/fastos/CMakeLists.txt b/fastos/src/vespa/fastos/CMakeLists.txt index 466c4f775a4..623d931e999 100644 --- a/fastos/src/vespa/fastos/CMakeLists.txt +++ b/fastos/src/vespa/fastos/CMakeLists.txt @@ -6,13 +6,10 @@ vespa_add_library(fastos_objects OBJECT file.cpp file_rw_ops.cpp linux_file.cpp - process.cpp thread.cpp unix_app.cpp unix_dynamiclibrary.cpp unix_file.cpp - unix_ipc.cpp - unix_process.cpp unix_thread.cpp ) diff --git a/fastos/src/vespa/fastos/app.cpp b/fastos/src/vespa/fastos/app.cpp index 0482cfea616..94e467d341d 100644 --- a/fastos/src/vespa/fastos/app.cpp +++ b/fastos/src/vespa/fastos/app.cpp @@ -8,13 +8,10 @@ #include "app.h" #include "file.h" -#include "process.h" #include "thread.h" #include <cstring> #include <fcntl.h> -FastOS_ApplicationInterface *FastOS_ProcessInterface::_app = nullptr; - FastOS_ThreadPool *FastOS_ApplicationInterface::GetThreadPool () { return _threadPool; @@ -22,12 +19,9 @@ FastOS_ThreadPool *FastOS_ApplicationInterface::GetThreadPool () FastOS_ApplicationInterface::FastOS_ApplicationInterface() : _threadPool(nullptr), - _processList(nullptr), - _processListMutex(nullptr), _argc(0), _argv(nullptr) { - FastOS_ProcessInterface::_app = this; #ifdef __linux__ char * fadvise = getenv("VESPA_FADVISE_OPTIONS"); if (fadvise != nullptr) { @@ -51,7 +45,6 @@ bool FastOS_ApplicationInterface::Init () if (PreThreadInit()) { if (FastOS_Thread::InitializeClass()) { if (FastOS_File::InitializeClass()) { - _processListMutex = new std::mutex; _threadPool = new FastOS_ThreadPool(128 * 1024); rc = true; } else @@ -72,12 +65,6 @@ void FastOS_ApplicationInterface::Cleanup () delete _threadPool; _threadPool = nullptr; } - - if(_processListMutex != nullptr) { - delete _processListMutex; - _processListMutex = nullptr; - } - FastOS_File::CleanupClass(); FastOS_Thread::CleanupClass(); } @@ -97,44 +84,3 @@ int FastOS_ApplicationInterface::Entry (int argc, char **argv) return rc; } - -void -FastOS_ApplicationInterface::AddChildProcess (FastOS_ProcessInterface *node) -{ - node->_prev = nullptr; - node->_next = _processList; - - if(_processList != nullptr) - _processList->_prev = node; - - _processList = node; -} - -void -FastOS_ApplicationInterface::RemoveChildProcess (FastOS_ProcessInterface *node) -{ - if(node->_prev) - node->_prev->_next = node->_next; - else - _processList = node->_next; - - if(node->_next) - { - node->_next->_prev = node->_prev; - node->_next = nullptr; - } - - if(node->_prev != nullptr) - node->_prev = nullptr; -} - -bool -FastOS_ApplicationInterface::useProcessStarter() const -{ - return false; -} -bool -FastOS_ApplicationInterface::useIPCHelper() const -{ - return useProcessStarter(); -} diff --git a/fastos/src/vespa/fastos/app.h b/fastos/src/vespa/fastos/app.h index 8d6a0b5ecbb..6aa7a9346ed 100644 --- a/fastos/src/vespa/fastos/app.h +++ b/fastos/src/vespa/fastos/app.h @@ -12,7 +12,6 @@ #include <vespa/fastos/types.h> -class FastOS_ProcessInterface; class FastOS_ThreadPool; #include <mutex> @@ -132,18 +131,7 @@ private: FastOS_ApplicationInterface& operator=(const FastOS_ApplicationInterface&); protected: - /** - * - * Indicate if a process starter is going to be used. - * Only override this one if you are going to start other processes. - * @return true if you are going to use a process starter. - */ - virtual bool useProcessStarter() const; - virtual bool useIPCHelper() const; - FastOS_ThreadPool *_threadPool; - FastOS_ProcessInterface *_processList; - std::mutex *_processListMutex; virtual bool PreThreadInit () { return true; } @@ -195,11 +183,6 @@ public: */ virtual void Cleanup (); - void AddChildProcess (FastOS_ProcessInterface *node); - void RemoveChildProcess (FastOS_ProcessInterface *node); - std::unique_lock<std::mutex> getProcessGuard() { return std::unique_lock<std::mutex>(*_processListMutex); } - FastOS_ProcessInterface *GetProcessList () { return _processList; } - FastOS_ThreadPool *GetThreadPool (); }; diff --git a/fastos/src/vespa/fastos/process.cpp b/fastos/src/vespa/fastos/process.cpp deleted file mode 100644 index 8e4f4afdc98..00000000000 --- a/fastos/src/vespa/fastos/process.cpp +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. - -#include "process.h" - -FastOS_ProcessInterface::FastOS_ProcessInterface (const char *cmdLine, - FastOS_ProcessRedirectListener *stdoutListener, - FastOS_ProcessRedirectListener *stderrListener) : - _cmdLine(cmdLine), - _stdoutListener(stdoutListener), - _stderrListener(stderrListener), - _next(nullptr), - _prev(nullptr) -{ -} - -FastOS_ProcessInterface::~FastOS_ProcessInterface () = default; diff --git a/fastos/src/vespa/fastos/process.h b/fastos/src/vespa/fastos/process.h deleted file mode 100644 index f520fcd30f8..00000000000 --- a/fastos/src/vespa/fastos/process.h +++ /dev/null @@ -1,180 +0,0 @@ -// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -//************************************************************************ -/** - * @file - * Class definitions for FastOS_ProcessInterface and - * FastOS_ProcessRedirectListener. - * - * @author Oivind H. Danielsen - */ - -#pragma once - -#include "types.h" -#include <cstddef> -#include <string> - -/** - * This class serves as a sink for redirected (piped) output from - * subclasses of @ref FastOS_ProcessInterface. - */ -class FastOS_ProcessRedirectListener -{ -public: - /** - * This method is called when new data is available from the - * process. Subclass this method to process the data. - * You should assume that any thread can invoke this method. - * For convenience the data buffer is always zero- terminated - * (static_cast<uint8_t>(data[length]) = '\\0'). - * When the pipe closes, the method is invoked with data = nullptr - * and length = 0. - * @param data Pointer to data - * @param length Length of data block in bytes - */ - virtual void OnReceiveData (const void *data, size_t length) = 0; - - virtual ~FastOS_ProcessRedirectListener () {} -}; - -class FastOS_ThreadPool; -class FastOS_ApplicationInterface; - -/** - * This class can start a process, redirect standard input, output - * and error streams, kill process, wait for process to exit - * and send IPC messages to the process. - */ -class FastOS_ProcessInterface -{ -protected: - - std::string _cmdLine; - FastOS_ProcessRedirectListener *_stdoutListener; - FastOS_ProcessRedirectListener *_stderrListener; - -public: - FastOS_ProcessInterface *_next, *_prev; - static FastOS_ApplicationInterface *_app; - - enum Constants - { - KILL_EXITCODE = 65535, /* Process killed or failed */ - CONSTEND - }; - - /** - * Constructor. Does not start the process, use @ref Create or - * @ref CreateWithShell to actually start the process. - * @param cmdLine Command line - * @param stdoutListener non-nullptr to redirect stdout - * @param stderrListener non-nullptr to redirect stderr - * @param bufferSize Size of redirect buffers - */ - FastOS_ProcessInterface (const char *cmdLine, - FastOS_ProcessRedirectListener *stdoutListener = nullptr, - FastOS_ProcessRedirectListener *stderrListener = nullptr); - - FastOS_ProcessInterface(const FastOS_ProcessInterface&) = delete; - FastOS_ProcessInterface &operator=(const FastOS_ProcessInterface &) = delete; - /** - * Destructor. - * If @ref Wait has not been called yet, it is called here. - */ - virtual ~FastOS_ProcessInterface (); - - /** - * Create and start the process. If your command line includes - * commands specific to the shell use @ref CreateWithShell instead. - * - * IPC communication currently only supports direct parent/child - * relationships. If you launch a FastOS application through - * the shell or some other script/process, the FastOS application - * might not be a direct child of your process and IPC communication - * will not work (the rest will work ok, though). - * - * This limitation might be removed in the future. - * @return Boolean success / failure - */ - virtual bool Create() = 0; - - /** - * Create and start the process using the default OS shell - * (UNIX: /bin/sh). - * - * IPC communication currently only supports direct parent/child - * relationships. If you launch a FastOS application through - * the shell or some other script/process, the FastOS application - * might not be a direct child of your process and IPC communication - * will not work (the rest will work ok, though). - * - * This limitation might be removed in the future. - * @return Boolean success / failure - */ - virtual bool CreateWithShell() = 0; - - /** - * If you are redirecting the standard input stream of the process, - * use this method to write data. To close the input stream, - * invoke @ref WriteStdin with data=nullptr. If the input stream - * is not redirected, @ref WriteStdin will fail. - * @param data Pointer to data - * @param length Length of data block in bytes - * @return Boolean success / failure - */ - virtual bool WriteStdin (const void *data, size_t length) = 0; - - /** - * Terminate the process. !!IMPORTANT LIMITATION!!: There is no guarantee - * that child processes (of the process to be killed) will be killed - * as well. - * @return Boolean success / failure - */ - virtual bool Kill () = 0; - - /** - * Wait for the process to finish / terminate. This is called - * automatically by the destructor, but it is recommended that - * it is called as early as possible to free up resources. - * @param returnCode Pointer to int which will receive - * the process return code. - * @param timeOutSeconds Number of seconds to wait before - * the process is violently killed. - * -1 = infinite wait / no timeout - * @return Boolean success / failure - */ - virtual bool Wait (int *returnCode, int timeOutSeconds = -1) = 0; - - /** - * Poll version of @ref Wait. - * This is basically @ref Wait with a timeout of 0 seconds. - * The process is not killed if the "timeout" expires. - * A boolean value, stillRunning, is set to indicate whether - * the process is still running or not. - * There is no need to invoke @ref Wait if @ref PollWait - * indicates that the process is finished. - * @param returnCode Pointer to int which will receive - * the process return code. - * @param stillRunning Pointer to boolean value which will - * be set to indicate whether the - * process is still running or not. - * @return Boolean success / failure - */ - virtual bool PollWait (int *returnCode, bool *stillRunning) = 0; - - /** - * Get process identification number. - * @return Process id - */ - virtual unsigned int GetProcessId() = 0; - - /** - * Get command line string. - * @return Command line string - */ - const char *GetCommandLine () const { return _cmdLine.c_str(); } -}; - -#include <vespa/fastos/unix_process.h> -typedef FastOS_UNIX_Process FASTOS_PREFIX(Process); - diff --git a/fastos/src/vespa/fastos/unix_app.cpp b/fastos/src/vespa/fastos/unix_app.cpp index b0baf3990ad..e94525fff22 100644 --- a/fastos/src/vespa/fastos/unix_app.cpp +++ b/fastos/src/vespa/fastos/unix_app.cpp @@ -9,19 +9,12 @@ #include "app.h" #include "time.h" -#include "process.h" -#include "unix_ipc.h" #include <unistd.h> #include <csignal> #include <getopt.h> -FastOS_UNIX_Application::FastOS_UNIX_Application () - : _processStarter(), - _ipcHelper(nullptr) -{ -} - +FastOS_UNIX_Application::FastOS_UNIX_Application() = default; FastOS_UNIX_Application::~FastOS_UNIX_Application() = default; extern "C" @@ -72,10 +65,6 @@ bool FastOS_UNIX_Application::PreThreadInit () sigemptyset(&act.sa_mask); act.sa_flags = 0; sigaction(SIGPIPE, &act, nullptr); - - if (useProcessStarter()) { - _processStarter = std::make_unique<FastOS_UNIX_ProcessStarter>(this); - } } else { rc = false; fprintf(stderr, "FastOS_ApplicationInterface::PreThreadInit failed\n"); @@ -89,13 +78,6 @@ bool FastOS_UNIX_Application::Init () if(FastOS_ApplicationInterface::Init()) { - int ipcDescriptor = -1; - - if (useIPCHelper()) { - _ipcHelper = new FastOS_UNIX_IPCHelper(this, ipcDescriptor); - GetThreadPool()->NewThread(_ipcHelper); - } - rc = true; } @@ -104,38 +86,5 @@ bool FastOS_UNIX_Application::Init () void FastOS_UNIX_Application::Cleanup () { - if(_ipcHelper != nullptr) - _ipcHelper->Exit(); - - if (_processStarter) { - { - std::unique_lock<std::mutex> guard; - if (_processListMutex) { - guard = getProcessGuard(); - } - } - _processStarter.reset(); - } - FastOS_ApplicationInterface::Cleanup(); } - -FastOS_UNIX_ProcessStarter * -FastOS_UNIX_Application::GetProcessStarter () -{ - return _processStarter.get(); -} - -void FastOS_UNIX_Application:: -AddToIPCComm (FastOS_UNIX_Process *process) -{ - if(_ipcHelper != nullptr) - _ipcHelper->AddProcess(process); -} - -void FastOS_UNIX_Application:: -RemoveFromIPCComm (FastOS_UNIX_Process *process) -{ - if(_ipcHelper != nullptr) - _ipcHelper->RemoveProcess(process); -} diff --git a/fastos/src/vespa/fastos/unix_app.h b/fastos/src/vespa/fastos/unix_app.h index 5bb6f14ad26..5e076f4482b 100644 --- a/fastos/src/vespa/fastos/unix_app.h +++ b/fastos/src/vespa/fastos/unix_app.h @@ -13,10 +13,6 @@ #include "app.h" #include <memory> -class FastOS_UNIX_ProcessStarter; -class FastOS_UNIX_IPCHelper; -class FastOS_UNIX_Process; - /** * This is the generic UNIX implementation of @ref FastOS_ApplicationInterface */ @@ -26,9 +22,6 @@ private: FastOS_UNIX_Application(const FastOS_UNIX_Application&); FastOS_UNIX_Application& operator=(const FastOS_UNIX_Application&); - std::unique_ptr<FastOS_UNIX_ProcessStarter> _processStarter; - FastOS_UNIX_IPCHelper *_ipcHelper; - protected: bool PreThreadInit () override; public: @@ -71,11 +64,6 @@ public: */ static void resetOptIndex(int OptionIndex); - FastOS_UNIX_ProcessStarter *GetProcessStarter (); bool Init () override; void Cleanup () override; - void AddToIPCComm (FastOS_UNIX_Process *process); - void RemoveFromIPCComm (FastOS_UNIX_Process *process); }; - - diff --git a/fastos/src/vespa/fastos/unix_ipc.cpp b/fastos/src/vespa/fastos/unix_ipc.cpp deleted file mode 100644 index b75d07751fa..00000000000 --- a/fastos/src/vespa/fastos/unix_ipc.cpp +++ /dev/null @@ -1,547 +0,0 @@ -// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -#include "unix_ipc.h" -#include "ringbuffer.h" -#include <cassert> -#include <cstring> -#include <cstdlib> -#include <unistd.h> -#include <fcntl.h> -#include <memory> -#include <future> - -FastOS_UNIX_IPCHelper:: -FastOS_UNIX_IPCHelper (FastOS_ApplicationInterface *app, int) - : _lock(), - _exitFlag(false), - _app(app) -{ - _wakeupPipe[0] = -1; - _wakeupPipe[1] = -1; - - if(pipe(_wakeupPipe) != 0) { - perror("pipe wakeuppipe"); - std::_Exit(1); - } else { - SetBlocking(_wakeupPipe[0], false); - SetBlocking(_wakeupPipe[1], true); - } -} - -FastOS_UNIX_IPCHelper::~FastOS_UNIX_IPCHelper () -{ - if(_wakeupPipe[0] != -1) { - close(_wakeupPipe[0]); - } - if(_wakeupPipe[1] != -1) { - close(_wakeupPipe[1]); - } -} - - -bool FastOS_UNIX_IPCHelper:: -DoWrite(FastOS_UNIX_Process::DescriptorHandle &desc) -{ - bool rc = true; - FastOS_RingBuffer *buffer = desc._writeBuffer.get(); - - auto bufferGuard = buffer->getGuard(); - int writeBytes = buffer->GetReadSpace(); - if(writeBytes > 0) - { - int bytesWritten; - do - { - bytesWritten = write(desc._fd, - buffer->GetReadPtr(), - writeBytes); - } while(bytesWritten < 0 && errno == EINTR); - - if(bytesWritten > 0) - buffer->Consume(bytesWritten); - else if(bytesWritten < 0) - { - desc.CloseHandle(); - perror("FastOS_UNIX_IPCHelper::DoWrite"); - rc = false; - } - else if(bytesWritten == 0) - desc.CloseHandle(); - } - return rc; -} - -bool FastOS_UNIX_IPCHelper:: -DoRead (FastOS_UNIX_Process::DescriptorHandle &desc) -{ - bool rc = true; - - FastOS_RingBuffer *buffer = desc._readBuffer.get(); - - auto bufferGuard = buffer->getGuard(); - int readBytes = buffer->GetWriteSpace(); - if(readBytes > 0) { - int bytesRead; - do { - bytesRead = read(desc._fd, buffer->GetWritePtr(), readBytes); - } while(bytesRead < 0 && errno == EINTR); - - if (bytesRead > 0) { - buffer->Produce(bytesRead); - } else if(bytesRead < 0) { - desc.CloseHandle(); - perror("FastOS_UNIX_IPCHelper::DoRead"); - rc = false; - } else if(bytesRead == 0) { - desc.CloseHandle(); - } - } - - return rc; -} - -bool FastOS_UNIX_IPCHelper:: -SetBlocking (int fileDescriptor, bool doBlock) -{ - bool rc=false; - - int flags = fcntl(fileDescriptor, F_GETFL, nullptr); - if (flags != -1) - { - if(doBlock) - flags &= ~O_NONBLOCK; - else - flags |= O_NONBLOCK; - rc = (fcntl(fileDescriptor, F_SETFL, flags) != -1); - } - return rc; -} - -void FastOS_UNIX_IPCHelper:: -BuildPollCheck(bool isRead, int filedes, - FastOS_RingBuffer *buffer, bool *check) -{ - if(buffer == nullptr || - filedes < 0 || - buffer->GetCloseFlag()) { - *check = false; - return; - } - - bool setIt = false; - if(isRead) - setIt = (buffer->GetWriteSpace() > 0); - else - setIt = (buffer->GetReadSpace() > 0); - *check = setIt; -} - - -void FastOS_UNIX_IPCHelper:: -PerformAsyncIO() -{ - FastOS_ProcessInterface *node; - for(node = _app->GetProcessList(); node != nullptr; node = node->_next) - { - FastOS_UNIX_Process *xproc = static_cast<FastOS_UNIX_Process *>(node); - - for(int type=0; type < int(FastOS_UNIX_Process::TYPE_READCOUNT); type++) - { - FastOS_UNIX_Process::DescriptorType type_ = - FastOS_UNIX_Process::DescriptorType(type); - FastOS_UNIX_Process::DescriptorHandle &desc = - xproc->GetDescriptorHandle(type_); - if (desc._canRead) - (void) DoRead(desc); - if (desc._canWrite) - (void) DoWrite(desc); - } - } -} - - -void FastOS_UNIX_IPCHelper:: -BuildPollChecks() -{ - FastOS_ProcessInterface *node; - for(node = _app->GetProcessList(); node != nullptr; node = node->_next) - { - FastOS_UNIX_Process *xproc = static_cast<FastOS_UNIX_Process *>(node); - - for(int type=0; type < int(FastOS_UNIX_Process::TYPE_READCOUNT); type++) - { - FastOS_UNIX_Process::DescriptorType type_ = - FastOS_UNIX_Process::DescriptorType(type); - FastOS_UNIX_Process::DescriptorHandle &desc = - xproc->GetDescriptorHandle(type_); - BuildPollCheck(false, desc._fd, desc._writeBuffer.get(), &desc._wantWrite); - BuildPollCheck(true, desc._fd, desc._readBuffer.get(), &desc._wantRead); - } - } -} - - -static pollfd * -__attribute__((__noinline__)) -ResizePollArray(pollfd **fds, unsigned int *allocnfds) -{ - pollfd *newfds; - unsigned int newallocnfds; - - if (*allocnfds == 0) - newallocnfds = 16; - else - newallocnfds = *allocnfds * 2; - newfds = static_cast<pollfd *>(malloc(newallocnfds * sizeof(pollfd))); - assert(newfds != nullptr); - - if (*allocnfds > 0) - memcpy(newfds, *fds, sizeof(pollfd) * *allocnfds); - - if (*fds != nullptr) - free(*fds); - - *fds = newfds; - newfds += *allocnfds; - *allocnfds = newallocnfds; - return newfds; -} - -void -FastOS_UNIX_IPCHelper:: -BuildPollArray(pollfd **fds, unsigned int *nfds, unsigned int *allocnfds) -{ - FastOS_ProcessInterface *node; - pollfd *rfds; - const pollfd *rfdsEnd; - int pollIdx; - - rfds = *fds; - rfdsEnd = *fds + *allocnfds; - - if (rfds >= rfdsEnd) { - rfds = ResizePollArray(fds, - allocnfds); - rfdsEnd = *fds + *allocnfds; - } - rfds->fd = _wakeupPipe[0]; - rfds->events = POLLIN; - rfds->revents = 0; - rfds++; - pollIdx = 1; - for(node = _app->GetProcessList(); node != nullptr; node = node->_next) - { - FastOS_UNIX_Process *xproc = static_cast<FastOS_UNIX_Process *>(node); - - for(int type=0; type < int(FastOS_UNIX_Process::TYPE_READCOUNT); type++) - { - FastOS_UNIX_Process::DescriptorType type_ = - FastOS_UNIX_Process::DescriptorType(type); - FastOS_UNIX_Process::DescriptorHandle &desc = - xproc->GetDescriptorHandle(type_); - - if (desc._fd >= 0 && - (desc._wantRead || desc._wantWrite)) { - if (rfds >= rfdsEnd) { - rfds = ResizePollArray(fds, - allocnfds); - rfdsEnd = *fds + *allocnfds; - } - rfds->fd = desc._fd; - rfds->events = 0; - if (desc._wantRead) - rfds->events |= POLLRDNORM; - if (desc._wantWrite) - rfds->events |= POLLWRNORM; - rfds->revents = 0; - desc._pollIdx = pollIdx; - rfds++; - pollIdx++; - } else { - desc._pollIdx = -1; - desc._canRead = false; - desc._canWrite = false; - } - } - } - - *nfds = rfds - *fds; -} - - -bool -FastOS_UNIX_IPCHelper:: -SavePollArray(pollfd *fds, unsigned int nfds) -{ - FastOS_ProcessInterface *node; - - for(node = _app->GetProcessList(); node != nullptr; node = node->_next) - { - FastOS_UNIX_Process *xproc = static_cast<FastOS_UNIX_Process *>(node); - - for(int type=0; type < int(FastOS_UNIX_Process::TYPE_READCOUNT); type++) - { - FastOS_UNIX_Process::DescriptorType type_ = - FastOS_UNIX_Process::DescriptorType(type); - FastOS_UNIX_Process::DescriptorHandle &desc = - xproc->GetDescriptorHandle(type_); - - if (desc._fd >= 0 && - static_cast<unsigned int>(desc._pollIdx) < nfds) { - int revents = fds[desc._pollIdx].revents; - - if (desc._wantRead && - (revents & - (POLLIN | POLLRDNORM | POLLERR | POLLHUP | POLLNVAL)) != 0) - desc._canRead = true; - else - desc._canRead = false; - if (desc._wantWrite && - (revents & - (POLLOUT | POLLWRNORM | POLLWRBAND | POLLERR | POLLHUP | - POLLNVAL)) != 0) - desc._canWrite = true; - else - desc._canWrite = false; - } - } - } - - if ((fds[0].revents & (POLLIN | POLLERR | POLLHUP)) != 0) - return true; - else - return false; -} - - -void FastOS_UNIX_IPCHelper:: -RemoveClosingProcesses() -{ - // We assume that not updating maxFD isn't harmless. - - FastOS_ProcessInterface *node, *next; - - for(node = _app->GetProcessList(); node != nullptr; node = next) - { - int type; - - next = node->_next; - FastOS_UNIX_Process *xproc = static_cast<FastOS_UNIX_Process *>(node); - - bool stillBusy = false; - if(!xproc->GetKillFlag()) - for(type=0; type < FastOS_UNIX_Process::TYPE_READCOUNT; type++) - { - FastOS_UNIX_Process::DescriptorType type_; - - type_ = static_cast<FastOS_UNIX_Process::DescriptorType>(type); - - FastOS_UNIX_Process::DescriptorHandle &desc = - xproc->GetDescriptorHandle(type_); - - if (desc._fd != -1) - { - if((type_ == FastOS_UNIX_Process::TYPE_STDOUT) || - (type_ == FastOS_UNIX_Process::TYPE_STDERR) || - desc._wantWrite) - { - // We still want to use this socket. - // Make sure we don't close the socket yet. - stillBusy = true; - break; - } - } - } - - if(!stillBusy) - { - if (xproc->_closing) { - // We already have the process lock at this point, - // so modifying the list is safe. - _app->RemoveChildProcess(node); - - for(type=0; type < FastOS_UNIX_Process::TYPE_READCOUNT; type++) - { - FastOS_UNIX_Process::DescriptorHandle &desc = - xproc->GetDescriptorHandle(FastOS_UNIX_Process::DescriptorType(type)); - if(desc._fd != -1) - { - // No more select on this one. - // We already know wantWrite is not set - if (desc._wantRead) - desc._wantRead = false; - } - } - - // The process destructor can now proceed - auto closingPromise(std::move(xproc->_closing)); - closingPromise->set_value(); - } - } - } -} - - -void FastOS_UNIX_IPCHelper:: -Run(FastOS_ThreadInterface *thisThread, void *arg) -{ - (void)arg; - (void)thisThread; - - FastOS_ProcessInterface *node; - pollfd *fds; - unsigned int nfds; - unsigned int allocnfds; - - fds = nullptr; - nfds = 0; - allocnfds = 0; - for(;;) - { - // Deliver messages to from child processes and parent. - { - auto guard = _app->getProcessGuard(); - for(node = _app->GetProcessList(); node != nullptr; node = node->_next) - { - FastOS_UNIX_Process *xproc = static_cast<FastOS_UNIX_Process *>(node); - PipeData(xproc, FastOS_UNIX_Process::TYPE_STDOUT); - PipeData(xproc, FastOS_UNIX_Process::TYPE_STDERR); - } - - // Setup file descriptor sets for the next select() call - BuildPollChecks(); - - // Close and signal closing processes - RemoveClosingProcesses(); - - BuildPollArray(&fds, &nfds, &allocnfds); - } - bool exitFlag = false; - { - std::lock_guard<std::mutex> guard(_lock); - exitFlag = _exitFlag; - } - if (exitFlag) - { - break; - } - - for (;;) - { - int pollRc = - poll(fds, nfds, -1); - - if(pollRc == -1) - { - int wasErrno = errno; - - if(wasErrno == EINTR) - { - continue; - } - - perror("FastOS_UNIX_IPCHelper::RunAsync select failure"); - printf("errno = %d\n", wasErrno); - for(unsigned int i = 0; i < nfds; i++) - { - if ((fds[i].events & POLLIN) != 0) - printf("Read %d\n", fds[i].fd); - if ((fds[i].events & POLLOUT) != 0) - printf("Write %d\n", fds[i].fd); - } - std::_Exit(1); - } else { - break; - } - } - - bool woken = false; - { - auto guard = _app->getProcessGuard(); - woken = SavePollArray(fds, nfds); - // Do actual IO (based on file descriptor sets and buffer contents) - PerformAsyncIO(); - } - - // Did someone want to wake us up from the poll() call? - if (woken) { - char dummy; - ssize_t nbrfp = read(_wakeupPipe[0], &dummy, 1); - if (nbrfp != 1) { - perror("FastOS_UNIX_IPCHelper wakeupPipe read failed"); - } - } - } - free(fds); - - delete this; -} - -void -FastOS_UNIX_IPCHelper::NotifyProcessListChange () -{ - char dummy = 'x'; - ssize_t nbwtp = write(_wakeupPipe[1], &dummy, 1); - if (nbwtp != 1) { - perror("FastOS_UNIX_IPCHelper: write to wakeupPipe failed"); - } -} - -void -FastOS_UNIX_IPCHelper::Exit () -{ - std::lock_guard<std::mutex> guard(_lock); - _exitFlag = true; - NotifyProcessListChange(); -} - -void -FastOS_UNIX_IPCHelper::AddProcess (FastOS_UNIX_Process *xproc) -{ - bool newStream = false; - for(int type=0; type < int(FastOS_UNIX_Process::TYPE_READCOUNT); type++) - { - FastOS_UNIX_Process::DescriptorType type_ = FastOS_UNIX_Process::DescriptorType(type); - FastOS_UNIX_Process::DescriptorHandle &desc = xproc->GetDescriptorHandle(type_); - - if (desc._fd != -1) { - newStream = true; - SetBlocking(desc._fd, false); - } - } - if(newStream) - NotifyProcessListChange(); -} - -void -FastOS_UNIX_IPCHelper::RemoveProcess (FastOS_UNIX_Process *xproc) -{ - auto closePromise = std::make_unique<std::promise<void>>(); - auto closeFuture = closePromise->get_future(); - xproc->_closing = std::move(closePromise); - NotifyProcessListChange(); - closeFuture.wait(); -} - -void -FastOS_UNIX_IPCHelper::PipeData(FastOS_UNIX_Process *process, FastOS_UNIX_Process::DescriptorType type) -{ - FastOS_UNIX_Process::DescriptorHandle &desc = process->GetDescriptorHandle(type); - FastOS_RingBuffer *buffer = desc._readBuffer.get(); - if(buffer == nullptr) - return; - - FastOS_ProcessRedirectListener *listener = process->GetListener(type); - if(listener == nullptr) - return; - - auto bufferGuard = buffer->getGuard(); - - unsigned int readSpace; - while((readSpace = buffer->GetReadSpace()) > 0) { - listener->OnReceiveData(buffer->GetReadPtr(), size_t(readSpace)); - buffer->Consume(readSpace); - } - - if(buffer->GetCloseFlag()) - process->CloseListener(type); -} diff --git a/fastos/src/vespa/fastos/unix_ipc.h b/fastos/src/vespa/fastos/unix_ipc.h deleted file mode 100644 index 13e92411011..00000000000 --- a/fastos/src/vespa/fastos/unix_ipc.h +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -#include "app.h" -#include "process.h" -#include "thread.h" -#include <poll.h> - -class FastOS_RingBuffer; - -class FastOS_UNIX_IPCHelper : public FastOS_Runnable -{ -private: - FastOS_UNIX_IPCHelper(const FastOS_UNIX_IPCHelper&); - FastOS_UNIX_IPCHelper& operator=(const FastOS_UNIX_IPCHelper&); - -protected: - std::mutex _lock; - volatile bool _exitFlag; - FastOS_ApplicationInterface *_app; - - int _wakeupPipe[2]; - - bool DoWrite (FastOS_UNIX_Process::DescriptorHandle &desc); - bool DoRead (FastOS_UNIX_Process::DescriptorHandle &desc); - bool SetBlocking (int fileDescriptor, bool doBlock); - void BuildPollCheck (bool isRead, int filedes, FastOS_RingBuffer *buffer, bool *check); - void BuildPollArray(pollfd **fds, unsigned int *nfds, unsigned int *allocnfds); - bool SavePollArray(pollfd *fds, unsigned int nfds); - void PerformAsyncIO (); - void BuildPollChecks(); - void PipeData (FastOS_UNIX_Process *process, FastOS_UNIX_Process::DescriptorType type); - void RemoveClosingProcesses(); - -public: - FastOS_UNIX_IPCHelper (FastOS_ApplicationInterface *app, int appDescriptor); - ~FastOS_UNIX_IPCHelper (); - void Run (FastOS_ThreadInterface *thisThread, void *arg) override; - void NotifyProcessListChange (); - void AddProcess (FastOS_UNIX_Process *xproc); - void RemoveProcess (FastOS_UNIX_Process *xproc); - void Exit (); -}; diff --git a/fastos/src/vespa/fastos/unix_process.cpp b/fastos/src/vespa/fastos/unix_process.cpp deleted file mode 100644 index cd29108c3d6..00000000000 --- a/fastos/src/vespa/fastos/unix_process.cpp +++ /dev/null @@ -1,985 +0,0 @@ -// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -#include "process.h" -#include "unix_ipc.h" -#include "ringbuffer.h" -#include <vector> -#include <cstring> -#include <cstdlib> -#include <unistd.h> -#include <fcntl.h> -#include <sys/wait.h> -#ifndef __linux__ -#include <signal.h> -#endif -#include <thread> - - -extern "C" -{ -extern char **environ; -} - - -using namespace std::chrono_literals; -using namespace std::chrono; - -static pid_t safe_fork () -{ - pid_t pid; - int retry = 1; - while((pid = fork()) == -1 && errno == EAGAIN) { - sleep(retry); - if (retry < 4) retry *= 2; - } - return pid; -} - -static int -normalizedWaitStatus(int status) -{ - if (WIFEXITED(status)) - return WEXITSTATUS(status); - else - return (0x80000000 | status); -} - - -// The actual process launched in the proxy process -class FastOS_UNIX_RealProcess -{ -private: - FastOS_UNIX_RealProcess(const FastOS_UNIX_RealProcess&); - FastOS_UNIX_RealProcess& operator=(const FastOS_UNIX_RealProcess&); - -public: - enum - { - STREAM_STDIN = (1 << 0), - STREAM_STDOUT = (1 << 1), - STREAM_STDERR = (1 << 2), - EXEC_SHELL = (1 << 3) - }; - -private: - pid_t _pid; - bool _terse; // Set if using direct fork (bypassing proxy process) - int _streamMask; - - int _stdinDes[2]; - int _stdoutDes[2]; - int _stderrDes[2]; - int _handshakeDes[2]; - std::string _runDir; - std::string _stdoutRedirName; - std::string _stderrRedirName; - const char *_path; - std::vector<char> _pathProgBuf; - - void CloseDescriptor(int fd); - void CloseAndResetDescriptor(int *fd); - void CloseDescriptors(); - -public: - void SetRunDir(const char * runDir) { _runDir = runDir; } - - int HandoverStdinDescriptor() { - int ret = _stdinDes[1]; - _stdinDes[1] = -1; - return ret; - } - - int HandoverStdoutDescriptor() { - int ret = _stdoutDes[0]; - _stdoutDes[0] = -1; - return ret; - } - - int HandoverStderrDescriptor() { - int ret = _stderrDes[0]; - _stderrDes[0] = -1; - return ret; - } - - FastOS_UNIX_RealProcess *_prev, *_next; - - FastOS_UNIX_RealProcess (int streamMask); - ~FastOS_UNIX_RealProcess(); - pid_t GetProcessID() const { return _pid; } - - bool IsStdinPiped() const { - return (_streamMask & STREAM_STDIN ) != 0; - } - - bool IsStdoutPiped() const { - return (_streamMask & STREAM_STDOUT) != 0; - } - - bool IsStderrPiped() const { - return (_streamMask & STREAM_STDERR) != 0; - } - - bool IsUsingShell() const { - return (_streamMask & EXEC_SHELL) != 0; - } - - void SetStdoutRedirName(const char *stdoutRedirName) { - _stdoutRedirName = stdoutRedirName; - } - - void SetStderrRedirName(const char *stderrRedirName) { - _stderrRedirName = stderrRedirName; - } - - void PrepareExecVPE (const char *prog); - - void - ExecVPE (const char *prog, - char *const args[], - char *const env[]); - - static bool IsWhiteSpace (char c); - - static const char * - NextArgument (const char *p, - const char **endArg, - int *length = nullptr); - - static int CountArguments (const char *commandLine); - - void - RedirOut(const std::string & filename, - int targetfd, - int exitCodeOnFailure); - - bool - ForkAndExec(const char *command, - char **environmentVariables, - FastOS_UNIX_Process *process); - - bool Setup(); - void SetTerse() { _terse = true; } - ssize_t HandshakeRead(void *buf, size_t len); - void HandshakeWrite(int val); -}; - - -void -FastOS_UNIX_RealProcess::CloseDescriptor(int fd) -{ - close(fd); -} - - -void -FastOS_UNIX_RealProcess::CloseAndResetDescriptor(int *fd) -{ - if (*fd == -1) - return; - CloseDescriptor(*fd); - *fd = -1; -} - - -void -FastOS_UNIX_RealProcess::CloseDescriptors() -{ - CloseAndResetDescriptor(&_stdinDes[0]); - CloseAndResetDescriptor(&_stdinDes[1]); - CloseAndResetDescriptor(&_stdoutDes[0]); - CloseAndResetDescriptor(&_stdoutDes[1]); - CloseAndResetDescriptor(&_stderrDes[0]); - CloseAndResetDescriptor(&_stderrDes[1]); - CloseAndResetDescriptor(&_handshakeDes[0]); - CloseAndResetDescriptor(&_handshakeDes[1]); -} - - -FastOS_UNIX_RealProcess::FastOS_UNIX_RealProcess(int streamMask) - : _pid(-1), - _terse(false), - _streamMask(streamMask), - _runDir(), - _stdoutRedirName(), - _stderrRedirName(), - _path(nullptr), - _pathProgBuf(), - _prev(nullptr), - _next(nullptr) -{ - _stdinDes[0] = _stdinDes[1] = -1; - _stdoutDes[0] = _stdoutDes[1] = -1; - _stderrDes[0] = _stderrDes[1] = -1; - _handshakeDes[0] = _handshakeDes[1] = -1; -} - - -FastOS_UNIX_RealProcess::~FastOS_UNIX_RealProcess() -{ - CloseDescriptors(); -} - - -void -FastOS_UNIX_RealProcess::PrepareExecVPE(const char *prog) -{ - const char *path = nullptr; - - char defaultPath[] = ":/usr/ucb:/bin:/usr/bin"; - - if (strchr(prog, '/') != nullptr) { - path = ""; - } else { - path = getenv("PATH"); - if (path == nullptr) path = defaultPath; - } - _path = path; - _pathProgBuf.resize(strlen(prog) + 1 + strlen(path) + 1); -} - - -void -FastOS_UNIX_RealProcess::ExecVPE (const char *prog, - char *const args[], - char *const env[]) -{ - - char *fullPath = &_pathProgBuf[0]; - const char *path = _path; - - for(;;) - { - char *p; - for (p = fullPath; (*path != '\0') && (*path != ':'); path++) - *p++ = *path; - - if (p > fullPath) *p++ = '/'; - - strcpy(p, prog); - // fprintf(stdout, "Attempting execve [%s]\n", fullPath); - // fflush(stdout); - execve(fullPath, args, env); - - if ((errno == ENOEXEC) || - (errno == ENOMEM) || - (errno == E2BIG) || - (errno == ETXTBSY)) - break; - - if (*path == '\0') break; - path++; - } -} - - -bool -FastOS_UNIX_RealProcess::IsWhiteSpace (char c) -{ - return (c == ' ' || c == '\t'); -} - - -const char * -FastOS_UNIX_RealProcess::NextArgument (const char *p, - const char **endArg, - int *length) -{ - while(*p != '\0') - { - if (!IsWhiteSpace(*p)) { - char quoteChar = '\0'; - if ((*p == '\'') || (*p == '"')) { - quoteChar = *p; - p++; - } - - const char *nextArg = p; - - // Find the end of the argument. - for(;;) - { - if (*p == '\0') { - if (length != nullptr) - *length = p - nextArg; - break; - } - - if (quoteChar != '\0') { - if (*p == quoteChar) { - if (length != nullptr) - *length = p - nextArg; - p++; - break; - } - } - else - { - if (IsWhiteSpace(*p)) { - if (length != nullptr) - *length = p - nextArg; - break; - } - } - p++; - } - - *endArg = p; - return nextArg; - } - p++; - } - return nullptr; -} - - -int -FastOS_UNIX_RealProcess::CountArguments (const char *commandLine) -{ - int numArgs = 0; - const char *nextArg = commandLine; - while(NextArgument(nextArg, &nextArg)) - numArgs++; - - return numArgs; -} - - -void -FastOS_UNIX_RealProcess::RedirOut(const std::string & filename, - int targetfd, - int exitCodeOnFailure) -{ - if (filename.empty() || filename[0] != '>') - return; - - int newfd; - if (filename[1] == '>') { - newfd = open(&filename[2], - O_WRONLY | O_CREAT | O_APPEND, - 0666); - if (newfd < 0) { - if (!_terse) { - fprintf(stderr, - "ERROR: Could not open %s for append: %s\n", - &filename[2], - strerror(errno)); - fflush(stderr); - } - _exit(exitCodeOnFailure); - } - } else { - newfd = open(&filename[1], - O_WRONLY | O_CREAT | O_TRUNC, - 0666); - if (newfd < 0) { - if (!_terse) { - fprintf(stderr, - "ERROR: Could not open %s for write: %s\n", - &filename[1], - strerror(errno)); - fflush(stderr); - } - _exit(exitCodeOnFailure); - } - } - if (newfd != targetfd) { - dup2(newfd, targetfd); - CloseDescriptor(newfd); - } -} - - -bool -FastOS_UNIX_RealProcess:: -ForkAndExec(const char *command, - char **environmentVariables, - FastOS_UNIX_Process *process) -{ - bool rc = false; - int numArguments = 0; - char **execArgs = nullptr; - - if (!IsUsingShell()) { - numArguments = CountArguments(command); - if (numArguments > 0) { - execArgs = new char *[numArguments + 1]; - const char *nextArg = command; - - for(int i=0; ; i++) { - int length; - const char *arg = NextArgument(nextArg, &nextArg, &length); - - if (arg == nullptr) { - // printf("ARG nullptr\n"); - execArgs[i] = nullptr; - break; - } - // printf("argLen = %d\n", length); - execArgs[i] = new char[length + 1]; - memcpy(execArgs[i], arg, length); - execArgs[i][length] = '\0'; - // printf("arg %d: [%s]\n", i, execArgs[i]); - } - PrepareExecVPE(execArgs[0]); - } - } - - _pid = safe_fork(); - if (_pid == static_cast<pid_t>(0)) { - // Fork success, child side. - if (IsStdinPiped() && _stdinDes[0] != STDIN_FILENO) { - dup2(_stdinDes[0], STDIN_FILENO); - CloseDescriptor(_stdinDes[0]); - } - _stdinDes[0] = -1; - if (IsStdoutPiped() && _stdoutDes[1] != STDOUT_FILENO) { - dup2(_stdoutDes[1], STDOUT_FILENO); - CloseDescriptor(_stdoutDes[1]); - } - _stdoutDes[1] = -1; - if (IsStderrPiped() && _stderrDes[1] != STDERR_FILENO) { - dup2(_stderrDes[1], STDERR_FILENO); - CloseDescriptor(_stderrDes[1]); - } - _stderrDes[1] = -1; - // FIX! Check error codes for dup2, and do _exit(127) if trouble - - if ( ! _runDir.empty()) { - if (chdir(_runDir.c_str())) { - if (!_terse) { - fprintf(stderr, - "ERROR: Could not chdir to %s: %s\n", - _runDir.c_str(), - strerror(errno)); - fflush(stderr); - } - _exit(126); - } - } - RedirOut(_stdoutRedirName.c_str(), STDOUT_FILENO, 124); - RedirOut(_stderrRedirName.c_str(), STDERR_FILENO, 125); - - CloseDescriptor(_handshakeDes[0]); - _handshakeDes[0] = -1; - if (process != nullptr) { - int fdlimit = sysconf(_SC_OPEN_MAX); - // Close everything else - // printf("fdlimit = %d\n", fdlimit); - for(int fd = STDERR_FILENO + 1; fd < fdlimit; fd++) - { - if (fd != _handshakeDes[1]) - CloseDescriptor(fd); - } - } - if (fcntl(_handshakeDes[1], F_SETFD, FD_CLOEXEC) != 0) _exit(127); - - HandshakeWrite(0); - - // printf("exev(p)e [%s]\n", command); - if (IsUsingShell()) { - const char *shExecArgs[4]; - - shExecArgs[0] = "sh"; - shExecArgs[1] = "-c"; - shExecArgs[2] = command; - shExecArgs[3] = nullptr; - execve("/bin/sh", - const_cast<char *const *> - (reinterpret_cast<const char *const *> - (shExecArgs)), - environmentVariables); - int error = errno; - if (!_terse) { - fprintf(stderr, - "ERROR: Could not execv /bin/sh -c '%s': %s\n", - command, - strerror(error)); - fflush(stderr); - } - HandshakeWrite(error); - } - else - { - if (numArguments > 0) { - // printf("Command: [%s]\n", execArgs[0]); - ExecVPE(execArgs[0], - static_cast<char *const *>(execArgs), - environmentVariables); - int error = errno; - if (!_terse) { - fprintf(stderr, - "ERROR: Could not execve %s with " - "path search: %s\n", - execArgs[0], - strerror(error)); - fflush(stderr); - } - HandshakeWrite(error); - } - } - _exit(127); // If execve fails, we'll get it here - } - else if(_pid != static_cast<pid_t>(-1)) - { - /* Fork success, parent side */ - - // Close unused file descriptors - if (IsStdinPiped()) { - CloseAndResetDescriptor(&_stdinDes[0]); - } - if (IsStdoutPiped()) { - CloseAndResetDescriptor(&_stdoutDes[1]); - } - if (IsStderrPiped()) { - CloseAndResetDescriptor(&_stderrDes[1]); - } - - CloseAndResetDescriptor(&_handshakeDes[1]); - - int flags = fcntl(_handshakeDes[0], F_GETFL, 0); - if (flags != -1) { - flags &= ~O_NONBLOCK; - fcntl(_handshakeDes[0], F_SETFL, flags); - } - int phase1res = 0; - ssize_t rgot = HandshakeRead(&phase1res, sizeof(int)); - bool wasError = false; - int error = 0; - if (static_cast<size_t>(rgot) != sizeof(int)) wasError = true; - else if (phase1res != 0) { - wasError = true; - error = phase1res; - } else { - int phase2res = 0; - rgot = HandshakeRead(&phase2res, sizeof(int)); - if (rgot >= 1) { - if (static_cast<size_t>(rgot) >= sizeof(int)) - error = phase2res; - wasError = true; - } - } - - if (wasError) { - int status = 0; - CloseDescriptors(); - pid_t wpid = waitpid(_pid, &status, 0); - if (wpid <= 0) { - fprintf(stderr, "ERROR: Could not start process %s\n", command); - } else if (WIFEXITED(status)) { - status = WEXITSTATUS(status); - switch (status) { - case 124: - if ( ! _stdoutRedirName.empty() && - _stdoutRedirName[0] == '>') { - if (_stdoutRedirName[1] == '>') - fprintf(stderr, "ERROR: Could not open %s for append", &_stdoutRedirName[2]); - else - fprintf(stderr, "ERROR: Could not open %s for write", &_stdoutRedirName[1]); - } - break; - case 125: - if ( ! _stderrRedirName.empty() && - _stderrRedirName[0] == '>') { - if (_stderrRedirName[1] == '>') - fprintf(stderr, "ERROR: Could not open %s for append", &_stderrRedirName[2]); - else - fprintf(stderr, "ERROR: Could not open %s for write", &_stderrRedirName[1]); - } - break; - case 126: - if ( ! _runDir.empty()) { - fprintf(stderr, "ERROR: Could not chdir to %s\n", _runDir.c_str()); - } - break; - case 127: - if (error != 0) { - std::error_code ec(error, std::system_category()); - fprintf(stderr, "ERROR: Could not execve %s: %s\n", command, ec.message().c_str()); - } else - fprintf(stderr, "ERROR: Could not execve %s\n", command); - break; - default: - fprintf(stderr, "ERROR: Could not start process %s\n", command); - break; - } - } else { - fprintf(stderr, "ERROR: Could not start process %s\n", command); - } - fflush(stderr); - } else { - rc = true; - } - } - if (execArgs != nullptr) { - char **arg = execArgs; - while (*arg != nullptr) { - delete [] *arg; - arg++; - } - delete [] execArgs; - } - - return rc; -} - - -void -FastOS_UNIX_RealProcess::HandshakeWrite(int val) -{ - if (_handshakeDes[1] == -1) - return; - const void *wbuf = &val; - size_t residue = sizeof(val); - for (;;) { - /* - * XXX: Might need to use syscall(SYS_write....) to avoid - * thread library interference. - */ - ssize_t wgot = write(_handshakeDes[1], wbuf, residue); - if (wgot < 0 && errno == EINTR) - continue; - if (wgot <= 0) - break; - wbuf = static_cast<const char *>(wbuf) + wgot; - residue -= wgot; - if (residue == 0) - break; - } -} - - -ssize_t -FastOS_UNIX_RealProcess::HandshakeRead(void *buf, size_t len) -{ - if (_handshakeDes[0] == -1) - return 0; - size_t residue = len; - ssize_t rgot = 0; - void *rbuf = buf; - for (;;) { - rgot = read(_handshakeDes[0], rbuf, residue); - if (rgot < 0 && errno == EINTR) - continue; - if (rgot <= 0) - break; - rbuf = static_cast<char *>(rbuf) + rgot; - residue -= rgot; - if (residue == 0) - break; - } - return (residue == len) ? rgot : len - residue; -} - - -bool -FastOS_UNIX_RealProcess::Setup() -{ - bool rc = true; - - if (IsStdinPiped()) rc = rc && (pipe(_stdinDes) == 0); - if (IsStdoutPiped()) rc = rc && (pipe(_stdoutDes) == 0); - if (IsStderrPiped()) rc = rc && (pipe(_stderrDes) == 0); - rc = rc && (pipe(_handshakeDes) == 0); - return rc; -} - - -FastOS_UNIX_Process:: -FastOS_UNIX_Process (const char *cmdLine, - FastOS_ProcessRedirectListener *stdoutListener, - FastOS_ProcessRedirectListener *stderrListener) : - FastOS_ProcessInterface(cmdLine, stdoutListener, stderrListener), - _pid(0), - _died(false), - _returnCode(-1), - _descriptor(), - _runDir(), - _stdoutRedirName(), - _stderrRedirName(), - _killed(false), - _closing(nullptr) -{ - constexpr uint32_t RING_BUFFER_SIZE = 0x10000; - if (stdoutListener != nullptr) - _descriptor[TYPE_STDOUT]._readBuffer = std::make_unique<FastOS_RingBuffer>(RING_BUFFER_SIZE); - if (stderrListener != nullptr) - _descriptor[TYPE_STDERR]._readBuffer = std::make_unique<FastOS_RingBuffer>(RING_BUFFER_SIZE); - - { - auto guard = _app->getProcessGuard(); - _app->AddChildProcess(this); - } - - // App::AddToIPCComm() is performed when the process is started -} - -FastOS_UNIX_Process::~FastOS_UNIX_Process () -{ - Kill(); // Kill if not dead or detached. - - if ((GetDescriptorHandle(TYPE_STDOUT)._fd != -1) || - (GetDescriptorHandle(TYPE_STDERR)._fd != -1)) - { - // Let the IPC helper flush write queues and remove us from the - // process list before we disappear. - static_cast<FastOS_UNIX_Application *>(_app)->RemoveFromIPCComm(this); - } else { - // No IPC descriptor, do it ourselves - auto guard = _app->getProcessGuard(); - _app->RemoveChildProcess(this); - } - - for(int i=0; i<int(TYPE_COUNT); i++) { - _descriptor[i]._readBuffer.reset(); - _descriptor[i]._writeBuffer.reset(); - CloseDescriptor(DescriptorType(i)); - } - - CloseListener(TYPE_STDOUT); - CloseListener(TYPE_STDERR); -} - -bool FastOS_UNIX_Process::CreateInternal (bool useShell) -{ - return GetProcessStarter()->CreateProcess(this, useShell, - _stdoutListener != nullptr, - _stderrListener != nullptr); -} - -bool FastOS_UNIX_Process::WriteStdin (const void *data, size_t length) -{ - bool rc = false; - DescriptorHandle &desc = GetDescriptorHandle(TYPE_STDIN); - - if (desc._fd != -1) { - if (data == nullptr) { - CloseDescriptor(TYPE_STDIN); - rc = true; - } - else - { - int writerc = write(desc._fd, data, length); - if (writerc < int(length)) - CloseDescriptor(TYPE_STDIN); - else - rc = true; - } - } - - return rc; -} - -bool FastOS_UNIX_Process::Signal(int sig) -{ - bool rc = false; - pid_t pid; - - auto guard = _app->getProcessGuard(); - pid = GetProcessId(); - if (pid == 0) { - /* Do nothing */ - } else if (GetDeathFlag()) { - rc = true; // The process is no longer around. - } else if (kill(pid, sig) == 0) { - if (sig == SIGKILL) - _killed = true; - rc = true; - } - return rc; -} - -bool FastOS_UNIX_Process::Kill () -{ - return Signal(SIGKILL); -} - -bool FastOS_UNIX_Process::InternalWait (int *returnCode, - int timeOutSeconds, - bool *pollStillRunning) -{ - bool rc = GetProcessStarter()->Wait(this, timeOutSeconds, - pollStillRunning); - if (rc) { - if (_killed) - *returnCode = KILL_EXITCODE; - else - *returnCode = _returnCode; - } - - return rc; -} - -bool FastOS_UNIX_Process::Wait (int *returnCode, int timeOutSeconds) -{ - return InternalWait(returnCode, timeOutSeconds, nullptr); -} - -bool FastOS_UNIX_Process::PollWait (int *returnCode, bool *stillRunning) -{ - return InternalWait(returnCode, -1, stillRunning); -} - -int FastOS_UNIX_Process::BuildStreamMask (bool useShell) -{ - int streamMask = FastOS_UNIX_RealProcess::STREAM_STDIN; - if (_stdoutListener) streamMask |= FastOS_UNIX_RealProcess::STREAM_STDOUT; - if (_stderrListener) streamMask |= FastOS_UNIX_RealProcess::STREAM_STDERR; - if (useShell) streamMask |= FastOS_UNIX_RealProcess::EXEC_SHELL; - - return streamMask; -} - - -FastOS_UNIX_ProcessStarter::FastOS_UNIX_ProcessStarter (FastOS_ApplicationInterface *app) - : _app(app), - _hasDirectChildren(false) -{ -} - -FastOS_UNIX_ProcessStarter::~FastOS_UNIX_ProcessStarter () = default; - -bool -FastOS_UNIX_ProcessStarter:: -CreateProcess (FastOS_UNIX_Process *process, bool useShell, bool pipeStdout, bool pipeStderr) -{ - bool rc = false; - - const char *cmdLine = process->GetCommandLine(); - - auto guard = _app->getProcessGuard(); - - _hasDirectChildren = true; - FastOS_UNIX_RealProcess *rprocess = new FastOS_UNIX_RealProcess(process->BuildStreamMask(useShell)); - const char *runDir = process->GetRunDir(); - if (runDir != nullptr) { - rprocess->SetRunDir(runDir); // Handover - } - const char *stdoutRedirName = process->GetStdoutRedirName(); - if (stdoutRedirName != nullptr) { - rprocess->SetStdoutRedirName(stdoutRedirName); - } - const char *stderrRedirName = process->GetStderrRedirName(); - if (stderrRedirName != nullptr) { - rprocess->SetStderrRedirName(stderrRedirName); - } - rprocess->SetTerse(); - rprocess->Setup(); - process->SetDescriptor(FastOS_UNIX_Process::TYPE_STDIN, rprocess->HandoverStdinDescriptor()); - if (pipeStdout) - process->SetDescriptor(FastOS_UNIX_Process::TYPE_STDOUT, rprocess->HandoverStdoutDescriptor()); - if (pipeStderr) - process->SetDescriptor(FastOS_UNIX_Process::TYPE_STDERR, rprocess->HandoverStderrDescriptor()); - pid_t processId = -1; - if (rprocess->ForkAndExec(cmdLine, environ, process)) { - processId = rprocess->GetProcessID(); - } - if (processId != -1) { - process->SetProcessId(static_cast<unsigned int>(processId)); - if (!useShell || pipeStdout || pipeStderr) - static_cast<FastOS_UNIX_Application *>(_app)->AddToIPCComm(process); - rc = true; - } else { - fprintf(stderr, "Forkandexec %s failed\n", cmdLine); - } - guard.unlock(); - delete rprocess; - return rc; -} - - -void -FastOS_UNIX_ProcessStarter::PollReapDirectChildren() -{ - int status; - pid_t pid; - - for (;;) { - pid = waitpid(-1, &status, WNOHANG); - if (pid <= 0) - break; - - FastOS_ProcessInterface *node; - for(node = _app->GetProcessList(); - node != nullptr; node = node->_next) - { - FastOS_UNIX_Process *xproc = static_cast<FastOS_UNIX_Process *>(node); - - if (xproc->GetProcessId() == static_cast<unsigned int>(pid)) - xproc->DeathNotification(normalizedWaitStatus(status)); - } - } -} - - -bool -FastOS_UNIX_ProcessStarter::Wait(FastOS_UNIX_Process *process, - int timeOutSeconds, - bool *pollStillRunning) -{ - bool rc = true; - - bool timeOutKillAttempted = false; - - steady_clock::time_point start = steady_clock::now(); - - if (pollStillRunning != nullptr) - *pollStillRunning = true; - - for (;;) { - { - auto guard = process->_app->getProcessGuard(); - - if (_hasDirectChildren) PollReapDirectChildren(); - } - - if (process->GetDeathFlag()) { - if (pollStillRunning != nullptr) - *pollStillRunning = false; - break; - } - - if (pollStillRunning != nullptr) - break; - - if ((timeOutSeconds != -1) && !timeOutKillAttempted) { - - if ((steady_clock::now() - start) >= seconds(timeOutSeconds)) { - process->Kill(); - timeOutKillAttempted = true; - } - } - - std::this_thread::sleep_for(100ms); - } - - return rc; -} - -FastOS_UNIX_Process::DescriptorHandle::DescriptorHandle() - : _fd(-1), - _wantRead(false), - _wantWrite(false), - _canRead(false), - _canWrite(false), - _pollIdx(-1), - _readBuffer(), - _writeBuffer() -{ -} -FastOS_UNIX_Process::DescriptorHandle::~DescriptorHandle() = default; -void -FastOS_UNIX_Process::DescriptorHandle::CloseHandle() -{ - _wantRead = false; - _wantWrite = false; - _canRead = false; - _canWrite = false; - _pollIdx = -1; - if (_fd != -1) { - close(_fd); - _fd = -1; - } - if (_readBuffer) - _readBuffer->Close(); - if (_writeBuffer) - _writeBuffer->Close(); -} diff --git a/fastos/src/vespa/fastos/unix_process.h b/fastos/src/vespa/fastos/unix_process.h deleted file mode 100644 index 43b4388e0c3..00000000000 --- a/fastos/src/vespa/fastos/unix_process.h +++ /dev/null @@ -1,172 +0,0 @@ -// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -//************************************************************************ -/** - * Class definitions for FastOS_UNIX_Process. - * - * @author Div, Oivind H. Danielsen - */ - -#pragma once - -#include "process.h" -#include "app.h" -#include <string> -#include <memory> -#include <future> - -class FastOS_UNIX_RealProcess; -class FastOS_RingBuffer; - -class FastOS_UNIX_Process : public FastOS_ProcessInterface -{ -private: - FastOS_UNIX_Process(const FastOS_UNIX_Process&); - FastOS_UNIX_Process& operator=(const FastOS_UNIX_Process&); - - unsigned int _pid; - bool _died; - int _returnCode; -public: - class DescriptorHandle - { - private: - DescriptorHandle(const DescriptorHandle &); - DescriptorHandle& operator=(const DescriptorHandle &); - - public: - int _fd; - bool _wantRead; - bool _wantWrite; - bool _canRead; - bool _canWrite; - int _pollIdx; - std::unique_ptr<FastOS_RingBuffer> _readBuffer; - std::unique_ptr<FastOS_RingBuffer> _writeBuffer; - DescriptorHandle(); - ~DescriptorHandle(); - void CloseHandle(); - }; -private: - DescriptorHandle _descriptor[4]; - - std::string _runDir; - std::string _stdoutRedirName; - std::string _stderrRedirName; - bool _killed; - - FastOS_UNIX_ProcessStarter *GetProcessStarter () { - return static_cast<FastOS_UNIX_Application *>(_app)->GetProcessStarter(); - } - - bool InternalWait (int *returnCode, int timeOutSeconds, bool *pollStillRunning); -public: - enum DescriptorType - { - TYPE_STDOUT, - TYPE_STDERR, - TYPE_STDIN, - TYPE_COUNT - }; - - enum Constants - { - TYPE_READCOUNT = 3 - }; - std::unique_ptr<std::promise<void>> _closing; - FastOS_ProcessRedirectListener *GetListener (DescriptorType type) - { - if(type == TYPE_STDOUT) - return _stdoutListener; - else if(type == TYPE_STDERR) - return _stderrListener; - - return nullptr; - } - - void CloseListener (DescriptorType type) - { - if(type == TYPE_STDOUT) - { - if(_stdoutListener != nullptr) - { - _stdoutListener->OnReceiveData(nullptr, 0); - _stdoutListener = nullptr; - } - } - else if(type == TYPE_STDERR) - { - if(_stderrListener != nullptr) - { - _stderrListener->OnReceiveData(nullptr, 0); - _stderrListener = nullptr; - } - } - } - - FastOS_UNIX_Process (const char *cmdLine, - FastOS_ProcessRedirectListener *stdoutListener = nullptr, - FastOS_ProcessRedirectListener *stderrListener = nullptr); - ~FastOS_UNIX_Process (); - bool CreateInternal (bool useShell); - bool Create () override { return CreateInternal(false); } - bool CreateWithShell () override { return CreateInternal(true); } - bool WriteStdin (const void *data, size_t length) override; - bool Signal(int sig); - bool Kill () override; - bool Wait (int *returnCode, int timeOutSeconds = -1) override; - bool PollWait (int *returnCode, bool *stillRunning) override; - void SetProcessId (unsigned int pid) { _pid = pid; } - unsigned int GetProcessId() override { return _pid; } - void DeathNotification (int returnCode) { - _returnCode = returnCode; - _died = true; - } - bool GetDeathFlag () { return _died; } - int BuildStreamMask (bool useShell); - - void CloseDescriptor (DescriptorType type) - { - _descriptor[type].CloseHandle(); - } - - void SetDescriptor (DescriptorType type, int descriptor) - { - _descriptor[type]._fd = descriptor; - } - - DescriptorHandle &GetDescriptorHandle(DescriptorType type) - { - return _descriptor[type]; - } - - bool GetKillFlag () { return _killed; } - - const char *GetRunDir() const { return _runDir.c_str(); } - const char *GetStdoutRedirName() const { return _stdoutRedirName.c_str(); } - const char *GetStderrRedirName() const { return _stderrRedirName.c_str(); } -}; - - -class FastOS_UNIX_RealProcess; -class FastOS_UNIX_ProcessStarter -{ -private: - FastOS_UNIX_ProcessStarter(const FastOS_UNIX_ProcessStarter&); - FastOS_UNIX_ProcessStarter& operator=(const FastOS_UNIX_ProcessStarter&); - -protected: - FastOS_ApplicationInterface *_app; - bool _hasDirectChildren; - - void PollReapDirectChildren(); - -public: - FastOS_UNIX_ProcessStarter (FastOS_ApplicationInterface *app); - ~FastOS_UNIX_ProcessStarter (); - - bool CreateProcess (FastOS_UNIX_Process *process, bool useShell, - bool pipeStdout, bool pipeStderr); - bool Wait (FastOS_UNIX_Process *process, int timeOutSeconds, bool *pollStillRunning); -}; - - diff --git a/fnet/src/tests/examples/examples_test.cpp b/fnet/src/tests/examples/examples_test.cpp index dc7b051bd80..b6476311adb 100644 --- a/fnet/src/tests/examples/examples_test.cpp +++ b/fnet/src/tests/examples/examples_test.cpp @@ -1,6 +1,6 @@ // Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. #include <vespa/vespalib/testkit/test_kit.h> -#include <vespa/vespalib/util/child_process.h> +#include <vespa/vespalib/process/process.h> #include <vespa/vespalib/util/size_literals.h> #include <vespa/vespalib/util/stringfmt.h> #include <vespa/vespalib/util/thread.h> @@ -11,209 +11,200 @@ static const int PORT0 = 18570; static const int PORT1 = 18571; -using vespalib::ChildProcess; +using vespalib::Process; +using vespalib::make_string_short::fmt; -bool runProc(ChildProcess &proc, std::atomic<bool> &done) { - char buf[4_Ki]; - proc.close(); // close stdin - while (proc.running() && !done) { - if (!proc.eof()) { - uint32_t res = proc.read(buf, sizeof(buf), 10); - std::string tmp(buf, res); - fprintf(stderr, "%s", tmp.c_str()); - } - vespalib::Thread::sleep(10); +int run_proc(Process &proc, vespalib::string &output) { + proc.close(); + for (auto mem = proc.obtain(); mem.size > 0; mem = proc.obtain()) { + output.append(mem.data, mem.size); + proc.evict(mem.size); } - if (done && proc.running()) { - kill(proc.getPid(), SIGTERM); - return proc.wait(60000); + return proc.join(); +} + +void consume_result(Process &proc) { + vespalib::string output; + int status = run_proc(proc, output); + fprintf(stderr, "child output(server): >>>%s<<<\n", output.c_str()); + if (status != 0) { + // Allow 'killed by SIGTERM' result status. This is needed as + // some clients will exit with success status even when the + // server is not yet running, resulting in the server being + // killed before it has installed any signal handlers. + EXPECT_TRUE(status & 0x80000000); + status &= 0x7fffffff; + EXPECT_TRUE(WIFSIGNALED(status)); + EXPECT_EQUAL(WTERMSIG(status), SIGTERM); } - return !proc.failed(); } -bool runProc(const std::string &cmd) { - bool ok = false; - for (size_t retry = 0; !ok && retry < 60; ++retry) { +bool run_with_retry(const vespalib::string &cmd) { + for (size_t retry = 0; retry < 60; ++retry) { if (retry > 0) { fprintf(stderr, "retrying command in 500ms...\n"); vespalib::Thread::sleep(500); } - std::atomic<bool> done(false); - ChildProcess proc(cmd.c_str()); - ok = runProc(proc, done); + vespalib::string output; + Process proc(cmd, true); + int status = run_proc(proc, output); + fprintf(stderr, "child output(client): >>>%s<<<\n", output.c_str()); + if (status == 0) { + return true; + } } - return ok; + fprintf(stderr, "giving up...\n"); + return false; } TEST("usage") { - std::atomic<bool> done(false); - { - ChildProcess proc("exec ../../examples/proxy/fnet_proxy_app"); - EXPECT_FALSE(runProc(proc, done)); - } - { - ChildProcess proc("exec ../../examples/ping/fnet_pingserver_app"); - EXPECT_FALSE(runProc(proc, done)); - } - { - ChildProcess proc("exec ../../examples/ping/fnet_pingclient_app"); - EXPECT_FALSE(runProc(proc, done)); - } - { - ChildProcess proc("exec ../../examples/frt/rpc/fnet_rpc_client_app"); - EXPECT_FALSE(runProc(proc, done)); - } - { - ChildProcess proc("exec ../../examples/frt/rpc/fnet_rpc_server_app"); - EXPECT_FALSE(runProc(proc, done)); - } - { - ChildProcess proc("exec ../../examples/frt/rpc/fnet_echo_client_app"); - EXPECT_FALSE(runProc(proc, done)); - } - { - ChildProcess proc("exec ../../examples/frt/rpc/vespa-rpc-info"); - EXPECT_FALSE(runProc(proc, done)); - } - { - ChildProcess proc("exec ../../examples/frt/rpc/vespa-rpc-invoke-bin"); - EXPECT_FALSE(runProc(proc, done)); - } - { - ChildProcess proc("exec ../../examples/frt/rpc/fnet_rpc_callback_server_app"); - EXPECT_FALSE(runProc(proc, done)); - } - { - ChildProcess proc("exec ../../examples/frt/rpc/fnet_rpc_callback_client_app"); - EXPECT_FALSE(runProc(proc, done)); - } + EXPECT_FALSE(Process::run("exec ../../examples/proxy/fnet_proxy_app")); + EXPECT_FALSE(Process::run("exec ../../examples/ping/fnet_pingserver_app")); + EXPECT_FALSE(Process::run("exec ../../examples/ping/fnet_pingclient_app")); + EXPECT_FALSE(Process::run("exec ../../examples/frt/rpc/fnet_rpc_client_app")); + EXPECT_FALSE(Process::run("exec ../../examples/frt/rpc/fnet_rpc_server_app")); + EXPECT_FALSE(Process::run("exec ../../examples/frt/rpc/fnet_echo_client_app")); + EXPECT_FALSE(Process::run("exec ../../examples/frt/rpc/vespa-rpc-info")); + EXPECT_FALSE(Process::run("exec ../../examples/frt/rpc/vespa-rpc-invoke-bin")); + EXPECT_FALSE(Process::run("exec ../../examples/frt/rpc/fnet_rpc_callback_server_app")); + EXPECT_FALSE(Process::run("exec ../../examples/frt/rpc/fnet_rpc_callback_client_app")); } TEST("timeout") { - std::string out; - EXPECT_TRUE(ChildProcess::run("exec ../../examples/timeout/fnet_timeout_app", out)); + vespalib::string out; + EXPECT_TRUE(Process::run("exec ../../examples/timeout/fnet_timeout_app", out)); fprintf(stderr, "%s\n", out.c_str()); } -TEST_MT_F("ping", 2, std::atomic<bool>()) { +TEST_MT_F("ping", 2, pid_t(-1)) { if (thread_id == 0) { - ChildProcess proc(vespalib::make_string("exec ../../examples/ping/fnet_pingserver_app tcp/%d", - PORT0).c_str()); + Process proc(fmt("exec ../../examples/ping/fnet_pingserver_app tcp/%d", + PORT0), true); + f1 = proc.pid(); TEST_BARRIER(); - EXPECT_TRUE(runProc(proc, f1)); + TEST_DO(consume_result(proc)); } else { TEST_BARRIER(); - EXPECT_TRUE(runProc(vespalib::make_string("exec ../../examples/ping/fnet_pingclient_app tcp/localhost:%d", - PORT0).c_str())); - f1 = true; + EXPECT_TRUE(run_with_retry(fmt("exec ../../examples/ping/fnet_pingclient_app tcp/localhost:%d", + PORT0))); + kill(f1, SIGTERM); } } -TEST_MT_F("ping times out", 2, std::atomic<bool>()) { +TEST_MT_F("ping times out", 2, pid_t(-1)) { if (thread_id == 0) { - ChildProcess proc(vespalib::make_string("exec ../../examples/frt/rpc/fnet_rpc_server_app tcp/%d", - PORT0).c_str()); + Process proc(fmt("exec ../../examples/frt/rpc/fnet_rpc_server_app tcp/%d", + PORT0), true); + f1 = proc.pid(); TEST_BARRIER(); - EXPECT_TRUE(runProc(proc, f1)); + TEST_DO(consume_result(proc)); } else { float timeout_s = 0.1; TEST_BARRIER(); - EXPECT_TRUE(runProc(vespalib::make_string("exec ../../examples/ping/fnet_pingclient_app tcp/localhost:%d %f", - PORT0, timeout_s).c_str())); - f1 = true; + EXPECT_TRUE(run_with_retry(fmt("exec ../../examples/ping/fnet_pingclient_app tcp/localhost:%d %f", + PORT0, timeout_s))); + kill(f1, SIGTERM); } } -TEST_MT_F("ping with proxy", 3, std::atomic<bool>()) { +TEST_MT_FF("ping with proxy", 3, pid_t(-1), pid_t(-1)) { if (thread_id == 0) { - ChildProcess proc(vespalib::make_string("exec ../../examples/ping/fnet_pingserver_app tcp/%d", - PORT0).c_str()); + Process proc(fmt("exec ../../examples/ping/fnet_pingserver_app tcp/%d", + PORT0), true); + f1 = proc.pid(); TEST_BARRIER(); - EXPECT_TRUE(runProc(proc, f1)); + TEST_DO(consume_result(proc)); } else if (thread_id == 1) { - ChildProcess proc(vespalib::make_string("exec ../../examples/proxy/fnet_proxy_app tcp/%d tcp/localhost:%d", - PORT1, PORT0).c_str()); + Process proc(fmt("exec ../../examples/proxy/fnet_proxy_app tcp/%d tcp/localhost:%d", + PORT1, PORT0), true); + f2 = proc.pid(); TEST_BARRIER(); - EXPECT_TRUE(runProc(proc, f1)); + TEST_DO(consume_result(proc)); } else { TEST_BARRIER(); - EXPECT_TRUE(runProc(vespalib::make_string("exec ../../examples/ping/fnet_pingclient_app tcp/localhost:%d", - PORT1).c_str())); - f1 = true; + EXPECT_TRUE(run_with_retry(fmt("exec ../../examples/ping/fnet_pingclient_app tcp/localhost:%d", + PORT1))); + kill(f1, SIGTERM); + kill(f2, SIGTERM); } } -TEST_MT_F("rpc client server", 2, std::atomic<bool>()) { +TEST_MT_F("rpc client server", 2, pid_t(-1)) { if (thread_id == 0) { - ChildProcess proc(vespalib::make_string("exec ../../examples/frt/rpc/fnet_rpc_server_app tcp/%d", - PORT0).c_str()); + Process proc(fmt("exec ../../examples/frt/rpc/fnet_rpc_server_app tcp/%d", + PORT0), true); + f1 = proc.pid(); TEST_BARRIER(); - EXPECT_TRUE(runProc(proc, f1)); + TEST_DO(consume_result(proc)); } else { TEST_BARRIER(); - EXPECT_TRUE(runProc(vespalib::make_string("exec ../../examples/frt/rpc/fnet_rpc_client_app tcp/localhost:%d", - PORT0).c_str())); - f1 = true; + EXPECT_TRUE(run_with_retry(fmt("exec ../../examples/frt/rpc/fnet_rpc_client_app tcp/localhost:%d", + PORT0))); + kill(f1, SIGTERM); } } -TEST_MT_F("rpc echo client", 2, std::atomic<bool>()) { +TEST_MT_F("rpc echo client", 2, pid_t(-1)) { if (thread_id == 0) { - ChildProcess proc(vespalib::make_string("exec ../../examples/frt/rpc/fnet_rpc_server_app tcp/%d", - PORT0).c_str()); + Process proc(fmt("exec ../../examples/frt/rpc/fnet_rpc_server_app tcp/%d", + PORT0), true); + f1 = proc.pid(); TEST_BARRIER(); - EXPECT_TRUE(runProc(proc, f1)); + TEST_DO(consume_result(proc)); } else { TEST_BARRIER(); - EXPECT_TRUE(runProc(vespalib::make_string("exec ../../examples/frt/rpc/fnet_echo_client_app tcp/localhost:%d", - PORT0).c_str())); - f1 = true; + EXPECT_TRUE(run_with_retry(fmt("exec ../../examples/frt/rpc/fnet_echo_client_app tcp/localhost:%d", + PORT0))); + kill(f1, SIGTERM); } } -TEST_MT_F("rpc info", 2, std::atomic<bool>()) { +TEST_MT_F("rpc info", 2, pid_t(-1)) { if (thread_id == 0) { - ChildProcess proc(vespalib::make_string("exec ../../examples/frt/rpc/fnet_rpc_server_app tcp/%d", - PORT0).c_str()); + Process proc(fmt("exec ../../examples/frt/rpc/fnet_rpc_server_app tcp/%d", + PORT0), true); + f1 = proc.pid(); TEST_BARRIER(); - EXPECT_TRUE(runProc(proc, f1)); + TEST_DO(consume_result(proc)); } else { TEST_BARRIER(); - EXPECT_TRUE(runProc(vespalib::make_string("exec ../../examples/frt/rpc/vespa-rpc-info tcp/localhost:%d", - PORT0).c_str())); - EXPECT_TRUE(runProc(vespalib::make_string("exec ../../examples/frt/rpc/vespa-rpc-info tcp/localhost:%d verbose", - PORT0).c_str())); - f1 = true; + EXPECT_TRUE(run_with_retry(fmt("exec ../../examples/frt/rpc/vespa-rpc-info tcp/localhost:%d", + PORT0))); + EXPECT_TRUE(run_with_retry(fmt("exec ../../examples/frt/rpc/vespa-rpc-info tcp/localhost:%d verbose", + PORT0))); + kill(f1, SIGTERM); } } -TEST_MT_F("rpc invoke", 2, std::atomic<bool>()) { +TEST_MT_F("rpc invoke", 2, pid_t(-1)) { if (thread_id == 0) { - ChildProcess proc(vespalib::make_string("exec ../../examples/frt/rpc/fnet_rpc_server_app tcp/%d", - PORT0).c_str()); + Process proc(fmt("exec ../../examples/frt/rpc/fnet_rpc_server_app tcp/%d", + PORT0), true); + f1 = proc.pid(); TEST_BARRIER(); - EXPECT_TRUE(runProc(proc, f1)); + TEST_DO(consume_result(proc)); } else { TEST_BARRIER(); - EXPECT_TRUE(runProc(vespalib::make_string("exec ../../examples/frt/rpc/vespa-rpc-invoke-bin tcp/localhost:%d frt.rpc.echo " - "b:1 h:2 i:4 l:8 f:0.5 d:0.25 s:foo", - PORT0).c_str())); - f1 = true; + EXPECT_TRUE(run_with_retry(fmt("exec ../../examples/frt/rpc/vespa-rpc-invoke-bin tcp/localhost:%d frt.rpc.echo " + "b:1 h:2 i:4 l:8 f:0.5 d:0.25 s:foo", + PORT0))); + kill(f1, SIGTERM); } } -TEST_MT_F("rpc callback client server", 2, std::atomic<bool>()) { +TEST_MT_F("rpc callback client server", 2, pid_t(-1)) { if (thread_id == 0) { - ChildProcess proc(vespalib::make_string("exec ../../examples/frt/rpc/fnet_rpc_callback_server_app tcp/%d", - PORT0).c_str()); + Process proc(fmt("exec ../../examples/frt/rpc/fnet_rpc_callback_server_app tcp/%d", + PORT0), true); + f1 = proc.pid(); TEST_BARRIER(); - EXPECT_TRUE(runProc(proc, f1)); + TEST_DO(consume_result(proc)); } else { TEST_BARRIER(); - EXPECT_TRUE(runProc(vespalib::make_string("exec ../../examples/frt/rpc/fnet_rpc_callback_client_app tcp/localhost:%d", - PORT0).c_str())); - f1 = true; + EXPECT_TRUE(run_with_retry(fmt("exec ../../examples/frt/rpc/fnet_rpc_callback_client_app tcp/localhost:%d", + PORT0))); + kill(f1, SIGTERM); } } -TEST_MAIN_WITH_PROCESS_PROXY() { TEST_RUN_ALL(); } +TEST_MAIN() { TEST_RUN_ALL(); } diff --git a/searchcore/src/apps/vespa-dump-feed/vespa-dump-feed.cpp b/searchcore/src/apps/vespa-dump-feed/vespa-dump-feed.cpp index ddb41ae67a6..133912548b1 100644 --- a/searchcore/src/apps/vespa-dump-feed/vespa-dump-feed.cpp +++ b/searchcore/src/apps/vespa-dump-feed/vespa-dump-feed.cpp @@ -9,11 +9,12 @@ #include <vespa/messagebus/network/rpcnetworkparams.h> #include <vespa/vespalib/io/fileutil.h> #include <vespa/vespalib/util/signalhandler.h> -#include <vespa/vespalib/util/child_process.h> +#include <vespa/vespalib/process/process.h> #include <vespa/vespalib/util/stringfmt.h> #include <vespa/vespalib/objects/nbostream.h> #include <vespa/config/common/exceptions.h> #include <vespa/config/helper/configgetter.hpp> +#include <vespa/fastos/app.h> #include <iostream> @@ -114,7 +115,6 @@ FeedHandler::~FeedHandler() class App : public FastOS_Application { public: - virtual bool useProcessStarter() const override { return true; } virtual int Main() override; }; @@ -200,8 +200,8 @@ App::Main() std::string feedCmd(vespalib::make_string("vespa-feeder --route \"%s\" %s", route.c_str(), feedFile.c_str())); fprintf(stderr, "running feed command: %s\n", feedCmd.c_str()); - std::string feederOutput; - bool feedingOk = vespalib::ChildProcess::run(feedCmd.c_str(), feederOutput); + vespalib::string feederOutput; + bool feedingOk = vespalib::Process::run(feedCmd, feederOutput); if (!feedingOk) { fprintf(stderr, "error: feed command failed\n"); fprintf(stderr, "feed command output:\n-----\n%s\n-----\n", feederOutput.c_str()); diff --git a/searchcore/src/tests/proton/verify_ranksetup/verify_ranksetup_test.cpp b/searchcore/src/tests/proton/verify_ranksetup/verify_ranksetup_test.cpp index c6c65f45e4b..1feb46cfdb7 100644 --- a/searchcore/src/tests/proton/verify_ranksetup/verify_ranksetup_test.cpp +++ b/searchcore/src/tests/proton/verify_ranksetup/verify_ranksetup_test.cpp @@ -1,6 +1,6 @@ // Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. #include <vespa/vespalib/testkit/test_kit.h> -#include <vespa/vespalib/util/child_process.h> +#include <vespa/vespalib/process/process.h> #include <vespa/vespalib/util/stringfmt.h> #include <vespa/searchcommon/common/schema.h> #include <vespa/searchlib/fef/indexproperties.h> @@ -220,7 +220,7 @@ struct Setup { } bool verify() { generate(); - return vespalib::ChildProcess::run(fmt("%s dir:%s", prog, gen_dir.c_str()).c_str()); + return vespalib::Process::run(fmt("%s dir:%s", prog, gen_dir.c_str())); } void verify_valid(std::initializer_list<std::string> features) { for (const std::string &f: features) { @@ -288,12 +288,12 @@ struct ShadowSetup : Setup { }; TEST_F("print usage", Setup()) { - EXPECT_TRUE(!vespalib::ChildProcess::run(fmt("%s", prog).c_str())); + EXPECT_TRUE(!vespalib::Process::run(fmt("%s", prog))); } TEST_F("setup output directory", Setup()) { - ASSERT_TRUE(vespalib::ChildProcess::run(fmt("rm -rf %s", gen_dir.c_str()).c_str())); - ASSERT_TRUE(vespalib::ChildProcess::run(fmt("mkdir %s", gen_dir.c_str()).c_str())); + ASSERT_TRUE(vespalib::Process::run(fmt("rm -rf %s", gen_dir.c_str()))); + ASSERT_TRUE(vespalib::Process::run(fmt("mkdir %s", gen_dir.c_str()))); } //----------------------------------------------------------------------------- @@ -458,7 +458,7 @@ TEST_F("require that broken fragile model without dry-run passes verification", //----------------------------------------------------------------------------- TEST_F("cleanup files", Setup()) { - ASSERT_TRUE(vespalib::ChildProcess::run(fmt("rm -rf %s", gen_dir.c_str()).c_str())); + ASSERT_TRUE(vespalib::Process::run(fmt("rm -rf %s", gen_dir.c_str()))); } -TEST_MAIN_WITH_PROCESS_PROXY() { TEST_RUN_ALL(); } +TEST_MAIN() { TEST_RUN_ALL(); } diff --git a/searchlib/src/apps/vespa-ranking-expression-analyzer/vespa-ranking-expression-analyzer.cpp b/searchlib/src/apps/vespa-ranking-expression-analyzer/vespa-ranking-expression-analyzer.cpp index 8db85559f49..d4c11d5ac2f 100644 --- a/searchlib/src/apps/vespa-ranking-expression-analyzer/vespa-ranking-expression-analyzer.cpp +++ b/searchlib/src/apps/vespa-ranking-expression-analyzer/vespa-ranking-expression-analyzer.cpp @@ -331,7 +331,6 @@ State::~State() {} struct MyApp : public FastOS_Application { int Main() override; int usage(); - virtual bool useProcessStarter() const override { return false; } }; int diff --git a/staging_vespalib/src/tests/state_server/state_server_test.cpp b/staging_vespalib/src/tests/state_server/state_server_test.cpp index 5903970b740..cb80a44a675 100644 --- a/staging_vespalib/src/tests/state_server/state_server_test.cpp +++ b/staging_vespalib/src/tests/state_server/state_server_test.cpp @@ -12,7 +12,7 @@ #include <vespa/vespalib/net/simple_metrics_producer.h> #include <vespa/vespalib/net/simple_component_config_producer.h> #include <vespa/vespalib/util/stringfmt.h> -#include <vespa/vespalib/util/child_process.h> +#include <vespa/vespalib/process/process.h> #include <vespa/vespalib/net/state_explorer.h> #include <vespa/vespalib/net/slime_explorer.h> #include <vespa/vespalib/net/generic_state_handler.h> @@ -39,8 +39,8 @@ std::map<vespalib::string,vespalib::string> empty_params; //----------------------------------------------------------------------------- vespalib::string run_cmd(const vespalib::string &cmd) { - std::string out; - ASSERT_TRUE(ChildProcess::run(cmd.c_str(), out)); + vespalib::string out; + ASSERT_TRUE(Process::run(cmd.c_str(), out)); return out; } @@ -511,7 +511,7 @@ TEST("require that generic state can be explored") { check_json(json_list_two, state_handler.get(host_tag, root_path + "list/two", empty_params)); } -TEST_MAIN_WITH_PROCESS_PROXY() { +TEST_MAIN() { mkdir("var", S_IRWXU); mkdir("var/run", S_IRWXU); TEST_RUN_ALL(); diff --git a/vbench/src/tests/app_dumpurl/app_dumpurl_test.cpp b/vbench/src/tests/app_dumpurl/app_dumpurl_test.cpp index 0d5ee1e507c..3dc01725a3e 100644 --- a/vbench/src/tests/app_dumpurl/app_dumpurl_test.cpp +++ b/vbench/src/tests/app_dumpurl/app_dumpurl_test.cpp @@ -1,11 +1,11 @@ // Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. #include <vespa/vespalib/testkit/testapp.h> #include <vbench/test/all.h> -#include <vespa/vespalib/util/child_process.h> +#include <vespa/vespalib/process/process.h> #include <vespa/vespalib/net/crypto_engine.h> using namespace vbench; -using vespalib::ChildProcess; +using vespalib::Process; using InputReader = vespalib::InputReader; using OutputWriter = vespalib::OutputWriter; @@ -31,8 +31,8 @@ void readUntil(Input &input, SimpleBuffer &buffer, const string &end) { } TEST("dumpurl usage") { - std::string out; - EXPECT_FALSE(ChildProcess::run("../../apps/dumpurl/vbench_dumpurl_app", out)); + vespalib::string out; + EXPECT_FALSE(Process::run("../../apps/dumpurl/vbench_dumpurl_app", out)); fprintf(stderr, "%s\n", out.c_str()); } @@ -47,11 +47,11 @@ TEST_MT_F("run dumpurl", 2, ServerSocket()) { out.write("\r\n"); out.write("data"); } else { - std::string out; - EXPECT_TRUE(ChildProcess::run(strfmt("../../apps/dumpurl/vbench_dumpurl_app localhost %d /foo", - f1.port()).c_str(), out)); + vespalib::string out; + EXPECT_TRUE(Process::run(strfmt("../../apps/dumpurl/vbench_dumpurl_app localhost %d /foo", + f1.port()).c_str(), out)); fprintf(stderr, "%s\n", out.c_str()); } } -TEST_MAIN_WITH_PROCESS_PROXY() { TEST_RUN_ALL(); } +TEST_MAIN() { TEST_RUN_ALL(); } diff --git a/vbench/src/tests/app_vbench/app_vbench_test.cpp b/vbench/src/tests/app_vbench/app_vbench_test.cpp index f48c6fe0b05..f3223e84092 100644 --- a/vbench/src/tests/app_vbench/app_vbench_test.cpp +++ b/vbench/src/tests/app_vbench/app_vbench_test.cpp @@ -1,7 +1,7 @@ // Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. #include <vespa/vespalib/testkit/testapp.h> #include <vbench/test/all.h> -#include <vespa/vespalib/util/child_process.h> +#include <vespa/vespalib/process/process.h> #include <vespa/vespalib/net/crypto_engine.h> #include <vespa/vespalib/net/tls/tls_crypto_engine.h> #include <vespa/vespalib/test/make_tls_options_for_testing.h> @@ -11,7 +11,7 @@ #include <fcntl.h> using namespace vbench; -using vespalib::ChildProcess; +using vespalib::Process; using InputReader = vespalib::InputReader; using OutputWriter = vespalib::OutputWriter; @@ -31,8 +31,8 @@ void write_file(const vespalib::string &file_name, const vespalib::string &conte } TEST("vbench usage") { - std::string out; - EXPECT_FALSE(ChildProcess::run("../../apps/vbench/vbench_app", out)); + vespalib::string out; + EXPECT_FALSE(Process::run("../../apps/vbench/vbench_app", out)); fprintf(stderr, "%s\n", out.c_str()); } @@ -68,18 +68,18 @@ struct Servers { TEST_MT_F("run vbench", 2, Servers()) { if (thread_id == 0) { - std::string out; - EXPECT_TRUE(ChildProcess::run(strfmt("sed 's/_LOCAL_PORT_/%d/' vbench.cfg.template > vbench.cfg", f1.portal->listen_port()).c_str())); - EXPECT_TRUE(ChildProcess::run("../../apps/vbench/vbench_app run vbench.cfg 2> vbench.out", out)); + vespalib::string out; + EXPECT_TRUE(Process::run(strfmt("sed 's/_LOCAL_PORT_/%d/' vbench.cfg.template > vbench.cfg", f1.portal->listen_port()).c_str())); + EXPECT_TRUE(Process::run("../../apps/vbench/vbench_app run vbench.cfg 2> vbench.out", out)); fprintf(stderr, "null crypto: %s\n", out.c_str()); EXPECT_GREATER(f1.my_get.cnt, 10u); } else { - std::string tls_out; - EXPECT_TRUE(ChildProcess::run(strfmt("sed 's/_LOCAL_PORT_/%d/' vbench.tls.cfg.template > vbench.tls.cfg", f1.tls_portal->listen_port()).c_str())); - EXPECT_TRUE(ChildProcess::run("../../apps/vbench/vbench_app run vbench.tls.cfg 2> vbench.tls.out", tls_out)); + vespalib::string tls_out; + EXPECT_TRUE(Process::run(strfmt("sed 's/_LOCAL_PORT_/%d/' vbench.tls.cfg.template > vbench.tls.cfg", f1.tls_portal->listen_port()).c_str())); + EXPECT_TRUE(Process::run("../../apps/vbench/vbench_app run vbench.tls.cfg 2> vbench.tls.out", tls_out)); fprintf(stderr, "tls crypto: %s\n", tls_out.c_str()); EXPECT_GREATER(f1.my_tls_get.cnt, 10u); } } -TEST_MAIN_WITH_PROCESS_PROXY() { TEST_RUN_ALL(); } +TEST_MAIN() { TEST_RUN_ALL(); } diff --git a/vespalib/CMakeLists.txt b/vespalib/CMakeLists.txt index 80308a75bd5..6fb832b21d3 100644 --- a/vespalib/CMakeLists.txt +++ b/vespalib/CMakeLists.txt @@ -29,7 +29,6 @@ vespa_define_module( src/tests/btree/btree_store src/tests/btree/btree-scan-speed src/tests/btree/btree-stress - src/tests/child_process src/tests/component src/tests/compress src/tests/compression diff --git a/vespalib/src/tests/assert/assert_test.cpp b/vespalib/src/tests/assert/assert_test.cpp index 49f4d2194f4..e953fc439c3 100644 --- a/vespalib/src/tests/assert/assert_test.cpp +++ b/vespalib/src/tests/assert/assert_test.cpp @@ -1,6 +1,6 @@ // Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. #include <vespa/vespalib/testkit/test_kit.h> -#include <vespa/vespalib/util/child_process.h> +#include <vespa/vespalib/process/process.h> #include <vespa/vespalib/util/stringfmt.h> #include <vespa/vespalib/util/assert.h> #include <vespa/vespalib/io/fileutil.h> @@ -16,24 +16,21 @@ TEST("that it borks the first time.") { vespalib::rmdir("var", true); ASSERT_TRUE(vespalib::mkdir(assertDir, true)); { - ChildProcess proc("ulimit -c 0 && exec env VESPA_HOME=./ ./vespalib_asserter_app myassert 10000"); - proc.wait(); - ASSERT_EQUAL(proc.getExitCode() & 0x7f, 6); + Process proc("ulimit -c 0 && exec env VESPA_HOME=./ ./vespalib_asserter_app myassert 10000"); + ASSERT_EQUAL(proc.join() & 0x7f, 6); } { - ChildProcess proc("ulimit -c 0 && exec env VESPA_HOME=./ ./vespalib_asserter_app myassert 10000"); - proc.readLine(assertName); - proc.wait(); - ASSERT_EQUAL(proc.getExitCode() & 0x7f, 0); + Process proc("ulimit -c 0 && exec env VESPA_HOME=./ ./vespalib_asserter_app myassert 10000"); + assertName = proc.read_line(); + ASSERT_EQUAL(proc.join() & 0x7f, 0); } ASSERT_EQUAL(0, unlink(assertName.c_str())); { - ChildProcess proc("ulimit -c 0 && exec env VESPA_HOME=./ ./vespalib_asserter_app myassert 10000"); - proc.wait(); - ASSERT_EQUAL(proc.getExitCode() & 0x7f, 6); + Process proc("ulimit -c 0 && exec env VESPA_HOME=./ ./vespalib_asserter_app myassert 10000"); + ASSERT_EQUAL(proc.join() & 0x7f, 6); } ASSERT_EQUAL(0, unlink(assertName.c_str())); ASSERT_TRUE(vespalib::rmdir("var", true)); } -TEST_MAIN_WITH_PROCESS_PROXY() { TEST_RUN_ALL(); } +TEST_MAIN() { TEST_RUN_ALL(); } diff --git a/vespalib/src/tests/child_process/.gitignore b/vespalib/src/tests/child_process/.gitignore deleted file mode 100644 index 7e094c772a6..00000000000 --- a/vespalib/src/tests/child_process/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -.depend -Makefile -child_process_test -vespalib_child_process_test_app diff --git a/vespalib/src/tests/child_process/CMakeLists.txt b/vespalib/src/tests/child_process/CMakeLists.txt deleted file mode 100644 index b0503f7b08c..00000000000 --- a/vespalib/src/tests/child_process/CMakeLists.txt +++ /dev/null @@ -1,8 +0,0 @@ -# Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -vespa_add_executable(vespalib_child_process_test_app TEST - SOURCES - child_process_test.cpp - DEPENDS - vespalib -) -vespa_add_test(NAME vespalib_child_process_test_app COMMAND vespalib_child_process_test_app COST 30) diff --git a/vespalib/src/tests/child_process/child_process_test.cpp b/vespalib/src/tests/child_process/child_process_test.cpp deleted file mode 100644 index d8ed707c1bf..00000000000 --- a/vespalib/src/tests/child_process/child_process_test.cpp +++ /dev/null @@ -1,187 +0,0 @@ -// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -#include <vespa/vespalib/testkit/test_kit.h> -#include <vespa/vespalib/util/child_process.h> - -using vespalib::ChildProcess; - -TEST("simple run, ignore output") { - EXPECT_TRUE(ChildProcess::run("echo foo")); -} - -TEST("simple run, ignore output, failure") { - EXPECT_TRUE(!ChildProcess::run("false")); -} - -TEST("simple run, ignore output, timeout") { - EXPECT_TRUE(!ChildProcess::run("exec sleep 60", 10)); -} - -TEST("simple run") { - std::string out; - EXPECT_TRUE(ChildProcess::run("/bin/echo -n foo", out)); - EXPECT_EQUAL(out, "foo"); -} - -TEST("simple run, strip single-line trailing newline") { - std::string out; - EXPECT_TRUE(ChildProcess::run("echo foo", out)); - EXPECT_EQUAL(out, "foo"); -} - -TEST("simple run, don't strip multi-line output") { - std::string out; - EXPECT_TRUE(ChildProcess::run("perl -e 'print \"foo\\n\\n\"'", out)); - EXPECT_EQUAL(out, "foo\n\n"); -} - -TEST("simple run with input") { - std::string in = "bar"; - std::string out; - EXPECT_TRUE(ChildProcess::run(in, "cat", out)); - EXPECT_EQUAL(out, "bar"); -} - -TEST("simple run with input, strip single-line trailing newline") { - std::string in = "bar\n"; - std::string out; - EXPECT_TRUE(ChildProcess::run(in, "cat", out)); - EXPECT_EQUAL(out, "bar"); -} - -TEST("simple run with input, don't strip multi-line output") { - std::string in = "bar\n\n"; - std::string out; - EXPECT_TRUE(ChildProcess::run(in, "cat", out)); - EXPECT_EQUAL("bar\n\n", out); -} - -TEST_MT("simple run, partial output due to timeout", 2) { - std::string out; - std::vector<size_t> timeouts({150, 300, 3000, 6000, 60000}); - const char *my_cmd = "exec perl -e '$| = 1; print \"foo\\\n\"; sleep(600); print \"bar\\\n\"'"; - for (size_t timeout: timeouts) { - fprintf(stderr, "... verifying partial output with%s input (timeout = %zu)\n", - (thread_id == 0) ? "out" : "", timeout); - if (thread_id == 0) { - out.clear(); - EXPECT_TRUE(!ChildProcess::run(my_cmd, out, timeout)); - } else { - out.clear(); - std::string in = "ignored\n"; - EXPECT_TRUE(!ChildProcess::run(in, my_cmd, out, timeout)); - } - if (out == "foo") { - break; - } - } - EXPECT_EQUAL(out, "foo"); -} - -TEST("proc failure") { - ChildProcess proc("false"); - // read with length 0 will wait for output - EXPECT_TRUE(proc.read(NULL, 0) == 0); - EXPECT_TRUE(proc.wait(60000)); - EXPECT_TRUE(!proc.running()); - EXPECT_TRUE(proc.failed()); -} - -TEST("basic read/write") { - int x; - int read; - char buf[64]; - ChildProcess proc("cat"); - - EXPECT_TRUE(proc.running()); - EXPECT_TRUE(!proc.failed()); - EXPECT_TRUE(proc.write("foo", 3)); - for (x = 0, read = 0; x < 10 && read < 3; ++x) { - read += proc.read(buf + read, sizeof(buf) - read); - } - EXPECT_TRUE(read == 3 && memcmp(buf, "foo", 3) == 0); - EXPECT_TRUE(proc.write("bar!", 4)); - for (x = 0, read = 0; x < 10 && read < 4; ++x) { - read += proc.read(buf + read, sizeof(buf) - read); - } - EXPECT_TRUE(read == 4 && memcmp(buf, "bar!", 4) == 0); - EXPECT_TRUE(!proc.eof()); // not eof yet - EXPECT_TRUE(proc.close()); // close stdin - EXPECT_TRUE(!proc.eof()); // eof not detected yet - EXPECT_TRUE(proc.read(buf, sizeof(buf)) == 0); - EXPECT_TRUE(proc.eof()); - EXPECT_TRUE(proc.read(buf, sizeof(buf)) == 0); - EXPECT_TRUE(proc.wait(60000)); - EXPECT_TRUE(!proc.running()); - EXPECT_TRUE(!proc.failed()); -} - -TEST("continuos run, readLine") { - std::string str; - ChildProcess proc("cat"); - - EXPECT_TRUE(proc.running()); - EXPECT_TRUE(!proc.failed()); - EXPECT_TRUE(proc.write("foo\n", 4)); - EXPECT_TRUE(proc.readLine(str)); - EXPECT_EQUAL(str, "foo"); - EXPECT_TRUE(proc.write("bar!\n", 5)); - EXPECT_TRUE(proc.readLine(str)); - EXPECT_EQUAL(str, "bar!"); - EXPECT_TRUE(!proc.eof()); // not eof yet - EXPECT_TRUE(proc.close()); // close stdin - EXPECT_TRUE(!proc.eof()); // eof not detected yet - EXPECT_TRUE(!proc.readLine(str)); - EXPECT_EQUAL(str, ""); - EXPECT_TRUE(proc.eof()); - EXPECT_TRUE(!proc.readLine(str)); - EXPECT_EQUAL(str, ""); - EXPECT_TRUE(proc.wait(60000)); - EXPECT_TRUE(!proc.running()); - EXPECT_TRUE(!proc.failed()); -} - -TEST("readLine, eof flushes last line") { - std::string str; - ChildProcess proc("cat"); - - EXPECT_TRUE(proc.running()); - EXPECT_TRUE(!proc.failed()); - EXPECT_TRUE(proc.write("foo\n", 4)); - EXPECT_TRUE(proc.readLine(str)); - EXPECT_EQUAL(str, "foo"); - EXPECT_TRUE(proc.write("bar!", 4)); - EXPECT_TRUE(!proc.eof()); // not eof yet - EXPECT_TRUE(proc.close()); // close stdin - EXPECT_TRUE(!proc.eof()); // eof not detected yet - EXPECT_TRUE(proc.readLine(str)); - EXPECT_EQUAL(str, "bar!"); - EXPECT_TRUE(proc.eof()); - EXPECT_TRUE(!proc.readLine(str)); - EXPECT_EQUAL(str, ""); - EXPECT_TRUE(proc.wait(60000)); - EXPECT_TRUE(!proc.running()); - EXPECT_TRUE(!proc.failed()); -} - -TEST("long continuos run, readLine") { - std::string in; - std::string out; - ChildProcess proc("cat"); - - EXPECT_TRUE(proc.running()); - EXPECT_TRUE(!proc.failed()); - for (uint32_t i = 0; i < 10000; ++i) { - char num[32]; - sprintf(num, "%d", i); - in.assign("long continous run, line "); - in.append(num).append("\n"); - EXPECT_TRUE(proc.write(in.data(), in.length())); - in.erase(in.size() - 1, 1); - EXPECT_TRUE(proc.readLine(out)); - EXPECT_EQUAL(in, out); - } - EXPECT_TRUE(proc.running()); - EXPECT_TRUE(!proc.failed()); -} - -TEST_MAIN_WITH_PROCESS_PROXY() { TEST_RUN_ALL(); } diff --git a/vespalib/src/tests/drop-file-from-cache/drop_file_from_cache_test.cpp b/vespalib/src/tests/drop-file-from-cache/drop_file_from_cache_test.cpp index e3344fb4106..63cec1caee1 100644 --- a/vespalib/src/tests/drop-file-from-cache/drop_file_from_cache_test.cpp +++ b/vespalib/src/tests/drop-file-from-cache/drop_file_from_cache_test.cpp @@ -1,25 +1,22 @@ // Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. #include <vespa/vespalib/testkit/test_kit.h> -#include <vespa/vespalib/util/child_process.h> +#include <vespa/vespalib/process/process.h> -using vespalib::ChildProcess; +using vespalib::Process; TEST("no arguments") { - ChildProcess drop("../../apps/vespa-drop-file-from-cache/vespa-drop-file-from-cache"); - drop.wait(); - EXPECT_EQUAL(1, drop.getExitCode()); + Process drop("../../apps/vespa-drop-file-from-cache/vespa-drop-file-from-cache"); + EXPECT_EQUAL(1, drop.join()); } TEST("file does not exist") { - ChildProcess drop("../../apps/vespa-drop-file-from-cache/vespa-drop-file-from-cache not_exist"); - drop.wait(); - EXPECT_EQUAL(2, drop.getExitCode()); + Process drop("../../apps/vespa-drop-file-from-cache/vespa-drop-file-from-cache not_exist"); + EXPECT_EQUAL(2, drop.join()); } TEST("All is well") { - ChildProcess drop("../../apps/vespa-drop-file-from-cache/vespa-drop-file-from-cache vespalib_drop_file_from_cache_test_app"); - drop.wait(); - EXPECT_EQUAL(0, drop.getExitCode()); + Process drop("../../apps/vespa-drop-file-from-cache/vespa-drop-file-from-cache vespalib_drop_file_from_cache_test_app"); + EXPECT_EQUAL(0, drop.join()); } -TEST_MAIN_WITH_PROCESS_PROXY() { TEST_RUN_ALL(); } +TEST_MAIN() { TEST_RUN_ALL(); } diff --git a/vespalib/src/tests/exception_classes/silenceuncaught_test.cpp b/vespalib/src/tests/exception_classes/silenceuncaught_test.cpp index 6c757b00af6..a15e9327d16 100644 --- a/vespalib/src/tests/exception_classes/silenceuncaught_test.cpp +++ b/vespalib/src/tests/exception_classes/silenceuncaught_test.cpp @@ -1,7 +1,7 @@ // Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. #include <vespa/vespalib/testkit/test_kit.h> #include <vespa/vespalib/util/exception.h> -#include <vespa/vespalib/util/child_process.h> +#include <vespa/vespalib/process/process.h> using namespace vespalib; @@ -14,27 +14,23 @@ using namespace vespalib; #endif TEST("that uncaught exception causes negative exitcode.") { - ChildProcess proc("ulimit -c 0 && exec ./vespalib_caught_uncaught_app uncaught"); - proc.wait(); - EXPECT_LESS(proc.getExitCode(), 0); + Process proc("ulimit -c 0 && exec ./vespalib_caught_uncaught_app uncaught"); + EXPECT_LESS(proc.join(), 0); } TEST("that uncaught silenced exception causes exitcode 66") { - ChildProcess proc("exec ./vespalib_caught_uncaught_app silenced_and_uncaught"); - proc.wait(); - EXPECT_EQUAL(proc.getExitCode(), 66); + Process proc("exec ./vespalib_caught_uncaught_app silenced_and_uncaught"); + EXPECT_EQUAL(proc.join(), 66); } TEST("that caught silenced exception followed by an uncaught causes negative exitcode.") { - ChildProcess proc("ulimit -c 0 && exec ./vespalib_caught_uncaught_app uncaught_after_silenced_and_caught"); - proc.wait(); - EXPECT_LESS(proc.getExitCode(), 0); + Process proc("ulimit -c 0 && exec ./vespalib_caught_uncaught_app uncaught_after_silenced_and_caught"); + EXPECT_LESS(proc.join(), 0); } TEST("that caught silenced exception causes exitcode 0") { - ChildProcess proc("exec ./vespalib_caught_uncaught_app silenced_and_caught"); - proc.wait(); - EXPECT_EQUAL(proc.getExitCode(), 0); + Process proc("exec ./vespalib_caught_uncaught_app silenced_and_caught"); + EXPECT_EQUAL(proc.join(), 0); } #ifndef __SANITIZE_ADDRESS__ @@ -42,23 +38,20 @@ TEST("that caught silenced exception causes exitcode 0") { // setrlimit with RLIMIT_AS is broken on Darwin #else TEST("that mmap within limits are fine cause exitcode 0") { - ChildProcess proc("exec ./vespalib_mmap_app 150000000 10485760 1"); - proc.wait(); - EXPECT_EQUAL(proc.getExitCode(), 0); + Process proc("exec ./vespalib_mmap_app 150000000 10485760 1"); + EXPECT_EQUAL(proc.join(), 0); } TEST("that mmap beyond limits cause negative exitcode.") { - ChildProcess proc("ulimit -c 0 && exec ./vespalib_mmap_app 100000000 10485760 10"); - proc.wait(); - EXPECT_LESS(proc.getExitCode(), 0); + Process proc("ulimit -c 0 && exec ./vespalib_mmap_app 100000000 10485760 10"); + EXPECT_LESS(proc.join(), 0); } TEST("that mmap beyond limits with set VESPA_SILENCE_CORE_ON_OOM cause exitcode 66.") { - ChildProcess proc("VESPA_SILENCE_CORE_ON_OOM=1 exec ./vespalib_mmap_app 100000000 10485760 10"); - proc.wait(); - EXPECT_EQUAL(proc.getExitCode(), 66); + Process proc("VESPA_SILENCE_CORE_ON_OOM=1 exec ./vespalib_mmap_app 100000000 10485760 10"); + EXPECT_EQUAL(proc.join(), 66); } #endif #endif -TEST_MAIN_WITH_PROCESS_PROXY() { TEST_RUN_ALL(); } +TEST_MAIN() { TEST_RUN_ALL(); } diff --git a/vespalib/src/tests/host_name/host_name_test.cpp b/vespalib/src/tests/host_name/host_name_test.cpp index ca5aebe8c95..b68762cebb3 100644 --- a/vespalib/src/tests/host_name/host_name_test.cpp +++ b/vespalib/src/tests/host_name/host_name_test.cpp @@ -1,7 +1,6 @@ // Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. #include <vespa/vespalib/testkit/test_kit.h> #include <vespa/vespalib/util/host_name.h> -#include <vespa/vespalib/util/child_process.h> using namespace vespalib; @@ -9,4 +8,4 @@ TEST("require that host name can be obtained") { EXPECT_NOT_EQUAL("", HostName::get()); } -TEST_MAIN_WITH_PROCESS_PROXY() { TEST_RUN_ALL(); } +TEST_MAIN() { TEST_RUN_ALL(); } diff --git a/vespalib/src/tests/make_fixture_macros/make_fixture_macros_test.cpp b/vespalib/src/tests/make_fixture_macros/make_fixture_macros_test.cpp index 0264a5aad0e..1cddddf7edc 100644 --- a/vespalib/src/tests/make_fixture_macros/make_fixture_macros_test.cpp +++ b/vespalib/src/tests/make_fixture_macros/make_fixture_macros_test.cpp @@ -1,12 +1,12 @@ // Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. #include <vespa/vespalib/testkit/test_kit.h> -#include <vespa/vespalib/util/child_process.h> +#include <vespa/vespalib/process/process.h> using namespace vespalib; -bool runPrint(const char *cmd) { - std::string out; - bool res = ChildProcess::run(cmd, out); +bool runPrint(const vespalib::string &cmd) { + vespalib::string out; + bool res = Process::run(cmd, out); fprintf(stderr, "%s", out.c_str()); return res; } @@ -15,9 +15,9 @@ TEST("make fixture macros") { EXPECT_FALSE(runPrint("../../apps/make_fixture_macros/vespalib_make_fixture_macros_app")); EXPECT_TRUE(runPrint("../../apps/make_fixture_macros/vespalib_make_fixture_macros_app 9 > macros.tmp")); - std::string diffCmd("diff -u "); + vespalib::string diffCmd("diff -u "); diffCmd += TEST_PATH("../../vespa/vespalib/testkit/generated_fixture_macros.h macros.tmp"); EXPECT_TRUE(runPrint(diffCmd.c_str())); } -TEST_MAIN_WITH_PROCESS_PROXY() { TEST_RUN_ALL(); } +TEST_MAIN() { TEST_RUN_ALL(); } diff --git a/vespalib/src/tests/process/process_test.cpp b/vespalib/src/tests/process/process_test.cpp index 5f22e74ae4d..ec91bad4369 100644 --- a/vespalib/src/tests/process/process_test.cpp +++ b/vespalib/src/tests/process/process_test.cpp @@ -66,6 +66,46 @@ TEST(ProcessTest, proc_kill) { //----------------------------------------------------------------------------- +vespalib::string line1 = "this is a line"; +vespalib::string line2 = "this is also a line"; +vespalib::string line3 = "this is last line"; + +TEST(ProcessTest, read_line) { + Process proc("cat"); + for (const vespalib::string &line: {std::cref(line1), std::cref(line2), std::cref(line3)}) { + auto mem = proc.reserve(line.size() + 1); + memcpy(mem.data, line.data(), line.size()); + mem.data[line.size()] = '\n'; + proc.commit(line.size() + 1); + fprintf(stderr, "write: %s\n", line.c_str()); + auto res = proc.read_line(); + fprintf(stderr, "read: %s\n", line.c_str()); + EXPECT_EQ(res, line); + EXPECT_FALSE(proc.eof()); + } + proc.close(); + EXPECT_EQ(proc.read_line(), ""); + EXPECT_TRUE(proc.eof()); + EXPECT_EQ(proc.join(), 0); +} + +TEST(ProcessTest, read_line_without_newline) { + Process proc("cat"); + const auto &line = line3; + auto mem = proc.reserve(line.size()); + memcpy(mem.data, line.data(), line.size()); + proc.commit(line.size()); + fprintf(stderr, "write: %s\n", line.c_str()); + proc.close(); // need eof to flush line + auto res = proc.read_line(); + fprintf(stderr, "read: %s\n", line.c_str()); + EXPECT_EQ(res, line); + EXPECT_TRUE(proc.eof()); + EXPECT_EQ(proc.join(), 0); +} + +//----------------------------------------------------------------------------- + void write_slime(const Slime &slime, Output &out) { JsonFormat::encode(slime, out, true); out.reserve(1).data[0] = '\n'; diff --git a/vespalib/src/tests/tutorial/make_tutorial.cpp b/vespalib/src/tests/tutorial/make_tutorial.cpp index 46292a81005..58e3f529405 100644 --- a/vespalib/src/tests/tutorial/make_tutorial.cpp +++ b/vespalib/src/tests/tutorial/make_tutorial.cpp @@ -1,6 +1,6 @@ // Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. #include <vespa/vespalib/testkit/test_kit.h> -#include <vespa/vespalib/util/child_process.h> +#include <vespa/vespalib/process/process.h> #include <vespa/vespalib/util/stringfmt.h> #include <sys/mman.h> #include <sys/stat.h> @@ -10,48 +10,48 @@ using namespace vespalib; -std::string readFile(const std::string &filename) { +vespalib::string readFile(const vespalib::string &filename) { TEST_STATE(filename.c_str()); MappedFileInput file(filename); ASSERT_TRUE(file.valid()); Memory data = file.get(); - return std::string(data.data, data.size); + return vespalib::string(data.data, data.size); } -std::string runCommand(const std::string &cmd) { - std::string out; - ASSERT_TRUE(ChildProcess::run(cmd.c_str(), out)); +vespalib::string runCommand(const vespalib::string &cmd) { + vespalib::string out; + ASSERT_TRUE(Process::run(cmd, out)); return out; } -void insertExample(const std::string &name, const std::string &src_dir) { - std::string str = runCommand(make_string("%s/make_example.sh %s", src_dir.c_str(), +void insertExample(const vespalib::string &name, const vespalib::string &src_dir) { + vespalib::string str = runCommand(make_string("%s/make_example.sh %s", src_dir.c_str(), name.c_str())); fprintf(stdout, "%s", str.c_str()); } -void insertSource(const std::string &name, const std::string &src_dir) { - std::string str = runCommand(make_string("%s/make_source.sh %s", src_dir.c_str(), +void insertSource(const vespalib::string &name, const vespalib::string &src_dir) { + vespalib::string str = runCommand(make_string("%s/make_source.sh %s", src_dir.c_str(), name.c_str())); fprintf(stdout, "%s", str.c_str()); } -void insertFile(const std::string &name, const std::string &src_dir) { - std::string str = readFile(src_dir + "/" + name); +void insertFile(const vespalib::string &name, const vespalib::string &src_dir) { + vespalib::string str = readFile(src_dir + "/" + name); fprintf(stdout, "%s", str.c_str()); } -TEST_MAIN_WITH_PROCESS_PROXY() { - std::string pre("[insert:"); - std::string example("example:"); - std::string source("source:"); - std::string file("file:"); - std::string post("]\n"); +TEST_MAIN() { + vespalib::string pre("[insert:"); + vespalib::string example("example:"); + vespalib::string source("source:"); + vespalib::string file("file:"); + vespalib::string post("]\n"); size_t pos = 0; size_t end = 0; size_t cursor = 0; - std::string input = readFile(TEST_PATH("tutorial_source.html")); + vespalib::string input = readFile(TEST_PATH("tutorial_source.html")); while ((pos = input.find(pre, cursor)) < input.size() && (end = input.find(post, pos)) < input.size()) { @@ -59,15 +59,15 @@ TEST_MAIN_WITH_PROCESS_PROXY() { pos += pre.size(); if (input.find(example, pos) == pos) { pos += example.size(); - insertExample(std::string((input.data() + pos), (end - pos)), TEST_PATH("")); + insertExample(vespalib::string((input.data() + pos), (end - pos)), TEST_PATH("")); } else if (input.find(source, pos) == pos) { pos += source.size(); - insertSource(std::string((input.data() + pos), (end - pos)), TEST_PATH("")); + insertSource(vespalib::string((input.data() + pos), (end - pos)), TEST_PATH("")); } else if (input.find(file, pos) == pos) { pos += file.size(); - insertFile(std::string((input.data() + pos), (end - pos)), TEST_PATH("")); + insertFile(vespalib::string((input.data() + pos), (end - pos)), TEST_PATH("")); } else { - std::string str((input.data() + pos), (end - pos)); + vespalib::string str((input.data() + pos), (end - pos)); TEST_FATAL(make_string("invalid directive >%s<", str.c_str()).c_str()); } cursor = end + post.size(); diff --git a/vespalib/src/vespa/vespalib/process/process.cpp b/vespalib/src/vespa/vespalib/process/process.cpp index 3b202a830f5..7749ffdff65 100644 --- a/vespalib/src/vespa/vespalib/process/process.cpp +++ b/vespalib/src/vespa/vespalib/process/process.cpp @@ -104,6 +104,23 @@ Process::commit(size_t bytes) return *this; } +vespalib::string +Process::read_line() { + vespalib::string line; + for (auto mem = obtain(); (mem.size > 0); mem = obtain()) { + for (size_t i = 0; i < mem.size; ++i) { + if (mem.data[i] == '\n') { + evict(i + 1); + return line; + } else { + line.push_back(mem.data[i]); + } + } + evict(mem.size); + } + return line; +} + int Process::join() { diff --git a/vespalib/src/vespa/vespalib/process/process.h b/vespalib/src/vespa/vespalib/process/process.h index 97771752faa..d15784f5d31 100644 --- a/vespalib/src/vespa/vespalib/process/process.h +++ b/vespalib/src/vespa/vespalib/process/process.h @@ -43,6 +43,8 @@ public: Input &evict(size_t bytes) override; // Input (stdout) WritableMemory reserve(size_t bytes) override; // Output (stdin) Output &commit(size_t bytes) override; // Output (stdin) + vespalib::string read_line(); + bool eof() const { return _eof; } int join(); ~Process(); diff --git a/vespalib/src/vespa/vespalib/testkit/test_macros.h b/vespalib/src/vespa/vespalib/testkit/test_macros.h index 5ee8e170ef0..ebf3e06d283 100644 --- a/vespalib/src/vespa/vespalib/testkit/test_macros.h +++ b/vespalib/src/vespa/vespalib/testkit/test_macros.h @@ -18,28 +18,24 @@ #define TEST_TRACE() TEST_MASTER.trace(__FILE__, __LINE__) #define TEST_THREAD(name) TEST_MASTER.setThreadName(name) #define TEST_BARRIER() TEST_MASTER.awaitThreadBarrier(__FILE__, __LINE__) -#define TEST_MAIN_IMPL(useProxy) \ - void test_kit_main(); \ - struct TestKitApp : FastOS_Application \ - { \ - bool useProcessStarter() const override { return useProxy; } \ - int Main() override; \ - }; \ - int main(int argc, char **argv) \ - { \ - TestKitApp app; \ - return app.Entry(argc, argv); \ - } \ - int TestKitApp::Main() { \ - TEST_MASTER.init(__FILE__); \ - test_kit_main(); \ - return (TEST_MASTER.fini() ? 0 : 1); \ - } \ +#define TEST_MAIN() \ + void test_kit_main(); \ + struct TestKitApp : FastOS_Application \ + { \ + int Main() override; \ + }; \ + int main(int argc, char **argv) \ + { \ + TestKitApp app; \ + return app.Entry(argc, argv); \ + } \ + int TestKitApp::Main() { \ + TEST_MASTER.init(__FILE__); \ + test_kit_main(); \ + return (TEST_MASTER.fini() ? 0 : 1); \ + } \ void test_kit_main() -#define TEST_MAIN() TEST_MAIN_IMPL(false) -#define TEST_MAIN_WITH_PROCESS_PROXY() TEST_MAIN_IMPL(true) - //----------------------------------------------------------------------------- #include "generated_fixture_macros.h" //----------------------------------------------------------------------------- diff --git a/vespalib/src/vespa/vespalib/testkit/testapp.h b/vespalib/src/vespa/vespalib/testkit/testapp.h index facb0857e8c..8ffb9958555 100644 --- a/vespalib/src/vespa/vespalib/testkit/testapp.h +++ b/vespalib/src/vespa/vespalib/testkit/testapp.h @@ -22,14 +22,6 @@ public: int Main() override; \ }; \ TEST_APPHOOK(test) -#define TEST_SETUP_WITHPROCESSPROXY(test) \ - class test : public vespalib::TestApp \ - { \ - public: \ - int Main(); \ - virtual bool useProcessStarter() const { return true; } \ - }; \ - TEST_APPHOOK(test) namespace vespalib { diff --git a/vespalib/src/vespa/vespalib/util/CMakeLists.txt b/vespalib/src/vespa/vespalib/util/CMakeLists.txt index 747ed736aad..f7bdd4427e3 100644 --- a/vespalib/src/vespa/vespalib/util/CMakeLists.txt +++ b/vespalib/src/vespa/vespalib/util/CMakeLists.txt @@ -14,7 +14,6 @@ vespa_add_library(vespalib_vespalib_util OBJECT binary_hamming_distance.cpp blockingthreadstackexecutor.cpp box.cpp - child_process.cpp classname.cpp compress.cpp compressor.cpp diff --git a/vespalib/src/vespa/vespalib/util/child_process.cpp b/vespalib/src/vespa/vespalib/util/child_process.cpp deleted file mode 100644 index 93db56cdf67..00000000000 --- a/vespalib/src/vespa/vespalib/util/child_process.cpp +++ /dev/null @@ -1,346 +0,0 @@ -// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. - -#include "guard.h" -#include "child_process.h" -#include <cstring> -#include <vespa/vespalib/util/size_literals.h> - -namespace vespalib { - -namespace child_process { - -using namespace std::chrono; - -/** - * @brief ChildProcess internal timeout management. - **/ -class Timer -{ -private: - const steady_clock::time_point _startTime; - const int64_t _maxTimeMS; - milliseconds _elapsed; - -public: - Timer(int64_t maxTimeMS) - : _startTime(steady_clock::now()), - _maxTimeMS(maxTimeMS), - _elapsed(0) - { } - Timer &update() { - _elapsed = duration_cast<milliseconds>(steady_clock::now() - _startTime); - return *this; - } - int64_t elapsed() const { - return _elapsed.count(); - } - int64_t remaining() const { - if (_maxTimeMS == -1) { - return -1; - } - if (elapsed() > _maxTimeMS) { - return 0; - } - return (_maxTimeMS - _elapsed.count()); - } - int64_t waitTime() const { - int res = remaining(); - if (res >= 0 && res <= 10000) { - return res; - } - return 10000; - } - bool timeOut() const { - return (remaining() == 0); - } -}; - -} // namespace child_process - - -void -ChildProcess::Reader::OnReceiveData(const void *data, size_t length) -{ - const char *buf = (const char *) data; - std::unique_lock lock(_lock); - if (_gotEOF || (buf != nullptr && length == 0)) { // ignore special cases - return; - } - if (buf == nullptr) { // EOF - if (--_num_streams == 0) { - _gotEOF = true; - } - } else { - _queue.push(std::string(buf, length)); - } - if (_waitCnt > 0) { - _cond.notify_one(); - } -} - - -bool -ChildProcess::Reader::hasData() -{ - // NB: caller has lock on _cond - return (!_data.empty() || !_queue.empty()); -} - - -bool -ChildProcess::Reader::waitForData(child_process::Timer &timer, std::unique_lock<std::mutex> &guard) -{ - // NB: caller has lock on _cond - CounterGuard count(_waitCnt); - while (!timer.update().timeOut() && !hasData() && !_gotEOF) { - _cond.wait_for(guard, std::chrono::milliseconds(timer.waitTime())); - } - return hasData(); -} - - -void -ChildProcess::Reader::updateEOF() -{ - // NB: caller has lock on _cond - if (_data.empty() && _queue.empty() && _gotEOF) { - _readEOF = true; - } -} - - -ChildProcess::Reader::Reader(int num_streams) - : _lock(), - _cond(), - _queue(), - _data(), - _num_streams(num_streams), - _gotEOF(false), - _waitCnt(0), - _readEOF(false) -{ -} - - -ChildProcess::Reader::~Reader() = default; - - -uint32_t -ChildProcess::Reader::read(char *buf, uint32_t len, int msTimeout) -{ - if (eof()) { - return 0; - } - child_process::Timer timer(msTimeout); - std::unique_lock guard(_lock); - waitForData(timer, guard); - uint32_t bytes = 0; - while (bytes < len && hasData()) { - if (_data.empty()) { - _data = _queue.front(); - _queue.pop(); - } - if (len - bytes < _data.length()) { - memcpy(buf + bytes, _data.data(), len - bytes); - _data.erase(0, len - bytes); - bytes = len; - } else { - memcpy(buf + bytes, _data.data(), _data.length()); - bytes += _data.length(); - _data.clear(); - } - } - updateEOF(); - return bytes; -} - - -bool -ChildProcess::Reader::readLine(std::string &line, int msTimeout) -{ - line.clear(); - if (eof()) { - return false; - } - child_process::Timer timer(msTimeout); - std::unique_lock guard(_lock); - while (waitForData(timer, guard)) { - while (hasData()) { - if (_data.empty()) { - _data = _queue.front(); - _queue.pop(); - } - std::string::size_type ofs = _data.find('\n'); - if (ofs == std::string::npos) { - line.append(_data); - _data.clear(); - } else { - line.append(_data, 0, ofs); - _data.erase(0, ofs + 1); - updateEOF(); - return true; - } - } - } - updateEOF(); - if (eof()) { - return !line.empty(); - } - _data.swap(line); - return false; -} - -//----------------------------------------------------------------------------- - -void -ChildProcess::checkProc() -{ - if (_running) { - bool stillRunning; - if (_proc.PollWait(&_exitCode, &stillRunning) && !stillRunning) { - _running = false; - _failed = (_exitCode != 0); - } - } -} - - -ChildProcess::ChildProcess(const char *cmd) - : _reader(1), - _proc(cmd, &_reader), - _running(false), - _failed(false), - _exitCode(-918273645) -{ - _running = _proc.CreateWithShell(); - _failed = !_running; -} - -ChildProcess::ChildProcess(const char *cmd, capture_stderr_tag) - : _reader(2), - _proc(cmd, &_reader, &_reader), - _running(false), - _failed(false), - _exitCode(-918273645) -{ - _running = _proc.CreateWithShell(); - _failed = !_running; -} - - -ChildProcess::~ChildProcess() = default; - - -bool -ChildProcess::write(const char *buf, uint32_t len) -{ - if (len == 0) { - return true; - } - return _proc.WriteStdin(buf, len); -} - - -bool -ChildProcess::close() -{ - return _proc.WriteStdin(nullptr, 0); -} - - -uint32_t -ChildProcess::read(char *buf, uint32_t len, int msTimeout) -{ - return _reader.read(buf, len, msTimeout); -} - - -bool -ChildProcess::readLine(std::string &line, int msTimeout) -{ - return _reader.readLine(line, msTimeout); -} - - -bool -ChildProcess::wait(int msTimeout) -{ - bool done = true; - checkProc(); - if (_running) { - if (msTimeout != -1) { - msTimeout = (msTimeout + 999) / 1000; - } - if (_proc.Wait(&_exitCode, msTimeout)) { - _failed = (_exitCode != 0); - } else { - _failed = true; - done = false; - } - _running = false; - } - return done; -} - - -bool -ChildProcess::running() -{ - checkProc(); - return _running; -} - - -bool -ChildProcess::failed() -{ - checkProc(); - return _failed; -} - -int -ChildProcess::getExitCode() -{ - return _exitCode; -} - - -bool -ChildProcess::run(const std::string &input, const char *cmd, - std::string &output, int msTimeout) -{ - ChildProcess proc(cmd); - child_process::Timer timer(msTimeout); - char buf[4_Ki]; - proc.write(input.data(), input.length()); - proc.close(); // close stdin - while (!proc.eof() && !timer.timeOut()) { - uint32_t res = proc.read(buf, sizeof(buf), timer.remaining()); - output.append(buf, res); - timer.update(); - } - if ( ! output.empty() && output.find('\n') == output.size() - 1) { - output.erase(output.size() - 1, 1); - } - proc.wait(timer.update().remaining()); - return (!proc.running() && !proc.failed()); -} - - -bool -ChildProcess::run(const char *cmd, std::string &output, int msTimeout) -{ - std::string input; // empty input - return run(input, cmd, output, msTimeout); -} - - -bool -ChildProcess::run(const char *cmd, int msTimeout) -{ - std::string input; // empty input - std::string output; // ignore output - return run(input, cmd, output, msTimeout); -} - -} // namespace vespalib diff --git a/vespalib/src/vespa/vespalib/util/child_process.h b/vespalib/src/vespa/vespalib/util/child_process.h deleted file mode 100644 index 877c56a8cb1..00000000000 --- a/vespalib/src/vespa/vespalib/util/child_process.h +++ /dev/null @@ -1,212 +0,0 @@ -// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. - -#pragma once - -#include <vespa/fastos/process.h> -#ifndef FASTOS_NO_THREADS -#include <string> -#include <queue> -#include <condition_variable> - -namespace vespalib::child_process { class Timer; } - -namespace vespalib { -/** - * @brief Child Process utility class for running external programs - * - * Designed for use in unit tests and other places - * where you need to run, control and communicate with - * some external program. - **/ -class ChildProcess -{ -private: - class Reader : public FastOS_ProcessRedirectListener - { - private: - std::mutex _lock; - std::condition_variable _cond; - std::queue<std::string> _queue; - std::string _data; - int _num_streams; - bool _gotEOF; - int _waitCnt; - bool _readEOF; - - void OnReceiveData(const void *data, size_t length) override; - bool hasData(); - bool waitForData(child_process::Timer &timer, std::unique_lock<std::mutex> &lock); - void updateEOF(); - - public: - Reader(int num_streams); - ~Reader() override; - - uint32_t read(char *buf, uint32_t len, int msTimeout); - bool readLine(std::string &line, int msTimeout); - bool eof() const { return _readEOF; }; - }; - - Reader _reader; - FastOS_Process _proc; - bool _running; - bool _failed; - int _exitCode; - - void checkProc(); - -public: - ChildProcess(const ChildProcess &) = delete; - ChildProcess &operator=(const ChildProcess &) = delete; - struct capture_stderr_tag{}; - - /** - * @brief Run a child process - * - * Starts a process running the given command - * @param cmd A shell command line to run - **/ - explicit ChildProcess(const char *cmd); - - /** - * @brief Run a child process - * - * Starts a process running the given command. stderr is - * redirected into stdout. - * @param cmd A shell command line to run - **/ - explicit ChildProcess(const char *cmd, capture_stderr_tag); - - /** @brief destructor doing cleanup if needed */ - ~ChildProcess(); - - /** - * @return process id - **/ - pid_t getPid() { return _proc.GetProcessId(); } - - /** - * @brief send data as input to the running process - * - * The given data will be sent so it becomes - * available on the running process's standard input. - * - * @param buf the data containing len bytes to be sent - * @param len the number of bytes to send - * @return true if successful - **/ - bool write(const char *buf, uint32_t len); - - /** - * @brief close the running process's standard input - * - * when running a program that consumes stdin, - * make sure to call this method to signal - * that it can finish. - * @return true if successful - **/ - bool close(); - - /** - * @brief read program output - * - * If the running program writes data to its standard output, you - * can and should get the data with read() or readLine() calls. - * - * @param buf pointer where data is stored - * @param len number of bytes to try to read - * @param msTimeout number of milliseconds to wait for data - **/ - uint32_t read(char *buf, uint32_t len, int msTimeout = 10000); - - /** - * @brief read a line of program output - * - * See read(). - * @param line reference to a string where a line of program output will be stored - * @param msTimeout number of milliseconds to wait for data - * @return true if successful - **/ - bool readLine(std::string &line, int msTimeout = 10000); - - /** - * @brief check if the program has finished writing output - * @return true if standard output from the process is closed - **/ - bool eof() const { return _reader.eof(); } - - /** - * @brief wait for the program to exit - * @param msTimeout milliseconds to wait; the default (-1) will wait forever - * @return true if the program exited, false on timeout - **/ - bool wait(int msTimeout = -1); - - /** @brief check if the program is still running */ - bool running(); - - /** - * @brief get the exit code of the started program. - * - * Will return the stored exitcode. - * It assumes that the command is done. - * @return exit code. - **/ - int getExitCode(); - - /** - * @brief check if program failed - * - * Check if we failed to run the program or the program finished with - * a non-zero exit status. - * @return true if something went wrong - **/ - bool failed(); - - /** - * @brief run a command - * - * Utility function that runs the given command, sends input, and - * loops reading output until the program finishes or the timeout - * expires. - * Any final terminating newline will be erased from output, - * just like for shell backticks. - * - * @param input The input the program will receive - * @param cmd The command to run - * @param output Any output will be appended to this string - * @param msTimeout milliseconds timeout; -1 means wait forever for program to finish - **/ - static bool run(const std::string &input, const char *cmd, - std::string &output, int msTimeout = -1); - /** - * @brief run a command - * - * Utility function that runs the given command with no input, and - * loops reading output until the program finishes or the timeout - * expires. - * Any final terminating newline will be erased from output, - * just like for shell backticks. - * - * @param cmd The command to run - * @param output Any output will be appended to this string - * @param msTimeout milliseconds timeout; -1 means wait forever for program to finish - **/ - static bool run(const char *cmd, std::string &output, int msTimeout = -1); - - /** - * @brief run a command - * - * Utility function that runs the given command with no input, and - * loops reading output until the program finishes or the timeout - * expires. Output (if any) is ignored. - * - * @param cmd The command to run - * @param msTimeout milliseconds timeout; -1 means wait forever for program to finish - **/ - static bool run(const char *cmd, int msTimeout = -1); -}; - -} // namespace vespalib - -#endif // FASTOS_NO_THREADS diff --git a/vespamalloc/src/tests/doubledelete/expectsignal.cpp b/vespamalloc/src/tests/doubledelete/expectsignal.cpp index e9b71649268..df6398dcd20 100644 --- a/vespamalloc/src/tests/doubledelete/expectsignal.cpp +++ b/vespamalloc/src/tests/doubledelete/expectsignal.cpp @@ -1,6 +1,6 @@ // Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. #include <vespa/vespalib/testkit/testapp.h> -#include <vespa/vespalib/util/child_process.h> +#include <vespa/vespalib/process/process.h> #include <sys/wait.h> using namespace vespalib; @@ -9,9 +9,6 @@ class Test : public TestApp { public: int Main() override; -private: - bool useProcessStarter() const override { return true; } - }; int Test::Main() @@ -25,14 +22,11 @@ int Test::Main() fprintf(stderr, "argc=%d : Running '%s' expecting signal %d\n", _argc, _argv[2], retval); - ChildProcess cmd(_argv[2]); - for(std::string line; cmd.readLine(line, 60000);) { + Process cmd(_argv[2]); + for (vespalib::string line = cmd.read_line(); !line.empty(); line = cmd.read_line()) { fprintf(stdout, "%s\n", line.c_str()); } - - ASSERT_TRUE(cmd.wait(60000)); - - int exitCode = cmd.getExitCode(); + int exitCode = cmd.join(); if (exitCode == 65535) { fprintf(stderr, "[ERROR] child killed (timeout)\n"); diff --git a/vespamalloc/src/tests/overwrite/expectsignal.cpp b/vespamalloc/src/tests/overwrite/expectsignal.cpp index 904cedd479f..df6398dcd20 100644 --- a/vespamalloc/src/tests/overwrite/expectsignal.cpp +++ b/vespamalloc/src/tests/overwrite/expectsignal.cpp @@ -1,6 +1,6 @@ // Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. #include <vespa/vespalib/testkit/testapp.h> -#include <vespa/vespalib/util/child_process.h> +#include <vespa/vespalib/process/process.h> #include <sys/wait.h> using namespace vespalib; @@ -9,8 +9,6 @@ class Test : public TestApp { public: int Main() override; -private: - virtual bool useProcessStarter() const override { return true; } }; int Test::Main() @@ -24,14 +22,11 @@ int Test::Main() fprintf(stderr, "argc=%d : Running '%s' expecting signal %d\n", _argc, _argv[2], retval); - ChildProcess cmd(_argv[2]); - for(std::string line; cmd.readLine(line, 60000);) { + Process cmd(_argv[2]); + for (vespalib::string line = cmd.read_line(); !line.empty(); line = cmd.read_line()) { fprintf(stdout, "%s\n", line.c_str()); } - - ASSERT_TRUE(cmd.wait(60000)); - - int exitCode = cmd.getExitCode(); + int exitCode = cmd.join(); if (exitCode == 65535) { fprintf(stderr, "[ERROR] child killed (timeout)\n"); diff --git a/vespamalloc/src/tests/thread/thread.cpp b/vespamalloc/src/tests/thread/thread.cpp index b101b862268..d6f1ad5c9c0 100644 --- a/vespamalloc/src/tests/thread/thread.cpp +++ b/vespamalloc/src/tests/thread/thread.cpp @@ -11,8 +11,6 @@ class Test : public TestApp public: ~Test(); int Main() override; -private: - bool useIPCHelper() const override { return true; } }; Test::~Test() |