diff options
Diffstat (limited to 'boost/process/detail/posix/handles.hpp')
-rw-r--r-- | boost/process/detail/posix/handles.hpp | 146 |
1 files changed, 146 insertions, 0 deletions
diff --git a/boost/process/detail/posix/handles.hpp b/boost/process/detail/posix/handles.hpp new file mode 100644 index 0000000000..1572f05933 --- /dev/null +++ b/boost/process/detail/posix/handles.hpp @@ -0,0 +1,146 @@ +// Copyright (c) 2019 Klemens D. Morgenstern +// +// 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_DETAIL_POSIX_HANDLES_HPP_ +#define BOOST_PROCESS_DETAIL_POSIX_HANDLES_HPP_ + +#include <vector> +#include <system_error> +#include <dirent.h> +#include <sys/stat.h> +#include <algorithm> +#include <boost/process/detail/posix/handler.hpp> + +namespace boost { namespace process { namespace detail { namespace posix { + + +using native_handle_type = int; + +inline std::vector<native_handle_type> get_handles(std::error_code & ec) +{ + std::vector<native_handle_type> res; + + std::unique_ptr<DIR, void(*)(DIR*)> dir{::opendir("/dev/fd"), +[](DIR* p){::closedir(p);}}; + if (!dir) + { + ec = ::boost::process::detail::get_last_error(); + return {}; + } + else + ec.clear(); + + auto my_fd = ::dirfd(dir.get()); + + 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 == my_fd) + continue; + + res.push_back(conv); + } + return res; +} + +inline std::vector<native_handle_type> get_handles() +{ + std::error_code ec; + + auto res = get_handles(ec); + if (ec) + boost::process::detail::throw_error(ec, "open_dir(\"/dev/fd\") failed"); + + return res; +} + + +inline bool is_stream_handle(native_handle_type handle, std::error_code & ec) +{ + struct ::stat stat_; + + if (::fstat(handle, &stat_) != 0) + { + ec = ::boost::process::detail::get_last_error(); + } + else + ec.clear(); + + return S_ISCHR (stat_.st_mode) //This macro returns non-zero if the file is a character special file (a device like a terminal). + || S_ISBLK (stat_.st_mode) // This macro returns non-zero if the file is a block special file (a device like a disk). + || S_ISREG (stat_.st_mode) // This macro returns non-zero if the file is a regular file. + || 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. + || S_ISSOCK (stat_.st_mode) ;// This macro returns non-zero if the file is a socket. See section 16. Sockets.; +} + + +inline bool is_stream_handle(native_handle_type handle) +{ + std::error_code ec; + auto res = is_stream_handle(handle, ec); + if (ec) + boost::process::detail::throw_error(ec, "fstat() failed"); + + return res; +} + +struct limit_handles_ : handler_base_ext +{ + limit_handles_() {} + ~limit_handles_() {} + mutable std::vector<int> used_handles; + + template<typename Executor> + void on_setup(Executor & exec) const + { + used_handles = get_used_handles(exec); + } + + template<typename Executor> + void on_exec_setup(Executor & exec) const + { + auto dir = ::opendir("/dev/fd"); + if (!dir) + { + exec.set_error(::boost::process::detail::get_last_error(), "opendir(\"/dev/fd\")"); + return; + } + + auto my_fd = ::dirfd(dir); + struct ::dirent * ent_p; + + while ((ent_p = readdir(dir)) != nullptr) + { + if (ent_p->d_name[0] == '.') + continue; + + const auto conv = std::atoi(ent_p->d_name); + + if ((conv == my_fd) || (conv == -1)) + continue; + + if (std::find(used_handles.begin(), used_handles.end(), conv) != used_handles.end()) + continue; + + if (::close(conv) != 0) + { + exec.set_error(::boost::process::detail::get_last_error(), "close() failed"); + return; + } + } + ::closedir(dir); + } +}; + +}}}} + +#endif //PROCESS_HANDLES_HPP |