// // boost/process/v2/poxix/detail/close_handles.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2022 Klemens D. Morgenstern (klemens dot morgenstern at gmx dot net) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef BOOST_PROCESS_V2_POSIX_DETAIL_CLOSE_HANDLES_IPP #define BOOST_PROCESS_V2_POSIX_DETAIL_CLOSE_HANDLES_IPP #include #include #include // linux has close_range since 5.19 #if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__) // 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 // https://man.netbsd.org/closefrom.3 // __FreeBSD__ // // gives us // // int closefrom(int fd); // int close_range(u_int lowfd, u_int highfd, int flags); #include #define BOOST_PROCESS_V2_HAS_CLOSE_RANGE_AND_CLOSEFROM 1 #elif defined(__sun) /*https://docs.oracle.com/cd/E36784_01/html/E36874/closefrom-3c.html int fdwalk(int (*func)(void *, int), void *cd); */ #include #define BOOST_PROCESS_V2_HAS_PDFORK 1 #elif defined(__linux__) #include #if LINUX_VERSION_CODE >= KERNEL_VERSION(5,11,0) // https://man7.org/linux/man-pages/man2/close_range.2.html #include #define BOOST_PROCESS_V2_HAS_CLOSE_RANGE 1 #else #include #endif #else #include #endif BOOST_PROCESS_V2_BEGIN_NAMESPACE namespace posix { namespace detail { #if defined(BOOST_PROCESS_V2_HAS_PDFORK) void close_all(const std::vector & whitelist, error_code & ec) { fdwalk(+[](void * p, int fd) { const auto & wl = *static_cast*>(p); if (std::find(wl.begin(), wl.end(), fd) == wl.end()) return ::close(fd); else return 0; }, const_cast(static_cast(&whitelist)) ); ec = BOOST_PROCESS_V2_NAMESPACE::detail::get_last_error(); } #elif defined(BOOST_PROCESS_V2_HAS_CLOSE_RANGE_AND_CLOSEFROM) // freeBSD impl - whitelist must be ordered void close_all(const std::vector & whitelist, error_code & ec) { //the most common scenario is whitelist = {0,1,2} if (!whitelist.empty()) { if (whitelist.front() != 0) ::close_range(0, whitelist.front() - 1, 0); for (std::size_t idx = 0u; idx < (whitelist.size() - 1u); idx++) { const auto mine = whitelist[idx]; const auto next = whitelist[idx]; if ((mine + 1) != next && (mine != next)) { ::close_range(mine + 1, next - 1, 0); } } ::closefrom(whitelist.back() + 1); } else ::closefrom(0); } #elif defined(BOOST_PROCESS_V2_HAS_CLOSE_RANGE) // linux impl - whitelist must be ordered void close_all(const std::vector & whitelist, error_code & ec) { // https://patchwork.kernel.org/project/linux-fsdevel/cover/20200602204219.186620-1-christian.brauner@ubuntu.com/ //the most common scenario is whitelist = {0,1,2} if (!whitelist.empty()) { if (whitelist.front() != 0) ::close_range(0, whitelist.front() - 1, CLOSE_RANGE_UNSHARE); for (std::size_t idx = 0u; idx < (whitelist.size() - 1u); idx++) { const auto mine = whitelist[idx]; const auto next = whitelist[idx]; if ((mine + 1) != next && (mine != next)) { ::close_range(mine + 1, next - 1, CLOSE_RANGE_UNSHARE); } } ::close_range(whitelist.back() + 1, std::numeric_limits::max(), CLOSE_RANGE_UNSHARE); } else ::close_range(0, std::numeric_limits::max(), CLOSE_RANGE_UNSHARE); } #else // default one void close_all(const std::vector & whitelist, error_code & ec) { std::unique_ptr dir{::opendir("/dev/fd"), +[](DIR* p){::closedir(p);}}; if (dir.get() == nullptr) { ec = BOOST_PROCESS_V2_NAMESPACE::detail::get_last_error(); return ; } auto dir_fd = ::dirfd(dir.get()); if (dir_fd == -1) { ec = BOOST_PROCESS_V2_NAMESPACE::detail::get_last_error(); return ; } struct ::dirent * ent_p; while ((ent_p = ::readdir(dir.get())) != nullptr) { if (ent_p->d_name[0] == '.') continue; const auto conv = std::atoi(ent_p->d_name); if (conv == 0 && (ent_p->d_name[0] != '0' && ent_p->d_name[1] != '\0')) continue; if (conv == dir_fd || (std::find(whitelist.begin(), whitelist.end(), conv) != whitelist.end())) continue; ::close(conv); } } #endif } } BOOST_PROCESS_V2_END_NAMESPACE #endif //BOOST_PROCESS_V2_POSIX_DETAIL_CLOSE_HANDLES_IPP