Multiple children reading from/writing to a single parent - c

Here I have a program where a parent process creates several child processes, passes
a distinct integer to each of them. Then each child process writes back the integer read to the parent process, which prints the result to standard output:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#define R 0
#define W 1
void run_child(int in, int out){
int r;
int it;
while((r = read(in, &it, sizeof(it))) != 0){
if(r == -1){
perror("read");
exit(1);
}
//word[r] = '\0';
int w = write(out, &it, sizeof(it));
if(w == -1){
perror("write");
exit(1);
}
if(close(out) == -1){
perror("close");
exit(1);
}
}
}
int main(int argc, char **argv) {
// Process fan
int i;
int n;
int num_kids;
int from_parent[2];
int to_parent[2];
if(argc != 2) {
fprintf(stderr, "Usage: fan_write <numkids>\n");
exit(1);
}
num_kids = atoi(argv[1]);
int status;
char word[32];
for(i = 0; i < num_kids; i++) {
if(pipe(from_parent) == -1){
perror("pipe");
exit(1);
}
if(pipe(to_parent) == -1){
perror("pipe");
exit(1);
}
int g = i;
write(from_parent[W], &g, sizeof(int));
n = fork();
if(n < 0) {
perror("fork");
exit(1);
}
if(n == 0){
if(close(from_parent[W]) == -1){
perror("close");
exit(1);
}
if(close(to_parent[R]) == -1){
perror("close");
exit(1);
}
dup2(from_parent[R], STDIN_FILENO);
if(close(from_parent[R]) == -1){
perror("close");
exit(1);
}
run_child(STDIN_FILENO, to_parent[W]);
close(to_parent[W]);
exit(0);
}
if(close(from_parent[R]) == -1){
perror("close");
exit(1);
}
if(close(to_parent[W]) == -1){
perror("close");
exit(1);
}
if(close(from_parent[W]) == -1){
perror("close");
exit(1);
}
for(i=0;i<num_kids;i++){
int read_int;
int r = read(to_parent[R], &read_int, sizeof(int));
printf("read %d bytes\n", r);
if(r == -1){
perror("read");
exit(1);
}
printf("%d\n", read_int);
}
}
for(i = 0; i < num_kids; i++){
wait(&status);
}
return 0;
}
With num_kids = 4 I would expect the program to read 4 bytes each time and print the
distinct integer. However, when run it reads 4 bytes in one iteration, and then it reads
0 bytes on the following iterations, and prints the same integer over and over. I'm not sure how to fix it.
Edit: Solved! Hint: use a matrix of file descriptors for pipes.

you have wrong concept to read numbers from all num_kids child process in a loop, your are reading every time from single child and in each child, by loop:
for(i=0;i<num_kids;i++){
int read_int;
int r = read(to_parent[R], &read_int, sizeof(int));
printf("read %d bytes\n", r);
if(r == -1){
perror("read");
exit(1);
}
printf("%d\n", read_int);
}
Is code runs for each child including parent too because you are unconditionally running this loop in child/parent process. But parent can read from single child that the reason you fist time get 4 byte then 0. because child return a number one time. Remove above for loop from your code and do like I am suggesting below:
you should do like(read comments):
if(n == 0){
//child
}
else{
// read in parent without loop
}
So correct way is:
if(n == 0){//child
if(close(from_parent[W]) == -1){
perror("close");
exit(1);
}
if(close(to_parent[R]) == -1){
perror("close");
exit(1);
}
dup2(from_parent[R], STDIN_FILENO);
if(close(from_parent[R]) == -1){
perror("close");
exit(1);
}
run_child(STDIN_FILENO, to_parent[W]);
close(to_parent[W]);
exit(0);
}
else{ // parent
write(from_parent[W], &g, sizeof(int));
int read_int;
int r = read(to_parent[R], &read_int, sizeof(int));
printf("read %d bytes\n", r);
if(r == -1){
perror("read");
exit(1);
}
printf("%d\n", read_int);
}
And its working like:
:~$ ./a.out 4
read 4 bytes
0
read 4 bytes
1
read 4 bytes
2
read 4 bytes
3

if (n == 0) { //child
if(close(from_parent[W]) == -1) {
perror("close");
exit(1);
}
if(close(to_parent[R]) == -1) {
perror("close");
exit(1);
}
run_child(from_parent[R], to_parent[W]);
close(from_parent[R]); // ignore checking return code here!
close(to_parent[W]); // ignore checking return code here!
exit(0);
}
// And this is what run_child looks like
void run_child(int in, int out){
int r;
int it;
while((r = read(in, &it, sizeof(it))) != 0){
if(r == -1){
perror("read");
exit(1);
}
int w = write(out, &it, sizeof(it));
if(w == -1){
perror("write");
exit(1);
}
}
}

