Segmentation fault with multiple process - c

Hello guys i have this code that is supposed to implement pipes (|) just like the terminal for example: ls | sort | grep main.
this is the part of code where i handle the pipes:
int pp[numPipes*2];
for(i = 0; i < numPipes; i++){
if(pipe(pp + i*2) < 0){ perror("pipe failed"); exit(-1); }
}
char **hold[numPipes+1];
int j = 0;
hold[j++] = args;
for(i = 0; i < nargs; i++){
if(!strcmp(args[i], "|")){
hold[j++] = args+i+1;
args[i] = NULL;
}
}
hold[j] = NULL;
i = 0;
j = 0;
while(hold[i][0]){
pid = fork();
if(pid == 0) { //child process
if(i != 0){
dup2(pp[(i-1)*2], 0);
}
if(i != numPipes){
dup2(pp[i*2+1], 1);
}
int c;
for( c = 0; c < 2 * numPipes; c++ ){
close( pp[c] );
}
execvp(hold[i][0], hold[i]);
//return only when exec fails
perror("exec failed");
exit(-1);
} else if(pid > 0) {//parent process
if(!async){
//waitpid(-1, NULL, 0);
}else printf("this is an async call\n");
} else { //error occurred
perror("fork failed");
exit(1);
}
i++;
}
int c;
for( c = 0; c < 2 * numPipes; c++ ){
close( pp[c] );
}
for(i = 0; i < numPipes+1; i++){
wait(&status);
}
this code works and execute the command correctly however its giving me a segmentation fault every time i run it and kills the parent process which is supposed not to happen. Does anyone see where this segmentation fault is happening? i cant find it.

Related

Tiny Shell with N commands and N-1 Pipes

I am working to create a tiny shell in C that can accept multiple commands and pipe them together as needed
I have the below code, it is accepting commands and piping them together but it is quitting my tiny shell after running one command (I beleive an EOF is being returned). I want it to be able to accept many lines of commands.
Any tips/advice would greatly be appreciated!
int i;
pid_t children[numberOfCommands - 1];
pid_t groupId = -1;
for (i = 1; i < numberOfCommands - 1; i++)
{
int pd[2];
if(pipe(pd) < 0){
unix_error("Pipe failed");
}
pid = fork();
children[i] = pid;
if (!pid)
{
dup2(pd[1], 1);
if (execve(argv[cmds[i]], argv, environ) < 0)
{
unix_error("First Execve failed");
}
}
else
{
if (i == 0)
{
groupId = pid;
}
setpgid(pid, groupId);
}
dup2(pd[0], 0);
close(pd[0]);
close(pd[1]);
}
if (execve(argv[0], argv, environ) < 0) // I believe this is where the EOF is coming from
{
unix_error("Second Execve failed");
}
for (int j = 0; j < numberOfCommands; j++)
{
waitpid(children[j], NULL, 0);
}

Simple Shell: Handling pipes results in infinite loop

I'm trying to hand pipes with the function below but it results in an infinite loop. My code is based off of this: Implementation of multiple pipes in C.
I'm new to C so I'd appreciate any thoughts or feedback on this solution. Please let me know if any other information would be required. There's no longer an infinite loop if I remove the third if statement, so the issue is somewhere there.
void command_with_pipes(char*** command_table, int count_pipes, bool converted_to_tee) {
int fd[2 * count_pipes];
pid_t pid;
int status;
// Create all necessary pipes at the beginning
for (int i = 0; i < count_pipes; i++) {
if (pipe(fd + i*2) < 0) {
perror("fatal error: pipe");
exit(1);
}
}
// Iterate over every command in the table
for (int commandNum = 0; commandNum <= count_pipes; commandNum++){
pid = fork();
if (pid < 0){
perror("fork error");
exit(-1);
}
else if (pid == 0){
// If it's not the first command, should read to the pipe
if (commandNum != 0) {
if(dup2(fd[(commandNum - 1) * 2], 0) < 0) {
perror("2 can't dup");
exit(1);
}
}
// if not the last command, should write to the pipe
if (commandNum != count_pipes) {
if(dup2(fd[commandNum * 2 + 1], 1) < 0) {
perror("1 can't dup");
exit(1);
}
}
// Last command includes a converted tee, should write to pipe instead of going to standard output
if (commandNum == count_pipes && strcmp(command_table[commandNum][0], "tee") ==0 && converted_to_tee == true){
if(dup2(fd[(commandNum - 1) * 2 + 1], 1) < 0) {
perror("1 can't dup");
exit(1);
}
}
// Close pipes
for (int i = 0; i < 2 * count_pipes; i++){
close(fd[i]);
}
// Execute the command
status = execvp(command_table[commandNum][0], command_table[commandNum]);
if (status < 0) {
perror("exec problem");
exit(1);
}
}
}
// Parent closes everything at the end
for (int i = 0; i < 2 * count_pipes; i++){
close(fd[i]);
}
wait(0);
}

