use strings for subprocess errors
This commit is contained in:
parent
d5fe5dc292
commit
af951a243c
|
@ -15,7 +15,6 @@
|
||||||
#define OSTD_PROCESS_HH
|
#define OSTD_PROCESS_HH
|
||||||
|
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <system_error>
|
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
@ -78,8 +77,8 @@ OutputRange &&split_args(OutputRange &&out, string_range str) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @brief Thrown on errors in ostd::subprocess. */
|
/** @brief Thrown on errors in ostd::subprocess. */
|
||||||
struct process_error: std::system_error {
|
struct process_error: std::runtime_error {
|
||||||
using std::system_error::system_error;
|
using std::runtime_error::runtime_error;
|
||||||
};
|
};
|
||||||
|
|
||||||
/** @brief The mode used for standard streams in ostd::subprocess.
|
/** @brief The mode used for standard streams in ostd::subprocess.
|
||||||
|
@ -180,12 +179,12 @@ struct OSTD_EXPORT subprocess {
|
||||||
*
|
*
|
||||||
* No streams will be set to redirect.
|
* No streams will be set to redirect.
|
||||||
*/
|
*/
|
||||||
subprocess() {}
|
subprocess() noexcept {}
|
||||||
|
|
||||||
/** @brief Initializes a subprocess with the given stream redirections.*/
|
/** @brief Initializes a subprocess with the given stream redirections.*/
|
||||||
subprocess(
|
subprocess(
|
||||||
process_stream in_use, process_stream out_use, process_stream err_use
|
process_stream in_use, process_stream out_use, process_stream err_use
|
||||||
):
|
) noexcept:
|
||||||
use_in(in_use), use_out(out_use), use_err(err_use)
|
use_in(in_use), use_out(out_use), use_err(err_use)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
@ -313,7 +312,7 @@ private:
|
||||||
std::vector<std::string> argv;
|
std::vector<std::string> argv;
|
||||||
if (cmd.empty()) {
|
if (cmd.empty()) {
|
||||||
if (args.empty()) {
|
if (args.empty()) {
|
||||||
throw process_error{EINVAL, std::generic_category()};
|
throw process_error{"no arguments given"};
|
||||||
}
|
}
|
||||||
cmd = args[0];
|
cmd = args[0];
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
#include <new>
|
#include <new>
|
||||||
|
|
||||||
#include "ostd/process.hh"
|
#include "ostd/process.hh"
|
||||||
|
#include "ostd/format.hh"
|
||||||
|
|
||||||
#ifdef OSTD_PLATFORM_WIN32
|
#ifdef OSTD_PLATFORM_WIN32
|
||||||
# include "ostd/filesystem.hh"
|
# include "ostd/filesystem.hh"
|
||||||
|
@ -70,12 +71,7 @@ OSTD_EXPORT void split_args_impl(
|
||||||
if (!MultiByteToWideChar(
|
if (!MultiByteToWideChar(
|
||||||
CP_UTF8, 0, str.data(), str.size(), wstr.get(), str.size() + 1
|
CP_UTF8, 0, str.data(), str.size(), wstr.get(), str.size() + 1
|
||||||
)) {
|
)) {
|
||||||
switch (GetLastError()) {
|
throw word_error{"unicode conversion failed"};
|
||||||
case ERROR_NO_UNICODE_TRANSLATION:
|
|
||||||
throw word_error{"unicode conversion failed"};
|
|
||||||
default:
|
|
||||||
throw word_error{"unknown error"};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int argc = 0;
|
int argc = 0;
|
||||||
|
@ -101,24 +97,14 @@ OSTD_EXPORT void split_args_impl(
|
||||||
if (!(req = WideCharToMultiByte(
|
if (!(req = WideCharToMultiByte(
|
||||||
CP_UTF8, 0, arg, arglen, nullptr, 0, nullptr, nullptr
|
CP_UTF8, 0, arg, arglen, nullptr, 0, nullptr, nullptr
|
||||||
))) {
|
))) {
|
||||||
switch (GetLastError()) {
|
throw word_error{"unicode conversion failed"};
|
||||||
case ERROR_NO_UNICODE_TRANSLATION:
|
|
||||||
throw word_error{"unicode conversion failed"};
|
|
||||||
default:
|
|
||||||
throw word_error{"unknown error"};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<char[]> buf{new char[req]};
|
std::unique_ptr<char[]> buf{new char[req]};
|
||||||
if (!WideCharToMultiByte(
|
if (!WideCharToMultiByte(
|
||||||
CP_UTF8, 0, arg, arglen, buf.get(), req, nullptr, nullptr
|
CP_UTF8, 0, arg, arglen, buf.get(), req, nullptr, nullptr
|
||||||
)) {
|
)) {
|
||||||
switch (GetLastError()) {
|
throw word_error{"unicode conversion failed"};
|
||||||
case ERROR_NO_UNICODE_TRANSLATION:
|
|
||||||
throw word_error{"unicode conversion failed"};
|
|
||||||
default:
|
|
||||||
throw word_error{"unknown error"};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func(string_range{buf.get(), buf.get() + req - 1}, data);
|
func(string_range{buf.get(), buf.get() + req - 1}, data);
|
||||||
|
@ -150,7 +136,7 @@ struct pipe {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (::pipe(fd) < 0) {
|
if (::pipe(fd) < 0) {
|
||||||
throw process_error{errno, std::generic_category()};
|
throw process_error{"could not open pipe"};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -161,7 +147,7 @@ struct pipe {
|
||||||
void fdopen(file_stream &s, bool write) {
|
void fdopen(file_stream &s, bool write) {
|
||||||
FILE *p = ::fdopen(fd[std::size_t(write)], write ? "w" : "r");
|
FILE *p = ::fdopen(fd[std::size_t(write)], write ? "w" : "r");
|
||||||
if (!p) {
|
if (!p) {
|
||||||
throw process_error{errno, std::generic_category()};
|
throw process_error{"could not open redirected stream"};
|
||||||
}
|
}
|
||||||
/* do not close twice, the stream will close it */
|
/* do not close twice, the stream will close it */
|
||||||
fd[std::size_t(write)] = -1;
|
fd[std::size_t(write)] = -1;
|
||||||
|
@ -193,7 +179,7 @@ OSTD_EXPORT void subprocess::open_impl(
|
||||||
bool use_path
|
bool use_path
|
||||||
) {
|
) {
|
||||||
if (use_in == process_stream::STDOUT) {
|
if (use_in == process_stream::STDOUT) {
|
||||||
throw process_error{EINVAL, std::generic_category()};
|
throw process_error{"could not redirect stdin to stdout"};
|
||||||
}
|
}
|
||||||
|
|
||||||
auto argp = std::make_unique<char *[]>(args.size() + 1);
|
auto argp = std::make_unique<char *[]>(args.size() + 1);
|
||||||
|
@ -212,7 +198,7 @@ OSTD_EXPORT void subprocess::open_impl(
|
||||||
|
|
||||||
auto cpid = fork();
|
auto cpid = fork();
|
||||||
if (cpid == -1) {
|
if (cpid == -1) {
|
||||||
throw process_error{errno, std::generic_category()};
|
throw process_error{"fork failed"};
|
||||||
} else if (!cpid) {
|
} else if (!cpid) {
|
||||||
/* child process */
|
/* child process */
|
||||||
fd_errno.close(false);
|
fd_errno.close(false);
|
||||||
|
@ -287,22 +273,29 @@ OSTD_EXPORT void subprocess::reset() {
|
||||||
|
|
||||||
OSTD_EXPORT int subprocess::close() {
|
OSTD_EXPORT int subprocess::close() {
|
||||||
if (!p_current) {
|
if (!p_current) {
|
||||||
throw process_error{ECHILD, std::generic_category()};
|
throw process_error{"no child process"};
|
||||||
}
|
}
|
||||||
data *pd = static_cast<data *>(p_current);
|
data *pd = static_cast<data *>(p_current);
|
||||||
int retc = 0;
|
int retc = 0;
|
||||||
if (pid_t wp; (wp = waitpid(pd->pid, &retc, 0)) < 0) {
|
if (pid_t wp; (wp = waitpid(pd->pid, &retc, 0)) < 0) {
|
||||||
reset();
|
reset();
|
||||||
throw process_error{errno, std::generic_category()};
|
throw process_error{"child process wait failed"};
|
||||||
}
|
}
|
||||||
if (retc) {
|
if (retc) {
|
||||||
int eno;
|
int eno;
|
||||||
auto r = read(pd->errno_fd, &eno, sizeof(int));
|
auto r = read(pd->errno_fd, &eno, sizeof(int));
|
||||||
reset();
|
reset();
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
throw process_error{errno, std::generic_category()};
|
throw process_error{"could not read from pipe"};
|
||||||
} else if (r == sizeof(int)) {
|
} else if (r == sizeof(int)) {
|
||||||
throw process_error{eno, std::generic_category()};
|
char buf[1024];
|
||||||
|
if (!strerror_r(eno, buf, sizeof(buf))) {
|
||||||
|
auto app = appender<std::string>();
|
||||||
|
format(app, "could not execute subprocess (%s)", buf);
|
||||||
|
throw process_error{std::move(app.get())};
|
||||||
|
} else {
|
||||||
|
throw process_error{"could not execute subprocess"};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
reset();
|
reset();
|
||||||
|
@ -332,10 +325,10 @@ struct pipe {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!CreatePipe(&p_r, &p_w, &sa, 0)) {
|
if (!CreatePipe(&p_r, &p_w, &sa, 0)) {
|
||||||
throw process_error{EPIPE, std::generic_category()};
|
throw process_error{"could not open pipe"};
|
||||||
}
|
}
|
||||||
if (!SetHandleInformation(read ? p_r : p_w, HANDLE_FLAG_INHERIT, 0)) {
|
if (!SetHandleInformation(read ? p_r : p_w, HANDLE_FLAG_INHERIT, 0)) {
|
||||||
throw process_error{EPIPE, std::generic_category()};
|
throw process_error{"could not set pipe parameters"};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -345,7 +338,7 @@ struct pipe {
|
||||||
read ? _O_RDONLY : 0
|
read ? _O_RDONLY : 0
|
||||||
);
|
);
|
||||||
if (fd < 0) {
|
if (fd < 0) {
|
||||||
throw process_error{EPIPE, std::generic_category()};
|
throw process_error{"could not open redirected stream"};
|
||||||
}
|
}
|
||||||
if (read) {
|
if (read) {
|
||||||
p_r = nullptr;
|
p_r = nullptr;
|
||||||
|
@ -355,7 +348,7 @@ struct pipe {
|
||||||
auto p = _fdopen(fd, read ? "r" : "w");
|
auto p = _fdopen(fd, read ? "r" : "w");
|
||||||
if (!p) {
|
if (!p) {
|
||||||
_close(fd);
|
_close(fd);
|
||||||
throw process_error{EPIPE, std::generic_category()};
|
throw process_error{"could not open redirected stream"};
|
||||||
}
|
}
|
||||||
s.open(p, [](FILE *f) {
|
s.open(p, [](FILE *f) {
|
||||||
std::fclose(f);
|
std::fclose(f);
|
||||||
|
@ -512,7 +505,7 @@ OSTD_EXPORT void subprocess::open_impl(
|
||||||
std::string const &cmd, std::vector<std::string> const &args, bool use_path
|
std::string const &cmd, std::vector<std::string> const &args, bool use_path
|
||||||
) {
|
) {
|
||||||
if (use_in == process_stream::STDOUT) {
|
if (use_in == process_stream::STDOUT) {
|
||||||
throw process_error{EINVAL, std::generic_category()};
|
throw process_error{"could not redirect stdin to stdout"};
|
||||||
}
|
}
|
||||||
|
|
||||||
/* pipes */
|
/* pipes */
|
||||||
|
@ -544,7 +537,7 @@ OSTD_EXPORT void subprocess::open_impl(
|
||||||
} else {
|
} else {
|
||||||
si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
|
si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
|
||||||
if (si.hStdInput == INVALID_HANDLE_VALUE) {
|
if (si.hStdInput == INVALID_HANDLE_VALUE) {
|
||||||
throw process_error{EINVAL, std::generic_category()};
|
throw process_error{"could not get standard input handle"};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (use_out == process_stream::PIPE) {
|
if (use_out == process_stream::PIPE) {
|
||||||
|
@ -553,7 +546,7 @@ OSTD_EXPORT void subprocess::open_impl(
|
||||||
} else {
|
} else {
|
||||||
si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
|
si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||||
if (si.hStdOutput == INVALID_HANDLE_VALUE) {
|
if (si.hStdOutput == INVALID_HANDLE_VALUE) {
|
||||||
throw process_error{EINVAL, std::generic_category()};
|
throw process_error{"could not get standard output handle"};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (use_err == process_stream::PIPE) {
|
if (use_err == process_stream::PIPE) {
|
||||||
|
@ -564,7 +557,7 @@ OSTD_EXPORT void subprocess::open_impl(
|
||||||
} else {
|
} else {
|
||||||
si.hStdError = GetStdHandle(STD_ERROR_HANDLE);
|
si.hStdError = GetStdHandle(STD_ERROR_HANDLE);
|
||||||
if (si.hStdError == INVALID_HANDLE_VALUE) {
|
if (si.hStdError == INVALID_HANDLE_VALUE) {
|
||||||
throw process_error{EINVAL, std::generic_category()};
|
throw process_error{"could not get standard error handle"};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
si.dwFlags |= STARTF_USESTDHANDLES;
|
si.dwFlags |= STARTF_USESTDHANDLES;
|
||||||
|
@ -576,7 +569,7 @@ OSTD_EXPORT void subprocess::open_impl(
|
||||||
if (!MultiByteToWideChar(
|
if (!MultiByteToWideChar(
|
||||||
CP_UTF8, 0, cmd.data(), cmd.size(), wcmd.data(), cmd.size() + 1
|
CP_UTF8, 0, cmd.data(), cmd.size(), wcmd.data(), cmd.size() + 1
|
||||||
)) {
|
)) {
|
||||||
throw process_error{EINVAL, std::generic_category()};
|
throw process_error{"unicode conversion failed"};
|
||||||
}
|
}
|
||||||
if (use_path) {
|
if (use_path) {
|
||||||
cmdpath = wcmd.get();
|
cmdpath = wcmd.get();
|
||||||
|
@ -592,7 +585,7 @@ OSTD_EXPORT void subprocess::open_impl(
|
||||||
if (!MultiByteToWideChar(
|
if (!MultiByteToWideChar(
|
||||||
CP_UTF8, 0, astr.data(), astr.size(), cmdline.data(), astr.size() + 1
|
CP_UTF8, 0, astr.data(), astr.size(), cmdline.data(), astr.size() + 1
|
||||||
)) {
|
)) {
|
||||||
throw process_error{EINVAL, std::generic_category()};
|
throw process_error{"unicode conversion failed"};
|
||||||
}
|
}
|
||||||
|
|
||||||
/* owned by CreateProcess, do not close explicitly */
|
/* owned by CreateProcess, do not close explicitly */
|
||||||
|
@ -618,7 +611,7 @@ OSTD_EXPORT void subprocess::open_impl(
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!success) {
|
if (!success) {
|
||||||
throw process_error{ECHILD, std::generic_category()};
|
throw process_error{"could not execute subprocess"};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -628,7 +621,7 @@ OSTD_EXPORT void subprocess::reset() {
|
||||||
|
|
||||||
OSTD_EXPORT int subprocess::close() {
|
OSTD_EXPORT int subprocess::close() {
|
||||||
if (!p_current) {
|
if (!p_current) {
|
||||||
throw process_error{ECHILD, std::generic_category()};
|
throw process_error{"no child process"};
|
||||||
}
|
}
|
||||||
|
|
||||||
data *pd = static_cast<data *>(p_current);
|
data *pd = static_cast<data *>(p_current);
|
||||||
|
@ -637,7 +630,7 @@ OSTD_EXPORT int subprocess::close() {
|
||||||
CloseHandle(pd->process);
|
CloseHandle(pd->process);
|
||||||
CloseHandle(pd->thread);
|
CloseHandle(pd->thread);
|
||||||
reset();
|
reset();
|
||||||
throw process_error{ECHILD, std::generic_category()};
|
throw process_error{"child process wait failed"};
|
||||||
}
|
}
|
||||||
|
|
||||||
int ec;
|
int ec;
|
||||||
|
@ -645,7 +638,7 @@ OSTD_EXPORT int subprocess::close() {
|
||||||
CloseHandle(pd->process);
|
CloseHandle(pd->process);
|
||||||
CloseHandle(pd->thread);
|
CloseHandle(pd->thread);
|
||||||
reset();
|
reset();
|
||||||
throw process_error{ECHILD, std::generic_category()};
|
throw process_error{"could not retrieve exit code"};
|
||||||
}
|
}
|
||||||
|
|
||||||
CloseHandle(pd->process);
|
CloseHandle(pd->process);
|
||||||
|
|
Loading…
Reference in a new issue