failed using scanf to read from pipe











up vote
0
down vote

favorite












Working on IPC I was asked to write a C program that works as pipe between other two C executables:



The first executable named 'sln1.out' receive six arguments and print three numbers.



The second executable named 'sln2.out' receive three arguments and print one number.



I devided the following code to two parts - the first is the write to the pipe and as much as I know it works. The problem start in the second part: I closed the stdin so now when I use dup(fd[0]) the new file descriptor duplicate shall be allocated where the stdin was, and I suppose I can use scanf to read from the pipe under those circumstances - But for some reason it didn't work



#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char* argv)
{
// check number of arguments.
if(argc != 7)
{
printf("Wrong parameters");
exit(1);
}

// creating the pipe.
int fd[2];
pipe(fd);

/* PART ONE: forking a child for 'sln1.out' that writes to fd[1] */

// I want to fork this process, and change the image of the child process to the 'sln1.out' process.
pid_t pid_sln1 = fork();
int sln1_status;
if (pid_sln1 < 0)
{
perror("fork error, sln1");
}
else if(pid_sln1 == 0)
{
char* const parmListSln1 = {"./sln1.out",argv[1],argv[2],argv[3],
argv[4],argv[5],argv[6],NULL};
// i closed the stdout, and used 'dup' that return the file descriptor
// of stdout as duplicate of fd[1]!
close(STDOUT_FILENO);
dup(fd[1]);

execv("./sln1.out",parmListSln1);
printf("Return not expected, exacv error.n");
exit(1);
}

// wait untill the child process terminated.
wait(&sln1_status);
if(sln1_status == 0)
{
printf("child process terminated successfullyn");
// if we want to read from fd[0] we must close the write to fd[1]

close(fd[1]);
}
else
{
printf("child process failedn");
exit(1);
}


/* PART TWO: forking a child for 'sln2.out' that reads from fd[0] */

// The same idea - forking a child to change its image to the 'sln2.out' process.
pid_t pid_sln2 = fork();
int sln2_status;

if(pid_sln2 < 0)
{
printf("fork error, sln2.n");
exit(1);
}
else if(pid_sln2 == 0)
{
// closing 'stdin' and the use fo 'dup' create a duplicate to the readable
// side of the pipe where the standard input should be
close(STDIN_FILENO);
dup(fd[0]);

// reading the input from the pipe - with the same method used to 'stdin'!
char* in[3];
scanf("%s %s %s",in[0],in[1],in[2]);
// build the parameters list for 'sln2.out'
char* const paramListSln2 = { "./sln2.out", in[0], in[1], in[2], NULL };

// execute 'sln2.out'
execv("./sln2.out",paramListSln2);
printf("Return not expexted, execv error");
exit(1);

}

// wait untill the child process terminated and determine success.
wait(&sln2_status);
if (sln2_status == 0)
{
printf("2nd child process terminated successfully!n");
exit(0);
}
else
{
printf("error with 'sln2.out' child process.n");
exit(1);
}

exit(0);
}


The output I get can give some more details:



child process terminated successfully
error with 'sln2.out' child process.


I'm pretty sure the problem with the sln2.out process is because the failure of scanf since I tried to print the scanned arguments and it also failed...










share|improve this question













migrated from unix.stackexchange.com 2 days ago


