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))).
Related
I'm reading lines of text from file, and for each line I'm processing it using several { fork() --> child process invokes execvp(), and parent invokes wait() } .
at the end of process I'm writing the results to a file.
Problem is: the while loop seems to iterate too much and also the writing to the file.
The results.csv file contains 6 lines instead of just 2 (the while iteration
iterates a text file with 2 lines, but also when I use printf it seems like the last line is read twice).
What am I missing here?
The code example is:
FILE* results = fopen("results.csv", "w");
if (results == NULL){
fclose(fp);
perror("Failed opening results file");
exit(-1);
}
fdIn = open(inputPath, O_RDONLY);
if (fdIn < 0){
perror("Failed opening input file");
exit(-1);
}
while (fgets(student, sizeof(student), fp) != NULL) {
// override end line char of unix ('\n') with '\0'
student[strlen(student)-1] ='\0';
pid = fork();
if (pid < 0){
close(fdIn);
perror("Failed creating process for executing student's program");
exit(-1);
}
if (pid == 0) {// son process code
fdOut = open("tempOutput.txt", (O_WRONLY | O_CREAT | O_TRUNC), 0666);
if (fdOut < 0){
perror("Failed opening temporary output file");
exit(-1);
}
close(1);
dup(fdOut);
close(fdOut);
close(0);
dup(fdIn);
close(fdIn);
char studProgPath[bufSize];
strcpy(studProgPath,studentsFolderPath);
strcat(studProgPath,"/");
strcat(studProgPath,student);
strcat(studProgPath,"/");
strcat(studProgPath,"a.out");
char * args[] = {"a.out", NULL};
ret_code = execvp(studProgPath,args);
if (ret_code == -1){
perror("Failed executing student program");
exit(-1);
}
}
waited = wait(&stat);
if (stat == -1){ // need to grade 0
printf("%s,0\n",student);
}else{ // open process to compare the output with the expected
pid = fork();
if (pid < 0){
perror("Failed opening process for comparing outputs");
exit(-1);
}
if(pid == 0) { // son process
char * args[] = {"comp.exe",outputPath,"tempOutput.txt",NULL};
ret_code = execvp("comp.exe",args);
exit(ret_code);
}
waited = wait(&stat);
if (stat == -1) {
perror("Failed executing comparing program");
exit(-1);
} else if (stat == 0 || stat == 1) { // if outputs are not the same
fprintf(results,"%s,0\n",student);
} else { // matching outputs grade 100
fprintf(results,"%s,100, pid: %d\n",student,getpid());
}
}
}
The file which gets triple entries gets opened here:
FILE* results = fopen("results.csv", "w");
The following lines write to this results file, slightly before the function calls fork():
} else if (stat == 0 || stat == 1) { // if outputs are not the same
fprintf(results,"%s,0\n",student);
} else { // matching outputs grade 100
fprintf(results,"%s,100, pid: %d\n",student,getpid());
}
This file should be flushed with fflush(results) before the fork, otherwise the buffer of results might be flushed three times: in the parent, and in the two copies in the children.
Also, results and student should be closed with fclose(results) and student, before calling execvp. If the files are not closed, then the a.out might manipulate the results file. I assume that a.out is an external code which you don't control.
while (fgets(student, sizeof(student), fp) != NULL) {
// override end line char of unix ('\n') with '\0'
student[strlen(student)-1] ='\0';
fflush(results); // otherwise each child may flush the same chars
pid = fork();
if (pid < 0){
fclose(results); // otherwise ./a.out might write to this file
fclose(fp); // better also close it.
close(fdIn);
I have to create 2 child process and send data from the parent to the two, so I used the pipe.
If I just use 1 child process and 1 pipe, all works perfectly with fdopen, fscanf and fprintf.
Also, if I create 2 pipe and send data to a single process, still works perfectly.
But, if I create a second process and try to read from the second pipe, nothing happen.
for example:
int main() {
pid_t pid1, pid2;
int a[2];
pipe(a);
pid1 = fork();
if(pid1 == 0) {
char x,y;
FILE *stream;
stream = fdopen(a[0],"r");
fscanf(stream,"%c",&x);
printf("%c\n", x);
close(a[1]);
close(a[0]);
} else {
int b[2];
pipe(b);
pid2 = fork();
FILE *stream1, *stream2;
close(a[0]);
close(b[0]);
stream1 = fdopen(a[1],"w");
stream2 = fdopen(b[1],"w");
fprintf(stream1, "yo bella zio\n");
fprintf(stream2, "como estas\n");
fflush(stream1);
fflush(stream2);
close(a[1]);
close(b[1]);
waitpid (pid1, NULL, 0);
waitpid (pid2, NULL, 0);
if (pid2 == 0) {
FILE *stream;
close(b[1]);
close(a[1]);
close(a[0]);
stream = fdopen(b[0],"r");
fscanf(stream,"%c",&x);
printf("%c\n", x);
} else {
}
}
}
I really tried all combination. Declare all the pipe together, close or not close pipe. everything but nothing.
This code fixes the problems identified in my comment and some stray issues.
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main(void)
{
pid_t pid1, pid2;
int a[2];
int b[2];
pipe(a);
pid1 = fork();
if (pid1 < 0)
{
fprintf(stderr, "failed to fork child 1 (%d: %s)\n", errno, strerror(errno));
exit(EXIT_FAILURE);
}
else if (pid1 == 0)
{
close(a[1]); // Must be closed before the loop
FILE *stream = fdopen(a[0], "r");
if (stream == NULL)
{
fprintf(stderr, "failed to create stream for reading (%d: %s)\n", errno, strerror(errno));
exit(EXIT_FAILURE);
}
int c;
while ((c = getc(stream)) != EOF)
putchar(c);
//char x;
//fscanf(stream, "%c", &x);
//printf("%c\n", x);
//close(a[0]); -- Bad idea once you've used fdopen() on the descriptor
printf("Child 1 done\n");
exit(0);
}
else
{
pipe(b);
pid2 = fork();
if (pid2 < 0)
{
fprintf(stderr, "failed to fork child 2 (%d: %s)\n", errno, strerror(errno));
exit(EXIT_FAILURE);
}
else if (pid2 == 0)
{
close(b[1]);
close(a[1]);
close(a[0]);
FILE *stream = fdopen(b[0], "r");
if (stream == NULL)
{
fprintf(stderr, "failed to create stream for reading (%d: %s)\n", errno, strerror(errno));
exit(EXIT_FAILURE);
}
int c;
while ((c = getc(stream)) != EOF)
putchar(c);
//char x;
//fscanf(stream, "%c", &x);
//printf("%c\n", x);
printf("Child 2 done\n");
exit(0);
}
}
close(a[0]);
close(b[0]);
FILE *stream1 = fdopen(a[1], "w");
if (stream1 == NULL)
{
fprintf(stderr, "failed to create stream for writing (%d: %s)\n", errno, strerror(errno));
exit(EXIT_FAILURE);
}
FILE *stream2 = fdopen(b[1], "w");
if (stream2 == NULL)
{
fprintf(stderr, "failed to create stream for writing (%d: %s)\n", errno, strerror(errno));
exit(EXIT_FAILURE);
}
fprintf(stream1, "yo bella zio\n");
fprintf(stream2, "como estas\n");
fflush(stream1); // Not necessary because fclose flushes the stream
fflush(stream2); // Not necessary because fclose flushes the stream
fclose(stream1); // Necessary because child won't get EOF until this is closed
fclose(stream2); // Necessary because child won't get EOF until this is closed
//close(a[1]); -- bad idea once you've used fdopen() on the descriptor
//close(b[1]); -- bad idea once you've used fdopen() on the descriptor
waitpid(pid1, NULL, 0);
waitpid(pid2, NULL, 0);
printf("All done!\n");
return 0;
}
Note that I changed the child processes so that (a) they explicitly exit in the code block, and (b) made their body into a loop so that all the data sent is printed. That required me to move the close(a[1]) in the first child; otherwise, the loop doesn't terminate because the o/s sees that child 1 has the descriptor open for writing.
When executed on a Mac running macOS 10.13.6 High Sierra (GCC 8.2.0 as the compiler), I get the output:
yo bella zio
Child 1 done
como estas
Child 2 done
All done!
I am trying to use pipes in C. I have two create two pipes between parent and child process.I have to read a file in chunks of 4096 bytes (or smaller if there is less) and I have to send through the pipes the amount of data that was read and how many times there have been readings. For example, to copy a 6KB file, the parent writes the first 4KB data of the file to the shared memory and send two integers, 1 and 4096, to the child via the pipe. The child receives these two numbers, copies 4096 bytes from the shared memory to the output file, and sends back 1 to the parent via the other pipe. After receiving 1, the parent copies the left 2KB data to the shared memory and send 2 and 2048 to the child. The child receives them from the pipe, copies 2048 bytes to the output file, and replies with 2 to the parent. The parent then send 0, 0 to the child. The child receives 0 and replies with a 0 and then exit. The parent receives 0 and exits too.
Currently my program works for file less than one block but not for file greater then one block (4096 bytes)
697, thank you for pointing out, I have modified my program as following but still has issues, basically how to control the flow as parent-child-parent-child ...
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#define SIZE 4096
int file_exist (char *filename)
{
struct stat buffer;
return (stat (filename, &buffer) == 0);
}
int main(int argv, char *argc[]) {
/*Check if program is called correctly*/
if(argv != 3) {
printf("Please call program appropriately\n");
exit(EXIT_FAILURE);
}
FILE *r, *w;
void *sharedMem;
int datapipe[2];
int blockpipe[2];
int shm;
char userInput[5];
char *name = "fsuid_cop4610";
if (file_exist (argc[2]))
{
printf("Would you like to overwrite file (yes/no): ");
scanf("%s", userInput);
if(!strcmp(userInput, "yes")) {
printf("Overwriting file...\n");
//fclose(w);
w = fopen(argc[2], "wb");
if(w == NULL) {
perror("Error with write file");
exit(EXIT_FAILURE);
}
}
else if(!strcmp(userInput, "no")) {
printf("Will not overwrite\n");
exit(EXIT_FAILURE);
}
else {
printf("User input not accepted\n");
exit(EXIT_FAILURE);
}
}
/*Check if read file can open*/
r = fopen(argc[1], "rb");
if(r == NULL) {
perror("Error opening read file");
exit(EXIT_FAILURE);
}
fseek(r, 0, SEEK_END); // seek to end of file
int inputlength = ftell(r); // get current file pointer
printf("inputlength is %d\n",inputlength);
int numofblock = inputlength/SIZE + 1;
fseek(r, 0, SEEK_SET); // seek back to beginning of file
/*Check if write file can open*/
if (pipe(datapipe) < 0) {
perror("Pipe");
exit(EXIT_FAILURE);
}
if (pipe(blockpipe) < 0) {
perror("Pipe");
exit(EXIT_FAILURE);
}
/*Check if forking process is successful*/
pid_t pid = fork();
if(pid < 0) {
perror("Fork");
exit(EXIT_FAILURE);
}
shm = shm_open(name, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
if(shm == -1) {
perror("Shared memory");
exit(EXIT_FAILURE);
}
if(ftruncate(shm, SIZE) == -1) {
perror("Shared Memory");
exit(EXIT_FAILURE);
}
sharedMem = mmap(NULL, SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, shm, 0);
if(sharedMem == MAP_FAILED) {
perror("Mapping shared memory");
exit(EXIT_FAILURE);
}
if (pid>0) { // parent
printf(" parent before close data0,block1\n");
close(datapipe[0]); // close read, will write data
close(blockpipe[1]); // close write, will read block number
printf(" parent close data0,block1\n");
for (int i=1; i<=numofblock; i++)
{
printf("... parent process\n");
int blocknumber=i;
printf("parent read from input file into shared memory\n");
int P2SHM = fread(sharedMem, 1, SIZE, r);
if(P2SHM < 0) {
perror("Could not store to shared memory");
exit(EXIT_FAILURE);
}
//printf("parent shared memory conent: %s\n",(char *)sharedMem);
printf("parent data read %d\n",P2SHM);
if (i==1)
{
printf("i=%d parent write to data pipe\n",i);
if(write(datapipe[1], &P2SHM, sizeof(int)) < 0) {
perror("parent failed to write to pipe bytes");
exit(EXIT_FAILURE);
}
if(write(datapipe[1], &blocknumber, sizeof(int)) < 0) {
perror("parent failed to write to pipe block number");
exit(EXIT_FAILURE);
}
}
printf("parent read from block pipe\n");
int C2P = read(blockpipe[0], &blocknumber, sizeof(int));
if(C2P < 0) {
perror("parent failed to read value from blockpipe");
exit(EXIT_FAILURE);
}
/*else if(C2P == 0) {
printf("End of file reached\n");
}*/
else {
printf("parent block %d Received succesfully\n", blocknumber);
if (i>= 2)
{
printf("i=%d parent write to data pipe\n",i);
//close(datapipe[0]); // close read, will write data
if(write(datapipe[1], &P2SHM, sizeof(int)) < 0) {
perror("parent failed to write to pipe bytes");
exit(EXIT_FAILURE);
}
if(write(datapipe[1], &blocknumber, sizeof(int)) < 0) {
perror("parent failed to write to pipe block number");
exit(EXIT_FAILURE);
}
//close(datapipe[1]);
}
if(C2P == 0) {
printf("parent End of file reached\n");
}
}
} // end for
printf(" parent before close data1,block0\n");
close(datapipe[1]);
close(blockpipe[0]);
printf(" parent close data1,block0\n");
printf(" ... existing parent process\n");
}
else { // pid=0 child
printf(" child before close data1,block0\n");
close(datapipe[1]); // close write, will read data
close(blockpipe[0]); // close read, will write block number
printf(" child close data1,block0\n");
for (int j=1; j<=numofblock; j++)
{
printf(".... child process\n");
int cBytes, len, len2;
int blocknumber = 1;
printf("child read from datapipe\n");
len = read(datapipe[0], &cBytes, sizeof(cBytes));
len2 = read(datapipe[0], &blocknumber, sizeof(blocknumber));
printf("child wrote to blockpipe blocknumber=%d\n", blocknumber);
write(blockpipe[1], &blocknumber, sizeof(blocknumber));
printf("child There are %i bytes\n", cBytes);
if(len >= 0)
{
printf("child writing to file\n");
//fwrite(sharedMem, 1, sizeof(sharedMem), w);
//printf("child shared memory conent: %s\n",(char *)sharedMem);
char* res = (char *)sharedMem;
//printf("res = %s\n",res);
//printf("errno before write=%d",errno);
shm_unlink(name);
//int writtenbyte = fwrite(res, sizeof(char), strlen(res), w);
int writtenbyte = fwrite(res, sizeof(char), cBytes, w);
if(errno == EINTR) {
printf("somewhting wrong");
}
//printf("errno after write=%d",errno);
printf("writtenbyte = %d\n",writtenbyte);
//fclose(w);
//fputs((char *)sharedMem, w);
//fwrite(s->name, sizeof(char), strlen(s->name) + 1, fp);
}
/*else if (len == 0) {
printf("End of fle reached\n");
}*/
else { // len < 0
perror("Failed to read from pipe");
exit(EXIT_FAILURE);
}
} // after for
printf(" child before close data0,block1\n");
close(datapipe[0]);
close(blockpipe[1]);
printf(" child close data0,block1\n");
printf("... exiting Child process\n");
}
//shm_unlink(name);
//fclose(r);
fclose(w);
return 0;
}
Result:
inputlength is 4177
parent before close data0,block1
parent close data0,block1
... parent process
parent read from input file into shared memory
parent data read 4096
i=1 parent write to data pipe
parent read from block pipe
child before close data1,block0
child close data1,block0
.... child process
child read from datapipe
child wrote to blockpipe blocknumber=1
child There are 4096 bytes
child writing to file
parent block 1 Received succesfully
... parent process
parent read from input file into shared memory
parent data read 81
parent read from block pipe
parent block 2 Received succesfully
i=2 parent write to data pipe
Note child write block number 1, but parent received block number 1, then received block number 2, and after the 2nd write from parent it stopped, without writing output file. Any thoughts for that?
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.
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;
}