diff --git a/changes/bug20988 b/changes/bug20988 new file mode 100644 index 0000000000..b1d73f08e7 --- /dev/null +++ b/changes/bug20988 @@ -0,0 +1,4 @@ + o Minor bugfixes (portability): + - Add Tor compatibility function for fgets(3) due to inconsistency of + returned values in different supported C libraries. This fixes unit test + failures reported on FreeBSD. Fixes bug 20988. diff --git a/src/common/compat.c b/src/common/compat.c index 0dbede6bed..da4283fbaa 100644 --- a/src/common/compat.c +++ b/src/common/compat.c @@ -3476,6 +3476,45 @@ tor_getpass(const char *prompt, char *output, size_t buflen) #endif } +/** Read at most one less than the number of characters specified by + * size from the given stream and store it in str. + * + * Reading stops when a newline character is found or at EOF or error. If any + * characters are read and there's no error, a trailing NUL byte is appended to + * the end of str. + * + * Upon successful completion, this function returns a pointer to the string + * str. If EOF occurs before any characters are read the function will + * return NULL and the content of str is unchanged. Upon error, the + * function returns NULL and the caller must check for error using foef(3) and + * ferror(3). + */ +char * +tor_fgets(char *str, int size, FILE *stream) +{ + char *ret; + + /* Reset errno before our call to fgets(3) to avoid a situation where the + * caller is calling us again because we just returned NULL and errno == + * EAGAIN, but when they call us again we will always return NULL because the + * error flag on the file handler remains set and errno is set to EAGAIN. + */ + errno = 0; + + ret = fgets(str, size, stream); + + /* FreeBSD, OpenBSD, Linux (glibc), and Linux (musl) seem to disagree about + * what to do in the given situation. We check if the stream has been flagged + * with an error-bit and return NULL in that situation if errno is also set + * to EAGAIN. + */ + if (ferror(stream) && errno == EAGAIN) { + return NULL; + } + + return ret; +} + /** Return the amount of free disk space we have permission to use, in * bytes. Return -1 if the amount of free space can't be determined. */ int64_t diff --git a/src/common/compat.h b/src/common/compat.h index ee1c9454de..1f51ece61f 100644 --- a/src/common/compat.h +++ b/src/common/compat.h @@ -740,6 +740,8 @@ STATIC int tor_ersatz_socketpair(int family, int type, int protocol, ssize_t tor_getpass(const char *prompt, char *output, size_t buflen); +char *tor_fgets(char *str, int size, FILE *stream); + /* This needs some of the declarations above so we include it here. */ #include "compat_threads.h"