This question came from our site for users of Linux, FreeBSD and other Un*x-like operating systems.











  • 1




    If you are writing all 3 executable, sln1, sln2, and the wrapper shown above, and they are tightly integrated. Then it may be better to put them into one executable (no exec). I also note that you wait for 1 to finish before starting 2, this will cause problems if the pipe fills.
    – ctrl-alt-delor
    2 days ago










  • First I'm writing all three programs, but I have to keep this structure since that what the college demands... I can't see a real reason that the pipe will filled, if I got this right the pipe can hold up to 4KB right? and the inputs/outputs here are very small.
    – Z E Nir
    2 days ago










  • Yes, pipes can hold more than 4 KiB, but filling a pipe is a problem in 'the real world' (outside university exercises). Keep that in mind. What diagnostics have you put in the two executables? Do you print the argument lists etc to stderr? Do you print whatever number they generate to stderr as well? When things are not going according to plan (as now, given that you're asking the question), then careful diagnostic output tracking which process is generating what output etc becomes crucial (or, if not crucial, very helpful). Also ensure you test every system call to ensure it works.
    – Jonathan Leffler
    2 days ago






  • 1




    The consecutive lines char* in[3]; scanf("%s %s %s",in[0],in[1],in[2]); are a source of crashes — the pointers don't point anywhere. You need to allocate storage for the pointers to point at, or use the m modifier to %s and other consequential changes so that scanf() allocates the memory for you. Note that some systems (e.g. macOS) do not support the POSIX-mandated sscanf() modifier. Using char in[3][50]; is probably easiest (though you should use %49s each time in the scanf() format string.)
    – Jonathan Leffler
    2 days ago








  • 1




    I don't know that it is the only problem; it is one problem that my compiler told me about as I tried to compile your code. I called your program ctrl61.c and (tried to) compile with: gcc -O3 -g -std=c11 -Wall -Wextra -Werror -Wmissing-prototypes -Wstrict-prototypes ctrl61.c -o ctrl61 — and the compiler refused to compile it.
    – Jonathan Leffler
    2 days ago

















up vote
0
down vote

favorite












Working on IPC I was asked to write a C program that works as pipe between other two C executables:



The first executable named 'sln1.out' receive six arguments and print three numbers.



The second executable named 'sln2.out' receive three arguments and print one number.



I devided the following code to two parts - the first is the write to the pipe and as much as I know it works. The problem start in the second part: I closed the stdin so now when I use dup(fd[0]) the new file descriptor duplicate shall be allocated where the stdin was, and I suppose I can use scanf to read from the pipe under those circumstances - But for some reason it didn't work



#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char* argv)
{
// check number of arguments.
if(argc != 7)
{
printf("Wrong parameters");
exit(1);
}

// creating the pipe.
int fd[2];
pipe(fd);

/* PART ONE: forking a child for 'sln1.out' that writes to fd[1] */

// I want to fork this process, and change the image of the child process to the 'sln1.out' process.
pid_t pid_sln1 = fork();
int sln1_status;
if (pid_sln1 < 0)
{
perror("fork error, sln1");
}
else if(pid_sln1 == 0)
{
char* const parmListSln1 = {"./sln1.out",argv[1],argv[2],argv[3],
argv[4],argv[5],argv[6],NULL};
// i closed the stdout, and used 'dup' that return the file descriptor
// of stdout as duplicate of fd[1]!
close(STDOUT_FILENO);
dup(fd[1]);

execv("./sln1.out",parmListSln1);
printf("Return not expected, exacv error.n");
exit(1);
}

// wait untill the child process terminated.
wait(&sln1_status);
if(sln1_status == 0)
{
printf("child process terminated successfullyn");
// if we want to read from fd[0] we must close the write to fd[1]

close(fd[1]);
}
else
{
printf("child process failedn");
exit(1);
}


/* PART TWO: forking a child for 'sln2.out' that reads from fd[0] */

// The same idea - forking a child to change its image to the 'sln2.out' process.
pid_t pid_sln2 = fork();
int sln2_status;

if(pid_sln2 < 0)
{
printf("fork error, sln2.n");
exit(1);
}
else if(pid_sln2 == 0)
{
// closing 'stdin' and the use fo 'dup' create a duplicate to the readable
// side of the pipe where the standard input should be
close(STDIN_FILENO);
dup(fd[0]);

// reading the input from the pipe - with the same method used to 'stdin'!
char* in[3];
scanf("%s %s %s",in[0],in[1],in[2]);
// build the parameters list for 'sln2.out'
char* const paramListSln2 = { "./sln2.out", in[0], in[1], in[2], NULL };

// execute 'sln2.out'
execv("./sln2.out",paramListSln2);
printf("Return not expexted, execv error");
exit(1);

}

// wait untill the child process terminated and determine success.
wait(&sln2_status);
if (sln2_status == 0)
{
printf("2nd child process terminated successfully!n");
exit(0);
}
else
{
printf("error with 'sln2.out' child process.n");
exit(1);
}

exit(0);
}


The output I get can give some more details:



child process terminated successfully
error with 'sln2.out' child process.


I'm pretty sure the problem with the sln2.out process is because the failure of scanf since I tried to print the scanned arguments and it also failed...










share|improve this question













migrated from unix.stackexchange.com 2 days ago


This question came from our site for users of Linux, FreeBSD and other Un*x-like operating systems.











  • 1




    If you are writing all 3 executable, sln1, sln2, and the wrapper shown above, and they are tightly integrated. Then it may be better to put them into one executable (no exec). I also note that you wait for 1 to finish before starting 2, this will cause problems if the pipe fills.
    – ctrl-alt-delor
    2 days ago










  • First I'm writing all three programs, but I have to keep this structure since that what the college demands... I can't see a real reason that the pipe will filled, if I got this right the pipe can hold up to 4KB right? and the inputs/outputs here are very small.
    – Z E Nir
    2 days ago










  • Yes, pipes can hold more than 4 KiB, but filling a pipe is a problem in 'the real world' (outside university exercises). Keep that in mind. What diagnostics have you put in the two executables? Do you print the argument lists etc to stderr? Do you print whatever number they generate to stderr as well? When things are not going according to plan (as now, given that you're asking the question), then careful diagnostic output tracking which process is generating what output etc becomes crucial (or, if not crucial, very helpful). Also ensure you test every system call to ensure it works.
    – Jonathan Leffler
    2 days ago






  • 1




    The consecutive lines char* in[3]; scanf("%s %s %s",in[0],in[1],in[2]); are a source of crashes — the pointers don't point anywhere. You need to allocate storage for the pointers to point at, or use the m modifier to %s and other consequential changes so that scanf() allocates the memory for you. Note that some systems (e.g. macOS) do not support the POSIX-mandated sscanf() modifier. Using char in[3][50]; is probably easiest (though you should use %49s each time in the scanf() format string.)
    – Jonathan Leffler
    2 days ago








  • 1




    I don't know that it is the only problem; it is one problem that my compiler told me about as I tried to compile your code. I called your program ctrl61.c and (tried to) compile with: gcc -O3 -g -std=c11 -Wall -Wextra -Werror -Wmissing-prototypes -Wstrict-prototypes ctrl61.c -o ctrl61 — and the compiler refused to compile it.
    – Jonathan Leffler
    2 days ago















up vote
0
down vote

favorite









up vote
0
down vote

favorite











Working on IPC I was asked to write a C program that works as pipe between other two C executables:



The first executable named 'sln1.out' receive six arguments and print three numbers.



The second executable named 'sln2.out' receive three arguments and print one number.



I devided the following code to two parts - the first is the write to the pipe and as much as I know it works. The problem start in the second part: I closed the stdin so now when I use dup(fd[0]) the new file descriptor duplicate shall be allocated where the stdin was, and I suppose I can use scanf to read from the pipe under those circumstances - But for some reason it didn't work



#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char* argv)
{
// check number of arguments.
if(argc != 7)
{
printf("Wrong parameters");
exit(1);
}

// creating the pipe.
int fd[2];
pipe(fd);

/* PART ONE: forking a child for 'sln1.out' that writes to fd[1] */

// I want to fork this process, and change the image of the child process to the 'sln1.out' process.
pid_t pid_sln1 = fork();
int sln1_status;
if (pid_sln1 < 0)
{
perror("fork error, sln1");
}
else if(pid_sln1 == 0)
{
char* const parmListSln1 = {"./sln1.out",argv[1],argv[2],argv[3],
argv[4],argv[5],argv[6],NULL};
// i closed the stdout, and used 'dup' that return the file descriptor
// of stdout as duplicate of fd[1]!
close(STDOUT_FILENO);
dup(fd[1]);

execv("./sln1.out",parmListSln1);
printf("Return not expected, exacv error.n");
exit(1);
}

// wait untill the child process terminated.
wait(&sln1_status);
if(sln1_status == 0)
{
printf("child process terminated successfullyn");
// if we want to read from fd[0] we must close the write to fd[1]

close(fd[1]);
}
else
{
printf("child process failedn");
exit(1);
}


/* PART TWO: forking a child for 'sln2.out' that reads from fd[0] */

// The same idea - forking a child to change its image to the 'sln2.out' process.
pid_t pid_sln2 = fork();
int sln2_status;

if(pid_sln2 < 0)
{
printf("fork error, sln2.n");
exit(1);
}
else if(pid_sln2 == 0)
{
// closing 'stdin' and the use fo 'dup' create a duplicate to the readable
// side of the pipe where the standard input should be
close(STDIN_FILENO);
dup(fd[0]);

// reading the input from the pipe - with the same method used to 'stdin'!
char* in[3];
scanf("%s %s %s",in[0],in[1],in[2]);
// build the parameters list for 'sln2.out'
char* const paramListSln2 = { "./sln2.out", in[0], in[1], in[2], NULL };

// execute 'sln2.out'
execv("./sln2.out",paramListSln2);
printf("Return not expexted, execv error");
exit(1);

}

// wait untill the child process terminated and determine success.
wait(&sln2_status);
if (sln2_status == 0)
{
printf("2nd child process terminated successfully!n");
exit(0);
}
else
{
printf("error with 'sln2.out' child process.n");
exit(1);
}

exit(0);
}


The output I get can give some more details:



child process terminated successfully
error with 'sln2.out' child process.


I'm pretty sure the problem with the sln2.out process is because the failure of scanf since I tried to print the scanned arguments and it also failed...










share|improve this question













Working on IPC I was asked to write a C program that works as pipe between other two C executables:



The first executable named 'sln1.out' receive six arguments and print three numbers.



The second executable named 'sln2.out' receive three arguments and print one number.



I devided the following code to two parts - the first is the write to the pipe and as much as I know it works. The problem start in the second part: I closed the stdin so now when I use dup(fd[0]) the new file descriptor duplicate shall be allocated where the stdin was, and I suppose I can use scanf to read from the pipe under those circumstances - But for some reason it didn't work



#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char* argv)
{
// check number of arguments.
if(argc != 7)
{
printf("Wrong parameters");
exit(1);
}

// creating the pipe.
int fd[2];
pipe(fd);

/* PART ONE: forking a child for 'sln1.out' that writes to fd[1] */

// I want to fork this process, and change the image of the child process to the 'sln1.out' process.
pid_t pid_sln1 = fork();
int sln1_status;
if (pid_sln1 < 0)
{
perror("fork error, sln1");
}
else if(pid_sln1 == 0)
{
char* const parmListSln1 = {"./sln1.out",argv[1],argv[2],argv[3],
argv[4],argv[5],argv[6],NULL};
// i closed the stdout, and used 'dup' that return the file descriptor
// of stdout as duplicate of fd[1]!
close(STDOUT_FILENO);
dup(fd[1]);

execv("./sln1.out",parmListSln1);
printf("Return not expected, exacv error.n");
exit(1);
}

// wait untill the child process terminated.
wait(&sln1_status);
if(sln1_status == 0)
{
printf("child process terminated successfullyn");
// if we want to read from fd[0] we must close the write to fd[1]

close(fd[1]);
}
else
{
printf("child process failedn");
exit(1);
}


/* PART TWO: forking a child for 'sln2.out' that reads from fd[0] */

// The same idea - forking a child to change its image to the 'sln2.out' process.
pid_t pid_sln2 = fork();
int sln2_status;

if(pid_sln2 < 0)
{
printf("fork error, sln2.n");
exit(1);
}
else if(pid_sln2 == 0)
{
// closing 'stdin' and the use fo 'dup' create a duplicate to the readable
// side of the pipe where the standard input should be
close(STDIN_FILENO);
dup(fd[0]);

// reading the input from the pipe - with the same method used to 'stdin'!
char* in[3];
scanf("%s %s %s",in[0],in[1],in[2]);
// build the parameters list for 'sln2.out'
char* const paramListSln2 = { "./sln2.out", in[0], in[1], in[2], NULL };

// execute 'sln2.out'
execv("./sln2.out",paramListSln2);
printf("Return not expexted, execv error");
exit(1);

}

// wait untill the child process terminated and determine success.
wait(&sln2_status);
if (sln2_status == 0)
{
printf("2nd child process terminated successfully!n");
exit(0);
}
else
{
printf("error with 'sln2.out' child process.n");
exit(1);
}

exit(0);
}


The output I get can give some more details:



child process terminated successfully
error with 'sln2.out' child process.


I'm pretty sure the problem with the sln2.out process is because the failure of scanf since I tried to print the scanned arguments and it also failed...







pipe c exec ipc






share|improve this question













share|improve this question











share|improve this question




share|improve this question










asked 2 days ago









Z E Nir

237




237




migrated from unix.stackexchange.com 2 days ago


This question came from our site for users of Linux, FreeBSD and other Un*x-like operating systems.






migrated from unix.stackexchange.com 2 days ago


This question came from our site for users of Linux, FreeBSD and other Un*x-like operating systems.










  • 1




    If you are writing all 3 executable, sln1, sln2, and the wrapper shown above, and they are tightly integrated. Then it may be better to put them into one executable (no exec). I also note that you wait for 1 to finish before starting 2, this will cause problems if the pipe fills.
    – ctrl-alt-delor
    2 days ago










  • First I'm writing all three programs, but I have to keep this structure since that what the college demands... I can't see a real reason that the pipe will filled, if I got this right the pipe can hold up to 4KB right? and the inputs/outputs here are very small.
    – Z E Nir
    2 days ago










  • Yes, pipes can hold more than 4 KiB, but filling a pipe is a problem in 'the real world' (outside university exercises). Keep that in mind. What diagnostics have you put in the two executables? Do you print the argument lists etc to stderr? Do you print whatever number they generate to stderr as well? When things are not going according to plan (as now, given that you're asking the question), then careful diagnostic output tracking which process is generating what output etc becomes crucial (or, if not crucial, very helpful). Also ensure you test every system call to ensure it works.
    – Jonathan Leffler
    2 days ago






  • 1




    The consecutive lines char* in[3]; scanf("%s %s %s",in[0],in[1],in[2]); are a source of crashes — the pointers don't point anywhere. You need to allocate storage for the pointers to point at, or use the m modifier to %s and other consequential changes so that scanf() allocates the memory for you. Note that some systems (e.g. macOS) do not support the POSIX-mandated sscanf() modifier. Using char in[3][50]; is probably easiest (though you should use %49s each time in the scanf() format string.)
    – Jonathan Leffler
    2 days ago








  • 1




    I don't know that it is the only problem; it is one problem that my compiler told me about as I tried to compile your code. I called your program ctrl61.c and (tried to) compile with: gcc -O3 -g -std=c11 -Wall -Wextra -Werror -Wmissing-prototypes -Wstrict-prototypes ctrl61.c -o ctrl61 — and the compiler refused to compile it.
    – Jonathan Leffler
    2 days ago
















  • 1




    If you are writing all 3 executable, sln1, sln2, and the wrapper shown above, and they are tightly integrated. Then it may be better to put them into one executable (no exec). I also note that you wait for 1 to finish before starting 2, this will cause problems if the pipe fills.
    – ctrl-alt-delor
    2 days ago










  • First I'm writing all three programs, but I have to keep this structure since that what the college demands... I can't see a real reason that the pipe will filled, if I got this right the pipe can hold up to 4KB right? and the inputs/outputs here are very small.
    – Z E Nir
    2 days ago










  • Yes, pipes can hold more than 4 KiB, but filling a pipe is a problem in 'the real world' (outside university exercises). Keep that in mind. What diagnostics have you put in the two executables? Do you print the argument lists etc to stderr? Do you print whatever number they generate to stderr as well? When things are not going according to plan (as now, given that you're asking the question), then careful diagnostic output tracking which process is generating what output etc becomes crucial (or, if not crucial, very helpful). Also ensure you test every system call to ensure it works.
    – Jonathan Leffler
    2 days ago






  • 1




    The consecutive lines char* in[3]; scanf("%s %s %s",in[0],in[1],in[2]); are a source of crashes — the pointers don't point anywhere. You need to allocate storage for the pointers to point at, or use the m modifier to %s and other consequential changes so that scanf() allocates the memory for you. Note that some systems (e.g. macOS) do not support the POSIX-mandated sscanf() modifier. Using char in[3][50]; is probably easiest (though you should use %49s each time in the scanf() format string.)
    – Jonathan Leffler
    2 days ago








  • 1




    I don't know that it is the only problem; it is one problem that my compiler told me about as I tried to compile your code. I called your program ctrl61.c and (tried to) compile with: gcc -O3 -g -std=c11 -Wall -Wextra -Werror -Wmissing-prototypes -Wstrict-prototypes ctrl61.c -o ctrl61 — and the compiler refused to compile it.
    – Jonathan Leffler
    2 days ago










1




1




If you are writing all 3 executable, sln1, sln2, and the wrapper shown above, and they are tightly integrated. Then it may be better to put them into one executable (no exec). I also note that you wait for 1 to finish before starting 2, this will cause problems if the pipe fills.
– ctrl-alt-delor
2 days ago




If you are writing all 3 executable, sln1, sln2, and the wrapper shown above, and they are tightly integrated. Then it may be better to put them into one executable (no exec). I also note that you wait for 1 to finish before starting 2, this will cause problems if the pipe fills.
– ctrl-alt-delor
2 days ago












First I'm writing all three programs, but I have to keep this structure since that what the college demands... I can't see a real reason that the pipe will filled, if I got this right the pipe can hold up to 4KB right? and the inputs/outputs here are very small.
– Z E Nir
2 days ago




First I'm writing all three programs, but I have to keep this structure since that what the college demands... I can't see a real reason that the pipe will filled, if I got this right the pipe can hold up to 4KB right? and the inputs/outputs here are very small.
– Z E Nir
2 days ago












Yes, pipes can hold more than 4 KiB, but filling a pipe is a problem in 'the real world' (outside university exercises). Keep that in mind. What diagnostics have you put in the two executables? Do you print the argument lists etc to stderr? Do you print whatever number they generate to stderr as well? When things are not going according to plan (as now, given that you're asking the question), then careful diagnostic output tracking which process is generating what output etc becomes crucial (or, if not crucial, very helpful). Also ensure you test every system call to ensure it works.
– Jonathan Leffler
2 days ago




Yes, pipes can hold more than 4 KiB, but filling a pipe is a problem in 'the real world' (outside university exercises). Keep that in mind. What diagnostics have you put in the two executables? Do you print the argument lists etc to stderr? Do you print whatever number they generate to stderr as well? When things are not going according to plan (as now, given that you're asking the question), then careful diagnostic output tracking which process is generating what output etc becomes crucial (or, if not crucial, very helpful). Also ensure you test every system call to ensure it works.
– Jonathan Leffler
2 days ago




1




1




The consecutive lines char* in[3]; scanf("%s %s %s",in[0],in[1],in[2]); are a source of crashes — the pointers don't point anywhere. You need to allocate storage for the pointers to point at, or use the m modifier to %s and other consequential changes so that scanf() allocates the memory for you. Note that some systems (e.g. macOS) do not support the POSIX-mandated sscanf() modifier. Using char in[3][50]; is probably easiest (though you should use %49s each time in the scanf() format string.)
– Jonathan Leffler
2 days ago






The consecutive lines char* in[3]; scanf("%s %s %s",in[0],in[1],in[2]); are a source of crashes — the pointers don't point anywhere. You need to allocate storage for the pointers to point at, or use the m modifier to %s and other consequential changes so that scanf() allocates the memory for you. Note that some systems (e.g. macOS) do not support the POSIX-mandated sscanf() modifier. Using char in[3][50]; is probably easiest (though you should use %49s each time in the scanf() format string.)
– Jonathan Leffler
2 days ago






1




1




I don't know that it is the only problem; it is one problem that my compiler told me about as I tried to compile your code. I called your program ctrl61.c and (tried to) compile with: gcc -O3 -g -std=c11 -Wall -Wextra -Werror -Wmissing-prototypes -Wstrict-prototypes ctrl61.c -o ctrl61 — and the compiler refused to compile it.
– Jonathan Leffler
2 days ago






I don't know that it is the only problem; it is one problem that my compiler told me about as I tried to compile your code. I called your program ctrl61.c and (tried to) compile with: gcc -O3 -g -std=c11 -Wall -Wextra -Werror -Wmissing-prototypes -Wstrict-prototypes ctrl61.c -o ctrl61 — and the compiler refused to compile it.
– Jonathan Leffler
2 days ago














1 Answer
1






active

oldest

votes

















up vote
1
down vote



accepted










Primary problem — uninitialized pointers



When I compiled the code in the question (source file, ctrl61.c) using the command line:



gcc -O3 -g -std=c11 -Wall -Wextra -Werror -Wmissing-prototypes -Wstrict-prototypes ctrl61.c -o ctrl61


(with GCC 8.2.0 running on a Mac with macOS 10.14.2 Mojave), I got warnings like:



ctrl61.c:78:13: error: ‘in[0]’ may be used uninitialized in this function [-Werror=maybe-uninitialized]


for each of in[0], in[1] and in[2], and the line identified was the call to scanf(). Uninitialized pointers are a source of crashes and, indeed, inspecting the code shows that the pointers are not initialized.
You need to allocate storage for the pointers to point at. The simplest change is to use:



char in[3][50];


(though you should then use %49s each time in the scanf() format string.)
Or you could use the m modifier to %s and other consequential changes so that scanf() allocates the memory for you. Note that some systems (e.g. macOS) do not support the POSIX-mandated sscanf() modifier.



You aren't closing enough file descriptors in the child (or, indeed, in the parent).



Rule of thumb: 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.



In this program, it may not matter, but if you are working with pipes more generally, it is often crucial to make sure you close all the unused pipes, as processes may not get EOF when they need to.



Error reporting



In comments, you mentioned using perror() to report problems. Personally, I don't like perror() for reporting errors; its formatting isn't powerful enough. However, it is better than some alternatives.



I usually use some code that is available in my SOQ (Stack Overflow Questions) repository on GitHub as files stderr.c and stderr.h in the src/libsoq sub-directory. This has extensive control over the formatting.



There is a conceptually similar package, err(3) available on Linux and BSD (including macOS). I prefer mine, simply because it is mine (and because it has more powerful controls than the err(3) package).



Control code ctrl61.c



#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(int argc, char *argv)
{
if (argc != 7)
{
fprintf(stderr, "Usage: %s arg1 arg2 arg3 arg4 arg5 arg6n", argv[0]);
exit(1);
}

int fd[2];
pipe(fd);

pid_t pid_sln1 = fork();
int sln1_status;
if (pid_sln1 < 0)
{
perror("fork error, sln1");
}
else if (pid_sln1 == 0)
{
char *paramListSln1 =
{
"./sln1.out", argv[1], argv[2], argv[3],
argv[4], argv[5], argv[6], NULL
};

close(STDOUT_FILENO);
dup(fd[1]);
close(fd[0]);
close(fd[1]);

execv(paramListSln1[0], paramListSln1);
fprintf(stderr, "%s: failed to exec %sn", argv[0], paramListSln1[0]);
exit(1);
}

pid_t pid_sln2 = fork();
int sln2_status;

if (pid_sln2 < 0)
{
printf("fork error, sln2.n");
exit(1);
}
else if (pid_sln2 == 0)
{
close(STDIN_FILENO);
dup(fd[0]);
close(fd[0]);
close(fd[1]);

char in[3][50];
scanf("%49s %49s %49s", in[0], in[1], in[2]);

char *const paramListSln2 = { "./sln2.out", in[0], in[1], in[2], NULL };

execv(paramListSln2[0], paramListSln2);
fprintf(stderr, "%s: failed to exec %sn", argv[0], paramListSln2[0]);
exit(1);
}

close(fd[0]);
close(fd[1]);

int pid1 = wait(&sln1_status);
if (sln1_status == 0)
{
fprintf(stderr, "child process %d terminated successfullyn", pid1);
close(fd[1]);
}
else
{
fprintf(stderr, "child process %d failed 0x%.4Xn", pid1, sln1_status);
exit(1);
}

int pid2 = wait(&sln2_status);
if (sln2_status == 0)
{
fprintf(stderr, "child process %d terminated successfullyn", pid2);
close(fd[1]);
}
else
{
fprintf(stderr, "child process %d failed 0x%.4Xn", pid2, sln2_status);
exit(1);
}

return(0);
}


There are stark repetitions in this code that should be fixed by writing functions.



Note that this version launches both programs before waiting for either to exit.



Auxilliary program sln1.out.c



This is closely based on the code hypothesized in a comment but fixes a bug where the comment used argv[1] but should have used argv[0].



#include <stdio.h>

static inline void dump_args(int argc, char **argv)
{
int argnum = 0;
fprintf(stderr, "%s: %d argumentsn", argv[0], argc);
while (*argv != 0)
fprintf(stderr, "%d: [%s]n", argnum++, *argv++);
}

int main(int argc, char **argv)
{
dump_args(argc, argv);
if (argc != 7)
{
fprintf(stderr, "%s: incorrect argument count %dn", argv[0], argc);
return(1);
}
printf("1 2 3n");
return(0);
}


The program sln2.out.c differs in requiring 3 arguments and printing 321 instead of 1 2 3.



Example run



$ ./ctrl61 abc zoo def pqr tuv 999
./sln1.out: 7 arguments
0: [./sln1.out]
1: [abc]
2: [zoo]
3: [def]
4: [pqr]
5: [tuv]
6: [999]
child process 15443 terminated successfully
./sln2.out: 4 arguments
0: [./sln2.out]
1: [1]
2: [2]
3: [3]
321
child process 15444 terminated successfully
$


This shows that sln2.out was passed three arguments read from the standard output of sln1.out.






share|improve this answer





















  • Thanks again, that's a wonderful explanation.
    – Z E Nir
    yesterday











Your Answer






StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");

StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});

function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});


}
});














draft saved

draft discarded


















StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53695503%2ffailed-using-scanf-to-read-from-pipe%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown

























1 Answer
1






active

oldest

votes








1 Answer
1






active

oldest

votes









active

oldest

votes






active

oldest

votes








up vote
1
down vote



accepted










Primary problem — uninitialized pointers



When I compiled the code in the question (source file, ctrl61.c) using the command line:



gcc -O3 -g -std=c11 -Wall -Wextra -Werror -Wmissing-prototypes -Wstrict-prototypes ctrl61.c -o ctrl61


(with GCC 8.2.0 running on a Mac with macOS 10.14.2 Mojave), I got warnings like:



ctrl61.c:78:13: error: ‘in[0]’ may be used uninitialized in this function [-Werror=maybe-uninitialized]


for each of in[0], in[1] and in[2], and the line identified was the call to scanf(). Uninitialized pointers are a source of crashes and, indeed, inspecting the code shows that the pointers are not initialized.
You need to allocate storage for the pointers to point at. The simplest change is to use:



char in[3][50];


(though you should then use %49s each time in the scanf() format string.)
Or you could use the m modifier to %s and other consequential changes so that scanf() allocates the memory for you. Note that some systems (e.g. macOS) do not support the POSIX-mandated sscanf() modifier.



You aren't closing enough file descriptors in the child (or, indeed, in the parent).



Rule of thumb: 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.



In this program, it may not matter, but if you are working with pipes more generally, it is often crucial to make sure you close all the unused pipes, as processes may not get EOF when they need to.



Error reporting



In comments, you mentioned using perror() to report problems. Personally, I don't like perror() for reporting errors; its formatting isn't powerful enough. However, it is better than some alternatives.



I usually use some code that is available in my SOQ (Stack Overflow Questions) repository on GitHub as files stderr.c and stderr.h in the src/libsoq sub-directory. This has extensive control over the formatting.



There is a conceptually similar package, err(3) available on Linux and BSD (including macOS). I prefer mine, simply because it is mine (and because it has more powerful controls than the err(3) package).



Control code ctrl61.c



#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(int argc, char *argv)
{
if (argc != 7)
{
fprintf(stderr, "Usage: %s arg1 arg2 arg3 arg4 arg5 arg6n", argv[0]);
exit(1);
}

int fd[2];
pipe(fd);

pid_t pid_sln1 = fork();
int sln1_status;
if (pid_sln1 < 0)
{
perror("fork error, sln1");
}
else if (pid_sln1 == 0)
{
char *paramListSln1 =
{
"./sln1.out", argv[1], argv[2], argv[3],
argv[4], argv[5], argv[6], NULL
};

close(STDOUT_FILENO);
dup(fd[1]);
close(fd[0]);
close(fd[1]);

execv(paramListSln1[0], paramListSln1);
fprintf(stderr, "%s: failed to exec %sn", argv[0], paramListSln1[0]);
exit(1);
}

pid_t pid_sln2 = fork();
int sln2_status;

if (pid_sln2 < 0)
{
printf("fork error, sln2.n");
exit(1);
}
else if (pid_sln2 == 0)
{
close(STDIN_FILENO);
dup(fd[0]);
close(fd[0]);
close(fd[1]);

char in[3][50];
scanf("%49s %49s %49s", in[0], in[1], in[2]);

char *const paramListSln2 = { "./sln2.out", in[0], in[1], in[2], NULL };

execv(paramListSln2[0], paramListSln2);
fprintf(stderr, "%s: failed to exec %sn", argv[0], paramListSln2[0]);
exit(1);
}

close(fd[0]);
close(fd[1]);

int pid1 = wait(&sln1_status);
if (sln1_status == 0)
{
fprintf(stderr, "child process %d terminated successfullyn", pid1);
close(fd[1]);
}
else
{
fprintf(stderr, "child process %d failed 0x%.4Xn", pid1, sln1_status);
exit(1);
}

int pid2 = wait(&sln2_status);
if (sln2_status == 0)
{
fprintf(stderr, "child process %d terminated successfullyn", pid2);
close(fd[1]);
}
else
{
fprintf(stderr, "child process %d failed 0x%.4Xn", pid2, sln2_status);
exit(1);
}

return(0);
}


