How to implement a multi-pipe in C? - c

I have to write a small shell - called shhh - that has the
following capabilities:
Can execute a command with the accompanying arguments.
Recognize multiple pipe requests and handle them.
Example command:
shhh> ls | more | wc
I need help specifically with the pipes. I've googled and researched a good few days worth of how to implement this in c, to no avail. Generally speaking, I have a fairly good idea of how a pipe works, but when it comes to implement this multi-pipe part, I haven't the slightest clue.
I've tried researching how to do this and have tried implementing it in my code in all sort of ways to no avail. Either it doesn't work, or I don't understand it and can't get it to work. I've also tried Chegg, but their experts are useless, since they keep asking for more code and the only thing I've left out of the snippet below is my own code that I've written that has nothing to do with the problem I'm currently having.
This is all the code that is pertinent to the problem.
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<signal.h>
#include<fcntl.h>
main()
{
char *path, *argv[20], buf[80], n, *p;
int m, status, inword, continu;
int fd[2];
while(1) {
inword = 0;
p = buf;
m = 0;
continu=0;
printf( "\nshhh> ");
while ( ( n = getchar() ) != '\n' || continu ) {
if ( n == ' ' ) {
if ( inword ) {
inword = 0;
*p++ = 0;
}
}
else if ( n == '\n' )
continu = 0;
else if ( n == '\\' && !inword )
continu = 1;
else {
if ( !inword ) {
inword = 1;
argv[m++] = p;
*p++ = n;
}
else
*p++ = n;
}
}
*p++ = 0;
argv[m] = 0;
if ( strcmp(argv[0],"exit") == 0 )
exit (0);
here's the snippet of code I currently have:
if(fork() == 0) {
for(y = 0; arg[v] != 0; y++) {
if(strcmp("|", argv[y]) == 0) {
argv[y] = 0;
pipe(fd);
if(fork() == 0) {
close(fd[0]);
dup(fd[0]);
close(fd[1]);
execvp(argv[0], argv);
}
else {
close(fd[1]);
dup(fd[1]);
close(fd[0]);
}
}
execvp(argv[0], argv);
printf(" didn't exec \n");
n2 = wait(&status);
}
wait(&status);
}
}
Any and all help is extremely very much appreciated. Also, I must use execvp() and I must use the array of arguments (argv[]).

Related

Using pipes to communicate between two programs

I need the main prog to get two strings from the user and an argument for the other program, call fork() and then in child process I need to write the strings into pipe and send them to the other program which returns an int which I want to pass to parent so I'm trying to use another pipe for it but every time it stops right after inserting the strings.
So the main program: (EDITED)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#include <sys/wait.h>
#define LINELEN (80)
char *mygets(char *buf, int len);
int mygeti();
int main(int argc, char *argv[])
{
char *cmpstr[] = {"lexcmp", "lencmp"};
int veclen = sizeof(cmpstr)/sizeof(char *);
char str1[LINELEN + 1];
char str2[LINELEN + 1];
int index;
int pid[2];
int pfd[4][2];
for(int i = 0; i < 4; i++)
{
if(pipe(pfd[i]) < 0)
{
perror("pipe");
return -2;
}
}
pid[0] = fork();
if(pid[0] == 0) // child a
{
close(pfd[0][1]);
close(pfd[2][0]);
dup2(pfd[0][0], STDIN_FILENO);
dup2(pfd[2][1], STDOUT_FILENO);
char *myargs[3];
myargs[0] = "./loopcmp";
myargs[1] = "lexcmp";
myargs[2] = NULL;
if(execvp(myargs[0], myargs) == -1)
{
perror("exec");
return -2;
}
close(pfd[0][0]);
close(pfd[2][1]);
}
else
{
pid[1] = fork();
if(pid[1] == 0) //child b
{
close(pfd[1][1]);
close(pfd[3][0]);
dup2(pfd[1][0], STDIN_FILENO);
dup2(pfd[3][1], STDOUT_FILENO);
char *myargs[3];
myargs[0] = "./loopcmp";
myargs[1] = "lencmp";
myargs[2] = NULL;
if(execvp(myargs[0], myargs) == -1)
{
perror("exec");
return -2;
}
close(pfd[1][0]);
close(pfd[3][1]);
}
else // parent
{
while (1)
{
printf("Please enter first string:\n");
if (mygets(str1, LINELEN) == NULL)
break;
printf("Please enter second string:\n");
if (mygets(str2, LINELEN) == NULL)
break;
do {
printf("Please choose:\n");
for (int i=0 ; i < veclen ; i++)
printf("%d - %s\n", i, cmpstr[i]);
index = mygeti();
} while ((index < 0) || (index >= veclen));
close(pfd[index][0]);
if(write(pfd[index][1], str1, strlen(str1)) == -1)
{
perror("writeToPipe");
return -2;
}
if(write(pfd[index][1], str2, strlen(str2)) == -1)
{
perror("writeToPipe");
return -2;
}
if(index == 0)
{
close(pfd[2][1]);
char rbuf[1];
while(read(pfd[2][0], &rbuf, 1) > 0)
{
write(STDOUT_FILENO, &rbuf, 1);
}
}
if(index == 1)
{
close(pfd[3][1]);
char rbuf[1];
while(read(pfd[3][0], &rbuf, 1) > 0)
{
write(STDOUT_FILENO, &rbuf, 1);
}
}
}
}
}
return 0;
}
char *mygets(char *buf, int len)
{
char *retval;
retval = fgets(buf, len, stdin);
buf[len] = '\0';
if (buf[strlen(buf) - 1] == 10) /* trim \r */
buf[strlen(buf) - 1] = '\0';
else if (retval)
while (getchar() != '\n'); /* get to eol */
return retval;
}
int mygeti()
{
int ch;
int retval=0;
while(isspace(ch=getchar()));
while(isdigit(ch))
{
retval = retval * 10 + ch - '0';
ch = getchar();
}
while (ch != '\n')
ch = getchar();
return retval;
}
The other program - loopcmp: (Here I shouldn't change anything)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define LINELEN (80)
int lencmp(const char *str1, const char *str2);
int lexcmp(const char *str1, const char *str2);
char *mygets(char *buf, int len);
int main(int argc, char *argv[])
{
int(*cmpfunc)(const char *, const char *) = NULL;
char str1[LINELEN + 1];
char str2[LINELEN + 1];
if (argc != 2)
return -1;
if (!strcmp(argv[1], "lexcmp"))
cmpfunc = lexcmp;
else if (!strcmp(argv[1], "lencmp"))
cmpfunc = lencmp;
else
return -1;
while (1)
{
if (mygets(str1, LINELEN) == NULL)
break;
if (mygets(str2, LINELEN) == NULL)
break;
printf("%d\n", cmpfunc(str1, str2));
fflush(stdout);
}
return 0;
}
int lencmp(const char *str1, const char *str2)
{
int val;
val = strlen(str1) - strlen(str2);
if (val < 0)
return 1;
if (val > 0)
return 2;
return 0;
}
int lexcmp(const char *str1, const char *str2)
{
int val;
val = strcmp(str1, str2);
if (val < 0)
return 1;
if (val > 0)
return 2;
return 0;
}
char *mygets(char *buf, int len)
{
char *retval;
retval = fgets(buf, len, stdin);
buf[len] = '\0';
if (buf[strlen(buf) - 1] == 10) /* trim \r */
buf[strlen(buf) - 1] = '\0';
else if (retval) while (getchar() != '\n'); /* get to eol */
return retval;
}
This is what I get:
Picture
and what I actually need it to print the interger returned from the exec of the child and then start again and get new two strings and so on till the user exits. what am I doing wrong? I can only modify the main program (the first one)
The first thing to do is ensure you are closing all unnecessary file descriptors in each process.
This means anything relating to the lexcmp child process should be closed in the lencmp child process, and vice versa. The parent needs to close the read ends of both "TO" pipes, and the write end of both "FROM" pipes.
Each of these closures should happen exactly once, where appropriate.
As is, in the parent, you are calling close(pfd[index][0]);, close(pfd[2][1]);, and close(pfd[3][1]); in a loop.
After calling dup2, you should immediately close the first argument (the original pipe end). As is, in the the children, you are attempting to close them after execvp is called, which leads into the next issue...
If execvp succeeds, it NEVER returns, as it will completely replace the process image. Anything expected to run after it is really operating in a failure state. So
if(execvp(myargs[0], myargs) == -1)
{
perror("exec");
return -2;
}
could be written as
execvp(myargs[0], myargs)
perror("exec");
return -2;
to the same effect.
Aside: the large if .. else if .. else structure of main is a bit hard to read, and not needed since the body of each if statement results in the child processes being replaced, or exiting on error.
The next issues have to do with deadlocking, which most typically occurs when two intercommunicating processes attempt blocking reads from one another at the same time.
Your child processes expect input in a very specific way: 2 lines at a time, creating a pair of strings. The two write calls, in the form of,
write(pfd[index][1], strX, strlen(strX))
do not write any newlines, thus the children wait forever, never to send any data back, and the parent will wait forever, never receiving any data.
Aside: mygets is severely flawed, in a few ways, including being unable to detect EOF or I/O failures (this function is a SIGSEGV in waiting). One of the more obnoxious failings is that the comment here
if (buf[strlen(buf) - 1] == 10) /* trim \r */
is just plain wrong. ASCII decimal 10 is '\n', the line feed, or newline character. '\r', or carriage return, would be decimal 13. This is why using character constants 'A' instead of integer constants 65 is highly encouraged.
The side effect here, generally speaking, is your strings are stripped of a trailing newline character.
The second deadlock occurs when you go to read the child process' response.
Firstly, this example
char rbuf[1];
while(read(pfd[N][0], &rbuf, 1) > 0)
{
write(STDOUT_FILENO, &rbuf, 1);
}
is malformed. Either remove the & operators, OR change char rbuf[1]; to char rbuf;. Fixing this, and the newline problem from above, will result in the parent process reading data back from the child.
The problem then becomes that a while (read(...) > 0) loop will continuously block execution of the calling process, waiting for more data to be available.
This means another deadlock when the child process has already moved on to trying to read another pair of lines from the parent process.
A simple solution is to attempt a single, reasonably large read in the parent, and rely on the behaviour of fflush(stdout); in the child to flush the pipe to the parent.
Here is a functional -ish example, with minimal changes made. This program still has some problems, such as: the parent process generally has no idea of the status of the child processes, and relying signal propagation (^C) from the terminal to end the process tree gracefully, since loopcmp does not handle EOF (should really discuss this with whoever wrote loopcmp.c / mygets).
Additionally, mygeti is flawed as well, as an invalid input cannot be distinguished from a valid input of 0. It also does not handle EOF, or prevent signed integer overflow.
Some more robust abstraction (functions and structures) around creating child processes would help a lot to clean this up further.
This should help you progress, though.
#define _POSIX_C_SOURCE 200809L
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
#define LINELEN (80)
char *mygets(char *buf, int len);
int mygeti();
void close_pipe(int fd[2])
{
close(fd[0]);
close(fd[1]);
}
int main(void)
{
char *cmpstr[] = {"lexcmp", "lencmp"};
int veclen = sizeof(cmpstr)/sizeof(char *);
char str1[LINELEN + 1];
char str2[LINELEN + 1];
int index;
int pid[2];
int pfd[4][2];
/* pfd[0] is TO lexcmp
* pfd[1] is TO lencmp
* pfd[2] is FROM lexcmp
* pfd[3] is FROM lencmp
*/
for(int i = 0; i < 4; i++)
if(pipe(pfd[i]) < 0) {
perror("pipe");
return -2;
}
pid[0] = fork();
if (pid[0] == 0) {
/* child lexcmp */
close_pipe(pfd[1]);
close_pipe(pfd[3]);
close(pfd[0][1]);
close(pfd[2][0]);
dup2(pfd[0][0], STDIN_FILENO);
dup2(pfd[2][1], STDOUT_FILENO);
close(pfd[0][0]);
close(pfd[2][1]);
char *args[] = { "./loopcmp", "lexcmp", NULL };
execvp(*args, args);
perror("exec");
return -2; /* This only returns from the child */
}
pid[1] = fork();
if (pid[1] == 0) {
/* child lencmp */
close_pipe(pfd[0]);
close_pipe(pfd[2]);
close(pfd[1][1]);
close(pfd[3][0]);
dup2(pfd[1][0], STDIN_FILENO);
dup2(pfd[3][1], STDOUT_FILENO);
close(pfd[1][0]);
close(pfd[3][1]);
char *args[] = { "./loopcmp", "lencmp", NULL };
execvp(*args, args);
perror("exec");
return -2; /* This only returns from the child */
}
/* parent */
close(pfd[0][0]);
close(pfd[1][0]);
close(pfd[2][1]);
close(pfd[3][1]);
while (1) {
printf("Please enter first string: ");
if (mygets(str1, LINELEN) == NULL)
break;
printf("Please enter second string: ");
if (mygets(str2, LINELEN) == NULL)
break;
do {
printf("Please choose (");
for (int i=0 ; i < veclen ; i++)
printf(" [%d] %s", i, cmpstr[i]);
printf(" ): ");
index = mygeti();
} while ((index < 0) || (index >= veclen));
if (0 >= dprintf(pfd[index][1], "%s\n%s\n", str1, str2)) {
fprintf(stderr, "Failed to write to child %d\n", index);
perror("dprintf");
return -2;
}
char buf[64];
ssize_t bytes = read(pfd[index + 2][0], buf, sizeof buf - 1);
if (-1 == bytes) {
perror("read from child");
return -2;
}
buf[bytes] = 0;
printf("Result: %s", buf);
}
}
char *mygets(char *buf, int len)
{
char *retval;
retval = fgets(buf, len, stdin);
buf[len] = '\0';
if (buf[strlen(buf) - 1] == 10) /* trim \r */
buf[strlen(buf) - 1] = '\0';
else if (retval)
while (getchar() != '\n'); /* get to eol */
return retval;
}
int mygeti()
{
int ch;
int retval=0;
while(isspace(ch=getchar()));
while(isdigit(ch))
{
retval = retval * 10 + ch - '0';
ch = getchar();
}
while (ch != '\n')
ch = getchar();
return retval;
}
Note the use of dprintf. If not available for whatever reason, just make sure to write a single newline after each string.
Final aside: with the way fgets works, the + 1 to the string buffer sizes are rather meaningless (although they are indirectly required here due to mygets performing its own, poorly designed buf[len] = '\0'). fgets writes at most len - 1 non-null bytes, always leaving room for the null terminating byte, which it places.

Forked process isn't calling function in C

C beginner here. The function send_chars_to_reducers does not appeared to be getting called inside the forked processes created in fork_mappers function.
C Code
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#define BUFFER_SIZE 1024
#define ALPHA_OFFSET 97
#define LETTERS 26
const int NUM_OF_MAPPERS = 4;
const int NUM_OF_REDUCERS = 26;
const int PIPE_READ_END = 0;
const int PIPE_WRITE_END = 1;
const int PIPE_BUFFER_SIZE = 1000;
int mapper_pipes[4][2];
int reducer_pipes[26][2];
void pipe_wrapper(int pipefd[]) {
int ret = pipe(pipefd);
if (ret == -1) {
perror("Error. Failed when trying to create pipes.");
exit(EXIT_FAILURE);
}
}
void create_mapper_pipes(void) {
int i;
for (i = 0; i < NUM_OF_MAPPERS; i++) {
pipe_wrapper(mapper_pipes[i]);
}
}
void create_reducer_pipes(void) {
int i;
for (i=0; i < NUM_OF_REDUCERS; i++) {
pipe_wrapper(reducer_pipes[i]);
}
}
// Prints an error msg and exits if one occurs. Else, returns the system call value.
int print_if_err(int syscall_val, const char* syscall_name) {
if (syscall_val < 0) {
perror(syscall_name);
exit(errno);
} else {
//No syscall error we can return
return syscall_val;
}
}
void send_chars_to_reducers(void) {
printf("hello from send_chars_to_reducers\n");
}
void fork_mappers(void) {
/* Constants useful to all children */
char ibuf[PIPE_BUFFER_SIZE]; // input pipe buffer
int rlen = 0;
int i;
for (i=0; i<NUM_OF_MAPPERS; i++) {
pid_t mapper_pid = print_if_err(fork(), "fork");
close(mapper_pipes[i][PIPE_WRITE_END]);
if (mapper_pid == 0) {
rlen = print_if_err(read(mapper_pipes[i][PIPE_READ_END], ibuf, 1000), "read");
while(rlen > 0) {
send_chars_to_reducers();
printf("read line from forked_mappers, p%d: %s\n", i, ibuf);
rlen = print_if_err(read(mapper_pipes[i][PIPE_READ_END], ibuf, 1000), "read");
}
_exit(0);
}
}
}
void fork_reducers(void) {
printf("hello from fork_reducers\n");
int i;
for (i = 0; i < NUM_OF_REDUCERS; i++) {
pid_t reducer_pid = print_if_err(fork(), "fork");
if (reducer_pid == 0) {
while (1 == 1) {
}
}
}
}
void send_lines_to_mappers(void) {
int wlen = 0;
char obuf[PIPE_BUFFER_SIZE];
int ob_size;
int count = 0;
char buff[BUFFER_SIZE]; // a buffer for each line of the file
FILE *input_file = fopen("input.txt", "r");
// read the input file line by line
while(fgets(buff, BUFFER_SIZE, input_file) > 0) {
printf("read line from send_lin_to_mappers: %s\n", buff);
ob_size = sizeof buff;
switch(count) {
case 0 :
write(mapper_pipes[0][PIPE_WRITE_END], buff, ob_size);
close(mapper_pipes[0][PIPE_WRITE_END]);
break;
case 1 :
write(mapper_pipes[1][PIPE_WRITE_END], buff, ob_size);
close(mapper_pipes[1][PIPE_WRITE_END]);
break;
case 2 :
write(mapper_pipes[2][PIPE_WRITE_END], buff, ob_size);
close(mapper_pipes[2][PIPE_WRITE_END]);
break;
case 3 :
write(mapper_pipes[3][PIPE_WRITE_END], buff, ob_size);
close(mapper_pipes[3][PIPE_WRITE_END]);
break;
default :
printf("you did something wrong in send_lines_to_mappers loop");
}
count++;
}
fclose(input_file);
}
int main(void) {
// Setup the mapper pipes
create_mapper_pipes();
create_reducer_pipes();
fork_reducers();
fork_mappers();
send_lines_to_mappers();
return 0;
}
Output
hello from fork_reducers
read line from send_lin_to_mappers: I like coding in C.
read line from send_lin_to_mappers: I like manually allocating memory, and opening the registers window in Visual Studio to see the values of the eax register and blitting graphics to the screen and all the stuff that Dr. Dobbs wrote about in the 90s.
read line from send_lin_to_mappers: My programming friends seem to believe that understanding this level of programming is good in a hand-wavy, theoretical sense, but when you consider all the web development, Java frameworks, and existing libraries most programmers today rely on, it's hard to really pin down a solid answer to the question "Why learn C?"
read line from send_lin_to_mappers: This is my attempt to answer that question, and I believe it comes down to the basic programming concept of abstraction.
The problem is the number of processes which share handles.
Each reducer has the pipes open
Each mapper has the pipes open, so the pipes are never properly closed ( the reducers are in a busy loop.
if (mapper_pid == 0) {
int j;
for( j = 0; j < NUM_OF_MAPPERS; j++ ){
close( mapper_pipes[j][PIPE_WRITE_END]);
if( j != i ){
close(mapper_pipes[j][PIPE_READ_END]);
}
}
I commented out the reducer fork's and then fixed the mapper forks to close all the pipe handles in the child except the one we want to read from.
This started to make the program work.
From pipe documentation, you should close the unused bits of a pipe. But you have pre-opened all the pipes (probably better localizing them), and have therefore got to close all the unused handles in the sub-processes to have the OS tidy up the resource correctly

segmentation fault unix shell

I have an assignment for school where I have to create a shell which can do the following:
read incoming command, parse each part of command
fork child process and execute each command without (< > >> |)
successfully execute each command with <, >, >>
successfully execute each command with |
I am seriously lost... I am new to shell and I have no clue on what to do from here.
My code gives me an error stating segmentation fault (core dumped). Any and all help will be greatly appreciated.
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#define MAX_ARG 10
int main()
{
char line [256];
char prompt[] = "sh2 % ";
char command[256], *args[MAX_ARG];
int pid;
char status;
int i;
/* spit out the prompt */
printf("%s", prompt );
while( fgets(line, sizeof line, stdin) != NULL)
{
/* fgets leaves '\n' in input buffer. ditch it */
line [strlen(line)-1] = '\0';
while (line != NULL)
{
//parse command and arg
args[0] = strtok(line, " "); //grab command
for (i=1; i<MAX_ARG; i++) //grab arguments, to assume max = 10?
{
//if a single command with arguments then set command & argument
//for (i>0)
{
// check to see if the command is 'exit'
if(!strcmp("exit", args[i]))
{
exit(0);
}
{
int p[2];
pipe(p)
if (fork() == 0) //child
{
close (0);
dup(p[0]);
exec("cmd2");
}
else
{
close(1);
close(p[0]);
close(p[1]);
dup(p[1]);
exec("cmd1");
}
close(0);
open("stdout.txt", "r");
if (fork()== 0)
{
exec("cmd3");
}
}
else if (!strcmp(">", args[i]))
open("stderr.txt". "w")
if (fork() == 0)
{
exec("cmd1");
}
}
else if (!strcmp(">>", args[i]))
{
close(1);
open("stdout_stderr.txt", "w");
if (fork() == 0)
{
close(2);
dup(1);
exec("cmd2");
}
}
else
{
pid = fork();
if (pid == 0)
{
status = execvp(command,args);
exit(0);
}
else
{
waitpid(-1);
}
}
}
}
}
}
return 0;
}
You have quite a few things wrong in your program, so it's hard to point to one line and say change this. I think you are trying to do too much at once and not building on a solid base.
In programming you want to start small and build on your progress, your professor did you a favor by lining up the steps:
read incoming command, parse each part of command
fork child process
and execute each command without (< > >> |)
successfully execute
each command with <, >, >>
successfully execute each command with |
Try to get #1 working befor moving on to #2 and as mentioned before using functions is going to help a lot. I would suggest looking at this post http://bytes.com/topic/c/answers/215994-writing-shell-c which will give you a simple shell you could model from. Here is a baseline parser to get you started on #1 (based on the post mentioned)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char line[4096] = {0};
char safeline[4096] = {0};
int done = 0;
int i = 0;
char *s = line;
char **t = NULL;
char *prompt = ">";
char *args = NULL;
char *nl = NULL;
int main(void)
{
while(!done)
{
printf("%s", prompt);
fflush(stdout);
if(NULL == fgets(line, sizeof line, stdin))
{
t = NULL;
done = 1;
}
else
{
nl = strchr(line, '\n');
if(nl != NULL)
{
*nl = '\0';
strcpy(safeline, line);
}
else
{
int ch;
printf("Line too long! Ignored.\n");
while((ch = getchar()) != '\n' && ch != EOF)
{
continue;
}
if(ch == EOF)
{
done = 1;
}
}
args = strchr(line, ' ');
if(args != NULL)
{
*args++ = '\0';
}
if(!done)
{
printf("command - %s : args - %s\n",s, args);
}
}
}
}

Issue with multiple dup2s, forks, pipes and execs

I am working on a shell(yes, it's an assignment, so I am not looking for a solution, more of a guide and clues to what I am doing wrong), and I'm stuck with pipes. I have read numerous tutorials, mans and what not, even copy-pasted some codes that "work", nothing seems to do the trick.
This is what I have so far:
void ft_run_pipe(char **cmd, int num)
{
int i;
int j;
int piped[num][2];
pid_t pid;
i = 0;
j = 0;
while (i < num)
{
ft_putstr("Creating pipe");
ft_putnbr(i);
ft_putchar('\n');
if (pipe(piped[i]) < 0)
ft_putstrn("piped failed");
i++;
}
while (cmd[j] != '\0')
{
pid = fork();
if (pid == 0)
{
ft_putchar('a');
if (j != 0)
{
ft_putstrn("If not first");
ft_putnbr(j - 1);
if (dup2(piped[j - 1][0], 0) < 0)
{
ft_putstrn("dup1 failed");
ft_putstrn(strerror(errno));
}
close(piped[j - 1][1]);
close(piped[j - 1][0]);
}
if (j != num)
{
ft_putstrn("If not last");
ft_putnbr(j);
if (dup2(piped[j][1], 1) < 0)
{
ft_putstrn("dup2i failed");
ft_putstrn(strerror(errno));
}
close (piped[j][0]);
close (piped[j][0]);
}
i = 0;
while (i < num)
{
close(piped[i][0]);
close(piped[i][1]);
i++;
}
// ft_run_special(cmd[j], environ);
}
else
{
i = 0;
while (i < num)
{
close(piped[i][0]);
close(piped[i][1]);
i++;
}
}
j++;
}
wait (NULL);
}
The various ft_something functions are coded by me alternatives to some libc functions(ft_putstr writes a string, with n in the end it adds a \n, etc.). ft_exec_special is a function that executes an executable with execve, searching in the PATH variable.
When ran with two commands and a single pipe, it returns
Creating pipe0
aIf not last
0aIf not first
0dup1 failed
Bad file descriptor
While, normally, it seems to me, it should work - piped[0][0] exists, is properly piped, and something was written to piped[0][1] - why would it say piped[0][0] is a bad file descriptor?

Pipe communication in C problems

I'm trying to write some code which uses pipes to communicate between a parent process and it's children. However, my pipe seems to give up after the first time I use it (that is, it stops working after the first use of the pipe). I'm not really sure how to fix this problem, and any help will be greatly appreciated. I also know that some of the coding practice I am using in this are not really ideal (mainly use of sleep).
const int READ = 0;
const int WRITE = 1;
char* COOP = "Criminal cooperates\n";
char* SIL = "Criminal doesn't talk\n";
char* reader(int);
void writer(int, char *c);
int main()
{
int c1pipe1[2];
int c1pipe2[2];
int c2pipe1[2];
int c2pipe2[2];
int c1sentence = 0;
int c2sentence = 0;
int r;
int c;
pipe(c1pipe1);
pipe(c1pipe2);
pipe(c2pipe1);
pipe(c2pipe2);
int C2;
int C1 = fork();
if(C1 > 0)
C2 = fork();
if(C1 < 0 || C2 < 0) //error
{
perror("fork() failed");
exit(1);
}
else if(C1 == 0)
{
close(c1pipe1[WRITE]);
close(c1pipe2[READ]);
for(c = 0; c < 10; c++)
{
r = rand();
//printf("C1 rand = %d\n", r%2);
if(r % 2 == 1)
writer(c1pipe2[WRITE], "1");
else
writer(c1pipe2[WRITE], "0");
sleep(1);
}
exit(0);
}
else if(C2 == 0)
{
close(c2pipe1[WRITE]);
close(c2pipe2[READ]);
for(c = 0; c < 10; c++)
{
r = rand();
//printf("C2 rand = %d\n", r%2);
if(r % 2 == 1)
writer(c2pipe2[WRITE], "1");
else
writer(c2pipe2[WRITE], "0");
sleep(1);
}
exit(0);
}
else //parent
{
int buff1; //stores choice of c1
int buff2; //stores choice of c2
close(c1pipe1[READ]);
close(c1pipe2[WRITE]);
close(c2pipe1[READ]);
close(c2pipe2[WRITE]);
for(c = 0; c< 10; c++)
{
buff1 = atoi(reader(c1pipe2[READ]));
buff2 = atoi(reader(c2pipe2[READ]));
printf("C1's \(%d)\ choice trial %d : %d\n", C1, c+1, buff1);
printf("C2's \(%d)\ choice trial %d : %d\n", C2, c+1, buff2);
if(buff1 && buff2) //c1 and c2 cooperate with police
{
c1sentence = c1sentence + 6;
c2sentence = c2sentence + 6;
}
else if(buff1 || buff2) // one cooperates, one is silent
{
if(buff1) // if c1 cooperates and c2 is silent
{
c1sentence = c1sentence + 0;
c2sentence = c2sentence + 10;
}
else // if c2 cooperates and c1 is silent
{
c1sentence = c1sentence + 10;
c2sentence = c2sentence + 0;
}
}
else if(!(buff1 && buff2)) //both c1 and c2 are silent
{
c1sentence = c1sentence + 1;
c2sentence = c2sentence + 1;
}
sleep(1);
}
printf("C1 is in jail for %d years total\n", c1sentence);
printf("C2 is in jail for %d years total\n", c2sentence);
exit(0);
}
exit(0);
}
void writer(int pipe_write_fd, char *c)
{
open(pipe_write_fd);
char* choice = c;
// Write to the pipe
write(pipe_write_fd, choice, strlen(choice));
// Close the pipe
// (Sends 'end of file' to reader)
close(pipe_write_fd);
}
char* reader(int pipe_read_fd)
{
open(pipe_read_fd);
// Allocate buffer to store
// result of read
int buffer_size = 1024;
char buffer[buffer_size];
// Keep reading until we exhaust
// buffer or reach end of file
int i = 0;
while (i < buffer_size
&& read(pipe_read_fd, &buffer[i], 1) > 0)
{ i++; }
if (i < buffer_size) {
// Add null termination
buffer[i] = '\0';
} else {
// We exhausted buffer
fprintf(stderr, "Warning: buffer full.\n");
buffer[buffer_size-1] = '\0';
}
//printf("%s", buffer);
// Close the pipe
close(pipe_read_fd);
return buffer;
}
You need to close more of the pipes. The child processes must close every pipe file descriptor that they are not using. You have 8 pipe file descriptors; each child process has to close 6 of those - at least! You would be very well advised not to create all the pipes up front as you have done - it is complicated to control things and get all the right descriptors closed.
Looking at the code more closely, the parent does not write messages to the child processes, so you have twice as many pipes as you need - you only need one pipe for each child process to write back to the parent with.
You also do not open() already open file descriptors to the pipes...but how did you get the code to compile? You must be missing the correct header (#include <fcntl.h>) for open() and compiling without enough warning options enabled.
Your variables COOP and SIL are unused in the code presented.
Your writer() function not only mistakenly tries to open an already closed file descriptor, it also closes it, which means that there is no way to send back the extra messages after the first. You should only close the file descriptor once finished - after the loop in the main program for each child. This is why you only see one message.
It is also worth getting into the habit of error-checking the return from every system call that can fail. There are a few that can't fail - getpid() is one such. But I/O operations are notorious for failing for reasons outside the direct control of the program (or, in this case, within the control of the program), so you should check that writes succeed. When you get back an EBADF - bad file descriptor - error, you know something is up.
You have similar problems with close() (and open()) in reader(), plus the additional problem that you attempt to return a pointer to a local automatic variable - which is not a good idea, ever. Again, a decent compiler (like GCC) with warnings enabled will tell you about such things. I used this command to compile your program:
gcc -O -std=c99 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes \
pipe.c -o pipe
Your child processes are always going to generate the same sequence of (pseudo-)random numbers, which isn't very exciting. You should probably use something like:
srand(getpid());
to ensure they get different sequences.
Your reader() function is both not enthusiastic enough and too enthusiastic about reading the data. You read a single byte at a time, but you then loop to accumulate single bytes, so the code waits around for all 10 results to be known, and then spits everything out at once. Since a 32-bit integer can store a number up to 1,111,111,111 without problem, you would get just one number back from your call to atoi() on the first iteration, which isn't quite what you wanted.
Reads and writes on pipes are atomic - in the sense that if the writing process writes 6 bytes and the reading process attempts to read more than 6 bytes, then the packet of 6 bytes will be returned by a single read, even if there are other bytes in the pipe waiting to be read; those extra bytes will be returned on subsequent calls to read().
So, your reader() function should be passed in a buffer to use, along with its size; the code should attempt to read that buffer size; it should null terminate what it does receive; it can return the pointer to the buffer it was passed; it should error check the returned value from read().
The code for the two child processes is essentially the same - you should use an appropriately parameterized function rather than writing out the code twice.
Putting it all together, you end up with something like this (which works fine for me on MacOS X 10.6.6 with GCC 4.5.2):
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <stdarg.h>
#include <stdlib.h>
const int READ = 0;
const int WRITE = 1;
static char* reader(int fd, char *buffer, size_t bufsiz);
static void writer(int fd, const char *c);
static void child_process(int *my_pipe, int *his_pipe);
static void err_exit(const char *fmt, ...)
{
va_list args;
int errnum = errno;
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
if (errnum != 0)
fprintf(stderr, "%d: %s\n", errnum, strerror(errnum));
exit(1);
}
int main(void)
{
int c1pipe[2];
int c2pipe[2];
int c1sentence = 0;
int c2sentence = 0;
int c;
if (pipe(c1pipe) != 0 || pipe(c2pipe) != 0)
err_exit("Failed to open a pipe\n");
int C2 = 0;
int C1 = fork();
if (C1 > 0)
C2 = fork();
if (C1 < 0 || C2 < 0) //error
err_exit("fork() failed\n");
else if (C1 == 0)
child_process(c1pipe, c2pipe);
else if (C2 == 0)
child_process(c2pipe, c1pipe);
else //parent
{
int choice1; //stores choice of c1
int choice2; //stores choice of c2
char buffer1[BUFSIZ];
char buffer2[BUFSIZ];
close(c1pipe[WRITE]);
close(c2pipe[WRITE]);
for (c = 0; c< 10; c++)
{
choice1 = atoi(reader(c1pipe[READ], buffer1, sizeof(buffer1)));
choice2 = atoi(reader(c2pipe[READ], buffer2, sizeof(buffer1)));
printf("C1's (%d) choice trial %d : %d\n", C1, c+1, choice1);
printf("C2's (%d) choice trial %d : %d\n", C2, c+1, choice2);
if (choice1 && choice2) //c1 and c2 cooperate with police
{
c1sentence = c1sentence + 6;
c2sentence = c2sentence + 6;
}
else if (!(choice1 && choice2)) //both c1 and c2 are silent
{
c1sentence = c1sentence + 1;
c2sentence = c2sentence + 1;
}
else if (choice1) // if c1 cooperates and c2 is silent
{
c1sentence = c1sentence + 0;
c2sentence = c2sentence + 10;
}
else // if c2 cooperates and c1 is silent
{
c1sentence = c1sentence + 10;
c2sentence = c2sentence + 0;
}
}
printf("C1 is in jail for %d years total\n", c1sentence);
printf("C2 is in jail for %d years total\n", c2sentence);
}
return(0);
}
static void writer(int pipe_write_fd, const char *c)
{
int len = strlen(c);
if (write(pipe_write_fd, c, len) != len)
err_exit("Write failed\n");
}
static char* reader(int pipe_read_fd, char *buffer, size_t bufsiz)
{
int i = read(pipe_read_fd, buffer, bufsiz-1);
if (i < 0)
err_exit("Read failed\n");
buffer[i] = '\0';
return buffer;
}
static void child_process(int *my_pipe, int *his_pipe)
{
int c;
srand(getpid());
close(my_pipe[READ]);
close(his_pipe[READ]);
close(his_pipe[WRITE]);
for (c = 0; c < 10; c++)
{
writer(my_pipe[WRITE], ((rand() % 2) == 1) ? "1" : "0");
sleep(1);
}
close(my_pipe[WRITE]);
}
Note how the error routine captures errno early - to avoid damaging it. It is one of the perils of using global variables; they may change when you call a function. Don't use them when you can avoid them (but note that you can't avoid using errno completely, in general).
void writer(int pipe_write_fd, char *c)
{
open(pipe_write_fd);
char* choice = c;
// Write to the pipe
write(pipe_write_fd, choice, strlen(choice));
// Close the pipe
// (Sends 'end of file' to reader)
close(pipe_write_fd);
}
I'm not sure which function open you are trying yo use but the usual one takes a filename and returns a file descriptor. In any case you are discarding the return value so I suppose that this doesn't matter.
What is clear is that you close the pipe immediately after the first write so it is "correct" that the next write will fail; the pipe has been closed.
If you fix this problem then the next problem is that reader will, one byte at a time, all of the available input - up to 1024 bytes - before closing the read pipe. As reader is called in a loop, the read attempt in the second iteration will fail.

Resources