I've mixed up the aswers code and this is the final result..
All succesful
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#define R 0
#define W 1
void run_child(int in, int out){
int r;
int it;
while((r = read(in, &it, sizeof(it))) != 0){
if(r == -1){
perror("read");
exit(1);
}
int w = write(out, &it, sizeof(it));
//perror("write:");
//close(out) ;
exit(1);
}
}
int main(int argc, char **argv) {
// Process fan
int i;
int n;
int num_kids;
int from_parent[2];
int to_parent[2];
if(argc != 2) {
fprintf(stderr, "Usage: fan_write <numkids>\n");
exit(1);
}
num_kids = atoi(argv[1]);
int status;
char word[32];
for(i = 0; i < num_kids; i++) {
if(pipe(from_parent) == -1){
perror("pipe");
exit(1);
}
if(pipe(to_parent) == -1){
perror("pipe");
exit(1);
}
int g = i;
write(from_parent[W], &g, sizeof(int));
n = fork();
if(n < 0) {
perror("fork");
exit(1);
}
if(n == 0){//child
if(close(from_parent[W]) == -1){
perror("close");
exit(1);
}
if(close(to_parent[R]) == -1){
perror("close");
exit(1);
}
run_child(from_parent[R], to_parent[W]);
if(close(from_parent[R]) == -1){
perror("close");
exit(1);
}
if(close(to_parent[W]) == -1){
perror("close");
exit(1);
}
exit(0);
}
else{ // parent
write(from_parent[W], &g, sizeof(int));
int read_int;
int r = read(to_parent[R], &read_int, sizeof(int));
printf("read %d bytes\n", r);
if(r == -1){
perror("read");
exit(1);
}
printf("%d\n", read_int);
}
}
for(i = 0; i < num_kids; i++){
wait(&status);
}
return 0;
}

Related

FIFO pipe - Child wont read properly

I try to use two FIFOs, one for the child process and one for the parent.
First, the child should write a question in the parent FIFO.
Parent waits until question arrives, then reads it, answers it and writes the answer in the child's FIFO.
Child waits for the answer, then reads it and puts it out.
Problem is, somehow the child reads always an empty answer and I don't know why.
Code below:
MAIN:
int main() {
pid_t child, parent;
char quest[PIPE_BUF];
int n=0;
switch(fork()) {
case CHILD: {
while(n==0){
printf("Bitte Frage stellen\n");
n=scanf("%s", quest);
}
}
child = getpid();
question(quest, child);
break;
case ERROR:
perror("fork():");
break;
default:
printf("waiting for input\n");
parent = getpid();
oracle(parent);
}
return EXIT_SUCCESS;
}
CHILD FUNCTION
void question(const char *buf, pid_t pid ){
char filename[PIPE_BUF];
if (mkfifo(ANSW, 0666) == -1){
if(errno == EEXIST)
perror(" oracle mkfifo:");
else{
perror(" oracle mkfifo:");
exit(EXIT_FAILURE);
}
}
int fd, fd2;
fd = open(FIFO, O_WRONLY);
char quest[PIPE_BUF + 1];
char answer[PIPE_BUF + 1];
int n = 0;
sprintf(quest, "%d:", pid);
strcat(quest,buf);
write(fd,quest,strlen(quest));
sleep(2);
fd2= open(ANSW,O_RDONLY);
printf("%s\n", answer);
while(1) {
n=read(fd2, answer, strlen(answer));
if (n == -1) {
perror(" client read():");
break;
} else if (n > 0){
puts(answer);
break;
}
}
remove("/tmp/answer.me");
unlink(FIFO);
unlink(ANSW);
close(fd);
close(fd2);
}
PARENT FUNCTIONS:
int isVokal(const char* buf, int i){
if (buf[i] == '?' && ((buf[i-1] == 'a') || (buf[i-1] == 'e') ||(buf[i-1] == 'i') || (buf[i-1] == 'o') || (buf[i-1] == 'u')))
return 1;
else
return 0;
}
int answer(char * buf, pid_t pid){
int i = 0;
int fd = open(ANSW, O_WRONLY);
char answer[PIPE_BUF + 1];
size_t x = strlen(buf);
buf[x+1] = '\0';
do{
i++;
if(buf[i] == '\0'){
--i;
if(buf[i] != '?'){
sprintf(answer, "%s","Dies ist keine Frage.\n");
write(fd,answer, strlen(answer));
i++;
}else if(isVokal(buf,i)){
sprintf(answer, "%s","Yes!\n");
write(fd,answer, strlen(answer));
i++;
}else{
sprintf(answer, "%s","No!\n");
write(fd,answer, strlen(answer));
i++;
}
}
}while ( buf[i] != '\0');
close(fd);
return 0;
}
void oracle(pid_t pid) {
int i = 1;
if ((mkfifo(FIFO, 0666) == -1)){
if(errno == EEXIST)
perror(" oracle mkfifo:");
else{
perror(" oracle mkfifo:");
exit(EXIT_FAILURE);
}
}
int fd = open(FIFO, O_RDONLY);
if(fd == -1)
perror(" oracle open():");
char buf[PIPE_BUF + 1];
while (i) {
printf("waiting for input\n");
int n = read(fd, buf, PIPE_BUF);
if (n == -1)
perror(" oracle read():");
else if (n >= 0)
i=answer(buf, pid);
}
close(fd);
remove("/tmp/ask.me");
unlink(FIFO);
unlink(ANSW);
}

