/* based on concepts from the mutt filter code...
*
* This code basically does what popen should have been... and what
* popen2/popen3/popen4 in python do... it allows you access to
* as many of stdin/stdout/stderr for a sub program as you want, instead
* of just one (which is what popen is).
*/
#include "cs_config.h"
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include "util/neo_misc.h"
#include "util/neo_err.h"
#include "util/filter.h"
NEOERR *filter_wait (pid_t pid, int options, int *exitcode)
{
int r;
pid_t rpid;
rpid = waitpid (pid, &r, options);
if (WIFEXITED(r))
{
r = WEXITSTATUS(r);
if (exitcode)
{
*exitcode = r;
/* If they're asking for the exit code, we don't generate an error */
return STATUS_OK;
}
if (r == 0) return STATUS_OK;
else return nerr_raise(NERR_SYSTEM, "Child %d returned status %d:", rpid,
r);
}
if (WIFSIGNALED(r))
{
r = WTERMSIG(r);
return nerr_raise(NERR_SYSTEM, "Child %d died on signal %d:", rpid, r);
}
if (WIFSTOPPED(r))
{
r = WSTOPSIG(r);
return nerr_raise(NERR_SYSTEM, "Child %d stopped on signal %d:", rpid, r);
}
return nerr_raise(NERR_ASSERT, "ERROR: waitpid(%d, %d) returned (%d, %d)",
pid, options, rpid, r);
}
NEOERR *filter_create_fd (const char *cmd, int *fdin, int *fdout, int *fderr,
pid_t *pid)
{
int pi[2]={-1,-1}, po[2]={-1,-1}, pe[2]={-1,-1};
int rpid;
*pid = 0;
if (fdin)
{
*fdin = 0;
if (pipe (pi) == -1)
return nerr_raise_errno(NERR_SYSTEM,
"Unable to open in pipe for command: %s", cmd);
}
if (fdout)
{
*fdout = 0;
if (pipe (po) == -1)
{
if (fdin)
{
close (pi[0]);
close (pi[1]);
}
return nerr_raise_errno(NERR_SYSTEM,
"Unable to open out pipe for command: %s", cmd);
}
}
if (fderr)
{
*fderr = 0;
if (pipe (pe) == -1)
{
if (fdin)
{
close (pi[0]);
close (pi[1]);
}
if (fdout)
{
close (po[0]);
close (po[1]);
}
return nerr_raise_errno(NERR_SYSTEM, "Unable to open err pipe for command: %s", cmd);
}
}
/* block signals */
if ((rpid = fork ()) == 0)
{
/* unblock signals */
if (fdin)
{
close (pi[1]);
dup2 (pi[0], 0);
close (pi[0]);
}
if (fdout)
{
close (po[0]);
dup2 (po[1], 1);
close (po[1]);
}
if (fderr)
{
close (pe[0]);
dup2 (pe[1], 2);
close (pe[1]);
}
execl ("/bin/sh", "sh", "-c", cmd, (void *)NULL);
_exit (127);
}
else if (rpid == -1)
{
/* unblock signals */
if (fdin)
{
close (pi[0]);
close (pi[1]);
}
if (fdout)
{
close (po[0]);
close (po[1]);
}
if (fderr)
{
close (pe[0]);
close (pe[1]);
}
return nerr_raise_errno(NERR_SYSTEM, "Unable to fork for command: %s", cmd);
}
if (fdout)
{
close (po[1]);
*fdout = po[0];
}
if (fdin)
{
close (pi[0]);
*fdin = pi[1];
}
if (fderr)
{
close (pe[1]);
*fderr = pe[0];
}
*pid = rpid;
return STATUS_OK;
}
NEOERR *filter_create_fp(const char *cmd, FILE **in, FILE **out, FILE **err,
pid_t *pid)
{
NEOERR *nerr;
int fdin = 0, fdout = 0, fderr = 0;
int *pfdin = NULL, *pfdout = NULL, *pfderr = NULL;
if (in) pfdin = &fdin;
if (out) pfdout = &fdout;
if (err) pfderr = &fderr;
nerr = filter_create_fd(cmd, pfdin, pfdout, pfderr, pid);
if (nerr) return nerr_pass(nerr);
if (in)
{
*in = fdopen (fdin, "w");
if (*in == NULL)
return nerr_raise_errno(NERR_IO, "Unable to fdopen in for command: %s",
cmd);
}
if (out)
{
*out = fdopen (fdout, "r");
if (*out == NULL)
{
if (in) fclose(*in);
return nerr_raise_errno(NERR_IO, "Unable to fdopen out for command: %s",
cmd);
}
}
if (err)
{
*err = fdopen (fderr, "r");
if (*err == NULL)
{
if (in) fclose(*in);
if (out) fclose(*out);
return nerr_raise_errno(NERR_IO, "Unable to fdopen err for command: %s",
cmd);
}
}
return STATUS_OK;
}