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