Skip to content

Commit 2ef9f16

Browse files
authored
Add retcode to CalledProcessError (#81)
It's useful to be able to know the exit code of the process when it fails with a non-zero exit code. To get this to work, I had to fix a bug in the implementation of check_output. Previously, check_output would call both `p.communicate()` and `p.poll()`. The former has the effect of waiting for EOF on the input and then waiting for the child to exit, reaping it with `waitpid(2)`. Unfortunately, `p.poll()` was hoping to be able to also use `waitpid(2)` to retrieve the exit code of the process. But since the child had already been reaped, the given pid no longer existed, and thus `waitpid(2)` would return `ECHILD`. Luckily the call to `p.poll()` is unnecessary, as the process already provides `p.retcode()` for retrieving the exit code.
1 parent bf4289c commit 2ef9f16

File tree

2 files changed

+23
-6
lines changed

2 files changed

+23
-6
lines changed

subprocess.hpp

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -123,8 +123,9 @@ static const size_t DEFAULT_BUF_CAP_BYTES = 8192;
123123
class CalledProcessError: public std::runtime_error
124124
{
125125
public:
126-
explicit CalledProcessError(const std::string& error_msg):
127-
std::runtime_error(error_msg)
126+
int retcode;
127+
CalledProcessError(const std::string& error_msg, int retcode):
128+
std::runtime_error(error_msg), retcode(retcode)
128129
{}
129130
};
130131

@@ -1630,10 +1631,10 @@ inline void Popen::execute_process() noexcept(false)
16301631
// Call waitpid to reap the child process
16311632
// waitpid suspends the calling process until the
16321633
// child terminates.
1633-
wait();
1634+
int retcode = wait();
16341635

16351636
// Throw whatever information we have about child failure
1636-
throw CalledProcessError(err_buf);
1637+
throw CalledProcessError(err_buf, retcode);
16371638
}
16381639
} catch (std::exception& exp) {
16391640
stream_.cleanup_fds();
@@ -1984,9 +1985,9 @@ namespace detail
19841985
static_assert(!detail::has_type<output, detail::param_pack<Args...>>::value, "output not allowed in args");
19851986
auto p = Popen(std::forward<F>(farg), std::forward<Args>(args)..., output{PIPE});
19861987
auto res = p.communicate();
1987-
auto retcode = p.poll();
1988+
auto retcode = p.retcode();
19881989
if (retcode > 0) {
1989-
throw CalledProcessError("Command failed : Non zero retcode");
1990+
throw CalledProcessError("Command failed : Non zero retcode", retcode);
19901991
}
19911992
return std::move(res.first);
19921993
}

test/test_ret_code.cc

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,25 @@ void test_ret_code_comm()
2727
std::cout << "retcode: " << cut.retcode() << std::endl;
2828
}
2929

30+
void test_ret_code_check_output()
31+
{
32+
using namespace sp;
33+
bool caught = false;
34+
try {
35+
auto obuf = check_output({"/bin/false"}, shell{false});
36+
assert(false); // Expected to throw
37+
} catch (CalledProcessError &e) {
38+
std::cout << "retcode: " << e.retcode << std::endl;
39+
assert (e.retcode == 1);
40+
caught = true;
41+
}
42+
assert(caught);
43+
}
44+
3045
int main() {
3146
// test_ret_code();
3247
test_ret_code_comm();
48+
test_ret_code_check_output();
3349

3450
return 0;
3551
}

0 commit comments

Comments
 (0)