1 /* tee - read from standard input and write to standard output and files.
2    Copyright (C) 1985-2023 Free Software Foundation, Inc.
3 
4    This program is free software: you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation, either version 3 of the License, or
7    (at your option) any later version.
8 
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13 
14    You should have received a copy of the GNU General Public License
15    along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
16 
17 /* Mike Parker, Richard M. Stallman, and David MacKenzie */
18 
19 #include <config.h>
20 #include <sys/types.h>
21 #include <signal.h>
22 #include <getopt.h>
23 
24 #include "system.h"
25 #include "argmatch.h"
26 #include "fadvise.h"
27 #include "iopoll.h"
28 #include "stdio--.h"
29 #include "xbinary-io.h"
30 #include "iopoll.h"
31 
32 /* The official name of this program (e.g., no 'g' prefix).  */
33 #define PROGRAM_NAME "tee"
34 
35 #define AUTHORS \
36   proper_name ("Mike Parker"), \
37   proper_name ("Richard M. Stallman"), \
38   proper_name ("David MacKenzie")
39 
40 static bool tee_files (int nfiles, char **files, bool);
41 
42 /* If true, append to output files rather than truncating them. */
43 static bool append;
44 
45 /* If true, ignore interrupts. */
46 static bool ignore_interrupts;
47 
48 enum output_error
49   {
50     output_error_sigpipe,      /* traditional behavior, sigpipe enabled.  */
51     output_error_warn,         /* warn on EPIPE, but continue.  */
52     output_error_warn_nopipe,  /* ignore EPIPE, continue.  */
53     output_error_exit,         /* exit on any output error.  */
54     output_error_exit_nopipe   /* exit on any output error except EPIPE.  */
55   };
56 
57 static enum output_error output_error;
58 
59 static struct option const long_options[] =
60 {
61   {"append", no_argument, nullptr, 'a'},
62   {"ignore-interrupts", no_argument, nullptr, 'i'},
63   {"output-error", optional_argument, nullptr, 'p'},
64   {GETOPT_HELP_OPTION_DECL},
65   {GETOPT_VERSION_OPTION_DECL},
66   {nullptr, 0, nullptr, 0}
67 };
68 
69 static char const *const output_error_args[] =
70 {
71   "warn", "warn-nopipe", "exit", "exit-nopipe", nullptr
72 };
73 static enum output_error const output_error_types[] =
74 {
75   output_error_warn, output_error_warn_nopipe,
76   output_error_exit, output_error_exit_nopipe
77 };
78 ARGMATCH_VERIFY (output_error_args, output_error_types);
79 
80 void
usage(int status)81 usage (int status)
82 {
83   if (status != EXIT_SUCCESS)
84     emit_try_help ();
85   else
86     {
87       printf (_("Usage: %s [OPTION]... [FILE]...\n"), program_name);
88       fputs (_("\
89 Copy standard input to each FILE, and also to standard output.\n\
90 \n\
91   -a, --append              append to the given FILEs, do not overwrite\n\
92   -i, --ignore-interrupts   ignore interrupt signals\n\
93 "), stdout);
94       fputs (_("\
95   -p                        operate in a more appropriate MODE with pipes.\n\
96       --output-error[=MODE]   set behavior on write error.  See MODE below\n\
97 "), stdout);
98       fputs (HELP_OPTION_DESCRIPTION, stdout);
99       fputs (VERSION_OPTION_DESCRIPTION, stdout);
100       fputs (_("\
101 \n\
102 MODE determines behavior with write errors on the outputs:\n\
103   warn           diagnose errors writing to any output\n\
104   warn-nopipe    diagnose errors writing to any output not a pipe\n\
105   exit           exit on error writing to any output\n\
106   exit-nopipe    exit on error writing to any output not a pipe\n\
107 The default MODE for the -p option is 'warn-nopipe'.\n\
108 With \"nopipe\" MODEs, exit immediately if all outputs become broken pipes.\n\
109 The default operation when --output-error is not specified, is to\n\
110 exit immediately on error writing to a pipe, and diagnose errors\n\
111 writing to non pipe outputs.\n\
112 "), stdout);
113       emit_ancillary_info (PROGRAM_NAME);
114     }
115   exit (status);
116 }
117 
118 int
main(int argc,char ** argv)119 main (int argc, char **argv)
120 {
121   initialize_main (&argc, &argv);
122   set_program_name (argv[0]);
123   setlocale (LC_ALL, "");
124   bindtextdomain (PACKAGE, LOCALEDIR);
125   textdomain (PACKAGE);
126 
127   atexit (close_stdout);
128 
129   append = false;
130   ignore_interrupts = false;
131 
132   int optc;
133   while ((optc = getopt_long (argc, argv, "aip", long_options, nullptr)) != -1)
134     {
135       switch (optc)
136         {
137         case 'a':
138           append = true;
139           break;
140 
141         case 'i':
142           ignore_interrupts = true;
143           break;
144 
145         case 'p':
146           if (optarg)
147             output_error = XARGMATCH ("--output-error", optarg,
148                                       output_error_args, output_error_types);
149           else
150             output_error = output_error_warn_nopipe;
151           break;
152 
153         case_GETOPT_HELP_CHAR;
154 
155         case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
156 
157         default:
158           usage (EXIT_FAILURE);
159         }
160     }
161 
162   if (ignore_interrupts)
163     signal (SIGINT, SIG_IGN);
164 
165   if (output_error != output_error_sigpipe)
166     signal (SIGPIPE, SIG_IGN);
167 
168   /* Whether to detect and close a broken pipe output.
169      There is no need if the input is always ready for reading.  */
170   bool pipe_check = ((output_error == output_error_warn_nopipe
171                       || output_error == output_error_exit_nopipe)
172                      && iopoll_input_ok (STDIN_FILENO));
173 
174   /* Do *not* warn if tee is given no file arguments.
175      POSIX requires that it work when given no arguments.  */
176 
177   bool ok = tee_files (argc - optind, &argv[optind], pipe_check);
178   if (close (STDIN_FILENO) != 0)
179     error (EXIT_FAILURE, errno, "%s", _("standard input"));
180 
181   return ok ? EXIT_SUCCESS : EXIT_FAILURE;
182 }
183 
184 
185 /* Return the index of the first non-null descriptor after idx,
186    or -1 if all are null.  */
187 
188 static int
get_next_out(FILE ** descriptors,int nfiles,int idx)189 get_next_out (FILE **descriptors, int nfiles, int idx)
190 {
191   for (idx++; idx <= nfiles; idx++)
192     if (descriptors[idx])
193       return idx;
194   return -1;  /* no outputs remaining */
195 }
196 
197 /* Remove descriptors[i] due to write failure or broken pipe.
198    Return true if this indicates a reportable error.  */
199 
200 static bool
fail_output(FILE ** descriptors,char ** files,int i)201 fail_output (FILE **descriptors, char **files, int i)
202 {
203   int w_errno = errno;
204   bool fail = errno != EPIPE
205               || output_error == output_error_exit
206               || output_error == output_error_warn;
207   if (descriptors[i] == stdout)
208     clearerr (stdout); /* Avoid redundant close_stdout diagnostic.  */
209   if (fail)
210     {
211       error (output_error == output_error_exit
212              || output_error == output_error_exit_nopipe,
213              w_errno, "%s", quotef (files[i]));
214     }
215   descriptors[i] = nullptr;
216   return fail;
217 }
218 
219 
220 /* Copy the standard input into each of the NFILES files in FILES
221    and into the standard output.  As a side effect, modify FILES[-1].
222    Return true if successful.  */
223 
224 static bool
tee_files(int nfiles,char ** files,bool pipe_check)225 tee_files (int nfiles, char **files, bool pipe_check)
226 {
227   size_t n_outputs = 0;
228   FILE **descriptors;
229   bool *out_pollable IF_LINT ( = nullptr);
230   char buffer[BUFSIZ];
231   ssize_t bytes_read = 0;
232   int i;
233   int first_out = 0;  /* idx of first non-null output in descriptors */
234   bool ok = true;
235   char const *mode_string =
236     (O_BINARY
237      ? (append ? "ab" : "wb")
238      : (append ? "a" : "w"));
239 
240   xset_binary_mode (STDIN_FILENO, O_BINARY);
241   xset_binary_mode (STDOUT_FILENO, O_BINARY);
242   fadvise (stdin, FADVISE_SEQUENTIAL);
243 
244   /* Set up FILES[0 .. NFILES] and DESCRIPTORS[0 .. NFILES].
245      In both arrays, entry 0 corresponds to standard output.  */
246 
247   descriptors = xnmalloc (nfiles + 1, sizeof *descriptors);
248   if (pipe_check)
249     out_pollable = xnmalloc (nfiles + 1, sizeof *out_pollable);
250   files--;
251   descriptors[0] = stdout;
252   if (pipe_check)
253     out_pollable[0] = iopoll_output_ok (fileno (descriptors[0]));
254   files[0] = bad_cast (_("standard output"));
255   setvbuf (stdout, nullptr, _IONBF, 0);
256   n_outputs++;
257 
258   for (i = 1; i <= nfiles; i++)
259     {
260       /* Do not treat "-" specially - as mandated by POSIX.  */
261        descriptors[i] = fopen (files[i], mode_string);
262       if (descriptors[i] == nullptr)
263         {
264           if (pipe_check)
265             out_pollable[i] = false;
266           error (output_error == output_error_exit
267                  || output_error == output_error_exit_nopipe,
268                  errno, "%s", quotef (files[i]));
269           ok = false;
270         }
271       else
272         {
273           if (pipe_check)
274             out_pollable[i] = iopoll_output_ok (fileno (descriptors[i]));
275           setvbuf (descriptors[i], nullptr, _IONBF, 0);
276           n_outputs++;
277         }
278     }
279 
280   while (n_outputs)
281     {
282       if (pipe_check && out_pollable[first_out])
283         {
284           /* Monitor for input, or errors on first valid output.  */
285           int err = iopoll (STDIN_FILENO, fileno (descriptors[first_out]),
286                             true);
287 
288           /* Close the output if it became a broken pipe.  */
289           if (err == IOPOLL_BROKEN_OUTPUT)
290             {
291               errno = EPIPE;  /* behave like write produced EPIPE */
292               if (fail_output (descriptors, files, first_out))
293                 ok = false;
294               n_outputs--;
295               first_out = get_next_out (descriptors, nfiles, first_out);
296               continue;
297             }
298           else if (err == IOPOLL_ERROR)
299             {
300               error (0, errno, _("iopoll error"));
301               ok = false;
302             }
303         }
304 
305       bytes_read = read (STDIN_FILENO, buffer, sizeof buffer);
306       if (bytes_read < 0 && errno == EINTR)
307         continue;
308       if (bytes_read <= 0)
309         break;
310 
311       /* Write to all NFILES + 1 descriptors.
312          Standard output is the first one.  */
313       for (i = 0; i <= nfiles; i++)
314         if (descriptors[i]
315             && ! fwrite_wait (buffer, bytes_read, descriptors[i]))
316           {
317             if (fail_output (descriptors, files, i))
318               ok = false;
319             n_outputs--;
320             if (i == first_out)
321               first_out = get_next_out (descriptors, nfiles, first_out);
322           }
323     }
324 
325   if (bytes_read == -1)
326     {
327       error (0, errno, _("read error"));
328       ok = false;
329     }
330 
331   /* Close the files, but not standard output.  */
332   for (i = 1; i <= nfiles; i++)
333     if (descriptors[i] && ! fclose_wait (descriptors[i]))
334       {
335         error (0, errno, "%s", quotef (files[i]));
336         ok = false;
337       }
338 
339   free (descriptors);
340   if (pipe_check)
341     free (out_pollable);
342 
343   return ok;
344 }
345