I have a program that should be run from sshd. It should print few lines to stdout and then fork and exit. The forked program should do its stuff detached from sshd without blocking the program. The thing is that the program below runs correctly when executed from a terminal shell or via ssh with option -t. It blocks ssh connection when the terminal is not assigned.
#! /usr/bin/env python3
import os, time, sys
print("Hello world", flush=True)
if os.fork()==0:
# child
sys.stdin.close()
sys.stdout.close()
sys.stderr.close()
os.setsid()
# just in case make a second fork
if os.fork()>0:
os.exit()
os.setsid()
time.sleep(30)
os.exit(0)
os.wait()
print("bye, world")
How can I make the program release ssh connection even if invoked without terminal?
- Direct run (runs correctly, exits immediately leaving forked child):
./test-prog.py
- Via ssh with forced terminal still works correctly:
ssh -t server ./test-prog.py
- Via ssh without forced terminal doesn't work correctly. It prints both lines and then waits 30 seconds to release ssh:
ssh server ./test-prog.py
I would run this program from a daemon connecting via ssh and exit leaving forked child detached from ssh. In real application this program just opens few tcp socket and writes their port number on stdout. The client reads the ports and then connects to it.
I found that in python
sys.stdin.close()doesn't really close fd 0, neither does closingstdoutandstderrclose fd 1 and 2.ssh keeps the connection if:
the pty is open or
all fds 0,1,2 (and perhaps other passed using
-Land-Roptions) are closed if there is no pty allocated, i.e. there is no pty in calling process and no-toption to ssh.So the correct way to close standard streams is to
dup2them to/dev/null, for instance with the following code: