// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*-

// Copyright (c) 2001-2007 International Computer Science Institute
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software")
// to deal in the Software without restriction, subject to the conditions
// listed in the XORP LICENSE file. These conditions include: you must
// preserve this copyright notice, and you cannot mention the copyright
// holders in advertising related to the Software without their permission.
// The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
// notice is a summary of the XORP LICENSE file; the license in that file is
// legally binding.

#ident "$XORP: xorp/cli/xrl_cli_node.cc,v 1.30 2007/02/16 22:45:29 pavlin Exp $"

#include "cli_module.h"
#include "libxorp/xorp.h"
#include "libxorp/xlog.h"
#include "libxorp/debug.h"
#include "libxorp/ipvx.hh"
#include "libxorp/status_codes.h"
#include "libxorp/token.hh"

#include "xrl_cli_node.hh"
#include "cli_node.hh"


XrlCliNode::XrlCliNode(EventLoop&	eventloop,
		       const string&	class_name,
		       const string&	finder_hostname,
		       uint16_t		finder_port,
		       const string&	finder_target,
		       CliNode&		cli_node)
    : XrlStdRouter(eventloop, class_name.c_str(), finder_hostname.c_str(),
		   finder_port),
    XrlCliTargetBase(&xrl_router()),
      _eventloop(eventloop),
      _cli_node(cli_node),
      _xrl_cli_processor_client(&xrl_router()),
      _is_finder_alive(false)
{
    _cli_node.set_send_process_command_callback(
	callback(this, &XrlCliNode::send_process_command));

    UNUSED(finder_target);
}

//
// XrlCliNode front-end interface
//
int
XrlCliNode::enable_cli()
{
    int ret_code = XORP_OK;
    
    cli_node().enable();
    
    return (ret_code);
}

int
XrlCliNode::disable_cli()
{
    int ret_code = XORP_OK;
    
    cli_node().disable();
    
    return (ret_code);
}

int
XrlCliNode::start_cli()
{
    int ret_code = XORP_OK;
    
    if (cli_node().start() < 0)
	ret_code = XORP_ERROR;
    
    return (ret_code);
}

int
XrlCliNode::stop_cli()
{
    int ret_code = XORP_OK;
    
    if (cli_node().stop() < 0)
	ret_code = XORP_ERROR;
    
    return (ret_code);
}

//
// Finder-related events
//
/**
 * Called when Finder connection is established.
 *
 * Note that this method overwrites an XrlRouter virtual method.
 */
void
XrlCliNode::finder_connect_event()
{
    _is_finder_alive = true;
}

/**
 * Called when Finder disconnect occurs.
 *
 * Note that this method overwrites an XrlRouter virtual method.
 */
void
XrlCliNode::finder_disconnect_event()
{
    XLOG_ERROR("Finder disconnect event. Exiting immediately...");

    _is_finder_alive = false;

    stop_cli();
}

XrlCmdError
XrlCliNode::common_0_1_get_target_name(
    // Output values, 
    string&	name)
{
    name = my_xrl_target_name();
    return XrlCmdError::OKAY();
}

XrlCmdError
XrlCliNode::common_0_1_get_version(
    // Output values, 
    string&	version)
{
    version = XORP_MODULE_VERSION;
    return XrlCmdError::OKAY();
}

XrlCmdError
XrlCliNode::common_0_1_get_status(
    // Output values, 
    uint32_t& status,
    string&	reason)
{
    // XXX: Default to PROC_READY status because this probably won't be used.
    status = PROC_READY;
    reason = "Ready";
    return XrlCmdError::OKAY();
}

XrlCmdError
XrlCliNode::common_0_1_shutdown()
{
    // TODO: XXX: PAVPAVPAV: implement it!!
    return XrlCmdError::COMMAND_FAILED("Not implemented yet");
}

XrlCmdError
XrlCliNode::cli_manager_0_1_enable_cli(
    // Input values,
    const bool&	enable)
{
    string error_msg;
    int ret_value;

    if (enable)
	ret_value = enable_cli();
    else
	ret_value = disable_cli();

    if (ret_value != XORP_OK) {
	if (enable)
	    error_msg = "Failed to enable CLI";
	else
	    error_msg = "Failed to disable CLI";
	return XrlCmdError::COMMAND_FAILED(error_msg);
    }

    return XrlCmdError::OKAY();
}

XrlCmdError
XrlCliNode::cli_manager_0_1_start_cli()
{
    if (start_cli() != XORP_OK) {
	return XrlCmdError::COMMAND_FAILED("Failed to start CLI");
    }
    
    return XrlCmdError::OKAY();
}

XrlCmdError
XrlCliNode::cli_manager_0_1_stop_cli()
{
    string error_msg;

    if (stop_cli() != XORP_OK) {
	error_msg = c_format("Failed to stop CLI");
	return XrlCmdError::COMMAND_FAILED(error_msg);
    }
    
    return XrlCmdError::OKAY();
}

XrlCmdError
XrlCliNode::cli_manager_0_1_add_enable_cli_access_from_subnet4(
    // Input values, 
    const IPv4Net&	subnet_addr)
{
    //
    // XXX: we don't need to verify the address family, because we are
    // handling both IPv4 and IPv6.
    //

    cli_node().add_enable_cli_access_from_subnet(IPvXNet(subnet_addr));
    
    return XrlCmdError::OKAY();
}

XrlCmdError
XrlCliNode::cli_manager_0_1_add_enable_cli_access_from_subnet6(
    // Input values, 
    const IPv6Net&	subnet_addr)
{
    //
    // XXX: we don't need to verify the address family, because we are
    // handling both IPv4 and IPv6.
    //

    cli_node().add_enable_cli_access_from_subnet(IPvXNet(subnet_addr));
    
    return XrlCmdError::OKAY();
}

XrlCmdError
XrlCliNode::cli_manager_0_1_delete_enable_cli_access_from_subnet4(
    // Input values, 
    const IPv4Net&	subnet_addr)
{
    string error_msg;

    //
    // XXX: we don't need to verify the address family, because we are
    // handling both IPv4 and IPv6.
    //

    if (cli_node().delete_enable_cli_access_from_subnet(IPvXNet(subnet_addr))
	!= XORP_OK) {
	error_msg = c_format("Failed to delete enabled CLI access from subnet %s",
			     cstring(subnet_addr));
	return XrlCmdError::COMMAND_FAILED(error_msg);
    }
    
    return XrlCmdError::OKAY();
}

XrlCmdError
XrlCliNode::cli_manager_0_1_delete_enable_cli_access_from_subnet6(
    // Input values, 
    const IPv6Net&	subnet_addr)
{
    string error_msg;

    //
    // XXX: we don't need to verify the address family, because we are
    // handling both IPv4 and IPv6.
    //

    if (cli_node().delete_enable_cli_access_from_subnet(IPvXNet(subnet_addr))
	!= XORP_OK) {
	error_msg = c_format("Failed to delete enabled CLI access from subnet %s",
			     cstring(subnet_addr));
	return XrlCmdError::COMMAND_FAILED(error_msg);
    }
    
    return XrlCmdError::OKAY();
}

XrlCmdError
XrlCliNode::cli_manager_0_1_add_disable_cli_access_from_subnet4(
    // Input values, 
    const IPv4Net&	subnet_addr)
{
    //
    // XXX: we don't need to verify the address family, because we are
    // handling both IPv4 and IPv6.
    //

    cli_node().add_disable_cli_access_from_subnet(IPvXNet(subnet_addr));
    
    return XrlCmdError::OKAY();
}

XrlCmdError
XrlCliNode::cli_manager_0_1_add_disable_cli_access_from_subnet6(
    // Input values, 
    const IPv6Net&	subnet_addr)
{
    //
    // XXX: we don't need to verify the address family, because we are
    // handling both IPv4 and IPv6.
    //

    cli_node().add_disable_cli_access_from_subnet(IPvXNet(subnet_addr));
    
    return XrlCmdError::OKAY();
}

