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 
6 #ifndef BOOST_PROCESS_DETAIL_POSIX_HANDLES_HPP_
7 #define BOOST_PROCESS_DETAIL_POSIX_HANDLES_HPP_
8 
9 #include <vector>
10 #include <system_error>
11 #include <dirent.h>
12 #include <sys/stat.h>
13 #include <algorithm>
14 #include <boost/process/detail/posix/handler.hpp>
15 
16 namespace boost { namespace process { namespace detail { namespace posix {
17 
18 
19 using native_handle_type = int;
20 
get_handles(std::error_code & ec)21 inline std::vector<native_handle_type> get_handles(std::error_code & ec)
22 {
23     std::vector<native_handle_type> res;
24 
25     std::unique_ptr<DIR, void(*)(DIR*)> dir{::opendir("/dev/fd"), +[](DIR* p){::closedir(p);}};
26     if (!dir)
27     {
28         ec = ::boost::process::detail::get_last_error();
29         return {};
30     }
31     else
32         ec.clear();
33 
34     auto my_fd = ::dirfd(dir.get());
35 
36     struct ::dirent * ent_p;
37 
38     while ((ent_p = readdir(dir.get())) != nullptr)
39     {
40         if (ent_p->d_name[0] == '.')
41             continue;
42 
43         const auto conv = std::atoi(ent_p->d_name);
44         if (conv == 0 && (ent_p->d_name[0] != '0' && ent_p->d_name[1] != '\0'))
45             continue;
46 
47         if (conv == my_fd)
48             continue;
49 
50         res.push_back(conv);
51     }
52     return res;
53 }
54 
get_handles()55 inline std::vector<native_handle_type> get_handles()
56 {
57     std::error_code ec;
58 
59     auto res = get_handles(ec);
60     if (ec)
61         boost::process::detail::throw_error(ec, "open_dir(\"/dev/fd\") failed");
62 
63     return res;
64 }
65 
66 
is_stream_handle(native_handle_type handle,std::error_code & ec)67 inline bool is_stream_handle(native_handle_type handle, std::error_code & ec)
68 {
69     struct ::stat stat_;
70 
71     if (::fstat(handle, &stat_) != 0)
72     {
73         ec = ::boost::process::detail::get_last_error();
74     }
75     else
76         ec.clear();
77 
78     return S_ISCHR  (stat_.st_mode)  //This macro returns non-zero if the file is a character special file (a device like a terminal).
79         || S_ISBLK  (stat_.st_mode) // This macro returns non-zero if the file is a block special file (a device like a disk).
80         || S_ISREG  (stat_.st_mode) // This macro returns non-zero if the file is a regular file.
81         || 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.
82         || S_ISSOCK (stat_.st_mode) ;// This macro returns non-zero if the file is a socket. See section 16. Sockets.;
83 }
84 
85 
is_stream_handle(native_handle_type handle)86 inline bool is_stream_handle(native_handle_type handle)
87 {
88     std::error_code ec;
89     auto res = is_stream_handle(handle, ec);
90     if (ec)
91         boost::process::detail::throw_error(ec, "fstat() failed");
92 
93     return res;
94 }
95 
96 struct limit_handles_ : handler_base_ext
97 {
limit_handles_boost::process::detail::posix::limit_handles_98     limit_handles_() {}
~limit_handles_boost::process::detail::posix::limit_handles_99     ~limit_handles_() {}
100     mutable std::vector<int> used_handles;
101 
102     template<typename Executor>
on_setupboost::process::detail::posix::limit_handles_103     void on_setup(Executor & exec) const
104     {
105         used_handles = get_used_handles(exec);
106     }
107 
108     template<typename Executor>
on_exec_setupboost::process::detail::posix::limit_handles_109     void on_exec_setup(Executor & exec) const
110     {
111         auto dir = ::opendir("/dev/fd");
112         if (!dir)
113         {
114             exec.set_error(::boost::process::detail::get_last_error(), "opendir(\"/dev/fd\")");
115             return;
116         }
117 
118         auto my_fd = ::dirfd(dir);
119         struct ::dirent * ent_p;
120 
121         while ((ent_p = readdir(dir)) != nullptr)
122         {
123             if (ent_p->d_name[0] == '.')
124                 continue;
125 
126             const auto conv = std::atoi(ent_p->d_name);
127 
128             if ((conv == my_fd) || (conv == -1))
129                 continue;
130 
131             if (std::find(used_handles.begin(), used_handles.end(), conv) != used_handles.end())
132                 continue;
133 
134             if (::close(conv) != 0)
135             {
136                 exec.set_error(::boost::process::detail::get_last_error(), "close() failed");
137                 return;
138             }
139         }
140         ::closedir(dir);
141     }
142 };
143 
144 }}}}
145 
146 #endif //PROCESS_HANDLES_HPP
147