2
0
mirror of https://github.com/boostorg/process.git synced 2026-01-19 04:22:15 +00:00

extern process management.

This commit is contained in:
Samuel Venable
2023-02-06 20:43:32 +08:00
committed by Klemens Morgenstern
parent 6f0d6a2e24
commit f1302430cb
28 changed files with 2974 additions and 43 deletions

1
.gitignore vendored
View File

@@ -31,4 +31,5 @@
/notes.cpp
/notes_p.txt
.settings
.DS_Store

View File

@@ -14,7 +14,7 @@
#include <boost/process/v2/detail/config.hpp>
#include <boost/process/v2/cstring_ref.hpp>
#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
#endif

View File

@@ -9,9 +9,20 @@
#include <boost/process/v2/detail/last_error.hpp>
#include <boost/process/v2/detail/throw_error.hpp>
#include <boost/process/v2/detail/process_handle_windows.hpp>
#include <boost/process/v2/ext/detail/proc_info.hpp>
#include <windows.h>
#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<enum_windows_data_t*>(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<>;

View File

@@ -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)

View File

@@ -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)
{

View File

@@ -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

View File

@@ -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))

View File

@@ -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 <string>
#include <vector>
#include <boost/process/v2/detail/config.hpp>
#include <boost/process/v2/detail/throw_error.hpp>
#include <boost/process/v2/process_handle.hpp>
#include <boost/process/v2/pid.hpp>
#include <boost/process/v2/shell.hpp>
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<typename Executor>
BOOST_PROCESS_V2_DECL shell cmd(basic_process_handle<Executor> & handle, error_code & ec)
{
return cmd(handle.native_handle(), ec);
}
template<typename Executor>
BOOST_PROCESS_V2_DECL shell cmd(basic_process_handle<Executor> & handle)
{
return cmd(handle.native_handle());
}
} // namespace ext
BOOST_PROCESS_V2_END_NAMESPACE
#if defined(BOOST_PROCESS_V2_HEADER_ONLY)
#include <boost/process/v2/ext/impl/cmd.ipp>
#endif
#endif // BOOST_PROCESS_V2_CMD_HPP

View File

@@ -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 <boost/process/v2/detail/config.hpp>
#include <boost/process/v2/detail/throw_error.hpp>
#include <boost/process/v2/pid.hpp>
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<typename Executor>
BOOST_PROCESS_V2_DECL filesystem::path cwd(basic_process_handle<Executor> & handle, error_code & ec)
{
return cwd(handle.native_handle(), ec);
}
template<typename Executor>
BOOST_PROCESS_V2_DECL filesystem::path cwd(basic_process_handle<Executor> & handle)
{
return cwd(handle.native_handle());
}
} // namespace ext
BOOST_PROCESS_V2_END_NAMESPACE
#if defined(BOOST_PROCESS_V2_HEADER_ONLY)
#include <boost/process/v2/ext/impl/cwd.ipp>
#endif
#endif // BOOST_PROCESS_V2_CWD_HPP

View File

@@ -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 <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/ext/detail/proc_info.hpp>
#include <string>
#if (defined(__APPLE__) && defined(__MACH__))
#include <cstdlib>
#include <sys/types.h>
#include <sys/sysctl.h>
#include <sys/proc_info.h>
#include <libproc.h>
#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

View File

@@ -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 <boost/process/v2/detail/config.hpp>
#include <boost/process/v2/detail/throw_error.hpp>
#include <boost/process/v2/pid.hpp>
#include <string>
#include <vector>
#if defined(BOOST_PROCESS_V2_WINDOWS)
#include <iterator>
#include <algorithm>
#include <windows.h>
#include <winternl.h>
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 <pshpack8.h>
#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 <poppack.h>
#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 <boost/process/v2/ext/detail/impl/proc_info.ipp>
#endif
#endif // BOOST_PROCESS_V2_DETAIL_PROC_INFO_HPP

View File

