handles.hpp 3.9 KB

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