File Coverage

blib/lib/System/InitD/Runner.pm
Criterion Covered Total %
statement 36 130 27.6
branch 0 46 0.0
condition 0 8 0.0
subroutine 12 26 46.1
pod 3 14 21.4
total 51 224 22.7


line stmt bran cond sub pod time code
1             package System::InitD::Runner;
2              
3             =head NAME
4              
5             System::InitD::Runner
6              
7             =head1 DESCRIPTION
8              
9             Simple module to process common init.d tasks.
10             init.d bash scripts replacement
11              
12             =head1 AUTHOR
13              
14             Dmitriy @justnoxx Shamatrin
15              
16             =head1 USAGE
17              
18             =cut
19              
20 1     1   4 use strict;
  1         2  
  1         26  
21 1     1   4 use warnings;
  1         1  
  1         23  
22 1     1   4 no warnings qw/once/;
  1         1  
  1         34  
23              
24 1     1   5 use Carp;
  1         1  
  1         65  
25 1     1   739 use System::Process;
  1         11786  
  1         11  
26 1     1   45 use POSIX;
  1         1  
  1         6  
27 1     1   15883 use Time::HiRes;
  1         2006  
  1         5  
28              
29 1     1   1371 use System::InitD::Const;
  1         4  
  1         168  
30 1     1   1015 use System::InitD::Base;
  1         4  
  1         52  
31 1     1   11 use System::InitD::Const;
  1         3  
  1         1512  
