Linux C - child process thinks the pipe is empty - c

I have a problem with this homework exercise. I am learning the Linux C so I am a beginner.
Now the exercise is simple: I have to create a child process. Now the parent process needs to read a text file (e.g. a.txt) and sends through a pipe. The child process reads from pipe and prints the content of the pipe to the terminal. But I don't understand that the child process doesn't read the pipe because it thinks the pipe is empty.
I post the code what I did so far:
#include "myinclude.h" //a separate file which contains all needed headers to run the program.
#define MERET 80
int main(int argc,char *argv[]){
int pfd[2];
int status;
char buffer[MERET];
pid_t pid;
FILE *fp1,*fp2;
if(argc != 2){
printf("Nincs eleg argumentum");
}
if(pipe(pfd) < 0){
syserr("pipe");
}
if((pid = fork()) < 0){
syserr("fork");
}
if(pid == 0){
close(pfd[1]);
if ((fp1 = fdopen (pfd[0],"r")) <0){
syserr("fdopen");
}
printf("mukodsz");
while(fgets(buffer,MERET,fp1) != NULL){//something here is not good
printf("%s",buffer);
fprintf(stdout,"Siker");
}
close(pfd[0]);
exit(0);
}
close(pfd[0]);
if ((fp1 = fdopen (pfd[1],"w")) == NULL){
syserr("fdopen");
}
if((fp2 = fopen(argv[1],"r")) < 0){
syserr("fopen");
}
while(fgets(buffer,MERET,fp2) != NULL){
fprintf(fp1,"%s",buffer);
//fprintf(stdout,"Siker\n");
}
close(pfd[1]);
wait(&status);
//fprintf(stdout,"Siker");
exit(0);
}
In my language "siker" means Success. I used it to debug the program but while loop of the child process is not printing anything.

When you fdopen. you must fclose.
If you close the original file descriptor instead, all not-yet-written data in buffers associated with the FILE* get lost.

Related

Having issues when using dup2 twice within the same process

I cannot figure out why does this program display only garbage characters when trying to print (at the standard output) some data read from both a file and a pipe. It is interesting that this problem occurs only when I use more than one dup2 instruction within the same process.
#define BSIZE 256
const char in[] = "i.txt";
int main(){
int id, fd[2];
pipe(fd);
id = fork();
if(id == 0){
close(fd[0]);
dup2(fd[1],1);
printf("blbl");
close(fd[1]);
}
if(id != 0){
close(fd[1]);
char tmp[BSIZE];
int f = open(in,O_RDONLY);
dup2(f,0);
fgets(tmp,BSIZE,stdin);
puts(tmp);
dup2(fd[0],0);
fgets(tmp,BSIZE,stdin);
puts(tmp);
close(fd[0]);
close(f);
wait(NULL);
}
return 0;
}

Pipeline multiple children from same parent

