root / lib / utils / wrapper.py @ 95e7e85e
History | View | Annotate | Download (5.3 kB)
1 |
#
|
---|---|
2 |
#
|
3 |
|
4 |
# Copyright (C) 2006, 2007, 2010, 2011, 2012 Google Inc.
|
5 |
#
|
6 |
# This program is free software; you can redistribute it and/or modify
|
7 |
# it under the terms of the GNU General Public License as published by
|
8 |
# the Free Software Foundation; either version 2 of the License, or
|
9 |
# (at your option) any later version.
|
10 |
#
|
11 |
# This program is distributed in the hope that it will be useful, but
|
12 |
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
13 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
14 |
# General Public License for more details.
|
15 |
#
|
16 |
# You should have received a copy of the GNU General Public License
|
17 |
# along with this program; if not, write to the Free Software
|
18 |
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
19 |
# 02110-1301, USA.
|
20 |
|
21 |
"""Utility functions wrapping other functions.
|
22 |
|
23 |
"""
|
24 |
|
25 |
import sys |
26 |
import time |
27 |
import socket |
28 |
import errno |
29 |
import tempfile |
30 |
import fcntl |
31 |
import os |
32 |
import select |
33 |
import logging |
34 |
|
35 |
|
36 |
def TestDelay(duration): |
37 |
"""Sleep for a fixed amount of time.
|
38 |
|
39 |
@type duration: float
|
40 |
@param duration: the sleep duration, in seconds
|
41 |
@rtype: (boolean, str)
|
42 |
@return: False for negative value, and an accompanying error message;
|
43 |
True otherwise (and msg is None)
|
44 |
|
45 |
"""
|
46 |
if duration < 0: |
47 |
return False, "Invalid sleep duration" |
48 |
time.sleep(duration) |
49 |
return True, None |
50 |
|
51 |
|
52 |
def CloseFdNoError(fd, retries=5): |
53 |
"""Close a file descriptor ignoring errors.
|
54 |
|
55 |
@type fd: int
|
56 |
@param fd: the file descriptor
|
57 |
@type retries: int
|
58 |
@param retries: how many retries to make, in case we get any
|
59 |
other error than EBADF
|
60 |
|
61 |
"""
|
62 |
try:
|
63 |
os.close(fd) |
64 |
except OSError, err: |
65 |
if err.errno != errno.EBADF:
|
66 |
if retries > 0: |
67 |
CloseFdNoError(fd, retries - 1)
|
68 |
# else either it's closed already or we're out of retries, so we
|
69 |
# ignore this and go on
|
70 |
|
71 |
|
72 |
def SetCloseOnExecFlag(fd, enable): |
73 |
"""Sets or unsets the close-on-exec flag on a file descriptor.
|
74 |
|
75 |
@type fd: int
|
76 |
@param fd: File descriptor
|
77 |
@type enable: bool
|
78 |
@param enable: Whether to set or unset it.
|
79 |
|
80 |
"""
|
81 |
flags = fcntl.fcntl(fd, fcntl.F_GETFD) |
82 |
|
83 |
if enable:
|
84 |
flags |= fcntl.FD_CLOEXEC |
85 |
else:
|
86 |
flags &= ~fcntl.FD_CLOEXEC |
87 |
|
88 |
fcntl.fcntl(fd, fcntl.F_SETFD, flags) |
89 |
|
90 |
|
91 |
def SetNonblockFlag(fd, enable): |
92 |
"""Sets or unsets the O_NONBLOCK flag on on a file descriptor.
|
93 |
|
94 |
@type fd: int
|
95 |
@param fd: File descriptor
|
96 |
@type enable: bool
|
97 |
@param enable: Whether to set or unset it
|
98 |
|
99 |
"""
|
100 |
flags = fcntl.fcntl(fd, fcntl.F_GETFL) |
101 |
|
102 |
if enable:
|
103 |
flags |= os.O_NONBLOCK |
104 |
else:
|
105 |
flags &= ~os.O_NONBLOCK |
106 |
|
107 |
fcntl.fcntl(fd, fcntl.F_SETFL, flags) |
108 |
|
109 |
|
110 |
def RetryOnSignal(fn, *args, **kwargs): |
111 |
"""Calls a function again if it failed due to EINTR.
|
112 |
|
113 |
"""
|
114 |
while True: |
115 |
try:
|
116 |
return fn(*args, **kwargs)
|
117 |
except EnvironmentError, err: |
118 |
if err.errno != errno.EINTR:
|
119 |
raise
|
120 |
except (socket.error, select.error), err:
|
121 |
# In python 2.6 and above select.error is an IOError, so it's handled
|
122 |
# above, in 2.5 and below it's not, and it's handled here.
|
123 |
if not (err.args and err.args[0] == errno.EINTR): |
124 |
raise
|
125 |
|
126 |
|
127 |
def IgnoreProcessNotFound(fn, *args, **kwargs): |
128 |
"""Ignores ESRCH when calling a process-related function.
|
129 |
|
130 |
ESRCH is raised when a process is not found.
|
131 |
|
132 |
@rtype: bool
|
133 |
@return: Whether process was found
|
134 |
|
135 |
"""
|
136 |
try:
|
137 |
fn(*args, **kwargs) |
138 |
except EnvironmentError, err: |
139 |
# Ignore ESRCH
|
140 |
if err.errno == errno.ESRCH:
|
141 |
return False |
142 |
raise
|
143 |
|
144 |
return True |
145 |
|
146 |
|
147 |
def IgnoreSignals(fn, *args, **kwargs): |
148 |
"""Tries to call a function ignoring failures due to EINTR.
|
149 |
|
150 |
"""
|
151 |
try:
|
152 |
return fn(*args, **kwargs)
|
153 |
except EnvironmentError, err: |
154 |
if err.errno == errno.EINTR:
|
155 |
return None |
156 |
else:
|
157 |
raise
|
158 |
except (select.error, socket.error), err:
|
159 |
# In python 2.6 and above select.error is an IOError, so it's handled
|
160 |
# above, in 2.5 and below it's not, and it's handled here.
|
161 |
if err.args and err.args[0] == errno.EINTR: |
162 |
return None |
163 |
else:
|
164 |
raise
|
165 |
|
166 |
|
167 |
def GetClosedTempfile(*args, **kwargs): |
168 |
"""Creates a temporary file and returns its path.
|
169 |
|
170 |
"""
|
171 |
(fd, path) = tempfile.mkstemp(*args, **kwargs) |
172 |
CloseFdNoError(fd) |
173 |
return path
|
174 |
|
175 |
|
176 |
def IsExecutable(filename): |
177 |
"""Checks whether a file exists and is executable.
|
178 |
|
179 |
@type filename: string
|
180 |
@param filename: Filename
|
181 |
@rtype: bool
|
182 |
|
183 |
"""
|
184 |
return os.path.isfile(filename) and os.access(filename, os.X_OK) |
185 |
|
186 |
|
187 |
def ResetTempfileModule(_time=time.time): |
188 |
"""Resets the random name generator of the tempfile module.
|
189 |
|
190 |
This function should be called after C{os.fork} in the child process to
|
191 |
ensure it creates a newly seeded random generator. Otherwise it would
|
192 |
generate the same random parts as the parent process. If several processes
|
193 |
race for the creation of a temporary file, this could lead to one not getting
|
194 |
a temporary name.
|
195 |
|
196 |
"""
|
197 |
# pylint: disable=W0212
|
198 |
if ((sys.hexversion >= 0x020703F0 and sys.hexversion < 0x03000000) or |
199 |
sys.hexversion >= 0x030203F0):
|
200 |
# Python 2.7 automatically resets the RNG on pid changes (i.e. forking)
|
201 |
return
|
202 |
|
203 |
try:
|
204 |
lock = tempfile._once_lock |
205 |
lock.acquire() |
206 |
try:
|
207 |
# Re-seed random name generator
|
208 |
if tempfile._name_sequence:
|
209 |
tempfile._name_sequence.rng.seed(hash(_time()) ^ os.getpid())
|
210 |
finally:
|
211 |
lock.release() |
212 |
except AttributeError: |
213 |
logging.critical("The tempfile module misses at least one of the"
|
214 |
" '_once_lock' and '_name_sequence' attributes")
|