Running concurrent processes using pipe in C - c

I'm working on an assignment in C aimed at using a pipe to pass variables between two processes. Both processes must be forked from the parent, and they must run concurrently to pass one character at a time (sort of demonstrated below).
The issue I'm having is that the fork()ed processes are not running concurrently. The sender seems to go first, and after running for ~26 seconds the receiver begins. Here is the code I have written:
#include <stdio.h>
int ret;
int pipearray[2];
char buffer[26];
void mysender();
void myreceiver();
int main()
{
int pid = 0;
int i = 0;
ret = pipe(pipearray);
while (i < 2) {
pid = fork();
if ( pid == 0 && i == 0 ) /* child process execution (receiver) */
{
myreceiver();
printf("Your receiver is done\n");
exit(0);
}
else if ( pid == 0 && i == 1 ) /* now executes sender */
{
mysender();
printf("Your sender is done\n");
exit(0);
}
++i;
}
close(pipearray[0]);
close(pipearray[1]);
sleep(30);
printf("Parent function has finished.\n");
return 0;
}
void mysender()
{
char c;
int index = 90;
close(pipearray[0]);
while (index > 64) /* loop for all values of A-Z in ASCII */
{
c = (char) index;
open(pipearray[1]);
write(pipearray[1], c, sizeof(c)); /* Sends letter to pipe */
--index;
sleep(1);
}
close(pipearray[1]);
}
void myreceiver()
{
int index = 0;
close(pipearray[1]);
while(buffer != 'A') /*loop runs until 'A' is handled */
{
sleep(1);
open(pipearray[0]);
read(pipearray[0], buffer, 1);
printf("%s", &buffer);
index++;
if ( index == 26 ) { break; }
}
close(pipearray[0]);
}
Expected Result:
ZYXWVUTSRQPONMLKJIHGFEDCBA
Your sender is done
Your receiver is done
The parent function has finished.
My result:
Your sender is done
The parent function has finished.
Your receiver is done
I'm very new to C programming but I've been banging away at this for a while. Any tips to why these might not be running simultaneously would be very appreciated.

There is many errors in your code. Don't try to open the pipe after fork, it is already open and has no name. Write should use the address of c. Read must read into the right place. A flush must be done after write to stdout. Your conditional must be slightly modified to be guaranteed correct. The parent process must wait its children. Here is the modified code :
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
int ret;
int pipearray[2];
char buffer[26];
void mysender();
void myreceiver();
int main()
{
int pid = 0;
int i = 0;
ret = pipe(pipearray);
while (i < 2) {
pid = fork();
if ( pid == 0 && i == 0 ) /* child process execution (receiver) */
{
myreceiver();
printf("Your receiver is done\n");
exit(0);
}
else if ( pid == 0 && i == 1 ) /* now executes sender */
{
mysender();
printf("Your sender is done\n");
exit(0);
}
++i;
}
close(pipearray[0]);
close(pipearray[1]);
// Don't sleep, but wait until the end of the two children
wait(NULL);
wait(NULL);
// sleep(30);
printf("Parent function has finished.\n");
return 0;
}
void mysender()
{
char c;
int index = 90;
close(pipearray[0]);
while (index > 64) /* loop for all values of A-Z in ASCII */
{
c = (char) index;
// try to open a anonymous pipe is a non-sense
// open(pipearray[1]);
// Send a buffer by its address
write(pipearray[1], &c, sizeof(c)); /* Sends letter to pipe */
--index;
sleep(1);
}
close(pipearray[1]);
}
void myreceiver()
{
int index = 0;
close(pipearray[1]);
// Ensure condition is entered first
buffer[index] = 0;
// This is not the best condition ever, but ok.
while(buffer[index] != 'A') /*loop runs until 'A' is handled */
{
sleep(1);
// Don't open an anonymous pipe
// open(pipearray[0]);
// Read at the right position
read(pipearray[0], buffer+index, 1);
// print and flush, could also be printf("%s"...); flush(stdout);
printf("%s\n", buffer);
index++;
if ( index == 26 ) { break; }
}
close(pipearray[0]);
}
Now, consider to remove the sleeps in the reader, as it will be synchronized with the writes such that no read is possible if no write has been done. Alos consider to read more that one byte, because there is no concept of message, so that you can read as much byte you consider necessary to read, and as usual it is better to try reading a bunch of bytes when you can.

Related

Implementing a semaphore through a file