Why can't I simulate auto-completion with '\t' in busybox

I want to use "yp\t\n\0" to run “ypdomainname” command by exploiting auto-completion in busybox, but it failed. my code and result are below:
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>
#include <fcntl.h>
#define DEFAULT_BUSYBOX_PATH "/bin/busybox"
#define MAX_BUF 1000
int main()
{
int fd[2];
pid_t pid;
FILE *file;
int status;
if(pipe(fd) < 0){
fprintf(stderr, "pipe error!\n");
return -1;
}
if((pid = fork()) < 0){
fprintf(stderr, "pipe error!\n");
}else if(pid == 0){ //child
close(fd[1]);
int fd_output;
fd_output = open("result", O_CREAT | O_RDWR, 777);
if(fd_output != STDOUT_FILENO){
if(dup2(fd_output, STDOUT_FILENO) != STDOUT_FILENO)
fprintf(stderr, "dup2 error to stdout\n");
}
if(fd[0] != STDIN_FILENO){
if(dup2(fd[0], STDIN_FILENO) != STDIN_FILENO)
fprintf(stderr, "dup2 error to stdin\n");
}
execl(DEFAULT_BUSYBOX_PATH, DEFAULT_BUSYBOX_PATH, "ash", NULL);
close(fd[0]);
close(fd_output);
return 0;
}else{ //parent
close(fd[0]);
char buf[MAX_BUF] = "yp";
buf[2] = '\t';
buf[3] = '\n';
buf[4] = '\0';
write(fd[1], buf, strlen(buf));
close(fd[1]);
return 0;
}
}
Result of my code
What makes me confused is that the character is not changed in function lineedit_read_key() in file lineedit.c and it will run the function input_tab() when the character is '\t'.
input_tab will be executed when character is '\t'
Recently, i took some time to learn about terminal to simulate auto-completion, but i still failed. My code is below:
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <termios.h>
#include <errno.h>
#include <signal.h>
#include <pty.h>
#define DEFAULT_BUSYBOX_PATH "/bin/busybox"
#define MAX_BUF 1000
#define BUFFSIZE 512
typedef void Sigfunc(int);
static void sig_term(int);
static volatile sig_atomic_t sigcaught;
ssize_t writen(int fd, const void *ptr, size_t n){
size_t nleft;
ssize_t nwritten;
nleft = n;
while(nleft > 0){
if((nwritten = write(fd, ptr, nleft)) < 0){
if(nleft == n){
return(-1);
}else{
break;
}
}else if(nwritten == 0) {
break;
}
nleft -= nwritten;
ptr += nwritten;
}
return(n - nleft);
}
Sigfunc *signal_intr(int signo, Sigfunc *func){
struct sigaction act;
struct sigaction oact;
act.sa_handler = func;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
#ifdef SA_INTERRUPT
act.sa_flags |= SA_INTERRUPT;
#endif
if (sigaction(signo, &act, &oact) < 0)
return(SIG_ERR);
return(oact.sa_handler);
}
int ptym_open(char *pts_name, int pts_namesz)
{
char ptr[50];
int fdm;
int err;
if((fdm = posix_openpt(O_RDWR)) < 0){
return(-1);
}
if(grantpt(fdm) < 0){
goto errout;
}
if(unlockpt(fdm) < 0){
goto errout;
}
if(ptsname_r(fdm, ptr, 50) != 0){
goto errout;
}
strncpy(pts_name, ptr, pts_namesz);
pts_name[pts_namesz - 1] = '\0';
return(fdm); /* return fd of master */
errout:
err = errno;
close(fdm);
errno = err;
return(-1);
}
int ptys_open(char *pts_name){
int fds;
if((fds = open(pts_name, O_RDWR)) < 0)
return(-1);
return(fds);
}
pid_t pty_fork(int *ptrfdm, char *slave_name, int slave_namesz, const struct termios *slave_termios, const struct winsize *slave_winsize){
int fdm, fds;
pid_t pid;
char pts_name[20];
if((fdm = ptym_open(pts_name, sizeof(pts_name))) < 0){
fprintf(stderr, "can't open master pty: %s, error %d", pts_name, fdm);
}
if(slave_name != NULL) {
strncpy(slave_name, pts_name, slave_namesz);
slave_name[slave_namesz - 1] = '\0';
}
if((pid = fork()) < 0) {
return(-1);
}else if (pid == 0) { /* child */
if(setsid() < 0){
fprintf(stderr, "setsid error");
}
if((fds = ptys_open(pts_name)) < 0){
fprintf(stderr, "can't open slave pty");
}
close(fdm);
if(slave_termios != NULL) {
if (tcsetattr(fds, TCSANOW, slave_termios) < 0)
fprintf(stderr, "tcsetattr error on slave pty");
}
if(slave_winsize != NULL) {
if (ioctl(fds, TIOCSWINSZ, slave_winsize) < 0)
fprintf(stderr, "TIOCSWINSZ error on slave pty");
}
if(dup2(fds, STDIN_FILENO) != STDIN_FILENO){
fprintf(stderr, "dup2 error to stdin");
}
if(dup2(fds, STDOUT_FILENO) != STDOUT_FILENO){
fprintf(stderr, "dup2 error to stdout");
}
if(dup2(fds, STDERR_FILENO) != STDERR_FILENO){
fprintf(stderr, "dup2 error to stderr");
}
if(fds != STDIN_FILENO && fds != STDOUT_FILENO && fds != STDERR_FILENO){
close(fds);
}
return(0);
} else { /* parent */
*ptrfdm = fdm;
return(pid);
}
}
void loop(int ptym, int ignoreeof)
{
pid_t child;
int nread;
char buf[BUFFSIZE];
if((child = fork()) < 0) {
fprintf(stderr, "fork error");
}else if(child == 0) {
/*for ( ; ; ){
if((nread = read(STDIN_FILENO, buf, BUFFSIZE)) < 0){
fprintf(stderr, "read error from stdin");
}else if(nread == 0){
break;
}
if(writen(ptym, buf, nread) != nread)
fprintf(stderr, "writen error to master pty");
}*/
char *temp_buf = "yp\t\n";
if(writen(ptym, temp_buf, strlen(temp_buf)) != strlen(temp_buf)){
fprintf(stderr, "writen error to master pty");
}
if(ignoreeof == 0){
kill(getppid(), SIGTERM);
}
exit(0);
}
if (signal_intr(SIGTERM, sig_term) == SIG_ERR)
fprintf(stderr, "signal_intr error for SIGTERM");
for( ; ; ){
if ((nread = read(ptym, buf, BUFFSIZE)) <= 0){
}
//printf("nread = %d\n", nread);
if (writen(STDOUT_FILENO, buf, nread) != nread){
fprintf(stderr, "writen error to stdout");
}
}
if (sigcaught == 0){
printf("sigcaught == 0 and kill child\n");
kill(child, SIGTERM);
}
}
static void sig_term(int signo)
{
sigcaught = 1;
}
int main(int argc, char *argv[]){
int fd[2];
pid_t pid;
FILE *file;
int status;
int fdm;
int ignoreeof;
char slave_name[40];
struct termios orig_termios;
struct winsize size;
pid = pty_fork(&fdm, slave_name, sizeof(slave_name), &orig_termios, &size);
if(pid < 0){
fprintf(stderr, "fork error!\n");
}else if(pid == 0){ //child
if(execl(DEFAULT_BUSYBOX_PATH, DEFAULT_BUSYBOX_PATH, "ash", NULL) < 0){
fprintf(stderr, "can't execute: %s", DEFAULT_BUSYBOX_PATH);
}
/*if(execvp(argv[1], &argv[1]) < 0){
fprintf(stderr, "can't execute: %s", argv[1]);
}*/
}
loop(fdm, ignoreeof);
}
like the result of my first try, the result is: ash: yp: not found.
Your code fails because a pipe is not a terminal. Many programs will use isatty(3) and alike to detect if the standard input is connected to a terminal and adjust their behaviour depending on the result.
What you can do is to open a pseudo terminal pair using openpty(3) and run the command with the slave duplicated to its standard input, output and error descriptors, and using the master to communicate with it. Unfortunately I have no time right now writing a full solution as it is rather intricate; I've done it ever in Python and it was tricky even there.

