2017-04-25 16:45:34 +01:00
|
|
|
"""
|
|
|
|
Miscellaneous utility functions, wrappers around some subprocess procedures.
|
|
|
|
"""
|
|
|
|
|
|
|
|
import ast
|
|
|
|
import os
|
|
|
|
import subprocess
|
|
|
|
|
2016-01-29 23:21:49 +00:00
|
|
|
import fcntl
|
|
|
|
|
2017-04-25 16:45:34 +01:00
|
|
|
from core.misc import log
|
|
|
|
|
|
|
|
logger = log.get_logger(__name__)
|
|
|
|
|
|
|
|
|
2016-01-29 23:21:49 +00:00
|
|
|
def closeonexec(fd):
|
|
|
|
fdflags = fcntl.fcntl(fd, fcntl.F_GETFD)
|
|
|
|
fcntl.fcntl(fd, fcntl.F_SETFD, fdflags | fcntl.FD_CLOEXEC)
|
2013-08-29 15:21:13 +01:00
|
|
|
|
2017-04-25 16:45:34 +01:00
|
|
|
|
|
|
|
def check_executables(executables):
|
|
|
|
for executable in executables:
|
|
|
|
if not (os.path.isfile(executable) and os.access(executable, os.X_OK)):
|
|
|
|
raise EnvironmentError("executable not found: %s" % executable)
|
|
|
|
|
2013-08-29 15:21:13 +01:00
|
|
|
|
2013-09-12 20:07:41 +01:00
|
|
|
def which(program):
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
|
|
|
From: http://stackoverflow.com/questions/377017/test-if-executable-exists-in-python
|
|
|
|
|
|
|
|
:param str program: program to check for
|
|
|
|
:return: path if it exists, none otherwise
|
|
|
|
"""
|
|
|
|
|
2013-09-12 20:07:41 +01:00
|
|
|
def is_exe(fpath):
|
|
|
|
return os.path.isfile(fpath) and os.access(fpath, os.X_OK)
|
|
|
|
|
|
|
|
fpath, fname = os.path.split(program)
|
|
|
|
if fpath:
|
|
|
|
if is_exe(program):
|
|
|
|
return program
|
|
|
|
else:
|
|
|
|
for path in os.environ["PATH"].split(os.pathsep):
|
|
|
|
path = path.strip('"')
|
|
|
|
exe_file = os.path.join(path, program)
|
|
|
|
if is_exe(exe_file):
|
|
|
|
return exe_file
|
|
|
|
|
|
|
|
return None
|
|
|
|
|
2017-04-25 16:45:34 +01:00
|
|
|
|
2013-08-29 15:21:13 +01:00
|
|
|
def ensurepath(pathlist):
|
|
|
|
searchpath = os.environ["PATH"].split(":")
|
|
|
|
for p in set(pathlist):
|
|
|
|
if p not in searchpath:
|
|
|
|
os.environ["PATH"] += ":" + p
|
|
|
|
|
2017-04-25 16:45:34 +01:00
|
|
|
|
2013-08-29 15:21:13 +01:00
|
|
|
def maketuple(obj):
|
|
|
|
if hasattr(obj, "__iter__"):
|
|
|
|
return tuple(obj)
|
|
|
|
else:
|
2017-04-25 16:45:34 +01:00
|
|
|
return obj,
|
|
|
|
|
|
|
|
|
2013-08-29 15:21:13 +01:00
|
|
|
def maketuplefromstr(s, type):
|
|
|
|
s.replace('\\', '\\\\')
|
|
|
|
return ast.literal_eval(s)
|
2017-04-25 16:45:34 +01:00
|
|
|
# return tuple(type(i) for i in s[1:-1].split(','))
|
|
|
|
# r = ()
|
|
|
|
# for i in s.strip("()").split(','):
|
2013-08-29 15:21:13 +01:00
|
|
|
# r += (i.strip("' "), )
|
|
|
|
# chop empty last element from "('a',)" strings
|
2017-04-25 16:45:34 +01:00
|
|
|
# if r[-1] == '':
|
2013-08-29 15:21:13 +01:00
|
|
|
# r = r[:-1]
|
2017-04-25 16:45:34 +01:00
|
|
|
# return r
|
2013-08-29 15:21:13 +01:00
|
|
|
|
|
|
|
|
|
|
|
def mutecall(*args, **kwds):
|
|
|
|
kwds["stdout"] = open(os.devnull, "w")
|
|
|
|
kwds["stderr"] = subprocess.STDOUT
|
2017-04-25 16:45:34 +01:00
|
|
|
return subprocess.call(*args, **kwds)
|
2013-08-29 15:21:13 +01:00
|
|
|
|
|
|
|
|
|
|
|
def mutecheck_call(*args, **kwds):
|
|
|
|
kwds["stdout"] = open(os.devnull, "w")
|
|
|
|
kwds["stderr"] = subprocess.STDOUT
|
|
|
|
return subprocess.check_call(*args, **kwds)
|
|
|
|
|
2017-04-25 16:45:34 +01:00
|
|
|
|
2013-08-29 15:21:13 +01:00
|
|
|
def spawn(*args, **kwds):
|
|
|
|
return subprocess.Popen(*args, **kwds).pid
|
|
|
|
|
2017-04-25 16:45:34 +01:00
|
|
|
|
2013-08-29 15:21:13 +01:00
|
|
|
def mutespawn(*args, **kwds):
|
|
|
|
kwds["stdout"] = open(os.devnull, "w")
|
|
|
|
kwds["stderr"] = subprocess.STDOUT
|
|
|
|
return subprocess.Popen(*args, **kwds).pid
|
|
|
|
|
2017-04-25 16:45:34 +01:00
|
|
|
|
2013-08-29 15:21:13 +01:00
|
|
|
def detachinit():
|
|
|
|
if os.fork():
|
2017-04-25 16:45:34 +01:00
|
|
|
# parent exits
|
|
|
|
os._exit(0)
|
2013-08-29 15:21:13 +01:00
|
|
|
os.setsid()
|
|
|
|
|
2017-04-25 16:45:34 +01:00
|
|
|
|
2013-08-29 15:21:13 +01:00
|
|
|
def detach(*args, **kwds):
|
|
|
|
kwds["preexec_fn"] = detachinit
|
|
|
|
return subprocess.Popen(*args, **kwds).pid
|
|
|
|
|
2017-04-25 16:45:34 +01:00
|
|
|
|
2013-08-29 15:21:13 +01:00
|
|
|
def mutedetach(*args, **kwds):
|
|
|
|
kwds["preexec_fn"] = detachinit
|
|
|
|
kwds["stdout"] = open(os.devnull, "w")
|
|
|
|
kwds["stderr"] = subprocess.STDOUT
|
|
|
|
return subprocess.Popen(*args, **kwds).pid
|
|
|
|
|
2017-04-25 16:45:34 +01:00
|
|
|
|
2013-10-22 15:32:42 +01:00
|
|
|
def cmdresult(args):
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
|
|
|
Execute a command on the host and return a tuple containing the
|
|
|
|
exit status and result string. stderr output
|
|
|
|
is folded into the stdout result string.
|
|
|
|
"""
|
|
|
|
cmdid = subprocess.Popen(args, stdin=open(os.devnull, 'r'),
|
|
|
|
stdout=subprocess.PIPE,
|
|
|
|
stderr=subprocess.STDOUT)
|
|
|
|
# err will always be None
|
|
|
|
result, err = cmdid.communicate()
|
2013-10-22 15:32:42 +01:00
|
|
|
status = cmdid.wait()
|
2017-04-25 16:45:34 +01:00
|
|
|
return status, result
|
2013-10-22 15:32:42 +01:00
|
|
|
|
2017-04-25 16:45:34 +01:00
|
|
|
|
|
|
|
def hexdump(s, bytes_per_word=2, words_per_line=8):
|
2013-08-29 15:21:13 +01:00
|
|
|
dump = ""
|
|
|
|
count = 0
|
|
|
|
bytes = bytes_per_word * words_per_line
|
|
|
|
while s:
|
|
|
|
line = s[:bytes]
|
|
|
|
s = s[bytes:]
|
|
|
|
tmp = map(lambda x: ("%02x" * bytes_per_word) % x,
|
|
|
|
zip(*[iter(map(ord, line))] * bytes_per_word))
|
|
|
|
if len(line) % 2:
|
|
|
|
tmp.append("%x" % ord(line[-1]))
|
|
|
|
dump += "0x%08x: %s\n" % (count, " ".join(tmp))
|
|
|
|
count += len(line)
|
|
|
|
return dump[:-1]
|
|
|
|
|
2017-04-25 16:45:34 +01:00
|
|
|
|
2013-08-29 15:21:13 +01:00
|
|
|
def filemunge(pathname, header, text):
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
|
|
|
Insert text at the end of a file, surrounded by header comments.
|
|
|
|
"""
|
|
|
|
# prevent duplicates
|
|
|
|
filedemunge(pathname, header)
|
2013-08-29 15:21:13 +01:00
|
|
|
f = open(pathname, 'a')
|
|
|
|
f.write("# BEGIN %s\n" % header)
|
|
|
|
f.write(text)
|
|
|
|
f.write("# END %s\n" % header)
|
|
|
|
f.close()
|
|
|
|
|
2017-04-25 16:45:34 +01:00
|
|
|
|
2013-08-29 15:21:13 +01:00
|
|
|
def filedemunge(pathname, header):
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
|
|
|
Remove text that was inserted in a file surrounded by header comments.
|
|
|
|
"""
|
2013-08-29 15:21:13 +01:00
|
|
|
f = open(pathname, 'r')
|
|
|
|
lines = f.readlines()
|
|
|
|
f.close()
|
|
|
|
start = None
|
|
|
|
end = None
|
|
|
|
for i in range(len(lines)):
|
|
|
|
if lines[i] == "# BEGIN %s\n" % header:
|
|
|
|
start = i
|
|
|
|
elif lines[i] == "# END %s\n" % header:
|
|
|
|
end = i + 1
|
|
|
|
if start is None or end is None:
|
|
|
|
return
|
|
|
|
f = open(pathname, 'w')
|
|
|
|
lines = lines[:start] + lines[end:]
|
|
|
|
f.write("".join(lines))
|
|
|
|
f.close()
|
2017-04-25 16:45:34 +01:00
|
|
|
|
|
|
|
|
2013-08-29 15:21:13 +01:00
|
|
|
def expandcorepath(pathname, session=None, node=None):
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
|
|
|
Expand a file path given session information.
|
|
|
|
"""
|
2013-08-29 15:21:13 +01:00
|
|
|
if session is not None:
|
|
|
|
pathname = pathname.replace('~', "/home/%s" % session.user)
|
|
|
|
pathname = pathname.replace('%SESSION%', str(session.sessionid))
|
|
|
|
pathname = pathname.replace('%SESSION_DIR%', session.sessiondir)
|
|
|
|
pathname = pathname.replace('%SESSION_USER%', session.user)
|
|
|
|
if node is not None:
|
|
|
|
pathname = pathname.replace('%NODE%', str(node.objid))
|
|
|
|
pathname = pathname.replace('%NODENAME%', node.name)
|
|
|
|
return pathname
|
2017-04-25 16:45:34 +01:00
|
|
|
|
|
|
|
|
2013-08-29 15:21:13 +01:00
|
|
|
def sysctldevname(devname):
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
|
|
|
Translate a device name to the name used with sysctl.
|
|
|
|
"""
|
2013-08-29 15:21:13 +01:00
|
|
|
if devname is None:
|
|
|
|
return None
|
|
|
|
return devname.replace(".", "/")
|
|
|
|
|
2017-04-25 16:45:34 +01:00
|
|
|
|
|
|
|
def daemonize(rootdir="/", umask=0, close_fds=False, dontclose=(),
|
|
|
|
stdin=os.devnull, stdout=os.devnull, stderr=os.devnull,
|
|
|
|
stdoutmode=0644, stderrmode=0644, pidfilename=None,
|
|
|
|
defaultmaxfd=1024):
|
|
|
|
"""
|
|
|
|
Run the background process as a daemon.
|
|
|
|
"""
|
2013-08-29 15:21:13 +01:00
|
|
|
if not hasattr(dontclose, "__contains__"):
|
|
|
|
if not isinstance(dontclose, int):
|
|
|
|
raise TypeError, "dontclose must be an integer"
|
|
|
|
dontclose = (int(dontclose),)
|
|
|
|
else:
|
|
|
|
for fd in dontclose:
|
|
|
|
if not isinstance(fd, int):
|
|
|
|
raise TypeError, "dontclose must contain only integers"
|
|
|
|
# redirect stdin
|
|
|
|
if stdin:
|
|
|
|
fd = os.open(stdin, os.O_RDONLY)
|
|
|
|
os.dup2(fd, 0)
|
|
|
|
os.close(fd)
|
|
|
|
# redirect stdout
|
|
|
|
if stdout:
|
|
|
|
fd = os.open(stdout, os.O_WRONLY | os.O_CREAT | os.O_APPEND,
|
|
|
|
stdoutmode)
|
|
|
|
os.dup2(fd, 1)
|
2017-04-25 16:45:34 +01:00
|
|
|
if stdout == stderr:
|
2013-08-29 15:21:13 +01:00
|
|
|
os.dup2(1, 2)
|
|
|
|
os.close(fd)
|
|
|
|
# redirect stderr
|
|
|
|
if stderr and (stderr != stdout):
|
|
|
|
fd = os.open(stderr, os.O_WRONLY | os.O_CREAT | os.O_APPEND,
|
|
|
|
stderrmode)
|
|
|
|
os.dup2(fd, 2)
|
|
|
|
os.close(fd)
|
|
|
|
if os.fork():
|
2017-04-25 16:45:34 +01:00
|
|
|
# parent exits
|
|
|
|
os._exit(0)
|
2013-08-29 15:21:13 +01:00
|
|
|
os.setsid()
|
|
|
|
pid = os.fork()
|
|
|
|
if pid:
|
|
|
|
if pidfilename:
|
|
|
|
try:
|
|
|
|
f = open(pidfilename, "w")
|
|
|
|
f.write("%s\n" % pid)
|
|
|
|
f.close()
|
2017-04-25 16:45:34 +01:00
|
|
|
except IOError:
|
|
|
|
logger.exception("error writing to file: %s", pidfilename)
|
|
|
|
# parent exits
|
|
|
|
os._exit(0)
|
2013-08-29 15:21:13 +01:00
|
|
|
if rootdir:
|
|
|
|
os.chdir(rootdir)
|
|
|
|
os.umask(umask)
|
|
|
|
if close_fds:
|
|
|
|
try:
|
|
|
|
maxfd = resource.getrlimit(resource.RLIMIT_NOFILE)[1]
|
|
|
|
if maxfd == resource.RLIM_INFINITY:
|
|
|
|
raise ValueError
|
|
|
|
except:
|
|
|
|
maxfd = defaultmaxfd
|
|
|
|
for fd in xrange(3, maxfd):
|
|
|
|
if fd in dontclose:
|
|
|
|
continue
|
|
|
|
try:
|
|
|
|
os.close(fd)
|
|
|
|
except:
|
2017-04-25 16:45:34 +01:00
|
|
|
logger.exception("error closing file descriptor")
|
|
|
|
|
2013-08-29 15:21:13 +01:00
|
|
|
|
|
|
|
def readfileintodict(filename, d):
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
|
|
|
Read key=value pairs from a file, into a dict.
|
|
|
|
Skip comments; strip newline characters and spacing.
|
|
|
|
"""
|
2013-08-29 15:21:13 +01:00
|
|
|
with open(filename, 'r') as f:
|
|
|
|
lines = f.readlines()
|
|
|
|
for l in lines:
|
|
|
|
if l[:1] == '#':
|
|
|
|
continue
|
|
|
|
try:
|
|
|
|
key, value = l.split('=', 1)
|
|
|
|
d[key] = value.strip()
|
|
|
|
except ValueError:
|
2017-04-25 16:45:34 +01:00
|
|
|
logger.exception("error reading file to dict: %s", filename)
|
2013-10-22 15:32:42 +01:00
|
|
|
|
|
|
|
|
|
|
|
def checkforkernelmodule(name):
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
|
|
|
Return a string if a Linux kernel module is loaded, None otherwise.
|
2013-10-22 15:32:42 +01:00
|
|
|
The string is the line from /proc/modules containing the module name,
|
|
|
|
memory size (bytes), number of loaded instances, dependencies, state,
|
|
|
|
and kernel memory offset.
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
2013-10-22 15:32:42 +01:00
|
|
|
with open('/proc/modules', 'r') as f:
|
|
|
|
for line in f:
|
2016-01-29 23:21:49 +00:00
|
|
|
if line.startswith(name + ' '):
|
|
|
|
return line.rstrip()
|
2013-10-22 15:32:42 +01:00
|
|
|
return None
|