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.
Related
I am trying to create a simple pipe/fork function, so that child process modifies the value of text, then it is printed by the parent.
I have checked a similar question on Modify variable in child process, but I am unable to print the text variable in the parent.
int main()
{
pid_t childp;
char text[100];
pipe(text);
childp = fork();
if (childp ==0){
strncpy(text, "Hello world", 100); // child running
}
else{
printf("%s\n", text); // parent prints "Hello world"
}
return 1;
}
Any help is appreciated (I am very new to C language)
look into this website :
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
int main(void)
{
int fd[2], nbytes;
pid_t childpid;
char string[] = "Hello, world!\n";
char readbuffer[80];
pipe(fd);
if((childpid = fork()) == -1)
{
perror("fork");
exit(1);
}
if(childpid == 0)
{
/* Child process closes up input side of pipe */
close(fd[0]);
/* Send "string" through the output side of pipe */
write(fd[1], string, (strlen(string)+1));
exit(0);
}
else
{
/* Parent process closes up output side of pipe */
close(fd[1]);
/* Read in a string from the pipe */
nbytes = read(fd[0], readbuffer, sizeof(readbuffer));
printf("Received string: %s", readbuffer);
}
return(0);
}
It explain how to properly use C pipes. Take a look to this too.
Add this for error handling :
if (pipe(fd) == -1) {
fprintf(stderr, "Pipe Failed");
return 1;
}
The closest to your code working example I can come up with.
int main()
{
pid_t childp;
char text[100];
int fd[2];
pipe(fd);
childp = fork();
if (childp ==0){
FILE *f=fdopen(fd[1], "w"); // Write into this "file" what you want the other end to read.
fprintf(f, "Hello world\n");
}
else{
FILE *f=fdopen(fd[0], "r"); // We can read from this "file"
fgets(text, 100, f); // Read one line of text from the "file" up to 100 bytes
printf("read <%s> from by new child\n", text)
}
return 1;
}
Note again that this is not a shared buffer. So you need both end to agree on a "protocol". Because everything that is written by one end must be read by the other (otherwise the "write" instruction will be blocked), and everything that is read by one end, must be writter by the other (otherwise the "read" instruction will be blocked).
So, either you use a fixed size message, for example. If you choose 100, you need to write 100 bytes exactly at one end (fill with 0 if needed), and read 100 bytes exactly at the other.
Or you find some protocol so that the reading end knows exactly when to stop reading.
I choose the latter (because it is the closest to your code). By using fgets to read, that stop to read at each newline, and fprintf a message ended by a newline at the writing end.
EDIT: THE QUESTION IS ANSWERED IN COMMENTS
So, i'm studying pipes. Long story short, i have two programs:
first program creates a pipe and two forks: first fork closes read descriptor and writes some stuff to write one (then closes it), second fork closes write one, dup2 on read side of pipe to standard input (end closes read side itself) and execl second program, giving a size of a text the first fork writes as an argument; the parent closes both pipe sides and waitpids for child that wasexecld (second one).
second program just reads from its standard input (pipe side) the stuff and writes it out to standard output, then closes pipe side just in case.
In such a setup everything works as I intended, but when I delete waitpid in first program (or just wait for the first child that writes instead of the second one), the second one behaves weirdly - it executes till the end, passing through all the IO (that is, the printf before exit got executed), and then doesn't give me the prompt back. That is, the terminal looks like the program awaits for an input from standard input. If i execute the first program without execl, then everything works fine, If I execute just the second one with one argument, then it waits only until input is provided to standard input (as it should as it is not a part of a pipe in this case).
As i know, when parent terminates, the child is "inherited" by init and got waited. But even if it wasn't, that is, even if it remained as a zombie, then it still would be weird - why i cannot get my prompt back until i wait explicitly?
The code is below (of the setup that works correctly):
First program
/* headers */
int main(void)
{
int fildes[2];
pid_t p1, p2;
int status;
char mess[] = "written from execved program!\n";
int buf = strlen(mess);
if(pipe(fildes) == -1) {
perror("pipe in main");
exit(EXIT_FAILURE);
}
p1 = fork();
if(p1 == -1) {
perror("fork p1 in main");
exit(EXIT_FAILURE);
}
else if (p1 == 0) {
printf("Child 1!\n");
close(fildes[0]);
write(fildes[1], mess, buf);
close(fildes[1]);
printf("Before exit in child 1!\n");
exit(EXIT_SUCCESS);
}
p2 = fork();
if(p2 == -1) {
perror("fork p2 in main");
exit(EXIT_FAILURE);
}
else if (p2 == 0) {
printf("Child 2!\n");
dup2(fildes[0], 0);
close(fildes[0]);
close(fildes[1]);
char s_buf[30];
sprintf(s_buf, "%d", buf);
execl("./pipe2slave", "pipe2slave", s_buf, (char *) 0);
perror("execl have returned");
exit(EXIT_FAILURE);
}
close(fildes[0]);
close(fildes[1]);
/*
below if I wait for, say, p1, or don't wait it all,
the weird behavior described in my question happens
*/
if(waitpid(p2, &status, 0) == -1) {
perror("waitpid in main");
exit(EXIT_FAILURE);
}
if(WIFEXITED(status))
printf("pipe2slave exit status is %d\n", WEXITSTATUS(status));
printf("End of main in pipe2!\n");
exit(EXIT_SUCCESS);
}
Second program
/* headers */
int main(int argc, char **argv)
{
if (argc != 2) {
perror("pipe2slave - not enough args");
exit(EXIT_FAILURE);
}
printf("program name is %s\n", argv[0]);
int buf = atoi(argv[1]);
printf("%d\n", buf);
char mess_in[buf];
read(0, mess_in, buf);
write(1, mess_in, buf);
fsync(1);
close(0);
printf("end of slave!\n");
exit(EXIT_SUCCESS);
}
Thank you in advance!
I am learning about system calls, fork and pipe. I am creating a C program in which the parent process sends a character array to the child process and child process capitalizes the first 4 characters of the array and sends it back. The array is sent properly from parent to child, child makes the conversion and even writes to the second pipe properly, but parent process is not able to read the new array from the pipe 2.
I've tried closing the unnecessary descriptors as well, but that didn't work. I read somewhere that parent process might be finishing before there is something to read from the pipe, for which I tried wait function(but I might have done this wrong. I am not sure.)
I tried checking the size of values sent and received by the processes,
Parent writes (8)
Child reads (8)
Child writes (8)
Parent reads (1)
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
int main(int argc, char *argv[]){
int pipe1[2];
int pipe2[2];
char str[8], str1[8];
pid_t pid;
if(pipe(pipe1) < 0){
perror("Pipe 1 not created\n");
exit(EXIT_FAILURE);
}
if(pipe(pipe2) < 0){
perror("Pipe 2 not created\n");
exit(EXIT_FAILURE);
}
pid = fork();
if (pid == 0){
close(pipe1[1]);
close(pipe2[0]);
printf("\nChild Process");
ssize_t rd_stat_child = read(pipe1[0], str, 8*sizeof(char));
if(rd_stat_child > 0){
printf("rc%zd\n", rd_stat_child);
for(int i = 0; i < 4; i++){
str[i] = ((char)(((int)str[i])-32));
}
printf("\nFinal str in child: %s\n", str);
ssize_t wr_stat_child = write(pipe2[1], str, 8*sizeof(char));
printf("wc%zd\n", wr_stat_child);
if(wr_stat_child != sizeof(str)){
perror("Sending to parent failed");
exit(EXIT_FAILURE);
}
}else{
perror("Child failed to read");
exit(EXIT_FAILURE);
}
}else if (pid > 0){
close(pipe1[0]);
close(pipe2[1]);
printf("\nParent Process");
printf("\nEnter a 8 character string: ");
scanf("%s", str);
if(sizeof(str)/(8*sizeof(char)) != 1){
perror("Size of string greater than 8\n");
exit(EXIT_FAILURE);
}else{
ssize_t wr_stat_parent = write(pipe1[1], str, 8*sizeof(char));
printf("wp%zd\n", wr_stat_parent);
if(wr_stat_parent != sizeof(str)){
perror("Parent failed writing.\n");
exit(EXIT_FAILURE);
}
ssize_t rd_stat_parent = read(pipe2[0], str, 8*sizeof(char));
close(pipe2[0]);
if(rd_stat_parent <= sizeof(str)){
printf("rp%zd\n", rd_stat_parent);
printf("\nParent Recieved\n %s", str);
}else{
perror("Parent error while reading\n");
exit(EXIT_FAILURE);
}
}
}
return 0;
}
Expected Output
Parent Process
(input) >> lkybzqgv
Child Process
(process) >> LKYBzqgv
Parent Process
(output) >> LKYBzqgv
Actual Output
Parent Process
(input) >> lkybzqgv
Child Process
(process) >> LKYBzqgv
Parent Process
(output) >> kybzqgv
Your string-handling is broken. You need an array of length 9 to hold a string of length 8. (Remember that strings in c are zero-terminated). DO NOT WRITE scanf("%s", str); TO READ STRINGS !! That is just as bad as using gets(). It allows you to overflow the buffer (Which actually happens in your case). Read strings like this:
scanf("%8s", str);
This will read at most 8 (non-whitespace) characters and store them together with the zero-termination in str. (remember again that str must be large enough for 8 charecters + 1 termination character)
Then to check the length of a string, use strlen(), do not use sizeof(). sizeof may only tell the size of the array holding the string, or the pointer pointing to the string. Remember that the array holding the string must be at least 1 character larger than the string, but is allowed to be larger than that. And the size of the array is fixed at creation. It doesn't change size depending on what you put in it.
Oh, and by the way. You don't send/receive the termination character, so you have to set it yourself manually after you have called read():
read(pipe1[0], str, 8);
str[8] = 0;
There may be other problems with your code, but unless you fix the string-issues, you have undefined behavior, and everything else doesn't really matter.
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().)
Im trying to solve this exercise:
"make a program that receives as argument 2 other programs (ex:"flow ls wc"). It should run both programs, using the output of the 1st program as the input of the 2nd and measure the amount of data sent. Print that amount every second on your stdout."
I have this:
int fluxo = 0;
void fa(){
printf("\nFlux: %d\n", fluxo);
fluxo = 0;
alarm(1);
}
int main(int argc,char **argv){
int pd[2];
pid_t p;
pipe(pd);
char *buf;
signal(SIGALRM,fa);
alarm(1);
if( (p=fork())==0 ){
close(pd[0]);
dup2(pd[1],1);
close(pd[1]);
execlp(argv[1],argv[1],NULL);
exit(EXIT_FAILURE);
}
else{
wait(NULL);
while(read(pd[0],&buf,1)==1)
fluxo++;
close(pd[1]);
dup2(pd[0],0);
close(pd[0]);
execlp(argv[2],argv[2],NULL);
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
}
I'm using that while loop to measure the amount of data. However i'm getting stuck on that while even after the execlp in the child process sent it's data, and therefore it's not executing the 2nd program.. It's stuck the while loop even after reading all of the data in the pipe.. Why?
First of all, you are declaring char* buf, but your pointer isn't allocated. I think you meant to use char buf (since you are using &buf in the read).
Your while loop isn't stucked, the call to read is. In fact, your parent process still has pd[1] descriptor opened when you are calling readfunction. So when your buffer is empty, it will wait.
You should replace this :
else{
wait(NULL);
while(read(pd[0],&buf,1)==1)
fluxo++;
close(pd[1]);
dup2(pd[0],0);
close(pd[0]);
execlp(argv[2],argv[2],NULL);
exit(EXIT_FAILURE);
}
by this :
else{
close(pd[1]);
wait(NULL);
while(read(pd[0],&buf,1)==1)
fluxo++;
dup2(pd[0],0);
close(pd[0]);
execlp(argv[2],argv[2],NULL);
exit(EXIT_FAILURE);
}
EDIT:
Note:
Reading byte per byte is not efficient, you will get better performance with a larger buffer. For example:
char buf[1024]
and your loop :
for(;;){
ssize_t rd = read(pd[0], buf, 1024);
if(rd==-1){
perror("Reading");
exit(EXIT_FAILURE);
}
if(rd==0) break;
fluxo+=rd;
}