diff --git a/common/threading/SubProcess.cpp b/common/threading/SubProcess.cpp index 58aea7c565dc816bcc4180ede050ef070461353c..68ea251fbfae685eda2362ac057795e15ebd9177 100644 --- a/common/threading/SubProcess.cpp +++ b/common/threading/SubProcess.cpp @@ -23,6 +23,7 @@ #include "SubProcess.hpp" #include "common/exception/Errnum.hpp" +#include <memory> #include <string.h> #include <iostream> #include <sys/signal.h> @@ -98,14 +99,20 @@ SubProcess::SubProcess(const std::string & executable, const std::list<std::stri ScopedPosixSpawnAttr attr; cta::exception::Errnum::throwOnReturnedErrno(posix_spawnattr_setflags(attr, POSIX_SPAWN_USEVFORK), "In Subprocess::Subprocess(): failed to posix_spawnattr_setflags()"); - char ** cargv = new char*[argv.size()+1]; - int index = 0; - for (auto a=argv.cbegin(); a!=argv.cend(); a++) { - cargv[index++] = ::strdup(a->c_str()); + { + std::unique_ptr<char *[]> cargv (new char*[argv.size()+1]); + size_t index = 0; + std::list<std::unique_ptr<char, void (*)(char *)>> cargvStrings; + for (auto a=argv.cbegin(); a!=argv.cend(); a++) { + cargv[index] = ::strdup(a->c_str()); + std::unique_ptr<char, void (*)(char *)> upStr (cargv[index], [](char *s){::free(s);}); + cargvStrings.emplace_back(std::move(upStr)); + index++; + } + cargv[argv.size()] = NULL; + int spawnRc=::posix_spawnp(&m_child, executable.c_str(), fileActions, attr, cargv.get(), ::environ); + cta::exception::Errnum::throwOnReturnedErrno(spawnRc, "In Subprocess::Subprocess failed to posix_spawn()"); } - cargv[argv.size()] = NULL; - int spawnRc=::posix_spawnp(&m_child, executable.c_str(), fileActions, attr, cargv, ::environ); - cta::exception::Errnum::throwOnReturnedErrno(spawnRc, "In Subprocess::Subprocess failed to posix_spawn()"); // We are the parent process. Close the write sides of pipes. ::close(stdoutPipe[writeSide]); ::close(stderrPipe[writeSide]); diff --git a/tapeserver/CMakeLists.txt b/tapeserver/CMakeLists.txt index dab2e913625e5fb2c4a8243634e6972fe79488b6..d7aad618c080419b7245655ee20df7982d410cb6 100644 --- a/tapeserver/CMakeLists.txt +++ b/tapeserver/CMakeLists.txt @@ -19,8 +19,7 @@ add_library(cta-tapedSystemTests SHARED cta-tapedSystemtests.cpp) target_link_libraries(cta-tapedSystemTests - systemTestHelper unitTestHelper ctacommon) -install(TARGETS cta-tapedSystemTests DESTINATION usr/${CMAKE_INSTALL_LIBDIR}) \ No newline at end of file +install(TARGETS cta-tapedSystemTests DESTINATION usr/${CMAKE_INSTALL_LIBDIR}) diff --git a/tapeserver/cta-tapedSystemtests.cpp b/tapeserver/cta-tapedSystemtests.cpp index f868d5d06773e065707db0f7e2058244f91a4e32..df844e0d83d6f1f848e0dfa93d542b6aa7d57e42 100644 --- a/tapeserver/cta-tapedSystemtests.cpp +++ b/tapeserver/cta-tapedSystemtests.cpp @@ -16,7 +16,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include "tests/Subprocess.hpp" +#include "common/threading/SubProcess.hpp" #include "tests/TempFile.hpp" #include <gtest/gtest.h> @@ -25,12 +25,12 @@ namespace systemTests { TEST(cta_taped, InvocationTests) { { // Do we get help with -h or --help? - Subprocess spHelpShort("cta-taped", std::list<std::string>({"cta-taped", "-h"})); + cta::threading::SubProcess spHelpShort("cta-taped", std::list<std::string>({"cta-taped", "-h"})); spHelpShort.wait(); ASSERT_NE(std::string::npos, spHelpShort.stdout().find("Usage")); ASSERT_TRUE(spHelpShort.stderr().empty()); ASSERT_EQ(EXIT_SUCCESS, spHelpShort.exitValue()); - Subprocess spHelpLong("cta-taped", std::list<std::string>({"cta-taped", "--help"})); + cta::threading::SubProcess spHelpLong("cta-taped", std::list<std::string>({"cta-taped", "--help"})); spHelpLong.wait(); ASSERT_NE(std::string::npos, spHelpLong.stdout().find("Usage: cta-taped [options]")); ASSERT_TRUE(spHelpLong.stderr().empty()); @@ -39,7 +39,7 @@ TEST(cta_taped, InvocationTests) { { // Do we get proper complaint when the configuration file is not there? - Subprocess spNoConfigFile("cta-taped", std::list<std::string>({"cta-taped", "-f", "-s", "-c", "/no/such/file"})); + cta::threading::SubProcess spNoConfigFile("cta-taped", std::list<std::string>({"cta-taped", "-f", "-s", "-c", "/no/such/file"})); spNoConfigFile.wait(); ASSERT_NE(std::string::npos, spNoConfigFile.stdout().find("Failed to open configuration file")); ASSERT_TRUE(spNoConfigFile.stderr().empty()); @@ -58,11 +58,11 @@ TEST(cta_taped, InvocationTests) { "taped BufferCount 1\n" "taped TpConfigPath "); ctaConf.stringAppend(tpConfig.path()); - Subprocess spNoDrive("cta-taped", std::list<std::string>({"cta-taped", "-f", "-s", "-c", ctaConf.path()})); + cta::threading::SubProcess spNoDrive("cta-taped", std::list<std::string>({"cta-taped", "-f", "-s", "-c", ctaConf.path()})); spNoDrive.wait(); ASSERT_NE(std::string::npos, spNoDrive.stdout().find("MSG=\"Aborting\" Message=\"No drive found in configuration\"")); ASSERT_TRUE(spNoDrive.stderr().empty()); ASSERT_EQ(EXIT_FAILURE, spNoDrive.exitValue()); } } -} \ No newline at end of file +} diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 33a81bee83343da0bdf6e50461cd9221a0c07dfb..2b810fd03dc9766031706e3f5c5abb6f5de006ba 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -75,14 +75,10 @@ target_link_libraries(cta-unitTests-multiProcess add_library(unitTestHelper TempFile.cpp) -add_library(systemTestHelper - Subprocess.cpp) - add_library(systemTestHelperTests SHARED SubprocessSystemTests.cpp) target_link_libraries(systemTestHelperTests - systemTestHelper ctacommon) add_executable(cta-systemTests @@ -90,7 +86,6 @@ add_executable(cta-systemTests ${GMOCK_SRC}) target_link_libraries(cta-systemTests - systemTestHelper systemTestHelperTests cta-tapedSystemTests gtest diff --git a/tests/Subprocess.cpp b/tests/Subprocess.cpp deleted file mode 100644 index 00b7ce0024f53919ac23eb98143f9018ce19bfa8..0000000000000000000000000000000000000000 --- a/tests/Subprocess.cpp +++ /dev/null @@ -1,157 +0,0 @@ -/* - * The CERN Tape Archive (CTA) project - * Copyright (C) 2015 CERN - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#include "Subprocess.hpp" -#include "common/exception/Errnum.hpp" -#include <string.h> -#include <iostream> -#include <sys/signal.h> -#include <sys/wait.h> -#include <fcntl.h> -#include <unistd.h> -#include <spawn.h> -#include <memory> - -namespace { -class ScopedPosixSpawnFileActions{ -public: - ScopedPosixSpawnFileActions() { - ::posix_spawn_file_actions_init(&m_action); - } - ~ScopedPosixSpawnFileActions() { - ::posix_spawn_file_actions_destroy(&m_action); - } - operator ::posix_spawn_file_actions_t * () { return &m_action; } -private: - ::posix_spawn_file_actions_t m_action; -}; -} - -namespace { -class ScopedPosixSpawnAttr{ -public: - ScopedPosixSpawnAttr() { - ::posix_spawnattr_init(&m_attr); - } - ~ScopedPosixSpawnAttr() { - ::posix_spawnattr_destroy(&m_attr); - } - operator ::posix_spawnattr_t * () { return &m_attr; } -private: - ::posix_spawnattr_t m_attr; -}; -} - -namespace systemTests { -Subprocess::Subprocess(const std::string & executable, const std::list<std::string>& argv) { - // Sanity checks - if (argv.size() < 1) - throw cta::exception::Exception( - "In Subprocess::Subprocess: not enough elements in argv"); - // Prepare the pipes for the child's stdout and stderr (stdin will be closed) - const size_t readSide=0; - const size_t writeSide=1; - int stdoutPipe[2]; - int stderrPipe[2]; - cta::exception::Errnum::throwOnNonZero(::pipe2(stdoutPipe, O_NONBLOCK), - "In Subprocess::Subprocess failed to create the stdout pipe"); - cta::exception::Errnum::throwOnNonZero(::pipe2(stderrPipe, O_NONBLOCK), - "In Subprocess::Subprocess failed to create the stderr pipe"); - // Prepare the actions to be taken on file descriptors - ScopedPosixSpawnFileActions fileActions; - // We will be the child process. Close the read sides of the pipes. - cta::exception::Errnum::throwOnReturnedErrno(posix_spawn_file_actions_addclose(fileActions, stdoutPipe[readSide]), - "In Subprocess::Subprocess(): failed to posix_spawn_file_actions_addclose() (1)"); - cta::exception::Errnum::throwOnReturnedErrno(posix_spawn_file_actions_addclose(fileActions, stderrPipe[readSide]), - "In Subprocess::Subprocess(): failed to posix_spawn_file_actions_addclose() (2)"); - // Close stdin and rewire the stdout and stderr to the pipes. - cta::exception::Errnum::throwOnReturnedErrno(posix_spawn_file_actions_adddup2(fileActions, stdoutPipe[writeSide], STDOUT_FILENO), - "In Subprocess::Subprocess(): failed to posix_spawn_file_actions_adddup2() (1)"); - cta::exception::Errnum::throwOnReturnedErrno(posix_spawn_file_actions_adddup2(fileActions, stderrPipe[writeSide], STDERR_FILENO), - "In Subprocess::Subprocess(): failed to posix_spawn_file_actions_adddup2() (2)"); - // Close the now duplicated pipe file descriptors - cta::exception::Errnum::throwOnReturnedErrno(posix_spawn_file_actions_addclose(fileActions, stdoutPipe[writeSide]), - "In Subprocess::Subprocess(): failed to posix_spawn_file_actions_addclose() (3)"); - cta::exception::Errnum::throwOnReturnedErrno(posix_spawn_file_actions_addclose(fileActions, stderrPipe[writeSide]), - "In Subprocess::Subprocess(): failed to posix_spawn_file_actions_addclose() (4)"); - // And finally spawn the subprocess - // Prepare the spawn attributes (we need vfork) - ScopedPosixSpawnAttr attr; - cta::exception::Errnum::throwOnReturnedErrno(posix_spawnattr_setflags(attr, POSIX_SPAWN_USEVFORK), - "In Subprocess::Subprocess(): failed to posix_spawnattr_setflags()"); - char ** cargv = new char*[argv.size()+1]; - int index = 0; - for (auto a=argv.cbegin(); a!=argv.cend(); a++) { - cargv[index++] = ::strdup(a->c_str()); - } - cargv[argv.size()] = NULL; - int spawnRc=::posix_spawnp(&m_child, executable.c_str(), fileActions, attr, cargv, ::environ); - cta::exception::Errnum::throwOnReturnedErrno(spawnRc, "In Subprocess::Subprocess failed to posix_spawn()"); - // We are the parent process. Close the write sides of pipes. - ::close(stdoutPipe[writeSide]); - ::close(stderrPipe[writeSide]); - m_stdoutFd = stdoutPipe[readSide]; - m_stderrFd = stderrPipe[readSide]; -} - -void Subprocess::kill(int signal) { - ::kill(m_child, signal); -} - -int Subprocess::exitValue() { - if(!m_child) - throw cta::exception::Exception("In Subprocess::exitValue: child process not waited for"); - return WEXITSTATUS(m_childStatus); -} - -void Subprocess::wait() { - ::waitpid(m_child, &m_childStatus, 0); - char buff[1000]; - int rc; - while (0<(rc=::read(m_stdoutFd, buff, sizeof(buff)))) { - m_stdout.append(buff, rc); - } - ::close(m_stdoutFd); - while (0<(rc=::read(m_stderrFd, buff, sizeof(buff)))) { - m_stderr.append(buff, rc); - } - ::close(m_stderrFd); - m_childComplete = true; -} - -std::string Subprocess::stdout() { - if(!m_child) - throw cta::exception::Exception("In Subprocess::stdout: child process not waited for"); - return m_stdout; -} - -std::string Subprocess::stderr() { - if(!m_child) - throw cta::exception::Exception("In Subprocess::stderr: child process not waited for"); - return m_stderr; -} - -Subprocess::~Subprocess() { - if(!m_childComplete) { - this->kill(SIGKILL); - this->wait(); - } -} - - -} // namespace systemTests \ No newline at end of file diff --git a/tests/Subprocess.hpp b/tests/Subprocess.hpp deleted file mode 100644 index 7e8005168388bba4bade4f3c004fd9edac22bde4..0000000000000000000000000000000000000000 --- a/tests/Subprocess.hpp +++ /dev/null @@ -1,43 +0,0 @@ -/* - * The CERN Tape Archive (CTA) project - * Copyright (C) 2015 CERN - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#pragma once - -#include <list> -#include <string> - -namespace systemTests { -class Subprocess { -public: - Subprocess(const std::string & program, const std::list<std::string> &argv); - ~Subprocess(); - void wait(void); - std::string stdout(); - std::string stderr(); - void kill(int signal); - int exitValue(); -private: - int m_stdoutFd; - int m_stderrFd; - pid_t m_child; - bool m_childComplete; - int m_childStatus; - std::string m_stdout; - std::string m_stderr; -}; -} \ No newline at end of file diff --git a/tests/SubprocessSystemTests.cpp b/tests/SubprocessSystemTests.cpp index 6b0b2a595b05c5fc92c363b64dee412ad0e62b99..9a04603ef162a429d37fc3b39d26ce7cadb94882 100644 --- a/tests/SubprocessSystemTests.cpp +++ b/tests/SubprocessSystemTests.cpp @@ -16,26 +16,26 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include "Subprocess.hpp" +#include "common/threading/SubProcess.hpp" #include <gtest/gtest.h> namespace systemTests { -TEST(SuprocessHelper, basicTests) { - Subprocess sp("echo", std::list<std::string>({"echo", "Hello,", "world."})); +TEST(SubProcessHelper, basicTests) { + cta::threading::SubProcess sp("echo", std::list<std::string>({"echo", "Hello,", "world."})); sp.wait(); ASSERT_EQ("Hello, world.\n", sp.stdout()); ASSERT_EQ("", sp.stderr()); ASSERT_EQ(0, sp.exitValue()); - Subprocess sp2("cat", std::list<std::string>({"cat", "/no/such/file"})); + cta::threading::SubProcess sp2("cat", std::list<std::string>({"cat", "/no/such/file"})); sp2.wait(); ASSERT_EQ("", sp2.stdout()); ASSERT_NE(std::string::npos, sp2.stderr().find("/no/such/file")); ASSERT_EQ(1, sp2.exitValue()); - Subprocess sp3("/no/such/file", std::list<std::string>({"/no/such/file"})); + cta::threading::SubProcess sp3("/no/such/file", std::list<std::string>({"/no/such/file"})); sp3.wait(); ASSERT_EQ("", sp3.stdout()); ASSERT_EQ(127, sp3.exitValue()); ASSERT_EQ("", sp3.stderr()); } -} \ No newline at end of file +}