Close pipe end error - c

I have a program that forks a child and want it to communicate with its parent. However, I seem to get an error when closing the write end in the child.
The program stops inside the child and in the if (close(pfd1[1]) == -1)
Apparently it fails when the child wants to close the write end. Why?
/* Note: working under the assumption that the messages are of equal length */
int main(int argc, char * argv[])
{
int pfd1[2];
char buf[BUF_SIZE];
//checks pipefd1
if (pipe(pfd1) == -1)
{
printf("Error opening pipe 1!\n");
exit(1);
}
printf("Pipe opened with success. Forking ...\n");
// child 1
switch (fork())
{
case -1:
printf("Error forking child 1!\n");
exit(1);
case 0:
printf("\nChild 1 executing...\n");
/* close writing end of first pipe */
if (close(pfd1[1]) == -1)
{
printf("Error closing writing end of pipe 1.\n");
_exit(1);
}
/* read from pipe 1 */
if (read(pfd1[0], buf, 2000))
{
printf("Error reading to pipe 1.\n");
_exit(1);
}
/* close reading end of first pipe */
if (close(pfd1[1]) == -1)
{
printf("Error closing writing end of pipe 1.\n");
_exit(1);
}
printf("Message received child ONE: %s", buf);
printf("Exiting child 1...\n");
_exit(0);
default: //parent breaks just out
break;
}
printf("inside parent\n");
int child = 1;
char *message = "Hey child1, this is your parent speaking";
if(child == 1)
{
//close read end of pipe
if(close(pfd1[0]) == -1)
{
printf("Error closing reading end of the pipe.\n");
exit(EXIT_FAILURE);
}
printf("Parent closed read end of pipe1\n");
//read end is closed, now write to child
if(write(pfd1[1], message, strlen(message)))
{
printf("Error writing to the pipe.");
_exit(EXIT_FAILURE);
}
printf("Writing to child1 succeeded\n");
}
if (wait(NULL) == -1)
{
printf("Error waiting.\n");
exit(EXIT_FAILURE);
}
if (wait(NULL) == -1)
{
printf("Error waiting.\n");
exit(EXIT_FAILURE);
}
printf("Parent finishing.\n");
exit(EXIT_SUCCESS);
}

First of all, in the child's case you attempt to close the writing end of the pipe twice. I guess the second call to close(2) was meant to close the reading end, as mentioned in the comment above it:
/* close reading end of first pipe */
if (close(pfd1[0]) == -1)
{
printf("Error closing writing end of pipe 1.\n");
_exit(1);
}
Besides that, note that both read(2) and write(2) return the number of bytes that were actually read or written; in the case of error the return value is -1, so your error-checking conditions there should be fixed too, to something like:
/* read from pipe 1 */
if (read(pfd1[0], buf, 2000) < 0) {
printf("Error reading to pipe 1.\n");
_exit(1);
}
and
//read end is closed, now write to child
if(write(pfd1[1], message, strlen(message)) < 0) {
printf("Error writing to the pipe.");
_exit(EXIT_FAILURE);
}

On the principle of teaching to fish, a good technique to diagnose problems like this is to check what the error was and print a more informative message. Here is a technique I frequently put into a header file and use:
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* This declaration and macro would really go into a header file: */
void fatal_error_helper( const char* msg, const char* sourcefile, int lineno, const char* syserr );
#define fatal_system_error(m) \
fatal_error_helper( (m), __FILE__, __LINE__, strerror(errno) )
/* This function definition would really go into a .c file: */
void fatal_error_helper( const char* const msg,
const char* const sourcefile,
const int lineno,
const char * const syserr )
{
fflush(stdout); /* Don't cross the streams! */
fprintf( stderr,
"%s at %s:%d: %s. Program terminated.\n",
msg, sourcefile, lineno, syserr
);
exit(EXIT_FAILURE);
}
/* Test driver: */
FILE* fails_to_open_file( const char* filename )
/* Returns a FILE* to an open file. If the operation fails, prints an
* error message and terminates the program.
*/
{
/* Do this in general before calling the function whose error value
* you check. Otherwise, you might report the wrong error message
* from an earlier call and really confuse someone.
*/
errno = 0;
FILE* result = NULL;
result = fopen(filename, ""); /* Fails. */
if (!result)
fatal_system_error("Failed to open file");
return result;
}
int main(void)
{
fails_to_open_file("nonexistent.file");
return EXIT_SUCCESS;
}
This gives an error message such as: Failed to open file at prog.c:26: Invalid argument. Program terminated.

