#!/usr/bin/env python
"""
$URL: svn+ssh://svn.mems-exchange.org/repos/trunk/durus/run_durus.py $
$Id: run_durus.py 29189 2007-01-12 15:42:02Z dbinger $
"""
import sys
import socket
from optparse import OptionParser
from durus.client_storage import ClientStorage
from durus.storage_server import DEFAULT_PORT, DEFAULT_HOST, StorageServer
from durus.storage_server import SocketAddress
from durus.file_storage import FileStorage, TempFileStorage
from durus.logger import log, logger, direct_output
from time import sleep

def get_storage(file, repair, readonly, masterhost, masterport):
    if masterport and file:
        print "Provide a file or a masterport, but not both."
        raise SystemExit
    if file:
        return FileStorage(file, repair=repair, readonly=readonly)
    elif masterport:
        port = int(masterport)
        return ClientStorage(host=masterhost, port=port)
    else:
        return TempFileStorage()

def start_durus(logfile, logginglevel, address, storage):
    if logfile is None:
        logfile = sys.stderr
    else:
        logfile = open(logfile, 'a+')
    direct_output(logfile)
    logger.setLevel(logginglevel)
    socket_address = SocketAddress.new(address)
    if isinstance(storage, FileStorage):
        log(20, 'Storage file=%s address=%s',
            storage.get_filename(), socket_address)
    else:
        log(20, 'Storage master=%s address=%s', storage.address, socket_address)
    StorageServer(storage, address=socket_address).serve()

def stop_durus(address):
    socket_address = SocketAddress.new(address)
    sock = socket_address.get_connected_socket()
    if sock is None:
        raise SystemExit("Durus server %s doesn't seem to be running." %
                          str(address))
    sock.send('Q') # graceful exit message
    sock.close()
    # Try to wait until the address is free.
    for attempt in xrange(20):
        sleep(0.5)
        sock = socket_address.get_connected_socket()
        if sock is None:
            break
        sock.close()

def run_durus_main():
    parser = OptionParser()
    parser.set_description('Run a Durus Server')
    parser.add_option(
        '--port', dest='port', default=DEFAULT_PORT, type='int',
        help='Port to listen on. (default=%s)' % DEFAULT_PORT)
    parser.add_option(
        '--file', dest='file', default=None,
        help=('If neither this nor masterhost is given, the storage is '
        'in a new temporary file.'))
    parser.add_option(
        '--host', dest='host', default=DEFAULT_HOST,
        help='Host to listen on. (default=%s)' % DEFAULT_HOST)
    parser.add_option(
        '--masterhost', dest='masterhost', default=DEFAULT_HOST,
        help='Host of master storage server. (default=%s)' % DEFAULT_HOST)
    parser.add_option(
        '--masterport', dest='masterport', default=None,
        help='Port of masterhost. (default=None)')
    if hasattr(socket, 'AF_UNIX'):
        parser.add_option(
            '--address', dest="address", default=None,
            help=(
                "Address of the server.\n"
                "If given, this is the path to a Unix domain socket for "
                "the server."))
        parser.add_option(
            '--owner', dest="owner", default=None,
            help="Owner of the Unix domain socket (the --address value).")
        parser.add_option(
            '--group', dest="group", default=None,
            help="group of the Unix domain socket (the --address value).")
        parser.add_option(
            '--umask', dest="umask", default=None, type="int",
            help="umask for the Unix domain socket (the --address value).")
    logginglevel = logger.getEffectiveLevel()
    parser.add_option(
        '--logginglevel', dest='logginglevel', default=logginglevel, type='int',
        help=('Logging level. Lower positive numbers log more. (default=%s)' %
              logginglevel))
    parser.add_option(
        '--logfile', dest='logfile', default=None,
        help=('Log file. (default=stderr)'))
    parser.add_option(
        '--repair', dest='repair', action='store_true',
        help=('Repair the filestorage by truncating to remove anything '
              'that is malformed.  Without this option, errors '
              'will cause the program to report and terminate without '
              'attempting any repair.'))
    parser.add_option(
        '--readonly', dest='readonly', action='store_true',
        help='Open the file in read-only mode.')
    parser.add_option(
        '--stop', dest='stop', action='store_true',
        help='Instead of starting the server, try to stop a running one.')
    (options, args) = parser.parse_args()
    if getattr(options, 'address', None) is None:
        address = SocketAddress.new((options.host, options.port))
    else:
        address = SocketAddress.new(address=options.address,
            owner=options.owner, group=options.group, umask=options.umask)
    if not options.stop:
        start_durus(options.logfile,
                    options.logginglevel,
                    address,
                    get_storage(options.file, options.repair, options.readonly,
                        options.masterhost, options.masterport))
    else:
        stop_durus(address)


if __name__ == '__main__':
    run_durus_main()

