Statistics
| Branch: | Tag: | Revision:

root / lib / utils / wrapper.py @ 10b86782

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
41
  @rtype: boolean
42
  @return: False for negative value, True otherwise
43

44
  """
45
  if duration < 0:
46
    return False, "Invalid sleep duration"
47
  time.sleep(duration)
48
  return True, None
49

    
50

    
51
def CloseFdNoError(fd, retries=5):
52
  """Close a file descriptor ignoring errors.
53

54
  @type fd: int
55
  @param fd: the file descriptor
56
  @type retries: int
57
  @param retries: how many retries to make, in case we get any
58
      other error than EBADF
59

60
  """
61
  try:
62
    os.close(fd)
63
  except OSError, err:
64
    if err.errno != errno.EBADF:
65
      if retries > 0:
66
        CloseFdNoError(fd, retries - 1)
67
    # else either it's closed already or we're out of retries, so we
68
    # ignore this and go on
69

    
70

    
71
def SetCloseOnExecFlag(fd, enable):
72
  """Sets or unsets the close-on-exec flag on a file descriptor.
73

74
  @type fd: int
75
  @param fd: File descriptor
76
  @type enable: bool
77
  @param enable: Whether to set or unset it.
78

79
  """
80
  flags = fcntl.fcntl(fd, fcntl.F_GETFD)
81

    
82
  if enable:
83
    flags |= fcntl.FD_CLOEXEC
84
  else:
85
    flags &= ~fcntl.FD_CLOEXEC
86

    
87
  fcntl.fcntl(fd, fcntl.F_SETFD, flags)
88

    
89

    
90
def SetNonblockFlag(fd, enable):
91
  """Sets or unsets the O_NONBLOCK flag on on a file descriptor.
92

93
  @type fd: int
94
  @param fd: File descriptor
95
  @type enable: bool
96
  @param enable: Whether to set or unset it
97

98
  """
99
  flags = fcntl.fcntl(fd, fcntl.F_GETFL)
100

    
101
  if enable:
102
    flags |= os.O_NONBLOCK
103
  else:
104
    flags &= ~os.O_NONBLOCK
105

    
106
  fcntl.fcntl(fd, fcntl.F_SETFL, flags)
107

    
108

    
109
def RetryOnSignal(fn, *args, **kwargs):
110
  """Calls a function again if it failed due to EINTR.
111

112
  """
113
  while True:
114
    try:
115
      return fn(*args, **kwargs)
116
    except EnvironmentError, err:
117
      if err.errno != errno.EINTR:
118
        raise
119
    except (socket.error, select.error), err:
120
      # In python 2.6 and above select.error is an IOError, so it's handled
121
      # above, in 2.5 and below it's not, and it's handled here.
122
      if not (err.args and err.args[0] == errno.EINTR):
123
        raise
124

    
125

    
126
def IgnoreProcessNotFound(fn, *args, **kwargs):
127
  """Ignores ESRCH when calling a process-related function.
128

129
  ESRCH is raised when a process is not found.
130

131
  @rtype: bool
132
  @return: Whether process was found
133

134
  """
135
  try:
136
    fn(*args, **kwargs)
137
  except EnvironmentError, err:
138
    # Ignore ESRCH
139
    if err.errno == errno.ESRCH:
140
      return False
141
    raise
142

    
143
  return True
144

    
145

    
146
def IgnoreSignals(fn, *args, **kwargs):
147
  """Tries to call a function ignoring failures due to EINTR.
148

149
  """
150
  try:
151
    return fn(*args, **kwargs)
152
  except EnvironmentError, err:
153
    if err.errno == errno.EINTR:
154
      return None
155
    else:
156
      raise
157
  except (select.error, socket.error), err:
158
    # In python 2.6 and above select.error is an IOError, so it's handled
159
    # above, in 2.5 and below it's not, and it's handled here.
160
    if err.args and err.args[0] == errno.EINTR:
161
      return None
162
    else:
163
      raise
164

    
165

    
166
def GetClosedTempfile(*args, **kwargs):
167
  """Creates a temporary file and returns its path.
168

169
  """
170
  (fd, path) = tempfile.mkstemp(*args, **kwargs)
171
  CloseFdNoError(fd)
172
  return path
173

    
174

    
175
def IsExecutable(filename):
176
  """Checks whether a file exists and is executable.
177

178
  @type filename: string
179
  @param filename: Filename
180
  @rtype: bool
181

182
  """
183
  return os.path.isfile(filename) and os.access(filename, os.X_OK)
184

    
185

    
186
def ResetTempfileModule(_time=time.time):
187
  """Resets the random name generator of the tempfile module.
188

189
  This function should be called after C{os.fork} in the child process to
190
  ensure it creates a newly seeded random generator. Otherwise it would
191
  generate the same random parts as the parent process. If several processes
192
  race for the creation of a temporary file, this could lead to one not getting
193
  a temporary name.
194

195
  """
196
  # pylint: disable=W0212
197
  if ((sys.hexversion >= 0x020703F0 and sys.hexversion < 0x03000000) or
198
      sys.hexversion >= 0x030203F0):
199
    # Python 2.7 automatically resets the RNG on pid changes (i.e. forking)
200
    return
201

    
202
  try:
203
    lock = tempfile._once_lock
204
    lock.acquire()
205
    try:
206
      # Re-seed random name generator
207
      if tempfile._name_sequence:
208
        tempfile._name_sequence.rng.seed(hash(_time()) ^ os.getpid())
209
    finally:
210
      lock.release()
211
  except AttributeError:
212
    logging.critical("The tempfile module misses at least one of the"
213
                     " '_once_lock' and '_name_sequence' attributes")