[LORAX 2/6] Add execWith utils from anaconda

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



From: "Brian C. Lane" <bcl@xxxxxxxxxx>

The anaconda execWithRedirect and execWithCapture functions are too
useful not to include. They also allow you to log all the execuatable's
output to a logfile. Added them under executils.py module which uses
the pylorax and a new program logger.
---
 src/pylorax/executils.py |  379 ++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 379 insertions(+), 0 deletions(-)
 create mode 100644 src/pylorax/executils.py

diff --git a/src/pylorax/executils.py b/src/pylorax/executils.py
new file mode 100644
index 0000000..f1be30c
--- /dev/null
+++ b/src/pylorax/executils.py
@@ -0,0 +1,379 @@
+#
+# executil.py - subprocess execution utility functions
+#
+# Copyright (C) 1999-2011
+# Red Hat, Inc.  All rights reserved.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+# Author(s): Erik Troan <ewt@xxxxxxxxxx>
+#
+
+import os, sys
+import subprocess
+import threading
+
+import logging
+log = logging.getLogger("pylorax")
+program_log = logging.getLogger("program")
+
+class ExecProduct(object):
+    def __init__(self, rc, stdout, stderr):
+        self.rc = rc
+        self.stdout = stdout
+        self.stderr = stderr
+
+#Python reimplementation of the shell tee process, so we can
+#feed the pipe output into two places at the same time
+class tee(threading.Thread):
+    def __init__(self, inputdesc, outputdesc, logmethod, command):
+        threading.Thread.__init__(self)
+        self.inputdesc = os.fdopen(inputdesc, "r")
+        self.outputdesc = outputdesc
+        self.logmethod = logmethod
+        self.running = True
+        self.command = command
+
+    def run(self):
+        while self.running:
+            try:
+                data = self.inputdesc.readline()
+            except IOError:
+                self.logmethod("Can't read from pipe during a call to %s. "
+                               "(program terminated suddenly?)" % self.command)
+                break
+            if data == "":
+                self.running = False
+            else:
+                self.logmethod(data.rstrip('\n'))
+                os.write(self.outputdesc, data)
+
+    def stop(self):
+        self.running = False
+        return self
+
+## Run an external program and redirect the output to a file.
+# @param command The command to run.
+# @param argv A list of arguments.
+# @param stdin The file descriptor to read stdin from.
+# @param stdout The file descriptor to redirect stdout to.
+# @param stderr The file descriptor to redirect stderr to.
+# @param root The directory to chroot to before running command.
+# @return The return code of command.
+def execWithRedirect(command, argv, stdin = None, stdout = None,
+                     stderr = None, root = '/'):
+    def chroot ():
+        os.chroot(root)
+
+    stdinclose = stdoutclose = stderrclose = lambda : None
+
+    argv = list(argv)
+    if isinstance(stdin, str):
+        if os.access(stdin, os.R_OK):
+            stdin = os.open(stdin, os.O_RDONLY)
+            stdinclose = lambda : os.close(stdin)
+        else:
+            stdin = sys.stdin.fileno()
+    elif isinstance(stdin, int):
+        pass
+    elif stdin is None or not isinstance(stdin, file):
+        stdin = sys.stdin.fileno()
+
+    if isinstance(stdout, str):
+        stdout = os.open(stdout, os.O_RDWR|os.O_CREAT)
+        stdoutclose = lambda : os.close(stdout)
+    elif isinstance(stdout, int):
+        pass
+    elif stdout is None or not isinstance(stdout, file):
+        stdout = sys.stdout.fileno()
+
+    if isinstance(stderr, str):
+        stderr = os.open(stderr, os.O_RDWR|os.O_CREAT)
+        stderrclose = lambda : os.close(stderr)
+    elif isinstance(stderr, int):
+        pass
+    elif stderr is None or not isinstance(stderr, file):
+        stderr = sys.stderr.fileno()
+
+    program_log.info("Running... %s" % (" ".join([command] + argv),))
+
+    #prepare os pipes for feeding tee proceses
+    pstdout, pstdin = os.pipe()
+    perrout, perrin = os.pipe()
+
+    env = os.environ.copy()
+    env.update({"LC_ALL": "C"})
+
+    try:
+        #prepare tee proceses
+        proc_std = tee(pstdout, stdout, program_log.info, command)
+        proc_err = tee(perrout, stderr, program_log.error, command)
+
+        #start monitoring the outputs
+        proc_std.start()
+        proc_err.start()
+
+        proc = subprocess.Popen([command] + argv, stdin=stdin,
+                                stdout=pstdin,
+                                stderr=perrin,
+                                preexec_fn=chroot, cwd=root,
+                                env=env)
+
+        proc.wait()
+        ret = proc.returncode
+
+        #close the input ends of pipes so we get EOF in the tee processes
+        os.close(pstdin)
+        os.close(perrin)
+
+        #wait for the output to be written and destroy them
+        proc_std.join()
+        del proc_std
+
+        proc_err.join()
+        del proc_err
+
+        stdinclose()
+        stdoutclose()
+        stderrclose()
+    except OSError as e:
+        errstr = "Error running %s: %s" % (command, e.strerror)
+        log.error(errstr)
+        program_log.error(errstr)
+        #close the input ends of pipes so we get EOF in the tee processes
+        os.close(pstdin)
+        os.close(perrin)
+        proc_std.join()
+        proc_err.join()
+
+        stdinclose()
+        stdoutclose()
+        stderrclose()
+        raise RuntimeError, errstr
+
+    return ret
+
+## Run an external program and capture standard out.
+# @param command The command to run.
+# @param argv A list of arguments.
+# @param stdin The file descriptor to read stdin from.
+# @param stderr The file descriptor to redirect stderr to.
+# @param root The directory to chroot to before running command.
+# @return The output of command from stdout.
+def execWithCapture(command, argv, stdin = None, stderr = None, root='/'):
+    def chroot():
+        os.chroot(root)
+
+    def closefds ():
+        stdinclose()
+        stderrclose()
+
+    stdinclose = stderrclose = lambda : None
+    rc = ""
+    argv = list(argv)
+
+    if isinstance(stdin, str):
+        if os.access(stdin, os.R_OK):
+            stdin = os.open(stdin, os.O_RDONLY)
+            stdinclose = lambda : os.close(stdin)
+        else:
+            stdin = sys.stdin.fileno()
+    elif isinstance(stdin, int):
+        pass
+    elif stdin is None or not isinstance(stdin, file):
+        stdin = sys.stdin.fileno()
+
+    if isinstance(stderr, str):
+        stderr = os.open(stderr, os.O_RDWR|os.O_CREAT)
+        stderrclose = lambda : os.close(stderr)
+    elif isinstance(stderr, int):
+        pass
+    elif stderr is None or not isinstance(stderr, file):
+        stderr = sys.stderr.fileno()
+
+    program_log.info("Running... %s" % (" ".join([command] + argv),))
+
+    env = os.environ.copy()
+    env.update({"LC_ALL": "C"})
+
+    try:
+        proc = subprocess.Popen([command] + argv, stdin=stdin,
+                                stdout=subprocess.PIPE,
+                                stderr=subprocess.PIPE,
+                                preexec_fn=chroot, cwd=root,
+                                env=env)
+
+        while True:
+            (outStr, errStr) = proc.communicate()
+            if outStr:
+                map(program_log.info, outStr.splitlines())
+                rc += outStr
+            if errStr:
+                map(program_log.error, errStr.splitlines())
+                os.write(stderr, errStr)
+
+            if proc.returncode is not None:
+                break
+    except OSError as e:
+        log.error ("Error running " + command + ": " + e.strerror)
+        closefds()
+        raise RuntimeError, "Error running " + command + ": " + e.strerror
+
+    closefds()
+    return rc
+
+def execWithCallback(command, argv, stdin = None, stdout = None,
+                     stderr = None, echo = True, callback = None,
+                     callback_data = None, root = '/'):
+    def chroot():
+        os.chroot(root)
+
+    def closefds ():
+        stdinclose()
+        stdoutclose()
+        stderrclose()
+
+    stdinclose = stdoutclose = stderrclose = lambda : None
+
+    argv = list(argv)
+    if isinstance(stdin, str):
+        if os.access(stdin, os.R_OK):
+            stdin = os.open(stdin, os.O_RDONLY)
+            stdinclose = lambda : os.close(stdin)
+        else:
+            stdin = sys.stdin.fileno()
+    elif isinstance(stdin, int):
+        pass
+    elif stdin is None or not isinstance(stdin, file):
+        stdin = sys.stdin.fileno()
+
+    if isinstance(stdout, str):
+        stdout = os.open(stdout, os.O_RDWR|os.O_CREAT)
+        stdoutclose = lambda : os.close(stdout)
+    elif isinstance(stdout, int):
+        pass
+    elif stdout is None or not isinstance(stdout, file):
+        stdout = sys.stdout.fileno()
+
+    if isinstance(stderr, str):
+        stderr = os.open(stderr, os.O_RDWR|os.O_CREAT)
+        stderrclose = lambda : os.close(stderr)
+    elif isinstance(stderr, int):
+        pass
+    elif stderr is None or not isinstance(stderr, file):
+        stderr = sys.stderr.fileno()
+
+    program_log.info("Running... %s" % (" ".join([command] + argv),))
+
+    p = os.pipe()
+    p_stderr = os.pipe()
+    childpid = os.fork()
+    if not childpid:
+        os.close(p[0])
+        os.close(p_stderr[0])
+        os.dup2(p[1], 1)
+        os.dup2(p_stderr[1], 2)
+        os.dup2(stdin, 0)
+        os.close(stdin)
+        os.close(p[1])
+        os.close(p_stderr[1])
+
+        os.execvp(command, [command] + argv)
+        os._exit(1)
+
+    os.close(p[1])
+    os.close(p_stderr[1])
+
+    log_output = ''
+    while 1:
+        try:
+            s = os.read(p[0], 1)
+        except OSError as e:
+            if e.errno != 4:
+                map(program_log.info, log_output.splitlines())
+                raise IOError, e.args
+
+        if echo:
+            os.write(stdout, s)
+        log_output += s
+
+        if callback:
+            callback(s, callback_data=callback_data)
+
+        # break out early if the sub-process changes status.
+        # no need to flush the stream if the process has exited
+        try:
+            (pid, status) = os.waitpid(childpid,os.WNOHANG)
+            if pid != 0:
+                break
+        except OSError as e:
+            log.critical("exception from waitpid: %s %s" %(e.errno, e.strerror))
+
+        if len(s) < 1:
+            break
+
+    map(program_log.info, log_output.splitlines())
+
+    log_errors = ''
+    while 1:
+        try:
+            err = os.read(p_stderr[0], 128)
+        except OSError as e:
+            if e.errno != 4:
+                map(program_log.error, log_errors.splitlines())
+                raise IOError, e.args
+            break
+        log_errors += err
+        if len(err) < 1:
+            break
+
+    os.write(stderr, log_errors)
+    map(program_log.error, log_errors.splitlines())
+    os.close(p[0])
+    os.close(p_stderr[0])
+
+    try:
+        #if we didn't already get our child's exit status above, do so now.
+        if not pid:
+            (pid, status) = os.waitpid(childpid, 0)
+    except OSError as e:
+        log.critical("exception from waitpid: %s %s" %(e.errno, e.strerror))
+
+    closefds()
+
+    rc = 1
+    if os.WIFEXITED(status):
+        rc = os.WEXITSTATUS(status)
+    return ExecProduct(rc, log_output , log_errors)
+
+def _pulseProgressCallback(data, callback_data=None):
+    if callback_data:
+        callback_data.pulse()
+
+def execWithPulseProgress(command, argv, stdin = None, stdout = None,
+                          stderr = None, echo = True, progress = None,
+                          root = '/'):
+    return execWithCallback(command, argv, stdin=stdin, stdout=stdout,
+                     stderr=stderr, echo=echo, callback=_pulseProgressCallback,
+                     callback_data=progress, root=root)
+
+## Run a shell.
+def execConsole():
+    try:
+        proc = subprocess.Popen(["/bin/sh"])
+        proc.wait()
+    except OSError as e:
+        raise RuntimeError, "Error running /bin/sh: " + e.strerror
+
+
-- 
1.7.6.4

_______________________________________________
Anaconda-devel-list mailing list
Anaconda-devel-list@xxxxxxxxxx
https://www.redhat.com/mailman/listinfo/anaconda-devel-list


[Index of Archives]     [Kickstart]     [Fedora Users]     [Fedora Legacy List]     [Fedora Maintainers]     [Fedora Desktop]     [Fedora SELinux]     [Big List of Linux Books]     [Yosemite News]     [Yosemite Photos]     [KDE Users]     [Fedora Tools]
  Powered by Linux