Related

write operation on pipe is always failing

I'm a bit new to pipes and concurrency, and have been frustrated with this problem for hours. I am struggling to understand why this write operation is constantly failing on my pipe. I am trying to have the child process write data through a pipe that will be received by the parent process. My current code is this:
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#define MAXSIZE 4096
int main() {
pid_t status;
int fd[2]; //The array of file descriptors
if (pipe(fd) == -1) {
printf("Error piping");
}
status = fork(); //Begin the fork process
switch (status) {
case -1:
perror("Error forking");
break;
case 0:
//Child process
close(fd[0]); //Only send data
char some_string[15] = "hi there";
if (write(fd[1], some_string, MAXSIZE) == -1) {
printf("Error writing to the pipe");
}
close(fd[1]); //Close write end
exit(1);
default:
close(fd[1]); //Only receive data
char readed[500] = "";
while(read(fd[0], readed, MAXSIZE) != 0) {
printf("read this %s\n", readed);
}
printf("Done reading");
close(fd[0]);
break;
}
return 1;
}
However, I constantly get the message "Error writing to pipe", meaning that the write operation has failed in the child process. Another interesting thing is that if I change some_string to a string literal instead, this code works fine with the exception that it never terminates and instead, the read operation in the parent process reads from STDIN! I don't understand why this could be happening, is it possible that we have a zombie child when parent executes so the pipe is "dead"? Or perhaps that the parent process terminates and we have an orphaned child? How can I avoid this and how does this explain the weird behaviour from the string literal instead? Any insights?
You told write() to read the data from out-of-range of the array and allowed read() to write the data read to out-of-range of the array. That is very bad.
Write only valid data and limit the length to read not to cause out-of-range access.
Try this:
#include <unistd.h>
#include <sys/types.h> /* add this to use pid_t */
#include <sys/wait.h> /* add this to use wait() */
#include <stdio.h>
#include <stdlib.h>
/* remove unused MAXSIZE */
int main() {
pid_t status;
int fd[2]; //The array of file descriptors
int st; /* variable for receiving the status */
if (pipe(fd) == -1) {
printf("Error piping");
return 1; /* return 1 when the execution failed */
}
status = fork(); //Begin the fork process
switch (status) {
case -1:
perror("Error forking");
return 1; /* return 1 when the execution failed */
break;
case 0:
//Child process
close(fd[0]); //Only send data
char some_string[15] = "hi there";
if (write(fd[1], some_string, sizeof(some_string)) == -1) {
printf("Error writing to the pipe");
}
close(fd[1]); //Close write end
exit(0); /* return 0 if the execution finished successfully */
default:
close(fd[1]); //Only receive data
char readed[500] = "";
while(read(fd[0], readed, sizeof(readed) - 1) != 0) { /* -1 for reserving space for terminating null-character */
printf("read this %s\n", readed);
}
printf("Done reading");
close(fd[0]);
wait(&st); /* wait for the child process to exit and release the data of the process */
break;
}
return 0; /* return 0 if the execution finished successfully */
}

c dup undefined error

