#!/usr/bin/perl

eval 'exec /usr/bin/perl  -S $0 ${1+"$@"}'
    if 0; # not running under some shell
#
# Copyright 2006-2007 SPARTA, Inc.  All rights reserved.  See the COPYING
# file distributed with this software for details.
#
#
# rollctl
#
#	This script controls the rollover daemon.
#	See the pod for more details.
#

use strict;

use Getopt::Long qw(:config no_ignore_case_always);

use Net::DNS::SEC::Tools::rollmgr;
use Net::DNS::SEC::Tools::tooloptions;

#
# Version information.
#
my $NAME   = "rollctl";
my $VERS   = "$NAME version: 0.7";
my $DTVERS = "DNSSEC-Tools Version: 1.3";

#######################################################################

#
# Data required for command line options.
#
my %options = ();			# Filled option array.
my @opts =
(
	"halt",				# Shutdown rollerd.
	"display",			# Turn on rollerd's graphical display.
	"dspub=s",			# Parent has published a DS record.
	"dspuball",			# Parents have published DS records.
	"logfile=s",			# Set rollerd's log file.
	"loglevel=s",			# Set rollerd's logging level.
	"nodisplay",			# Turn off rollerd's graphical display.
	"rollall",			# ZSK roll all our zones.
	"rollksk=s",			# Roll the specified zone.
	"rollrec=s",			# Change the rollrec file.
	"rollzone=s",			# ZSK roll the specified zone.
	"runqueue",			# Run the queue.
	"shutdown",			# Shutdown rollerd.
	"skipall",			# Stop all zones from rolling.
	"skipzone=s",			# Stop the named zone from rolling.
	"sleeptime=i",			# Set rollerd's sleep time.
	"status",			# Get rollerd's status.
	"zonelog=s",			# Set a zone's logging level.
	"zonestatus",			# Get status of zones.

	"Version",			# Display the version number.
	"quiet",			# Don't print anything.
	"help",				# Give a usage message and exit.
);

#
# Flags for the options.  Variable/option mapping should obvious.
#
my $dispflag;
my $dspubflag;
my $dspuballflag;
my $logfileflag;
my $loglevelflag;
my $nodispflag;
my $rollallflag;
my $rollkskflag;
my $rollrecflag;
my $rollzoneflag;
my $runqueueflag;
my $shutdownflag;
my $skipallflag;
my $skipzoneflag;
my $sleeptimeflag;
my $statusflag;
my $zonelogflag;
my $zonestatflag;

my $quiet;

my $version	= 0;			# Display the version number.

#######################################################################


my $ret;				# Return code from main().

$ret = main();
exit($ret);