@@ -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 <string>
#include <vector>
#include <boost/process/v2/detail/config.hpp>
#include <boost/process/v2/detail/throw_error.hpp>
#include <boost/process/v2/process_handle.hpp>
#include <boost/process/v2/pid.hpp>
#include <boost/process/v2/environment.hpp>
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<typename remove_pointer<detail::ext::native_env_handle_type>::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<typename Executor>
BOOST_PROCESS_V2_DECL env_view env(basic_process_handle<Executor> & handle, error_code & ec)
{
return env(handle.native_handle(), ec);
}
template<typename Executor>
BOOST_PROCESS_V2_DECL env_view env(basic_process_handle<Executor> & handle)
{
return env(handle.native_handle());
}
} // namespace ext
BOOST_PROCESS_V2_END_NAMESPACE
#if defined(BOOST_PROCESS_V2_HEADER_ONLY)
#include <boost/process/v2/ext/impl/env.ipp>
#endif
#endif // BOOST_PROCESS_V2_ENV_HPP

View File

@@ -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 <boost/process/v2/detail/config.hpp>
#include <boost/process/v2/detail/throw_error.hpp>
#include <boost/process/v2/process_handle.hpp>
#include <boost/process/v2/pid.hpp>
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<typename Executor>
filesystem::path exe(basic_process_handle<Executor> & handle, error_code & ec)
{
return exe(handle.native_handle(), ec);
}
template<typename Executor>
filesystem::path exe(basic_process_handle<Executor> & handle)
{
return exe(handle.native_handle());
}
} // namespace ext
BOOST_PROCESS_V2_END_NAMESPACE
#if defined(BOOST_PROCESS_V2_HEADER_ONLY)
#include <boost/process/v2/ext/impl/exe.ipp>
#endif
#endif // BOOST_PROCESS_V2_EXE_HPP

View File

@@ -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 <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/ext/detail/proc_info.hpp>
#include <boost/process/v2/ext/cmd.hpp>
#if defined(BOOST_PROCESS_V2_WINDOWS)
#include <windows.h>
#include <shellapi.h>
#else
#include <cstdlib>
#endif
#if (defined(__linux__) || defined(__ANDROID__))
#include <cstdio>
#endif
#if defined(__FreeBSD__)
#include <sys/socket.h>
#include <sys/sysctl.h>
#include <sys/param.h>
#include <sys/queue.h>
#include <sys/user.h>
#include <libprocstat.h>
#endif
#if (defined(__DragonFly__) || defined(__OpenBSD__))
#include <sys/types.h>
#include <sys/param.h>
#include <sys/sysctl.h>
#include <sys/user.h>
#include <kvm.h>
#endif
#if defined(__NetBSD__)
#include <sys/types.h>
#include <kvm.h>
#include <sys/param.h>
#include <sys/sysctl.h>
#endif
#if defined(__sun)
#include <sys/types.h>
#include <kvm.h>
#include <sys/param.h>
#include <sys/time.h>
#include <sys/proc.h>
#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<void, del> 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<int*>(procargs.data());
auto itr = procargs.begin() + sizeof(argc);
std::unique_ptr<char*[]> 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<char*[]> 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<struct procstat, cl_proc_stat> 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<struct kinfo_proc, proc_info_close> 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<kvm_t, closer> 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<std::string> vec;
int cntp = 0;
kinfo_proc2 *proc_info = nullptr;
struct closer
{
void operator()(kvm_t * kd)
{
kvm_close(kd);
}
};
std::unique_ptr<kvm_t, closer> 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<std::string> vec;
int cntp = 0;
kinfo_proc *proc_info = nullptr;
struct closer
{
void operator()(kvm_t * kd)
{
kvm_close(kd);
}
};
std::unique_ptr<kvm_t, closer> 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

View File

@@ -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 <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/ext/detail/proc_info.hpp>
#include <boost/process/v2/ext/cwd.hpp>
#include <string>
#if defined(BOOST_PROCESS_V2_WINDOWS)
#include <windows.h>
#else
#include <climits>
#endif
#if (defined(__APPLE__) && defined(__MACH__))
#include <sys/proc_info.h>
#include <libproc.h>
#endif
#if (defined(BOOST_PROCESS_V2_WINDOWS) || defined(__linux__) || defined(__ANDROID__) || defined(__sun))
#include <cstdlib>
#endif
#if defined(__FreeBSD__)
#include <sys/socket.h>
#include <sys/sysctl.h>
#include <sys/param.h>
#include <sys/queue.h>
#include <sys/user.h>
#include <libprocstat.h>
#endif
#if (defined(__NetBSD__) || defined(__OpenBSD__))
#include <sys/types.h>
#include <sys/sysctl.h>
#endif
#if defined(__DragonFly__)
#include <cstring>
#include <cstdio>
#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<void, del> 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

