summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHelmut Grohne <helmut@subdivi.de>2016-12-14 10:08:44 +0100
committerHelmut Grohne <helmut@subdivi.de>2016-12-14 10:08:44 +0100
commitef0055e5e245d331de48a62c42a85d2886d335b1 (patch)
tree28ae4a80937ff1ac0a5000071c3d883ce98b0389
parent3ab4d59a12b884bc0a58a6cc751cc85ca5036083 (diff)
downloadtcvt-ef0055e5e245d331de48a62c42a85d2886d335b1.tar.gz
fix SIGWINCH race condition during startup
Some programs (e.g. ncmpc) do not like receiving a terminal of unknown size. Before we issue the resizepty method that would be the case. Since there was no synchronisation between resizepty and execvp, a program that starts quickly could run into this situation. Now we ensure that resizepty is called before execvp using an extra pipe.
-rwxr-xr-xtcvt.py39
1 files changed, 33 insertions, 6 deletions
diff --git a/tcvt.py b/tcvt.py
index 0e1a528..a257c2b 100755
--- a/tcvt.py
+++ b/tcvt.py
@@ -732,32 +732,58 @@ class ForkPty(object):
self.environ = environ
self.pid = -1
self.masterfd = -1
+ self.startpipew = -1
+ self.errpiper = -1
self.exitcode = 255
def __enter__(self):
assert self.pid == -1
assert self.masterfd == -1
- errpiper, errpipew = os.pipe()
+ assert self.startpipew == -1
+ assert self.errpiper == -1
+ startpiper, self.startpipew = os.pipe()
+ self.errpiper, errpipew = os.pipe()
set_cloexec(errpipew)
self.pid, self.masterfd = pty.fork()
if self.pid == 0: # child
- os.close(errpiper)
+ os.close(self.startpipew)
+ os.close(self.errpiper)
os.environ.update(self.environ)
+ # wait for the parent
+ os.read(startpiper, 1)
+ os.close(startpiper)
try:
os.execvp(self.argv[0], self.argv)
except OSError as err:
os.write(errpipew, "exec failed: %s" % (err,))
sys.exit(255)
+ os.close(startpiper)
os.close(errpipew)
- data = os.read(errpiper, 1024)
- os.close(errpiper)
+ return self.masterfd
+
+ def start(self):
+ """Allow the process to start executing.
+ @raises ExecutionError: when execvp in the child fails
+ """
+ assert self.startpipew >= 0
+ assert self.errpiper >= 0
+ # signal that execvp can proceed
+ os.write(self.startpipew, b"\0")
+ os.close(self.startpipew)
+ self.startpipew = -1
+ # check for execvp errors
+ data = os.read(self.errpiper, 1024)
+ os.close(self.errpiper)
+ self.errpiper = -1
if data:
raise ExecutionError(data)
- return self.masterfd
-
def __exit__(self, *_):
+ assert self.pid > 0
+ assert self.masterfd >= 0
+ assert self.startpipew == -1
+ assert self.errpiper == -1
os.close(self.masterfd)
status = os.waitpid(self.pid, 0)[1]
if status & 0xff == 0: # not killed by a signal
@@ -779,6 +805,7 @@ def main():
with process as masterfd:
with Terminal(acsc, options.columns, reverse=options.reverse) as t:
t.resizepty(masterfd)
+ process.start()
while True:
timeout = 0 if t.refresh_needed() else None
try: