Commit 9fab68ee authored by Julien Muchembled's avatar Julien Muchembled

Fix file descriptor leaks

This should fix strange bugs after running the demo for a long time,
with certificate renewal happening every few minutes.
parent 343e910a
...@@ -350,10 +350,10 @@ def main(): ...@@ -350,10 +350,10 @@ def main():
cache.getDh(dh) cache.getDh(dh)
for iface, (port, proto) in server_tunnels.iteritems(): for iface, (port, proto) in server_tunnels.iteritems():
r, x = socket.socketpair(socket.AF_UNIX, socket.SOCK_DGRAM) r, x = socket.socketpair(socket.AF_UNIX, socket.SOCK_DGRAM)
utils.setCloexec(r)
cleanup.append(plib.server(iface, config.max_clients, cleanup.append(plib.server(iface, config.max_clients,
dh, x.fileno(), port, proto, cache.encrypt, dh, x.fileno(), port, proto, cache.encrypt,
'--ping-exit', str(timeout), *config.openvpn_args, '--ping-exit', str(timeout), *config.openvpn_args).stop)
preexec_fn=r.close).stop)
R[r] = partial(tunnel_manager.handleServerEvent, r) R[r] = partial(tunnel_manager.handleServerEvent, r)
x.close() x.close()
...@@ -422,13 +422,11 @@ def main(): ...@@ -422,13 +422,11 @@ def main():
if r: if r:
sys.exit(r) sys.exit(r)
exit.acquire() exit.acquire()
# Keep babeld cleanup at the end, so that babeld is stopped first,
# which gives a chance to send wildcard retractions.
for cmd in config.daemon or (): for cmd in config.daemon or ():
cleanup.insert(-1, utils.Popen(cmd, shell=True).stop) cleanup.insert(-1, utils.Popen(cmd, shell=True).stop)
try: cleanup.insert(-1, tunnel_manager.close)
cleanup[-1:-1] = (tunnel_manager.delInterfaces,
tunnel_manager.killAll)
except AttributeError:
pass
if config.console: if config.console:
from re6st.debug import Console from re6st.debug import Console
def console(socket, frame=sys._getframe()): def console(socket, frame=sys._getframe()):
...@@ -467,7 +465,7 @@ def main(): ...@@ -467,7 +465,7 @@ def main():
utils.log_exception() utils.log_exception()
sys.exit(1) sys.exit(1)
try: try:
sys.exitfunc() atexit._run_exitfuncs()
finally: finally:
os.execvp(sys.argv[0], sys.argv) os.execvp(sys.argv[0], sys.argv)
......
...@@ -201,7 +201,8 @@ class Babel(object): ...@@ -201,7 +201,8 @@ class Babel(object):
self.write_buffer = Buffer() self.write_buffer = Buffer()
self.read_buffer = Buffer() self.read_buffer = Buffer()
self.read_buffer.want(header.size) self.read_buffer.want(header.size)
s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) s = socket.socket(socket.AF_UNIX,
socket.SOCK_STREAM | socket.SOCK_CLOEXEC)
def select(*args): def select(*args):
try: try:
s.connect(self.socket_path) s.connect(self.socket_path)
...@@ -214,6 +215,7 @@ class Babel(object): ...@@ -214,6 +215,7 @@ class Babel(object):
self.socket = s self.socket = s
return self.select(*args) return self.select(*args)
self.select = select self.select = select
self.close = s.close
def request_dump(self): def request_dump(self):
if self.select({}, {}, ()): if self.select({}, {}, ()):
......
...@@ -48,9 +48,10 @@ class Console(object): ...@@ -48,9 +48,10 @@ class Console(object):
def __init__(self, path, pdb): def __init__(self, path, pdb):
self.path = path self.path = path
s = socket.socket(socket.AF_UNIX) s = self._sock = socket.socket(socket.AF_UNIX,
socket.SOCK_STREAM | socket.SOCK_CLOEXEC)
try: try:
self.close() self._removeSocket()
except OSError, e: except OSError, e:
if e.errno != errno.ENOENT: if e.errno != errno.ENOENT:
raise raise
...@@ -65,5 +66,9 @@ class Console(object): ...@@ -65,5 +66,9 @@ class Console(object):
self.select = select self.select = select
def close(self): def close(self):
self._removeSocket()
self._sock.close()
def _removeSocket(self):
if stat.S_ISSOCK(os.lstat(self.path).st_mode): if stat.S_ISSOCK(os.lstat(self.path).st_mode):
os.remove(self.path) os.remove(self.path)
...@@ -243,7 +243,8 @@ class BaseTunnelManager(object): ...@@ -243,7 +243,8 @@ class BaseTunnelManager(object):
for family, address in address_dict.iteritems() for family, address in address_dict.iteritems()
if address) if address)
self.sock = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) self.sock = socket.socket(socket.AF_INET6,
socket.SOCK_DGRAM | socket.SOCK_CLOEXEC)
# See also http://stackoverflow.com/questions/597225/ # See also http://stackoverflow.com/questions/597225/
# about binding and anycast. # about binding and anycast.
self.sock.bind(('::', PORT)) self.sock.bind(('::', PORT))
...@@ -259,6 +260,10 @@ class BaseTunnelManager(object): ...@@ -259,6 +260,10 @@ class BaseTunnelManager(object):
# TunnelManager when we don't need to check it anymore. # TunnelManager when we don't need to check it anymore.
self._next_refresh = time.time() self._next_refresh = time.time()
def close(self):
self.sock.close()
self.ctl.close()
def select(self, r, w, t): def select(self, r, w, t):
r[self.sock] = self.handlePeerEvent r[self.sock] = self.handlePeerEvent
t += self._timeouts t += self._timeouts
...@@ -665,6 +670,7 @@ class TunnelManager(BaseTunnelManager): ...@@ -665,6 +670,7 @@ class TunnelManager(BaseTunnelManager):
self.timeout = timeout self.timeout = timeout
self._read_sock, self.write_sock = socket.socketpair( self._read_sock, self.write_sock = socket.socketpair(
socket.AF_UNIX, socket.SOCK_DGRAM) socket.AF_UNIX, socket.SOCK_DGRAM)
utils.setCloexec(self._read_sock)
self._disconnected = 0 self._disconnected = 0
self._distant_peers = [] self._distant_peers = []
self._iface_to_prefix = {} self._iface_to_prefix = {}
...@@ -683,6 +689,13 @@ class TunnelManager(BaseTunnelManager): ...@@ -683,6 +689,13 @@ class TunnelManager(BaseTunnelManager):
for i in xrange(1, self._client_count + 1)) for i in xrange(1, self._client_count + 1))
self._free_iface_list = [] self._free_iface_list = []
def close(self):
self.killAll()
self.delInterfaces()
self._read_sock.close()
self.write_sock.close()
super(TunnelManager, self).close()
@property @property
def encrypt(self): def encrypt(self):
return self.cache.encrypt return self.cache.encrypt
......
import argparse, errno, hashlib, logging, os, select as _select import argparse, errno, fcntl, hashlib, logging, os, select as _select
import shlex, signal, socket, sqlite3, struct, subprocess import shlex, signal, socket, sqlite3, struct, subprocess
import sys, textwrap, threading, time, traceback import sys, textwrap, threading, time, traceback
# PY3: It will be even better to use Popen(pass_fds=...),
# and then socket.SOCK_CLOEXEC will be useless.
# (We already follow the good practice that consists in not
# relying on the GC for the closing of file descriptors.)
socket.SOCK_CLOEXEC = 0x80000
HMAC_LEN = len(hashlib.sha1('').digest()) HMAC_LEN = len(hashlib.sha1('').digest())
class ReexecException(Exception): class ReexecException(Exception):
...@@ -179,6 +185,10 @@ class Popen(subprocess.Popen): ...@@ -179,6 +185,10 @@ class Popen(subprocess.Popen):
return r return r
def setCloexec(fd):
flags = fcntl.fcntl(fd, fcntl.F_GETFD)
fcntl.fcntl(fd, fcntl.F_SETFD, flags | fcntl.FD_CLOEXEC)
def select(R, W, T): def select(R, W, T):
try: try:
r, w, _ = _select.select(R, W, (), r, w, _ = _select.select(R, W, (),
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment