Problem with piping commands in C - 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.

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);
}

Multiple pipes in shell

Im trying to replicate the multiple pipes execution of bash, and Im getting no output from terminal. First I create the number of pipes that my program will need depending on the number of commands, then in a while loop I launch each process executing each command in the child proccess and working with the correct FDs of pipes, in case it is first command, I dont read from any pipe, on the other hand, in case it is last command, I dont redirect the output to any pipe. I printed each fd to check if my program is doing well and it looks everything okay but as I said I dont get any output of the last commands.
Here is a snippet of my code about how I deal with the process and the pipes, if you need something more just let me know.
int i;
int *pipes;
i = 0;
pipes = ft_calloc(sizeof(int), gdata->n_pipes * 2);
while (i < gdata->n_pipes)
{
pipe(pipes + (i * 2));
i++;
}
int cc = 0; //command count
int r = 0;
int m;
pid_t pid;
while (gdata->cmds[r]) //double pointer array that contains the commands to execute ex: [["ls -l"], ["grep i"], ["wc -l"]]
{
pid = fork();
if (pid < 0)
{
perror("Fork: ");
exit(EXIT_FAILURE);
}
if (pid == 0)
{
if (r > 0)
{
if (dup2(pipes[(cc - 1) * 2], STDIN_FILENO) < 0)
{
perror("dup");
exit(EXIT_FAILURE);
}
}
if (r < gdata->commands - 1)
{
if (dup2(pipes[cc * 2 + 1], STDOUT_FILENO) < 0)
{
perror("dup");
exit(EXIT_FAILURE);
}
}
int k = 0;
while (k < gdata->n_pipes * 2)
{
close(pipes[k]);
k++;
}
handle_path(gdata->cmds[r], gdata->envp); // function that calls execve to execute commands
}
waitpid(pid, &m, 0);
r++;
cc++;
}
int y = 0;
while (y < gdata->n_pipes * 2)
{
close(pipes[y]);
y++;
}
Do you have any idea what I'm doing wrong, Thanks for the help!

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 - dup2() not executing

This is my first question so I apologize if I'm omitting anything important. So I've been working on an assignment that handles piping via forking. My code is pretty messy, littered with printf statements so I see what's going on.
I've looked around online and I think I get the idea of how to handle piping, but the problem I'm having is that my code skips dup2() on any file descriptor except inFD and outFD.
Here's the code for my function. Also, from what I understand, my teacher made a macro called CHK which checks for errors. If there is an error (such as dup2 returning -1), it'll terminate with a print to stderr.
My includes, global variables and myhandler() for signal
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
#include <fcntl.h>
#include <strings.h>
#include <math.h>
#include <signal.h>
// Function calls
void parse(char *w, char **ptrArray, char *inArray, char *outArray, int *pipeArray);
int flagHandler(char **ptrArray, char *inArray, char *outArray);
int pipeHandler(char **ptrArray, char *inArray, char *outArray, int *pipeArray);
// Global Variables
const int STORAGE = 254;
const int MAXITEM = 100;
int inFD; // file descriptor for <
int outFD; // file descriptor for >
int complete = 0; // for sighandler
int readDes = 0;
int writeDes = 1;
int numPipes = 0;
int status;
int forCounter = 0;
int fildes[4];
int pipeIndex = 0;
// MetaChar flags
int lessthanSign = 0; // < flag
int greaterthanSign = 0; // > flag
int firstChildFlag = 0;
int lastChildFlag = 0;
void myhandler(int signum)
{
complete = 1;
}
My main function
int main()
{
char s[STORAGE]; // array of words
char *newargv[MAXITEM];
char inArray[STORAGE]; // for <
char outArray[STORAGE]; // for >
int firstCheck;
int pidBackground; // holds value from fork(), used for background calls
struct stat st; // for stat(), checks if file exists
// dynamic array based on numPipes
// first child doesn't use this array, as it uses newargv[0] and newargv
// only the middle children and last child use this array, hence 10
int *pipeArray = malloc(10 * sizeof(int));
int numLoops = 0;
int i = 0;
signal(SIGTERM, myhandler);
for(;;)
{
// Reset flags here
lessthanSign = 0;
greaterthanSign = 0;
pipeSign = 0;
firstChildFlag = 0;
lastChildFlag = 0;
pipeIndex = 0;
parse(s, newargv, inArray, outArray, pipeArray);
pipeHandler(newargv, inArray, outArray, pipeArray);
wait(NULL);
fflush(NULL);
} // end for
printf("Entering killpg; numLoops = %d\n", numLoops);
killpg(getpid(), SIGTERM);
printf("p2 terminated.\n");
exit(0);
} // end main
Main calls parse which fills in newargv[]. It also fills in inArray[] and outArray[] with the string immediately after a < and > respectively. When detecting a pipe sign, it puts a null on newargv[], as well as putting a value in pipeArray[] for indexing the executable's name in newargv. I omitted the parse() and flagHandler() calls to keep it minimal.
My parseHandler() function
int pipeHandler(char **ptrArray, char *inArray, char *outArray, int *pipeArray)
{
pid_t firstChild;
pid_t firstChildBackground;
pid_t middleChild;
pid_t lastChild;
pid_t lastChildBackground;
int i = 0; // plain integer for for loops
printf("Initializing pipes\n");
//pipe(fildes);
//pipe(fildes + 2);
for (i = 0; i < (2*numPipes); i+=2)
{
printf("pipe initializing; i is %d\n", i);
if (pipe(fildes + i) < 0)
{
perror("pipe initialization failed");
exit(EXIT_FAILURE);
}
}
fflush(stdout);
if ((firstChild = fork()) < 0)
{
perror("First child's fork failed!");
exit(EXIT_FAILURE);
}
printf("firstChild pid = %d\n", getpid());
if (firstChild == 0)
{
if (firstChildFlag == 1)
{
printf("inFD = open...\n");
inFD = open(inArray, O_RDONLY);
printf("Doing dup2 inFD\n");
if (dup2(inFD, STDIN_FILENO) < 0)
{
perror("First child's < dup2 failed");
exit(EXIT_FAILURE);
}
}
printf("doing dup2 fildes[writeDes]\n");
if (dup2(fildes[writeDes], STDOUT_FILENO) < 0)
{
perror("First child's dup2 failed");
exit(EXIT_FAILURE);
}
printf("*****doing dup2 fildes[writeDes] was a success!\n");
for (i = 0; i < 4; i++)
{
if (close(fildes[i]) < 0)
{
perror("close failed");
exit(EXIT_FAILURE);
}
}
if (firstChildFlag == 1)
{
lessthanSign = 0;
firstChildFlag = 0;
if (close(inFD) < 0)
{
perror("close inFD failed");
exit(EXIT_FAILURE);
}
}
writeDes += 2;
printf("About to execvp first child\n");
if (execvp(ptrArray[0], ptrArray) < 0)
{
perror("execvp failed");
exit(EXIT_FAILURE);
}
}
else
{
fflush(stdout);
if ((middleChild = fork() < 0))
{
perror("Middle child's fork failed");
exit(EXIT_FAILURE);
}
printf("middleChild pid = %d\n", getpid());
if (middleChild == 0)
{
if (dup2(fildes[readDes], STDIN_FILENO) < 0)
{
perror("Middle child's dup2 on reading failed");
exit(EXIT_FAILURE);
}
if (dup2(fildes[writeDes], STDOUT_FILENO) < 0)
{
perror("Middle child's dup2 on writing failed");
exit(EXIT_FAILURE);
}
for (i = 0; i < 4; i++)
{
if (close(fildes[i]) < 0)
{
perror("close failed");
exit(EXIT_FAILURE);
}
}
readDes += 2;
writeDes += 2;
if (execvp(ptrArray[pipeArray[0]], ptrArray + pipeArray[0]) < 0)
{
perror("Middle child's execvp failed");
exit(EXIT_FAILURE);
}
}
else
{
fflush(stdout);
if ((lastChild = fork() < 0))
{
perror("Last child's fork failed");
exit(EXIT_FAILURE);
}
printf("lastChild pid = %d\n", getpid());
if (lastChild == 0)
{
if (dup2(fildes[readDes], STDOUT_FILENO) < 0)
{
perror("Last child's dup2 on reading failed");
exit(EXIT_FAILURE);
}
if (lastChildFlag == 1)
{
outFD = open(outArray, O_CREAT | O_RDWR, 0400 | 0200);
if (dup2(outFD, STDOUT_FILENO) < 0)
{
perror("Last child's > dup2 failed");
exit(EXIT_FAILURE);
}
}
for (i = 0; i < 4; i++)
{
if (close(fildes[i]) < 0)
{
perror("close failed");
exit(EXIT_FAILURE);
}
}
if (lastChildFlag == 1)
{
greaterthanSign = 0;
lastChildFlag = 0;
if (close(outFD) < 0)
{
perror("close on outFD failed");
exit(EXIT_FAILURE);
}
}
printf("Execvp last child\n");
if (execvp(ptrArray[pipeArray[1]], ptrArray + pipeArray[1]) < 0)
{
perror("Last child's execvp failed");
exit(EXIT_FAILURE);
}
printf("Last child execvp finished\n");
}
}
}
// Only the parent gets here
printf("Only the parent should be here\n");
printf("My pid is %d\n", getpid());
for (i = 0; i < 4; i++)
{
if (close(fildes[i]) < 0)
{
perror("close failed");
exit(EXIT_FAILURE);
}
}
for (;;)
{
pid_t pid;
if (pid = wait(NULL) < 0)
{
perror("wait failed");
exit(EXIT_FAILURE);
}
if (pid == lastChild)
{
printf("Parent is waiting for lastChild\n");
break;
}
}
printf("Parent finished waiting. Returning...\n");
return 0;
}
I did pipe(fildes) before any fork, so that all children and a parent have their copy. Therefore, I must close all file descriptors in each child (after dup2 but before execvp) and the parent. The parent will then wait until it gets the pid of lastChild.
With a lot of printf statements, I have found that no child does the dup2() command (except for dup2(inFD...) and dup2(outFD...) when the flags are appropriate). There is also no error printed.
I printed out my (char) newargv[] and my (int) pipeArray[] and they contain the correct values. It seems to be just the dup2 problem, and I have absolutely no idea what's going wrong with it.
I made a simple text file called test2 containing
ls | sort | cat someString
Where someString is just a file with some text. With all the print statements in the pipeHandler() function my output is:
EDIT: I fixed a couple typos I had. I forgot to lace an extra set of parenthesis on 3 ifs, if ((firstChild = fork()0 < 0)
I now have an infinite loop as the parent is waiting for the lastChild's pid. Here's the output:
Initializing pipes
numpipes = 2
pipe initializing; i is 0
pipe initializing; i is 2
firstChild pid = 20521
firstChild pid = 20522
doing dup2 fildes[writeDes]
middleChild pid = 20521
middleChild pid = 20523
lastChild pid = 20521
Only the parent should be here
My pid is 20521
lastChild pid = 20524
<infinite loop>
I'm still clueless though as to what's going on or what's potentially stopping the child.
#MarkPlotnick you're right! It's not that dup2 isn't executing or anything. Because I did dup2(fildes[1], STDOUT_FILENO), all print statements will be piped.
I fixed the typo mentioned as well. I tried my teacher's test file
< input1 cat|>your.outputc tr a-z A-Z | tr \ q
Which should result with a file called your.outputc. It does, and the contents are input1 with the effects of tr. However, I also have the printf statements at the top of this file.
I assumed the dup2 wasn't working because no printf statement followed, unlike it did in dup2(inFD, STDIN_FILENO), but that's probably because it was STDIN.

Segmentation fault with multiple process

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.

Resources