File Coverage

blib/lib/MMM/Daemon.pm
Criterion Covered Total %
statement 27 133 20.3
branch 0 52 0.0
condition 0 9 0.0
subroutine 9 22 40.9
pod 3 3 100.0
total 39 219 17.8


line stmt bran cond sub pod time code
1             package MMM::Daemon;
2              
3 1     1   1812 use strict;
  1         2  
  1         45  
4 1     1   7 use warnings;
  1         3  
  1         31  
5 1     1   5 use Sys::Syslog;
  1         3  
  1         154  
6 1     1   6 use MMM::Utils;
  1         78  
  1         67  
7 1     1   5 use MMM::Config;
  1         2  
  1         183  
8 1     1   7 use Fcntl qw(:flock);
  1         3  
  1         326  
9 1     1   7 use POSIX qw(:sys_wait_h);
  1         2  
  1         9  
10              
11 1     1   176 use base qw(MMM);
  1         2  
  1         241  
12 1     1   6 use base qw(MMM::Report::Mail);
  1         3  
  1         2603  
13              
14             =head1 NAME
15              
16             MMM::Daemon
17              
18             =head1 SYNOPSIS
19              
20             use MMM::Daemon;
21             my $mmm = MMM::Daemon->new() or die "Cannot find MMM installation";
22             $mmm->run();
23              
24             =head1 DESCRIPTION
25              
26             A daemon for mmm system
27              
28             =head1 METHODS
29              
30             =cut
31              
32             sub new {
33 0     0 1   my ($class, @args) = @_;
34 0 0         my $mmm = $class->SUPER::new(@args) or return;
35              
36 0 0         if (!$mmm->{nofork}) {
37 0           Sys::Syslog::openlog('mmm', 'pid', $mmm->configval('default', 'syslog_facilities', 'daemon'));
38 0           $mmm->{use_syslog} = 1;
39             }
40              
41             $mmm
42 0           }
43              
44             sub _create_pid_file {
45 0     0     my ($self) = @_;
46 0 0         if (my $pidf = $self->configval('default', 'pidfile', PIDFILE)) {
47 0 0         if (open(my $h, '>>', $pidf)) {
48 0           autoflush $h 1;
49 0 0         if (flock($h, LOCK_EX | LOCK_NB)) {
50 0           truncate($h, 0);
51 0           print $h "$$\n";
52 0           $self->{lockfh} = $h;
53             } else {
54 0           close($h);
55 0           my $pid;
56 0 0         if (open($h, '<', $pidf)) {
57 0   0       $pid = <$h> || '';
58 0           close($h);
59             }
60 0           chomp($pid);
61 0   0       $self->log('WARNING', 'Another mmm seems running pid `%s\'',
62             $pid || 'N/A');
63 0           return 0;
64             }
65 0           return 1;
66             } else {
67 0           $self->log('WARNING', 'Cannot create pid file `%s\' %s', $pidf, $!);
68 0           return 0
69             }
70             } else {
71 0           $self->log('WARNING', 'No pid file configured');
72 0           return 0;
73             }
74              
75 0           0
76             }
77              
78             sub _delete_pid_file {
79 0     0     my ($self) = @_;
80 0 0         if ($self->{lockfh}) {
81 0           close($self->{lockfh});
82             }
83 0 0         if (my $pidf = $self->configval('default', 'pidfile', PIDFILE)) {
84 0 0         unlink($pidf) or $self->log('WARNING', 'Cannot delete pid file `%s\' %s', $pidf, $!);
85             }
86             }
87              
88             sub _reload_config {
89 0     0     my ($self) = @_;
90 0           my $config = Config::IniFiles->new(
91             -file => $self->{configfile},
92             -default => 'default',
93             );
94 0 0         if ($config) {
95 0           $self->{config} = $config;
96 0           $self->_parse_config();
97 0           $self->log('NOTICE', 'Configuration reload');
98 0           return 1;
99             } else {
100 0           $self->log('ERROR', 'Failure will reloading configuration');
101 0           return 0;
102             }
103             }
104              
105             sub run {
106 0     0 1   my ($self) = @_;
107              
108              
109 0 0         if (!$self->{nofork}) {
110 0           $self->log('DEBUG', 'Going into background');
111 0           my $pid = fork;
112 0 0         if (!defined($pid)) {
113 0           return;
114             }
115              
116 0 0         if ($pid) {
117 0           sleep 1;
118 0           my $ret = waitpid($pid, &WNOHANG);
119 0 0         if ($ret) {
120 0           die "Daemon has stopped, check log (exit with $ret status)\n";
121             }
122 0           exit(0);
123             }
124             }
125              
126 0 0         $self->_create_pid_file() or return;
127              
128 0           $0 = 'mmm (DAEMON)';
129 0           $self->log('NOTICE', 'MMM::Daemon started at pid %d', $$);
130 0           $self->{dontdie} = 1;
131 0     0     $SIG{'ALRM'} = sub { $self->{next_alarm} = 0; $self->_start_pending_queue() };
  0            
  0            
132 0     0     $SIG{'TERM'} = sub { $self->{dontdie} = 0 };
  0            
133             $SIG{'INT'} = sub {
134 0     0     $self->log('INFO', 'Ctrl+C received, stoping');
135 0           $self->{dontdie} = 0;
136 0           };
137             $SIG{'HUP'} = sub {
138 0     0     alarm(0);
139 0           $self->_reload_config;
140 0           $self->{next_alarm} = 0;
141 0           $self->_start_pending_queue();
142 0           };
143             $SIG{'CHLD'} = sub {
144 0     0     $self->log('DEBUG', 'SIG CHILD received');
145 0           $self->_reap_child();
146 0           };
147 0           $self->_start_pending_queue();
148 0           while ($self->{dontdie}) {
149 0           $self->_reap_child();
150 0 0         $self->log('DEBUG', 'next alarm in %ss', ($self->{next_alarm} ? $self->{next_alarm} - scalar(time): 'N/A'));
151 0 0         sleep(3600) if ($self->{dontdie});
152             }
153 0 0         foreach (keys %{ $self->{process} || {} }) {
  0            
154 0           kill 15, $_;
155             }
156 0           $self->_delete_pid_file();
157 0           1;
158             }
159              
160             sub post_process {
161 0     0 1   my ($self, $job) = @_;
162 0           $self->log('DEBUG', 'Post process %s', $job->name);
163 0           $self->_start_pending_queue();
164             }
165              
166             sub _set_alarm {
167 0     0     my ($self, $when) = @_;
168 0 0 0       ($self->{next_alarm} || 0) != 0 && $when >= $self->{next_alarm} and return;
      0        
169 0           $self->log('DEBUG', 'Re-Alarm for %d, in %ds', $when, $when - scalar(time));
170 0           $self->{next_alarm} = $when;
171 0           my $s_alarm = $when - scalar(time);
172 0 0         alarm($s_alarm > 0 ? $s_alarm : 1);
173             }
174              
175             sub _start_pending_queue {
176 0     0     my ($self) = @_;
177 0 0         $self->{dontdie} or return;
178 0           my $waitdelay = 0;
179 0           foreach my $job ($self->get_tasks_by_name($self->list_tasks)) {
180 0 0         $job->is_disable and next;
181 0 0         $job->frequency or next;
182 0           my $next_run_time = $job->next_run_time;
183 0 0         if ($next_run_time <= scalar(time)) {
184 0           $self->_run_fork($job, 'NOTRAPLOG');
185 0 0         sleep(5) if($waitdelay); # small wait to avoid excessive load
186 0           $waitdelay = 1;
187             } else {
188 0 0         $self->log('DEBUG', '$when < 0: %d %s', $next_run_time, $job->name) if ($next_run_time <= 0);
189 0           $self->_set_alarm($next_run_time);
190             }
191             }
192             }
193              
194             1;
195              
196             __END__