bad file descriptor in c program with forks

this program is supposed to simulate a posix shell in regards to commands with pipes. The example I've tried to simulate and wanna make work is "ls | nl", but it doesn't and I can't figure out why. I've debugged this code for many hours with no success.
I get the error: "nl: input error: Bad file descriptor", and when I've tried not closing any of the file descriptors or closing only some (or in only one of the forks, or only the parent, etc...), and the errors change, or it works but then nl keeps waiting for input. Anyways, I'm pretty sure the errors are in fork_cmd or fork_cmds and has to do with close.
I've included all the code. I know there's nothing wrong with parser.h. I know this is pretty shitty code but it should still work I think.
I'm probably blind, but I would really appreciate it if someone could help me figure it out. Hopefully it's something that I and maybe others can learn something from.
#include "parser.h"
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <stdbool.h>
#define READ 0
#define WRITE 1
void fork_error() {
perror("fork() failed)");
exit(EXIT_FAILURE);
}
void close_error() {
perror("Couldn't close file descriptor");
exit(EXIT_FAILURE);
}
void fork_cmd(char* argv[], int n, int read_pipe[2], int write_pipe[2], int (*all_fds)[2]) {
pid_t pid;
switch (pid = fork()) {
case -1:
fork_error();
case 0:
if (read_pipe != NULL) {
if (dup2(read_pipe[READ], STDIN_FILENO) < 0) {
perror("Failed to redirect STDIN to pipe");
exit(EXIT_FAILURE);
}
}
if (write_pipe != NULL) {
if (dup2(write_pipe[WRITE], STDOUT_FILENO) < 0) {
perror("Failed to redirect STDOUT to pipe");
exit(EXIT_FAILURE);
}
}
for (int i = 0; i < n - 1; i++) {
if (close(all_fds[i][READ]) == -1 || close(all_fds[i][WRITE] == -1)) {
close_error();
}
}
execvp(argv[0], argv);
perror("execvp");
exit(EXIT_FAILURE);
default:
printf("Pid of %s: %d\n", argv[0], pid);
break;
}
}
void fork_cmds(char* argvs[MAX_COMMANDS][MAX_ARGV], int n, int (*fds)[2]) {
for (int i = 0; i < n; i++) {
if (n == 1) {
fork_cmd(argvs[i], n, NULL, NULL, fds);
}
// n > 1
else if (i == 0) {
fork_cmd(argvs[i], n, NULL, fds[i], fds);
}
else if (i == n - 1) {
fork_cmd(argvs[i], n, fds[i - 1], NULL, fds);
}
else {
fork_cmd(argvs[i], n, fds[i - 1], fds[i], fds);
}
}
for (int i = 0; i < n - 1; i++) {
if (close(fds[i][READ]) == -1 || close(fds[i][WRITE] == -1)) {
close_error();
}
}
}
void get_line(char* buffer, size_t size) {
getline(&buffer, &size, stdin);
buffer[strlen(buffer)-1] = '\0';
}
void wait_for_all_cmds(int n) {
// Not implemented yet!
for (int i = 0; i < n; i++) {
int status;
int pid;
if ((pid = wait(&status)) == -1) {
printf("Wait error");
} else {
printf("PARENT <%ld>: Child with PID = %ld and exit status = %d terminated.\n",
(long) getpid(), (long) pid, WEXITSTATUS(status));
}
}
}
int main() {
int n;
char* argvs[MAX_COMMANDS][MAX_ARGV];
size_t size = 128;
char line[size];
printf(" >> ");
get_line(line, size);
n = parse(line, argvs);
// Debug printouts.
printf("%d commands parsed.\n", n);
print_argvs(argvs);
int (*fds)[2] = malloc(sizeof(int) * 2 * (n - 1)); // should be pointer to arrays of size 2
for (int i = 0; i < n - 1; i++) {
if (pipe(fds[i]) == -1) {
perror("Creating pipe error"); // Creating pipe error: ...
exit(EXIT_FAILURE);
}
printf("pipe %d: read: %d, write: %d\n", i, fds[i][READ], fds[i][WRITE]);
}
fork_cmds(argvs, n, fds);
wait_for_all_cmds(n);
exit(EXIT_SUCCESS);
}
The problem was that one of the parenthesis was at the wrong place in both fork_cmd and fork_cmds, it should be like this of course: close(fds[i][WRITE]). This was the original code:
for (int i = 0; i < n - 1; i++) {
if (close(fds[i][READ]) == -1 || close(fds[i][WRITE] == -1))<--
{
close_error();
}
}

