149 lines
3.9 KiB
C++
149 lines
3.9 KiB
C++
|
// 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 <memory>
|
||
|
#include <cstdlib>
|
||
|
#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
|