32              
33             =over
34              
35             =item B
36              
37             new(%)
38              
39             Constructor, params:
40              
41             B
42              
43             A start command
44              
45             B
46              
47             Usage line, called by script usage
48              
49             B
50              
51             Now unused, reserved for output format
52              
53             B
54              
55             Timeout between stop and start in restart
56              
57             B
58              
59             Path to pid file, which used for monitoring
60              
61             B
62              
63             B daemon process name. Need for preventing wrong kill.
64              
65             B
66              
67             Signal, which used for daemon killing.
68              
69             =back
70              
71             =cut
72              
73              
74             sub new {
75 0     0 1   my ($class, %params) = @_;
76 0           my $self = {};
77              
78 0 0         if (!$params{start}) {
79 0           croak "Start param is required";
80             }
81              
82 0 0         if (!$params{usage}) {
83 0           croak 'Usage must be specified';
84             }
85              
86 0 0         if ($params{daemon_name}) {
87 0           $self->{daemon_name} = $params{daemon_name};
88             }
89              
90             else {
91 0           $self->{_text}->{usage} = $params{usage};
92             }
93              
94             # Command is array from now, for system
95 0           @{$self->{_commands}->{start}} = split /\s+/, $params{start};
  0            
96              
97 0 0         if ($params{restart_timeout}) {
98 0           $self->{_args}->{restart_timeout} = $params{restart_timeout};
99             }
100              
101 0 0         if ($params{pid_file}) {
102 0           $self->{pid} = System::Process::pidinfo(
103             file => $params{pid_file}
104             );
105             }
106              
107 0 0         if ($params{kill_signal}) {
108 0           $self->{_args}->{kill_signal} = $params{kill_signal};
109             }
110              
111 0 0         if ($params{process_name}) {
112 0           $self->{_args}->{process_name} = $params{process_name};
113             }
114              
115             # user and group params, added for right validation
116 0 0         if ($params{user}) {
117 0           $self->{_args}->{user} = $params{user};
118             }
119 0 0         if ($params{group}) {
120 0           $self->{_args}->{group} = $params{group};
121             }
122              
123 0           bless $self, $class;
124 0           return $self;
125             }
126              
127              
128             =over
129              
130             =item B
131              
132             Runner itself, service sub
133              
134             =back
135              
136             =cut
137              
138             sub run {
139 0     0 1   my $self = shift;
140 0 0         unless ($ARGV[0]) {
141 0           $self->usage();
142 0           return 1;
143             }
144              
145 0 0         if ($self->can($ARGV[0])) {
146 0           my $sub = $ARGV[0];
147 0           $self->$sub();
148             }
149             else {
150 0           $self->usage();
151             }
152 0           return 1;
153             }
154              
155              
156             sub start {
157 0     0 0   my $self = shift;
158              
159 0           $self->before_start();
160             # TODO: Add command check
161 0           my @command = @{$self->{_commands}->{start}};
  0            
162 0 0         if ($self->is_alive()) {
163 0           print DAEMON_ALREADY_RUNNING;
164 0           return;
165             }
166 0           system(@command);
167 0           $self->after_start();
168 0           return 1;
169             }
170              
171              
172             sub stop {
173 0     0 0   my $self = shift;
174              
175 0 0         $self->confirm_permissions() or croak "Incorrect permissions. Can't kill";
176              
177 0           $self->before_stop();
178 0 0         if ($self->{pid}) {
179 0   0       my $signal = $self->{kill_signal} // POSIX::SIGTERM;
180 0           $self->{pid}->kill($signal);
181             }
182              
183 0           $self->after_stop();
184 0           return 1;
185             }
186              
187              
188             sub restart {
189 0     0 0   my $self = shift;
190              
191 0           $self->stop();
192              
193 0           while ($self->is_alive()) {
194 0           Time::HiRes::usleep(1000);
195             }
196              
197 0           $self->start();
198 0           return 1;
199             }
200              
201              
202             sub status {
203 0     0 0   my $self = shift;
204              
205 0 0         unless ($self->{pid}) {
206 0           print DAEMON_IS_NOT_RUNNING;
207 0           exit 0;
208             }
209              
210 0 0         if ($self->is_alive()) {
211 0           print DAEMON_ALREADY_RUNNING;
212             }
213              
214 0           exit 0;
215             }
216              
217              
218             sub usage {
219 0     0 0   my $self = shift;
220              
221 0           print $self->{_text}->{usage}, "\n";
222 0           return 1;
223             }
224              
225              
226             sub is_alive {
227 0     0 0   my $self = shift;
228 0 0         return 0 unless $self->{pid};
229              
230 0 0 0       return 1 if $self->{_args}->{process_name} eq $self->{pid}->command() && $self->{pid}->cankill();
231              
232 0           return 0;
233             }
234              
235              
236             =over
237              
238             =item B
239              
240             load($,\&)
241              
242             Loads additional actions to init script, for example, add `script hello` possible via:
243              
244             $runner->load('hello', sub {print 'Hello world'})
245              
246             =back
247              
248             =cut
249              
250             sub load {
251 0     0 1   my ($self, $subname, $subref) = @_;
252              
253 0 0 0       if (!$subname || !$subref) {
254 0           croak 'Missing params';
255             }
256              
257 0 0         croak 'Subref must be a CODE ref' if (ref $subref ne 'CODE');
258              
259 1     1   5 no strict 'refs';
  1         1  
  1         110  
260 0           *{__PACKAGE__ . "\::$subname"} = $subref;
  0            
261 1     1   5 use strict 'refs';
  1         1  
  1         222  
262              
263 0           return 1;
264             }
265              
266              
267             sub confirm_permissions {
268 0     0 0   my ($self) = @_;
269              
270 0 0         unless ($self->{pid}) {
271 0           carp 'Usage of System::InitD without pidfile is deprecated ' .
272             'and will be forbidden in the future releases';
273 0           return 1;
274             }
275              
276 0 0         unless ($self->{_args}->{user}) {
277 0           carp 'Usage of System::InitD without specified user is extremely insecure ' .
278             'and will be forbidden in the future releases';
279 0           return 1;
280             }
281              
282 0 0         if ($self->{_args}->{user} ne $self->{pid}->user()) {
283 0           carp "Expected: $self->{_args}->{user}, but got: " . $self->{pid}->user()
284             . " looks like very strange. Execution was aborted.";
285 0           return 0;
286             }
287              
288 0           return 1;
289             }
290              
291 0     0 0   sub before_start {1;}
292 0     0 0   sub after_start {1;}
293              
294 0     0 0   sub before_stop {1;}
295 0     0 0   sub after_stop {1;}
296              
297              
298             1;
299              
300             __END__