XrlCmdError
XrlCliNode::cli_manager_0_1_delete_disable_cli_access_from_subnet4(
    // Input values, 
    const IPv4Net&	subnet_addr)
{
    string error_msg;

    //
    // XXX: we don't need to verify the address family, because we are
    // handling both IPv4 and IPv6.
    //

    if (cli_node().delete_disable_cli_access_from_subnet(IPvXNet(subnet_addr))
	!= XORP_OK) {
	error_msg = c_format("Failed to delete disabled CLI access from subnet %s",
			     cstring(subnet_addr));
	return XrlCmdError::COMMAND_FAILED(error_msg);
    }
    
    return XrlCmdError::OKAY();
}

XrlCmdError
XrlCliNode::cli_manager_0_1_delete_disable_cli_access_from_subnet6(
    // Input values, 
    const IPv6Net&	subnet_addr)
{
    string error_msg;

    //
    // XXX: we don't need to verify the address family, because we are
    // handling both IPv4 and IPv6.
    //

    if (cli_node().delete_disable_cli_access_from_subnet(IPvXNet(subnet_addr))
	!= XORP_OK) {
	error_msg = c_format("Failed to delete disabled CLI access from subnet %s",
			     cstring(subnet_addr));
	return XrlCmdError::COMMAND_FAILED(error_msg);
    }
    
    return XrlCmdError::OKAY();
}


XrlCmdError
XrlCliNode::cli_manager_0_1_add_cli_command(
    // Input values, 
    const string&	processor_name, 
    const string&	command_name, 
    const string&	command_help, 
    const bool&		is_command_cd, 
    const string&	command_cd_prompt, 
    const bool&		is_command_processor)
{
    string error_msg;
    
    if (cli_node().add_cli_command(processor_name,
				   command_name,
				   command_help,
				   is_command_cd,
				   command_cd_prompt,
				   is_command_processor,
				   error_msg)
	!= XORP_OK) {
	return XrlCmdError::COMMAND_FAILED(error_msg);
    }
    return XrlCmdError::OKAY();
}

XrlCmdError
XrlCliNode::cli_manager_0_1_delete_cli_command(
    // Input values, 
    const string&	processor_name, 
    const string&	command_name
    )
{
    string error_msg;

    if (cli_node().delete_cli_command(processor_name,
				      command_name,
				      error_msg)
	!= XORP_OK) {
	return XrlCmdError::COMMAND_FAILED(error_msg);
    }

    return XrlCmdError::OKAY();
}

//
// The CLI client-side (i.e., the CLI sending XRLs)
//

//
// Send a request to a CLI processor
//
void
XrlCliNode::send_process_command(const string& target,
				 const string& processor_name,
				 const string& cli_term_name,
				 uint32_t cli_session_id,
				 const vector<string>& command_global_name,
				 const vector<string>& command_argv)
{
    if (! _is_finder_alive)
	return;		// The Finder is dead

    string command_line = token_vector2line(command_global_name);
    string args_line = token_vector2line(command_argv);

    //
    // Send the request
    //
    _xrl_cli_processor_client.send_process_command(
	target.c_str(),
	processor_name,
	cli_term_name,
	cli_session_id,
	command_line,
	args_line,
	callback(this, &XrlCliNode::recv_process_command_output));
    
    return;
}

//
// Process the response of a command processed by a remote CLI processor
//
void
XrlCliNode::recv_process_command_output(const XrlError& xrl_error,
					const string *processor_name,
					const string *cli_term_name,
					const uint32_t *cli_session_id,
					const string *command_output)
{
    if (xrl_error == XrlError::OKAY()) {
	cli_node().recv_process_command_output(processor_name,
					       cli_term_name,
					       cli_session_id,
					       command_output);
	return;
    }

    //
    // TODO: if the command failed because of transport error,
    // then we should retransmit it.
    //
    XLOG_ERROR("Failed to process a command: %s", xrl_error.str().c_str());
}


syntax highlighted by Code2HTML, v. 0.9.1