1 #ifndef SILICIUM_ASYNC_PROCESS_HPP
2 #define SILICIUM_ASYNC_PROCESS_HPP
9 #include <silicium/posix/pipe.hpp>
10 #include <silicium/observable/virtualized.hpp>
11 #include <silicium/observable/spawn_coroutine.hpp>
12 #include <silicium/observable/spawn_observable.hpp>
13 #include <silicium/observable/thread.hpp>
14 #include <silicium/observable/ref.hpp>
15 #include <silicium/sink/buffering_sink.hpp>
17 #include <silicium/asio/posting_observable.hpp>
18 #include <silicium/asio/process_output.hpp>
23 # include <sys/wait.h>
24 # include <sys/prctl.h>
28 #define SILICIUM_HAS_LAUNCH_PROCESS SILICIUM_HAS_EXCEPTIONS
67 #if SILICIUM_COMPILER_GENERATES_MOVES
72 : process(
std::
move(other.process))
83 child_error =
std::move(other.child_error);
101 ssize_t read_error =
read(child_error.
handle, &error,
sizeof(error));
108 assert(read_error ==
sizeof(error));
109 return boost::system::error_code(error, boost::system::system_category());
112 return process.wait_for_exit();
122 #if SILICIUM_HAS_LAUNCH_PROCESS
127 inline os_string build_command_line(std::vector<os_string>
const &arguments)
130 for (
auto a =
begin(arguments); a !=
end(arguments); ++a)
132 if (a !=
begin(arguments))
134 command_line += L
" ";
142 inline error_or<async_process> launch_process(
143 async_process_parameters parameters,
144 native_file_descriptor standard_input,
145 native_file_descriptor standard_output,
146 native_file_descriptor standard_error,
147 std::vector<std::pair<os_char const *, os_char const *>> environment,
150 std::vector<os_string> all_arguments;
151 all_arguments.emplace_back(L
"\"" + parameters.executable.underlying().wstring() + L
"\"");
152 all_arguments.insert(all_arguments.end(), parameters.arguments.begin(), parameters.arguments.end());
153 win32::winapi_string command_line = detail::build_command_line(all_arguments);
155 SECURITY_ATTRIBUTES security{};
156 security.nLength =
sizeof(security);
157 security.bInheritHandle = TRUE;
159 STARTUPINFOW startup{};
160 startup.cb =
sizeof(startup);
161 startup.dwFlags |= STARTF_USESTDHANDLES;
162 startup.hStdError = standard_error;
163 startup.hStdInput = standard_input;
164 startup.hStdOutput = standard_output;
166 DWORD flags = CREATE_NO_WINDOW;
167 std::vector<WCHAR> environment_block;
170 flags |= CREATE_UNICODE_ENVIRONMENT;
173 std::vector<os_char> mutable_parent_variables;
179 os_char const *
const parent_variables = GetEnvironmentStringsW();
180 os_char const *terminator_found = parent_variables;
181 while (*terminator_found != L
'\0')
183 terminator_found += wcslen(terminator_found) + 1;
185 mutable_parent_variables.assign(parent_variables, terminator_found + 1);
186 for (
auto i = mutable_parent_variables.begin(); *i != L
'\0';)
188 auto assign = std::find(i, mutable_parent_variables.end(), L
'=');
198 assign = std::find(assign + 1, mutable_parent_variables.end(), L
'=');
201 os_char const *
const key = &*i;
202 os_char const *
const value = (&*assign) + 1;
203 environment.emplace_back(std::make_pair(key, value));
204 i = assign + 1 + wcslen(value) + 1;
215 typedef std::pair<os_char const *, os_char const *> environment_entry;
216 std::sort(environment.begin(), environment.end(), [](environment_entry
const &left, environment_entry
const &right)
218 return (wcscmp(left.first, right.first) < 0);
220 for (environment_entry
const &entry : environment)
222 environment_block.insert(environment_block.end(), entry.first, entry.first + wcslen(entry.first));
223 environment_block.emplace_back(
'=');
224 std::size_t
const zero_terminated = 1;
225 environment_block.insert(environment_block.end(), entry.second, entry.second + wcslen(entry.second) + zero_terminated);
227 if (environment_block.empty())
231 environment_block.insert(environment_block.end(), key_and_assignment.begin(), key_and_assignment.end());
233 std::size_t
const begin_of_value = environment_block.size();
234 std::size_t estimated_value_size = 10;
237 std::size_t
const size_including_buffer = std::max(environment_block.size(), begin_of_value + estimated_value_size);
238 environment_block.resize(size_including_buffer);
239 DWORD
const buffer_size =
static_cast<DWORD
>(size_including_buffer - begin_of_value);
240 DWORD
const actual_value_size = GetEnvironmentVariableW(
242 environment_block.data() + begin_of_value,
244 if (actual_value_size <= buffer_size)
246 environment_block.resize(begin_of_value + actual_value_size + 1);
249 estimated_value_size = actual_value_size;
252 environment_block.emplace_back(L
'\0');
255 PROCESS_INFORMATION process{};
257 parameters.executable.c_str(), &command_line[0], &security,
nullptr, TRUE,
258 flags, environment_block.empty() ? NULL : environment_block.data(),
259 parameters.current_path.c_str(), &startup, &process))
264 win32::unique_handle thread_closer(process.hThread);
265 process_handle process_closer(process.hProcess);
266 return async_process(
std::move(process_closer));
269 inline error_or<async_process> launch_process(
270 async_process_parameters parameters,
271 native_file_descriptor standard_input,
272 native_file_descriptor standard_output,
273 native_file_descriptor standard_error,
274 std::vector<std::pair<os_char const *, os_char const *>> environment,
277 auto executable = parameters.executable.underlying();
278 auto arguments = parameters.arguments;
279 std::vector<char *> argument_pointers;
280 argument_pointers.emplace_back(const_cast<char *>(executable.c_str()));
285 argument_pointers.emplace_back(
nullptr);
287 pipe child_error = make_pipe().get();
289 pid_t
const forked = fork();
292 return boost::system::error_code(errno, boost::system::system_category());
300 ssize_t written =
write(child_error.write.handle, &error,
sizeof(error));
301 if (written !=
sizeof(error))
305 child_error.write.close();
311 fail_with_error(errno);
314 if (dup2(standard_output, STDOUT_FILENO) < 0)
318 if (dup2(standard_error, STDERR_FILENO) < 0)
322 if (dup2(standard_input, STDIN_FILENO) < 0)
327 child_error.read.close();
329 boost::system::error_code ec = detail::set_close_on_exec(child_error.write.handle);
332 fail_with_error(ec.value());
335 boost::filesystem::current_path(parameters.current_path.to_boost_path(), ec);
338 fail_with_error(ec.value());
342 long max_fd = sysconf(_SC_OPEN_MAX);
343 for (
int i = 3; i < max_fd; ++i)
345 if (i == child_error.write.handle)
353 if (prctl(PR_SET_PDEATHSIG, SIGHUP) < 0)
361 for (
auto const &var : environment)
363 int result = setenv(var.first, var.second, 1);
366 fail_with_error(errno);
369 execvp(parameters.executable.c_str(), argument_pointers.data());
375 std::vector<char *> environment_for_exec;
376 for (
auto const &entry : environment)
378 auto const first_length = std::strlen(entry.first);
379 auto const second_length = std::strlen(entry.second);
380 char *
const formatted =
new char[first_length + 1 + second_length + 1];
381 std::copy_n(entry.first, first_length, formatted);
382 formatted[first_length] =
'=';
383 std::copy_n(entry.second, second_length, formatted + first_length + 1);
384 formatted[first_length + 1 + second_length] =
'\0';
385 environment_for_exec.emplace_back(formatted);
387 environment_for_exec.emplace_back(
nullptr);
388 execvpe(parameters.executable.c_str(), argument_pointers.data(), environment_for_exec.data());
400 return async_process(process_handle(forked),
std::move(child_error.read));
410 template <
class ByteSink>
411 void copy_whole_pipe(HANDLE pipe_in, ByteSink &&sink_out)
413 auto buffered_out = make_buffering_sink(std::forward<ByteSink>(sink_out));
416 auto buffer = buffered_out.make_append_space((std::numeric_limits<DWORD>::max)());
417 DWORD read_bytes = 0;
420 BOOL
const peeked = PeekNamedPipe(pipe_in, buffer.begin(),
static_cast<DWORD
>(buffer.size()), &read_bytes, &available, &left);
423 auto error = ::GetLastError();
424 if (error == ERROR_BROKEN_PIPE)
426 buffered_out.make_append_space(read_bytes);
427 buffered_out.flush_append_space();
430 throw boost::system::system_error(error, boost::system::native_ecat);
434 auto buffer = buffered_out.make_append_space(1);
435 DWORD read_bytes = 0;
436 BOOL
const read_result = ReadFile(pipe_in, buffer.begin(),
static_cast<DWORD
>(buffer.size()), &read_bytes,
nullptr);
439 buffered_out.flush_append_space();
444 auto error = ::GetLastError();
445 if (error == ERROR_BROKEN_PIPE)
447 buffered_out.make_append_space(read_bytes);
448 buffered_out.flush_append_space();
451 throw boost::system::system_error(error, boost::system::native_ecat);
454 if (ReadFile(pipe_in, buffer.begin(), available, &read_bytes,
nullptr))
456 assert(available == read_bytes);
457 buffered_out.make_append_space(read_bytes);
458 buffered_out.flush_append_space();
465 buffered_out.flush();
469 namespace experimental
471 #define SILICIUM_HAS_EXPERIMENTAL_READ_FROM_ANONYMOUS_PIPE SILICIUM_HAS_THREAD_OBSERVABLE
473 #if SILICIUM_HAS_EXPERIMENTAL_READ_FROM_ANONYMOUS_PIPE
475 template <
class CharSink>
476 void read_from_anonymous_pipe(boost::asio::io_service &io, CharSink &&destination,
Si::file_handle file)
480 auto work = std::make_shared<boost::asio::io_service::work>(io);
481 Si::spawn_observable(
482 Si::asio::make_posting_observable(
484 Si::make_thread_observable<Si::std_threading>([work, copyable_file, destination]()
486 Si::win32::copy_whole_pipe(copyable_file->handle, destination);
491 #elif SILICIUM_HAS_SPAWN_COROUTINE
493 Si::spawn_coroutine([&io, destination, copyable_file](Si::spawn_context yield)
495 Si::process_output output_reader(Si::make_unique<Si::process_output::stream>(io, copyable_file->handle));
496 copyable_file->release();
499 auto piece = yield.get_one(Si::ref(output_reader));
501 if (piece->is_error())
510 Si::append(destination, data);
514 typedef typename std::decay<CharSink>::type clean_destination;
515 struct pipe_reader : std::enable_shared_from_this<pipe_reader>
517 pipe_reader(boost::asio::io_service &io,
Si::file_handle file, clean_destination destination)
518 : m_output(Si::make_unique<Si::process_output::stream>(io, file.
handle))
526 auto this_ = this->shared_from_this();
527 m_output.async_get_one(Si::make_function_observer([this_](optional<error_or<memory_range>> piece)
530 if (piece->is_error())
539 Si::append(this_->m_destination, data);
546 Si::process_output m_output;
547 clean_destination m_destination;
549 auto reader = std::make_shared<pipe_reader>(io,
std::move(file), std::forward<CharSink>(destination));
Definition: config.hpp:160
std::remove_reference< T >::type && move(T &&ref)
Definition: move.hpp:10
async_process(async_process &&other) BOOST_NOEXCEPT
Definition: async_process.hpp:71
Definition: absolute_path.hpp:21
native_file_descriptor handle
Definition: file_handle.hpp:12
~async_process() BOOST_NOEXCEPT
Definition: async_process.hpp:93
Definition: async_process.hpp:43
async_process() BOOST_NOEXCEPT
Definition: async_process.hpp:50
error_or< std::size_t > read(native_file_descriptor file, mutable_memory_range destination)
Definition: read_file.hpp:10
environment_inheritance
Definition: async_process.hpp:116
auto to_shared(T &&t) -> std::shared_ptr< typename std::decay< T >::type >
Definition: to_shared.hpp:10
std::vector< os_string > arguments
the values for the child's argv[1...]
Definition: async_process.hpp:37
Definition: absolute_path.hpp:352
error_or< int > wait_for_exit() BOOST_NOEXCEPT
Definition: async_process.hpp:97
Definition: absolute_path.hpp:19
Si::absolute_path current_path
must be an existing path, otherwise the child cannot launch properly
Definition: async_process.hpp:40
BOOST_CONSTEXPR Iterator const & end(iterator_range< Iterator > const &range)
Definition: iterator_range.hpp:136
auto make_c_str_range(C const *str) -> iterator_range< C const * >
Definition: memory_range.hpp:64
boost::container::string noexcept_string
Definition: noexcept_string.hpp:26
async_process(process_handle process, file_handle child_error) BOOST_NOEXCEPT
Definition: async_process.hpp:60
process_handle process
Definition: async_process.hpp:45
void throw_last_error()
Definition: throw_last_error.hpp:13
char os_char
Definition: os_string.hpp:19
native_file_descriptor release() BOOST_NOEXCEPT
Definition: file_handle.hpp:47
BOOST_CONSTEXPR Iterator const & begin(iterator_range< Iterator > const &range)
Definition: iterator_range.hpp:123
Definition: iterator_range.hpp:26
#define SILICIUM_UNREACHABLE()
Definition: config.hpp:44
file_handle child_error
Definition: async_process.hpp:47
async_process & operator=(async_process &&other) BOOST_NOEXCEPT
Definition: async_process.hpp:79
SILICIUM_USE_RESULT error_or< std::size_t > write(native_file_descriptor file, memory_range data)
Definition: write.hpp:13
BOOST_CONSTEXPR bool empty() const BOOST_NOEXCEPT
Definition: iterator_range.hpp:71
Si::absolute_path executable
Definition: async_process.hpp:34
#define SILICIUM_DELETED_FUNCTION(f)
Definition: config.hpp:111
#define SILICIUM_NORETURN
Definition: config.hpp:63
Definition: error_or.hpp:48
Definition: file_handle.hpp:10
SILICIUM_USE_RESULT boost::system::error_code get_last_error()
Definition: get_last_error.hpp:16
noexcept_string os_string
Definition: os_string.hpp:28
Definition: async_process.hpp:32