the upcoming 2.0 version
https://redmine.lighttpd.net/projects/lighttpd2
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
185 lines
4.8 KiB
185 lines
4.8 KiB
# -*- coding: utf-8 -*- |
|
|
|
import os |
|
import atexit |
|
import subprocess |
|
import socket |
|
import select |
|
import signal |
|
|
|
import base |
|
|
|
__all__ = [ "Service", "ServiceException", "devnull", "Lighttpd", "FastCGI" ] |
|
|
|
class ServiceException(Exception): |
|
def __init__(self, value): self.value = value |
|
def __str__(self): return repr(self.value) |
|
|
|
def devnull(): |
|
try: |
|
f = open("/dev/null", "r") |
|
return f |
|
except: |
|
return None |
|
|
|
|
|
straceargs = [ 'strace', '-tt', '-f', '-s', '4096', '-o' ] |
|
trussargs = [ 'truss', '-d', '-f', '-s', '4096', '-o' ] |
|
|
|
def preexec(): |
|
os.setsid() |
|
|
|
class Service(object): |
|
name = None |
|
|
|
def __init__(self): |
|
self.proc = None |
|
self.tests = None |
|
self.failed = False |
|
|
|
def devnull(self): |
|
return devnull() |
|
|
|
def fork(self, *args, **kwargs): |
|
if kwargs.has_key('inp'): |
|
inp = kwargs['inp'] |
|
else: |
|
inp = devnull() |
|
|
|
if None == self.name: |
|
raise ServiceException("Service needs a name!") |
|
logfile = open(self.log, "w") |
|
|
|
if base.Env.strace: |
|
slog = self.tests.PrepareFile("log/strace-%s.log" % self.name, "") |
|
args = straceargs + [ slog ] + list(args) |
|
elif base.Env.truss: |
|
tlog = self.tests.PrepareFile("log/truss-%s.log" % self.name, "") |
|
args = trussargs + [ tlog ] + list(args) |
|
|
|
print >> base.Env.log, "Spawning '%s': %s" % (self.name, ' '.join(args)) |
|
proc = subprocess.Popen(args, stdin = inp, stdout = logfile, stderr = logfile, close_fds = True, preexec_fn = preexec) |
|
if None != inp: inp.close() |
|
logfile.close() |
|
self.proc = proc |
|
atexit.register(self.kill) |
|
|
|
def kill(self): |
|
proc = self.proc |
|
if None == proc: return |
|
self.proc = None |
|
if None == proc.poll(): |
|
print >> base.Env.log, "Terminating service '%s'" % (self.name) |
|
try: |
|
os.killpg(proc.pid, signal.SIGINT) |
|
proc.terminate() |
|
except: |
|
pass |
|
print >> base.Env.log, "Waiting for service '%s'" % (self.name) |
|
proc.wait() |
|
|
|
def portfree(self, port): |
|
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) |
|
try: |
|
s.connect(("127.0.0.1", port)) |
|
except: |
|
pass |
|
else: |
|
raise ServiceException("Cannot start service '%s', port 127.0.0.1:%i already in use" % (self.name, port)) |
|
finally: |
|
s.close() |
|
|
|
def waitconnect(self, port): |
|
timeout = 5*10 |
|
while True: |
|
if None != self.proc.poll(): |
|
raise ServiceException("Service %s died before we could establish a connection" % (self.name)) |
|
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) |
|
try: |
|
s.connect(("127.0.0.1", port)) |
|
except: |
|
pass |
|
else: |
|
return True |
|
finally: |
|
s.close() |
|
select.select([], [], [], 0.1) |
|
timeout -= 1 |
|
if 0 > timeout: |
|
raise ServiceException("Timeout: cannot establish a connection to service %s on port %i" % (self.name, port)) |
|
|
|
def _prepare(self): |
|
self.log = self.tests.PrepareFile("log/service-%s.log" % self.name, "") |
|
self.failed = True |
|
self.Prepare() |
|
self.failed = False |
|
|
|
def _cleanup(self): |
|
self.kill() |
|
if not base.Env.force_cleanup and self.failed: |
|
return |
|
self.tests.CleanupFile("log/service-%s.log" % self.name) |
|
self.tests.CleanupFile("log/strace-%s.log" % self.name) |
|
self.tests.CleanupFile("log/truss-%s.log" % self.name) |
|
self.Cleanup() |
|
|
|
def _stop(self): |
|
self.kill() |
|
self.Stop() |
|
|
|
def Prepare(self): |
|
raise BaseException("Not implemented yet") |
|
|
|
def Cleanup(self): |
|
pass |
|
|
|
def Stop(self): |
|
pass |
|
|
|
class Lighttpd(Service): |
|
name = "lighttpd" |
|
|
|
def TestConfig(self): |
|
logfile = open(self.log, "w") |
|
inp = self.devnull() |
|
args = [base.Env.worker, '-m', base.Env.plugindir, '-c', base.Env.lighttpdconf, '-t'] |
|
print >> base.Env.log, "Testing lighttpd config: %s" % (' '.join(args)) |
|
proc = subprocess.Popen(args, stdin = inp, stdout = logfile, stderr = logfile, close_fds = True) |
|
if None != inp: inp.close() |
|
logfile.close() |
|
status = proc.wait() |
|
if 0 != status: |
|
os.system("cat '%s'" % self.log) |
|
raise BaseException("testing lighttpd config failed with returncode %i" % (status)) |
|
|
|
def Prepare(self): |
|
self.TestConfig() |
|
|
|
self.portfree(base.Env.port) |
|
if base.Env.no_angel: |
|
self.fork(base.Env.worker, '-m', base.Env.plugindir, '-c', base.Env.lighttpdconf) |
|
else: |
|
self.fork(base.Env.angel, '-m', base.Env.plugindir, '-c', base.Env.angelconf) |
|
self.waitconnect(base.Env.port) |
|
|
|
class FastCGI(Service): |
|
binary = [None] |
|
|
|
def __init__(self): |
|
self.sockfile = os.path.join(base.Env.dir, "tmp", "sockets", self.name + ".sock") |
|
super(FastCGI, self).__init__() |
|
|
|
def Prepare(self): |
|
sockdir = self.tests.PrepareDir(os.path.join("tmp", "sockets")) |
|
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) |
|
sock.bind(os.path.relpath(self.sockfile)) |
|
sock.listen(8) |
|
self.fork(*self.binary, inp = sock) |
|
|
|
def Cleanup(self): |
|
if None != self.sockfile: |
|
try: |
|
os.remove(self.sockfile) |
|
except BaseException, e: |
|
print >>sys.stderr, "Couldn't delete socket '%s': %s" % (self.sockfile, e) |
|
self.tests.CleanupDir(os.path.join("tmp", "sockets"))
|
|
|