1fc80fd3a1add02446f26e3d5153e3b45fd855e4
[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, ovs.poller.POLLOUT)
81     if len(p.poll(0)) == 1:
82         return get_socket_error(sock)
83     else:
84         return errno.EAGAIN
85
86
87 def inet_parse_active(target, default_port):
88     address = target.split(":")
89     host_name = address[0]
90     if not host_name:
91         raise ValueError("%s: bad peer name format" % target)
92     if len(address) >= 2:
93         port = int(address[1])
94     elif default_port:
95         port = default_port
96     else:
97         raise ValueError("%s: port number must be specified" % target)
98     return (host_name, port)
99
100
101 def inet_open_active(style, target, default_port, dscp):
102     address = inet_parse_active(target, default_port)
103     try:
104         sock = socket.socket(socket.AF_INET, style, 0)
105     except socket.error, e:
106         return get_exception_errno(e), None
107
108     try:
109         set_nonblocking(sock)
110         set_dscp(sock, dscp)
111         try:
112             sock.connect(address)
113         except socket.error, e:
114             if get_exception_errno(e) != errno.EINPROGRESS:
115                 raise
116         return 0, sock
117     except socket.error, e:
118         sock.close()
119         return get_exception_errno(e), None
120
121
122 def get_socket_error(sock):
123     """Returns the errno value associated with 'socket' (0 if no error) and
124     resets the socket's error status."""
125     return sock.getsockopt(socket.SOL_SOCKET, socket.SO_ERROR)
126
127
128 def get_exception_errno(e):
129     """A lot of methods on Python socket objects raise socket.error, but that
130     exception is documented as having two completely different forms of
131     arguments: either a string or a (errno, string) tuple.  We only want the
132     errno."""
133     if type(e.args) == tuple:
134         return e.args[0]
135     else:
136         return errno.EPROTO
137
138
139 null_fd = -1
140
141
142 def get_null_fd():
143     """Returns a readable and writable fd for /dev/null, if successful,
144     otherwise a negative errno value.  The caller must not close the returned
145     fd (because the same fd will be handed out to subsequent callers)."""
146     global null_fd
147     if null_fd < 0:
148         try:
149             null_fd = os.open("/dev/null", os.O_RDWR)
150         except OSError, e:
151             vlog.err("could not open /dev/null: %s" % os.strerror(e.errno))
152             return -e.errno
153     return null_fd
154
155
156 def write_fully(fd, buf):
157     """Returns an (error, bytes_written) tuple where 'error' is 0 on success,
158     otherwise a positive errno value, and 'bytes_written' is the number of
159     bytes that were written before the error occurred.  'error' is 0 if and
160     only if 'bytes_written' is len(buf)."""
161     bytes_written = 0
162     if len(buf) == 0:
163         return 0, 0
164     while True:
165         try:
166             retval = os.write(fd, buf)
167             assert retval >= 0
168             if retval == len(buf):
169                 return 0, bytes_written + len(buf)
170             elif retval == 0:
171                 vlog.warn("write returned 0")
172                 return errno.EPROTO, bytes_written
173             else:
174                 bytes_written += retval
175                 buf = buf[:retval]
176         except OSError, e:
177             return e.errno, bytes_written
178
179
180 def set_nonblocking(sock):
181     try:
182         sock.setblocking(0)
183     except socket.error, e:
184         vlog.err("could not set nonblocking mode on socket: %s"
185                  % os.strerror(get_socket_error(e)))
186
187
188 def set_dscp(sock, dscp):
189     if dscp > 63:
190         raise ValueError("Invalid dscp %d" % dscp)
191     val = dscp << 2
192     sock.setsockopt(socket.IPPROTO_IP, socket.IP_TOS, val)