There are stark repetitions in this code that should be fixed by writing functions.



Note that this version launches both programs before waiting for either to exit.



Auxilliary program sln1.out.c



This is closely based on the code hypothesized in a comment but fixes a bug where the comment used argv[1] but should have used argv[0].



#include <stdio.h>

static inline void dump_args(int argc, char **argv)
{
int argnum = 0;
fprintf(stderr, "%s: %d argumentsn", argv[0], argc);
while (*argv != 0)
fprintf(stderr, "%d: [%s]n", argnum++, *argv++);
}

int main(int argc, char **argv)
{
dump_args(argc, argv);
if (argc != 7)
{
fprintf(stderr, "%s: incorrect argument count %dn", argv[0], argc);
return(1);
}
printf("1 2 3n");
return(0);
}


The program sln2.out.c differs in requiring 3 arguments and printing 321 instead of 1 2 3.



Example run



$ ./ctrl61 abc zoo def pqr tuv 999
./sln1.out: 7 arguments
0: [./sln1.out]
1: [abc]
2: [zoo]
3: [def]
4: [pqr]
5: [tuv]
6: [999]
child process 15443 terminated successfully
./sln2.out: 4 arguments
0: [./sln2.out]
1: [1]
2: [2]
3: [3]
321
child process 15444 terminated successfully
$


This shows that sln2.out was passed three arguments read from the standard output of sln1.out.






share|improve this answer





















  • Thanks again, that's a wonderful explanation.
    – Z E Nir
    yesterday















up vote
1
down vote



accepted










Primary problem — uninitialized pointers



When I compiled the code in the question (source file, ctrl61.c) using the command line:



gcc -O3 -g -std=c11 -Wall -Wextra -Werror -Wmissing-prototypes -Wstrict-prototypes ctrl61.c -o ctrl61


(with GCC 8.2.0 running on a Mac with macOS 10.14.2 Mojave), I got warnings like:



ctrl61.c:78:13: error: ‘in[0]’ may be used uninitialized in this function [-Werror=maybe-uninitialized]


for each of in[0], in[1] and in[2], and the line identified was the call to scanf(). Uninitialized pointers are a source of crashes and, indeed, inspecting the code shows that the pointers are not initialized.
You need to allocate storage for the pointers to point at. The simplest change is to use:



char in[3][50];


(though you should then use %49s each time in the scanf() format string.)
Or you could use the m modifier to %s and other consequential changes so that scanf() allocates the memory for you. Note that some systems (e.g. macOS) do not support the POSIX-mandated sscanf() modifier.



You aren't closing enough file descriptors in the child (or, indeed, in the parent).



Rule of thumb: 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.



In this program, it may not matter, but if you are working with pipes more generally, it is often crucial to make sure you close all the unused pipes, as processes may not get EOF when they need to.



Error reporting



In comments, you mentioned using perror() to report problems. Personally, I don't like perror() for reporting errors; its formatting isn't powerful enough. However, it is better than some alternatives.



I usually use some code that is available in my SOQ (Stack Overflow Questions) repository on GitHub as files stderr.c and stderr.h in the src/libsoq sub-directory. This has extensive control over the formatting.



There is a conceptually similar package, err(3) available on Linux and BSD (including macOS). I prefer mine, simply because it is mine (and because it has more powerful controls than the err(3) package).



Control code ctrl61.c



#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(int argc, char *argv)
{
if (argc != 7)
{
fprintf(stderr, "Usage: %s arg1 arg2 arg3 arg4 arg5 arg6n", argv[0]);
exit(1);
}

int fd[2];
pipe(fd);

pid_t pid_sln1 = fork();
int sln1_status;
if (pid_sln1 < 0)
{
perror("fork error, sln1");
}
else if (pid_sln1 == 0)
{
char *paramListSln1 =
{
"./sln1.out", argv[1], argv[2], argv[3],
argv[4], argv[5], argv[6], NULL
};

close(STDOUT_FILENO);
dup(fd[1]);
close(fd[0]);
close(fd[1]);

execv(paramListSln1[0], paramListSln1);
fprintf(stderr, "%s: failed to exec %sn", argv[0], paramListSln1[0]);
exit(1);
}

pid_t pid_sln2 = fork();
int sln2_status;

if (pid_sln2 < 0)
{
printf("fork error, sln2.n");
exit(1);
}
else if (pid_sln2 == 0)
{
close(STDIN_FILENO);
dup(fd[0]);
close(fd[0]);
close(fd[1]);

char in[3][50];
scanf("%49s %49s %49s", in[0], in[1], in[2]);

char *const paramListSln2 = { "./sln2.out", in[0], in[1], in[2], NULL };

execv(paramListSln2[0], paramListSln2);
fprintf(stderr, "%s: failed to exec %sn", argv[0], paramListSln2[0]);
exit(1);
}

close(fd[0]);
close(fd[1]);

int pid1 = wait(&sln1_status);
if (sln1_status == 0)
{
fprintf(stderr, "child process %d terminated successfullyn", pid1);
close(fd[1]);
}
else
{
fprintf(stderr, "child process %d failed 0x%.4Xn", pid1, sln1_status);
exit(1);
}

int pid2 = wait(&sln2_status);
if (sln2_status == 0)
{
fprintf(stderr, "child process %d terminated successfullyn", pid2);
close(fd[1]);
}
else
{
fprintf(stderr, "child process %d failed 0x%.4Xn", pid2, sln2_status);
exit(1);
}

return(0);
}


