viewvc-4intranet/lib/popen.py

191 lines
5.4 KiB
Python

# -*-python-*-
#
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
#
# By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC
# distribution or at http://viewvc.org/license-1.html.
#
# For more information, visit http://viewvc.org/
#
# -----------------------------------------------------------------------
#
# popen.py: a replacement for os.popen()
#
# This implementation of popen() provides a cmd + args calling sequence,
# rather than a system() type of convention. The shell facilities are not
# available, but that implies we can avoid worrying about shell hacks in
# the arguments.
#
# -----------------------------------------------------------------------
import os
import sys
import sapi
import threading
if sys.platform == "win32":
import win32popen
import win32event
import win32process
import debug
import StringIO
def popen(cmd, args, mode, capture_err=1):
if sys.platform == "win32":
command = win32popen.CommandLine(cmd, args)
if mode.find('r') >= 0:
hStdIn = None
if debug.SHOW_CHILD_PROCESSES:
dbgIn, dbgOut = None, StringIO.StringIO()
handle, hStdOut = win32popen.MakeSpyPipe(0, 1, (dbgOut,))
if capture_err:
hStdErr = hStdOut
dbgErr = dbgOut
else:
dbgErr = StringIO.StringIO()
x, hStdErr = win32popen.MakeSpyPipe(None, 1, (dbgErr,))
else:
handle, hStdOut = win32popen.CreatePipe(0, 1)
if capture_err:
hStdErr = hStdOut
else:
hStdErr = win32popen.NullFile(1)
else:
if debug.SHOW_CHILD_PROCESSES:
dbgIn, dbgOut, dbgErr = StringIO.StringIO(), StringIO.StringIO(), StringIO.StringIO()
hStdIn, handle = win32popen.MakeSpyPipe(1, 0, (dbgIn,))
x, hStdOut = win32popen.MakeSpyPipe(None, 1, (dbgOut,))
x, hStdErr = win32popen.MakeSpyPipe(None, 1, (dbgErr,))
else:
hStdIn, handle = win32popen.CreatePipe(0, 1)
hStdOut = None
hStdErr = None
phandle, pid, thandle, tid = win32popen.CreateProcess(command, hStdIn, hStdOut, hStdErr)
if debug.SHOW_CHILD_PROCESSES:
debug.Process(command, dbgIn, dbgOut, dbgErr)
return _pipe(win32popen.File2FileObject(handle, mode), phandle)
# flush the stdio buffers since we are about to change the FD under them
sys.stdout.flush()
sys.stderr.flush()
r, w = os.pipe()
pid = os.fork()
if pid:
# in the parent
# close the descriptor that we don't need and return the other one.
if mode.find('r') >= 0:
os.close(w)
return _pipe(os.fdopen(r, mode), pid)
os.close(r)
return _pipe(os.fdopen(w, mode), pid)
# in the child
# we'll need /dev/null for the discarded I/O
null = os.open('/dev/null', os.O_RDWR)
if mode.find('r') >= 0:
# hook stdout/stderr to the "write" channel
os.dup2(w, 1)
# "close" stdin; the child shouldn't use it
### this isn't quite right... we may want the child to read from stdin
os.dup2(null, 0)
# what to do with errors?
if capture_err:
os.dup2(w, 2)
else:
os.dup2(null, 2)
else:
# hook stdin to the "read" channel
os.dup2(r, 0)
# "close" stdout/stderr; the child shouldn't use them
### this isn't quite right... we may want the child to write to these
os.dup2(null, 1)
os.dup2(null, 2)
# don't need these FDs any more
os.close(null)
os.close(r)
os.close(w)
# the stdin/stdout/stderr are all set up. exec the target
try:
os.execvp(cmd, (cmd,) + tuple(args))
except:
# aid debugging, if the os.execvp above fails for some reason:
print "<h2>exec failed:</h2><pre>", cmd, ' '.join(args), "</pre>"
raise
# crap. shouldn't be here.
sys.exit(127)
class _pipe:
"Wrapper for a file which can wait() on a child process at close time."
def __init__(self, file, child_pid, done_event = None, thread = None):
self.file = file
self.child_pid = child_pid
if sys.platform == "win32":
if done_event:
self.wait_for = (child_pid, done_event)
else:
self.wait_for = (child_pid,)
else:
self.thread = thread
def eof(self):
### should be calling file.eof() here instead of file.close(), there
### may be data in the pipe or buffer after the process exits
if sys.platform == "win32":
r = win32event.WaitForMultipleObjects(self.wait_for, 1, 0)
if r == win32event.WAIT_OBJECT_0:
self.file.close()
self.file = None
return win32process.GetExitCodeProcess(self.child_pid)
return None
if self.thread and self.thread.isAlive():
return None
pid, status = os.waitpid(self.child_pid, os.WNOHANG)
if pid:
self.file.close()
self.file = None
return status
return None
def close(self):
if self.file:
self.file.close()
self.file = None
if sys.platform == "win32":
win32event.WaitForMultipleObjects(self.wait_for, 1, win32event.INFINITE)
return win32process.GetExitCodeProcess(self.child_pid)
else:
if self.thread:
self.thread.join()
if type(self.child_pid) == type([]):
for pid in self.child_pid:
exit = os.waitpid(pid, 0)[1]
return exit
else:
return os.waitpid(self.child_pid, 0)[1]
return None
def __getattr__(self, name):
return getattr(self.file, name)
def __del__(self):
self.close()