mirror of
https://github.com/boostorg/process.git
synced 2026-01-20 04:42:24 +00:00
Compare commits
24 Commits
boost-1.80
...
group-v2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
deeb975c33 | ||
|
|
dcee0936c1 | ||
|
|
4cc469b2a4 | ||
|
|
6e4d1e29d2 | ||
|
|
dada865fd0 | ||
|
|
380dd1b00f | ||
|
|
7832cb6af3 | ||
|
|
68f4c50be9 | ||
|
|
cd226a7616 | ||
|
|
4243ce72f8 | ||
|
|
9065833e61 | ||
|
|
7cb7af6c8b | ||
|
|
90cbe7cec0 | ||
|
|
df33c1ad7b | ||
|
|
bbabea30dd | ||
|
|
c1b6eb4eb8 | ||
|
|
4ef1792b0a | ||
|
|
1a6956134a | ||
|
|
eb6bce0910 | ||
|
|
9a1c6991c9 | ||
|
|
352b6cf89f | ||
|
|
317b1b7c62 | ||
|
|
a7b65bfc44 | ||
|
|
8a61f8daa3 |
375
.github/workflows/ci.yml
vendored
Normal file
375
.github/workflows/ci.yml
vendored
Normal file
@@ -0,0 +1,375 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- develop
|
||||
- feature/**
|
||||
|
||||
env:
|
||||
UBSAN_OPTIONS: print_stacktrace=1
|
||||
|
||||
jobs:
|
||||
posix:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- toolset: gcc-4.8
|
||||
cxxstd: "11"
|
||||
os: ubuntu-18.04
|
||||
install: g++-4.8
|
||||
- toolset: gcc-5
|
||||
cxxstd: "11,14,1z"
|
||||
os: ubuntu-18.04
|
||||
install: g++-5
|
||||
- toolset: gcc-6
|
||||
cxxstd: "11,14,1z"
|
||||
os: ubuntu-18.04
|
||||
install: g++-6
|
||||
- toolset: gcc-7
|
||||
cxxstd: "11,14,17"
|
||||
os: ubuntu-18.04
|
||||
- toolset: gcc-8
|
||||
cxxstd: "11,14,17,2a"
|
||||
os: ubuntu-18.04
|
||||
install: g++-8
|
||||
- toolset: gcc-9
|
||||
cxxstd: "11,14,17,2a"
|
||||
os: ubuntu-20.04
|
||||
- toolset: gcc-10
|
||||
cxxstd: "11,14,17,2a"
|
||||
os: ubuntu-20.04
|
||||
install: g++-10
|
||||
- toolset: gcc-11
|
||||
cxxstd: "11,14,17,2a"
|
||||
os: ubuntu-20.04
|
||||
install: g++-11
|
||||
- toolset: gcc-12
|
||||
cxxstd: "11,14,17,20,2b"
|
||||
os: ubuntu-22.04
|
||||
install: g++-12
|
||||
- toolset: clang
|
||||
compiler: clang++-3.9
|
||||
cxxstd: "11,14"
|
||||
os: ubuntu-18.04
|
||||
install: clang-3.9
|
||||
- toolset: clang
|
||||
compiler: clang++-4.0
|
||||
cxxstd: "11,14"
|
||||
os: ubuntu-18.04
|
||||
install: clang-4.0
|
||||
- toolset: clang
|
||||
compiler: clang++-5.0
|
||||
cxxstd: "11,14,1z"
|
||||
os: ubuntu-18.04
|
||||
install: clang-5.0
|
||||
- toolset: clang
|
||||
compiler: clang++-6.0
|
||||
cxxstd: "11,14,17"
|
||||
os: ubuntu-18.04
|
||||
install: clang-6.0
|
||||
- toolset: clang
|
||||
compiler: clang++-7
|
||||
cxxstd: "11,14,17"
|
||||
os: ubuntu-18.04
|
||||
install: clang-7
|
||||
- toolset: clang
|
||||
compiler: clang++-8
|
||||
cxxstd: "11,14,17"
|
||||
os: ubuntu-20.04
|
||||
install: clang-8
|
||||
- toolset: clang
|
||||
compiler: clang++-9
|
||||
cxxstd: "11,14,17,2a"
|
||||
os: ubuntu-20.04
|
||||
install: clang-9
|
||||
- toolset: clang
|
||||
compiler: clang++-10
|
||||
cxxstd: "11,14,17,2a"
|
||||
os: ubuntu-20.04
|
||||
install: clang-10
|
||||
- toolset: clang
|
||||
compiler: clang++-11
|
||||
cxxstd: "11,14,17,2a"
|
||||
os: ubuntu-20.04
|
||||
install: clang-11
|
||||
- toolset: clang
|
||||
compiler: clang++-12
|
||||
cxxstd: "11,14,17,2a"
|
||||
os: ubuntu-20.04
|
||||
install: clang-12
|
||||
- toolset: clang
|
||||
compiler: clang++-13
|
||||
cxxstd: "11,14,17,20,2b"
|
||||
os: ubuntu-22.04
|
||||
install: clang-13
|
||||
- toolset: clang
|
||||
compiler: clang++-14
|
||||
cxxstd: "11,14,17,20,2b"
|
||||
os: ubuntu-22.04
|
||||
install: clang-14
|
||||
- toolset: clang
|
||||
cxxstd: "11,14,17,2a"
|
||||
os: macos-11
|
||||
|
||||
runs-on: ${{matrix.os}}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Install packages
|
||||
if: matrix.install
|
||||
run: sudo apt install ${{matrix.install}}
|
||||
|
||||
- name: Setup Boost
|
||||
run: |
|
||||
echo GITHUB_REPOSITORY: $GITHUB_REPOSITORY
|
||||
LIBRARY=${GITHUB_REPOSITORY#*/}
|
||||
echo LIBRARY: $LIBRARY
|
||||
echo "LIBRARY=$LIBRARY" >> $GITHUB_ENV
|
||||
echo GITHUB_BASE_REF: $GITHUB_BASE_REF
|
||||
echo GITHUB_REF: $GITHUB_REF
|
||||
REF=${GITHUB_BASE_REF:-$GITHUB_REF}
|
||||
REF=${REF#refs/heads/}
|
||||
echo REF: $REF
|
||||
BOOST_BRANCH=develop && [ "$REF" == "master" ] && BOOST_BRANCH=master || true
|
||||
echo BOOST_BRANCH: $BOOST_BRANCH
|
||||
cd ..
|
||||
git clone -b $BOOST_BRANCH --depth 1 https://github.com/boostorg/boost.git boost-root
|
||||
cd boost-root
|
||||
cp -r $GITHUB_WORKSPACE/* libs/$LIBRARY
|
||||
git submodule update --init tools/boostdep
|
||||
python tools/boostdep/depinst/depinst.py --git_args "--jobs 3" $LIBRARY
|
||||
./bootstrap.sh
|
||||
./b2 -d0 headers
|
||||
|
||||
- name: Create user-config.jam
|
||||
if: matrix.compiler
|
||||
run: |
|
||||
echo "using ${{matrix.toolset}} : : ${{matrix.compiler}} ;" > ~/user-config.jam
|
||||
|
||||
- name: Run tests
|
||||
run: |
|
||||
cd ../boost-root
|
||||
./b2 -j3 libs/$LIBRARY/test toolset=${{matrix.toolset}} cxxstd=${{matrix.cxxstd}} variant=debug,release
|
||||
|
||||
windows:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- toolset: msvc-14.0
|
||||
cxxstd: "14,latest"
|
||||
addrmd: 32,64
|
||||
os: windows-2019
|
||||
- toolset: msvc-14.2
|
||||
cxxstd: "14,17,20,latest"
|
||||
addrmd: 32,64
|
||||
os: windows-2019
|
||||
- toolset: msvc-14.3
|
||||
cxxstd: "14,17,20,latest"
|
||||
addrmd: 32,64
|
||||
os: windows-2022
|
||||
- toolset: clang-win
|
||||
cxxstd: "14,17,latest"
|
||||
addrmd: 32,64
|
||||
os: windows-2022
|
||||
- toolset: gcc
|
||||
cxxstd: "11,14,17,2a"
|
||||
addrmd: 64
|
||||
os: windows-2019
|
||||
|
||||
runs-on: ${{matrix.os}}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Setup Boost
|
||||
shell: cmd
|
||||
run: |
|
||||
echo GITHUB_REPOSITORY: %GITHUB_REPOSITORY%
|
||||
for /f %%i in ("%GITHUB_REPOSITORY%") do set LIBRARY=%%~nxi
|
||||
echo LIBRARY: %LIBRARY%
|
||||
echo LIBRARY=%LIBRARY%>>%GITHUB_ENV%
|
||||
echo GITHUB_BASE_REF: %GITHUB_BASE_REF%
|
||||
echo GITHUB_REF: %GITHUB_REF%
|
||||
if "%GITHUB_BASE_REF%" == "" set GITHUB_BASE_REF=%GITHUB_REF%
|
||||
set BOOST_BRANCH=develop
|
||||
for /f %%i in ("%GITHUB_BASE_REF%") do if "%%~nxi" == "master" set BOOST_BRANCH=master
|
||||
echo BOOST_BRANCH: %BOOST_BRANCH%
|
||||
cd ..
|
||||
git clone -b %BOOST_BRANCH% --depth 1 https://github.com/boostorg/boost.git boost-root
|
||||
cd boost-root
|
||||
xcopy /s /e /q %GITHUB_WORKSPACE% libs\%LIBRARY%\
|
||||
git submodule update --init tools/boostdep
|
||||
python tools/boostdep/depinst/depinst.py --git_args "--jobs 3" %LIBRARY%
|
||||
cmd /c bootstrap
|
||||
b2 -d0 headers
|
||||
|
||||
- name: Run tests
|
||||
shell: cmd
|
||||
run: |
|
||||
cd ../boost-root
|
||||
b2 -j3 libs/%LIBRARY%/test toolset=${{matrix.toolset}} cxxstd=${{matrix.cxxstd}} address-model=${{matrix.addrmd}} variant=debug,release embed-manifest-via=linker
|
||||
|
||||
posix-cmake-subdir:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- os: ubuntu-18.04
|
||||
- os: ubuntu-20.04
|
||||
- os: ubuntu-22.04
|
||||
- os: macos-11
|
||||
|
||||
runs-on: ${{matrix.os}}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Install packages
|
||||
if: matrix.install
|
||||
run: sudo apt install ${{matrix.install}}
|
||||
|
||||
- name: Setup Boost
|
||||
run: |
|
||||
echo GITHUB_REPOSITORY: $GITHUB_REPOSITORY
|
||||
LIBRARY=${GITHUB_REPOSITORY#*/}
|
||||
echo LIBRARY: $LIBRARY
|
||||
echo "LIBRARY=$LIBRARY" >> $GITHUB_ENV
|
||||
echo GITHUB_BASE_REF: $GITHUB_BASE_REF
|
||||
echo GITHUB_REF: $GITHUB_REF
|
||||
REF=${GITHUB_BASE_REF:-$GITHUB_REF}
|
||||
REF=${REF#refs/heads/}
|
||||
echo REF: $REF
|
||||
BOOST_BRANCH=develop && [ "$REF" == "master" ] && BOOST_BRANCH=master || true
|
||||
echo BOOST_BRANCH: $BOOST_BRANCH
|
||||
cd ..
|
||||
git clone -b $BOOST_BRANCH --depth 1 https://github.com/boostorg/boost.git boost-root
|
||||
cd boost-root
|
||||
cp -r $GITHUB_WORKSPACE/* libs/$LIBRARY
|
||||
git submodule update --init tools/boostdep
|
||||
python tools/boostdep/depinst/depinst.py --git_args "--jobs 3" $LIBRARY
|
||||
|
||||
- name: Use library with add_subdirectory
|
||||
run: |
|
||||
cd ../boost-root/libs/$LIBRARY/test/cmake_subdir_test
|
||||
mkdir __build__ && cd __build__
|
||||
cmake ..
|
||||
cmake --build .
|
||||
ctest --output-on-failure --no-tests=error
|
||||
|
||||
posix-cmake-install:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- os: ubuntu-18.04
|
||||
- os: ubuntu-20.04
|
||||
- os: ubuntu-22.04
|
||||
- os: macos-11
|
||||
|
||||
runs-on: ${{matrix.os}}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Install packages
|
||||
if: matrix.install
|
||||
run: sudo apt install ${{matrix.install}}
|
||||
|
||||
- name: Setup Boost
|
||||
run: |
|
||||
echo GITHUB_REPOSITORY: $GITHUB_REPOSITORY
|
||||
LIBRARY=${GITHUB_REPOSITORY#*/}
|
||||
echo LIBRARY: $LIBRARY
|
||||
echo "LIBRARY=$LIBRARY" >> $GITHUB_ENV
|
||||
echo GITHUB_BASE_REF: $GITHUB_BASE_REF
|
||||
echo GITHUB_REF: $GITHUB_REF
|
||||
REF=${GITHUB_BASE_REF:-$GITHUB_REF}
|
||||
REF=${REF#refs/heads/}
|
||||
echo REF: $REF
|
||||
BOOST_BRANCH=develop && [ "$REF" == "master" ] && BOOST_BRANCH=master || true
|
||||
echo BOOST_BRANCH: $BOOST_BRANCH
|
||||
cd ..
|
||||
git clone -b $BOOST_BRANCH --depth 1 https://github.com/boostorg/boost.git boost-root
|
||||
cd boost-root
|
||||
cp -r $GITHUB_WORKSPACE/* libs/$LIBRARY
|
||||
git submodule update --init tools/boostdep
|
||||
python tools/boostdep/depinst/depinst.py --git_args "--jobs 3" $LIBRARY
|
||||
|
||||
- name: Configure
|
||||
run: |
|
||||
cd ../boost-root
|
||||
mkdir __build__ && cd __build__
|
||||
cmake -DBOOST_INCLUDE_LIBRARIES=$LIBRARY -DCMAKE_INSTALL_PREFIX=~/.local ..
|
||||
|
||||
- name: Install
|
||||
run: |
|
||||
cd ../boost-root/__build__
|
||||
cmake --build . --target install
|
||||
|
||||
- name: Use the installed library
|
||||
run: |
|
||||
cd ../boost-root/libs/$LIBRARY/test/cmake_install_test && mkdir __build__ && cd __build__
|
||||
cmake -DCMAKE_INSTALL_PREFIX=~/.local ..
|
||||
cmake --build .
|
||||
ctest --output-on-failure --no-tests=error
|
||||
|
||||
posix-cmake-test:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- os: ubuntu-18.04
|
||||
- os: ubuntu-20.04
|
||||
- os: ubuntu-22.04
|
||||
- os: macos-11
|
||||
|
||||
runs-on: ${{matrix.os}}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Install packages
|
||||
if: matrix.install
|
||||
run: sudo apt install ${{matrix.install}}
|
||||
|
||||
- name: Setup Boost
|
||||
run: |
|
||||
echo GITHUB_REPOSITORY: $GITHUB_REPOSITORY
|
||||
LIBRARY=${GITHUB_REPOSITORY#*/}
|
||||
echo LIBRARY: $LIBRARY
|
||||
echo "LIBRARY=$LIBRARY" >> $GITHUB_ENV
|
||||
echo GITHUB_BASE_REF: $GITHUB_BASE_REF
|
||||
echo GITHUB_REF: $GITHUB_REF
|
||||
REF=${GITHUB_BASE_REF:-$GITHUB_REF}
|
||||
REF=${REF#refs/heads/}
|
||||
echo REF: $REF
|
||||
BOOST_BRANCH=develop && [ "$REF" == "master" ] && BOOST_BRANCH=master || true
|
||||
echo BOOST_BRANCH: $BOOST_BRANCH
|
||||
cd ..
|
||||
git clone -b $BOOST_BRANCH --depth 1 https://github.com/boostorg/boost.git boost-root
|
||||
cd boost-root
|
||||
cp -r $GITHUB_WORKSPACE/* libs/$LIBRARY
|
||||
git submodule update --init tools/boostdep
|
||||
python tools/boostdep/depinst/depinst.py --git_args "--jobs 3" $LIBRARY
|
||||
|
||||
- name: Configure
|
||||
run: |
|
||||
cd ../boost-root
|
||||
mkdir __build__ && cd __build__
|
||||
cmake -DBOOST_INCLUDE_LIBRARIES=$LIBRARY -DBUILD_TESTING=ON ..
|
||||
|
||||
- name: Build tests
|
||||
run: |
|
||||
cd ../boost-root/__build__
|
||||
cmake --build . --target tests
|
||||
|
||||
- name: Run tests
|
||||
run: |
|
||||
cd ../boost-root/__build__
|
||||
ctest --output-on-failure --no-tests=error
|
||||
5
.gitignore
vendored
5
.gitignore
vendored
@@ -32,3 +32,8 @@
|
||||
/notes_p.txt
|
||||
.settings
|
||||
|
||||
*.make
|
||||
*.cmake
|
||||
*.rsp
|
||||
*.marks
|
||||
cmake-*
|
||||
@@ -26,7 +26,7 @@ namespace boost { namespace process {
|
||||
#if defined(BOOST_PROCESS_DOXYGEN)
|
||||
|
||||
|
||||
/** Class implementing and asnychronous I/O-Object for use with boost.asio.
|
||||
/** Class implementing an asnychronous I/O-Object for use with boost.asio.
|
||||
* It is based on the corresponding I/O Object, that is either boost::asio::windows::stream_handle or
|
||||
* boost::asio::posix::stream_descriptor.
|
||||
*
|
||||
|
||||
@@ -139,7 +139,7 @@ struct exe_cmd_init<char> : boost::process::detail::api::handler_base_ext
|
||||
}
|
||||
static exe_cmd_init cmd_shell(std::string&& cmd)
|
||||
{
|
||||
std::vector<std::string> args = {"-c", "\"" + cmd + "\""};
|
||||
std::vector<std::string> args = {"-c", cmd};
|
||||
std::string sh = shell().string();
|
||||
|
||||
return exe_cmd_init(
|
||||
|
||||
@@ -155,8 +155,8 @@ class executor
|
||||
void write_error(const std::error_code & ec, const char * msg)
|
||||
{
|
||||
//I am the child
|
||||
const auto len = std::strlen(msg);
|
||||
int data[2] = {ec.value(), static_cast<int>(len + 1)};
|
||||
const auto len = static_cast<int>(std::strlen(msg));
|
||||
int data[2] = {ec.value(), len + 1};
|
||||
|
||||
boost::ignore_unused(::write(_pipe_sink, &data[0], sizeof(int) * 2));
|
||||
boost::ignore_unused(::write(_pipe_sink, msg, len));
|
||||
@@ -444,6 +444,8 @@ child executor<Sequence>::invoke(boost::mpl::false_, boost::mpl::false_)
|
||||
}
|
||||
if (_ec)
|
||||
{
|
||||
//if an error occured we need to reap the child process
|
||||
::waitpid(this->pid, nullptr, WNOHANG);
|
||||
boost::fusion::for_each(seq, call_on_error(*this, _ec));
|
||||
return child();
|
||||
}
|
||||
@@ -537,6 +539,7 @@ child executor<Sequence>::invoke(boost::mpl::false_, boost::mpl::true_)
|
||||
|
||||
if (_ec)
|
||||
{
|
||||
::waitpid(this->pid, nullptr, WNOHANG);
|
||||
boost::fusion::for_each(seq, call_on_error(*this, _ec));
|
||||
return child();
|
||||
}
|
||||
|
||||
@@ -159,8 +159,13 @@ struct exe_cmd_init : handler_base_ext
|
||||
return exe_cmd_init<Char>(std::move(sh), std::move(args_));
|
||||
}
|
||||
|
||||
#ifdef BOOST_PROCESS_USE_STD_FS
|
||||
static std:: string get_shell(char) {return shell(). string(); }
|
||||
static std::wstring get_shell(wchar_t) {return shell().wstring(); }
|
||||
#else
|
||||
static std:: string get_shell(char) {return shell(). string(codecvt()); }
|
||||
static std::wstring get_shell(wchar_t) {return shell().wstring(codecvt());}
|
||||
#endif
|
||||
|
||||
static exe_cmd_init<Char> cmd_shell(string_type&& cmd)
|
||||
{
|
||||
|
||||
@@ -263,7 +263,7 @@ public:
|
||||
auto st1 = key + ::boost::process::detail::equal_sign<Char>();
|
||||
while (*p != nullptr)
|
||||
{
|
||||
const int len = std::char_traits<Char>::length(*p);
|
||||
const auto len = std::char_traits<Char>::length(*p);
|
||||
if ((std::distance(st1.begin(), st1.end()) < len)
|
||||
&& std::equal(st1.begin(), st1.end(), *p))
|
||||
break;
|
||||
|
||||
417
include/boost/process/v2/detail/group_impl_windows.hpp
Normal file
417
include/boost/process/v2/detail/group_impl_windows.hpp
Normal file
@@ -0,0 +1,417 @@
|
||||
// Copyright (c) 2022 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_V2_GROUP_IMPL_HPP
|
||||
#define BOOST_PROCESS_V2_GROUP_IMPL_HPP
|
||||
|
||||
#include <boost/process/v2/detail/config.hpp>
|
||||
|
||||
#if defined(BOOST_PROCESS_V2_STANDALONE)
|
||||
#include <asio/any_io_executor.hpp>
|
||||
#include <asio/append.hpp>
|
||||
#include <asio/compose.hpp>
|
||||
#include <asio/dispatch.hpp>
|
||||
#include <asio/post.hpp>
|
||||
#include <asio/windows/basic_object_handle.hpp>
|
||||
#else
|
||||
#include <boost/asio/any_io_executor.hpp>
|
||||
#include <boost/asio/append.hpp>
|
||||
#include <boost/asio/compose.hpp>
|
||||
#include <boost/asio/dispatch.hpp>
|
||||
#include <boost/asio/post.hpp>
|
||||
#include <boost/asio/windows/basic_object_handle.hpp>
|
||||
#endif
|
||||
|
||||
BOOST_PROCESS_V2_BEGIN_NAMESPACE
|
||||
|
||||
struct single_process_exit;
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
BOOST_PROCESS_V2_DECL bool job_object_is_empty(HANDLE job_object, error_code & ec);
|
||||
|
||||
template<typename Allocator>
|
||||
BOOST_PROCESS_V2_DECL std::pair<DWORD, DWORD> job_object_something_exited(HANDLE job_object, std::vector<int> & store, Allocator alloc_, error_code & ec)
|
||||
{
|
||||
// let's start with a
|
||||
typename std::aligned_storage<sizeof(JOBOBJECT_BASIC_PROCESS_ID_LIST) * 16, alignof(JOBOBJECT_BASIC_PROCESS_ID_LIST)>::type storage;
|
||||
JOBOBJECT_BASIC_PROCESS_ID_LIST * id_list = static_cast<JOBOBJECT_BASIC_PROCESS_ID_LIST*>(storage);
|
||||
|
||||
if (!QueryInformationJobObject(job_object,
|
||||
JobObjectBasicProcessIdList,
|
||||
id_list, sizeof(storage), nullptr))
|
||||
ec = detail::get_last_error();
|
||||
|
||||
if (ec)
|
||||
return {};
|
||||
|
||||
using allocator_type = std::allocator_traits<Allocator>::rebind_alloc<JOBOBJECT_BASIC_PROCESS_ID_LIST>;
|
||||
allocator_type alloc{alloc_};
|
||||
|
||||
std::size_t sz = (std::numeric_limits<std::size_t>::max)();
|
||||
// dit not fit in the buffer, alloc a buffer
|
||||
if (id_list.NumberOfAssignedProcesses != id_list.NumberOfProcessIdsInList)
|
||||
{
|
||||
// required size:
|
||||
const additional_size = id_list.NumberOfAssignedProcesses - 1;
|
||||
sz = (additional_size / (sizeof(JOBOBJECT_BASIC_PROCESS_ID_LIST) / sizeof(ULONG_PTR))) + 1;
|
||||
id_list = std::allocator_traits<allocator_type>::allocate(alloc_, sz);
|
||||
|
||||
if (!QueryInformationJobObject(job_object,
|
||||
JobObjectBasicProcessIdList,
|
||||
id_list, sz, nullptr))
|
||||
{
|
||||
ec = detail::get_last_error();
|
||||
goto complete;
|
||||
}
|
||||
|
||||
}
|
||||
std::pair<DWORD, DWORD> result;
|
||||
|
||||
auto * begin = id_list->ProcessIdList,
|
||||
* end = id_list->ProcessIdList + id_list->NumberOfProcessIdsInList;
|
||||
|
||||
for (auto itr = store.begin(); itr != store.end(); itr++)
|
||||
{
|
||||
// cross check if it's in the job object
|
||||
auto it = std::find(begin, end, *itr);
|
||||
if (it == end) // missing a job
|
||||
{
|
||||
result.first = *itr;
|
||||
// ::GetExitCodeProcess(); // this can't be done based on PID, i need the f'in handle.
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
complete:
|
||||
if (sz != (std::numeric_limits<std::size_t>::max)())
|
||||
std::allocator_traits<allocator_type>::deallocate(alloc_, id_list, sz);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#if !defined(BOOST_PROCESS_V2_HEADER_ONLY)
|
||||
//extern template
|
||||
#endif
|
||||
|
||||
struct basic_group_impl_base
|
||||
{
|
||||
using native_handle_type = HANDLE;
|
||||
|
||||
struct job_object_deleter
|
||||
{
|
||||
bool terminate_on_delete = true;
|
||||
void operator()(HANDLE h)
|
||||
{
|
||||
if (h != nullptr && terminate_on_delete)
|
||||
::TerminateJobObject(h, 255u);
|
||||
if (h != nullptr && h != INVALID_HANDLE_VALUE)
|
||||
::CloseHandle(h);
|
||||
}
|
||||
};
|
||||
BOOST_PROCESS_V2_DECL void add(DWORD pid, HANDLE handle, error_code & ec);
|
||||
BOOST_PROCESS_V2_DECL bool contains(DWORD pid, error_code & ec);
|
||||
BOOST_PROCESS_V2_DECL void wait_one(DWORD &pid, int &exit_code, error_code & ec);
|
||||
BOOST_PROCESS_V2_DECL void wait_all(error_code & ec);
|
||||
BOOST_PROCESS_V2_DECL void interrupt(error_code & ec);
|
||||
BOOST_PROCESS_V2_DECL void request_exit(error_code & ec);
|
||||
BOOST_PROCESS_V2_DECL void terminate(error_code & ec);
|
||||
bool is_open() const
|
||||
{
|
||||
return job_object_.get() != nullptr;
|
||||
}
|
||||
|
||||
struct initializer_t
|
||||
{
|
||||
basic_group_impl_base * self;
|
||||
error_code & success_ec;
|
||||
|
||||
error_code on_setup(windows::default_launcher & launcher,
|
||||
const filesystem::path &,
|
||||
const std::wstring &) const
|
||||
{
|
||||
launcher.creation_flags |= CREATE_SUSPENDED;
|
||||
return error_code {};
|
||||
};
|
||||
|
||||
|
||||
void on_success(windows::default_launcher & launcher,
|
||||
const filesystem::path &,
|
||||
const std::wstring &) const
|
||||
{
|
||||
if (!::AssignProcessToJobObject(self->job_object_.get(), launcher.process_information.hProcess))
|
||||
success_ec = detail::get_last_error();
|
||||
|
||||
if (!success_ec &&
|
||||
::ResumeThread(launcher.process_information.hThread) == static_cast<DWORD>(-1))
|
||||
success_ec = detail::get_last_error();
|
||||
};
|
||||
};
|
||||
initializer_t get_initializer(error_code & ec)
|
||||
{
|
||||
return initializer_t{this, ec};
|
||||
}
|
||||
basic_group_impl_base(BOOST_PROCESS_V2_ASIO_NAMESPACE::execution_context & ctx)
|
||||
{
|
||||
}
|
||||
~basic_group_impl_base()
|
||||
{
|
||||
}
|
||||
void detach() {job_object_.get_deleter().terminate_on_delete = false;}
|
||||
protected:
|
||||
std::unique_ptr<void, job_object_deleter> job_object_{CreateJobObject(nullptr, nullptr)};
|
||||
};
|
||||
|
||||
template<typename Executor = BOOST_PROCESS_V2_ASIO_NAMESPACE::any_io_executor>
|
||||
struct basic_group_impl : basic_group_impl_base
|
||||
{
|
||||
|
||||
// Get the native group handle
|
||||
native_handle_type native_handle() {return port_.native_handle();}
|
||||
|
||||
basic_group_impl(Executor exec)
|
||||
: basic_group_impl_base(BOOST_PROCESS_V2_ASIO_NAMESPACE::query(exec,
|
||||
BOOST_PROCESS_V2_ASIO_NAMESPACE::execution::context)),
|
||||
port_(exec, ::CreateIoCompletionPort(INVALID_HANDLE_VALUE, nullptr, 0, 1))
|
||||
{
|
||||
error_code ec;
|
||||
JOBOBJECT_ASSOCIATE_COMPLETION_PORT port{job_object_.get(), port_.native_handle()};
|
||||
|
||||
if (!::SetInformationJobObject(
|
||||
job_object_.get(),
|
||||
JobObjectAssociateCompletionPortInformation,
|
||||
&port, sizeof(port)))
|
||||
ec = v2::detail::get_last_error();
|
||||
}
|
||||
|
||||
|
||||
template<BOOST_PROCESS_V2_COMPLETION_TOKEN_FOR(void(error_code, single_process_exit))
|
||||
WaitHandler BOOST_PROCESS_V2_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type)>
|
||||
BOOST_PROCESS_V2_INITFN_AUTO_RESULT_TYPE(WaitHandler, void (error_code, single_process_exit))
|
||||
async_wait_one(
|
||||
WaitHandler &&handler BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type))
|
||||
{
|
||||
printf("FOOBAR %d %p\n", port_.native_handle(), this);
|
||||
|
||||
return BOOST_PROCESS_V2_ASIO_NAMESPACE::async_compose<
|
||||
WaitHandler,
|
||||
void (error_code, single_process_exit)>(
|
||||
wait_one_op{this},
|
||||
handler, port_);
|
||||
}
|
||||
|
||||
template<BOOST_PROCESS_V2_COMPLETION_TOKEN_FOR(void(error_code))
|
||||
WaitHandler BOOST_PROCESS_V2_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type)>
|
||||
BOOST_PROCESS_V2_INITFN_AUTO_RESULT_TYPE(WaitHandler, void (error_code))
|
||||
async_wait_all(
|
||||
WaitHandler &&handler BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type))
|
||||
{
|
||||
return BOOST_PROCESS_V2_ASIO_NAMESPACE::async_compose<
|
||||
WaitHandler,
|
||||
void (error_code)>(
|
||||
wait_all_op{this},
|
||||
handler, port_);
|
||||
}
|
||||
|
||||
using executor_type = Executor;
|
||||
executor_type get_executor() {return port_.get_executor();}
|
||||
|
||||
void wait_all(error_code &ec)
|
||||
{
|
||||
if (job_object_is_empty(job_object_.get(), ec))
|
||||
return ;
|
||||
|
||||
DWORD completion_code;
|
||||
ULONG_PTR completion_key;
|
||||
LPOVERLAPPED overlapped;
|
||||
|
||||
int res;
|
||||
while (!!(res = GetQueuedCompletionStatus(
|
||||
port_.native_handle(),
|
||||
&completion_code,
|
||||
&completion_key,
|
||||
&overlapped,
|
||||
INFINITE)))
|
||||
{
|
||||
if (completion_code == JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO)
|
||||
return;
|
||||
}
|
||||
|
||||
if (!res)
|
||||
ec = detail::get_last_error();
|
||||
}
|
||||
|
||||
void wait_one(DWORD & pid, int & exit_code, error_code &ec)
|
||||
{
|
||||
if (job_object_is_empty(job_object_.get(), ec))
|
||||
{
|
||||
ec = BOOST_PROCESS_V2_ASIO_NAMESPACE::error::broken_pipe;
|
||||
return ;
|
||||
}
|
||||
DWORD completion_code;
|
||||
ULONG_PTR completion_key;
|
||||
LPOVERLAPPED overlapped;
|
||||
|
||||
int res;
|
||||
while (!!(res = GetQueuedCompletionStatus(
|
||||
port_.native_handle(),
|
||||
&completion_code,
|
||||
&completion_key,
|
||||
&overlapped,
|
||||
INFINITE)))
|
||||
{
|
||||
if (completion_code == JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO)
|
||||
{
|
||||
ec = BOOST_PROCESS_V2_ASIO_NAMESPACE::error::broken_pipe;
|
||||
return;
|
||||
}
|
||||
else if (completion_code == JOB_OBJECT_MSG_EXIT_PROCESS
|
||||
|| completion_code == JOB_OBJECT_MSG_ABNORMAL_EXIT_PROCESS)
|
||||
{
|
||||
pid = *reinterpret_cast<DWORD*>(&overlapped);
|
||||
const auto p = ::OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid);
|
||||
|
||||
if (p == INVALID_HANDLE_VALUE || p == nullptr)
|
||||
{
|
||||
ec = detail::get_last_error();
|
||||
break;
|
||||
}
|
||||
|
||||
DWORD code;
|
||||
res = ::GetExitCodeProcess(p, &code);
|
||||
exit_code = static_cast<int>(code);
|
||||
break;
|
||||
}
|
||||
else if (job_object_is_empty(job_object_.get(), ec))
|
||||
break;
|
||||
}
|
||||
|
||||
if (!res)
|
||||
ec = detail::get_last_error();
|
||||
}
|
||||
|
||||
private:
|
||||
BOOST_PROCESS_V2_ASIO_NAMESPACE::windows::basic_object_handle<Executor> port_;
|
||||
|
||||
struct wait_one_op
|
||||
{
|
||||
basic_group_impl * this_;
|
||||
|
||||
template<typename Self>
|
||||
void operator()(Self && self)
|
||||
{
|
||||
printf("FOOBAR %d %p\n", __LINE__, this_);
|
||||
BOOST_PROCESS_V2_ASIO_NAMESPACE::dispatch(this_->port_.get_executor(),
|
||||
BOOST_PROCESS_V2_ASIO_NAMESPACE::append(std::move(self), error_code{}));
|
||||
}
|
||||
|
||||
template<typename Self>
|
||||
void operator()(Self && self, error_code ec)
|
||||
{
|
||||
using namespace BOOST_PROCESS_V2_ASIO_NAMESPACE;
|
||||
single_process_exit res;
|
||||
|
||||
/*// we need to install our handler first, THEN check, THEN
|
||||
using allocator_type = associated_allocator_t<typename std::decay<Handler::type>;
|
||||
allocator_type allocator = get_associated_allocator(handler);
|
||||
*/
|
||||
printf("FOOBAR %d %p\n", __LINE__, this_);
|
||||
if (ec)
|
||||
return self.complete(ec, res);
|
||||
auto exec = self.get_executor();
|
||||
printf("FOOBAR %d %p\n", __LINE__, this_);
|
||||
if (job_object_is_empty(this_->job_object_.get(), ec))
|
||||
return self.complete(BOOST_PROCESS_V2_ASIO_NAMESPACE::error::broken_pipe, res);
|
||||
printf("FOOBAR %d %p\n", __LINE__, this_);
|
||||
|
||||
//check if done
|
||||
if (this->poll_one(ec, res))
|
||||
return self.complete(ec, res);
|
||||
else
|
||||
this_->port_.async_wait(std::move(self));
|
||||
printf("FOOBAR %d %p\n", __LINE__, this_);
|
||||
}
|
||||
|
||||
bool poll_one(error_code & ec, single_process_exit & se)
|
||||
{
|
||||
DWORD completion_code;
|
||||
ULONG_PTR completion_key;
|
||||
LPOVERLAPPED overlapped;
|
||||
auto res = GetQueuedCompletionStatus(
|
||||
this_->port_.native_handle(),
|
||||
&completion_code,
|
||||
&completion_key,
|
||||
&overlapped,
|
||||
0u);
|
||||
|
||||
if (!res && ::GetLastError() == ERROR_ABANDONED_WAIT_0)
|
||||
return false;
|
||||
|
||||
if (completion_code == JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO)
|
||||
{
|
||||
ec = BOOST_PROCESS_V2_ASIO_NAMESPACE::error::broken_pipe;
|
||||
return true;
|
||||
}
|
||||
else if (completion_code == JOB_OBJECT_MSG_EXIT_PROCESS
|
||||
|| completion_code == JOB_OBJECT_MSG_ABNORMAL_EXIT_PROCESS)
|
||||
{
|
||||
se.pid = *reinterpret_cast<DWORD*>(&overlapped);
|
||||
const auto p = ::OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, se.pid);
|
||||
|
||||
if (p == INVALID_HANDLE_VALUE || p == nullptr)
|
||||
{
|
||||
ec = detail::get_last_error();
|
||||
return true;
|
||||
}
|
||||
|
||||
DWORD code;
|
||||
res = ::GetExitCodeProcess(p, &code);
|
||||
se.exit_code = static_cast<int>(code);
|
||||
return true;
|
||||
}
|
||||
else if (job_object_is_empty(this_->job_object_.get(), ec))
|
||||
{
|
||||
ec = BOOST_PROCESS_V2_ASIO_NAMESPACE::error::broken_pipe;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
struct wait_all_op
|
||||
{
|
||||
basic_group_impl * this_;
|
||||
template<typename Self>
|
||||
void operator()(Self && self)
|
||||
{
|
||||
/*
|
||||
using namespace BOOST_PROCESS_V2_ASIO_NAMESPACE;
|
||||
using allocator_type = associated_allocator_t<typename std::decay<Handler::type>;
|
||||
allocator_type allocator = get_associated_allocator(handler);
|
||||
*/
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
};
|
||||
|
||||
#if !defined(BOOST_PROCESS_V2_HEADER_ONLY)
|
||||
extern template struct basic_group_impl<BOOST_PROCESS_V2_ASIO_NAMESPACE::any_io_executor>;
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
BOOST_PROCESS_V2_END_NAMESPACE
|
||||
|
||||
#if defined(BOOST_PROCESS_V2_HEADER_ONLY)
|
||||
|
||||
#include <boost/process/v2/detail/impl/group_impl_windows.ipp>
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
#endif //BOOST_PROCESS_V2_GROUP_IMPL_HPP
|
||||
79
include/boost/process/v2/detail/impl/group_impl_windows.ipp
Normal file
79
include/boost/process/v2/detail/impl/group_impl_windows.ipp
Normal file
@@ -0,0 +1,79 @@
|
||||
//
|
||||
// boost/process/v2/windows/impl/job_object_service.ipp
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2022 Klemens D. Morgenstern (klemens dot morgenstern at gmx dot net)
|
||||
//
|
||||
// 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_V2_DETAIL_IMPL_GROUP_IMPL_WINDOWS_IPP
|
||||
#define BOOST_PROCESS_V2_DETAIL_IMPL_GROUP_IMPL_WINDOWS_IPP
|
||||
|
||||
#include <boost/process/v2/detail/config.hpp>
|
||||
#include <boost/process/v2/detail/last_error.hpp>
|
||||
#include <boost/process/v2/detail/group_impl_windows.hpp>
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
BOOST_PROCESS_V2_BEGIN_NAMESPACE
|
||||
namespace detail
|
||||
{
|
||||
|
||||
bool job_object_is_empty(HANDLE job_object, error_code & ec)
|
||||
{
|
||||
JOBOBJECT_BASIC_ACCOUNTING_INFORMATION info;
|
||||
if (!QueryInformationJobObject(job_object,
|
||||
JobObjectBasicAccountingInformation,
|
||||
&info, sizeof(info), nullptr))
|
||||
ec = detail::get_last_error();
|
||||
return info.ActiveProcesses == 0u;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#if !defined(BOOST_PROCESS_V2_HEADER_ONLY)
|
||||
template struct basic_group_impl<BOOST_PROCESS_V2_ASIO_NAMESPACE::any_io_executor>;
|
||||
#endif
|
||||
|
||||
|
||||
void basic_group_impl_base::add(DWORD pid, HANDLE handle, error_code & ec)
|
||||
{
|
||||
if (!AssignProcessToJobObject(job_object_.get(), handle))
|
||||
ec = detail::get_last_error();
|
||||
}
|
||||
|
||||
void basic_group_impl_base::terminate(error_code & ec)
|
||||
{
|
||||
if (!::TerminateJobObject(job_object_.get(), 255u))
|
||||
ec = detail::get_last_error();
|
||||
}
|
||||
|
||||
|
||||
bool basic_group_impl_base::contains(DWORD pid, error_code & ec)
|
||||
{
|
||||
BOOL res = FALSE;
|
||||
//
|
||||
struct del
|
||||
{
|
||||
void operator()(HANDLE h)
|
||||
{
|
||||
if (h != nullptr && h != INVALID_HANDLE_VALUE)
|
||||
::CloseHandle(h);
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<void, del> proc{::OpenProcess(PROCESS_QUERY_INFORMATION , FALSE, pid)};
|
||||
|
||||
if (proc.get() != INVALID_HANDLE_VALUE &&
|
||||
!IsProcessInJob(proc.get(), job_object_.get(), &res))
|
||||
ec = detail::get_last_error();
|
||||
|
||||
return res == TRUE;
|
||||
}
|
||||
|
||||
}
|
||||
BOOST_PROCESS_V2_END_NAMESPACE
|
||||
|
||||
|
||||
#endif //BOOST_PROCESS_V2_DETAIL_IMPL_GROUP_IMPL_WINDOWS_IPP
|
||||
@@ -9,7 +9,7 @@
|
||||
#include <boost/process/v2/detail/last_error.hpp>
|
||||
|
||||
#if defined(BOOST_PROCESS_V2_WINDOWS)
|
||||
#include <windows.h>
|
||||
#include <Windows.h>
|
||||
#else
|
||||
#include <cerrno>
|
||||
#endif
|
||||
|
||||
@@ -77,14 +77,14 @@ static BOOL CALLBACK enum_window(HWND hwnd, LPARAM param)
|
||||
auto data = reinterpret_cast<enum_windows_data_t*>(param);
|
||||
DWORD pid{0u};
|
||||
GetWindowThreadProcessId(hwnd, &pid);
|
||||
|
||||
if (pid != data->pid)
|
||||
return TRUE;
|
||||
|
||||
|
||||
LRESULT res = ::SendMessageW(hwnd, WM_CLOSE, 0, 0);
|
||||
if (!res)
|
||||
|
||||
if (res)
|
||||
data->ec = detail::get_last_error();
|
||||
return res != 0;
|
||||
return res == 0;
|
||||
}
|
||||
|
||||
void request_exit_(pid_type pid_, error_code & ec)
|
||||
|
||||
@@ -87,17 +87,16 @@ struct basic_process_handle_win
|
||||
{
|
||||
}
|
||||
|
||||
basic_process_handle_win(basic_process_handle_win && handle)
|
||||
basic_process_handle_win(basic_process_handle_win && handle)
|
||||
: pid_(handle.id()), handle_(std::move(handle.handle_))
|
||||
{
|
||||
pid_ = handle.id();
|
||||
handle_ = std::move(handle.handle_);
|
||||
handle.pid_ = static_cast<DWORD>(-1);
|
||||
}
|
||||
|
||||
basic_process_handle_win& operator=(basic_process_handle_win && handle)
|
||||
{
|
||||
pid_ = handle.pid_;
|
||||
handle_ = std::mopve(handle_))
|
||||
handle_ = std::move(handle.handle_);
|
||||
handle.pid_ = static_cast<DWORD>(-1);
|
||||
return *this;
|
||||
}
|
||||
@@ -166,7 +165,6 @@ struct basic_process_handle_win
|
||||
{
|
||||
if (!detail::check_pid_(pid_, ec))
|
||||
return;
|
||||
|
||||
detail::request_exit_(pid_, ec);
|
||||
}
|
||||
|
||||
|
||||
@@ -979,7 +979,8 @@ struct key_value_pair
|
||||
const std::pair<Key, Value> & kv/*,
|
||||
typename std::enable_if<std::is_constructible<struct key, Key >::value &&
|
||||
std::is_constructible<struct value, Value>::value
|
||||
>::type = 0*/) : value_(((struct key)(kv.first)).string() + equality_sign + ((struct value)(kv.second)).string())
|
||||
>::type = 0*/) : value_(((struct key)(kv.first)).basic_string<char_type, traits_type>() + equality_sign
|
||||
+ ((struct value)(kv.second)).basic_string<char_type, traits_type>())
|
||||
{}
|
||||
|
||||
key_value_pair(const typename conditional<is_same<value_type, char>::value, wchar_t, char>::type * raw)
|
||||
@@ -1045,6 +1046,7 @@ struct key_value_pair
|
||||
|
||||
operator string_type() const {return native();}
|
||||
operator string_view_type() const {return native_view();}
|
||||
operator typename string_view_type::string_view_type() const {return native_view();}
|
||||
operator key_value_pair_view() const {return native_view();}
|
||||
|
||||
int compare( const key_value_pair& p ) const noexcept
|
||||
@@ -1432,8 +1434,9 @@ auto find_key(Environment & env, key_view ky)
|
||||
template<typename Environment = current_view>
|
||||
inline filesystem::path home(Environment && env = current())
|
||||
{
|
||||
#if defined(ASIO_WINDOWS)
|
||||
return detail::find_key(env, L"HOMEDRIVE") + detail::find_key(env, L"HOMEPATH").native_string();
|
||||
#if defined(BOOST_PROCESS_V2_WINDOWS)
|
||||
return detail::find_key(env, L"HOMEDRIVE").native_string()
|
||||
+ detail::find_key(env, L"HOMEPATH").native_string();
|
||||
#else
|
||||
return detail::find_key(env, "HOME").native_string();
|
||||
#endif
|
||||
@@ -1468,7 +1471,7 @@ inline BOOST_PROCESS_V2_NAMESPACE::filesystem::path find_executable(
|
||||
// first check if it has the extension already
|
||||
BOOST_PROCESS_V2_NAMESPACE::filesystem::path full_nm(name);
|
||||
BOOST_PROCESS_V2_NAMESPACE::filesystem::path pp(pp_view.begin(), pp_view.end());
|
||||
auto p = pp / nm;
|
||||
auto p = pp / full_nm;
|
||||
error_code ec;
|
||||
|
||||
if (detail::is_executable(p, ec) && !ec)
|
||||
@@ -1695,67 +1698,52 @@ struct process_environment
|
||||
|
||||
|
||||
template<typename Args>
|
||||
void build_env(Args && args, string_view rs)
|
||||
static
|
||||
std::vector<wchar_t> build_env(Args && args,
|
||||
typename std::enable_if<
|
||||
std::is_convertible<
|
||||
decltype(*std::begin(std::declval<Args>())),
|
||||
wcstring_ref>::value>::type * = nullptr)
|
||||
{
|
||||
std::size_t length = 0u;
|
||||
for (string_view v : args)
|
||||
length += detail::size_as_wide(v.data(), v.size(), ec) + 1u;
|
||||
std::vector<wchar_t> res;
|
||||
std::size_t sz = 1;
|
||||
for (wcstring_ref cs : std::forward<Args>(args))
|
||||
sz =+ cs.size() + 1;
|
||||
res.reserve(sz);
|
||||
|
||||
for (wcstring_ref cs : std::forward<Args>(args))
|
||||
res.insert(res.end(), cs.begin(), std::next(cs.end()));
|
||||
|
||||
|
||||
if (ec)
|
||||
return;
|
||||
length ++ ;
|
||||
|
||||
unicode_env.resize(length);
|
||||
|
||||
auto itr = &unicode_env.front();
|
||||
for (string_view v : args)
|
||||
{
|
||||
itr += detail::convert_to_wide(
|
||||
v.data(), v.size(),
|
||||
itr, &unicode_env.back() - itr,
|
||||
ec);
|
||||
if (ec)
|
||||
break;
|
||||
*(itr++) = '\0';
|
||||
}
|
||||
unicode_env.back() = '\0';
|
||||
}
|
||||
template<typename Args>
|
||||
void build_env(Args && args, wstring_view rs)
|
||||
{
|
||||
std::size_t length = 0u;
|
||||
for (const auto & v : std::forward<Args>(args))
|
||||
length += v.size() + 1u;
|
||||
|
||||
length ++ ;
|
||||
|
||||
unicode_env.resize(length);
|
||||
|
||||
auto itr = unicode_env.begin();
|
||||
for (wstring_view v : args )
|
||||
{
|
||||
itr = std::copy(v.begin(), v.end(), itr);
|
||||
*(itr++) = L'\0';
|
||||
}
|
||||
unicode_env.back() = L'\0';
|
||||
res.push_back(L'\0');
|
||||
return res;
|
||||
}
|
||||
|
||||
template<typename Args>
|
||||
std::vector<wchar_t> build_env(Args && args,
|
||||
typename std::enable_if<
|
||||
!std::is_convertible<
|
||||
decltype(*std::begin(std::declval<Args>())),
|
||||
wcstring_ref>::value>::type * = nullptr)
|
||||
{
|
||||
for (auto && arg: std::forward<Args>(args))
|
||||
env_buffer.emplace_back(arg);
|
||||
return build_env(env_buffer);
|
||||
}
|
||||
|
||||
process_environment(std::initializer_list<string_view> sv) { build_env(sv, ""); }
|
||||
process_environment(std::initializer_list<wstring_view> sv) { build_env(sv, L""); }
|
||||
process_environment(std::initializer_list<string_view> sv) : unicode_env{build_env(sv, "")} {}
|
||||
process_environment(std::initializer_list<wstring_view> sv) : unicode_env{build_env(sv, L"")} {}
|
||||
|
||||
template<typename Args>
|
||||
process_environment(Args && args)
|
||||
process_environment(Args && args) : unicode_env{build_env(std::forward<Args>(args))}
|
||||
{
|
||||
if (std::begin(args) != std::end(args))
|
||||
build_env(std::forward<Args>(args), *std::begin(args));
|
||||
}
|
||||
|
||||
error_code error() {return ec;}
|
||||
error_code ec;
|
||||
std::vector<environment::key_value_pair> env_buffer;
|
||||
std::vector<wchar_t> unicode_env;
|
||||
|
||||
|
||||
error_code on_setup(windows::default_launcher & launcher,
|
||||
const filesystem::path &, const std::wstring &);
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#define BOOST_PROCESS_V2_EXIT_CODE_HPP
|
||||
|
||||
#include <boost/process/v2/detail/config.hpp>
|
||||
#include <boost/process/v2/pid.hpp>
|
||||
|
||||
#if defined(BOOST_PROCESS_V2_POSIX)
|
||||
#include <sys/wait.h>
|
||||
@@ -19,6 +20,15 @@
|
||||
|
||||
BOOST_PROCESS_V2_BEGIN_NAMESPACE
|
||||
|
||||
/// Result of a single process exiting.
|
||||
struct single_process_exit
|
||||
{
|
||||
/// The pid of the process that exited
|
||||
pid_type pid;
|
||||
/// The exit code of the code
|
||||
int exit_code;
|
||||
};
|
||||
|
||||
#if defined(GENERATING_DOCUMENTATION)
|
||||
|
||||
/// The native exit-code type, usually an integral value
|
||||
|
||||
321
include/boost/process/v2/group.hpp
Normal file
321
include/boost/process/v2/group.hpp
Normal file
@@ -0,0 +1,321 @@
|
||||
// Copyright (c) 2022 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_V2_GROUP_HPP
|
||||
#define BOOST_PROCESS_V2_GROUP_HPP
|
||||
|
||||
#include <boost/process/v2/detail/config.hpp>
|
||||
#include <boost/process/v2/default_launcher.hpp>
|
||||
#include <boost/process/v2/exit_code.hpp>
|
||||
#include <boost/process/v2/process_handle.hpp>
|
||||
|
||||
|
||||
#if defined(BOOST_PROCESS_V2_WINDOWS)
|
||||
#include <boost/process/v2/detail/group_impl_windows.hpp>
|
||||
#else
|
||||
#include <boost/process/v2/detail/group_impl_posix.hpp>
|
||||
#endif
|
||||
|
||||
BOOST_PROCESS_V2_BEGIN_NAMESPACE
|
||||
|
||||
/** A process handle is an unmanaged version of a process.
|
||||
* This means it does not terminate the proces on destruction and
|
||||
* will not keep track of the exit-code.
|
||||
*
|
||||
* Note that the exit code might be discovered early, during a call to `running`.
|
||||
* Thus it can only be discovered that process has exited already.
|
||||
*/
|
||||
template<typename Executor = BOOST_PROCESS_V2_ASIO_NAMESPACE::any_io_executor,
|
||||
typename Launcher = default_process_launcher>
|
||||
struct basic_group
|
||||
{
|
||||
/// The native handle of the process.
|
||||
/** This might be undefined on posix systems that only support signals */
|
||||
#if defined(GENERATING_DOCUMENTATION)
|
||||
using native_handle_type = implementation_defined;
|
||||
#else
|
||||
using native_handle_type = typename detail::basic_group_impl<Executor>::native_handle_type;
|
||||
#endif
|
||||
/// The executor_type of the group
|
||||
using executor_type = Executor;
|
||||
|
||||
/// The launcher used by the group for emplacing
|
||||
using launcher_type = Launcher;
|
||||
|
||||
// Get the native group handle
|
||||
native_handle_type native_handle() {return impl_.native_handle();}
|
||||
|
||||
/// Getter for the executor
|
||||
executor_type get_executor() { return impl_.get_executor();}
|
||||
|
||||
/// Rebinds the group to another executor.
|
||||
template<typename Executor1>
|
||||
struct rebind_executor
|
||||
{
|
||||
/// The socket type when rebound to the specified executor.
|
||||
typedef basic_group<Executor1, Launcher> other;
|
||||
};
|
||||
|
||||
|
||||
/// Construct a basic_group from an execution_context.
|
||||
/**
|
||||
* @tparam ExecutionContext The context must fulfill the asio::execution_context requirements
|
||||
*/
|
||||
template<typename ExecutionContext>
|
||||
basic_group(ExecutionContext &context,
|
||||
typename std::enable_if<
|
||||
std::is_convertible<ExecutionContext&,
|
||||
BOOST_PROCESS_V2_ASIO_NAMESPACE::execution_context&>::value,
|
||||
launcher_type>::type launcher = launcher_type())
|
||||
: basic_group(context.get_executor(), std::move(launcher))
|
||||
{}
|
||||
|
||||
/// Construct an empty group from an executor.
|
||||
basic_group(executor_type executor, launcher_type launcher = launcher_type())
|
||||
: impl_(std::move(executor)), launcher_(std::move(launcher)) {}
|
||||
|
||||
/// Construct an empty group from an executor and bind it to a pid and the native-handle
|
||||
/** On some non-linux posix systems this overload is not present.
|
||||
*/
|
||||
basic_group(executor_type executor, native_handle_type group,
|
||||
launcher_type launcher = launcher_type())
|
||||
: impl_(std::move(executor), group), , launcher_(std::move(launcher)) {}
|
||||
|
||||
/// Move construct and rebind the executor.
|
||||
template<typename Executor1>
|
||||
basic_group(basic_group<Executor1> &&handle)
|
||||
: procs_(std::move(handle.procs_)), impl_(std::move(handle.impl_)),
|
||||
launcher_(std::move(handle.launcher_)) {}
|
||||
|
||||
/// wait for one process to exit and store the exit code in exit_status.
|
||||
single_process_exit wait_one(error_code &ec)
|
||||
{
|
||||
single_process_exit res;
|
||||
impl_.wait_one(res.pid, res.exit_code, ec);
|
||||
return res;
|
||||
}
|
||||
/// Throwing @overload wait_one(error_code & ec)
|
||||
single_process_exit wait_one()
|
||||
{
|
||||
error_code ec;
|
||||
auto res = wait_one(ec);
|
||||
detail::throw_error(ec, "wait_one");
|
||||
return res;
|
||||
}
|
||||
|
||||
/// wait for all processes to exit. Returns the number of processes exited
|
||||
void wait_all(error_code &ec)
|
||||
{
|
||||
impl_.wait_all(ec);
|
||||
}
|
||||
/// Throwing @overload wait_all(error_code & ec)
|
||||
void wait_all()
|
||||
{
|
||||
error_code ec;
|
||||
wait_all(ec);
|
||||
detail::throw_error(ec, "wait_all");
|
||||
}
|
||||
|
||||
/// Sends the processes a signal to ask for an interrupt, which the process may interpret as a shutdown.
|
||||
/** Maybe be ignored by the subprocess. */
|
||||
void interrupt(error_code &ec)
|
||||
{
|
||||
impl_.interrupt(ec);
|
||||
}
|
||||
|
||||
/// Throwing @overload void interrupt()
|
||||
void interrupt()
|
||||
{
|
||||
error_code ec;
|
||||
interrupt(ec);
|
||||
detail::throw_error(ec, "interrupt");
|
||||
}
|
||||
|
||||
/// Sends the processes a signal to ask for a graceful shutdown. Maybe be ignored by the subprocess.
|
||||
void request_exit(error_code &ec)
|
||||
{
|
||||
impl_.request_exit(ec);
|
||||
}
|
||||
|
||||
/// Throwing @overload void request_exit(error_code & ec)
|
||||
void request_exit()
|
||||
{
|
||||
error_code ec;
|
||||
auto res = request_exit(ec);
|
||||
detail::throw_error(ec, "request_exit");
|
||||
}
|
||||
|
||||
/// Unconditionally terminates the processes and stores the exit code in exit_status.
|
||||
void terminate(error_code &ec)
|
||||
{
|
||||
impl_.terminate(ec);
|
||||
}
|
||||
/// Throwing @overload void terminate(native_exit_code_type &exit_code, error_code & ec)
|
||||
void terminate()
|
||||
{
|
||||
error_code ec;
|
||||
terminate(ec);
|
||||
detail::throw_error(ec, "terminate");
|
||||
}
|
||||
|
||||
/// Check if the process handle is referring to an existing process.
|
||||
bool is_open() const
|
||||
{
|
||||
return impl_.is_open();
|
||||
}
|
||||
|
||||
/// Asynchronously wait for one process to exit and deliver the exit-code in the completion handler.
|
||||
template<BOOST_PROCESS_V2_COMPLETION_TOKEN_FOR(void(error_code, single_process_exit))
|
||||
WaitHandler BOOST_PROCESS_V2_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type)>
|
||||
BOOST_PROCESS_V2_INITFN_AUTO_RESULT_TYPE(WaitHandler, void (error_code, single_process_exit))
|
||||
async_wait_one(WaitHandler &&handler BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type))
|
||||
{
|
||||
return impl_.async_wait_one(std::forward<WaitHandler>(handler));
|
||||
}
|
||||
|
||||
/// Asynchronously wait for all processes to exit and the gorup to be empty.
|
||||
template<BOOST_PROCESS_V2_COMPLETION_TOKEN_FOR(void(error_code))
|
||||
WaitHandler BOOST_PROCESS_V2_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type)>
|
||||
BOOST_PROCESS_V2_INITFN_AUTO_RESULT_TYPE(WaitHandler, void (error_code))
|
||||
async_wait_all(WaitHandler &&handler BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type))
|
||||
{
|
||||
return impl_.async_wait_all(std::forward<WaitHandler>(handler));
|
||||
}
|
||||
/// Throwing overload of @overload contains(pid_type pid, error_code & ec)
|
||||
bool contains(pid_type pid)
|
||||
{
|
||||
error_code ec;
|
||||
auto res = contains(ec);
|
||||
detail::throw_error(ec, "contains");
|
||||
return res;
|
||||
}
|
||||
/// Check if the pid is in the group
|
||||
bool contains(pid_type pid, error_code & ec)
|
||||
{
|
||||
impl_.contains(std::move(proc), ec);
|
||||
}
|
||||
|
||||
template<typename Executor_>
|
||||
void add(basic_process_handle<Executor_> proc)
|
||||
{
|
||||
error_code ec;
|
||||
add(proc.id(), proc.native_handle(), ec);
|
||||
detail::throw_error(ec, "add");
|
||||
}
|
||||
|
||||
template<typename Executor_>
|
||||
void add(basic_process_handle<Executor_> proc, error_code & ec)
|
||||
{
|
||||
impl_.add(std::move(proc), ec);
|
||||
}
|
||||
|
||||
template<typename ... Inits>
|
||||
pid_type emplace(
|
||||
const filesystem::path& exe,
|
||||
std::initializer_list<string_view> args,
|
||||
Inits&&... inits)
|
||||
{
|
||||
error_code ec;
|
||||
auto ph = launcher_(impl_.get_executor(), exe, std::move(args),
|
||||
std::forward<Inits>(inits)..., impl_.get_initializer(ec));
|
||||
if (ec)
|
||||
detail::throw_error(ec);
|
||||
return ph.detach().id();
|
||||
}
|
||||
|
||||
template<typename ... Inits>
|
||||
pid_type emplace(
|
||||
const filesystem::path& exe,
|
||||
std::initializer_list<wstring_view> args,
|
||||
Inits&&... inits)
|
||||
{
|
||||
error_code ec;
|
||||
auto ph = launcher_(impl_.get_executor(), exe, std::move(args),
|
||||
std::forward<Inits>(inits)..., impl_.get_initializer(ec));
|
||||
if (ec)
|
||||
detail::throw_error(ec);
|
||||
return ph.detach().id();
|
||||
}
|
||||
|
||||
template<typename Args, typename ... Inits>
|
||||
pid_type emplace(
|
||||
const filesystem::path& exe,
|
||||
Args && args,
|
||||
Inits&&... inits)
|
||||
{
|
||||
error_code ec;
|
||||
auto ph = launcher_(impl_.get_executor(), exe, std::move(args),
|
||||
std::forward<Inits>(inits)..., impl_.get_initializer(ec));
|
||||
if (ec)
|
||||
detail::throw_error(ec);
|
||||
return ph.detach().id();
|
||||
}
|
||||
|
||||
template<typename ... Inits>
|
||||
pid_type emplace(
|
||||
error_code & ec,
|
||||
const filesystem::path& exe,
|
||||
std::initializer_list<string_view> args,
|
||||
Inits&&... inits)
|
||||
{
|
||||
auto ph = launcher_(impl_.get_executor(), ec, exe, std::move(args),
|
||||
std::forward<Inits>(inits)..., impl_.get_initializer(ec));
|
||||
if (ec)
|
||||
return pid_type{-1};
|
||||
return ph.detach().id();
|
||||
}
|
||||
|
||||
template<typename ... Inits>
|
||||
pid_type emplace(
|
||||
error_code & ec,
|
||||
const filesystem::path& exe,
|
||||
std::initializer_list<wstring_view> args,
|
||||
Inits&&... inits)
|
||||
{
|
||||
auto ph = launcher_(impl_.get_executor(), ec, exe, std::move(args),
|
||||
std::forward<Inits>(inits)..., impl_.get_initializer(ec));
|
||||
if (ec)
|
||||
return pid_type{-1};
|
||||
return ph.detach().id();
|
||||
}
|
||||
|
||||
template<typename Args, typename ... Inits>
|
||||
pid_type emplace(
|
||||
error_code & ec,
|
||||
const filesystem::path& exe,
|
||||
Args && args,
|
||||
Inits&&... inits)
|
||||
{
|
||||
auto ph = launcher_(impl_.get_executor(), ec, exe, std::move(args),
|
||||
std::forward<Inits>(inits)..., impl_.get_initializer(ec));
|
||||
if (ec)
|
||||
return pid_type{-1};
|
||||
return ph.detach().id();
|
||||
}
|
||||
|
||||
void detach() { impl_.detach(); }
|
||||
private:
|
||||
detail::basic_group_impl<Executor> impl_;
|
||||
launcher_type launcher_;
|
||||
};
|
||||
|
||||
/// Process group with the default executor.
|
||||
using group = basic_group<>;
|
||||
|
||||
#if !defined(BOOST_PROCESS_V2_HEADER_ONLY)
|
||||
extern template struct basic_group<BOOST_PROCESS_V2_ASIO_NAMESPACE::any_io_executor, default_process_launcher>;
|
||||
#endif
|
||||
|
||||
BOOST_PROCESS_V2_END_NAMESPACE
|
||||
|
||||
#if defined(BOOST_PROCESS_V2_HEADER_ONLY)
|
||||
|
||||
#include <boost/process/v2/impl/group.ipp>
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
#endif //BOOST_PROCESS_V2_GROUP_HPP
|
||||
25
include/boost/process/v2/impl/group.ipp
Normal file
25
include/boost/process/v2/impl/group.ipp
Normal file
@@ -0,0 +1,25 @@
|
||||
//
|
||||
// boost/process/v2/windows/impl/job_object_service.ipp
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2022 Klemens D. Morgenstern (klemens dot morgenstern at gmx dot net)
|
||||
//
|
||||
// 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_V2_IMPL_GROUP_IPP
|
||||
#define BOOST_PROCESS_V2_IMPL_GROUP_IPP
|
||||
|
||||
#include <boost/process/v2/detail/config.hpp>
|
||||
#include <boost/process/v2/group.hpp>
|
||||
|
||||
BOOST_PROCESS_V2_BEGIN_NAMESPACE
|
||||
|
||||
#if !defined(BOOST_PROCESS_V2_HEADER_ONLY)
|
||||
template struct basic_group_impl<BOOST_PROCESS_V2_ASIO_NAMESPACE::any_io_executor, default_process_launcher>;
|
||||
#endif
|
||||
|
||||
BOOST_PROCESS_V2_END_NAMESPACE
|
||||
|
||||
|
||||
#endif //BOOST_PROCESS_V2_IMPL_GROUP_IPP
|
||||
131
include/boost/process/v2/impl/shell.ipp
Normal file
131
include/boost/process/v2/impl/shell.ipp
Normal file
@@ -0,0 +1,131 @@
|
||||
// Copyright (c) 2022 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_V2_IMPL_SHELL_IPP
|
||||
#define BOOST_PROCESS_V2_IMPL_SHELL_IPP
|
||||
|
||||
#include <boost/process/v2/detail/config.hpp>
|
||||
#include <boost/process/v2/detail/last_error.hpp>
|
||||
#include <boost/process/v2/detail/throw_error.hpp>
|
||||
#include <boost/process/v2/detail/config.hpp>
|
||||
#include <boost/process/v2/error.hpp>
|
||||
#include <boost/process/v2/shell.hpp>
|
||||
|
||||
#if defined(BOOST_PROCESS_V2_WINDOWS)
|
||||
#include <shellapi.h>
|
||||
#else
|
||||
#include <wordexp.h>
|
||||
#endif
|
||||
|
||||
BOOST_PROCESS_V2_BEGIN_NAMESPACE
|
||||
|
||||
#if defined(BOOST_PROCESS_V2_WINDOWS)
|
||||
BOOST_PROCESS_V2_DECL const error_category& get_shell_category()
|
||||
{
|
||||
return system_category();
|
||||
}
|
||||
#else
|
||||
|
||||
struct shell_category_t final : public error_category
|
||||
{
|
||||
shell_category_t() : error_category(0xDAF1u) {}
|
||||
|
||||
const char* name() const noexcept
|
||||
{
|
||||
return "process.v2.utf8";
|
||||
}
|
||||
std::string message(int value) const
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case WRDE_BADCHAR:
|
||||
return "Illegal occurrence of newline or one of |, &, ;, <, >, (, ), {, }.";
|
||||
case WRDE_BADVAL:
|
||||
return "An undefined shell variable was referenced, and the WRDE_UNDEF flag told us to consider this an error.";
|
||||
case WRDE_CMDSUB:
|
||||
return "Command substitution occurred, and the WRDE_NOCMD flag told us to consider this an error.";
|
||||
case WRDE_NOSPACE:
|
||||
return "Out of memory.";
|
||||
case WRDE_SYNTAX:
|
||||
return "Shell syntax error, such as unbalanced parentheses or unmatched quotes.";
|
||||
default:
|
||||
return "process.v2.wordexp error";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
BOOST_PROCESS_V2_DECL const error_category& get_shell_category()
|
||||
{
|
||||
static shell_category_t instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if defined (BOOST_PROCESS_V2_WINDOWS)
|
||||
|
||||
void shell::parse_()
|
||||
{
|
||||
argv_ = ::CommandLineToArgvW(input_.c_str(), &argc_);
|
||||
if (argv_ == nullptr)
|
||||
detail::throw_last_error();
|
||||
}
|
||||
|
||||
shell::~shell()
|
||||
{
|
||||
if (argv_ != nullptr)
|
||||
LocalFree(argv_);
|
||||
}
|
||||
|
||||
auto shell::args() const-> args_type
|
||||
{
|
||||
return input_.c_str();
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
void shell::parse_()
|
||||
{
|
||||
wordexp_t we{};
|
||||
auto cd = wordexp(input_.c_str(), &we, WRDE_NOCMD);
|
||||
|
||||
if (cd != 0)
|
||||
detail::throw_error(error_code(cd, get_shell_category()), "shell::parse");
|
||||
else
|
||||
{
|
||||
argc_ = static_cast<int>(we.we_wordc);
|
||||
argv_ = we.we_wordv;
|
||||
reserved_ = static_cast<int>(we.we_offs);
|
||||
}
|
||||
}
|
||||
|
||||
shell::~shell()
|
||||
{
|
||||
if (argv_ != nullptr)
|
||||
{
|
||||
wordexp_t we{
|
||||
.we_wordc = static_cast<std::size_t>(argc_),
|
||||
.we_wordv = argv_,
|
||||
.we_offs = static_cast<std::size_t>(reserved_)
|
||||
};
|
||||
wordfree(&we);
|
||||
}
|
||||
}
|
||||
|
||||
auto shell::args() const -> args_type
|
||||
{
|
||||
if (argc() == 0)
|
||||
{
|
||||
static const char * helper = nullptr;
|
||||
return &helper;
|
||||
}
|
||||
else
|
||||
return const_cast<const char**>(argv());
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
BOOST_PROCESS_V2_END_NAMESPACE
|
||||
|
||||
#endif //BOOST_PROCESS_V2_IMPL_SHELL_IPP
|
||||
@@ -91,13 +91,16 @@ struct bind_fd
|
||||
{
|
||||
}
|
||||
|
||||
error_code on_setup(posix::default_launcher & launcher, const filesystem::path &, const char * const *)
|
||||
{
|
||||
launcher.fd_whitelist.push_back(target);
|
||||
}
|
||||
|
||||
/// Implementation of the initialization function.
|
||||
error_code on_exec_setup(posix::default_launcher & launcher, const filesystem::path &, const char * const *)
|
||||
{
|
||||
if (::dup2(fd, target) == -1)
|
||||
return error_code(errno, system_category());
|
||||
|
||||
launcher.fd_whitelist.push_back(target);
|
||||
return error_code ();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -378,6 +378,7 @@ struct default_launcher
|
||||
detail::on_error(*this, executable, argv, ec, inits...);
|
||||
return basic_process<Executor>(exec);
|
||||
}
|
||||
fd_whitelist.push_back(pg.p[1]);
|
||||
|
||||
auto & ctx = BOOST_PROCESS_V2_ASIO_NAMESPACE::query(
|
||||
exec, BOOST_PROCESS_V2_ASIO_NAMESPACE::execution::context);
|
||||
@@ -399,7 +400,6 @@ struct default_launcher
|
||||
ec = detail::on_exec_setup(*this, executable, argv, inits...);
|
||||
if (!ec)
|
||||
{
|
||||
fd_whitelist.push_back(pg.p[1]);
|
||||
close_all_fds(ec);
|
||||
}
|
||||
if (!ec)
|
||||
@@ -485,6 +485,11 @@ struct default_launcher
|
||||
return argv_.data();
|
||||
}
|
||||
|
||||
const char * const * build_argv_(const filesystem::path &, const char ** argv)
|
||||
{
|
||||
return argv;
|
||||
}
|
||||
|
||||
template<typename Args>
|
||||
const char * const * build_argv_(const filesystem::path & pt, const Args & args,
|
||||
typename std::enable_if<
|
||||
|
||||
@@ -99,6 +99,7 @@ struct pdfork_launcher : default_launcher
|
||||
detail::on_error(*this, executable, argv, ec, inits...);
|
||||
return basic_process<Executor>(exec);
|
||||
}
|
||||
fd_whitelist.push_back(pg.p[1]);
|
||||
|
||||
auto & ctx = BOOST_PROCESS_V2_ASIO_NAMESPACE::query(
|
||||
exec, BOOST_PROCESS_V2_ASIO_NAMESPACE::execution::context);
|
||||
@@ -121,7 +122,6 @@ struct pdfork_launcher : default_launcher
|
||||
ec = detail::on_exec_setup(*this, executable, argv, inits...);
|
||||
if (!ec)
|
||||
{
|
||||
fd_whitelist.push_back(pg.p[1]);
|
||||
close_all_fds(ec);
|
||||
}
|
||||
if (!ec)
|
||||
|
||||
@@ -19,9 +19,11 @@
|
||||
|
||||
#if defined(BOOST_PROCESS_V2_STANDALONE)
|
||||
#include <asio/any_io_executor.hpp>
|
||||
#include <asio/post.hpp>
|
||||
#include <utility>
|
||||
#else
|
||||
#include <boost/asio/any_io_executor.hpp>
|
||||
#include <boost/asio/post.hpp>
|
||||
#include <boost/core/exchange.hpp>
|
||||
#endif
|
||||
|
||||
@@ -164,7 +166,7 @@ struct basic_process
|
||||
typename std::enable_if<
|
||||
std::is_convertible<ExecutionContext&,
|
||||
BOOST_PROCESS_V2_ASIO_NAMESPACE::execution_context&>::value, void *>::type = nullptr)
|
||||
: process_handle_(context, pid, native_handle) {}
|
||||
: process_handle_(context.get_executor(), pid, native_handle) {}
|
||||
|
||||
/// Create an invalid handle
|
||||
template <typename ExecutionContext>
|
||||
@@ -172,7 +174,7 @@ struct basic_process
|
||||
typename std::enable_if<
|
||||
is_convertible<ExecutionContext&,
|
||||
BOOST_PROCESS_V2_ASIO_NAMESPACE::execution_context&>::value, void *>::type = nullptr)
|
||||
: process_handle_(context) {}
|
||||
: process_handle_(context.get_executor()) {}
|
||||
|
||||
|
||||
|
||||
@@ -339,7 +341,7 @@ private:
|
||||
};
|
||||
|
||||
BOOST_PROCESS_V2_ASIO_NAMESPACE::post(handle.get_executor(),
|
||||
completer{res, std::move(self)});
|
||||
completer{static_cast<int>(res), std::move(self)});
|
||||
}
|
||||
else
|
||||
handle.async_wait(std::move(self));
|
||||
|
||||
139
include/boost/process/v2/shell.hpp
Normal file
139
include/boost/process/v2/shell.hpp
Normal file
@@ -0,0 +1,139 @@
|
||||
// Copyright (c) 2022 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_V2_SHELL_HPP
|
||||
#define BOOST_PROCESS_V2_SHELL_HPP
|
||||
|
||||
#include <boost/core/exchange.hpp>
|
||||
#include <boost/process/v2/cstring_ref.hpp>
|
||||
#include <boost/process/v2/detail/config.hpp>
|
||||
#include <boost/process/v2/detail/utf8.hpp>
|
||||
#include <boost/process/v2/detail/throw_error.hpp>
|
||||
#include <boost/process/v2/environment.hpp>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
BOOST_PROCESS_V2_BEGIN_NAMESPACE
|
||||
|
||||
/// Error category used by the shell parser.
|
||||
extern BOOST_PROCESS_V2_DECL const error_category& get_shell_category();
|
||||
static const error_category& shell_category = get_shell_category();
|
||||
|
||||
/// Utility to parse commands
|
||||
/** This utility class parses command lines into tokens
|
||||
* and allows users to executed based on textual inputs.
|
||||
*
|
||||
* In v1, this was possible directly when starting a process,
|
||||
* but has been removed based on the security risks associated with this.
|
||||
*
|
||||
* By making the shell parsing explicity, it is encouraged
|
||||
* that a user runs a sanity check on the executable before launching it.
|
||||
*
|
||||
* @par Example
|
||||
* @code {.cpp}
|
||||
* asio::io_context ctx;
|
||||
*
|
||||
* auto cmd = shell("my-app --help");
|
||||
* auto exe = cmd.exe();
|
||||
* check_if_malicious(exe);
|
||||
*
|
||||
* process proc{ctx, exe, cmd.args()};
|
||||
*
|
||||
* @endcode
|
||||
*
|
||||
*
|
||||
*/
|
||||
struct shell
|
||||
{
|
||||
#if defined(BOOST_PROCESS_V2_WINDOWS)
|
||||
using char_type = wchar_t;
|
||||
using args_type = const wchar_t *;
|
||||
#else
|
||||
using char_type = char;
|
||||
using args_type = const char **;
|
||||
#endif
|
||||
|
||||
shell() = default;
|
||||
|
||||
template<typename Char, typename Traits>
|
||||
shell(basic_string_view<Char, Traits> input)
|
||||
: buffer_(detail::conv_string<char_type>(input.data(), input.size()))
|
||||
{
|
||||
parse_();
|
||||
}
|
||||
|
||||
shell(basic_cstring_ref<char_type> input) : input_(input) {parse_();}
|
||||
shell(basic_string_view<
|
||||
typename std::conditional<
|
||||
std::is_same<char_type, char>::value,
|
||||
wchar_t, char>::type> input) : buffer_(detail::conv_string<char_type>(input.data(), input.size()))
|
||||
{
|
||||
parse_();
|
||||
}
|
||||
|
||||
shell(const shell &) = delete;
|
||||
shell& operator=(const shell &) = delete;
|
||||
|
||||
shell(shell && lhs) noexcept
|
||||
: buffer_(std::move(lhs.buffer_)),
|
||||
input_(std::move(lhs.input_)),
|
||||
argc_(boost::exchange(lhs.argc_, 0)),
|
||||
argv_(boost::exchange(lhs.argv_, nullptr)),
|
||||
reserved_(boost::exchange(lhs.reserved_, 0))
|
||||
{
|
||||
}
|
||||
shell& operator=(shell && lhs) noexcept
|
||||
{
|
||||
shell tmp(std::move(*this));
|
||||
buffer_ = std::move(lhs.buffer_);
|
||||
input_ = std::move(lhs.input_);
|
||||
argc_ = boost::exchange(lhs.argc_, 0);
|
||||
argv_ = boost::exchange(lhs.argv_, nullptr);
|
||||
reserved_ = boost::exchange(lhs.reserved_, 0);
|
||||
return *this;
|
||||
}
|
||||
|
||||
// the length of the parsed shell, including the executable
|
||||
int argc() const { return argc_; }
|
||||
char_type** argv() const { return argv_; }
|
||||
|
||||
char_type** begin() const {return argv();}
|
||||
char_type** end() const {return argv() + argc();}
|
||||
|
||||
bool empty() const {return argc() == 0;}
|
||||
std::size_t size() const {return static_cast<std::size_t>(argc()); }
|
||||
/// Native representation of the arguments to be used - excluding the executable
|
||||
BOOST_PROCESS_V2_DECL args_type args() const;
|
||||
template<typename Environment = environment::current_view>
|
||||
filesystem::path exe(Environment && env = environment::current()) const
|
||||
{
|
||||
if (argc() == 0)
|
||||
return "";
|
||||
else
|
||||
return environment::find_executable(0[argv()], std::forward<Environment>(env));
|
||||
}
|
||||
BOOST_PROCESS_V2_DECL ~shell();
|
||||
|
||||
private:
|
||||
BOOST_PROCESS_V2_DECL void parse_();
|
||||
|
||||
// storage in case we need a conversion
|
||||
std::basic_string<char_type> buffer_;
|
||||
basic_cstring_ref<char_type> input_{buffer_};
|
||||
// impl details
|
||||
int argc_ = 0;
|
||||
char_type ** argv_ = nullptr;
|
||||
int reserved_ = 0;
|
||||
|
||||
};
|
||||
|
||||
BOOST_PROCESS_V2_END_NAMESPACE
|
||||
|
||||
#if defined(BOOST_PROCESS_V2_HEADER_ONLY)
|
||||
|
||||
#include <boost/process/v2/impl/shell.ipp>
|
||||
|
||||
#endif
|
||||
|
||||
#endif //BOOST_PROCESS_V2_ERROR_HPP
|
||||
@@ -22,5 +22,10 @@
|
||||
#include <boost/process/v2/impl/default_launcher.ipp>
|
||||
#include <boost/process/v2/impl/environment.ipp>
|
||||
#include <boost/process/v2/impl/process_handle.ipp>
|
||||
#include <boost/process/v2/impl/shell.ipp>
|
||||
|
||||
#if defined(BOOST_PROCESS_V2_WINDOWS)
|
||||
#include <boost/process/v2/detail/impl/group_impl_windows.ipp>
|
||||
#endif
|
||||
|
||||
#endif //BOOST_PROCESS_V2_SRC_HPP
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
#include <boost/process/v2/detail/config.hpp>
|
||||
#include <boost/process/v2/default_launcher.hpp>
|
||||
|
||||
#include <cstddef>
|
||||
#if defined(BOOST_PROCESS_V2_STANDALONE)
|
||||
#include <asio/connect_pipe.hpp>
|
||||
#else
|
||||
@@ -52,7 +52,7 @@ struct handle_closer
|
||||
DWORD flags{0xFFFFFFFFu};
|
||||
};
|
||||
|
||||
template<DWORD Io>
|
||||
template<DWORD Target>
|
||||
struct process_io_binding
|
||||
{
|
||||
HANDLE prepare()
|
||||
@@ -62,7 +62,7 @@ struct process_io_binding
|
||||
return hh;
|
||||
}
|
||||
|
||||
std::unique_ptr<void, handle_closer> h{::GetStdHandle(Io), false};
|
||||
std::unique_ptr<void, handle_closer> h{::GetStdHandle(Target), false};
|
||||
|
||||
static DWORD get_flags(HANDLE h)
|
||||
{
|
||||
@@ -82,10 +82,11 @@ struct process_io_binding
|
||||
process_io_binding(FILE * f) : process_io_binding(_get_osfhandle(_fileno(f))) {}
|
||||
process_io_binding(HANDLE h) : h{h, get_flags(h)} {}
|
||||
process_io_binding(std::nullptr_t) : process_io_binding(filesystem::path("NUL")) {}
|
||||
process_io_binding(const filesystem::path & pth)
|
||||
template<typename T, typename = typename std::enable_if<std::is_same<T, filesystem::path>::value>::type>
|
||||
process_io_binding(const T & pth)
|
||||
: h(::CreateFileW(
|
||||
pth.c_str(),
|
||||
Io == STD_INPUT_HANDLE ? GENERIC_READ : GENERIC_WRITE,
|
||||
Target == STD_INPUT_HANDLE ? GENERIC_READ : GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||||
nullptr,
|
||||
OPEN_ALWAYS,
|
||||
@@ -101,11 +102,13 @@ struct process_io_binding
|
||||
typename std::enable_if<Target != STD_INPUT_HANDLE, Executor*>::type = 0)
|
||||
{
|
||||
BOOST_PROCESS_V2_ASIO_NAMESPACE::detail::native_pipe_handle p[2];
|
||||
error_code ec;
|
||||
BOOST_PROCESS_V2_ASIO_NAMESPACE::detail::create_pipe(p, ec);
|
||||
if (ec)
|
||||
return ;
|
||||
detail::throw_error(ec, "create_pipe");
|
||||
|
||||
h = std::unique_ptr<void, handle_closer>{p[1], true};
|
||||
readable_pipe.assign(p[0], ec);
|
||||
readable_pipe.assign(p[0]);
|
||||
}
|
||||
|
||||
|
||||
@@ -114,11 +117,13 @@ struct process_io_binding
|
||||
typename std::enable_if<Target == STD_INPUT_HANDLE, Executor*>::type = 0)
|
||||
{
|
||||
BOOST_PROCESS_V2_ASIO_NAMESPACE::detail::native_pipe_handle p[2];
|
||||
error_code ec;
|
||||
BOOST_PROCESS_V2_ASIO_NAMESPACE::detail::create_pipe(p, ec);
|
||||
if (ec)
|
||||
return ;
|
||||
detail::throw_error(ec, "create_pipe");
|
||||
|
||||
h = std::unique_ptr<void, handle_closer>{p[0], true};
|
||||
writable_pipe.assign(p[1], ec);
|
||||
writable_pipe.assign(p[1]);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -289,11 +294,6 @@ struct process_stdio
|
||||
if (::dup2(err.fd, err.target) == -1)
|
||||
return error_code(errno, system_category());
|
||||
|
||||
|
||||
launcher.fd_whitelist.push_back(STDIN_FILENO);
|
||||
launcher.fd_whitelist.push_back(STDOUT_FILENO);
|
||||
launcher.fd_whitelist.push_back(STDERR_FILENO);
|
||||
|
||||
return error_code {};
|
||||
};
|
||||
#endif
|
||||
|
||||
@@ -11,8 +11,9 @@
|
||||
#ifndef BOOST_PROCESS_V2_WINDOWS_DEFAULT_LAUNCHER_HPP
|
||||
#define BOOST_PROCESS_V2_WINDOWS_DEFAULT_LAUNCHER_HPP
|
||||
|
||||
#include <boost/process/v2/cstring_ref.hpp>
|
||||
#include <boost/process/v2/detail/config.hpp>
|
||||
|
||||
#include <boost/process/v2/cstring_ref.hpp>
|
||||
#include <boost/process/v2/detail/last_error.hpp>
|
||||
#include <boost/process/v2/detail/utf8.hpp>
|
||||
#include <boost/process/v2/error.hpp>
|
||||
@@ -398,6 +399,11 @@ struct default_launcher
|
||||
return build_command_line_impl(pt, args, *std::begin(args));
|
||||
}
|
||||
|
||||
static std::wstring build_command_line(const filesystem::path & pt, const wchar_t * args)
|
||||
{
|
||||
return args;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
23
include/boost/process/v2/windows/impl/job_object_service.ipp
Normal file
23
include/boost/process/v2/windows/impl/job_object_service.ipp
Normal file
@@ -0,0 +1,23 @@
|
||||
//
|
||||
// boost/process/v2/windows/impl/job_object_service.ipp
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2022 Klemens D. Morgenstern (klemens dot morgenstern at gmx dot net)
|
||||
//
|
||||
// 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_V2_WINDOWS_IMPL_JOB_OBJECT_SERVICE_IPP
|
||||
#define BOOST_PROCESS_V2_WINDOWS_IMPL_JOB_OBJECT_SERVICE_IPP
|
||||
|
||||
#include <boost/process/v2/detail/config.hpp>
|
||||
#include <boost/process/v2/windows/job_object_service.hpp>
|
||||
|
||||
BOOST_PROCESS_V2_BEGIN_NAMESPACE
|
||||
namespace windows
|
||||
{
|
||||
}
|
||||
BOOST_PROCESS_V2_END_NAMESPACE
|
||||
|
||||
|
||||
#endif //BOOST_PROCESS_V2_WINDOWS_IMPL_JOB_OBJECT_SERVICE_IPP
|
||||
@@ -2,16 +2,16 @@ enable_testing()
|
||||
|
||||
|
||||
add_executable(boost_process_sparring_partner sparring_partner.cpp )
|
||||
target_link_libraries(boost_process_sparring_partner Boost::program_options Boost::filesystem Boost::iostreams)
|
||||
target_link_libraries(boost_process_sparring_partner Boost::process Boost::lambda Boost::program_options Boost::filesystem Boost::iostreams)
|
||||
|
||||
add_executable(boost_process_exit_argc exit_argc.cpp)
|
||||
|
||||
add_executable(boost_process_sub_launch sub_launcher.cpp)
|
||||
target_link_libraries(boost_process_sub_launch Boost::program_options Boost::filesystem Boost::iostreams Boost::system)
|
||||
target_link_libraries(boost_process_sub_launch Boost::process Boost::program_options Boost::filesystem Boost::iostreams Boost::system)
|
||||
|
||||
function(process_standalone_test name )
|
||||
add_executable(boost_process_${name} ${name}.cpp)
|
||||
target_link_libraries(boost_process_${name} Boost::system Boost::filesystem)
|
||||
target_link_libraries(boost_process_${name} Boost::process Boost::system Boost::filesystem Boost::unit_test_framework)
|
||||
add_test(NAME boost_process_${name} COMMAND $<TARGET_FILE:boost_process_${name}> )
|
||||
endfunction()
|
||||
|
||||
@@ -21,7 +21,7 @@ process_standalone_test(pipe)
|
||||
|
||||
function(process_sub_launch_test name )
|
||||
add_executable(boost_process_${name} ${name}.cpp)
|
||||
target_link_libraries(boost_process_${name} Boost::system Boost::filesystem Boost::thread)
|
||||
target_link_libraries(boost_process_${name} Boost::process Boost::system Boost::filesystem Boost::thread Boost::unit_test_framework)
|
||||
add_test(NAME boost_process_${name} COMMAND $<TARGET_FILE:boost_process_${name}> $<TARGET_FILE:boost_process_sub_launch> )
|
||||
endfunction()
|
||||
|
||||
@@ -30,7 +30,7 @@ process_sub_launch_test(group_wait)
|
||||
|
||||
function(process_sparring_partner_launch name )
|
||||
add_executable(boost_process_${name} ${name}.cpp)
|
||||
target_link_libraries(boost_process_${name} Boost::system Boost::filesystem Boost::thread)
|
||||
target_link_libraries(boost_process_${name} Boost::process Boost::system Boost::filesystem Boost::thread Boost::unit_test_framework Boost::program_options)
|
||||
add_test(NAME boost_process_${name} COMMAND $<TARGET_FILE:boost_process_${name}> $<TARGET_FILE:boost_process_sparring_partner> )
|
||||
endfunction()
|
||||
|
||||
@@ -70,7 +70,11 @@ process_sparring_partner_launch(wait_for)
|
||||
process_sparring_partner_launch(on_exit)
|
||||
process_sparring_partner_launch(on_exit2)
|
||||
process_sparring_partner_launch(on_exit3)
|
||||
process_sparring_partner_launch(posix_specific)
|
||||
process_sparring_partner_launch(windows_specific)
|
||||
|
||||
if(WIN32)
|
||||
process_sparring_partner_launch(windows_specific)
|
||||
else()
|
||||
process_sparring_partner_launch(posix_specific)
|
||||
endif()
|
||||
|
||||
add_subdirectory(v2)
|
||||
@@ -113,3 +113,17 @@ BOOST_AUTO_TEST_CASE(ignore_error)
|
||||
BOOST_CHECK_NO_THROW(bp::child c("doesnt-exit", bp::ignore_error));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE(not_found)
|
||||
{
|
||||
try
|
||||
{
|
||||
bp::child c("doesnt-exit");
|
||||
BOOST_CHECK_MESSAGE(false, "Should throw");
|
||||
}
|
||||
catch( bp::process_error & se)
|
||||
{
|
||||
BOOST_CHECK(se.code());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
enable_testing()
|
||||
|
||||
add_library(boost_process_v2_test_impl test_impl.cpp)
|
||||
target_link_libraries(boost_process_v2_test_impl Boost::unit_test_framework Boost::process)
|
||||
target_link_libraries(boost_process_v2_test_impl Boost::process Boost::unit_test_framework Boost::process)
|
||||
target_compile_definitions(boost_process_v2_test_impl PUBLIC -DBOOST_PROCESS_V2_SEPARATE_COMPILATION=1)
|
||||
target_include_directories(boost_process_v2_test_impl PUBLIC ../../include)
|
||||
|
||||
if (WIN32)
|
||||
target_compile_definitions(boost_process_v2_test_impl PUBLIC WIN32_LEAN_AND_MEAN=1)
|
||||
endif()
|
||||
|
||||
function(boost_process_v2_standalone_test name)
|
||||
add_executable(boost_process_v2_${name} ${name}.cpp)
|
||||
target_link_libraries(boost_process_v2_${name} Boost::system Boost::filesystem boost_process_v2_test_impl)
|
||||
target_link_libraries(boost_process_v2_${name} Boost::process Boost::system Boost::filesystem boost_process_v2_test_impl)
|
||||
add_test(NAME boost_process_v2_${name} COMMAND $<TARGET_FILE:boost_process_v2_${name}> )
|
||||
endfunction()
|
||||
|
||||
@@ -15,19 +18,20 @@ boost_process_v2_standalone_test(utf8)
|
||||
boost_process_v2_standalone_test(cstring_ref)
|
||||
boost_process_v2_standalone_test(pid)
|
||||
boost_process_v2_standalone_test(environment)
|
||||
boost_process_v2_standalone_test(shell)
|
||||
|
||||
add_library(boost_process_v2_header_test header_1.cpp header_2.cpp)
|
||||
|
||||
target_link_libraries(boost_process_v2_header_test PUBLIC Boost::process)
|
||||
add_executable(boost_process_v2_test_target target.cpp)
|
||||
target_link_libraries(boost_process_v2_test_target PUBLIC Boost::system)
|
||||
target_include_directories(boost_process_v2_test_target PUBLIC ../../../..)
|
||||
target_link_libraries(boost_process_v2_test_target PUBLIC Boost::process Boost::system)
|
||||
|
||||
function(boost_process_v2_test_with_target name)
|
||||
add_executable(boost_process_v2_${name} ${name}.cpp)
|
||||
target_link_libraries(boost_process_v2_${name} Boost::system Boost::filesystem boost_process_v2_test_impl)
|
||||
target_link_libraries(boost_process_v2_${name} Boost::process Boost::system Boost::filesystem boost_process_v2_test_impl)
|
||||
add_dependencies(boost_process_v2_${name} boost_process_v2_test_target)
|
||||
add_test(NAME boost_process_v2_${name} COMMAND $<TARGET_FILE:boost_process_v2_${name}>
|
||||
-- $<TARGET_FILE:boost_process_v2_test_target>)
|
||||
endfunction()
|
||||
|
||||
boost_process_v2_test_with_target(process)
|
||||
boost_process_v2_test_with_target(process)
|
||||
boost_process_v2_test_with_target(group)
|
||||
@@ -53,10 +53,12 @@ test-suite standalone :
|
||||
[ run cstring_ref.cpp test_impl ]
|
||||
[ run environment.cpp test_impl ]
|
||||
[ run pid.cpp test_impl ]
|
||||
[ run shell.cpp test_impl ]
|
||||
;
|
||||
|
||||
test-suite with_target :
|
||||
[ run process.cpp test_impl : --log_level=all --catch_system_errors=no -- : target ]
|
||||
[ run windows.cpp test_impl : --log_level=all --catch_system_errors=no -- : target : <build>no <target-os>windows:<build>yes <target-os>windows:<source>Advapi32 ]
|
||||
[ run group.cpp test_impl : --log_level=all --catch_system_errors=no -- : target ]
|
||||
;
|
||||
|
||||
|
||||
@@ -95,9 +95,9 @@ BOOST_AUTO_TEST_CASE(environment)
|
||||
#else
|
||||
std::unordered_map<std::wstring, std::wstring> custom_env =
|
||||
{
|
||||
L"HOME", L"/home/byzantium",
|
||||
L"HOMEDRIVE", L"X:",
|
||||
L"HOMEPATH", L"\\users\\theodora"
|
||||
{L"HOME", L"/home/byzantium"},
|
||||
{L"HOMEDRIVE", L"X:"},
|
||||
{L"HOMEPATH", L"\\users\\theodora"}
|
||||
};
|
||||
|
||||
std::vector<std::wstring> custom_env2 =
|
||||
@@ -106,8 +106,8 @@ BOOST_AUTO_TEST_CASE(environment)
|
||||
{L"HOMEDRIVE=X:"},
|
||||
{L"HOMEPATH=\\users\\theodora"}
|
||||
};
|
||||
BOOST_CHECK_EQUAL(bpe::home(custom_env), L"X:\\Users\\theodora");
|
||||
BOOST_CHECK_EQUAL(bpe::home(custom_env2), L"X:\\Users\\theodora");
|
||||
BOOST_CHECK_EQUAL(bpe::home(custom_env), "X:\\users\\theodora");
|
||||
BOOST_CHECK_EQUAL(bpe::home(custom_env2), "X:\\users\\theodora");
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
179
test/v2/group.cpp
Normal file
179
test/v2/group.cpp
Normal file
@@ -0,0 +1,179 @@
|
||||
// Copyright (c) 2022 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)
|
||||
|
||||
|
||||
// Disable autolinking for unit tests.
|
||||
#if !defined(BOOST_ALL_NO_LIB)
|
||||
#define BOOST_ALL_NO_LIB 1
|
||||
#endif // !defined(BOOST_ALL_NO_LIB)
|
||||
|
||||
// Test that header file is self-contained.
|
||||
|
||||
#include <boost/test/unit_test.hpp>
|
||||
#include <boost/asio/io_context.hpp>
|
||||
#include <boost/process/v2/group.hpp>
|
||||
#include <boost/process/v2.hpp>
|
||||
|
||||
namespace bp2 = boost::process::v2;
|
||||
namespace bpw = boost::process::v2::windows;
|
||||
namespace bpd = boost::process::v2::detail;
|
||||
namespace asio = boost::asio;
|
||||
|
||||
BOOST_AUTO_TEST_CASE(wait_one)
|
||||
{
|
||||
using boost::unit_test::framework::master_test_suite;
|
||||
asio::io_context ctx;
|
||||
const auto pth = master_test_suite().argv[1];
|
||||
|
||||
bp2::error_code ec;
|
||||
bp2::group grp{ctx};
|
||||
|
||||
BOOST_CHECK_MESSAGE(!ec, ec.message());
|
||||
|
||||
auto pid1 = grp.emplace(pth, {"sleep", "100"});
|
||||
auto pid2 = grp.emplace(pth, {"sleep", "300"});
|
||||
auto pid3 = grp.emplace(pth, {"sleep", "500"});
|
||||
|
||||
auto res1 = grp.wait_one(ec); BOOST_CHECK_MESSAGE(!ec, ec.message());
|
||||
auto res2 = grp.wait_one(ec); BOOST_CHECK_MESSAGE(!ec, ec.message());
|
||||
auto res3 = grp.wait_one(ec); BOOST_CHECK_MESSAGE(!ec, ec.message());
|
||||
|
||||
|
||||
grp.wait_all(); //
|
||||
|
||||
BOOST_CHECK_EQUAL(res1.exit_code, 0);
|
||||
BOOST_CHECK_EQUAL(res2.exit_code, 0);
|
||||
BOOST_CHECK_EQUAL(res3.exit_code, 0);
|
||||
|
||||
BOOST_CHECK_EQUAL(res1.pid, pid1);
|
||||
BOOST_CHECK_EQUAL(res2.pid, pid2);
|
||||
BOOST_CHECK_EQUAL(res3.pid, pid3);
|
||||
|
||||
BOOST_CHECK(grp.is_open());
|
||||
BOOST_CHECK_MESSAGE(!ec, ec.message());
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(wait_all)
|
||||
{
|
||||
using boost::unit_test::framework::master_test_suite;
|
||||
asio::io_context ctx;
|
||||
const auto pth = master_test_suite().argv[1];
|
||||
|
||||
bp2::group grp{ctx};
|
||||
|
||||
|
||||
auto pid1 = grp.emplace(pth, {"sleep", "10"});
|
||||
auto pid2 = grp.emplace(pth, {"sleep", "30"});
|
||||
auto pid3 = grp.emplace(pth, {"sleep", "50"});
|
||||
|
||||
grp.wait_all(); //
|
||||
|
||||
BOOST_CHECK(grp.is_open());
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(terminate_)
|
||||
{
|
||||
using boost::unit_test::framework::master_test_suite;
|
||||
asio::io_context ctx;
|
||||
const auto pth = master_test_suite().argv[1];
|
||||
|
||||
bp2::error_code ec;
|
||||
bp2::group grp{ctx};
|
||||
|
||||
BOOST_CHECK_MESSAGE(!ec, ec.message());
|
||||
|
||||
const auto pid1 = grp.emplace(pth, {"sleep", "10000000"});
|
||||
const auto pid2 = grp.emplace(pth, {"sleep", "10000000"});
|
||||
const auto pid3 = grp.emplace(pth, {"sleep", "10000000"});
|
||||
|
||||
const auto start = std::chrono::steady_clock::now();
|
||||
|
||||
grp.terminate();
|
||||
grp.wait_all();
|
||||
|
||||
const auto end = std::chrono::steady_clock::now();
|
||||
BOOST_CHECK((start + std::chrono::milliseconds(5000)) > end);
|
||||
|
||||
BOOST_CHECK(grp.is_open());
|
||||
BOOST_CHECK_MESSAGE(!ec, ec.message());
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(async_wait_one)
|
||||
{
|
||||
using boost::unit_test::framework::master_test_suite;
|
||||
asio::io_context ctx;
|
||||
const auto pth = master_test_suite().argv[1];
|
||||
|
||||
bp2::error_code ec;
|
||||
bp2::group grp{ctx};
|
||||
|
||||
BOOST_CHECK_MESSAGE(!ec, ec.message());
|
||||
|
||||
auto pid1 = grp.emplace(pth, {"sleep", "100"});
|
||||
auto pid2 = grp.emplace(pth, {"sleep", "300"});
|
||||
auto pid3 = grp.emplace(pth, {"sleep", "500"});
|
||||
|
||||
std::vector<bp2::pid_type> res;
|
||||
|
||||
grp.async_wait_one(
|
||||
[&](bp2::error_code ec, bp2::single_process_exit sp)
|
||||
{
|
||||
res.push_back(sp.pid);
|
||||
BOOST_CHECK_EQUAL(sp.exit_code, 0u);
|
||||
BOOST_CHECK_MESSAGE(!ec, ec.message());
|
||||
});
|
||||
grp.async_wait_one(
|
||||
[&](bp2::error_code ec, bp2::single_process_exit sp)
|
||||
{
|
||||
res.push_back(sp.pid);
|
||||
BOOST_CHECK_EQUAL(sp.exit_code, 0u);
|
||||
BOOST_CHECK_MESSAGE(!ec, ec.message());
|
||||
});
|
||||
grp.async_wait_one(
|
||||
[&](bp2::error_code ec, bp2::single_process_exit sp)
|
||||
{
|
||||
res.push_back(sp.pid);
|
||||
BOOST_CHECK_EQUAL(sp.exit_code, 0u);
|
||||
BOOST_CHECK_MESSAGE(!ec, ec.message());
|
||||
});
|
||||
|
||||
BOOST_CHECK_GE(ctx.run(), 0u);
|
||||
|
||||
BOOST_CHECK_EQUAL(res.at(0), pid1);
|
||||
BOOST_CHECK_EQUAL(res.at(1), pid2);
|
||||
BOOST_CHECK_EQUAL(res.at(2), pid3);
|
||||
|
||||
|
||||
BOOST_CHECK(grp.is_open());
|
||||
BOOST_CHECK_MESSAGE(!ec, ec.message());
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(async_wait_all)
|
||||
{
|
||||
using boost::unit_test::framework::master_test_suite;
|
||||
asio::io_context ctx;
|
||||
const auto pth = master_test_suite().argv[1];
|
||||
|
||||
bp2::error_code ec;
|
||||
bp2::group grp{ctx};
|
||||
|
||||
BOOST_CHECK_MESSAGE(!ec, ec.message());
|
||||
|
||||
auto pid1 = grp.emplace(pth, {"sleep", "10"});
|
||||
auto pid2 = grp.emplace(pth, {"sleep", "30"});
|
||||
auto pid3 = grp.emplace(pth, {"sleep", "50"});
|
||||
|
||||
grp.async_wait_all(
|
||||
[](bp2::error_code ec)
|
||||
{
|
||||
BOOST_CHECK_MESSAGE(!ec, ec.message());
|
||||
});
|
||||
|
||||
ctx.run();
|
||||
|
||||
|
||||
BOOST_CHECK(grp.is_open());
|
||||
BOOST_CHECK_MESSAGE(!ec, ec.message());
|
||||
}
|
||||
@@ -164,6 +164,7 @@ BOOST_AUTO_TEST_CASE(request_exit)
|
||||
, asio::windows::show_window_minimized_not_active
|
||||
#endif
|
||||
);
|
||||
BOOST_CHECK(proc.running());
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(250));
|
||||
proc.request_exit();
|
||||
proc.wait();
|
||||
@@ -188,6 +189,8 @@ void trim_end(std::string & str)
|
||||
{
|
||||
auto itr = std::find_if(str.rbegin(), str.rend(), &std::char_traits<char>::not_eof);
|
||||
str.erase(itr.base(), str.end());
|
||||
if (!str.empty() && str.back() == '\r')
|
||||
str.pop_back();
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(print_args_out)
|
||||
@@ -356,16 +359,16 @@ BOOST_AUTO_TEST_CASE(popen)
|
||||
// default CWD
|
||||
bpv::popen proc(ctx, pth, {"echo"});
|
||||
|
||||
asio::write(proc, asio::buffer("FOOBAR"));
|
||||
|
||||
auto written = asio::write(proc, asio::buffer("FOOBAR"));
|
||||
proc.get_stdin().close();
|
||||
|
||||
std::string res;
|
||||
boost::system::error_code ec;
|
||||
std::size_t n = asio::read(proc, asio::dynamic_buffer(res), ec);
|
||||
res.resize(n - 1);
|
||||
BOOST_CHECK_EQUAL(ec, asio::error::eof);
|
||||
BOOST_CHECK(ec == asio::error::eof || ec == asio::error::broken_pipe);
|
||||
BOOST_REQUIRE_GE(n, 1);
|
||||
// remove EOF
|
||||
res.pop_back();
|
||||
BOOST_CHECK_EQUAL(res, "FOOBAR");
|
||||
|
||||
proc.wait();
|
||||
@@ -429,7 +432,7 @@ std::string read_env(const char * name, Inits && ... inits)
|
||||
BOOST_CHECK_MESSAGE((ec == asio::error::broken_pipe) || (ec == asio::error::eof), ec.message());
|
||||
out.resize(sz);
|
||||
trim_end(out);
|
||||
printf("Read env (%ld) %s: '%s'\n", sz, name, out.c_str());
|
||||
printf("Read env (%ld) %s: '%s'\n", static_cast<long>(sz), name, out.c_str());
|
||||
|
||||
proc.wait();
|
||||
BOOST_CHECK_EQUAL(proc.exit_code(), 0);
|
||||
@@ -449,12 +452,12 @@ BOOST_AUTO_TEST_CASE(environment)
|
||||
BOOST_CHECK_EQUAL("FOO-BAR", read_env("FOOBAR", bpv::process_environment{sub_env}));
|
||||
|
||||
sub_env.push_back("XYZ=ZYX");
|
||||
auto itr = std::find_if(sub_env.begin(), sub_env.end(), [](const bpv::environment::key_value_pair & kv) {return kv.key() == "PATH";});
|
||||
auto itr = std::find_if(sub_env.begin(), sub_env.end(), [](const bpv::environment::key_value_pair & kv) {return kv.key() == bpv::environment::key("PATH");});
|
||||
path += static_cast<char>(bpv::environment::delimiter);
|
||||
path += "/bar/foo";
|
||||
bpv::environment::value pval = itr->value();
|
||||
pval.push_back("/bar/foo");
|
||||
*itr = bpv::environment::key_value_pair("PATH", pval);
|
||||
*itr = bpv::environment::key_value_pair(bpv::environment::key("PATH"), pval);
|
||||
BOOST_CHECK_EQUAL(path, read_env("PATH", bpv::process_environment{sub_env}));
|
||||
|
||||
#if defined(BOOST_PROCESS_V2_WINDOWS)
|
||||
@@ -462,12 +465,13 @@ BOOST_AUTO_TEST_CASE(environment)
|
||||
BOOST_CHECK_EQUAL("FOO-BAR", read_env("FOOBAR", bpv::process_environment{L"FOOBAR=FOO-BAR", wpath.c_str()}));
|
||||
wpath += bpv::environment::delimiter;
|
||||
wpath += L"C:\\bar\\foo";
|
||||
BOOST_CHECK_EQUAL(wpath.substr(5), read_env("pATH", bpv::process_environment{wpath.c_str(), std::wstring(L"XYZ=ZYX")}));
|
||||
BOOST_CHECK_EQUAL(
|
||||
bpv::detail::conv_string<char>(wpath.c_str() + 5, wpath.size() - 5)
|
||||
, read_env("pATH", bpv::process_environment{wpath.c_str(), std::wstring(L"XYZ=ZYX")}));
|
||||
#endif
|
||||
|
||||
BOOST_CHECK_EQUAL(read_env("PATH", bpv::process_environment(bpv::environment::current())), ::getenv("PATH"));
|
||||
}
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END();
|
||||
|
||||
|
||||
56
test/v2/shell.cpp
Executable file
56
test/v2/shell.cpp
Executable file
@@ -0,0 +1,56 @@
|
||||
// Copyright (c) 2022 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)
|
||||
|
||||
// Disable autolinking for unit tests.
|
||||
#if !defined(BOOST_ALL_NO_LIB)
|
||||
#define BOOST_ALL_NO_LIB 1
|
||||
#endif // !defined(BOOST_ALL_NO_LIB)
|
||||
|
||||
// Test that header file is self-contained.
|
||||
#include <boost/process/v2/shell.hpp>
|
||||
#include <boost/process/v2/process.hpp>
|
||||
#include <boost/asio/io_context.hpp>
|
||||
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
#if defined(BOOST_PROCESS_V2_WINDOWS)
|
||||
#define STR(Value) L##Value
|
||||
#define STR_VIEW(Value) boost::process::v2::wcstring_ref(STR(Value))
|
||||
#else
|
||||
#define STR(Value) Value
|
||||
#define STR_VIEW(Value) boost::process::v2::cstring_ref(STR(Value))
|
||||
#endif
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE(test_shell_parser)
|
||||
{
|
||||
using boost::process::v2::shell;
|
||||
namespace bpv = boost::process::v2;
|
||||
#if defined(BOOST_PROCESS_V2_POSIX)
|
||||
BOOST_CHECK_THROW(shell("foo \""), bpv::system_error);
|
||||
#endif
|
||||
|
||||
auto sh = shell(STR("foo bar \"foo bar\""));
|
||||
BOOST_CHECK(sh.argc() == 3u);
|
||||
BOOST_CHECK(sh.argv()[0] == STR_VIEW("foo"));
|
||||
BOOST_CHECK(sh.argv()[1] == STR_VIEW("bar"));
|
||||
BOOST_CHECK(sh.argv()[2] == STR_VIEW("foo bar"));
|
||||
|
||||
#if defined(BOOST_PROCESS_V2_POSIX)
|
||||
auto raw_shell = "sh -c false";
|
||||
#else
|
||||
auto raw_shell = "cmd /c exit 1";
|
||||
#endif
|
||||
sh = shell(raw_shell);
|
||||
|
||||
auto exe = sh.exe();
|
||||
BOOST_CHECK(bpv::filesystem::exists(exe));
|
||||
|
||||
boost::asio::io_context ctx;
|
||||
bpv::process proc{ctx, exe, sh.args()};
|
||||
|
||||
proc.wait();
|
||||
BOOST_CHECK_EQUAL(proc.exit_code(), 1);
|
||||
}
|
||||
@@ -60,7 +60,7 @@ BOOST_AUTO_TEST_CASE(creation_flags)
|
||||
BOOST_CHECK_EQUAL(proc.wait() & ~EXTENDED_STARTUPINFO_PRESENT, 0);
|
||||
|
||||
proc = bpv::process{ctx, master_test_suite().argv[1], {"creation-flags"}, bpv::windows::process_creation_flags<STARTF_TITLEISAPPID>()};
|
||||
BOOST_CHECK(proc);
|
||||
BOOST_CHECK(proc.running());
|
||||
BOOST_CHECK_EQUAL(proc.wait() & ~EXTENDED_STARTUPINFO_PRESENT, STARTF_TITLEISAPPID);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user