Created
July 17, 2022 16:47
-
-
Save skeeto/e605c2fc104c641f389f86087066add0 to your computer and use it in GitHub Desktop.
Demonstrates using splice(2) as a pipeline probe
This file contains 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
// Demonstrates using splice(2) as a pipeline probe | |
// Ref: https://old.reddit.com/r/C_Programming/comments/w0xg5l | |
// This is free and unencumbered software released into the public domain. | |
#define _GNU_SOURCE | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <fcntl.h> | |
#include <sys/wait.h> | |
#include <unistd.h> | |
// Start a process with standard output connected to fdout, returning its | |
// standard input file descriptor. fdout will be automatically closed if not | |
// standard output. The interface is designed to be chained with other run() | |
// calls. | |
static int | |
run(int fdout, pid_t *pid, char **argv) | |
{ | |
int pfd[2]; | |
if (pipe(pfd) == -1) { | |
perror("pipe(2)"); | |
exit(1); | |
} | |
switch ((*pid = fork())) { | |
default: close(pfd[0]); | |
if (fdout != 1) { | |
close(fdout); | |
} | |
return pfd[1]; | |
case -1: perror("fork(2)"); | |
exit(1); | |
case 0: dup2(pfd[0], 0); | |
close(pfd[0]); | |
close(pfd[1]); | |
if (fdout != 1) { | |
dup2(fdout, 1); | |
close(fdout); | |
} | |
execvp(argv[0], argv); | |
perror("execvp(2)"); | |
exit(1); | |
} | |
} | |
struct probe { | |
int fdout; | |
int pfd[2]; | |
}; | |
// Create a probe pipe with semantics like run(). | |
static int | |
probe(int fdout, struct probe *p) | |
{ | |
p->fdout = fdout; | |
if (pipe(p->pfd) == -1) { | |
perror("pipe(2)"); | |
exit(1); | |
} | |
return p->pfd[1]; | |
} | |
// Drive the probe, measuring the number of bytes passed. Automatically closes | |
// all file descriptors when done. | |
static long long | |
probe_run(struct probe *p) | |
{ | |
long long c = 0; | |
for (close(p->pfd[1]);;) { | |
int n = splice(p->pfd[0], 0, p->fdout, 0, 1<<20, SPLICE_F_MOVE); | |
switch (n) { | |
case -1: perror("splice(2)"); | |
exit(1); | |
case 0: close(p->pfd[0]); | |
close(p->pfd[1]); | |
close(p->fdout); | |
return c; | |
default: c += n; | |
} | |
} | |
} | |
int main(void) | |
{ | |
char *echo[] = {"echo", "Vaurren", 0}; | |
char *wc[] = {"wc", 0}; | |
char *cowsay[] = {"cowsay", 0}; | |
pid_t pid[3]; // FIXME: children are not reaped on error | |
int npid = sizeof(pid) / sizeof(*pid); | |
struct probe p[1]; | |
// construct a pipeline | |
close(run(probe(run(run(1, pid+2, cowsay), pid+1, wc), p), pid+0, echo)); | |
printf("%lld\n", probe_run(p)); | |
// reap children | |
for (int i = 0; i < npid; i++) { | |
waitpid(pid[i], 0, 0); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment