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():
cache.getDh(dh)
for iface, (port, proto) in server_tunnels.iteritems():
r, x = socket.socketpair(socket.AF_UNIX, socket.SOCK_DGRAM)
utils.setCloexec(r)
cleanup.append(plib.server(iface, config.max_clients,
dh, x.fileno(), port, proto, cache.encrypt,
'--ping-exit', str(timeout), *config.openvpn_args,
preexec_fn=r.close).stop)
'--ping-exit', str(timeout), *config.openvpn_args).stop)
R[r] = partial(tunnel_manager.handleServerEvent, r)
x.close()
......@@ -422,13 +422,11 @@ def main():
if r:
sys.exit(r)
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 ():
cleanup.insert(-1, utils.Popen(cmd, shell=True).stop)
try:
cleanup[-1:-1] = (tunnel_manager.delInterfaces,
tunnel_manager.killAll)
except AttributeError:
pass
cleanup.insert(-1, tunnel_manager.close)
if config.console:
from re6st.debug import Console
def console(socket, frame=sys._getframe()):
......@@ -467,7 +465,7 @@ def main():
utils.log_exception()
sys.exit(1)
try:
sys.exitfunc()
atexit._run_exitfuncs()
finally:
os.execvp(sys.argv[0], sys.argv)
......
......@@ -201,7 +201,8 @@ class Babel(object):
self.write_buffer = Buffer()
self.read_buffer = Buffer()
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):
try:
s.connect(self.socket_path)
......@@ -214,6 +215,7 @@ class Babel(object):
self.socket = s
return self.select(*args)
self.select = select
self.close = s.close
def request_dump(self):
if self.select({}, {}, ()):
......
......@@ -48,9 +48,10 @@ class Console(object):
def __init__(self, path, pdb):
self.path = path
s = socket.socket(socket.AF_UNIX)
s = self._sock = socket.socket(socket.AF_UNIX,
socket.SOCK_STREAM | socket.SOCK_CLOEXEC)
try:
self.close()
self._removeSocket()
except OSError, e:
if e.errno != errno.ENOENT:
raise
......@@ -65,5 +66,9 @@ class Console(object):
self.select = select
def close(self):
self._removeSocket()
self._sock.close()
def _removeSocket(self):
if stat.S_ISSOCK(os.lstat(self.path).st_mode):
os.remove(self.path)
......@@ -243,7 +243,8 @@ class BaseTunnelManager(object):
for family, address in address_dict.iteritems()
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/
# about binding and anycast.
self.sock.bind(('::', PORT))
......@@ -259,6 +260,10 @@ class BaseTunnelManager(object):
# TunnelManager when we don't need to check it anymore.
self._next_refresh = time.time()
def close(self):
self.sock.close()
self.ctl.close()
def select(self, r, w, t):
r[self.sock] = self.handlePeerEvent
t += self._timeouts
......@@ -665,6 +670,7 @@ class TunnelManager(BaseTunnelManager):
self.timeout = timeout
self._read_sock, self.write_sock = socket.socketpair(
socket.AF_UNIX, socket.SOCK_DGRAM)
utils.setCloexec(self._read_sock)
self._disconnected = 0
self._distant_peers = []
self._iface_to_prefix = {}
......@@ -683,6 +689,13 @@ class TunnelManager(BaseTunnelManager):
for i in xrange(1, self._client_count + 1))
self._free_iface_list = []
def close(self):
self.killAll()
self.delInterfaces()
self._read_sock.close()
self.write_sock.close()
super(TunnelManager, self).close()
@property
def encrypt(self):
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 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())
class ReexecException(Exception):
......@@ -179,6 +185,10 @@ class Popen(subprocess.Popen):
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):
try:
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