I'm trying to create two child processes:
One child reads its input from a file, which is passed in as an argument, and writes output to the pipe.
The other child reads its output from the pipe and writes its output to a file, also passed in as an argument.
The parent sets up some of the file descriptors for the children and when the children are created they finish manipulating the descriptors to their needs.
However, I'm having an issue with setting up the file descriptors, specifically, when I try to close and dup the input file descriptor to take the place of stdin.
Here's all my code:
#include <sys/types.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
int main(int argc, char *argv[])
{
//Open input file
int fdin;
fdin = open(argv[1], O_RDONLY);
//Check if opening the file was successful
if(fdin > 0)
{
//Open output file
int fdout;
//Create the output file and
//check if the output file was created
if((fdout = creat(argv[2], 0644)) > 0)
{
//Captures whether the dup's were successful
int dup_in;
int dup_out;
//Close stdin so we can replace it with our input file
close(0);
//Attempt to put the input file at position 0
//and check if the dup was successful
if((dup_in = dup(fdin)) > 0)
{
//Close stdout so we can replace it with our output file
close(1);
//Attempt to put the output file at position 1
//and check if the dup was successful
if((dup_out = dup(fdout)) > 0)
{
//Pipe success
int pipecreate;
//Pipe file descriptors
int pipe_fd[2];
//Make the pipe and check
//if it was successful
if((pipecreate = pipe(pipe_fd)) > 0)
{
//close unneeded file descriptors
close(fdin);
close(fdout);
//Process id for first child
int cpid1;
//Create first child process
cpid1 = fork();
//Process creation successful, child block
if(cpid1 == 0)
{
//Read pipe dup success
int rpipe_dup;
//close f_in
close(0);
rpipe_dup = dup(pipe_fd[0]);
//Dup successful
if(rpipe_dup > 0)
{
//close uneeded file descriptors
close(pipe_fd[0]);
close(pipe_fd[1]);
execl("count", "count", (char *) 0);
char readbuf[100] = {0};
read(2, readbuf, 100);
}
//Dup failed
else
{
fprintf(stderr, "Read pipe dup failed.\n");
exit(EXIT_FAILURE);
}
}
//Process creation successful, parent block
else if(cpid1 > 0)
{
//Process id for second child
int cpid2;
//Create second child process
cpid2 = fork();
//Process creation successful, child block
if(cpid2 == 0)
{
//Write pipe dup success
int wpipe_dup;
//close f_out
close(1);
wpipe_dup = dup(pipe_fd[1]);
//Dup successful
if(wpipe_dup > 0)
{
//close uneeded file descriptors
close(pipe_fd[0]);
close(pipe_fd[1]);
execl("convert", "convert", (char *) 0);
char readbuf[100] = {0};
write(1, readbuf, 100);
}
//Dup failed
else
{
fprintf(stderr, "Write pipe dup failed.\n");
exit(EXIT_FAILURE);
}
}
//Process creation successful, parent block
else if(cpid2 > 0)
{
//Close unneeded file descriptors
close(pipe_fd[0]);
close(pipe_fd[1]);
int pid;
int status;
pid = wait(&status);
pid = wait(&status);
}
//Dup failed
else
{
fprintf(stderr, "Error creating child process 2.\n");
exit(EXIT_FAILURE);
}
}
//Process creation unsuccessful
else
{
fprintf(stderr, "Error creating child process 1.\n");
exit(EXIT_FAILURE);
}
}
//Pipe creation failed
else
{
fprintf(stderr, "Error creating pipe.\n");
exit(EXIT_FAILURE);
}
}
//Dup'ing the output file descriptor failed
else
{
fprintf(stderr, "Error dup'ing out file descriptor.\n");
exit(EXIT_FAILURE);
}
}
//Dup'ing the input file descriptor failed
else
{
//fprintf(stderr, "Error dup'ing in file descriptor.\n
perror("\nError dup'ing in file descriptor: ");
exit(EXIT_FAILURE);
}
}
//Creat failed
else
{
fprintf(stderr, "Error creating out file.\n");
exit(EXIT_FAILURE);
}
}
//Opening input file failed
else
{
fprintf(stderr, "Error opening in file.\n");
exit(EXIT_FAILURE);
}
}
I'm compiling and running this on MINIX3.
Here's how I run it, including program output:
cc fork.c
./a.out input.txt output.txt
Error dup'ing in file descriptor: : Undefined error: 0
Any help is greatly appreciated.
I found out why.
dup returns the file descriptor number, which, in this case, is 0. if ((dup_in = dup(fdin)) > 0) should be if ((dup_in >= dup(fdin))).

how to store output from "bc" into a variable?

