summaryrefslogtreecommitdiffstats
path: root/Sshuttle VPN.app/Contents/Resources/sshuttle
diff options
context:
space:
mode:
Diffstat (limited to 'Sshuttle VPN.app/Contents/Resources/sshuttle')
-rw-r--r--Sshuttle VPN.app/Contents/Resources/sshuttle/assembler.py26
-rw-r--r--Sshuttle VPN.app/Contents/Resources/sshuttle/client.py356
-rw-r--r--Sshuttle VPN.app/Contents/Resources/sshuttle/compat/__init__.py0
-rw-r--r--Sshuttle VPN.app/Contents/Resources/sshuttle/compat/ssubprocess.py1305
-rw-r--r--Sshuttle VPN.app/Contents/Resources/sshuttle/firewall.py304
-rw-r--r--Sshuttle VPN.app/Contents/Resources/sshuttle/helpers.py37
-rw-r--r--Sshuttle VPN.app/Contents/Resources/sshuttle/hostwatch.py277
-rwxr-xr-xSshuttle VPN.app/Contents/Resources/sshuttle/main.py122
-rw-r--r--Sshuttle VPN.app/Contents/Resources/sshuttle/options.py201
-rw-r--r--Sshuttle VPN.app/Contents/Resources/sshuttle/server.py176
-rw-r--r--Sshuttle VPN.app/Contents/Resources/sshuttle/ssh.py95
-rwxr-xr-xSshuttle VPN.app/Contents/Resources/sshuttle/sshuttle122
-rw-r--r--Sshuttle VPN.app/Contents/Resources/sshuttle/ssnet.py520
-rw-r--r--Sshuttle VPN.app/Contents/Resources/sshuttle/ssyslog.py16
14 files changed, 3557 insertions, 0 deletions
diff --git a/Sshuttle VPN.app/Contents/Resources/sshuttle/assembler.py b/Sshuttle VPN.app/Contents/Resources/sshuttle/assembler.py
new file mode 100644
index 0000000..c478e37
--- /dev/null
+++ b/Sshuttle VPN.app/Contents/Resources/sshuttle/assembler.py
@@ -0,0 +1,26 @@
+import sys, zlib
+
+z = zlib.decompressobj()
+mainmod = sys.modules[__name__]
+while 1:
+ name = sys.stdin.readline().strip()
+ if name:
+ nbytes = int(sys.stdin.readline())
+ if verbosity >= 2:
+ sys.stderr.write('server: assembling %r (%d bytes)\n'
+ % (name, nbytes))
+ content = z.decompress(sys.stdin.read(nbytes))
+ exec compile(content, name, "exec")
+
+ # FIXME: this crushes everything into a single module namespace,
+ # then makes each of the module names point at this one. Gross.
+ assert(name.endswith('.py'))
+ modname = name[:-3]
+ mainmod.__dict__[modname] = mainmod
+ else:
+ break
+
+verbose = verbosity
+sys.stderr.flush()
+sys.stdout.flush()
+main()
diff --git a/Sshuttle VPN.app/Contents/Resources/sshuttle/client.py b/Sshuttle VPN.app/Contents/Resources/sshuttle/client.py
new file mode 100644
index 0000000..dbd11de
--- /dev/null
+++ b/Sshuttle VPN.app/Contents/Resources/sshuttle/client.py
@@ -0,0 +1,356 @@
+import struct, socket, select, errno, re, signal
+import compat.ssubprocess as ssubprocess
+import helpers, ssnet, ssh, ssyslog
+from ssnet import SockWrapper, Handler, Proxy, Mux, MuxWrapper
+from helpers import *
+
+_extra_fd = os.open('/dev/null', os.O_RDONLY)
+
+def _islocal(ip):
+ sock = socket.socket()
+ try:
+ try:
+ sock.bind((ip, 0))
+ except socket.error, e:
+ if e.args[0] == errno.EADDRNOTAVAIL:
+ return False # not a local IP
+ else:
+ raise
+ finally:
+ sock.close()
+ return True # it's a local IP, or there would have been an error
+
+
+def got_signal(signum, frame):
+ log('exiting on signal %d\n' % signum)
+ sys.exit(1)
+
+
+_pidname = None
+def check_daemon(pidfile):
+ global _pidname
+ _pidname = os.path.abspath(pidfile)
+ try:
+ oldpid = open(_pidname).read(1024)
+ except IOError, e:
+ if e.errno == errno.ENOENT:
+ return # no pidfile, ok
+ else:
+ raise Fatal("can't read %s: %s" % (_pidname, e))
+ if not oldpid:
+ os.unlink(_pidname)
+ return # invalid pidfile, ok
+ oldpid = int(oldpid.strip() or 0)
+ if oldpid <= 0:
+ os.unlink(_pidname)
+ return # invalid pidfile, ok
+ try:
+ os.kill(oldpid, 0)
+ except OSError, e:
+ if e.errno == errno.ESRCH:
+ os.unlink(_pidname)
+ return # outdated pidfile, ok
+ elif e.errno == errno.EPERM:
+ pass
+ else:
+ raise
+ raise Fatal("%s: sshuttle is already running (pid=%d)"
+ % (_pidname, oldpid))
+
+
+def daemonize():
+ if os.fork():
+ os._exit(0)
+ os.setsid()
+ if os.fork():
+ os._exit(0)
+
+ outfd = os.open(_pidname, os.O_WRONLY|os.O_CREAT|os.O_EXCL, 0666)
+ try:
+ os.write(outfd, '%d\n' % os.getpid())
+ finally:
+ os.close(outfd)
+ os.chdir("/")
+
+ # Normal exit when killed, or try/finally won't work and the pidfile won't
+ # be deleted.
+ signal.signal(signal.SIGTERM, got_signal)
+
+ si = open('/dev/null', 'r+')
+ os.dup2(si.fileno(), 0)
+ os.dup2(si.fileno(), 1)
+ si.close()
+
+ ssyslog.stderr_to_syslog()
+
+
+def daemon_cleanup():
+ try:
+ os.unlink(_pidname)
+ except OSError, e:
+ if e.errno == errno.ENOENT:
+ pass
+ else:
+ raise
+
+
+def original_dst(sock):
+ try:
+ SO_ORIGINAL_DST = 80
+ SOCKADDR_MIN = 16
+ sockaddr_in = sock.getsockopt(socket.SOL_IP,
+ SO_ORIGINAL_DST, SOCKADDR_MIN)
+ (proto, port, a,b,c,d) = struct.unpack('!HHBBBB', sockaddr_in[:8])
+ assert(socket.htons(proto) == socket.AF_INET)
+ ip = '%d.%d.%d.%d' % (a,b,c,d)
+ return (ip,port)
+ except socket.error, e:
+ if e.args[0] == errno.ENOPROTOOPT:
+ return sock.getsockname()
+ raise
+
+
+class FirewallClient:
+ def __init__(self, port, subnets_include, subnets_exclude):
+ self.port = port
+ self.auto_nets = []
+ self.subnets_include = subnets_include
+ self.subnets_exclude = subnets_exclude
+ argvbase = ([sys.argv[0]] +
+ ['-v'] * (helpers.verbose or 0) +
+ ['--firewall', str(port)])
+ if ssyslog._p:
+ argvbase += ['--syslog']
+ argv_tries = [
+ ['sudo', '-p', '[local sudo] Password: '] + argvbase,
+ ['su', '-c', ' '.join(argvbase)],
+ argvbase
+ ]
+
+ # we can't use stdin/stdout=subprocess.PIPE here, as we normally would,
+ # because stupid Linux 'su' requires that stdin be attached to a tty.
+ # Instead, attach a *bidirectional* socket to its stdout, and use
+ # that for talking in both directions.
+ (s1,s2) = socket.socketpair()
+ def setup():
+ # run in the child process
+ s2.close()
+ e = None
+ if os.getuid() == 0:
+ argv_tries = argv_tries[-1:] # last entry only
+ for argv in argv_tries:
+ try:
+ if argv[0] == 'su':
+ sys.stderr.write('[local su] ')
+ self.p = ssubprocess.Popen(argv, stdout=s1, preexec_fn=setup)
+ e = None
+ break
+ except OSError, e:
+ pass
+ self.argv = argv
+ s1.close()
+ self.pfile = s2.makefile('wb+')
+ if e:
+ log('Spawning firewall manager: %r\n' % self.argv)
+ raise Fatal(e)
+ line = self.pfile.readline()
+ self.check()
+ if line != 'READY\n':
+ raise Fatal('%r expected READY, got %r' % (self.argv, line))
+
+ def check(self):
+ rv = self.p.poll()
+ if rv:
+ raise Fatal('%r returned %d' % (self.argv, rv))
+
+ def start(self):
+ self.pfile.write('ROUTES\n')
+ for (ip,width) in self.subnets_include+self.auto_nets:
+ self.pfile.write('%d,0,%s\n' % (width, ip))
+ for (ip,width) in self.subnets_exclude:
+ self.pfile.write('%d,1,%s\n' % (width, ip))
+ self.pfile.write('GO\n')
+ self.pfile.flush()
+ line = self.pfile.readline()
+ self.check()
+ if line != 'STARTED\n':
+ raise Fatal('%r expected STARTED, got %r' % (self.argv, line))
+
+ def sethostip(self, hostname, ip):
+ assert(not re.search(r'[^-\w]', hostname))
+ assert(not re.search(r'[^0-9.]', ip))
+ self.pfile.write('HOST %s,%s\n' % (hostname, ip))
+ self.pfile.flush()
+
+ def done(self):
+ self.pfile.close()
+ rv = self.p.wait()
+ if rv:
+ raise Fatal('cleanup: %r returned %d' % (self.argv, rv))
+
+
+def _main(listener, fw, ssh_cmd, remotename, python, seed_hosts, auto_nets,
+ syslog, daemon):
+ handlers = []
+ if helpers.verbose >= 1:
+ helpers.logprefix = 'c : '
+ else:
+ helpers.logprefix = 'client: '
+ debug1('connecting to server...\n')
+
+ try:
+ (serverproc, serversock) = ssh.connect(ssh_cmd, remotename, python,
+ stderr=ssyslog._p and ssyslog._p.stdin)
+ except socket.error, e:
+ if e.args[0] == errno.EPIPE:
+ raise Fatal("failed to establish ssh session (1)")
+ else:
+ raise
+ mux = Mux(serversock, serversock)
+ handlers.append(mux)
+
+ expected = 'SSHUTTLE0001'
+ try:
+ initstring = serversock.recv(len(expected))
+ except socket.error, e:
+ if e.args[0] == errno.ECONNRESET:
+ raise Fatal("failed to establish ssh session (2)")
+ else:
+ raise
+
+ rv = serverproc.poll()
+ if rv:
+ raise Fatal('server died with error code %d' % rv)
+
+ if initstring != expected:
+ raise Fatal('expected server init string %r; got %r'
+ % (expected, initstring))
+ debug1('connected.\n')
+ print 'Connected.'
+ sys.stdout.flush()
+ if daemon:
+ daemonize()
+ log('daemonizing (%s).\n' % _pidname)
+ elif syslog:
+ debug1('switching to syslog.\n')
+ ssyslog.stderr_to_syslog()
+
+ def onroutes(routestr):
+ if auto_nets:
+ for line in routestr.strip().split('\n'):
+ (ip,width) = line.split(',', 1)
+ fw.auto_nets.append((ip,int(width)))
+
+ # we definitely want to do this *after* starting ssh, or we might end
+ # up intercepting the ssh connection!
+ #
+ # Moreover, now that we have the --auto-nets option, we have to wait
+ # for the server to send us that message anyway. Even if we haven't
+ # set --auto-nets, we might as well wait for the message first, then
+ # ignore its contents.
+ mux.got_routes = None
+ fw.start()
+ mux.got_routes = onroutes
+
+ def onhostlist(hostlist):
+ debug2('got host list: %r\n' % hostlist)
+ for line in hostlist.strip().split():
+ if line:
+ name,ip = line.split(',', 1)
+ fw.sethostip(name, ip)
+ mux.got_host_list = onhostlist
+
+ def onaccept():
+ global _extra_fd
+ try:
+ sock,srcip = listener.accept()
+ except socket.error, e:
+ if e.args[0] in [errno.EMFILE, errno.ENFILE]:
+ debug1('Rejected incoming connection: too many open files!\n')
+ # free up an fd so we can eat the connection
+ os.close(_extra_fd)
+ try:
+ sock,srcip = listener.accept()
+ sock.close()
+ finally:
+ _extra_fd = os.open('/dev/null', os.O_RDONLY)
+ return
+ else:
+ raise
+ dstip = original_dst(sock)
+ debug1('Accept: %s:%r -> %s:%r.\n' % (srcip[0],srcip[1],
+ dstip[0],dstip[1]))
+ if dstip[1] == listener.getsockname()[1] and _islocal(dstip[0]):
+ debug1("-- ignored: that's my address!\n")
+ sock.close()
+ return
+ chan = mux.next_channel()
+ mux.send(chan, ssnet.CMD_CONNECT, '%s,%s' % dstip)
+ outwrap = MuxWrapper(mux, chan)
+ handlers.append(Proxy(SockWrapper(sock, sock), outwrap))
+ handlers.append(Handler([listener], onaccept))
+
+ if seed_hosts != None:
+ debug1('seed_hosts: %r\n' % seed_hosts)
+ mux.send(0, ssnet.CMD_HOST_REQ, '\n'.join(seed_hosts))
+
+ while 1:
+ rv = serverproc.poll()
+ if rv:
+ raise Fatal('server died with error code %d' % rv)
+
+ ssnet.runonce(handlers, mux)
+ mux.callback()
+ mux.check_fullness()
+
+
+def main(listenip, ssh_cmd, remotename, python, seed_hosts, auto_nets,
+ subnets_include, subnets_exclude, syslog, daemon, pidfile):
+ if syslog:
+ ssyslog.start_syslog()
+ if daemon:
+ try:
+ check_daemon(pidfile)
+ except Fatal, e:
+ log("%s\n" % e)
+ return 5
+ debug1('Starting sshuttle proxy.\n')
+ listener = socket.socket()
+ listener.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+ if listenip[1]:
+ ports = [listenip[1]]
+ else:
+ ports = xrange(12300,9000,-1)
+ last_e = None
+ bound = False
+ debug2('Binding:')
+ for port in ports:
+ debug2(' %d' % port)
+ try:
+ listener.bind((listenip[0], port))
+ bound = True
+ break
+ except socket.error, e:
+ last_e = e
+ debug2('\n')
+ if not bound:
+ assert(last_e)
+ raise last_e
+ listener.listen(10)
+ listenip = listener.getsockname()
+ debug1('Listening on %r.\n' % (listenip,))
+
+ fw = FirewallClient(listenip[1], subnets_include, subnets_exclude)
+
+ try:
+ return _main(listener, fw, ssh_cmd, remotename,
+ python, seed_hosts, auto_nets, syslog, daemon)
+ finally:
+ try:
+ if daemon:
+ # it's not our child anymore; can't waitpid
+ fw.p.returncode = 0
+ fw.done()
+ finally:
+ if daemon:
+ daemon_cleanup()
diff --git a/Sshuttle VPN.app/Contents/Resources/sshuttle/compat/__init__.py b/Sshuttle VPN.app/Contents/Resources/sshuttle/compat/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Sshuttle VPN.app/Contents/Resources/sshuttle/compat/__init__.py
diff --git a/Sshuttle VPN.app/Contents/Resources/sshuttle/compat/ssubprocess.py b/Sshuttle VPN.app/Contents/Resources/sshuttle/compat/ssubprocess.py
new file mode 100644
index 0000000..ee6b8da
--- /dev/null
+++ b/Sshuttle VPN.app/Contents/Resources/sshuttle/compat/ssubprocess.py
@@ -0,0 +1,1305 @@
+# subprocess - Subprocesses with accessible I/O streams
+#
+# For more information about this module, see PEP 324.
+#
+# This module should remain compatible with Python 2.2, see PEP 291.
+#
+# Copyright (c) 2003-2005 by Peter Astrand <astrand@lysator.liu.se>
+#
+# Licensed to PSF under a Contributor Agreement.
+# See http://www.python.org/2.4/license for licensing details.
+
+r"""subprocess - Subprocesses with accessible I/O streams
+
+This module allows you to spawn processes, connect to their
+input/output/error pipes, and obtain their return codes. This module
+intends to replace several other, older modules and functions, like:
+
+os.system
+os.spawn*
+os.popen*
+popen2.*
+commands.*
+
+Information about how the subprocess module can be used to replace these
+modules and functions can be found below.
+
+
+
+Using the subprocess module
+===========================
+This module defines one class called Popen:
+
+class Popen(args, bufsize=0, executable=None,
+ stdin=None, stdout=None, stderr=None,
+ preexec_fn=None, close_fds=False, shell=False,
+ cwd=None, env=None, universal_newlines=False,
+ startupinfo=None, creationflags=0):
+
+
+Arguments are:
+
+args should be a string, or a sequence of program arguments. The
+program to execute is normally the first item in the args sequence or
+string, but can be explicitly set by using the executable argument.
+
+On UNIX, with shell=False (default): In this case, the Popen class
+uses os.execvp() to execute the child program. args should normally
+be a sequence. A string will be treated as a sequence with the string
+as the only item (the program to execute).
+
+On UNIX, with shell=True: If args is a string, it specifies the
+command string to execute through the shell. If args is a sequence,
+the first item specifies the command string, and any additional items
+will be treated as additional shell arguments.
+
+On Windows: the Popen class uses CreateProcess() to execute the child
+program, which operates on strings. If args is a sequence, it will be
+converted to a string using the list2cmdline method. Please note that
+not all MS Windows applications interpret the command line the same
+way: The list2cmdline is designed for applications using the same
+rules as the MS C runtime.
+
+bufsize, if given, has the same meaning as the corresponding argument
+to the built-in open() function: 0 means unbuffered, 1 means line
+buffered, any other positive value means use a buffer of
+(approximately) that size. A negative bufsize means to use the system
+default, which usually means fully buffered. The default value for
+bufsize is 0 (unbuffered).
+
+stdin, stdout and stderr specify the executed programs' standard
+input, standard output and standard error file handles, respectively.
+Valid values are PIPE, an existing file descriptor (a positive
+integer), an existing file object, and None. PIPE indicates that a
+new pipe to the child should be created. With None, no redirection
+will occur; the child's file handles will be inherited from the
+parent. Additionally, stderr can be STDOUT, which indicates that the
+stderr data from the applications should be captured into the same
+file handle as for stdout.
+
+If preexec_fn is set to a callable object, this object will be called
+in the child process just before the child is executed.
+
+If close_fds is true, all file descriptors except 0, 1 and 2 will be
+closed before the child process is executed.
+
+if shell is true, the specified command will be executed through the
+shell.
+
+If cwd is not None, the current directory will be changed to cwd
+before the child is executed.
+
+If env is not None, it defines the environment variables for the new
+process.
+
+If universal_newlines is true, the file objects stdout and stderr are
+opened as a text files, but lines may be terminated by any of '\n',
+the Unix end-of-line convention, '\r', the Macintosh convention or
+'\r\n', the Windows convention. All of these external representations
+are seen as '\n' by the Python program. Note: This feature is only
+available if Python is built with universal newline support (the
+default). Also, the newlines attribute of the file objects stdout,
+stdin and stderr are not updated by the communicate() method.
+
+The startupinfo and creationflags, if given, will be passed to the
+underlying CreateProcess() function. They can specify things such as
+appearance of the main window and priority for the new process.
+(Windows only)
+
+
+This module also defines two shortcut functions:
+
+call(*popenargs, **kwargs):
+ Run command with arguments. Wait for command to complete, then
+ return the returncode attribute.
+
+ The arguments are the same as for the Popen constructor. Example:
+
+ retcode = call(["ls", "-l"])
+
+check_call(*popenargs, **kwargs):
+ Run command with arguments. Wait for command to complete. If the
+ exit code was zero then return, otherwise raise
+ CalledProcessError. The CalledProcessError object will have the
+ return code in the returncode attribute.
+
+ The arguments are the same as for the Popen constructor. Example:
+
+ check_call(["ls", "-l"])
+
+Exceptions
+----------
+Exceptions raised in the child process, before the new program has
+started to execute, will be re-raised in the parent. Additionally,
+the exception object will have one extra attribute called
+'child_traceback', which is a string containing traceback information
+from the childs point of view.
+
+The most common exception raised is OSError. This occurs, for
+example, when trying to execute a non-existent file. Applications
+should prepare for OSErrors.
+
+A ValueError will be raised if Popen is called with invalid arguments.
+
+check_call() will raise CalledProcessError, if the called process
+returns a non-zero return code.
+
+
+Security
+--------
+Unlike some other popen functions, this implementation will never call
+/bin/sh implicitly. This means that all characters, including shell
+metacharacters, can safely be passed to child processes.
+
+
+Popen objects
+=============
+Instances of the Popen class have the following methods:
+
+poll()
+ Check if child process has terminated. Returns returncode
+ attribute.
+
+wait()
+ Wait for child process to terminate. Returns returncode attribute.
+
+communicate(input=None)
+ Interact with process: Send data to stdin. Read data from stdout
+ and stderr, until end-of-file is reached. Wait for process to
+ terminate. The optional input argument should be a string to be
+ sent to the child process, or None, if no data should be sent to
+ the child.
+
+ communicate() returns a tuple (stdout, stderr).
+
+ Note: The data read is buffered in memory, so do not use this
+ method if the data size is large or unlimited.
+
+The following attributes are also available:
+
+stdin
+ If the stdin argument is PIPE, this attribute is a file object
+ that provides input to the child process. Otherwise, it is None.
+
+stdout
+ If the stdout argument is PIPE, this attribute is a file object
+ that provides output from the child process. Otherwise, it is
+ None.
+
+stderr
+ If the stderr argument is PIPE, this attribute is file object that
+ provides error output from the child process. Otherwise, it is
+ None.
+
+pid
+ The process ID of the child process.
+
+returncode
+ The child return code. A None value indicates that the process
+ hasn't terminated yet. A negative value -N indicates that the
+ child was terminated by signal N (UNIX only).
+
+
+Replacing older functions with the subprocess module
+====================================================
+In this section, "a ==> b" means that b can be used as a replacement
+for a.
+
+Note: All functions in this section fail (more or less) silently if
+the executed program cannot be found; this module raises an OSError
+exception.
+
+In the following examples, we assume that the subprocess module is
+imported with "from subprocess import *".
+
+
+Replacing /bin/sh shell backquote
+---------------------------------
+output=`mycmd myarg`
+==>
+output = Popen(["mycmd", "myarg"], stdout=PIPE).communicate()[0]
+
+
+Replacing shell pipe line
+-------------------------
+output=`dmesg | grep hda`
+==>
+p1 = Popen(["dmesg"], stdout=PIPE)
+p2 = Popen(["grep", "hda"], stdin=p1.stdout, stdout=PIPE)
+output = p2.communicate()[0]
+
+
+Replacing os.system()
+---------------------
+sts = os.system("mycmd" + " myarg")
+==>
+p = Popen("mycmd" + " myarg", shell=True)
+pid, sts = os.waitpid(p.pid, 0)
+
+Note:
+
+* Calling the program through the shell is usually not required.
+
+* It's easier to look at the returncode attribute than the
+ exitstatus.
+
+A more real-world example would look like this:
+
+try:
+ retcode = call("mycmd" + " myarg", shell=True)
+ if retcode < 0:
+ print >>sys.stderr, "Child was terminated by signal", -retcode
+ else:
+ print >>sys.stderr, "Child returned", retcode
+except OSError, e:
+ print >>sys.stderr, "Execution failed:", e
+
+
+Replacing os.spawn*
+-------------------
+P_NOWAIT example:
+
+pid = os.spawnlp(os.P_NOWAIT, "/bin/mycmd", "mycmd", "myarg")
+==>
+pid = Popen(["/bin/mycmd", "myarg"]).pid
+
+
+P_WAIT example:
+
+retcode = os.spawnlp(os.P_WAIT, "/bin/mycmd", "mycmd", "myarg")
+==>
+retcode = call(["/bin/mycmd", "myarg"])
+
+
+Vector example:
+
+os.spawnvp(os.P_NOWAIT, path, args)
+==>
+Popen([path] + args[1:])
+
+
+Environment example:
+
+os.spawnlpe(os.P_NOWAIT, "/bin/mycmd", "mycmd", "myarg", env)
+==>
+Popen(["/bin/mycmd", "myarg"], env={"PATH": "/usr/bin"})
+
+
+Replacing os.popen*
+-------------------
+pipe = os.popen(cmd, mode='r', bufsize)
+==>
+pipe = Popen(cmd, shell=True, bufsize=bufsize, stdout=PIPE).stdout
+
+pipe = os.popen(cmd, mode='w', bufsize)
+==>
+pipe = Popen(cmd, shell=True, bufsize=bufsize, stdin=PIPE).stdin
+
+
+(child_stdin, child_stdout) = os.popen2(cmd, mode, bufsize)
+==>
+p = Popen(cmd, shell=True, bufsize=bufsize,
+ stdin=PIPE, stdout=PIPE, close_fds=True)
+(child_stdin, child_stdout) = (p.stdin, p.stdout)
+
+
+(child_stdin,
+ child_stdout,
+ child_stderr) = os.popen3(cmd, mode, bufsize)
+==>
+p = Popen(cmd, shell=True, bufsize=bufsize,
+ stdin=PIPE, stdout=PIPE, stderr=PIPE, close_fds=True)
+(child_stdin,
+ child_stdout,
+ child_stderr) = (p.stdin, p.stdout, p.stderr)
+
+
+(child_stdin, child_stdout_and_stderr) = os.popen4(cmd, mode, bufsize)
+==>
+p = Popen(cmd, shell=True, bufsize=bufsize,
+ stdin=PIPE, stdout=PIPE, stderr=STDOUT, close_fds=True)
+(child_stdin, child_stdout_and_stderr) = (p.stdin, p.stdout)
+
+
+Replacing popen2.*
+------------------
+Note: If the cmd argument to popen2 functions is a string, the command
+is executed through /bin/sh. If it is a list, the command is directly
+executed.
+
+(child_stdout, child_stdin) = popen2.popen2("somestring", bufsize, mode)
+==>
+p = Popen(["somestring"], shell=True, bufsize=bufsize
+ stdin=PIPE, stdout=PIPE, close_fds=True)
+(child_stdout, child_stdin) = (p.stdout, p.stdin)
+
+
+(child_stdout, child_stdin) = popen2.popen2(["mycmd", "myarg"], bufsize, mode)
+==>
+p = Popen(["mycmd", "myarg"], bufsize=bufsize,
+ stdin=PIPE, stdout=PIPE, close_fds=True)
+(child_stdout, child_stdin) = (p.stdout, p.stdin)
+
+The popen2.Popen3 and popen2.Popen4 basically works as subprocess.Popen,
+except that:
+
+* subprocess.Popen raises an exception if the execution fails
+* the capturestderr argument is replaced with the stderr argument.
+* stdin=PIPE and stdout=PIPE must be specified.
+* popen2 closes all filedescriptors by default, but you have to specify
+ close_fds=True with subprocess.Popen.
+"""
+
+import sys
+mswindows = (sys.platform == "win32")
+
+import os
+import types
+import traceback
+import gc
+import signal
+
+# Exception classes used by this module.
+class CalledProcessError(Exception):
+ """This exception is raised when a process run by check_call() returns
+ a non-zero exit status. The exit status will be stored in the
+ returncode attribute."""
+ def __init__(self, returncode, cmd):
+ self.returncode = returncode
+ self.cmd = cmd
+ def __str__(self):
+ return "Command '%s' returned non-zero exit status %d" % (self.cmd, self.returncode)
+
+
+if mswindows:
+ import threading
+ import msvcrt
+ if 0: # <-- change this to use pywin32 instead of the _subprocess driver
+ import pywintypes
+ from win32api import GetStdHandle, STD_INPUT_HANDLE, \
+ STD_OUTPUT_HANDLE, STD_ERROR_HANDLE
+ from win32api import GetCurrentProcess, DuplicateHandle, \
+ GetModuleFileName, GetVersion
+ from win32con import DUPLICATE_SAME_ACCESS, SW_HIDE
+ from win32pipe import CreatePipe
+ from win32process import CreateProcess, STARTUPINFO, \
+ GetExitCodeProcess, STARTF_USESTDHANDLES, \
+ STARTF_USESHOWWINDOW, CREATE_NEW_CONSOLE
+ from win32process import TerminateProcess
+ from win32event import WaitForSingleObject, INFINITE, WAIT_OBJECT_0
+ else:
+ from _subprocess import *
+ class STARTUPINFO:
+ dwFlags = 0
+ hStdInput = None
+ hStdOutput = None
+ hStdError = None
+ wShowWindow = 0
+ class pywintypes:
+ error = IOError
+else:
+ import select
+ import errno
+ import fcntl
+ import pickle
+
+__all__ = ["Popen", "PIPE", "STDOUT", "call", "check_call", "CalledProcessError"]
+
+try:
+ MAXFD = os.sysconf("SC_OPEN_MAX")
+except:
+ MAXFD = 256
+
+# True/False does not exist on 2.2.0
+#try:
+# False
+#except NameError:
+# False = 0
+# True = 1
+
+_active = []
+
+def _cleanup():
+ for inst in _active[:]:
+ if inst._internal_poll(_deadstate=sys.maxint) >= 0:
+ try:
+ _active.remove(inst)
+ except ValueError:
+ # This can happen if two threads create a new Popen instance.
+ # It's harmless that it was already removed, so ignore.
+ pass
+
+PIPE = -1
+STDOUT = -2
+
+
+def call(*popenargs, **kwargs):
+ """Run command with arguments. Wait for command to complete, then
+ return the returncode attribute.
+
+ The arguments are the same as for the Popen constructor. Example:
+
+ retcode = call(["ls", "-l"])
+ """
+ return Popen(*popenargs, **kwargs).wait()
+
+
+def check_call(*popenargs, **kwargs):
+ """Run command with arguments. Wait for command to complete. If
+ the exit code was zero then return, otherwise raise
+ CalledProcessError. The CalledProcessError object will have the
+ return code in the returncode attribute.
+
+ The arguments are the same as for the Popen constructor. Example:
+
+ check_call(["ls", "-l"])
+ """
+ retcode = call(*popenargs, **kwargs)
+ cmd = kwargs.get("args")
+ if cmd is None:
+ cmd = popenargs[0]
+ if retcode:
+ raise CalledProcessError(retcode, cmd)
+ return retcode
+
+
+def list2cmdline(seq):
+ """
+ Translate a sequence of arguments into a command line
+ string, using the same rules as the MS C runtime:
+
+ 1) Arguments are delimited by white space, which is either a
+ space or a tab.
+
+ 2) A string surrounded by double quotation marks is
+ interpreted as a single argument, regardless of white space
+ or pipe characters contained within. A quoted string can be
+ embedded in an argument.
+
+ 3) A double quotation mark preceded by a backslash is
+ interpreted as a literal double quotation mark.
+
+ 4) Backslashes are interpreted literally, unless they
+ immediately precede a double quotation mark.
+
+ 5) If backslashes immediately precede a double quotation mark,
+ every pair of backslashes is interpreted as a literal
+ backslash. If the number of backslashes is odd, the last
+ backslash escapes the next double quotation mark as
+ described in rule 3.
+ """
+
+ # See
+ # http://msdn.microsoft.com/library/en-us/vccelng/htm/progs_12.asp
+ result = []
+ needquote = False
+ for arg in seq:
+ bs_buf = []
+
+ # Add a space to separate this argument from the others
+ if result:
+ result.append(' ')
+
+ needquote = (" " in arg) or ("\t" in arg) or ("|" in arg) or not arg
+ if needquote:
+ result.append('"')
+
+ for c in arg:
+ if c == '\\':
+ # Don't know if we need to double yet.
+ bs_buf.append(c)
+ elif c == '"':
+ # Double backslashes.
+ result.append('\\' * len(bs_buf)*2)
+ bs_buf = []
+ result.append('\\"')
+ else:
+ # Normal char
+ if bs_buf:
+ result.extend(bs_buf)
+ bs_buf = []
+ result.append(c)
+
+ # Add remaining backslashes, if any.
+ if bs_buf:
+ result.extend(bs_buf)
+
+ if needquote:
+ result.extend(bs_buf)
+ result.append('"')
+
+ return ''.join(result)
+
+
+def _closerange(start, max):
+ try:
+ os.closerange(start, max)
+ except AttributeError:
+ for i in xrange(start, max):
+ try:
+ os.close(i)
+ except:
+ pass
+
+
+class Popen(object):
+ def __init__(self, args, bufsize=0, executable=None,
+ stdin=None, stdout=None, stderr=None,
+ preexec_fn=None, close_fds=False, shell=False,
+ cwd=None, env=None, universal_newlines=False,
+ startupinfo=None, creationflags=0):
+ """Create new Popen instance."""
+ _cleanup()
+
+ self._child_created = False
+ if not isinstance(bufsize, (int, long)):
+ raise TypeError("bufsize must be an integer")
+
+ if mswindows:
+ if preexec_fn is not None:
+ raise ValueError("preexec_fn is not supported on Windows "
+