about summary refs log tree commit diff
path: root/src/nix-worker
diff options
context:
space:
mode:
authorEelco Dolstra <e.dolstra@tudelft.nl>2006-12-03T03·03+0000
committerEelco Dolstra <e.dolstra@tudelft.nl>2006-12-03T03·03+0000
commit3ed9e4ad9b72dfbe59d47823beec829fe550351e (patch)
treefc8a5086d483033f0a9d51a59de88b3f436fd531 /src/nix-worker
parent4251f94b32daed2abb0324439466876a97acdb77 (diff)
* Some hardcore magic to handle asynchronous client disconnects.
  The problem is that when we kill the client while the worker is
  building, and the builder is not writing anything to stderr, then
  the worker never notice that the socket is closed on the other side,
  so it just continues indefinitely.  The solution is to catch SIGIO,
  which is sent when the far side of the socket closes, and simulate
  an normal interruption.  Of course, SIGIO is also sent every time
  the client sends data over the socket, so we only enable the signal
  handler when we're not expecting any data...

Diffstat (limited to 'src/nix-worker')
-rw-r--r--src/nix-worker/main.cc38
1 files changed, 37 insertions, 1 deletions
diff --git a/src/nix-worker/main.cc b/src/nix-worker/main.cc
index 17e892c648fc..c4a2d8a7a026 100644
--- a/src/nix-worker/main.cc
+++ b/src/nix-worker/main.cc
@@ -6,6 +6,9 @@
 #include "archive.hh"
 
 #include <iostream>
+#include <unistd.h>
+#include <signal.h>
+#include <fcntl.h>
 
 using namespace nix;
 
@@ -31,6 +34,10 @@ static Sink * _to; /* !!! should make writeToStderr an object */
 bool canSendStderr;
 
 
+/* This function is called anytime we want to write something to
+   stderr.  If we're in a state where the protocol allows it (i.e.,
+   when canSendStderr), send the message to the client over the
+   socket. */
 static void tunnelStderr(const unsigned char * buf, size_t count)
 {
     writeFull(STDERR_FILENO, buf, count);
@@ -48,11 +55,28 @@ static void tunnelStderr(const unsigned char * buf, size_t count)
 }
 
 
+/* A SIGIO signal is received when data is available on the client
+   communication scoket, or when the client has closed its side of the
+   socket.  This handler is enabled at precisely those moments in the
+   protocol when we're doing work and the client is supposed to be
+   quiet.  Thus, if we get a SIGIO signal, it means that the client
+   has quit.  So we should quit as well. */
+static void sigioHandler(int sigNo)
+{
+    _isInterrupted = 1;
+    canSendStderr = false;
+    write(STDERR_FILENO, "SIGIO\n", 6);
+}
+
+
 /* startWork() means that we're starting an operation for which we
    want to send out stderr to the client. */
 static void startWork()
 {
     canSendStderr = true;
+
+    /* Handle client death asynchronously. */
+    signal(SIGIO, sigioHandler);
 }
 
 
@@ -60,6 +84,11 @@ static void startWork()
    client. */
 static void stopWork()
 {
+    /* Stop handling async client death; we're going to a state where
+       we're either sending or receiving from the client, so we'll be
+       notified of client death anyway. */
+    signal(SIGIO, SIG_IGN);
+    
     canSendStderr = false;
     writeInt(STDERR_LAST, *_to);
 }
@@ -178,7 +207,7 @@ static void processConnection(Source & from, Sink & to)
     writeInt(WORKER_MAGIC_2, to);
 
     debug("greeting exchanged");
-
+    
     _to = &to;
     canSendStderr = false;
     writeToStderr = tunnelStderr;
@@ -216,6 +245,13 @@ void run(Strings args)
         if (arg == "--daemon") daemon = true;
     }
 
+    /* Allow us to receive SIGIO for events on the client socket. */
+    signal(SIGIO, SIG_IGN);
+    if (fcntl(STDIN_FILENO, F_SETOWN, getpid()) == -1)
+        throw SysError("F_SETOWN");
+    if (fcntl(STDIN_FILENO, F_SETFL, fcntl(STDIN_FILENO, F_GETFL, 0) | FASYNC) == -1)
+        throw SysError("F_SETFL");
+
     if (slave) {
         FdSource source(STDIN_FILENO);
         FdSink sink(STDOUT_FILENO);