close_handles.ipp 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. //
  2. // boost/process/v2/posix/detail/close_handles.hpp
  3. // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  4. //
  5. // Copyright (c) 2022 Klemens D. Morgenstern (klemens dot morgenstern at gmx dot net)
  6. //
  7. // Distributed under the Boost Software License, Version 1.0. (See accompanying
  8. // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  9. //
  10. #ifndef BOOST_PROCESS_V2_POSIX_DETAIL_CLOSE_HANDLES_IPP
  11. #define BOOST_PROCESS_V2_POSIX_DETAIL_CLOSE_HANDLES_IPP
  12. #include <boost/process/v2/detail/config.hpp>
  13. #include <boost/process/v2/detail/last_error.hpp>
  14. #include <boost/process/v2/posix/detail/close_handles.hpp>
  15. // linux has close_range since 5.19
  16. #if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__)
  17. // https://www.freebsd.org/cgi/man.cgi?query=close_range&apropos=0&sektion=0&manpath=FreeBSD+13.1-RELEASE+and+Ports&arch=default&format=html
  18. // https://man.netbsd.org/closefrom.3
  19. // __FreeBSD__
  20. //
  21. // gives us
  22. //
  23. // int closefrom(int fd);
  24. // int close_range(u_int lowfd, u_int highfd, int flags);
  25. #include <unistd.h>
  26. #define BOOST_PROCESS_V2_HAS_CLOSE_RANGE_AND_CLOSEFROM 1
  27. #elif defined(__sun)
  28. /*https://docs.oracle.com/cd/E36784_01/html/E36874/closefrom-3c.html
  29. int fdwalk(int (*func)(void *, int), void *cd);
  30. */
  31. #include <stdlib.h>
  32. #define BOOST_PROCESS_V2_HAS_PDFORK 1
  33. #elif defined(__linux__)
  34. #include <linux/version.h>
  35. #if LINUX_VERSION_CODE >= KERNEL_VERSION(5,11,0)
  36. // https://man7.org/linux/man-pages/man2/close_range.2.html
  37. #include <linux/close_range.h>
  38. #define BOOST_PROCESS_V2_HAS_CLOSE_RANGE 1
  39. #else
  40. #include <dirent.h>
  41. #endif
  42. #else
  43. #include <dirent.h>
  44. #endif
  45. BOOST_PROCESS_V2_BEGIN_NAMESPACE
  46. namespace posix
  47. {
  48. namespace detail
  49. {
  50. #if defined(BOOST_PROCESS_V2_HAS_PDFORK)
  51. void close_all(const std::vector<int> & whitelist, error_code & ec)
  52. {
  53. fdwalk(+[](void * p, int fd)
  54. {
  55. const auto & wl = *static_cast<const std::vector<int>*>(p);
  56. if (std::find(wl.begin(), wl.end(), fd) == wl.end())
  57. return ::close(fd);
  58. else
  59. return 0;
  60. }, const_cast<void*>(static_cast<const void*>(&whitelist)) );
  61. ec = BOOST_PROCESS_V2_NAMESPACE::detail::get_last_error();
  62. }
  63. #elif defined(BOOST_PROCESS_V2_HAS_CLOSE_RANGE_AND_CLOSEFROM)
  64. // freeBSD impl - whitelist must be ordered
  65. void close_all(const std::vector<int> & whitelist, error_code & ec)
  66. {
  67. //the most common scenario is whitelist = {0,1,2}
  68. if (!whitelist.empty())
  69. {
  70. if (whitelist.front() != 0)
  71. ::close_range(0, whitelist.front() - 1, 0);
  72. for (std::size_t idx = 0u;
  73. idx < (whitelist.size() - 1u);
  74. idx++)
  75. {
  76. const auto mine = whitelist[idx];
  77. const auto next = whitelist[idx];
  78. if ((mine + 1) != next && (mine != next))
  79. {
  80. ::close_range(mine + 1, next - 1, 0);
  81. }
  82. }
  83. ::closefrom(whitelist.back() + 1);
  84. }
  85. else
  86. ::closefrom(0);
  87. }
  88. #elif defined(BOOST_PROCESS_V2_HAS_CLOSE_RANGE)
  89. // linux impl - whitelist must be ordered
  90. void close_all(const std::vector<int> & whitelist, error_code & ec)
  91. {
  92. // https://patchwork.kernel.org/project/linux-fsdevel/cover/20200602204219.186620-1-christian.brauner@ubuntu.com/
  93. //the most common scenario is whitelist = {0,1,2}
  94. if (!whitelist.empty())
  95. {
  96. if (whitelist.front() != 0)
  97. ::close_range(0, whitelist.front() - 1, CLOSE_RANGE_UNSHARE);
  98. for (std::size_t idx = 0u;
  99. idx < (whitelist.size() - 1u);
  100. idx++)
  101. {
  102. const auto mine = whitelist[idx];
  103. const auto next = whitelist[idx];
  104. if ((mine + 1) != next && (mine != next))
  105. {
  106. ::close_range(mine + 1, next - 1, CLOSE_RANGE_UNSHARE);
  107. }
  108. }
  109. ::close_range(whitelist.back() + 1, std::numeric_limits<int>::max(), CLOSE_RANGE_UNSHARE);
  110. }
  111. else
  112. ::close_range(0, std::numeric_limits<int>::max(), CLOSE_RANGE_UNSHARE);
  113. }
  114. #else
  115. // default one
  116. void close_all(const std::vector<int> & whitelist, error_code & ec)
  117. {
  118. std::unique_ptr<DIR, void(*)(DIR*)> dir{::opendir("/dev/fd"), +[](DIR* p){::closedir(p);}};
  119. if (dir.get() == nullptr)
  120. {
  121. ec = BOOST_PROCESS_V2_NAMESPACE::detail::get_last_error();
  122. return ;
  123. }
  124. auto dir_fd = ::dirfd(dir.get());
  125. if (dir_fd == -1)
  126. {
  127. ec = BOOST_PROCESS_V2_NAMESPACE::detail::get_last_error();
  128. return ;
  129. }
  130. struct ::dirent * ent_p;
  131. while ((ent_p = ::readdir(dir.get())) != nullptr)
  132. {
  133. if (ent_p->d_name[0] == '.')
  134. continue;
  135. const auto conv = std::atoi(ent_p->d_name);
  136. if (conv == 0 && (ent_p->d_name[0] != '0' && ent_p->d_name[1] != '\0'))
  137. continue;
  138. if (conv == dir_fd
  139. || (std::find(whitelist.begin(), whitelist.end(), conv) != whitelist.end()))
  140. continue;
  141. ::close(conv);
  142. }
  143. }
  144. #endif
  145. }
  146. }
  147. BOOST_PROCESS_V2_END_NAMESPACE
  148. #endif //BOOST_PROCESS_V2_POSIX_DETAIL_CLOSE_HANDLES_IPP