use strings for subprocess errors

master
Daniel Kolesa 2017-05-09 20:33:45 +02:00
parent d5fe5dc292
commit af951a243c
2 changed files with 38 additions and 46 deletions

View File

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

View File

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