I'm trying to run a program with a specific standard input. I succeed by using a file descriptor of a file where there is what I want to put in the stdin, but I fail to write directly on the stdin :
$cat input.test
echo Hello
$
Code C :
int main(int argc, char **argv)
{
int fd = 0;
fd = open("input.test", O_CREAT);
close(STDIN_FILENO);
dup2(fd, STDIN_FILENO);
char *const args[] = { "bash", NULL };
execvp("bash", args);
}
That works :
$./a.out
Hello
$
But if I try to write directly on the STDIN using pipe the program displays nothing and keeps running :
int main(int argc, char **argv)
{
int fds[2];
pipe(fds);
close(STDIN_FILENO);
dup2(fds[1], STDIN_FILENO);
write(fds[1], "echo Hello;", 11); // Résults are identics with fds[0]
char *const args[] = { "bash", NULL };
execvp("bash", args);
}
Thanks for your help
Cordially,
Bastien.
EDIT Problem solved:
Thanks for your answers, here the code which works :
int main(void)
{
int fd[2];
pid_t pid;
if (pipe(fd) < 0)
return EXIT_FAILURE;
if ((pid = fork()) < 0)
return EXIT_FAILURE;
else if (pid != 0) { /* father */
close(fd[1]);
dup2(fd[0], STDIN_FILENO);
execlp("bash", "bash", (char *)0);
} else { /* son */
close(fd[0]);
write(fd[1], "echo hello\n", 11);
}
return EXIT_SUCCESS;
}
You need to dup the read side of the pipe to stdin, not the write side. (And write to the write side, obviously.)
#include <unistd.h>
#include <string.h>
#include <stdio.h>
int main(int argc, char **argv)
{
int fds[2];
char cmd[] = "echo hello\nexit\n";
pipe(fds);
close(STDIN_FILENO);
dup2(fds[0], STDIN_FILENO);
write(fds[1], cmd, strlen(cmd));
char *const args[] = { "bash", NULL };
execvp("bash", args);
return 0;
}
Make sure you check the return values of all those functions though, you'll never manage to debug your code if you don't.
execv and friends replace the current running program with the specified one; they do not return - execution continues at the start of new program instead.
So what you normally do is fork and, in one of the forks, call execv. You then read and write through the pipe from your program continuing in the other fork. There are usually popen functions to do this in most languages; sadly in POSIX the popen() is strictly read or write and not bidirectional.
Luckily, I've made, tested and published a popen3 function. This gives you back three file descriptors - one for stdin to the process, and two for stdout and stderr. You can then use write() on the stdin.
When you call pipe, fd[ 0 ] is open for reading, and fd[ 1 ] is open for writing. You should be dup'ing stdin on the read side ( fd[ 0 ]) and writing to the write side( fd[ 1 ]). Check the return value of write: it is probably -1.
But there is a larger issue. You never close either side of the pipe. bash may block on a read and never do anything until the write side of the pipe is closed. You should close both sides of the pipe after you dup and write. (Or set FD_CLOEXEC).
Also note that doing it the way you do, you're dependent on pipe buffer size. If you write too much, write will be blocked as there's no reader. Do do it reliably, you should fork(), do exec in the child and write to the pipe in the parent. This way the pipe will have a reader and you will be able to write as much data as you want into it.
Related
How do I get the output of a program ran by exec(). Let's say I have this code:
int main(int argc, char ** argv) {
int fid = fork();
if(fid == 0) {
execlp("ls", "ls", NULL);
}
wait();
return 0;
}
How can the parent process get the output of the ls command?
The exec family of functions completely replaces the current process. However, they do not close file descriptors unless they marked close-on-exec. Thus, the typical way to do this is to create a pipe where the read side belongs to the parent and the write side belongs to the child.
This would look something like this (error checking omitted and obviously inefficient):
#include <stdint.h>
#include <sys/wait.h>
#include <unistd.h>
int main(int argc, char ** argv) {
int pipefd[2];
int status;
uint8_t buf[256];
pipe(pipefd);
int fid = fork();
if(fid == 0) {
close(pipefd[0]);
dup2(pipefd[1], 1);
close(pipefd[1]);
execlp("ls", "ls", NULL);
}
close(pipefd[1]);
while (read(pipefd[0], buf, 1) > 0)
write(1, buf, 1);
wait(&status);
return 0;
}
Note that to attach the pipe file descriptor to standard output (FD 1), you need to use dup2. You also need to close the ends of the pipe you're not using, or you may never end up reaching end of file.
If you're interested in the exit status, wait (or waitpid) will provide that for you; see the manual page for how to determine if it exited normally and if so, what that status was.
I'm making a C program where it basically reads in a line from the user, interprets it and then tries to execute the command with execve. I'm also forking the execve to a child process if '&' is in the input.
Now I wish to hide any terminal output which comes from the execve command when it's running in the child process.
Is there any relatively easy way to do this?
You can hide the output by redirecting stdout and stderr to /dev/null after forking but before execve(). The idea is to open /dev/null, then make stdout and stderr duplicates of the obtained file descriptor with dup2() (which will also close the originals first). It's almost the same as redirecting to a pipe.
An example (incomplete program, and skipping most error checking):
#include <unistd.h>
#include <fcntl.h>
...
int pid = fork();
if (pid == -1) {
/* fork error */
exit(1);
} else if (pid == 0) {
/* child process */
/* open /dev/null for writing */
int fd = open("/dev/null", O_WRONLY);
dup2(fd, 1); /* make stdout a copy of fd (> /dev/null) */
dup2(fd, 2); /* ...and same with stderr */
close(fd); /* close fd */
/* stdout and stderr now write to /dev/null */
/* ready to call exec */
execve(cmd, args, env);
exit(1);
} else {
/* parent process */
...
I've written simple example, maybe it will help you.
First, try to call it without | echo $1 > /dev/null - it should print files. When you add it, output is empty.
#include <stdio.h>
#include <unistd.h>
int main()
{
int ret;
char *cmd[] = { "ls", "-l", (char *)0 };
char *env[] = {(char *)0 };
ret = execve ("/bin/ls | echo $1 > /dev/null", cmd, env);
return 0;
}
I can barely understand the man page for pipe, so I kinda need help understanding how to take a piped input in an external executable.
I have 2 programs: main.o & log.o
I written main.o to fork. Here is what it is doing:
Parent fork will pipe data to the child
Child fork will exec log.o
I need the child fork for main to pipe to STDIN of log.o
log.o simply takes STDIN & logs with time stamp to a file.
My code is composed of some code from various StackOverflow pages I dont remember & the man page for pipe:
printf("\n> ");
while(fgets(input, MAXINPUTLINE, stdin)){
char buf;
int fd[2], num, status;
if(pipe(fd)){
perror("Pipe broke, dood");
return 111;
}
switch(fork()){
case -1:
perror("Fork is sad fais");
return 111;
case 0: // Child
close(fd[1]); // Close unused write end
while (read(fd[0], &buf, 1) > 0) write(STDOUT_FILENO, &buf, 1);
write(STDOUT_FILENO, "\n", 1);
close(fd[0]);
execlp("./log", "log", "log.txt", 0); // This is where I am confused
_exit(EXIT_FAILURE);
default: // Parent
data=stuff_happens_here();
close(fd[0]); // Close unused read end
write(fd[1], data, strlen(data));
close(fd[1]); // Reader will see EOF
wait(NULL); // Wait for child
}
printf("\n> ");
}
I suppose this is what you're going to do:
1. main fork, parent pass message to child via pipe.
2. child receive message from pipe, redirect message to STDIN, execute log.
3. log receive message from STDIN, do something.
the key to do this is dup2 to redirect file descriptor, from pipe to STDIN.
This is the modified simple version:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
int main(int argc, char *argv[]) {
int fd[2];
char buf[] = "HELLO WORLD!";
if(pipe(fd)){
perror("pipe");
return -1;
}
switch(fork()){
case -1:
perror("fork");
return -1;
case 0:
// child
close(fd[1]);
dup2(fd[0], STDIN_FILENO);
close(fd[0]);
execl("./log", NULL);
default:
// parent
close(fd[0]);
write(fd[1], buf, sizeof(buf));
close(fd[1]);
wait(NULL);
}
printf("END~\n");
return 0;
}
I can suggest a simpler approach. There's a function called popen(). It works very similar to the system() function except you can read or write to/from the child stdin/stdout.
Example:
int main(int argc, char* argv[])
{
FILE* fChild = popen("logApp.exe", "wb"); // the logger app is another application
if (NULL == fChild) return -1;
fprintf(fChild, "Hello world!\n");
pclose(fChild);
}
Write "man popen" in your console for a full description.
You could use dup2
See Mapping UNIX pipe descriptors to stdin and stdout in C
I'm trying to create a simple program which simulates the "ls -l | tail -n 2" call in terminal. I'm using "fork" and "execvp" for that purpose.
Well, here is the code:
int main(int argc, char *argv[])
{
int pipefd[2];
pid_t child1;
pid_t child2;
char* com1[] = {"ls", "-l",NULL};
char* com2[] = {"tail", "-n","2",NULL};
if (!(child1 = fork()))
{
close(STDOUT);
dup(pipefd[1]);
close(pipefd[1]);
execvp (com1[0], com1);
_exit(EXIT_SUCCESS);
}
else
{
close(pipefd[1]);
if (!(child2 = fork()))
{
close(STDIN);
dup(pipefd[0]); /* dupWR now holds the lowest fd available, meaning STDOUT's */
perror("dup 2");
close(pipefd[0]); /* reader will see EOF */
execvp (com2[0], com2);
_exit(EXIT_SUCCESS);
}
else
{
close(pipefd[0]);
waitpid(child2,0,0);
}
waitpid(child1,0,0);
}
return 0;
}
I get these errors:
dup 2: Bad file descriptor
tail: cannot fstat `standard input': Bad file descriptor
tail: -: Bad file descriptor
It seems to me that there is a problem in synchronization. In fact, if I declare:
com2[] = {"ls", "-l",NULL}; It works fine (I mean as in normal shell). Moreover, I found that the second "dup" in the second "fork" returns error. Why is that? I don't know where is the problem with this code. Please help!
Edit:
I added this code (forgot to create pipes):
if (pipe(pipefd) == -1) {
perror("pipe");
exit(EXIT_FAILURE);
}
Thanks, Useless!
close(STDOUT);
dup(pipefd[1]);
close(pipefd[1]);
Since dup returns the new file descriptor, and you don't use the return value, you're discarding it.
Did you want to replace stdout instead, like so?
dup2(pipefd[1], STDOUT_FILENO);
If so, pipefd[] should really be initialized first. Did you mean to call pipe somewhere?
I've been trying to use the pipe() system call to create a shell that supports piping (with an arbitrary number of commands).
Unfortunately, I haven't had much luck using pipe(). After spending a few days looking at various online resources, I decided to put together an oversimplified program that has the same effect as executing ls | sort to see if I could even get a pipe to work for two sibling, child processes. Here's the code:
#include <sys/wait.h>
#include <unistd.h>
void run(char *cmd) {
char *args[2];
args[0] = cmd;
args[1] = NULL;
execvp(cmd, args);
}
int main(void) {
int filedes[2];
pipe(filedes);
pid_t pid_a, pid_b;
if (!(pid_a = fork())) {
dup2(filedes[1], 1);
run("ls");
}
if (!(pid_b = fork())) {
dup2(filedes[0], 0);
run("sort");
}
waitpid(pid_a, NULL, 0);
waitpid(pid_b, NULL, 0);
return 0;
}
The pipe is created in the parent and I know that after the execvp() call, each child process inherits the file descriptors that pipe() creates in the parent. For the ls process, I'm using dup2() to redirect its standard out (1) to the write-end of the pipe and for the sort process, standard in (0) is being redirected to the read-end of the pipe.
Finally, I wait for both processes to finish before exiting.
My intuition tells me this should work, but it doesn't!
Any suggestions?
You have to close the pipes you're not using.
at least sort will read from its stdin until stdin is closed.
In this case, it's stdin is never closed, as you still have 2 open filedescriptors for it.
filedes[0] in the ls child (this likely gets closed when ls finishes)
filedes[0] in the parent program (this never gets closed as you waitpid() for sort to end, but it never will because the parent keeps its stdin open)
Change your program to
if (!(pid_a = fork())) {
dup2(filedes[1], 1);
closepipes(filedes);
run("ls");
}
if (!(pid_b = fork())) {
dup2(filedes[0], 0);
closepipes(filedes);
run("sort");
}
closepipe(filedes);
waitpid(pid_a, NULL, 0);
waitpid(pid_b, NULL, 0);
where closepipes is something like
void closepipes(int *fds)
{
close(fds[0]);
close(fds[1]);
}
Before calling waitpid in the parent process you have to close all file descriptors from the pipe that you don't need. These are:
filedes[0] in pid_a
filedes[1] in pid_b
both filedes[0] and filedes[1] in the parent process
You should also check that pipe() and fork() didn't return -1, which means an error happened.
You need to close (at least) the writing end of the pipe in the parent process. Otherwise, the reading end of the pipe will never read EOF status, and sort will never finish.
This code working properly...
#include <sys/wait.h>
#include <unistd.h>
using namespace std;
void run(char *cmd) {
char *args[2];
args[0] = cmd;
args[1] = NULL;
execvp(cmd, args);
}
void closepipe(int *fds)
{
close(fds[0]);
close(fds[1]);
}
int main(int argc,char *argv[]) {
int filedes[2];
pipe(filedes);
char lss[]="ls";
char sorts[]="sort";
pid_t pid_a, pid_b;
chdir(argv[1]);
if (!(pid_a = fork())) {
dup2(filedes[1], 1);
closepipe(filedes);
run(lss);
}
if (!(pid_b = fork())) {
dup2(filedes[0], 0);
closepipe(filedes);
run(sorts);
}
closepipe(filedes);
waitpid(pid_a, NULL, 0);
waitpid(pid_b, NULL, 0);
return 0;
}