I want to read input from Stdin. And I use fork() method in C. I have child and parent process. My input is multiline. Parent process will simply wait for the termination of the child process.Child process will read just first line.After the child terminates, parent will continues to read. And I want print lines. For Example; input ->
Monday
Tuesday
Wednesday
Child process prints 'Monday', parent process prints 'Tuesday' and 'Wednesday'. Once end-of-file is
reached the program terminates.
./program < input.txt
OK; sounds straight-forward — at least, as long as you're typing the input. In fact, it is hard to make it go wrong. What did you try and what happened when you tried it? Please read up on how to create an MCVE (Minimal, Complete, Verfiable Example).
However, it gets (a lot) trickier when you're reading from a file. The problem is that when you read from the terminal, the standard I/O routines in the child get the first line of data and then let the process work with that. The child exits without reading the second line, so the parent can pick up where the child left off. If, instead, you're reading from a file, the standard I/O routines read a buffer full of data, which can be many lines. And what the child reads, the parent can't. So, what works with terminal input doesn't work with file input.
How can you avoid the problem? 'Tis hard. One possibility, which would probably be regarded as cheating, is to have the initial (parent) process read one character before forking. This fills the buffer with one line if the input is coming from a terminal, or with the initial buffer full if reading from file. After forking, the child process can read the first line (fgets()) and print it and exit. Meanwhile, the parent process also has the first line of data in its copy of the input buffers (remember, the forked child and parent are almost identical after forking), so it can read and ignore the first line (the child is processing that), and then read and print the remaining lines. It can then wait for the child to die in a loop, and finally read the remaining lines. This leads to:
/* SO 4263-5451 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
int main(void)
{
pid_t pid;
int c = getchar();
if (c == EOF)
{
fprintf(stderr, "Standard input was empty!\n");
exit(1);
}
ungetc(c, stdin);
//setvbuf(stdin, 0, _IOLBF, 0);
if ((pid = fork()) < 0)
{
fprintf(stderr, "Failed to fork()!\n");
exit(2);
}
if (pid == 0)
{
/* Child */
char line[4096];
if (fgets(line, sizeof(line), stdin) == 0)
{
fprintf(stderr, "Failed to read line in child\n");
exit(3);
}
line[strcspn(line, "\n")] = '\0';
printf("Child: read [%s]\n", line);
exit(0);
}
else
{
int corpse;
int status;
while ((corpse = wait(&status)) > 0 && corpse != pid)
printf("Parent: child %d died with status 0x%.4X\n", corpse, status);
char line[4096];
if (fgets(line, sizeof(line), stdin) == 0)
{
fprintf(stderr, "Failed to read line in parent\n");
exit(4);
}
while (fgets(line, sizeof(line), stdin) != 0)
{
line[strcspn(line, "\n")] = '\0';
printf("Parent: read [%s]\n", line);
}
}
return 0;
}
This yielded:
Child: read [Monday]
Parent: read [Tuesday]
Parent: read [Wednesday]
I tried a variant setting line-buffering with setvbuf(stdin, 0, _IOLBF, 0); but that didn't affect the file input (on a Mac running macOS Sierra 10.12.3 with GCC 6.3.0), though it too worked fine with terminal input.
One option would be to replace the standard I/O functions with file descriptor code, and to use read(STDIN_FILENO, &c, 1) to read characters one at a time. This is slower (lots of system calls) but reliable:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
static int input_char(int fd)
{
char c;
if (read(fd, &c, 1) != 1)
return EOF;
return c;
}
static size_t input_line(int fd, char *buffer, size_t buflen)
{
int c;
size_t bufcnt = 0;
while (bufcnt < buflen - 1 && (c = input_char(fd)) != EOF)
{
if (c == '\n')
break;
buffer[bufcnt++] = c;
}
buffer[bufcnt] = '\0';
return bufcnt;
}
int main(void)
{
pid_t pid;
if ((pid = fork()) < 0)
{
fprintf(stderr, "Failed to fork()!\n");
exit(2);
}
if (pid == 0)
{
char line[4096];
if (input_line(STDIN_FILENO, line, sizeof(line)) == 0)
{
fprintf(stderr, "Failed to read line in child\n");
exit(3);
}
printf("Child: read [%s]\n", line);
exit(0);
}
else
{
int corpse;
int status;
while ((corpse = wait(&status)) > 0 && corpse != pid)
printf("Parent: child %d died with status 0x%.4X\n", corpse, status);
char line[4096];
while (input_line(STDIN_FILENO, line, sizeof(line)) != 0)
{
printf("Parent: read [%s]\n", line);
}
}
return 0;
}
Related
I am trying to understand why my program hangs. The Parent sends input froma
file it reads to the child program, and the child program will send the result of its computation back to it's parent. However, I have trouble sending the message back through a second pipe. The parent seems to hang when reading from the pipe.
From the other posts, I have read it seems to indicate that the parent should wait for the child to finish by using wait or waitpid (which in my case both of them does not resolve my issue).
I have notice by adding print statement that neither the PARENT or the CHILD finishes.. Could someone please explain to me why this is happening?
Why does this not work?
int main(int argc,char** argv) {
char buffer[1];
int i;
int fd1[2]; int fd2[2];
pipe(fd1); pipe(fd2);
pid_t pid;
// FIRST PROCESS.
// -------------------
pid = fork();
if(pid == 0) {
int cnt;
dup2(fd1[0], STDIN_FILENO);
dup2(fd2[1], STDOUT_FILENO);
for (i = 0; i < 2; i++) {
close(fd1[i]);
close(fd2[i]);
}
while(read(STDIN_FILENO, buffer, sizeof(buffer)) > 0) {
fprintf(stderr, "( %s )", buffer);
cnt = cnt + *buffer - 48;
}
write(STDOUT_FILENO, &cnt, sizeof(cnt));
exit(0);
}
// PARENT.
// ------------------------
int file = open(argv[1], O_RDONLY);
// READ THE FILE.
while(read(file, buffer, 1) > 0) {
if (48 <= *buffer && *buffer <= 57) {
// PIPE TO CHILD.
write(fd1[1], buffer, 1);
}
}
// WAIT FOR CHILD TO FINISH SENDING BACK.
// int status = 0;
// waitpid(pid, &status, 0);
// THIS BLOCK DOESN'T RESOLVE ANYTHING. IT HANGS AT WAIT OR WAITPID.
// **** THIS IS THE PART WHERE IT DOESN'T WORK.
while(read(fd2[0], buffer, 1) > 0) {
fprintf(stderr, "RESULT : %s", buffer);
}
// CLOSING PIPES
for (i = 0; i < 2; i++) {
close(fd1[i]);
close(fd2[i]);
}
close(file);
exit(0);
}
You aren't closing enough file descriptors in the parent soon enough.
Rule of thumb: If you
dup2()
one end of a pipe to standard input or standard output, close both of the
original file descriptors returned by
pipe()
as soon as possible.
In particular, you should close them before using any of the
exec*()
family of functions.
The rule also applies if you duplicate the descriptors with either
dup()
or
fcntl()
with F_DUPFD
Now, your child process is following the RoT perfectly. But the corollary for parent processes is that they need to close the unused ends of the pipe, and they must close the write end of a pipe that they use to signal EOF to the reading end of that pipe. This is where your code fails.
Arguably, before reading the file, the parent process should close the read end of the pipe it uses to write to the child, and it should close the write end of the pipe it uses to read from the child.
Then, after reading the whole of the file, it should close the write end of the pipe to the child, before going into the 'read from child' loop. That loop never terminates because the parent still has the write end of the pipe open, so there's a process that could (but won't) write to the pipe.
Also, since the child writes the bytes of an integer onto a pipe, the parent should read the bytes of an integer. Using char buffer[1]; with a %s format is pointless; you need a null terminator for the string, and a single char buffer can't hold both a null byte and any data.
Along with various other improvements ('0' instead of 48, for example), you might end up with:
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char** argv)
{
if (argc != 2) {
fprintf(stderr, "Usage: %s filename\n", argv[0]);
exit(EXIT_FAILURE);
}
int fd1[2];
int fd2[2];
char buffer[1];
pipe(fd1);
pipe(fd2);
pid_t pid = fork();
if (pid == 0) {
int cnt = 0;
dup2(fd1[0], STDIN_FILENO);
dup2(fd2[1], STDOUT_FILENO);
for (int i = 0; i < 2; i++) {
close(fd1[i]);
close(fd2[i]);
}
while (read(STDIN_FILENO, buffer, sizeof(buffer)) > 0) {
fprintf(stderr, "(%c)", buffer[0]); // Changed
cnt = cnt + buffer[0] - '0';
}
putc('\n', stderr); // Aesthetics
write(STDOUT_FILENO, &cnt, sizeof(cnt));
exit(0);
}
int file = open(argv[1], O_RDONLY);
if (file < 0) {
fprintf(stderr, "failed to open file '%s' for reading\n", argv[1]);
exit(EXIT_FAILURE);
}
close(fd1[0]); // Added
close(fd2[1]); // Added
while (read(file, buffer, sizeof(buffer)) > 0) {
if ('0' <= buffer[0] && buffer[0] <= '9') {
write(fd1[1], buffer, sizeof(buffer));
}
}
close(file); // Moved
close(fd1[1]); // Added
// Rewritten
int result;
while (read(fd2[0], &result, sizeof(result)) == sizeof(result)) {
fprintf(stderr, "RESULT : %d\n", result);
}
close(fd2[0]); // Added
// Close loop removed
return 0;
}
If that is stored in file pipe71.c and compiled, I get the following outputs when it is run:
$ ./pipe71 pipe71.c
(2)(0)(1)(2)(2)(2)(1)(1)(2)(0)(0)(2)(1)(0)(2)(2)(1)(0)(2)(1)(2)(0)(0)(0)(0)(0)(1)(0)(1)(1)(0)(2)(1)(0)(0)(0)(0)(9)(1)(1)(1)(1)(2)(0)(2)(0)(0)
RESULT : 49
$ ./pipe71 pipe71
(0)(0)(8)(0)(0)(2)(2)(0)(8)(1)(1)(5)(1)(1)(1)(1)(5)(1)(1)(1)(8)(5)(1)(9)(8)(5)(1)(1)(0)(4)(4)(4)(6)(0)(2)(8)(0)(0)(0)(2)(7)(1)(3)(8)(3)(0)(4)(3)(0)(4)(9)(0)(0)(0)(0)(7)(1)(9)(8)(1)(3)(0)
RESULT : 178
$
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <string.h>
void tokenizer(char* input, char** output) { //My tokenizer
char* input_dup = strdup(input);
output[0] = strtok(input_dup, " ");
int i = 1;
while ((output[i] = strtok(NULL, " ")) != NULL) {
i++;
}
}
void run_command(char** args, int* fd) { //no pipe
pid_t pid = fork();
if (pid < 0) {
printf("Forking failed...\n");
}
else if (pid == 0) {
close(fd[0]);
if (fd[1] != 1)
dup2(fd[1], 1);
execvp(args[0], args);
printf("Command failed...\n");
exit(1);
}
else {
close(fd[1]);
wait(pid);
char buff[1];
while (read(fd[0], buff, 1) > 0) {
if (buff[0] == EOF || buff[0] == '\0') {
printf("Caught something, returning out...");
return;
}
else {
printf("%c", buff[0]);
}
}
}
}
//pipeline function
void run_pipe(char** args, int* fd) {
pid_t pid = fork();
if (pid < 0) {
printf("Forking failed...\n");
}
else if (pid == 0) {
if (fd[1] != 1) {
dup2(fd[1], 1);
}
execvp(args[0], args);
printf("Command failed...\n");
exit(1);
}
else {
close(fd[1]);
if (fd[0] != 0) {
dup2(fd[0], 0);
}
wait(pid);
}
}
int main(int argc, char** argv) {
printf ("Starting myshell (mysh) \n..\n..\n");
while (1) {
char cwd[1024];
printf ("mysh :: %s -> ", getcwd(cwd, sizeof(cwd)));
char ch[1024];
memset(ch, 0, 1023); //for cleanup
char c = 0;
int i = 0;
while (c != '\n') {
c = getchar();
if (c == EOF) {
printf ("EOF Received, exiting...\n");
return 0;
}
if (c != '\n')
ch[i] = c;
i++;
}
if (ch[0] != '\0') {
char* tokens[128];
tokenizer(ch, tokens);
//first check for keywords
if (strcmp(tokens[0], "cd") == 0) {
if (chdir(tokens[1]) < 0) {
printf("ERROR: Directory %s does not exist\n", tokens[1]);
}
}
else if (strcmp(tokens[0], "exit") == 0) {
printf("Leaving shell...\n");
return 0;
}
else {
char* commands[50];
memset(commands, 0, sizeof(commands));
int j = 0;
int k = 0;
int fd[2];
//try something different...
while (tokens[j] != NULL) {
if (strcmp(tokens[j], "|") == 0) {
commands[k] = NULL;
pipe(fd);
run_pipe(commands, fd);
j++;
k = 0;
}
//more cases here
else { //nothing special
commands[k] = tokens[j];
j++;
k++;
}
}
commands[k] = NULL;
pipe(fd);
run_command(commands, fd);
}
}
}
}
The above code is meant to simulate a shell. It handles single commands and it handles the pipelining properly (i.e. ps | sort | wc is returning the correct output) however when the pipelining is done it returns an EOF which is caught by the condition in the loop with getchar(). If I try to ignore this EOF it segfaults. Am I leaving a pipe open somewhere and stdin is getting flooded? Any help is appreciated.
Compilation fixes
You need to add #include <sys/wait.h> and then fix the calls to wait(). I used (twice):
int status;
int corpse = wait(&status);
printf("PID %d status 0x%.4X\n", corpse, status);
Arguably, that should be a loop looking for a specific PID, or you should use waitpid() instead. While debugging a shell, you want to know about every PID that exits and its status.
I ran `ps | wc' and got:
Starting myshell (mysh)
..
..
mysh :: /usr/local/google/home/jleffler/soq -> ps | wc
PID 25960 status 0x0000
PID 25961 status 0x0000
4 16 117
mysh :: /usr/local/google/home/jleffler/soq -> EOF Received, exiting...
If you mean "the code should have continued instead of getting EOF", then there's some more work to do.
Tangential issues
I note the line:
if (buff[0] == EOF || buff[0] == '\0')
The character in buff[0] is from a read() call. It will never be EOF meaningfully; EOF is distinct from every character (hence getchar() returns an int). This becomes significant later:
char c = 0;
while (c != '\n')
{
c = getchar();
if (c == EOF)
Since c is a char, you cannot reliably compare it with EOF. You must store the result of getchar() in an int.
I'm not yet convinced these are the cause of the trouble, but you must be careful.
Probable cause
I think the trouble is in run_pipe() in the parent code (as amended):
else
{
close(fd[1]);
if (fd[0] != 0)
{
dup2(fd[0], 0);
}
int status;
int corpse = wait(&status);
printf("PID %d status 0x%.4X\n", corpse, status);
}
The fd[0] != 0 condition will always be true (very improbable that it will be false), so you then change your shell's input to read from fd[0]. You should review that; it means that you're reading standard input from the read end of the pipe to the child. That's bad; you've lost your original input!
Your code also seems to have the parent waiting for the child to die, and then reads the pipe and echoes to standard output. This is not a good idea; it is better to make the child (last child in the pipeline) write to the standard output directly. There are two reasons for this:
The child might write more data than fits in a pipe, so it will block waiting for something to read its output, but the reader will be blocked waiting for the child to die, so you'll have a deadlock.
It slows things up, and output from the child may well be buffered instead of appearing timely on the terminal.
I'm a little sceptical about how a three-part pipeline would be handled. You need two pipes created before you run the middle process of the three; I don't see that in your code.
I was able to fix this issue. It's probably not the right way to do it, but I saved a copy of stdin and used dup2 to reset it when the pipelining was finished.
int in_bak = dup(0);
//stuff
dup2(in_bak, 0);
close(in_bak);
In the code below, I am simply trying to send a file via stdin to a child process which will exec the cat OS command. The code compiles fine. Here is how I call it from the command line:
$ ./uniquify < words.txt
However, when I run it I get a seg fault error. I am really having a hard time understanding how the flow if information is supposed to work through pipes to children. I am trying to make the code as simple as possible, so I can understand it, but it is not yet making sense. Any help would be appreciated.
#include <unistd.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#define NUM_CHILDREN 2
int main(int argc, char *argv[])
{
pid_t catPid;
int writeFds[NUM_CHILDREN];
int catFds[2];
int c = 0;
FILE *writeToChildren[NUM_CHILDREN];
//create a pipe
(void) pipe(catFds);
if ((catPid = fork()) < 0) {
perror("cat fork failed");
exit(1);
}
//this is the child case
if (catPid == 0) {
//close the write end of the pipe
close(catFds[1]);
//close stdin?
close(0);
//duplicate the read side of the pipe
dup(catFds[0]);
//exec cat
execl("/bin/cat", "cat", (char *) 0);
perror("***** exec of cat failed");
exit(20);
}
else { //this is the parent case
//close the read end of the pipe
close(catFds[0]);
int p[2];
//create a pipe
pipe(p);
writeToChildren[c] = fdopen(p[1], "w");
} //only the the parent continues from here
//close file descriptor so the cat child can exit
close(catFds[1]);
char words[NUM_CHILDREN][50];
//read through the input file two words at a time
while (fscanf(stdin, "%s %s", words[0], words[1]) != EOF) {
//loop twice passing one of the words to each rev child
for (c = 0; c < NUM_CHILDREN; c++) {
fprintf(writeToChildren[c], "%s\n", words[c]);
}
}
//close all FILEs and fds by sending and EOF
for (c = 0; c < NUM_CHILDREN; c++) {
fclose(writeToChildren[c]);
close(writeFds[c]);
}
int status = 0;
//wait on all children
for (c = 0; c < (NUM_CHILDREN + 1); c++) {
wait(&status);
}
return 0;
}
Since your question seems to be about understanding how pipes and forks work, I hope below programs can help you. Please notice that this is for illustration only. It wouldn't qualify for commercial implementation, but I wanted to keep it short!
You can compile the two programs as follows:
cc pipechild.c -o pipechild
cc pipeparent.c -o pipeparent
Then execute with ./pipeparent
pipeparent.c source
/* pipeparent.c */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#define MESSAGE "HELLO!\n"
#define INBUFSIZE 80
#define RD 0 // Read end of pipe
#define WR 1 // Write end of pipe
int main(void)
{
int ptocpipe[2]; // Parent-to-child pipe
int ctoppipe[2]; // Chile-to-parent pipe
pid_t childpid; // Process ID of child
char inbuf[80]; // Input from child
int rd; // read() return
int rdup; // dup():ed stdin for child
int wdup; // dup():ed stdout for child
char *eol; // End of line
// Create pipe for writing to child
if (pipe(ptocpipe) < 0) {
fprintf(stderr, "pipe(ptocpipe) failed!\n");
return 2;
}
// Create pipe for writing back to parent
if (pipe(ctoppipe) < 0) {
fprintf(stderr, "pipe(ctoppipe) failed!\n");
return 2;
}
// Verify that one of the pipes are working by filling it first
// in one end and then reading it from the other. The OS will
// buffer the contents for us. Note, this is not at all necessary,
// it's just to illustrate how it works!
write(ptocpipe[WR], MESSAGE, strlen(MESSAGE));
read(ptocpipe[RD], inbuf, INBUFSIZE);
if (strlen(inbuf) != strlen(MESSAGE)) {
fprintf(stderr, "Failed to flush the toilet!\n");
return 6;
} else {
printf("Wrote to myself: %s", inbuf);
}
// Next, we want to launch some interactive program which
// replies with exactly one line to each line we send to it,
// until it gets tired and returns EOF to us.
// First, we must clone ourselves by using fork(). Then the
// child process must be replaced by the interactive program.
// Problem is: How do we cheat the program to read its stdin
// from us, and send its stdout back to us?
switch (childpid = fork()) {
case -1: // Error
fprintf(stderr, "Parent: fork() failed!\n");
return 3;
case 0: // Child process
// Close the ends we don't need. If not, we might
// write back to ourselves!
close(ptocpipe[WR]);
close(ctoppipe[RD]);
// Close stdin
close(0);
// Create a "new stdin", which WILL be 0 (zero)
if ((rdup = dup(ptocpipe[RD])) < 0) {
fprintf(stderr, "Failed dup(stdin)\n");
return 4;
}
// Close stdout
close(1);
// Create a "new stdout", which WILL be 1 (one)
if ((wdup = dup(ctoppipe[WR])) < 0) {
fprintf(stderr, "Failed dup(stdout)\n");
return 5;
}
// For debugging, verify stdin and stdout
fprintf(stderr, "rdup: %d, wdup %d\n", rdup, wdup);
// Overload current process by the interactive
// child process which we want to execute.
execlp("./pipechild", "pipechild", (char *) NULL);
// Getting here means we failed to launch the child
fprintf(stderr, "Parent: execl() failed!\n");
return 4;
}
// This code is executed by the parent only!
// Close the ends we don't need, to avoid writing back to ourself
close(ptocpipe[RD]);
close(ctoppipe[WR]);
// Write one line to the child and expect a reply, or EOF.
do {
write(ptocpipe[WR], MESSAGE, strlen(MESSAGE));
if ((rd = read(ctoppipe[RD], inbuf, INBUFSIZE)) > 0) {
// Chop off ending EOL
if ((eol = rindex(inbuf, '\n')) != NULL)
*eol = '\0';
printf("Parent: Read \"%s\" from child.\n", inbuf);
}
} while (rd > 0);
fprintf(stderr, "Parent: Child done!\n");
return 0;
}
pipechild.c source
/* pipechild.c
* Note - This is only for illustration purpose!
* To be stable, we should catch/ignore signals,
* and use select() to read.
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <strings.h>
#include <string.h>
#define MAXCOUNT 5 // Maximum input lines toread
#define INBUFSIZE 80 // Buffer size
int main(void)
{
char buff[INBUFSIZE];
int remains = MAXCOUNT;
pid_t mypid;
char *eol;
mypid = getpid(); // Process-ID
fprintf(stderr, "Child %d: Started!\n", mypid);
// For each line read, write one tostdout.
while (fgets(buff, INBUFSIZE, stdin) && remains--) {
// Chop off ending EOL
if ((eol = rindex(buff, '\n')) != NULL)
*eol = '\0';
// Debug to console
fprintf(stderr, "Child %d: I got %s. %d remains.\n",
mypid, buff, 1 + remains);
// Reply to parent
sprintf(buff, "Child %d: %d remains\n", mypid, 1 + remains);
write(1, buff, strlen(buff));
}
fprintf(stderr, "Child %d: I'm done!\n", mypid);
return 0;
}
I have a simple setup for a fork and pipe that I have used before. But this time around I'm getting a SIGPIPE in my write call. Here's the code
int fd[2];
int pid;
if (pipe(fd) == -1) {
perror("pipe init error");
exit(1);
}
// signal(SIGPIPE, SIG_IGN);
if ((pid = fork()) < -1) {
perror("fork error"); exit(1);
}
// parent
else if (pid > 0) {
close(fd[0]);
write(fd[1], "WHAT", MAXWORD); //SIGPIPE here
close(fd[1]);
int status;
wait(&status);
}
// child
else {
close(fd[1]);
// void foo(char *dirname, int in, int out);
// foo takes a path, reads from fd 'in' and outputs to 'fd' out
foo("./some/path", fd[0], 1);
close(fd[0]);
}
Here's function foo:
void foo(char *dirname, int in, int out){
int string_length;
char word[MAXWORD];
// to get rid of \n
char* sep;
sep = malloc(sizeof(char));
// read from piped stdin until it's closed
while ((string_length = read(in, word, MAXWORD)) > 0){
// get rid of \n
sep = strchr(word, '\n');
*sep = '\0';
printf("THe word is: %s\n", word);
}
}
If you get SIGPIPE when you write on a pipe, it means there is no process that can read from the pipe: neither the current process (you've close the read end of the pipe — which is good; you'd be deadlocked instead of dead if you'd not closed it) nor the other (child) process.
Since you've not shown what the function foo() does, we can't tell you any more about what's wrong.
Now that foo() has been added, it is not clear what's up. There are issues, but most are not show stoppers.
Argument dirname is unused.
Argument out is unused.
You leak the memory allocated to sep in the loop.
You do not ensure that the string read from the pipe is null terminated. This could lead to crashes, which in turn would lead to writes failing.
I suspect item 4 is the immediately critical issue; the others are more matters of tidiness.
I note that in the main code, you have:
write(fd[1], "WHAT", MAXWORD); //SIGPIPE here
Unless MAXWORD is either 4 or 5, you are on a losing path; you should only write 4 or 5 characters.
Combined with the read()...the read will attempt to read MAXWORD bytes but might get fewer. However, there's no sign that the data written contains a newline, so the search for a newline in the input is not going to work reliably. However, that problem should manifest itself after the pipe was successfully written too, not before.
I note that the variable int fd_parent_write_word[2]; is unused and the code uses variable int fd[2] without declaring it.
It is a nuisance when what you get to analyze is not an SSCCE (Short, Self-Contained, Correct Example). It is so much easier when the test case has been reduced to a simple program that can be compiled and run with the submitter confident that the problem reproduces with it.
This SSCCE code compiles cleanly and runs OK:
#include <assert.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
enum { MAXWORD = 5 };
static void foo(int in);
static void he_who_pays_the_piper(int signum)
{
assert(signum == SIGPIPE);
const char msg[] = "Received signal SIGPIPE\n";
write(2, msg, sizeof(msg)-1);
exit(1);
}
int main(void)
{
int fd[2];
int pid;
if (pipe(fd) == -1) {
perror("pipe init error");
exit(1);
}
signal(SIGPIPE, he_who_pays_the_piper);
if ((pid = fork()) < -1) {
perror("fork error"); exit(1);
}
else if (pid > 0) {
close(fd[0]);
write(fd[1], "WHAT", MAXWORD); //SIGPIPE here
close(fd[1]);
int status;
pid = wait(&status);
printf("Got status 0x%04X from %d\n", status, pid);
}
else {
close(fd[1]);
foo(fd[0]);
close(fd[0]);
}
return 0;
}
static void foo(int in)
{
int string_length;
char word[MAXWORD];
while ((string_length = read(in, word, MAXWORD)) > 0)
printf("The word is: %.*s\n", string_length, word);
}
Example output:
The word is: WHAT
Got status 0x0000 from 49458
Note that this works because the '\0' at the end of the string WHAT is written to the pipe, and read from the pipe. Most usually, you do not write the strings including the trailing '\0'.
Ok, I searched for this one but couldn't find it. Apologize if it's already been answered before.
Basically I have a program for class that creates two unnamed pipes and uses them to communicate between the parent and child process. The command is passed from the parent to the child, where the child executes it and returns a Success/Error message to the parent. Parent then prints out the Success/Error message. Easy enough, I have that working. Problem is now I need to loop it until the user gives the "exit" command. I think I need a while loop, but after experimenting with placement the program still only runs once and then exits. Here's what I have. Hopefully this makes sense, I left out the processing portion of the code since that part works (like I said, for a class) but if there's anything that doesn't make sense I'll clarify. Thanks in advance for any help.
while (strcmp(cmd,"exit") != 0)
{
/* Create Pipe P to pass command from the
parent process to the child process and check for errors.*/
pipe(p);
/*Create Pipe Q to pass command from the
child process to the parent process and check for errors. */
pipe(q);
/* Create child process */
pid = fork();
switch(pid){
case -1: /* fork failed */
perror("main: fork");
exit(1);
case 0: /* Child process */
/*****************************************
Stuff being executed in the child process
*****************************************/
default: /* Parent process */
printf ("Choose from the following list of commands.\n");
printf ("display\n");
printf ("chars\n");
printf ("lines\n");
printf ("words\n");
printf ("find\n");
printf ("exit\n");
fgets (cmd,10,stdin);
if ((c = strchr(cmd, '\n')) != NULL)
{
*c = '\0';
}
/**********************************
Pipes being opened and closed for
communication between parent and child
**************************************/
break;
}
return 0;
}
}
You need to create the child before you enter the loop.
You also need to be a lot more careful with the plumbing. The main (parent) process must close the ends of the pipes that it won't use, and the child likewise (noting that the child closes the opposite ends from the parent). Of course, if the child is reading standard input and writing on standard output, then you have to arrange for the pipes to be duplicated to the correct descriptors, and then the child closes all the descriptors returned by the pipe() call.
Try this for size - you'd have to expand be_childish() to do the real work...
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <errno.h>
static void be_childish(int p[2], int q[2]);
static void be_parental(int p[2], int q[2]);
static void err_exit(const char *fmt, ...)
{
int errnum = errno;
va_list args;
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
fprintf(stderr, "\n%d: %s\n", errnum, strerror(errnum));
exit(1);
}
int main(void)
{
int p[2]; /* Pipe to child */
int q[2]; /* Pipe to parent */
pid_t pid;
if (pipe(p) != 0)
err_exit("Failed to create pipe 1");
if (pipe(q) != 0)
err_exit("Failed to create pipe 2");
if ((pid = fork()) < 0)
err_exit("Failed to create child process");
else if (pid == 0)
be_childish(p, q);
else
be_parental(p, q);
return(0);
}
static int prompt(char *buffer, size_t buflen)
{
char *c;
printf("Choose from the following list of commands.\n");
printf("display\n");
printf("chars\n");
printf("lines\n");
printf("words\n");
printf("find\n");
printf("exit\n");
if (fgets(buffer, buflen, stdin) == 0)
return EOF;
if ((c = strchr(buffer, '\n')) != NULL)
*c = '\0';
if (strcmp(buffer, "exit") == 0)
return EOF;
return 0;
}
static void be_parental(int p[2], int q[2])
{
char cmd[10] = "";
if (close(p[0]) != 0 || close(q[1]) != 0)
err_exit("Parent: failed to close pipe");
while (prompt(cmd, sizeof(cmd)) != EOF)
{
char buffer[4096];
ssize_t nbytes;
if (write(p[1], cmd, strlen(cmd)) != (ssize_t)strlen(cmd))
err_exit("Write to child failed");
if ((nbytes = read(q[0], buffer, sizeof(buffer))) < 0)
err_exit("Read from child failed");
if (nbytes == 0)
return;
printf("%s\n", buffer);
}
}
static void be_childish(int p[2], int q[2])
{
char cmd[10] = "";
ssize_t nbytes;
if (close(p[1]) != 0 || close(q[0]) != 0)
err_exit("Child: failed to close pipe");
while ((nbytes = read(p[0], cmd, sizeof(cmd))) > 0)
{
char buffer[4096];
cmd[nbytes] = '\0';
/* Process command */
strcpy(buffer, "Response from child: ");
strcat(buffer, cmd);
if (write(q[1], buffer, strlen(buffer)) != (ssize_t)strlen(buffer))
err_exit("Write to parent failed");
}
}