This question already has answers here:
How do I execute a command and get the output of the command within C++ using POSIX?
(12 answers)
Closed 7 years ago.
I'm trying to start an external application through system() - for example, system("ls"). I would like to capture its output as it happens so I can send it to another function for further processing. What's the best way to do that in C/C++?
From the popen manual:
#include <stdio.h>
FILE *popen(const char *command, const char *type);
int pclose(FILE *stream);
Try the popen() function. It executes a command, like system(), but directs the output into a new file. A pointer to the stream is returned.
FILE *lsofFile_p = popen("lsof", "r");
if (!lsofFile_p)
{
return -1;
}
char buffer[1024];
char *line_p = fgets(buffer, sizeof(buffer), lsofFile_p);
pclose(lsofFile_p);
EDIT: misread question as wanting to pass output to another program, not another function. popen() is almost certainly what you want.
System gives you full access to the shell. If you want to continue using it, you can
redirect it's output to a temporary file, by system("ls > tempfile.txt"), but choosing a secure temporary file is a pain. Or, you can even redirect it through another program: system("ls | otherprogram");
Some may recommend the popen() command. This is what you want if you can process the output yourself:
FILE *output = popen("ls", "r");
which will give you a FILE pointer you can read from with the command's output on it.
You can also use the pipe() call to create a connection in combination with fork() to create new processes, dup2() to change the standard input and output of them, exec() to run the new programs, and wait() in the main program to wait for them. This is just setting up the pipeline much like the shell would. See the pipe() man page for details and an example.
The functions popen() and such don't redirect stderr and such; I wrote popen3() for that purpose.
Here's a bowdlerised version of my popen3():
int popen3(int fd[3],const char **const cmd) {
int i, e;
int p[3][2];
pid_t pid;
// set all the FDs to invalid
for(i=0; i<3; i++)
p[i][0] = p[i][1] = -1;
// create the pipes
for(int i=0; i<3; i++)
if(pipe(p[i]))
goto error;
// and fork
pid = fork();
if(-1 == pid)
goto error;
// in the parent?
if(pid) {
// parent
fd[STDIN_FILENO] = p[STDIN_FILENO][1];
close(p[STDIN_FILENO][0]);
fd[STDOUT_FILENO] = p[STDOUT_FILENO][0];
close(p[STDOUT_FILENO][1]);
fd[STDERR_FILENO] = p[STDERR_FILENO][0];
close(p[STDERR_FILENO][1]);
// success
return 0;
} else {
// child
dup2(p[STDIN_FILENO][0],STDIN_FILENO);
close(p[STDIN_FILENO][1]);
dup2(p[STDOUT_FILENO][1],STDOUT_FILENO);
close(p[STDOUT_FILENO][0]);
dup2(p[STDERR_FILENO][1],STDERR_FILENO);
close(p[STDERR_FILENO][0]);
// here we try and run it
execv(*cmd,const_cast<char*const*>(cmd));
// if we are there, then we failed to launch our program
perror("Could not launch");
fprintf(stderr," \"%s\"\n",*cmd);
_exit(EXIT_FAILURE);
}
// preserve original error
e = errno;
for(i=0; i<3; i++) {
close(p[i][0]);
close(p[i][1]);
}
errno = e;
return -1;
}
The most efficient way is to use stdout file descriptor directly, bypassing FILE stream:
pid_t popen2(const char *command, int * infp, int * outfp)
{
int p_stdin[2], p_stdout[2];
pid_t pid;
if (pipe(p_stdin) == -1)
return -1;
if (pipe(p_stdout) == -1) {
close(p_stdin[0]);
close(p_stdin[1]);
return -1;
}
pid = fork();
if (pid < 0) {
close(p_stdin[0]);
close(p_stdin[1]);
close(p_stdout[0]);
close(p_stdout[1]);
return pid;
} else if (pid == 0) {
close(p_stdin[1]);
dup2(p_stdin[0], 0);
close(p_stdout[0]);
dup2(p_stdout[1], 1);
dup2(::open("/dev/null", O_WRONLY), 2);
/// Close all other descriptors for the safety sake.
for (int i = 3; i < 4096; ++i) {
::close(i);
}
setsid();
execl("/bin/sh", "sh", "-c", command, NULL);
_exit(1);
}
close(p_stdin[0]);
close(p_stdout[1]);
if (infp == NULL) {
close(p_stdin[1]);
} else {
*infp = p_stdin[1];
}
if (outfp == NULL) {
close(p_stdout[0]);
} else {
*outfp = p_stdout[0];
}
return pid;
}
To read output from child use popen2() like this:
int child_stdout = -1;
pid_t child_pid = popen2("ls", 0, &child_stdout);
if (!child_pid) {
handle_error();
}
char buff[128];
ssize_t bytes_read = read(child_stdout, buff, sizeof(buff));
To both write and read:
int child_stdin = -1;
int child_stdout = -1;
pid_t child_pid = popen2("grep 123", &child_stdin, &child_stdout);
if (!child_pid) {
handle_error();
}
const char text = "1\n2\n123\n3";
ssize_t bytes_written = write(child_stdin, text, sizeof(text) - 1);
char buff[128];
ssize_t bytes_read = read(child_stdout, buff, sizeof(buff));
The functions popen() and pclose() could be what you're looking for.
Take a look at the glibc manual for an example.
In Windows, instead of using system(), use CreateProcess, redirect the output to a pipe and connect to the pipe.
I'm guessing this is also possible in some POSIX way?
Actually, I just checked, and:
popen is problematic, because the process is forked. So if you need to wait for the shell command to execute, then you're in danger of missing it. In my case, my program closed even before the pipe got to do it's work.
I ended up using system call with tar command on linux. The return value from system was the result of tar.
So: if you need the return value, then not no only is there no need to use popen, it probably won't do what you want.
In this page: capture_the_output_of_a_child_process_in_c describes the limitations of using popen vs. using fork/exec/dup2/STDOUT_FILENO approach.
I'm having problems capturing tshark output with popen.
And I'm guessing that this limitation might be my problem:
It returns a stdio stream as opposed to a raw file descriptor, which
is unsuitable for handling the output asynchronously.
I'll come back to this answer if I have a solution with the other approach.
I'm not entirely certain that its possible in standard C, as two different processes don't typically share memory space. The simplest way I can think of to do it would be to have the second program redirect its output to a text file (programname > textfile.txt) and then read that text file back in for processing. However, that may not be the best way.
Related
I have to do this as a university project so I cant share the whole code, im sorry for that.
I have to create a function called "read" that enables the user to create new env variables, thats the easy part. The problem comes when I call that function as the last one of the commands array e.g "ls | grep aux.txt | read a" this should give the env var A the value aux.txt, the problem is that it get stuck in the
fgets(value, sizeof(value),stdin);
and I cant even recover the terminal.
Thanks in advance for the help if you need more info about the problem I will happily give it.
I can't reproduce exactly the main function as there are parts that are not mine but I hope this helps:
char **argvv;
int fd[2][2];
int pid;
int main(int argc, char ***argvv) {
argvv[0][0] = "echo";
argvv[0][1] = "elpmaxe";
argvv[1][0] = "rev";
argvv[2][0] = "read";
argvv[2][1] = "a";
for (int i = 0; i < 2; i++) {
pipe(fd[i]);
}
for(int i = 0; i< 3; i++){
pid = fork();
if(pid == 0){
if(i ==0){
dup2(fd[0][1], 1);
fun_close(fd);
execvp(argvv[0][0], argvv[0]);
}
if(i == 1){
dup2(fd[0][0], 0);
dup2(fd[1][1], 1);
fun_close(fd);
execvp(argvv[1][0], argvv[0]);
}
}else{
if(i == 2){
close(fd[0][1]);
close(fd[0][0]);
fun_read("read a", 3, fd[1]);
}
}
}
int corpse;
int status;
while ((corpse = wait(&status)) > 0)
printf("Child %d exited with status 0x%.4X\n", corpse, status);
return 0;
void fun_close(int **fd){
close(fd[0][0]);
close(fd[0][1]);
close(fd[1][0]);
close(fd[1][1]);
}
And here is the fun_read:
int fun_read(char **command, int argc, int fd[]){
char **env_varv;
char value[1024];
char last_var[1024];
long size = 0;
char *token;
int status;
char *delim = " \t\n";
env_varv = malloc((argc-1) * sizeof(char *));
for(int i = 1; i < argc; i++){
env_varv[i-1] = strdup(command[i]);
wait(status);
}
if (fd[0] !=0){
printf("%d\n", fd[0]);
dup2(fd[0],0);
close(fd[0]);
close(fd[1]);
}
fgets(value, sizeof(value),stdin);
int i = 0;
token = strtok(value, delim);
last_var[0] = '\0';
while(token != NULL){
if(i == argc-2){
while (token != NULL){
strcat(last_var,token);
setenv(env_varv[i],last_var,1);
token = strtok(NULL,delim);
strcat(last_var," ");
}
}
else if (env_varv[i] != NULL){
setenv(env_varv[i],token,1);
token = strtok(NULL,delim);
i++;
}
else{
break;
}
}
return 0;
The program should put an envariomental variable called a with the value of example.
postscript: it seems like there is no problem if the previous command is a builtin "echo hi | echo hi2 | read a" $a=hi2
Sincerely I have tried all, changing the pipes doesnt work, changing fgets for read doesn't help either. Is the only part of the code I haven't been able to fix
This fragment of code shows some problems:
char ***argvv;
int fd[2][2];
int pid;
int main(int argc, char ***argvv) {
argvv[0][0] = "echo";
argvv[0][1] = "elpmaxe";
argvv[1][0] = "rev";
argvv[2][0] = "read";
argvv[2][1] = "a";
for (int i = 0; i < 2; i++) {
pipe(fd[i]);
}
for(int i = 0; i< 3; i++){
pid = fork();
if(pid == 0){
if(i ==0){
close(fd[0][0]);
close(fd[1][1]);
close(fd[1][0]);
dup2(fd[0][1], 1);
execvp(argvv[0][0], argvv[0]);
}
if(i = 1){
close(fd[0][1]);
close(fd[1][0]);
dup2(fd[0][0], 0);
dup2(fd[1][1], 1);
execvp(argvv[1][0], argvv[0]);
}
if(i = 2){
close(fd[0][1]);
close(fd[0][0]);
close(fd[1][1]);
dup2(fd[1][0], 0);
fun_read("read a", 3, fd[1]);
}
}
}
Rule of Thumb
You aren't closing enough pipe file descriptors in any of the processes.
If you dup2()
one end of a pipe to standard input or standard output, close both of the
original file descriptors returned by
pipe()
as soon as possible.
In particular, you should close them before using any of the
exec*()
family of functions.
The rule also applies if you duplicate the descriptors with either
dup()
or
fcntl()
with F_DUPFD or F_DUPFD_CLOEXEC.
Other comments on the use of pipes
If the parent process will not communicate with any of its children via
the pipe, it must ensure that it closes both ends of the pipe early
enough (before waiting, for example) so that its children can receive
EOF indications on read (or get SIGPIPE signals or write errors on
write), rather than blocking indefinitely.
Even if the parent uses the pipe without using dup2(), it should
normally close at least one end of the pipe — it is extremely rare for
a program to read and write on both ends of a single pipe.
Note that the O_CLOEXEC option to
open(),
and the FD_CLOEXEC and F_DUPFD_CLOEXEC options to fcntl() can also factor
into this discussion.
If you use
posix_spawn()
and its extensive family of support functions (21 functions in total),
you will need to review how to close file descriptors in the spawned process
(posix_spawn_file_actions_addclose(),
etc.).
Note that using dup2(a, b) is safer than using close(b); dup(a);
for a variety of reasons.
One is that if you want to force the file descriptor to a larger than
usual number, dup2() is the only sensible way to do that.
Another is that if a is the same as b (e.g. both 0), then dup2()
handles it correctly (it doesn't close b before duplicating a)
whereas the separate close() and dup() fails horribly.
This is an unlikely, but not impossible, circumstance.
Analyzing your code
The parent process has the pipes open; if the commands are reading from the pipes, they won't get EOF until the parent process closes them. Although you close most of the pipes in the child processes, you don't close those that you duplicate to the standard I/O channels — and yet that is required too.
Note that if (i = 1) should be if (i == 1), and if (i = 2) should be if (i == 2). The first of those bugs prevents your fun_read() from being invoked — which is why it isn't responding. Using diagnostic printing to standard error would confirm that fun_read() is never called.
So, at bare minimum, you need to have code like this:
char ***argvv;
int fd[2][2];
int pid;
int main(int argc, char ***argvv)
{
argvv[0][0] = "echo";
argvv[0][1] = "elpmaxe";
argvv[1][0] = "rev";
argvv[2][0] = "read";
argvv[2][1] = "a";
for (int i = 0; i < 2; i++)
{
pipe(fd[i]);
}
for (int i = 0; i < 3; i++)
{
pid = fork();
if (pid == 0)
{
if (i == 0)
{
dup2(fd[0][1], 1);
close(fd[0][0]);
close(fd[0][1]);
close(fd[1][0]);
close(fd[1][1]);
execvp(argvv[0][0], argvv[0]);
fprintf(stderr, "failed to execute %s\n", argvv[0][0]);
exit(EXIT_FAILURE);
}
if (i == 1)
{
dup2(fd[0][0], 0);
dup2(fd[1][1], 1);
close(fd[0][0]);
close(fd[0][1]);
close(fd[1][0]);
close(fd[1][1]);
execvp(argvv[1][0], argvv[0]);
fprintf(stderr, "failed to execute %s\n", argvv[1][0]);
exit(EXIT_FAILURE);
}
if (i == 2)
{
dup2(fd[1][0], 0);
close(fd[0][0]);
close(fd[0][1]);
close(fd[1][0]);
close(fd[1][1]);
fun_read("read a", 3, fd[1]);
exit(EXIT_SUCCESS);
}
}
}
close(fd[0][0]);
close(fd[0][1]);
close(fd[1][0]);
close(fd[1][1]);
/* wait loop here - and not before */
int corpse;
int status;
while ((corpse = wait(&status)) > 0)
printf("Child %d exited with status 0x%.4X\n", corpse, status);
return 0;
}
Note that it is important to handle failure to execute. And error messages should be reported to standard error, not to standard output.
Given that the same sequence of 4 calls to close() is made 4 times, a function to do the job seems appropriate. You could make it:
static inline void close_pipes(int fd[2][2])
{
close(fd[0][0]);
close(fd[0][1]);
close(fd[1][0]);
close(fd[1][1]);
}
There is a decent chance the compiler will inline the function, but it is easier to see that the same 4 descriptors are closed if one function always does the closing. For bigger arrays of pipes (more processes), you'd have a loop inside the close_pipes() function with a counter as well as the array.
There are still some issues to be resolved, notably with the fun_read() function. The fd[1] file descriptors were both closed, so passing those to fun_read() doesn't seem likely to be useful. Since fun_read() is executed in a separate process, any changes made by fun_read() won't be reflected in the parent process. There are probably other problems too.
AFAICT, on looking at fun_read() more closely, the fd argument should not be needed at all. The paragraph of code:
if (fd[0] != 0) {
printf("%d\n", fd[0]);
dup2(fd[0], 0);
}
is not useful. You've already redirected standard input so it comes from the pipe and then closed the pipe file descriptor. This paragraph then changes standard input to come from the closed descriptor, which isn't going to help anything. But none of this helps you with the fact that anything done by fun_read() is done in a child process of your shell, so the environment in the main shell is not going to be affected.
I am developing a simple shell program, a command line interpreter and I wanted to read input from the file line by line, so I used getline() function. At the first time, the program works correctly, however, when it reaches the end of the file, instead of terminating, it starts to read a file from the start and it runs infinitely.
Here are some codes in main function that are related to getline():
int main(int argc,char *argv[]){
int const IN_SIZE = 255;
char *input = NULL;
size_t len = IN_SIZE;
// get file address
fileAdr = argv[2];
// open file
srcFile = fopen(fileAdr, "r");
if (srcFile == NULL) {
printf("No such file!\n");
exit(-1);
}
while (getline( &input, &len, srcFile) != -1) {
strtok(input, "\n");
printf("%s\n", input);
// some code that parses input, firstArgs == input
execSimpleCmd(firstArgs);
}
fclose(srcFile);
}
I am using fork() in my program and most probably it causes this problem.
void execSimpleCmd(char **cmdAndArgs) {
pid_t pid = fork();
if (pid < 0) {
// error
fprintf(stderr, "Fork Failed");
exit(-1);
} else if (pid == 0) {
// child process
if (execvp(cmdAndArgs[0], cmdAndArgs) < 0) {
printf("There is no such command!\n");
}
exit(0);
} else {
// parent process
wait(NULL);
return;
}
}
In addition, sometimes the program reads and prints a combinations of multiple lines. For example, if an input file as below:
ping
ww
ls
ls -l
pwd
it prints something like pwdg, pwdww, etc. How to fix it?
It appears that closing a FILE in some cases seeks the underlying file descriptor back to the position where the application actually read to, effectively undoing the effect of the read buffering. This matters, since the OS level file descriptors of the parent and the child point to the same file description, and the same file offset in particular.
The POSIX description of fclose() has this phrase:
[CX] [Option Start] If the file is not already at EOF, and the file is one capable of seeking, the file offset of the underlying open file description shall be set to the file position of the stream if the stream is the active handle to the underlying file description.
(Where CX means an extension to the ISO C standard, and exit() of course runs fclose() on all streams.)
I can reproduce the odd behavior with this program (on Debian 9.8):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(int argc, char *argv[]){
FILE *f;
if ((f = fopen("testfile", "r")) == NULL) {
perror("fopen");
exit(1);
}
int right = 0;
if (argc > 1)
right = 1;
char *line = NULL;
size_t len = 0;
// first line
getline(&line, &len, f);
printf("%s", line);
pid_t p = fork();
if (p == -1) {
perror("fork");
} else if (p == 0) {
if (right)
_exit(0); // exit the child
else
exit(0); // wrong way to exit
} else {
wait(NULL); // parent
}
// rest of the lines
while (getline(&line, &len, f) > 0) {
printf("%s", line);
}
fclose(f);
}
Then:
$ printf 'a\nb\nc\n' > testfile
$ gcc -Wall -o getline getline.c
$ ./get
getline getline2
$ ./getline
a
b
c
b
c
Running it with strace -f ./getline clearly shows the child seeking the file descriptor back:
clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f63794e0710) = 25117
strace: Process 25117 attached
[pid 25116] wait4(-1, <unfinished ...>
[pid 25117] lseek(3, -4, SEEK_CUR) = 2
[pid 25117] exit_group(1) = ?
(I didn't see the seek back with a code that didn't involve forking, but I don't know why.)
So, what happens is that the C library on the main program reads a block of data from the file, and the application prints the first line. After the fork, the child exits, and seeks the fd back to where the application level file pointer is. Then the parent continues, processes the rest of the read buffer, and when it's finished, it continues reading from the file. Because the file descriptor was seeked back, the lines starting from the second are again available.
In your case, the repeated fork() on every iteration seems to result in an infinite loop.
Using _exit() instead of exit() in the child fixes the problem in this case, since _exit() only exits the process, it doesn't do any housekeeping with the stdio buffers.
With _exit(), any output buffers are also not flushed, so you'll need to call fflush() manually on stdout and any other files you're writing to.
However, if you did this the other way around, with the child reading and buffering more than it processes, then it would be useful for the child to seek back the fd so that the parent could continue from where the child actually left.
Another solution would be not to mix stdio with fork().
I'm fine executing commands like "ls" and stuff like that but I want to do something like "ls | sort" but the execvp system call doesn't support "|". How can I do this using only system calls? when I try something like
char *arg[] = {"ls","|","sort",NULL};
execvp(arg[0],arg);
it doesn't work, how can I do this?
Edit:
char* execString (char string[]){
int link[2];
pipe(link);
if (fork() == 0){
int i = 0;
char *p = strtok(string," ");
char *x[spacecount(string)+2];
while(p){
x[i++] = p;
p = strtok(NULL," ");
}
x[i] = NULL;
dup2(link[1],1);
close(link[0]);
close(link[0]);
execvp(x[0],x);
_exit(0);
} else {
wait(NULL);
close(link[1]);
char buf[512];
int i = 0;
while (read(link[0],&buf[i++],1) == 1);
close(link[0]);
buf[i-2] = '\0';
return strdup(buf);
}
}
This is the function i'm executing to exec a string that contains a command, its return value is a pointer to a string that contains the output from that command, how can I use that output as the input to a new command using execvp or another function from the exec family?
Edit2: So I made a new function that receives two strings as argument and execs the first one then the second one using as input the output from the first exec, I thought it was working fine it worked with ls | head -1 and other variations of ls but when I do something like ls | sort -R it doesn't work, i've tried several things and I can't understand why this is happening, here is the code:
char* execStrings (char previousstring[], char string[]){
int link[2];
pipe(link);
if (fork() == 0){
int i = 0;
char *previouscommand[spacecount(previousstring)+2];
char *temp = strtok(previousstring," ");
while(temp){
previouscommand[i++] = temp;
temp = strtok(NULL," ");
}
previouscommand[i] = NULL;
dup2(link[1],1); /* stdout result redrecting to write end of pipe */
close(link[1]);
close(link[0]);
execvp(previouscommand[0],previouscommand);
} else {
wait(NULL);
int res[2];
pipe(res);
if(fork() == 0){
int i = 0;
char *temp = strtok(string," ");
char *command[spacecount(string)+2];
while(temp){
command[i++] = temp;
temp = strtok(NULL," ");
}
command[i] = NULL;
dup2(link[0],0);
close(link[0]);
close(link[1]);
dup2(res[1],1);
close(res[1]);
close(res[0]);
execvp(command[0],command)
} else {
wait(NULL);
close(res[1]);
char buf[512];
int i = 0;
while (read(res[0],&buf[i++],1) == 1);
close(res[0]);
buf[i-2] = '\0';
return strdup(buf);
}
}
}
you want to do something like ls | sort but the way you are doing like
char *arg[] = {"ls","|","sort",NULL};
execvp(arg[0],arg); /*it won't work */
won't work because here you are calling execvp on ls and sort which are two separate process not single process. Also
ls | sort => output of process-1 make as input to process-2 & execute it
| |
process-1 process-2
To achieve the above create two process by calling fork() and use exec() family function to replace ls and sort in child & parent process.
here is the sample code
int main(void) {
int p[2];
pipe(p);
char *arg[] = {"ls","sort",NULL};
if(fork()==0) {
close(0);/* close the stdin stream so that this
process shoulbn't read from stdin */
dup(p[0]);/* read from read end of pipe */
close(p[1]);
execlp(arg[1],arg[1],(char*)NULL);
}
else{
close(1);/* close the stdout stream, so that o/p shouldn't print on monitor */
dup(p[1]); /* stdout result redrecting to write end of pipe */
close(p[0]);
execlp(arg[0],arg[0],(char*)NULL);
}
return 0;
}
| is a shell feature. You need to do the same thing the shell does, i.e. use pipe, fork, dup2, execvp to create a pipe, spawn a new process, and connect the pipe to the processes' stdin and stdout, respectively.
Take the code from your execString function but replace the else block. In the parent process you should dup2(link[0], 0) (connect the read end of your pipe to stdin), then execvp the other program (e.g. sort).
Remember to close the pipe ends you don't need! I.e. in the producer process, close(link[0]); in the consumer process, close(link[1]). If you forget this part, things may get "stuck" (commands seemingly hanging forever).
Of course, if you want your original program to keep going, you need to wrap that code inside another fork (and waitpid for it).
As an aside, your execString is broken if the command outputs a lot of data. At some point the pipe will get full and the command will pause, waiting for another process to drain the pipe (by reading from it). Your program will be stuck in wait, waiting for the command to terminate. The result is deadlock.
To fix this issue, only call wait after you're done reading from the pipe.
I'm trying to write a program that reads some text from a file and prints it to the screen. The parent will read the content of the file write it to n number of pipes and the children will read it and then print it.
So far this is what I've got:
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <string.h>
int main (void)
{
pid_t pid;
char c;
FILE *fd;
char buf[100];
int N_CHILDREN = 2;
int p[N_CHILDREN][2];
int i,j;
for(i=0; i<N_CHILDREN; i++)
{
pipe(p[i]);
}
fd=fopen("123.txt","r");
for(j=0; j < N_CHILDREN;j++)
{
pid = fork ();
if (pid == 0)
{
close (p[j][1]);
while(read(p[j][0], &fd,sizeof(buf)) > 0)
printf("\n%c",&fd);
}
if (pid < 0)
{
//Fork Failed
fprintf (stderr, "Fork failure.\n");
return EXIT_FAILURE;
}
if ( pid > 0) //Parent
{
close (p[j][0]);
write(p[j][1], fd ,sizeof(buf));
}
}
}
Problem is it's not really reading the content from the file. I've tried sending it a string of characters instead of reading from a file and it worked as intended, both children printed the message one time and the program ended.
Any thoughts about it? After reading the manuals I still can't see where the problem is.
You are confusing C Standard I/O streams (created with fopen(); written to with fprintf() et al., read with fscanf() et al.) with Unix file descriptor I/O (created with open() or pipe() et al., written to with write() et al., read with read() et al.)
Standard I/O functions take an opaque FILE * as a handle; Unix I/O functions take a file descriptor (a small int) as a handle.
Once you understand the conceptual difference, I'm sure you will realize that
FILE *fd = ...
read(..., &fd, ...);
is reading into a pointer-to-FILE -- not terribly useful :-)
Several problems here:
you make bad usage of read function by passing &fd, which is a FILE*. This function needs a pointer to the "buffer" to print, here I guess buf.
you don't check errors. For example if fopen fails.
you never read data from your file, so you have "nothing" to send to children.
you have to get returned value of read (in children) because it is the effective amount of data that you get. So it is the amount of data that you have to print after that (to stdout).
So here is an example code, see comments inside:
// put here all the needed includes (see manpages of functions)
// it is better to create a function for the child: the code
// is easier to read
// the child just get the file descriptor to read (the pipe)
void child(int fd) {
char buf[100]; // buffer to store data read
int ret; // the number of bytes that are read
// we read from 'fd', into 'buf'. It returns the number of bytes
// really read (could be smaller than size). Return <=0 when over
while((ret = read(fd, buf, sizeof(buf))) > 0) {
// write the 'ret' bytes to STDOUT (which as file descriptor 1)
write(1, buf, ret);
}
}
int main (void) {
pid_t pid;
char buf[100];
int N_CHILDREN = 2;
int p[N_CHILDREN][2];
int i,j, ret;
int fdi;
// create the pipes
for(i=0; i<N_CHILDREN; i++) {
if (pipe(p[i]) == -1) {
perror("pipe"); // ALWAYS check for errors
exit(1);
}
}
// open the file (with 'open' not 'fopen', more suitable for
// reading raw data
fdi = open("123.txt",O_RDONLY);
if (fdi < 0) {
perror("open"); // ALWAYS check for errors
exit(1);
}
// just spawn the children
for(j=0; j < N_CHILDREN;j++) {
pid = fork();
if (pid < 0) {
perror("fork"); // ALWAYS check for errors
exit(1);
}
if (pid == 0) { // child
close(p[j][1]); // close the writing part
child(p[j][0]); // call child function with corresp. FD
exit(0); // leave : the child should do nothing else
}
}
// don't need that part
for(j=0; j<N_CHILDREN; j++) {
close(p[j][0]); // close the read-part of pipes
}
// need to read file content, see comment in child() function
while ((ret = read(fdi, buf, sizeof(buf))) > 0) {
// write the data to all children
for(j=0; j<N_CHILDREN; j++) {
write(p[j][1], buf , ret); // we write the size we get
}
}
// close everithing
for(j=0; j<N_CHILDREN; j++) {
close(p[j][1]); // needed, see text after
}
close(fdi); // close read file
return(0); // main returns a int, 0 is "ok"
}
You have to close every parts of pipes when not needed or when it is over. Until a file descriptor is open a read will block the process. Only when last write counterpart is closed the read returns <=0.
Note: 1. the correct usage of read/write function 2. checking for errors 3. reading from the file and writing to the pipe(s) 4. dealing with effective amount of data read (ret variable) so that you can write (to "screen" or to an other file descriptor the right amount of data.
You're not reading anything in to buf as far as I can tell.
I want to preface this with the fact that I have no formal education in the use of pipes, so this is my first venture. Not to mention that I couldn't find any similar questions to my situation.
Note: This IS part of a larger project for a school assignment, so I am NOT asking for anyone to do this for me. I would just like some direction/helpful code segments. (I have tried to make this as generic as possible to avoid "cheater" remarks.)
I am trying to run a for-loop over int k elements in which a parent process spawns off k children with fork() and execl(), and then use a pipe() to send the output back to the parent. Here is some generic code that I am trying to use and the error/problem in which I encounter:
Note: helloworld= an executable compiled with GCC that produces printf("hello world\n");
int k = 10; //The number of children to make
int fd[2]; //Used for each pipe
int readFromMe[k]; //Holds the file IDs of each pipe fd[0]
int writeToMe[k]; //Holds the file IDs of each pipe fd[1]
int processID[k]; //Holds the list of child process IDs
//Create children
int i;
for(i = 0; i < k; i++)
{
if(pipe(fd) == -1)
{
printf("Error - Pipe error.\n");
exit(EXIT_FAILURE);
}
//Store the pipe ID
readFromMe[i] = fd[0];
writeToMe[i] = fd[1];
if((processID[i] = fork()) == -1)
{
fprintf(stderr, "fork failure");
exit(EXIT_FAILURE);
}
//If it is a child, change the STDOUT to the pipe-write file descriptor, and exec
if(processID[i] == 0)
{
dup2 (writeToMe[i], STDOUT_FILENO);
close(readFromMe[i]);
execl("./helloworld", (char *)0);
}
//If it is the parent, just close the unnecessary pipe-write descriptor and continue itterating
else
{
close(writeToMe[i]);
}
}
//Buffer for output
char output[100000];
//Read from each pipe and print out the result
for(i = 0; i < k; i++)
{
int r = read(readFromMe[i], &output, (sizeof(char) * 100000));
if(r > 0)
{
printf("result = %s\n", output);
}
close(readFromMe[i]);
}
I get no output from my program at all, so I am trying to figure out why this issue is occurring.
Probably unrelated, but you call execl wrong. The extra arguments after the program is what will the the argv array to the other programs main function. And as you know it always have one entry, the program name. So you need to call it like this:
execl("./helloworld", "helloworld", NULL);
More related to your problem, you should also check for errors, it might actually fail.
Try printing the value of 'r' in your printout function. I suspect the read is returning an error (perhaps EPIPE) that you're not seeing. Also, you example code is trying to printf 'c', not output like it looks like you meant.