示例,不保证可用性
#include <iostream>
#include <sstream>
#include <cstdarg>
#include <cerrno>
#include <cstring>
#include <cstdlib>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <sys/poll.h>
#define CREATE_PIPE(name) \
FDHolder fdI##name; \
FDHolder fdO##name; \
do { \
int tmp__pipeFd##name[2] = { -1, -1 }; \
status = pipe(tmp__pipeFd##name); \
if (status) \
{ \
THROW_TEXCEPTIONERRNO("pipe"); \
} \
fdI##name = tmp__pipeFd##name[0]; \
fdO##name = tmp__pipeFd##name[1]; \
} while (false)
#define THROW_TEXCEPTION(exceptionTitle, userData) \
do { throw (TaskMgmt::TException(SUBSYSTEM_NAME, \
COMPONENT_NAME, \
(exceptionTitle), \
__FILE__, \
__LINE__, \
(userData))); } while (false)
#define THROW_TEXCEPTIONERRNO(functionName) \
do { throw (TaskMgmt::TExceptionErrno(SUBSYSTEM_NAME, \
COMPONENT_NAME, \
(functionName), \
__FILE__, \
__LINE__)); } while (false)
void TaskMgmt::ForkAndExecIO(const std::string &in,
std::string &out,
std::string &err,
const std::vector<std::string> &cmdargs)
{
/* There has to be atleast the command */
assert(cmdargs.size() >= 1);
const char *command = cmdargs.front().c_str();
std::vector<const char *> args;
std::vector<std::string>::const_iterator i = cmdargs.begin();
for (++i; i != cmdargs.end(); ++i)
{
args.push_back(i->c_str());
}
ForkAndExecIO(in,
out,
err,
command,
args);
}
void TaskMgmt::ForkAndExecIO(const std::string &in,
std::string &out,
std::string &err,
const char *command,
const std::vector<const char *> &args)
{
assert_pointer(command);
/* Build argv */
std::vector<const char *> argv;
argv.push_back(command);
for (std::vector<const char *>::const_iterator i = args.begin();
i != args.end();
++i)
{
argv.push_back(*i);
}
argv.push_back(NULL);
int status;
CREATE_PIPE(In);
CREATE_PIPE(Out);
CREATE_PIPE(Err);
/* Fork a new child to execute the command. */
pid_t pid;
pid = fork();
if (pid == 0)
{
/* Child */
close(fdOIn);
fdOIn = -1;
close(fdIOut);
fdIOut = -1;
close(fdIErr);
fdIErr = -1;
/* Map in, out and err */
dup2(fdIIn,
STDIN_FILENO);
close(fdIIn);
dup2(fdOOut,
STDOUT_FILENO);
close(fdOOut);
dup2(fdOErr,
STDERR_FILENO);
close(fdOErr);
/* Clear signal mask */
clearSignalMask();
/* Exec */
execvp(argv.front(),
(char* const*)&argv[0]);
/* Reaching this point indicates an error with execvp */
std::cerr << "Error with execvp(" << command << "): " <<
strerror(errno) << std::endl;
_exit(EXIT_FAILURE);
}
else if (pid > 0)
{
/* Parent */
close(fdIIn);
fdIIn = -1;
close(fdOOut);
fdOOut = -1;
close(fdOErr);
fdOErr = -1;
/* Read out and err and write in until they are closed */
std::string::const_iterator iIn = in.begin();
while ((fdOIn >= 0) ||
(fdIOut >= 0) ||
(fdIErr >= 0))
{
struct pollfd pollfd[3] = { };
unsigned int nfds = 0;
if (fdOIn >= 0)
{
pollfd[nfds].fd = fdOIn;
pollfd[nfds].events = POLLOUT;
pollfd[nfds].revents = 0;
++nfds;
}
if (fdIOut >= 0)
{
pollfd[nfds].fd = fdIOut;
pollfd[nfds].events = POLLIN;
pollfd[nfds].revents = 0;
++nfds;
}
if (fdIErr >= 0)
{
pollfd[nfds].fd = fdIErr;
pollfd[nfds].events = POLLIN;
pollfd[nfds].revents = 0;
++nfds;
}
assert(nfds > 0);
do
{
status = poll(pollfd,
nfds,
-1);
}
while ((status < 0) &&
(errno == EINTR));
if (status < 0)
{
/* Something went wrong */
THROW_TEXCEPTIONERRNO("poll");
}
nfds = 0;
if (fdOIn >= 0)
{
assert(pollfd[nfds].fd == fdOIn);
if (pollfd[nfds].revents & POLLOUT)
{
ssize_t size;
size_t toWrite = in.end() - iIn;
do
{
size = write(fdOIn,
&(*iIn),
toWrite);
}
while ((size < 0) &&
(errno == EINTR));
if (size < 0)
{
/* Something went wrong */
THROW_TEXCEPTIONERRNO("read");
}
else if (static_cast<size_t>(size) < toWrite)
{
iIn += static_cast<size_t>(size);
}
else
{
/* All done */
close(fdOIn);
fdOIn = -1;
}
}
else if (pollfd[nfds].revents & (POLLERR | POLLHUP))
{
close(fdOIn);
fdOIn = -1;
}
++nfds;
}
if (fdIOut >= 0)
{
assert(pollfd[nfds].fd == fdIOut);
if (pollfd[nfds].revents & POLLIN)
{
ReadInto(fdIOut,
out);
}
else if (pollfd[nfds].revents & (POLLERR | POLLHUP))
{
close(fdIOut);
fdIOut = -1;
}
++nfds;
}
if (fdIErr >= 0)
{
assert(pollfd[nfds].fd == fdIErr);
if (pollfd[nfds].revents & POLLIN)
{
ReadInto(fdIErr,
err);
}
else if (pollfd[nfds].revents & (POLLERR | POLLHUP))
{
close(fdIErr);
fdIErr = -1;
}
++nfds;
}
}
/* Wait for the child to die */
do
{
pid = waitpid(pid,
&status,
0);
}
while ((pid < 0) &&
(errno == EINTR));
if (pid < 0)
{
/* Something went wrong */
THROW_TEXCEPTIONERRNO("waitpid");
}
/* Check how the child died */
if (WIFEXITED(status))
{
if (WEXITSTATUS(status) != EXIT_SUCCESS)
{
/* Abnormal exit */
THROW_TEXCEPTION(command,
err.c_str());
}
}
else if (WIFSIGNALED(status))
{
/* Terminated with signal */
std::ostringstream s;
s << command << " was killed with signal " <<
WTERMSIG(status);
THROW_TEXCEPTION(command,
s.str().c_str());
}
else
{
/* Unknown reason */
THROW_TEXCEPTION(command,
"unknown termination");
}
}
else
{
/* Something went wrong */
THROW_TEXCEPTIONERRNO("fork");
}
}
void TaskMgmt::clearSignalMask()
{
int status;
sigset_t sigSet;
sigemptyset(&sigSet);
status = pthread_sigmask(SIG_SETMASK,
&sigSet,
NULL);
if (status)
{
THROW_TEXCEPTIONERRNO("pthread_sigmask");
}
}