/* * A tee implementation using sys_tee. */ #include #include #include #include #include #include #include #include #include #include #include #if defined(__i386__) #define __NR_splice 313 #define __NR_tee 315 #elif defined(__x86_64__) #define __NR_splice 275 #define __NR_tee 276 #elif defined(__powerpc__) || defined(__powerpc64__) #define __NR_splice 283 #define __NR_tee 284 #else #error unsupported arch #endif #define SPLICE_F_NONBLOCK (0x02) static inline int splice(int fdin, loff_t *off_in, int fdout, loff_t *off_out, size_t len, unsigned int flags) { return syscall(__NR_splice, fdin, off_in, fdout, off_out, len, flags); } static inline int tee(int fdin, int fdout, size_t len, unsigned int flags) { return syscall(__NR_tee, fdin, fdout, len, flags); } static int error(const char *n) { perror(n); return -1; } static int do_splice(int infd, int outfd, unsigned int len, char *msg) { while (len) { int written = splice(infd, NULL, outfd, NULL, len, 0); if (written <= 0) return error(msg); len -= written; } return 0; } int main(int argc, char *argv[]) { struct stat sb; int fd; if (argc < 2) { fprintf(stderr, "%s: outfile\n", argv[0]); return 1; } if (fstat(STDIN_FILENO, &sb) < 0) return error("stat"); if (!S_ISFIFO(sb.st_mode)) { fprintf(stderr, "stdout must be a pipe\n"); return 1; } fd = open(argv[1], O_WRONLY | O_CREAT | O_TRUNC, 0644); if (fd < 0) return error("open output"); do { int tee_len = tee(STDIN_FILENO, STDOUT_FILENO, INT_MAX, SPLICE_F_NONBLOCK); if (tee_len < 0) { if (errno == EAGAIN) { usleep(1000); continue; } return error("tee"); } else if (!tee_len) break; /* * Send output to file, also consumes input pipe. */ if (do_splice(STDIN_FILENO, fd, tee_len, "splice-file")) break; } while (1); return 0; }