#-----------------------------------------------------------------------------
# Routine:	main()
#
# Purpose:	Yeah, yeah, a main() isn't necessary.  However, it offends my
#		sense of aesthetics to have great gobs of code on the same
#		level as a pile of globals.
#
#		But what about all those globals, you ask...
#
sub main()
{
	my $argc = @ARGV;		# Number of command line arguments.

	my $rcret = 0;			# Return code for rollctl.
	my $ret;			# Return code from rollerd.
	my $resp;			# Response message from rollerd.

	#
	# Check our options.  All the commands are alphabetized, except
	# for shutdown.  We'll save that for last.
	#
	doopts($argc);

	#
	# Send commands for all the specified options.
	#
	if($dispflag)
	{
		rollmgr_sendcmd(CHANNEL_WAIT,ROLLCMD_DISPLAY,1);

		($ret, $resp) = rollmgr_getresp();
		if($ret == ROLLCMD_RC_OKAY)
		{
			print "rollerd display started\n";
		}
		else
		{
			print STDERR "rollerd display not started\n";
			$rcret++;
		}
	}
	if($dspubflag)
	{
		rollmgr_sendcmd(CHANNEL_WAIT,ROLLCMD_DSPUB,$dspubflag);

		($ret, $resp) = rollmgr_getresp();
		if($ret == ROLLCMD_RC_OKAY)
		{
			print "rollerd informed that parent has published DS record for zone $dspubflag\n";
		}
		else
		{
			print STDERR "rollerd not informed that parent has published DS record for zone $dspubflag\n";
			print STDERR "resp - <$dspubflag>\n";
			$rcret++;
		}
	}
	if($dspuballflag)
	{
		rollmgr_sendcmd(CHANNEL_WAIT,ROLLCMD_DSPUBALL,$dspuballflag);

		($ret, $resp) = rollmgr_getresp();
		if($ret == ROLLCMD_RC_OKAY)
		{
			print "rollerd informed that parents have published DS record for all zones in KSK rollover phase 6\n";
		}
		else
		{
			print STDERR "rollerd not informed that parents have published DS record for all zones in KSK rollover phase 6\n";
			print STDERR "resp - <$dspubflag>\n";
			$rcret++;
		}
	}
	if($logfileflag)
	{
		rollmgr_sendcmd(CHANNEL_WAIT,ROLLCMD_LOGFILE,$logfileflag);

		($ret, $resp) = rollmgr_getresp();
		if($ret == ROLLCMD_RC_OKAY)
		{
			print "rollerd log file set to $logfileflag\n";
		}
		else
		{
			print STDERR "log-level set failed:  $resp\n";
			$rcret++;
		}
	}
	if($loglevelflag)
	{
		rollmgr_sendcmd(CHANNEL_WAIT,ROLLCMD_LOGLEVEL,$loglevelflag);

		($ret, $resp) = rollmgr_getresp();
		if($ret == ROLLCMD_RC_OKAY)
		{
			print "rollerd log level set to $loglevelflag\n";
		}
		else
		{
			print STDERR "log-level set failed:  $resp\n";
			$rcret++;
		}
	}
	if($nodispflag)
	{
		rollmgr_sendcmd(CHANNEL_WAIT,ROLLCMD_DISPLAY,0);

		($ret, $resp) = rollmgr_getresp();
		if($ret == ROLLCMD_RC_OKAY)
		{
			print "rollerd display stopped\n";
		}
		else
		{
			print STDERR "rollerd display not stopped\n";
			$rcret++;
		}
	}
	if($rollallflag)
	{
		rollmgr_sendcmd(CHANNEL_WAIT,ROLLCMD_ROLLALL);

		($ret, $resp) = rollmgr_getresp();
		if($ret == ROLLCMD_RC_OKAY)
		{
			print "all zones now in rollover:  $resp\n";
		}
		else
		{
			print STDERR "$resp";
			$rcret++;
		}
	}
	if($rollrecflag)
	{
		rollmgr_sendcmd(CHANNEL_WAIT,ROLLCMD_ROLLREC,$rollrecflag);

		($ret, $resp) = rollmgr_getresp();
		if($ret == ROLLCMD_RC_OKAY)
		{
			print "rollerd now using rollrec file $rollrecflag\n";
		}
		else
		{
			print STDERR "couldn't set rollrec file:  $resp\n";
			$rcret++;
		}
	}
	if($rollzoneflag)
	{
		rollmgr_sendcmd(CHANNEL_WAIT,ROLLCMD_ROLLZONE,$rollzoneflag);

		($ret, $resp) = rollmgr_getresp();
		if($ret == ROLLCMD_RC_OKAY)
		{
			print "$resp\n";
		}
		else
		{
			print STDERR "unable to force rollover process for $rollzoneflag:  $resp\n";
			$rcret++;
		}
	}
	if($runqueueflag)
	{
		rollmgr_sendcmd(CHANNEL_WAIT,ROLLCMD_RUNQUEUE);

		($ret, $resp) = rollmgr_getresp();
		if($ret == ROLLCMD_RC_OKAY)
		{
			print "rollerd checking rollrec queue\n";
		}
		else
		{
			#
			# Shouldn't ever get here...
			#
			print STDERR "couldn't force the rollrec queue:  $resp\n";
			$rcret++;
		}
	}
	if($skipallflag)
	{
		rollmgr_sendcmd(CHANNEL_WAIT,ROLLCMD_SKIPALL,$skipzoneflag);

		($ret, $resp) = rollmgr_getresp();
		if($ret == ROLLCMD_RC_OKAY)
		{
			print "rollover stopped for all zones:  $resp\n";
		}
		else
		{
			print STDERR "$resp";
			$rcret++;
		}
	}
	if($skipzoneflag)
	{
		rollmgr_sendcmd(CHANNEL_WAIT,ROLLCMD_SKIPZONE,$skipzoneflag);

		($ret, $resp) = rollmgr_getresp();
		if($ret == ROLLCMD_RC_OKAY)
		{
			print "rollover stopped for zone $skipzoneflag\n";
		}
		else
		{
			print STDERR "unable to stop rollover for zone $skipzoneflag:  \"$resp\"\n";
			$rcret++;
		}
	}
	if($sleeptimeflag)
	{
		rollmgr_sendcmd(CHANNEL_WAIT,ROLLCMD_SLEEPTIME,$sleeptimeflag);

		($ret, $resp) = rollmgr_getresp();
		if($ret == ROLLCMD_RC_OKAY)
		{
			print "rollerd sleep time set to $sleeptimeflag\n";
		}
		else
		{
			print STDERR "sleep-time set failed:  \"$resp\"\n";
			$rcret++;
		}
	}
	if($statusflag)
	{
		rollmgr_sendcmd(CHANNEL_WAIT,ROLLCMD_STATUS);

		($ret, $resp) = rollmgr_getresp();
		if($ret == ROLLCMD_RC_OKAY)
		{
			print "$resp";
		}
		else
		{
			print STDERR "status failed:  \"$resp\"\n";
			$rcret++;
		}
	}
	if($shutdownflag)
	{
		rollmgr_sendcmd(CHANNEL_WAIT,ROLLCMD_SHUTDOWN);
		($ret, $resp) = rollmgr_getresp();
		if($ret == ROLLCMD_RC_OKAY)
		{
			print "$resp\n";
		}
		else
		{
			print STDERR "shutdown failed:  \"$resp\"\n";
			$rcret++;
		}
	}
	if($zonelogflag)
	{
		rollmgr_sendcmd(CHANNEL_WAIT,ROLLCMD_ZONELOG,$zonelogflag);

		($ret, $resp) = rollmgr_getresp();
		if($ret == ROLLCMD_RC_OKAY)
		{
			print "rollerd logging changed for $zonelogflag\n";
		}
		else
		{
			print STDERR "zonelog failed:  $resp\n";
			$rcret++;
		}
	}
	if($zonestatflag)
	{
		rollmgr_sendcmd(CHANNEL_WAIT,ROLLCMD_ZONESTATUS);

		($ret, $resp) = rollmgr_getresp();
		if($ret == ROLLCMD_RC_OKAY)
		{
			print "$resp";
		}
		else
		{
			print STDERR "zonestatus failed:  \"$resp\"\n";
			$rcret++;
		}
	}

	return($rcret);
}

#-----------------------------------------------------------------------------
# Routine:	doopts()
#
# Purpose:	This routine shakes and bakes our command line options.
#		A bunch of option variables are set according to the specified
#		options.  Then a little massaging is done to make sure that
#		the proper actions are taken.  A few options imply others, so
#		the implied options are set if the implying options are given.
#
sub doopts
{
	my $argc = shift;			# Command line argument count.

	#
	# Give a usage flag if there aren't any options.
	#
	usage() if($argc == 0);

	#
	# Parse the options.
	#
	GetOptions(\%options,@opts) || usage();

	#
	# Set our option variables based on the parsed options.
	#
	$dispflag	= $options{'display'}	 || 0;
	$nodispflag	= $options{'nodisplay'}	 || 0;
	$dspubflag	= $options{'dspub'}	 || 0;
	$dspuballflag	= $options{'dspuball'}	 || 0;
	$quiet		= $options{'quiet'}	 || 0;
	$logfileflag	= $options{'logfile'}	 || 0;
	$loglevelflag	= $options{'loglevel'}	 || 0;
	$rollallflag	= $options{'rollall'}	 || 0;
	$rollkskflag	= $options{'rollksk'}	 || 0;
	$rollrecflag	= $options{'rollrec'}	 || 0;
	$rollzoneflag	= $options{'rollzone'}	 || 0;
	$runqueueflag	= $options{'runqueue'}	 || 0;
	$shutdownflag	= ($options{'shutdown'}  || $options{'halt'})   || 0;
	$skipallflag	= $options{'skipall'}	 || 0;
	$skipzoneflag	= $options{'skipzone'}	 || 0;
	$sleeptimeflag	= $options{'sleeptime'}	 || 0;
	$statusflag	= $options{'status'}	 || 0;
	$zonelogflag	= $options{'zonelog'}	 || 0;
	$zonestatflag	= $options{'zonestatus'} || 0;
	$version        = $options{'Version'}    || 0;

	#
	# Close our output descriptors if the -quiet option was given.
	#
	if($quiet)
	{
		close(STDOUT);
		close(STDERR);
	}

	#
	# Show the version number if requested
	#
	version() if(defined($options{'Version'}));

	#
	# Give a usage flag if asked.
	#
	usage() if(defined($options{'help'}));

	#
	# Ensure that conflicting options weren't given.
	#
	if($dispflag && $nodispflag)
	{
		print STDERR "-display and -nodisplay are mutually exclusive\n";
		exit(1);
	}
}

#----------------------------------------------------------------------
# Routine:	version()
#
# Purpose:	Print the version number(s) and exit.
#
sub version
{
	print STDERR "$VERS\n";
	print STDERR "$DTVERS\n";

	exit(1);
}


