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...
pipe c exec ipc
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.
|
show 10 more comments
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...
pipe c exec ipc
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 tostderr
? Do you print whatever number they generate tostderr
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 lineschar* 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 them
modifier to%s
and other consequential changes so thatscanf()
allocates the memory for you. Note that some systems (e.g. macOS) do not support the POSIX-mandatedsscanf()
modifier. Usingchar in[3][50];
is probably easiest (though you should use%49s
each time in thescanf()
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 programctrl61.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
|
show 10 more comments
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...
pipe c exec ipc
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
pipe c exec ipc
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 tostderr
? Do you print whatever number they generate tostderr
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 lineschar* 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 them
modifier to%s
and other consequential changes so thatscanf()
allocates the memory for you. Note that some systems (e.g. macOS) do not support the POSIX-mandatedsscanf()
modifier. Usingchar in[3][50];
is probably easiest (though you should use%49s
each time in thescanf()
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 programctrl61.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
|
show 10 more comments
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 tostderr
? Do you print whatever number they generate tostderr
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 lineschar* 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 them
modifier to%s
and other consequential changes so thatscanf()
allocates the memory for you. Note that some systems (e.g. macOS) do not support the POSIX-mandatedsscanf()
modifier. Usingchar in[3][50];
is probably easiest (though you should use%49s
each time in thescanf()
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 programctrl61.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
|
show 10 more comments
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
.
Thanks again, that's a wonderful explanation.
– Z E Nir
yesterday
add a comment |
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
.
Thanks again, that's a wonderful explanation.
– Z E Nir
yesterday
add a comment |
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
.
Thanks again, that's a wonderful explanation.
– Z E Nir
yesterday
add a comment |
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
.
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
.
answered 2 days ago
Jonathan Leffler
557k896641016
557k896641016
Thanks again, that's a wonderful explanation.
– Z E Nir
yesterday
add a comment |
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
add a comment |
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.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
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
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 tostderr
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 them
modifier to%s
and other consequential changes so thatscanf()
allocates the memory for you. Note that some systems (e.g. macOS) do not support the POSIX-mandatedsscanf()
modifier. Usingchar in[3][50];
is probably easiest (though you should use%49s
each time in thescanf()
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