handles.hpp 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  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. #ifndef BOOST_PROCESS_DETAIL_POSIX_HANDLES_HPP_
  6. #define BOOST_PROCESS_DETAIL_POSIX_HANDLES_HPP_
  7. #include <vector>
  8. #include <system_error>
  9. #include <dirent.h>
  10. #include <sys/stat.h>
  11. #include <algorithm>
  12. #include <memory>
  13. #include <cstdlib>
  14. #include <boost/process/v1/detail/posix/handler.hpp>
  15. namespace boost { namespace process { BOOST_PROCESS_V1_INLINE namespace v1 { namespace detail { namespace posix {
  16. using native_handle_type = int;
  17. inline std::vector<native_handle_type> get_handles(std::error_code & ec)
  18. {
  19. std::vector<native_handle_type> res;
  20. std::unique_ptr<DIR, void(*)(DIR*)> dir{::opendir("/dev/fd"), +[](DIR* p){::closedir(p);}};
  21. if (!dir)
  22. {
  23. ec = ::boost::process::v1::detail::get_last_error();
  24. return {};
  25. }
  26. else
  27. ec.clear();
  28. auto my_fd = dirfd(dir.get());
  29. struct ::dirent * ent_p;
  30. while ((ent_p = readdir(dir.get())) != nullptr)
  31. {
  32. if (ent_p->d_name[0] == '.')
  33. continue;
  34. const auto conv = std::atoi(ent_p->d_name);
  35. if (conv == 0 && (ent_p->d_name[0] != '0' && ent_p->d_name[1] != '\0'))
  36. continue;
  37. if (conv == my_fd)
  38. continue;
  39. res.push_back(conv);
  40. }
  41. return res;
  42. }
  43. inline std::vector<native_handle_type> get_handles()
  44. {
  45. std::error_code ec;
  46. auto res = get_handles(ec);
  47. if (ec)
  48. boost::process::v1::detail::throw_error(ec, "open_dir(\"/dev/fd\") failed");
  49. return res;
  50. }
  51. inline bool is_stream_handle(native_handle_type handle, std::error_code & ec)
  52. {
  53. struct ::stat stat_;
  54. if (::fstat(handle, &stat_) != 0)
  55. {
  56. ec = ::boost::process::v1::detail::get_last_error();
  57. }
  58. else
  59. ec.clear();
  60. return S_ISCHR (stat_.st_mode) //This macro returns non-zero if the file is a character special file (a device like a terminal).
  61. || S_ISBLK (stat_.st_mode) // This macro returns non-zero if the file is a block special file (a device like a disk).
  62. || S_ISREG (stat_.st_mode) // This macro returns non-zero if the file is a regular file.
  63. || S_ISFIFO (stat_.st_mode) // This macro returns non-zero if the file is a FIFO special file, or a pipe. See section 15. Pipes and FIFOs.
  64. || S_ISSOCK (stat_.st_mode) ;// This macro returns non-zero if the file is a socket. See section 16. Sockets.;
  65. }
  66. inline bool is_stream_handle(native_handle_type handle)
  67. {
  68. std::error_code ec;
  69. auto res = is_stream_handle(handle, ec);
  70. if (ec)
  71. boost::process::v1::detail::throw_error(ec, "fstat() failed");
  72. return res;
  73. }
  74. struct limit_handles_ : handler_base_ext
  75. {
  76. limit_handles_() {}
  77. ~limit_handles_() {}
  78. mutable std::vector<int> used_handles;
  79. template<typename Executor>
  80. void on_setup(Executor & exec) const
  81. {
  82. used_handles = get_used_handles(exec);
  83. }
  84. template<typename Executor>
  85. void on_exec_setup(Executor & exec) const
  86. {
  87. auto dir = ::opendir("/dev/fd");
  88. if (!dir)
  89. {
  90. exec.set_error(::boost::process::v1::detail::get_last_error(), "opendir(\"/dev/fd\")");
  91. return;
  92. }
  93. auto my_fd = dirfd(dir);
  94. struct ::dirent * ent_p;
  95. while ((ent_p = readdir(dir)) != nullptr)
  96. {
  97. if (ent_p->d_name[0] == '.')
  98. continue;
  99. const auto conv = std::atoi(ent_p->d_name);
  100. if ((conv == my_fd) || (conv == -1))
  101. continue;
  102. if (std::find(used_handles.begin(), used_handles.end(), conv) != used_handles.end())
  103. continue;
  104. if (::close(conv) != 0)
  105. {
  106. exec.set_error(::boost::process::v1::detail::get_last_error(), "close() failed");
  107. return;
  108. }
  109. }
  110. ::closedir(dir);
  111. }
  112. };
  113. }}}}}
  114. #endif //PROCESS_HANDLES_HPP