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);
}
}
Related
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.
So I have created a simple shell program which takes input from the user through cmd, and then creates a new task and executes the input as a command. For example:
ls -l /home/user/Downloads | sort –u > listing.txt
But my program can only handle one piping. Can I somehow make it work with no limitations to pipings?? For example:
ls -l /home/user/Downloads | sort –u | wc –l > count.txt
So far I have been unable to implement something like that.
Here is my code:
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <string.h>
#include <stdlib.h>
int main(void)
{
pid_t pid1,pid2,waitPid;
int status,n,j,k,in,out,pdes[2],current_out,current_in,returnValue;
char *token;
char array[256];
char *temp[256];
char *temp2[256];
current_out=dup(1);
current_in=dup(0);
for(;;)
{
if(dup2(current_out,1) < 0)
{
printf("Error restoring output");
}
if(dup2(current_in, 0) < 0)
{
printf("Error restoring input");
}
int i=0;
k=0;
printf("mysh4>");
int check = scanf("%d",&n);
if(check == EOF)
{
printf("You now exit.\n");
break;
}
else
{
fgets(array,sizeof(array),stdin);
array[strcspn(array,"\n")] = 0;
token = strtok(array, " ");
while(token != NULL)
{
temp[i] = token;
token = strtok(NULL, " ");
i++;
k++;
}
temp[i]= NULL;
int p=0;
int qq;
for(i=0;i<k;i++)
{
if(strcmp(temp[i],"|") == 0)
{
for(qq=i+1; qq<k; qq++)
{
temp2[p]=temp[qq];
temp[qq-1] = NULL;
printf("Temp2[%d] is: %s\n" , p , temp2[p]);
printf("Temp1[%d] is: %s\n" , p , temp[p]);
p++;
}
temp2[p]= NULL;
}
}
returnValue=pipe(pdes);
if (returnValue == -1)
{
printf("ERROR: Pipe command failed.\n");
return -1;
}
close(pdes[0]);
close(pdes[1]);
pid1 = fork();
if (pid1 < 0)
{
perror("ERROR: Fork failed.\n");
return -1;
}
//child process
if (pid1 == 0)
{
close(pdes[0]);
dup2(pdes[1],1);
printf("Array:%s", array);
execvp(array,temp);
printf("Invalid Code\n");
return -1;
}
pid2 = fork();
if(pid2 < 0)
{
perror("Error: Fork2 failed.\n");
return -1;
}
//child process 2
if(pid2 == 0)
{
close(pdes[1]);
dup2(pdes[0],0);
for(j=0;j<k;j++)
{
if(strcmp(temp[j],">") == 0)
{
out=j+1;
printf("Output is %s\n", temp[out]);
temp[j] = temp[out];
pdes[1]=open(temp[out],
O_APPEND | O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
if(dup2(pdes[1],1) < 0)
{
printf("error in dup2");
return -1;
}
}
}
execvp(temp2[0],temp2);
printf("Invalid Code 2\n");
return -1;
}
//father process
else
{
//father waits child process to finish
waitPid = wait(NULL);
if (waitPid == -1)
{
perror("ERROR: Waitpid failed.\n");
return -1;
}
}
}
}
return 0;
}
I found the same question but there was no answer.
In building my own unix shell, my output redirection is working fine, but when I try the input it does not do anything. If you could help me figure out the problem that would be great.
This is my exec function code:
void execute (char **args)
{
int pid, status;
pid = fork ();
if (pid < 0)
{
perror ("Error forking!");
return;
}
else if (pid > 0)
{
fflush(0);
while (wait (&status) != pid)
continue;
}
else if (pid == 0)
{
int i,in=0,out=0;
char input[BUF_SIZE],output[BUF_SIZE];
for(i=0;args[i]!=NULL;i++)
{
if(strcmp(args[i],"<")==0)
{
args[i]=NULL;
strcpy(input,args[i+1]);
in=2;
}
if(strcmp(args[i],">")==0)
{
args[i]=NULL;
strcpy(output,args[i+1]);
out=2;
}
}
if(in)
{
int fd0;
if ((fd0 = open(input, O_RDONLY, 0)) < 0)
{
perror("Couldn't open input file");
exit(0);
}
dup2(fd0, 0);
close(fd0);
}
if (out)
{
int fd1;
if ((fd1 = creat(output , 0644)) < 0)
{
perror("Couldn't open the output file");
exit(0);
}
dup2(fd1, 1);
close(fd1);
}
execvp (*args, args);
perror("execvp");
_exit(1);
}
Here is my whole code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#define ARGSIZE 20
#define BUF_SIZE 1024
void execute (char **args);
void cd (char *directory);
int killpid (char *pitstr, int sig);
int main (void)
{
char line[BUF_SIZE] = {0};
char *args[ARGSIZE] = {NULL};
char *token;
int i, argIndex = 0;
while (1)
{
argIndex = 0;
for (i = 0; i < ARGSIZE; i++)
args[i] = NULL;
printf ("shell> ");
if (fgets (line, BUF_SIZE, stdin) == NULL)
{
printf ("EOF received\n");
return 0;
}
if (*line == '\n')
continue;
token = strtok (line, " \n");
while (token != NULL)
{
args[argIndex] = token;
token = strtok (NULL, " \n");
argIndex++;
}
if (!argIndex)
continue;
if (strcmp (args[0], "quit") == 0 || strcmp (args[0], "exit") == 0)
break;
if ((strcmp (args[0], "cd") == 0))
cd (args[1]);
else if ((strcmp (args[0], "kill") == 0))
{
if (args[1])
killpid (args[1], SIGTERM);
}
else
execute (args);
}
return 0;
}
void execute (char **args)
{
int pid, status;
pid = fork ();
if (pid < 0)
{
perror ("Error forking!");
return;
}
else if (pid > 0)
{
fflush(0);
while (wait (&status) != pid)
continue;
}
else if (pid == 0)
{
int i,in=0,out=0;
char input[BUF_SIZE],output[BUF_SIZE];
for(i=0;args[i]!=NULL;i++)
{
if(strcmp(args[i],"<")==0)
{
args[i]=NULL;
strcpy(input,args[i+1]);
in=2;
}
if(strcmp(args[i],">")==0)
{
args[i]=NULL;
strcpy(output,args[i+1]);
out=2;
}
}
if(in)
{
int fd0;
if ((fd0 = open(input, O_RDONLY, 0)) < 0)
{
perror("Couldn't open input file");
exit(0);
}
dup2(fd0, 0);
close(fd0);
}
if (out)
{
int fd1;
if ((fd1 = creat(output , 0644)) < 0)
{
perror("Couldn't open the output file");
exit(0);
}
dup2(fd1, 1);
close(fd1);
}
execvp (*args, args);
perror("execvp");
_exit(1);
}
}
void cd (char *directory)
{
char dir[BUF_SIZE] = {0};
if (!directory)
{
directory = getenv ("HOME");
if (chdir (directory))
fprintf (stderr, "Failed to enter directory: %s\n", directory);
else
printf ("%s\n", directory);
return;
}
if (*directory == '~')
{
strcpy (dir, getenv ("HOME"));
strcat (dir, "/");
strcat (dir, directory + 2);
if (chdir (dir))
fprintf (stderr, "Failed to enter directory: %s\n", dir);
else
printf ("%s\n", dir);
return;
}
if (chdir (directory))
fprintf (stderr, "Failed to enter directory: %s\n", directory);
else
printf ("%s\n", directory);
}
int killpid (char *pidstr, int sig)
{
pid_t pid = (pid_t)atoi (pidstr);
if (pid < 1)
{
fprintf (stderr, "warning: requested pid < 1, ignoring\n");
return (int)pid;
}
printf (" killing pid '%d' with signal '%d'\n", (int)pid, sig);
return 0;
}
When you see a < in the args array here
if(strcmp(args[i],"<")==0)
you set args[i] to NULL
args[i]=NULL;
But then, you pass it to strcmp()
if(strcmp(args[i],">")==0)
and your child process will happily segfault. Use an if-else-construct here:
if(strcmp(args[i],"<")==0) {
args[i]=NULL;
strcpy(input,args[i+1]);
in=2;
} else if(strcmp(args[i],">")==0) {
args[i]=NULL;
strcpy(output,args[i+1]);
out=2;
}
This should fix the error.
Furthermore, this might come in handy to detect such situations:
...
while (wait (&status) != pid)
continue;
if (WIFSIGNALED(status))
printf("Killed by signal %d%s\n",
WTERMSIG(status), WCOREDUMP(status)?" (Core dumped)":"");
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;
}
Could anyone tell me what's wrong with this code?
In summary, it creates input and output pipes and fork-exec's the sort program. The parent reads the dictionary /usr/share/dict/words and writes it to the pipe that is dup2()'d to sort's standard in and, likewise, reads the output from it, printing it to the terminal (the standard output of the parent). Or, at least, that's what's supposed to be happening.
A backtrace says that the parent hangs at the read() on line 130 (marked with the comment 'XXX'). It's almost as though sort isn't aware of the end-of-file, but closing the write end of pipeIn should 'signal' this, right?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc, char** argv)
{
int pipeIn[2];
int pipeOut[2];
if ((pipe(pipeIn)) == -1)
{
perror("pipe");
exit(EXIT_FAILURE);
}
if ((pipe(pipeOut)) == -1)
{
perror("pipe");
exit(EXIT_FAILURE);
}
pid_t child = fork();
if (child == 0)
{
// This is child!
if ((dup2(pipeIn[0], STDIN_FILENO)) == -1)
{
perror("dup2");
exit(EXIT_FAILURE);
}
if ((dup2(pipeOut[1], STDOUT_FILENO)) == -1)
{
perror("dup2");
exit(EXIT_FAILURE);
}
if ((dup2(pipeOut[1], STDERR_FILENO)) == -1)
{
perror("dup2");
exit(EXIT_FAILURE);
}
if ((close(pipeIn[0])) == -1)
{
perror("close");
exit(EXIT_FAILURE);
}
if ((close(pipeOut[1])) == -1)
{
perror("close");
exit(EXIT_FAILURE);
}
if ((execlp("sort", "-r", NULL)) == -1)
{
perror("execlp");
exit(EXIT_FAILURE);
}
}
else if (child == -1)
{
perror("fork");
exit(EXIT_FAILURE);
}
else
{
// This is parent!
if ((close(pipeIn[0])) == -1)
{
perror("close");
exit(EXIT_FAILURE);
}
if ((close(pipeOut[1])) == -1)
{
perror("close");
exit(EXIT_FAILURE);
}
int dict = open("/usr/share/dict/words", O_RDONLY);
if (dict == -1)
{
perror("open");
exit(EXIT_FAILURE);
}
char buf[1024];
int count;
while ((count = read(dict, buf, sizeof(char) * 1024)) > 0)
{
putchar('.');
if ((write(pipeIn[1], buf, count)) == -1)
{
perror("write 1");
exit(EXIT_FAILURE);
}
}
if (count == -1)
{
perror("read");
exit(EXIT_FAILURE);
}
if ((close(dict)) == -1)
{
perror("close");
exit(EXIT_FAILURE);
}
if ((close(pipeIn[1])) == -1)
{
perror("close");
exit(EXIT_FAILURE);
}
while ((count = read(pipeOut[0], buf, sizeof(char) * 1024)) > 0) // XXX
{
putchar('!');
if ((write(STDOUT_FILENO, buf, count)) == -1)
{
perror("write 2");
exit(EXIT_FAILURE);
}
}
if (count == -1)
{
perror("read");
exit(EXIT_FAILURE);
}
if ((close(pipeOut[0])) == -1)
{
perror("close");
exit(EXIT_FAILURE);
}
}
return EXIT_SUCCESS;
}
Thank you for any input (pardon the pun).
Your problem is that you are not closing the unused ends of your pipe in the chile process. So you need to add the following code somewhere before the exec
if ((close(pipeIn[1])) == -1)
{
perror("close");
exit(EXIT_FAILURE);
}
if ((close(pipeOut[0])) == -1)
{
perror("close");
exit(EXIT_FAILURE);
}