python/ovs: socket_util uses select.poll
[sliver-openvswitch.git] / python / ovs / socket_util.py
1 # Copyright (c) 2010, 2012 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 select
18 import socket
19 import sys
20
21 import ovs.fatal_signal
22 import ovs.poller
23 import ovs.vlog
24
25 vlog = ovs.vlog.Vlog("socket_util")
26
27
28 def make_unix_socket(style, nonblock, bind_path, connect_path):
29     """Creates a Unix domain socket in the given 'style' (either
30     socket.SOCK_DGRAM or socket.SOCK_STREAM) that is bound to 'bind_path' (if
31     'bind_path' is not None) and connected to 'connect_path' (if 'connect_path'
32     is not None).  If 'nonblock' is true, the socket is made non-blocking.
33
34     Returns (error, socket): on success 'error' is 0 and 'socket' is a new
35     socket object, on failure 'error' is a positive errno value and 'socket' is
36     None."""
37
38     try:
39         sock = socket.socket(socket.AF_UNIX, style)
40     except socket.error, e:
41         return get_exception_errno(e), None
42
43     try:
44         if nonblock:
45             set_nonblocking(sock)
46         if bind_path is not None:
47             # Delete bind_path but ignore ENOENT.
48             try:
49                 os.unlink(bind_path)
50             except OSError, e:
51                 if e.errno != errno.ENOENT:
52                     return e.errno, None
53
54             ovs.fatal_signal.add_file_to_unlink(bind_path)
55             sock.bind(bind_path)
56
57             try:
58                 if sys.hexversion >= 0x02060000:
59                     os.fchmod(sock.fileno(), 0700)
60                 else:
61                     os.chmod("/dev/fd/%d" % sock.fileno(), 0700)
62             except OSError, e:
63                 pass
64         if connect_path is not None:
65             try:
66                 sock.connect(connect_path)
67             except socket.error, e:
68                 if get_exception_errno(e) != errno.EINPROGRESS:
69                     raise
70         return 0, sock
71     except socket.error, e:
72         sock.close()
73         if bind_path is not None:
74             ovs.fatal_signal.unlink_file_now(bind_path)
75         return get_exception_errno(e), None
76
77
78 def check_connection_completion(sock):
79     p = ovs.poller.SelectPoll()
80     p.register(sock, select.POLLOUT)
81     if len(p.poll(0)) == 1:
82         return get_socket_error(sock)
83     else:
84         return errno.EAGAIN
85
86
87 def get_socket_error(sock):
88     """Returns the errno value associated with 'socket' (0 if no error) and
89     resets the socket's error status."""
90     return sock.getsockopt(socket.SOL_SOCKET, socket.SO_ERROR)
91
92
93 def get_exception_errno(e):
94     """A lot of methods on Python socket objects raise socket.error, but that
95     exception is documented as having two completely different forms of
96     arguments: either a string or a (errno, string) tuple.  We only want the
97     errno."""
98     if type(e.args) == tuple:
99         return e.args[0]
100     else:
101         return errno.EPROTO
102
103
104 null_fd = -1
105
106
107 def get_null_fd():
108     """Returns a readable and writable fd for /dev/null, if successful,
109     otherwise a negative errno value.  The caller must not close the returned
110     fd (because the same fd will be handed out to subsequent callers)."""
111     global null_fd
112     if null_fd < 0:
113         try:
114             null_fd = os.open("/dev/null", os.O_RDWR)
115         except OSError, e:
116             vlog.err("could not open /dev/null: %s" % os.strerror(e.errno))
117             return -e.errno
118     return null_fd
119
120
121 def write_fully(fd, buf):
122     """Returns an (error, bytes_written) tuple where 'error' is 0 on success,
123     otherwise a positive errno value, and 'bytes_written' is the number of
124     bytes that were written before the error occurred.  'error' is 0 if and
125     only if 'bytes_written' is len(buf)."""
126     bytes_written = 0
127     if len(buf) == 0:
128         return 0, 0
129     while True:
130         try:
131             retval = os.write(fd, buf)
132             assert retval >= 0
133             if retval == len(buf):
134                 return 0, bytes_written + len(buf)
135             elif retval == 0:
136                 vlog.warn("write returned 0")
137                 return errno.EPROTO, bytes_written
138             else:
139                 bytes_written += retval
140                 buf = buf[:retval]
141         except OSError, e:
142             return e.errno, bytes_written
143
144
145 def set_nonblocking(sock):
146     try:
147         sock.setblocking(0)
148     except socket.error, e:
149         vlog.err("could not set nonblocking mode on socket: %s"
150                  % os.strerror(get_socket_error(e)))