├── .gitignore ├── example ├── send_input │ ├── README │ ├── CMakeLists.txt │ └── main.cpp ├── run_ls │ ├── README │ ├── CMakeLists.txt │ └── main.cpp └── CMakeLists.txt ├── .travis.yml ├── test ├── main.cpp ├── util.hpp ├── test_pipe.cpp ├── CMakeLists.txt ├── test_version.cpp ├── test_output.cpp ├── test_util.cpp ├── util.cpp ├── test_output_file.cpp ├── test_run.cpp └── test_input_file.cpp ├── include └── process │ ├── exception.hpp │ ├── version.hpp │ ├── output.hpp │ ├── impl │ ├── pipe.hpp │ ├── output_file.hpp │ └── input_file.hpp │ └── process.hpp ├── README.md └── CMakeLists.txt /.gitignore: -------------------------------------------------------------------------------- 1 | bin 2 | -------------------------------------------------------------------------------- /example/send_input/README: -------------------------------------------------------------------------------- 1 | This example shows how to send input to an external process. 2 | 3 | -------------------------------------------------------------------------------- /example/run_ls/README: -------------------------------------------------------------------------------- 1 | This example shows how to use the library by running the ls command and 2 | displaying its output. 3 | 4 | -------------------------------------------------------------------------------- /example/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright Abel Sinkovics (abel@sinkovics.hu) 2013. 2 | # Distributed under the Boost Software License, Version 1.0. 3 | # (See accompanying file LICENSE_1_0.txt or copy at 4 | # http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | subdirs(run_ls send_input) 7 | 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: cpp 2 | compiler: 3 | - gcc 4 | - clang 5 | before_install: 6 | - sudo apt-get install libboost1.48-dev libboost-test1.48-dev 7 | script: 8 | - mkdir bin 9 | - cd bin 10 | - cmake .. -DCMAKE_CXX_FLAGS:STRING="-std=c++0x" 11 | - make 12 | - make test 13 | -------------------------------------------------------------------------------- /example/run_ls/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright Abel Sinkovics (abel@sinkovics.hu) 2013. 2 | # Distributed under the Boost Software License, Version 1.0. 3 | # (See accompanying file LICENSE_1_0.txt or copy at 4 | # http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | aux_source_directory(. SOURCES) 7 | add_executable(run_ls ${SOURCES}) 8 | -------------------------------------------------------------------------------- /example/send_input/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright Abel Sinkovics (abel@sinkovics.hu) 2013. 2 | # Distributed under the Boost Software License, Version 1.0. 3 | # (See accompanying file LICENSE_1_0.txt or copy at 4 | # http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | aux_source_directory(. SOURCES) 7 | add_executable(send_input ${SOURCES}) 8 | -------------------------------------------------------------------------------- /test/main.cpp: -------------------------------------------------------------------------------- 1 | // Copyright Abel Sinkovics (abel@sinkovics.hu) 2013. 2 | // Distributed under the Boost Software License, Version 1.0. 3 | // (See accompanying file LICENSE_1_0.txt or copy at 4 | // http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #define BOOST_TEST_MODULE process 7 | 8 | #include 9 | 10 | 11 | -------------------------------------------------------------------------------- /include/process/exception.hpp: -------------------------------------------------------------------------------- 1 | #ifndef PROCESS_EXCEPTION_HPP 2 | #define PROCESS_EXCEPTION_HPP 3 | 4 | // Copyright Abel Sinkovics (abel@sinkovics.hu) 2013. 5 | // Distributed under the Boost Software License, Version 1.0. 6 | // (See accompanying file LICENSE_1_0.txt or copy at 7 | // http://www.boost.org/LICENSE_1_0.txt) 8 | 9 | #include 10 | #include 11 | 12 | namespace process 13 | { 14 | struct exception : std::runtime_error 15 | { 16 | explicit exception(const std::string& reason_) : 17 | std::runtime_error(reason_) 18 | {} 19 | }; 20 | } 21 | 22 | #endif 23 | 24 | -------------------------------------------------------------------------------- /example/send_input/main.cpp: -------------------------------------------------------------------------------- 1 | // Copyright Abel Sinkovics (abel@sinkovics.hu) 2013. 2 | // Distributed under the Boost Software License, Version 1.0. 3 | // (See accompanying file LICENSE_1_0.txt or copy at 4 | // http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | int main() 13 | { 14 | const std::vector cmd(1, "/bin/cat"); 15 | 16 | const process::output o = process::run(cmd, "Hello World"); 17 | std::cout 18 | << "Recevied output:" << std::endl 19 | << o.standard_output() << std::endl; 20 | } 21 | 22 | -------------------------------------------------------------------------------- /test/util.hpp: -------------------------------------------------------------------------------- 1 | #ifndef PROCESS_TEMP_FILE_HPP 2 | #define PROCESS_TEMP_FILE_HPP 3 | 4 | // Copyright Abel Sinkovics (abel@sinkovics.hu) 2013. 5 | // Distributed under the Boost Software License, Version 1.0. 6 | // (See accompanying file LICENSE_1_0.txt or copy at 7 | // http://www.boost.org/LICENSE_1_0.txt) 8 | 9 | #include 10 | 11 | extern const std::string temp_filename; 12 | 13 | bool file_exists(const std::string& filename_); 14 | bool file_descriptor_is_open(int fd_); 15 | void write_file(const std::string& filename_, const std::string& content_); 16 | std::string read_file(const std::string& filename_); 17 | 18 | #endif 19 | 20 | 21 | -------------------------------------------------------------------------------- /test/test_pipe.cpp: -------------------------------------------------------------------------------- 1 | // Copyright Abel Sinkovics (abel@sinkovics.hu) 2013. 2 | // Distributed under the Boost Software License, Version 1.0. 3 | // (See accompanying file LICENSE_1_0.txt or copy at 4 | // http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #include 7 | 8 | #include "util.hpp" 9 | 10 | #include 11 | 12 | using namespace process::impl; 13 | 14 | BOOST_AUTO_TEST_CASE(test_pipe_creation) 15 | { 16 | process::impl::pipe(); 17 | } 18 | 19 | BOOST_AUTO_TEST_CASE(test_pipe_write_and_read) 20 | { 21 | process::impl::pipe p; 22 | p.output.write("Hello"); 23 | p.output.close(); 24 | 25 | BOOST_CHECK_EQUAL("Hello", p.input.read()); 26 | } 27 | 28 | -------------------------------------------------------------------------------- /include/process/version.hpp: -------------------------------------------------------------------------------- 1 | #ifndef PROCESS_VERSION_HPP 2 | #define PROCESS_VERSION_HPP 3 | 4 | // Copyright Abel Sinkovics (abel@sinkovics.hu) 2013. 5 | // Distributed under the Boost Software License, Version 1.0. 6 | // (See accompanying file LICENSE_1_0.txt or copy at 7 | // http://www.boost.org/LICENSE_1_0.txt) 8 | 9 | #ifdef PROCESS_MAJOR_VERSION 10 | #error PROCESS_MAJOR_VERSION already defined 11 | #endif 12 | 13 | #ifdef PROCESS_MINOR_VERSION 14 | #error PROCESS_MINOR_VERSION already defined 15 | #endif 16 | 17 | #ifdef PROCESS_PATCH_VERSION 18 | #error PROCESS_PATCH_VERSION already defined 19 | #endif 20 | 21 | #define PROCESS_MAJOR_VERSION 0 22 | #define PROCESS_MINOR_VERSION 1 23 | #define PROCESS_PATCH_VERSION 0 24 | 25 | #endif 26 | 27 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright Abel Sinkovics (abel@sinkovics.hu) 2013. 2 | # Distributed under the Boost Software License, Version 1.0. 3 | # (See accompanying file LICENSE_1_0.txt or copy at 4 | # http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | aux_source_directory(. SOURCES) 7 | add_executable(process_test ${SOURCES}) 8 | 9 | add_definitions(-DBUILD_MAJOR_VERSION=${MAJOR_VERSION}) 10 | add_definitions(-DBUILD_MINOR_VERSION=${MINOR_VERSION}) 11 | add_definitions(-DBUILD_PATCH_VERSION=${PATCH_VERSION}) 12 | 13 | if(NOT Boost_USE_STATIC_LIBS) 14 | add_definitions(-DBOOST_TEST_DYN_LINK) 15 | endif(NOT Boost_USE_STATIC_LIBS) 16 | 17 | target_link_libraries(process_test ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) 18 | 19 | add_test(process_unit_tests process_test) 20 | 21 | -------------------------------------------------------------------------------- /test/test_version.cpp: -------------------------------------------------------------------------------- 1 | // Copyright Abel Sinkovics (abel@sinkovics.hu) 2013. 2 | // Distributed under the Boost Software License, Version 1.0. 3 | // (See accompanying file LICENSE_1_0.txt or copy at 4 | // http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #define BOOST_TEST_DYN_LINK 7 | 8 | #include 9 | 10 | #include 11 | 12 | BOOST_AUTO_TEST_CASE(process_version) 13 | { 14 | // verifying that the version numbers coming from the build system are the 15 | // same as the ones defined in the headers 16 | 17 | BOOST_CHECK_EQUAL(PROCESS_MAJOR_VERSION, BUILD_MAJOR_VERSION); 18 | BOOST_CHECK_EQUAL(PROCESS_MINOR_VERSION, BUILD_MINOR_VERSION); 19 | BOOST_CHECK_EQUAL(PROCESS_PATCH_VERSION, BUILD_PATCH_VERSION); 20 | } 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /include/process/output.hpp: -------------------------------------------------------------------------------- 1 | #ifndef PROCESS_OUTPUT_HPP 2 | #define PROCESS_OUTPUT_HPP 3 | 4 | // Copyright Abel Sinkovics (abel@sinkovics.hu) 2013. 5 | // Distributed under the Boost Software License, Version 1.0. 6 | // (See accompanying file LICENSE_1_0.txt or copy at 7 | // http://www.boost.org/LICENSE_1_0.txt) 8 | 9 | #include 10 | 11 | namespace process 12 | { 13 | class output 14 | { 15 | public: 16 | output(const std::string& stdout_, const std::string& stderr_) : 17 | _out(stdout_), 18 | _err(stderr_) 19 | { 20 | } 21 | 22 | const std::string& standard_output() const 23 | { 24 | return _out; 25 | } 26 | 27 | const std::string& standard_error() const 28 | { 29 | return _err; 30 | } 31 | private: 32 | std::string _out; 33 | std::string _err; 34 | }; 35 | } 36 | 37 | #endif 38 | 39 | -------------------------------------------------------------------------------- /test/test_output.cpp: -------------------------------------------------------------------------------- 1 | // Copyright Abel Sinkovics (abel@sinkovics.hu) 2013. 2 | // Distributed under the Boost Software License, Version 1.0. 3 | // (See accompanying file LICENSE_1_0.txt or copy at 4 | // http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #include 7 | 8 | #include 9 | 10 | using namespace process; 11 | 12 | BOOST_AUTO_TEST_CASE(test_output_empty_stdout) 13 | { 14 | output o("", ""); 15 | 16 | BOOST_CHECK_EQUAL("", o.standard_output()); 17 | } 18 | 19 | BOOST_AUTO_TEST_CASE(test_output_stdout) 20 | { 21 | output o("normal", ""); 22 | 23 | BOOST_CHECK_EQUAL("normal", o.standard_output()); 24 | } 25 | 26 | BOOST_AUTO_TEST_CASE(test_output_empty_stderr) 27 | { 28 | output o("", ""); 29 | 30 | BOOST_CHECK_EQUAL("", o.standard_error()); 31 | } 32 | 33 | BOOST_AUTO_TEST_CASE(test_output_stderr) 34 | { 35 | output o("", "error"); 36 | 37 | BOOST_CHECK_EQUAL("error", o.standard_error()); 38 | } 39 | 40 | 41 | -------------------------------------------------------------------------------- /example/run_ls/main.cpp: -------------------------------------------------------------------------------- 1 | // Copyright Abel Sinkovics (abel@sinkovics.hu) 2013. 2 | // Distributed under the Boost Software License, Version 1.0. 3 | // (See accompanying file LICENSE_1_0.txt or copy at 4 | // http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #include 7 | 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | void ls_path(const std::string& path_) 15 | { 16 | using std::cout; 17 | using std::endl; 18 | using std::vector; 19 | using std::string; 20 | 21 | using boost::assign::list_of; 22 | 23 | using process::output; 24 | using process::run; 25 | 26 | cout << "Running: /bin/ls " << path_ << endl; 27 | const vector cmd = list_of("/bin/ls")(path_); 28 | 29 | const output o = run(cmd, ""); 30 | 31 | cout 32 | << "Standard output:" << endl 33 | << o.standard_output() << endl 34 | << "-------------" << endl 35 | << "Standard error:" << endl 36 | << o.standard_error() << endl 37 | << "-------------" << endl; 38 | } 39 | 40 | int main() 41 | { 42 | ls_path("/"); 43 | ls_path("/something_that_does_not_exist"); 44 | } 45 | 46 | -------------------------------------------------------------------------------- /include/process/impl/pipe.hpp: -------------------------------------------------------------------------------- 1 | #ifndef PROCESS_IMPL_PIPE_HPP 2 | #define PROCESS_IMPL_PIPE_HPP 3 | 4 | // Copyright Abel Sinkovics (abel@sinkovics.hu) 2013. 5 | // Distributed under the Boost Software License, Version 1.0. 6 | // (See accompanying file LICENSE_1_0.txt or copy at 7 | // http://www.boost.org/LICENSE_1_0.txt) 8 | 9 | #include 10 | #include 11 | 12 | #include 13 | 14 | namespace process 15 | { 16 | namespace impl 17 | { 18 | class pipe 19 | { 20 | public: 21 | class fds 22 | { 23 | public: 24 | fds() 25 | { 26 | ::pipe(_fd); 27 | } 28 | 29 | int read_fd() const 30 | { 31 | return _fd[0]; 32 | } 33 | 34 | int write_fd() const 35 | { 36 | return _fd[1]; 37 | } 38 | private: 39 | int _fd[2]; 40 | }; 41 | 42 | input_file input; 43 | output_file output; 44 | 45 | pipe(fds fds_ = fds()) : 46 | input(fds_.read_fd()), 47 | output(fds_.write_fd()) 48 | { 49 | } 50 | }; 51 | } 52 | } 53 | 54 | #endif 55 | 56 | -------------------------------------------------------------------------------- /test/test_util.cpp: -------------------------------------------------------------------------------- 1 | // Copyright Abel Sinkovics (abel@sinkovics.hu) 2013. 2 | // Distributed under the Boost Software License, Version 1.0. 3 | // (See accompanying file LICENSE_1_0.txt or copy at 4 | // http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #include "util.hpp" 7 | 8 | #include 9 | 10 | #include 11 | #include 12 | 13 | BOOST_AUTO_TEST_CASE(test_existing_file_exists) 14 | { 15 | write_file(temp_filename, "foo"); 16 | BOOST_CHECK(file_exists(temp_filename)); 17 | } 18 | 19 | BOOST_AUTO_TEST_CASE(test_non_existing_file_does_not_exist) 20 | { 21 | BOOST_CHECK(!file_exists("/etc/no_such_file")); 22 | } 23 | 24 | BOOST_AUTO_TEST_CASE(test_open_file_descriptor_is_open) 25 | { 26 | BOOST_CHECK(file_descriptor_is_open(0)); 27 | } 28 | 29 | BOOST_AUTO_TEST_CASE(test_closed_file_descriptor_is_not_open) 30 | { 31 | write_file(temp_filename, "foo"); 32 | const int fd = open(temp_filename.c_str(), O_RDONLY); 33 | close(fd); 34 | 35 | BOOST_CHECK(!file_descriptor_is_open(fd)); 36 | } 37 | 38 | BOOST_AUTO_TEST_CASE(test_read_and_write_file) 39 | { 40 | write_file(temp_filename, "foo bar"); 41 | BOOST_CHECK_EQUAL("foo bar", read_file(temp_filename)); 42 | } 43 | 44 | -------------------------------------------------------------------------------- /test/util.cpp: -------------------------------------------------------------------------------- 1 | // Copyright Abel Sinkovics (abel@sinkovics.hu) 2013. 2 | // Distributed under the Boost Software License, Version 1.0. 3 | // (See accompanying file LICENSE_1_0.txt or copy at 4 | // http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #include "util.hpp" 7 | 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | namespace 15 | { 16 | std::string create_temp_filename() 17 | { 18 | char fn[] = "/tmp/process_XXXXXX"; 19 | return mktemp(fn); 20 | } 21 | } 22 | 23 | const std::string temp_filename = create_temp_filename(); 24 | 25 | bool file_exists(const std::string& filename_) 26 | { 27 | return std::ifstream(filename_.c_str()); 28 | } 29 | 30 | bool file_descriptor_is_open(int fd_) 31 | { 32 | std::ostringstream s; 33 | s << "/proc/self/fd/" << fd_; 34 | return file_exists(s.str()); 35 | } 36 | 37 | void write_file(const std::string& filename_, const std::string& content_) 38 | { 39 | std::ofstream f(filename_.c_str()); 40 | f << content_; 41 | } 42 | 43 | std::string read_file(const std::string& filename_) 44 | { 45 | std::ifstream f(filename_.c_str()); 46 | std::ostringstream s; 47 | const int buffsize = 1024; 48 | char buff[buffsize]; 49 | while (const std::streamsize len = f.readsome(buff, buffsize)) 50 | { 51 | s.write(buff, len); 52 | } 53 | return s.str(); 54 | } 55 | 56 | 57 | -------------------------------------------------------------------------------- /include/process/impl/output_file.hpp: -------------------------------------------------------------------------------- 1 | #ifndef PROCESS_IMPL_OUTPUT_FILE_HPP 2 | #define PROCESS_IMPL_OUTPUT_FILE_HPP 3 | 4 | // Copyright Abel Sinkovics (abel@sinkovics.hu) 2013. 5 | // Distributed under the Boost Software License, Version 1.0. 6 | // (See accompanying file LICENSE_1_0.txt or copy at 7 | // http://www.boost.org/LICENSE_1_0.txt) 8 | 9 | #include 10 | 11 | #include 12 | #include 13 | 14 | #include 15 | 16 | namespace process 17 | { 18 | namespace impl 19 | { 20 | class output_file : boost::noncopyable 21 | { 22 | public: 23 | explicit output_file(int fd_) : _fd(fd_) {} 24 | 25 | ~output_file() 26 | { 27 | if (_fd != -1) 28 | { 29 | close(); 30 | } 31 | } 32 | 33 | ssize_t write(const char* buff_, size_t count_) 34 | { 35 | return ::write(_fd, buff_, count_); 36 | } 37 | 38 | ssize_t write(const std::string& s_) 39 | { 40 | return write(s_.c_str(), s_.length()); 41 | } 42 | 43 | void close() 44 | { 45 | assert(_fd != -1); 46 | ::close(_fd); 47 | _fd = -1; 48 | } 49 | 50 | void use_as(int fd_) 51 | { 52 | dup2(_fd, fd_); 53 | } 54 | 55 | void close_on_exec() 56 | { 57 | fcntl(_fd, F_SETFD, fcntl(_fd, F_GETFD) | FD_CLOEXEC); 58 | } 59 | private: 60 | int _fd; 61 | }; 62 | } 63 | } 64 | 65 | #endif 66 | 67 | -------------------------------------------------------------------------------- /test/test_output_file.cpp: -------------------------------------------------------------------------------- 1 | // Copyright Abel Sinkovics (abel@sinkovics.hu) 2013. 2 | // Distributed under the Boost Software License, Version 1.0. 3 | // (See accompanying file LICENSE_1_0.txt or copy at 4 | // http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #include 7 | 8 | #include "util.hpp" 9 | 10 | #include 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | using namespace process::impl; 22 | 23 | namespace 24 | { 25 | int open_temp_file() 26 | { 27 | return 28 | open( 29 | temp_filename.c_str(), 30 | O_CREAT | O_WRONLY | O_TRUNC, 31 | S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH 32 | ); 33 | } 34 | } 35 | 36 | BOOST_AUTO_TEST_CASE(test_destructor_closing_output_file) 37 | { 38 | const int fd = open_temp_file(); 39 | { 40 | output_file f(fd); 41 | } 42 | 43 | BOOST_CHECK(!file_descriptor_is_open(fd)); 44 | } 45 | 46 | BOOST_AUTO_TEST_CASE(test_write_to_output_file) 47 | { 48 | output_file f(open_temp_file()); 49 | f.write("hello", 5); 50 | 51 | BOOST_CHECK_EQUAL("hello", read_file(temp_filename)); 52 | } 53 | 54 | BOOST_AUTO_TEST_CASE(test_close_output_file) 55 | { 56 | const int fd = open_temp_file(); 57 | output_file f(fd); 58 | f.close(); 59 | 60 | BOOST_CHECK(!file_descriptor_is_open(fd)); 61 | } 62 | 63 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Process 2 | 3 | [![Build Status](https://secure.travis-ci.org/sabel83/process.png?branch=master "Build Status")](http://travis-ci.org/sabel83/process) 4 | 5 | Process is a library for running an external process and capturing its standard 6 | output and error. 7 | 8 | ## Installing the library (Linux) 9 | 10 | * Get the source code: `git clone https://github.com/sabel83/process && cd process` 11 | * Create the build directory: `mkdir bin && bin` 12 | * Generate the Makefiles: `cmake ..` 13 | * Build the tests: `make` 14 | * Run the tests: `make test` 15 | * Copy the headers: `make install` (You may need to run this as `root`) 16 | 17 | ## Usage 18 | 19 | You need to include the `` header file which provides the 20 | following template function: 21 | 22 | ```cpp 23 | template 24 | process::output process::run(const Seq& cmd_, const std::string& input_) 25 | ``` 26 | 27 | The first argument, `cmd_` is a sequence of `std::string` objects. It defines 28 | the command to execute. The second argument, `input_` is what should be passed 29 | to the executed process as its standard input. The function launches the 30 | command and captures its standard output and error. It blocks until the process 31 | terminates and its entire standard output and error is available. 32 | 33 | The resulting `output` object contains the captured standard output and error 34 | which can be queried by calling its `standard_output` and `standard_error` 35 | methods. 36 | 37 | ## License 38 | 39 | Process is published under the 40 | [Boost Software License](http://www.boost.org/LICENSE_1_0.txt). 41 | 42 | 43 | -------------------------------------------------------------------------------- /include/process/impl/input_file.hpp: -------------------------------------------------------------------------------- 1 | #ifndef PROCESS_IMPL_INPUT_FILE_HPP 2 | #define PROCESS_IMPL_INPUT_FILE_HPP 3 | 4 | // Copyright Abel Sinkovics (abel@sinkovics.hu) 2013. 5 | // Distributed under the Boost Software License, Version 1.0. 6 | // (See accompanying file LICENSE_1_0.txt or copy at 7 | // http://www.boost.org/LICENSE_1_0.txt) 8 | 9 | #include 10 | 11 | #include 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | namespace process 18 | { 19 | namespace impl 20 | { 21 | class input_file : boost::noncopyable 22 | { 23 | public: 24 | explicit input_file(int fd_) : _fd(fd_), _eof(false) {} 25 | 26 | ~input_file() 27 | { 28 | if (_fd != -1) 29 | { 30 | close(); 31 | } 32 | } 33 | 34 | ssize_t read(char* buf_, size_t count_) 35 | { 36 | const ssize_t len = ::read(_fd, buf_, count_); 37 | _eof = (len == 0); 38 | return len; 39 | } 40 | 41 | bool eof() const 42 | { 43 | return _eof; 44 | } 45 | 46 | std::string read() 47 | { 48 | std::ostringstream s; 49 | static const int buffsize = 1024; 50 | char buff[buffsize]; 51 | while (!eof()) 52 | { 53 | const ssize_t len = read(buff, buffsize); 54 | s.write(buff, len); 55 | } 56 | return s.str(); 57 | } 58 | 59 | void close() 60 | { 61 | assert(_fd != -1); 62 | ::close(_fd); 63 | _fd = -1; 64 | } 65 | 66 | void use_as(int fd_) 67 | { 68 | dup2(_fd, fd_); 69 | } 70 | private: 71 | int _fd; 72 | bool _eof; 73 | }; 74 | } 75 | } 76 | 77 | #endif 78 | 79 | -------------------------------------------------------------------------------- /test/test_run.cpp: -------------------------------------------------------------------------------- 1 | // Copyright Abel Sinkovics (abel@sinkovics.hu) 2013. 2 | // Distributed under the Boost Software License, Version 1.0. 3 | // (See accompanying file LICENSE_1_0.txt or copy at 4 | // http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #include 7 | 8 | #include "util.hpp" 9 | 10 | #include 11 | 12 | #include 13 | 14 | #include 15 | #include 16 | 17 | using namespace process; 18 | 19 | BOOST_AUTO_TEST_CASE(test_command_is_executed) 20 | { 21 | using std::vector; 22 | using std::string; 23 | using boost::assign::list_of; 24 | 25 | const vector cmd = list_of 26 | ("/bin/bash")("-c")("echo hello > " + temp_filename); 27 | 28 | write_file(temp_filename, "something but hello"); 29 | run(cmd, ""); 30 | 31 | BOOST_CHECK_EQUAL("hello\n", read_file(temp_filename)); 32 | } 33 | 34 | BOOST_AUTO_TEST_CASE(test_output_of_command) 35 | { 36 | using std::vector; 37 | using std::string; 38 | using boost::assign::list_of; 39 | 40 | const vector cmd = list_of 41 | ("/bin/bash")("-c")("echo hello"); 42 | 43 | const output o = run(cmd, ""); 44 | 45 | BOOST_CHECK_EQUAL("hello\n", o.standard_output()); 46 | } 47 | 48 | BOOST_AUTO_TEST_CASE(test_error_of_command) 49 | { 50 | using std::vector; 51 | using std::string; 52 | using boost::assign::list_of; 53 | 54 | const vector cmd = list_of 55 | ("/bin/bash")("-c")("echo hello 1>&2"); 56 | 57 | const output o = run(cmd, ""); 58 | 59 | BOOST_CHECK_EQUAL("hello\n", o.standard_error()); 60 | } 61 | 62 | BOOST_AUTO_TEST_CASE(test_input_of_command) 63 | { 64 | using std::vector; 65 | using std::string; 66 | using boost::assign::list_of; 67 | 68 | const vector cmd(1, "/bin/cat"); 69 | 70 | const output o = run(cmd, "hello world"); 71 | 72 | BOOST_CHECK_EQUAL("hello world", o.standard_output()); 73 | } 74 | 75 | BOOST_AUTO_TEST_CASE(test_invalid_command) 76 | { 77 | using std::vector; 78 | using std::string; 79 | 80 | const vector cmd(1, "/some_invalid_command"); 81 | 82 | BOOST_CHECK_THROW(run(cmd, ""), process::exception); 83 | } 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /include/process/process.hpp: -------------------------------------------------------------------------------- 1 | #ifndef PROCESS_PROCESS_HPP 2 | #define PROCESS_PROCESS_HPP 3 | 4 | // Copyright Abel Sinkovics (abel@sinkovics.hu) 2013. 5 | // Distributed under the Boost Software License, Version 1.0. 6 | // (See accompanying file LICENSE_1_0.txt or copy at 7 | // http://www.boost.org/LICENSE_1_0.txt) 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | #include 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | namespace process 29 | { 30 | template 31 | output run(const Seq& cmd_, const std::string& input_) 32 | { 33 | using boost::bind; 34 | using boost::algorithm::join; 35 | 36 | using std::vector; 37 | using std::transform; 38 | using std::string; 39 | 40 | assert(!cmd_.empty()); 41 | 42 | vector cmd(cmd_.size() + 1, 0); 43 | transform(cmd_.begin(), cmd_.end(), cmd.begin(), bind(&string::c_str, _1)); 44 | 45 | impl::pipe standard_input; 46 | impl::pipe standard_output; 47 | impl::pipe standard_error; 48 | impl::pipe error_reporting; 49 | 50 | const pid_t pid = fork(); 51 | switch (pid) 52 | { 53 | case -1: 54 | return output("", ""); 55 | case 0: // in child 56 | standard_input.output.close(); 57 | standard_output.input.close(); 58 | standard_error.input.close(); 59 | error_reporting.input.close(); 60 | error_reporting.output.close_on_exec(); 61 | 62 | standard_input.input.use_as(STDIN_FILENO); 63 | standard_output.output.use_as(STDOUT_FILENO); 64 | standard_error.output.use_as(STDERR_FILENO); 65 | 66 | execv(cmd[0], const_cast(&cmd[0])); 67 | { 68 | const int err = errno; 69 | error_reporting.output.write( 70 | "Error running " + join(cmd_, " ") + ": " + strerror(err) 71 | ); 72 | } 73 | _exit(0); 74 | default: // in parent 75 | standard_input.input.close(); 76 | standard_input.output.write(input_); 77 | standard_input.output.close(); 78 | 79 | standard_output.output.close(); 80 | standard_error.output.close(); 81 | 82 | error_reporting.output.close(); 83 | 84 | int status; 85 | waitpid(pid, &status, 0); 86 | 87 | const std::string err = error_reporting.input.read(); 88 | if (err.empty()) 89 | { 90 | return 91 | output(standard_output.input.read(), standard_error.input.read()); 92 | } 93 | else 94 | { 95 | throw exception(err); 96 | } 97 | } 98 | } 99 | } 100 | 101 | #endif 102 | 103 | -------------------------------------------------------------------------------- /test/test_input_file.cpp: -------------------------------------------------------------------------------- 1 | // Copyright Abel Sinkovics (abel@sinkovics.hu) 2013. 2 | // Distributed under the Boost Software License, Version 1.0. 3 | // (See accompanying file LICENSE_1_0.txt or copy at 4 | // http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #include 7 | 8 | #include "util.hpp" 9 | 10 | #include 11 | 12 | #include 13 | #include 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | using namespace process::impl; 21 | 22 | BOOST_AUTO_TEST_CASE(test_destructor_closing_input_file) 23 | { 24 | write_file(temp_filename, "foo"); 25 | const int fd = open(temp_filename.c_str(), O_RDONLY); 26 | { 27 | input_file f(fd); 28 | } 29 | 30 | BOOST_CHECK(!file_descriptor_is_open(fd)); 31 | } 32 | 33 | BOOST_AUTO_TEST_CASE(test_read_from_input_file) 34 | { 35 | write_file(temp_filename, "foo"); 36 | input_file f(open(temp_filename.c_str(), O_RDONLY)); 37 | char buffer; 38 | 39 | BOOST_CHECK_EQUAL(1, f.read(&buffer, 1)); 40 | } 41 | 42 | BOOST_AUTO_TEST_CASE(test_read_content_input_file) 43 | { 44 | write_file(temp_filename, "foo"); 45 | input_file f(open(temp_filename.c_str(), O_RDONLY)); 46 | char buffer; 47 | 48 | BOOST_CHECK_EQUAL(1, f.read(&buffer, 1)); 49 | } 50 | 51 | BOOST_AUTO_TEST_CASE(test_read_first_byte_of_input_file) 52 | { 53 | write_file(temp_filename, "foo"); 54 | 55 | input_file f(open(temp_filename.c_str(), O_RDONLY)); 56 | char buffer; 57 | f.read(&buffer, 1); 58 | 59 | BOOST_CHECK_EQUAL('f', buffer); 60 | } 61 | 62 | BOOST_AUTO_TEST_CASE(test_read_entire_input_file) 63 | { 64 | write_file(temp_filename, "foo"); 65 | std::vector buff(3); 66 | 67 | input_file f(open(temp_filename.c_str(), O_RDONLY)); 68 | f.read(&buff[0], buff.size()); 69 | 70 | BOOST_CHECK_EQUAL("foo", std::string(buff.begin(), buff.end())); 71 | } 72 | 73 | BOOST_AUTO_TEST_CASE(test_not_eof) 74 | { 75 | write_file(temp_filename, "foo"); 76 | input_file f(open(temp_filename.c_str(), O_RDONLY)); 77 | 78 | BOOST_CHECK(!f.eof()); 79 | } 80 | 81 | BOOST_AUTO_TEST_CASE(test_eof_at_end_of_file) 82 | { 83 | write_file(temp_filename, "foo"); 84 | std::vector buff(3); 85 | 86 | input_file f(open(temp_filename.c_str(), O_RDONLY)); 87 | f.read(&buff[0], buff.size()); 88 | f.read(&buff[0], 1); 89 | 90 | BOOST_CHECK(f.eof()); 91 | } 92 | 93 | BOOST_AUTO_TEST_CASE(test_read_entire_file) 94 | { 95 | write_file(temp_filename, "foo bar"); 96 | input_file f(open(temp_filename.c_str(), O_RDONLY)); 97 | 98 | BOOST_CHECK_EQUAL("foo bar", f.read()); 99 | } 100 | 101 | BOOST_AUTO_TEST_CASE(test_close_input_file) 102 | { 103 | write_file(temp_filename, "foo"); 104 | const int fd = open(temp_filename.c_str(), O_RDONLY); 105 | input_file f(fd); 106 | f.close(); 107 | 108 | BOOST_CHECK(!file_descriptor_is_open(fd)); 109 | } 110 | 111 | 112 | 113 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright Abel Sinkovics (abel@sinkovics.hu) 2013. 2 | # Distributed under the Boost Software License, Version 1.0. 3 | # (See accompanying file LICENSE_1_0.txt or copy at 4 | # http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | cmake_minimum_required(VERSION 2.6) 7 | project(process) 8 | set(CMAKE_PROJECT_NAME process) 9 | set(MAJOR_VERSION 0) 10 | set(MINOR_VERSION 1) 11 | set(PATCH_VERSION 0) 12 | 13 | # Boost 14 | # Turn this on to link statically against Boost 15 | set(Boost_USE_STATIC_LIBS ON) 16 | find_package(Boost COMPONENTS 17 | unit_test_framework 18 | ) 19 | include_directories(${Boost_INCLUDE_DIR}) 20 | link_directories(${Boost_LIBRARY_DIRS}) 21 | 22 | enable_testing() 23 | 24 | include_directories("include") 25 | 26 | subdirs(test example) 27 | 28 | # Warning level 29 | if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX) 30 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Werror -pedantic") 31 | message("Setting warning level for GCC") 32 | elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") 33 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Werror -pedantic") 34 | message("Setting warning level for Clang") 35 | endif() 36 | 37 | # Installation 38 | macro(install_with_dir PREFIX_TO_CUT) 39 | 40 | string(LENGTH ${PREFIX_TO_CUT} PREFIX_LEN) 41 | 42 | foreach(F ${ARGN}) 43 | string(REGEX MATCH "(.*)[/\\]" DIR ${F}) 44 | string(LENGTH ${DIR} DIR_LEN) 45 | math(EXPR DIR_KEEP "${DIR_LEN} - ${PREFIX_LEN}") 46 | string(SUBSTRING ${DIR} ${PREFIX_LEN} ${DIR_KEEP} DIR2) 47 | install(FILES ${F} DESTINATION include/${DIR2}) 48 | endforeach(F) 49 | 50 | endmacro(install_with_dir) 51 | 52 | if (CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) 53 | set(CMAKE_INSTALL_PREFIX "/usr") 54 | endif() 55 | 56 | file(GLOB_RECURSE HEADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/include/*.hpp) 57 | install_with_dir(${CMAKE_CURRENT_SOURCE_DIR}/include ${HEADER_FILES}) 58 | 59 | # Debian package 60 | if(EXISTS "${CMAKE_ROOT}/Modules/CPack.cmake") 61 | include(InstallRequiredSystemLibraries) 62 | 63 | set(CPACK_SET_DESTDIR "on") 64 | set(CPACK_PACKAGING_INSTALL_PREFIX "/tmp") 65 | set(CPACK_GENERATOR "DEB") 66 | 67 | set(CPACK_PACKAGE_DESCRIPTION "Process") 68 | set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Library for capturing standard output and error of a process") 69 | set(CPACK_PACKAGE_VENDOR "Abel Sinkovics") 70 | set(CPACK_PACKAGE_CONTACT "abel@sinkovics.hu") 71 | set(CPACK_PACKAGE_VERSION_MAJOR "${MAJOR_VERSION}") 72 | set(CPACK_PACKAGE_VERSION_MINOR "${MINOR_VERSION}") 73 | set(CPACK_PACKAGE_VERSION_PATCH "${PATCH_VERSION}") 74 | set(CPACK_PACKAGE_FILE_NAME "lib${CMAKE_PROJECT_NAME}-${MAJOR_VERSION}.${MINOR_VERSION}.${CPACK_PACKAGE_VERSION_PATCH}-dev") 75 | set(CPACK_SOURCE_PACKAGE_FILE_NAME "lib${CMAKE_PROJECT_NAME}-${MAJOR_VERSION}.${MINOR_VERSION}.${CPACK_PACKAGE_VERSION_PATCH}-dev") 76 | 77 | set(CPACK_DEBIAN_PACKAGE_PRIORITY "optional") 78 | set(CPACK_DEBIAN_PACKAGE_SECTION "devel") 79 | set(CPACK_DEBIAN_ARCHITECTURE "all") # Header-only 80 | 81 | set(CPACK_COMPONENTS_ALL headers) 82 | include(CPack) 83 | 84 | endif(EXISTS "${CMAKE_ROOT}/Modules/CPack.cmake") 85 | 86 | 87 | 88 | --------------------------------------------------------------------------------