thank you for taking your time to read this.
I'm trying to implement a semaphore through a file using C on a linux machine.
I have two process that I must synchronize, one has all the consonants of a file stored in an array, the other has all the vowels.
I've arranged these arrays so that if I alternate between them, I can reconstruct the original file and paste it in another file.
The issue now is making these two process alternate.
This exercise in particular wants me to implement a semaphore using a 3rd file.
What I've done is use the first byte of this file as a semaphore, lot loop one process until the other is finished.
I've tested with long sleep()s and yes, if these processes do alternate, the output file is exactly how I want it, but with that said, my current implementation of a semaphore seems not to be working.
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <fcntl.h>
int main (){
int child1=0, child2=0, fd, fd2, fs, i=0, i2=0;
int count=0, count2=0;
char buf1[20], buf2[20], a, b, con='1', vow='0', check1, check2;
fd=open("text", O_RDONLY);
fd2=open("text2", O_CREAT|O_RDWR|O_TRUNC,0777);
fs=open("semaphore", O_RDWR, 0777);
if (fork()==0)
child1=1;
else {
if (fork()==0)
child2=1;
}
//access vowel child
if (child1){
printf("I'm the first child\n");
while ((read(fd,&a,1))==1){
if (a=='a' || a=='e' || a=='i' || a=='o' || a=='u')
buf1[count]=a;
count++;
}
printf("count: %d\n", count);
//vowels are now into buf1
//wait for the brother to do the same
sleep(2);
for (i; i<=count+1; i++){
if (buf1[i]!='\0'){
printf("%c\n", buf1[i]);
a=buf1[i];
write(fd2,&a,1);
}
lseek(fs,0,SEEK_SET);
write(fs,&con,1);//tell the semaphore it's the consonant's turn (1)
while(check1=='1'){
lseek(fs,0,SEEK_SET);
read(fs,&a,1);
check1=a;
sleep(1);
} //get stuck until it's somebody's else turn
}
}
//access consonant child
else if (child2){
sleep(1);
lseek(fd,0,SEEK_SET);
printf ("I'm the second child\n");
while ((read(fd,&a,1))==1){
if (a=='a' || a=='e' || a=='i' || a=='o' || a=='u')
;
else
buf2[count2]=a;
count2++;
}
//resync
sleep(1);
printf("count: %d\n", count2);
//consonants are now into buf1
for (i; i<=count2+1; i++){
lseek(fs,0,SEEK_SET);
if (buf2[i]!='\0'){
printf("%c\n", buf2[i]);
b=buf2[i];
write(fd2,&b,1);
} //wait for vowel
while(check2=='0'){
lseek(fs,0,SEEK_SET);
read(fs,&b,1);
check2=b;
sleep(1);
}
lseek(fs,0,SEEK_SET);
write(fs,&vow,1);//tell the semaphore it's the vowel's turn (0)
}
}
else
printf("I'm the father\n");
sleep(10);
exit(0);
}
The file "text" has "hello world" stored in it. What happens when I execute this code is that what's copy and pasted is "hll wrld eoo". What exactly am I doing wrong with my semaphore?
Basically, you want to implement acquire/release similar to pthread_mutex_t or sem_t.
You need two functions:
acquire to wait for and acquire ownership
release to release ownership and grant it to the "other" process
You're conflating the two operations into one. And, your code doesn't actually do locking properly.
Side note: You are creating "zombie" processes because the father does not do wait on the children.
Here's some refactored code.
I added acquire and release calls to the places where you had your sempahore code (e.g. the second loops for each child). But, you might need them in the first loops as well (which I did not do).
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <fcntl.h>
void
acquire(int fs,char iam)
{
char own;
while (1) {
lseek(fs,0,SEEK_SET);
read(fs,&own,1);
if (own == iam)
break;
usleep(1);
}
}
void
release(int fs,char iam)
{
char other;
if (iam == 1)
other = 2;
else
other = 1;
lseek(fs,0,SEEK_SET);
write(fs,&other,1);
}
int
main()
{
int child1 = 0,
child2 = 0,
fd,
fd2,
fs,
i = 0,
i2 = 0;
int count = 0,
count2 = 0;
char buf1[20],
buf2[20],
a,
b,
con = '1',
vow = '0',
check1,
check2;
fd = open("text", O_RDONLY);
fd2 = open("text2", O_CREAT | O_RDWR | O_TRUNC, 0777);
fs = open("semaphore", O_RDWR | O_CREAT, 0777);
// initialize semaphore to grant access to one or the other
buf1[0] = 1;
write(fs,buf1,1);
if (fork() == 0)
child1 = 1;
else {
if (fork() == 0)
child2 = 1;
}
// access vowel child
if (child1) {
printf("I'm the first child\n");
while ((read(fd, &a, 1)) == 1) {
if (a == 'a' || a == 'e' || a == 'i' || a == 'o' || a == 'u')
buf1[count] = a;
count++;
}
printf("count: %d\n", count);
// vowels are now into buf1
// wait for the brother to do the same
sleep(2);
for (i; i <= count + 1; i++) {
acquire(fs,1);
if (buf1[i] != '\0') {
printf("%c\n", buf1[i]);
a = buf1[i];
write(fd2, &a, 1);
}
release(fs,1);
}
}
// access consonant child
else if (child2) {
sleep(1);
lseek(fd, 0, SEEK_SET);
printf("I'm the second child\n");
while ((read(fd, &a, 1)) == 1) {
if (a == 'a' || a == 'e' || a == 'i' || a == 'o' || a == 'u');
else
buf2[count2] = a;
count2++;
}
// resync
sleep(1);
printf("count: %d\n", count2);
// consonants are now into buf1
for (i; i <= count2 + 1; i++) {
acquire(fs,2);
if (buf2[i] != '\0') {
printf("%c\n", buf2[i]);
b = buf2[i];
write(fd2, &b, 1);
} // wait for vowel
release(fs,2);
}
}
else {
printf("I'm the father\n");
while (wait(NULL) >= 0);
printf("all done\n");
}
//sleep(10);
exit(0);
}
UPDATE:
The two child processes share a common file position for the semaphore file because the open call is done in the parent.
So, there is a potential race condition between the lseek and the read or write.
There are two ways to solve this:
Have each child open the semaphore file after the fork. Then, they do not share the file position.
Instead of lseek followed by read or write, use pread or pwrite.
Here is the latter:
void
acquire(int fs,char iam)
{
char own;
while (1) {
pread(fs,&own,1,0);
if (own == iam)
break;
usleep(1);
}
}
void
release(int fs,char iam)
{
char other;
if (iam == 1)
other = 2;
else
other = 1;
pwrite(fs,&other,1,0);
}

