NAME
Acme::Ghost - An yet another view to daemon processes
SYNOPSIS
use Acme::Ghost
my $g = Acme::Ghost->new(
logfile => '/tmp/daemon.log',
pidfile => '/tmp/daemon.pid',
user => 'nobody',
group => 'nogroup',
);
$g->daemonize;
$g->log->info('Oops! I am Your Ghost');
DESCRIPTION
An yet another view to daemon processes
new
my $g = Acme::Ghost->new(
name => 'myDaemon',
user => 'nobody',
group => 'nogroup',
pidfile => '/var/run/myDaemon.pid',
logfile => '/var/log/myDaemon.log',
ident => 'myDaemon',
logopt => 'ndelay,pid',
facility => 'user',
logger => Mojo::Log->new,
loglevel => 'debug',
loghandle => IO::Handler->new,
);
ATTRIBUTES
This class implements the following attributes
facility
facility => 'user',
This attribute sets facility for logging
See "facility" in Acme::Ghost::Log
group
group => 'nogroup',
group => 65534,
This attribute sets group/gid for spawned process
ident
ident => 'myDaemon',
This attribute sets ident string for system log (syslog)
logfile
logfile => '/var/log/myDaemon.log',
This attribute sets log file path. By default all log entries will be printed to syslog
See "file" in Acme::Ghost::Log
logger
logger => Mojo::Log->new,
This attribute perfoms to set predefined logger, eg. Mojo::Log. If you set this attribute, the specified logger will be used as the preferred logger
loghandle
Log filehandle, defaults to opening "file" or uses syslog if file not specified
See "handle" in Acme::Ghost::Log
loglevel
loglevel => 'debug',
This attribute sets the log level
See "level" in Acme::Ghost::Log
logopt
logopt => 'ndelay,pid',
This attribute contains zero or more of the options
See "logopt" in Acme::Ghost::Log
name
name => 'myDaemon',
This attribute sets name of daemon. Default: script name basename($0)
pidfile
pidfile => '/var/run/myDaemon.pid',
This attribute sets PID file path. Default: ./<NAME>.pid
user
user => 'nobody',
user => 65534,
This attribute sets user/uid for spawned process
METHODS
This class implements the following methods
again
This method is called immediately after creating the instance and returns it
NOTE: Internal use only for subclasses!
daemonize
$g = $g->daemonize;
Main routine for just daemonize. This routine will check on the pid file, safely fork, create the pid file (storing the pid in the file), become another user and group, close STDIN, STDOUT and STDERR, separate from the process group (become session leader), and install $SIG{INT} to remove the pid file. In otherwords - daemonize. All errors result in a die
filepid
my $filepid = $g->filepid;
This method returns Acme::Ghost::FilePid object
flush
$self = $self->flush;
This internal method flush (resets) process counters to defaults. Please do not use this method in your inherits
is_daemonized
$g->is_daemonized or die "Your ghost process really is not a daemon"
This method returns status of daemon:
True - the process is an daemon;
False - the process is not daemon;
is_spirited
my $is_spirited = $g->is_spirited;
This method returns status of spirit:
True - the process is an spirit;
False - the process is not spirit;
log
my $log = $g->log;
This method returns Acme::Ghost::Log object
ok
$g->ok or die "Interrupted!";
This method checks process state and returns boolean status of healthy. If this status is false, then it is immediately to shut down Your process as soon as possible, otherwise your process will be forcibly destroyed within 7 seconds from the moment your process receives the corresponding signal
pid
print $g->pid;
This method returns PID of the daemon
set_gid
$g = $g->set_gid('1000 10001 10002');
$g = $g->set_gid(1000);
$g = $g->set_gid('nogroup');
$g = $g->set_gid;
Become another group. Arguments are groups (or group ids or space delimited list of group ids). All errors die
set_uid
$g = $g->set_uid(1000);
$g = $g->set_uid('nobody');
$g = $g->set_uid;
Become another user. Argument is user (or userid). All errors die
CONTROL METHODS
List of LSB Daemon Control Methods
These methods can be used to control the daemon behavior. Every effort has been made to have these methods DWIM (Do What I Mean), so that you can focus on just writing the code for your daemon.
ctrl
exit $g->ctrl( shift @ARGV, 'USR2' );
# start, stop, restart, reload, status
Daemon Control Dispatcher with using USR2 to reloading
exit $g->ctrl( shift @ARGV, 0 );
This example shows how to forced suppress reloading (disable send users signals to daemon)
reload
$exit_code = $g->reload; # SIGHUP (by default)
$exit_code = $g->reload('USR2'); # SIGUSR2
$exit_code = $g->reload(12); # SIGUSR2 too
say "Reloading ". $g->pid;
This method performs sending signal to Your daemon and return 0
as exit code. This method is primarily intended to perform a daemon reload
restart
$exit_code = $g->restart;
if ($exit_code) {
say STDERR "Restart failed " . $g->pid;
} else {
say "Restart successful";
}
This method performs restarting the daemon and returns 0
as successfully exit code or 1
in otherwise
start
my $exit_code = $g->start;
say "Running ". $g->pid;
exit $exit_code;
This method performs starting the daemon and returns 0
as exit code. The spawned process calls the startup handler and exits with status 0
as exit code without anything return
status
if (my $runned = $g->status) {
say "Running $runned";
} else {
say "Not running";
}
This method checks the status of running daemon and returns its PID (alive). The method returns 0 if it is not running (dead).
stop
if (my $runned = $g->stop) {
if ($runned < 0) {
die "Daemon " . $g->pid ." is still running";
} else {
say "Stopped $runned";
}
} else {
say "Not running";
}
This method performs stopping the daemon and returns:
+PID -- daemon stopped successfully
0 -- daemon is not running
-PID -- daemon is still running, stop failed
HOOKS
This class implements the following user-methods (hooks). Each of the following methods may be implemented (overwriting) in a your class
preinit
sub preinit {
my $self = shift;
# . . .
}
The preinit() method is called before spawning (forking)
init
sub init {
my $self = shift;
# . . .
}
The init() method is called after spawning (forking) and after daemonizing
startup
sub startup {
my $self = shift;
# . . .
}
The startup() method is called after daemonizing in service mode
This is your main hook into the service, it will be called at service startup. Meant to be overloaded in a subclass.
cleanup
sub cleanup {
my $self = shift;
my $scope = shift; # 0 or 1
# . . .
}
The cleanup() method is called at before exit This method passes one argument:
0 -- called at normal DESTROY;
1 -- called at interrupt
NOTE! On DESTROY phase logging is unpossible. We not recommended to use logging in this method
hangup
sub hangup {
my $self = shift;
# . . .
}
The hangup() method is called on HUP or USR2 signals
For example (on Your inherit subclass):
sub init {
my $self = shift;
# Listen USR2 (reload)
$SIG{HUP} = sub { $self->hangup };
}
sub hangup {
my $self = shift;
$self->log->debug(">> Hang up!");
}
EXAMPLES
- ghost_simple.pl
-
This is traditional way to start daemons
use Acme::Ghost; my $g = Acme::Ghost->new( logfile => 'daemon.log', pidfile => 'daemon.pid', ); my $cmd = shift(@ARGV) // 'start'; if ($cmd eq 'status') { if (my $runned = $g->status) { print "Running $runned\n"; } else { print "Not running\n"; } exit 0; # Ok } elsif ($cmd eq 'stop') { if (my $runned = $g->stop) { if ($runned < 0) { print STDERR "Failed to stop " . $g->pid . "\n"; exit 1; # Error } print "Stopped $runned\n"; } else { print "Not running\n"; } exit 0; # Ok } elsif ($cmd ne 'start') { print STDERR "Command incorrect\n"; exit 1; # Error } # Daemonize $g->daemonize; my $max = 10; my $i = 0; while (1) { $i++; sleep 3; $g->log->debug(sprintf("> %d/%d", $i, $max)); last if $i >= $max; }
- ghost_acme.pl
-
Simple acme example of daemon with reloading demonstration
my $g = MyGhost->new( logfile => 'daemon.log', pidfile => 'daemon.pid', ); exit $g->ctrl(shift(@ARGV) // 'start'); # start, stop, restart, reload, status 1; package MyGhost; use parent 'Acme::Ghost'; sub init { my $self = shift; $SIG{HUP} = sub { $self->hangup }; # Listen USR2 (reload) } sub hangup { my $self = shift; $self->log->debug("Hang up!"); } sub startup { my $self = shift; my $max = 100; my $i = 0; while ($self->ok) { $i++; sleep 3; $self->log->debug(sprintf("> %d/%d", $i, $max)); last if $i >= $max; } } 1;
- ghost_ioloop.pl
-
Mojo::IOLoop example
my $g = MyGhost->new( logfile => 'daemon.log', pidfile => 'daemon.pid', ); exit $g->ctrl(shift(@ARGV) // 'start', 0); # start, stop, restart, reload, status 1; package MyGhost; use parent 'Acme::Ghost'; use Mojo::IOLoop; sub init { my $self = shift; $self->{loop} = Mojo::IOLoop->new; } sub startup { my $self = shift; my $loop = $self->{loop}; my $i = 0; # Add a timers my $timer = $loop->timer(5 => sub { my $l = shift; # loop $self->log->info("Timer!"); }); my $recur = $loop->recurring(1 => sub { my $l = shift; # loop $l->stop unless $self->ok; $self->log->info("Tick! " . ++$i); $l->stop if $i >= 10; }); $self->log->debug("Start IOLoop"); # Start event loop if necessary $loop->start unless $loop->is_running; $self->log->debug("Finish IOLoop"); } 1;
- ghost_ae.pl
-
AnyEvent example
my $g = MyGhost->new( logfile => 'daemon.log', pidfile => 'daemon.pid', ); exit $g->ctrl(shift(@ARGV) // 'start', 0); # start, stop, restart, reload, status 1; package MyGhost; use parent 'Acme::Ghost'; use AnyEvent; sub startup { my $self = shift; my $quit = AnyEvent->condvar; my $i = 0; # Create watcher timer my $watcher = AnyEvent->timer (after => 1, interval => 1, cb => sub { $quit->send unless $self->ok; }); # Create process timer my $timer = AnyEvent->timer(after => 3, interval => 3, cb => sub { $self->log->info("Tick! " . ++$i); $quit->send if $i >= 10; }); $self->log->debug("Start AnyEvent"); $quit->recv; # Run! $self->log->debug("Finish AnyEvent"); } 1;
- ghost_nobody.pl
-
This example shows how to start daemons over nobody user and logging to syslog (default)
my $g = MyGhost->new( pidfile => '/tmp/daemon.pid', user => 'nobody', group => 'nogroup', ); exit $g->ctrl(shift(@ARGV) // 'start', 0); # start, stop, restart, status 1; package MyGhost; use parent 'Acme::Ghost'; sub startup { my $self = shift; my $max = 100; my $i = 0; while ($self->ok) { $i++; sleep 3; $self->log->debug(sprintf("> %d/%d", $i, $max)); last if $i >= $max; } } 1;
DEBUGGING
You can set the ACME_GHOST_DEBUG
environment variable to get some advanced diagnostics information printed to STDERR
.
ACME_GHOST_DEBUG=1
TO DO
See TODO
file
SEE ALSO
CTK::Daemon, Net::Server::Daemonize, Mojo::Server, Mojo::Server::Prefork, Daemon::Daemonize, MooseX::Daemonize, Proc::Daemon
AUTHOR
Serż Minus (Sergey Lepenkov) https://www.serzik.com <abalama@cpan.org>
COPYRIGHT
Copyright (C) 1998-2024 D&D Corporation. All Rights Reserved
LICENSE
This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
See LICENSE
file and https://dev.perl.org/licenses/