From c792262b137a5f2daddac22f82e7d8d98d0d7d31 Mon Sep 17 00:00:00 2001 From: Andrew Gregory Date: Wed, 6 Aug 2014 16:36:00 -0400 Subject: wrap fgets to retry on EINTR The read() underlying fgets() can be interrupted by a signal handler causing fgets() to return NULL. Before we started handling SIGWINCH, the odds of interrupting a read were low and typically resulted in termination anyway. Replace all fgets calls with a wrapper that retries in EINTR. Signed-off-by: Andrew Gregory --- lib/libalpm/be_local.c | 14 +++++++------- lib/libalpm/trans.c | 2 +- lib/libalpm/util.c | 3 ++- src/common/util-common.c | 25 +++++++++++++++++++++++++ src/common/util-common.h | 3 +++ src/pacman/ini.c | 2 +- src/pacman/util.c | 8 ++++---- 7 files changed, 43 insertions(+), 14 deletions(-) diff --git a/lib/libalpm/be_local.c b/lib/libalpm/be_local.c index 1b333e44..091ed4c8 100644 --- a/lib/libalpm/be_local.c +++ b/lib/libalpm/be_local.c @@ -616,7 +616,7 @@ char *_alpm_local_db_pkgpath(alpm_db_t *db, alpm_pkg_t *info, } #define READ_NEXT() do { \ - if(fgets(line, sizeof(line), fp) == NULL && !feof(fp)) goto error; \ + if(safe_fgets(line, sizeof(line), fp) == NULL && !feof(fp)) goto error; \ _alpm_strip_newline(line, 0); \ } while(0) @@ -627,7 +627,7 @@ char *_alpm_local_db_pkgpath(alpm_db_t *db, alpm_pkg_t *info, #define READ_AND_STORE_ALL(f) do { \ char *linedup; \ - if(fgets(line, sizeof(line), fp) == NULL) {\ + if(safe_fgets(line, sizeof(line), fp) == NULL) {\ if(!feof(fp)) goto error; else break; \ } \ if(_alpm_strip_newline(line, 0) == 0) break; \ @@ -636,7 +636,7 @@ char *_alpm_local_db_pkgpath(alpm_db_t *db, alpm_pkg_t *info, } while(1) /* note the while(1) and not (0) */ #define READ_AND_SPLITDEP(f) do { \ - if(fgets(line, sizeof(line), fp) == NULL) {\ + if(safe_fgets(line, sizeof(line), fp) == NULL) {\ if(!feof(fp)) goto error; else break; \ } \ if(_alpm_strip_newline(line, 0) == 0) break; \ @@ -682,7 +682,7 @@ static int local_db_read(alpm_pkg_t *info, alpm_dbinfrq_t inforeq) } free(path); while(!feof(fp)) { - if(fgets(line, sizeof(line), fp) == NULL && !feof(fp)) { + if(safe_fgets(line, sizeof(line), fp) == NULL && !feof(fp)) { goto error; } if(_alpm_strip_newline(line, 0) == 0) { @@ -771,13 +771,13 @@ static int local_db_read(alpm_pkg_t *info, alpm_dbinfrq_t inforeq) goto error; } free(path); - while(fgets(line, sizeof(line), fp)) { + while(safe_fgets(line, sizeof(line), fp)) { _alpm_strip_newline(line, 0); if(strcmp(line, "%FILES%") == 0) { size_t files_count = 0, files_size = 0, len; alpm_file_t *files = NULL; - while(fgets(line, sizeof(line), fp) && + while(safe_fgets(line, sizeof(line), fp) && (len = _alpm_strip_newline(line, 0))) { if(!_alpm_greedy_grow((void **)&files, &files_size, (files_size ? files_size + sizeof(alpm_file_t) : 8 * sizeof(alpm_file_t)))) { @@ -797,7 +797,7 @@ static int local_db_read(alpm_pkg_t *info, alpm_dbinfrq_t inforeq) info->files.count = files_count; info->files.files = files; } else if(strcmp(line, "%BACKUP%") == 0) { - while(fgets(line, sizeof(line), fp) && _alpm_strip_newline(line, 0)) { + while(safe_fgets(line, sizeof(line), fp) && _alpm_strip_newline(line, 0)) { alpm_backup_t *backup; CALLOC(backup, 1, sizeof(alpm_backup_t), goto error); if(_alpm_split_backup(line, &backup)) { diff --git a/lib/libalpm/trans.c b/lib/libalpm/trans.c index 4a176086..aebd8dd2 100644 --- a/lib/libalpm/trans.c +++ b/lib/libalpm/trans.c @@ -280,7 +280,7 @@ static int grep(const char *fn, const char *needle) } while(!feof(fp)) { char line[1024]; - if(fgets(line, sizeof(line), fp) == NULL) { + if(safe_fgets(line, sizeof(line), fp) == NULL) { continue; } /* TODO: this will not work if the search string diff --git a/lib/libalpm/util.c b/lib/libalpm/util.c index 6dab0de2..43d0d7be 100644 --- a/lib/libalpm/util.c +++ b/lib/libalpm/util.c @@ -576,8 +576,9 @@ int _alpm_run_chroot(alpm_handle_t *handle, const char *cmd, char *const argv[]) .type = ALPM_EVENT_SCRIPTLET_INFO, .line = line }; - if(fgets(line, PATH_MAX, pipe_file) == NULL) + if(safe_fgets(line, PATH_MAX, pipe_file) == NULL) { break; + } alpm_logaction(handle, "ALPM-SCRIPTLET", "%s", line); EVENT(handle, &event); } diff --git a/src/common/util-common.c b/src/common/util-common.c index 3316eaec..ab74e7c6 100644 --- a/src/common/util-common.c +++ b/src/common/util-common.c @@ -17,6 +17,7 @@ * along with this program. If not, see . */ +#include #include #include @@ -102,6 +103,30 @@ int llstat(char *path, struct stat *buf) return ret; } +/** Wrapper around fgets() which properly handles EINTR + * @param s string to read into + * @param size maximum length to read + * @param stream stream to read from + * @return value returned by fgets() + */ +char *safe_fgets(char *s, int size, FILE *stream) +{ + char *ret; + int errno_save = errno, ferror_save = ferror(stream); + while((ret = fgets(s, size, stream)) == NULL && !feof(stream)) { + if(errno == EINTR) { + /* clear any errors we set and try again */ + errno = errno_save; + if(!ferror_save) { + clearerr(stream); + } + } else { + break; + } + } + return ret; +} + #ifndef HAVE_STRNDUP /* A quick and dirty implementation derived from glibc */ /** Determines the length of a fixed-size string. diff --git a/src/common/util-common.h b/src/common/util-common.h index 576702fa..ca8db5ab 100644 --- a/src/common/util-common.h +++ b/src/common/util-common.h @@ -20,6 +20,7 @@ #ifndef _PM_UTIL_COMMON_H #define _PM_UTIL_COMMON_H +#include #include /* struct stat */ const char *mbasename(const char *path); @@ -27,6 +28,8 @@ char *mdirname(const char *path); int llstat(char *path, struct stat *buf); +char *safe_fgets(char *s, int size, FILE *stream); + #ifndef HAVE_STRNDUP char *strndup(const char *s, size_t n); #endif diff --git a/src/pacman/ini.c b/src/pacman/ini.c index 2a3ef0ed..2eb32302 100644 --- a/src/pacman/ini.c +++ b/src/pacman/ini.c @@ -66,7 +66,7 @@ static int _parse_ini(const char *file, ini_parser_fn cb, void *data, goto cleanup; } - while(fgets(line, PATH_MAX, fp)) { + while(safe_fgets(line, PATH_MAX, fp)) { char *key, *value, *ptr; size_t line_len; diff --git a/src/pacman/util.c b/src/pacman/util.c index 6a095fdb..2671e54c 100644 --- a/src/pacman/util.c +++ b/src/pacman/util.c @@ -1403,7 +1403,7 @@ int multiselect_question(char *array, int count) flush_term_input(fileno(stdin)); - if(fgets(response, response_len, stdin)) { + if(safe_fgets(response, response_len, stdin)) { const size_t response_incr = 64; size_t len; /* handle buffer not being large enough to read full line case */ @@ -1416,7 +1416,7 @@ int multiselect_question(char *array, int count) lastchar = response + response_len - 1; /* sentinel byte */ *lastchar = 1; - if(fgets(response + response_len - response_incr - 1, + if(safe_fgets(response + response_len - response_incr - 1, response_incr + 1, stdin) == 0) { free(response); return -1; @@ -1467,7 +1467,7 @@ int select_question(int count) flush_term_input(fileno(stdin)); - if(fgets(response, sizeof(response), stdin)) { + if(safe_fgets(response, sizeof(response), stdin)) { size_t len = strtrim(response); if(len > 0) { int n; @@ -1521,7 +1521,7 @@ static int question(short preset, const char *format, va_list args) flush_term_input(fd_in); - if(fgets(response, sizeof(response), stdin)) { + if(safe_fgets(response, sizeof(response), stdin)) { size_t len = strtrim(response); if(len == 0) { return preset; -- cgit v1.2.3-54-g00ecf