The last thread Don't Run the task like other Threads in C

So in main function I have defined variable for creating variable.
Same thing for process.
Then I fork() the parent process and then inside the child process I have created 4 Threads and above the Main function ı have created the functions Where the Threads will run.
My question is The first 3 Thread works exactly as I want But the Fourth process not working as the First 3 Thread.
Here is my code that Child Process Execute.
I used Ordinary pipe to Write message from Parent Process and Read the Message from Child Process.
readBUFFER stands for the data I read from the Pipe and about 150 character length.
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <signal.h>
#ifndef NUM_THREADS
#define NUM_THREADS 4
#endif
[const char readMessage\[152\];][1]
void *encrypt(char *data)
{
int shift = 4;
int i = 0;
for(i = 0; data[i] != '\0' ; ++i) {
char sm = data[i];
if(sm> 'a' && sm<='z'){
sm = sm + shift;
if(sm>'z'){
sm = sm - 'z' + 'a' - 1;
}
data[i] = sm;
}
else if(sm >= 'A' && sm<='Z'){
sm = sm + shift;
if(sm > 'Z'){
sm = sm - 'Z' + 'A'-1;
}
data[i] = sm;
}
}
return data;
}
void *threadFunc(void *message) {
printf("encrypted data : %s\n", encrypt(message));
return encrypt(message);
}
int main(void)
{
pthread_t threads[NUM_THREADS];
int pipefds[2];
//pipefds[0] - READ
//pipefds[1] - WRİTE
int returnstatus;
char readBUFFER[152];
int pid;
char writeMessages[152]={"150 characters is between 20 words and 40 words with spaces included in the character count. If spaces are not included in the character count,then 150 "};
returnstatus = pipe(pipefds);
if(returnstatus == -1)
{
printf("Pipe failed!\n");
return 1;
}
pid = fork(); //Child Process Oluşturma
if(pid == -1)
{
printf("Fork failed!\n");
return 2;
}
//Child Process
if(pid == 0)
{
close(pipefds[1]);
read(pipefds[0], readBUFFER , sizeof(readBUFFER));
printf("Child process Reads from pipe --> :%s\n",readBUFFER);
int buffer_size = strlen(readBUFFER);
printf("buffer size : %d\n",buffer_size);
char str[50];
for(int i = 1; i<=NUM_THREADS ;i++)
{
for(int j = ((buffer_size/4) * (i - 1)) ; j<=((buffer_size/4) * i) ; j++)
{
strncat(str , &readBUFFER[j] , 1);
}
printf("%d. Part --> %s\n", i,str);
pthread_create(&threads[i] , NULL , threadFunc , str);
sleep(2);
int pos = 0;
while(str[pos] != '\0')
{
str[pos] = '\0';
}
//pthread_join(threads[i] , NULL);
}
sleep(2);
}
//Parent process
else
{
close(pipefds[0]);
printf("parent process write to the pipe -->\n");
write(pipefds[1], writeMessages , sizeof(writeMessages));
wait(NULL);
printf("Child process Executed!");
}
return 0;
}
Basically encrypt function do the Caesar Encryption.
My question is when I run the First Thread they return the encrypted message but when it comes to the Fourth Thread it returns nothing.
I couldn't solve this problem maybe it's because ı haven't yet had mastered threads.
Let me know if you need more information.
and this is the output I took. Look at the 4 part. It represent the Fourth Thread.

