Using the channel when executing a command through the parent

I have to implement the nameless pipe, and I have to execute the command in the parent process, and not in any of its children. each "-" is equal to a call to the pipeline ("|"), also part of the job I have this code. can someone explain to me why it is not working?

#include <stdio.h> #include <dirent.h> #include <sys/stat.h> #include <sys/types.h> #include <fcntl.h> // for open flags #include <time.h> // for time measurement #include <assert.h> #include <errno.h> #include <string.h> #include <unistd.h> #include <stdlib.h> void my_exec(char* cmd, char** argv) { int pipefd[2], f; if (pipe(pipefd) < 0) perror("pipe creation failed"); f = fork(); assert(f >= 0); if (f == 0) { // inside son process - connecting STDOUT to pipe write if (dup2(pipefd[1], STDOUT_FILENO) < 0) perror("dup2 failed"); close(pipefd[0]); close((int)stdout); } else { // inside parent process - connecting STDIN to pipe read and execute command with args if (dup2(pipefd[0], STDIN_FILENO) < 0) perror("dup2 failed"); close(pipefd[1]); close((int)stdin); if (execvp(cmd, argv) < 0) perror("execvp failed"); } } int main(int argc, char** argv) { assert(strcmp(argv[argc-1], "-")); int i; for (i = 1; i < argc; ++i) { if (!strcmp(argv[i], "-")) { argv[i] = NULL; my_exec(argv[1], &argv[1]); argv = &argv[i]; argc -= i; i = 0; } } char* args[argc]; args[argc-1] = NULL; for (i = 1; i < argc; ++i) { args[i-1] = argv[i]; } if (execvp(args[0], args) == -1) perror("execvp failed"); return; } 

command:

 ./mypipe.o ls -l - grep "pipe" 

returns

 total 24 -rw-rw-r-- 1 omer omer 1463 May 23 19:38 mypipe.c -rwxrwxr-x 1 omer omer 7563 May 23 19:37 mypipe.o -rw-rw-rw- 1 omer omer 873 May 23 20:01 nice.c -rwxrwxr-x 1 omer omer 7417 May 23 19:44 nice.o -rw-rw-r-- 1 omer omer 0 May 23 17:10 try 

which clearly means the pipe didn't work ... any ideas?

I need to make sure that each np_exec call starts one child process, which continues to parse the remaining arguments, while the original parent process executes the given program and arguments (using execvp),

EDIT: I think I found a mistake: i switch the read / write ends of the pipe.

correct function:

 void np_exec(char* cmd, char** argv) { int pipefd[2]; int file; if (pipe(pipefd) < 0) perror("failed to create pipe"); file = fork(); assert(file >= 0); if (file != 0) { // inside parent process - connecting STDOUT to pipe write and execute command with args if (dup2(pipefd[WRITE], STDOUT_FILENO) < 0) perror("the function dup2 failed"); close(pipefd[READ]); close((int)stdout); if (execvp(cmd, argv) < 0) perror("the function execvp failed"); } else { // inside son process - connecting STDIN to pipe read if (dup2(pipefd[READ], STDIN_FILENO) < 0) perror("the function dup2 failed"); close(pipefd[WRITE]); close((int)stdin); } } 
+6
source share
1 answer

UPDATE . So my original answer was out of the database. I did not quite understand what you wanted to do so far.

Your code is mostly right. This does not work for a very simple reason: shells assume that the command is executed when the process that started its completion.

Let me explain with an example. Imagine running ./mypipe a - b - c .

Here's what happens under the hood:

  • The shell starts the process to execute mypipe . This is when your program starts execution.

  • Your software forks, and the parent calls exec(a) . The child continues to analyze other arguments and process the creation of pipes as intended.

So now, at the moment, you have the parent program a running - which, in the eyes of the shell, is a process that matches the ./mypipe , and you have a child process that the shell completely neglects while doing mypipe work - setting up the pipes , reading the rest of the program to run, etc.

So now you have a race condition. Since the process behind mypipe been replaced by program a , as soon as a ends, the shell assumes that the command you typed is executed (i.e. assumes that mypipe is mypipe ), and prints a prompt, expecting you to type the following a team.

Problem: a terminates quickly, and your child process still loops through the list of other programs and tunes channels, redirects input and output and all that. So, for example, a child process can still work on creating channels and analyzing other programs, and by then a had already finished and wrote everything in stdout - Oops!

How do you fix this? Simple: invert the order of the command. Start by executing the last command, then the second to the last, etc. Why? Because if you do this, the parent will be the last in the pipeline, so it blocks waiting for input to enter the channel's read channel. When the parent ends, it means that the last command in the pipeline is completed, what exactly we want. There are no conditions for the race, and the shell is not deceived, thinking that our team is executed when it really is not.

So, it’s all about parsing lists of programs from right to left, and not from left to right. Also, if you do, you will no longer need the args helper array!

Here the code is checked and works:

 #include <stdio.h> #include <dirent.h> #include <sys/stat.h> #include <sys/types.h> #include <fcntl.h> // for open flags #include <time.h> // for time measurement #include <assert.h> #include <errno.h> #include <string.h> #include <unistd.h> #include <stdlib.h> void my_exec(char* cmd, char** argv) { int pipefd[2], f; if (pipe(pipefd) < 0) perror("pipe creation failed"); f = fork(); assert(f >= 0); if (f == 0) { if (dup2(pipefd[1], STDOUT_FILENO) < 0) perror("dup2 failed"); close(pipefd[0]); } else { if (dup2(pipefd[0], STDIN_FILENO) < 0) perror("dup2 failed"); close(pipefd[1]); if (execvp(cmd, argv) < 0) perror("execvp failed"); } } int main(int argc, char** argv) { assert(strcmp(argv[argc-1], "-")); int i; for (i = argc-1; i >= 1; i--) { if (!strcmp(argv[i], "-")) { argv[i] = NULL; my_exec(argv[i+1], &argv[i+1]); argc = i; } } if (execvp(argv[0], &argv[1]) == -1) perror("execvp failed"); return 0; } 
+3
source

Source: https://habr.com/ru/post/987797/


All Articles