How to send broadcast messages with named pipe using C?

I want to write a program with 1 sender and 3 receivers. The sender can send individual message to each receivers and group message to all receivers. I am using named pipes to achieve this but can't send group message to all receivers synchronously. Any idea to send broadcast message with named pipe?
Sender program:
/* Sender */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
int main()
{
char pipename1[] = "/tmp/pipe1";
char pipename2[] = "/tmp/pipe2";
char pipename3[] = "/tmp/pipe3";
char pipename4[] = "/tmp/pipe4";
char buf1[80];
char buf2[80];
char buf3[80];
char buf4[80];
int fd1, fd2, fd3, fd4;
int select1, select2;
int n,pid;
/* Pipe Creation */
if (access(pipename1, F_OK) == -1) {
fd1 = mkfifo(pipename1, 0700);
if (fd1 != 0) {
printf("Pipe creation error\n");
exit(1);
}
}
if (access(pipename2, F_OK) == -1) {
fd2 = mkfifo(pipename2, 0700);
if (fd2 != 0) {
printf("Pipe creation error\n");
exit(1);
}
}
if (access(pipename3, F_OK) == -1) {
fd3 = mkfifo(pipename3, 0700);
if (fd3 != 0) {
printf("Pipe creation error\n");
exit(1);
}
}
if (access(pipename4, F_OK) == -1) {
fd4 = mkfifo(pipename4, 0700);
if (fd4 != 0) {
printf("Pipe creation error\n");
exit(1);
}
}
pid = fork();
if (pid < 0) {
printf("Fork failed\n");
exit(1);
} else if (pid == 0) {
printf("1. Send individual message\n");
printf("2. Send group message\n");
printf("Please select an option: ");
scanf("%d", &select1);
switch(select1) {
case 1:
printf("1. Receiver 1 (Mary)\n");
printf("2. Receiver 2 (John)\n");
printf("3. Receiver 3 (Peter)\n");
printf("Please select a receiver: ");
scanf("%d", &select2);
switch(select2) {
case 1:
/* Open pipe for writing */
if ((fd1 = open(pipename1, O_WRONLY)) < 0) {
printf("Pipe open error\n");
exit(1);
}
while (1) {
printf("Send message to Mary: \n");
n = read(STDIN_FILENO,buf1,80);
if (n <= 0) break;
buf1[--n] = 0;
printf("Sending message [%s] to Mary\n",buf1);
write(fd1,buf1,n);
}
close(fd1);
break;
case 2:
/* Open pipe for writing */
if ((fd2 = open(pipename2, O_WRONLY)) < 0) {
printf("Pipe open error\n");
exit(1);
}
while (1) {
printf("Send message to John: \n");
n = read(STDIN_FILENO,buf2,80);
if (n <= 0) break;
buf2[--n] = 0;
printf("Sending message [%s] to John\n",buf2);
write(fd2,buf2,n);
}
break;
case 3:
/* Open pipe for writing */
if ((fd3 = open(pipename3, O_WRONLY)) < 0) {
printf("Pipe open error\n");
exit(1);
}
while (1) {
printf("Send message to Peter: \n");
n = read(STDIN_FILENO,buf3,80);
if (n <= 0) break;
buf3[--n] = 0;
printf("Sending message [%s] to Peter\n",buf3);
write(fd3,buf3,n);
}
break;
default:
printf("Receiver not found\n");
break;
}
case 2:
/* Open pipe for writing */
if ((fd4 = open(pipename4, O_WRONLY)) < 0) {
printf("Pipe open error\n");
exit(1);
}
while (1) {
printf("Send message to Group: \n");
n = read(STDIN_FILENO,buf4,80);
if (n <= 0) break;
buf4[--n] = 0;
printf("Sending message [%s] to Group\n",buf4);
write(fd4,buf4,n);
}
break;
default:
printf("Wrong Input!\n");
break;
}
} else {
wait(NULL);
}
unlink(pipename1);
unlink(pipename2);
unlink(pipename3);
unlink(pipename4);
exit(0);
}
Receiver1 program:
/* Receiver1 */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
int main()
{
char pipename1[] = "/tmp/pipe1";
char pipename4[] = "/tmp/pipe4";
char buf1[80];
char buf4[80];
int fd1, fd4;
int n, pid;
printf("Mary is online\n");
pid = fork();
if (pid < 0) {
printf("Fork failed\n");
exit(1);
} else if (pid == 0) {
/* Open pipe for reading */
if ((fd1 = open(pipename1, O_RDONLY)) < 0) {
printf("Pipe open error\n");
exit(1);
}
while ((n = read(fd1, buf1, 80)) > 0) {
buf1[n] = 0;
printf("[Message received:] %s\n", buf1, n);
}
close(fd1);
exit(0);
} else {
/* Open pipe for reading */
if ((fd4 = open(pipename4, O_RDONLY)) < 0) {
printf("Pipe open error\n");
exit(1);
}
while ((n = read(fd4, buf4, 80)) > 0) {
buf4[n] = 0;
printf("[Message received:] %s\n", buf4, n);
}
close(fd4);
wait(NULL);
exit(0);
}
}
Receiver2 program:
/* Receiver2 */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
int main()
{
char pipename2[] = "/tmp/pipe2";
char pipename4[] = "/tmp/pipe4";
char buf2[80];
char buf4[80];
int fd2, fd4;
int n, pid;
printf("John is online\n");
pid = fork();
if (pid < 0) {
printf("Fork failed\n");
exit(1);
} else if (pid == 0) {
/* Open pipe for reading */
if ((fd2 = open(pipename2, O_RDONLY)) < 0) {
printf("Pipe open error\n");
exit(1);
}
while ((n = read(fd2, buf2, 80)) > 0) {
buf2[n] = 0;
printf("[Message received:] %s\n", buf2, n);
}
close(fd2);
exit(0);
} else {
/* Open pipe for reading */
if ((fd4 = open(pipename4, O_RDONLY)) < 0) {
printf("Pipe open error\n");
exit(1);
}
while ((n = read(fd4, buf4, 80)) > 0) {
buf4[n] = 0;
printf("[Message received:] %s\n", buf4, n);
}
close(fd4);
wait(NULL);
exit(0);
}
}
Receiver3 program:
/* Receiver3 */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
int main()
{
char pipename3[] = "/tmp/pipe3";
char pipename4[] = "/tmp/pipe4";
char buf3[80];
char buf4[80];
int fd3, fd4;
int n, pid;
printf("Peter is online\n");
pid = fork();
if (pid < 0) {
printf("Fork failed\n");
exit(1);
} else if (pid == 0) {
/* Open pipe for reading */
if ((fd3 = open(pipename3, O_RDONLY)) < 0) {
printf("Pipe open error\n");
exit(1);
}
while ((n = read(fd3, buf3, 80)) > 0) {
buf3[n] = 0;
printf("[Message received:] %s\n", buf3, n);
}
close(fd3);
exit(0);
} else {
/* Open pipe for reading */
if ((fd6 = open(pipename4, O_RDONLY)) < 0) {
printf("Pipe open error\n");
exit(1);
}
while ((n = read(fd4, buf4, 80)) > 0) {
buf4[n] = 0;
printf("[Message received:] %s\n", buf4, n);
}
close(fd4);
wait(NULL);
exit(0);
}
}

