Compare commits

...

14 Commits

Author SHA1 Message Date
973d5a95a1 man page update for daemonization options. 2011-01-01 00:32:37 -08:00
95ab6e7119 ssyslog.py: use daemon.notice instead of daemon.info
...MacOS X seems to default (in /etc/syslog.conf) to not logging daemon.info
anywhere.  That kind of defeats the purpose, I think.
2011-01-01 00:28:31 -08:00
e6d7c44e27 Merge branch 'daemon'
* daemon:
  daemonization: make sure the firewall subproc sends to syslog too.
  Rearrange daemonization/syslog stuff and make it more resilient.
  run in background (daemon) and option
2011-01-01 00:22:43 -08:00
5bf6e40682 daemonization: make sure the firewall subproc sends to syslog too. 2011-01-01 00:22:09 -08:00
8a5ae1a40a Rearrange daemonization/syslog stuff and make it more resilient.
Rename --background to -D/--daemon, to match other programs (like smbd).

You can now have --syslog even without --daemon.

Avoid using atexit(); try/finally is better.

Don't just close stderr; we'll end up eating error output from ssh!
Instead, redirect stderr to a 'logger' subprocess that will send to syslog.

Delay redirecting stderr until after we know we're daemonizing, so handy
error messages can go to stderr instead of syslog.

Make pidfile stuff more resilient: support already-existing files, files
with strict permissions, outdated files containing an already-dead pid.  Add
a --pidfile option to let you specify the pidfile path.

chdir("/") while daemonizing, so that the filesystem containing $PWD can
later be unmounted without killing the daemon.

fw.done() can't wait on the firewall subprocess on exit when daemonized; we
no longer are the parent of that process.
2010-12-31 23:55:19 -08:00
651b607361 If ssh dies right after starting, we might get ECONNRESET.
Turn it into a nicer-looking fatal error instead of an exception dump.
2010-12-31 23:46:47 -08:00
dc9a5e63c7 Minor tweak to help for the python= option. 2010-12-31 23:46:05 -08:00
33bc55be27 Merge branch 'closing'
* closing:
  Correctly close server connection when client disconnects.
  "Too many open files" shouldn't be a fatal condition.
2010-12-31 22:12:54 -08:00
c3204d2728 Correctly close server connection when client disconnects.
When the server disconnected, we were forwarding that information to the
client.  But we weren't forwarding back the other way when the client
disconnected since there was no callback in place to do that.

Relatedly, when we failed entirely to connect to the server, we didn't notify the
client right away.  Now we do.

Thanks to 'Roger' on the mailing list for pointing out these bugs.
2010-12-31 21:59:36 -08:00
b1edb226a5 "Too many open files" shouldn't be a fatal condition.
It can happen if there are too many sockets open.  If that happens, just
throw away any connections that arrive in the meantime instead of aborting
completely.
2010-12-31 21:32:51 -08:00
7fa1c3c4e4 Listen on localhost:0 instead of 0.0.0.0:0 by default.
This avoids any possible problem caused by other people on your network
using you as a proxy.  If you want to allow this, you can force it back to
the old way using the --listen option.

Thanks to 'tass' on github for reporting portscans that revealed this
potential security problem.
2010-12-31 21:22:40 -08:00
cca69eb496 Don't allow proxying of connections to the proxy port.
Add some cleverness for breaking infinite loops.  Previously we'd only
detect it successfully if you connected to exactly the same IP as we were
listening on, but that was unreliable if we're listening on 0.0.0.0 and you
connected to one of the IP addresses we haven't heard of.

Now, if you try to connect to our listen port on *any* IP, we try binding to
that IP as a local socket; if it works, that's a local IP, and therefore
it's our socket, so reject the connection.  If it doesn't work, it's a
remote IP, so forward it along.

Thanks to 'tass' on github for noticing the problem.
2010-12-31 21:22:32 -08:00
91f65132be Get rid of ugly quotes on "Accept:" log messages. 2010-12-31 20:54:46 -08:00
2ef3a301fb run in background (daemon) and option 2010-12-12 12:08:54 +08:00
8 changed files with 226 additions and 32 deletions

156
client.py
View File

@ -1,9 +1,98 @@
import struct, socket, select, errno, re import struct, socket, select, errno, re, signal
import compat.ssubprocess as ssubprocess import compat.ssubprocess as ssubprocess
import helpers, ssnet, ssh import helpers, ssnet, ssh, ssyslog
from ssnet import SockWrapper, Handler, Proxy, Mux, MuxWrapper from ssnet import SockWrapper, Handler, Proxy, Mux, MuxWrapper
from helpers import * 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): def original_dst(sock):
try: try:
@ -30,6 +119,8 @@ class FirewallClient:
argvbase = ([sys.argv[0]] + argvbase = ([sys.argv[0]] +
['-v'] * (helpers.verbose or 0) + ['-v'] * (helpers.verbose or 0) +
['--firewall', str(port)]) ['--firewall', str(port)])
if ssyslog._p:
argvbase += ['--syslog']
argv_tries = [ argv_tries = [
['sudo', '-p', '[local sudo] Password: '] + argvbase, ['sudo', '-p', '[local sudo] Password: '] + argvbase,
['su', '-c', ' '.join(argvbase)], ['su', '-c', ' '.join(argvbase)],
@ -98,25 +189,34 @@ class FirewallClient:
raise Fatal('cleanup: %r returned %d' % (self.argv, rv)) raise Fatal('cleanup: %r returned %d' % (self.argv, rv))
def _main(listener, fw, ssh_cmd, remotename, python, seed_hosts, auto_nets): def _main(listener, fw, ssh_cmd, remotename, python, seed_hosts, auto_nets,
syslog, daemon):
handlers = [] handlers = []
if helpers.verbose >= 1: if helpers.verbose >= 1:
helpers.logprefix = 'c : ' helpers.logprefix = 'c : '
else: else:
helpers.logprefix = 'client: ' helpers.logprefix = 'client: '
debug1('connecting to server...\n') debug1('connecting to server...\n')
try: try:
(serverproc, serversock) = ssh.connect(ssh_cmd, remotename, python) (serverproc, serversock) = ssh.connect(ssh_cmd, remotename, python,
stderr=ssyslog._p and ssyslog._p.stdin)
except socket.error, e: except socket.error, e:
if e.errno == errno.EPIPE: if e.args[0] == errno.EPIPE:
raise Fatal("failed to establish ssh session") raise Fatal("failed to establish ssh session (1)")
else: else:
raise raise
mux = Mux(serversock, serversock) mux = Mux(serversock, serversock)
handlers.append(mux) handlers.append(mux)
expected = 'SSHUTTLE0001' expected = 'SSHUTTLE0001'
try:
initstring = serversock.recv(len(expected)) 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() rv = serverproc.poll()
if rv: if rv:
@ -126,6 +226,12 @@ def _main(listener, fw, ssh_cmd, remotename, python, seed_hosts, auto_nets):
raise Fatal('expected server init string %r; got %r' raise Fatal('expected server init string %r; got %r'
% (expected, initstring)) % (expected, initstring))
debug1('connected.\n') debug1('connected.\n')
if daemon:
daemonize()
log('daemonizing (%s).\n' % _pidname)
elif syslog:
debug1('switching to syslog.\n')
ssyslog.stderr_to_syslog()
def onroutes(routestr): def onroutes(routestr):
if auto_nets: if auto_nets:
@ -153,11 +259,26 @@ def _main(listener, fw, ssh_cmd, remotename, python, seed_hosts, auto_nets):
mux.got_host_list = onhostlist mux.got_host_list = onhostlist
def onaccept(): def onaccept():
global _extra_fd
try:
sock,srcip = listener.accept() 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) dstip = original_dst(sock)
debug1('Accept: %r:%r -> %r:%r.\n' % (srcip[0],srcip[1], debug1('Accept: %s:%r -> %s:%r.\n' % (srcip[0],srcip[1],
dstip[0],dstip[1])) dstip[0],dstip[1]))
if dstip == listener.getsockname(): if dstip[1] == listener.getsockname()[1] and _islocal(dstip[0]):
debug1("-- ignored: that's my address!\n") debug1("-- ignored: that's my address!\n")
sock.close() sock.close()
return return
@ -182,7 +303,15 @@ def _main(listener, fw, ssh_cmd, remotename, python, seed_hosts, auto_nets):
def main(listenip, ssh_cmd, remotename, python, seed_hosts, auto_nets, def main(listenip, ssh_cmd, remotename, python, seed_hosts, auto_nets,
subnets_include, subnets_exclude): 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') debug1('Starting sshuttle proxy.\n')
listener = socket.socket() listener = socket.socket()
listener.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) listener.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
@ -213,6 +342,13 @@ def main(listenip, ssh_cmd, remotename, python, seed_hosts, auto_nets,
try: try:
return _main(listener, fw, ssh_cmd, remotename, return _main(listener, fw, ssh_cmd, remotename,
python, seed_hosts, auto_nets) python, seed_hosts, auto_nets, syslog, daemon)
finally: finally:
try:
if daemon:
# it's not our child anymore; can't waitpid
fw.p.returncode = 0
fw.done() fw.done()
finally:
if daemon:
daemon_cleanup()

View File

@ -1,6 +1,6 @@
import re, errno import re, errno
import compat.ssubprocess as ssubprocess import compat.ssubprocess as ssubprocess
import helpers import helpers, ssyslog
from helpers import * from helpers import *
@ -216,7 +216,7 @@ def restore_etc_hosts(port):
# exit. In case that fails, it's not the end of the world; future runs will # exit. In case that fails, it's not the end of the world; future runs will
# supercede it in the transproxy list, at least, so the leftover rules # supercede it in the transproxy list, at least, so the leftover rules
# are hopefully harmless. # are hopefully harmless.
def main(port): def main(port, syslog):
assert(port > 0) assert(port > 0)
assert(port <= 65535) assert(port <= 65535)
@ -235,6 +235,10 @@ def main(port):
# can read from it. # can read from it.
os.dup2(1, 0) os.dup2(1, 0)
if syslog:
ssyslog.start_syslog()
ssyslog.stderr_to_syslog()
debug1('firewall manager ready.\n') debug1('firewall manager ready.\n')
sys.stdout.write('READY\n') sys.stdout.write('READY\n')
sys.stdout.flush() sys.stdout.flush()

18
main.py
View File

@ -1,6 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
import sys, os, re import sys, os, re
import helpers, options, client, server, firewall, hostwatch import helpers, options, client, server, firewall, hostwatch
import compat.ssubprocess as ssubprocess
from helpers import * from helpers import *
@ -46,18 +47,22 @@ def parse_ipport(s):
optspec = """ optspec = """
sshuttle [-l [ip:]port] [-r [username@]sshserver[:port]] <subnets...> sshuttle [-l [ip:]port] [-r [username@]sshserver[:port]] <subnets...>
sshuttle --firewall <port> <subnets...>
sshuttle --server sshuttle --server
sshuttle --firewall <port> <subnets...>
sshuttle --hostwatch
-- --
l,listen= transproxy to this ip address and port number [0.0.0.0:0] l,listen= transproxy to this ip address and port number [127.0.0.1:0]
H,auto-hosts scan for remote hostnames and update local /etc/hosts H,auto-hosts scan for remote hostnames and update local /etc/hosts
N,auto-nets automatically determine subnets to route N,auto-nets automatically determine subnets to route
python= specify the name/path of the python interpreter on the remote server [python] python= path to python interpreter on the remote server [python]
r,remote= ssh hostname (and optional username) of remote sshuttle server r,remote= ssh hostname (and optional username) of remote sshuttle server
x,exclude= exclude this subnet (can be used more than once) x,exclude= exclude this subnet (can be used more than once)
v,verbose increase debug message verbosity v,verbose increase debug message verbosity
e,ssh-cmd= the command to use to connect to the remote [ssh] e,ssh-cmd= the command to use to connect to the remote [ssh]
seed-hosts= with -H, use these hostnames for initial scan (comma-separated) seed-hosts= with -H, use these hostnames for initial scan (comma-separated)
D,daemon run in the background as a daemon
syslog send log messages to syslog (default if you use --daemon)
pidfile= pidfile name (only if using --daemon) [./sshuttle.pid]
server (internal use only) server (internal use only)
firewall (internal use only) firewall (internal use only)
hostwatch (internal use only) hostwatch (internal use only)
@ -65,6 +70,8 @@ hostwatch (internal use only)
o = options.Options('sshuttle', optspec) o = options.Options('sshuttle', optspec)
(opt, flags, extra) = o.parse(sys.argv[1:]) (opt, flags, extra) = o.parse(sys.argv[1:])
if opt.daemon:
opt.syslog = 1
helpers.verbose = opt.verbose helpers.verbose = opt.verbose
try: try:
@ -75,7 +82,7 @@ try:
elif opt.firewall: elif opt.firewall:
if len(extra) != 1: if len(extra) != 1:
o.fatal('exactly one argument expected') o.fatal('exactly one argument expected')
sys.exit(firewall.main(int(extra[0]))) sys.exit(firewall.main(int(extra[0]), opt.syslog))
elif opt.hostwatch: elif opt.hostwatch:
sys.exit(hostwatch.hw_main(extra)) sys.exit(hostwatch.hw_main(extra))
else: else:
@ -104,7 +111,8 @@ try:
sh, sh,
opt.auto_nets, opt.auto_nets,
parse_subnets(includes), parse_subnets(includes),
parse_subnets(excludes))) parse_subnets(excludes),
opt.syslog, opt.daemon, opt.pidfile))
except Fatal, e: except Fatal, e:
log('fatal: %s\n' % e) log('fatal: %s\n' % e)
sys.exit(99) sys.exit(99)

View File

@ -166,6 +166,7 @@ def main():
while mux.ok: while mux.ok:
if hw.pid: if hw.pid:
assert(hw.pid > 0)
(rpid, rv) = os.waitpid(hw.pid, os.WNOHANG) (rpid, rv) = os.waitpid(hw.pid, os.WNOHANG)
if rpid: if rpid:
raise Fatal('hostwatch exited unexpectedly: code 0x%04x\n' % rv) raise Fatal('hostwatch exited unexpectedly: code 0x%04x\n' % rv)

4
ssh.py
View File

@ -21,7 +21,7 @@ def empackage(z, filename):
return '%s\n%d\n%s' % (basename,len(content), content) return '%s\n%d\n%s' % (basename,len(content), content)
def connect(ssh_cmd, rhostport, python): def connect(ssh_cmd, rhostport, python, stderr):
main_exe = sys.argv[0] main_exe = sys.argv[0]
portl = [] portl = []
@ -87,7 +87,7 @@ def connect(ssh_cmd, rhostport, python):
s1.close() s1.close()
debug2('executing: %r\n' % argv) debug2('executing: %r\n' % argv)
p = ssubprocess.Popen(argv, stdin=s1a, stdout=s1b, preexec_fn=setup, p = ssubprocess.Popen(argv, stdin=s1a, stdout=s1b, preexec_fn=setup,
close_fds=True) close_fds=True, stderr=stderr)
os.close(s1a) os.close(s1a)
os.close(s1b) os.close(s1b)
s2.sendall(content) s2.sendall(content)

View File

@ -1,6 +1,6 @@
% sshuttle(8) Sshuttle 0.42 % sshuttle(8) Sshuttle 0.44
% Avery Pennarun <apenwarr@gmail.com> % Avery Pennarun <apenwarr@gmail.com>
% 2010-11-09 % 2010-12-31
# NAME # NAME
@ -41,7 +41,13 @@ entire subnet to the VPN.
-l, --listen=*[ip:]port* -l, --listen=*[ip:]port*
: use this ip address and port number as the transparent : use this ip address and port number as the transparent
proxy port. By default sshuttle finds an available proxy port. By default sshuttle finds an available
port automatically, so you don't need to override it. port automatically and listens on IP 127.0.0.1
(localhost), so you don't need to override it, and
connections are only proxied from the local machine,
not from outside machines. If you want to accept
connections from other machines on your network (ie. to
run sshuttle on a router) try enabling IP Forwarding in
your kernel, then using `--listen 0.0.0.0:0`.
-H, --auto-hosts -H, --auto-hosts
: scan for remote hostnames and update the local /etc/hosts : scan for remote hostnames and update the local /etc/hosts
@ -103,6 +109,20 @@ entire subnet to the VPN.
if you use this option to give it a few names to start if you use this option to give it a few names to start
from. from.
-D, --daemon
: automatically fork into the background after connecting
to the remote server. Implies `--syslog`.
--syslog
: after connecting, send all log messages to the
`syslog`(3) service instead of stderr. This is
implicit if you use `--daemon`.
--pidfile=*pidfilename*
: when using `--daemon`, save sshuttle's pid to
*pidfilename*. The default is `sshuttle.pid` in the
current directory.
--server --server
: (internal use only) run the sshuttle server on : (internal use only) run the sshuttle server on
stdin/stdout. This is what the client runs on stdin/stdout. This is what the client runs on
@ -139,8 +159,8 @@ Test locally by proxying all local connections, without using ssh:
s: 192.168.42.0/24 s: 192.168.42.0/24
c : connected. c : connected.
firewall manager: starting transproxy. firewall manager: starting transproxy.
c : Accept: '192.168.42.106':50035 -> '192.168.42.121':139. c : Accept: 192.168.42.106:50035 -> 192.168.42.121:139.
c : Accept: '192.168.42.121':47523 -> '77.141.99.22':443. c : Accept: 192.168.42.121:47523 -> 77.141.99.22:443.
...etc... ...etc...
^C ^C
firewall manager: undoing changes. firewall manager: undoing changes.
@ -166,7 +186,7 @@ and subnet guessing:
hostwatch: Found: testbox1: 1.2.3.4 hostwatch: Found: testbox1: 1.2.3.4
hostwatch: Found: mytest2: 5.6.7.8 hostwatch: Found: mytest2: 5.6.7.8
hostwatch: Found: domaincontroller: 99.1.2.3 hostwatch: Found: domaincontroller: 99.1.2.3
c : Accept: '192.168.42.121':60554 -> '77.141.99.22':22. c : Accept: 192.168.42.121:60554 -> 77.141.99.22:22.
^C ^C
firewall manager: undoing changes. firewall manager: undoing changes.
c : Keyboard interrupt: exiting. c : Keyboard interrupt: exiting.

