-
IPC: need redefine stdin
I want to execute a Perl code in memory from my C application, just opening Perl interpreter and send code to its stdin. So, my code is:
Code:
char *perl_args[] = { "/usr/bin/perl", NULL };
char *envp = {"INVOKER=myapp", NULL };
char *code = "print 'Hello!';";
int pipe_fd[2];
pipe(pipe_fd);
write(pipe_fd[1], (void*) code, strlen(code));
pid_t new_pid = fork();
if(!new_pid)
{
dup2(pipe_fd[0], STDIN_FILENO);
execve(perl_args[0], perl_args, envp);
perror("execve error");
}
close(pipe_fd[1]);
wait((int*)0);
but program just hangs up and never ends. Where I am wrong?.. I tried to `strace` bash when executing
Code:
echo 'print "HELLO!";' | perl
and it seems to me that it uses the same actions that I try to do in my code.
-
Okay, I found 'popen()' function, and I could pass data to a program...but in this way I cannot control its stdout :(
I still don't understand why the 'fork()' variant does not works. When I create an unnamed pipe, I do not use any buffered i/o calling 'write()' directly and closing write end of a pipe after it. But child program still continues waiting. Neither fflush(), write_unbuffered() or fsync() does not help. If UNIX was constructed for making IPC easy and to make small programs work together, why it's so hard to make it work?:) and why nobody knows how to do it?..) should I dig bash source code by myself to know it?..:)
-
Your problem is probably here:
Code:
if(!new_pid)
{
dup2(pipe_fd[0], STDIN_FILENO);
execve(perl_args[0], perl_args, envp);
perror("execve error");
}
close(pipe_fd[1]); /* Don't close pipe unil wait() detects the end of the child process. */
wait((int*)0);
Try this instead:
Code:
if(!new_pid)
{
dup2(pipe_fd[0], STDIN_FILENO);
execve(perl_args[0], perl_args, envp);
perror("execve error");
}
else
{
waitpid(new_pid, 0, 0);
close(pipe_fd[1]);
}
-
One other thing. You should not write to pipe_fd[1] in the parent until in the parent part of the process after the fork(). Also, in the child part, close pipe_fd[1] before exceve(). In the parent part, close pipe_fd[0], and then write to pipe_fd[1], close pipe_fd[1], waitpid(), and then exit. So, this is better:
Code:
char *perl_args[] = { "/usr/bin/perl", NULL };
char *envp = {"INVOKER=myapp", NULL };
char *code = "print 'Hello!';";
int pipe_fd[2];
pid_t new_pid = 0;
pipe(pipe_fd);
new_pid = fork();
if(!new_pid)
{
close(pipd_fd[1]);
dup2(pipe_fd[0], STDIN_FILENO);
execve(perl_args[0], perl_args, envp);
/* Error starting perl - clean up here */
perror("execve error");
close(pipe_fd[0]);
}
else
{
close(pipe_fd[0]);
write(pipe_fd[1], (const void*) code, strlen(code));
close(pipe_fd[1]);
waitpid(new_pid, 0, 0); /* Optionally, use wait(0) */
}
A decent overview for use of pipes is found in the section 7 man page for pipe: man 7 pipe
and man 2 pipe has a good example of use of pipes for IPC in a situation similar to what you are trying to do.
-
Thanks a lot, it works now! :) at last I understood how the unix-like I/O works by reading some books in addition to your post ) thanks for the light in my head)
-
Yeah. A brick (upside the head) works much the same for me! :-)