1 // Copyright (c) 2006, 2007 Julio M. Merino Vidal
2 // Copyright (c) 2008 Ilya Sokolov, Boris Schaeling
3 // Copyright (c) 2009 Boris Schaeling
4 // Copyright (c) 2010 Felipe Tanus, Boris Schaeling
5 // Copyright (c) 2011, 2012 Jeff Flinn, Boris Schaeling
6 // Copyright (c) 2016 Klemens D. Morgenstern
7 //
8 // Distributed under the Boost Software License, Version 1.0. (See accompanying
9 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
10 
11 #ifndef BOOST_PROCESS_WINDOWS_EXECUTOR_HPP
12 #define BOOST_PROCESS_WINDOWS_EXECUTOR_HPP
13 
14 #include <boost/process/detail/child_decl.hpp>
15 #include <boost/process/detail/windows/is_running.hpp>
16 #include <boost/process/detail/traits.hpp>
17 #include <boost/process/error.hpp>
18 #include <boost/fusion/algorithm/iteration/for_each.hpp>
19 #include <boost/winapi/handles.hpp>
20 #include <boost/winapi/process.hpp>
21 #include <boost/none.hpp>
22 #include <system_error>
23 #include <memory>
24 #include <atomic>
25 #include <cstring>
26 
27 namespace boost { namespace process {
28 
29 namespace detail { namespace windows {
30 
31 template<typename CharType> struct startup_info;
32 #if !defined( BOOST_NO_ANSI_APIS )
33 
34 template<> struct startup_info<char>
35 {
36     typedef ::boost::winapi::STARTUPINFOA_ type;
37 };
38 #endif
39 
40 template<> struct startup_info<wchar_t>
41 {
42     typedef ::boost::winapi::STARTUPINFOW_ type;
43 };
44 
45 #if BOOST_USE_WINAPI_VERSION >= BOOST_WINAPI_VERSION_WIN6
46 
47 template<typename CharType> struct startup_info_ex;
48 
49 #if !defined( BOOST_NO_ANSI_APIS )
50 template<> struct startup_info_ex<char>
51 {
52     typedef ::boost::winapi::STARTUPINFOEXA_ type;
53 };
54 #endif
55 
56 template<> struct startup_info_ex<wchar_t>
57 {
58     typedef ::boost::winapi::STARTUPINFOEXW_ type;
59 };
60 
61 
62 #endif
63 
64 #if ( BOOST_USE_WINAPI_VERSION >= BOOST_WINAPI_VERSION_WIN6 )
65 
66 template<typename CharT>
67 struct startup_info_impl
68 {
69     ::boost::winapi::DWORD_ creation_flags = 0;
70 
71     typedef typename startup_info_ex<CharT>::type startup_info_ex_t;
72     typedef typename startup_info<CharT>::type    startup_info_t;
73 
74     startup_info_ex_t  startup_info_ex
75             {startup_info_t {sizeof(startup_info_t), nullptr, nullptr, nullptr,
76                                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, nullptr,
77                                ::boost::winapi::invalid_handle_value,
78                                ::boost::winapi::invalid_handle_value,
79                                ::boost::winapi::invalid_handle_value},
80                 nullptr
81     };
82     startup_info_t & startup_info = startup_info_ex.StartupInfo;
83 
set_startup_info_exboost::process::detail::windows::startup_info_impl84     void set_startup_info_ex()
85     {
86        startup_info.cb = sizeof(startup_info_ex_t);
87        creation_flags |= ::boost::winapi::EXTENDED_STARTUPINFO_PRESENT_;
88     }
89 };
90 
91 
92 #else
93 
94 template<typename CharT>
95 struct startup_info_impl
96 {
97     typedef typename startup_info<CharT>::type    startup_info_t;
98 
99     ::boost::winapi::DWORD_ creation_flags = 0;
100     startup_info_t          startup_info
101             {sizeof(startup_info_t), nullptr, nullptr, nullptr,
102              0, 0, 0, 0, 0, 0, 0, 0, 0, 0, nullptr,
103              ::boost::winapi::invalid_handle_value,
104              ::boost::winapi::invalid_handle_value,
105              ::boost::winapi::invalid_handle_value};
106 };
107 #endif
108 
109 
110 
111 template<typename Char, typename Sequence>
112 class executor : public startup_info_impl<Char>
113 {
114 
internal_error_handle(const std::error_code &,const char *,boost::mpl::false_,boost::mpl::true_)115     void internal_error_handle(const std::error_code &, const char*, boost::mpl::false_, boost::mpl::true_) {}
internal_error_handle(const std::error_code &,const char *,boost::mpl::true_,boost::mpl::true_)116     void internal_error_handle(const std::error_code &, const char*, boost::mpl::true_,  boost::mpl::true_) {}
117 
internal_error_handle(const std::error_code & ec,const char *,boost::mpl::true_,boost::mpl::false_)118     void internal_error_handle(const std::error_code &ec, const char*, boost::mpl::true_,  boost::mpl::false_ )
119     {
120         this->_ec = ec;
121     }
internal_error_handle(const std::error_code & ec,const char * msg,boost::mpl::false_,boost::mpl::false_)122     void internal_error_handle(const std::error_code &ec, const char* msg, boost::mpl::false_, boost::mpl::false_ )
123     {
124         throw process_error(ec, msg);
125     }
126 
127     struct on_setup_t
128     {
129         executor & exec;
on_setup_tboost::process::detail::windows::executor::on_setup_t130         on_setup_t(executor & exec) : exec(exec) {};
131         template<typename T>
operator ()boost::process::detail::windows::executor::on_setup_t132         void operator()(T & t) const
133         {
134             if (!exec.error())
135                 t.on_setup(exec);
136         }
137     };
138 
139     struct on_error_t
140     {
141         executor & exec;
142         const std::error_code & error;
on_error_tboost::process::detail::windows::executor::on_error_t143         on_error_t(executor & exec, const std::error_code & error) : exec(exec), error(error) {};
144         template<typename T>
operator ()boost::process::detail::windows::executor::on_error_t145         void operator()(T & t) const
146         {
147             t.on_error(exec, error);
148         }
149     };
150 
151     struct on_success_t
152     {
153         executor & exec;
on_success_tboost::process::detail::windows::executor::on_success_t154         on_success_t(executor & exec) : exec(exec) {};
155         template<typename T>
operator ()boost::process::detail::windows::executor::on_success_t156         void operator()(T & t) const
157         {
158             if (!exec.error())
159                 t.on_success(exec);
160         }
161     };
162 
163     typedef typename ::boost::process::detail::has_error_handler<Sequence>::type has_error_handler;
164     typedef typename ::boost::process::detail::has_ignore_error <Sequence>::type has_ignore_error;
165 
166     std::error_code _ec{0, std::system_category()};
167 
168 public:
169 
170     std::shared_ptr<std::atomic<int>> exit_status = std::make_shared<std::atomic<int>>(still_active);
171 
executor(Sequence & seq)172     executor(Sequence & seq) : seq(seq)
173     {
174     }
175 
operator ()()176     child operator()()
177     {
178         on_setup_t on_setup_fn(*this);
179         boost::fusion::for_each(seq, on_setup_fn);
180 
181         if (_ec)
182         {
183             on_error_t on_error_fn(*this, _ec);
184             boost::fusion::for_each(seq, on_error_fn);
185             return child();
186         }
187 
188         //NOTE: The non-cast cmd-line string can only be modified by the wchar_t variant which is currently disabled.
189         int err_code = ::boost::winapi::create_process(
190             exe,                                        //       LPCSTR_ lpApplicationName,
191             const_cast<Char*>(cmd_line),                //       LPSTR_ lpCommandLine,
192             proc_attrs,                                 //       LPSECURITY_ATTRIBUTES_ lpProcessAttributes,
193             thread_attrs,                               //       LPSECURITY_ATTRIBUTES_ lpThreadAttributes,
194             inherit_handles,                            //       INT_ bInheritHandles,
195             this->creation_flags,                       //       DWORD_ dwCreationFlags,
196             reinterpret_cast<void*>(const_cast<Char*>(env)),  //     LPVOID_ lpEnvironment,
197             work_dir,                                   //       LPCSTR_ lpCurrentDirectory,
198             &this->startup_info,                        //       LPSTARTUPINFOA_ lpStartupInfo,
199             &proc_info);                                //       LPPROCESS_INFORMATION_ lpProcessInformation)
200 
201         child c{child_handle(proc_info), exit_status};
202 
203         if (err_code != 0)
204         {
205             _ec.clear();
206             on_success_t on_success_fn(*this);
207             boost::fusion::for_each(seq, on_success_fn);
208         }
209         else
210             set_error(::boost::process::detail::get_last_error(),
211                     " CreateProcess failed");
212 
213         if ( _ec)
214         {
215             on_error_t on_err(*this, _ec);
216             boost::fusion::for_each(seq, on_err);
217             return child();
218         }
219         else
220             return c;
221 
222     }
223 
set_error(const std::error_code & ec,const char * msg="Unknown Error.")224     void set_error(const std::error_code & ec, const char* msg = "Unknown Error.")
225     {
226         internal_error_handle(ec, msg, has_error_handler(),         has_ignore_error());
227     }
set_error(const std::error_code & ec,const std::string msg="Unknown Error.")228     void set_error(const std::error_code & ec, const std::string msg = "Unknown Error.")
229     {
230         internal_error_handle(ec, msg.c_str(), has_error_handler(), has_ignore_error());
231     }
232 
error() const233     const std::error_code& error() const {return _ec;}
234 
235     ::boost::winapi::LPSECURITY_ATTRIBUTES_ proc_attrs   = nullptr;
236     ::boost::winapi::LPSECURITY_ATTRIBUTES_ thread_attrs = nullptr;
237     ::boost::winapi::BOOL_ inherit_handles = false;
238     const Char * work_dir = nullptr;
239     const Char * cmd_line = nullptr;
240     const Char * exe      = nullptr;
241     const Char * env      = nullptr;
242 
243 
244     Sequence & seq;
245     ::boost::winapi::PROCESS_INFORMATION_ proc_info{nullptr, nullptr, 0,0};
246 };
247 
248 
249 
250 template<typename Char, typename Tup>
make_executor(Tup & tup)251 executor<Char, Tup> make_executor(Tup & tup)
252 {
253     return executor<Char, Tup>(tup);
254 }
255 
256 
257 }}}}
258 
259 #endif
260