From a3304564c65252fe6cb1a249a2f5c41d83f399e6 Mon Sep 17 00:00:00 2001 From: Samuel Venable Date: Mon, 6 Feb 2023 20:43:32 +0800 Subject: [PATCH] extern process management. --- .gitignore | 1 + .../process/v2/detail/environment_posix.hpp | 4 +- .../v2/detail/impl/process_handle_windows.ipp | 56 +- .../process/v2/detail/process_handle_fd.hpp | 37 + .../v2/detail/process_handle_fd_or_signal.hpp | 37 + .../v2/detail/process_handle_signal.hpp | 59 +- .../v2/detail/process_handle_windows.hpp | 28 + include/boost/process/v2/ext/cmd.hpp | 55 ++ include/boost/process/v2/ext/cwd.hpp | 47 ++ .../process/v2/ext/detail/impl/proc_info.ipp | 127 +++ .../boost/process/v2/ext/detail/proc_info.hpp | 125 +++ include/boost/process/v2/ext/env.hpp | 138 ++++ include/boost/process/v2/ext/exe.hpp | 49 ++ include/boost/process/v2/ext/impl/cmd.ipp | 468 +++++++++++ include/boost/process/v2/ext/impl/cwd.ipp | 234 ++++++ include/boost/process/v2/ext/impl/env.ipp | 295 +++++++ include/boost/process/v2/ext/impl/exe.ipp | 192 +++++ include/boost/process/v2/impl/pid.ipp | 739 +++++++++++++++++- include/boost/process/v2/impl/shell.ipp | 22 +- include/boost/process/v2/pid.hpp | 29 +- include/boost/process/v2/process.hpp | 34 +- include/boost/process/v2/shell.hpp | 18 +- include/boost/process/v2/src.hpp | 5 + test/v2/CMakeLists.txt | 9 +- test/v2/Jamfile.jam | 8 + test/v2/ext.cpp | 173 ++++ test/v2/pid.cpp | 26 +- test/v2/process.cpp | 2 + 28 files changed, 2974 insertions(+), 43 deletions(-) create mode 100644 include/boost/process/v2/ext/cmd.hpp create mode 100644 include/boost/process/v2/ext/cwd.hpp create mode 100644 include/boost/process/v2/ext/detail/impl/proc_info.ipp create mode 100644 include/boost/process/v2/ext/detail/proc_info.hpp create mode 100644 include/boost/process/v2/ext/env.hpp create mode 100644 include/boost/process/v2/ext/exe.hpp create mode 100644 include/boost/process/v2/ext/impl/cmd.ipp create mode 100644 include/boost/process/v2/ext/impl/cwd.ipp create mode 100644 include/boost/process/v2/ext/impl/env.ipp create mode 100644 include/boost/process/v2/ext/impl/exe.ipp create mode 100644 test/v2/ext.cpp diff --git a/.gitignore b/.gitignore index 4354fdab..51166dfa 100644 --- a/.gitignore +++ b/.gitignore @@ -31,4 +31,5 @@ /notes.cpp /notes_p.txt .settings +.DS_Store diff --git a/include/boost/process/v2/detail/environment_posix.hpp b/include/boost/process/v2/detail/environment_posix.hpp index 237a8739..e945a86c 100644 --- a/include/boost/process/v2/detail/environment_posix.hpp +++ b/include/boost/process/v2/detail/environment_posix.hpp @@ -14,7 +14,7 @@ #include #include -#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__APPLE__) || defined(__MACH__) +#if defined(__APPLE__) || defined(__MACH__) || defined(__FreeBSD__) || defined(__DragonFly__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__sun) extern "C" { extern char **environ; } #endif @@ -77,4 +77,4 @@ BOOST_PROCESS_V2_DECL bool is_executable(const filesystem::path & pth, error_cod BOOST_PROCESS_V2_END_NAMESPACE -#endif \ No newline at end of file +#endif diff --git a/include/boost/process/v2/detail/impl/process_handle_windows.ipp b/include/boost/process/v2/detail/impl/process_handle_windows.ipp index 96422480..638bc014 100644 --- a/include/boost/process/v2/detail/impl/process_handle_windows.ipp +++ b/include/boost/process/v2/detail/impl/process_handle_windows.ipp @@ -9,9 +9,20 @@ #include #include #include +#include #include +#if !defined(BOOST_PROCESS_V2_DISABLE_UNDOCUMENTED_API) +extern "C" +{ + +LONG WINAPI NtResumeProcess(HANDLE ProcessHandle); +LONG WINAPI NtSuspendProcess(HANDLE ProcessHandle); + +} +#endif + BOOST_PROCESS_V2_BEGIN_NAMESPACE namespace detail @@ -40,10 +51,10 @@ void terminate_if_running_(HANDLE handle) { DWORD exit_code = 0u; if (handle == INVALID_HANDLE_VALUE) - return ; + return ; if (::GetExitCodeProcess(handle, &exit_code)) - if (exit_code == STILL_ACTIVE) - ::TerminateProcess(handle, 260); + if (exit_code == STILL_ACTIVE) + ::TerminateProcess(handle, 260); } bool check_handle_(HANDLE handle, error_code & ec) @@ -60,8 +71,8 @@ bool check_pid_(pid_type pid_, error_code & ec) { if (pid_ == 0) { - ec.assign(ERROR_INVALID_HANDLE_STATE, system_category()); - return false; + ec.assign(ERROR_INVALID_HANDLE_STATE, system_category()); + return false; } return true; } @@ -73,19 +84,19 @@ struct enum_windows_data_t }; static BOOL CALLBACK enum_window(HWND hwnd, LPARAM param) - { +{ auto data = reinterpret_cast(param); DWORD pid{0u}; GetWindowThreadProcessId(hwnd, &pid); if (pid != data->pid) - return TRUE; + return TRUE; LRESULT res = ::SendMessageW(hwnd, WM_CLOSE, 0, 0); if (res) - data->ec = detail::get_last_error(); + data->ec = detail::get_last_error(); return res == 0; - } +} void request_exit_(pid_type pid_, error_code & ec) { @@ -113,6 +124,33 @@ void check_running_(HANDLE handle, error_code & ec, DWORD & exit_status) ec = detail::get_last_error(); } +#if !defined(BOOST_PROCESS_V2_DISABLE_UNDOCUMENTED_API) +void suspend_(HANDLE handle, error_code & ec) +{ + auto nt_err = NtSuspendProcess(handle); + ULONG dos_err = RtlNtStatusToDosError(nt_err); + if (dos_err) + ec = detail::get_last_error(); +} + +void resume_(HANDLE handle, error_code & ec) +{ + auto nt_err = NtResumeProcess(handle); + ULONG dos_err = RtlNtStatusToDosError(nt_err); + if (dos_err) + ec = detail::get_last_error(); +} +#else +void suspend_(HANDLE, error_code & ec) +{ + ec.assign(ERROR_CALL_NOT_IMPLEMENTED, system_category()); +} + +void resume_(HANDLE handle, error_code & ec) +{ + ec.assign(ERROR_CALL_NOT_IMPLEMENTED, system_category()); +} +#endif #if !defined(BOOST_PROCESS_V2_HEADER_ONLY) template struct basic_process_handle_win<>; diff --git a/include/boost/process/v2/detail/process_handle_fd.hpp b/include/boost/process/v2/detail/process_handle_fd.hpp index 86854266..d3b198dd 100644 --- a/include/boost/process/v2/detail/process_handle_fd.hpp +++ b/include/boost/process/v2/detail/process_handle_fd.hpp @@ -101,6 +101,8 @@ struct basic_process_handle_fd pid_type id() const { return pid_; } + native_handle_type native_handle() {return pid_;} + void terminate_if_running(error_code &) { if (pid_ <= 0) @@ -182,7 +184,42 @@ struct basic_process_handle_fd if (ec) detail::throw_error(ec, "request_exit"); } + + void suspend() + { + if (pid_ <= 0) + return ; + error_code ec; + suspend(ec); + if (ec) + detail::throw_error(ec, "suspend"); + } + void suspend(error_code &ec) + { + if (pid_ <= 0) + return ; + if (::kill(pid_, SIGSTOP) == -1) + ec = get_last_error(); + } + + void resume() + { + if (pid_ <= 0) + return ; + error_code ec; + resume(ec); + if (ec) + detail::throw_error(ec, "resume"); + } + + void resume(error_code &ec) + { + if (pid_ <= 0) + return ; + if (::kill(pid_, SIGCONT) == -1) + ec = get_last_error(); + } void terminate(native_exit_code_type &exit_status, error_code &ec) { if (pid_ <= 0) diff --git a/include/boost/process/v2/detail/process_handle_fd_or_signal.hpp b/include/boost/process/v2/detail/process_handle_fd_or_signal.hpp index c91b3153..dfbee11f 100644 --- a/include/boost/process/v2/detail/process_handle_fd_or_signal.hpp +++ b/include/boost/process/v2/detail/process_handle_fd_or_signal.hpp @@ -128,6 +128,7 @@ struct basic_process_handle_fd_or_signal pid_type id() const { return pid_; } + native_handle_type native_handle() {return pid_;} void terminate_if_running(error_code &) { @@ -211,6 +212,42 @@ struct basic_process_handle_fd_or_signal if (ec) detail::throw_error(ec, "request_exit"); } + + void suspend() + { + if (pid_ <= 0) + return ; + error_code ec; + suspend(ec); + if (ec) + detail::throw_error(ec, "suspend"); + } + + void suspend(error_code &ec) + { + if (pid_ <= 0) + return ; + if (::kill(pid_, SIGSTOP) == -1) + ec = get_last_error(); + } + + void resume() + { + if (pid_ <= 0) + return ; + error_code ec; + resume(ec); + if (ec) + detail::throw_error(ec, "resume"); + } + + void resume(error_code &ec) + { + if (pid_ <= 0) + return ; + if (::kill(pid_, SIGCONT) == -1) + ec = get_last_error(); + } void terminate(native_exit_code_type &exit_status, error_code &ec) { diff --git a/include/boost/process/v2/detail/process_handle_signal.hpp b/include/boost/process/v2/detail/process_handle_signal.hpp index cf042c40..00f7bbcc 100644 --- a/include/boost/process/v2/detail/process_handle_signal.hpp +++ b/include/boost/process/v2/detail/process_handle_signal.hpp @@ -41,7 +41,7 @@ struct basic_process_handle_signal { native_handle_type() = delete; native_handle_type(const native_handle_type & ) = delete; - ~native_handle_type() = delete; + ~native_handle_type() = default; }; typedef Executor executor_type; @@ -103,6 +103,7 @@ struct basic_process_handle_signal pid_type id() const { return pid_; } + native_handle_type native_handle() {return pid_;} void terminate_if_running(error_code &) { @@ -112,7 +113,7 @@ struct basic_process_handle_signal void terminate_if_running() { if (pid_ <= 0) - return ; + return; if (::waitpid(pid_, nullptr, WNOHANG) == 0) { ::kill(pid_, SIGKILL); @@ -123,7 +124,7 @@ struct basic_process_handle_signal void wait(native_exit_code_type &exit_status, error_code &ec) { if (pid_ <= 0) - return ; + return; while (::waitpid(pid_, &exit_status, 0) < 0) { if (errno != EINTR) @@ -137,7 +138,7 @@ struct basic_process_handle_signal void wait(native_exit_code_type &exit_status) { if (pid_ <= 0) - return ; + return; error_code ec; wait(exit_status, ec); if (ec) @@ -147,7 +148,7 @@ struct basic_process_handle_signal void interrupt(error_code &ec) { if (pid_ <= 0) - return ; + return; if (::kill(pid_, SIGTERM) == -1) ec = get_last_error(); } @@ -155,7 +156,7 @@ struct basic_process_handle_signal void interrupt() { if (pid_ <= 0) - return ; + return; error_code ec; interrupt(ec); if (ec) @@ -165,7 +166,7 @@ struct basic_process_handle_signal void request_exit(error_code &ec) { if (pid_ <= 0) - return ; + return; if (::kill(pid_, SIGTERM) == -1) ec = get_last_error(); } @@ -173,17 +174,53 @@ struct basic_process_handle_signal void request_exit() { if (pid_ <= 0) - return ; + return; error_code ec; request_exit(ec); if (ec) detail::throw_error(ec, "request_exit"); } + void suspend() + { + if (pid_ <= 0) + return; + error_code ec; + suspend(ec); + if (ec) + detail::throw_error(ec, "suspend"); + } + + void suspend(error_code &ec) + { + if (pid_ <= 0) + return; + if (::kill(pid_, SIGCONT) == -1) + ec = get_last_error(); + } + + void resume() + { + if (pid_ <= 0) + return; + error_code ec; + resume(ec); + if (ec) + detail::throw_error(ec, "resume"); + } + + void resume(error_code &ec) + { + if (pid_ <= 0) + return; + if (::kill(pid_, SIGTERM) == -1) + ec = get_last_error(); + } + void terminate(native_exit_code_type &exit_status, error_code &ec) { if (pid_ <= 0) - return ; + return; if (::kill(pid_, SIGKILL) == -1) ec = get_last_error(); } @@ -191,7 +228,7 @@ struct basic_process_handle_signal void terminate(native_exit_code_type &exit_status) { if (pid_ <= 0) - return ; + return; error_code ec; terminate(exit_status, ec); if (ec) @@ -284,7 +321,7 @@ struct basic_process_handle_signal if (!ec && (wait_res == 0)) { handle.async_wait(std::move(self)); - return ; + return; } struct completer diff --git a/include/boost/process/v2/detail/process_handle_windows.hpp b/include/boost/process/v2/detail/process_handle_windows.hpp index 275d0551..979536a5 100644 --- a/include/boost/process/v2/detail/process_handle_windows.hpp +++ b/include/boost/process/v2/detail/process_handle_windows.hpp @@ -32,6 +32,8 @@ BOOST_PROCESS_V2_DECL void terminate_if_running_(void * handle); BOOST_PROCESS_V2_DECL bool check_handle_(void* handle, error_code & ec); BOOST_PROCESS_V2_DECL bool check_pid_(pid_type pid_, error_code & ec); BOOST_PROCESS_V2_DECL void interrupt_(pid_type pid_, error_code & ec); +BOOST_PROCESS_V2_DECL void suspend_(void * handle, error_code & ec); +BOOST_PROCESS_V2_DECL void resume_(void * handle, error_code & ec); BOOST_PROCESS_V2_DECL void terminate_(void * handle, error_code & ec, native_exit_code_type & exit_code); BOOST_PROCESS_V2_DECL void request_exit_(pid_type pid_, error_code & ec); BOOST_PROCESS_V2_DECL void check_running_(void* handle, error_code & ec, native_exit_code_type & exit_status); @@ -176,6 +178,32 @@ struct basic_process_handle_win detail::throw_error(ec, "request_exit"); } + void suspend(error_code &ec) + { + detail::suspend_(handle_.native_handle(), ec); + } + + void suspend() + { + error_code ec; + suspend(ec); + if (ec) + detail::throw_error(ec, "suspend"); + } + + void resume(error_code &ec) + { + detail::resume_(handle_.native_handle(), ec); + } + + void resume() + { + error_code ec; + suspend(ec); + if (ec) + detail::throw_error(ec, "resume"); + } + void terminate(native_exit_code_type &exit_status, error_code &ec) { if (!detail::check_handle_(handle_.native_handle(), ec)) diff --git a/include/boost/process/v2/ext/cmd.hpp b/include/boost/process/v2/ext/cmd.hpp new file mode 100644 index 00000000..beca4ffd --- /dev/null +++ b/include/boost/process/v2/ext/cmd.hpp @@ -0,0 +1,55 @@ +// Copyright (c) 2022 Klemens D. Morgenstern +// Copyright (c) 2022 Samuel Venable +// +// 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_CMD_HPP +#define BOOST_PROCESS_V2_CMD_HPP + +#include +#include + +#include +#include +#include +#include + +#include + +BOOST_PROCESS_V2_BEGIN_NAMESPACE + +namespace ext { + +/// Get the argument vector from a given pid +BOOST_PROCESS_V2_DECL shell cmd(pid_type pid, error_code & ec); +BOOST_PROCESS_V2_DECL shell cmd(pid_type pid); + +#if defined(BOOST_PROCESS_V2_WINDOWS) +BOOST_PROCESS_V2_DECL shell cmd(HANDLE handle, error_code & ec); +BOOST_PROCESS_V2_DECL shell cmd(HANDLE handle); +#endif + +template +BOOST_PROCESS_V2_DECL shell cmd(basic_process_handle & handle, error_code & ec) +{ + return cmd(handle.native_handle(), ec); +} + +template +BOOST_PROCESS_V2_DECL shell cmd(basic_process_handle & handle) +{ + return cmd(handle.native_handle()); +} + +} // namespace ext + +BOOST_PROCESS_V2_END_NAMESPACE + +#if defined(BOOST_PROCESS_V2_HEADER_ONLY) + +#include + +#endif + + +#endif // BOOST_PROCESS_V2_CMD_HPP diff --git a/include/boost/process/v2/ext/cwd.hpp b/include/boost/process/v2/ext/cwd.hpp new file mode 100644 index 00000000..ec069316 --- /dev/null +++ b/include/boost/process/v2/ext/cwd.hpp @@ -0,0 +1,47 @@ +// Copyright (c) 2022 Klemens D. Morgenstern +// Copyright (c) 2022 Samuel Venable +// +// 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_CWD_HPP +#define BOOST_PROCESS_V2_CWD_HPP + +#include +#include + +#include + +BOOST_PROCESS_V2_BEGIN_NAMESPACE + +namespace ext { + +/// Obtain the current path of a process +BOOST_PROCESS_V2_DECL filesystem::path cwd(pid_type pid, error_code & ec); +BOOST_PROCESS_V2_DECL filesystem::path cwd(pid_type pid); + +#if defined(BOOST_PROCESS_V2_WINDOWS) +BOOST_PROCESS_V2_DECL filesystem::path cwd(HANDLE handle, error_code & ec); +BOOST_PROCESS_V2_DECL filesystem::path cwd(HANDLE handle); +#endif + +template +BOOST_PROCESS_V2_DECL filesystem::path cwd(basic_process_handle & handle, error_code & ec) +{ + return cwd(handle.native_handle(), ec); +} + +template +BOOST_PROCESS_V2_DECL filesystem::path cwd(basic_process_handle & handle) +{ + return cwd(handle.native_handle()); +} + +} // namespace ext + +BOOST_PROCESS_V2_END_NAMESPACE + +#if defined(BOOST_PROCESS_V2_HEADER_ONLY) +#include +#endif + +#endif // BOOST_PROCESS_V2_CWD_HPP diff --git a/include/boost/process/v2/ext/detail/impl/proc_info.ipp b/include/boost/process/v2/ext/detail/impl/proc_info.ipp new file mode 100644 index 00000000..ba866735 --- /dev/null +++ b/include/boost/process/v2/ext/detail/impl/proc_info.ipp @@ -0,0 +1,127 @@ +// Copyright (c) 2022 Klemens D. Morgenstern +// Copyright (c) 2022 Samuel Venable +// +// 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_DETAIL_PROC_INFO_IPP +#define BOOST_PROCESS_V2_IMPL_DETAIL_PROC_INFO_IPP + +#include +#include +#include +#include + +#include + +#if (defined(__APPLE__) && defined(__MACH__)) +#include +#include +#include +#include +#include +#endif + +BOOST_PROCESS_V2_BEGIN_NAMESPACE + +namespace detail +{ + +namespace ext +{ + +#if defined(BOOST_PROCESS_V2_WINDOWS) +// type of process memory to read? +enum MEMTYP {MEMCMD, MEMCWD}; +std::wstring cwd_cmd_from_proc(HANDLE proc, int type, boost::system::error_code & ec) +{ + std::wstring buffer; + PEB peb; + SIZE_T nRead = 0; + ULONG len = 0; + PROCESS_BASIC_INFORMATION pbi; + RTL_USER_PROCESS_PARAMETERS_EXTENDED upp; + + NTSTATUS status = 0; + PVOID buf = nullptr; + status = NtQueryInformationProcess(proc, ProcessBasicInformation, &pbi, sizeof(pbi), &len); + ULONG error = RtlNtStatusToDosError(status); + + if (error) + { + ec.assign(error, boost::system::system_category()); + return {}; + } + + if (!ReadProcessMemory(proc, pbi.PebBaseAddress, &peb, sizeof(peb), &nRead)) + { + ec = detail::get_last_error(); + return {}; + } + + if (!ReadProcessMemory(proc, peb.ProcessParameters, &upp, sizeof(upp), &nRead)) + { + ec = detail::get_last_error(); + return {}; + } + + if (type == MEMCWD) + { + buf = upp.CurrentDirectory.DosPath.Buffer; + len = upp.CurrentDirectory.DosPath.Length; + } + else if (type == MEMCMD) + { + buf = upp.CommandLine.Buffer; + len = upp.CommandLine.Length; + } + + buffer.resize(len / 2 + 1); + + if (!ReadProcessMemory(proc, buf, &buffer[0], len, &nRead)) + { + ec = detail::get_last_error(); + return {}; + } + + buffer.pop_back(); + return buffer; +} + +// with debug_privilege enabled allows reading info from more processes +// this includes stuff such as exe path, cwd path, cmdline, and environ +HANDLE open_process_with_debug_privilege(boost::process::v2::pid_type pid, boost::system::error_code & ec) +{ + HANDLE proc = nullptr; + HANDLE hToken = nullptr; + LUID luid; + TOKEN_PRIVILEGES tkp; + if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) + { + if (LookupPrivilegeValue(nullptr, SE_DEBUG_NAME, &luid)) + { + tkp.PrivilegeCount = 1; + tkp.Privileges[0].Luid = luid; + tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; + if (AdjustTokenPrivileges(hToken, false, &tkp, sizeof(tkp), nullptr, nullptr)) + { + proc = OpenProcess(PROCESS_ALL_ACCESS, false, pid); + } + } + CloseHandle(hToken); + } + if (!proc) + proc = OpenProcess(PROCESS_ALL_ACCESS, false, pid); + if (!proc) + ec = detail::get_last_error(); + return proc; +} +#endif + +} // namespace ext + +} // namespace detail + +BOOST_PROCESS_V2_END_NAMESPACE + +#endif // BOOST_PROCESS_V2_IMPL_DETAIL_PROC_INFO_IPP + diff --git a/include/boost/process/v2/ext/detail/proc_info.hpp b/include/boost/process/v2/ext/detail/proc_info.hpp new file mode 100644 index 00000000..c1c24fa3 --- /dev/null +++ b/include/boost/process/v2/ext/detail/proc_info.hpp @@ -0,0 +1,125 @@ +// Copyright (c) 2022 Klemens D. Morgenstern +// Copyright (c) 2022 Samuel Venable +// +// 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_PROC_INFO_HPP +#define BOOST_PROCESS_V2_DETAIL_PROC_INFO_HPP + +#include +#include +#include + +#include +#include + +#if defined(BOOST_PROCESS_V2_WINDOWS) +#include +#include +#include +#include +extern "C" ULONG NTAPI RtlNtStatusToDosError(NTSTATUS Status); +#endif + +BOOST_PROCESS_V2_BEGIN_NAMESPACE + +namespace detail +{ + +namespace ext +{ + +#if defined(BOOST_PROCESS_V2_WINDOWS) +#if !defined(_MSC_VER) +#pragma pack(push, 8) +#else +#include +#endif + +/* CURDIR struct from: + https://github.com/processhacker/phnt/ + CC BY 4.0 licence */ + +typedef struct { + UNICODE_STRING DosPath; + HANDLE Handle; +} CURDIR; + +/* RTL_DRIVE_LETTER_CURDIR struct from: + https://github.com/processhacker/phnt/ + CC BY 4.0 licence */ + +typedef struct { + USHORT Flags; + USHORT Length; + ULONG TimeStamp; + STRING DosPath; +} RTL_DRIVE_LETTER_CURDIR; + +/* RTL_USER_PROCESS_PARAMETERS struct from: + https://github.com/processhacker/phnt/ + CC BY 4.0 licence */ + +typedef struct { + ULONG MaximumLength; + ULONG Length; + ULONG Flags; + ULONG DebugFlags; + HANDLE ConsoleHandle; + ULONG ConsoleFlags; + HANDLE StandardInput; + HANDLE StandardOutput; + HANDLE StandardError; + CURDIR CurrentDirectory; + UNICODE_STRING DllPath; + UNICODE_STRING ImagePathName; + UNICODE_STRING CommandLine; + PVOID Environment; + ULONG StartingX; + ULONG StartingY; + ULONG CountX; + ULONG CountY; + ULONG CountCharsX; + ULONG CountCharsY; + ULONG FillAttribute; + ULONG WindowFlags; + ULONG ShowWindowFlags; + UNICODE_STRING WindowTitle; + UNICODE_STRING DesktopInfo; + UNICODE_STRING ShellInfo; + UNICODE_STRING RuntimeData; + RTL_DRIVE_LETTER_CURDIR CurrentDirectories[32]; + ULONG_PTR EnvironmentSize; + ULONG_PTR EnvironmentVersion; + PVOID PackageDependencyData; + ULONG ProcessGroupId; + ULONG LoaderThreads; + UNICODE_STRING RedirectionDllName; + UNICODE_STRING HeapPartitionName; + ULONG_PTR DefaultThreadpoolCpuSetMasks; + ULONG DefaultThreadpoolCpuSetMaskCount; +} RTL_USER_PROCESS_PARAMETERS_EXTENDED; + +#if !defined(_MSC_VER) +#pragma pack(pop) +#else +#include +#endif +BOOST_PROCESS_V2_DECL std::wstring cwd_cmd_from_proc(HANDLE proc, int type, boost::system::error_code & ec); +BOOST_PROCESS_V2_DECL HANDLE open_process_with_debug_privilege(boost::process::v2::pid_type pid, boost::system::error_code & ec); +#endif + +} // namespace ext + +} // namespace detail + +BOOST_PROCESS_V2_END_NAMESPACE + +#if defined(BOOST_PROCESS_V2_HEADER_ONLY) + +#include + +#endif + +#endif // BOOST_PROCESS_V2_DETAIL_PROC_INFO_HPP + diff --git a/include/boost/process/v2/ext/env.hpp b/include/boost/process/v2/ext/env.hpp new file mode 100644 index 00000000..daf2eac8 --- /dev/null +++ b/include/boost/process/v2/ext/env.hpp @@ -0,0 +1,138 @@ +// Copyright (c) 2022 Klemens D. Morgenstern +// Copyright (c) 2022 Samuel Venable +// +// 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_ENV_HPP +#define BOOST_PROCESS_V2_ENV_HPP +#include +#include + +#include +#include +#include +#include + +#include + +BOOST_PROCESS_V2_BEGIN_NAMESPACE + +namespace detail +{ +namespace ext +{ + +#if defined(BOOST_PROCESS_V2_WINDOWS) +using native_env_handle_type = wchar_t *; +using native_env_iterator = wchar_t *; +#else +using native_env_handle_type = char *; +using native_env_iterator = char *; +#endif + +struct native_env_handle_deleter +{ + BOOST_PROCESS_V2_DECL void operator()(native_env_handle_type) const; +}; + +BOOST_PROCESS_V2_DECL native_env_iterator next(native_env_iterator nh); +BOOST_PROCESS_V2_DECL native_env_iterator find_end(native_env_iterator nh); +BOOST_PROCESS_V2_DECL const environment::char_type * dereference(native_env_iterator iterator); + +} // namespace ext +} // namespace detail + +namespace ext { + +struct env_view +{ + using native_handle_type = detail::ext::native_env_handle_type; + using value_type = environment::key_value_pair_view; + + env_view() = default; + env_view(env_view && nt) = default; + + native_handle_type native_handle() { return handle_.get(); } + + + struct iterator + { + using value_type = environment::key_value_pair_view; + using difference_type = int; + using reference = environment::key_value_pair_view; + using pointer = environment::key_value_pair_view; + using iterator_category = std::forward_iterator_tag; + + iterator() = default; + iterator(const iterator & ) = default; + iterator(const detail::ext::native_env_iterator &native_handle) : iterator_(native_handle) {} + + iterator & operator++() + { + iterator_ = detail::ext::next(iterator_); + return *this; + } + + iterator operator++(int) + { + auto last = *this; + iterator_ = detail::ext::next(iterator_); + return last; + } + environment::key_value_pair_view operator*() const + { + return detail::ext::dereference(iterator_); + } + + friend bool operator==(const iterator & l, const iterator & r) {return l.iterator_ == r.iterator_;} + friend bool operator!=(const iterator & l, const iterator & r) {return l.iterator_ != r.iterator_;} + + private: + detail::ext::native_env_iterator iterator_; + }; + + iterator begin() const {return iterator(handle_.get());} + iterator end() const {return iterator(detail::ext::find_end(handle_.get()));} + + private: + friend BOOST_PROCESS_V2_DECL env_view env(pid_type pid, error_code & ec); + #if defined(BOOST_PROCESS_V2_WINDOWS) + friend BOOST_PROCESS_V2_DECL env_view env(HANDLE handle, error_code & ec); + #endif + + std::unique_ptr::type, + detail::ext::native_env_handle_deleter> handle_; +}; + +/// Get the argument vector from a given pid +BOOST_PROCESS_V2_DECL env_view env(pid_type pid, error_code & ec); +BOOST_PROCESS_V2_DECL env_view env(pid_type pid); + +#if defined(BOOST_PROCESS_V2_WINDOWS) +BOOST_PROCESS_V2_DECL env_view env(HANDLE handle, error_code & ec); +BOOST_PROCESS_V2_DECL env_view env(HANDLE handle); +#endif + +template +BOOST_PROCESS_V2_DECL env_view env(basic_process_handle & handle, error_code & ec) +{ + return env(handle.native_handle(), ec); +} + +template +BOOST_PROCESS_V2_DECL env_view env(basic_process_handle & handle) +{ + return env(handle.native_handle()); +} + +} // namespace ext + +BOOST_PROCESS_V2_END_NAMESPACE + +#if defined(BOOST_PROCESS_V2_HEADER_ONLY) + +#include + +#endif +#endif // BOOST_PROCESS_V2_ENV_HPP diff --git a/include/boost/process/v2/ext/exe.hpp b/include/boost/process/v2/ext/exe.hpp new file mode 100644 index 00000000..8b48ddfd --- /dev/null +++ b/include/boost/process/v2/ext/exe.hpp @@ -0,0 +1,49 @@ +// Copyright (c) 2022 Klemens D. Morgenstern +// Copyright (c) 2022 Samuel Venable +// +// 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_EXE_HPP +#define BOOST_PROCESS_V2_EXE_HPP + +#include +#include + +#include +#include + +BOOST_PROCESS_V2_BEGIN_NAMESPACE + +namespace ext { + +/// Return the executable path from pid +BOOST_PROCESS_V2_DECL filesystem::path exe(pid_type pid, error_code & ec); +BOOST_PROCESS_V2_DECL filesystem::path exe(pid_type pid); + +#if defined(BOOST_PROCESS_V2_WINDOWS) +BOOST_PROCESS_V2_DECL filesystem::path exe(HANDLE handle, error_code & ec); +BOOST_PROCESS_V2_DECL filesystem::path exe(HANDLE handle); +#endif + +template +filesystem::path exe(basic_process_handle & handle, error_code & ec) +{ + return exe(handle.native_handle(), ec); +} + +template +filesystem::path exe(basic_process_handle & handle) +{ + return exe(handle.native_handle()); +} + +} // namespace ext + +BOOST_PROCESS_V2_END_NAMESPACE + +#if defined(BOOST_PROCESS_V2_HEADER_ONLY) +#include +#endif + +#endif // BOOST_PROCESS_V2_EXE_HPP + diff --git a/include/boost/process/v2/ext/impl/cmd.ipp b/include/boost/process/v2/ext/impl/cmd.ipp new file mode 100644 index 00000000..466a4151 --- /dev/null +++ b/include/boost/process/v2/ext/impl/cmd.ipp @@ -0,0 +1,468 @@ +// Copyright (c) 2022 Klemens D. Morgenstern +// Copyright (c) 2022 Samuel Venable +// +// 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_CMD_IPP +#define BOOST_PROCESS_V2_IMPL_CMD_IPP + +#include +#include +#include +#include +#include + +#if defined(BOOST_PROCESS_V2_WINDOWS) +#include +#include +#else +#include +#endif + +#if (defined(__linux__) || defined(__ANDROID__)) +#include +#endif + +#if defined(__FreeBSD__) +#include +#include +#include +#include +#include +#include +#endif + +#if (defined(__DragonFly__) || defined(__OpenBSD__)) +#include +#include +#include +#include +#include +#endif + +#if defined(__NetBSD__) +#include +#include +#include +#include +#endif + +#if defined(__sun) +#include +#include +#include +#include +#include +#endif + +BOOST_PROCESS_V2_BEGIN_NAMESPACE + +struct make_cmd_shell_ +{ +#if defined(BOOST_PROCESS_V2_WINDOWS) + static shell make(std::wstring data) + { + shell res; + res.input_ = res.buffer_ = std::move(data); + res.parse_(); + return res; + } +#else + static shell make(std::string data, + int argc, char ** argv, + void(*free_func)(int, char**)) + { + shell res; + res.argc_ = argc; + res.input_ = res.buffer_ = std::move(data); + res.argv_ = argv; + res.free_argv_ = free_func; + + return res; + } + + static shell clone(char ** cmd) + { + shell res; + res.argc_ = 0; + std::size_t str_lengths = 0; + for (auto c = cmd; *c != nullptr; c++) + { + res.argc_++; + str_lengths += (std::strlen(*c) + 1); + } + // yes, not the greatest solution. + std::string buffer; + res.buffer_.resize(str_lengths); + + res.argv_ = new char*[res.argc_ + 1]; + res.free_argv_ = +[](int argc, char ** argv) {delete[] argv;}; + res.argv_[res.argc_] = nullptr; + auto p = &buffer[sizeof(int) * (res.argc_) + 1]; + + for (int i = 0; i < res.argc_; i++) + { + const auto ln = std::strlen(cmd[i]); + res.argv_[i] = std::strcpy(p, cmd[i]); + p += (ln + 1); + } + + return res; + } + +#endif +}; + +namespace ext { + +#if defined(BOOST_PROCESS_V2_WINDOWS) + +shell cmd(HANDLE proc, error_code & ec) +{ + std::wstring buffer = boost::process::v2::detail::ext::cwd_cmd_from_proc(proc, 0/*=MEMCMD*/, ec); + + if (!ec) + return make_cmd_shell_::make(std::move(buffer)); + else + return {}; +} + +shell cmd(HANDLE proc) +{ + boost::system::error_code ec; + auto res = cmd(proc, ec); + if (ec) + detail::throw_error(ec, "cmd"); + return res; +} + +shell cmd(boost::process::v2::pid_type pid, boost::system::error_code & ec) +{ + struct del + { + void operator()(HANDLE h) + { + ::CloseHandle(h); + }; + }; + std::unique_ptr proc{detail::ext::open_process_with_debug_privilege(pid, ec)}; + if (proc == nullptr) + ec = detail::get_last_error(); + else + return cmd(proc.get(), ec); + +} + +#elif (defined(__APPLE__) && defined(__MACH__)) + +shell cmd(boost::process::v2::pid_type pid, boost::system::error_code & ec) +{ + int mib[3] = {CTL_KERN, KERN_ARGMAX, 0}; + int argmax = 0; + auto size = sizeof(argmax); + if (sysctl(mib, 2, &argmax, &size, nullptr, 0) == -1) + { + ec = detail::get_last_error(); + return {}; + } + + std::string procargs; + procargs.resize(argmax - 1); + mib[1] = KERN_PROCARGS; + mib[2] = pid; + + size = argmax; + + if (sysctl(mib, 3, &*procargs.begin(), &size, nullptr, 0) != 0) + { + ec = detail::get_last_error(); + return {}; + } + + int argc = *reinterpret_cast(procargs.data()); + auto itr = procargs.begin() + sizeof(argc); + + std::unique_ptr argv{new char*[argc + 1]}; + const auto end = procargs.end(); + + argv[argc] = nullptr; //args is a null-terminated list + + for (auto n = 0u; n <= argc; n++) + { + auto e = std::find(itr, end, '\0'); + if (e == end && n < argc) // something off + { + ec.assign(EINVAL, system_category()); + return {}; + } + argv[n] = &*itr; + itr = e + 1; // start searching start + } + + auto fr_func = +[](int argc, char ** argv) {delete [] argv;}; + + return make_cmd_shell_::make(std::move(procargs), argc, argv.release(), fr_func); +} + +#elif (defined(__linux__) || defined(__ANDROID__)) + +shell cmd(boost::process::v2::pid_type pid, boost::system::error_code & ec) +{ + std::string procargs; + procargs.resize(4096); + int f = ::open(("/proc/" + std::to_string(pid) + "/cmdline").c_str(), O_RDONLY); + + while (procargs.back() != EOF) + { + auto r = ::read(f, &*(procargs.end() - 4096), 4096); + if (r < 0) + { + ec = detail::get_last_error(); + ::close(f); + return {}; + } + if (r < 4096) // done! + { + procargs.resize(procargs.size() - 4096 + r); + break; + } + procargs.resize(procargs.size() + 4096); + } + ::close(f); + + if (procargs.back() == EOF) + procargs.pop_back(); + + auto argc = std::count(procargs.begin(), procargs.end(), '\0'); + + auto itr = procargs.begin(); + + std::unique_ptr argv{new char*[argc + 1]}; + const auto end = procargs.end(); + + argv[argc] = nullptr; //args is a null-terminated list + + + for (auto n = 0u; n <= argc; n++) + { + auto e = std::find(itr, end, '\0'); + if (e == end && n < argc) // something off + { + ec.assign(EINVAL, system_category()); + return {}; + } + argv[n] = &*itr; + itr = e + 1; // start searching start + } + + auto fr_func = +[](int argc, char ** argv) {delete [] argv;}; + + return make_cmd_shell_::make(std::move(procargs), argc, argv.release(), fr_func); +} + +#elif defined(__FreeBSD__) + +shell cmd(boost::process::v2::pid_type pid, boost::system::error_code & ec) +{ + struct cl_proc_stat + { + void operator()(struct procstat *proc_stat) + { + procstat_close(proc_stat); + } + }; + std::unique_ptr proc_stat{procstat_open_sysctl()}; + if (!proc_stat) + { + ec = detail::get_last_error(); + return {}; + } + + struct proc_info_close + { + struct procstat * proc_stat; + + void operator()(struct kinfo_proc * proc_info) + { + procstat_freeprocs(proc_stat, proc_info); + } + }; + + unsigned cntp; + std::unique_ptr proc_info{ + procstat_getprocs(proc_stat.get(), KERN_PROC_PID, pid, &cntp), + proc_info_close{proc_stat.get()}}; + + if (!proc_info) + { + ec = detail::get_last_error(); + return {}; + } + + char **cmd = procstat_getargv(proc_stat.get(), proc_info.get(), 0); + if (!cmd) + { + ec = detail::get_last_error(); + return {}; + } + struct free_argv + { + struct procstat * proc_stat; + ~free_argv() + { + procstat_freeargv(proc_stat); + } + }; + + return make_cmd_shell_::clone(cmd); +} + +#elif defined(__DragonFly__) + +shell cmd(boost::process::v2::pid_type pid, boost::system::error_code & ec) +{ + int cntp = 0; + kinfo_proc *proc_info = nullptr; + const char *nlistf, *memf; + nlistf = memf = "/dev/null"; + struct closer + { + void operator()(kvm_t * kd) + { + kvm_close(kd); + } + }; + + std::unique_ptr kd{kvm_openfiles(nlistf, memf, nullptr, O_RDONLY, nullptr)}; + if (!kd) {ec = detail::get_last_error(); return {};} + if ((proc_info = kvm_getprocs(kd.get(), KERN_PROC_PID, pid, &cntp))) + { + char **cmd = kvm_getargv(kd.get(), proc_info, 0); + if (cmd) + return make_cmd_shell_::clone(cmd); + else + ec = detail::get_last_error(); + } + else + ec = detail::get_last_error(); + return {}; +} + +#elif defined(__NetBSD__) + +shell cmd(boost::process::v2::pid_type pid, boost::system::error_code & ec) +{ + + std::vector vec; + int cntp = 0; + kinfo_proc2 *proc_info = nullptr; + struct closer + { + void operator()(kvm_t * kd) + { + kvm_close(kd); + } + }; + + std::unique_ptr kd{kvm_openfiles(nullptr, nullptr, nullptr, KVM_NO_FILES, nullptr)}; + + if (!kd) {ec = detail::get_last_error(); return vec;} + if ((proc_info = kvm_getproc2(kd.get(), KERN_PROC_PID, pid, sizeof(struct kinfo_proc2), &cntp))) + { + char **cmd = kvm_getargv2(kd.get(), proc_info, 0); + if (cmd) + return make_cmd_shell_::clone(cmd); + else + ec = detail::get_last_error(); + } + else + ec = detail::get_last_error(); + return vec; +} + +#elif defined(__OpenBSD__) + +shell cmd(boost::process::v2::pid_type pid, boost::system::error_code & ec) +{ + std::vector vec; + int cntp = 0; + kinfo_proc *proc_info = nullptr; + + struct closer + { + void operator()(kvm_t * kd) + { + kvm_close(kd); + } + }; + + std::unique_ptr kd{kvm_openfiles(nullptr, nullptr, nullptr, KVM_NO_FILES, nullptr)}; + if (!kd) {ec = detail::get_last_error(); return vec;} + if ((proc_info = kvm_getprocs(kd.get(), KERN_PROC_PID, pid, sizeof(struct kinfo_proc), &cntp))) + { + char **cmd = kvm_getargv(kd.get(), proc_info, 0); + if (cmd) + return make_cmd_shell_::clone(cmd); + else + ec = detail::get_last_error(); + } + else + ec = detail::get_last_error(); + kvm_close(kd); + return {}; +} + +#elif defined(__sun) + +shell cmd(boost::process::v2::pid_type pid, boost::system::error_code & ec) +{ + char **cmd = nullptr; + proc *proc_info = nullptr; + user *proc_user = nullptr; + kd = kvm_open(nullptr, nullptr, nullptr, O_RDONLY, nullptr); + if (!kd) {ec = detail::get_last_error(); return {};} + if ((proc_info = kvm_getproc(kd, pid))) + { + if ((proc_user = kvm_getu(kd, proc_info))) + { + if (!kvm_getcmd(kd, proc_info, proc_user, &cmd, nullptr)) + { + int argc = 0; + for (int i = 0; cmd[i] != nullptr; i++) + argc ++; + return make_cmd_shell_::make( + {}, argc, cmd, + +[](int, char ** argv) {::free(argv);}) + } + else + ec = detail::get_last_error(); + } + else + ec = detail::get_last_error(); + } + else + ec = detail::get_last_error(); + + kvm_close(kd); + return {}; +} + +#else +#error "Platform not supported" +#endif + +shell cmd(boost::process::v2::pid_type pid) +{ + boost::system::error_code ec; + auto res = cmd(pid, ec); + if (ec) + detail::throw_error(ec, "cmd"); + return res; +} + +} // namespace ext + +BOOST_PROCESS_V2_END_NAMESPACE + +#endif // BOOST_PROCESS_V2_IMPL_CMD_IPP + diff --git a/include/boost/process/v2/ext/impl/cwd.ipp b/include/boost/process/v2/ext/impl/cwd.ipp new file mode 100644 index 00000000..a4a4e45b --- /dev/null +++ b/include/boost/process/v2/ext/impl/cwd.ipp @@ -0,0 +1,234 @@ +// Copyright (c) 2022 Klemens D. Morgenstern +// Copyright (c) 2022 Samuel Venable +// +// 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_CWD_IPP +#define BOOST_PROCESS_V2_IMPL_CWD_IPP + +#include +#include +#include +#include +#include + +#include + +#if defined(BOOST_PROCESS_V2_WINDOWS) +#include +#else +#include +#endif + +#if (defined(__APPLE__) && defined(__MACH__)) +#include +#include +#endif + +#if (defined(BOOST_PROCESS_V2_WINDOWS) || defined(__linux__) || defined(__ANDROID__) || defined(__sun)) +#include +#endif + +#if defined(__FreeBSD__) +#include +#include +#include +#include +#include +#include +#endif + +#if (defined(__NetBSD__) || defined(__OpenBSD__)) +#include +#include +#endif + +#if defined(__DragonFly__) +#include +#include +#endif + +#ifdef BOOST_PROCESS_USE_STD_FS +namespace filesystem = std::filesystem; +#else +namespace filesystem = boost::filesystem; +#endif + +BOOST_PROCESS_V2_BEGIN_NAMESPACE + +namespace ext { + +#if defined(BOOST_PROCESS_V2_WINDOWS) + +filesystem::path cwd(HANDLE proc, boost::system::error_code & ec) +{ + auto buffer = boost::process::v2::detail::ext::cwd_cmd_from_proc(proc, 1/*=MEMCWD*/, ec); + if (!buffer.empty()) + return filesystem::canonical(buffer, ec); + else + ec = detail::get_last_error(); + return ""; +} + +filesystem::path cwd(boost::process::v2::pid_type pid, boost::system::error_code & ec) +{ + struct del + { + void operator()(HANDLE h) + { + ::CloseHandle(h); + }; + }; + std::unique_ptr proc{detail::ext::open_process_with_debug_privilege(pid, ec)}; + if (proc == nullptr) + ec = detail::get_last_error(); + else + return cwd(proc.get(), ec); + return {}; +} + +filesystem::path cwd(HANDLE proc) +{ + boost::system::error_code ec; + auto res = cwd(proc, ec); + if (ec) + detail::throw_error(ec, "cwd"); + return res; +} + +#elif (defined(__APPLE__) && defined(__MACH__)) + +filesystem::path cwd(boost::process::v2::pid_type pid, boost::system::error_code & ec) +{ + proc_vnodepathinfo vpi; + if (proc_pidinfo(pid, PROC_PIDVNODEPATHINFO, 0, &vpi, sizeof(vpi)) > 0) { + return filesystem::canonical(vpi.pvi_cdir.vip_path, ec); + } + ec = detail::get_last_error(); + return ""; +} + +#elif (defined(__linux__) || defined(__ANDROID__) || defined(__sun)) + +filesystem::path cwd(boost::process::v2::pid_type pid, boost::system::error_code & ec) +{ + return filesystem::canonical( + filesystem::path("/proc") / std::to_string(pid) / "cwd", ec); +} + +#elif defined(__FreeBSD__) + +// FIXME: Add error handling. +filesystem::path cwd(boost::process::v2::pid_type pid, boost::system::error_code & ec) +{ + filesystem::path path; + unsigned cntp = 0; + procstat *proc_stat = procstat_open_sysctl(); + if (proc_stat) { + kinfo_proc *proc_info = procstat_getprocs(proc_stat, KERN_PROC_PID, pid, &cntp); + if (proc_info) { + filestat_list *head = procstat_getfiles(proc_stat, proc_info, 0); + if (head) { + filestat *fst = nullptr; + STAILQ_FOREACH(fst, head, next) { + if (fst->fs_uflags & PS_FST_UFLAG_CDIR) + { + path = filesystem::canonical(fst->fs_path, ec); + } + } + procstat_freefiles(proc_stat, head); + } + else + ec = detail::get_last_error(); + procstat_freeprocs(proc_stat, proc_info); + } + else + ec = detail::get_last_error(); + procstat_close(proc_stat); + } + else + ec = detail::get_last_error(); + return path; +} + +#elif defined(__DragonFly__) + +// FIXME: Add error handling. +filesystem::path cwd(boost::process::v2::pid_type pid, boost::system::error_code & ec) +{ + filesystem::path path; + /* Probably the hackiest thing ever we are doing here, because the "official" API is broken OS-level. */ + FILE *fp = popen(("pos=`ans=\\`/usr/bin/fstat -w -p " + std::to_string(pid) + " | /usr/bin/sed -n 1p\\`; " + + "/usr/bin/awk -v ans=\"$ans\" 'BEGIN{print index(ans, \"INUM\")}'`; str=`/usr/bin/fstat -w -p " + + std::to_string(pid) + " | /usr/bin/sed -n 3p`; /usr/bin/awk -v str=\"$str\" -v pos=\"$pos\" " + + "'BEGIN{print substr(str, 0, pos + 4)}' | /usr/bin/awk 'NF{NF--};1 {$1=$2=$3=$4=\"\"; print" + + " substr($0, 5)'}").c_str(), "r"); + if (fp) + { + char buffer[PATH_MAX]; + if (fgets(buffer, sizeof(buffer), fp)) + { + std::string str = buffer; + std::size_t pos = str.find("\n", strlen(buffer) - 1); + if (pos != std::string::npos) + { + str.replace(pos, 1, ""); + } + path = filesystem::canonical(str.c_str(), ec); + } + else + ec = detail::get_last_error(); + pclose(fp); + } + else + ec = detail::get_last_error(); + return path; +} + +#elif (defined(__NetBSD__) || defined(__OpenBSD__)) + +filesystem::path cwd(boost::process::v2::pid_type pid, boost::system::error_code & ec) +{ + std::string path; +#if defined(__NetBSD__) + int mib[4] = {CTL_KERN, KERN_PROC_ARGS, pid, KERN_PROC_CWD}; + const std::size_t sz = 4; +#elif defined(__OpenBSD__) + int mib[3] = {CTL_KERN, KERN_PROC_CWD, pid}; + const std::size_t sz = 3; +#endif + std::size_t len = 0; + if (sysctl(mib, sz, nullptr, &len, nullptr, 0) == 0) + { + std::string strbuff; + strbuff.resize(len); + if (sysctl(mib, 4, &strbuff[0], &len, nullptr, 0) == 0) + { + filesystem::canonical(strbuff, ec); + } + else + ec = detail::get_last_error(); + } + + return ""; +} + +#else +#error "Platform not supported" +#endif + +filesystem::path cwd(boost::process::v2::pid_type pid) +{ + boost::system::error_code ec; + auto res = cwd(pid, ec); + if (ec) + detail::throw_error(ec, "cwd"); + return res; +} + +} // namespace ext + +BOOST_PROCESS_V2_END_NAMESPACE + +#endif // BOOST_PROCESS_V2_IMPL_CWD_IPP + diff --git a/include/boost/process/v2/ext/impl/env.ipp b/include/boost/process/v2/ext/impl/env.ipp new file mode 100644 index 00000000..7143d907 --- /dev/null +++ b/include/boost/process/v2/ext/impl/env.ipp @@ -0,0 +1,295 @@ +// 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_EXT_IMPL_ENV_IPP +#define BOOST_PROCESS_V2_EXT_IMPL_ENV_IPP + +#include +#include +#include +#include +#include + +#if defined(BOOST_PROCESS_V2_WINDOWS) +#include +#else +#include +#endif + +#if (defined(__linux__) || defined(__ANDROID__)) +#include +#endif + +BOOST_PROCESS_V2_BEGIN_NAMESPACE + +namespace detail { +namespace ext { + +#if defined(BOOST_PROCESS_V2_WINDOWS) + +void native_env_handle_deleter::operator()(native_env_handle_type h) const +{ + delete [] h; +} + +native_env_iterator next(native_env_iterator nh) +{ + while (*nh != L'\0') + nh ++; + return ++nh ; +} +native_env_iterator find_end(native_env_iterator nh) +{ + while (*nh - 1 != L'\0' && *nh != L'\0') + nh ++; + return nh ; +} + +const environment::char_type * dereference(native_env_iterator iterator) +{ + return iterator; +} + +#elif (defined(__linux__) || defined(__ANDROID__)) +//linux stores this as a blob with an EOF at the end + +void native_env_handle_deleter::operator()(native_env_handle_type h) const +{ + delete [] h; +} + +native_env_iterator next(native_env_iterator nh) +{ + while (*nh != '\0') + nh ++; + return ++nh ; +} +native_env_iterator find_end(native_env_iterator nh) +{ + while (*nh != EOF) + nh ++; + return nh ; +} + +const environment::char_type * dereference(native_env_iterator iterator) +{ + return iterator; +} + +#else + +void native_env_handle_deleter::operator()(native_env_handle_type h) const +{ + delete [] h; +} + +native_env_iterator next(native_env_iterator nh) +{ + while (*nh != '\0') + nh ++; + return ++nh ; +} +native_env_iterator find_end(native_env_iterator nh) +{ + while (*nh - 1 != '\0' && *nh != '\0') + nh ++; + return nh ; +} + +#endif + +} +} + +namespace ext +{ + +#if defined(BOOST_PROCESS_V2_WINDOWS) + +env_view env(HANDLE proc, boost::system::error_code & ec) +{ + wchar_t *buffer = nullptr; + PEB peb; + SIZE_T nRead = 0; + ULONG len = 0; + PROCESS_BASIC_INFORMATION pbi; + detail::ext::RTL_USER_PROCESS_PARAMETERS_EXTENDED upp; + + NTSTATUS status = 0; + PVOID buf = nullptr; + status = NtQueryInformationProcess(proc, ProcessBasicInformation, &pbi, sizeof(pbi), &len); + ULONG error = RtlNtStatusToDosError(status); + + if (error) + { + ec.assign(error, boost::system::system_category()); + return {}; + } + + if (!ReadProcessMemory(proc, pbi.PebBaseAddress, &peb, sizeof(peb), &nRead)) + { + ec = detail::get_last_error(); + return {}; + } + + if (!ReadProcessMemory(proc, peb.ProcessParameters, &upp, sizeof(upp), &nRead)) + { + ec = detail::get_last_error(); + return {}; + } + + env_view ev; + buf = upp.Environment; + len = (ULONG)upp.EnvironmentSize; + ev.handle_.reset(new wchar_t[len / 2 + 1]()); + + if (!ReadProcessMemory(proc, buf, ev.handle_.get(), len, &nRead)) + { + ec = detail::get_last_error(); + return {}; + } + + ev.handle_.get()[len / 2] = L'\0'; + return ev; +} + +env_view env(HANDLE handle) +{ + boost::system::error_code ec; + auto res = env(handle, ec); + if (ec) + detail::throw_error(ec, "env"); + return res; +} + +env_view env(boost::process::v2::pid_type pid, boost::system::error_code & ec) +{ + struct del + { + void operator()(HANDLE h) + { + ::CloseHandle(h); + }; + }; + std::unique_ptr proc{detail::ext::open_process_with_debug_privilege(pid, ec)}; + if (proc == nullptr) + ec = detail::get_last_error(); + else + return env(proc.get(), ec); + + return {}; +} + +#elif (defined(__APPLE___) || defined(__MACH__)) + +env_view env(boost::process::v2::pid_type pid, boost::system::error_code & ec) +{ + int mib[3] = {CTL_KERN, KERN_ARGMAX, 0}; + int argmax = 0; + auto size = sizeof(argmax); + if (sysctl(mib, 2, &argmax, &size, nullptr, 0) == -1) + { + ec = detail::get_last_error(); + return {}; + } + + std::string procargs; + procargs.resize(argmax - 1); + mib[1] = KERN_PROCARGS2; + mib[2] = pid; + + size = argmax; + + if (sysctl(mib, 3, &*procargs.begin(), &size, nullptr, 0) != 0) + { + ec = detail::get_last_error(); + return {}; + } + + memcpy(&nargs, &*procargs.begin(), sizeof(nargs)); + char *cp = &*procargs.begin() + sizeof(nargs); + + for (; cp < &&*procargs.begin()[size]; cp++) { + if (*cp == '\0') break; + } + + if (cp == &procargs[s]) { + return {}; + } + + for (; cp < &&*procargs.begin()[size]; cp++) { + if (*cp != '\0') break; + } + + if (cp == &&*procargs.begin()[size]) { + return {}; + } + + int i = 0; + char *sp = cp; + std::vector vec; + + while ((*sp != '\0' || i < nargs) && sp < &&*procargs.begin()[size]) { + if (i >= nargs) { + vec.push_back(*sp); + } + sp += 1; + } + + env_view ev; + ev.handle_.reset(new char[vec.size()]()); + std::copy(vec.begin(), vec.end(), ev.handle_.get()); + return ev; +} + +#elif (defined(__linux__) || defined(__ANDROID__)) + +env_view env(boost::process::v2::pid_type pid, boost::system::error_code & ec) +{ + std::size_t size = 0; + std::unique_ptr procargs{}; + + int f = ::open(("/proc/" + std::to_string(pid) + "/environ").c_str(), O_RDONLY); + + while (!procargs || procargs.get()[size - 1] != EOF) + { + std::unique_ptr buf{new char[size + 4096]}; + if (size > 0) + std::memmove(buf.get(), procargs.get(), size); + auto r = ::read(f, buf.get() + size, 4096); + if (r < 0) + { + ec = detail::get_last_error(); + ::close(f); + return {}; + } + procargs = std::move(buf); + size += r; + if (r < 4096) // done! + { + procargs.get()[size] = EOF; + break; + } + } + ::close(f); + + env_view ev; + ev.handle_ = std::move(procargs); + return ev; +} + +#endif + +env_view env(boost::process::v2::pid_type pid) +{ + boost::system::error_code ec; + auto res = env(pid, ec); + if (ec) + detail::throw_error(ec, "env"); + return res; +} +} +BOOST_PROCESS_V2_END_NAMESPACE + +#endif //BOOST_PROCESS_V2_EXT_IMPL_ENV_IPP diff --git a/include/boost/process/v2/ext/impl/exe.ipp b/include/boost/process/v2/ext/impl/exe.ipp new file mode 100644 index 00000000..fa772821 --- /dev/null +++ b/include/boost/process/v2/ext/impl/exe.ipp @@ -0,0 +1,192 @@ +// Copyright (c) 2022 Klemens D. Morgenstern +// Copyright (c) 2022 Samuel Venable +// +// 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_EXE_IPP +#define BOOST_PROCESS_V2_IMPL_EXE_IPP + +#include +#include +#include +#include +#include + +#include +#include + +#if defined(BOOST_PROCESS_V2_WINDOWS) +#include +#else +#include +#endif + +#if (defined(__APPLE__) && defined(__MACH__)) +#include +#include +#endif + +#if (defined(BOOST_PROCESS_V2_WINDOWS) || defined(__linux__) || defined(__ANDROID__) || defined(__sun)) +#include +#endif + +#if (defined(__FreeBSD__) || defined(__DragonFly__) || defined(__NetBSD__)) +#include +#include +#if !defined(__FreeBSD__) +#include +#endif +#endif + +#if defined(__OpenBSD__) +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif + +BOOST_PROCESS_V2_BEGIN_NAMESPACE + +namespace ext { + +#if defined(BOOST_PROCESS_V2_WINDOWS) + +filesystem::path exe(HANDLE process_handle) +{ + boost::system::error_code ec; + auto res = exe(process_handle, ec); + if (ec) + detail::throw_error(ec, "exe"); + return res; +} + + +filesystem::path exe(HANDLE proc, boost::system::error_code & ec) +{ + wchar_t buffer[MAX_PATH]; + // On input, specifies the size of the lpExeName buffer, in characters. + DWORD size = MAX_PATH; + if (QueryFullProcessImageNameW(proc, 0, buffer, &size)) + { + return filesystem::canonical(buffer, ec); + } + else + ec = detail::get_last_error(); + + return ""; +} + +filesystem::path exe(boost::process::v2::pid_type pid, boost::system::error_code & ec) +{ + if (pid == GetCurrentProcessId()) + { + wchar_t buffer[MAX_PATH]; + if (GetModuleFileNameW(nullptr, buffer, sizeof(buffer))) + { + return filesystem::canonical(buffer, ec); + } + } + else + { + struct del + { + void operator()(HANDLE h) + { + ::CloseHandle(h); + }; + }; + std::unique_ptr proc{detail::ext::open_process_with_debug_privilege(pid, ec)}; + if (proc == nullptr) + ec = detail::get_last_error(); + else + return exe(proc.get(), ec); + } + return ""; +} + +#elif (defined(__APPLE__) && defined(__MACH__)) + +filesystem::path exe(boost::process::v2::pid_type pid, boost::system::error_code & ec) +{ + char buffer[PROC_PIDPATHINFO_MAXSIZE]; + if (proc_pidpath(pid, buffer, sizeof(buffer)) > 0) + { + return filesystem::canonical(buffer, ec); + } + ec = detail::get_last_error(); + return ""; +} + +#elif (defined(__linux__) || defined(__ANDROID__) || defined(__sun)) + +filesystem::path exe(boost::process::v2::pid_type pid, boost::system::error_code & ec) +{ +#if (defined(__linux__) || defined(__ANDROID__)) + return filesystem::canonical( + filesystem::path("/proc") / std::to_string(pid) / "exe", ec + ); +#elif defined(__sun) + return fileystem::canonical( + filesystem::path("/proc") / std::to_string(pid) / "path/a.out" + ); +#endif +} + +#elif (defined(__FreeBSD__) || defined(__DragonFly__) || defined(__NetBSD__)) + +filesystem::path exe(boost::process::v2::pid_type pid, boost::system::error_code & ec) +{ +#if (defined(__FreeBSD__) || defined(__DragonFly__)) + int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, pid}; +#elif defined(__NetBSD__) + int mib[4] = {CTL_KERN, KERN_PROC_ARGS, pid, KERN_PROC_PATHNAME}; +#endif + std::size_t len = 0; + if (sysctl(mib, 4, nullptr, &len, nullptr, 0) == 0) + { + std::string strbuff; + strbuff.resize(len); + if (sysctl(mib, 4, &strbuff[0], &len, nullptr, 0) == 0) + { + return filesystem::canonical(strbuff, ec); + } + } + + ec = detail::get_last_error(); + return ""; +} + +#elif defined(__OpenBSD__) + +filesystem::path exe(boost::process::v2::pid_type pid, boost::system::error_code & ec) +{ + ec.assign(ENOTSUP, boost::system::system_category()); + return ""; +} + +#else +#error "Platform not supported" +#endif + +filesystem::path exe(boost::process::v2::pid_type pid) +{ + boost::system::error_code ec; + auto res = exe(pid, ec); + if (ec) + detail::throw_error(ec, "exe"); + return res; +} + + +} // namespace ext + +BOOST_PROCESS_V2_END_NAMESPACE + +#endif // BOOST_PROCESS_V2_IMPL_EXE_IPP + diff --git a/include/boost/process/v2/impl/pid.ipp b/include/boost/process/v2/impl/pid.ipp index 1ac40acb..37dae095 100644 --- a/include/boost/process/v2/impl/pid.ipp +++ b/include/boost/process/v2/impl/pid.ipp @@ -1,4 +1,5 @@ // Copyright (c) 2022 Klemens D. Morgenstern +// Copyright (c) 2022 Samuel Venable // // 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) @@ -6,14 +7,56 @@ #define BOOST_PROCESS_V2_IMPL_PID_IPP #include +#include +#include #include #if defined(BOOST_PROCESS_V2_WINDOWS) #include +#include #else #include #endif +#if (defined(__APPLE__) && defined(__MACH__)) +#include +#include +#endif + +#if (defined(__linux__) || defined(__ANDROID__)) +#include +#endif + +#if defined(__FreeBSD__) +#include +#include +#include +#include +#endif + +#if (defined(__DragonFly__) || defined(__OpenBSD__)) +#include +#include +#include +#include +#include +#endif + +#if defined(__NetBSD__) +#include +#include +#include +#include +#endif + +#if defined(__sun) +#include +#include +#include +#include +#include +#endif + BOOST_PROCESS_V2_BEGIN_NAMESPACE #if defined(BOOST_PROCESS_V2_WINDOWS) @@ -22,6 +65,700 @@ pid_type current_pid() {return ::GetCurrentProcessId();} pid_type current_pid() {return ::getpid();} #endif +#if defined(BOOST_PROCESS_V2_WINDOWS) + +std::vector all_pids(boost::system::error_code & ec) +{ + std::vector vec; + HANDLE hp = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + if (!hp) + { + ec = detail::get_last_error(); + return vec; + } + PROCESSENTRY32 pe; + pe.dwSize = sizeof(PROCESSENTRY32); + if (Process32First(hp, &pe)) + { + do + { + vec.push_back(pe.th32ProcessID); + } while (Process32Next(hp, &pe)); + } + CloseHandle(hp); + return vec; +} + +pid_type parent_pid(pid_type pid, boost::system::error_code & ec) +{ + pid_type ppid = static_cast(-1); + HANDLE hp = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + if (!hp) + { + ec = detail::get_last_error(); + return ppid; + } + PROCESSENTRY32 pe; + pe.dwSize = sizeof(PROCESSENTRY32); + if (Process32First(hp, &pe)) + { + do + { + if (pe.th32ProcessID == pid) + { + ppid = pe.th32ParentProcessID; + break; + } + } + while (Process32Next(hp, &pe)); + } + CloseHandle(hp); + return ppid; +} + +std::vector child_pids(pid_type pid, boost::system::error_code & ec) +{ + std::vector vec; + HANDLE hp = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + if (!hp) + { + ec = detail::get_last_error(); + return vec; + } + PROCESSENTRY32 pe; + pe.dwSize = sizeof(PROCESSENTRY32); + if (Process32First(hp, &pe)) + { + do + { + if (pe.th32ParentProcessID == pid) + { + vec.push_back(pe.th32ProcessID); + } + } + while (Process32Next(hp, &pe)); + } + CloseHandle(hp); + return vec; +} + +#elif (defined(__APPLE__) && defined(__MACH__)) + +std::vector all_pids(boost::system::error_code & ec) +{ + std::vector vec; + vec.reserve(proc_listpids(PROC_ALL_PIDS, 0, nullptr, 0)); + if (proc_listpids(PROC_ALL_PIDS, 0, &vec[0], sizeof(pid_type) * vec.size())) + { + ec = detail::get_last_error(); + return {}; + } + auto itr = std::partition(vec.begin(), vec.end(), [](pid_type pt) {return pt != 0;}); + vec.erase(itr, vec.end()); + std::reverse(vec.begin(), vec.end()); + return vec; +} + +pid_type parent_pid(pid_type pid, boost::system::error_code & ec) +{ + pid_type ppid = static_cast(-1); + proc_bsdinfo proc_info; + if (proc_pidinfo(pid, PROC_PIDTBSDINFO, 0, &proc_info, sizeof(proc_info)) <= 0) + { + ec = detail::get_last_error(); + return ppid; + } + else + ppid = proc_info.pbi_ppid; + return ppid; +} + +std::vector child_pids(pid_type pid, boost::system::error_code & ec) +{ + std::vector vec; + vec.reserve(proc_listpids(PROC_PPID_ONLY, (uint32_t)pid, nullptr, 0)); + if (proc_listpids(PROC_PPID_ONLY, (uint32_t)pid, &proc_info[0], sizeof(pid_type) * cntp)) + { + ec = detail::get_last_error(); + return {}; + } + auto itr = std::partition(vec.begin(), vec.end(), [](pid_type pt) {return pt != 0;}); + vec.erase(itr, vec.end()); + std::reverse(vec.begin(), vec.end()); + return vec; +} + +#elif (defined(__linux__) || defined(__ANDROID__)) + +std::vector all_pids(boost::system::error_code & ec) +{ + std::vector vec; + DIR *proc = opendir("/proc"); + if (!proc) + { + ec = detail::get_last_error(); + return vec; + } + struct dirent *ent = nullptr; + while ((ent = readdir(proc))) + { + if (!isdigit(*ent->d_name)) + continue; + vec.push_back(atoi(ent->d_name)); + } + closedir(proc); + return vec; +} + +pid_type parent_pid(pid_type pid, boost::system::error_code & ec) +{ + pid_type ppid = static_cast(-1); + char buffer[BUFSIZ]; + sprintf(buffer, "/proc/%d/stat", pid); + FILE *stat = fopen(buffer, "r"); + if (!stat) + { + ec = detail::get_last_error(); + return ppid; + } + else + { + std::size_t size = fread(buffer, sizeof(char), sizeof(buffer), stat); + if (size > 0) + { + char *token = nullptr; + if ((token = strtok(buffer, " "))) + { + if ((token = strtok(nullptr, " "))) + { + if ((token = strtok(nullptr, " "))) + { + if ((token = strtok(nullptr, " "))) + { + ppid = (pid_type)strtoul(token, nullptr, 10); + } + } + } + } + if (!token) + { + fclose(stat); + ec = detail::get_last_error(); + return ppid; + } + } + fclose(stat); + } + return ppid; +} + +std::vector child_pids(pid_type pid, boost::system::error_code & ec) +{ + std::vector vec; + std::vector pids = all_pids(ec); + if (!pids.empty()) + vec.reserve(pids.size()); + for (std::size_t i = 0; i < pids.size(); i++) + { + pid_type ppid = parent_pid(pids[i], ec); + if (ppid != -1 && ppid == pid) + { + vec.push_back(pids[i]); + } + } + return vec; +} + +#elif defined(__FreeBSD__) + +std::vector all_pids(boost::system::error_code & ec) +{ + std::vector vec; + int cntp = 0; + kinfo_proc *proc_info = kinfo_getallproc(&cntp); + if (proc_info) + { + vec.reserve(cntp); + for (int i = 0; i < cntp; i++) + vec.push_back(proc_info[i].ki_pid); + free(proc_info); + } + else + ec = detail::get_last_error(); + return vec; +} + +pid_type parent_pid(pid_type pid, boost::system::error_code & ec) +{ + pid_type ppid = static_cast(-1); + kinfo_proc *proc_info = kinfo_getproc(pid); + if (proc_info) + { + ppid = proc_info->ki_ppid; + free(proc_info); + } + else + ec = detail::get_last_error(); + return ppid; +} + +std::vector child_pids(pid_type pid, boost::system::error_code & ec) +{ + std::vector vec; + int cntp = 0; + kinfo_proc *proc_info = kinfo_getallproc(&cntp); + if (proc_info) + { + vec.reserve(cntp); + for (int i = 0; i < cntp; i++) + { + if (proc_info[i].ki_ppid == pid) + { + vec.push_back(proc_info[i].ki_pid); + } + } + free(proc_info); + } + else + ec = detail::get_last_error(); + return vec; +} + +#elif defined(__DragonFly__) + +std::vector all_pids(boost::system::error_code & ec) +{ + std::vector vec; + int cntp = 0; + kinfo_proc *proc_info = nullptr; + const char *nlistf, *memf; + nlistf = memf = "/dev/null"; + struct closer + { + void operator()(kvm_t * kd) + { + kvm_close(kd); + } + }; + + std::unique_ptr kd{kvm_openfiles(nlistf, memf, nullptr, O_RDONLY, nullptr)}; + if (!kd) + { + ec = detail::get_last_error(); + return vec; + } + if ((proc_info = kvm_getprocs(kd.get(), KERN_PROC_ALL, 0, &cntp))) + { + vec.reserve(cntp); + for (int i = 0; i < cntp; i++) + if (proc_info[i].kp_pid >= 0) + vec.push_back(proc_info[i].kp_pid); + } + else + ec = detail::get_last_error(); + return vec; +} + +pid_type parent_pid(pid_type pid, boost::system::error_code & ec) +{ + pid_type ppid = static_cast(-1); + int cntp = 0; + kinfo_proc *proc_info = nullptr; + const char *nlistf, *memf; + nlistf = memf = "/dev/null"; + struct closer + { + void operator()(kvm_t * kd) + { + kvm_close(kd); + } + }; + + std::unique_ptr kd{kvm_openfiles(nlistf, memf, nullptr, O_RDONLY, nullptr)}; + if (!kd) + { + ec = detail::get_last_error(); + return ppid; + } + if ((proc_info = kvm_getprocs(kd.get(), KERN_PROC_PID, pid, &cntp))) + { + if (proc_info->kp_ppid >= 0) + { + ppid = proc_info->kp_ppid; + } + } + else + ec = detail::get_last_error(); + return ppid; +} + +std::vector child_pids(pid_type pid, boost::system::error_code & ec) +{ + std::vector vec; + int cntp = 0; + kinfo_proc *proc_info = nullptr; + const char *nlistf, *memf; + nlistf = memf = "/dev/null"; + struct closer + { + void operator()(kvm_t * kd) + { + kvm_close(kd); + } + }; + + std::unique_ptr kd{kvm_openfiles(nlistf, memf, nullptr, O_RDONLY, nullptr)}; + if (!kd) + { + ec = detail::get_last_error(); + return vec; + } + if ((proc_info = kvm_getprocs(kd.get(), KERN_PROC_ALL, 0, &cntp))) + { + vec.reserve(cntp); + for (int i = 0; i < cntp; i++) + { + if (proc_info[i].kp_pid >= 0 && proc_info[i].kp_ppid >= 0 && proc_info[i].kp_ppid == pid) + { + vec.push_back(proc_info[i].kp_pid); + } + } + } + else + ec = detail::get_last_error(); + return vec; +} + +#elif defined(__NetBSD__) + +std::vector all_pids(boost::system::error_code & ec) +{ + std::vector vec; + int cntp = 0; + kinfo_proc2 *proc_info = nullptr; + struct closer + { + void operator()(kvm_t * kd) + { + kvm_close(kd); + } + }; + + std::unique_ptr kd{kvm_openfiles(nullptr, nullptr, nullptr, KVM_NO_FILES, nullptr)}; + if (!kd) + { + ec = detail::get_last_error(); + return vec; + } + if ((proc_info = kvm_getproc2(kd.get(), KERN_PROC_ALL, 0, sizeof(struct kinfo_proc2), &cntp))) + { + vec.reserve(cntp); + for (int i = cntp - 1; i >= 0; i--) + { + vec.push_back(proc_info[i].p_pid); + } + } + else + ec = detail::get_last_error(); + return vec; +} + +pid_type parent_pid(pid_type pid, boost::system::error_code & ec) +{ + pid_type ppid = static_cast(-1); + int cntp = 0; + kinfo_proc2 *proc_info = nullptr; + struct closer + { + void operator()(kvm_t * kd) + { + kvm_close(kd); + } + }; + + std::unique_ptr kd{kvm_openfiles(nullptr, nullptr, nullptr, KVM_NO_FILES, nullptr)}; + if (!kd) + { + ec = detail::get_last_error(); + return ppid; + } + if ((proc_info = kvm_getproc2(kd.get(), KERN_PROC_PID, pid, sizeof(struct kinfo_proc2), &cntp))) + { + ppid = proc_info->p_ppid; + } + else + ec = detail::get_last_error(); + return ppid; +} + +std::vector child_pids(pid_type pid, boost::system::error_code & ec) +{ + std::vector vec; + int cntp = 0; + kinfo_proc2 *proc_info = nullptr; + struct closer + { + void operator()(kvm_t * kd) + { + kvm_close(kd); + } + }; + + std::unique_ptr kd{kvm_openfiles(nullptr, nullptr, nullptr, KVM_NO_FILES, nullptr)}; + if (!kd) + { + ec = detail::get_last_error(); + return vec; + } + if ((proc_info = kvm_getproc2(kd.get(), KERN_PROC_ALL, 0, sizeof(struct kinfo_proc2), &cntp))) + { + vec.reserve(cntp); + for (int i = cntp - 1; i >= 0; i--) + { + if (proc_info[i].p_ppid == pid) + { + vec.push_back(proc_info[i].p_pid); + } + } + } + else + ec = detail::get_last_error(); + return vec; +} + +#elif defined(__OpenBSD__) + +std::vector all_pids(boost::system::error_code & ec) +{ + std::vector vec; + int cntp = 0; + kinfo_proc *proc_info = nullptr; + struct closer + { + void operator()(kvm_t * kd) + { + kvm_close(kd); + } + }; + + std::unique_ptr kd{kvm_openfiles(nullptr, nullptr, nullptr, KVM_NO_FILES, nullptr)}; + if (!kd) + { + ec = detail::get_last_error(); + return vec; + } + if ((proc_info = kvm_getprocs(kd.get(), KERN_PROC_ALL, 0, sizeof(struct kinfo_proc), &cntp))) + { + vec.reserve(cntp); + for (int i = cntp - 1; i >= 0; i--) + { + if (proc_info[i].kp_pid >= 0) + { + vec.push_back(proc_info[i].kp_pid); + } + } + } + else + ec = detail::get_last_error(); + return vec; +} + +pid_type parent_pid(pid_type pid, boost::system::error_code & ec) +{ + pid_type ppid = static_cast(-1); + int cntp = 0; + kinfo_proc *proc_info = nullptr; + struct closer + { + void operator()(kvm_t * kd) + { + kvm_close(kd); + } + }; + + std::unique_ptr kd{kvm_openfiles(nullptr, nullptr, nullptr, KVM_NO_FILES, nullptr)}; + if (!kd) + { + ec = detail::get_last_error(); + return ppid; + } + if ((proc_info = kvm_getprocs(kd.get(), KERN_PROC_PID, pid, sizeof(struct kinfo_proc), &cntp))) + { + ppid = proc_info->p_ppid; + } + else + ec = detail::get_last_error(); + return ppid; +} + +std::vector child_pids(pid_type pid, boost::system::error_code & ec) +{ + std::vector vec; + int cntp = 0; + kinfo_proc *proc_info = nullptr; + struct closer + { + void operator()(kvm_t * kd) + { + kvm_close(kd); + } + }; + + std::unique_ptr kd{kvm_openfiles(nullptr, nullptr, nullptr, KVM_NO_FILES, nullptr)}; + if (!kd) + { + ec = detail::get_last_error(); + return vec; + } + if ((proc_info = kvm_getprocs(kd.get(), KERN_PROC_ALL, 0, sizeof(struct kinfo_proc), &cntp))) + { + vec.reserve(cntp); + for (int i = cntp - 1; i >= 0; i--) + { + if (proc_info[i].p_ppid == pid) + { + vec.push_back(proc_info[i].p_pid); + } + } + } + else + ec = detail::get_last_error(); + return vec; +} + + +#elif defined(__sun) + +std::vector all_pids(boost::system::error_code & ec) +{ + std::vector vec; + struct pid cur_pid; + proc *proc_info = nullptr; + struct closer + { + void operator()(kvm_t * kd) + { + kvm_close(kd); + } + }; + + std::unique_ptr kd{kvm_open(nullptr, nullptr, nullptr, O_RDONLY, nullptr)}; + if (!kd) + { + ec = detail::get_last_error(); + return vec; + } + while ((proc_info = kvm_nextproc(kd))) + { + if (kvm_kread(kd, (std::uintptr_t)proc_info->p_pidp, &cur_pid, sizeof(cur_pid)) != -1) + { + vec.insert(vec.begin(), cur_pid.pid_id); + } + else + { + ec = detail::get_last_error(); + break; + } + } + return vec; +} + +pid_type parent_pid(pid_type pid, boost::system::error_code & ec) +{ + pid_type ppid = static_cast(-1); + proc *proc_info = nullptr; + struct closer + { + void operator()(kvm_t * kd) + { + kvm_close(kd); + } + }; + + std::unique_ptr kd{kvm_open(nullptr, nullptr, nullptr, O_RDONLY, nullptr)}; + if (!kd) + { + ec = detail::get_last_error(); + return ppid; + } + if ((proc_info = kvm_getproc(kd, pid))) + { + ppid = proc_info->p_ppid; + } + else + ec = detail::get_last_error(); + return ppid; +} + +std::vector child_pids(pid_type pid, boost::system::error_code & ec) +{ + std::vector vec; + struct pid cur_pid; + proc *proc_info = nullptr; + struct closer + { + void operator()(kvm_t * kd) + { + kvm_close(kd); + } + }; + + std::unique_ptr kd{kvm_open(nullptr, nullptr, nullptr, O_RDONLY, nullptr); + if (!kd) + { + ec = detail::get_last_error(); + return vec; + } + while ((proc_info = kvm_nextproc(kd))) + { + if (proc_info->p_ppid == pid) + { + if (kvm_kread(kd, (std::uintptr_t)proc_info->p_pidp, &cur_pid, sizeof(cur_pid)) != -1) + { + vec.insert(vec.begin(), cur_pid.pid_id); + } + else + { + ec = detail::get_last_error(); + break; + } + } + } + return vec; +} + +#else +#error "Platform not supported" +#endif + +std::vector all_pids() +{ + boost::system::error_code ec; + auto res = all_pids(ec); + if (ec) + detail::throw_error(ec, "all_pids"); + return res; +} + +pid_type parent_pid(pid_type pid) +{ + boost::system::error_code ec; + auto res = parent_pid(pid, ec); + if (ec) + detail::throw_error(ec, "parent_pid"); + return res; +} + +std::vector child_pids(pid_type pid) +{ + boost::system::error_code ec; + auto res = child_pids(pid, ec); + if (ec) + detail::throw_error(ec, "child_pids"); + return res; +} + BOOST_PROCESS_V2_END_NAMESPACE -#endif //BOOST_PROCESS_V2_IMPL_PID_IPP +#endif // BOOST_PROCESS_V2_IMPL_PID_IPP + diff --git a/include/boost/process/v2/impl/shell.ipp b/include/boost/process/v2/impl/shell.ipp index 1870297b..7a54f038 100644 --- a/include/boost/process/v2/impl/shell.ipp +++ b/include/boost/process/v2/impl/shell.ipp @@ -96,21 +96,23 @@ void shell::parse_() { argc_ = static_cast(we.we_wordc); argv_ = we.we_wordv; - reserved_ = static_cast(we.we_offs); } + + free_argv_ = +[](int argc, char ** argv) + { + wordexp_t we{ + .we_wordc = static_cast(argc), + .we_wordv = argv, + .we_offs = 0 + }; + wordfree(&we); + }; } shell::~shell() { - if (argv_ != nullptr) - { - wordexp_t we{ - .we_wordc = static_cast(argc_), - .we_wordv = argv_, - .we_offs = static_cast(reserved_) - }; - wordfree(&we); - } + if (argv_ != nullptr && free_argv_) + free_argv_(argc_, argv_); } auto shell::args() const -> args_type diff --git a/include/boost/process/v2/pid.hpp b/include/boost/process/v2/pid.hpp index 48a5e143..6f7baa04 100644 --- a/include/boost/process/v2/pid.hpp +++ b/include/boost/process/v2/pid.hpp @@ -1,4 +1,5 @@ // Copyright (c) 2022 Klemens D. Morgenstern +// Copyright (c) 2022 Samuel Venable // // 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) @@ -6,6 +7,7 @@ #define BOOST_PROCESS_V2_PID_HPP #include +#include BOOST_PROCESS_V2_BEGIN_NAMESPACE @@ -14,7 +16,6 @@ BOOST_PROCESS_V2_BEGIN_NAMESPACE //An integral type representing a process id. typedef implementation_defined pid_type; - #else #if defined(BOOST_PROCESS_V2_WINDOWS) @@ -28,14 +29,38 @@ typedef int pid_type; #endif #endif +#if (defined(BOOST_PROCESS_V2_WINDOWS) || defined(__FreeBSD__) || defined(__DragonFly__) || defined(__NetBSD__) || defined(__sun)) +constexpr static pid_type root_pid = 0; +#elif (defined(__APPLE__) && defined(__MACH__) || defined(__linux__) || defined(__ANDROID__) || defined(__OpenBSD__)) +constexpr static pid_type root_pid = 1; +#endif + /// Get the process id of the current process. BOOST_PROCESS_V2_DECL pid_type current_pid(); +/// List all available pids. +BOOST_PROCESS_V2_DECL std::vector all_pids(boost::system::error_code & ec); + +/// List all available pids. +BOOST_PROCESS_V2_DECL std::vector all_pids(); + +// return parent pid of pid. +BOOST_PROCESS_V2_DECL pid_type parent_pid(pid_type pid, boost::system::error_code & ec); + +// return parent pid of pid. +BOOST_PROCESS_V2_DECL pid_type parent_pid(pid_type pid); + +// return child pids of pid. +BOOST_PROCESS_V2_DECL std::vector child_pids(pid_type pid, boost::system::error_code & ec); + +// return child pids of pid. +BOOST_PROCESS_V2_DECL std::vector child_pids(pid_type pid); + BOOST_PROCESS_V2_END_NAMESPACE #if defined(BOOST_PROCESS_V2_HEADER_ONLY) #include #endif +#endif // BOOST_PROCESS_V2_PID_HPP -#endif //BOOST_PROCESS_V2_PID_HPP diff --git a/include/boost/process/v2/process.hpp b/include/boost/process/v2/process.hpp index 798d1394..6c987a8b 100644 --- a/include/boost/process/v2/process.hpp +++ b/include/boost/process/v2/process.hpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #if defined(BOOST_PROCESS_V2_STANDALONE) @@ -214,6 +215,37 @@ struct basic_process process_handle_.request_exit(ec); } + /// Send the process a signal requesting it to stop. This may rely on undocmented functions. + void suspend(error_code &ec) + { + process_handle_.suspend(ec); + } + + /// Send the process a signal requesting it to stop. This may rely on undocmented functions. + void suspend() + { + error_code ec; + suspend(ec); + if (ec) + detail::throw_error(ec, "suspend"); + } + + + /// Send the process a signal requesting it to resume. This may rely on undocmented functions. + void resume(error_code &ec) + { + process_handle_.resume(ec); + } + + /// Send the process a signal requesting it to resume. This may rely on undocmented functions. + void resume() + { + error_code ec; + suspend(ec); + if (ec) + detail::throw_error(ec, "resume"); + } + /// Throwing @overload void terminate(native_exit_code_type &exit_code, error_code & ec) void terminate() { @@ -368,4 +400,4 @@ typedef basic_process<> process; BOOST_PROCESS_V2_END_NAMESPACE -#endif //BOOST_PROCESS_V2_PROCESS_HPP \ No newline at end of file +#endif //BOOST_PROCESS_V2_PROCESS_HPP diff --git a/include/boost/process/v2/shell.hpp b/include/boost/process/v2/shell.hpp index b58d2b41..a271f36c 100644 --- a/include/boost/process/v2/shell.hpp +++ b/include/boost/process/v2/shell.hpp @@ -79,8 +79,10 @@ struct shell : 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)) + argv_(boost::exchange(lhs.argv_, nullptr)) +#if defined(BOOST_PROCESS_V2_POSIX) + , free_argv_(boost::exchange(lhs.free_argv_, nullptr)) +#endif { } shell& operator=(shell && lhs) noexcept @@ -90,7 +92,9 @@ struct shell input_ = std::move(lhs.input_); argc_ = boost::exchange(lhs.argc_, 0); argv_ = boost::exchange(lhs.argv_, nullptr); - reserved_ = boost::exchange(lhs.reserved_, 0); +#if defined(BOOST_PROCESS_V2_POSIX) + free_argv_ = boost::exchange(lhs.free_argv_, nullptr); +#endif return *this; } @@ -116,6 +120,9 @@ struct shell BOOST_PROCESS_V2_DECL ~shell(); private: + + friend struct make_cmd_shell_; + BOOST_PROCESS_V2_DECL void parse_(); // storage in case we need a conversion @@ -124,7 +131,10 @@ struct shell // impl details int argc_ = 0; char_type ** argv_ = nullptr; - int reserved_ = 0; + +#if defined(BOOST_PROCESS_V2_POSIX) + void(*free_argv_)(int, char **); +#endif }; diff --git a/include/boost/process/v2/src.hpp b/include/boost/process/v2/src.hpp index 9c3b3fb7..86470a4d 100644 --- a/include/boost/process/v2/src.hpp +++ b/include/boost/process/v2/src.hpp @@ -15,6 +15,11 @@ #include #include +#include +#include +#include +#include +#include #include #include #include diff --git a/test/v2/CMakeLists.txt b/test/v2/CMakeLists.txt index 51a1101e..4a27e6a7 100644 --- a/test/v2/CMakeLists.txt +++ b/test/v2/CMakeLists.txt @@ -1,11 +1,13 @@ enable_testing() add_library(boost_process_v2_test_impl test_impl.cpp) -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) if (WIN32) target_compile_definitions(boost_process_v2_test_impl PUBLIC WIN32_LEAN_AND_MEAN=1) + target_link_libraries(boost_process_v2_test_impl Boost::process Boost::unit_test_framework Boost::process Ntdll) +else() + target_link_libraries(boost_process_v2_test_impl Boost::process Boost::unit_test_framework Boost::process) endif() function(boost_process_v2_standalone_test name) @@ -31,6 +33,9 @@ function(boost_process_v2_test_with_target name) add_dependencies(boost_process_v2_${name} boost_process_v2_test_target) add_test(NAME boost_process_v2_${name} COMMAND $ -- $) + + endfunction() -boost_process_v2_test_with_target(process) \ No newline at end of file +boost_process_v2_test_with_target(process) +boost_process_v2_test_with_target(ext) \ No newline at end of file diff --git a/test/v2/Jamfile.jam b/test/v2/Jamfile.jam index ef4a0679..f15cc1db 100644 --- a/test/v2/Jamfile.jam +++ b/test/v2/Jamfile.jam @@ -21,7 +21,12 @@ project : requirements msvc:_CRT_SECURE_NO_DEPRECATE msvc:/bigobj windows:WIN32_LEAN_AND_MEAN + windows:_WIN32_WINNT=0x0601 linux:-lpthread + freebsd:-lutil + freebsd:-lkvm + freebsd:-lprocstat + bsd:-lkvm NT,cw:ws2_32 NT,gcc:ws2_32 BOOST_PROCESS_V2_SEPARATE_COMPILATION=1 @@ -46,6 +51,8 @@ lib test_impl : test_impl.cpp filesystem : static windows:shell32 windows:user32 + windows:Ntdll + windows:Advapi32 ; test-suite standalone : @@ -59,5 +66,6 @@ test-suite standalone : 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 : no windows:yes windows:Advapi32 ] + [ run ext.cpp test_impl : --log_level=all --catch_system_errors=no -- : target ] ; diff --git a/test/v2/ext.cpp b/test/v2/ext.cpp new file mode 100644 index 00000000..cab6ec13 --- /dev/null +++ b/test/v2/ext.cpp @@ -0,0 +1,173 @@ +// Copyright (c) 2022 Klemens D. Morgenstern +// Copyright (c) 2022 Samuel Venable +// +// 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) + +#include +#include +#include +#include +#include +#include +#include +#include + +BOOST_AUTO_TEST_SUITE(ext) + +BOOST_AUTO_TEST_CASE(test_exe) +{ + using boost::unit_test::framework::master_test_suite; + + namespace bp2 = boost::process::v2; + auto pth = bp2::ext::exe(bp2::current_pid()); + BOOST_CHECK(!pth.empty()); + BOOST_CHECK_EQUAL(master_test_suite().argv[0], pth.string()); +} + + +BOOST_AUTO_TEST_CASE(test_child_exe) +{ + namespace bp2 = boost::process::v2; + using boost::unit_test::framework::master_test_suite; + const auto pth = bp2::filesystem::canonical(master_test_suite().argv[1]); + + boost::asio::io_context ctx; + bp2::process proc(ctx, pth, {"sleep", "10000"}); + BOOST_CHECK_EQUAL(bp2::ext::exe(proc.handle()), pth); +} + +BOOST_AUTO_TEST_CASE(cmd) +{ + using boost::unit_test::framework::master_test_suite; + + namespace bp2 = boost::process::v2; + auto cmd = bp2::ext::cmd(bp2::current_pid()); + + // the test framework drops a bunch of args. + bp2::basic_cstring_ref ref(cmd.argv()[0]); + BOOST_CHECK_EQUAL( + bp2::detail::conv_string( + ref.data(), ref.size() + ), master_test_suite().argv[0]); + + auto cm = cmd.argv() + (cmd.argc() - master_test_suite().argc); + for (auto i = 1; i < master_test_suite().argc; i++) + { + bp2::basic_cstring_ref ref(cm[i]); + + BOOST_CHECK_EQUAL(bp2::detail::conv_string(ref.data(), ref.size()), + master_test_suite().argv[i]); + } + +} + + +BOOST_AUTO_TEST_CASE(cmd_exe) +{ + using boost::unit_test::framework::master_test_suite; + const auto pth = master_test_suite().argv[1]; + + namespace bp2 = boost::process::v2; + + boost::asio::io_context ctx; + std::vector args = {"sleep", "10000", "moar", "args", " to test "}; + bp2::process proc(ctx, pth, args); + auto cm = bp2::ext::cmd(proc.handle()); + + bp2::basic_cstring_ref ref(cm.argv()[0]); + BOOST_CHECK_EQUAL(bp2::detail::conv_string(ref.data(), ref.size()), pth); + + BOOST_REQUIRE_EQUAL(cm.argc(), args.size() + 1); + for (auto i = 0; i < args.size(); i++) + { + ref = cm.argv()[i + 1]; + + BOOST_CHECK_EQUAL(bp2::detail::conv_string(ref.data(), ref.size()), args[i]); + } + +} + + +BOOST_AUTO_TEST_CASE(test_cwd) +{ + namespace bp2 = boost::process::v2; + auto pth = bp2::ext::cwd(bp2::current_pid()).string(); + if (pth.back() == '\\') + pth.pop_back(); + BOOST_CHECK_EQUAL(pth, bp2::filesystem::current_path()); +} + +BOOST_AUTO_TEST_CASE(test_cwd_exe) +{ + using boost::unit_test::framework::master_test_suite; + const auto pth = master_test_suite().argv[1]; + namespace bp2 = boost::process::v2; + + auto tmp = bp2::filesystem::temp_directory_path(); + + boost::asio::io_context ctx; + bp2::process proc(ctx, pth, {"sleep", "10000"}, + bp2::process_start_dir{tmp}); + auto tt = bp2::ext::cwd(proc.handle()).string(); + if (tt.back() == '\\') + tt.pop_back(); + BOOST_CHECK_EQUAL(tt, tmp); + bp2::error_code ec; + bp2::filesystem::remove(tmp, ec); +} + +BOOST_AUTO_TEST_CASE(test_env) +{ + namespace bp2 = boost::process::v2; + auto env = bp2::ext::env(bp2::current_pid()); + + + for (const auto & kp : bp2::environment::current()) + { + auto itr = std::find_if(env.begin(), env.end(), + [&](bp2::environment::key_value_pair_view kp_) + { + return kp.key() == kp_.key(); + }); + BOOST_REQUIRE(itr != env.end()); + BOOST_CHECK_EQUAL(kp.value(), (*itr).value()); + } +} + +BOOST_AUTO_TEST_CASE(test_env_exe) +{ + using boost::unit_test::framework::master_test_suite; + const auto pth = master_test_suite().argv[1]; + namespace bp2 = boost::process::v2; + + auto tmp = bp2::filesystem::temp_directory_path(); + + boost::asio::io_context ctx; + + std::vector new_env; + { + auto cr = bp2::environment::current(); + new_env.assign(cr.begin(), cr.end()); + } + + new_env.push_back("FOO=42"); + new_env.push_back("BAR=FOO"); + + bp2::process proc(ctx, pth, {"sleep", "10000"}, + bp2::process_environment(new_env)); + + auto env = bp2::ext::env(proc.handle()); + for (const auto & kp : new_env) + { + auto itr = std::find_if(env.begin(), env.end(), + [&](bp2::environment::key_value_pair_view kp_) + { + return kp.key() == kp_.key(); + }); + BOOST_REQUIRE(itr != env.end()); + BOOST_CHECK_EQUAL(kp.value(), (*itr).value()); + } +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/test/v2/pid.cpp b/test/v2/pid.cpp index a0664bcc..ae1841a9 100644 --- a/test/v2/pid.cpp +++ b/test/v2/pid.cpp @@ -1,4 +1,5 @@ // Copyright (c) 2022 Klemens D. Morgenstern +// Copyright (c) 2022 Samuel Venable // // 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) @@ -7,9 +8,32 @@ #include +#include +#include BOOST_AUTO_TEST_CASE(test_pid) { namespace bp2 = boost::process::v2; BOOST_CHECK_NE(bp2::current_pid(), static_cast(0)); -} \ No newline at end of file + + auto all = bp2::all_pids(); + auto itr = std::find(all.begin(), all.end(), bp2::current_pid()); + BOOST_CHECK(itr != all.end()); + + std::vector children, grand_children; + auto grand_child_pids = [](bp2::pid_type pid, + std::vector & children, + std::vector & grand_children) + { + children = bp2::child_pids(pid); + for (unsigned i = 0; i < children.size(); i++) + { + std::vector tmp1; + std::vector tmp2 = bp2::child_pids(children[i]); + tmp1.insert(std::end(tmp1), std::begin(tmp2), std::end(tmp2)); + grand_children = tmp1; + } + return (!children.empty() || !grand_children.empty()); + }; + BOOST_CHECK_NE(grand_child_pids(bp2::root_pid, children, grand_children), false); +} diff --git a/test/v2/process.cpp b/test/v2/process.cpp index e1c142c0..703e820e 100644 --- a/test/v2/process.cpp +++ b/test/v2/process.cpp @@ -150,6 +150,8 @@ BOOST_AUTO_TEST_CASE(terminate) BOOST_CHECK_MESSAGE(!sh.empty(), sh); bpv::process proc(ctx, sh, {}); + proc.suspend(); + proc.resume(); proc.terminate(); proc.wait(); }