File Coverage

blib/lib/Svsh.pm
Criterion Covered Total %
statement 3 42 7.1
branch 0 12 0.0
condition 0 17 0.0
subroutine 1 5 20.0
pod 2 2 100.0
total 6 78 7.6


line stmt bran cond sub pod time code
1             package Svsh;
2              
3             # ABSTRACT: Process supervision shell for daemontools/perp/s6/runit
4              
5             our $VERSION = "1.003000";
6             $VERSION = eval $VERSION;
7              
8 1     1   75540 use Moo::Role;
  1         19079  
  1         4  
9              
10             =head1 NAME
11              
12             Svsh - Process supervision shell for daemontools/perp/s6/runit (base class)
13              
14             =head1 SYNOPSIS
15              
16             package Svsh::SomeSupervisor;
17              
18             use Moo;
19             use namespace::clean;
20              
21             with 'Svsh';
22              
23             # implement required methods
24              
25             =head1 DESCRIPTION
26              
27             C is a shell for process supervision suites of the C family,
28             including C, C and C. Refer to L for documentation of
29             the shell itself. This file documents the base class for Svsh adapter classes.
30              
31             =head1 ATTRIBUTES
32              
33             =head2 basedir
34              
35             I.
36              
37             The base directory from which the process supervisor is managing services.
38              
39             =cut
40              
41             has 'basedir' => (
42             is => 'ro',
43             required => 1
44             );
45              
46             =head2 bindir
47              
48             I.
49              
50             The directory where the process supervisor's tools are located. Any call to
51             the supervisor's tools will be prefixed with this path if provided. For usage
52             in case the tools are not in the running user's C environment variable.
53              
54             =cut
55              
56             has 'bindir' => (
57             is => 'ro'
58             );
59              
60             =head2 collapse
61              
62             I.
63              
64             A boolean indicating whether the L option should be
65             enabled.
66              
67             =cut
68              
69             has 'collapse' => (
70             is => 'rw',
71             default => sub { 0 }
72             );
73              
74             =head2 statuses
75              
76             I.
77              
78             A hash-ref of services and their statuses (this is automatically populated by
79             the respective C method in the adapter classes. For every service,
80             a hash-ref with C, C and C keys should exist.
81              
82             =cut
83              
84             has 'statuses' => (
85             is => 'ro',
86             writer => '_set_statuses'
87             );
88              
89             =head1 REQUIRED METHODS
90              
91             =head2 status()
92              
93             Finds all services managed by the supervisor, and populates
94             the L attribute.
95              
96             =head2 start( @services )
97              
98             Starts a list of services if they are down.
99              
100             =head2 stop( @services )
101              
102             Stops a list of services (should not restart them).
103              
104             =head2 restart( @services )
105              
106             Stops and starts a list of services. Generally, this is implemented
107             with a C signal to the services, but check with the specific
108             adapter class.
109              
110             =head2 signal( $signal, @services )
111              
112             Sends UNIX signal to a list of services.
113              
114             =head2 fg( $service )
115              
116             Finds the log file to which a service is writing, and displays it
117             on screen with the C command.
118              
119             =head1 WANTED METHODS
120              
121             These methods are not required by adapter classes. If they are not
122             implemented, they will be unavailable in the shell.
123              
124             =head2 rescan()
125              
126             Causes the supervisor to rescan the service directory to find
127             new or removed services.
128              
129             =head2 terminate()
130              
131             Terminates the supervisor. Should also terminate all running services.
132              
133             =cut
134              
135             requires qw/status start stop restart signal fg/;
136              
137             before [qw/start stop restart/] => sub {
138             $_[2]->{args} = [$_[0]->_expand_wildcards(@{$_[2]->{args}})];
139             };
140              
141             before signal => sub {
142             my ($signal, @svcs) = @{$_[2]->{args}};
143             $_[2]->{args} = [$signal, $_[0]->_expand_wildcards(@svcs)];
144             };
145              
146             around 'status' => sub {
147             my ($orig, $self) = (shift, shift);
148             $self->_set_statuses($orig->($self, @_));
149             return $self->statuses;
150             };
151              
152             =head1 METHODS
153              
154             =head2 run_cmd( $cmd, [ @args ] )
155              
156             Runs a shell command with zero or more arguments and returns its
157             output. If the C attribute is set, and the C<$cmd> is one
158             of the supervision suite's library of tools, C<$cmd> will be prefixed
159             with C.
160              
161             =cut
162              
163             sub run_cmd {
164 0     0 1   my ($self, $cmd, @args) = @_;
165              
166 0           my $options = {};
167              
168 0 0 0       $cmd = $self->bindir . '/' . $cmd
169             if $self->bindir && $cmd =~ m/^(perp|s6|sv)/;
170              
171 0 0 0       if (scalar @args && ref $args[-1]) {
172 0           $options = pop @args;
173             }
174              
175 0 0         if ($options->{as_system}) {
176 0           system($cmd, @args);
177             } else {
178 0           $cmd = join(' ', $cmd, @args);
179 0           return qx/$cmd 2>&1/;
180             }
181             }
182              
183             =head2 find_logfile( $pid )
184              
185             Finds the log file into which a logging program is currently
186             writing to. C<$pid> is the process ID of the logging program.
187             Currently, C, C, C and C
188             are supported.
189              
190             Returns C if the file is not found.
191              
192             =cut
193              
194             sub find_logfile {
195 0     0 1   my ($self, $pid) = @_;
196              
197 0   0       my $exe = readlink("/proc/$pid/exe")
198             || return;
199              
200 0           my $file;
201              
202 0 0 0       if ($exe =~ m/tinylog/ || $exe =~ m/s6-log/ || $exe =~ m/svlogd/ || $exe =~ m/multilog/) {
      0        
      0        
203             # look for a link to a /current file under /proc/$pid/fd
204 0           opendir my $dir, "/proc/$pid/fd";
205 0           ($file) = grep { m!/current$! } map { readlink("/proc/$pid/fd/$_") } grep { !/^\.\.?$/ } readdir $dir;
  0            
  0            
  0            
206 0           closedir $dir;
207             }
208              
209 0           return $file;
210             }
211              
212             ######################################################################
213             # _expand_wildcards( @services )
214             # goes over a list of services, possibly (but not necessarily)
215             # with wildcards, and returns a new list with all services
216             # that match. For example, if @services = ('sv1', 'sv2', 'worker*'),
217             # and the services worker-1 and worker-2 exist, then the
218             # method will return ('sv1', 'sv2', 'worker-1', 'worker-2')
219             ######################################################################
220              
221             sub _expand_wildcards {
222 0     0     my $self = shift;
223              
224 0           my %services;
225 0           foreach (@_) {
226 0 0         if (m/\*/) {
227             # this is a wildcard, find all services that match it
228 0           my $regex = $_; $regex =~ s/\*/.*/; $regex = qr/^$regex$/;
  0            
  0            
229 0           foreach my $sv (grep { m/$regex/ } keys %{$self->statuses}) {
  0            
  0            
230 0           $services{$sv} = 1;
231             }
232             } else {
233 0           $services{$_} = 1;
234             }
235             }
236              
237 0           return keys %services;
238             }
239              
240             #########################################################
241             # _service_dirs()
242             # returns a list of all service directories inside the
243             # base directory
244             #########################################################
245              
246             sub _service_dirs {
247 0     0     my $basedir = shift->basedir;
248              
249 0           opendir(my $dh, $basedir);
250 0 0         my @dirs = grep { !/^\./ && -d "$basedir/$_" } readdir $dh;
  0            
251 0           closedir $dh;
252              
253 0           return sort @dirs;
254             }
255              
256             =head1 BUGS AND LIMITATIONS
257              
258             Please report any bugs or feature requests to
259             L.
260              
261             =head1 AUTHOR
262              
263             Ido Perlmuter
264              
265             Thanks to the guys at the L,
266             especially Colin Booth, for helping out with suggestions and information.
267              
268             =head1 LICENSE AND COPYRIGHT
269              
270             Copyright (c) 2015-2023, Ido Perlmuter C<< ido@ido50.net >>.
271              
272             Licensed under the Apache License, Version 2.0 (the "License");
273             you may not use this file except in compliance with the License.
274             You may obtain a copy of the License at
275              
276             http://www.apache.org/licenses/LICENSE-2.0
277              
278             Unless required by applicable law or agreed to in writing, software
279             distributed under the License is distributed on an "AS IS" BASIS,
280             WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
281             See the License for the specific language governing permissions and
282             limitations under the License.
283              
284             =cut
285              
286             1;
287             __END__