Ordinary pipeline for inter process communication - c

I am learning the way to use ordinary pipeline in linux for the communication between parent and child process. The basic task is just to send a message to the child process from parent process, and then the child do some conversion and pass the result back to the parent. My result shown is some random character like ���. I have been contemplating for a long while and still couldn't figure out the bug. Thanks for your help.
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#define READ_END 0
#define WRITE_END 1
void convert(char* str);
int main(int argc, char *argv[]){
int pid; /* Process ID */
int status;
char *input;
char *read_msg_c;
char *read_msg_p;
int pfd1[2], pfd2[2];
if (argc !=2){/* argc should be 2 for correct execution */
/* We print argv[0] assuming it is the program name */
printf("Please provide the string for conversion \n");
exit(-1);
}
input = argv[1];
if(pipe(pfd1) < 0 || pipe(pfd2) < 0){
printf("Failed to create a pipe between parent and child \n");
exit(-1);
}
if((pid = fork()) < 0){ /* Fork the process */
printf("Fork error \n");
exit(-1);
}
else if(pid > 0){ /* Parent code */
close(pfd1[READ_END]);
close(pfd2[WRITE_END]);
printf("Process ID of the parent is %d. \n", getpid()); /* Print parent's process ID */
write(pfd1[WRITE_END],input,strlen(input)+1);
close(pfd1[WRITE_END]);
read(pfd2[READ_END],read_msg_p,strlen(input)+1);
printf("%s\n",read_msg_p);
close(pfd2[READ_END]);
}
else if(pid == 0){ /* Child code */
close(pfd1[WRITE_END]);
close(pfd2[READ_END]);
printf("Process ID of the child is %d. \n", getpid()); /* Print child's process ID */
read(pfd1[READ_END],read_msg_c, strlen(input)+1);
printf("Child: Reversed the case of the received string. \n");
write(pfd2[WRITE_END],read_msg_c,strlen(input)+1);
close(pfd1[READ_END]);
close(pfd2[WRITE_END]);
exit(0); /* Child exits */
}
}
void convert(char *str){
int i = 0;
while (str[i]){
if (isupper(str[i])){
str[i] = tolower(str[i]);
}
else if (islower(str[i])){
str[i] = toupper(str[i]);
}
i++;
}
}

Your primary bug is that your variables read_msg_p and read_msg_c are uninitialized pointers.
Make them into arrays:
char read_msg_p[1024];
char read_msg_c[1024];
You seem to be missing <stdio.h> (but you don't really need <sys/types.h> any more). You should error check your reads and writes; your reads will probably use a different maximum size once you've allocated the space for them. Etc.
I spotted the problem by looking at the compiler warnings:
$ gcc -O3 -g -std=c99 -Wall -Wextra pipes-14420398.c -o pipes-14420398
pipes-14420398.c: In function ‘main’:
pipes-14420398.c:40:22: warning: ‘read_msg_p’ may be used uninitialized in this function [-Wuninitialized]
pipes-14420398.c:52:22: warning: ‘read_msg_c’ may be used uninitialized in this function [-Wuninitialized]
$
Ignore the line numbers; I'd moderately seriously hacked your code by the time these were the only warnings left. But the lines in question are the read() calls.
Example output form the hacked code, working correctly.
$ ./pipes-14420398 string-to-convert
Process ID of the parent is 37327.
Process ID of the child is 37328.
Child read 18 bytes: <<string-to-convert>>
Parent read 18 bytes: <<string-to-convert>>
$
Note that the code below reads 18 bytes (including the null), but does not print the null (because of the nbytes-1 argument to printf().
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#define READ_END 0
#define WRITE_END 1
int main(int argc, char *argv[])
{
int pid; /* Process ID */
char *input;
char read_msg_c[1024];
char read_msg_p[1024];
int pfd1[2], pfd2[2];
if (argc !=2){/* argc should be 2 for correct execution */
/* We print argv[0] assuming it is the program name */
fprintf(stderr, "Usage: %s string-to-convert\n", argv[0]);
exit(-1);
}
input = argv[1];
if(pipe(pfd1) < 0 || pipe(pfd2) < 0){
printf("Failed to create a pipe between parent and child \n");
exit(-1);
}
if((pid = fork()) < 0){ /* Fork the process */
printf("Fork error \n");
exit(-1);
}
else if(pid > 0){ /* Parent code */
close(pfd1[READ_END]);
close(pfd2[WRITE_END]);
printf("Process ID of the parent is %d. \n", getpid()); /* Print parent's process ID */
write(pfd1[WRITE_END], input, strlen(input)+1);
close(pfd1[WRITE_END]);
int nbytes = read(pfd2[READ_END], read_msg_p, sizeof(read_msg_p));
if (nbytes <= 0)
printf("Parent: read failed\n");
else
printf("Parent read %d bytes: <<%.*s>>\n", nbytes, nbytes-1, read_msg_p);
close(pfd2[READ_END]);
}
else if(pid == 0){ /* Child code */
close(pfd1[WRITE_END]);
close(pfd2[READ_END]);
printf("Process ID of the child is %d. \n", getpid()); /* Print child's process ID */
int nbytes = read(pfd1[READ_END], read_msg_c, sizeof(read_msg_c));
if (nbytes <= 0)
printf("Child: read failed\n");
else
{
printf("Child read %d bytes: <<%.*s>>\n", nbytes, nbytes-1, read_msg_c);
write(pfd2[WRITE_END], read_msg_c, nbytes);
}
close(pfd1[READ_END]);
close(pfd2[WRITE_END]);
exit(0); /* Child exits */
}
}
As noted by WhozCraig, there are numerous other changes that could be made. This, however, gets things working reasonably cleanly. You were very close to OK.
Note the debugging techniques:
Compile with high warning levels and fix all warnings.
Print information as it becomes available (or run in a debugger and observe the information as it becomes available).

Related

Unexpected output while using Pipes

I am a new to pipes in C.
I am trying to Write "hello" on the pipe from a child process & read the same from parent process, but I am getting unexpected output.
I using this piece of code:
#include<stdio.h>
#include<unistd.h>
#include<unistd.h>
#include<stdlib.h>
int main()
{
pid_t pid;
int fds[2];
int ret;
char ch[20];
ret = pipe(fds);
if(ret == -1)
{
perror("pipe failed");
exit(0);
}
pid = fork();
if (pid == 0)
{
printf("Child process\n");
write(fds[1],"Hello",5);
}
if (pid > 0)
{
printf("Parent Process\n");
read(fds[0],ch,15);
printf("%s\n",ch);
}
return 0;
}
I am getting this as output :
Parent Process
Child process
Helloq.
I can't understand why this extra "q." is coming ??
You are trying to write 6 bytes but are setting the size to 5. You need to also send the '\0' at the end of Hello along.
Just change your write call to
write(fds[1],"Hello",6);
and you should be fine.
Use memset() function in your code before writing data into buffer, which fill memory with a constant byte. like,
memset(ch,'\0',20);
Full code may be help you.
#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<unistd.h>
#include<stdlib.h>
int main()
{
pid_t pid;
int fds[2];
int ret;
char ch[20];
memset(ch,'\0',20);
ret = pipe(fds);
if(ret == -1)
{
perror("pipe failed");
exit(0);
}
pid = fork();
if (pid == 0)
{
printf("Child process\n");
write(fds[1],"Hello",5);
}
if (pid > 0)
{
printf("Parent Process\n");
read(fds[0],ch,15);
printf("%s\n",ch);
}
}
Since you don't record how many bytes were read off the pipe, your code is printing the garbage that was already in the ch variable. There are numerous ways to deal with it. This code shows two of them. I used memset() to ensure that ch contained some data (and the assignment makes sure it is null terminated).
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
int main(void)
{
pid_t pid;
int fds[2];
int ret;
char ch[20];
memset(ch, 'X', sizeof(ch)-1);
ch[sizeof(ch)-1] = '\0';
ret = pipe(fds);
if (ret == -1)
{
perror("pipe failed");
exit(0);
}
pid = fork();
if (pid == 0)
{
printf("Child process\n");
write(fds[1], "Hello", 5);
}
else if (pid > 0)
{
printf("Parent Process\n");
int nbytes = read(fds[0], ch, 15);
printf("[%s]\n", ch);
printf("[%.*s]\n", nbytes, ch);
ch[nbytes] = '\0';
printf("[%s]\n", ch);
}
else
fprintf(stderr, "fork() failed\n");
return 0;
}
The code records how many bytes were written (truly diligent code would ensure that the correct amount of data was written, too). It prints the data 3 times — once using your original technique, then once using the number of bytes read off the pipe to limit the output, and then null-terminating the data so that it can be written as a simple string.
The %.*s conversion specification uses two values — a number and the string. The number is the maximum number of bytes that will be written. If the string is shorter than that, so be it. If the string is longer, the excess bytes are ignored.
Sample output:
Parent Process
[HelloXXXXXXXXXXXXXX]
[Hello]
[Hello]
Child process
This was the result of piping the program output. Visually, on the terminal, I usually got:
Parent Process
Child process
[HelloXXXXXXXXXXXXXX]
[Hello]
[Hello]
Both outputs are valid. Note how the first printing of the data also includes a number of the X's because there was no null byte read from the pipe.
Another alternative is to have the child write the null of the null-terminated string to the pipe: write(fds[1], "Hello", sizeof("Hello"));. Other options include writing the length of the string on the pipe followed by the data and then reading the length and that many bytes of data. This is a minor variant on a TLV (type, length, value) encoding system — the type is not explicitly specified as it is assumed to be char.

How to distinguish one child process from other child processes

I have an assignment for class and I am confused on this part of the requirements. So we need to make a multi process word counter with n number of processes and n will be an input argument for the program. Each process needs to do their own mini word count of a select portion of the inputted file. So essentially the inputted file will be divided into 1/n parts and split between n processes.
I understand how to fork the processes through a for loop and how to use pipes to send the mini word count from the children processes to the parent process, but I unsure of how to tell a certain process to do a select part of the input file.
Would you use their PID values to check which process they are then assign them their task?
This is my code so far.
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#define MSGLEN 64
#define MESSES 3
int main(){
int fd[2];
pid_t pid;
int result;
//Creating a pipe
result = pipe (fd);
if (result < 0) {
//failure in creating a pipe
perror("pipe error\n");
exit (1);
}
//Creating a child process
for(int i = 0; i < MESSES; i++){
if ((pid = fork()) < 0) {
//failure in creating a child
perror ("fork error\n");
exit(2);
}
if(pid == 0)
break;
}
if (pid == 0) {
// ACTUALLY CHILD PROCESS
char message[MSGLEN];
//Clearing the message
memset (message, 0, sizeof(message));
printf ("Enter a message: ");
//scanf ("%s",message);
fgets (message, 1024, stdin);
close(fd[0]);
//Writing message to the pipe
write(fd[1], message, strlen(message));
close(fd[1]);
close(fd[0]);
exit (0);
}
else {
//Parent Process
char message[MSGLEN];
char *ptr;
long wc;
close(fd[1]);
while (1) {
//Clearing the message buffer
memset (message, 0, sizeof(message));
//Reading message from the pipe
if(read(fd[0], message, sizeof(message)) == 0)
exit(0);
printf("Message entered %s\n",message);
/*
Message entered needs to be in the format of number first space then string for it to work
*/
wc = 0;
wc = strtol(message, &ptr, 10);
printf("The number(unsigned long integer) is %ld\n", wc);
printf("String part is %s", ptr);
}
close(fd[0]);
wait(NULL);
// exit(0);
}
return 0;
}
The key thing to remember when using fork is that the parent and child share the same memory and a copy of everything the parent has is passed to the child. At which point the child has now forked the parents data.
In the code below we're counting how many processes we've created. You could if you wanted use this as an argument in the child ie the nth child gets value n.
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#define PROCESS_COUNT 50
int main(void) {
pid_t pid;
size_t pid_count = 0;
//pid_t pid_array[PROCESS_COUNT];
for(int i = 0; i < PROCESS_COUNT; i++) {
if ((pid = fork()) < 0) {
perror ("fork error\n");
exit(2);
}
if (pid == 0) {//child
size_t n = 0;
size_t p = getpid();
while(n++ < 2) {
//Next line is illustration purposes only ie I'm taking liberties by
//printing a pid_t value
printf("child %zu has pid_count == %zu\n", p, pid_count);
sleep(1);
}
exit (0);
}
else {
//Count how many process we've created.
pid_count++;
int status;
waitpid( -1, &status, WNOHANG);
}
}
wait(NULL);
return 0;
}
If you want to get really fancy you can use IPC using pipes or shared memory. There are lots of ways to get data from one process to another, sometimes something as simple as temporary files is more than sufficient. For your problem I'd use mmap but it does not need to be that complicated

Working with pipes in Unix C

I am having serious trouble working with pipes in C. I'm supposed to take in arguments from the command line (example: ./myprogram 123 45 67), read the arguments one character at a time into a buffer, send the character to the child process to be counted, and then return the total number of characters read to the parent process. My code is as follows(note: the comments are what I'm supposed to be doing):
// Characters from command line arguments are sent to child process
// from parent process one at a time through pipe.
// Child process counts number of characters sent through pipe.
// Child process returns number of characters counted to parent process.
// Parent process prints number of characters counted by child process.
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
static int toChild[2];
static int fromChild[2];
static char buffer;
int main(int argc, char **argv)
{
int status;
int nChars = 0;
pid_t pid;
pipe(toChild);
pipe(fromChild);
if ((pid = fork()) == -1) {
printf("fork error %d\n", pid);
return -1;
}
else if (pid == 0) {
close(toChild[1]);
close(fromChild[0]);
// Receive characters from parent process via pipe
// one at a time, and count them.
int count = 0;
printf("child about to read\n");
while(read(toChild[0], &buffer, 1)){
count++;
}
// Return number of characters counted to parent process.
write(fromChild[1], &count, sizeof(count));
close(toChild[0]);
close(fromChild[1]);
printf("child exits\n");
}
else {
close(toChild[0]);
close(fromChild[1]);
// -- running in parent process --
printf("CS201 - Assignment 3 - Chris Gavette\n");
write(toChild[1], &argv[1], 1);
// Send characters from command line arguments starting with
// argv[1] one at a time through pipe to child process.
read(fromChild[0], &nChars, 1);
// Wait for child process to return. Reap child process.
// Receive number of characters counted via the value
// returned when the child process is reaped.
close(toChild[1]);
close(fromChild[0]);
waitpid(pid, &status, 0);
printf("child counted %d chars\n", nChars);
printf("parent exits\n");
return 0;
}
}
The child process seems to hang even though I've closed both ends of both pipes.
For starters, this is wrong.
write(toChild[1], &count, 1)
It will eventually contribute to your problem. count is a int, not char or unsigned char. You need to send sizeof(count). Also, the read-function upon hitting an error will return EOF, which is non-zero, so your child exit condition is not appropriate. it should look something like this:
while(read(toChild[0], &buffer, 1) == 1)
Finally, your parent process should cycle through each argument in argv[] sending each as a strlen sized buffer.
I'm nearly certain this is what you're trying to do. Note that in order to maintain sanity in knowing which descriptor is used for a specific purpose, I prefer using a #define to note what each process uses for reading and writing. This can be extended to any number of processes, btw, which I'm sure is not too far down the line for your next assignment:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
// P0_READ - parent read source
// P0_WRITE - parent write target
// P1_READ - child read source
// P1_WRITE - child write target
#define P0_READ 0
#define P1_WRITE 1
#define P1_READ 2
#define P0_WRITE 3
#define N_PIPES 4
int main(int argc, char **argv)
{
int fd[N_PIPES], count = 0, i;
pid_t pid;
char c;
if (pipe(fd) || pipe(fd+2))
{
perror("Failed to open pipe(s)");
return EXIT_FAILURE;
}
// fork child process
if ((pid = fork()) == -1)
{
perror("Failed to fork child process");
return EXIT_FAILURE;
}
// child process
if (pid == 0)
{
// close non P1 descriptors
close(fd[P0_READ]);
close(fd[P0_WRITE]);
// get chars from input pipe, counting each one.
while(read(fd[P1_READ], &c, 1) == 1)
count++;
printf("Child: count = %d\n", count);
write(fd[P1_WRITE], &count, sizeof(count));
// close remaining descriptors
close(fd[P1_READ]);
close(fd[P1_WRITE]);
return EXIT_SUCCESS;
}
// parent process. start by closing unused descriptors
close(fd[P1_READ]);
close(fd[P1_WRITE]);
// send each arg
for (i=1; i<argc; ++i)
write(fd[P0_WRITE], argv[i], strlen(argv[i]));
// finished sending args
close(fd[P0_WRITE]);
// Wait for child process to return.
wait(NULL);
// wait for total count
if (read(fd[P0_READ], &count, sizeof(count)) == sizeof(count))
printf("Parent: count = %d\n", count);
// close last descriptor
close(fd[P0_READ]);
return 0;
}
Input
./progname argOne argTwo
Output
Child: count = 12
Parent: count = 12
Edit: Single Pipe with Child Return Status
It seems from the comments of the original question your assignment may call for reaping the return status of the child process as the result count rather than returning it in a pipe. In doing so, you can do this with a single pipe-descriptor pair. I prefer the first method, but this works as well:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
// P0_WRITE - parent write target
// P1_READ - child read source
#define P1_READ 0
#define P0_WRITE 1
#define N_PIPES 2
int main(int argc, char **argv)
{
int fd[N_PIPES], count = 0;
pid_t pid;
char c;
if (pipe(fd))
{
perror("Failed to open pipe(s)");
return EXIT_FAILURE;
}
// fork child process
pid = fork();
if (pid == -1)
{
perror("Failed to fork child process");
return EXIT_FAILURE;
}
if (pid == 0)
{
// close non P1 descriptors
close(fd[P0_WRITE]);
// Return number of characters counted to parent process.
while(read(fd[P1_READ], &c, 1) == 1)
++count;
close(fd[P1_READ]);
printf("Child: count = %d\n", count);
return count;
}
// parent process. start by closing unused descriptors
close(fd[P1_READ]);
// eacn each arg entirely
for (int i=1; i<argc; ++i)
write(fd[P0_WRITE], argv[i], strlen(argv[i]));
// finished sending args
close(fd[P0_WRITE]);
// Wait for child process to return.
if (wait(&count) == -1)
{
perror("Failed to wait for child process");
return EXIT_FAILURE;
}
printf("Parent: count = %d\n", WEXITSTATUS(count));
return 0;
}
The results are the same, but note this is a biach to to debug as most debuggers will signal-trip on your child process and the real exit status is lost. On my Mac, for example, running this under Xcode trips:
Failed to wait for child process: Interrupted system call
while running from the command line gives:
Child: count = 12
Parent: count = 12
One of the many reasons I prefer the two-pipe methodology.

New to IPC, can't get my pipe to work

Sorry for the length of this post... I've encountered about a zillion problems in this. Up front I'll say I'm a student and my professor is a worthless resource. So, all I want to to do is have producer fork, then the parent producer will count some stuff in a file and send two ints to consumer, which was launched by the child process. I've tested everything, the fork and the file stuff works and I have printf statements all over the place so I know what is being done and where the code is at.
When I added the
if (pipe(pipefd) == -1) {
perror("pipe");
}
it caused my parent to just terminate. It reaches "parent pipe open" but then it dies. I checked with $ ps to see if it was just hung, but it's not there; it just dies. If I take that snippet out, it runs to the end but I presume if that code isn't there, then it's not actually aware that pipefd is a pipe... right?
I did search on this site and found another example of this and followed what he did as well as the answer and mine just refuses to work. I'm pretty sure it's a trivially easy thing to fix but I've run out of ideas of what to try :(
I don't really want to post all my code because it'll be a huge wall of text but I don't want to accidentally cut something out that turns out to be important either.
producer.c
#include <stdio.h> /* printf, stderr, fprintf */
#include <sys/types.h> /* pid_t */
#include <unistd.h> /* _exit, fork, execl */
#include <stdlib.h> /* exit */
#include <errno.h> /* errno */
#include <string.h> /* strlen */
#include <sys/wait.h> /* wait */
#define SLEEP_TIME 8
int main (int argc, char *argv[]){
//PID
pid_t local_pid;
local_pid = fork();
//Logic to determine if the process running is the parent or the child
if (local_pid == -1) {
/* Error:
* When fork() returns -1, an error happened
* (for example, number of processes reached the limit).
*/
fprintf(stderr, "can't fork, error %d\n", errno);
exit(EXIT_FAILURE);
} else if (local_pid == 0) {
//Child specific code
int child;
char *temp[] = {NULL};
printf("Child PID found\n");
child = execv("./consumer", temp);
_exit(0);
} else {
//Parent specific code
printf("Parent running\n");
//open file
FILE * randStrings;
randStrings = fopen("randStrings.txt", "r");
int file_length;
int num_of_e = 0;
int c; //using this as a char
//until eof
while (feof(randStrings) == 0) {
c = fgetc(randStrings);
//calculate length of file
file_length++;
//count e chars
if (c == 'e') {
num_of_e++;
}
}
//close file
fclose(randStrings);
//send bundle to child
int a[2];
a[0] = num_of_e;
a[1] = file_length;
printf("num of e = %i\n", a[0]);
printf("len = %i\n", a[1]);
//set up parent pipe
int pipefd[2];
if (pipe(pipefd) == -1) {
perror("pipe");
printf("x\n");
}
printf("parent pipe open\n");
close(pipefd[0]); //close the read end
write(pipefd[1], &a[0], sizeof(int));
write(pipefd[1], &a[1], sizeof(int));
close(pipefd[1]);
printf("parent pipe closed\n");
//wait for child to finish running
wait(NULL);
printf("parent out\n");
//terminate
}
}
and consumer.c
#include <stdio.h> /* printf, stderr, fprintf */
#include <sys/types.h> /* pid_t */
#include <unistd.h> /* _exit, fork, execl */
#include <stdlib.h> /* exit */
#include <errno.h> /* errno */
#define SLEEP_TIME 5
int main (int argc, char *argv[]){
sleep(SLEEP_TIME);
printf("Child program launched\n");
//receive bundle
int pipefd[2];
int buf[2];
if (pipe(pipefd) == -1) {
perror("pipe");
printf("child x\n");
}
close(pipefd[1]); //child closes write end
buf[0] = 0;
buf[1] = 0;
/*int i = 0; // i dont like this
while (read(pipefd[0], &buf[i], sizeof(int)) > 0) {
i++;
}*/
printf("child reading pipe\n");
read(pipefd[0], &buf[0], sizeof(int));
read(pipefd[0], &buf[1], sizeof(int));
close(pipefd[0]);
//buf should have the stuff in it
int num_of_e = buf[0];
int file_length = buf[1];
printf("child num of e = %i\n", num_of_e);
printf("child len = %i\n", file_length);
//open file
FILE * resultStrings;
resultStrings = fopen("resultStrings.txt", "w");
for (int i = 0; i < num_of_e; i++) {
//write num_of_e e chars
fputc('e', resultStrings);
}
//or if no e chars, write - chars
if (num_of_e == 0) {
for (int i = 0; i < file_length; i++) {
//write file_length '-' chars
fputc('-', resultStrings);
}
}
//close file
fclose(resultStrings);
printf("child out\n");
}
if you're still here after all that, you deserve a thank you just due to the length of this.
You're doing it wrong. The whole mechanism works because a child process inherits the parent's open file descriptors.
It should go like this:
Open the pipe with pipe(pipefd)
fork()
Parent (producer):
closes the read side (pipefd[0])
writes to the write side (pipefd[1])
Child (consumer):
closes the write side (pipefd[1])
reads from the read side (pipefd[0]) or calls exec
You are opening distinct pipes in both the parent and child process (after you've forked.) It needs to happen before you fork.
Now since you're execing, the new process needs to be aware of read-only pipe. There are a couple ways you could do this:
Pass it the file descriptor number (pipefd[0]) on the command line
dup2(1, fd) it to be the stdin of the newly exec'd process

SIGPIPE in a simple two process program

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'.

Resources