1/* This is part of GDB, the GNU debugger.
2
3   Copyright 2011-2023 Free Software Foundation, Inc.
4
5   This program is free software; you can redistribute it and/or modify
6   it under the terms of the GNU General Public License as published by
7   the Free Software Foundation; either version 3 of the License, or
8   (at your option) any later version.
9
10   This program is distributed in the hope that it will be useful,
11   but WITHOUT ANY WARRANTY; without even the implied warranty of
12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   GNU General Public License for more details.
14
15   You should have received a copy of the GNU General Public License
16   along with this program.  If not, see <http://www.gnu.org/licenses/>.
17*/
18
19#define _GNU_SOURCE 1
20#include <dlfcn.h>
21#include <unistd.h>
22#include <fcntl.h>
23#include <stdlib.h>
24#include <string.h>
25#include <errno.h>
26#include <stdio.h>
27
28/* Default READMORE method.  */
29#define READMORE_METHOD_DEFAULT 2
30
31/* Default READMORE sleep time in miliseconds.  */
32#define READMORE_SLEEP_DEFAULT 10
33
34/* Helper function.  Intialize *METHOD according to environment variable
35   READMORE_METHOD, and *SLEEP according to environment variable
36   READMORE_SLEEP.  */
37
38static void
39init_readmore (int *method, unsigned int *sleep, FILE **log)
40{
41  char *env = getenv ("READMORE_METHOD");
42  if (env == NULL)
43    *method = READMORE_METHOD_DEFAULT;
44  else if (strcmp (env, "1") == 0)
45    *method = 1;
46  else if (strcmp (env, "2") == 0)
47    *method = 2;
48  else
49    /* Default.  */
50    *method = READMORE_METHOD_DEFAULT;
51
52  env = getenv ("READMORE_SLEEP");
53  if (env == NULL)
54    *sleep = READMORE_SLEEP_DEFAULT;
55  else
56    *sleep = atoi (env);
57
58  env = getenv ("READMORE_LOG");
59  if (env == NULL)
60    *log = NULL;
61  else
62    *log = fopen (env, "w");
63}
64
65/* Wrap 'read', and modify it's behaviour using READ1 or READMORE style.  */
66
67ssize_t
68read (int fd, void *buf, size_t count)
69{
70  static ssize_t (*read2) (int fd, void *buf, size_t count) = NULL;
71  static FILE *log;
72  int readmore;
73#ifdef READMORE
74  readmore = 1;
75#else
76  readmore = 0;
77#endif
78  static int readmore_method;
79  static unsigned int readmore_sleep;
80  if (read2 == NULL)
81    {
82      /* Use setenv (v, "", 1) rather than unsetenv (v) to work around
83         https://core.tcl-lang.org/tcl/tktview?name=67fd4f973a "incorrect
84	 results of 'info exists' when unset env var in one interp and check
85	 for existence from another interp".  */
86      setenv ("LD_PRELOAD", "", 1);
87      read2 = dlsym (RTLD_NEXT, "read");
88      if (readmore)
89	init_readmore (&readmore_method, &readmore_sleep, &log);
90    }
91
92  /* Only modify 'read' behaviour when reading from the terminal.  */
93  if (isatty (fd) == 0)
94    goto fallback;
95
96  if (!readmore)
97    {
98      /* READ1.  Force read to return only one byte at a time.  */
99      return read2 (fd, buf, 1);
100    }
101
102  if (readmore_method == 1)
103    {
104      /* READMORE, method 1.  Wait a little before doing a read.  */
105      usleep (readmore_sleep * 1000);
106      return read2 (fd, buf, count);
107    }
108
109  if (readmore_method == 2)
110    {
111      /* READMORE, method 2.  After doing a read, either return or wait
112	 a little and do another read, and so on.  */
113      ssize_t res, total;
114      int iteration;
115      int max_iterations = -1;
116
117      total = 0;
118      for (iteration = 1; ; iteration++)
119	{
120	  res = read2 (fd, (char *)buf + total, count - total);
121	  if (log != NULL)
122	    fprintf (log,
123		     "READ (%d): fd: %d, COUNT: %zd, RES: %zd, ERRNO: %s\n",
124		     iteration, fd, count - total, res,
125		     res == -1 ? strerror (errno) : "none");
126	  if (res == -1)
127	    {
128	      if (iteration == 1)
129		{
130		  /* Error on first read, report.  */
131		  total = -1;
132		  break;
133		}
134
135	      if (total > 0
136		  && (errno == EAGAIN || errno == EWOULDBLOCK || errno == EIO))
137		{
138		  /* Ignore error, but don't try anymore reading.  */
139		  errno = 0;
140		  break;
141		}
142
143	      /* Other error, report back.  */
144	      total = -1;
145	      break;
146	    }
147
148	  total += res;
149	  if (total == count)
150	    /* Buf full, no need to do any more reading.  */
151	    break;
152
153	  /* Handle end-of-file.  */
154	  if (res == 0)
155	    break;
156
157	  if (iteration == max_iterations)
158	    break;
159
160	  usleep (readmore_sleep * 1000);
161	}
162
163      if (log)
164	fprintf (log, "READ returning: RES: %zd, ERRNO: %s\n",
165		 total, total == -1 ? strerror (errno) : "none");
166      return total;
167    }
168
169 fallback:
170  /* Fallback, regular read.  */
171  return read2 (fd, buf, count);
172}
173