There are stark repetitions in this code that should be fixed by writing functions.



Note that this version launches both programs before waiting for either to exit.



Auxilliary program sln1.out.c



This is closely based on the code hypothesized in a comment but fixes a bug where the comment used argv[1] but should have used argv[0].



#include <stdio.h>

static inline void dump_args(int argc, char **argv)
{
int argnum = 0;
fprintf(stderr, "%s: %d argumentsn", argv[0], argc);
while (*argv != 0)
fprintf(stderr, "%d: [%s]n", argnum++, *argv++);
}

int main(int argc, char **argv)
{
dump_args(argc, argv);
if (argc != 7)
{
fprintf(stderr, "%s: incorrect argument count %dn", argv[0], argc);
return(1);
}
printf("1 2 3n");
return(0);
}


The program sln2.out.c differs in requiring 3 arguments and printing 321 instead of 1 2 3.



Example run



$ ./ctrl61 abc zoo def pqr tuv 999
./sln1.out: 7 arguments
0: [./sln1.out]
1: [abc]
2: [zoo]
3: [def]
4: [pqr]
5: [tuv]
6: [999]
child process 15443 terminated successfully
./sln2.out: 4 arguments
0: [./sln2.out]
1: [1]
2: [2]
3: [3]
321
child process 15444 terminated successfully
$


This shows that sln2.out was passed three arguments read from the standard output of sln1.out.






share|improve this answer





















  • Thanks again, that's a wonderful explanation.
    – Z E Nir
    yesterday













up vote
1
down vote



accepted







up vote
1
down vote



accepted






Primary problem — uninitialized pointers



When I compiled the code in the question (source file, ctrl61.c) using the command line:



gcc -O3 -g -std=c11 -Wall -Wextra -Werror -Wmissing-prototypes -Wstrict-prototypes ctrl61.c -o ctrl61


(with GCC 8.2.0 running on a Mac with macOS 10.14.2 Mojave), I got warnings like:



ctrl61.c:78:13: error: ‘in[0]’ may be used uninitialized in this function [-Werror=maybe-uninitialized]


for each of in[0], in[1] and in[2], and the line identified was the call to scanf(). Uninitialized pointers are a source of crashes and, indeed, inspecting the code shows that the pointers are not initialized.
You need to allocate storage for the pointers to point at. The simplest change is to use:



char in[3][50];


(though you should then use %49s each time in the scanf() format string.)
Or you could use the m modifier to %s and other consequential changes so that scanf() allocates the memory for you. Note that some systems (e.g. macOS) do not support the POSIX-mandated sscanf() modifier.



You aren't closing enough file descriptors in the child (or, indeed, in the parent).



Rule of thumb: 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.



In this program, it may not matter, but if you are working with pipes more generally, it is often crucial to make sure you close all the unused pipes, as processes may not get EOF when they need to.



Error reporting



In comments, you mentioned using perror() to report problems. Personally, I don't like perror() for reporting errors; its formatting isn't powerful enough. However, it is better than some alternatives.



I usually use some code that is available in my SOQ (Stack Overflow Questions) repository on GitHub as files stderr.c and stderr.h in the src/libsoq sub-directory. This has extensive control over the formatting.



There is a conceptually similar package, err(3) available on Linux and BSD (including macOS). I prefer mine, simply because it is mine (and because it has more powerful controls than the err(3) package).



Control code ctrl61.c



#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(int argc, char *argv)
{
if (argc != 7)
{
fprintf(stderr, "Usage: %s arg1 arg2 arg3 arg4 arg5 arg6n", argv[0]);
exit(1);
}

int fd[2];
pipe(fd);

pid_t pid_sln1 = fork();
int sln1_status;
if (pid_sln1 < 0)
{
perror("fork error, sln1");
}
else if (pid_sln1 == 0)
{
char *paramListSln1 =
{
"./sln1.out", argv[1], argv[2], argv[3],
argv[4], argv[5], argv[6], NULL
};

close(STDOUT_FILENO);
dup(fd[1]);
close(fd[0]);
close(fd[1]);

execv(paramListSln1[0], paramListSln1);
fprintf(stderr, "%s: failed to exec %sn", argv[0], paramListSln1[0]);
exit(1);
}

pid_t pid_sln2 = fork();
int sln2_status;

if (pid_sln2 < 0)
{
printf("fork error, sln2.n");
exit(1);
}
else if (pid_sln2 == 0)
{
close(STDIN_FILENO);
dup(fd[0]);
close(fd[0]);
close(fd[1]);

char in[3][50];
scanf("%49s %49s %49s", in[0], in[1], in[2]);

char *const paramListSln2 = { "./sln2.out", in[0], in[1], in[2], NULL };

execv(paramListSln2[0], paramListSln2);
fprintf(stderr, "%s: failed to exec %sn", argv[0], paramListSln2[0]);
exit(1);
}

close(fd[0]);
close(fd[1]);

int pid1 = wait(&sln1_status);
if (sln1_status == 0)
{
fprintf(stderr, "child process %d terminated successfullyn", pid1);
close(fd[1]);
}
else
{
fprintf(stderr, "child process %d failed 0x%.4Xn", pid1, sln1_status);
exit(1);
}

int pid2 = wait(&sln2_status);
if (sln2_status == 0)
{
fprintf(stderr, "child process %d terminated successfullyn", pid2);
close(fd[1]);
}
else
{
fprintf(stderr, "child process %d failed 0x%.4Xn", pid2, sln2_status);
exit(1);
}

return(0);
}


There are stark repetitions in this code that should be fixed by writing functions.



Note that this version launches both programs before waiting for either to exit.



Auxilliary program sln1.out.c



This is closely based on the code hypothesized in a comment but fixes a bug where the comment used argv[1] but should have used argv[0].



#include <stdio.h>

static inline void dump_args(int argc, char **argv)
{
int argnum = 0;
fprintf(stderr, "%s: %d argumentsn", argv[0], argc);
while (*argv != 0)
fprintf(stderr, "%d: [%s]n", argnum++, *argv++);
}

int main(int argc, char **argv)
{
dump_args(argc, argv);
if (argc != 7)
{
fprintf(stderr, "%s: incorrect argument count %dn", argv[0], argc);
return(1);
}
printf("1 2 3n");
return(0);
}


The program sln2.out.c differs in requiring 3 arguments and printing 321 instead of 1 2 3.



Example run



$ ./ctrl61 abc zoo def pqr tuv 999
./sln1.out: 7 arguments
0: [./sln1.out]
1: [abc]
2: [zoo]
3: [def]
4: [pqr]
5: [tuv]
6: [999]
child process 15443 terminated successfully
./sln2.out: 4 arguments
0: [./sln2.out]
1: [1]
2: [2]
3: [3]
321
child process 15444 terminated successfully
$


