CTKlib

 view release on metacpan or  search on metacpan

lib/CTK/Daemon.pm  view on Meta::CPAN

    sub run {
        my $self = shift;
        my $logger = $self->logger;
        $logger->log_info("Code is running");

        my $step = 5;
        while ($self->ok) { # Check it every time

            # If occurred usual error:
            #    $logger->log_error("...");
            #    mysleep SLEEP;
            #    next;

            # If occurred exception error
            #    $logger->log_crit("...");
            #    $self->exception(1);
            #    last;

            # For skip this loop
            #    $self->skip(1);
            #    next;

            last unless $self->ok; # Check it every time (after loop too)
        } continue {
            CTK::Daemon::mysleep $step if $step; # Delay! For avoid fast restarts
        }

        return 1;
    }

=head1 DESCRIPTION

Abstract class to implement Daemons

=head2 FEATURES

=over 8

=item *

Write PID file /var/run/$name.pid to make sure only one instance is running.

=item *

Correctly daemonize (redirect STDIN/STDOUT)

=item *

Restart by stop/start, exec, or signal HUP

=item *

Daemon restart on error

=item *

Handle worker processes

=item *

Run as different user using setuid/setgid

=back

=head2 METHODS

=over 8

=item new

    my $daemon = CTK::Daemon->new('testdaemon', (
        ctk         => CTK::App->new(...), # Or create CTKx instance first
        debug       => 1, # Default: 0
        loglevel    => "debug", # Default: undef
        forks       => 3, # Default: 1
        uid         => "username", # Default: undef
        gid         => "groupname", # Default: undef
        saferun     => 0, # Set to 1 to enable safe mode for the run method calling
    ));

Daemon constructor

=item ctk, get_ctk

    my $ctk = $daemon->get_ctk;

Returns CTK object

=item ctrl

    exit ctrl( shift @ARGV ); # start, stop, restart, reload, status

LSB Control handler. Dispatching

=item logger

    my $logger = $daemon->logger;

Returns logger object

=item logger_close

    $daemon->logger_close;

Destroy logger

=item exit_daemon

    $self->exit_daemon(0);
    $self->exit_daemon(1);

Exit with status code

=item init, down, run, reload, cleanup

Base methods for overwriting in your class.

The init() method is called at startup - before forking

The run() method is called at inside process and describes body of the your code. This code is called at startup of each forks

The down() method is called at cleanup - after processing each forks

The reload() method is called at received HUP signal, this code is called at before running of each forks

The cleanup() method is called at before exit from main fork

=item start, stop, restart, status and hup

LSB methods. For internal use only

=item exception

    $exception = $self->exception;
    $self->exception(exception);

Gets/Sets exception value

lib/CTK/Daemon.pm  view on Meta::CPAN

                    $logger->log_debug("Checking if pid $pidstat is still running...") if $debug && $logger;
                    last unless $pidf->running;
                    sleep 1;
                }
                last unless $pidf->running;
            }
            if ( $pidf->running ) {
                warn("Failed to Stop");
                $logger->log_warn("Failed to Stop") if $logger;
                return 1;
            }
            my $tpid = $pidf->_get_pid_from_file;
            unlink($pidfile) if $tpid && (-e $pidfile) && $pidstat == $tpid;
            print("Stopped\n");
            $logger->log_debug("Stopped") if $logger;
        } else {
            print("Not Running\n");
            $logger->log_debug("Not Running") if $logger;
        }
        $pidstat = 0;
        return 0;
    };

    # Reloading processes (LSB)
    my $lsbreload = sub {
        if ($pidstat) {
            kill "HUP" => $pidstat;
            print("Reloaded\n");
            $logger->log_debug("Reloaded") if $logger;
        } else {
            print("Not Running\n");
            $logger->log_debug("Not Running") if $logger;
        }
        return 0;
    };

    # Get status (LSB)
    my $lsbstatus = sub {
        if ($pidstat) {
            print("Running\n");
            $logger->log_debug("Running") if $logger;
        } else {
            print("Not Running\n");
            $logger->log_debug("Not Running") if $logger;
        }
        return 0;
    };

    my $self = bless {
        name        => $name,
        ctk         => $ctk,
        ppid        => 0,
        pidfile     => $pidfile,
        initpid     => $$,
        initpidf    => $pidf,
        initpidstat => $pidstat, # From pid file!
        masterpid   => undef,
        workerpid   => undef,
        workerident => undef,
        sigs        => $sigs,
        gid         => $params{gid} || undef,
        uid         => $params{uid} || undef,
        forkers     => $forkers,
        saferun     => $params{saferun} || 0,

        debug       => $debug,
        loglevel    => $loglevel,
        logger      => undef,
        socketopts  => $params{socketopts},
        syslogopts  => $params{syslogopts},

        lsbstop     => $lsbstop,
        lsbreload   => $lsbreload,
        lsbstatus   => $lsbstatus,

        # General properties
        reloaded    => 0, # Reload counter
        interrupt   => 0, # For common interruption only
        exception   => 0, # For exceptions
        hangup      => 0, # For reloading
        skip        => 0, # For skipping of subprocesses
    }, $class;

    $logger = $self->logger(); # For init logger

    return $self;
}

#
# General methods
#

