Skip to content

Instantly share code, notes, and snippets.

@Elizafox
Last active January 9, 2018 17:11
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Elizafox/95cd4f66764d53e6c6cb4bf483517728 to your computer and use it in GitHub Desktop.
Save Elizafox/95cd4f66764d53e6c6cb4bf483517728 to your computer and use it in GitHub Desktop.
C++ process stuff
/* sunrise - a C++ installer for alpine
* Coypright (C) 2018 Interlinked Foundation
* All rights reserved
*/
#include <string> // std::string
#include <vector> // std::vector
#include <algorithm> // std::transform
#include <iterator> // std::back_inserter
#include <exception> // std::generic_category
#include <system_error> // std::system_error
#include <cerrno> // errno
#include <csignal> // raise, SIGQUIT
#include <unistd.h> // STD*_FILENO, close, dup2, pipe, fork, exec, kill,
// pid_t
#include <sys/wait.h> // waitpid, WEXITED, WIFEXITED, WNOHANG
#include "process.hpp" // SubProcess, SubProcessStatus
namespace sunrise { namespace process {
SubProcess::SubProcess(std::vector<std::string> &args)
{
if(SubProcess::fork())
SubProcess::exec(args);
}
SubProcess::~SubProcess()
{
SubProcessStatus sp_status = status();
if(!sp_status.exited)
// End process
::kill(pid, SIGQUIT);
if(p_stdin >= 0)
::close(p_stdin);
if(p_stdout >= 0)
::close(p_stdout);
if(p_stderr >= 0)
::close(p_stderr);
}
bool SubProcess::fork()
{
// Perform the fork before exec
// returns true if child, false if parent
int pipe_stdin[2], pipe_stdout[2], pipe_stderr[2];
// Create our pipes
if(::pipe(pipe_stdin) < 0)
{
throw std::system_error(errno, std::generic_category(),
"Could not create stdin pipe");
}
if(::pipe(pipe_stdout) < 0)
{
::close(pipe_stdin[0]);
::close(pipe_stdin[1]);
throw std::system_error(errno, std::generic_category(),
"Could not create stdout pipe");
}
if(::pipe(pipe_stderr) < 0)
{
::close(pipe_stdin[0]);
::close(pipe_stdin[1]);
::close(pipe_stdout[0]);
::close(pipe_stdout[1]);
throw std::system_error(errno, std::generic_category(),
"Could not create stderr pipe");
}
switch((pid = ::fork()))
{
case -1:
// Error
throw std::system_error(errno, std::generic_category(),
"Could not fork");
case 0:
// Child
// Close original file descriptors
::close(STDIN_FILENO);
::close(STDOUT_FILENO);
::close(STDERR_FILENO);
// Close extra fd's
::close(pipe_stdin[PIPE_W]);
::close(pipe_stdout[PIPE_R]);
::close(pipe_stderr[PIPE_R]);
// Redirect file descriptors
::dup2(pipe_stdin[PIPE_R], STDIN_FILENO);
::dup2(pipe_stdout[PIPE_W], STDOUT_FILENO);
::dup2(pipe_stderr[PIPE_W], STDERR_FILENO);
return true;
default:
// Parent
// Close whatever we don't need
::close(pipe_stdin[PIPE_R]);
::close(pipe_stdout[PIPE_W]);
::close(pipe_stderr[PIPE_W]);
// Copy
p_stdin = pipe_stdin[PIPE_W];
p_stdout = pipe_stdout[PIPE_R];
p_stderr = pipe_stdout[PIPE_R];
return false;
}
}
void SubProcess::exec(std::vector<std::string> &args)
{
// Convert into char vector
std::vector<const char *> args_c;
args_c.reserve(args.size() + 1);
std::transform(args.begin(), args.end(),
std::back_inserter(args_c),
[](std::string const &a) { return a.c_str(); });
args_c.push_back(""); // Null-terminated
// launch
::execv(args_c[0], const_cast<char * const *>(&(args_c[1])));
// If we get here, execv failed
// Terminate ourselves with extreme prejudice
::raise(SIGQUIT);
}
SubProcessStatus SubProcess::wait()
{
// Wait for child to terminate
int wstatus = 0;
::waitpid(pid, &wstatus, WEXITED);
return SubProcessStatus(true, WEXITSTATUS(wstatus));
}
SubProcessStatus SubProcess::status()
{
// Get status of child
int wstatus = 0;
::waitpid(pid, &wstatus, WNOHANG | WEXITED);
if(WIFEXITED(wstatus))
{
return SubProcessStatus(true, WEXITSTATUS(wstatus));
}
else
{
return SubProcessStatus(false);
}
}
} } // namespace
/* sunrise - a C++ installer for alpine
* Coypright (C) 2018 Interlinked Foundation
* All rights reserved
*/
#pragma once
#include <string> // std::string
#include <vector> // std::vector
#include <unistd.h> // pid_t
namespace sunrise { namespace process {
class SubProcessStatus
{
// Status of a subprocess
public:
bool exited = false;
int ret = 0;
SubProcessStatus(bool exited, int ret) :
exited(exited),
ret(ret)
{
}
SubProcessStatus(bool exited) :
exited(exited)
{
}
SubProcessStatus()
{
}
};
class SubProcess
{
public:
int p_stdin = -1, p_stdout = -1, p_stderr = -1;
SubProcess(std::vector<std::string> &);
~SubProcess();
SubProcessStatus wait();
SubProcessStatus status();
private:
virtual bool fork();
virtual void exec(std::vector<std::string> &);
pid_t pid;
// for pipe()
static const int PIPE_R = 0;
static const int PIPE_W = 1;
};
} } // namespace
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment