I am working on a shell(yes, it's an assignment, so I am not looking for a solution, more of a guide and clues to what I am doing wrong), and I'm stuck with pipes. I have read numerous tutorials, mans and what not, even copy-pasted some codes that "work", nothing seems to do the trick.
This is what I have so far:
void ft_run_pipe(char **cmd, int num)
{
int i;
int j;
int piped[num][2];
pid_t pid;
i = 0;
j = 0;
while (i < num)
{
ft_putstr("Creating pipe");
ft_putnbr(i);
ft_putchar('\n');
if (pipe(piped[i]) < 0)
ft_putstrn("piped failed");
i++;
}
while (cmd[j] != '\0')
{
pid = fork();
if (pid == 0)
{
ft_putchar('a');
if (j != 0)
{
ft_putstrn("If not first");
ft_putnbr(j - 1);
if (dup2(piped[j - 1][0], 0) < 0)
{
ft_putstrn("dup1 failed");
ft_putstrn(strerror(errno));
}
close(piped[j - 1][1]);
close(piped[j - 1][0]);
}
if (j != num)
{
ft_putstrn("If not last");
ft_putnbr(j);
if (dup2(piped[j][1], 1) < 0)
{
ft_putstrn("dup2i failed");
ft_putstrn(strerror(errno));
}
close (piped[j][0]);
close (piped[j][0]);
}
i = 0;
while (i < num)
{
close(piped[i][0]);
close(piped[i][1]);
i++;
}
// ft_run_special(cmd[j], environ);
}
else
{
i = 0;
while (i < num)
{
close(piped[i][0]);
close(piped[i][1]);
i++;
}
}
j++;
}
wait (NULL);
}
The various ft_something functions are coded by me alternatives to some libc functions(ft_putstr writes a string, with n in the end it adds a \n, etc.). ft_exec_special is a function that executes an executable with execve, searching in the PATH variable.
When ran with two commands and a single pipe, it returns
Creating pipe0
aIf not last
0aIf not first
0dup1 failed
Bad file descriptor
While, normally, it seems to me, it should work - piped[0][0] exists, is properly piped, and something was written to piped[0][1] - why would it say piped[0][0] is a bad file descriptor?
Related
I have to write a small shell - called shhh - that has the
following capabilities:
Can execute a command with the accompanying arguments.
Recognize multiple pipe requests and handle them.
Example command:
shhh> ls | more | wc
I need help specifically with the pipes. I've googled and researched a good few days worth of how to implement this in c, to no avail. Generally speaking, I have a fairly good idea of how a pipe works, but when it comes to implement this multi-pipe part, I haven't the slightest clue.
I've tried researching how to do this and have tried implementing it in my code in all sort of ways to no avail. Either it doesn't work, or I don't understand it and can't get it to work. I've also tried Chegg, but their experts are useless, since they keep asking for more code and the only thing I've left out of the snippet below is my own code that I've written that has nothing to do with the problem I'm currently having.
This is all the code that is pertinent to the problem.
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<signal.h>
#include<fcntl.h>
main()
{
char *path, *argv[20], buf[80], n, *p;
int m, status, inword, continu;
int fd[2];
while(1) {
inword = 0;
p = buf;
m = 0;
continu=0;
printf( "\nshhh> ");
while ( ( n = getchar() ) != '\n' || continu ) {
if ( n == ' ' ) {
if ( inword ) {
inword = 0;
*p++ = 0;
}
}
else if ( n == '\n' )
continu = 0;
else if ( n == '\\' && !inword )
continu = 1;
else {
if ( !inword ) {
inword = 1;
argv[m++] = p;
*p++ = n;
}
else
*p++ = n;
}
}
*p++ = 0;
argv[m] = 0;
if ( strcmp(argv[0],"exit") == 0 )
exit (0);
here's the snippet of code I currently have:
if(fork() == 0) {
for(y = 0; arg[v] != 0; y++) {
if(strcmp("|", argv[y]) == 0) {
argv[y] = 0;
pipe(fd);
if(fork() == 0) {
close(fd[0]);
dup(fd[0]);
close(fd[1]);
execvp(argv[0], argv);
}
else {
close(fd[1]);
dup(fd[1]);
close(fd[0]);
}
}
execvp(argv[0], argv);
printf(" didn't exec \n");
n2 = wait(&status);
}
wait(&status);
}
}
Any and all help is extremely very much appreciated. Also, I must use execvp() and I must use the array of arguments (argv[]).
C beginner here. The function send_chars_to_reducers does not appeared to be getting called inside the forked processes created in fork_mappers function.
C Code
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#define BUFFER_SIZE 1024
#define ALPHA_OFFSET 97
#define LETTERS 26
const int NUM_OF_MAPPERS = 4;
const int NUM_OF_REDUCERS = 26;
const int PIPE_READ_END = 0;
const int PIPE_WRITE_END = 1;
const int PIPE_BUFFER_SIZE = 1000;
int mapper_pipes[4][2];
int reducer_pipes[26][2];
void pipe_wrapper(int pipefd[]) {
int ret = pipe(pipefd);
if (ret == -1) {
perror("Error. Failed when trying to create pipes.");
exit(EXIT_FAILURE);
}
}
void create_mapper_pipes(void) {
int i;
for (i = 0; i < NUM_OF_MAPPERS; i++) {
pipe_wrapper(mapper_pipes[i]);
}
}
void create_reducer_pipes(void) {
int i;
for (i=0; i < NUM_OF_REDUCERS; i++) {
pipe_wrapper(reducer_pipes[i]);
}
}
// Prints an error msg and exits if one occurs. Else, returns the system call value.
int print_if_err(int syscall_val, const char* syscall_name) {
if (syscall_val < 0) {
perror(syscall_name);
exit(errno);
} else {
//No syscall error we can return
return syscall_val;
}
}
void send_chars_to_reducers(void) {
printf("hello from send_chars_to_reducers\n");
}
void fork_mappers(void) {
/* Constants useful to all children */
char ibuf[PIPE_BUFFER_SIZE]; // input pipe buffer
int rlen = 0;
int i;
for (i=0; i<NUM_OF_MAPPERS; i++) {
pid_t mapper_pid = print_if_err(fork(), "fork");
close(mapper_pipes[i][PIPE_WRITE_END]);
if (mapper_pid == 0) {
rlen = print_if_err(read(mapper_pipes[i][PIPE_READ_END], ibuf, 1000), "read");
while(rlen > 0) {
send_chars_to_reducers();
printf("read line from forked_mappers, p%d: %s\n", i, ibuf);
rlen = print_if_err(read(mapper_pipes[i][PIPE_READ_END], ibuf, 1000), "read");
}
_exit(0);
}
}
}
void fork_reducers(void) {
printf("hello from fork_reducers\n");
int i;
for (i = 0; i < NUM_OF_REDUCERS; i++) {
pid_t reducer_pid = print_if_err(fork(), "fork");
if (reducer_pid == 0) {
while (1 == 1) {
}
}
}
}
void send_lines_to_mappers(void) {
int wlen = 0;
char obuf[PIPE_BUFFER_SIZE];
int ob_size;
int count = 0;
char buff[BUFFER_SIZE]; // a buffer for each line of the file
FILE *input_file = fopen("input.txt", "r");
// read the input file line by line
while(fgets(buff, BUFFER_SIZE, input_file) > 0) {
printf("read line from send_lin_to_mappers: %s\n", buff);
ob_size = sizeof buff;
switch(count) {
case 0 :
write(mapper_pipes[0][PIPE_WRITE_END], buff, ob_size);
close(mapper_pipes[0][PIPE_WRITE_END]);
break;
case 1 :
write(mapper_pipes[1][PIPE_WRITE_END], buff, ob_size);
close(mapper_pipes[1][PIPE_WRITE_END]);
break;
case 2 :
write(mapper_pipes[2][PIPE_WRITE_END], buff, ob_size);
close(mapper_pipes[2][PIPE_WRITE_END]);
break;
case 3 :
write(mapper_pipes[3][PIPE_WRITE_END], buff, ob_size);
close(mapper_pipes[3][PIPE_WRITE_END]);
break;
default :
printf("you did something wrong in send_lines_to_mappers loop");
}
count++;
}
fclose(input_file);
}
int main(void) {
// Setup the mapper pipes
create_mapper_pipes();
create_reducer_pipes();
fork_reducers();
fork_mappers();
send_lines_to_mappers();
return 0;
}
Output
hello from fork_reducers
read line from send_lin_to_mappers: I like coding in C.
read line from send_lin_to_mappers: I like manually allocating memory, and opening the registers window in Visual Studio to see the values of the eax register and blitting graphics to the screen and all the stuff that Dr. Dobbs wrote about in the 90s.
read line from send_lin_to_mappers: My programming friends seem to believe that understanding this level of programming is good in a hand-wavy, theoretical sense, but when you consider all the web development, Java frameworks, and existing libraries most programmers today rely on, it's hard to really pin down a solid answer to the question "Why learn C?"
read line from send_lin_to_mappers: This is my attempt to answer that question, and I believe it comes down to the basic programming concept of abstraction.
The problem is the number of processes which share handles.
Each reducer has the pipes open
Each mapper has the pipes open, so the pipes are never properly closed ( the reducers are in a busy loop.
if (mapper_pid == 0) {
int j;
for( j = 0; j < NUM_OF_MAPPERS; j++ ){
close( mapper_pipes[j][PIPE_WRITE_END]);
if( j != i ){
close(mapper_pipes[j][PIPE_READ_END]);
}
}
I commented out the reducer fork's and then fixed the mapper forks to close all the pipe handles in the child except the one we want to read from.
This started to make the program work.
From pipe documentation, you should close the unused bits of a pipe. But you have pre-opened all the pipes (probably better localizing them), and have therefore got to close all the unused handles in the sub-processes to have the OS tidy up the resource correctly
Why is it that execvp isnt writing to the redirected STDOUT?
I tried using printf() as a test within this block, and that writes exactly where it should, which is in the file that I redirected STDOUT to.
edit: I changed the code, and added my implementation of makesubcommand, and added some debugging messages.
pid = fork();
wait(0);
if(pid == 0)
{
fd = open(subargs[next_redirect + 1], O_CREAT|O_TRUNC|O_WRONLY, 0644);
dup2(fd, STDOUT_FILENO);
close(fd);
//create sub-command
int val = (next_redirect - (last_redirect + 1));
fprintf(stderr,"subcommand will have %i indexes\n", val);
char* subcommand[val];
makesubcommand(subcommand, subargs, last_redirect + 1, next_redirect);
execvp(subcommand[0], subcommand);
fprintf(stderr,"execvp failed\n");
}
last_redirect = next_redirect;
next_redirect = getnextredirect(subargs, last_redirect+2, subargc);
heres makesubcommand(4):
void makesubcommand(char** newcommand, char** oldcommand, int lowerbound, int upperbound)
{
int i;
fprintf(stderr, "lowerbound: %i upperbound: %i\n",lowerbound, upperbound);
for(i = lowerbound; i < upperbound; i++)
{
fprintf(stderr,"subarg[%i]: %s\n", (i-lowerbound), oldcommand[i]);
newcommand[i - lowerbound] = oldcommand[i];
}
for(i = lowerbound; i < upperbound; i++)
{
fprintf(stderr, "newcommand[%i] = %s\n",(i - lowerbound), newcommand[i]);
}
fprintf(stderr, "it worked\n");
}
heres a testrun:
{12425}/home/chris/2240New/WMU-CS2240/A3_Shell$ ls > a
subcommand will have 1 indexes
lowerbound: 0 upperbound: 1
subarg[0]: ls
newcommand[0] = ls
it worked
execvp failed
My problem ended up being that I needed to allocate memory for the subcommand. malloc did the trick!
I'm trying to write some code which uses pipes to communicate between a parent process and it's children. However, my pipe seems to give up after the first time I use it (that is, it stops working after the first use of the pipe). I'm not really sure how to fix this problem, and any help will be greatly appreciated. I also know that some of the coding practice I am using in this are not really ideal (mainly use of sleep).
const int READ = 0;
const int WRITE = 1;
char* COOP = "Criminal cooperates\n";
char* SIL = "Criminal doesn't talk\n";
char* reader(int);
void writer(int, char *c);
int main()
{
int c1pipe1[2];
int c1pipe2[2];
int c2pipe1[2];
int c2pipe2[2];
int c1sentence = 0;
int c2sentence = 0;
int r;
int c;
pipe(c1pipe1);
pipe(c1pipe2);
pipe(c2pipe1);
pipe(c2pipe2);
int C2;
int C1 = fork();
if(C1 > 0)
C2 = fork();
if(C1 < 0 || C2 < 0) //error
{
perror("fork() failed");
exit(1);
}
else if(C1 == 0)
{
close(c1pipe1[WRITE]);
close(c1pipe2[READ]);
for(c = 0; c < 10; c++)
{
r = rand();
//printf("C1 rand = %d\n", r%2);
if(r % 2 == 1)
writer(c1pipe2[WRITE], "1");
else
writer(c1pipe2[WRITE], "0");
sleep(1);
}
exit(0);
}
else if(C2 == 0)
{
close(c2pipe1[WRITE]);
close(c2pipe2[READ]);
for(c = 0; c < 10; c++)
{
r = rand();
//printf("C2 rand = %d\n", r%2);
if(r % 2 == 1)
writer(c2pipe2[WRITE], "1");
else
writer(c2pipe2[WRITE], "0");
sleep(1);
}
exit(0);
}
else //parent
{
int buff1; //stores choice of c1
int buff2; //stores choice of c2
close(c1pipe1[READ]);
close(c1pipe2[WRITE]);
close(c2pipe1[READ]);
close(c2pipe2[WRITE]);
for(c = 0; c< 10; c++)
{
buff1 = atoi(reader(c1pipe2[READ]));
buff2 = atoi(reader(c2pipe2[READ]));
printf("C1's \(%d)\ choice trial %d : %d\n", C1, c+1, buff1);
printf("C2's \(%d)\ choice trial %d : %d\n", C2, c+1, buff2);
if(buff1 && buff2) //c1 and c2 cooperate with police
{
c1sentence = c1sentence + 6;
c2sentence = c2sentence + 6;
}
else if(buff1 || buff2) // one cooperates, one is silent
{
if(buff1) // if c1 cooperates and c2 is silent
{
c1sentence = c1sentence + 0;
c2sentence = c2sentence + 10;
}
else // if c2 cooperates and c1 is silent
{
c1sentence = c1sentence + 10;
c2sentence = c2sentence + 0;
}
}
else if(!(buff1 && buff2)) //both c1 and c2 are silent
{
c1sentence = c1sentence + 1;
c2sentence = c2sentence + 1;
}
sleep(1);
}
printf("C1 is in jail for %d years total\n", c1sentence);
printf("C2 is in jail for %d years total\n", c2sentence);
exit(0);
}
exit(0);
}
void writer(int pipe_write_fd, char *c)
{
open(pipe_write_fd);
char* choice = c;
// Write to the pipe
write(pipe_write_fd, choice, strlen(choice));
// Close the pipe
// (Sends 'end of file' to reader)
close(pipe_write_fd);
}
char* reader(int pipe_read_fd)
{
open(pipe_read_fd);
// Allocate buffer to store
// result of read
int buffer_size = 1024;
char buffer[buffer_size];
// Keep reading until we exhaust
// buffer or reach end of file
int i = 0;
while (i < buffer_size
&& read(pipe_read_fd, &buffer[i], 1) > 0)
{ i++; }
if (i < buffer_size) {
// Add null termination
buffer[i] = '\0';
} else {
// We exhausted buffer
fprintf(stderr, "Warning: buffer full.\n");
buffer[buffer_size-1] = '\0';
}
//printf("%s", buffer);
// Close the pipe
close(pipe_read_fd);
return buffer;
}
You need to close more of the pipes. The child processes must close every pipe file descriptor that they are not using. You have 8 pipe file descriptors; each child process has to close 6 of those - at least! You would be very well advised not to create all the pipes up front as you have done - it is complicated to control things and get all the right descriptors closed.
Looking at the code more closely, the parent does not write messages to the child processes, so you have twice as many pipes as you need - you only need one pipe for each child process to write back to the parent with.
You also do not open() already open file descriptors to the pipes...but how did you get the code to compile? You must be missing the correct header (#include <fcntl.h>) for open() and compiling without enough warning options enabled.
Your variables COOP and SIL are unused in the code presented.
Your writer() function not only mistakenly tries to open an already closed file descriptor, it also closes it, which means that there is no way to send back the extra messages after the first. You should only close the file descriptor once finished - after the loop in the main program for each child. This is why you only see one message.
It is also worth getting into the habit of error-checking the return from every system call that can fail. There are a few that can't fail - getpid() is one such. But I/O operations are notorious for failing for reasons outside the direct control of the program (or, in this case, within the control of the program), so you should check that writes succeed. When you get back an EBADF - bad file descriptor - error, you know something is up.
You have similar problems with close() (and open()) in reader(), plus the additional problem that you attempt to return a pointer to a local automatic variable - which is not a good idea, ever. Again, a decent compiler (like GCC) with warnings enabled will tell you about such things. I used this command to compile your program:
gcc -O -std=c99 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes \
pipe.c -o pipe
Your child processes are always going to generate the same sequence of (pseudo-)random numbers, which isn't very exciting. You should probably use something like:
srand(getpid());
to ensure they get different sequences.
Your reader() function is both not enthusiastic enough and too enthusiastic about reading the data. You read a single byte at a time, but you then loop to accumulate single bytes, so the code waits around for all 10 results to be known, and then spits everything out at once. Since a 32-bit integer can store a number up to 1,111,111,111 without problem, you would get just one number back from your call to atoi() on the first iteration, which isn't quite what you wanted.
Reads and writes on pipes are atomic - in the sense that if the writing process writes 6 bytes and the reading process attempts to read more than 6 bytes, then the packet of 6 bytes will be returned by a single read, even if there are other bytes in the pipe waiting to be read; those extra bytes will be returned on subsequent calls to read().
So, your reader() function should be passed in a buffer to use, along with its size; the code should attempt to read that buffer size; it should null terminate what it does receive; it can return the pointer to the buffer it was passed; it should error check the returned value from read().
The code for the two child processes is essentially the same - you should use an appropriately parameterized function rather than writing out the code twice.
Putting it all together, you end up with something like this (which works fine for me on MacOS X 10.6.6 with GCC 4.5.2):
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <stdarg.h>
#include <stdlib.h>
const int READ = 0;
const int WRITE = 1;
static char* reader(int fd, char *buffer, size_t bufsiz);
static void writer(int fd, const char *c);
static void child_process(int *my_pipe, int *his_pipe);
static void err_exit(const char *fmt, ...)
{
va_list args;
int errnum = errno;
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
if (errnum != 0)
fprintf(stderr, "%d: %s\n", errnum, strerror(errnum));
exit(1);
}
int main(void)
{
int c1pipe[2];
int c2pipe[2];
int c1sentence = 0;
int c2sentence = 0;
int c;
if (pipe(c1pipe) != 0 || pipe(c2pipe) != 0)
err_exit("Failed to open a pipe\n");
int C2 = 0;
int C1 = fork();
if (C1 > 0)
C2 = fork();
if (C1 < 0 || C2 < 0) //error
err_exit("fork() failed\n");
else if (C1 == 0)
child_process(c1pipe, c2pipe);
else if (C2 == 0)
child_process(c2pipe, c1pipe);
else //parent
{
int choice1; //stores choice of c1
int choice2; //stores choice of c2
char buffer1[BUFSIZ];
char buffer2[BUFSIZ];
close(c1pipe[WRITE]);
close(c2pipe[WRITE]);
for (c = 0; c< 10; c++)
{
choice1 = atoi(reader(c1pipe[READ], buffer1, sizeof(buffer1)));
choice2 = atoi(reader(c2pipe[READ], buffer2, sizeof(buffer1)));
printf("C1's (%d) choice trial %d : %d\n", C1, c+1, choice1);
printf("C2's (%d) choice trial %d : %d\n", C2, c+1, choice2);
if (choice1 && choice2) //c1 and c2 cooperate with police
{
c1sentence = c1sentence + 6;
c2sentence = c2sentence + 6;
}
else if (!(choice1 && choice2)) //both c1 and c2 are silent
{
c1sentence = c1sentence + 1;
c2sentence = c2sentence + 1;
}
else if (choice1) // if c1 cooperates and c2 is silent
{
c1sentence = c1sentence + 0;
c2sentence = c2sentence + 10;
}
else // if c2 cooperates and c1 is silent
{
c1sentence = c1sentence + 10;
c2sentence = c2sentence + 0;
}
}
printf("C1 is in jail for %d years total\n", c1sentence);
printf("C2 is in jail for %d years total\n", c2sentence);
}
return(0);
}
static void writer(int pipe_write_fd, const char *c)
{
int len = strlen(c);
if (write(pipe_write_fd, c, len) != len)
err_exit("Write failed\n");
}
static char* reader(int pipe_read_fd, char *buffer, size_t bufsiz)
{
int i = read(pipe_read_fd, buffer, bufsiz-1);
if (i < 0)
err_exit("Read failed\n");
buffer[i] = '\0';
return buffer;
}
static void child_process(int *my_pipe, int *his_pipe)
{
int c;
srand(getpid());
close(my_pipe[READ]);
close(his_pipe[READ]);
close(his_pipe[WRITE]);
for (c = 0; c < 10; c++)
{
writer(my_pipe[WRITE], ((rand() % 2) == 1) ? "1" : "0");
sleep(1);
}
close(my_pipe[WRITE]);
}
Note how the error routine captures errno early - to avoid damaging it. It is one of the perils of using global variables; they may change when you call a function. Don't use them when you can avoid them (but note that you can't avoid using errno completely, in general).
void writer(int pipe_write_fd, char *c)
{
open(pipe_write_fd);
char* choice = c;
// Write to the pipe
write(pipe_write_fd, choice, strlen(choice));
// Close the pipe
// (Sends 'end of file' to reader)
close(pipe_write_fd);
}
I'm not sure which function open you are trying yo use but the usual one takes a filename and returns a file descriptor. In any case you are discarding the return value so I suppose that this doesn't matter.
What is clear is that you close the pipe immediately after the first write so it is "correct" that the next write will fail; the pipe has been closed.
If you fix this problem then the next problem is that reader will, one byte at a time, all of the available input - up to 1024 bytes - before closing the read pipe. As reader is called in a loop, the read attempt in the second iteration will fail.
I know there are many threads that talk about this problem but I don't really understand the way it can be done.
I'm trying to make a shell that can execute a linux command sucha as ps | grep | less
I've donne the parsing by puting every command and its args in a simply linked list.
Here's my implementation that doesn't work. Hope that's clear enough.
if ((son = fork()) < 0)
return printerr_sys("Unable to fork", 0);
if (son == 0)
{
if (first > 1 && data->format[first - 1] &&
is_directing_elt(data->format[first - 1]) == DIRECT_TPIPE)
dup2(tube_p[0], STDIN_FILENO);
first = make_argv(data, first, &argv);
if (next)
{
dup2(tube_v[1], STDOUT_FILENO);
close(tube_v[0]);
}
if (execvp(argv[0], argv) < 0)
return printerr_cmd(argv[0], 1);
}
else
{
if (next)
{
close(tube_v[1]);
cmdline_executer(data, next, tube_v);
}
waitpid(son, &(data->lastcmd), WUNTRACED);
data->lastcmd = WEXITSTATUS(data->lastcmd);
}
return TRUE;
My questions are:
What would be the correct implementation?
Is it possible to do it with recursion?
Do I need to fork from right to left or left to right (logically it give the same result)?
Here's a part of a UNIX Shell I had to implement in C for Operating System subject in my Computer Science career.
/* Executes the command 'buffer' assuming that doesn't contain redirections */
void execute_only_pipes(char* buffer)
{
char *temp = NULL, *pipeCommands[MAX_PIPES], *cmdArgs[MAX_ARGUMENTS];
int newPipe[2], oldPipe[2], pipesCount, aCount, i, status;
pid_t pid;
pipesCount = -1; /* This variable will contain how many pipes the command contains */
/* Counting the number of pipes and splitting them into pipeCommands */
do
{
temp = strsep(&buffer, "|");
if(temp != NULL)
{
if(strlen(temp) > 0)
{
pipeCommands[++pipesCount] = temp;
}
}
} while(temp);
cmdArgs[++pipesCount] = NULL;
for(i = 0; i < pipesCount; i++) /* For each command */
{
aCount = -1;
/* Parsing command & arguments */
do
{
temp = strsep(&pipeCommands[i], " ");
if(temp != NULL)
{
if(strlen(temp) > 0)
{
/* If a parameter is ~, then replace it by /home/user */
if (!strcmp(temp, "~"))
strcpy(temp, home);
cmdArgs[++aCount] = temp;
}
}
} while(temp);
cmdArgs[++aCount] = NULL;
/* If there still are commands to be executed */
if(i < pipesCount-1)
{
pipe(newPipe); /* just create a pipe */
}
pid = fork();
if(pid == 0) /* Child */
{
/* If there is a previous command */
if(i > 0)
{
close(oldPipe[1]);
dup2(oldPipe[0], 0);
close(oldPipe[0]);
}
/* If there still are commands to be executed */
if(i < pipesCount-1)
{
close(newPipe[0]);
dup2(newPipe[1], 1);
close(newPipe[1]);
}
/* Execute it */
int res = execvp(cmdArgs[0], cmdArgs);
if (res == -1)
{
printf("Error. Command not found: %s\n", cmdArgs[0]);
}
exit(1);
}
else /* Father */
{
/* If there is a previous command */
if(i > 0)
{
close(oldPipe[0]);
close(oldPipe[1]);
}
/* do we have a next command? */
if(i < pipesCount-1)
{
oldPipe[0] = newPipe[0];
oldPipe[1] = newPipe[1];
}
/* wait for last command process? */
if(i == pipesCount-1)
{
waitpid(pid, &status, 0);
}
}
}
}
It might be a little buggy (I'm not checking if fork() < 0, etc) but the main idea is correct.
> Is it possible to do it with recursion?
Most of the time I try to avoid recursion, if I can write a similar understandable code without using it.
Processes run independently, so you need to set up the pipe for at least the first pair of commands before you fork, but you're doing that in the child (son == 0). You could code a recursive solution that, as long as there are at least two commands left, creates a pipe, then forks, then runs the first command.