From d7cf13b968b99cb070c7d5d2e1f7ff1a8072f2f3 Mon Sep 17 00:00:00 2001
From: Mattias Andrée <maandree@kth.se>
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 <maandree@kth.se>
---
 src/gammad.c | 53 ++++++++++++++++++++++++++++++++++++++++++++++++++++-
 src/util.c   | 39 ++++++++++++++++++++++++++++++++++++++-
 src/util.h   | 16 ++++++++++++++++
 3 files changed, 106 insertions(+), 2 deletions(-)

(limited to 'src')

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