Pipe's related arguments to pass to a function?

I'm a beginner in C programming and I started learning about pipes today.
I need them because my program has to run up to 4 processes at the time, so to avoid creating more processes than those required, I have to use a shared variable between all of them to keep track how may can still be created.
I tried to simplify my program:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
void forking(int p, int pid);
int main(int argc, char *argv[])
{
int fd[2];
int p = 4; // Max number of processes that can run at the same time
int pid;
if(pipe(fd) == -1)
{
perror("pipe: ");
return 0;
}
//It will try the function forking 10 times to execute SOME CODE that
// changes everytime something operates on it
for(int i = 0; i < 10; i++)
{
forking(p, pid);
}
return 0;
}
void forking(int p, int pid)
{
if (p > 0) //We can create another process
{
p -= 1; // update the p before creating a child process
write(fd[1], &p, (sizeof(int)*3)); //Tell everyone about the update
pid = fork();
if (pid == 0)
{
//The child process turn to elaborate SOME CODE
// SOME CODE
// Then there will be a point where
// we will need to check if the p has been modified!
read(fd[0], &p, sizeof(int)*3);
//So that forking can decide whether we can create another process
// to operate on SOME OTHER CODE
forking(p, pid);
//Once we are done, we can terminate the child
//but first we'll need to update the process n° p
p += 1;
write(fd[1], &p, (sizeof(int)*3));
exit(0);
}
else if(pid > 1) //Father time
{
// check the updated value
//the father will do nothing
// since a process it's already on it (on the SOME CODE part)
return;
}
}
else
{
//else the father does SOME CODE itself
// SOME CODE
}
return;
}
My 2 doubts is whether I should pass something else to the function "forking" (which can be recursive), like "fd", or if it is okay to just leave the code like this, and whether this will have the desired result.
Hopefully I made myself clear enough.
EDIT 1:
void forking(int p, int pid, int *fd)
{
if (p > 0) //We can create another process
{
p -= 1; // update the p before creating a child process
write(fd[1], &p, (sizeof(int)*3)); //Tell everyone about the update
pid = fork();
if (pid == 0)
{
//The child process turn to elaborate SOME CODE
// SOME CODE
// Then there will be a point where
// we will need to check if the p has been modified!
read(fd[0], &p, sizeof(int)*3);
//So that forking can decide whether we can create another process
// to operate on SOME OTHER CODE
forking(p, pid, fd);
//Once we are done, we can terminate the child
//but first we'll need to update the process n° p
p += 1;
write(fd[1], &p, (sizeof(int)*3));
exit(0);
}
else if(pid > 1) //Father time
{
// check the updated value
//the father will do nothing
// since a process it's already on it (on the SOME CODE part)
return;
}
}
else
{
//else the father does SOME CODE itself
// SOME CODE
}
return;
}
Passing fd resulted as a success, now I'm wondering whether I should add pipe(fd) at the start of the forking program like so . . .
void forking(int p, int pid, int *fd)
{
if(pipe(fd) == -1)
{
perror("pipe: ");
return;
}
//Rest of the code
}

Implementing a pipe functionality in shell. In C

