Using pipes to pass data between child processes in C - c

My homework task is to write a C program that creates 4 child processes and each child has to do something with an integer number and send it to the next child that does something else with it and the last one has to print the changed value. I have to use anonymous pipes to communicate between children. Parent process has no other work than to open pipes for children. I have written the program, but the problem is I get funny output when I try to print the number using the last child. It prints out 8 numbers instead of one (actually one of them is the right one). The part of code is following:
int pipe1[2];
int pipe2[2];
int pipe3[2];
pipe(pipe1);
pipe(pipe2);
pipe(pipe3);
int j;
close(pipe1[1]); //close pipes for writing on parent process
close(pipe2[1]);
close(pipe3[1]);
for (j = 0; j < 4; j++) {
switch(fork()) {
case 0:
if (j == 0) {
int value = 100;
write(pipe1[1], &value, sizeof(int));
close(pipe1[1]);
}
else if (j == 1) {
int value;
read(pipe1[0], &value, sizeof(int));
close(pipe1[0]);
value = value * 10;
write(pipe2[1], &value, sizeof(int));
close(pipe2[1]);
}
//and so on until the last process
else if (j == 3) {
int value;
read(pipe3[0], &value, sizeof(int));
char buf[4] = {0};
memset(buf, 0, sizeof(buf));
snprintf(buf, sizeof(value), "%d ", value);
write(1, buf, strlen(buf));
}
break;
}
}
close(pipe1[0]);
close(pipe2[0]);
close(pipe3[0]);
sleep(1);
int k;
for (k = 0; k < 4; k++) {
wait(0);
}
What do I have to do for this case, just to get one (and the proper one) output?

What do you think happens when one of your child processes executes the break statement in the code you presented? Hint: it doesn't exit.
Additionally, I'm doubtful that your pipes can work quite like that. You close the write ends of two of them (one of those twice) before forking any children. Those will not magically be reopened for the children, so their whole pipes are useless.

Related

Minibash in C, problem making pipes between execvp and parent proccess

I have to do this as a university project so I cant share the whole code, im sorry for that.
I have to create a function called "read" that enables the user to create new env variables, thats the easy part. The problem comes when I call that function as the last one of the commands array e.g "ls | grep aux.txt | read a" this should give the env var A the value aux.txt, the problem is that it get stuck in the
fgets(value, sizeof(value),stdin);
and I cant even recover the terminal.
Thanks in advance for the help if you need more info about the problem I will happily give it.
I can't reproduce exactly the main function as there are parts that are not mine but I hope this helps:
char **argvv;
int fd[2][2];
int pid;
int main(int argc, char ***argvv) {
argvv[0][0] = "echo";
argvv[0][1] = "elpmaxe";
argvv[1][0] = "rev";
argvv[2][0] = "read";
argvv[2][1] = "a";
for (int i = 0; i < 2; i++) {
pipe(fd[i]);
}
for(int i = 0; i< 3; i++){
pid = fork();
if(pid == 0){
if(i ==0){
dup2(fd[0][1], 1);
fun_close(fd);
execvp(argvv[0][0], argvv[0]);
}
if(i == 1){
dup2(fd[0][0], 0);
dup2(fd[1][1], 1);
fun_close(fd);
execvp(argvv[1][0], argvv[0]);
}
}else{
if(i == 2){
close(fd[0][1]);
close(fd[0][0]);
fun_read("read a", 3, fd[1]);
}
}
}
int corpse;
int status;
while ((corpse = wait(&status)) > 0)
printf("Child %d exited with status 0x%.4X\n", corpse, status);
return 0;
void fun_close(int **fd){
close(fd[0][0]);
close(fd[0][1]);
close(fd[1][0]);
close(fd[1][1]);
}
And here is the fun_read:
int fun_read(char **command, int argc, int fd[]){
char **env_varv;
char value[1024];
char last_var[1024];
long size = 0;
char *token;
int status;
char *delim = " \t\n";
env_varv = malloc((argc-1) * sizeof(char *));
for(int i = 1; i < argc; i++){
env_varv[i-1] = strdup(command[i]);
wait(status);
}
if (fd[0] !=0){
printf("%d\n", fd[0]);
dup2(fd[0],0);
close(fd[0]);
close(fd[1]);
}
fgets(value, sizeof(value),stdin);
int i = 0;
token = strtok(value, delim);
last_var[0] = '\0';
while(token != NULL){
if(i == argc-2){
while (token != NULL){
strcat(last_var,token);
setenv(env_varv[i],last_var,1);
token = strtok(NULL,delim);
strcat(last_var," ");
}
}
else if (env_varv[i] != NULL){
setenv(env_varv[i],token,1);
token = strtok(NULL,delim);
i++;
}
else{
break;
}
}
return 0;
The program should put an envariomental variable called a with the value of example.
postscript: it seems like there is no problem if the previous command is a builtin "echo hi | echo hi2 | read a" $a=hi2
Sincerely I have tried all, changing the pipes doesnt work, changing fgets for read doesn't help either. Is the only part of the code I haven't been able to fix
This fragment of code shows some problems:
char ***argvv;
int fd[2][2];
int pid;
int main(int argc, char ***argvv) {
argvv[0][0] = "echo";
argvv[0][1] = "elpmaxe";
argvv[1][0] = "rev";
argvv[2][0] = "read";
argvv[2][1] = "a";
for (int i = 0; i < 2; i++) {
pipe(fd[i]);
}
for(int i = 0; i< 3; i++){
pid = fork();
if(pid == 0){
if(i ==0){
close(fd[0][0]);
close(fd[1][1]);
close(fd[1][0]);
dup2(fd[0][1], 1);
execvp(argvv[0][0], argvv[0]);
}
if(i = 1){
close(fd[0][1]);
close(fd[1][0]);
dup2(fd[0][0], 0);
dup2(fd[1][1], 1);
execvp(argvv[1][0], argvv[0]);
}
if(i = 2){
close(fd[0][1]);
close(fd[0][0]);
close(fd[1][1]);
dup2(fd[1][0], 0);
fun_read("read a", 3, fd[1]);
}
}
}
Rule of Thumb
You aren't closing enough pipe file descriptors in any of the processes.
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 or F_DUPFD_CLOEXEC.
Other comments on the use of pipes
If the parent process will not communicate with any of its children via
the pipe, it must ensure that it closes both ends of the pipe early
enough (before waiting, for example) so that its children can receive
EOF indications on read (or get SIGPIPE signals or write errors on
write), rather than blocking indefinitely.
Even if the parent uses the pipe without using dup2(), it should
normally close at least one end of the pipe — it is extremely rare for
a program to read and write on both ends of a single pipe.
Note that the O_CLOEXEC option to
open(),
and the FD_CLOEXEC and F_DUPFD_CLOEXEC options to fcntl() can also factor
into this discussion.
If you use
posix_spawn()
and its extensive family of support functions (21 functions in total),
you will need to review how to close file descriptors in the spawned process
(posix_spawn_file_actions_addclose(),
etc.).
Note that using dup2(a, b) is safer than using close(b); dup(a);
for a variety of reasons.
One is that if you want to force the file descriptor to a larger than
usual number, dup2() is the only sensible way to do that.
Another is that if a is the same as b (e.g. both 0), then dup2()
handles it correctly (it doesn't close b before duplicating a)
whereas the separate close() and dup() fails horribly.
This is an unlikely, but not impossible, circumstance.
Analyzing your code
The parent process has the pipes open; if the commands are reading from the pipes, they won't get EOF until the parent process closes them. Although you close most of the pipes in the child processes, you don't close those that you duplicate to the standard I/O channels — and yet that is required too.
Note that if (i = 1) should be if (i == 1), and if (i = 2) should be if (i == 2). The first of those bugs prevents your fun_read() from being invoked — which is why it isn't responding. Using diagnostic printing to standard error would confirm that fun_read() is never called.
So, at bare minimum, you need to have code like this:
char ***argvv;
int fd[2][2];
int pid;
int main(int argc, char ***argvv)
{
argvv[0][0] = "echo";
argvv[0][1] = "elpmaxe";
argvv[1][0] = "rev";
argvv[2][0] = "read";
argvv[2][1] = "a";
for (int i = 0; i < 2; i++)
{
pipe(fd[i]);
}
for (int i = 0; i < 3; i++)
{
pid = fork();
if (pid == 0)
{
if (i == 0)
{
dup2(fd[0][1], 1);
close(fd[0][0]);
close(fd[0][1]);
close(fd[1][0]);
close(fd[1][1]);
execvp(argvv[0][0], argvv[0]);
fprintf(stderr, "failed to execute %s\n", argvv[0][0]);
exit(EXIT_FAILURE);
}
if (i == 1)
{
dup2(fd[0][0], 0);
dup2(fd[1][1], 1);
close(fd[0][0]);
close(fd[0][1]);
close(fd[1][0]);
close(fd[1][1]);
execvp(argvv[1][0], argvv[0]);
fprintf(stderr, "failed to execute %s\n", argvv[1][0]);
exit(EXIT_FAILURE);
}
if (i == 2)
{
dup2(fd[1][0], 0);
close(fd[0][0]);
close(fd[0][1]);
close(fd[1][0]);
close(fd[1][1]);
fun_read("read a", 3, fd[1]);
exit(EXIT_SUCCESS);
}
}
}
close(fd[0][0]);
close(fd[0][1]);
close(fd[1][0]);
close(fd[1][1]);
/* wait loop here - and not before */
int corpse;
int status;
while ((corpse = wait(&status)) > 0)
printf("Child %d exited with status 0x%.4X\n", corpse, status);
return 0;
}
Note that it is important to handle failure to execute. And error messages should be reported to standard error, not to standard output.
Given that the same sequence of 4 calls to close() is made 4 times, a function to do the job seems appropriate. You could make it:
static inline void close_pipes(int fd[2][2])
{
close(fd[0][0]);
close(fd[0][1]);
close(fd[1][0]);
close(fd[1][1]);
}
There is a decent chance the compiler will inline the function, but it is easier to see that the same 4 descriptors are closed if one function always does the closing. For bigger arrays of pipes (more processes), you'd have a loop inside the close_pipes() function with a counter as well as the array.
There are still some issues to be resolved, notably with the fun_read() function. The fd[1] file descriptors were both closed, so passing those to fun_read() doesn't seem likely to be useful. Since fun_read() is executed in a separate process, any changes made by fun_read() won't be reflected in the parent process. There are probably other problems too.
AFAICT, on looking at fun_read() more closely, the fd argument should not be needed at all. The paragraph of code:
if (fd[0] != 0) {
printf("%d\n", fd[0]);
dup2(fd[0], 0);
}
is not useful. You've already redirected standard input so it comes from the pipe and then closed the pipe file descriptor. This paragraph then changes standard input to come from the closed descriptor, which isn't going to help anything. But none of this helps you with the fact that anything done by fun_read() is done in a child process of your shell, so the environment in the main shell is not going to be affected.

How to implement a logger function as a child process in c?

I've been struggling to implement a logger function in C that records when messages are written to a text file using communication via a pipe. In the simplified implementation below I'm trying to write messages from the parent process and print them from the child process without the file I/O but I don't ever get the child printfs.
In my main function, I spawn the logger by calling spawn_logger which forks a child process (log_message) that will run continuously. The parent process returns to the main, starts to send messages through the pipe, and finally kills the child process.
The main function:
int main(void){
spawn_logger();
char wmsg[] = "Greetings";
send_message(wmsg);
strcpy(wmsg, "Hello");
send_message(wmsg);
kill_child();
return 0;
}
The spawn_logger function:
// global vars
pid_t pid;
int fd[2];
int spawn_logger() {
if (pipe(fd) == -1) {
printf("Pipe failed\n");
return -1;
}
pid = fork();
if (pid < 0) { // fork error
printf("fork failed");
return -1;
}
if (pid > 0) { // parent process
close(fd[READ_END]);
return 0; // return to main
}
// child process
// spawn the receiver process
log_message();
// the receiver process will never reach this point
return 0;
}
The send_message function:
int send_message(char message[]){
// do something with the message
// e.g. write in a file
printf("Message by parent sent: %s \n", message);
// write the message to logger process
int n = strlen(message) + 1;
write(fd[WRITE_END], &n, sizeof(int));
write(fd[WRITE_END], &message, sizeof(char) * strlen(message));
return 0;
}
The log_message and kill_child functions:
// global vars
extern pid_t pid;
extern int fd[2];
int log_message(){
//child process
// will read from the pipe every time the parent process writes to it
close(fd[WRITE_END]);
int n;
char *message;
// read messages until parent process closes the pipe
while (read(fd[READ_END], &n, sizeof(int)) > 0) {
message = malloc(sizeof(char) * n);
read(fd[READ_END], &message, sizeof(char) * n);
printf("Message by logger received: %s \n", message);
}
close(fd[READ_END]);
exit(0);
}
int kill_child(){
close(fd[WRITE_END]);
kill(pid, SIGKILL);
return 0;
}
When I run the program all I get are the print messages printf("Message by parent sent: %s \n", message); and I think the problem comes from log_message.
I thought the child process would remain stuck in the while loop trying to read the buffer as long as the parent's write end is open but while debugging the child process in Clion I noticed that once it reaches the first line the program just stops. When I debug the parent process it just goes over all the writing instructions without any broken pipe errors.
How can I fix that? Thanks in advance for the help.
You don't send the null-terminator.
But the value of n includes the null-terminator.
This means that the child process will wait forever for the null-terminator that never comes.
There's an even worse problem: You use &message when sending the message.
This is worse, because message is not the array but a pointer to the first character of the array. So &message is a pointer to the pointer. So you write the pointer itself, plus some indeterminate data.
Drop the pointer-to operator & from that call to write:
write(fd[WRITE_END], message, n);
Note that I also updated the above to send the null-terminator.
After solving the issues pointed out by by andrew-henle and some-programmer-dude, I found the following solution to work the best.
int log_message(){ //child process
close(fd[WRITE_END]);
char buffer[BUFSIZ];
char message[BUFSIZ];
FILE * log;
log = fopen("gateway.log", "a");
// read is looping over every byte in the pipe
// and is a blocking call until there's something to read
// or the pipe is closed
while(read( fd[READ_END], buffer, BUFSIZ) > 0 ) {
int j = 0;
memset(message, ' ', BUFSIZ); // make sure its empty
// look for null terminator in buffer
for(int i= 0; i < BUFSIZ; i++){
// copy every byte until the null terminator
message[i-j] = buffer[i];
if(buffer[i] == '\0'){
if(message[0] != '\0'){
printf("Message by logger received: %s \n", message);
}
buffer[i] = ' ';
// reset j such that i - j is 0 for the next char of the buffer
j = i + 1;
}
}
memset(message, ' ', BUFSIZ); // clear message
}
fclose(log); // for now, we'll open and close the log file every time
close(fd[READ_END]);
kill(getpid(), SIGSEGV);
return 0;

Get pid from brother process

I want to have a parent process and three child processes. I want these child processes to know the pids of the other child processes.
The problem is that when I do fork and then I do it again, the second fork is also executed in the child process creating an extra process (or so I think).
How could I solve it?
Thanks.
The parent should fork three times, the children should not fork. This way, the parent will know the pids of all three children.
After the fork, you'll need some kind of separate communication channel by which the parent can communicate these pids to all children. A simple way would be to open a pipe (see pipe(2)) before forking each child, so the child inherits the pipe's file descriptor (at least the read end) and the parent keeps the write end. Then have the parent send the three pids down each pipe and close it.
Example code (long, but that's the nature of C):
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#define NUM_CHILDREN 3
/* Entry point for the child processes */
int child_main(int pipe_read_end) {
pid_t my_pid = getpid();
/* Read child pids from pipe */
int child_pids[NUM_CHILDREN];
unsigned int bytes_read = 0;
while (bytes_read < sizeof(child_pids)) {
ssize_t result = read(pipe_read_end, ((unsigned char *) child_pids) + bytes_read, sizeof(child_pids) - bytes_read);
if (result < 0) {
perror("error reading from pipe");
return 1;
} else if (result == 0) {
fprintf(stderr, "unexpected end of file\n");
return 1;
} else {
bytes_read += result;
}
}
close(pipe_read_end);
/* Do something useful with these child pids */
for (int i = 0; i < NUM_CHILDREN; i++) {
printf("Child %d received sibling pid %d\n", my_pid, child_pids[i]);
}
return 0;
}
/* Entry point for the parent process. */
int main() {
int child_pids[NUM_CHILDREN];
int pipe_write_ends[NUM_CHILDREN];
for (int i = 0; i < NUM_CHILDREN; i++) {
/* Create the pipe for child i */
int pipefd[2];
if (pipe(pipefd)) {
perror("error creating pipe");
return 1;
}
int pipe_read_end = pipefd[0];
int pipe_write_end = pipefd[1];
/* Fork child i */
pid_t child_pid = fork();
if (child_pid < 0) {
perror("error forking");
return 1;
} else if (child_pid == 0) {
printf("Child %d was forked\n", getpid());
close(pipe_write_end);
return child_main(pipe_read_end);
} else {
printf("Parent forked child %d\n", child_pid);
close(pipe_read_end);
pipe_write_ends[i] = pipe_write_end;
child_pids[i] = child_pid;
}
}
/* Send pids down the pipes for each child */
for (int i = 0; i < NUM_CHILDREN; i++) {
unsigned int bytes_written = 0;
while (bytes_written < sizeof(child_pids)) {
ssize_t result = write(pipe_write_ends[i], ((unsigned char *) child_pids) + bytes_written, sizeof(child_pids) - bytes_written);
if (result < 0) {
perror("error writing to pipe");
return 1;
} else {
bytes_written += result;
}
}
close(pipe_write_ends[i]);
}
/* Wait for children to exit */
for (int i = 0; i < NUM_CHILDREN; i++) {
if (waitpid(child_pids[i], 0, 0) < 0) {
perror("error waiting for child");
return 1;
}
}
}
As #PSkocik points out in their answer, you should probably not be doing this. Pids can be reused by the OS, so there's no way for the children to know that their sibling pids still actually refer to their siblings; only the parent can be sure, because it has to wait for each pid before it can be reused.
However, this same mechanism can be used for other forms of IPC (inter-process communication); you could, for example, use it to create pipes between the children directly.
You can use shared memory or some other kind of IPC to communicate the PIDs, but you probably shouldn't even try.
PIDs are subject to recycling and you can only ever know for sure if a PID refers to the process you think it refers to if that PID belongs to a child process of yours (because then you can know if you've waited on it or not).
Otherwise, PIDs (of non-children) are racy references which are basically only usable for hacky debugging.

How processes work sequentially with C pipe?

I want to do that 2 child processes will put their names and wait until other process put his name. For instance, if there are first and second process, first will put her name and will wait for other's name in screen. So I want to work with processes and I wanna to see they are working sequentially.
Output:
first
second
first
second
first
second
I just tried something about C(linux).
int main(void)
{
pid_t child_a, child_b;
int pipe1[2], pipe2[2];
char mesazhi1[] = "first";
char mesazhi2[] = "second";
char buf[1024];
int first_pipe = pipe(pipe1);
pipe(pipe2);
if(first_pipe == -1){
perror("pipe");
exit(1);
}
child_a = fork();
if (child_a == 0)
{
/* Child A code */
int i;
for (i = 0; i < 3; i++)
{
write(pipe1[1],mesazhi1, strlen(mesazhi1) + 1);
//printf("first\n");
int a = read(pipe2[0], buf, strlen(mesazhi2) + 1);
printf("%s - %d\n", buf, a);
}
}
else
{
child_b = fork();
if (child_b == 0)
{
int i;
for (i = 0; i < 3; i++)
{
write(pipe2[1],mesazhi2, strlen(mesazhi2) + 1);
//printf("second\n");
int a = read(pipe1[0], buf, strlen(mesazhi1) + 1);
printf("%s - %d\n", buf, a);
}
}
else
{
/* Parent Code */
int returnStatusA,returnStatusB;
waitpid(child_a, &returnStatusA, 0); // Parent process waits here for child to terminate.
waitpid(child_b, &returnStatusB, 0); // Parent process waits here for child to terminate.
if (returnStatusA == 0 && returnStatusB == 0) // Verify child process terminated without error.
{
printf("%s\n", "The child processes terminated normally.\n");
}
if (returnStatusA == 1 && returnStatusB == 1)
{
printf("%s\n", "The child processes terminated with an error!. \n" );
}
}
}
}
It is putting name randomly. I mean that I think, sometimes second process works faster than first. Output like that:
first
second
second
first
second
...
So why second process doesn't wait for first one, because I think that read() function should wait until there is something in pipe1.
In the posted code, both processes write to their respective pipes, and then read. After that, it's a race to see which process gets to print first.
For a more controlled situation, have child B call read and printf before calling write. That way B has to wait for A before printing, and vice versa.
if (child_b == 0)
{
int i;
for (i = 0; i < 3; i++)
{
int a = read(pipe1[0], buf, strlen(mesazhi1) + 1);
printf("%s - %d\n", buf, a);
write(pipe2[1],mesazhi2, strlen(mesazhi2) + 1);
}
}

Forking in loops

Say I have a process. I fork it, then it has a parent and a child.
I want the parent to write from 2 to n to a pipe and the child to read from it.
The child will pass through each value through some conditions, and they don't pass any of the conditions, it will go back to the parent by calling exit().
In the parent, I will need to fork the original process and now the current parent will read 3 into the fd used in the master process and write to the newly created child, which goes through what the previous child went through.
if (pid > 0){ //parent which writes n to fd
close(fd[0]); //close read
for (j = 2; j <= n; j++){
if (write(fd[1], &j, sizeof(int)) == -1){ //write j = 2 to fd
perror("write j");
}
}
close(fd[1]); //close write
int status;
if(wait(&status) != -1){
if (WIFEXITED(status)){
if (WEXITSTATUS(status) == 2){
pid = fork() //should I even be calling fork here?
}
}
}
else{ //CHILD
close(fd[1]); //close write
for (j = 2; j <= n; j++){
if (read(fd[0], &j, sizeof(int)) == -1){ //read j from fd
perror("read j");
}
if (SOME CONDITION){
exit(2);
}
So far this only gets me through value 2, and I'm not sure make the parent send the value 3 into the next child.
Here's a diagram if my explanation was confusing.
Any help would be much appreciated, thanks!
You'll have to leave the child end of the pipe open in the parent process, and pass that to the next child that is created. Sorry, haven't used linux in a while, and I don't have a linux box right now, but I figured since this is kind of psuedocode at this point, it wouldn't hurt too much to try. If you want to be able to hand an arbitrary n, you'll have to either loop or use recursion, with loop being preferably because it won't use stack memory for each iteration.
Also not entirely sure I understand what you are trying to do, so I made a few guesses. So, something like:
int keep_going = 1;
while(keep_going){
pid = fork();
if (pid > 0){ //parent which writes n to fd
/* can't close read, have to pass it to next child
close(fd[0]);*/ //close read
for (j = 2; j <= n; j++){
if (write(fd[1], &j, sizeof(int)) == -1){ //write j = 2 to fd
perror("write j");
}
}
/* can't close write either, since it gets closed in child, so that
would have to be rewritten to check if it is open before
closing. close(fd[1]);*/ //close write
int status;
if(wait(&status) != -1){
if (WIFEXITED(status)){
if (WEXITSTATUS(status) == 2){
continue; /* continues the while loop */
}
/* handle errors */
break;
}
/* handle errors */
break;
}
/* handle more errors */
break;
assert(0); /* shouldn't be here */
}
else{ //CHILD
close(fd[1]); //close write
for(;;) { /* child needs its own loop */
for (j = 2; j <= n; j++){
if (read(fd[0], &j, sizeof(int)) == -1){ //read j from fd
perror("read j");
}
if (SOME CONDITION){
exit(2);
}
/* going to repeat for(;;) loop for next n */
}
}
}
assert(0); /* shouldn't be here */
}

Resources