1 // Copyright (c) 2019 Klemens D. Morgenstern 2 // 3 // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 6 #define BOOST_TEST_MAIN 7 #define BOOST_TEST_IGNORE_SIGCHLD 8 #include <boost/test/included/unit_test.hpp> 9 10 #include <iostream> 11 12 #include <boost/process.hpp> 13 #include <boost/process/handles.hpp> 14 #include <boost/process/pipe.hpp> 15 #include <boost/process/io.hpp> 16 #include <boost/process/async_pipe.hpp> 17 #include <boost/process/extend.hpp> 18 19 #include <boost/filesystem.hpp> 20 21 #include <system_error> 22 #include <string> 23 24 #include <boost/asio/ip/tcp.hpp> 25 #include <boost/asio/ip/udp.hpp> 26 27 #if defined(BOOST_WINDOWS_API) 28 #include <boost/winapi/get_current_thread.hpp> 29 #include <boost/winapi/get_current_process.hpp> 30 #endif 31 32 namespace fs = boost::filesystem; 33 namespace bp = boost::process; 34 namespace bt = boost::this_process; 35 36 BOOST_AUTO_TEST_CASE(leak_test, *boost::unit_test::timeout(5)) 37 { 38 using boost::unit_test::framework::master_test_suite; 39 40 #if defined(BOOST_WINDOWS_API) __anon928a30290102(FILE * f) 41 const auto get_handle = [](FILE * f) {return reinterpret_cast<bt::native_handle_type>(_get_osfhandle(_fileno(f)));}; __anon928a30290202(::boost::winapi::UINT_PTR_ sock)42 const auto socket_to_handle = [](::boost::winapi::UINT_PTR_ sock){return reinterpret_cast<::boost::winapi::HANDLE_>(sock);}; 43 #else __anon928a30290302(FILE * f) 44 const auto get_handle = [](FILE * f) {return fileno(f);}; __anon928a30290402(int i)45 const auto socket_to_handle = [](int i){ return i;}; 46 #endif 47 48 std::error_code ec; 49 auto fd_list = bt::get_handles(ec); 50 51 BOOST_CHECK_EQUAL(std::count(fd_list.begin(), fd_list.end(), get_handle(stdin)), 1); 52 BOOST_CHECK_EQUAL(std::count(fd_list.begin(), fd_list.end(), get_handle(stdout)), 1); 53 BOOST_CHECK_EQUAL(std::count(fd_list.begin(), fd_list.end(), get_handle(stderr)), 1); 54 55 BOOST_CHECK(bt::is_stream_handle(get_handle(stdin), ec)); BOOST_CHECK_MESSAGE(!ec, ec.message()); 56 BOOST_CHECK(bt::is_stream_handle(get_handle(stdout), ec)); BOOST_CHECK_MESSAGE(!ec, ec.message()); 57 BOOST_CHECK(bt::is_stream_handle(get_handle(stderr), ec)); BOOST_CHECK_MESSAGE(!ec, ec.message()); 58 59 60 BOOST_CHECK_GE(fd_list.size(), 3u); 61 BOOST_CHECK_GE(bt::get_handles(ec).size(), fd_list.size()); 62 63 bp::pipe p; 64 65 { 66 67 auto fd_list_new = bt::get_handles(ec); 68 BOOST_CHECK_MESSAGE(!ec, ec); 69 BOOST_CHECK_LE(fd_list.size() + 2u, fd_list_new.size()); 70 fd_list = std::move(fd_list_new); 71 } 72 73 74 75 BOOST_CHECK_EQUAL(std::count(fd_list.begin(), fd_list.end(), p.native_source()), 1u); 76 BOOST_CHECK_EQUAL(std::count(fd_list.begin(), fd_list.end(), p.native_sink()), 1u); 77 78 BOOST_CHECK(bt::is_stream_handle(p.native_source(), ec)); BOOST_CHECK_MESSAGE(!ec, ec.message()); 79 BOOST_CHECK(bt::is_stream_handle(p.native_sink(), ec)); BOOST_CHECK_MESSAGE(!ec, ec.message()); 80 81 82 p.close(); 83 fd_list = bt::get_handles(ec); 84 85 BOOST_CHECK_EQUAL(std::count(fd_list.begin(), fd_list.end(), p.native_source()), 0u); 86 BOOST_CHECK_EQUAL(std::count(fd_list.begin(), fd_list.end(), p.native_sink()), 0u); 87 88 #if defined( BOOST_WINDOWS_API ) __anon928a30290502null89 std::thread thr([]{}); 90 BOOST_CHECK(!bt::is_stream_handle(thr.native_handle(), ec)); BOOST_CHECK_MESSAGE(!ec, ec.message()); 91 thr.join(); 92 #else 93 # if defined(TFD_CLOEXEC) //check timer 94 int timer_fd = ::timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC); 95 BOOST_CHECK(!bt::is_stream_handle(timer_fd , ec)); BOOST_CHECK_MESSAGE(!ec, ec.message()); 96 #endif 97 # if defined(EFD_CLOEXEC) && defined(EFD_NONBLOCK) 98 int event_fd =::eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK); 99 BOOST_CHECK(!bt::is_stream_handle(event_fd , ec)); BOOST_CHECK_MESSAGE(!ec, ec.message()); 100 #endif 101 int dir_fd = ::dirfd(::opendir(".")); 102 BOOST_CHECK(!bt::is_stream_handle(dir_fd , ec)); BOOST_CHECK_MESSAGE(!ec, ec.message()); 103 #endif 104 105 106 boost::asio::io_context ioc; 107 boost::asio::ip::tcp::socket tcp_socket(ioc); 108 boost::asio::ip::udp::socket udp_socket(ioc); 109 bp::async_pipe ap(ioc); 110 111 tcp_socket.open(boost::asio::ip::tcp::v4()); 112 udp_socket.open(boost::asio::ip::udp::v4()); 113 114 BOOST_CHECK(bt::is_stream_handle(socket_to_handle(tcp_socket.native_handle()), ec)); BOOST_CHECK_MESSAGE(!ec, ec.message()); 115 BOOST_CHECK(bt::is_stream_handle(socket_to_handle(udp_socket.native_handle()), ec)); BOOST_CHECK_MESSAGE(!ec, ec.message()); 116 BOOST_CHECK(bt::is_stream_handle(std::move(ap).sink(). native_handle(), ec)); BOOST_CHECK_MESSAGE(!ec, ec.message()); 117 BOOST_CHECK(bt::is_stream_handle(std::move(ap).source().native_handle(), ec)); BOOST_CHECK_MESSAGE(!ec, ec.message()); 118 } 119 120 struct on_setup_t 121 { 122 std::vector<bt::native_handle_type> &res; 123 on_setup_ton_setup_t124 on_setup_t(std::vector<bt::native_handle_type> & res) : res(res) {} 125 template<typename Executor> operator ()on_setup_t126 void operator()(Executor & e) 127 { 128 bp::extend::foreach_used_handle(e, [this](bt::native_handle_type handle) 129 { 130 res.push_back(handle); 131 }); 132 } 133 }; 134 135 BOOST_AUTO_TEST_CASE(iterate_handles, *boost::unit_test::timeout(5)) 136 { 137 using boost::unit_test::framework::master_test_suite; 138 139 std::vector<bt::native_handle_type> res; 140 141 bp::pipe p_in; 142 bp::pipe p_out; 143 144 auto source = p_in.native_source(); 145 auto sink = p_out.native_sink(); 146 std::error_code ec; 147 148 BOOST_WARN_NE(source, sink); //Sanity check 149 150 const auto ret = bp::system(master_test_suite().argv[1], "--exit-code" , "42", 151 bp::std_in < p_out, 152 bp::std_out > p_in, 153 bp::extend::on_setup(on_setup_t(res)), ec); 154 155 BOOST_CHECK_MESSAGE(!ec, ec.message()); 156 157 BOOST_CHECK_EQUAL(ret, 42u); 158 BOOST_CHECK_EQUAL(std::count(res.begin(), res.end(), p_in. native_sink()), 0u); 159 BOOST_CHECK_EQUAL(std::count(res.begin(), res.end(), p_out.native_source()), 0u); 160 } 161 162 BOOST_AUTO_TEST_CASE(limit_fd, *boost::unit_test::timeout(5)) 163 { 164 #if defined(BOOST_WINDOWS_API) __anon928a30290702(FILE * f)165 const auto get_handle = [](FILE * f){return std::to_string(_get_osfhandle(_fileno(f)));}; 166 #else 167 const auto get_handle = [](FILE * f){return std::to_string(fileno(f));}; 168 #endif 169 170 using boost::unit_test::framework::master_test_suite; 171 172 BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", get_handle(stdin), bp::std_err > stderr), EXIT_SUCCESS); 173 BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", get_handle(stderr), bp::std_err > stderr), EXIT_SUCCESS); 174 175 176 BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", get_handle(stdin), bp::std_err > stderr, bp::limit_handles), EXIT_FAILURE); 177 BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", get_handle(stderr), bp::std_err > stderr, bp::limit_handles), EXIT_SUCCESS); 178 179 180 } 181