In the provided code below, the redirection methods work, however i've been having trouble with getting the pipe function to work.
This is how I'm viewing the flow of the code (at least for the pipe) and correct me if I'm wrong: When the array containing the users command (argv[]) is being traversed and checked for pipe declarations ("|"), for however many ("|") it detects, it will do that number of processes (processes++). It will then clear out the user command for future use, and go through the checks to continue the program.
I will also say I only have processes declared and iterated once, and I wasn't sure if I return it or implement it somewhere else.
Here is what I have so far:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <fcntl.h>
int main()
{
char *path, //shell system path indicator
*argv[20], //user command
buf[80], //will pass user command to argv
n, //user input
*p; //points to buffer
int m, // represents size
status, // checks if ant conditions violate the while loop
inword,
check,
continu;
while(1) //an infinite loop to keep the shell running
{
inword = 0; //set as false
p = buf; // p represents buffer
m = 0; //size of argv array
continu = 0; //set as false
printf("\nshhh> "); //display of shell prompt
while ((n = getchar()) != '\n' || continu) //while user input is not end of line OR
{
if (n == ' ') // if input is a space
{
if (inword) // if 0
{
inword = 0; // remains 0
*p++ = 0; //increment to next position in buffer[80]
}
}
else if (n == '\n') //if user input == end of line
continu = 0; // continue is 0 which breaks the while loop
else if (n == '\\' && !inword) // if user input == '\\' and inword is not 0
continu = 1; // continu is changed to 1 and you stay in while loop
else
{
if (!inword)
{
inword = 1;
argv[m++] = p; //next iteration of user command contains buffer
*p++ = n; //next slot in buffer contains user input
}
else
*p++ = n; //next slot in buffer contains user input
}
}
*p++ = 0; // next slot in buffer is 0
argv[m] = 0; //clear user command
if (strcmp(argv[0],"exit") == 0) //if user command is exit
exit (0); //end program
int processes = 0,
gate1,
gate2,
pipe_end[2],
pipe_id;
if (fork() == 0)// passing command to array
{
for(int iteratr = 0; argv[iteratr] != 0; iteratr++)
{
if(strcmp("|", argv[iteratr]) == 0)// pipe configuration
{
processes++;
argv[iteratr] = 0; //empty array for next user command
pipe(pipe_end);
if (pipe(pipe_end)==-1) // Check for pipe functionality
{
perror("Pipe Failed");
return 1;
}
pipe_id = fork();
if (pipe_id < 0) //Check for fork functionality
{
printf("Fork failed");
return 1;
}
else if(pipe_id == 0)//Child
{
close(1); //close stdout
dup(pipe_end[1]); //copy stdout
close(pipe_end[0]); //close read in
close(pipe_end[1]); //close write out
execvp(argv[0], argv); //execute "pre" read in
}
else //Parent
{
close(0); //close stdin
dup(pipe_end[0]); //copy stdin
close(pipe_end[0]); //close read in
close(pipe_end[1]); //close write out
execvp(argv[0], argv); //execute "sort" read in
}
}
if(strcmp("<", argv[iteratr]) == 0)// Input redirect
{
gate1 = open("input.txt", O_RDONLY);
close(0);
dup(gate1);
close(gate1);
argv[iteratr] = 0;
execvp(argv[0], argv);
}
if(strcmp(">", argv[iteratr]) == 0) // Output redirect
{
gate2 = creat("output.txt", 0750);
close(1);
dup(gate2);
close(gate2);
argv[iteratr] = 0;
execvp(argv[0], argv);
}
}
execvp(argv[0], argv);
printf(" didn't exec \n ");
check = wait(&status);
}
wait(&status); //check
}
return 0;
}

n-pipeline producing EOF on end

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <string.h>
void tokenizer(char* input, char** output) { //My tokenizer
char* input_dup = strdup(input);
output[0] = strtok(input_dup, " ");
int i = 1;
while ((output[i] = strtok(NULL, " ")) != NULL) {
i++;
}
}
void run_command(char** args, int* fd) { //no pipe
pid_t pid = fork();
if (pid < 0) {
printf("Forking failed...\n");
}
else if (pid == 0) {
close(fd[0]);
if (fd[1] != 1)
dup2(fd[1], 1);
execvp(args[0], args);
printf("Command failed...\n");
exit(1);
}
else {
close(fd[1]);
wait(pid);
char buff[1];
while (read(fd[0], buff, 1) > 0) {
if (buff[0] == EOF || buff[0] == '\0') {
printf("Caught something, returning out...");
return;
}
else {
printf("%c", buff[0]);
}
}
}
}
//pipeline function
void run_pipe(char** args, int* fd) {
pid_t pid = fork();
if (pid < 0) {
printf("Forking failed...\n");
}
else if (pid == 0) {
if (fd[1] != 1) {
dup2(fd[1], 1);
}
execvp(args[0], args);
printf("Command failed...\n");
exit(1);
}
else {
close(fd[1]);
if (fd[0] != 0) {
dup2(fd[0], 0);
}
wait(pid);
}
}
int main(int argc, char** argv) {
printf ("Starting myshell (mysh) \n..\n..\n");
while (1) {
char cwd[1024];
printf ("mysh :: %s -> ", getcwd(cwd, sizeof(cwd)));
char ch[1024];
memset(ch, 0, 1023); //for cleanup
char c = 0;
int i = 0;
while (c != '\n') {
c = getchar();
if (c == EOF) {
printf ("EOF Received, exiting...\n");
return 0;
}
if (c != '\n')
ch[i] = c;
i++;
}
if (ch[0] != '\0') {
char* tokens[128];
tokenizer(ch, tokens);
//first check for keywords
if (strcmp(tokens[0], "cd") == 0) {
if (chdir(tokens[1]) < 0) {
printf("ERROR: Directory %s does not exist\n", tokens[1]);
}
}
else if (strcmp(tokens[0], "exit") == 0) {
printf("Leaving shell...\n");
return 0;
}
else {
char* commands[50];
memset(commands, 0, sizeof(commands));
int j = 0;
int k = 0;
int fd[2];
//try something different...
while (tokens[j] != NULL) {
if (strcmp(tokens[j], "|") == 0) {
commands[k] = NULL;
pipe(fd);
run_pipe(commands, fd);
j++;
k = 0;
}
//more cases here
else { //nothing special
commands[k] = tokens[j];
j++;
k++;
}
}
commands[k] = NULL;
pipe(fd);
run_command(commands, fd);
}
}
}
}
The above code is meant to simulate a shell. It handles single commands and it handles the pipelining properly (i.e. ps | sort | wc is returning the correct output) however when the pipelining is done it returns an EOF which is caught by the condition in the loop with getchar(). If I try to ignore this EOF it segfaults. Am I leaving a pipe open somewhere and stdin is getting flooded? Any help is appreciated.
Compilation fixes
You need to add #include <sys/wait.h> and then fix the calls to wait(). I used (twice):
int status;
int corpse = wait(&status);
printf("PID %d status 0x%.4X\n", corpse, status);
Arguably, that should be a loop looking for a specific PID, or you should use waitpid() instead. While debugging a shell, you want to know about every PID that exits and its status.
I ran `ps | wc' and got:
Starting myshell (mysh)
..
..
mysh :: /usr/local/google/home/jleffler/soq -> ps | wc
PID 25960 status 0x0000
PID 25961 status 0x0000
4 16 117
mysh :: /usr/local/google/home/jleffler/soq -> EOF Received, exiting...
If you mean "the code should have continued instead of getting EOF", then there's some more work to do.
Tangential issues
I note the line:
if (buff[0] == EOF || buff[0] == '\0')
The character in buff[0] is from a read() call. It will never be EOF meaningfully; EOF is distinct from every character (hence getchar() returns an int). This becomes significant later:
char c = 0;
while (c != '\n')
{
c = getchar();
if (c == EOF)
Since c is a char, you cannot reliably compare it with EOF. You must store the result of getchar() in an int.
I'm not yet convinced these are the cause of the trouble, but you must be careful.
Probable cause
I think the trouble is in run_pipe() in the parent code (as amended):
else
{
close(fd[1]);
if (fd[0] != 0)
{
dup2(fd[0], 0);
}
int status;
int corpse = wait(&status);
printf("PID %d status 0x%.4X\n", corpse, status);
}
The fd[0] != 0 condition will always be true (very improbable that it will be false), so you then change your shell's input to read from fd[0]. You should review that; it means that you're reading standard input from the read end of the pipe to the child. That's bad; you've lost your original input!
Your code also seems to have the parent waiting for the child to die, and then reads the pipe and echoes to standard output. This is not a good idea; it is better to make the child (last child in the pipeline) write to the standard output directly. There are two reasons for this:
The child might write more data than fits in a pipe, so it will block waiting for something to read its output, but the reader will be blocked waiting for the child to die, so you'll have a deadlock.
It slows things up, and output from the child may well be buffered instead of appearing timely on the terminal.
I'm a little sceptical about how a three-part pipeline would be handled. You need two pipes created before you run the middle process of the three; I don't see that in your code.
I was able to fix this issue. It's probably not the right way to do it, but I saved a copy of stdin and used dup2 to reset it when the pipelining was finished.
int in_bak = dup(0);
//stuff
dup2(in_bak, 0);
close(in_bak);

Resources