summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenjamin Schubert <contact@benschubert.me>2019-10-08 18:33:48 +0100
committerBenjamin Schubert <contact@benschubert.me>2019-10-09 09:43:06 +0000
commitb592cb85c7e8f3580e75dff531d44ec1bed84dc1 (patch)
tree13d2c7d408fb98d60b8695c941c29ddd23d52cee
parent4c0bb3404f5d760d4825b886b18fc8b5b052b20e (diff)
downloadbuildstream-bschubert/fuse-permissions.tar.gz
_fuse/mount.py: Monitor the fuse process while waiting for the mountbschubert/fuse-permissions
In some cases, users might not have permissions to use fuse, or fuse might crash. This was previously leading to a hanged process and, with chance an error message on the UI, which could be overwritten. This ensures we are explicitely monitoring the fuse process while waiting and adds better reporting of the fuse errors.
-rw-r--r--src/buildstream/_fuse/mount.py49
1 files changed, 35 insertions, 14 deletions
diff --git a/src/buildstream/_fuse/mount.py b/src/buildstream/_fuse/mount.py
index ac5fb2295..52d3ed65a 100644
--- a/src/buildstream/_fuse/mount.py
+++ b/src/buildstream/_fuse/mount.py
@@ -27,7 +27,7 @@ from multiprocessing import Process
from .fuse import FUSE
from .._exceptions import ImplError
-from .. import _signals
+from .. import _signals, utils
# Just a custom exception to raise here, for identifying possible
@@ -82,6 +82,7 @@ class Mount():
__mountpoint = None
__operations = None
__process = None
+ __logfile = None
################################################
# User Facing API #
@@ -102,7 +103,7 @@ class Mount():
assert self.__process is None
self.__mountpoint = mountpoint
- self.__process = Process(target=self.__run_fuse)
+ self.__process = Process(target=self.__run_fuse, args=(self.__logfile.name,))
# Ensure the child process does not inherit our signal handlers, if the
# child wants to handle a signal then it will first set its own
@@ -110,8 +111,12 @@ class Mount():
with _signals.blocked([signal.SIGTERM, signal.SIGTSTP, signal.SIGINT], ignore=False):
self.__process.start()
- # This is horrible, we're going to wait until mountpoint is mounted and that's it.
while not os.path.ismount(mountpoint):
+ if not self.__process.is_alive():
+ self.__logfile.seek(0)
+ stderr = self.__logfile.read()
+ raise FuseMountError("Unable to mount {}: {}".format(mountpoint, stderr.decode().strip()))
+
time.sleep(1 / 100)
# unmount():
@@ -127,8 +132,11 @@ class Mount():
# Report an error if ever the underlying operations crashed for some reason.
if self.__process.exitcode != 0:
- raise FuseMountError("{} reported exit code {} when unmounting"
- .format(type(self).__name__, self.__process.exitcode))
+ self.__logfile.seek(0)
+ stderr = self.__logfile.read()
+
+ raise FuseMountError("{} reported exit code {} when unmounting: {}"
+ .format(type(self).__name__, self.__process.exitcode, stderr))
self.__mountpoint = None
self.__process = None
@@ -145,12 +153,17 @@ class Mount():
@contextmanager
def mounted(self, mountpoint):
- self.mount(mountpoint)
- try:
- with _signals.terminator(self.unmount):
- yield
- finally:
- self.unmount()
+ with utils._tempnamedfile() as logfile:
+ self.__logfile = logfile
+
+ self.mount(mountpoint)
+ try:
+ with _signals.terminator(self.unmount):
+ yield
+ finally:
+ self.unmount()
+
+ self.__logfile = None
################################################
# Abstract Methods #
@@ -169,7 +182,11 @@ class Mount():
################################################
# Child Process #
################################################
- def __run_fuse(self):
+ def __run_fuse(self, filename):
+ # Override stdout/stderr to the file given as a pointer, that way our parent process can get our output
+ out = open(filename, "w")
+ os.dup2(out.fileno(), sys.stdout.fileno())
+ os.dup2(out.fileno(), sys.stderr.fileno())
# First become session leader while signals are still blocked
#
@@ -187,8 +204,12 @@ class Mount():
# Run fuse in foreground in this child process, internally libfuse
# will handle SIGTERM and gracefully exit its own little main loop.
#
- FUSE(self.__operations, self.__mountpoint, nothreads=True, foreground=True, nonempty=True,
- **self._fuse_mount_options)
+ try:
+ FUSE(self.__operations, self.__mountpoint, nothreads=True, foreground=True, nonempty=True,
+ **self._fuse_mount_options)
+ except RuntimeError as exc:
+ # FUSE will throw a RuntimeError with the exit code of libfuse as args[0]
+ sys.exit(exc.args[0])
# Explicit 0 exit code, if the operations crashed for some reason, the exit
# code will not be 0, and we want to know about it.