View File

@ -15,7 +15,7 @@ CMD_EXIT = 0x4200
CMD_PING = 0x4201 CMD_PING = 0x4201
CMD_PONG = 0x4202 CMD_PONG = 0x4202
CMD_CONNECT = 0x4203 CMD_CONNECT = 0x4203
# CMD_CLOSE = 0x4204 # never used - removed CMD_STOP_SENDING = 0x4204
CMD_EOF = 0x4205 CMD_EOF = 0x4205
CMD_DATA = 0x4206 CMD_DATA = 0x4206
CMD_ROUTES = 0x4207 CMD_ROUTES = 0x4207
@ -27,6 +27,7 @@ cmd_to_name = {
CMD_PING: 'PING', CMD_PING: 'PING',
CMD_PONG: 'PONG', CMD_PONG: 'PONG',
CMD_CONNECT: 'CONNECT', CMD_CONNECT: 'CONNECT',
CMD_STOP_SENDING: 'STOP_SENDING',
CMD_EOF: 'EOF', CMD_EOF: 'EOF',
CMD_DATA: 'DATA', CMD_DATA: 'DATA',
CMD_ROUTES: 'ROUTES', CMD_ROUTES: 'ROUTES',
@ -106,6 +107,8 @@ class SockWrapper:
def seterr(self, e): def seterr(self, e):
if not self.exc: if not self.exc:
self.exc = e self.exc = e
self.nowrite()
self.noread()
def try_connect(self): def try_connect(self):
if self.connect_to and self.shut_write: if self.connect_to and self.shut_write:
@ -162,8 +165,6 @@ class SockWrapper:
except OSError, e: except OSError, e:
# unexpected error... stream is dead # unexpected error... stream is dead
self.seterr(e) self.seterr(e)
self.nowrite()
self.noread()
return 0 return 0
def write(self, buf): def write(self, buf):
@ -231,6 +232,11 @@ class Proxy(Handler):
self.wrap2 = wrap2 self.wrap2 = wrap2
def pre_select(self, r, w, x): def pre_select(self, r, w, x):
if self.wrap1.shut_read: self.wrap2.nowrite()
if self.wrap1.shut_write: self.wrap2.noread()
if self.wrap2.shut_read: self.wrap1.nowrite()
if self.wrap2.shut_write: self.wrap1.noread()
if self.wrap1.connect_to: if self.wrap1.connect_to:
_add(w, self.wrap1.rsock) _add(w, self.wrap1.rsock)
elif self.wrap1.buf: elif self.wrap1.buf:
@ -430,6 +436,7 @@ class MuxWrapper(SockWrapper):
def noread(self): def noread(self):
if not self.shut_read: if not self.shut_read:
self.shut_read = True self.shut_read = True
self.mux.send(self.channel, CMD_STOP_SENDING, '')
self.maybe_close() self.maybe_close()
def nowrite(self): def nowrite(self):
@ -464,6 +471,8 @@ class MuxWrapper(SockWrapper):
def got_packet(self, cmd, data): def got_packet(self, cmd, data):
if cmd == CMD_EOF: if cmd == CMD_EOF:
self.noread() self.noread()
elif cmd == CMD_STOP_SENDING:
self.nowrite()
elif cmd == CMD_DATA: elif cmd == CMD_DATA:
self.buf.append(data) self.buf.append(data)
else: else:

16
ssyslog.py Normal file
View File

@ -0,0 +1,16 @@
import sys, os
from compat import ssubprocess
_p = None
def start_syslog():
global _p
_p = ssubprocess.Popen(['logger',
'-p', 'daemon.notice',
'-t', 'sshuttle'], stdin=ssubprocess.PIPE)
def stderr_to_syslog():
sys.stdout.flush()
sys.stderr.flush()
os.dup2(_p.stdin.fileno(), 2)