#!/usr/bin/python
#
# $Id: ods-signer.in 3657 2010-08-06 10:04:25Z matthijs $
#
# Copyright (c) 2009 NLNet Labs. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
# GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
# IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
# IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#

"""Simple command line interface to the command channel of the signer
engine"""

import optparse

#
# simple python command line interface to the signer engine
#

import sys
import socket
import os
import subprocess

def send_msg(msg, c_sock):
    """Send a message to the engine"""
    c_sock.send(msg)
    if msg[:4] == "stop":
        print "Sent stop command to engine"
        sys.exit(0)
    msg = ''
    chunk = ''
    prevchunk = ''
    while not (chunk == '\n' and prevchunk == '\n'):
        prevchunk = chunk
        chunk = c_sock.recv(1)
        if chunk == '':
            raise RuntimeError, "socket connection broken"
        if chunk != '\n':
            msg = msg + chunk
        else:
            print msg
            msg = ""


def run(c_sock):
    """Read commands from stdin, and send them to the engine. Responses
    are printed to stdout"""
    print "cmd> ",
    cmd = sys.stdin.readline()
    cmd = cmd.strip() + "\n"
    while cmd:
        if cmd[:4] == "quit" or cmd[:4] == "exit":
            sys.exit(0)
        send_msg(cmd, c_sock)
        print "cmd> ",
        cmd = sys.stdin.readline()
        cmd = cmd.strip() + "\n"

def engine_cli(args, file):
    """Command interface to engine, args is a List of strings, if not
    None or empty, the list will be concatenated and sent to the engine
    at host:port"""

    try:
        cl_sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
        print "connecting to " + file
        cl_sock.connect(file)
        if args and len(args) > 0:
            send_msg(" ".join(args) + "\n", cl_sock)
        else:
            run(cl_sock)
        cl_sock.shutdown(0)
        return 0
    except socket.error, serr:
        if args and len(args) > 0:
            start_cmd = " ".join(args)
            if start_cmd[:5] == "start":
                try:
                    p = subprocess.Popen("/usr/sbin/ods-signerd", shell=True)
                    retcode = os.waitpid(p.pid, 0)[1]
                    if retcode < 0:
                        print "Engine was terminated by signal"
                    else:
                        return retcode
                except OSError, e:
                    print "Unable to start engine: " + e
                    return 1

        if (hasattr(serr, "errno") and serr.errno == 2) or \
           isinstance(serr, socket.error) and serr[0] == 2:
            print "Unable to connect to engine. It might not be running."
        else:
            print "Error opening domain socket %s: %s" % (file, str(serr))
            print serr.__class__
            raise serr
        return 1

def parse_args():
    """Parse options"""
    try:
        parser = optparse.OptionParser("Usage: %prog [options] command\n\nThe command 'help' shows a complete list of commands")
        #parser.add_option("-n", "--host", dest="host", default="localhost")
        parser.add_option("-a", "--all",
                  action="store_true", dest="all", default=False,
                  help="when using the command sign, sign all zones")

        parser.add_option("-f", "--file", dest="file",
                          help="connect to domain socket <file>",
                          default="/var".replace("${prefix}", "/usr")+\
                                  os.sep + \
                                  "run" + os.sep +\
                                  "opendnssec" + os.sep + \
                                  "engine.sock")
        (options, arguments) = parser.parse_args()
        if options.all:
            arguments.append("--all")
        return engine_cli(arguments, options.file)
    except ValueError, vae:
        print str(vae)
        return 1

if __name__ == "__main__":
    # if we have no arguments, go to interactive mode
    try:
        result = parse_args()
        sys.exit(result)
    except SystemExit, e:
        raise
    except Exception, e:
        sys.exit(2)
