comparison mercurial/pure/osutil.py @ 27474:e517a89c24e1

osutil: implement pure version of recvfds() for PyPy This is less portable than the C version, but PyPy can't load CPython extensions. So for now, this will be used on PyPy. I've tested it on Linux amd64 and Mac OS X.
author Yuya Nishihara <yuya@tcha.org>
date Thu, 17 Dec 2015 23:53:09 +0900
parents 810337ae1b76
children e2aa9c4030c4
comparison
equal deleted inserted replaced
27473:34a37a2e03e6 27474:e517a89c24e1
5 # This software may be used and distributed according to the terms of the 5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version. 6 # GNU General Public License version 2 or any later version.
7 7
8 from __future__ import absolute_import 8 from __future__ import absolute_import
9 9
10 import ctypes
11 import ctypes.util
10 import os 12 import os
13 import socket
11 import stat as statmod 14 import stat as statmod
15 import sys
12 16
13 def _mode_to_kind(mode): 17 def _mode_to_kind(mode):
14 if statmod.S_ISREG(mode): 18 if statmod.S_ISREG(mode):
15 return statmod.S_IFREG 19 return statmod.S_IFREG
16 if statmod.S_ISDIR(mode): 20 if statmod.S_ISDIR(mode):
57 result.append((fn, _mode_to_kind(st.st_mode))) 61 result.append((fn, _mode_to_kind(st.st_mode)))
58 return result 62 return result
59 63
60 if os.name != 'nt': 64 if os.name != 'nt':
61 posixfile = open 65 posixfile = open
66
67 _SCM_RIGHTS = 0x01
68 _socklen_t = ctypes.c_uint
69
70 if sys.platform == 'linux2':
71 # socket.h says "the type should be socklen_t but the definition of
72 # the kernel is incompatible with this."
73 _cmsg_len_t = ctypes.c_size_t
74 _msg_controllen_t = ctypes.c_size_t
75 _msg_iovlen_t = ctypes.c_size_t
76 else:
77 _cmsg_len_t = _socklen_t
78 _msg_controllen_t = _socklen_t
79 _msg_iovlen_t = ctypes.c_int
80
81 class _iovec(ctypes.Structure):
82 _fields_ = [
83 ('iov_base', ctypes.c_void_p),
84 ('iov_len', ctypes.c_size_t),
85 ]
86
87 class _msghdr(ctypes.Structure):
88 _fields_ = [
89 ('msg_name', ctypes.c_void_p),
90 ('msg_namelen', _socklen_t),
91 ('msg_iov', ctypes.POINTER(_iovec)),
92 ('msg_iovlen', _msg_iovlen_t),
93 ('msg_control', ctypes.c_void_p),
94 ('msg_controllen', _msg_controllen_t),
95 ('msg_flags', ctypes.c_int),
96 ]
97
98 class _cmsghdr(ctypes.Structure):
99 _fields_ = [
100 ('cmsg_len', _cmsg_len_t),
101 ('cmsg_level', ctypes.c_int),
102 ('cmsg_type', ctypes.c_int),
103 ('cmsg_data', ctypes.c_ubyte * 0),
104 ]
105
106 _libc = ctypes.CDLL(ctypes.util.find_library('c'), use_errno=True)
107 _recvmsg = _libc.recvmsg
108 _recvmsg.restype = ctypes.c_ssize_t
109 _recvmsg.argtypes = (ctypes.c_int, ctypes.POINTER(_msghdr), ctypes.c_int)
110
111 def _CMSG_FIRSTHDR(msgh):
112 if msgh.msg_controllen < ctypes.sizeof(_cmsghdr):
113 return
114 cmsgptr = ctypes.cast(msgh.msg_control, ctypes.POINTER(_cmsghdr))
115 return cmsgptr.contents
116
117 # The pure version is less portable than the native version because the
118 # handling of socket ancillary data heavily depends on C preprocessor.
119 # Also, some length fields are wrongly typed in Linux kernel.
120 def recvfds(sockfd):
121 """receive list of file descriptors via socket"""
122 dummy = (ctypes.c_ubyte * 1)()
123 iov = _iovec(ctypes.cast(dummy, ctypes.c_void_p), ctypes.sizeof(dummy))
124 cbuf = ctypes.create_string_buffer(256)
125 msgh = _msghdr(None, 0,
126 ctypes.pointer(iov), 1,
127 ctypes.cast(cbuf, ctypes.c_void_p), ctypes.sizeof(cbuf),
128 0)
129 r = _recvmsg(sockfd, ctypes.byref(msgh), 0)
130 if r < 0:
131 e = ctypes.get_errno()
132 raise OSError(e, os.strerror(e))
133 # assumes that the first cmsg has fds because it isn't easy to write
134 # portable CMSG_NXTHDR() with ctypes.
135 cmsg = _CMSG_FIRSTHDR(msgh)
136 if not cmsg:
137 return []
138 if (cmsg.cmsg_level != socket.SOL_SOCKET or
139 cmsg.cmsg_type != _SCM_RIGHTS):
140 return []
141 rfds = ctypes.cast(cmsg.cmsg_data, ctypes.POINTER(ctypes.c_int))
142 rfdscount = ((cmsg.cmsg_len - _cmsghdr.cmsg_data.offset) /
143 ctypes.sizeof(ctypes.c_int))
144 return [rfds[i] for i in xrange(rfdscount)]
145
62 else: 146 else:
63 import ctypes
64 import msvcrt 147 import msvcrt
65 148
66 _kernel32 = ctypes.windll.kernel32 149 _kernel32 = ctypes.windll.kernel32
67 150
68 _DWORD = ctypes.c_ulong 151 _DWORD = ctypes.c_ulong