This shows that sln2.out was passed three arguments read from the standard output of sln1.out.






share|improve this answer












Primary problem — uninitialized pointers



When I compiled the code in the question (source file, ctrl61.c) using the command line:



gcc -O3 -g -std=c11 -Wall -Wextra -Werror -Wmissing-prototypes -Wstrict-prototypes ctrl61.c -o ctrl61


(with GCC 8.2.0 running on a Mac with macOS 10.14.2 Mojave), I got warnings like:



ctrl61.c:78:13: error: ‘in[0]’ may be used uninitialized in this function [-Werror=maybe-uninitialized]


for each of in[0], in[1] and in[2], and the line identified was the call to scanf(). Uninitialized pointers are a source of crashes and, indeed, inspecting the code shows that the pointers are not initialized.
You need to allocate storage for the pointers to point at. The simplest change is to use:



char in[3][50];


(though you should then use %49s each time in the scanf() format string.)
Or you could use the m modifier to %s and other consequential changes so that scanf() allocates the memory for you. Note that some systems (e.g. macOS) do not support the POSIX-mandated sscanf() modifier.



You aren't closing enough file descriptors in the child (or, indeed, in the parent).



Rule of thumb: 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.



In this program, it may not matter, but if you are working with pipes more generally, it is often crucial to make sure you close all the unused pipes, as processes may not get EOF when they need to.



Error reporting



In comments, you mentioned using perror() to report problems. Personally, I don't like perror() for reporting errors; its formatting isn't powerful enough. However, it is better than some alternatives.



I usually use some code that is available in my SOQ (Stack Overflow Questions) repository on GitHub as files stderr.c and stderr.h in the src/libsoq sub-directory. This has extensive control over the formatting.



There is a conceptually similar package, err(3) available on Linux and BSD (including macOS). I prefer mine, simply because it is mine (and because it has more powerful controls than the err(3) package).



Control code ctrl61.c



#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(int argc, char *argv)
{
if (argc != 7)
{
fprintf(stderr, "Usage: %s arg1 arg2 arg3 arg4 arg5 arg6n", argv[0]);
exit(1);
}

int fd[2];
pipe(fd);

pid_t pid_sln1 = fork();
int sln1_status;
if (pid_sln1 < 0)
{
perror("fork error, sln1");
}
else if (pid_sln1 == 0)
{
char *paramListSln1 =
{
"./sln1.out", argv[1], argv[2], argv[3],
argv[4], argv[5], argv[6], NULL
};

close(STDOUT_FILENO);
dup(fd[1]);
close(fd[0]);
close(fd[1]);

execv(paramListSln1[0], paramListSln1);
fprintf(stderr, "%s: failed to exec %sn", argv[0], paramListSln1[0]);
exit(1);
}

pid_t pid_sln2 = fork();
int sln2_status;

if (pid_sln2 < 0)
{
printf("fork error, sln2.n");
exit(1);
}
else if (pid_sln2 == 0)
{
close(STDIN_FILENO);
dup(fd[0]);
close(fd[0]);
close(fd[1]);

char in[3][50];
scanf("%49s %49s %49s", in[0], in[1], in[2]);

char *const paramListSln2 = { "./sln2.out", in[0], in[1], in[2], NULL };

execv(paramListSln2[0], paramListSln2);
fprintf(stderr, "%s: failed to exec %sn", argv[0], paramListSln2[0]);
exit(1);
}

close(fd[0]);
close(fd[1]);

int pid1 = wait(&sln1_status);
if (sln1_status == 0)
{
fprintf(stderr, "child process %d terminated successfullyn", pid1);
close(fd[1]);
}
else
{
fprintf(stderr, "child process %d failed 0x%.4Xn", pid1, sln1_status);
exit(1);
}

int pid2 = wait(&sln2_status);
if (sln2_status == 0)
{
fprintf(stderr, "child process %d terminated successfullyn", pid2);
close(fd[1]);
}
else
{
fprintf(stderr, "child process %d failed 0x%.4Xn", pid2, sln2_status);
exit(1);
}

return(0);
}


There are stark repetitions in this code that should be fixed by writing functions.



Note that this version launches both programs before waiting for either to exit.



Auxilliary program sln1.out.c



This is closely based on the code hypothesized in a comment but fixes a bug where the comment used argv[1] but should have used argv[0].



#include <stdio.h>

static inline void dump_args(int argc, char **argv)
{
int argnum = 0;
fprintf(stderr, "%s: %d argumentsn", argv[0], argc);
while (*argv != 0)
fprintf(stderr, "%d: [%s]n", argnum++, *argv++);
}

int main(int argc, char **argv)
{
dump_args(argc, argv);
if (argc != 7)
{
fprintf(stderr, "%s: incorrect argument count %dn", argv[0], argc);
return(1);
}
printf("1 2 3n");
return(0);
}


The program sln2.out.c differs in requiring 3 arguments and printing 321 instead of 1 2 3.



Example run



$ ./ctrl61 abc zoo def pqr tuv 999
./sln1.out: 7 arguments
0: [./sln1.out]
1: [abc]
2: [zoo]
3: [def]
4: [pqr]
5: [tuv]
6: [999]
child process 15443 terminated successfully
./sln2.out: 4 arguments
0: [./sln2.out]
1: [1]
2: [2]
3: [3]
321
child process 15444 terminated successfully
$


This shows that sln2.out was passed three arguments read from the standard output of sln1.out.







share|improve this answer












share|improve this answer



share|improve this answer










answered 2 days ago









Jonathan Leffler

557k896641016




557k896641016












  • Thanks again, that's a wonderful explanation.
    – Z E Nir
    yesterday


















  • Thanks again, that's a wonderful explanation.
    – Z E Nir
    yesterday
















Thanks again, that's a wonderful explanation.
– Z E Nir
yesterday




Thanks again, that's a wonderful explanation.
– Z E Nir
yesterday


















draft saved

draft discarded




















































Thanks for contributing an answer to Stack Overflow!


  • Please be sure to answer the question. Provide details and share your research!

But avoid



  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.


To learn more, see our tips on writing great answers.





Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


Please pay close attention to the following guidance:


  • Please be sure to answer the question. Provide details and share your research!

But avoid



  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.


To learn more, see our tips on writing great answers.




draft saved


draft discarded














StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53695503%2ffailed-using-scanf-to-read-from-pipe%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown





















































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown

































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown







Popular posts from this blog

Accessing regular linux commands in Huawei's Dopra Linux

Can't connect RFCOMM socket: Host is down

Kernel panic - not syncing: Fatal Exception in Interrupt