Infinite pipe insanity - c

I'm trying to create an infinte set of pipes to traverse from the left process to the right process. I'm using a fd to keep the previous out fd and input it to the new process. Can anyone see where I'm going wrong. It should be pretty simple to see at this point. I documented well.
//Keep the previous out fd for the in of the subsequent process
int prev_out_fd;
for (x = 0; x < prog_count; ++x)
{
//Create a pipe for both processes to share
int pipefd[2];
if (x != prog_count -1)
{
pipe(pipefd);
}
prog_defs[x].pid = fork();
if(prog_defs[x].pid == 0)
{
//If this is the first process we don't need a read end
if (x == 0)
{
close(pipefd[0]);
}
//If this is not the first process, set the input to the output of the previous pipe
if (x != 0)
{
dup2(prev_out_fd, STDIN_FILENO);
//Pipe now garbage. Get rid of it.
close (prev_out_fd);
}
if(x != prog_count - 1)
{
dup2(pipefd[1], STDOUT_FILENO);
close(pipefd[0]);
close(pipefd[1]);
}
execvp(prog_defs[x].bin, prog_defs[x].args);
}
if (x != 0)
close(prev_out_fd);
prev_out_fd = pipefd[0];
close(pipefd[1]);
}

Related

Creating a new child process when old one is terminated in C

I've included code that creates a series of child processes to divide the work for a task. There's a random chance for it to terminate (handled by the word_count function from which it calls abort()) and on this event, it should create a new child process to replace it. However, the program is being blocked on the read. I know this code is messy, but I want understand the problem before cleaning it up.
int pipes[nChildProc][2]; //pipe fd[0] is read end, fd[1] is write end
long child_f_size = fsize / nChildProc;
pid_t pids[nChildProc];
//start dividing the work among child processes
for(int i = 0; i < nChildProc; ++i) {
//srand(time(NULL));
//int crash = ((rand() / RAND_MAX + 1.0) < crashRate) ? 1 : 0;
if(pipe(pipes[i]) != 0) {
printf("Failed to create pipe.\n");
exit(1);
}
pid_t pid = fork();
FILE *child_fp;
pids[i] = pid;
if(pid < 0) {
printf("Failed to create child process.\n");
exit(1);
}
else if(pid == 0) { //child process
count_t temp_count = readFromFile(child_fp, fsize, child_f_size, char* name, int i, int nChildProc);
//IPC with the main process
if(write(pipes[i][1], &temp_count, sizeof(temp_count)) == -1)
printf("failed to write to pipe.\n");
close(pipes[i][1]);
close(pipes[i][0]);
exit(0); //deallocate process' memory space
}
}
//wait for a children to finish
int ret, status, i = 0;
while(wait(NULL) != -1) { // while there are children to wait on
ret = waitpid(pids[i], &status, WUNTRACED);
if(ret == -1) {
continue;
}
if(ret != 0) {// didn't exit normally
if(pipe(pipes[i]) != 0) {
printf("Failed to create pipe.\n");
exit(1);
}
pid_t pid = fork();
FILE *child_fp;
pids[i] = pid;
if(pid < 0) {
printf("Failed to create child process.\n");
exit(1);
}
else if(pid == 0) { //child process
count_t temp_count = readFromFile(child_fp, fsize, child_f_size, char* name, int i, int nChildProc);
//IPC with the main process
if(write(pipes[i][1], &temp_count, sizeof(temp_count)) == -1)
printf("failed to write to pipe.\n");
close(pipes[i][1]);
close(pipes[i][0]);
exit(0); //deallocate process' memory space
}
}
i = (i + 1) % nChildProc;//loop back to detect more processes that were terminated
}
long bytes;
count_t temp;
temp.linecount = 0;
temp.wordcount = 0;
temp.charcount = 0;
//add up all the values from children to count
printf("time to read.\n");
for(unsigned int j = 0; j < nChildProc; ++j) {
if((bytes = read(pipes[j][0], &temp, sizeof(temp))) < 0) {//blocked here
printf("Failed to read from pipe {%d}.\n", j);
exit(1);
}
if(bytes != 0) {
count.linecount += temp.linecount;
count.wordcount += temp.wordcount;
count.charcount += temp.charcount;
}
close(pipes[j][1]);
close(pipes[j][0]);
}
A couple of issues jump out:
if(ret != 0) {// didn't exit normally you've confused ret (which is the pid) for status (which is the exit code of the child)
You can't call wait on a process twice, since calling wait allows the system to release the resources associated with the process. You have several options on how to rewrite this code:
while(wait(NULL) != -1) { // while there are children to wait on
ret = waitpid(pids[i], &status, WUNTRACED);
One easy way is to use wait then lookup in the array which index it belongs to.
while((pid = wait(&status)) {
if (pid == -1) { // no children to wait on
break;
}
for(int i = 0; i < nChildProc; ++i) {
if (pid == pids[i]) break;
}
if (i >= nChildProc) {
unexpected_pid_do_something_smart();
}
// Leave the rest of the loop the same
Note: I didn't compile or test the above code.

Piping output between N processes in C

I'm trying to write a C program to simulate the piping of two or more processes together, like ls | sort | wc so running my code as ./driver ls sort wc should show the same result. I think I'm really close, but I can't seem to find the bug in my code below. Any help would be appreciated, I'm really stumped here.
I think I understand what is supposed to happen, but Im crossing my wires somehow in making it happen. The parent should fork child processes who in turn reroute their STDOUT to the write end of a pipe(a). Any child who is created beyond the first child should consider the read end of this pipe(a) as its STDIN, as well as redirect it's own output to a pipe(b) of it's own.
Say a third process is piped. It should consider the read end of the pipe(b) as STDIN, and again pipe its output to the write end of a new pipe(c) before executing the requested command.
The last case is the when the final process is passed to the pipe. In this example, a fourth process would consider the read end of the pipe(c) but should not need to redirect the STDOUT, just send it to STDOUT as normal.
#include <errno.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#define FORK_CHILD 0
static void error_and_exit(void) {
fprintf(stderr, "Error: %s\n", strerror(errno));
exit(EXIT_FAILURE);
}
static int fork_or_die(void) {
int pid = fork();
if (pid == -1) {
error_and_exit();
}
return pid;
}
int main(const int argc, const char * argv[])
{
int processes = argc - 1;
int apipe[argc - 1][2];
int pid;
int result = -1;
for (int i = 0; i < processes; i++) {
result = pipe(apipe[i]);
if (result == -1) {
error_and_exit();
}
}
for (int i = 0; i < processes; i++) {
pid = fork_or_die();
// Child process executes process
if (pid == FORK_CHILD) {
// If we are not the first program in the pipe
if (i > 1) {
// Use the output from the previous program in the pipe as our input
// Check the read end of the pipe and STDIN are different descriptors
if (apipe[i - 1][0] != STDIN_FILENO) {
// Send the read end of the pipe to STDIN
if (dup2(apipe[i - 1][0], STDIN_FILENO) == -1) {
error_and_exit();
}
}
}
// Before we execute a process, bind the write end of the pipe to STDOUT
// Don't do this to the last process in the pipe, just send output to STDOUT as normal
if (i < processes - 1) {
// Check the write end of the pipe and STDOUT are different descriptors
if (apipe[i][1] != STDOUT_FILENO) {
// Send the write end of the pipe to STDOUT
if (dup2(apipe[i][1], STDOUT_FILENO) == -1) {
error_and_exit();
}
}
}
// Child executes requested process
if (execlp(argv[i + 1], argv[i + 1], (char *)NULL) == -1) {
error_and_exit();
}
wait(NULL);
}
// Parent does nothing until loop exits (waits for children)
}
return 0;
}
I've spotted three issues with your code:
As You have decided to index children starting from 0, not one but two processes will skip this part of code. The simplest fix that comes to my mind right now is to change the 1 to 0 or > to >=.
// If we are not the first program in the pipe
if (i > 1) {
You are calling wait in code that is not executed by parent. Moving the call outside the if (pid == FORK_CHILD) branch won't help as parent will wait for one child to finish before another one is started. Child process's write operation may wait for next child to consume some data and make place in buffer. Simplest solution that comes to my mind right now is to move wait calls to another loop.
You keep all pipe's descriptor open in parent and child processes. You should close it before wait loop in parent and before execlp in forked processes. The programs like grep, sort won't finish unless they receive EOF in their incoming streams. The pipe won't send EOF as long as at least one write descriptor is still open.
The code with minimal changes applied:
#include <errno.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#define FORK_CHILD 0
static void error_and_exit(void) {
fprintf(stderr, "Error: %s\n", strerror(errno));
exit(EXIT_FAILURE);
}
static int fork_or_die(void) {
int pid = fork();
if (pid == -1) {
error_and_exit();
}
return pid;
}
int main(const int argc, const char * argv[])
{
int processes = argc - 1;
int apipe[argc - 1][2];
int pid;
int result = -1;
for (int i = 0; i < processes; i++) {
result = pipe(apipe[i]);
if (result == -1) {
error_and_exit();
}
}
for (int i = 0; i < processes; i++) {
pid = fork_or_die();
// Child process executes process
if (pid == FORK_CHILD) {
// If we are not the first program in the pipe
if (i > 0) {
// Use the output from the previous program in the pipe as our input
// Check the read end of the pipe and STDIN are different descriptors
if (apipe[i - 1][0] != STDIN_FILENO) {
// Send the read end of the pipe to STDIN
if (dup2(apipe[i - 1][0], STDIN_FILENO) == -1) {
error_and_exit();
}
}
}
// Before we execute a process, bind the write end of the pipe to STDOUT
// Don't do this to the last process in the pipe, just send output to STDOUT as normal
if (i < processes - 1) {
// Check the write end of the pipe and STDOUT are different descriptors
if (apipe[i][1] != STDOUT_FILENO) {
// Send the write end of the pipe to STDOUT
if (dup2(apipe[i][1], STDOUT_FILENO) == -1) {
error_and_exit();
}
}
}
for (int j = 0; j < processes; j++) {
if(close(apipe[j][0]) == -1)
error_and_exit();
if(close(apipe[j][1]) == -1)
error_and_exit();
}
// Child executes requested process
if (execlp(argv[i + 1], argv[i + 1], (char *)NULL) == -1) {
error_and_exit();
}
}
}
// Parent does nothing until loop exits (waits for children)
for (int i = 0; i < processes; i++) {
if(close(apipe[i][0]) == -1)
error_and_exit();
if(close(apipe[i][1]) == -1)
error_and_exit();
}
for (int i = 0; i < processes; i++) {
wait(NULL);
}
return 0;
}

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 */
}

Parallel pipes not closing in C?

I'm trying to pipe in C in parallel but for some reason it's not closing... it's just waiting.... Not sure if I'm describing this well cuz i'm new at this, but here's the code
... some code up here
if(child_pid == 0) {
if(p0 >=0 && p1 == -1) {
dup2(p0, STDIN_FILENO);
close(p0);
close(p1);
}
else if (p0 == -1 && p1 >= 0) {
dup2(p1, STDOUT_FILENO);
close(p0);
close(p1);
}
else if (p0 >= 0 && p1 >=0) {
dup2(p0, STDIN_FILENO);
dup2(p1, STDOUT_FILENO);
close(p0);
close(p1);
}
if (input)
iofunc(input, 0);
if (output)
iofunc(output, 1);
if (sub){
command_exec (SUBSHELL_COMMAND, sub, -1, -1, -1);
exit(0);
}
else {
execvp(w[0], w);
exit(0);
}
}
else {
if (waitpid (-1, &child_status, 0) != child_pid)
child_status =-1;
close(p0);
close(p1);
return WEXITSTATUS(child_status);
}
... some code down here
when p0 == -1 && p1 >= 0, the process exits/returns, but for some reason, when p0 >= 0 && p1 == -1, the process just hangs and the pipe doesn't close. I ran a command ls | cat using two parallel child processes with ls writing to p1 and the cat reading from p0. The output was correct, but the pipe didn't close and cat never exited. What is going on?
#William I tried the following in my parent else block, but it didn't work
close(p0);
close(p1);
if (waitpid (-1, &child_status, 0) != child_pid)
child_status =-1;
#William here is the code that calls a function that calls the above code
int i = 0;
int num = 2;
int status;
pid_t pid;
pid_t pids[num];
int fd[2];
int p = pipe(fd);
int a;
int b;
//printf("pipe0:%d\n", fd[0]);
//printf("pipe1:%d\n", fd[1]);
for (i = 0; i < num; i++) {
if ((pids[i] = fork()) < 0) {
error(1,0, "child not forked");
} else if (pids[i] == 0) {
if (i == 0){
b = command_exec (c->type,
c->u.command[1],
0, f[0], p1);
}
else if (i == 1){
a = command_exec (c->type,
c->u.command[0],
1, p0, fd[1]);
}
exit(a && b);
}
}
while (num > 0) {
pid = wait(&status);
printf("Child with PID %ld exited with status 0x%x.\n", (long)pid, status);
num--;
}
The printf only prints the child process that finishes ls but not cat
Most likely, the parent is holding open the other side of the pipe. The ls has an open file descriptor on the write side of the pipe into cat, but so does the parent. cat will not exit until all the write side file descriptors are closed. Probably, you just need to reverse your code and close the pipes before you call waitpid instead of after.

Implementing pipelining in a Linux shell

I'm trying to develop a shell in Linux as an Operating Systems project. One of the requirements is to support pipelining (where calling something like ls -l|less passes the output of the first command to the second). I'm trying to use the C pipe() and dup2() commands but the redirection doesn't seem to be happening (less complains that it didn't receive a filename). Can you identify where I'm going wrong/how I might go about fixing that?
EDIT: I'm thinking that I need to use either freopen or fdopen somewhere since I'm not using read() or write()... is that correct?
(I've heard from others who've done this project that using freopen() is another way to solve this problem; if you think that would be better, tips for going that direction would also be appreciated.)
Here's my execute_external() function, which executes all commands not built-in to the shell. The various commands in the pipe (e.g. [ls -l] and [less]) are stored in the commands[] array.
void execute_external()
{
int numCommands = 1;
char **commands;
commands = malloc(sizeof(char *));
if(strstr(raw_command, "|") != NULL)
{
numCommands = separate_pipeline_commands(commands);
}
else
{
commands[0] = malloc(strlen(raw_command) * sizeof(char));
commands[0] = raw_command;
}
int i;
int pipefd[2];
for (i = 0; i < numCommands; i++)
{
char **parameters_array = malloc(strlen(commands[i]) * sizeof(char *));
int num_params;
num_params = str_to_str_array(commands[i], parameters_array);
if (numCommands > 1 && i > 0 && i != numCommands - 1)
{
if (pipe(pipefd) == -1)
{
printf("Could not open a pipe.");
}
}
pid_t pid = fork();
pmesg(2, "Process forked. ID = %i. \n", pid);
int status;
if (fork < 0)
{
fprintf(to_write_to, "Could not fork a process to complete the external command.\n");
exit(EXIT_FAILURE);
}
if (pid == 0) // This is the child process
{
if (numCommands > 1) { close(pipefd[1]); } // close the unused write end of the pipe
if (i == 0) // we may be pipelining and this is the first process
{
dup2(1, pipefd[1]); // set the source descriptor (for the next iteration of the loop) to this proc's stdout
}
if (i !=0 && (i != numCommands-1)) // we are pipelining and this is not the first or last process
{
dup2(pipefd[0], 0); // set the stdin of this process to the source of the previous process
}
if (execvp(parameters_array[0], parameters_array) < 0)
{
fprintf(to_write_to, "Could not execute the external command. errno: %i.\n", errno);
exit(EXIT_FAILURE);
}
else { pmesg(2, "Executed the child process.\n");}
}
else
{
if (numCommands > 1) { close(pipefd[0]); } // close the unused read end of the pipe
if (backgrounding == 0) { while(wait(&status) != pid); }// Wait for the child to finish executing
}
free(parameters_array);
}
free(commands);
}
It looks like there are a couple of bugs going on in your code.
First, all your dup2's are only in the child. In order to connect a pipe you will need to dup2 the stdout of the parent to the write end pipefd[1] of the pipe. Then you would hook up the read end to stdin.
Also it looks like on of your dup2's is backwards with dup2 fildes is duplicated to fildes2. So when you reassign stdin you want dup2(in, 0) and for stdout you want dup2(out, 1).
So a stripped down piece of piping code is going to look like:
int pipefd[2];
pipe(pipefd);
pid_t pid = fork();
if (pid == 0) //The child
{
dup2(pipefd[0], 0);
}
else
{
dup2(pipefd[1], 1);
}

Resources