sub worker {
    my $self = shift;
    my $logger = $self->logger;
    my $j = $self->{workerident} || 0;
    $self->{ppid} = _getppid();

    # Signals Trapping for worker-proccess interruption
    my $anon = sub {
        if ($self->interrupt >= TRIES) {
            $logger->log_crit("Can't terminate worker #%d pid=%d", $j, $$) if $logger;
            die(sprintf("Can't terminate worker #%d pid=%d\n", $j, $$));
        }
        $self->{interrupt}++;
    };
    local $SIG{TERM} = $anon;
    local $SIG{INT} = $anon;
    local $SIG{QUIT} = $anon;
    local $SIG{KILL} = $anon;
    local $SIG{HUP} = sub {$self->{hangup}++};

    $logger->log_debug("Start worker #%d pid=%d", $j, $$) if $self->{debug} && $logger;
    RELOAD: if ($self->hangup) {
        $self->reinit_worker();
        $self->{reloaded}++;
        $self->reload(); # User defined method
    }

    # Running
    my $status;

lib/CTK/Daemon.pm  view on Meta::CPAN

    # Starting
    if (my $pidstat = $self->{initpidstat}) {
        printf STDERR "Daemon already started (pid=%d; file=%s)\n", $pidstat, $self->{initpidf}->file();
    } else {
        return $self->start();
    }
    return 0;
}

# CTK object getters
sub get_ctk {
    my $self = shift;
    return $self->{ctk};
}
sub ctk { goto &get_ctk }

#
# Methods for overwriting in user class
#

# Please overwrite in subclass
#  this is called at startup - before forking
sub init {
    my $self = shift;
    return 1;
}
# Please overwrite in subclass
#  this is called at cleanup - after processing each forks
sub down {
    my $self = shift;
    return 1;
}
# Please overwrite in subclass
#  this is called at startup of each forks
sub run {
    my $self = shift;
    return 1;
}
# Please overwrite in subclass
#  this is called at before running of each forks
sub reload {
    my $self = shift;
    return 1;
}
# Please overwrite in subclass
#  this is called at before exit from main fork
sub cleanup {
    my $self = shift;
    return 1;
}

#
# LSB methods
#

sub start {
    my $self = shift;
    my $logger = $self->logger;

    # Load GID and UID
    my ($uid, $gid);
    if (my $uidstr = $self->{uid}) {
        $uid = getpwnam($uidstr) || croak "getpwnam failed - $!\n";
    }
    if (my $gidstr = $self->{gid}) {
        $gid = getgrnam($gidstr) || croak "getgrnam failed - $!\n";
    }

    # PidFile prepare
    if (defined($uid) or defined($gid)) {
        my $pidfile = $self->{pidfile};
        unless (-e $pidfile) {
            CTK::Util::fsave($pidfile, "0\n");
            chown($uid, $gid, $pidfile) if -e $pidfile;
        }
    }

    # Set GID and UID
    if (defined($gid)) {
        POSIX::setgid($gid) || croak "setgid $gid failed - $!\n";
        $) = "$gid $gid"; # this calls setgroups
        croak "detected strange gid\n" if !($( eq "$gid $gid" && $) eq "$gid $gid"); # just to be sure
    }
    if (defined($uid)) {
        POSIX::setuid($uid) || croak "setuid $uid failed - $!\n";
        croak "detected strange uid\n" if !($< == $uid && $> == $uid); # just to be sure
    }

    my $save_pid = $$;
    #say "PID> $$";
    #say "INITPID> ".$self->{initpid};
    my $pidf = $self->{initpidf};

    # Run init handler
    $self->init() or return 1; # error level (exit code)

    # Start master process
    my $pid = myfork();
    if ($pid) {
        print("Started\n");
        if ($self->{debug}) {
            $logger->log_debug("Master process (pid=%d) successfully started", $pid) if $logger;
        } else {
            $logger->log_debug("Started") if $logger;
        }
    }
    $self->logger_close;

    if ( defined($pid) && $pid == 0 ) { # The master child runs here.
        $pidf->pid(isostype('Windows') ? $save_pid : $$);
        $self->{masterpid} = $pidf->pid;
        $pidf->write;

        # Detach the child from the terminal (no controlling tty), make it the
        # session-leader and the process-group-leader of a new process group.
        unless ($DEV_DEBUG || isostype('Windows')) {
            die "Can't detach from controlling terminal" if POSIX::setsid() < 0;
        }

        # Catching the signals
        $SIG{$_} = $sigproxy for keys %LOCAL_SIG;
        $self->{sigs} = {%LOCAL_SIG};
        #say Dumper($self);


        # Second fork. See Proc::Daemon
        my (@pids, %pidh);
        for (my $j = 1; $j <= $self->{forkers}; $j++) {
            my $cpid = myfork();
            if ( defined($cpid) && $cpid == 0 ) { # Here the second child is running.
                # Close all file handles and descriptors the user does not want to preserve.
                my $devnull = File::Spec->devnull;
                unless ($DEV_DEBUG || ($self->{debug} && isostype('Windows'))) {
                    open( STDIN, "<", $devnull ) or die "Failed to open STDIN to $devnull: $!";;
                    open( STDOUT, ">>", $devnull ) or die "Failed to open STDOUT to $devnull: $!";
                    open( STDERR, ">>", $devnull ) or die "Failed to open STDERR to $devnull: $!";
                }

                # CODE
                $self->{workerpid} = $$;
                $self->{workerident} = $j;
                my $status = $self->worker();



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