libfuse/fuse_daemonize:  wait until daemon child process is ready (#55)

Mounting a FUSE file system remotely using SSH in combination with
pseudo-terminal allocation (-t), results in "Transport endpoint is
not connected" errors when trying to access the file system contents.

For example:

  # ssh -t root@localhost  "cmsfs-fuse /dev/disk/by-path/ccw-0.0.0190 /CMSFS"
  Connection to localhost closed.
  # ls /CMSFS
  ls: cannot access '/CMSFS': Transport endpoint is not connected

The cmsfs-fuse main program (which can also be any other FUSE file
system) calls into the fuse_main() libfuse library function.
The fuse_main() function later calls fuse_daemonize() to fork the
daemon process to handle the FUSE file system I/O.

The fuse_daemonize() function calls fork() as usual.  The child
proceeds with setsid() and then redirecting its file descriptors
to /dev/null etc.  The parent process, simply exits.

The child's functions and the parent's exit creates a subtle race.
This is seen with an SSH connection.  The SSH command above calls
cmsfs-fuse on an allocated pseudo-terminal device (-t option).

If the parent exits, SSH receives the command completion and closes
the connection, that means, it closes the master side of the
pseudo-terminal.  This causes a HUP signal being sent to the process
group on the pseudo-terminal.  At this point in time, the child might
not have completed the setsid() call and, hence, becomes terminated.
Note that fuse daemon sets up its signal handlers after fuse_daemonize()
has completed.

Even if the child has the chance to disassociate from its parent process
group to become it's own process group with setsid(), the child still
has the pseudo-terminal opened as stdin, stdout, and stderr.  So the
pseudo-terminal still behave as controlling terminal and might cause a
SIGHUP at closing the the master side.

To solve the problem, the parent has to wait until the child (the fuse
daemon process) has completed its processing, that means, has become
its own process group with setsid() and closed any file descriptors
pointing to the pseudo-terminal.

Closes: #27

Reported-by: Ofer Baruch <oferba@il.ibm.com>
Reviewed-by: Gerald Schaefer <gerald.schaefer@de.ibm.com>
Signed-off-by: Hendrik Brueckner <brueckner@linux.vnet.ibm.com>
diff --git a/ChangeLog b/ChangeLog
index d18888d..0990310 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -2,6 +2,8 @@
 ==================
 
 * Added SELinux support.
+* Fixed race-condition when session is terminated right after starting
+  a FUSE file system.
 
 FUSE 2.9.6 (2016-04-23)
 =======================
diff --git a/lib/helper.c b/lib/helper.c
index b644012..49d30f9 100644
--- a/lib/helper.c
+++ b/lib/helper.c
@@ -181,6 +181,13 @@
 {
 	if (!foreground) {
 		int nullfd;
+		int waiter[2];
+		char completed;
+
+		if (pipe(waiter)) {
+			perror("fuse_daemonize: pipe");
+			return -1;
+		}
 
 		/*
 		 * demonize current process by forking it and killing the
@@ -193,6 +200,7 @@
 		case 0:
 			break;
 		default:
+			read(waiter[0], &completed, sizeof(completed));
 			_exit(0);
 		}
 
@@ -211,6 +219,12 @@
 			if (nullfd > 2)
 				close(nullfd);
 		}
+
+		/* Propagate completion of daemon initializatation */
+		completed = 1;
+		write(waiter[1], &completed, sizeof(completed));
+		close(waiter[0]);
+		close(waiter[1]);
 	}
 	return 0;
 }