import datetime
import calendar
import collections
+import struct
+import IN
from cStringIO import StringIO
_RANDOM_UUID_FILE = "/proc/sys/kernel/random/uuid"
+# Structure definition for getsockopt(SOL_SOCKET, SO_PEERCRED, ...):
+# struct ucred { pid_t pid; uid_t uid; gid_t gid; };
+#
+# The GNU C Library defines gid_t and uid_t to be "unsigned int" and
+# pid_t to "int".
+#
+# IEEE Std 1003.1-2008:
+# "nlink_t, uid_t, gid_t, and id_t shall be integer types"
+# "blksize_t, pid_t, and ssize_t shall be signed integer types"
+_STRUCT_UCRED = "iII"
+_STRUCT_UCRED_SIZE = struct.calcsize(_STRUCT_UCRED)
+
class RunResult(object):
"""Holds the result of running external programs.
return rr
+def GetSocketCredentials(sock):
+ """Returns the credentials of the foreign process connected to a socket.
+
+ @param sock: Unix socket
+ @rtype: tuple; (number, number, number)
+ @return: The PID, UID and GID of the connected foreign process.
+
+ """
+ peercred = sock.getsockopt(socket.SOL_SOCKET, IN.SO_PEERCRED,
+ _STRUCT_UCRED_SIZE)
+ return struct.unpack(_STRUCT_UCRED, peercred)
+
+
def RemoveFile(filename):
"""Remove a file ignoring some errors.
@return: True if the process exists
"""
+ def _TryStat(name):
+ try:
+ os.stat(name)
+ return True
+ except EnvironmentError, err:
+ if err.errno in (errno.ENOENT, errno.ENOTDIR):
+ return False
+ elif err.errno == errno.EINVAL:
+ raise RetryAgain(err)
+ raise
+
+ assert isinstance(pid, int), "pid must be an integer"
if pid <= 0:
return False
+ proc_entry = "/proc/%d/status" % pid
+ # /proc in a multiprocessor environment can have strange behaviors.
+ # Retry the os.stat a few times until we get a good result.
try:
- os.stat("/proc/%d/status" % pid)
- return True
- except EnvironmentError, err:
- if err.errno in (errno.ENOENT, errno.ENOTDIR):
- return False
- raise
+ return Retry(_TryStat, (0.01, 1.5, 0.1), 0.5, args=[proc_entry])
+ except RetryTimeout, err:
+ err.RaiseInner()
def ReadPidFile(pidfile):
return None
-def all(seq, pred=bool): # pylint: disable-msg=W0622
- "Returns True if pred(x) is True for every element in the iterable"
- for _ in itertools.ifilterfalse(pred, seq):
- return False
- return True
-
-
-def any(seq, pred=bool): # pylint: disable-msg=W0622
- "Returns True if pred(x) is True for at least one element in the iterable"
- for _ in itertools.ifilter(pred, seq):
- return True
- return False
-
-
def SingleWaitForFdCondition(fdobj, event, timeout):
"""Waits for a condition to occur on the socket.
return result
-def partition(seq, pred=bool): # # pylint: disable-msg=W0622
- "Partition a list in two, based on the given predicate"
- return (list(itertools.ifilter(pred, seq)),
- list(itertools.ifilterfalse(pred, seq)))
-
-
def UniqueSequence(seq):
"""Returns a list with unique elements.
class RetryTimeout(Exception):
"""Retry loop timed out.
+ Any arguments which was passed by the retried function to RetryAgain will be
+ preserved in RetryTimeout, if it is raised. If such argument was an exception
+ the RaiseInner helper method will reraise it.
+
"""
+ def RaiseInner(self):
+ if self.args and isinstance(self.args[0], Exception):
+ raise self.args[0]
+ else:
+ raise RetryTimeout(*self.args)
class RetryAgain(Exception):
"""Retry again.
+ Any arguments passed to RetryAgain will be preserved, if a timeout occurs, as
+ arguments to RetryTimeout. If an exception is passed, the RaiseInner() method
+ of the RetryTimeout() method can be used to reraise it.
+
"""
assert calc_delay is None or callable(calc_delay)
while True:
+ retry_args = []
try:
# pylint: disable-msg=W0142
return fn(*args)
- except RetryAgain:
- pass
+ except RetryAgain, err:
+ retry_args = err.args
except RetryTimeout:
raise errors.ProgrammerError("Nested retry loop detected that didn't"
" handle RetryTimeout")
remaining_time = end_time - _time_fn()
if remaining_time < 0.0:
- raise RetryTimeout()
+ # pylint: disable-msg=W0142
+ raise RetryTimeout(*retry_args)
assert remaining_time >= 0.0