C: Pipe and Fork closing. Nothing gets printed

cmds is a list of commands to call. In my case, I'm tring to call ls | grep c. When I run the program, nothing gets printed. It seems grep is waiting for something?
Note: If I only use ls (via execPipe(cmds,1)), everything works.
What is wrong?
int execPipe(char*** cmds,int len){
int i;
int pipefd[100][2];
for(i = 0; i < len; i++)
pipe(pipefd[i]);
i = 0;
for(i = 0; i < len; i++){
if (fork() == 0){
printf("executing #%d %s\n",i,cmds[i][0]);
//i=0: in=sdtin, out=1
//i=1: in=1,out=3
//i=2: in=3,out=5
//i=len in=len*2-1, out=sdtout
close(pipefd[i][0]);
if(i != 0){
dup2(pipefd[i-1][1],0); //read becomes the write of last one
}
if(i != len-1){
dup2(pipefd[i][1],1); //write becomes pipefd[i][1]
}
execvp(cmds[i][0],cmds[i]);
return EXIT_SUCCESS;
}
close(pipefd[i][0]);
close(pipefd[i][1]);
wait(NULL);
}
return 0;
}
int main(){
char*** cmds = malloc(2*sizeof(char**));
cmds[0] = malloc(2*sizeof(char**));
cmds[0][0] = "ls";
cmds[0][1] = NULL;
cmds[1] = malloc(3*sizeof(char**));
cmds[1][0] = "grep";
cmds[1][1] = "c";
cmds[1][2] = NULL;
execPipe(cmds,2);
return 0;
}
This code works:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
static
int execPipe(char ***cmds, int len)
{
int i;
int pids[len];
int pipefd[len][2];
for (i = 0; i < len - 1; i++)
pipe(pipefd[i]);
for (i = 0; i < len; i++)
{
int pid;
if ((pid = fork()) == 0)
{
printf("PID %d: executing #%d %s\n", (int)getpid(), i, cmds[i][0]);
if (i != 0)
{
dup2(pipefd[i - 1][0], 0); // JL: Fix
}
if (i != len - 1)
{
dup2(pipefd[i][1], 1); // write becomes pipefd[i][1]
}
for (int j = 0; j < len - 1; j++) // JL: Fix
{
close(pipefd[j][0]);
close(pipefd[j][1]);
}
execvp(cmds[i][0], cmds[i]);
fprintf(stderr, "Failed to execute command %s\n", cmds[i][0]);
return EXIT_FAILURE;
}
else if (pid < 0)
{
fprintf(stderr, "failed to fork for %s\n", cmds[i][0]);
exit(1);
}
else
pids[i] = pid;
}
for (i = 0; i < len - 1; i++) // JL: Fix
{
close(pipefd[i][0]);
close(pipefd[i][1]);
}
int corpse;
int status;
int kids = len;
while (kids > 0 && (corpse = wait(&status)) > 0)
{
printf("PID %d died with status 0x%.4X\n", corpse, status);
for (i = 0; i < kids; i++)
{
if (pids[i] == corpse)
{
pids[i] = pids[kids-1];
kids--;
break;
}
}
}
return 0;
}
int main(void)
{
char ***cmds = malloc(2 * sizeof(char **));
cmds[0] = malloc(2 * sizeof(char **));
cmds[0][0] = "ls";
cmds[0][1] = NULL;
cmds[1] = malloc(3 * sizeof(char **));
cmds[1][0] = "grep";
cmds[1][1] = "c";
cmds[1][2] = NULL;
execPipe(cmds, 2);
return 0;
}
Comments:
Notice how many close operations there are.
It would be reasonable to factor the close loop into a function that gets called where needed.
You could get away without the first child closing the pipes, but it is silly to break the symmetry.
It is crucial that the parent close the pipes — but only after the pipes are finished with (that is, after all the children are created).
The wait() loop deals with the situation where the parent process had children that it didn't know about that terminate before the children it launches — a rather unusual but far from impossible circumstance. It would be possible simply to wait until all children die, but maybe one of the previously created children isn't going to terminate. The loop waits until all the known children have died and then exits.
A more complex mechanism would only have two pipes open at any one time, even in a 50 process pipeline, rather than opening all 49 pipes at once, but that's a refinement for later.
You should extend this to a 3-process or longer pipeline and check that it works. Possible pipelines include:
who | awk '{print $1}' | sort
who | awk '{print $1}' | sort | uniq -c
who | awk '{print $1}' | sort | uniq -c | sort -n
Beware: the shell removes single quotes.

Implementation of multiple pipes in C

I'm trying to implement multiple pipes in my shell in C. I found a tutorial on this website and the function I made is based on this example. Here's the function
void executePipes(cmdLine* command, char* userInput) {
int numPipes = 2 * countPipes(userInput);
int status;
int i = 0, j = 0;
int pipefds[numPipes];
for(i = 0; i < (numPipes); i += 2)
pipe(pipefds + i);
while(command != NULL) {
if(fork() == 0){
if(j != 0){
dup2(pipefds[j - 2], 0);
}
if(command->next != NULL){
dup2(pipefds[j + 1], 1);
}
for(i = 0; i < (numPipes); i++){
close(pipefds[i]);
}
if( execvp(*command->arguments, command->arguments) < 0 ){
perror(*command->arguments);
exit(EXIT_FAILURE);
}
}
else{
if(command != NULL)
command = command->next;
j += 2;
for(i = 0; i < (numPipes ); i++){
close(pipefds[i]);
}
while(waitpid(0,0,0) < 0);
}
}
}
After executing it and typing a command like for example ls | grep bin, the shell just hangs there and doesn't output any result. I made sure I closed all pipes. But it just hangs there. I thought that it was the waitpid that's was the problem. I removed the waitpid and after executing I get no results. What did I do wrong? Thanks.
Added code:
void runPipedCommands(cmdLine* command, char* userInput) {
int numPipes = countPipes(userInput);
int status;
int i = 0, j = 0;
pid_t pid;
int pipefds[2*numPipes];
for(i = 0; i < 2*(numPipes); i++){
if(pipe(pipefds + i*2) < 0) {
perror("pipe");
exit(EXIT_FAILURE);
}
}
while(command) {
pid = fork();
if(pid == 0) {
//if not first command
if(j != 0){
if(dup2(pipefds[(j-1) * 2], 0) < 0){
perror(" dup2");///j-2 0 j+1 1
exit(EXIT_FAILURE);
//printf("j != 0 dup(pipefd[%d], 0])\n", j-2);
}
//if not last command
if(command->next){
if(dup2(pipefds[j * 2 + 1], 1) < 0){
perror("dup2");
exit(EXIT_FAILURE);
}
}
for(i = 0; i < 2*numPipes; i++){
close(pipefds[i]);
}
if( execvp(*command->arguments, command->arguments) < 0 ){
perror(*command->arguments);
exit(EXIT_FAILURE);
}
} else if(pid < 0){
perror("error");
exit(EXIT_FAILURE);
}
command = command->next;
j++;
}
for(i = 0; i < 2 * numPipes; i++){
close(pipefds[i]);
puts("closed pipe in parent");
}
while(waitpid(0,0,0) <= 0);
}
}
I believe the issue here is that your waiting and closing inside the same loop that's creating children. On the first iteration, the child will exec (which will destroy the child program, overwriting it with your first command) and then the parent closes all of its file descriptors and waits for the child to finish before it iterates on to creating the next child. At that point, since the parent has closed all of its pipes, any further children will have nothing to write to or read from. Since you are not checking for the success of your dup2 calls, this is going un-noticed.
If you want to keep the same loop structure, you'll need to make sure the parent only closes the file descriptors that have already been used, but leaves those that haven't alone. Then, after all children have been created, your parent can wait.
EDIT: I mixed up the parent/child in my answer, but the reasoning still holds: the process that goes on to fork again closes all of its copies of the pipes, so any process after the first fork will not have valid file descriptors to read to/write from.
pseudo code, using an array of pipes created up-front:
/* parent creates all needed pipes at the start */
for( i = 0; i < num-pipes; i++ ){
if( pipe(pipefds + i*2) < 0 ){
perror and exit
}
}
commandc = 0
while( command ){
pid = fork()
if( pid == 0 ){
/* child gets input from the previous command,
if it's not the first command */
if( not first command ){
if( dup2(pipefds[(commandc-1)*2], 0) < ){
perror and exit
}
}
/* child outputs to next command, if it's not
the last command */
if( not last command ){
if( dup2(pipefds[commandc*2+1], 1) < 0 ){
perror and exit
}
}
close all pipe-fds
execvp
perror and exit
} else if( pid < 0 ){
perror and exit
}
cmd = cmd->next
commandc++
}
/* parent closes all of its copies at the end */
for( i = 0; i < 2 * num-pipes; i++ ){
close( pipefds[i] );
}
In this code, the original parent process creates a child for each command and therefore survives the entire ordeal. The children check to see if they should get their input from the previous command and if they should send their output to the next command. Then they close all of their copies of the pipe file descriptors and then exec. The parent doesn't do anything but fork until it's created a child for each command. It then closes all of its copies of the descriptors and can go on to wait.
Creating all of the pipes you need first, and then managing them in the loop, is tricky and requires some array arithmetic. The goal, though, looks like this:
cmd0 cmd1 cmd2 cmd3 cmd4
pipe0 pipe1 pipe2 pipe3
[0,1] [2,3] [4,5] [6,7]
Realizing that, at any given time, you only need two sets of pipes (the pipe to the previous command and the pipe to the next command) will simplify your code and make it a little more robust. Ephemient gives pseudo-code for this here. His code is cleaner, because the parent and child do not have to do unnecessary looping to close un-needed file descriptors and because the parent can easily close its copies of the file descriptors immediately after the fork.
As a side note: you should always check the return values of pipe, dup2, fork, and exec.
EDIT 2: typo in pseudo code. OP: num-pipes would be the number of pipes. E.g., "ls | grep foo | sort -r" would have 2 pipes.
Here's the correct functioning code
void runPipedCommands(cmdLine* command, char* userInput) {
int numPipes = countPipes(userInput);
int status;
int i = 0;
pid_t pid;
int pipefds[2*numPipes];
for(i = 0; i < (numPipes); i++){
if(pipe(pipefds + i*2) < 0) {
perror("couldn't pipe");
exit(EXIT_FAILURE);
}
}
int j = 0;
while(command) {
pid = fork();
if(pid == 0) {
//if not last command
if(command->next){
if(dup2(pipefds[j + 1], 1) < 0){
perror("dup2");
exit(EXIT_FAILURE);
}
}
//if not first command&& j!= 2*numPipes
if(j != 0 ){
if(dup2(pipefds[j-2], 0) < 0){
perror(" dup2");///j-2 0 j+1 1
exit(EXIT_FAILURE);
}
}
for(i = 0; i < 2*numPipes; i++){
close(pipefds[i]);
}
if( execvp(*command->arguments, command->arguments) < 0 ){
perror(*command->arguments);
exit(EXIT_FAILURE);
}
} else if(pid < 0){
perror("error");
exit(EXIT_FAILURE);
}
command = command->next;
j+=2;
}
/**Parent closes the pipes and wait for children*/
for(i = 0; i < 2 * numPipes; i++){
close(pipefds[i]);
}
for(i = 0; i < numPipes + 1; i++)
wait(&status);
}
The (shortened) relevant code is:
if(fork() == 0){
// do child stuff here
....
}
else{
// do parent stuff here
if(command != NULL)
command = command->next;
j += 2;
for(i = 0; i < (numPipes ); i++){
close(pipefds[i]);
}
while(waitpid(0,0,0) < 0);
}
Which means the parent (controlling) process does this:
fork
close all pipes
wait for child process
next loop / child
But it should be something like this:
fork
fork
fork
close all pipes (everything should have been duped now)
wait for childs
You only need two pipes alternating like below:
typedef int io[2];
extern int I; //piped command current index
extern int pipe_count; //count of '|'
#define CURRENT 0
#define PREVIOUS 1
#define READ 0
#define WRITE 1
#define is_last_command (I == pipe_count)
bool connect(io pipes[2])
{
if (pipe_count)
{
if (is_last_command || I != 0)
dup2(pipes[PREVIOUS][READ], STDIN_FILENO);
if (I == 0 || !is_last_command)
dup2(pipes[CURRENT][WRITE], STDOUT_FILENO);
}
return (true);
}
void close_(io pipes[2])
{
if (pipe_count)
{
if (is_last_command || I != 0)
close(pipes[PREVIOUS][READ]);
if (I == 0 || !is_last_command)
close(pipes[CURRENT][WRITE]);
}
}
void alternate(int **pipes)
{
int *pipe_current;
pipe_current = pipes[CURRENT];
pipes[CURRENT] = pipes[PREVIOUS];
pipes[PREVIOUS] = pipe_current;
}
Example usage:
#define ERROR -1
#define CHILD 0
void execute(char **command)
{
static io pipes[2];
if (pipe_count && pipe(pipes[CURRENT]) == ERROR)
exit_error("pipe");
if (fork()==CHILD && connect(pipes))
{
execvp(command[0], command);
_exit(EXIT_FAILURE);
}
while (wait(NULL) >= 0);
close_(pipes);
alternate((int **)pipes);
}
static void run(char ***commands)
{
for (I = 0; commands[I]; I++)
if (*commands[I])
execute(commands[I]);
}
I'll leave a link to a full working code for someone who needs it.
Building upon the idea of using a maximum of two pipes at a given time mentioned by Christopher Neylan, I put together pseudocode for n-pipes. args is an array of character pointers of size 'args_size' which is a global variable.
// MULTIPLE PIPES
// Test case: char *args[] = {"ls", "-l", "|", "head", "|", "tail", "-4",
0};// "|", "grep", "Txt", 0};
enum fileEnd{READ, WRITE};
void multiple pipes( char** args){
pid_t cpid;
// declare pipes
int pipeA[2]
int pipeB[2]
// I have done getNumberofpipes
int numPipes = getNumberOfPipes;
int command_num = numPipes+1;
// holds sub array of args
// which is a statement to execute
// for example: cmd = {"ls", "-l", NULL}
char** cmd
// iterate over args
for(i = 0; i < args_size; i++){
//
// strip subarray from main array
// cmd 1 | cmd 2 | cmd3 => cmd
// cmd = {"ls", "-l", NULL}
//Open/reopen one pipe
//if i is even open pipeB
if(i % 2) pipe(pipeB);
//if i is odd open pipeA
else pipe(pipeA);
switch(cpid = fork(){
case -1: error forking
case 0: // child process
childprocess(i);
default: // parent process
parentprocess(i, cpid);
}
}
}
// parent pipes must be closed in parent
void parentprocess(int i, pid_t cpid){
// if first command
if(i == 0)
close(pipeB[WRITE]);
// if last command close WRITE
else if (i == numPipes){
// if i is even close pipeB[WRITE]
// if i is odd close pipeA[WRITE]
}
// otherwise if in middle close READ and WRITE
// for appropriate pipes
// if i is even
close(pipeA[READ])
close(pipeB[WRITE])
// if i is odd
close(pipeB[READ])
close(pipeA[WRITE])
}
int returnvalue, status;
waitpid(cpid, returnvalue, status);
}
void childprocess(int i){
// if in first command
if(i == 0)
dup2(pipeB[WRITE], STDOUT_FILENO);
//if in last command change stdin for
// the necessary pipe. Don't touch stdout -
// stdout goes to shell
else if( numPipes == i){
// if i is even
dup2(pipeB[READ], STDIN_FILENO)
//if i is odd
dup2(pipeA[READ], STDIN_FILENO);
}
// otherwise, we are in middle command where
// both pipes are used.
else{
// if i is even
dup2(pipeA[READ], STDIN_FILENO)
dupe(pipeB[WRITE], STDOUT_FILENO)
// if i is odd
dup2(pipeB[READ], STDIN_FILENO)
dup2(pipeA[WRITE], STDOUT_FILENO)
}
// execute command for this iteration
// check for errors!!
// The exec() functions only return if an error has occurred. The return value is -1, and errno is set to indicate the error.
if(exec(cmd, cmd) < 0)
printf("Oh dear, something went wrong with read()! %s\n", strerror(errno));
}
}
Basically what you wanna do is a recursive function where the child executes the first command and the parent executes the second one if no other commands are left or calls the function again.

Problem with piping commands in C

I'm trying to create a simple shell in C for Unix. I've been able to do all the parsing of commands and execution, but I'm having a problem with piping. I think the problem is that I'm not hooking into the correct pipe for the input of the second command.
For example, if I type "ls | wc", it will pause after the "wc" command, which I think is because its waiting for input. I think the problem is when I use dup2(reading[i],0), and its not hooking into the correct pipe.
I know this is a bit of a broad question, but if there are any pointers I could get, I would appreciate it. Here is the code that creates new processes and tries to pipe them.
int fileds[2];
int reading[num_cmds];
int writing[num_cmds];
int p;
for(p=0; p < num_cmds; p++)
{
reading[p] = -1;
writing[p] = -1;
}
int j;
for(j=0; j < num_cmds-1; j++) //Create pipes for commands
{
int fileds[2];
pipe(fileds);
reading[j+1] = fileds[0];
writing[j] = fileds[1];
}
int i = 0;
for(i = 0; i < num_cmds;i++)
{
cmd_args = parse_cmd(cmds[i],output_file,input_file,&run_bg); //Get command and args
pid_t childpid;
int status;
childpid=fork();
if (childpid >= 0)
{
if (childpid == 0)
{
if(writing[i] != -1)
{
dup2(writing[i],1);
close(writing[i]);
}
if(reading[i] != -1)
{
dup2(reading[i],0);
close(reading[i]);
}
int h;
for(h = 0; h < num_cmds; h++)
{
close(writing[h]);
close(reading[h]);
}
if(execvp(cmd_args[0],cmd_args) == -1)
{
perror("Problem with command");
exit(0);
}
}
else
{
wait(&status);
int m;
for(m = 0; m < num_cmds; m++)
{
if( writing[m] != -1) close(writing[m]);
if( reading[m] != -1) close(reading[m]);
}
}
}
else
{
perror("fork");
continue;
}
input_file[0] = 0;
output_file[0] = 0;
run_bg = 0;
}
}
UPDATE: I was able to figure it out, thanks to Richard. It was a combination of closing the file descriptors in the wrong order and not closing some at all. Here's the working code.
int fileds[2];
int reading[num_cmds];
int writing[num_cmds];
int p;
for(p=0; p < num_cmds; p++)
{
reading[p] = -1;
writing[p] = -1;
}
int j;
for(j=0; j < num_cmds-1; j++)
{
int fileds[2];
pipe(fileds);
reading[j+1] = fileds[0];
writing[j] = fileds[1];
}
int i = 0;
for(i = 0; i < num_cmds;i++)
{
cmd_args = parse_cmd(cmds[i],output_file,input_file,&run_bg);
pid_t childpid;
int status;
childpid=fork();
if (childpid >= 0)
{
if (childpid == 0)
{
if(writing[i] != -1)
{
close(1);
dup2(writing[i],1);
}
if(reading[i] != -1)
{
close(0);
dup2(reading[i],0);
}
if(execvp(cmd_args[0],cmd_args) == -1)
{
perror("Problem with command");
exit(0);
}
}
else
{
wait(&status);
close(writing[i]);
if(i > 0)
{
close(reading[i]);
}
}
}
else
{
perror("fork");
}
input_file[0] = 0;
output_file[0] = 0;
run_bg = 0;
}
I think your problem may be that you wait for each process inside the loop and then close all the file descriptors. This makes the file descriptors invalid for the next call to dup2() and results in stdin for the next process staying unchanged.
Just a guess, I haven't run the code.
When I type "ls | wc" wc does as expected and prints the number of words output by the ls command. Remember that when you are piping commands using "|" you don't need to create pipes in your application. The first command needs to output to stdout and the second command needs to read that output from standard in.

Resources