package Win32::Daemon::Simple; use Data::Dumper; use Win32; use Win32::Daemon; use Win32::EventLog::Carp; use Module::Reload; use strict; use warnings; sub new { my $type = shift; my $self = bless {}, ref($type) || $type; $self->_init(@_); $self; } sub _init { my $self = shift; my %params = @_; $self->getServiceInfo(%params); # Stuff the user can override $self->{sleepTime} = $params{sleepTime} || 50; # 50 Milliseconds; # Stuff that we use internally $self->{prevState} = undef; } sub run { my $self = shift; my ($state, $message); #Win32::Daemon::AcceptedControls(SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE | SERVICE_ACCEPT_SHUTDOWN); $self->{prevState} = SERVICE_START_PENDING; Win32::Daemon::StartService(); while( SERVICE_STOPPED != ( $state = Win32::Daemon::State() ) ) { croak "Forced Stop" if (-e 'E:\commands\Daemons\stop.dat'); Module::Reload->check(); if ($state == SERVICE_START_PENDING) { $self->_startPending() } elsif ($state == SERVICE_STOP_PENDING) { $self->_stopPending() } elsif ($state == SERVICE_PAUSE_PENDING) { $self->_pausePending(); next } elsif ($state == SERVICE_CONTINUE_PENDING) { $self->_continuePending(); next } elsif ($state == SERVICE_RUNNING) { $self->body } else {$self->_unhandledMessage()}; if (SERVICE_CONTROL_NONE != ($message = Win32::Daemon::QueryLastMessage( 1 )) ) { $self->_controlInterrogate() if ($message == SERVICE_CONTROL_INTERROGATE); $self->_controlShutdown() if ($message == SERVICE_CONTROL_SHUTDOWN); } Win32::Sleep( $self->{sleepTime} ); } Win32::Daemon::StopService(); } sub body { Win32::EventLog::Carp::click('Abstract Base Class body called'); } sub _startPending { # Initialization code Win32::Daemon::State( SERVICE_RUNNING ); $_[0]->{prevState} = SERVICE_RUNNING; } sub _stopPending { # "Stopping..."; Win32::Daemon::State( SERVICE_STOPPED ); $_[0]->{prevState} = SERVICE_STOPPED; } sub _pausePending { # "Pausing..."; Win32::Daemon::State( SERVICE_PAUSED ); $_[0]->{prevState} = SERVICE_PAUSED; } sub _continuePending { # "Resuming..."; Win32::Daemon::State( SERVICE_RUNNING ); $_[0]->{prevState} = SERVICE_RUNNING; } sub _controlInterrogate { # Got here if the Service Control Manager is requesting # the current state of the service. This can happen for # a variety of reasons. Report the last state we set. my $self = shift; Win32::Daemon::State( $self->{prevState} ); } sub _controlShutdown { # Yikes! The system is shutting down. We had better clean up # and stop. # Tell the SCM that we are preparing to shutdown and that we expect # it to take 25 seconds (so don't terminate us for at least 25 seconds)... Win32::Daemon::State( SERVICE_STOP_PENDING, 25000 ); } sub _unhandledMessage { Win32::Daemon::State( $_[0]->{prevState} ); } sub getServiceInfo { my $self = shift; my %params = @_; my $this = ($self->{serviceInfo} ||= {}); return %$this if (keys(%$this)); for (qw[machine name display path user pwd description parameters]) { $this->{$_} = $params{$_}; } croak "Service Name required" unless $this->{name}; $this->{machine} ||= ''; $this->{display} ||= $self->{name}; $this->{path} ||= $^X; $this->{user} ||= ''; $this->{pwd} ||= ''; $this->{description} ||= $0; $this->{parameters} ||= $0; %$this; } sub install { my $self = shift; print Dumper($self->{serviceInfo}); if( Win32::Daemon::CreateService( $self->{serviceInfo} ) ) { print "Successfully added.\n"; } else { print "Failed to add service: " . Win32::FormatMessage( Win32::Daemon::GetLastError() ) . "\n"; } } sub remove { my $self = shift; my $serviceName = $self->{serviceInfo}{name}; if ( Win32::Daemon::DeleteService($serviceName)) { print "Successfully removed '$serviceName'.\n"; } else { print "Error: "; print Win32::Daemon::GetLastError(); } } 1;