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");
}
}
Related
I work on a program that downloads the best (marked as '(best)') video format using youtube-dl. It reads a command-line argument then it launches a child process 'youtube-dl -F [url]'. Then it passes the line with '(best)' to a routine that extracts the format and executes, again as a child, 'youtube-dl -f [best format] [url]'. The problem is it works only for the first link. Maybe a child doesn't write to a pipe properly, maybe a parent doesn't read from the pipe. I'm lost. Thanks for your help.
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
#define LINE_LEN 255
enum { ERROR=-1, CHILD };
void error(char *msg)
{
fprintf(stderr, "%s: %s\n", msg, strerror(errno));
exit(1);
}
void dl_best(char *format, char *url)
{
char fmt[4];
int status;
pid_t pid;
for (int i = 0; *format != ' '; format++, i++)
fmt[i] = *format;
fmt[3] = '\0';
switch(pid = fork()) {
case ERROR:
error("Failed to pipe in dl_best");
break;
case CHILD:
if (execlp("youtube-dl", "youtube-dl", "-f", fmt, url, NULL) == -1)
error("Failed to execle() in dl_best");
break;
default:
if (waitpid(pid, &status, 0) == -1)
error("Waitpid failed in dl_best()");
break;
}
}
void get_format(char *url)
{
pid_t pid;
int fd[2], status;
char line[LINE_LEN];
if (pipe(fd) == -1)
error("Pipe failed");
if ((pid = fork()) == ERROR) {
error("Failed to create a child precess in get_format()");
} else if (pid == CHILD) {
if (close(fd[0]) == -1)
error("Child failed to close reading pipe");
if (dup2(fd[1],1) == -1)
error("Dup2 failed in get_format()");
if (execlp("youtube-dl", "youtube-dl", "-F", url, NULL) == -1)
error("Failed to execute get_formats");
} else { //parent
if (close(fd[1]) == -1)
error("Parent failed to close writing pipe");
if (dup2(fd[0],0) == -1)
error("Dup2 failed in get_format()");
if (waitpid(pid, &status, 0) == -1)
error("Waitpid failed in get_format()");
while (fgets(line, LINE_LEN, stdin)) {
if (strstr(line, "(best)") != NULL)
dl_best(line, url);
}
if (close(fd[0]) == -1)
error("Parent failed to close reading pipe");
}
}
int main(int argc, char *argv[])
{
//int fd[2], status, argc_cp = argc;
//dl_best("22 ", argv[--argc]);
while (--argc)
get_format(argv[argc]);
return 0;
}
if (dup2(fd[0],0) == -1)
error("Dup2 failed in get_format()");
if (waitpid(pid, &status, 0) == -1)
error("Waitpid failed in get_format()");
while (fgets(line, LINE_LEN, stdin)) {
if (strstr(line, "(best)") != NULL)
dl_best(line, url);
}
This is a common mistake when piping from child to parent in C. With this code, the child will fill up the pipe buffer and then block waiting for the parent to drain the pipe, but the parent won't ever do that because it's blocked waiting for the child to exit, so the overall program will deadlock.
(You won't hit the deadlock for invocations where the child's complete output is smaller than the size of the pipe buffer. This may be why it appears to work for the first command line argument only.) You need to read all of the data produced by the child before you wait for the child to terminate. For this program, that's as simple as moving the waitpid and its conditional below the while loop.
Your repeated replacement of file descriptor 0 may also be running foul of the C99 rule that end-of-file is a sticky condition. (This may also explain why it appears to work for the first command line argument only.) You could address that by calling clearerr after the dup2, but it would be better not to mess with stdin at all. Instead, use fdopen to convert fd[0] into a FILE.
Putting both of those fixes together, your parent-side code in get_format should look something like this:
} else { //parent
if (close(fd[1]) != 0)
error("Parent failed to close writing pipe");
FILE *fp = fdopen(fd[0], "rt");
if (!fp)
error("Parent failed to allocate a FILE");
while (fgets(line, LINE_LEN, fp)) {
if (strstr(line, "(best)") != NULL)
dl_best(line, url);
}
if (fclose(fp) != 0)
error("Parent failed to close reading pipe");
if (waitpid(pid, &status, 0) != pid)
error("Waitpid failed in get_format()");
}
(Note that fd[0] is closed together with fp, by the fclose; it is not necessary (in fact, it would be wrong) to call close on it.)
I would also consider allowing the dl_best child to run asynchronously - that is, have dl_best return the child PID rather than waiting for it itself, and then get_format waits for both children after its while loop - but that's an optimization, not a bugfix.
I have user read/write permissions on a pipe. Group has read. Other has read. But program gets "stuck" when I run it. Program 1 is the "parent". Program 2 is the "child".
Program 1:
int main(int argc, char * argv[])
{
FILE *fptr; //for opening and closing input file
int fdw;// write to pipe;
int fdr; //read to pipe;
pid_t pid;
int inputarray[500];
int arraylength = 0; int j =0;
char *mypipe = "mypipe";
if (argc < 2)
{
printf("Need to provide the file's name. \n");
return EXIT_FAILURE;
}
//open input file
fptr = fopen(argv[1], "r");
if (fptr==NULL)
{
printf("fopen fail.\n");
return EXIT_FAILURE;
}
//read input file and fill array with integers
while (!feof(fptr))
{
fscanf(fptr,"%d",&inputarray[arraylength]);
arraylength = arraylength + 1;
}
fclose(fptr); //close input file
pid = fork();
mkfifo(mypipe, 0666);
fdw = open("mypipe",O_WRONLY);
if (fdw < 0)
{
perror("File can't open to write.");
return;
}
int b;
b=3;
write(fdw,&b,sizeof(b));
close(fdw);
if ( pid ==-1)
{
perror("fork");
exit(1);
}
int status; //exit status of child
if(pid==0)//if child process
{
execl("program2", (char*) NULL);
}
else //if parent process
{
wait(&status);}
if((WIFEXITED(status)))
{
printf("Child's exit code %d", WEXITSTATUS(status));
}
else{
printf("Child did not terminate with exit");}
}
Program 2:
int fdl;
int data;
fdl = open("mypipe",O_RDONLY);
if ( fdl < 0)
{
perror("File can't open to read.");
return;
}
read(fdl,&data,sizeof(data));
close(fdl);
The program will block on writing to the fifo until what it's writing is being read. The reading in the child process won't happen since the execl() doesn't happen until after the writing.
Also, it looks like both processes will actually attempt to write to the fifo since you fork() and then immediately start writing.
You should fork(), then test on the returned PID. The parent should then write to the fifo while the child should call execl(). The fifo should be created by the parent before the fork() call.
You should also consider using indent or clang-format to properly format your code, which eases reading it and may expose bugs (forgotten curly braces etc.).
A simple complete example program. The parent writes a string to the child and the child reads it character by character and outputs it to standard output:
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
void parent(void);
void child(void);
int main(void) {
pid_t pid;
mkfifo("myfifo", 0666); /* fails if exists, but we don't care here */
if ((pid = fork()) < 0)
abort();
if (pid == 0)
child(); /* will not return */
else
parent();
return EXIT_SUCCESS;
}
void parent(void) {
int fd;
int len;
int ret;
int stat;
char *ptr;
char *msg = "Hello World!";
if ((fd = open("myfifo", O_WRONLY)) < 0)
abort();
len = strlen(msg) + 1;
ptr = msg;
puts("Parent: About to write to child");
while ((ret = write(fd, ptr, len)) != 0) {
if (ret > 0) {
len -= ret;
ptr += ret;
} else
abort();
}
close(fd);
puts("Parent: Waiting for child to exit");
wait(&stat);
printf("Parent: Child exited with status %d\n", stat);
}
void child(void) {
int fd;
int ret;
char ch;
if ((fd = open("myfifo", O_RDONLY)) < 0)
abort();
puts("Child: About to read from parent");
while ((ret = read(fd, &ch, 1)) != 0) {
if (ret > 0)
putchar(ch);
else
abort();
}
putchar('\n');
close(fd);
puts("Child: I'm done here");
exit(EXIT_SUCCESS);
}
In this case, since both child and parent processes are in the same context, I could have used an anonymous pipe pair created with pipe(), but this illustrates the flow, including the creation of the named pipe.
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;
}
I am trying to send my command line arguments through from the child process to the parent process using a pipe but can't figure out what I'm doing wrong. My code is below. Any help is appreciated. Thanks in advance.
int main(int argc, char argv[])
pid_t child;
int fd[2];
pipe(fd);
if((child = fork() == 0)
{
int len = strlen(argv[1]);
close(fd[0];
write(fd[1], argv[1], len);
exit(0);
}
else //Assuming process won't fail for now
{
char src[10]; //Just using 10 for now, no arguments have more than 10 characters
read(fd[0], src, (strlen(src)));
fprintf(stderr, "%s\n", src);
close(fd[0]);
}
}
You had a bunch of little errors but as far as I can see, believe it or not, this may be your real problem.
read(fd[0], src, (strlen(src)));
My guess is that the first char is null and you are successfully reading 0 bytes.
Change to
read(fd[0], src, (sizeof(src)));
In your larger project make sure you read and write in loops. You are not guaranteed to read or write what you specify.
You may need to close fd[1] inside the else block first.
check this example
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
int
main(int argc, char *argv[])
{
int pipefd[2];
pid_t cpid;
char buf;
if (argc != 2) {
fprintf(stderr, "Usage: %s <string>\n", argv[0]);
exit(EXIT_FAILURE);
}
if (pipe(pipefd) == -1) {
perror("pipe");
exit(EXIT_FAILURE);
}
cpid = fork();
if (cpid == -1) {
perror("fork");
exit(EXIT_FAILURE);
}
if (cpid == 0) { /* Child reads from pipe */
close(pipefd[1]); /* Close unused write end */
while (read(pipefd[0], &buf, 1) > 0)
write(STDOUT_FILENO, &buf, 1);
write(STDOUT_FILENO, "\n", 1);
close(pipefd[0]);
_exit(EXIT_SUCCESS);
} else { /* Parent writes argv[1] to pipe */
close(pipefd[0]); /* Close unused read end */
write(pipefd[1], argv[1], strlen(argv[1]));
close(pipefd[1]); /* Reader will see EOF */
wait(NULL); /* Wait for child */
exit(EXIT_SUCCESS);
}
}
You have assumed that fork() will not fail.
But what about pipe()??
Assume both get completed successfully, then closing fds properly is requered.
your if-else blocks should be like this.
if((child = fork() == 0)
{
int len = strlen(argv[1]);
close(fd[0]);//I assume this was your typo. otherwise it would not even get compiled
write(fd[1], argv[1], len);
close(fd[1]);
exit(0);
}
else //Assuming process won't fail for now
{
close(fd[1]);
char src[10]; //Just using 10 for now, no arguments have more than 10 characters
read(fd[0], src, (strlen(src)));
fprintf(stderr, "%s\n", src);
close(fd[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'.