xref: /netbsd-src/external/gpl3/gdb.old/dist/gdb/testsuite/lib/read1.c (revision 8b657b0747480f8989760d71343d6dd33f8d4cf9)
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 
38 static void
39 init_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 
67 ssize_t
68 read (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