aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/gammad.c53
-rw-r--r--src/util.c39
-rw-r--r--src/util.h16
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;
@@ -99,6 +103,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
* specified minimum value
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
@@ -42,6 +44,20 @@ 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
* specified minimum value