ExecUtil.pm: new utility class for re-exec'ing oneself
[girocco.git] / Girocco / ExecUtil.pm
blobed0e2e366030cd7256966c3e6bf6f2c2f8a390c7
1 # Girocco::ExecUtil.pm -- utility to assist with re-exec'ing oneself
2 # Copyright (C) 2016 Kyle J. McKay. All rights reserved.
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, download a copy from
16 # http://www.gnu.org/licenses/gpl-2.0.html
17 # or write to the Free Software Foundation, Inc.,
18 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 package Girocco::ExecUtil;
22 use strict;
23 use warnings;
24 use Cwd;
25 use POSIX qw(_exit);
27 use base qw(Exporter);
28 use vars qw(@EXPORT @EXPORT_OK $VERSION);
30 BEGIN {
31 @EXPORT = ();
32 @EXPORT_OK = qw();
33 *VERSION = \'1.0';
36 sub new {
37 my $class = shift || __PACKAGE__;
38 $class = ref($class) if ref($class);
39 my $program = shift;
40 defined($program) or $program = $0;
41 my $argv0 = shift;
42 defined($argv0) or $argv0 = $program;
43 my $self = {};
44 %{$self->{ENV}} = %ENV;
45 $self->{program} = $program;
46 $self->{argv0} = $argv0;
47 @{$self->{ARGV}} = @ARGV;
48 $self->{cwd} = getcwd;
49 -d $self->{cwd} or die __PACKAGE__ . "::new: fatal: unable to getcwd\n";
50 return bless $self, $class;
53 # similar to exec except forks first and then _exit(0)'s on success
54 # if first arg is CODE ref, call that after successful fork and exec
55 # first arg is program second and following are argv[0], argv[1], ...
56 sub _forkexec {
57 my $cleanup;
58 $cleanup = shift if ref($_[0]) eq 'CODE';
59 my $program = shift;
60 my ($read, $write);
61 pipe($read, $write) or return undef;
62 select((select($write),$|=1)[0]);
63 my $oldsigchld = $SIG{'CHLD'};
64 my $retries = 3;
65 my $child;
66 while (!defined($child) && $retries--) {
67 $child = fork;
68 sleep 1 unless defined($child) || !$retries;
70 defined($child) or return undef;
71 if (!$child) {
72 close $read;
73 { exec({$program} @_) };
74 my $ec = 0 + $!;
75 $ec = 255 unless $ec;
76 print $write $ec;
77 close $write;
78 _exit 127;
80 close $write;
81 my $result = <$read>;
82 chomp $result if defined($result);
83 if ($result && $result != 0) {
84 $! = $result;
85 return undef;
87 &$cleanup if defined $cleanup;
88 _exit 0;
91 sub reexec {
92 my $self = shift;
93 my @cleanup = ();
94 push(@cleanup, shift) if ref($_[0]) eq 'CODE';
95 ref($self->{ENV}) eq 'HASH' &&
96 defined($self->{program}) &&
97 defined($self->{argv0}) &&
98 ref($self->{ARGV}) eq 'ARRAY' &&
99 defined($self->{cwd}) or die __PACKAGE__ . "::reexec: fatal: invalid instance\n";
100 my %envsave = %ENV;
101 my $cwdsave = eval { no warnings; getcwd; };
102 my $result = chdir($self->{cwd});
103 return $result unless $result;
104 %ENV = %{$self->{ENV}};
105 $result = _forkexec(@cleanup, $self->{program}, $self->{argv0}, @{$self->{ARGV}});
106 %ENV = %envsave;
107 chdir($cwdsave) if defined($cwdsave);
108 return $result;
113 __END__
115 =head1 NAME
117 Girocco::ExecUtil - Re-execution utility
119 =head1 SYNOPSIS
121 use Girocco::ExecUtil;
123 my $exec_state = Girocco::ExecUtil->new;
124 # do some stuff and run for a while
125 $exec_state->reexec;
127 =head1 DESCRIPTION
129 This module provides a re-exec function for long-running processes that
130 may want to re-start themselves at a later point in time if they have been
131 updated.
133 The C<Girocco::ExecUtil> instance records various information about the current
134 state of the process when it's called (C<@ARGV>, C<%ENV>, current working directory,
135 C<$0> process name) for subsequent use by the C<reexec> function.
137 When the C<reexec> function is called, it restores C<%ENV> and the current working
138 directory to the previously saved state and then C<exec>'s the previously saved
139 C<$0> using the previously saved C<@ARGV> in a new process and C<_exit>'s the
140 old one.
142 The following functions are provided:
144 =over 4
146 =item Girocco::ExecUtil->new
148 =item Girocco::ExecUtil->new(I<program>)
150 =item Girocco::ExecUtil->new(I<program>, I<argv0>)
152 Create and return a new instance recording the current environment (C<%ENV>),
153 program (C<$0>), arguments (C<@ARGV>) and working directory (C<getcwd>) for later
154 use by the reexec function. If I<program> is passed it is used in place
155 of C<$0> for both the program to execute and C<argv[0]>. If I<program> and
156 I<argv0> are passed then I<program> will be executed but passed I<argv0> as its
157 C<argv[0]>.
159 =item $instance->reexec
161 =item $instance->reexec(I<coderef>)
163 Restore the saved environment and current working directory recorded in
164 the instance and then fork and exec the program and arguments recorded in the
165 instance and if successful call I<coderef>, if provided, and then _exit(0).
167 Only returns if the chdir, fork or exec call fails (in which case I<coderef> is
168 NOT called and the current working directory may have been restored to its
169 saved value).
171 Note that I<coderef>, if provided, is called B<after> the successful fork and
172 exec so the newly exec'd process may already be running by then (I<coderef> is
173 B<never> called if the C<reexec> fails).
175 =back
177 =head1 COPYRIGHT
179 Copyright (C) 2016 Kyle J. McKay. All rights reserved.
181 License GPLv2+: GNU General Public License version 2 or later.
182 See comments at top of source file for details.
184 L<http://www.gnu.org/licenses/gpl-2.0.html>
186 This is free software: you are free to change and redistribute it.
187 There is NO WARRANTY, to the extent permitted by law.
189 =cut