The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
=head1 NAME

Clustericious::Command::stop

=head1 DESCRIPTION

Stop a running daemon.

=head1 NOTES

The different methods of starting put their pid files in
different places in the config file.   Here are some
examples :

   "hypnotoad" : {
      "pid_file"  : "/tmp/filename.pid",
      ....
    }

   "plackup" : {
      "pidfile"   : "/tmp/nother_filename.pid",
      "daemonize" : "null"    # means include "--daemonize"
   ...
   },

   "lighttpd" : {
      "env" : {
          "lighttpd_pid"    : "/tmp/third_filename.pid"
           ...
      },
   },

   "nginx" : {
     '-p' : '/home/foo/appname/nginx'
    }

=cut

package Clustericious::Command::stop;
use Clustericious::Log;
use Clustericious::App;
use Mojo::URL;
use File::Basename qw/dirname/;

use base 'Mojolicious::Command';
use Clustericious::Config;
use File::Slurp qw/slurp/;

use strict;
use warnings;

__PACKAGE__->attr(description => <<EOT);
Stop a running daemon.
EOT

__PACKAGE__->attr(usage => <<EOT);
usage $0: stop

Send a TERM signal to running daemon(s).
See Clustericious::Command::Stop.
EOT

sub _stop_pidfile {
    my $pid_file = shift;
    -e $pid_file or do { WARN "No pid file $pid_file\n"; return; };
    TRACE "pid_file : $pid_file";
    my $pid = slurp $pid_file; # dies automatically
    chomp $pid;
    TRACE "file $pid_file has pid $pid";
    _stop_pid($pid,@_);
    -e $pid_file or return;
    unlink $pid_file or WARN "Could not remove $pid_file";
}

sub _stop_pid {
    my $pid = shift;
    my $signal = shift || 'QUIT';
    unless ($pid && $pid=~/^\d+$/) {
        WARN "Bad pid '$pid'.  Not stopping process.";
        return;
    }
    kill 0, $pid or do { WARN "$pid is not running"; return; };
    INFO "Sending $signal to $pid";
    kill $signal, $pid;
    sleep 1;
    my $nap = 1;
    while (kill 0, $pid) {
        INFO "waiting for $pid";
        sleep $nap++;
        LOGDIE "pid $pid did not die" if $nap > 10;
    }
}

sub _stop_daemon {
    my $listen = shift; # e.g. http://localhost:9123
    my $port = Mojo::URL->new($listen)->port;
    my @got = `lsof -n -FR -i:$port`;
    return unless @got && $got[0];
    # Only the first one; others may be child processes
    my ($pid) = $got[0] =~ /^p(\d+)$/;
    unless ($pid) {
        WARN "could not find pid for daemon on port $port";
        return;
    }
    TRACE "Stopping pid $pid";
    _stop_pid($pid);
}

sub _stop_nginx {
    my %conf = @_;
    my $prefix = $conf{'-p'};
    my $cnf = $conf{'-c'} || "";
    $cnf = "-c $cnf" if $cnf;
    INFO "stopping nginx in $prefix";
    system("nginx -p $prefix $cnf -s quit")==0 or WARN "could not stop nginx";
}

sub _stop_apache {
    my %conf = @_;
    my $prefix = $conf{'-d'};
    INFO "stopping apache in $prefix";
    _stop_pidfile("$prefix/logs/httpd.pid",'TERM');
}

sub run {
    my $self     = shift;
    my $conf     = Clustericious::Config->new( $ENV{MOJO_APP} );

    Clustericious::App->init_logging();

    my $exe = $0;
    for (reverse $conf->start_mode) {
        DEBUG "Stopping $_ server";
        /hypnotoad/ and _stop_pidfile($conf->hypnotoad->pid_file(default => dirname($exe).'/hypnotoad.pid' ));
        /plackup/   and _stop_pidfile($conf->plackup->pidfile);
        /lighttpd/  and _stop_pidfile($conf->lighttpd->env->lighttpd_pid);
        /daemon/    and _stop_daemon($conf->daemon->listen);
        /nginx/     and _stop_nginx($conf->nginx);
        /apache/    and _stop_apache($conf->apache);
    }

    1;
}

1;