File Coverage

blib/lib/App/DistSync/Lock.pm
Criterion Covered Total %
statement 18 85 21.1
branch 0 42 0.0
condition 0 23 0.0
subroutine 6 11 54.5
pod 4 4 100.0
total 28 165 16.9


line stmt bran cond sub pod time code
1             package App::DistSync::Lock; # $Id: Lock.pm 27 2019-07-23 11:26:37Z abalama $
2 1     1   71340 use warnings;
  1         10  
  1         40  
3 1     1   5 use strict;
  1         2  
  1         22  
4 1     1   620 use utf8;
  1         17  
  1         5  
5              
6             =encoding utf-8
7              
8             =head1 NAME
9              
10             App::DistSync::Lock - Lock File Manipulation
11              
12             =head1 VERSION
13              
14             Version 1.01
15              
16             =head1 SYNOPSIS
17              
18             use File::Pid;
19              
20             my $lock = new App::DistSync::Lock(
21             file => '/some/file.lock',
22             hold => 3600,
23             pid => $$,
24             );
25              
26             if ( my $pid = $lock->running ) {
27             die "Already running: $num\n";
28             }
29              
30             =head1 DESCRIPTION
31              
32             This module manages a lock file. It will create a lock file,
33             query the process within to discover if it's still running, and remove
34             the lock file. This module based on L and L.
35              
36             =head2 new
37              
38             my $lock = new App::DistSync::Lock;
39              
40             my $lock = new App::DistSync::Lock(
41             file => '/var/run/daemon.pid',
42             hold => 3600,
43             pid => $$,
44             );
45              
46              
47             This constructor takes three optional paramters.
48              
49             C - The name of the lock file to work on. If not specified, a lock
50             file located in current directory will be created that matches
51             C.
52              
53             C - Max amount of seconds before breaking lock (0 for never, default is 3600)
54              
55             C - The pid to write to a new lockfile. If not specified, C<$$> is
56             used when the lock file doesn't exist. When the lock file does exist, the
57             pid inside it is used.
58              
59             =head2 running
60              
61             my $pid = $lock->running;
62             die "Service already running: $pid\n" if $pid;
63              
64             Checks to see if the pricess identified in the lock file is still
65             running. If the process is still running, the pid is returned. Otherwise
66             C or 0 is returned.
67              
68             =head2 status
69              
70             my $status = $lock->status;
71              
72             Returns current status
73              
74             =head2 error
75              
76             my $error = $lock->error;
77              
78             Returns current error message
79              
80             =head1 HISTORY
81              
82             See C file
83              
84             =head1 DEPENDENCIES
85              
86             L
87              
88             =head1 TO DO
89              
90             See C file
91              
92             =head1 BUGS
93              
94             * none noted
95              
96             =head1 SEE ALSO
97              
98             L
99              
100             =head1 AUTHOR
101              
102             Serż Minus (Sergey Lepenkov) L Eabalama@cpan.orgE
103              
104             =head1 COPYRIGHT
105              
106             Copyright (C) 1998-2019 D&D Corporation. All Rights Reserved
107              
108             =head1 LICENSE
109              
110             This program is free software; you can redistribute it and/or
111             modify it under the same terms as Perl itself.
112              
113             See C file and L
114              
115             =cut
116              
117 1     1   52 use vars qw/$VERSION/;
  1         2  
  1         80  
118             $VERSION = '1.01';
119              
120 1     1   8 use File::Basename qw/basename/;
  1         1  
  1         90  
121 1     1   7 use Carp;
  1         2  
  1         865  
