#!/usr/local/bin/perl -w

use strict;
use warnings;
use English;
use OpenXPKI::Debug;
use OpenXPKI::Control;
use Getopt::Long;
use Pod::Usage;
use POSIX ":sys_wait_h";
use Errno;
use File::Spec;
use Log::Log4perl qw( :easy );

use OpenXPKI::VERSION;


my $MAX_ATTEMPTS = 300;
our $VERSION ;

$| = 1;


my %params;
GetOptions(\%params,
       qw(
          help|?
          man
          cfg|cfgfile|conf|config=s
          instance|i=s
          version
          debug=s@
          keep-temp-files=s
          quiet
          fg|foreground
          nd|no-detach
          nocensor
          )) or pod2usage(-verbose => 0);

if ($params{version}) {
    print "OpenXPKI Version: $OpenXPKI::VERSION::VERSION\n";
    exit 0;
}

pod2usage(-exitstatus => 0, -verbose => 2) if $params{man};
pod2usage(-verbose => 1) if ($params{help});
if (scalar @ARGV != 1) {
    print STDERR "Usage: openxpkictl [OPTIONS] start|stop|restart|reload|status|version\n";
    exit 0;
}

# The arguments array for all calls
my $args;

if ($params{quiet}) {
    $args->{SILENT} = 1;
} else {
    $args->{SILENT} = 0;
}

if (defined $params{cfg}) {
    $args->{CONFIG} = File::Spec->rel2abs($params{cfg});
} elsif (defined $params{instance}) {
    $args->{CONFIG} = sprintf '/usr/local/etc/openxpki/%s/config.d/', $params{instance};
}

Log::Log4perl->easy_init($ERROR);

my $cmd = shift;

if ($cmd eq 'version') {

    OpenXPKI::Control::version( $args );
    exit 0;

} elsif ($cmd eq 'status') {

    if (OpenXPKI::Control::status( $args )) {
        exit 3;
    }
    exit 0;

} elsif ($cmd eq 'start' || $cmd eq 'restart') {

    if ($cmd eq 'restart') {
        $args->{RESTART} = 1;
    }

    if (defined $params{fg}) {
        $args->{FOREGROUND} = 1;
    }

    if (defined $params{nd}) {
        $args->{NODETACH} = 1;
    }

    if (defined $params{debug}) {
        @{$params{debug}} = split(m{,}, join(',', @{$params{debug}}));
        $args->{DEBUG_LEVEL} = {};
        $args->{DEBUG_BITMASK} = {};
        $args->{DEBUG_NOCENSOR} = 1 if (defined $params{nocensor});

        for my $param (@{ $params{debug} }) {
            my ($module, $op, $level) = ($param =~ m{ \A ((?!\d).+?)?([:=])?((0b)?\d+)? \z }xms);

            # default values if not specified
            $level //= 1;
            $op = ':' if (not $module and not $op); # if only number was given: interpret as level
            $module //= '.*';

            # convert binary bitmask/level specifications
            if ($level =~ /^0b(.*)/) {
                $level = unpack("N", pack("B32", substr("0"x32 . $1, -32)));
            }

            # operator ":" - a maximum level
            if ($op and $op eq ":") {
                $args->{DEBUG_LEVEL}->{$module} = $level;
            }
            # operator "=" - a bitmask
            else {
                # also assume it's a bitmask if no operator and no number were given
                $args->{DEBUG_BITMASK}->{$module} = $level;
            }
        }
    }

    if ($params{'keep-temp-files'}) {
        if ($params{'keep-temp-files'} eq 'yes') {
            $args->{KEEP_TEMP} = 1;
        } else {
            warn sprintf("You need to set --keep-temp-files to 'yes' ('%s' given) ", $params{'keep-temp-files'});
        }
    }

    exit OpenXPKI::Control::start( $args );

} elsif ($cmd eq 'stop') {

    exit OpenXPKI::Control::stop( $args );

} elsif ($cmd eq 'reload') {

    exit OpenXPKI::Control::reload( $args );

}

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

print STDERR "Unknown command: $cmd.\n";
exit 1;

__END__

=head1 NAME

openxpkictl - start/stop script for OpenXPKI server

=head1 USAGE

openxpkictl [options] COMMAND

 Commands:
   start            Start OpenXPKI daemon
   stop             Stop OpenXPKI daemon
   reload           Reload the configuration
   restart          Restart OpenXPKI daemon
   status           Get OpenXPKI daemon status
   version          Print the OpenXPKI version and license info

See below for supported options.

=head1 ARGUMENTS

Available commands:

=over 8

=item B<start>

Starts the OpenXPKI daemon.

=item B<stop>

Stops the OpenXPKI daemon.

=item B<reload>

Reloads the OpenXPKI daemon, re-reading the config repository.
Note: Some changes need a restart, see the documentation!

=item B<restart>

Restarts the OpenXPKI daemon.

=item B<status>

Checks the OpenXPKI daemon status.

=item B<version>

Print information on the version and license.

=back

=head1 OPTIONS

=over 8

=item B<--help>

Print a brief help message and exits.

=item B<--man>

Prints the manual page and exits.

=item B<--config|cfg PATH>

Use PATH to point to the configuration repository (base of yaml tree).
Defaults to /usr/local/etc/openxpki/config.d

=item B<--instance|i NAME>

Shortcut to set the config path when running multiple instances using
the proposed config path layout (/usr/local/etc/openxpki/I<instance>/config.d).

=item B<--version>

Print program version and exit.

=item B<--debug MODULE:LEVEL>

Set specific module debug level to LEVEL (must be a positive integer).
Higher values mean more debug output. MODULE must be a module
specification (e. g. OpenXPKI::Server) and may contain Perl Regular
expressions.

LEVEL can be specified as a decadic or binary number (e.g. 5 or 0b101).
LEVEL defaults to 1 if not specified.

If MODULE is omitted the given LEVEL will be set for all modules.

You can add multiple --debug options on one command line.

Examples:

  --debug
 (equivalent to --debug .*:1)

  --debug OpenPKI::Server
  (equivalent to --debug OpenXPKI::Server:1)

  --debug OpenPKI::Server:100
  (equivalent to --debug OpenXPKI::Server:100)

  --debug OpenPKI::Server:10 --debug OpenXPKI::Crypto::.*:20

=item B<--debug MODULE[=BITMASK]>

Show debug messages of MODULE whose level fits into the given BITMASK
(i.e. "level AND BITMASK == level").
BITMASK can be specified as a decadic or binary number (e.g. 5 or
0b101). If not given BITMASK defaults to 1.

=item B<--nocensor>

Turn off censoring in the Debug module.

=item B<--keep-temp-files yes>

Do not delete temporary files.
B<WARNING>: Files might contain confidential data!

=item B<--no-detach|nd>

Do not fork away the control process - useful to run inside containers
or from systemd.

=item B<--foreground|fg>

Does not fork program away and B<uses a non-forking server>. This
limits the OpenXPKI server to handle only one request at a time, so
this is pretty much useful only for debugging and profiling.

B<DO NOT USE THIS FOR PRODUCTION>.

=back

=head1 DESCRIPTION

B<openxpkictl> is the start script for the OpenXPKI server process.

=over 8

The openxpkictl script returns a 0 exit value on success, and >0 if  an
error occurs.

=back
