Created
October 6, 2017 09:18
-
-
Save apage43/15e2919299417ff7ee1178834f6d1bbc to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// how to create a process pipeline in C, or: | |
// how does the shell execute something like this: | |
//// $ awk '{print $3}' < /proc/mounts | sort | uniq -c | |
#define _GNU_SOURCE // needed for pipe2 | |
#include <fcntl.h> | |
#include <stdlib.h> | |
#include <unistd.h> | |
#include <sys/wait.h> | |
#include <stdio.h> | |
// i like the error check macro | |
#define CHECK(x) if ((x) < 0) { perror(#x); exit(1); } | |
// because i get them backwards all the time | |
#define READ_END(pipe) pipe[0] | |
#define WRITE_END(pipe) pipe[1] | |
int main(int argc, char **argv) { | |
int procmounts = open("/proc/mounts", O_RDONLY | O_CLOEXEC); | |
CHECK(procmounts); | |
int awktosort[2]; | |
// we pass O_CLOEXEC to pipe so that when our child procs | |
// dup2 over their stdin/stdout and then exec() their new process | |
// that new process will not have the higher-numbered fds open, | |
// only the stdio fds. | |
CHECK(pipe2(awktosort, O_CLOEXEC)); | |
int awk = fork(); | |
CHECK(awk); | |
if (awk == 0) { | |
// replace our stdin with the procmounts fd | |
CHECK(dup2(procmounts, 0)); | |
// send our stdout to sort | |
CHECK(dup2(WRITE_END(awktosort), 1)); | |
// using execlp to search $PATH is -kiinda- cheating since that's a lot | |
// more work from libc than just a syscall wrapper | |
execlp("awk", "awk", "{print $3}", NULL); | |
exit(1); // should not get here | |
} | |
close(procmounts); | |
close(WRITE_END(awktosort)); | |
int sorttouniq[2]; | |
CHECK(pipe2(sorttouniq, O_CLOEXEC)); | |
int sort = fork(); | |
CHECK(sort); | |
if (sort == 0) { | |
// replace our stdin with the pipe connected to awk's stdout | |
CHECK(dup2(READ_END(awktosort), 0)); | |
// replace our stdout with the pipe to uniq | |
CHECK(dup2(WRITE_END(sorttouniq), 1)); | |
execlp("sort", "sort", NULL); | |
exit(1); | |
} | |
close(READ_END(awktosort)); | |
close(WRITE_END(sorttouniq)); | |
int uniq = fork(); | |
CHECK(uniq); | |
if (uniq == 0) { | |
CHECK(dup2(READ_END(sorttouniq), 0)); | |
execlp("uniq", "uniq", "-c", NULL); | |
exit(1); | |
} | |
close(READ_END(sorttouniq)); | |
int wstat; | |
// just like an actual shell, we only preserve the exit code | |
// of the last process in the pipeline | |
waitpid(uniq, &wstat, 0); | |
if (WIFEXITED(wstat)) { | |
return WEXITSTATUS(wstat); | |
} | |
return 1; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment