Skip to content

Instantly share code, notes, and snippets.

@zed
Last active February 24, 2024 13:26
Show Gist options
  • Star 10 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zed/7540510 to your computer and use it in GitHub Desktop.
Save zed/7540510 to your computer and use it in GitHub Desktop.
/** Pipeline between three processes.
http://stackoverflow.com/q/20056084/
See @chill's answer: http://stackoverflow.com/a/8092270
*/
#include <assert.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h> /* pid_t */
#include <unistd.h>
#define PROGNAME "pipeline-three-processes"
#define Close(FD) do { \
int Close_fd = (FD); \
if (close(Close_fd) == -1) { \
perror("close"); \
fprintf(stderr, "%s:%d: close(" #FD ") %d\n", \
__FILE__, __LINE__, Close_fd); \
} \
}while(0)
static void
report_error_and_exit_helper(const char* message, void (*exit_func)(int)) {
fprintf(stderr, "%s: error: %s (system: %s)\n",
PROGNAME, message ? message : "", strerror(errno));
exit_func(EXIT_FAILURE);
}
static void report_error_and_exit(const char* message) {
report_error_and_exit_helper(message, exit);
}
static void _report_error_and_exit(const char* message) {
report_error_and_exit_helper(message, _exit);
}
/* move oldfd to newfd */
static void redirect(int oldfd, int newfd) {
if (oldfd != newfd) {
if (dup2(oldfd, newfd) != -1)
Close(oldfd); /* successfully redirected */
else
_report_error_and_exit("dup2");
}
}
static void run(char* const argv[], int in, int out) {
redirect(in, STDIN_FILENO); /* <&in : child reads from in */
redirect(out, STDOUT_FILENO); /* >&out : child writes to out */
execvp(argv[0], argv);
_report_error_and_exit("execvp");
}
int main(void) {
const char *ls[] = { "ls", "-l", NULL };
const char *sort[] = { "sort", "-k9", NULL };
const char *less[] = { "less", NULL };
const char** command[] = { ls, sort, less };
int n = sizeof(command) / sizeof(*command);
/* run all commands but the last */
int i = 0, in = STDIN_FILENO; /* the first command reads from stdin */
for ( ; i < (n-1); ++i) {
int fd[2]; /* in/out pipe ends */
pid_t pid; /* child's pid */
if (pipe(fd) == -1)
report_error_and_exit("pipe");
else if ((pid = fork()) == -1)
report_error_and_exit("fork");
else if (pid == 0) { /* run command[i] in the child process */
Close(fd[0]); /* close unused read end of the pipe */
run((char * const*)command[i], in, fd[1]); /* $ command < in > fd[1] */
}
else { /* parent */
assert (pid > 0);
Close(fd[1]); /* close unused write end of the pipe */
Close(in); /* close unused read end of the previous pipe */
in = fd[0]; /* the next command reads from here */
}
}
/* run the last command */
run((char * const*)command[i], in, STDOUT_FILENO); /* $ command < in */
}
@bca12345
Copy link

Doing a shell exercise and find this helpful gist! I guess when in = 0 it should not be closed on line 85, or the shell cannot read the next line.

@zed
Copy link
Author

zed commented Feb 24, 2024

@bca12345 What do you mean? Could you provide an example?

ls process would read from 0 in the code, the process that starts ls process doesn't read from 0 (it is unused and can be closed there).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment