C Unix Pipes Example

An attempt to realize a shell, mainly a pipeline. I wrote this test case, which I expect will just connect ls to wc ... it definitely does not work properly. It prints ls on the terminal, and then prints out the exhausted memory. I really lost how to fix this and make it work. find_path works in all my tests.

Edit - I need to use execv for the project, its class, but I tried it with execvp just in case, and it does the same. In addition, this is just an example, a test, to understand why it does not work, I call fork for both commands and waitpid twice because I have nothing more to do.

#include <unistd.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <sys/stat.h> #include <stdio.h> int find_path(char* execname, char** dst) { char *path = getenv("PATH"); path = strdup(path); char *pos; path = strtok_r(path, ":", &pos); char *originalpath = path; do { char* test = (char*)calloc(strlen(path) + strlen(execname) + 2, sizeof(char)); test = strcpy(test, path); int testlen = strlen(test); (*(test+testlen)) = '/'; strcpy(test + testlen + 1,execname); struct stat buf; int result = stat(test, &buf); if (result == 0) { *dst = test; free (originalpath); return 1; } else { free(test); } } while ((path = strtok_r(NULL, ":", &pos)) != NULL); free(originalpath); return 0; } int main() { char *cmd1 = "ls"; char *cmd2 = "wc"; int filedes[2]; pipe(filedes); char** argv = (char**)calloc(1, sizeof(char*)); argv[0] = (char*)malloc(sizeof(char*)); argv[0] = NULL; pid_t pid = fork(); if (pid == 0) { char *path; find_path(cmd1, &path); dup2(filedes[1],stdout); execv(path,argv); } pid = fork(); if (pid == 0) { dup2(filedes[0], stdin); char *path; find_path(cmd2, &path); execv(path, argv); } else waitpid(pid); } 
+4
source share
2 answers

Often, when it is difficult to debug a program, it is best to simplify it a bit to eliminate the sources of errors. Here is your program simplified to remove find_path as a source of errors:

 #include <unistd.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <sys/stat.h> #include <stdio.h> int main(void) { int filedes[2]; pipe(filedes); /* Run LS. */ pid_t pid = fork(); if (pid == 0) { /* Set stdout to the input side of the pipe, and run 'ls'. */ dup2(filedes[1], 1); char *argv[] = {"ls", NULL}; execv("/bin/ls", argv); } else { /* Close the input side of the pipe, to prevent it staying open. */ close(filedes[1]); } /* Run WC. */ pid = fork(); if (pid == 0) { dup2(filedes[0], 0); char *argv[] = {"wc", NULL}; execv("/usr/bin/wc", argv); } /* Wait for WC to finish. */ waitpid(pid); } 

This should behave as you expect.

During simplification, several errors appeared:

  • argv[] incorrectly configured, in particular, argv [0] was set to NULL;
  • The program did not close the input side of the channel transmitted by ls . When ls finished, the pipe did not close (because the wc process was still open), preventing wc from completing.
  • The program confused the values โ€‹โ€‹of stdout and stdin (which are of type FILE* ) with the file descriptor numbers 0 and 1 ( dup , pipe , etc. are used)
+8
source

There are many possibilities for improving this code (for example, breaking it into smaller functions would be the beginning), but I suspect that the problem with your memory is related to the code in find_path (), which you could completely avoid using execvp , which will find the executable file using the standard PATH mechanism for you. It is probably a good idea to install a signal handler using sigaction to handle SIGCHLD and call waitpid from the signal handler, rather than just calling waitpid () ad-hoc, as you do. It seems that you are deploying more time than you want and you are not checking for errors. Hope these suggestions help.

+1
source

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


All Articles