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.
--- 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:
--- 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):
--- 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))