Fix exception safety in subprocess

This commit is contained in:
Sebastian Messmer 2018-05-20 22:28:28 -07:00
parent aa4e3c7c08
commit 76dc5cbba8

View File

@ -26,44 +26,54 @@ constexpr const char* openmode = "r";
using std::string;
namespace cpputils {
//TODO Exception safety
namespace {
FILE *_call(const string &command) {
FILE *subprocess = popen(command.c_str(), openmode);
if (!subprocess)
{
throw std::runtime_error("Error starting subprocess "+command + ". Errno: " + std::to_string(errno));
}
return subprocess;
}
class SubprocessHandle final {
public:
SubprocessHandle(const string &command)
: _subprocess(popen(command.c_str(), openmode)) {
if (!_subprocess) {
throw std::runtime_error("Error starting subprocess " + command + ". Errno: " + std::to_string(errno));
}
}
string _getOutput(FILE *subprocess) {
string output;
char buffer[1024];
while(fgets(buffer, sizeof(buffer), subprocess) != nullptr) {
output += buffer;
}
return output;
}
~SubprocessHandle() {
if (_subprocess != nullptr) {
close();
}
}
string getOutput() {
string output;
char buffer[1024];
while (fgets(buffer, sizeof(buffer), _subprocess) != nullptr) {
output += buffer;
}
return output;
}
int close() {
auto returncode = pclose(_subprocess);
_subprocess = nullptr;
if (returncode == -1) {
throw std::runtime_error("Error calling pclose. Errno: " + std::to_string(errno));
}
if (!WIFEXITED(returncode)) {
// WEXITSTATUS is only valid if WIFEXITED is 0.
throw std::runtime_error("WIFEXITED returned " + std::to_string(WIFEXITED(returncode)));
}
return WEXITSTATUS(returncode);
}
private:
FILE *_subprocess;
};
int _close(FILE *subprocess) {
auto returncode = pclose(subprocess);
if(returncode == -1) {
throw std::runtime_error("Error calling pclose. Errno: " + std::to_string(errno));
}
if (!WIFEXITED(returncode)) {
// WEXITSTATUS is only valid if WIFEXITED is 0.
throw std::runtime_error("WIFEXITED returned " + std::to_string(WIFEXITED(returncode)));
}
return WEXITSTATUS(returncode);
}
}
SubprocessResult Subprocess::call(const string &command) {
FILE *subprocess = _call(command);
string output = _getOutput(subprocess);
int exitcode = _close(subprocess);
SubprocessHandle subprocess(command);
string output = subprocess.getOutput();
int exitcode = subprocess.close();
return SubprocessResult {output, exitcode};
}