Acme-Ghost

 view release on metacpan or  search on metacpan

lib/Acme/Ghost.pm  view on Meta::CPAN


See C<TODO> file

=head1 SEE ALSO

L<CTK::Daemon>, L<Net::Server::Daemonize>, L<Mojo::Server>,
L<Mojo::Server::Prefork>, L<Daemon::Daemonize>, L<MooseX::Daemonize>,
L<Proc::Daemon>

=head1 AUTHOR

Serż Minus (Sergey Lepenkov) L<https://www.serzik.com> E<lt>abalama@cpan.orgE<gt>

=head1 COPYRIGHT

Copyright (C) 1998-2026 D&D Corporation

=head1 LICENSE

This program is distributed under the terms of the Artistic License Version 2.0

See the C<LICENSE> file or L<https://opensource.org/license/artistic-2-0> for details

=cut

our $VERSION = '1.03';

use Carp qw/carp croak/;
use Cwd qw/getcwd/;
use File::Basename qw//;
use File::Spec qw//;
use POSIX qw/ :sys_wait_h SIGINT SIGTERM SIGQUIT SIGKILL SIGHUP SIG_BLOCK SIG_UNBLOCK /;

use Acme::Ghost::FilePid;
use Acme::Ghost::Log;

use constant {
    DEBUG       => $ENV{ACME_GHOST_DEBUG} || 0,
    IS_ROOT     => (($> == 0) || ($< == 0)) ? 1 : 0,
    SLEEP       => 60,
    INT_TRIES   => 3,
    LSB_COMMANDS=> [qw/start stop reload restart status/],
};

sub new {
    my $class = shift;
    my $args = @_ ? @_ > 1 ? {@_} : {%{$_[0]}} : {};
    my $name = $args->{name} || File::Basename::basename($0);
    my $user = $args->{user} // '';
    my $group = $args->{group} // '';

    # Get UID by User
    my $uid = $>; # Effect. UID
    if (IS_ROOT) {
        if ($user =~ /^(\d+)$/) {
            $uid = $user;
        } elsif (length($user)) {
            $uid = getpwnam($user) || croak "getpwnam failed - $!\n";
        }
    }
    $user = getpwuid($uid || 0) unless length $user;

    # Get GID by Group
    my $gids = $); # Effect. GIDs
    if (IS_ROOT) {
        if ($group =~ /^(\d+)$/) {
            $gids = $group;
        } elsif (length($group)) {
            $gids = getgrnam($group) || croak "getgrnam failed - $!\n";
        }
    }
    my $gid  = (split /\s+/, $gids)[0]; # Get first GID
    $group = getpwuid($gid || 0) unless length $group;

    # Check name
    croak "Can't create unnamed daemon\n" unless $name;

    my $self = bless {
        name        => $name,
        user        => $user,
        group       => $group,
        uid         => $uid,
        gid         => $gid,
        gids        => $gids,

        # PID
        pidfile     => $args->{pidfile} || File::Spec->catfile(getcwd(), sprintf("%s.pid", $name)),
        _filepid    => undef,

        # Log
        facility    => $args->{facility},
        logfile     => $args->{logfile},
        ident       => $args->{ident} || $name,
        logopt      => $args->{logopt},
        logger      => $args->{logger},
        loglevel    => $args->{loglevel},
        loghandle   => $args->{loghandle},
        _log        => undef,

        # Runtime
        initpid     => $$,  # PID of root process
        ppid        => 0,   # PID before daemonize
        pid         => 0,   # PID daemonized process
        daemonized  => 0,   # 0 - no daemonized; 1 - daemonized
        spirited    => 0,   # 0 - is not spirit; 1 - is spirit (See ::Prefork)

        # Manage
        ok          => 0,   # 1 - Ok. Process is healthy (ok)
        signo       => 0,   # The caught signal number
        interrupt   => 0,   # The interrupt counter

    }, $class;
    return $self->again(%$args);
}
sub again { shift }
sub log {
    my $self = shift;
    return $self->{_log} //= Acme::Ghost::Log->new(
        facility    => $self->{facility},
        ident       => $self->{ident},
        logopt      => $self->{logopt},
        logger      => $self->{logger},
        level       => $self->{loglevel},
        file        => $self->{logfile},
        handle      => $self->{loghandle},
    );
}
sub filepid {
    my $self = shift;
    return $self->{_filepid} //= Acme::Ghost::FilePid->new(
        file => $self->{pidfile}
    );
}



( run in 0.540 second using v1.01-cache-2.11-cpan-5735350b133 )