Skip to content

Instantly share code, notes, and snippets.

@skeeto
Created July 17, 2022 16:47
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save skeeto/e605c2fc104c641f389f86087066add0 to your computer and use it in GitHub Desktop.
Save skeeto/e605c2fc104c641f389f86087066add0 to your computer and use it in GitHub Desktop.
Demonstrates using splice(2) as a pipeline probe
// 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