What this program is supposed to do is ask user a simple arithmetic question, e.g. 5 + 7 and then check the answer with "bc" (whether it's correct).
I have the following code, but I don't understand how to edit it to to store the result from "5 + 7" into a variable (currently the result goes into STDOUT).
Any help is welcome.
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
int main( int argc, char *argv[], char *env[] )
{
char *expr = "5 + 7\n";
int answer;
printf("%s = ", expr);
scanf("%d", &answer);
int pfds[2];
pipe(pfds);
if (!fork()) {
close(1); /* close normal stdout */
dup(pfds[1]); /* make stdout same as pfds[1] */
close(pfds[0]); /* we don't need this */
printf("%s\n", expr);
/***********************/
/* How to store printf()'s output into a variable? */
exit(0);
} else {
close(0); /* close normal stdin */
dup(pfds[0]); /* make stdin same as pfds[0] */
close(pfds[1]); /* we don't need this */
execlp("bc", "bc", NULL);
}
return 0;
}
You need to create a second pipe and redirect stdout to it in the child process.
You can simply read STOUD or read on your pipe's Output. Then a call to Read() and to Atoi might do the work. Atoi man page here
I can't code it for you, but here the logic
`int fds[2];
pipe(fds);
dup2(fds[1], stdout);
read(fds[1], buf, buf_sz);
int ResultOfbc = Atoi(buf)`
Ooof oof, this stuff I remember doing last year.
I was basically a program which used my own telnet clone to communicate with the internet.
Problem is telnet used stdin and stdout to work.
It looks like you have the same situation!
What I did to solve this was to fork a process, grab stdin and stdout and put them in pipes and then overwrite the forked process image with an invocation of telnet (which now instead of stdin and stdout, uses those pipes).
You need ONE pipe to send text to bc and ANOTHER to receive from bc.
If you use a single pipe for everything, chances are you'll end up reading what you sent to bc and mix data.
WARNING: MASSIVE AMOUNT OF CODE INCOMING.
I'm sure you won't need to understand everything, since I use threads to write and read simultaneously and select() to see if there's anything to read in the pipe.
VERY IMPORTANT!!! When communications break, your process will receive SIGPIPE which terminates not cleanly (if you're using dynamic memory or stuff like that).
And you MUST fflush(outpipe) or else bc won't receive it. (This is because system only flushes when it finds '\n' or something like that, if I recall correctly).
I put all the code just in case you want to read what X does. But what you need is only the small fork right after the comment "LOCAL FUNCTIONS END HERE"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <errno.h>
#include <signal.h>
#include <stdbool.h>
#include "irc.h"
#include "coloring.h"
#include "rtsp.h"
#define BUFFERSIZE 255
int main (int argc, char *argv[]) {
/* XXX: When kid dies, program doesn't exit */
char *serverName, *port, *nick, *channel;
int ptelnetin[2];
int ptelnetout[2];
FILE *fpipes[2];
bool running = true;
pid_t kid;
pthread_t pthread_input, pthread_output;
/************************************************
LOCAL FUNCTIONS START HERE
***********************************************/
void *inputprocess(void *pipes) {
bool bracket;
int i;
fd_set rfds;
struct timeval tv;
tv.tv_sec = 0.2;
tv.tv_usec = 0;
char buffer[BUFFERSIZE];
FILE *out = ((FILE **) pipes)[1];
while (running){
FD_ZERO(&rfds);
FD_SET(fileno(stdin), &rfds);
switch (select(FD_SETSIZE, &rfds, NULL, NULL, &tv)) {
case -1:
fprintf(stderr, "Error reading data at select() Line %d, File %s\n", __LINE__, __FILE__);
running = false;
break;
case 0:
/* There's no data avaiable just yet.
Do nothing and keep checking */
break;
default:
/* This check needs to be done;
select isn't completely reliable */
if(!fgets(buffer, BUFFERSIZE, stdin)) {
running = false;
break;
}
/* Check message not to contain brackets*/
for (i = 0, bracket = false; running && !bracket && buffer[i] && i < BUFFERSIZE; i++) {
if (buffer[i] == '[' || buffer[i] == ']') {
PRINT_YELLOW;
printf("Use of brackets not allowed\n");
RESET_COLOR;
fflush(stdout);
bracket = true;
break;
}
}
if (running && !bracket) ircInputWrite(out, buffer);
fflush(out);
}
}
}
void *outputprocess(void *pipes){
fd_set rfds;
struct timeval tv;
tv.tv_sec = 0.2;
tv.tv_usec = 0;
char buffer[BUFFERSIZE];
char from[100];
FILE *in = ((FILE **) pipes)[0];
FILE *out = ((FILE **) pipes)[1];
while (running){
FD_ZERO(&rfds);
FD_SET(fileno(in), &rfds);
switch (select(FD_SETSIZE, &rfds, NULL, NULL, &tv)) {
case -1:
fprintf(stderr, "Error reading data at select() Line %d, File %s\n", __LINE__, __FILE__);
running = false;
break;
case 0:
/* There's no data avaiable just yet. */
/* Select sometimes returns 0 when there IS
data to read so we'll read anyway */
default:
/* This check needs to be done;
select isn't completely reliable */
if(!fgets(buffer, BUFFERSIZE, in)) {
running = false;
break;
}
switch(ircWhatsthis(buffer)) {
case iPING:
PRINT_BLUE;
printf("PingPong!\n");
RESET_COLOR;
ircPingPong(out, buffer); fflush(out);
fflush(stdout);
break;
case iROOMMSG:
if (ircUnpackPRIVMSG(from, buffer, buffer)) {
PRINT_BRIGHT_RED;
fprintf(stdout, "Malformed private message received\n");
RESET_COLOR;
}
else {
PRINT_CYAN;
printf("<%s>: ", from);
puts(buffer);
RESET_COLOR;
}
fflush(stdout);
break;
case iPRIVMSG:
fflush(stdout);
if (ircUnpackPRIVMSG(from, buffer, buffer)) {
PRINT_BRIGHT_RED;
fprintf(stdout, "Malformed private message received\n");
RESET_COLOR;
fflush(stdout);
}
else {
if (rtspExecBrackets(out, from, buffer)) {
PRINT_BRIGHT_MAGENTA;
printf("[%s]: ", from);
puts(buffer);
RESET_COLOR;
fflush(stdout);
}
}
break;
case iERROR:
PRINT_BRIGHT_RED;
fwrite(buffer, sizeof(char), strlen(buffer)+1, stdout);
RESET_COLOR;
break;
case iOK:
PRINT_BRIGHT_CYAN;
fwrite(buffer, sizeof(char), strlen(buffer)+1, stdout);
RESET_COLOR;
break;
default:
PRINT_BRIGHT_BLACK;
fwrite(buffer, sizeof(char), strlen(buffer)+1, stdout);
RESET_COLOR;
}
fflush(stdout);
}
}
}
void terminate(int signum) {
/* XXX irc.c calls rtsp.c which uses threads.
These threads never clean up if exiting via ^C
*/
RESET_COLOR;
running = false;
/*Close IO*/
fclose(fpipes[0]);
fclose(fpipes[1]);
/* Call child */
kill(kid, SIGINT);
wait(NULL);
exit(0);
}
/************************************************
LOCAL FUNCTIONS END HERE
***********************************************/
signal(SIGPIPE, terminate);
signal(SIGINT, terminate);
/* Get parameters */
if (argc != 5) {
fprintf(stderr, "Usage:\n %s <server> <port> <nick> <channel>\n", argv[0]);
return -1;
}
serverName = argv[1];
port = argv[2];
nick = argv[3];
channel = argv[4];
/* Startup pipes */
pipe(ptelnetin);
pipe(ptelnetout);
/* Launch telnete */
switch (kid = fork()) {
case -1:
perror("OMG ABORTION at main");
exit(-2);
case 0: /* CHILD */
/*Overwrite stdin with pipein and discard pipe*/
dup2(ptelnetin[0], 0);
close(ptelnetin[0]);
close(ptelnetin[1]);
/*Overwrite stdout with pipeout and discard pipe*/
dup2(ptelnetout[1], 1);
close(ptelnetout[0]);
close(ptelnetout[1]);
/*Overwrite process image with telnete*/
execlp("./telnete", "./telnete", argv[1], argv[2], (char *) NULL);
perror("Call to exec failed at main");
exit(-3);
default: /* PARENT */
/* Close reading end of pipein */
close(ptelnetin[0]);
/* Close writing end on pipeout */
close(ptelnetout[1]);
}
/* Turn (fileno) into (FILE *) */
fpipes[1] = fdopen(ptelnetin[1],"w");
if(!fpipes[1]) {
perror("Error at fdopen(in) at main");
kill(kid, SIGINT);
abort();
}
fpipes[0] = fdopen(ptelnetout[0],"r");
if(!fpipes[0]) {
perror("Error at fdopen(out) at main");
kill(kid, SIGINT);
abort();
}
/* Sleep for a few seconds so server doesn't ignore it */
PRINT_YELLOW;
printf("Logging in IRC...\n");
RESET_COLOR;
fflush(stdout);
if (ircRegister(argv[3], fpipes[1], fpipes[0])) {
fprintf(stderr, "Error registering in IRC.\n");
terminate(-1);
}
PRINT_YELLOW;
printf("Joining room %s\n", argv[4]);
RESET_COLOR;
ircJOIN(fpipes[1], argv[4]);
fflush(fpipes[1]);
/* Launch threads */
if (pthread_create(&pthread_input, NULL, inputprocess, fpipes)){
fprintf(stderr,"Couldn't launch input thread");
kill(kid, SIGINT);
abort();
}
if (pthread_create(&pthread_output, NULL, outputprocess, fpipes)){
fprintf(stderr,"Couldn't launch output thread");
kill(kid, SIGINT);
abort();
}
/* Wait for threads */
if (pthread_join(pthread_input,NULL)){
fprintf(stderr, "Error joining thread.\n");
}
if (pthread_join(pthread_output,NULL)){
fprintf(stderr,"Error joining thread.\n");
}
terminate(0);
}
I'll put the key fragment here so it's clearer:
/* Startup pipes */
pipe(ptelnetin);
pipe(ptelnetout);
/* Launch telnete */
switch (kid = fork()) {
case -1:
perror("OMG ABORTION at main");
exit(-2);
case 0: /* CHILD */
/*Overwrite stdin with pipein and discard pipe*/
dup2(ptelnetin[0], 0);
close(ptelnetin[0]);
close(ptelnetin[1]);
/*Overwrite stdout with pipeout and discard pipe*/
dup2(ptelnetout[1], 1);
close(ptelnetout[0]);
close(ptelnetout[1]);
/*Overwrite process image with telnete*/
execlp("./telnete", "./telnete", argv[1], argv[2], (char *) NULL);
perror("Call to exec failed at main");
exit(-3);
default: /* PARENT */
/* Close reading end of pipein */
close(ptelnetin[0]);
/* Close writing end on pipeout */
close(ptelnetout[1]);
}
/* Turn (fileno) into (FILE *) */
fpipes[1] = fdopen(ptelnetin[1],"w");
if(!fpipes[1]) {
perror("Error at fdopen(in) at main");
kill(kid, SIGINT);
abort();
}
fpipes[0] = fdopen(ptelnetout[0],"r");
if(!fpipes[0]) {
perror("Error at fdopen(out) at main");
kill(kid, SIGINT);
abort();
}
After doing this you can read bc's result from the (FILE *) fpipes[0] and write to it in fpipes[1].
Remember to fflush(fpipes[1]) after each write.
Treat those two as you would with any file.

How to loop through stdin & pipe output to a child execl command in C?

I have been trying to figure out how to loop through stdin from a file, then send it to a child process who sorts int using execl(). The code below works in that it takes the file & sorts the lines, but I am not seeing the "end of sentence" debug string I have added. Somehow this part of the code is being bypassed. I could use some help understanding the flow of data as it comes in from the file, then gets printed out to the screen.
int main(int argc, char *argv[])
{
pid_t p;
int status;
int fds[2];
FILE *writeToChild;
char word[50];
if(pipe(fds) == -1) {
perror("Error creating pipes");
exit(EXIT_FAILURE);
}
switch(p = fork()) {
case 0: //this is the child process
close(fds[1]); //close the write end of the pipe
execl("/usr/bin/sort", "sort", (char *) 0);
break;
case -1: //failure to fork case
perror("Could not create child");
exit(EXIT_FAILURE);
default: //this is the parent process
close(fds[0]); //close the read end of the pipe
writeToChild = fdopen(fds[1], "w");
wait(&status);
break;
}
while (fscanf(stdin, "%s", word) != EOF) {
//the below isn't being printed. Why?
fprintf(writeToChild, "%s end of sentence\n", word);
}
return 0;
}
Your primary problem is that you have the wait() in the wrong place. You wait for the child to die before you've written anything to it. You also have a secondary problem that don't redirect the read end of the pipe to the sort process's standard input.
You're not closing fds[0] in the child; cleanliness suggests that you should. You do need to fclose(writeToChild) before waiting; the sort won't stop until the parent has closed the pipe to the child.
These changes (and a few other ones) lead to:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(void)
{
pid_t p;
int status;
int fds[2];
FILE *writeToChild;
char word[50];
if (pipe(fds) == -1)
{
perror("Error creating pipes");
exit(EXIT_FAILURE);
}
switch (p = fork())
{
case 0: //this is the child process
close(fds[1]); //close the write end of the pipe
dup2(fds[0], 0);
close(fds[0]);
execl("/usr/bin/sort", "sort", (char *) 0);
fprintf(stderr, "Failed to exec sort\n");
exit(EXIT_FAILURE);
case -1: //failure to fork case
perror("Could not create child");
exit(EXIT_FAILURE);
default: //this is the parent process
close(fds[0]); //close the read end of the pipe
writeToChild = fdopen(fds[1], "w");
break;
}
if (writeToChild != 0)
{
while (fscanf(stdin, "%49s", word) != EOF)
{
//the below isn't being printed. Why?
fprintf(writeToChild, "%s end of sentence\n", word);
}
fclose(writeToChild);
}
wait(&status);
return 0;
}

Pipes and Forks

The goal of this project is to use pipes and forks to execute a line-count utility already written in a multi-process manner (one process per argument). I'm currently working on getting a single process working before expanding to handle multiple args.
Given two executables, lc1 and lc2, I want lc2 to establish a pipe to the stdout file descriptor of lc1, so that when execlp("lc1", argv[1], NULL) is called, the output will be read in by
while ((c= read(pipefd[0], readin, SIZE)) > 0)
According to my Unix book, I should use the open, dup2, close method for redirecting stdout to stdin, and here's my code:
int pid, c, i;
char *readin= (char *)malloc(sizeof(SIZE));
if (pipe(pipefd)== -1)
perror("Can't open a pipe\n");
for (i=1; i< argc; i++){
if ((pid= fork())==-1)
perror("Can't fork\n");
run(argv[i]);
}
//close pipe
close(1);
if (dup2(pipefd[0], 0)==-1)
perror("Can't redirect stdin");
close(pipefd[1]);
for (i=1; i< argc; i++){
if ((wait(NULL))== -1)
perror("Wait error");
while ((c= read(pipefd[0], readin, SIZE)) > 0){;
//print buf count
total += atoi(readin);
}
}
The run function is
void run(char *f){
int fp;
if ((fp= open(f, O_RDONLY)) == -1)
perror("Can't open the file");
close(pipefd[0]);
dup2(pipefd[1], 1);
close(pipefd[1]);
execlp("ls1", f, NULL);
}
When I try to execute this code, I get a stdin redirect error saying bad file descriptor. Why is this happening, and would appreciate any hints to for fixing.
run(argv[i]) is executed by both parent and child because are not assigning the functionality based on the returned PID, so one close after the other may have closed.
See below code, can he handy, I will use the code sample for situations like this. :
int main()
{
int pipe_fd[2] = {0};
int pid = -1;
int status = -1;
int ret_value = INVALID_CMD;
int cmd_output_len = -1;
status = pipe(pipe_fd);
if(status<0)
{
perror("pipe create err");
}
else
{
pid = fork();
if(pid<0)
{
}
else if (pid == 0)
{
/*Child functionality*/
child_func(pipe_fd, cmd);
}
else
{
/*Parent functionality*/
cmd_output_len = parent_fun(pid, pipe_fd);
}
}
return ret_value;
}
int child_func(int pipe_fd[], const char * cmd)
{
int status = 5;
int read_fd = pipe_fd[0]; /*read file descriptor*/
int write_fd = pipe_fd[1]; /*write file descriptor*/
int exit_status = 0;
/*close read fd*/
close(read_fd);
/*dup2 stdout to write fd*/
//status = dup2(1, write_fd);
status = dup2(write_fd, 1);
if(status<0)
{
exit(-1);
}
else
{
system(cmd);
exit(0);
}
}
int parent_fun(int child_id, int pipe_fd[])
{
int status = -1;
int len = 0;
bool_e break_loop = FALSE;
int read_fd = pipe_fd[0]; /*read file descriptor*/
int write_fd = pipe_fd[1]; /*write file descriptor*/
/*close write fd*/
close(write_fd);
while(1)
{
sleep(1);
status = waitpid(child_id, &status, WNOHANG);
switch(status)
{
case 0:
/*Child is still active*/
printf("No process waiting to exit..\n");
len = do_ur_fun(read_fd);
write(1, output, len);
break;
/*case EINTR:
case ECHILD:
case EINVAL:
perror("waitpid error");
break_loop = TRUE;
break;*/
default:
if(status<0)
{
perror("waitpid error");
break_loop = TRUE;
len = -1;
}
else if(child_id == status)
{
/*Valid staus from child*/
len = read_output(read_fd, output);
//write(1, output, len);
break_loop = TRUE;
}
else
{
}
break;
}
if(TRUE == break_loop)
{
break;
}
}
return len;
}
int do_ur_fun (int read_fd)
{
/*Do your exec*/
}
MaheshGupta024 identified a very important problem in your code; I'm assuming you will fix that.
One of the other problem areas is:
close(1);
if (dup2(pipefd[0], 0)==-1)
perror("Can't redirect stdin");
close(pipefd[1]);
for (i=1; i< argc; i++){
if ((wait(NULL))== -1)
perror("Wait error");
while ((c= read(pipefd[0], readin, SIZE)) > 0){;
//print buf count
total += atoi(readin);
}
}
The first close closes the process's standard output; this is seldom a good idea. The next line duplicates the read end of the pipe to standard input - which is fine. As noted in a comment above, perror() does not exit. You then close the write end of the pipe - that's correct; but you should presumably close the read end of the pipe too since you have set it to come from the pipe.
Your loop starts OK; you have redundant parentheses in the wait() line. You read from pipefd[0] instead of standard input - so maybe you didn't want to close pipefd[0] but neither did you need to duplicate it to standard input. You then have a nested loop that reads on the pipe while there's more data to be read from a child - you don't absolutely need the wait() code with its loop since the inner while won't terminate until all the children are dead. On the other hand, there's no great harm in it - after the first child dies, you'll read the data from all the other children, then go into the outer loop and wait for each other child, with the inner loop terminating immediately since there is no data left to read.
So:
Don't close stdout.
Don't dup the pipe read to stdin.
Decide whether you want to clean up the loop - it will work, but could be cleaner.
The run() function is:
void run(char *f){
int fp;
if ((fp= open(f, O_RDONLY)) == -1)
perror("Can't open the file");
close(pipefd[0]);
dup2(pipefd[1], 1);
close(pipefd[1]);
execlp("ls1", f, NULL);
}
The argument should be const char *f (or use name or file instead of f). I would also pass the pipefd array to the function rather than use a global variable
.
Do not call a file descriptor fp; that name conventionally indicates a variable of type FILE *, not int.
However, you don't need to open the file in the first place - unless you want the calling program to do the error reporting instead of the invoked program. However, if you do want the calling program to do the error reporting, you should close the file descriptor before proceeding. (I've already commented on perror() returning).
It would be a good idea to print an error message after execlp(); the only time the function returns is when it fails, so there is no need to test its return value. You might want to exit too - rather than have the failed function go through the rest of the main program after the call to run().
Good points: you did close both the pipe file descriptors.
Hence:
void run(const char *file, int *pipefd)
{
close(pipefd[0]);
dup2(pipefd[1], 1);
close(pipefd[1]);
execlp("ls1", f, NULL);
perror("Failed to exec ls1");
exit(EXIT_FAILURE);
}

Resources