Statistics
| Branch: | Tag: | Revision:

root / lib / utils / wrapper.py @ 364c350f

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")