$RodHat_
Syscall reference · I/O

tee(2)

Duplicate data from one pipe into another without consuming it from the source.

Since
Linux 2.6.17check your kernel →
glibc
glibc 2.5
System
Linux
Man page
tee(2)

Synopsis

#define _GNU_SOURCE
#include <fcntl.h>

ssize_t tee(int fd_in, int fd_out, size_t size, unsigned int flags);

Rod's take

tee(1) reads stdin and writes it twice, through userspace both times. tee(2) does it inside the kernel: it duplicates what's sitting in one pipe into another without consuming the source, so a following splice can still drain the original. Both ends must be pipes. Log fanout is tee into a scratch pipe, splice the original to one place, splice the copy to the other, and nobody forks a shell pipeline. Only page pointers get duplicated, never the pages. Small, obscure, and exactly right for the job.

Flags and arguments that matter

NameWhat it does
SPLICE_F_NONBLOCKDo not block on I/O; see splice(2).
SPLICE_F_MOVECurrently has no effect for tee().
SPLICE_F_MORECurrently has no effect for tee(); may be implemented in future.
SPLICE_F_GIFTUnused for tee(); see vmsplice(2).

Errors you will actually hit

errnoMeaning
EINVALfd_in or fd_out is not a pipe, or both refer to the same pipe.
EAGAINSPLICE_F_NONBLOCK or O_NONBLOCK set and the operation would block.
ENOMEMOut of memory.

Example

/* stdin and stdout are pipes; also copy everything to logfd */
for (;;) {
    ssize_t n = tee(STDIN_FILENO, STDOUT_FILENO, INT_MAX, SPLICE_F_NONBLOCK);
    if (n < 0 && errno == EAGAIN)
        continue;               /* real code: poll() first */
    if (n < 0)
        err(1, "tee");
    if (n == 0)
        break;
    while (n > 0) {             /* consume what tee duplicated */
        ssize_t s = splice(STDIN_FILENO, NULL, logfd, NULL, n, 0);
        if (s <= 0)
            err(1, "splice");
        n -= s;
    }
}

Trimmed from the full walkthrough in splice and tee: zero-copy data movement, 2.6.17 edition.

BSD equivalent

none

The long version, from the console

Sources

  1. tee(2), man7.org
  2. splice(2), man7.org

Version numbers are read off the cited man pages, not memory. Spot one that's wrong? Tell Rod.