| OLD | NEW |
| (Empty) |
| 1 # Copyright © 2008-2009 Raphaël Hertzog <hertzog@debian.org> | |
| 2 # Copyright © 2008 Frank Lichtenheld <djpig@debian.org> | |
| 3 # | |
| 4 # This program is free software; you can redistribute it and/or modify | |
| 5 # it under the terms of the GNU General Public License as published by | |
| 6 # the Free Software Foundation; either version 2 of the License, or | |
| 7 # (at your option) any later version. | |
| 8 # | |
| 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. | |
| 13 # | |
| 14 # You should have received a copy of the GNU General Public License | |
| 15 # along with this program. If not, see <https://www.gnu.org/licenses/>. | |
| 16 | |
| 17 package Dpkg::IPC; | |
| 18 | |
| 19 use strict; | |
| 20 use warnings; | |
| 21 | |
| 22 our $VERSION = '1.00'; | |
| 23 | |
| 24 use Dpkg::ErrorHandling; | |
| 25 use Dpkg::Gettext; | |
| 26 | |
| 27 use Carp; | |
| 28 use Exporter qw(import); | |
| 29 our @EXPORT = qw(spawn wait_child); | |
| 30 | |
| 31 =encoding utf8 | |
| 32 | |
| 33 =head1 NAME | |
| 34 | |
| 35 Dpkg::IPC - helper functions for IPC | |
| 36 | |
| 37 =head1 DESCRIPTION | |
| 38 | |
| 39 Dpkg::IPC offers helper functions to allow you to execute | |
| 40 other programs in an easy, yet flexible way, while hiding | |
| 41 all the gory details of IPC (Inter-Process Communication) | |
| 42 from you. | |
| 43 | |
| 44 =head1 METHODS | |
| 45 | |
| 46 =over 4 | |
| 47 | |
| 48 =item spawn | |
| 49 | |
| 50 Creates a child process and executes another program in it. | |
| 51 The arguments are interpreted as a hash of options, specifying | |
| 52 how to handle the in and output of the program to execute. | |
| 53 Returns the pid of the child process (unless the wait_child | |
| 54 option was given). | |
| 55 | |
| 56 Any error will cause the function to exit with one of the | |
| 57 Dpkg::ErrorHandling functions. | |
| 58 | |
| 59 Options: | |
| 60 | |
| 61 =over 4 | |
| 62 | |
| 63 =item exec | |
| 64 | |
| 65 Can be either a scalar, i.e. the name of the program to be | |
| 66 executed, or an array reference, i.e. the name of the program | |
| 67 plus additional arguments. Note that the program will never be | |
| 68 executed via the shell, so you can't specify additional arguments | |
| 69 in the scalar string and you can't use any shell facilities like | |
| 70 globbing. | |
| 71 | |
| 72 Mandatory Option. | |
| 73 | |
| 74 =item from_file, to_file, error_to_file | |
| 75 | |
| 76 Filename as scalar. Standard input/output/error of the | |
| 77 child process will be redirected to the file specified. | |
| 78 | |
| 79 =item from_handle, to_handle, error_to_handle | |
| 80 | |
| 81 Filehandle. Standard input/output/error of the child process will be | |
| 82 dup'ed from the handle. | |
| 83 | |
| 84 =item from_pipe, to_pipe, error_to_pipe | |
| 85 | |
| 86 Scalar reference or object based on IO::Handle. A pipe will be opened for | |
| 87 each of the two options and either the reading (C<to_pipe> and | |
| 88 C<error_to_pipe>) or the writing end (C<from_pipe>) will be returned in | |
| 89 the referenced scalar. Standard input/output/error of the child process | |
| 90 will be dup'ed to the other ends of the pipes. | |
| 91 | |
| 92 =item from_string, to_string, error_to_string | |
| 93 | |
| 94 Scalar reference. Standard input/output/error of the child | |
| 95 process will be redirected to the string given as reference. Note | |
| 96 that it wouldn't be strictly necessary to use a scalar reference | |
| 97 for C<from_string>, as the string is not modified in any way. This was | |
| 98 chosen only for reasons of symmetry with C<to_string> and | |
| 99 C<error_to_string>. C<to_string> and C<error_to_string> imply the | |
| 100 C<wait_child> option. | |
| 101 | |
| 102 =item wait_child | |
| 103 | |
| 104 Scalar. If containing a true value, wait_child() will be called before | |
| 105 returning. The return value of spawn() will be a true value, not the pid. | |
| 106 | |
| 107 =item nocheck | |
| 108 | |
| 109 Scalar. Option of the wait_child() call. | |
| 110 | |
| 111 =item timeout | |
| 112 | |
| 113 Scalar. Option of the wait_child() call. | |
| 114 | |
| 115 =item chdir | |
| 116 | |
| 117 Scalar. The child process will chdir in the indicated directory before | |
| 118 calling exec. | |
| 119 | |
| 120 =item env | |
| 121 | |
| 122 Hash reference. The child process will populate %ENV with the items of the | |
| 123 hash before calling exec. This allows exporting environment variables. | |
| 124 | |
| 125 =item delete_env | |
| 126 | |
| 127 Array reference. The child process will remove all environment variables | |
| 128 listed in the array before calling exec. | |
| 129 | |
| 130 =back | |
| 131 | |
| 132 =cut | |
| 133 | |
| 134 sub _sanity_check_opts { | |
| 135 my (%opts) = @_; | |
| 136 | |
| 137 croak 'exec parameter is mandatory in spawn()' | |
| 138 unless $opts{exec}; | |
| 139 | |
| 140 my $to = my $error_to = my $from = 0; | |
| 141 foreach (qw(file handle string pipe)) { | |
| 142 $to++ if $opts{"to_$_"}; | |
| 143 $error_to++ if $opts{"error_to_$_"}; | |
| 144 $from++ if $opts{"from_$_"}; | |
| 145 } | |
| 146 croak 'not more than one of to_* parameters is allowed' | |
| 147 if $to > 1; | |
| 148 croak 'not more than one of error_to_* parameters is allowed' | |
| 149 if $error_to > 1; | |
| 150 croak 'not more than one of from_* parameters is allowed' | |
| 151 if $from > 1; | |
| 152 | |
| 153 foreach (qw(to_string error_to_string from_string)) { | |
| 154 if (exists $opts{$_} and | |
| 155 (not ref($opts{$_}) or ref($opts{$_}) ne 'SCALAR')) { | |
| 156 croak "parameter $_ must be a scalar reference"; | |
| 157 } | |
| 158 } | |
| 159 | |
| 160 foreach (qw(to_pipe error_to_pipe from_pipe)) { | |
| 161 if (exists $opts{$_} and | |
| 162 (not ref($opts{$_}) or (ref($opts{$_}) ne 'SCALAR' and | |
| 163 not $opts{$_}->isa('IO::Handle')))) { | |
| 164 croak "parameter $_ must be a scalar reference or " . | |
| 165 'an IO::Handle object'; | |
| 166 } | |
| 167 } | |
| 168 | |
| 169 if (exists $opts{timeout} and defined($opts{timeout}) and | |
| 170 $opts{timeout} !~ /^\d+$/) { | |
| 171 croak 'parameter timeout must be an integer'; | |
| 172 } | |
| 173 | |
| 174 if (exists $opts{env} and ref($opts{env}) ne 'HASH') { | |
| 175 croak 'parameter env must be a hash reference'; | |
| 176 } | |
| 177 | |
| 178 if (exists $opts{delete_env} and ref($opts{delete_env}) ne 'ARRAY') { | |
| 179 croak 'parameter delete_env must be an array reference'; | |
| 180 } | |
| 181 | |
| 182 return %opts; | |
| 183 } | |
| 184 | |
| 185 sub spawn { | |
| 186 my (%opts) = _sanity_check_opts(@_); | |
| 187 $opts{close_in_child} ||= []; | |
| 188 my @prog; | |
| 189 if (ref($opts{exec}) =~ /ARRAY/) { | |
| 190 push @prog, @{$opts{exec}}; | |
| 191 } elsif (not ref($opts{exec})) { | |
| 192 push @prog, $opts{exec}; | |
| 193 } else { | |
| 194 croak 'invalid exec parameter in spawn()'; | |
| 195 } | |
| 196 my ($from_string_pipe, $to_string_pipe, $error_to_string_pipe); | |
| 197 if ($opts{to_string}) { | |
| 198 $opts{to_pipe} = \$to_string_pipe; | |
| 199 $opts{wait_child} = 1; | |
| 200 } | |
| 201 if ($opts{error_to_string}) { | |
| 202 $opts{error_to_pipe} = \$error_to_string_pipe; | |
| 203 $opts{wait_child} = 1; | |
| 204 } | |
| 205 if ($opts{from_string}) { | |
| 206 $opts{from_pipe} = \$from_string_pipe; | |
| 207 } | |
| 208 # Create pipes if needed | |
| 209 my ($input_pipe, $output_pipe, $error_pipe); | |
| 210 if ($opts{from_pipe}) { | |
| 211 pipe($opts{from_handle}, $input_pipe) | |
| 212 or syserr(_g('pipe for %s'), "@prog"); | |
| 213 ${$opts{from_pipe}} = $input_pipe; | |
| 214 push @{$opts{close_in_child}}, $input_pipe; | |
| 215 } | |
| 216 if ($opts{to_pipe}) { | |
| 217 pipe($output_pipe, $opts{to_handle}) | |
| 218 or syserr(_g('pipe for %s'), "@prog"); | |
| 219 ${$opts{to_pipe}} = $output_pipe; | |
| 220 push @{$opts{close_in_child}}, $output_pipe; | |
| 221 } | |
| 222 if ($opts{error_to_pipe}) { | |
| 223 pipe($error_pipe, $opts{error_to_handle}) | |
| 224 or syserr(_g('pipe for %s'), "@prog"); | |
| 225 ${$opts{error_to_pipe}} = $error_pipe; | |
| 226 push @{$opts{close_in_child}}, $error_pipe; | |
| 227 } | |
| 228 # Fork and exec | |
| 229 my $pid = fork(); | |
| 230 syserr(_g('cannot fork for %s'), "@prog") unless defined $pid; | |
| 231 if (not $pid) { | |
| 232 # Define environment variables | |
| 233 if ($opts{env}) { | |
| 234 foreach (keys %{$opts{env}}) { | |
| 235 $ENV{$_} = $opts{env}{$_}; | |
| 236 } | |
| 237 } | |
| 238 if ($opts{delete_env}) { | |
| 239 delete $ENV{$_} foreach (@{$opts{delete_env}}); | |
| 240 } | |
| 241 # Change the current directory | |
| 242 if ($opts{chdir}) { | |
| 243 chdir($opts{chdir}) or syserr(_g('chdir to %s'), $opts{chdir}); | |
| 244 } | |
| 245 # Redirect STDIN if needed | |
| 246 if ($opts{from_file}) { | |
| 247 open(STDIN, '<', $opts{from_file}) | |
| 248 or syserr(_g('cannot open %s'), $opts{from_file}); | |
| 249 } elsif ($opts{from_handle}) { | |
| 250 open(STDIN, '<&', $opts{from_handle}) | |
| 251 or syserr(_g('reopen stdin')); | |
| 252 close($opts{from_handle}); # has been duped, can be closed | |
| 253 } | |
| 254 # Redirect STDOUT if needed | |
| 255 if ($opts{to_file}) { | |
| 256 open(STDOUT, '>', $opts{to_file}) | |
| 257 or syserr(_g('cannot write %s'), $opts{to_file}); | |
| 258 } elsif ($opts{to_handle}) { | |
| 259 open(STDOUT, '>&', $opts{to_handle}) | |
| 260 or syserr(_g('reopen stdout')); | |
| 261 close($opts{to_handle}); # has been duped, can be closed | |
| 262 } | |
| 263 # Redirect STDERR if needed | |
| 264 if ($opts{error_to_file}) { | |
| 265 open(STDERR, '>', $opts{error_to_file}) | |
| 266 or syserr(_g('cannot write %s'), $opts{error_to_file}); | |
| 267 } elsif ($opts{error_to_handle}) { | |
| 268 open(STDERR, '>&', $opts{error_to_handle}) | |
| 269 or syserr(_g('reopen stdout')); | |
| 270 close($opts{error_to_handle}); # has been duped, can be closed | |
| 271 } | |
| 272 # Close some inherited filehandles | |
| 273 close($_) foreach (@{$opts{close_in_child}}); | |
| 274 # Execute the program | |
| 275 exec({ $prog[0] } @prog) or syserr(_g('unable to execute %s'), "@prog"); | |
| 276 } | |
| 277 # Close handle that we can't use any more | |
| 278 close($opts{from_handle}) if exists $opts{from_handle}; | |
| 279 close($opts{to_handle}) if exists $opts{to_handle}; | |
| 280 close($opts{error_to_handle}) if exists $opts{error_to_handle}; | |
| 281 | |
| 282 if ($opts{from_string}) { | |
| 283 print { $from_string_pipe } ${$opts{from_string}}; | |
| 284 close($from_string_pipe); | |
| 285 } | |
| 286 if ($opts{to_string}) { | |
| 287 local $/ = undef; | |
| 288 ${$opts{to_string}} = readline($to_string_pipe); | |
| 289 } | |
| 290 if ($opts{error_to_string}) { | |
| 291 local $/ = undef; | |
| 292 ${$opts{error_to_string}} = readline($error_to_string_pipe); | |
| 293 } | |
| 294 if ($opts{wait_child}) { | |
| 295 my $cmdline = "@prog"; | |
| 296 if ($opts{env}) { | |
| 297 foreach (keys %{$opts{env}}) { | |
| 298 $cmdline = "$_=\"" . $opts{env}{$_} . "\" $cmdline"; | |
| 299 } | |
| 300 } | |
| 301 wait_child($pid, nocheck => $opts{nocheck}, | |
| 302 timeout => $opts{timeout}, cmdline => $cmdline); | |
| 303 return 1; | |
| 304 } | |
| 305 | |
| 306 return $pid; | |
| 307 } | |
| 308 | |
| 309 | |
| 310 =item wait_child | |
| 311 | |
| 312 Takes as first argument the pid of the process to wait for. | |
| 313 Remaining arguments are taken as a hash of options. Returns | |
| 314 nothing. Fails if the child has been ended by a signal or | |
| 315 if it exited non-zero. | |
| 316 | |
| 317 Options: | |
| 318 | |
| 319 =over 4 | |
| 320 | |
| 321 =item cmdline | |
| 322 | |
| 323 String to identify the child process in error messages. | |
| 324 Defaults to "child process". | |
| 325 | |
| 326 =item nocheck | |
| 327 | |
| 328 If true do not check the return status of the child (and thus | |
| 329 do not fail it it has been killed or if it exited with a | |
| 330 non-zero return code). | |
| 331 | |
| 332 =item timeout | |
| 333 | |
| 334 Set a maximum time to wait for the process, after that fail | |
| 335 with an error message. | |
| 336 | |
| 337 =back | |
| 338 | |
| 339 =cut | |
| 340 | |
| 341 sub wait_child { | |
| 342 my ($pid, %opts) = @_; | |
| 343 $opts{cmdline} ||= _g('child process'); | |
| 344 croak 'no PID set, cannot wait end of process' unless $pid; | |
| 345 eval { | |
| 346 local $SIG{ALRM} = sub { die "alarm\n" }; | |
| 347 alarm($opts{timeout}) if defined($opts{timeout}); | |
| 348 $pid == waitpid($pid, 0) or syserr(_g('wait for %s'), $opts{cmdline}); | |
| 349 alarm(0) if defined($opts{timeout}); | |
| 350 }; | |
| 351 if ($@) { | |
| 352 die $@ unless $@ eq "alarm\n"; | |
| 353 error(ngettext("%s didn't complete in %d second", | |
| 354 "%s didn't complete in %d seconds", | |
| 355 $opts{timeout}), | |
| 356 $opts{cmdline}, $opts{timeout}); | |
| 357 } | |
| 358 unless ($opts{nocheck}) { | |
| 359 subprocerr($opts{cmdline}) if $?; | |
| 360 } | |
| 361 } | |
| 362 | |
| 363 1; | |
| 364 __END__ | |
| 365 | |
| 366 =back | |
| 367 | |
| 368 =head1 AUTHORS | |
| 369 | |
| 370 Written by Raphaël Hertzog <hertzog@debian.org> and | |
| 371 Frank Lichtenheld <djpig@debian.org>. | |
| 372 | |
| 373 =head1 SEE ALSO | |
| 374 | |
| 375 Dpkg, Dpkg::ErrorHandling | |
| OLD | NEW |