Parent reads operations from file and the child sums it with bc - c

I'm trying to make a program where the parent reads from a file some operations, passes them to the child with a pipe, and the child makes all the operations with bc. Later on, the child has to pass it back to the parent and this one has to write it on a file.
However, when I execute it, I don't get any result and don't know where the problem is. The child seems to receive the operation correctly, but with 'EXT' character.
My code works perfectly when the parent doesn't read from the file, the problem is when I try to read from a file.
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define EOL '\n'
int main(int argc, char *argv[]){
int tube1[2];
int tube2[2];
int fID;
pipe(tube1);
pipe(tube2);
if(argc != 2){
perror("./yourProgram.c [origin]");
exit(EXIT_FAILURE);
}
if (fork() == 0){
//Child Process
close(tube1[1]); // writing end of the pipe
dup2(tube1[0], 0); // stdin ----> pipe reading end
close(tube2[0]); // reading end of the pipe
dup2(tube2[1], 1); // stdout ---> pipe writing end
//Execute and write the output in the tube2
execlp("bc", "bc", "-q", NULL);
}else {
//Parent Process
close(tube1[0]); // reading end of the pipe
//dup2(tube1[1], 1); // stdout ---> pipe writing end
close(tube2[1]); // reading end of the pipe
//dup2(tube1[1], 1); // stdout ---> pipe writing end
//Files
//Destiny
char *destiny = "final.txt";
int destinyFile = open(destiny, O_WRONLY | O_CREAT | O_TRUNC, 0644);
//Origin
char *origin = argv[1];
int originFile = open(origin, O_RDONLY);
//Variables
char block;
char result;
char buffer[4096];
int i = 0;
int numbytes;
while(numbytes = read(originFile, &block, sizeof(block)) > 0){
if(block == EOL){
//Write on the tube, so the child can read it
if(write(tube1[1], buffer, strlen(buffer)) == -1){
perror("error en write en pipe");
exit(EXIT_FAILURE);
}
//Read the child's answer
while(numbytes = read(tube2[0], &result, 1) > 0){
if(result != EOL){
//Concatenate strings as: 'X + Y = Result \n'
char str[80];
strcat(str, buffer);
strcat(str, " = ");
strcat(str, &result);
strcat(str, "\n");
//Write the result in the Final File
if(write(destinyFile, str, strlen(str)) == -1){
perror("error en write en stdout");
exit(EXIT_FAILURE);
}
}else
continue;
}
//Reset Buffer
buffer[0] = '\0';
i = 0;
}else{
buffer[i] = block;
i = i + 1;
}
}
}
}
And the file from where I read is:
2+3
4*5
8/2
quit

This code fixes the critical problems noted in the comments (but not the performance issues from reading one byte at a time). If the calculation produces a result that's big enough, bc splits its output over multiple lines (calculating factorial 100, for example). The code does not attempt to deal with that. Also, some operations produce no output. For example, c=2345 produces no output. The program does not handle this, either.
You aren't closing enough file descriptors in the child.
Rule of thumb: If you
dup2()
one end of a pipe to standard input or standard output, close both ends
of the pipe as soon as possible.
That means before using any of the
exec*()
family of functions in particular.
The rule of thumb also includes using
dup()
or
fcntl()
with F_DUPFD.
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define EOL '\n'
int main(int argc, char *argv[])
{
int tube1[2]; // Parent -> child
int tube2[2]; // Child -> parent
if (argc != 2)
{
perror("./yourProgram.c origin");
exit(EXIT_FAILURE);
}
pipe(tube1);
pipe(tube2);
if (fork() == 0)
{
// Child Process
dup2(tube1[0], 0); // stdin ----> pipe reading end
close(tube1[0]); // reading end of the pipe to child
close(tube1[1]); // writing end of the pipe to child
dup2(tube2[1], 1); // stdout ---> pipe writing end
close(tube2[0]); // reading end of the pipe to parent
close(tube2[1]); // writing end of the pipe to parent
// Execute and write the output in the tube2
execlp("bc", "bc", "-q", NULL);
perror("bc");
exit(EXIT_FAILURE);
}
else
{
// Parent Process
close(tube1[0]); // reading end of the pipe to child
close(tube2[1]); // writing end of the pipe to parent
// Files
// Destiny
char *destiny = "final.txt";
int destinyFile = open(destiny, O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (destinyFile < 0)
{
perror(destiny);
exit(EXIT_FAILURE);
}
// Origin
char *origin = argv[1];
int originFile = open(origin, O_RDONLY);
if (originFile < 0)
{
perror(origin);
exit(EXIT_FAILURE);
}
// Variables
char block;
char result;
char buffer[256];
int i = 0;
int numbytes;
while ((numbytes = read(originFile, &block, sizeof(block))) > 0)
{
buffer[i++] = block;
//printf("Block: [%.*s]\n", i, buffer);
if (block == EOL)
{
// Write on the tube, so the child can read it
if (write(tube1[1], buffer, i) == -1)
{
perror("error en write en pipe");
exit(EXIT_FAILURE);
}
buffer[i-1] = '\0';
// Read the child's answer
int j = 0;
char reply[256];
while ((numbytes = read(tube2[0], &result, sizeof(result))) > 0)
{
reply[j++] = result;
//printf("Reply: [%.*s]\n", j, reply);
if (result == EOL)
{
// Concatenate strings as: 'X + Y = Result \n'
char str[256];
str[0] = '\0';
strcat(str, buffer);
strcat(str, " = ");
strcat(str, reply);
// Write the result in the Final File
if (write(destinyFile, str, strlen(str)) == -1)
{
perror("error en write en stdout");
exit(EXIT_FAILURE);
}
printf("Response: [%.*s]\n", j-1, reply);
break;
}
}
// Reset Buffer
i = 0;
}
}
close(tube1[1]);
close(tube2[0]);
}
return 0;
}
Given the sample input file:
2+3
4*5
8/2
quit
the output on the screen is:
Response: [5]
Response: [20]
Response: [4]
and the output in final.txt is:
2+3 = 5
4*5 = 20
8/2 = 4

Related

Read and write using pipes

I have a program with 2 child processes which has to do the following:
use the parent to read data from a file 'data.txt' and write in a pipe
use a child to read the data from the pipe and filter the lowercase letters
use another child to write the filtered letters in a new file, each on a new line
I tried to do it and it works... kinda. The problem is, it writes the filtered letters in the desired file, but the program does not stop. What am I doing wrong?
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <string.h>
int parentChildpipeFileDescriptors[2], child1Child2FileDescriptors[2];
void parentProcess()
{
close(child1Child2FileDescriptors[0]);
close(child1Child2FileDescriptors[1]);
close(parentChildpipeFileDescriptors[0]);
int fileDescriptor = open("data.txt", O_RDONLY);
char buffer[8];
int store;
while ((store = read(fileDescriptor, buffer, 8)))
{
write(parentChildpipeFileDescriptors[1], buffer, store);
}
close(fileDescriptor);
close(parentChildpipeFileDescriptors[1]);
}
void child1Process()
{
close(parentChildpipeFileDescriptors[1]);
close(child1Child2FileDescriptors[0]);
char buffer[8];
int store, count = 0;
while ((store = read(parentChildpipeFileDescriptors[0], buffer, 8)))
{
for (int i = 0; i < store; i++)
{
if (buffer[i] >= 'a' && buffer[i] <= 'z')
{
count++;
write(child1Child2FileDescriptors[1], &buffer[i], sizeof(buffer[i]));
}
}
}
printf("CHILD 1 FINISHED FILTERING\n");
close(parentChildpipeFileDescriptors[0]);
close(child1Child2FileDescriptors[1]);
exit(count);
}
void child2Process()
{
close(parentChildpipeFileDescriptors[0]);
close(child1Child2FileDescriptors[1]);
mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
char *fileName = "stat.txt";
int newFileDescriptor = creat(fileName, mode);
char buffer;
int store;
while ((store = read(child1Child2FileDescriptors[0], &buffer, 1)))
{
write(newFileDescriptor, &buffer, sizeof(buffer));
write(newFileDescriptor, "\n", 1);
}
close(newFileDescriptor);
printf("CHILD 2 FINISHED WRITING'\n");
close(child1Child2FileDescriptors[0]);
close(parentChildpipeFileDescriptors[1]);
exit(444);
}
int main(int argc, char const *argv[])
{
if (pipe(parentChildpipeFileDescriptors) < 0)
{
printf("ERROR CREATING PIPE\n");
exit(-100);
}
if (pipe(child1Child2FileDescriptors) < 0)
{
printf("ERROR CREATING PIPE\n");
exit(-101);
}
pid_t child1PID = fork();
if (child1PID < 0)
{
printf("ERROR CREATING CHILD\n");
exit(-200);
}
if (!child1PID)
{
child1Process();
}
pid_t child2PID = fork();
if (child2PID < 0)
{
printf("ERROR CREATING CHILD\n");
exit(-201);
}
if (!child2PID)
{
child2Process();
}
parentProcess();
int status1, status2;
waitpid(child1PID, &status1, 0);
waitpid(child2PID, &status2, 0);
printf("CHILD 1 TERMINATED WITH EXIT STATUS: %d\n", WEXITSTATUS(status1));
printf("CHILD 2 TERMINATED WITH EXIT STATUS: %d\n", WEXITSTATUS(status2));
return 0;
}
The read loop in child1process will never terminate, because child2 still has the write side of that pipe open. You need to execute:
close(parentChildpipeFileDescriptors[1]);
before you enter the read loop. The general rule is that if a process isn't going to use a file descriptor, it should close it immediately.
your while ((store = read(parentChildpipeFileDescriptors[0], buffer, 8))) loop is never gonna end.
The parent needs to say to the child that there is no more data coming and it shall not do another read.
You can do this by sending a special byte.
Example :
in the parent:
char endByte = 0x1;
write(parentChildpipeFileDescriptors[1], &endByte, 1);
//then close
in the while loop of the child :
if(buffer[i] == 0x1){
printf("CHILD 1 FINISHED FILTERING\n");
fflush(stdout);
close(parentChildpipeFileDescriptors[0]);
close(child1Child2FileDescriptors[1]);
exit(count);
};

Strange letter when passing character through a pipe

I've an input file whose content is abcefghz. I want to use a pipe so that a child process p1 sends 1-letter per time to his parent process which will converts this string using the ASCII code+1 (abcefghz will become cdfghi{). This new string will be send through another pipe to another child process which will print the result on an output file.
This is the code:
int main (int argc, char **argv)
{
pid_t pid1, pid2;
int inputFile, outputFile;
char stringaDalFile[256];
char successivo;
char stringaRisultato[256];
int fd1[2], fd2[2]; // Pipe
inputFile = open(argv[1], O_RDONLY);
outputFile = open(argv[2], O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
pipe(fd1);
pipe(fd2);
pid1 = fork();
if (pid1 == 0) {
while ( (nread=read(inputFile, stringaDalFile, 1)) > 0) {
close(fd1[0]);
write(fd1[1], stringaDalFile, 1);
}
close(inputFile);
}
else {
close(fd1[1]);
while ( read(fd1[0], stringaDalFile, 1) > 0 ) {
successivo = converti(stringaDalFile[0]);
write(fd2[1], &successivo, 1);
}
}
pid2 = fork();
if (pid2 == 0) {
close(fd2[1]);
if (read(fd2[0], stringaRisultato, 1) == -1) {
perror("Errore");
exit(1);
}
else {
while ( read(fd2[0], stringaRisultato, 1) > 0 ) {
write(STDOUT_FILENO, stringaRisultato, strlen(stringaRisultato)); //dbg
write(outputFile, stringaRisultato, strlen(stringaRisultato));
}
}
close(outputFile);
exit(0);
}
return 0;
}
char converti (char carattere)
{
return carattere+1;
}
Unfortunately, this seems to not work at 100%, the string is converted but the program enters what seems an infinite loop:
If I CTRL-C and gedit file2.txt, this is its content:
.
How do I fix this?
There are a few problems in your code:
The real problem, here:
write(outputFile, stringaRisultato, strlen(stringaRisultato));
you're using strlen(stringaRisultato) when stringaRisultato only has one valid character at the beginning and no NUL-terminator after it. Just use 1 instead.
You are only reading one character at a time, you don't need a string of 256 characters. Change stringaDalFile and stringaRisultato into two single char variables: carattereDalFile and carattereRisultante.
In the first child (inside if (pid1 == 0)) you are doing close(fd1[0]) in a loop. You should move it out of the while.
Again in the first child, you're not doing exit(0). This is what causes your program to keep running.
This line inside the second child:
if (read(fd2[0], stringaRisultato, 1) == -1) {
is only useful if you want to skip the first character. Is that intended? If not, remove that if and only use the while (read(...) > 0).
You are writing everything to the pipe fd2[1] before starting the second child (that will read from it). If the input file is too large (some KB) this will result in filling the pipe internal buffer and will block your program on the next write() that it tries to perform, making it never end. To fix this, you should start the two child processes together. This would require changing the structure and logic of the code, but it's doable.
Since I don't think that's the real problem here and this program is most probably written for educational purposes I'll leave that to you and fix the rest of the above points.
Working code:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
char converti (char carattere);
int main (int argc, char **argv)
{
pid_t pid1, pid2;
int inputFile, outputFile;
char carattereDalFile, carattereRisultante, successivo; // renamed
int fd1[2], fd2[2];
inputFile = open(argv[1], O_RDONLY);
outputFile = open(argv[2], O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
pipe(fd1);
pipe(fd2);
pid1 = fork();
if (pid1 == 0) {
close(fd1[0]); // <== moved out of the while loop
while (read(inputFile, &carattereDalFile, 1) > 0) {
write(fd1[1], &carattereDalFile, 1);
}
close(inputFile);
exit(0); // <== added
} else {
close(fd1[1]);
while (read(fd1[0], &carattereDalFile, 1) > 0) {
successivo = converti(carattereDalFile);
write(fd2[1], &successivo, 1);
}
}
pid2 = fork();
if (pid2 == 0) {
close(fd2[1]);
if (read(fd2[0], &carattereRisultante, 1) == -1) {
perror("Errore");
exit(1);
} else {
while (read(fd2[0], &carattereRisultante, 1) > 0) {
write(outputFile, &carattereRisultante, 1);
}
}
close(outputFile);
exit(0);
}
return 0;
}
char converti (char carattere)
{
return carattere+1;
}
read returns the number of byte read in so you should use that to add the NUL terminating character to the string it's populated like this
while ( (bytesread = read(inputFile, stringaDalFile, 1)) > 0) {
stringaDalFile[bytesread] = '\0';
....

Almost done linux shell pipe

Hi i'm trying to build a shell on linux and i'm stuck with the pipelining part.First i take the inputs from the user like "ls | sort" then when i try to run the program it lookls like the commands ls and sort doesnt work
It looks like i've done everything right but it still cant seem to work. can you help please. thanks in advance
include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include <sys/param.h>
#include <fcntl.h>
#include <sys/stat.h>
#define CREATE_FLAGS (O_WRONLY | O_CREAT | O_APPEND)
#define CREATE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
int setup();
int main(int argc, const char * argv[])
{
while(1)
{
printf("333sh: ");
if(setup())
break;
}
return 0;
}
int setup(){
char input [128];
char *arg[32];
int i = 1;
while(fgets(input,128,stdin)!=NULL)
{
arg[0] = strtok(input," \n");
while((arg[i]=strtok(NULL," \n")) != NULL){
i++;
}
if (arg[1]!=NULL && strcmp(arg[1],"|")==0 && arg[2]!=NULL ){
pid_t pid;
int fd[3];
pipe(fd);
pid=fork();
if(pid<0){
printf("fork");
}
else if(pid==0){
pid_t cpid;
cpid=fork();
if(cpid==0){
dup2(fd[2], 1); // Replace stdin with the read end of the pipe
close(fd[0]); // Don't need another copy of the pipe read end hanging about
close(fd[2]);
execvp(arg[0],arg);
}
else if(pid>0){
dup2(fd[0], 0); // Replace stdout with the write end of the pipe
close(fd[0]); //close read from pipe, in parent
close(fd[2]); // Don't need another copy of the pipe write end hanging about
execvp(arg[2], arg);
}
}
else if(pid>0){
waitpid(pid, NULL,0);
}
}
}
}
Your biggest problem is that your argument lists for your commands are malformed (after you've resolved the index 2 vs index 1 issue with the pipe file descriptors diagnosed by Ben Jackson in his answer).
I added a function:
static void dump_args(int pid, char **argv)
{
int i = 0;
fprintf(stderr, "args for %d:\n", pid);
while (*argv != 0)
fprintf(stderr, "%d: [%s]\n", i++, *argv++);
}
and called it just before the calls to execvp(), and the output I got was:
$ ./ns
333sh: ls | sort
args for 29780:
0: [ls]
1: [|]
2: [sort]
ls: sort: No such file or directory
ls: |: No such file or directory
^C
$
The control-C was me interrupting the program. The arguments for each command must be 'the command name' (conventionally, the name of the executable), followed by the remaining arguments and a null pointer.
Your tokenization code is not providing two correct commands.
You also have a problem with which PID you're looking at:
cpid = fork();
if (cpid == 0)
{
dup2(fd[1], 1);
close(fd[0]);
close(fd[1]);
dump_args(getpid(), arg);
execvp(arg[0], arg);
fprintf(stderr, "Failed to exec %s\n", arg[0]);
exit(1);
}
else if (pid > 0) // should be cpid!
{
dup2(fd[0], 0);
close(fd[0]);
close(fd[1]);
dump_args(pid, arg);
execvp(arg[1], arg);
fprintf(stderr, "Failed to exec %s\n", arg[1]);
exit(1);
}
You also need to close the pipe file descriptors in the parent process before waiting.
This code compiles and 'works' for simple x | y command sequences such as ls | sort or ls | sort -r. However, it is far from being a general solution; you'll need to fix your argument parsing code quite a lot before you reach a general solution.
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int setup(void);
int main(void)
{
while (1)
{
printf("333sh: ");
if (setup())
break;
}
return 0;
}
static void dump_args(int pid, char **argv)
{
int i = 0;
fprintf(stderr, "args for %d:\n", pid);
while (*argv != 0)
fprintf(stderr, "%d: [%s]\n", i++, *argv++);
}
int setup(void)
{
char input[128];
char *arg[32];
int i = 1;
while (fgets(input, sizeof(input), stdin) != NULL)
{
arg[0] = strtok(input, " \n");
while ((arg[i] = strtok(NULL, " \n")) != NULL)
{
i++;
}
if (arg[1] != NULL && strcmp(arg[1], "|") == 0 && arg[2] != NULL)
{
pid_t pid;
int fd[2];
arg[1] = NULL;
pipe(fd);
pid = fork();
if (pid < 0)
{
fprintf(stderr, "fork failed\n");
return 1;
}
else if (pid == 0)
{
pid_t cpid = fork();
if (cpid < 0)
{
fprintf(stderr, "fork failed\n");
return 1;
}
else if (cpid == 0)
{
printf("Writer: [%s]\n", arg[0]);
dup2(fd[1], 1);
close(fd[0]);
close(fd[1]);
dump_args(getpid(), arg);
execvp(arg[0], arg);
fprintf(stderr, "Failed to exec %s\n", arg[0]);
exit(1);
}
else
{
printf("Reader: [%s]\n", arg[2]);
assert(cpid > 0);
dup2(fd[0], 0);
close(fd[0]);
close(fd[1]);
dump_args(getpid(), &arg[2]);
execvp(arg[2], &arg[2]);
fprintf(stderr, "Failed to exec %s\n", arg[2]);
exit(1);
}
}
else
{
close(fd[0]);
close(fd[1]);
assert(pid > 0);
while (waitpid(pid, NULL, 0) != -1)
;
}
}
}
return 1;
}
You're using fd[0] and fd[2] but pipe(fd) only sets fd[0] and fd[1].
Couple of immediate problems:
setup() has no return value, but you expect an int
The definition of fgets is:
char * fgets ( char * str, int num, FILE * stream );
Get string from stream
Reads characters from stream and stores them as a C string into str until (num-1) characters have been read or either a newline or the end-of-file is reached, whichever happens first.
A newline character makes fgets stop reading, but it is considered a valid character by the function and included in the string copied to str.
fgets() returns NULL on an error; otherwise it returns a pointer to str. So this seems like a very unsound test condition in your while loop.

New to IPC, can't get my pipe to work

Sorry for the length of this post... I've encountered about a zillion problems in this. Up front I'll say I'm a student and my professor is a worthless resource. So, all I want to to do is have producer fork, then the parent producer will count some stuff in a file and send two ints to consumer, which was launched by the child process. I've tested everything, the fork and the file stuff works and I have printf statements all over the place so I know what is being done and where the code is at.
When I added the
if (pipe(pipefd) == -1) {
perror("pipe");
}
it caused my parent to just terminate. It reaches "parent pipe open" but then it dies. I checked with $ ps to see if it was just hung, but it's not there; it just dies. If I take that snippet out, it runs to the end but I presume if that code isn't there, then it's not actually aware that pipefd is a pipe... right?
I did search on this site and found another example of this and followed what he did as well as the answer and mine just refuses to work. I'm pretty sure it's a trivially easy thing to fix but I've run out of ideas of what to try :(
I don't really want to post all my code because it'll be a huge wall of text but I don't want to accidentally cut something out that turns out to be important either.
producer.c
#include <stdio.h> /* printf, stderr, fprintf */
#include <sys/types.h> /* pid_t */
#include <unistd.h> /* _exit, fork, execl */
#include <stdlib.h> /* exit */
#include <errno.h> /* errno */
#include <string.h> /* strlen */
#include <sys/wait.h> /* wait */
#define SLEEP_TIME 8
int main (int argc, char *argv[]){
//PID
pid_t local_pid;
local_pid = fork();
//Logic to determine if the process running is the parent or the child
if (local_pid == -1) {
/* Error:
* When fork() returns -1, an error happened
* (for example, number of processes reached the limit).
*/
fprintf(stderr, "can't fork, error %d\n", errno);
exit(EXIT_FAILURE);
} else if (local_pid == 0) {
//Child specific code
int child;
char *temp[] = {NULL};
printf("Child PID found\n");
child = execv("./consumer", temp);
_exit(0);
} else {
//Parent specific code
printf("Parent running\n");
//open file
FILE * randStrings;
randStrings = fopen("randStrings.txt", "r");
int file_length;
int num_of_e = 0;
int c; //using this as a char
//until eof
while (feof(randStrings) == 0) {
c = fgetc(randStrings);
//calculate length of file
file_length++;
//count e chars
if (c == 'e') {
num_of_e++;
}
}
//close file
fclose(randStrings);
//send bundle to child
int a[2];
a[0] = num_of_e;
a[1] = file_length;
printf("num of e = %i\n", a[0]);
printf("len = %i\n", a[1]);
//set up parent pipe
int pipefd[2];
if (pipe(pipefd) == -1) {
perror("pipe");
printf("x\n");
}
printf("parent pipe open\n");
close(pipefd[0]); //close the read end
write(pipefd[1], &a[0], sizeof(int));
write(pipefd[1], &a[1], sizeof(int));
close(pipefd[1]);
printf("parent pipe closed\n");
//wait for child to finish running
wait(NULL);
printf("parent out\n");
//terminate
}
}
and consumer.c
#include <stdio.h> /* printf, stderr, fprintf */
#include <sys/types.h> /* pid_t */
#include <unistd.h> /* _exit, fork, execl */
#include <stdlib.h> /* exit */
#include <errno.h> /* errno */
#define SLEEP_TIME 5
int main (int argc, char *argv[]){
sleep(SLEEP_TIME);
printf("Child program launched\n");
//receive bundle
int pipefd[2];
int buf[2];
if (pipe(pipefd) == -1) {
perror("pipe");
printf("child x\n");
}
close(pipefd[1]); //child closes write end
buf[0] = 0;
buf[1] = 0;
/*int i = 0; // i dont like this
while (read(pipefd[0], &buf[i], sizeof(int)) > 0) {
i++;
}*/
printf("child reading pipe\n");
read(pipefd[0], &buf[0], sizeof(int));
read(pipefd[0], &buf[1], sizeof(int));
close(pipefd[0]);
//buf should have the stuff in it
int num_of_e = buf[0];
int file_length = buf[1];
printf("child num of e = %i\n", num_of_e);
printf("child len = %i\n", file_length);
//open file
FILE * resultStrings;
resultStrings = fopen("resultStrings.txt", "w");
for (int i = 0; i < num_of_e; i++) {
//write num_of_e e chars
fputc('e', resultStrings);
}
//or if no e chars, write - chars
if (num_of_e == 0) {
for (int i = 0; i < file_length; i++) {
//write file_length '-' chars
fputc('-', resultStrings);
}
}
//close file
fclose(resultStrings);
printf("child out\n");
}
if you're still here after all that, you deserve a thank you just due to the length of this.
You're doing it wrong. The whole mechanism works because a child process inherits the parent's open file descriptors.
It should go like this:
Open the pipe with pipe(pipefd)
fork()
Parent (producer):
closes the read side (pipefd[0])
writes to the write side (pipefd[1])
Child (consumer):
closes the write side (pipefd[1])
reads from the read side (pipefd[0]) or calls exec
You are opening distinct pipes in both the parent and child process (after you've forked.) It needs to happen before you fork.
Now since you're execing, the new process needs to be aware of read-only pipe. There are a couple ways you could do this:
Pass it the file descriptor number (pipefd[0]) on the command line
dup2(1, fd) it to be the stdin of the newly exec'd process

How to pipe stdin to a child and execl cat in C

In the code below, I am simply trying to send a file via stdin to a child process which will exec the cat OS command. The code compiles fine. Here is how I call it from the command line:
$ ./uniquify < words.txt
However, when I run it I get a seg fault error. I am really having a hard time understanding how the flow if information is supposed to work through pipes to children. I am trying to make the code as simple as possible, so I can understand it, but it is not yet making sense. Any help would be appreciated.
#include <unistd.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#define NUM_CHILDREN 2
int main(int argc, char *argv[])
{
pid_t catPid;
int writeFds[NUM_CHILDREN];
int catFds[2];
int c = 0;
FILE *writeToChildren[NUM_CHILDREN];
//create a pipe
(void) pipe(catFds);
if ((catPid = fork()) < 0) {
perror("cat fork failed");
exit(1);
}
//this is the child case
if (catPid == 0) {
//close the write end of the pipe
close(catFds[1]);
//close stdin?
close(0);
//duplicate the read side of the pipe
dup(catFds[0]);
//exec cat
execl("/bin/cat", "cat", (char *) 0);
perror("***** exec of cat failed");
exit(20);
}
else { //this is the parent case
//close the read end of the pipe
close(catFds[0]);
int p[2];
//create a pipe
pipe(p);
writeToChildren[c] = fdopen(p[1], "w");
} //only the the parent continues from here
//close file descriptor so the cat child can exit
close(catFds[1]);
char words[NUM_CHILDREN][50];
//read through the input file two words at a time
while (fscanf(stdin, "%s %s", words[0], words[1]) != EOF) {
//loop twice passing one of the words to each rev child
for (c = 0; c < NUM_CHILDREN; c++) {
fprintf(writeToChildren[c], "%s\n", words[c]);
}
}
//close all FILEs and fds by sending and EOF
for (c = 0; c < NUM_CHILDREN; c++) {
fclose(writeToChildren[c]);
close(writeFds[c]);
}
int status = 0;
//wait on all children
for (c = 0; c < (NUM_CHILDREN + 1); c++) {
wait(&status);
}
return 0;
}
Since your question seems to be about understanding how pipes and forks work, I hope below programs can help you. Please notice that this is for illustration only. It wouldn't qualify for commercial implementation, but I wanted to keep it short!
You can compile the two programs as follows:
cc pipechild.c -o pipechild
cc pipeparent.c -o pipeparent
Then execute with ./pipeparent
pipeparent.c source
/* pipeparent.c */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#define MESSAGE "HELLO!\n"
#define INBUFSIZE 80
#define RD 0 // Read end of pipe
#define WR 1 // Write end of pipe
int main(void)
{
int ptocpipe[2]; // Parent-to-child pipe
int ctoppipe[2]; // Chile-to-parent pipe
pid_t childpid; // Process ID of child
char inbuf[80]; // Input from child
int rd; // read() return
int rdup; // dup():ed stdin for child
int wdup; // dup():ed stdout for child
char *eol; // End of line
// Create pipe for writing to child
if (pipe(ptocpipe) < 0) {
fprintf(stderr, "pipe(ptocpipe) failed!\n");
return 2;
}
// Create pipe for writing back to parent
if (pipe(ctoppipe) < 0) {
fprintf(stderr, "pipe(ctoppipe) failed!\n");
return 2;
}
// Verify that one of the pipes are working by filling it first
// in one end and then reading it from the other. The OS will
// buffer the contents for us. Note, this is not at all necessary,
// it's just to illustrate how it works!
write(ptocpipe[WR], MESSAGE, strlen(MESSAGE));
read(ptocpipe[RD], inbuf, INBUFSIZE);
if (strlen(inbuf) != strlen(MESSAGE)) {
fprintf(stderr, "Failed to flush the toilet!\n");
return 6;
} else {
printf("Wrote to myself: %s", inbuf);
}
// Next, we want to launch some interactive program which
// replies with exactly one line to each line we send to it,
// until it gets tired and returns EOF to us.
// First, we must clone ourselves by using fork(). Then the
// child process must be replaced by the interactive program.
// Problem is: How do we cheat the program to read its stdin
// from us, and send its stdout back to us?
switch (childpid = fork()) {
case -1: // Error
fprintf(stderr, "Parent: fork() failed!\n");
return 3;
case 0: // Child process
// Close the ends we don't need. If not, we might
// write back to ourselves!
close(ptocpipe[WR]);
close(ctoppipe[RD]);
// Close stdin
close(0);
// Create a "new stdin", which WILL be 0 (zero)
if ((rdup = dup(ptocpipe[RD])) < 0) {
fprintf(stderr, "Failed dup(stdin)\n");
return 4;
}
// Close stdout
close(1);
// Create a "new stdout", which WILL be 1 (one)
if ((wdup = dup(ctoppipe[WR])) < 0) {
fprintf(stderr, "Failed dup(stdout)\n");
return 5;
}
// For debugging, verify stdin and stdout
fprintf(stderr, "rdup: %d, wdup %d\n", rdup, wdup);
// Overload current process by the interactive
// child process which we want to execute.
execlp("./pipechild", "pipechild", (char *) NULL);
// Getting here means we failed to launch the child
fprintf(stderr, "Parent: execl() failed!\n");
return 4;
}
// This code is executed by the parent only!
// Close the ends we don't need, to avoid writing back to ourself
close(ptocpipe[RD]);
close(ctoppipe[WR]);
// Write one line to the child and expect a reply, or EOF.
do {
write(ptocpipe[WR], MESSAGE, strlen(MESSAGE));
if ((rd = read(ctoppipe[RD], inbuf, INBUFSIZE)) > 0) {
// Chop off ending EOL
if ((eol = rindex(inbuf, '\n')) != NULL)
*eol = '\0';
printf("Parent: Read \"%s\" from child.\n", inbuf);
}
} while (rd > 0);
fprintf(stderr, "Parent: Child done!\n");
return 0;
}
pipechild.c source
/* pipechild.c
* Note - This is only for illustration purpose!
* To be stable, we should catch/ignore signals,
* and use select() to read.
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <strings.h>
#include <string.h>
#define MAXCOUNT 5 // Maximum input lines toread
#define INBUFSIZE 80 // Buffer size
int main(void)
{
char buff[INBUFSIZE];
int remains = MAXCOUNT;
pid_t mypid;
char *eol;
mypid = getpid(); // Process-ID
fprintf(stderr, "Child %d: Started!\n", mypid);
// For each line read, write one tostdout.
while (fgets(buff, INBUFSIZE, stdin) && remains--) {
// Chop off ending EOL
if ((eol = rindex(buff, '\n')) != NULL)
*eol = '\0';
// Debug to console
fprintf(stderr, "Child %d: I got %s. %d remains.\n",
mypid, buff, 1 + remains);
// Reply to parent
sprintf(buff, "Child %d: %d remains\n", mypid, 1 + remains);
write(1, buff, strlen(buff));
}
fprintf(stderr, "Child %d: I'm done!\n", mypid);
return 0;
}

Resources