]>
git.proxmox.com Git - ovs.git/blob - python/ovs/socket_util.py
1 # Copyright (c) 2010, 2012, 2014, 2015 Nicira, Inc.
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:
7 # http://www.apache.org/licenses/LICENSE-2.0
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.
22 import ovs
.fatal_signal
26 if sys
.platform
== 'win32':
27 import ovs
.winutils
as winutils
30 vlog
= ovs
.vlog
.Vlog("socket_util")
33 def make_short_name(long_name
):
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):
41 '%s/ovs-un-py-%d-%d' % (tmpdir
, random
.randint(0, 10000), x
)
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
))
47 if e
.errno
!= errno
.EEXIST
:
49 raise Exception("Failed to create temporary symlink")
52 def free_short_name(short_name
):
53 if short_name
is None:
55 link_name
= os
.path
.dirname(short_name
)
56 ovs
.fatal_signal
.unlink_file_now(link_name
)
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.
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
70 sock
= socket
.socket(socket
.AF_UNIX
, style
)
71 except socket
.error
as e
:
72 return get_exception_errno(e
), None
77 if bind_path
is not None:
78 # Delete bind_path but ignore ENOENT.
82 if e
.errno
!= errno
.ENOENT
:
85 ovs
.fatal_signal
.add_file_to_unlink(bind_path
)
89 os
.fchmod(sock
.fileno(), 0o700)
92 if connect_path
is not None:
94 sock
.connect(connect_path
)
95 except socket
.error
as e
:
96 if get_exception_errno(e
) != errno
.EINPROGRESS
:
99 except socket
.error
as e
:
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
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
)
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
,
123 if bind_path
is not None:
124 dirname
= os
.path
.dirname(bind_path
)
125 basename
= os
.path
.basename(bind_path
)
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
,
134 return make_unix_socket(style
, nonblock
, short_bind_path
,
137 if connect_dirfd
is not None:
138 os
.close(connect_dirfd
)
139 if bind_dirfd
is not None:
141 elif (eno
== "AF_UNIX path too long"):
143 return get_exception_errno(e
), None
144 short_bind_path
= None
146 short_bind_path
= make_short_name(bind_path
)
147 short_connect_path
= make_short_name(connect_path
)
149 free_short_name(short_bind_path
)
150 return errno
.ENAMETOOLONG
, None
152 return make_unix_socket(style
, nonblock
, short_bind_path
,
153 short_connect_path
, short
=True)
155 free_short_name(short_bind_path
)
156 free_short_name(short_connect_path
)
158 return get_exception_errno(e
), None
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
,
169 win32file
.FD_CONNECT |
171 p
.register(event
, ovs
.poller
.POLLOUT
)
173 p
= ovs
.poller
.get_system_poll()
174 p
.register(sock
, ovs
.poller
.POLLOUT
)
178 if revents
& ovs
.poller
.POLLERR
or revents
& ovs
.poller
.POLLHUP
:
180 # The following should raise an exception.
181 sock
.send("\0".encode(), socket
.MSG_DONTWAIT
)
183 # (Here's where we end up if it didn't.)
185 vlog
.err("poll return POLLERR but send succeeded")
187 except socket
.error
as e
:
188 return get_exception_errno(e
)
195 def is_valid_ipv4_address(address
):
197 socket
.inet_pton(socket
.AF_INET
, address
)
198 except AttributeError:
200 socket
.inet_aton(address
)
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])
218 raise ValueError("%s: port number must be specified" % target
)
219 host_name
= address
[0]
221 raise ValueError("%s: bad peer name format" % target
)
222 return (host_name
, port
)
225 def inet_open_active(style
, target
, default_port
, dscp
):
226 address
= inet_parse_active(target
, default_port
)
228 is_addr_inet
= is_valid_ipv4_address(address
[0])
230 sock
= socket
.socket(socket
.AF_INET
, style
, 0)
231 family
= socket
.AF_INET
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
239 set_nonblocking(sock
)
240 set_dscp(sock
, family
, dscp
)
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
:
252 except socket
.error
as e
:
254 return get_exception_errno(e
), None
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
262 if isinstance(e
.args
, tuple):
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)."""
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
)
282 vlog
.err("could not open %s: %s" % (os
.devnull
,
283 os
.strerror(e
.errno
)))
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)."""
296 if not isinstance(buf
, bytes
):
297 buf
= bytes(buf
, 'utf-8')
300 retval
= os
.write(fd
, buf
)
302 if retval
== len(buf
):
303 return 0, bytes_written
+ len(buf
)
305 vlog
.warn("write returned 0")
306 return errno
.EPROTO
, bytes_written
308 bytes_written
+= retval
311 return e
.errno
, bytes_written
314 def set_nonblocking(sock
):
317 except socket
.error
as e
:
318 vlog
.err("could not set nonblocking mode on socket: %s"
319 % os
.strerror(get_exception_errno(e
)))
322 def set_dscp(sock
, family
, dscp
):
324 raise ValueError("Invalid dscp %d" % dscp
)
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
)
332 raise ValueError('Invalid family %d' % family
)