# HG changeset patch # User Bryan O'Sullivan # Date 1355879713 28800 # Node ID ae54cff742e288220dbf9674a604664569415a24 # Parent cd53e40ab0e2ab9eff4f67508bc47b2a09d5c311 posix: move server side of unix domain sockets out of inotify We also turn the unix domain socket into a class, so that we have a sensible place to hang its logically related attributes and behaviour. We'll shortly want to reuse this in other code. diff -r cd53e40ab0e2 -r ae54cff742e2 hgext/inotify/linuxserver.py --- a/hgext/inotify/linuxserver.py Tue Dec 18 17:33:32 2012 -0800 +++ b/hgext/inotify/linuxserver.py Tue Dec 18 17:15:13 2012 -0800 @@ -405,14 +405,7 @@ def shutdown(self): self.sock.close() - try: - os.unlink(self.sockpath) - if self.realsockpath: - os.unlink(self.realsockpath) - os.rmdir(os.path.dirname(self.realsockpath)) - except OSError, err: - if err.errno != errno.ENOENT: - raise + self.sock.cleanup() def answer_stat_query(self, cs): if self.repowatcher.timeout: diff -r cd53e40ab0e2 -r ae54cff742e2 hgext/inotify/server.py --- a/hgext/inotify/server.py Tue Dec 18 17:33:32 2012 -0800 +++ b/hgext/inotify/server.py Tue Dec 18 17:15:13 2012 -0800 @@ -6,7 +6,7 @@ # GNU General Public License version 2 or any later version. from mercurial.i18n import _ -from mercurial import cmdutil, osutil, util +from mercurial import cmdutil, posix, osutil, util import common import errno @@ -330,41 +330,15 @@ def __init__(self, ui, root, repowatcher, timeout): self.ui = ui self.repowatcher = repowatcher - self.sock = socket.socket(socket.AF_UNIX) - self.sockpath = join(root, '.hg/inotify.sock') - - self.realsockpath = self.sockpath - if os.path.islink(self.sockpath): - if os.path.exists(self.sockpath): - self.realsockpath = os.readlink(self.sockpath) - else: - os.unlink(self.sockpath) try: - self.sock.bind(self.realsockpath) - except socket.error, err: - if err.args[0] == errno.EADDRINUSE: - raise AlreadyStartedException(_('cannot start: socket is ' - 'already bound')) - if err.args[0] == "AF_UNIX path too long": - tempdir = tempfile.mkdtemp(prefix="hg-inotify-") - self.realsockpath = os.path.join(tempdir, "inotify.sock") - try: - self.sock.bind(self.realsockpath) - os.symlink(self.realsockpath, self.sockpath) - except (OSError, socket.error), inst: - try: - os.unlink(self.realsockpath) - except OSError: - pass - os.rmdir(tempdir) - if inst.errno == errno.EEXIST: - raise AlreadyStartedException(_('cannot start: tried ' - 'linking .hg/inotify.sock to a temporary socket but' - ' .hg/inotify.sock already exists')) - raise - else: - raise - self.sock.listen(5) + self.sock = posix.unixdomainserver( + lambda p: os.path.join(root, '.hg', p), + 'inotify') + except (OSError, socket.error), err: + if err.errno == errno.EADDRINUSE: + raise AlreadyStartedException(_('cannot start: ' + 'socket is already bound')) + raise self.fileno = self.sock.fileno def answer_stat_query(self, cs): diff -r cd53e40ab0e2 -r ae54cff742e2 mercurial/posix.py --- a/mercurial/posix.py Tue Dec 18 17:33:32 2012 -0800 +++ b/mercurial/posix.py Tue Dec 18 17:15:13 2012 -0800 @@ -7,7 +7,7 @@ from i18n import _ import encoding -import os, sys, errno, stat, getpass, pwd, grp, tempfile, unicodedata +import os, sys, errno, stat, getpass, pwd, grp, socket, tempfile, unicodedata posixfile = open normpath = os.path.normpath @@ -483,3 +483,43 @@ def executablepath(): return None # available on Windows only + +class unixdomainserver(socket.socket): + def __init__(self, join, subsystem): + '''Create a unix domain socket with the given prefix.''' + super(unixdomainserver, self).__init__(socket.AF_UNIX) + sockname = subsystem + '.sock' + self.realpath = self.path = join(sockname) + if os.path.islink(self.path): + if os.path.exists(self.path): + self.realpath = os.readlink(self.path) + else: + os.unlink(self.path) + try: + self.bind(self.realpath) + except socket.error, err: + if err.args[0] == 'AF_UNIX path too long': + tmpdir = tempfile.mkdtemp(prefix='hg-%s-' % subsystem) + self.realpath = os.path.join(tmpdir, sockname) + try: + self.bind(self.realpath) + os.symlink(self.realpath, self.path) + except (OSError, socket.error): + self.cleanup() + raise + else: + raise + self.listen(5) + + def cleanup(self): + def okayifmissing(f, path): + try: + f(path) + except OSError, err: + if err.errno != errno.ENOENT: + raise + + okayifmissing(os.unlink, self.path) + if self.realpath != self.path: + okayifmissing(os.unlink, self.realpath) + okayifmissing(os.rmdir, os.path.dirname(self.realpath))