From d7cf13b968b99cb070c7d5d2e1f7ff1a8072f2f3 Mon Sep 17 00:00:00 2001 From: Mattias Andrée Date: Tue, 12 Jul 2016 10:42:29 +0200 Subject: Implement re-exec MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mattias Andrée --- src/gammad.c | 53 ++++++++++++++++++++++++++++++++++++++++++++++++++++- src/util.c | 39 ++++++++++++++++++++++++++++++++++++++- src/util.h | 16 ++++++++++++++++ 3 files changed, 106 insertions(+), 2 deletions(-) diff --git a/src/gammad.c b/src/gammad.c index 254dd52..fa24d82 100644 --- a/src/gammad.c +++ b/src/gammad.c @@ -1210,7 +1210,8 @@ static void usage(void) int main(int argc, char** argv) { int rc = 1, preserve = 0, foreground = 0, keep_stderr = 0, r; - const char* statefile = NULL; + char* statefile = NULL; + char* statebuffer = NULL; ARGBEGIN { @@ -1245,6 +1246,8 @@ int main(int argc, char** argv) if (argc > 0) usage(); + restart: /* If C had comefrom: comefrom reexec_failure; */ + switch ((r = initialise(statefile == NULL, preserve, foreground, keep_stderr))) { case 1: @@ -1265,12 +1268,60 @@ int main(int argc, char** argv) if (unmarshal_and_merge_state(statefile) < 0) goto fail; unlink(statefile), statefile = NULL; + reexec = 0; /* See `if (reexec && !terminate)` */ + } + + if (reexec && !terminate) + { + size_t buffer_size; + int fd; + + /* `reexec = 0;` is done later in case of re-execute failure, + * since it determines whether `statefile` shall be freed. */ + + statefile = get_state_pathname(); + if (statefile == NULL) + goto fail; + + buffer_size = marshal(NULL); + statebuffer = malloc(buffer_size); + if (statebuffer == NULL) + goto fail; + if (marshal(statebuffer) != buffer_size) + abort(); + + fd = open(statefile, O_CREAT, S_IRUSR | S_IWUSR); + if (fd < 0) + goto fail; + + if (nwrite(fd, statebuffer, buffer_size) != buffer_size) + { + perror(argv0); + close(fd); + errno = 0; + goto fail; + } + free(statebuffer), statebuffer = NULL; + + if ((close(fd) < 0) && (errno != EINTR)) + goto fail; + + destroy(0); + + execlp(argv0_real ? argv0_real : argv0, argv0, "-#", statefile, preserve ? "-p" : NULL, NULL); + perror(argv0); + fprintf(stderr, "%s: restoring state without re-executing\n", argv0); + free(argv0_real), argv0_real = NULL; + goto restart; } rc = 0; done: + free(statebuffer); if (statefile) unlink(statefile); + if (reexec) + free(statefile); destroy(1); return rc; fail: diff --git a/src/util.c b/src/util.c index f73634a..5cbe44b 100644 --- a/src/util.c +++ b/src/util.c @@ -82,7 +82,11 @@ void* nread(int fd, size_t* n) got = read(fd, buf + *n, size - *n); if (got < 0) - goto fail; + { + if (errno == EINTR) + continue; + goto fail; + } if (got == 0) break; *n += (size_t)got; @@ -98,6 +102,39 @@ void* nread(int fd, size_t* n) } +/** + * Write an entire buffer to a file + * + * Not cancelled by `EINTR` + * + * @param fd The file descriptor + * @param buf The buffer which shall be written to the fail + * @param n The size of the buffer + * @return The number of written bytes, less than `n` + * on error, cannot exceed `n` + */ +size_t nwrite(int fd, const void* buf, size_t n) +{ + const char* bs = buf; + ssize_t wrote; + size_t ptr = 0; + + while (ptr < n) + { + wrote = write(fd, bs + ptr, n - ptr); + if (wrote <= 0) + { + if ((wrote < 0) && (errno == EINTR)) + continue; + return ptr; + } + ptr += (size_t)wrote; + } + + return ptr; +} + + /** * Duplicate a file descriptor an make sure * the new file descriptor's index as a diff --git a/src/util.h b/src/util.h index 858d972..18066fa 100644 --- a/src/util.h +++ b/src/util.h @@ -33,6 +33,8 @@ void* memdup(const void* src, size_t n); /** * Read an entire file * + * Not cancelled by `EINTR` + * * @param fd The file descriptor * @param n Output for the size of the file * @return The read content, plus a NUL byte at @@ -41,6 +43,20 @@ void* memdup(const void* src, size_t n); void* nread(int fd, size_t* n); +/** + * Write an entire buffer to a file + * + * Not cancelled by `EINTR` + * + * @param fd The file descriptor + * @param buf The buffer which shall be written to the fail + * @param n The size of the buffer + * @return The number of written bytes, less than `n` + * on error, cannot exceed `n` + */ +size_t nwrite(int fd, const void* buf, size_t n); + + /** * Duplicate a file descriptor an make sure * the new file descriptor's index as a -- cgit v1.2.3-70-g09d2