2 pipe, one fork, bc and execlp (C) - c

I'm trying to get something like this to work in c using piping and fork :
echo "an operation like 10+10" | bc
To be precise, I tried to create 2 pipes, one where the server will write the operation and bc will read and another where the result of the operation (by bc) will go and the server will read it and printf it. For this, I'm changing the output and input of the parent and child process.
Here is my code:
int main(){
int t1, t2;
char resultat[5];
int pipe1[2];
int pipe2[2];
pipe(pipe1);
pipe(pipe2);
int resultat_fork = fork();
if(resultat_fork == -1){
exit(EXIT_FAILURE);
}
if(resultat_fork!=0){
printf("I am the parent\n");
close(pipe1[0]);
close(pipe2[1]);
//We only want to write into pipe1 and read from pipe2, we can close the two other
write(pipe1[1], "20*5\n", 20);
//write on pipe1 the expression
read(pipe2[0], resultat, sizeof(resultat));
//read from pipe2 the answer (written by bc)
printf("resultat : %s\n",resultat);
close(pipe1[1]);
close(pipe2[0]);
}else{
printf("I am the children\n");
close(pipe1[1]);
close(pipe2[0]);
//We only want to write into pipe2 and read from pipe1, we can close the two other
dup2(pipe1[0], 0);
//redirection standard input to pipe1[0]
dup2(pipe2[1], 1);
//redirection standard output to pipe2[1]
execlp("bc", "bc", NULL);
//execute bc, which normaly will read the operation from pipe1 and write the answer into pipe2, but I think it's here the problem come out
close(pipe1[0]);
close(pipe2[1]);
}
return 0;
}
I'm getting the correct answer but with the error :
"(standard_in) 2: illegal character : ^#"
"(standard_in) 2: illegal character : :"
plus I have to CTRL C to quit.
I guess it comes from BC but why...
What am I doing wrong? Thank you!
I've already seen few exemple but only threw one pipe.

