]> git.proxmox.com Git - ovs.git/blob - python/ovs/socket_util.py
Remove dependency on python3-six
[ovs.git] / python / ovs / socket_util.py
1 # Copyright (c) 2010, 2012, 2014, 2015 Nicira, Inc.
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at:
6 #
7 # http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14
15 import errno
16 import os
17 import os.path
18 import random
19 import socket
20 import sys
21
22 import ovs.fatal_signal
23 import ovs.poller
24 import ovs.vlog
25
26 if sys.platform == 'win32':
27 import ovs.winutils as winutils
28 import win32file
29
30 vlog = ovs.vlog.Vlog("socket_util")
31
32
33 def make_short_name(long_name):
34 if long_name is None:
35 return None
36 long_name = os.path.abspath(long_name)
37 long_dirname = os.path.dirname(long_name)
38 tmpdir = os.getenv('TMPDIR', '/tmp')
39 for x in range(0, 1000):
40 link_name = \
41 '%s/ovs-un-py-%d-%d' % (tmpdir, random.randint(0, 10000), x)
42 try:
43 os.symlink(long_dirname, link_name)
44 ovs.fatal_signal.add_file_to_unlink(link_name)
45 return os.path.join(link_name, os.path.basename(long_name))
46 except OSError as e:
47 if e.errno != errno.EEXIST:
48 break
49 raise Exception("Failed to create temporary symlink")
50
51
52 def free_short_name(short_name):
53 if short_name is None:
54 return
55 link_name = os.path.dirname(short_name)
56 ovs.fatal_signal.unlink_file_now(link_name)
57
58
59 def make_unix_socket(style, nonblock, bind_path, connect_path, short=False):
60 """Creates a Unix domain socket in the given 'style' (either
61 socket.SOCK_DGRAM or socket.SOCK_STREAM) that is bound to 'bind_path' (if
62 'bind_path' is not None) and connected to 'connect_path' (if 'connect_path'
63 is not None). If 'nonblock' is true, the socket is made non-blocking.
64
65 Returns (error, socket): on success 'error' is 0 and 'socket' is a new
66 socket object, on failure 'error' is a positive errno value and 'socket' is
67 None."""
68
69 try:
70 sock = socket.socket(socket.AF_UNIX, style)
71 except socket.error as e:
72 return get_exception_errno(e), None
73
74 try:
75 if nonblock:
76 set_nonblocking(sock)
77 if bind_path is not None:
78 # Delete bind_path but ignore ENOENT.
79 try:
80 os.unlink(bind_path)
81 except OSError as e:
82 if e.errno != errno.ENOENT:
83 return e.errno, None
84
85 ovs.fatal_signal.add_file_to_unlink(bind_path)
86 sock.bind(bind_path)
87
88 try:
89 os.fchmod(sock.fileno(), 0o700)
90 except OSError:
91 pass
92 if connect_path is not None:
93 try:
94 sock.connect(connect_path)
95 except socket.error as e:
96 if get_exception_errno(e) != errno.EINPROGRESS:
97 raise
98 return 0, sock
99 except socket.error as e:
100 sock.close()
101 if (bind_path is not None and
102 os.path.exists(bind_path)):
103 ovs.fatal_signal.unlink_file_now(bind_path)
104 eno = ovs.socket_util.get_exception_errno(e)
105 if (eno == "AF_UNIX path too long" and
106 os.uname()[0] == "Linux"):
107 short_connect_path = None
108 short_bind_path = None
109 connect_dirfd = None
110 bind_dirfd = None
111 # Try workaround using /proc/self/fd
112 if connect_path is not None:
113 dirname = os.path.dirname(connect_path)
114 basename = os.path.basename(connect_path)
115 try:
116 connect_dirfd = os.open(dirname,
117 os.O_DIRECTORY | os.O_RDONLY)
118 except OSError as err:
119 return get_exception_errno(err), None
120 short_connect_path = "/proc/self/fd/%d/%s" % (connect_dirfd,
121 basename)
122
123 if bind_path is not None:
124 dirname = os.path.dirname(bind_path)
125 basename = os.path.basename(bind_path)
126 try:
127 bind_dirfd = os.open(dirname, os.O_DIRECTORY | os.O_RDONLY)
128 except OSError as err:
129 return get_exception_errno(err), None
130 short_bind_path = "/proc/self/fd/%d/%s" % (bind_dirfd,
131 basename)
132
133 try:
134 return make_unix_socket(style, nonblock, short_bind_path,
135 short_connect_path)
136 finally:
137 if connect_dirfd is not None:
138 os.close(connect_dirfd)
139 if bind_dirfd is not None:
140 os.close(bind_dirfd)
141 elif (eno == "AF_UNIX path too long"):
142 if short:
143 return get_exception_errno(e), None
144 short_bind_path = None
145 try:
146 short_bind_path = make_short_name(bind_path)
147 short_connect_path = make_short_name(connect_path)
148 except:
149 free_short_name(short_bind_path)
150 return errno.ENAMETOOLONG, None
151 try:
152 return make_unix_socket(style, nonblock, short_bind_path,
153 short_connect_path, short=True)
154 finally:
155 free_short_name(short_bind_path)
156 free_short_name(short_connect_path)
157 else:
158 return get_exception_errno(e), None
159
160
161 def check_connection_completion(sock):
162 if sys.platform == "win32":
163 p = ovs.poller.SelectPoll()
164 event = winutils.get_new_event(None, False, True, None)
165 # Receive notification of readiness for writing, of completed
166 # connection or multipoint join operation, and of socket closure.
167 win32file.WSAEventSelect(sock, event,
168 win32file.FD_WRITE |
169 win32file.FD_CONNECT |
170 win32file.FD_CLOSE)
171 p.register(event, ovs.poller.POLLOUT)
172 else:
173 p = ovs.poller.get_system_poll()
174 p.register(sock, ovs.poller.POLLOUT)
175 pfds = p.poll(0)
176 if len(pfds) == 1:
177 revents = pfds[0][1]
178 if revents & ovs.poller.POLLERR or revents & ovs.poller.POLLHUP:
179 try:
180 # The following should raise an exception.
181 sock.send("\0".encode(), socket.MSG_DONTWAIT)
182
183 # (Here's where we end up if it didn't.)
184 # XXX rate-limit
185 vlog.err("poll return POLLERR but send succeeded")
186 return errno.EPROTO
187 except socket.error as e:
188 return get_exception_errno(e)
189 else:
190 return 0
191 else:
192 return errno.EAGAIN
193
194
195 def is_valid_ipv4_address(address):
196 try:
197 socket.inet_pton(socket.AF_INET, address)
198 except AttributeError:
199 try:
200 socket.inet_aton(address)
201 except socket.error:
202 return False
203 except socket.error:
204 return False
205
206 return True
207
208
209 def inet_parse_active(target, default_port):
210 address = target.split(":")
211 if len(address) >= 2:
212 host_name = ":".join(address[0:-1]).lstrip('[').rstrip(']')
213 port = int(address[-1])
214 else:
215 if default_port:
216 port = default_port
217 else:
218 raise ValueError("%s: port number must be specified" % target)
219 host_name = address[0]
220 if not host_name:
221 raise ValueError("%s: bad peer name format" % target)
222 return (host_name, port)
223
224
225 def inet_open_active(style, target, default_port, dscp):
226 address = inet_parse_active(target, default_port)
227 try:
228 is_addr_inet = is_valid_ipv4_address(address[0])
229 if is_addr_inet:
230 sock = socket.socket(socket.AF_INET, style, 0)
231 family = socket.AF_INET
232 else:
233 sock = socket.socket(socket.AF_INET6, style, 0)
234 family = socket.AF_INET6
235 except socket.error as e:
236 return get_exception_errno(e), None
237
238 try:
239 set_nonblocking(sock)
240 set_dscp(sock, family, dscp)
241 try:
242 sock.connect(address)
243 except socket.error as e:
244 error = get_exception_errno(e)
245 if sys.platform == 'win32' and error == errno.WSAEWOULDBLOCK:
246 # WSAEWOULDBLOCK would be the equivalent on Windows
247 # for EINPROGRESS on Unix.
248 error = errno.EINPROGRESS
249 if error != errno.EINPROGRESS:
250 raise
251 return 0, sock
252 except socket.error as e:
253 sock.close()
254 return get_exception_errno(e), None
255
256
257 def get_exception_errno(e):
258 """A lot of methods on Python socket objects raise socket.error, but that
259 exception is documented as having two completely different forms of
260 arguments: either a string or a (errno, string) tuple. We only want the
261 errno."""
262 if isinstance(e.args, tuple):
263 return e.args[0]
264 else:
265 return errno.EPROTO
266
267
268 null_fd = -1
269
270
271 def get_null_fd():
272 """Returns a readable and writable fd for /dev/null, if successful,
273 otherwise a negative errno value. The caller must not close the returned
274 fd (because the same fd will be handed out to subsequent callers)."""
275 global null_fd
276 if null_fd < 0:
277 try:
278 # os.devnull ensures compatibility with Windows, returns
279 # '/dev/null' for Unix and 'nul' for Windows
280 null_fd = os.open(os.devnull, os.O_RDWR)
281 except OSError as e:
282 vlog.err("could not open %s: %s" % (os.devnull,
283 os.strerror(e.errno)))
284 return -e.errno
285 return null_fd
286
287
288 def write_fully(fd, buf):
289 """Returns an (error, bytes_written) tuple where 'error' is 0 on success,
290 otherwise a positive errno value, and 'bytes_written' is the number of
291 bytes that were written before the error occurred. 'error' is 0 if and
292 only if 'bytes_written' is len(buf)."""
293 bytes_written = 0
294 if len(buf) == 0:
295 return 0, 0
296 if not isinstance(buf, bytes):
297 buf = bytes(buf, 'utf-8')
298 while True:
299 try:
300 retval = os.write(fd, buf)
301 assert retval >= 0
302 if retval == len(buf):
303 return 0, bytes_written + len(buf)
304 elif retval == 0:
305 vlog.warn("write returned 0")
306 return errno.EPROTO, bytes_written
307 else:
308 bytes_written += retval
309 buf = buf[:retval]
310 except OSError as e:
311 return e.errno, bytes_written
312
313
314 def set_nonblocking(sock):
315 try:
316 sock.setblocking(0)
317 except socket.error as e:
318 vlog.err("could not set nonblocking mode on socket: %s"
319 % os.strerror(get_exception_errno(e)))
320
321
322 def set_dscp(sock, family, dscp):
323 if dscp > 63:
324 raise ValueError("Invalid dscp %d" % dscp)
325
326 val = dscp << 2
327 if family == socket.AF_INET:
328 sock.setsockopt(socket.IPPROTO_IP, socket.IP_TOS, val)
329 elif family == socket.AF_INET6:
330 sock.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_TCLASS, val)
331 else:
332 raise ValueError('Invalid family %d' % family)