122              
123             sub new {
124 0     0 1   my $class = shift;
125 0           my $stamp = time;
126 0           my ($name) = basename($0);
127              
128 0           my $self = bless({@_,
129             status => 0,
130             error => "",
131             stamp => $stamp,
132             name => $name,
133             run => 0,
134             }, $class);
135              
136 0   0       $self->{file} ||= 'MANIFEST.LOCK';
137 0           my $file = $self->{file};
138              
139 0 0 0       if (exists($self->{pid}) && defined($self->{pid})) {
140 0 0         croak("Incorrect pid specified") unless $self->{pid} =~ /^[0-9]{1,11}$/;
141             } else {
142 0           $self->{pid} = $$;
143             }
144 0           my $pid = $self->{pid};
145              
146 0 0 0       if (exists($self->{hold}) && defined($self->{hold})) {
147 0 0         croak("Incorrect hold specified") unless $self->{hold} =~ /^[0-9]{1,11}$/;
148             } else {
149 0           $self->{hold} = 3600;
150             }
151 0           my $hold = $self->{hold};
152              
153             # Current string
154 0           my $str = sprintf("%d#%d#%s", $pid, $stamp, $name);
155              
156             # Check existing file
157 0           local *RD_LOCK_FILE;
158 0 0 0       if ($file && -e $file) {
159 0 0         unless (open(RD_LOCK_FILE, "<", $file)) {
160 0           $self->{error} = sprintf("Can't open file %s to read: %s", $file, $!);
161 0           return $self;
162             }
163 0           my $l;
164 0           chomp($l = );
165 0 0         $l = "" unless defined $l;
166 0 0         unless (close RD_LOCK_FILE) {
167 0           $self->{error} = sprintf("Can't close file %s: %s", $file, $!);
168 0           return $self;
169             }
170 0 0         if ($l eq $str) {
171             # This procces == Checking process
172 0           $self->{error} = "Process already exists";
173 0           return $self;
174             } else {
175 0           my ($r_pid, $r_stamp, $r_name) = split(/#/, $l);
176 0 0 0       if ($r_pid && $self->running($r_pid)) {
177 0           $self->{run} = $r_pid;
178             # If file too old to be considered stale?
179 0 0 0       if (($hold > 0) && (($stamp - $r_stamp) > $hold)) {
180 0 0         unless (unlink $file) {
181 0           $self->{error} = sprintf("Can't unlink file %s: %s", $file, $!);
182 0           return $self;
183             }
184             } else {
185 0           $self->{error} = "Process #$r_pid already running";
186 0           return $self;
187             }
188             }
189             }
190             }
191              
192             # Create new file
193 0           local *MY_LOCK_FILE;
194 0 0         unless (open(MY_LOCK_FILE, ">", $file)) {
195 0           $self->{error} = sprintf("Can't open file %s to write: %s", $file, $!);
196 0           return $self;
197             }
198 0 0         unless (print MY_LOCK_FILE sprintf("%s\n", $str)) {
199 0           $self->{error} = sprintf("Can't print to file %s: %s", $file, $!);
200 0           return $self;
201             }
202 0 0         unless (close MY_LOCK_FILE) {
203 0           $self->{error} = sprintf("Can't close file %s: %s", $file, $!);
204 0           return $self;
205             }
206              
207 0           $self->{status} = 1;
208 0           return $self;
209             }
210             sub error {
211 0     0 1   my $self = shift;
212             #my $s = shift;
213             #$self->{error} = $s if defined $s;
214 0           return $self->{error};
215             }
216             sub status {
217 0     0 1   my $self = shift;
218             #my $s = shift;
219             #$self->{status} = $s if defined $s;
220 0           return $self->{status};
221             }
222             sub running {
223 0     0 1   my $self = shift;
224 0           my $pid = shift;
225 0 0         $pid = $self->{run} unless defined $pid;
226 0 0         return 0 unless $pid;
227 0 0         unless ($pid =~ /^[0-9]{1,11}$/) {
228 0           carp("Incorrect pid specified");
229 0           return 0;
230             }
231 0 0         return kill(0, $pid)
232             ? $pid
233             : 0;
234             }
235              
236             sub DESTROY {
237 0     0     my $self = shift;
238 0           my $file = $self->{file};
239 0           my $status = $self->{status};
240 0 0 0       return unless $file && $status && -e $file;
      0        
241 0 0         unlink $file or carp(sprintf("Can't unlink file %s: %s", $file, $!));
242             }
243              
244             1;