Patchwork [3,of,4] posix: move server side of unix domain sockets out of inotify

login
register
mail settings
Submitter Bryan O'Sullivan
Date Dec. 19, 2012, 6:47 p.m.
Message ID <547172cf2a975f7806f0.1355942842@fedora>
Download mbox | patch
Permalink /patch/201/
State Accepted
Commit ae54cff742e288220dbf9674a604664569415a24
Headers show

Comments

Bryan O'Sullivan - Dec. 19, 2012, 6:47 p.m.
# HG changeset patch
# User Bryan O'Sullivan <bryano at fb.com>
# Date 1355879713 28800
# Node ID 547172cf2a975f7806f078285b3712f38ecb53f5
# Parent  d1828f471b089e81496fd56beba7d040d307fb2c
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.

Patch

diff --git a/hgext/inotify/linuxserver.py b/hgext/inotify/linuxserver.py
--- a/hgext/inotify/linuxserver.py
+++ b/hgext/inotify/linuxserver.py
@@ -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 --git a/hgext/inotify/server.py b/hgext/inotify/server.py
--- a/hgext/inotify/server.py
+++ b/hgext/inotify/server.py
@@ -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 --git a/mercurial/posix.py b/mercurial/posix.py
--- a/mercurial/posix.py
+++ b/mercurial/posix.py
@@ -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))