Skip to main content

tee/
tee.rs

1// Copyright (c) 2020 Xu Shaohua <shaohua@biofan.org>. All rights reserved.
2// Use of this source is governed by Apache-2.0 License that can be found
3// in the LICENSE file.
4
5/// This code is rewritten of tee(2) example code.
6/// ```C
7/// #define _GNU_SOURCE
8/// #include <errno.h>
9/// #include <fcntl.h>
10/// #include <limits.h>
11/// #include <stdio.h>
12/// #include <stdlib.h>
13/// #include <unistd.h>
14///
15/// int main(int argc, char *argv[]) {
16///   int fd;
17///   int len, slen;
18///
19///   if (argc != 2) {
20///     fprintf(stderr, "Usage: %s <file>\n", argv[0]);
21///     exit(EXIT_FAILURE);
22///   }
23///
24///   fd = open(argv[1], O_WRONLY | O_CREAT | O_TRUNC, 0644);
25///   if (fd == -1) {
26///     perror("open");
27///     exit(EXIT_FAILURE);
28///   }
29///
30///   do {
31///     /*
32///      * tee stdin to stdout.
33///      */
34///     len = tee(STDIN_FILENO, STDOUT_FILENO, INT_MAX, SPLICE_F_NONBLOCK);
35///
36///     if (len < 0) {
37///       if (errno == EAGAIN)
38///         continue;
39///       perror("tee");
40///       exit(EXIT_FAILURE);
41///     } else if (len == 0)
42///       break;
43///
44///     /*
45///      * Consume stdin by splicing it to a file.
46///      */
47///     while (len > 0) {
48///       slen = splice(STDIN_FILENO, NULL, fd, NULL, len, SPLICE_F_MOVE);
49///       if (slen < 0) {
50///         perror("splice");
51///         break;
52///       }
53///       len -= slen;
54///     }
55///   } while (1);
56///
57///   close(fd);
58///   exit(EXIT_SUCCESS);
59/// }
60/// ```
61fn main() {
62    let output_file = "/tmp/nc-splice";
63    let ret = unsafe {
64        nc::openat(
65            nc::AT_FDCWD,
66            output_file,
67            nc::O_WRONLY | nc::O_CREAT | nc::O_TRUNC,
68            0o644,
69        )
70    };
71    assert!(ret.is_ok());
72    let fd = ret.unwrap();
73
74    // Tee stdin to stdout
75    loop {
76        let stdin_fileno = 0;
77        let stdout_fileno = 1;
78        let ret = unsafe {
79            nc::tee(
80                stdin_fileno,
81                stdout_fileno,
82                usize::MAX,
83                nc::SPLICE_F_NONBLOCK,
84            )
85        };
86        let mut tee_len = match ret {
87            Ok(0) => break,
88            Err(nc::EAGAIN) => continue,
89            Err(errno) => {
90                eprintln!("tee error: {}", nc::strerror(errno));
91                unsafe { nc::exit(1) };
92            }
93            Ok(len) => len,
94        };
95
96        // Consume stdin by splicing it to a file.
97        while tee_len > 0 {
98            let ret = unsafe {
99                nc::splice(
100                    stdin_fileno,
101                    None,
102                    fd,
103                    None,
104                    tee_len as usize,
105                    nc::SPLICE_F_MOVE,
106                )
107            };
108            match ret {
109                Err(errno) => {
110                    eprintln!("splice error: {}", nc::strerror(errno));
111                    break;
112                }
113                Ok(len) => tee_len -= len,
114            }
115        }
116    }
117
118    let ret = unsafe { nc::close(fd) };
119    assert!(ret.is_ok());
120}