blob: 1b19f365cb66176063a683d7b36fa389dd20742f [file] [log] [blame]
#
#
# Copyright (C) 2006, 2007, 2010, 2011, 2012 Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# 1. Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""Utility functions wrapping other functions.
"""
import sys
import time
import socket
import errno
import tempfile
import fcntl
import os
import select
import logging
def TestDelay(duration):
"""Sleep for a fixed amount of time.
@type duration: float
@param duration: the sleep duration, in seconds
@rtype: (boolean, str)
@return: False for negative value, and an accompanying error message;
True otherwise (and msg is None)
"""
if duration < 0:
return False, "Invalid sleep duration"
time.sleep(duration)
return True, None
def CloseFdNoError(fd, retries=5):
"""Close a file descriptor ignoring errors.
@type fd: int
@param fd: the file descriptor
@type retries: int
@param retries: how many retries to make, in case we get any
other error than EBADF
"""
try:
os.close(fd)
except OSError, err:
if err.errno != errno.EBADF:
if retries > 0:
CloseFdNoError(fd, retries - 1)
# else either it's closed already or we're out of retries, so we
# ignore this and go on
def SetCloseOnExecFlag(fd, enable):
"""Sets or unsets the close-on-exec flag on a file descriptor.
@type fd: int
@param fd: File descriptor
@type enable: bool
@param enable: Whether to set or unset it.
"""
flags = fcntl.fcntl(fd, fcntl.F_GETFD)
if enable:
flags |= fcntl.FD_CLOEXEC
else:
flags &= ~fcntl.FD_CLOEXEC
fcntl.fcntl(fd, fcntl.F_SETFD, flags)
def SetNonblockFlag(fd, enable):
"""Sets or unsets the O_NONBLOCK flag on on a file descriptor.
@type fd: int
@param fd: File descriptor
@type enable: bool
@param enable: Whether to set or unset it
"""
flags = fcntl.fcntl(fd, fcntl.F_GETFL)
if enable:
flags |= os.O_NONBLOCK
else:
flags &= ~os.O_NONBLOCK
fcntl.fcntl(fd, fcntl.F_SETFL, flags)
def RetryOnSignal(fn, *args, **kwargs):
"""Calls a function again if it failed due to EINTR.
"""
while True:
try:
return fn(*args, **kwargs)
except EnvironmentError, err:
if err.errno != errno.EINTR:
raise
except (socket.error, select.error), err:
# In python 2.6 and above select.error is an IOError, so it's handled
# above, in 2.5 and below it's not, and it's handled here.
if not (err.args and err.args[0] == errno.EINTR):
raise
def IgnoreProcessNotFound(fn, *args, **kwargs):
"""Ignores ESRCH when calling a process-related function.
ESRCH is raised when a process is not found.
@rtype: bool
@return: Whether process was found
"""
try:
fn(*args, **kwargs)
except EnvironmentError, err:
# Ignore ESRCH
if err.errno == errno.ESRCH:
return False
raise
return True
def IgnoreSignals(fn, *args, **kwargs):
"""Tries to call a function ignoring failures due to EINTR.
"""
try:
return fn(*args, **kwargs)
except EnvironmentError, err:
if err.errno == errno.EINTR:
return None
else:
raise
except (select.error, socket.error), err:
# In python 2.6 and above select.error is an IOError, so it's handled
# above, in 2.5 and below it's not, and it's handled here.
if err.args and err.args[0] == errno.EINTR:
return None
else:
raise
def GetClosedTempfile(*args, **kwargs):
"""Creates a temporary file and returns its path.
"""
(fd, path) = tempfile.mkstemp(*args, **kwargs)
CloseFdNoError(fd)
return path
def IsExecutable(filename):
"""Checks whether a file exists and is executable.
@type filename: string
@param filename: Filename
@rtype: bool
"""
return os.path.isfile(filename) and os.access(filename, os.X_OK)
def ResetTempfileModule(_time=time.time):
"""Resets the random name generator of the tempfile module.
This function should be called after C{os.fork} in the child process to
ensure it creates a newly seeded random generator. Otherwise it would
generate the same random parts as the parent process. If several processes
race for the creation of a temporary file, this could lead to one not getting
a temporary name.
"""
# pylint: disable=W0212
if ((sys.hexversion >= 0x020703F0 and sys.hexversion < 0x03000000) or
sys.hexversion >= 0x030203F0):
# Python 2.7 automatically resets the RNG on pid changes (i.e. forking)
return
try:
lock = tempfile._once_lock
lock.acquire()
try:
# Re-seed random name generator
if tempfile._name_sequence:
tempfile._name_sequence.rng.seed(hash(_time()) ^ os.getpid())
finally:
lock.release()
except AttributeError:
logging.critical("The tempfile module misses at least one of the"
" '_once_lock' and '_name_sequence' attributes")