#!/usr/bin/env python
"""
classic rpyc server (threaded, forking or std) running a SlaveService
usage: 
    classic_server.py                         # default settings
    classic_server.py -m forking -p 12345     # custom settings
    classic_server.py --vdb file.vdb          # tlslite-authenticated server
"""
import sys
import os
import rpyc
from optparse import OptionParser
from rpyc.utils.server import ThreadedServer, ForkingServer
from rpyc.utils.classic import DEFAULT_SERVER_PORT
from rpyc.utils.registry import REGISTRY_PORT
from rpyc.utils.registry import UDPRegistryClient, TCPRegistryClient
from rpyc.utils.authenticators import VdbAuthenticator
from rpyc.core import SlaveService


parser = OptionParser()
parser.add_option("-m", "--mode", action="store", dest="mode", metavar="MODE",
    default="threaded", type="string", help="mode can be 'threaded', 'forking', "
    "or 'stdio' to operate over the standard IO pipes (for inetd, etc.). "
    "Default is 'threaded'")
parser.add_option("-p", "--port", action="store", dest="port", type="int", 
    metavar="PORT", default=DEFAULT_SERVER_PORT, help="specify a different "
    "TCP listener port. Default is 18812")
parser.add_option("--host", action="store", dest="host", type="str", 
    metavar="HOST", default="0.0.0.0", help="specify a different "
    "host to bind to. Default is 0.0.0.0")
parser.add_option("--logfile", action="store", dest="logfile", type="str", 
    metavar="FILE", default=None, help="specify the log file to use; the "
    "default is stderr")
parser.add_option("-q", "--quiet", action="store_true", dest="quiet", 
    default=False, help="quiet mode (no logging). in stdio mode, "
    "writes to /dev/null")
parser.add_option("--vdb", action="store", dest="vdbfile", metavar="FILENAME",
    default=None, help="starts an TLS/SSL authenticated server (using tlslite);"
    "the credentials are loaded from the vdb file. if not given, the server"
    "is not secure (unauthenticated). use vdbconf.py to manage vdb files"
)
parser.add_option("--dont-register", action="store_false", dest="auto_register", 
    default=True, help="disables this server from registering at all. "
    "By default, the server will attempt to register")
parser.add_option("--registry-type", action="store", dest="regtype", type="str", 
    default="udp", help="can be 'udp' or 'tcp', default is 'udp'")
parser.add_option("--registry-port", action="store", dest="regport", type="int", 
    default=REGISTRY_PORT, help="the UDP/TCP port. default is %s" % (REGISTRY_PORT,))
parser.add_option("--registry-host", action="store", dest="reghost", type="str", 
    default=None, help="the registry host machine. for UDP, the default is "
    "255.255.255.255; for TCP, a value is required")


def get_options():
    options, args = parser.parse_args()
    if args:
        parser.error("does not take positional arguments: %r" % (args,))

    options.mode = options.mode.lower()

    if options.regtype.lower() == "udp":
        if options.reghost is None:
            options.reghost = "255.255.255.255"
        options.registrar = UDPRegistryClient(ip = options.reghost, port = options.regport)
    elif options.regtype.lower() == "tcp":
        if options.reghost is None:
            parser.error("must specific --registry-host")
        options.registrar = TCPRegistryClient(ip = options.reghost, port = options.regport)
    else:
        parse.error("invalid registry type %r" % (options.regtype,))

    if options.vdbfile:
        if not os.path.exists(options.vdbfile):
            parser.error("vdb file does not exist")
        options.authenticator = VdbAuthenticator.from_file(options.vdbfile, mode = "r")
    else:
        options.authenticator = None

    options.handler = "serve_%s" % (options.mode,)
    if options.handler not in globals():
        parser.error("invalid mode %r" % (options.mode,))
    
    return options

def serve_threaded(options):
    t = ThreadedServer(SlaveService, hostname = options.host, 
        port = options.port, reuse_addr = True, 
        authenticator = options.authenticator, registrar = options.registrar,
        auto_register = options.auto_register)
    t.logger.quiet = options.quiet
    if options.logfile:
        t.logger.console = open(options.logfile, "w")
    t.start()

def serve_forking(options):    
    t = ForkingServer(SlaveService, hostname = options.host, 
        port = options.port, reuse_addr = True, 
        authenticator = options.authenticator, registrar = options.registrar,
        auto_register = options.auto_register)
    t.logger.quiet = options.quiet
    if options.logfile:
        t.logger.console = open(options.logfile, "w")
    t.start()

def serve_stdio(options):
    origstdin = sys.stdin
    origstdout = sys.stdout
    if options.quiet:
        dev = os.devnull
    elif sys.platform == "win32":
        dev = "con:"
    else:
        dev = "/dev/tty"
    try:
        sys.stdin = open(dev, "r")
        sys.stdout = open(dev, "w")
    except (IOError, OSError):
        sys.stdin = open(os.devnull, "r")
        sys.stdout = open(os.devnull, "w")
    conn = rpyc.classic.connect_pipes(origstdin, origstdout)
    try:
        try:
            conn.serve_all()
        except KeyboardInterrupt:
            print("User interrupt!")
    finally:
        conn.close()


def main():
    options = get_options()
    handler = globals()[options.handler]
    handler(options)


if __name__ == "__main__":
    main()