Good morning, I've been "fighting" with this method for a long time and finally I decided to ask for help because I don't get what am I doing wrong. I am trying to create multiple children from the same parent and make the STDOUT of "child 1" the STDIN of "child 2" that way until there are no more children like a pipeline.
My actual code
void filter(void) {
if(Number_cmd != 0) {
int p,i;
int fd[2];
for(i=0;i<Number_cmd;i++)
pipe(fd);
for(p=(Number_cmd-1); p>=0; p--){
switch(fork()) {
case -1:
perror("fork");
exit(1);
case 0:
/* Child */
close(fd[1]);
close(0);
dup(fd[0]);
close(fd[0]);
execlp(filter[p], filter[p], NULL);
perror("exec");
exit(1);
default:
/* Father */
close(fd[0]);
close(1);
dup(fd[1]);
close(fd[1]);
break;
}
}
}
}
void directory(char* directory_name) {
DIR* dir = NULL;
struct dirent* ent;
char fich[1024];
char buff[4096];
int fd, reading;
struct stat sdata;
dir = opendir(directory_name);
while((ent=readdir(dir))!=NULL) {
if(ent->d_name[0]=='.')
continue;
fich[0]='\0';
strcat(fich, directory_name);
strcat(fich, "/");
strcat(fich, ent->d_name);
stat(fich,&sdata);
if(S_ISDIR(sdata.st_mode))
continue;
fd = open(fich, O_RDONLY);
while((reading= read(fd, buff, 4096)) > 0){
(write(1, buff, reading) < reading);
continue;
}
close(fd);
}
closedir(dir);
}
The problem is when im trying to call the method with more than one command, it looks like ii doesnt do anything, but when i run it with one command is working ok.
Thank everyone in advance. (Sorry for my English; it's not my native language)
EDIT
This is the main method:
char** cmd;
int Number_cmd;
int main(int argc, char* argv[]){
cmd = &(argv[2]); /*list of the commands*/
Number_cmd = argc-2; /* number of commands*/
filter();
directory(argv[1]);
return 0;
}
Problem 1:
for(i=0;i<Number_cmd;i++)
pipe(fd);
Unless the variable Number_cmd is 1 (or smaller), you leak pipe descriptors like crazy. You need some sort of array of file descriptors:
int fds[Number_cmd][2];
for (int i = 0; i < Number_cmd; i++)
if (pipe(fd[i]) != 0)
…report error and abandon ship (remembering to close any opened pipes)…
Problem 2:
This is mainly a consequence of Problem 1 — but you don't close enough file descriptors. Essentially, if you have N pipes open, your child will end up closing 2*N file descriptors, after duplicating two to standard input and standard output. The first and last children will be different; they don't override standard input and standard output respectively.
There are probably other issues, but these two spring to mind at once from a fairly quick look at the code.

Passing data from a parent to a child and back with Unix C

I am very new to C and am trying to learn how to use pipes to transfer data from a parent process to a child process and vice versa in a Unix environment. In the code below, I receive a command line argument and build a char array based on the argument's value. I then use pipes to pass the char array to a child which will execute a program called vc. This program returns a number result based on the char array. My question is how can I use a second pipe to get the result back to the parent? Also, once the parent has it, how can I print it to the screen since the parent is set to send output to the child? Thank you.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
int main(int argc,char *argv[])
{
int
pfildes[2],
pid,
argNumber;
char
buffer[256],
charString[1024];
//Check for arguments
if(argc != 2) {
printf("No command line arguements given.\n");
argNumber=10; //default
}
else
argNumber=atoi(argv[1]);
//***********************************
//Build charString based on argNumber
//***********************************
//create pipes
if(pipe(pfildes) == -1) {
//error occured when creating pipe
perror("demo");
exit(1);
}
//create child process
if((pid=fork()) < 0) {
//error occured when forking child
perror("demo");
exit(1);
}
else if(pid > 0) {
//parent process
close(pfildes[0]);
dup2(pfildes[1],1);
printf("%s", charString);
close(pfildes[1]);
perror("demo");
_exit(1);
}
else {
//child process
close(pfildes[1]);
dup2(pfildes[0],0);
execlp("/usr/bin/vc","vc", NULL);
close(pfildes[0]);
perror("demo");
exit(1);
}
while(wait(NULL) >0);
return 0;
}
You can use socketpair() instead of pipe() to generate a bidirectional communication channel between the parent and child process:
//...
if (socketpair(PF_UNIX, SOCK_STREAM, 0, pfildes) == -1) {
//error occured when socket pair
perror("demo: socketpair");
exit(1);
}
//..
In the child process, you can dup() one of the pair into both input and output before calling exec():
//...
else {
//child process
close(pfildes[1]);
dup2(pfildes[0],0);
dup2(pfildes[0],1);
dup2(pfildes[0],2);
close(pfildes[0]);
execlp("/usr/bin/vc","vc", NULL);
perror("demo: child exec");
exit(1);
}
//...
In the parent process, you can create a FILE * from a file descriptor using fdopen(), so you don't need to dup() over your existing stdout file descriptor:
//...
else if(pid > 0) {
//parent process
close(pfildes[0]);
FILE *to_child = fdopen(dup(pfildes[1]), "w");
FILE *from_child = fdopen(dup(pfildes[1]), "r");
close(pfildes[1]);
fprintf(to_child, "%s", charString);
while (fgets(buf, sizeof(buf), from_child) != NULL) {
//...do something with output
}
//...
} else { //...

Troubles with a pipe and a fork

I'm making a program that search files and sends it's results to other commands, like a pipe. ls | sort
When I run the program nothing happens.The problem I think is that the child's waits for the parent to stop writting in the SO buffer for starting the reading.
This is what it sends to stdout and what the pipe should send to the other command.
troneras#troneras-VirtualBox:~/Escritorio/busca.2012$ ./busca . -n . -print
./permisos.txt
./busca2.c
./mmap.pdf
./busca3.c~
./cuadernoso4.2011b.pdf
./busca.c~
./busca.c
./busca2.c~
./busca3.c
I don't understand what the problem is.
if(!strcmp(argv[4],"-pipe"))
{
int pipefd[2];
int pid,dummi;
if (pipe(pipefd)<0){
perror("pipe");
exit(1);
}
pid = fork();
if (pid<0){
perror("fork");
exit(1);
}
if (pid == 0){//Child process
close(pipefd[1]);//The child is only reading from the pipe
if(dup2(pipefd[0],0)!=0){perror("dup2");exit(1);}
close(pipefd[0]);
char *argumentos[argc-4];
int j;
for (j=5;j<argc;j++){
argumentos[j-5]=argv[j];
}
argumentos[j-5]= NULL;
execvp(argv[5],argumentos);
perror("execve: ");
}else{ //parent
close(pipefd[0]);
if(dup2(pipefd[1],1)!=1){perror("dup2");exit(1);}
close(pipefd[1]);
while(count--){
if(strcmp(files[count]->d_name,".") && strcmp(files[count]->d_name,"..")){
printf("%s/%s\n",argv[1],files[count]->d_name);
free(files[count]);
}
wait(&dummi);
}
}//end pipe
free(files);
BTW There is no reason to duplicate the argv[] array. Instead of
char *argumentos[argc-4];
int j;
for (j=5;j<argc;j++){
argumentos[j-5]=argv[j];
}
argumentos[j-5]= NULL;
execvp(argv[5],argumentos);
You could just as well do
execvp(argv[5],argv+5);

process termination doesn't affect waitpid()

I need to simulate the following bash commands using C under Linux (with fork, exec, kill, signal, wait, waitpid, dup2, open, sleep, pipe etc).
[0] echo 'tail-f $1' > /tmp/rtail
[1]/tmp/rtail ~/.bash_history >> /tmp/1.txt &
PID of process [1] should be saved.
[2] Expect termination of the command started on step [1]. After termination print on the screen: "Program 1 terminated."
So far I have this code:
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
int main(int argc, char *argv[]) {
pid_t pID = fork();
if (pID == 0) // child
{
int file = open("/tmp/rtail", O_CREAT | O_WRONLY);
//Now we redirect standard output to the file using dup2
dup2(file, 1);
puts("tail -f $1");
close(file);
system("chmod 777 /tmp/rtail");
exit(0);
} else if (pID < 0) // failed to fork
{
printf("Failed to fork");
exit(1);
// Throw exception
} else // parent
{
pid_t pID2 = fork();
if (pID2 == 0) {
char tmp1[20];
sprintf(tmp1, "echo %i > /tmp/pidprog1", getpid());
system(tmp1);
int file = open("/tmp/1.txt", O_APPEND | O_WRONLY);
//Now we redirect standard output to the file using dup2
dup2(file, 1);
FILE* proc = popen("sh /tmp/rtail ~/.bash_history", "r");
char tmp[20];
while (fgets(tmp, 40, proc) != NULL) {
printf(tmp);
}
fclose(proc);
exit(0);
}
else if (pID2 < 0) // failed to fork
{
printf("Failed to fork");
exit(1);
// Throw exception
} else {
FILE* fl = fopen("/tmp/pidprog1", "r");
char buff[10];
fgets(buff, 10, fl);
int pidc = atoi(buff);
fclose(fl);
int status;
waitpid(pidc, &status, 0);
printf("Program 1 terminated\n");
}
}
// Code executed by both parent and child.
return 0;
}
The problem is that when I manually kill the process using PID saved into /tmp/pidprog1, parent process doesn't stop waiting and doesn't print "Program 1 terminated" line.
The parent is very likely reading a garbage value into pidc. You are doing nothing to ensure that the grandchild has actually written the pid before the parent tries to read it. You need to use wait to ensure that valid pids are in the file. (Or, just keep track of the pids from the return value of fork.)
You are not doing enough error checking: what happens if any open fails? (eg, when you try
to open /tmp/1.txt for appending but it doesn't already exist?)
Why are you using fgets to read 40 characters into a buffer of size 20?
Why are you dup'ing and using fputs instead of just writing to the fd?
Why are you printing error messages to stdout instead of stderr ( use perror ).

Resources