Commits

Evan Gates  committed 7118956

retry syscalls on EINTR

  • Participants
  • Parent commits 5a4be7d

Comments (0)

Files changed (1)

File find_replace.c

 #include <sys/stat.h>
 #include <unistd.h>
 
-#define check_goto(label, expr, msg) ({ if (!(expr)) { fprintf(stderr, "%s: " msg " %s: %s\n", *argv, *path, strerror(errno)); goto label; } })
+#define print_err(msg)               fprintf(stderr, "%s: " msg " %s: %s\n", *argv, *path, strerror(errno))
+#define check_goto(label, expr, msg) do { if (expr) break; print_err(msg); goto label; } while (0)
 
-int read_all(int fd, char *buf, size_t count)
-{
-	ssize_t red;
-	while (count) {
-		if ((red = read(fd, buf, count)) < 0) {
-			if (errno == EINTR)
-				continue;
-			else
-				return -1;
-		}
-		buf   += red;
-		count -= red;
-	}
-	return 0;
-}
+// retry syscalls when interrupted by signal
+// NOTE: close(3p) is not included as state of fildes is unspecified after interruption
+int     open_     (const char *path, int oflag)         { int     r; do { r = open     (path, oflag);        } while (r < 0 && errno == EINTR); return r; }
+ssize_t read_     (int fildes, void *buf, size_t nbyte) { ssize_t r; do { r = read     (fildes, buf, nbyte); } while (r < 0 && errno == EINTR); return r; }
+ssize_t write_    (int fildes, void *buf, size_t nbyte) { ssize_t r; do { r = write    (fildes, buf, nbyte); } while (r < 0 && errno == EINTR); return r; }
+int     ftruncate_(int fildes, off_t length)            { int     r; do { r = ftruncate(fildes, length);     } while (r < 0 && errno == EINTR); return r; }
+
+#define open      open_
+#define read      read_
+#define write     write_
+#define ftruncate ftruncate_
 
-int write_all(int fd, char *buf, size_t count)
+int rwall(ssize_t (*rw)(), int fd, char *buf, size_t count)
 {
-	ssize_t wrote;
-	while (count) {
-		if ((wrote = write(fd, buf, count)) < 0) {
-			if (errno == EINTR)
-				continue;
-			else
-				return -1;
-		}
-		buf   += wrote;
-		count -= wrote;
-	}
+	for (ssize_t r; count; buf += r, count -= r)
+		if ((r = rw(fd, buf, count)) < 0)
+			return r;
 	return 0;
 }
 
 int main(int argc, char **argv)
 {
-	char **path, *beg, *end, *find, *repl, *p, *file = NULL;
-	int    fd = -1;
-	size_t count, find_len, repl_len;
-	off_t  size;
+	char *find, *repl;
+	size_t find_len, repl_len;
 
 	if (argc < 4 || strlen(argv[1]) == 0) {
 		fprintf(stderr, "Usage: %s find replace file...\n", *argv);
 	find_len = strlen(find);
 	repl_len = strlen(repl);
 
-	for (path = argv + 3; *path; path++) {
-		check_goto(cleanup,    0 <= (fd   = open  (*path, O_RDWR))  , "Failed to open"      );
-		check_goto(cleanup,    0 <= (size = lseek (fd, 0, SEEK_END)), "Failed to lseek EOF" );
-		if (size == 0) goto cleanup; // empty file
-		check_goto(cleanup,    0 == lseek   (fd, 0, SEEK_SET)       , "Failed to lseek BOF" );
-		check_goto(cleanup, NULL != (file = malloc(size))           , "Failed to malloc for");
-		check_goto(cleanup,    0 == read_all(fd, file, size)        , "Failed to read"      );
-		check_goto(cleanup,    0 == lseek   (fd, 0, SEEK_SET)       , "Failed to lseek BOF" );
+	for (char **path = argv + 3; *path; path++) {
+		char *beg, *end, *p, *file = NULL;
+		int   fd = -1;
+		off_t size;
+		check_goto(cleanup,    0 <= (fd   = open  (*path, O_RDWR))      , "Failed to open"      );
+		check_goto(cleanup,    0 <= (size = lseek (fd, 0, SEEK_END))    , "Failed to lseek EOF" ); if (size == 0) goto cleanup; // empty file
+		check_goto(cleanup,    0 ==         lseek (fd, 0, SEEK_SET)     , "Failed to lseek BOF" );
+		check_goto(cleanup, NULL != (file = malloc(size))               , "Failed to malloc for");
+		check_goto(cleanup,    0 ==         rwall (read, fd, file, size), "Failed to read"      );
+		check_goto(cleanup,    0 ==         lseek (fd, 0, SEEK_SET)     , "Failed to lseek BOF" );
 
 		for (beg = file, end = beg + size, size = 0; (p = memmem(beg, end - beg, find, find_len)); size += repl_len + (p - beg), beg = p + find_len) {
-			check_goto(cleanup, 0 == write_all(fd, beg , p - beg ), "Failed to write to");
-			check_goto(cleanup, 0 == write_all(fd, repl, repl_len), "Failed to write to");
+			check_goto(cleanup, 0 == rwall(write, fd, beg , p - beg ), "Failed to write to");
+			check_goto(cleanup, 0 == rwall(write, fd, repl, repl_len), "Failed to write to");
 		}
-		check_goto(cleanup, 0 == write_all(fd, beg, end - beg), "Failed to write to");
-		size += end - beg;
-		check_goto(cleanup, 0 == ftruncate(fd, size)          , "Failed to truncate");
+		check_goto(cleanup, 0 == rwall(write, fd, beg, end - beg), "Failed to write to"); size += end - beg;
+		check_goto(cleanup, 0 == ftruncate(fd, size)             , "Failed to truncate");
 
 cleanup:
-		if (fd >= 0 && close(fd) < 0) fprintf(stderr, "%s: Error closing %s: %s\n", *argv, *path, strerror(errno));
+		if (fd >= 0 && close(fd) < 0) print_err("Failed to close");
 		if (file) free(file);
-		fd   = -1;
-		file = NULL;
 	}
 
 	return 0;