#!/usr/bin/env python import argparse, sys, logging, logging.handlers import os from libpathod import pathod, utils, version, rparse def daemonize (stdin='/dev/null', stdout='/dev/null', stderr='/dev/null'): try: pid = os.fork() if pid > 0: sys.exit(0) except OSError, e: sys.stderr.write ("fork #1 failed: (%d) %s\n" % (e.errno, e.strerror) ) sys.exit(1) os.chdir("/") os.umask(0) os.setsid() try: pid = os.fork() if pid > 0: sys.exit(0) except OSError, e: sys.stderr.write ("fork #2 failed: (%d) %s\n" % (e.errno, e.strerror) ) sys.exit(1) si = open(stdin, 'r') so = open(stdout, 'a+') se = open(stderr, 'a+', 0) os.dup2(si.fileno(), sys.stdin.fileno()) os.dup2(so.fileno(), sys.stdout.fileno()) os.dup2(se.fileno(), sys.stderr.fileno()) def main(parser, args): sl = [args.ssl_keyfile, args.ssl_certfile] if any(sl) and not all(sl): parser.error("Both --certfile and --keyfile must be specified.") if args.ssl: ssl = dict( keyfile = args.ssl_keyfile or utils.data.path("resources/server.key"), certfile = args.ssl_certfile or utils.data.path("resources/server.crt"), ) else: ssl = None alst = [] for i in args.anchors: parts = utils.parse_anchor_spec(i) if not parts: parser.error("Invalid anchor specification: %s"%i) alst.append(parts) root = logging.getLogger() if root.handlers: for handler in root.handlers: root.removeHandler(handler) log = logging.getLogger('pathod') log.setLevel(logging.DEBUG) fmt = logging.Formatter( '%(asctime)s: %(message)s', datefmt='%d-%m-%y %H:%M:%S', ) if args.logfile: fh = logging.handlers.WatchedFileHandler(args.logfile) fh.setFormatter(fmt) log.addHandler(fh) if not args.daemonize: sh = logging.StreamHandler() sh.setFormatter(fmt) log.addHandler(sh) sizelimit = None if args.sizelimit: try: sizelimit = utils.parse_size(args.sizelimit) except ValueError, v: parser.error(v) try: pd = pathod.Pathod( (args.address, args.port), craftanchor = args.craftanchor, ssloptions = ssl, staticdir = args.staticdir, anchors = alst, sizelimit = sizelimit, noweb = args.noweb, nocraft = args.nocraft, noapi = args.noapi, nohang = args.nohang ) except pathod.PathodError, v: parser.error(str(v)) except rparse.FileAccessDenied, v: parser.error("%s You probably want to a -d argument."%str(v)) try: print "%s listening on %s:%s"%(version.NAMEVERSION, args.address, pd.port) pd.serve_forever() except KeyboardInterrupt: pass if __name__ == "__main__": parser = argparse.ArgumentParser(description='A pathological HTTP/S daemon.') parser.add_argument("-p", dest='port', default=9999, type=int, help='Port. Specify 0 to pick an arbitrary empty port.') parser.add_argument("-l", dest='address', default="127.0.0.1", type=str, help='Listening address.') parser.add_argument( "-a", dest='anchors', default=[], type=str, action="append", metavar="ANCHOR", help='Add an anchor. Specified as a string with the form pattern=pagespec' ) parser.add_argument( "-c", dest='craftanchor', default="/p/", type=str, help='Anchorpoint for URL crafting commands.' ) parser.add_argument( "-d", dest='staticdir', default=None, type=str, help='Directory for static files.' ) parser.add_argument( "-D", dest='daemonize', default=False, action="store_true", help='Daemonize.' ) parser.add_argument( "-f", dest='logfile', default=None, type=str, help='Log file.' ) parser.add_argument( "--debug", dest='debug', default=False, action="store_true", help='Enable debug output.' ) parser.add_argument( "-s", dest='ssl', default=False, action="store_true", help='Serve with SSL.' ) parser.add_argument( "--limit-size", dest='sizelimit', default=None, type=str, help='Size limit of served responses. Understands size suffixes, i.e. 100k.' ) parser.add_argument( "--noapi", dest='noapi', default=False, action="store_true", help='Disable API.' ) parser.add_argument( "--nohang", dest='nohang', default=False, action="store_true", help='Disable pauses during crafted response generation.' ) parser.add_argument( "--noweb", dest='noweb', default=False, action="store_true", help='Disable both web interface and API.' ) parser.add_argument( "--nocraft", dest='nocraft', default=False, action="store_true", help='Disable response crafting. If anchors are specified, they still work.' ) parser.add_argument( "--keyfile", dest='ssl_keyfile', default=None, type=str, help='SSL key file. If not specified, a default key is used.' ) parser.add_argument( "--certfile", dest='ssl_certfile', default=None, type=str, help='SSL cert file. If not specified, a default cert is used.' ) args = parser.parse_args() if args.daemonize: daemonize() main(parser, args)