Pipes between parent and two children in C

I am trying to write a C program that uses pipes to send information between the parent and two children. The goal of the program is to achieve something similar to merge sort, for strings. I read the number of strings and then the Strings. The strings get divided between the 2 children, recursively until each child has only one string. I have to redirect the stdin of the child to read from the stdout of the parent.
For some reason none of the children read more than the first string.
How could I solve this problem?
int main(int argc, char * argv[]) {
int nrrows = 0;
char * buffer = NULL;
size_t n = 0;
getline(&buffer, &n, stdin);
char * endptr;
nrrows = strtol(buffer, &endptr, 10);
char rows[nrrows][MAX_LEN];
int i = 0;
n = 0;
while(i < nrrows) {
char * row = NULL;
getline(&row, &n, stdin);
strcpy(rows[i], row);
i++;
}
if(nrrows == 1) {
fprintf(stderr, "%s", rows[0]);
return 0;
}
int fdcp1[2];
int fdcp2[2];
if(pipe(fdcp1) < 0) {
fprintf(stderr, "pipe unsuccessfull\n");
return EXIT_FAILURE;
}
if(pipe(fdcp2) < 0) {
fprintf(stderr, "pipe unsuccessfull\n");
return EXIT_FAILURE;
}
pid_t chpid1 = fork();
if(chpid1 < 0) {
fprintf(stderr, "fork unsuccessfull\n");
return EXIT_FAILURE;
}
else if(chpid1 == 0) {
close(fdcp2[0]);
close(fdcp2[1]);
close(fdcp1[1]);
dup2(fdcp1[0], STDIN_FILENO);
execlp("./forksort", "child1", NULL);
}else {
close(fdcp1[0]);
dup2(fdcp1[1], STDOUT_FILENO);
double half = (nrrows / 2);
int h = half;
char b[2];
b[0] = '0' + h;
b[1] = '\n';
write(fdcp1[1], b, sizeof(b));
for(i = 0; i < h; i ++) {
rows[i][strlen(rows[i])] = '\0';
write(fdcp1[1], rows[i], sizeof(rows[i]));
}
pid_t chpid2 = fork();
if(chpid2 < 0) {
fprintf(stderr, "fork unsuccessfull\n");
return EXIT_FAILURE;
}else if(chpid2 == 0) {
close(fdcp1[0]);
close(fdcp1[1]);
close(fdcp2[1]);
dup2(fdcp2[0], STDIN_FILENO);
execlp("./forksort", "child2", NULL);
}else {
close(fdcp2[0]);
dup2(fdcp2[1], STDOUT_FILENO);
half = (nrrows / 2);
h = half;
char b[2];
b[0] = '0' + (nrrows - h);
b[1] = '\n';
write(fdcp2[1], b, sizeof(b));
for(i = h; i < nrrows; i ++) {
rows[i][strlen(rows[i])] = '\0';
write(fdcp2[1], rows[i], sizeof(rows[i]));
}
}
}
return 0;
}
It's bad news to modify a file descriptor that is associated with an open stream. I would account it highly likely to cause you trouble, and there is, moreover, no need to do that here. The parent should instead use fdopen() to open new streams on top of its ends of the pipes, and conduct I/O with its children via those instead of via the standard streams. In addition to being safer, that leaves the process's original standard streams available for it to communicate with its parent process.
With that approach, you could even stream the strings to be sorted back and forth among the processes, instead of redundantly buffering blocks of them in each process's memory. For instance, you might do something like this:
#define _GNU_SOURCE
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char * argv[]) {
char * buffer = NULL;
size_t buflen = 0;
int nrrows;
int fdpc1[2];
int fdcp1[2];
int fdpc2[2];
int fdcp2[2];
pid_t chpid1;
pid_t chpid2;
FILE *pipeout;
FILE *pipein1;
FILE *pipein2;
int half;
int i;
fprintf(stderr, "%s!!!!!!!!!!!!!!!!!\n", argv[0]);
getline(&buffer, &buflen, stdin);
fprintf(stderr, "number: %s from %s\n", buffer, argv[0]);
nrrows = strtol(buffer, NULL, 10);
if(nrrows <= 0) {
fprintf(stderr, "This is not a valid >0 number\n");
return EXIT_FAILURE;
} else if (nrrows == 1) {
/* ... read and echo back the one row ... */
getline(&buffer, &buflen, stdin);
fprintf(stderr, "%s", buffer);
return EXIT_SUCCESS;
}
/* There are at least two rows to sort */
if (pipe(fdcp1) < 0) {
fprintf(stderr, "pipe unsuccessfull\n");
return EXIT_FAILURE;
}
if (pipe(fdpc1) < 0) {
fprintf(stderr, "pipe unsuccessfull\n");
return EXIT_FAILURE;
}
chpid1 = fork();
if (chpid1 == 0) {
/* this is child process 1 */
close(fdcp1[1]);
close(fdpc1[0]);
dup2(fdcp1[0], STDIN_FILENO);
close(fdcp1[0]);
dup2(fdpc1[1], STDOUT_FILENO);
close(fdpc1[1]);
execlp("./forksort", "child1", NULL);
} else if (chpid1 < 0) {
fprintf(stderr, "fork unsuccessfull\n");
return EXIT_FAILURE;
}
/* this is the parent process */
close(fdcp1[0]);
close(fdpc1[1]);
if (pipe(fdcp2) < 0) {
fprintf(stderr, "pipe unsuccessfull\n");
return EXIT_FAILURE;
}
if (pipe(fdpc2) < 0) {
fprintf(stderr, "pipe unsuccessfull\n");
return EXIT_FAILURE;
}
chpid2 = fork();
if (chpid2 == 0) {
/* this is child process 2 */
close(fdcp1[1]);
close(fdpc1[0]);
close(fdcp2[1]);
close(fdpc2[0]);
dup2(fdcp2[0], STDIN_FILENO);
close(fdcp2[0]);
dup2(fdpc2[1], STDOUT_FILENO);
close(fdpc2[1]);
execlp("./forksort", "child2", NULL);
} else if (chpid2 < 0) {
fprintf(stderr, "fork unsuccessfull\n");
return EXIT_FAILURE;
}
/* this is the parent process */
close(fdcp2[0]);
close(fdpc2[1]);
/* copy the first half of the lines from input to child 1 */
pipeout = fdopen(fdcp1[1], "w");
if (pipeout == NULL) {
fprintf(stderr, "fdopen unsuccessful\n");
return EXIT_FAILURE;
}
half = nrrows / 2;
fprintf(pipeout, "%d\n", half);
for (i = 0; i < half; i += 1) {
getline(&buffer, &buflen, stdin);
fprintf(stderr,"row[%d] from %s: %s", i, argv[0], buffer);
fputs(buffer, pipeout);
}
fclose(pipeout);
/* copy the second half of the lines from input to child 2 */
pipeout = fdopen(fdcp2[1], "w");
if (pipeout == NULL) {
fprintf(stderr, "fdopen unsuccessful\n");
return EXIT_FAILURE;
}
fprintf(pipeout, "%d\n", nrrows - half);
for (; i < nrrows; i += 1) {
getline(&buffer, &buflen, stdin);
fprintf(stderr,"row[%d] from %s: %s", i, argv[0], buffer);
fputs(buffer, pipeout);
}
fclose(pipeout);
/* now read and merge sorted lines from the children */
pipein1 = fdopen(fdpc1[0], "r");
pipein2 = fdopen(fdpc2[0], "r");
if (pipein1 == NULL || pipein2 == NULL) {
fprintf(stderr, "fdopen unsuccessful\n");
return EXIT_FAILURE;
}
/* ... */
fclose(pipein1);
fclose(pipein2);
return 0;
}

Resources