View File

@@ -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 <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/ext/detail/proc_info.hpp>
#include <boost/process/v2/ext/env.hpp>
#if defined(BOOST_PROCESS_V2_WINDOWS)
#include <shellapi.h>
#else
#include <cstdlib>
#endif
#if (defined(__linux__) || defined(__ANDROID__))
#include <cstdio>
#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<void, del> 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<char> 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<char, detail::ext::native_env_handle_deleter> procargs{};
int f = ::open(("/proc/" + std::to_string(pid) + "/environ").c_str(), O_RDONLY);
while (!procargs || procargs.get()[size - 1] != EOF)
{
std::unique_ptr<char, detail::ext::native_env_handle_deleter> 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

View File

@@ -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 <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/ext/detail/proc_info.hpp>
#include <boost/process/v2/ext/exe.hpp>
#include <string>
#include <vector>
#if defined(BOOST_PROCESS_V2_WINDOWS)
#include <windows.h>
#else
#include <climits>
#endif
#if (defined(__APPLE__) && defined(__MACH__))
#include <sys/proc_info.h>
#include <libproc.h>
#endif
#if (defined(BOOST_PROCESS_V2_WINDOWS) || defined(__linux__) || defined(__ANDROID__) || defined(__sun))
#include <cstdlib>
#endif
#if (defined(__FreeBSD__) || defined(__DragonFly__) || defined(__NetBSD__))
#include <sys/types.h>
#include <sys/sysctl.h>
#if !defined(__FreeBSD__)
#include <alloca.h>
#endif
#endif
#if defined(__OpenBSD__)
#include <boost/process/v2/ext/cwd.hpp>
#include <boost/process/v2/ext/cmd.hpp>
#include <boost/process/v2/ext/env.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string/classification.hpp>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/sysctl.h>
#include <kvm.h>
#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<void, del> 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

View File

@@ -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 <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/pid.hpp>
#if defined(BOOST_PROCESS_V2_WINDOWS)
#include <windows.h>
#include <tlhelp32.h>
#else
#include <unistd.h>
#endif
#if (defined(__APPLE__) && defined(__MACH__))
#include <sys/proc_info.h>
#include <libproc.h>
#endif
#if (defined(__linux__) || defined(__ANDROID__))
#include <dirent.h>
#endif
#if defined(__FreeBSD__)
#include <sys/types.h>
#include <sys/user.h>
#include <libutil.h>
#include <cstdlib>
#endif
#if (defined(__DragonFly__) || defined(__OpenBSD__))
#include <sys/types.h>
#include <sys/param.h>
#include <sys/sysctl.h>
#include <sys/user.h>
#include <kvm.h>
#endif
#if defined(__NetBSD__)
#include <sys/types.h>
#include <kvm.h>
#include <sys/param.h>
#include <sys/sysctl.h>
#endif
#if defined(__sun)
#include <sys/types.h>
#include <kvm.h>
#include <sys/param.h>
#include <sys/time.h>
#include <sys/proc.h>
#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<pid_type> all_pids(boost::system::error_code & ec)
{
std::vector<pid_type> 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<pid_type>(-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<pid_type> child_pids(pid_type pid, boost::system::error_code & ec)
{
std::vector<pid_type> 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<pid_type> all_pids(boost::system::error_code & ec)
{
std::vector<pid_type> 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<pid_type>(-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<pid_type> child_pids(pid_type pid, boost::system::error_code & ec)
{
std::vector<pid_type> 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<pid_type> all_pids(boost::system::error_code & ec)
{
std::vector<pid_type> 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<pid_type>(-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<pid_type> child_pids(pid_type pid, boost::system::error_code & ec)
{
std::vector<pid_type> vec;
std::vector<pid_type> 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<pid_type> all_pids(boost::system::error_code & ec)
{
std::vector<pid_type> 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<pid_type>(-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<pid_type> child_pids(pid_type pid, boost::system::error_code & ec)
{
std::vector<pid_type> 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<pid_type> all_pids(boost::system::error_code & ec)
{
std::vector<pid_type> 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<kvm_t, closer> 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<pid_type>(-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<kvm_t, closer> 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<pid_type> child_pids(pid_type pid, boost::system::error_code & ec)
{
std::vector<pid_type> 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<kvm_t, closer> 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<pid_type> all_pids(boost::system::error_code & ec)
{
std::vector<pid_type> vec;
int cntp = 0;
kinfo_proc2 *proc_info = nullptr;
struct closer
{
void operator()(kvm_t * kd)
{
kvm_close(kd);
}
};
std::unique_ptr<kvm_t, closer> 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<pid_type>(-1);
int cntp = 0;
kinfo_proc2 *proc_info = nullptr;
struct closer
{
void operator()(kvm_t * kd)
{
kvm_close(kd);
}
};
std::unique_ptr<kvm_t, closer> 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<pid_type> child_pids(pid_type pid, boost::system::error_code & ec)
{
std::vector<pid_type> vec;
int cntp = 0;
kinfo_proc2 *proc_info = nullptr;
struct closer
{
void operator()(kvm_t * kd)
{
kvm_close(kd);
}
};
std::unique_ptr<kvm_t, closer> 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<pid_type> all_pids(boost::system::error_code & ec)
{
std::vector<pid_type> vec;
int cntp = 0;
kinfo_proc *proc_info = nullptr;
struct closer
{
void operator()(kvm_t * kd)
{
kvm_close(kd);
}
};
std::unique_ptr<kvm_t, closer> 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<pid_type>(-1);
int cntp = 0;
kinfo_proc *proc_info = nullptr;
struct closer
{
void operator()(kvm_t * kd)
{
kvm_close(kd);
}
};
std::unique_ptr<kvm_t, closer> 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<pid_type> child_pids(pid_type pid, boost::system::error_code & ec)
{
std::vector<pid_type> vec;
int cntp = 0;
kinfo_proc *proc_info = nullptr;
struct closer
{
void operator()(kvm_t * kd)
{
kvm_close(kd);
}
};
std::unique_ptr<kvm_t, closer> 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<pid_type> all_pids(boost::system::error_code & ec)
{
std::vector<pid_type> vec;
struct pid cur_pid;
proc *proc_info = nullptr;
struct closer
{
void operator()(kvm_t * kd)
{
kvm_close(kd);
}
};
std::unique_ptr<kvm_t, closer> 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<pid_type>(-1);
proc *proc_info = nullptr;
struct closer
{
void operator()(kvm_t * kd)
{
kvm_close(kd);
}
};
std::unique_ptr<kvm_t, closer> 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<pid_type> child_pids(pid_type pid, boost::system::error_code & ec)
{
std::vector<pid_type> vec;
struct pid cur_pid;
proc *proc_info = nullptr;
struct closer
{
void operator()(kvm_t * kd)
{
kvm_close(kd);
}
};
std::unique_ptr<kvm_t, closer> 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<pid_type> 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<pid_type> 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

View File

@@ -96,21 +96,23 @@ void shell::parse_()
{
argc_ = static_cast<int>(we.we_wordc);
argv_ = we.we_wordv;
reserved_ = static_cast<int>(we.we_offs);
}
free_argv_ = +[](int argc, char ** argv)
{
wordexp_t we{
.we_wordc = static_cast<std::size_t>(argc),
.we_wordv = argv,
.we_offs = 0
};
wordfree(&we);
};
}
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);
}
if (argv_ != nullptr && free_argv_)
free_argv_(argc_, argv_);
}
auto shell::args() const -> args_type

View File

@@ -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 <boost/process/v2/detail/config.hpp>
#include <boost/process/v2/detail/throw_error.hpp>
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<pid_type> all_pids(boost::system::error_code & ec);
/// List all available pids.
BOOST_PROCESS_V2_DECL std::vector<pid_type> 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<pid_type> child_pids(pid_type pid, boost::system::error_code & ec);
// return child pids of pid.
BOOST_PROCESS_V2_DECL std::vector<pid_type> child_pids(pid_type pid);
BOOST_PROCESS_V2_END_NAMESPACE
#if defined(BOOST_PROCESS_V2_HEADER_ONLY)
#include <boost/process/v2/impl/pid.ipp>
#endif
#endif // BOOST_PROCESS_V2_PID_HPP
#endif //BOOST_PROCESS_V2_PID_HPP

View File

@@ -15,6 +15,7 @@
#include <boost/process/v2/default_launcher.hpp>
#include <boost/process/v2/exit_code.hpp>
#include <boost/process/v2/pid.hpp>
#include <boost/process/v2/ext/exe.hpp>
#include <boost/process/v2/process_handle.hpp>
#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
#endif //BOOST_PROCESS_V2_PROCESS_HPP

View File

@@ -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
};

View File

@@ -15,6 +15,11 @@
#include <boost/process/v2/impl/error.ipp>
#include <boost/process/v2/impl/pid.ipp>
#include <boost/process/v2/ext/impl/exe.ipp>
#include <boost/process/v2/ext/impl/cwd.ipp>
#include <boost/process/v2/ext/impl/cmd.ipp>
#include <boost/process/v2/ext/impl/env.ipp>
#include <boost/process/v2/ext/detail/impl/proc_info.ipp>
#include <boost/process/v2/detail/impl/environment.ipp>
#include <boost/process/v2/detail/impl/last_error.ipp>
#include <boost/process/v2/detail/impl/throw_error.ipp>

View File

@@ -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 $<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(ext)

View File

@@ -21,7 +21,12 @@ project : requirements
<toolset>msvc:<define>_CRT_SECURE_NO_DEPRECATE
<toolset>msvc:<cxxflags>/bigobj
<target-os>windows:<define>WIN32_LEAN_AND_MEAN
<target-os>windows:<define>_WIN32_WINNT=0x0601
<target-os>linux:<linkflags>-lpthread
<target-os>freebsd:<linkflags>-lutil
<target-os>freebsd:<linkflags>-lkvm
<target-os>freebsd:<linkflags>-lprocstat
<target-os>bsd:<linkflags>-lkvm
<os>NT,<toolset>cw:<library>ws2_32
<os>NT,<toolset>gcc:<library>ws2_32
<define>BOOST_PROCESS_V2_SEPARATE_COMPILATION=1
@@ -46,6 +51,8 @@ lib test_impl : test_impl.cpp filesystem :
<link>static
<target-os>windows:<source>shell32
<target-os>windows:<source>user32
<target-os>windows:<source>Ntdll
<target-os>windows:<source>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 : <build>no <target-os>windows:<build>yes <target-os>windows:<source>Advapi32 ]
[ run ext.cpp test_impl : --log_level=all --catch_system_errors=no -- : target ]
;

173
test/v2/ext.cpp Normal file
View File

@@ -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 <boost/process/v2/ext/cmd.hpp>
#include <boost/process/v2/ext/cwd.hpp>
#include <boost/process/v2/ext/env.hpp>
#include <boost/process/v2/ext/exe.hpp>
#include <boost/process/v2/pid.hpp>
#include <boost/process/v2/process.hpp>
#include <boost/process/v2/start_dir.hpp>
#include <boost/test/unit_test.hpp>
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<typename bp2::shell::char_type> ref(cmd.argv()[0]);
BOOST_CHECK_EQUAL(
bp2::detail::conv_string<char>(
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<typename bp2::shell::char_type> ref(cm[i]);
BOOST_CHECK_EQUAL(bp2::detail::conv_string<char>(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<std::string> args = {"sleep", "10000", "moar", "args", " to test "};
bp2::process proc(ctx, pth, args);
auto cm = bp2::ext::cmd(proc.handle());
bp2::basic_cstring_ref<typename bp2::shell::char_type> ref(cm.argv()[0]);
BOOST_CHECK_EQUAL(bp2::detail::conv_string<char>(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<char>(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<bp2::environment::key_value_pair> 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()

View File

@@ -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 <boost/test/unit_test.hpp>
#include <algorithm>
#include <vector>
BOOST_AUTO_TEST_CASE(test_pid)
{
namespace bp2 = boost::process::v2;
BOOST_CHECK_NE(bp2::current_pid(), static_cast<bp2::pid_type>(0));
}
auto all = bp2::all_pids();
auto itr = std::find(all.begin(), all.end(), bp2::current_pid());
BOOST_CHECK(itr != all.end());
std::vector<bp2::pid_type> children, grand_children;
auto grand_child_pids = [](bp2::pid_type pid,
std::vector<bp2::pid_type> & children,
std::vector<bp2::pid_type> & grand_children)
{
children = bp2::child_pids(pid);
for (unsigned i = 0; i < children.size(); i++)
{
std::vector<bp2::pid_type> tmp1;
std::vector<bp2::pid_type> 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);
}

View File

@@ -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();
}