186 lines
5.5 KiB
Python
186 lines
5.5 KiB
Python
|
|
# -------------------------------------------------------------
|
|
# router.py: Router control module
|
|
# -------------------------------------------------------------
|
|
|
|
"""
|
|
Router control module
|
|
"""
|
|
|
|
import i2p
|
|
import i2p.socket
|
|
import i2p.eep
|
|
from i2p.pylib import socket as pysocket # Import Python socket
|
|
|
|
import os, sys
|
|
import os.path
|
|
import time
|
|
import threading
|
|
import urllib2
|
|
|
|
check_addrlist = [i2p.socket.samaddr, i2p.eep.eepaddr]
|
|
|
|
router_config = 'router.config' # Router config filename
|
|
|
|
# True if our Python program started the router
|
|
our_router = False
|
|
our_router_lock = threading.Lock()
|
|
|
|
|
|
def find(dir=None):
|
|
"""Find the absolute path to a locally installed I2P router.
|
|
|
|
An I2P installation is located by looking in the
|
|
the dir argument given to the function, then in the
|
|
environment I2P, then in PATH. It looks for startRouter.sh
|
|
or startRouter.bat. Raises ValueError if an I2P installation
|
|
could not be located.
|
|
"""
|
|
sep = os.pathsep # Path separator
|
|
L = []
|
|
if dir != None and dir != '': L += dir.split(sep)
|
|
if 'I2P' in os.environ: L += os.environ['I2P'].split(sep)
|
|
if 'PATH' in os.environ: L += os.environ['PATH'].split(sep)
|
|
for dirname in L:
|
|
filename = os.path.join(dirname, 'startRouter.bat')
|
|
if os.path.exists(filename):
|
|
return dirname
|
|
filename = os.path.join(dirname, 'startRouter.sh')
|
|
if os.path.exists(filename):
|
|
return dirname
|
|
raise ValueError('I2P installation not found')
|
|
|
|
|
|
def check(dir=None):
|
|
"""Checks whether a locally installed router is running. Does
|
|
nothing if successful, otherwise raises i2p.RouterError.
|
|
|
|
An I2P installation is located by using find(dir).
|
|
The router.config file is parsed for 'router.adminPort'.
|
|
This port is queried to determine whether the router is
|
|
running.
|
|
"""
|
|
config = _parse_config(os.path.join(find(dir), router_config))
|
|
port = config.get('router.adminPort', '')
|
|
try:
|
|
port = int(port)
|
|
except:
|
|
raise ValueError('router.adminPort missing or bad in ' +
|
|
router_config)
|
|
|
|
try:
|
|
s = pysocket.socket(pysocket.AF_INET, pysocket.SOCK_STREAM)
|
|
s.connect(('127.0.0.1', port))
|
|
s.close()
|
|
except pysocket.error:
|
|
raise i2p.RouterError('could not contact 127.0.0.1:' + str(port))
|
|
|
|
def _run_program(filename):
|
|
"""Runs the given program in a new process and new terminal."""
|
|
if sys.platform[:3] == 'win':
|
|
os.startfile(filename)
|
|
global our_router
|
|
our_router = True
|
|
else:
|
|
# Linux possibilities:
|
|
# sh -c command
|
|
# xterm -e command
|
|
# bash -c command
|
|
# Try os.spawnl() with the above.
|
|
raise ValueError('unimplemented')
|
|
|
|
def start(dir=None, hidden=False):
|
|
"""Start a locally installed I2P router. Does nothing if
|
|
the router has already been started.
|
|
|
|
An I2P installation is located by using find(dir).
|
|
|
|
If hidden is True, do not show a terminal for the router.
|
|
"""
|
|
routerdir = find(dir)
|
|
router = os.path.join(routerdir, 'startRouter.bat')
|
|
try:
|
|
check(dir)
|
|
return # Already running
|
|
except:
|
|
pass # Not yet running
|
|
|
|
olddir = os.getcwd()
|
|
|
|
if hidden:
|
|
raise ValueError('unimplemented')
|
|
|
|
our_router_lock.acquire()
|
|
try:
|
|
os.chdir(routerdir)
|
|
try:
|
|
_run_program(router)
|
|
finally:
|
|
os.chdir(olddir)
|
|
finally:
|
|
our_router_lock.release()
|
|
|
|
# Ideas for hidden=True:
|
|
# Parse startRouter.bat, and run same command with javaw
|
|
# on Windows to hide command box.
|
|
# Perhaps use javaw (?) or javaws (j2sdk1.4.2/jre/javaws/javaws)
|
|
# Perhaps /path-to/program 2>/dev/null 1>/dev/null&
|
|
|
|
def _parse_config(filename):
|
|
"""Return a dict with (name, value) items for the given I2P configuration file."""
|
|
f = open(filename, 'r')
|
|
s = f.read()
|
|
f.close()
|
|
ans = {}
|
|
for line in s.split('\n'):
|
|
line = line.strip()
|
|
if '#' in line: line = line[:line.find('#')]
|
|
pair = line.split('=')
|
|
if len(pair) == 2:
|
|
ans[pair[0].strip()] = pair[1].strip()
|
|
return ans
|
|
|
|
def stop(dir=None, force=False):
|
|
"""Stop a locally installed I2P router, if it was started by
|
|
the current Python program. If force is True, stop the
|
|
router even if it was started by another process. Do nothing
|
|
if force is False and the router was started by another program.
|
|
|
|
The file 'router.config' is located using the same search
|
|
process used for find(dir). It is parsed for
|
|
'router.shutdownPassword' and 'router.adminPort'. The
|
|
router is shut down through the admin port.
|
|
|
|
Raises i2p.RouterError if the I2P router is running but cannot
|
|
be stopped. You must uncomment the
|
|
'router.shutdownPassword' line for this command to work.
|
|
"""
|
|
if force == False and our_router == False:
|
|
return
|
|
|
|
config = _parse_config(os.path.join(find(dir), router_config))
|
|
|
|
password = config.get('router.shutdownPassword', '')
|
|
if password == '':
|
|
raise ValueError('router.shutdownPassword not found in ' +
|
|
router_config)
|
|
admin_port = config.get('router.adminPort', '')
|
|
if admin_port == '':
|
|
raise ValueError('router.adminPort not found in ' + router_config)
|
|
|
|
try:
|
|
admin_port = int(admin_port)
|
|
except:
|
|
raise ValueError('invalid router.adminPort in ' + router_config)
|
|
|
|
try:
|
|
sock = pysocket.socket(pysocket.AF_INET, pysocket.SOCK_STREAM)
|
|
sock.connect(('127.0.0.1', admin_port))
|
|
sock.send('GET /shutdown?password=' + password + ' HTTP/1.0\r\n\r\n')
|
|
time.sleep(0.01)
|
|
sock.close()
|
|
except:
|
|
raise i2p.RouterError('router shutdown failed')
|
|
|
|
# Assume shutdown succeeded (it will take 30 seconds).
|