#-----------------------------------------------------------------------------
# Routine:	usage()
#
sub usage
{
	print STDERR "usage:  rollctl [options] \n";
	print STDERR "\t-halt				shutdown rollerd\n";
	print STDERR "\t-display			start graphical display\n";
	print STDERR "\t-dspub <zone>			parent has published DS record for zone\n";
	print STDERR "\t-dspuball			parents have published DS records for zones\n";
	print STDERR "\t-logfile <logfile>		set log file\n";
	print STDERR "\t-loglevel <loglevel>		set logging level\n";
	print STDERR "\t-nodisplay			stop graphical display\n";
	print STDERR "\t-rollall			roll all zones\n";
	print STDERR "\t-rollksk <rollrec>		roll specified zone's KSK\n";
	print STDERR "\t-rollrec <rollrec>		set rollrec file\n";
	print STDERR "\t-rollzone <zone>		roll named zone\n";
	print STDERR "\t-runqueue			run queue\n";
	print STDERR "\t-shutdown			shutdown rollerd\n";
	print STDERR "\t-skipall <zone>			skip named zone\n";
	print STDERR "\t-skipzone <zone>		skip named zone\n";
	print STDERR "\t-sleeptime <sleeptime>		set sleep time\n";
	print STDERR "\t-status				get rollerd's status\n";
	print STDERR "\t-zonelog			set a zone's log level\n";
	print STDERR "\t-zonestatus			get status of zones\n";
	print STDERR "\t-Version			display version number\n";
	print STDERR "\t-quiet				don't give any output\n";
	print STDERR "\t-help				help message \n";
	exit(0);
}

1;

##############################################################################
#

=pod

=head1 NAME

rollctl - Send commands to the DNSSEC-Tools rollover daemon

=head1 SYNOPSIS

  rollctl [options]

=head1 DESCRIPTION

The B<rollctl> command sends commands to the DNSSEC-Tools rollover daemon,
B<rollerd>.  Multiple options may be specified on a single command line and
they will be executed in I<alphabetical> order.  The exception to this
ordering is that the I<-shutdown> command will always be executed last.

In most cases, B<rollerd> will send a response to B<rollctl>.  B<rollctl> will
print a success or failure message, as appropriate.

=head1 OPTIONS

The following options are handled by B<rollctl>.

=over 4

=item B<-display>

Starts the rollover status GUI.

=item B<-dspub zone>

Indicates that I<zone>'s parent has published a new DS record for I<zone>.

=item B<-dspuball>

Indicates that DS records have been published for all zones in phase 6 of
KSK rollover.

=item B<-halt>

Cleanly halts B<rollerd> execution.

=item B<-logfile logfile>

Sets the B<rollerd> log file to I<logfile>.
This must be a valid logging file, meaning that if I<logfile> already
exists, it must be a regular file.  The only exceptions to this are if
I<logfile> is B</dev/stdout> or B</dev/tty>.

=item B<-loglevel loglevel>

Sets the B<rollerd> logging level to I<loglevel>.
This must be one of the valid logging levels defined in B<rollmgr.pm(3)>.

=item B<-nodisplay>

Stops the rollover status GUI.

=item B<-rollall>

Initiates rollover for all the zones defined in the current I<rollrec> file.

=item B<-rollrec rollrec_file>

Sets the I<rollrec> file to be processed by B<rollerd> to I<rollrec_file>.

=item B<-rollzone zone>

Initiates rollover for the zone named by I<zone>.

=item B<-runqueue>

Wakes up B<rollerd> and has it run its queue of I<rollrec> entries.

=item B<-shutdown>

Synonym for B<-halt>.

=item B<-skipall>

Stops rollover for all zones in the current I<rollrec> file.

=item B<-skipzone zone>

Stops rollover for the zone named by I<zone>.

=item B<-sleeptime sleeptime>

Sets B<rollerd>'s sleep time to I<sleeptime>.  I<sleeptime> must be an integer
at least as large as the B<$MIN_SLEEP> value in B<rollerd>.

=item B<-status>

Has B<rollerd> write several of its operational parameters to its log file.
The parameters are also reported to B<rollctl>, which prints them to the
screen.

=item B<-zonelog>

Set the logging level for the specified zone.  The new logging level is only
for the current execution of B<rollerd> and is not saved to the active
I<rollrec> file.

=item B<-zonestatus>

Has B<rollerd> write the status of zones in the current I<rollrec> file to the
B<rollerd> log file.  The status is also reported to B<rollctl>, which prints
it to the screen.

=item B<-quiet>

Prevents output from being given.  Both error and non-error output is
stopped.

=item B<-help>

Displays a usage message.

=back

=head1 FUTURE

The following modifications may be made in the future:

=over 4

=item command execution order

The commands will be executed in the order given on the command line rather
than in alphabetical order.

=back

=head1 COPYRIGHT

Copyright 2006-2007 SPARTA, Inc.  All rights reserved.
See the COPYING file included with the DNSSEC-Tools package for details.

=head1 AUTHOR

Wayne Morrison, tewok@users.sourceforge.net

=head1 SEE ALSO

B<Net::DNS::SEC::Tools::rollmgr.pm(3)>,
B<Net::DNS::SEC::Tools::rollrec.pm(3)>

B<rollerd(8)>

=cut
