I am trying to implement multiple pipes in C like
ls - al | less | wc
I have trouble with creating the pipeline. I have a loop that is supposed to create the processes and connect them with pipes:
for(i=0;i<num_cmds;i++){
create_commands(cmds[i]);
}
My create_commands() function looks like this
void create_commands (char cmd[MAX_CMD_LENGTH]) // Command be processed
{
int pipeid[2];
pipe(pipeid);
if (childpid = fork())
{
/* this is the parent process */
dup2(pipeid[1], 1); // dup2() the write end of the pipe to standard output.
close(pipeid[1]); // close() the write end of the pipe
//parse the command
parse_command(cmd, argvector);
// execute the command
execvp(argvector[0], argvector);
close(1); // close standard output
}
else
{
/* child process */
dup2( pipeid[0], 0); // the read end of the pipe to standard input
close( pipeid[0] ); // close() the read end of the pipe
}
}
But this doesn't work, I'm getting my stdin and stdout messed up.
Could anyone please point me to what I am doing wrong?
Thank you in advance!
The popen() function executes the command specified by the string command. It creates a pipe between the calling program and the executed command, and returns a pointer to a stream that can be used to either read from or write to the pipe.
#include <stdio.h>
int main(int argc, char *argv[])
{
FILE *fp;
int status;
int PATH_MAX = 1024;
char path[PATH_MAX];
fp = popen("ls -al | less | wc", "r");
if (fp == NULL)
/* Handle error */;
while (fgets(path, PATH_MAX, fp) != NULL)
printf("%s", path);
status = pclose(fp);
if (status == -1) {
/* Error reported by pclose() */
} else {
/* Use macros described under wait() to inspect `status' in order
to determine success/failure of command executed by popen() */
}
}
You can use a preset string to be called within popen(), you can also use your argv[] arguments to be piped in you'ld like.
popen() gives you a pipe, a FIFO First In First Out stream, and popen also feeds the STDOUT back to your program.
Here's the man page for popen():
http://linux.die.net/man/3/popen
Related
I have two C programs and am trying to call some child program "child.c" inside of some parent program "parent.c", and capturing the output to stdout from child.c. How would I go about doing this?
I am using macOS.
Here's an example of what parent.c and child.c might look like
parent.c
while (1)
{
// call the child program
// capture the output from the child
if (child_output == some_condition)
{
break;
}
}
child.c
printf("Hello world!")
Thanks for the help.
Simply use popen() and create a stream object of type FILE * which you can use with fread()/fgets() to get the output from the child program. Reading the manual page should be enough to get you started.
But here is an example
#include <stdio.h>
int
main(void)
{
FILE *pipe;
char line[256];
pipe = popen("ls", "r");
if (pipe != NULL) {
while (fgets(line, sizeof line, pipe) != NULL) {
fprintf(stdout, "%s", line);
}
pclose(pipe);
}
return 0;
}
Also, read the manual to get an idea of how this actually works.
I have a problem with child processes and pipes in C.
My program creates two child processes (silblings) and also two pipes. The father reads from stdin and sends the data through the first pipe. The first child redirects the stdin to the first pipe and the stdout to the second pipe. After that the child runs gzip -cf with execlp(3). The second child reads from the second pipe and writes the data in the stdout (or in a file).
I tried it but it doesnt work with the binary data. If the first child only sends the text data through the second pipe (without using gzip) and the second child reads it, the program works fine. But if I use gzip with execlp() and the process writes binary data the program doesn't work, because I don't get all data.
Here is a part of the code:
First child:
/* Close the pipe-ends which are not needed [...] */
/* Redirect the stdin and stdout with dup2 [...] */
if(execlp(EXEC,EXEC_ARG0,EXEC_ARG1,(char*) 0) == -1) /* run execlp with 'gzip -cf' */
bail_out(EXIT_FAILURE,"Child_1: Cannot execute "EXEC_ARG0" "EXEC_ARG1"!");
Second child:
/* Close the pipe-ends which are not needed [...] */
FILE *fd = NULL;
char str[4096];
fd = fdopen(pipefd_com2[0],"rb");
(void) fread(str,sizeof(str),1,fd); /* read from pipe */
(void) fclose(fd);
FILE *fw = NULL;
if(file_arg == NULL) /* check if there was a file as argument */
fw = stdout;
else
fw = fopen(file_arg,"wb");
if(fw == NULL)
bail_out(EXIT_FAILURE,"Failed to write file!"); /* does not return */
(void) fwrite(str,strlen(str),1,fw); /* write to stdout or file */
(void) fclose(fw);
The strlen function is for C-style strings, not for binary data. Also, your parameters to fread are wrong. The second parameter is the size of each object you are reading. For arbitrary binary data, that should be 1, since each object is a byte. That will make the return value from fread the number of bytes you read, which is what you'll need to pass to fwrite as the third parameter.
In sum:
size_t bytes_read;
// ...
bytes_read = fread(str,1,sizeof(str),fd); /* read from pipe */
// ...
(void) fwrite(str,1,bytes_read,fw); /* write to stdout or file */
You should also check the return value from fread.
I want to run node.js as a subprocess and feed it input. Using C, here is some sample code of mine that does that.
The issue I have is that although the subprocess's stdout is still directed to the terminal, I see nothing after having fed the subprocess stdin a print 'Hello World' line. Even if I fflush() the pipe, I see nothing on output. However, if I close the pipe's input, then the 'Hello World' appears on the terminal.
The subprocess seems to simply buffer - why is that?
I would like to eventually redirect the subprocess stdout to another pipe and read it
in from main().
int main(int argc, char* argv[]) {
int toNode[2];
pipe(toNode);
pid_t child_pid = fork();
if (child_pid == 0) { // child
// close write end
close(toNode[1]);
// connect read end to stdin
dup2(toNode[0], STDIN_FILENO);
// run node executable
char* arg_list[] = { "/usr/bin/node", NULL};
execvp(arg_list[0], arg_list);
fprintf(stderr, "process failed to start: %s\n", strerror(errno));
abort();
}
else { // parent
FILE* stream;
// close read end
close(toNode[0]);
// convert write fd to FILE object
stream = fdopen(toNode[1], "w");
fprintf(stream, "console.log('Hello World');\n");
fflush(stream);
//close(toNode[1]);
waitpid(child_pid, NULL, 0);
}
return 0; }
There's no problem with the pipe being read. The problem is that /usr/bin/node only invokes the REPL (read-eval-print loop), by default, if it detects that stdin is interactive. If you have a sufficiently recent version of nodejs, then you can provide the -i or --interactive command line flag, but that will do more than just execute each line as it is read; it also really will act as a console, including inserting ANSI colour sequences into the output and printing the value of each expression.
See this forum thread for more information.
I'm trying to make a C program in UNIX in which the parent process generates two child processes.
The parent will read data from stdin and will send it to his children.
The 1st child will show on screen the text read from his father, to standard output.
The 2nd child will show the read data in a file. When the parent has as input an "exit", he will send termination signals to his children and will exit.
So here is the complete code I did, but where I need help is in void ProcesoHijo2(). I still have a warning when I have to compile,this one:
74: warning: passing argument 3 of ‘fgets’ makes pointer from integer without a cast
/usr/include/stdio.h:624: note: expected ‘struct FILE * __restrict__’ but argument is of type ‘int’ -->in
void ProcesoHijo2()
The program is in spanish, so the variable names too, I hope that won't be a problem and you can help me soon because I'm desperate...THANK YOU!!!!
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <fcntl.h>
#include <signal.h>
int pidHijo1;
int descrTub1[2];
int pidHijo2;
int descrTub2[2];
void ProcesoHijo1();
void ProcesoHijo2();
void ProcesoPadre();
int main(int argc, char *argv[]) {
pipe(descrTub1);
pipe(descrTub2);
pidHijo1 = fork();
if (pidHijo1 == 0) {
ProcesoHijo1();
return 0;
}
pidHijo2 = fork();
if (pidHijo2 == 0)
ProcesoHijo2();
else
ProcesoPadre();
}
void ProcesoHijo1() {
char bufferLectura[256];
close(0);
dup(descrTub1[0]);
while (1) {
fgets(bufferLectura, 255, stdin);
printf("%sn", bufferLectura);
}
}
void ProcesoHijo2() { //-->So here is where I have trouble to program...I don't know if it's just something from inside this process or from above...
char bufferLectura[256];
int descrFichero;
close(0);
dup(descrTub1[0]);
descrFichero = open("salida.txt", O_CREAT | O_TRUNC, 0600);
descrFichero = open("salida.txt", O_RDWR | O_TRUNC, 0600);//-->do I need this one?
while(1) {
fgets(bufferLectura,255,descrTub1[0]); //-->I have to read it from pipe and save it in bufferLectura, but I don't know how to put it...
write(descrFichero, bufferLectura, strlen(bufferLectura));
descrFichero = open("salida.txt", O_WRONLY|O_APPEND, 0600); //-->Is this correct?
}
}
void ProcesoPadre() {
char bufferLectura[256];
close(2);
dup(descrTub1[1]);
printf("[Proceso padre]Introduce un texto, o exit para salir");
fgets(bufferLectura, 255,stdin);
while(strcmp(bufferLectura, "exitn")) {
fprintf(stderr,"%s/n", bufferLectura);
write(descrTub2[1], bufferLectura, strlen(bufferLectura));
printf("[Proceso padre] Introduce un texto, o exit para salir ");
fgets(bufferLectura, 255,stdin);
}
kill(pidHijo1, SIGTERM);
kill(pidHijo2, SIGTERM);
}
The function fgets wants a pointer to FILE, while you are giving it a file descriptor which is an int.
You can get a FILE pointer from a file descriptor by using the function fdopen:
FILE *fp = fdopen(descrTub1[0], "r");
And use that in the call to fgets:
fgets(bufferLectura,255,fp);
pipe is OS/lower level and gives you a unix file descriptor (int). fgets is libstdc/higher level and uses FILE*. You can either read from the file low-level style (OS function read) or you can use fdopen to get a FILE* for your descrTub1[0].
dup returns file descriptors which are int & not file streams i.e. FILE * so you are getting the warning. Use read instead of fgets or use fdopen to get file stream FILE* for the file descriptor to use with fgets. Open the file only once with either one of the open calls outside the while as per your need and close once you are done with the operations. As you are using a lot of system calls check the return value & in case of failure use perror or srterror to print meaningful error messages (which are useful to debug) maybe something on these lines:
if( dup(descrTub1[0]) < 0 )
{
perror("dup");
/*Handle error */
}
Hope this helps!
Continuing on this problem, but I'll reiterate:
For a homework assignment I have to write a basic shell including redirection. The program uses readline to prompt for input, parses the input string, and breaks it down into the executable name, the arguments, and the input/output file(s), if applicable. After parsing the string, it forks and the child execv()'s to the executable that was passed in. I'm using dup2() to change the file descriptors after the fork and before the execv, but am having a problem once the program has execv'd to the new executable. If in my shell I run ls > foo.out, I get: ls: write error: Bad file descriptor
Here is the code for my child process (this is after fork()):
int _child(struct command *c){
int ret;
/* When given `ls > foo.out`, these next two lines output:
** c->infile is (null)
** c->outfile is foo.out
*/
printf("c->infile is %s\n",c->infile);
printf("c->outfile is %s\n",c->outfile);
if(c->infile){
int fd = open( c->infile, O_RDONLY);
int _fd_dup = dup2(fd,0);
close(0);
if(_fd_dup != 0){
fprintf(stderr, "Failed to redirect command input.\n");
return 0;
}
}
if(c->outfile){
int fd = open( c->outfile, O_WRONLY | O_CREAT | O_TRUNC, 0600);
int _fd_dup = dup2(fd,1);
close(1);
if(_fd_dup != 1){
fprintf(stderr, "Failed to redirect command output.\n");
return 0;
}
}
I do not get the "Failed to redirect command output." error. As I mentioned, this is a homework assignment so I'm not looking for anyone to fix this, but rather point me in the right direction.
The problem is in this bit of code:
int _fd_dup = dup2(fd,1);
close(1);
You should be closing fd, not 1. You have the same problem in the fd 0 case, too.