This works. Note how it specifies the size of the data to be written, and how it checks the writes and reads, and also how it closes file descriptors. (Remember: sizeof("string literal") counts the null byte, unlike strlen(). It's also a compile time constant. However, more general purpose code would use strlen() on the current expression string.)
Rule of Thumb: If you use dup2() to duplicate a pipe file descriptor to standard input or standard output, close both ends of the pipe.
That also applies if you use dup(), or fcntl() with F_DUPFD or F_DUPFD_CLOEXEC instead.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(void)
{
char resultat[5];
int pipe1[2];
int pipe2[2];
pipe(pipe1);
pipe(pipe2);
int resultat_fork = fork();
if (resultat_fork == -1)
{
exit(EXIT_FAILURE);
}
if (resultat_fork != 0)
{
printf("I am the parent\n");
close(pipe1[0]);
close(pipe2[1]);
if (write(pipe1[1], "20*5\n", sizeof("20*5\n") - 1) != sizeof("20*5\n") - 1)
fprintf(stderr, "write to child failed\n");
int nbytes = read(pipe2[0], resultat, sizeof(resultat));
if (nbytes <= 0)
fprintf(stderr, "read from child failed\n");
else
printf("resultat: [%.*s]\n", nbytes, resultat);
close(pipe1[1]);
close(pipe2[0]);
}
else
{
printf("I am the child\n");
close(pipe1[1]);
close(pipe2[0]);
dup2(pipe1[0], 0);
dup2(pipe2[1], 1);
close(pipe1[0]); /* More closes! */
close(pipe2[1]); /* More closes! */
execlp("bc", "bc", NULL);
fprintf(stderr, "Failed to execute bc\n");
exit(EXIT_FAILURE);
}
return 0;
}
There is still room for improving the error handling; the code ploughs on after reporting some of the errors, which is probably not the best behaviour.
Output:
I am the parent
I am the child
resultat: [100
]
(Note: if you pipe the output of the program somewhere, you don't see the I am the child message. For the reasons why, see printf() anomaly after fork().)

Related

dup2() is blocking with child processes? C

I'm writing a function that echo an input to a sed and then another sed. I thinck i used all my wait signal in the right way but the last print i can get is before the call to dup2() in my first child process in the echo.
void sendbc (char * str_ ) {
int fd[2];
int fd1[2];
int pid,pid1;
char* echo[] = {"echo", str_,NULL};
char* sed1[] = {"sed","s/[^:]*;"" " "//",NULL};
char* sed2[] = {"sed","s/[^:]*."" " "//",NULL};
int status,er;
FILE *f;
if(pipe(fd) < 0){
exit(100);
}
if(pipe(fd1) < 0){
exit(100);
}
pid = fork();
if (pid == 0) {
dup2(fd[1], 1) //last command before blocking
close(fd[1]);
close(fd[0]);
execvp(echo[0], echo);
printf("Error in execvp1\n");
}else{
wait(&status);
pid = fork();
if (pid == 0){
dup2(fd[0], 0);
dup2(fd1[1], 1);
dup2(fd1[1], 2);
close(fd[1]);
close(fd[0]);
close(fd1[1]);
close(fd1[0]);
execvp(sed1[0],sed1);
printf("Error in execvp2\n");
}else{
wait(&status);
dup2(fd1[0],0);
dup2(1,2);
//dup2(1,1);
close(fd1[1]);
close(fd1[0]);
execvp(sed2[0],sed2);
printf("Error in execvp3\n");
}
}
if(pid!=0)
wait(&status);
close(fd[0]);
close(fd[1]);
close(fd1[1]);
close(fd1[0]);
}
I can imagine 2 possibilities... dup2 is blocking or i need to create more process because it end process on call, but this sounds not right after a quick read on his man page... what could it be?
General Problem
You aren't closing enough file descriptors in the various processes.
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 or F_DUPFD_CLOEXEC.
If the parent process will not communicate with any of its children via
the pipe, it must ensure that it closes both ends of the pipe early
enough (before waiting, for example) so that its children can receive
EOF indications on read (or get SIGPIPE signals or write errors on
write), rather than blocking indefinitely.
Even if the parent uses the pipe without using dup2(), it should
normally close at least one end of the pipe — it is extremely rare for
a program to read and write on both ends of a single pipe.
Note that the O_CLOEXEC option to
open(),
and the FD_CLOEXEC and F_DUPFD_CLOEXEC options to fcntl() can also factor
into this discussion.
If you use
posix_spawn()
and its extensive family of support functions (21 functions in total),
you will need to review how to close file descriptors in the spawned process
(posix_spawn_file_actions_addclose(),
etc.).
Note that using dup2(a, b) is safer than using close(b); dup(a);
for a variety of reasons.
One is that if you want to force the file descriptor to a larger than
usual number, dup2() is the only sensible way to do that.
Another is that if a is the same as b (e.g. both 0), then dup2()
handles it correctly (it doesn't close b before duplicating a)
whereas the separate close() and dup() fails horribly.
This is an unlikely, but not impossible, circumstance.
Specific Issues
You aren't closing enough file descriptors for safety.
Your regexes are dubious.
You should not make processes in a pipeline wait for each other.
Pet peeve: I prefer to use fd1 and fd2 when I have two closely related variables like the pairs of pipe file descriptors; I find fd and fd1 and the like silly. You may, however, choose to ignore this.
Working Code
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
static void dump_argv(char **argv)
{
printf("%d:\n", getpid());
while (*argv != NULL)
{
printf("%d: <<%s>>\n", getpid(), *argv++);
}
}
static void sendbc(char *str)
{
int fd1[2];
int fd2[2];
int pid;
char *echo[] = {"echo", str, NULL};
char *sed1[] = {"sed", "s/[^:]*[;]//", NULL};
char *sed2[] = {"sed", "s/[^:]*[.]//", NULL};
if (pipe(fd1) < 0)
exit(100);
if (pipe(fd2) < 0)
exit(101);
printf("%d: at work\n", getpid());
pid = fork();
if (pid < 0)
exit(102);
else if (pid == 0)
{
printf("%d: child 1 - echo\n", getpid());
dump_argv(echo);
dup2(fd1[1], 1);
close(fd1[1]);
close(fd1[0]);
close(fd2[0]);
close(fd2[1]);
execvp(echo[0], echo);
fprintf(stderr, "Error in execvp1\n");
exit(103);
}
else
{
printf("%d: parent - before second fork\n", getpid());
pid = fork();
if (pid == 0)
{
printf("%d: child 2 - sed 1\n", getpid());
dump_argv(sed1);
dup2(fd1[0], 0);
dup2(fd2[1], 1);
close(fd1[1]);
close(fd1[0]);
close(fd2[1]);
close(fd2[0]);
execvp(sed1[0], sed1);
fprintf(stderr, "Error in execvp2\n");
exit(104);
}
else
{
printf("%d: parent - sed 2\n", getpid());
dump_argv(sed1);
dup2(fd2[0], 0);
close(fd1[1]);
close(fd1[0]);
close(fd2[1]);
close(fd2[0]);
execvp(sed2[0], sed2);
fprintf(stderr, "Error in execvp3\n");
exit(105);
}
}
fprintf(stderr, "Reached unexpectedly\n");
exit(106);
}
int main(void)
{
char message[] =
"This is the first line\n"
"and this is the second - with a semicolon ; here before a :\n"
"and the third line has a colon : before the semicolon ;\n"
"but the fourth line has a dot . before the colon\n"
"whereas the fifth line has a colon : before the dot .\n"
;
sendbc(message);
return 0;
}
Example output
$ ./pipe29
74829: at work
74829: parent - before second fork
74829: parent - sed 2
74829:
74829: <<sed>>
74829: <<s/[^:]*[;]//>>
74830: child 1 - echo
74830:
74830: <<echo>>
74830: <<This is the first line
and this is the second - with a semicolon ; here before a :
and the third line has a colon : before the semicolon ;
but the fourth line has a dot . before the colon
whereas the fifth line has a colon : before the dot .
>>
74831: child 2 - sed 1
74831:
74831: <<sed>>
74831: <<s/[^:]*[;]//>>
This is the first line
here before a :
and the third line has a colon :
before the colon
whereas the fifth line has a colon :
$
Apart from the diagnostic printing, the primary differences are that this code rigorously closes all the unused ends of the pipes and it contains no calls to wait() or its relatives — they are not needed and in general are harmful when they block concurrent execution of the processes in the pipeline.

Using bc through pipes and fork

I'm trying to get the answer of a char expression using bc through pipes.
I would like first to write the expression in the pipe1, which the bc will read and write the answer in pipe2. For this I am changing the input and output. This does work if I do not use a char[] and just put the expression in the write :
write(pipe1[1], "20*5\n", sizeof("20*5\n")-1) != sizeof("20*5\n")-1)
But if I declare a tab, I keep getting the error :
(standard_in) 2: illegal character: ^#
Sometimes it is 1 instead of 2
What I am doing wrong? If someone could explain me, thank you.
Code :
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(void)
{
char resultat[5];
int pipe1[2];
int pipe2[2];
pipe(pipe1);
pipe(pipe2);
int resultat_fork = fork();
if (resultat_fork == -1)
{
exit(EXIT_FAILURE);
}
char* expression = "20*5\n";
if (resultat_fork != 0)
{
//printf("I am the parent\n");
close(pipe1[0]);
close(pipe2[1]);
if (write(pipe1[1], expression, sizeof(expression)) != sizeof(expression))
fprintf(stderr, "write to child failed\n");
int nbytes = read(pipe2[0], resultat, sizeof(resultat));
if (nbytes <= 0)
fprintf(stderr, "read from child failed\n");
else
printf("resultat: %.*s\n", nbytes, resultat);
close(pipe1[1]);
close(pipe2[0]);
}
else
{
printf("I am the child\n");
close(pipe1[1]);
close(pipe2[0]);
dup2(pipe1[0], 0);
dup2(pipe2[1], 1);
close(pipe1[0]); /* More closes! */
close(pipe2[1]); /* More closes! */
execlp("bc", "bc", NULL);
fprintf(stderr, "Failed to execute bc\n");
exit(EXIT_FAILURE);
}
return 0;
}
^# is the nul character i.e. '\0'. This would suggest you are overrunning the end of the string when you write it to bc. The problem is here:
sizeof(expression)
expression is not an array, it is a char pointer that points to the first character of the string "20*5\n" and, if you are on a 64 bit machine, its size is 8. To get the length of the string to send, use strlen(expression) instead.
Another thing you need to do, not related to your problem is, in the parent process, after you've read the answer, wait for the child process to finish. Otherwise, you'll be left with a zombie.
This is wrong
if (write(pipe1[1], expression, sizeof(expression)) != sizeof(expression))
You are using sizeof operator with expression which is a char*, if your system is 32-bit, it will result in 4, which is the size of any pointer variable in your code.
You need to use strlen(expression) instead of sizeof.
As pointed out in the answer as well, you need to wait for child process to finish its execution and terminate, if the parent process has more than one child processes. Ideally, you should also check the return value of wait, which provides you more context on how the child process was terminated.

using a child process to read input?

I'm trying to use a child process to count words from console input (what I type in UNIX). Here is my code:
int main(){
int pipe1[2];
int child_value;
pipe(pipe1);
child_value= fork();
if(child_value > 0){
/*parent*/
int word_count;
dup2(STDIN_FILENO, pipe1[0]);
close(pipe1[0]);
scanf("%d", &word_count);
printf("%d\n", word_count);
} else if (child_value == 0) {
/*child*/
dup2(pipe1[1], STDOUT_FILENO);
close(pipe1[1]);
execl("/usr/bin/wc", "wc", "-w", NULL);
err(EX_OSERR, "exec error");
} else err(EX_OSERR, "fork error");
return 0;
}
The output displayed on my console is always 0 disregarding what I type into the console, and I always get an error saying:
wc: standard input: Input/output error
As noted in comments:
When you use dup2() or dup() to map one end of a pipe to standard input or standard output, it is almost invariably correct to close both ends of the pipe afterwards. The exceptions are very few and far between; you will know when you need to avoid closing both ends of the pipe. However, that isn't the direct cause of your problem.
Compare: dup2(STDIN_FILENO, pipe1[0]); and dup2(pipe1[1], STDOUT_FILENO);. They should both list the standard file number as the second argument.
#include <err.h>
#include <fcntl.h>
#include <stdio.h>
#include <sysexits.h>
#include <unistd.h>
int main(void)
{
int pipe1[2];
int child_value;
pipe(pipe1);
child_value = fork();
if (child_value > 0)
{
/*parent*/
int word_count;
dup2(pipe1[0], STDIN_FILENO);
close(pipe1[0]);
close(pipe1[1]);
scanf("%d", &word_count);
printf("%d\n", word_count);
}
else if (child_value == 0)
{
/*child*/
dup2(pipe1[1], STDOUT_FILENO);
close(pipe1[1]);
close(pipe1[0]);
execl("/usr/bin/wc", "wc", "-w", NULL);
err(EX_OSERR, "exec error");
}
else
err(EX_OSERR, "fork error");
return 0;
}
Example output (program xx19):
$ ./xx19
So she went into the garden
to cut a cabbage-leaf
to make an apple-pie
and at the same time
a great she-bear coming down the street
pops its head into the shop
What no soap
So he died
and she very imprudently married the Barber
and there were present
the Picninnies
and the Joblillies
and the Garyulies
and the great Panjandrum himself
with the little round button at top
and they all fell to playing the game of catch-as-catch-can
till the gunpowder ran out at the heels of their boots
90
$
(You can search on Google for 'Panjandrum' to find out where that nonsense prose comes from.)

C Pipe to STDIN of Another Program

I can barely understand the man page for pipe, so I kinda need help understanding how to take a piped input in an external executable.
I have 2 programs: main.o & log.o
I written main.o to fork. Here is what it is doing:
Parent fork will pipe data to the child
Child fork will exec log.o
I need the child fork for main to pipe to STDIN of log.o
log.o simply takes STDIN & logs with time stamp to a file.
My code is composed of some code from various StackOverflow pages I dont remember & the man page for pipe:
printf("\n> ");
while(fgets(input, MAXINPUTLINE, stdin)){
char buf;
int fd[2], num, status;
if(pipe(fd)){
perror("Pipe broke, dood");
return 111;
}
switch(fork()){
case -1:
perror("Fork is sad fais");
return 111;
case 0: // Child
close(fd[1]); // Close unused write end
while (read(fd[0], &buf, 1) > 0) write(STDOUT_FILENO, &buf, 1);
write(STDOUT_FILENO, "\n", 1);
close(fd[0]);
execlp("./log", "log", "log.txt", 0); // This is where I am confused
_exit(EXIT_FAILURE);
default: // Parent
data=stuff_happens_here();
close(fd[0]); // Close unused read end
write(fd[1], data, strlen(data));
close(fd[1]); // Reader will see EOF
wait(NULL); // Wait for child
}
printf("\n> ");
}
I suppose this is what you're going to do:
1. main fork, parent pass message to child via pipe.
2. child receive message from pipe, redirect message to STDIN, execute log.
3. log receive message from STDIN, do something.
the key to do this is dup2 to redirect file descriptor, from pipe to STDIN.
This is the modified simple version:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
int main(int argc, char *argv[]) {
int fd[2];
char buf[] = "HELLO WORLD!";
if(pipe(fd)){
perror("pipe");
return -1;
}
switch(fork()){
case -1:
perror("fork");
return -1;
case 0:
// child
close(fd[1]);
dup2(fd[0], STDIN_FILENO);
close(fd[0]);
execl("./log", NULL);
default:
// parent
close(fd[0]);
write(fd[1], buf, sizeof(buf));
close(fd[1]);
wait(NULL);
}
printf("END~\n");
return 0;
}
I can suggest a simpler approach. There's a function called popen(). It works very similar to the system() function except you can read or write to/from the child stdin/stdout.
Example:
int main(int argc, char* argv[])
{
FILE* fChild = popen("logApp.exe", "wb"); // the logger app is another application
if (NULL == fChild) return -1;
fprintf(fChild, "Hello world!\n");
pclose(fChild);
}
Write "man popen" in your console for a full description.
You could use dup2
See Mapping UNIX pipe descriptors to stdin and stdout in C

Unix Pipes - Pipeline between three processes

I'm creating a small program which contains three processes; a source process, a filter process and a sink process. The stdout of the source process is redirected to the stdin of the filter process, and the filter process' stdout is redirected to the sink process' stdin.
My problem is that no output is printed to stdout from the sink process. Can any of you see the problem in the following tiny snippet of code?
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char** argv)
{
// Pipes
// pipe1 is from source to filter, pipe2 is from filter to sink
int pipe1[2], pipe2[2];
// Create pipes
if (pipe(pipe1) < 0 || pipe(pipe2) < 0)
{
perror("Creating pipes failed!");
}
if (fork() == 0)
{
close(1);
dup(pipe1[1]);
close(pipe1[0]);
close(pipe2[0]);
close(pipe2[1]);
execlp("ls", "ls", NULL);
exit(0);
}
else
{
if (fork() == 0)
{
close(0);
dup(pipe1[0]);
close(pipe1[1]);
close(1);
dup(pipe2[1]);
close(pipe2[0]);
execlp("sort", "sort", NULL);
exit(0);
}
else
{
if (fork() == 0)
{
close(0);
dup(pipe2[0]);
execlp("more", "more", NULL);
exit(0);
}
}
}
wait(NULL);
printf("Done.\n");
return 0;
}
BR
Jacob
I think problem may be, wait will only wait for one process. And when the parent exits after first child returns, I suspect more command also decides to terminate, because it may get SIGHUP (speculation, not sure).
But, check for errors from on all system calls! Also for wait calls which succeeded, print why the child exited (was it signal or normal exit, and if it was normal exit, what was exit code).
Also note, perror does not exit, it only prints.
It is kind of pointless trying to see why some code fails, if it does not have error handling in it...
Some easy way to do pipes for your scenario:
char cmd[MAX_LEN];
sprintf(cmd, "%s | %s | %s", app1, app2, app3); //app123 holds app name + args
system(cmd);
if you want to capture the output of the last app, use popen:
FILE pPipe = popen(cmd, "rt"); /* same access flag as fopen()*/
while (NULL != fget(buf, buf_len, pPipe)) {
// do something with the read line in 'buf'
}

Resources