limit_fd.cpp 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  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. #define BOOST_TEST_MAIN
  6. #define BOOST_TEST_IGNORE_SIGCHLD
  7. #include <boost/test/included/unit_test.hpp>
  8. #include <iostream>
  9. #include <boost/process.hpp>
  10. #include <boost/process/handles.hpp>
  11. #include <boost/process/pipe.hpp>
  12. #include <boost/process/io.hpp>
  13. #include <boost/process/async_pipe.hpp>
  14. #include <boost/process/extend.hpp>
  15. #include <boost/filesystem.hpp>
  16. #include <system_error>
  17. #include <string>
  18. #include <boost/asio/ip/tcp.hpp>
  19. #include <boost/asio/ip/udp.hpp>
  20. #if defined(BOOST_WINDOWS_API)
  21. #include <boost/winapi/get_current_thread.hpp>
  22. #include <boost/winapi/get_current_process.hpp>
  23. #endif
  24. namespace fs = boost::filesystem;
  25. namespace bp = boost::process;
  26. namespace bt = boost::this_process;
  27. BOOST_AUTO_TEST_CASE(leak_test, *boost::unit_test::timeout(5))
  28. {
  29. using boost::unit_test::framework::master_test_suite;
  30. #if defined(BOOST_WINDOWS_API)
  31. const auto get_handle = [](FILE * f) {return reinterpret_cast<bt::native_handle_type>(_get_osfhandle(_fileno(f)));};
  32. const auto socket_to_handle = [](::boost::winapi::UINT_PTR_ sock){return reinterpret_cast<::boost::winapi::HANDLE_>(sock);};
  33. #else
  34. const auto get_handle = [](FILE * f) {return fileno(f);};
  35. const auto socket_to_handle = [](int i){ return i;};
  36. #endif
  37. std::error_code ec;
  38. auto fd_list = bt::get_handles(ec);
  39. BOOST_CHECK_EQUAL(std::count(fd_list.begin(), fd_list.end(), get_handle(stdin)), 1);
  40. BOOST_CHECK_EQUAL(std::count(fd_list.begin(), fd_list.end(), get_handle(stdout)), 1);
  41. BOOST_CHECK_EQUAL(std::count(fd_list.begin(), fd_list.end(), get_handle(stderr)), 1);
  42. BOOST_CHECK(bt::is_stream_handle(get_handle(stdin), ec)); BOOST_CHECK_MESSAGE(!ec, ec.message());
  43. BOOST_CHECK(bt::is_stream_handle(get_handle(stdout), ec)); BOOST_CHECK_MESSAGE(!ec, ec.message());
  44. BOOST_CHECK(bt::is_stream_handle(get_handle(stderr), ec)); BOOST_CHECK_MESSAGE(!ec, ec.message());
  45. BOOST_CHECK_GE(fd_list.size(), 3);
  46. BOOST_CHECK_GE(bt::get_handles(ec).size(), fd_list.size());
  47. bp::pipe p;
  48. {
  49. auto fd_list_new = bt::get_handles(ec);
  50. BOOST_CHECK_MESSAGE(!ec, ec);
  51. BOOST_CHECK_LE(fd_list.size() + 2, fd_list_new.size());
  52. fd_list = std::move(fd_list_new);
  53. }
  54. BOOST_CHECK_EQUAL(std::count(fd_list.begin(), fd_list.end(), p.native_source()), 1);
  55. BOOST_CHECK_EQUAL(std::count(fd_list.begin(), fd_list.end(), p.native_sink()), 1);
  56. BOOST_CHECK(bt::is_stream_handle(p.native_source(), ec)); BOOST_CHECK_MESSAGE(!ec, ec.message());
  57. BOOST_CHECK(bt::is_stream_handle(p.native_sink(), ec)); BOOST_CHECK_MESSAGE(!ec, ec.message());
  58. p.close();
  59. fd_list = bt::get_handles(ec);
  60. BOOST_CHECK_EQUAL(std::count(fd_list.begin(), fd_list.end(), p.native_source()), 0);
  61. BOOST_CHECK_EQUAL(std::count(fd_list.begin(), fd_list.end(), p.native_sink()), 0);
  62. #if defined( BOOST_WINDOWS_API )
  63. std::thread thr([]{});
  64. BOOST_CHECK(!bt::is_stream_handle(thr.native_handle(), ec)); BOOST_CHECK_MESSAGE(!ec, ec.message());
  65. thr.join();
  66. #else
  67. # if defined(TFD_CLOEXEC) //check timer
  68. int timer_fd = ::timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC);
  69. BOOST_CHECK(!bt::is_stream_handle(timer_fd , ec)); BOOST_CHECK_MESSAGE(!ec, ec.message());
  70. #endif
  71. # if defined(EFD_CLOEXEC) && defined(EFD_NONBLOCK)
  72. int event_fd =::eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
  73. BOOST_CHECK(!bt::is_stream_handle(event_fd , ec)); BOOST_CHECK_MESSAGE(!ec, ec.message());
  74. #endif
  75. int dir_fd = ::dirfd(::opendir("."));
  76. BOOST_CHECK(!bt::is_stream_handle(dir_fd , ec)); BOOST_CHECK_MESSAGE(!ec, ec.message());
  77. #endif
  78. boost::asio::io_context ioc;
  79. boost::asio::ip::tcp::socket tcp_socket(ioc);
  80. boost::asio::ip::udp::socket udp_socket(ioc);
  81. bp::async_pipe ap(ioc);
  82. tcp_socket.open(boost::asio::ip::tcp::v4());
  83. udp_socket.open(boost::asio::ip::udp::v4());
  84. BOOST_CHECK(bt::is_stream_handle(socket_to_handle(tcp_socket.native_handle()), ec)); BOOST_CHECK_MESSAGE(!ec, ec.message());
  85. BOOST_CHECK(bt::is_stream_handle(socket_to_handle(udp_socket.native_handle()), ec)); BOOST_CHECK_MESSAGE(!ec, ec.message());
  86. BOOST_CHECK(bt::is_stream_handle(std::move(ap).sink(). native_handle(), ec)); BOOST_CHECK_MESSAGE(!ec, ec.message());
  87. BOOST_CHECK(bt::is_stream_handle(std::move(ap).source().native_handle(), ec)); BOOST_CHECK_MESSAGE(!ec, ec.message());
  88. }
  89. struct on_setup_t
  90. {
  91. std::vector<bt::native_handle_type> &res;
  92. on_setup_t(std::vector<bt::native_handle_type> & res) : res(res) {}
  93. template<typename Executor>
  94. void operator()(Executor & e)
  95. {
  96. bp::extend::foreach_used_handle(e, [this](bt::native_handle_type handle)
  97. {
  98. res.push_back(handle);
  99. std::cout << "Pushing " << handle << std::endl;
  100. });
  101. }
  102. };
  103. BOOST_AUTO_TEST_CASE(iterate_handles, *boost::unit_test::timeout(5))
  104. {
  105. using boost::unit_test::framework::master_test_suite;
  106. std::vector<bt::native_handle_type> res;
  107. bp::pipe p_in;
  108. bp::pipe p_out;
  109. auto source = p_in.native_source();
  110. auto sink = p_out.native_sink();
  111. std::error_code ec;
  112. BOOST_WARN_NE(source, sink); //Sanity check
  113. const auto ret = bp::system(master_test_suite().argv[1], "--exit-code" , "42",
  114. bp::std_in < p_out,
  115. bp::std_out > p_in,
  116. bp::extend::on_setup(on_setup_t(res)), ec);
  117. BOOST_CHECK_MESSAGE(!ec, ec.message());
  118. BOOST_CHECK_EQUAL(ret, 42);
  119. BOOST_CHECK_EQUAL(std::count(res.begin(), res.end(), p_in. native_sink()), 0);
  120. BOOST_CHECK_EQUAL(std::count(res.begin(), res.end(), p_out.native_source()), 0);
  121. }
  122. BOOST_AUTO_TEST_CASE(limit_fd, *boost::unit_test::timeout(5))
  123. {
  124. #if defined(BOOST_WINDOWS_API)
  125. const auto get_handle = [](FILE * f){return std::to_string(_get_osfhandle(_fileno(f)));};
  126. #else
  127. const auto get_handle = [](FILE * f){return std::to_string(fileno(f));};
  128. #endif
  129. using boost::unit_test::framework::master_test_suite;
  130. BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", get_handle(stdout), bp::std_err > stderr), EXIT_SUCCESS);
  131. BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", get_handle(stderr), bp::std_err > stderr), EXIT_SUCCESS);
  132. BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", get_handle(stdout), bp::std_err > stderr, bp::limit_handles), EXIT_FAILURE);
  133. BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", get_handle(stderr), bp::std_err > stderr, bp::limit_handles), EXIT_SUCCESS);
  134. }