No, piping is exactly how you want to go. Gimme an hour and I'll get you some goodies.
EDIT (Ten minutes late...)
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <sys/types.h>
#include <unistd.h>
#define READFD 0
#define WRITEFD 1
#define STDIN 0
#define STDOUT 1
#define STDERR 2
/*
FORK_OBEDIENT_CHILD
Creates a child process, redirecting the STDIN, STDOUT, and STDERR descriptors
so that the parent can read from STDOUT and STDERR and write to STDIN.
Inputs:
const char *program Path to the program we want to execute
char* const env[] Environment for the child program
char* const args[] Arguments to pass to the child program
Note that env[] and args[] may be of any length, but MUST have a null point-
er as the last element. Otherwise execve() will gag and your computer will
most likely catch fire and/or explode. I'm not responsible for any injuries
resulting from your negligence.
Outputs:
int *child_stdin File descriptor to the child's STDIN. Write-only.
int *child_stdout File descriptor to the child's STDOUT. Read-only.
int *child_stderr File descriptor to the child's STDERR. Read-only.
These integer file descriptors will only work with the kernel's read(),
write(), and other similar functions. Any of the C stdio stuff won't be able
to handle it. However, you can do this:
FILE *fstdin = fdopen(*child_stdin, "w");
FILE *fstdout = fdopen(*child_stdout, "r");
FILE *fstderr = fdopen(*child_stderr, "r");
DO NOT SIMPLY USE stdin, stdout, OR stderr AS YOUR VARIABLES. You'll destroy
your own descriptors.
If you're expecting binary data or something else, change the mode to suit
your needs. I'm almost 100% sure that you should NOT close the integer
descriptors you get back from this function. In either case, once your child
has exited, you may close the descriptors with the usual fclose(). Closing
fstdout or fstderr shouldn't do too much, but closing fstdin would probably
cause the child to crash or at least hang indefinitely if it ever tries to
read from it after you've closed it on your side.
Return value: PID of the child process, or -1 if an error occured. Should the
function return -1, the values of the child descriptor outputs
are undefined.
*/
pid_t fork_obedient_child(int *child_stdin, int *child_stdout, int *child_stderr,
const char *program, char* const env[], char* const args[])
{
int stdin_pipe[2], stdout_pipe[2], stderr_pipe[2];
pid_t pid;
/* gag on bad parameters */
if( !child_stdin || !child_stdout || !child_stderr || !program )
return -1;
/* create pipe pairs we'll use later. if pipe() returns -1 then it failed.*/
if( pipe(stdin_pipe) == -1 )
return -1;
/* create STDOUT pair */
if( pipe(stdout_pipe) == -1 )
{
close(stdin_pipe[0]);
close(stdin_pipe[1]);
return -1;
}
/* create STDERR pair */
if( pipe(stderr_pipe) == -1 )
{
close(stdin_pipe[0]);
close(stdin_pipe[1]);
close(stdout_pipe[0]);
close(stdout_pipe[1]);
return -1;
}
pid = fork();
if( pid == -1 )
/* failed to fork */
return -1;
else if( pid == 0 )
{
/* CHILD CODE
We're never going to write to STDIN nor read from STDOUT or STDERR, so
we should close those file descriptors. */
close(stdin_pipe[WRITEFD]);
close(stdout_pipe[READFD]);
close(stderr_pipe[READFD]);
/* The operating system opens new file descriptors for the new process
for STDIN, STDOUT, and STDERR. We don't want those, as the parent pro-
gram can't access them. Close them all. */
close(STDIN);
close(STDOUT);
close(STDERR);
/* What we do want is for the stdin/stdout descriptors to be associated
with our pipe descriptors. Since STDIN, STDOUT, and STDERR are always
descriptors 0, 1, and 2 respectively, this is quite easy. dup2() opens
the second descriptor argument as an independent clone of the first. So
if we clone our pipe descriptors (stdin_pipe[READFD], etc.) into the
fixed STDIN, STDOUT and STDERR descriptors, then the parent program can
communicate with the child via the descriptors returned in child_stdin
child_stdout and child_stderr. */
dup2(stdin_pipe[READFD], STDIN);
dup2(stdout_pipe[WRITEFD], STDOUT);
dup2(stderr_pipe[WRITEFD], STDERR);
/* Now that we've cloned the descriptors, we don't need the originals
that we inherited from the parent anymore. Note that this won't affect
the parent at all, because the child has its own copies of the descrip-
tors. Closing these won't close them on the parent. */
close(stdin_pipe[READFD]);
close(stdout_pipe[WRITEFD]);
close(stderr_pipe[WRITEFD]);
/* Execute the child process */
execve(program, args, env);
/* Everything after the EXEC() call should never execute unless the call
failed. If it does, then we have a problem and should crash the child.
If you want to stick any special error-handling code, do it here. Just
remember that the output of whatever you print will now go to the parent
program, not the terminal.*/
exit(-1);
}
else
{
/* PARENT CODE
We're not going to read from the child's STDIN nor write to its STDOUT.
That's the child's job. We're going to do the opposite, in fact. We
read from the child's STDOUT so we know what it's doing, and write to
its STDIN so we can tell it what to do. */
close(stdin_pipe[READFD]);
close(stdout_pipe[WRITEFD]);
close(stderr_pipe[WRITEFD]);
/* Set the file descriptor variables we're returning. These will be used
to communicate with the child process. Remember, you write to the
stdin descriptor, and read from the stdout/stderr descriptors. */
*child_stdin = stdin_pipe[WRITEFD];
*child_stdout = stdout_pipe[READFD];
*child_stderr = stderr_pipe[READFD];
return pid;
}
/* Should never get here, but the compiler complains unless we put a return
statement at the end of the function. */
return -1;
}
Try that. Make sure you adhere to the rules I outlined in the documentation.
By the way...for you old CodeCallers out there who still remember the days when we had syntax highlighting...what happened to that?
Edited by dargueta, 02 July 2010 - 12:07 AM.
Added code/nostalgic note