| 1 |
#!/usr/bin/perl |
|---|
| 2 |
|
|---|
| 3 |
# This script is designed to be run launched by svn's post hooks under the |
|---|
| 4 |
# uid of the Mercurial repository. It generally lives in ~svn/bin |
|---|
| 5 |
# and called from the repository post hooks |
|---|
| 6 |
|
|---|
| 7 |
# This program might be of interest to people who want to do some sort of |
|---|
| 8 |
# operation on files after an SVN commit that requires some set of |
|---|
| 9 |
# commands to be run on the repository's local system. In other words, if |
|---|
| 10 |
# you really wish that when an SVN commit happened, you could log into the |
|---|
| 11 |
# server and run a bunch of commands against a local checkout of what was |
|---|
| 12 |
# just committed, then this script is for you. |
|---|
| 13 |
|
|---|
| 14 |
# The whole thing is a hack -- no question. If there's a better way to do |
|---|
| 15 |
# this job, I'd love to hear about it. |
|---|
| 16 |
|
|---|
| 17 |
# The sub's "safe_read_from_pipe" and read_from_process are: |
|---|
| 18 |
# ==================================================================== |
|---|
| 19 |
# Copyright (c) 2000-2004 CollabNet. All rights reserved. |
|---|
| 20 |
# |
|---|
| 21 |
# This software is licensed as described in the file COPYING, which |
|---|
| 22 |
# you should have received as part of this distribution. The terms |
|---|
| 23 |
# are also available at http://subversion.tigris.org/license-1.html. |
|---|
| 24 |
# If newer versions of this license are posted there, you may use a |
|---|
| 25 |
# newer version instead, at your option. |
|---|
| 26 |
# |
|---|
| 27 |
# This software consists of voluntary contributions made by many |
|---|
| 28 |
# individuals. For exact contribution history, see the revision |
|---|
| 29 |
# history and logs, available at http://subversion.tigris.org/. |
|---|
| 30 |
# ==================================================================== |
|---|
| 31 |
# |
|---|
| 32 |
# |
|---|
| 33 |
# The rest of the file is: |
|---|
| 34 |
# |
|---|
| 35 |
# Copyright (c) 2006, 2007 Software Freedom Law Center, Inc. |
|---|
| 36 |
# |
|---|
| 37 |
# This software gives you freedom; it is licensed to you under version 3 |
|---|
| 38 |
# of the GNU General Public License. |
|---|
| 39 |
# |
|---|
| 40 |
# This software is distributed WITHOUT ANY WARRANTY, without even the |
|---|
| 41 |
# implied warranties of MERCHANTABILITY and FITNESS FOR A PARTICULAR |
|---|
| 42 |
# PURPOSE. See the GNU General Public License for further details. |
|---|
| 43 |
# |
|---|
| 44 |
# You should have received a copy of the GNU General Public License, |
|---|
| 45 |
# version 3. If not, see <http://www.gnu.org/licenses/> |
|---|
| 46 |
# |
|---|
| 47 |
# License: GPLv3-only |
|---|
| 48 |
|
|---|
| 49 |
|
|---|
| 50 |
use strict; |
|---|
| 51 |
use warnings; |
|---|
| 52 |
|
|---|
| 53 |
my $SVN = "/usr/bin/svn"; |
|---|
| 54 |
my $SVNLOOK = "/usr/bin/svnlook"; |
|---|
| 55 |
my $HG = '/usr/bin/hg'; |
|---|
| 56 |
|
|---|
| 57 |
my $LOBLAW_TRUNK_CHECKOUT = "/home/hg/SVNCheckouts/loblaw-trunk"; |
|---|
| 58 |
my $LOBLAW_TEST_HG_CHECKOUT = "/home/hg/HGCheckouts/loblaw-test"; |
|---|
| 59 |
|
|---|
| 60 |
my $PIPE_FILE = "/home/svn/pipes/loblaw-update"; |
|---|
| 61 |
|
|---|
| 62 |
use POSIX ":sys_wait_h"; |
|---|
| 63 |
use Fcntl; # for sysopen |
|---|
| 64 |
use Carp; |
|---|
| 65 |
use File::Temp qw/:POSIX tempfile mkstemps/; |
|---|
| 66 |
use File::Path; |
|---|
| 67 |
use File::Basename; |
|---|
| 68 |
use File::Copy; |
|---|
| 69 |
############################################################################### |
|---|
| 70 |
use Unix::Syslog qw(:subs); |
|---|
| 71 |
use Unix::Syslog qw(:macros); |
|---|
| 72 |
use Carp; |
|---|
| 73 |
{ |
|---|
| 74 |
# my $PAGE_EMAIL = '6463617006@tmomail.net'; |
|---|
| 75 |
my $PAGE_EMAIL = 'root@softwarefreedom.org'; |
|---|
| 76 |
my %messageHistory; |
|---|
| 77 |
|
|---|
| 78 |
sub DoLog ($$$;$) { |
|---|
| 79 |
my($type, $user, $message, $cleanupCode) = @_; |
|---|
| 80 |
|
|---|
| 81 |
use Date::Manip; |
|---|
| 82 |
my $NOW = ParseDate("now"); |
|---|
| 83 |
syslog LOG_INFO, $message; |
|---|
| 84 |
|
|---|
| 85 |
my $lastTime = $messageHistory{$message}; |
|---|
| 86 |
|
|---|
| 87 |
my $sendIt = 0; |
|---|
| 88 |
if (not defined $lastTime) { |
|---|
| 89 |
$sendIt = 1; |
|---|
| 90 |
} else { |
|---|
| 91 |
my $err; |
|---|
| 92 |
my $sinceLast = DateCalc($lastTime,"+ 10 minutes",\$err); |
|---|
| 93 |
$sendIt = 1 if ($NOW gt $sinceLast); |
|---|
| 94 |
} |
|---|
| 95 |
if ($sendIt) { |
|---|
| 96 |
open(SENDMAIL, "|/usr/sbin/sendmail -f root\@softwarefreedom.org -t"); |
|---|
| 97 |
print SENDMAIL "To: $PAGE_EMAIL,root\@softwarefreedom.org"; |
|---|
| 98 |
print SENDMAIL ", $user\@softwarefreedom.org" if defined $user; |
|---|
| 99 |
print SENDMAIL "\nFrom: root\@softwarefreedom.org\n", |
|---|
| 100 |
"Subject: LOBLAW,COMMIT_HOOK\n\n$message\n.\n"; |
|---|
| 101 |
close(SENDMAIL); |
|---|
| 102 |
if ($? != 0) { |
|---|
| 103 |
syslog LOG_INFO, "Unable to perform sendmail: $!"; |
|---|
| 104 |
} else { |
|---|
| 105 |
$messageHistory{$message} = $NOW; |
|---|
| 106 |
} |
|---|
| 107 |
} |
|---|
| 108 |
my $more; |
|---|
| 109 |
$more = &$cleanupCode if defined $cleanupCode and ref $cleanupCode; |
|---|
| 110 |
$message .= " $more" if (defined $more and $more !~ /^\s*$/); |
|---|
| 111 |
croak $message if $type eq "die"; |
|---|
| 112 |
warn $message; |
|---|
| 113 |
} |
|---|
| 114 |
sub DieLog ($;$) { |
|---|
| 115 |
DoLog("die", undef, $_[0], $_[1]); |
|---|
| 116 |
} |
|---|
| 117 |
sub WarnLog ($$) { |
|---|
| 118 |
DoLog("warn", $_[0], $_[1]); |
|---|
| 119 |
} |
|---|
| 120 |
} |
|---|
| 121 |
############################################################################### |
|---|
| 122 |
|
|---|
| 123 |
# Start a child process safely without using /bin/sh. |
|---|
| 124 |
sub safe_read_from_pipe |
|---|
| 125 |
{ |
|---|
| 126 |
unless (@_) |
|---|
| 127 |
{ |
|---|
| 128 |
DieLog("$0: safe_read_from_pipe passed no arguments."); |
|---|
| 129 |
} |
|---|
| 130 |
|
|---|
| 131 |
my $pid = open(SAFE_READ, '-|'); |
|---|
| 132 |
unless (defined $pid) |
|---|
| 133 |
{ |
|---|
| 134 |
DieLog("$0: cannot fork: $!"); |
|---|
| 135 |
} |
|---|
| 136 |
unless ($pid) |
|---|
| 137 |
{ |
|---|
| 138 |
open(STDERR, ">&STDOUT") |
|---|
| 139 |
or DieLog("$0: cannot dup STDOUT: $!"); |
|---|
| 140 |
exec(@_) |
|---|
| 141 |
or DieLog("$0: cannot exec `@_': $!\n"); |
|---|
| 142 |
} |
|---|
| 143 |
my @output; |
|---|
| 144 |
while (<SAFE_READ>) |
|---|
| 145 |
{ |
|---|
| 146 |
s/[\r\n]+$//; |
|---|
| 147 |
push(@output, $_); |
|---|
| 148 |
} |
|---|
| 149 |
close(SAFE_READ); |
|---|
| 150 |
my $result = $?; |
|---|
| 151 |
my $exit = $result >> 8; |
|---|
| 152 |
my $signal = $result & 127; |
|---|
| 153 |
my $cd = $result & 128 ? "with core dump" : ""; |
|---|
| 154 |
if ($signal or $cd) |
|---|
| 155 |
{ |
|---|
| 156 |
DieLog("$0: pipe from `@_' failed $cd: exit=$exit signal=$signal\n"); |
|---|
| 157 |
} |
|---|
| 158 |
if (wantarray) |
|---|
| 159 |
{ |
|---|
| 160 |
return ($result, @output); |
|---|
| 161 |
} |
|---|
| 162 |
else |
|---|
| 163 |
{ |
|---|
| 164 |
return $result; |
|---|
| 165 |
} |
|---|
| 166 |
} |
|---|
| 167 |
############################################################################### |
|---|
| 168 |
# Use safe_read_from_pipe to start a child process safely and return |
|---|
| 169 |
# the output if it succeeded or an error message followed by the output |
|---|
| 170 |
# if it failed. |
|---|
| 171 |
sub read_from_process |
|---|
| 172 |
{ |
|---|
| 173 |
unless (@_) |
|---|
| 174 |
{ |
|---|
| 175 |
DieLog("$0: read_from_process passed no arguments."); |
|---|
| 176 |
} |
|---|
| 177 |
my ($status, @output) = &safe_read_from_pipe(@_); |
|---|
| 178 |
if ($status) |
|---|
| 179 |
{ |
|---|
| 180 |
return ("$0: `@_' failed with this output:", @output); |
|---|
| 181 |
} |
|---|
| 182 |
else |
|---|
| 183 |
{ |
|---|
| 184 |
return @output; |
|---|
| 185 |
} |
|---|
| 186 |
} |
|---|
| 187 |
############################################################################### |
|---|
| 188 |
my $LOCK_FILE = "/home/hg/locks/update-loblaw-test-unpackaged-lock"; |
|---|
| 189 |
|
|---|
| 190 |
my $LOCK_CLEANUP_CODE = sub { |
|---|
| 191 |
return (unlink($LOCK_FILE) != 1) ? |
|---|
| 192 |
"Failed unlink of $LOCK_FILE. Could cause trouble." : |
|---|
| 193 |
""; |
|---|
| 194 |
}; |
|---|
| 195 |
|
|---|
| 196 |
############################################################################### |
|---|
| 197 |
############################################################################### |
|---|
| 198 |
############################################################################### |
|---|
| 199 |
sub DoUpdate ($) { |
|---|
| 200 |
my ($revision) = @_; |
|---|
| 201 |
|
|---|
| 202 |
chdir $LOBLAW_TRUNK_CHECKOUT or |
|---|
| 203 |
DieLog("unable to change directory to $LOBLAW_TRUNK_CHECKOUT: $!", |
|---|
| 204 |
$LOCK_CLEANUP_CODE); |
|---|
| 205 |
|
|---|
| 206 |
my @cleanupLines = read_from_process($SVN, 'cleanup'); |
|---|
| 207 |
my @updateLines = read_from_process($SVN, 'update'); |
|---|
| 208 |
foreach my $line (@updateLines) { |
|---|
| 209 |
DieLog("conflict found on calendar update: $line", $LOCK_CLEANUP_CODE) |
|---|
| 210 |
if ($line =~ /^\s*C\s+/); |
|---|
| 211 |
} |
|---|
| 212 |
DieLog("update failure; missing revision line or " . |
|---|
| 213 |
"invalid revision number $1 (expect $revision or more) at end of: " . |
|---|
| 214 |
join("\n", @updateLines), $LOCK_CLEANUP_CODE) |
|---|
| 215 |
unless ($updateLines[$#updateLines] =~ |
|---|
| 216 |
/^\s*(?:Updated\s+to|At)\s+revision\s+(\d+)[\s\.]*$/i |
|---|
| 217 |
and $1 >= $revision); |
|---|
| 218 |
} |
|---|
| 219 |
############################################################################### |
|---|
| 220 |
|
|---|
| 221 |
# Keep track of which revisions we've finished with. |
|---|
| 222 |
my %CAUGHT_REVISIONS; |
|---|
| 223 |
my %DUAL_REVISIONED_FILE_LIST; |
|---|
| 224 |
my $LOBLAW_LIST_FILE = 'dev-tools/loblaw-unpackaged-files.dat'; |
|---|
| 225 |
############################################################################### |
|---|
| 226 |
sub BuildDualVersionFileList () { |
|---|
| 227 |
%DUAL_REVISIONED_FILE_LIST = (); |
|---|
| 228 |
|
|---|
| 229 |
open(FILE_LIST, "<$LOBLAW_LIST_FILE") |
|---|
| 230 |
or DieLog("unable to open $LOBLAW_LIST_FILE: $!"); |
|---|
| 231 |
while (my $line = <FILE_LIST>) { |
|---|
| 232 |
next if $line =~ /^\s*#/ or $line =~ /^\s*$/; |
|---|
| 233 |
unless ($line =~ |
|---|
| 234 |
m%^\s*([/a-zA-Z0-9\-\.\_]+)\s+\=\>\s+/([/a-zA-Z0-9\-\.\_]+)\s*$%) { |
|---|
| 235 |
# I am extremely picky in this regex to be super-security concious |
|---|
| 236 |
DieLog("Invalid line in $LOBLAW_LIST_FILE: $line"); |
|---|
| 237 |
} |
|---|
| 238 |
my($source, $destination) = ($1, $2); |
|---|
| 239 |
$DUAL_REVISIONED_FILE_LIST{$source} = $destination; |
|---|
| 240 |
} |
|---|
| 241 |
close FILE_LIST; |
|---|
| 242 |
} |
|---|
| 243 |
############################################################################### |
|---|
| 244 |
sub ProcessRevision ($$) { |
|---|
| 245 |
my($repository, $revision) = @_; |
|---|
| 246 |
|
|---|
| 247 |
return unless $repository =~ m|Repository/loblaw\s*$|; |
|---|
| 248 |
|
|---|
| 249 |
chdir $LOBLAW_TRUNK_CHECKOUT or |
|---|
| 250 |
DieLog("unable to change directory to $LOBLAW_TRUNK_CHECKOUT: $!"); |
|---|
| 251 |
|
|---|
| 252 |
my @svnLookChangedLines = |
|---|
| 253 |
read_from_process($SVNLOOK, 'changed', $repository, '-r', $revision); |
|---|
| 254 |
|
|---|
| 255 |
my @operateOnFiles; |
|---|
| 256 |
|
|---|
| 257 |
my $updateFileList = 0; |
|---|
| 258 |
$updateFileList = 1 if (keys %DUAL_REVISIONED_FILE_LIST <= 0); |
|---|
| 259 |
|
|---|
| 260 |
foreach my $changeLine (@svnLookChangedLines) { |
|---|
| 261 |
chomp $changeLine; |
|---|
| 262 |
# This script only keeps loblaw-test in sync with trunks' scripts and configs |
|---|
| 263 |
|
|---|
| 264 |
my(%vals); |
|---|
| 265 |
$vals{changeLine} = $changeLine; |
|---|
| 266 |
if ($changeLine |
|---|
| 267 |
=~ m|^\s*(\S)\s+trunk/$LOBLAW_LIST_FILE\s*$|) { |
|---|
| 268 |
$updateFileList = 1; |
|---|
| 269 |
next; |
|---|
| 270 |
} elsif ($changeLine =~ m%^\s*(\S)\s+trunk/((scripts|configs)/\S+)\s*$%) { |
|---|
| 271 |
($vals{operation}, $vals{file}) = ($1, $2); |
|---|
| 272 |
} |
|---|
| 273 |
push(@operateOnFiles, \%vals) |
|---|
| 274 |
if (defined $vals{operation} and $vals{operation} !~ /^\s*I\s*/); |
|---|
| 275 |
# skip those cases where we didn't want to do anything with the file |
|---|
| 276 |
# and those that we *would* do something but were ignored items |
|---|
| 277 |
} |
|---|
| 278 |
my(@lockData) = read_from_process("/usr/bin/lockfile -r 8 $LOCK_FILE"); |
|---|
| 279 |
|
|---|
| 280 |
DieLog("Failure to aquire lock in $repository for $revision. " . |
|---|
| 281 |
"loblaw-test Mercurial update aborted!") |
|---|
| 282 |
if defined $lockData[0] and $lockData[0] =~ /failed/; |
|---|
| 283 |
|
|---|
| 284 |
DoUpdate($revision); # Make sure we have the latest and the greatest |
|---|
| 285 |
|
|---|
| 286 |
BuildDualVersionFileList() if $updateFileList; |
|---|
| 287 |
|
|---|
| 288 |
# HG process will read this var. |
|---|
| 289 |
$ENV{EMAIL} = 'svn@softwarefreedom.org'; |
|---|
| 290 |
my $commitDone = 0; |
|---|
| 291 |
|
|---|
| 292 |
foreach my $vals (@operateOnFiles) { |
|---|
| 293 |
my($operation, $inputFile, $changeLine) = |
|---|
| 294 |
($vals->{operation}, $vals->{file}, $vals->{changeLine}); |
|---|
| 295 |
|
|---|
| 296 |
# if we already, through some stretch, we've done this version already, |
|---|
| 297 |
# then just skip this one. |
|---|
| 298 |
next if defined $CAUGHT_REVISIONS{"$repository,$revision"}; |
|---|
| 299 |
|
|---|
| 300 |
# See if this is a file we are actually processing over to the hg repository |
|---|
| 301 |
next unless defined $DUAL_REVISIONED_FILE_LIST{$inputFile}; |
|---|
| 302 |
my $outputFile = $DUAL_REVISIONED_FILE_LIST{$inputFile}; |
|---|
| 303 |
|
|---|
| 304 |
chdir $LOBLAW_TEST_HG_CHECKOUT or |
|---|
| 305 |
DieLog("unable to change directory to $LOBLAW_TEST_HG_CHECKOUT: $!"); |
|---|
| 306 |
|
|---|
| 307 |
read_from_process($HG, 'update'); |
|---|
| 308 |
read_from_process($HG, 'merge'); |
|---|
| 309 |
read_from_process($HG, 'update'); |
|---|
| 310 |
|
|---|
| 311 |
chdir $LOBLAW_TRUNK_CHECKOUT or |
|---|
| 312 |
DieLog("unable to change directory to $LOBLAW_TRUNK_CHECKOUT: $!"); |
|---|
| 313 |
|
|---|
| 314 |
if ($operation =~ /^\s*(?:U|M|\~|L|A)\s*/) { |
|---|
| 315 |
my($dir, $filename); |
|---|
| 316 |
if (-d "$LOBLAW_TRUNK_CHECKOUT/$inputFile") { |
|---|
| 317 |
($dir, $filename) = ($outputFile, ""); |
|---|
| 318 |
} else { |
|---|
| 319 |
($dir, $filename) = fileparse($outputFile); |
|---|
| 320 |
} |
|---|
| 321 |
eval { mkpath("$LOBLAW_TEST_HG_CHECKOUT/$dir", 0, 0755) }; |
|---|
| 322 |
DieLog("Couldn't create $LOBLAW_TEST_HG_CHECKOUT/$dir: $@", |
|---|
| 323 |
$LOCK_CLEANUP_CODE) if ($@); |
|---|
| 324 |
|
|---|
| 325 |
# copy the actual file over, if it is a file and not a directory. |
|---|
| 326 |
unless ($filename =~ /^\s*$/) { |
|---|
| 327 |
copy($inputFile, "$LOBLAW_TEST_HG_CHECKOUT/$outputFile") |
|---|
| 328 |
or DieLog("Couldn't copy $inputFile into " . |
|---|
| 329 |
"$LOBLAW_TEST_HG_CHECKOUT/$outputFile: $!", |
|---|
| 330 |
$LOCK_CLEANUP_CODE); |
|---|
| 331 |
} |
|---|
| 332 |
chdir $LOBLAW_TEST_HG_CHECKOUT or |
|---|
| 333 |
DieLog("unable to change directory to $LOBLAW_TEST_HG_CHECKOUT: $!"); |
|---|
| 334 |
read_from_process($HG, 'add', "$outputFile") |
|---|
| 335 |
if ($operation =~ /^\s*A\s*/); |
|---|
| 336 |
} elsif ($operation =~ /^\s*D\s*/) { |
|---|
| 337 |
unlink("$LOBLAW_TEST_HG_CHECKOUT/$outputFile"); |
|---|
| 338 |
chdir $LOBLAW_TEST_HG_CHECKOUT or |
|---|
| 339 |
DieLog("unable to change directory to $LOBLAW_TEST_HG_CHECKOUT: $!"); |
|---|
| 340 |
read_from_process($HG, 'remove', "$outputFile"); |
|---|
| 341 |
} else { |
|---|
| 342 |
DieLog("Invalid svnlook status, $operation, in $changeLine", |
|---|
| 343 |
$LOCK_CLEANUP_CODE); |
|---|
| 344 |
} |
|---|
| 345 |
chdir $LOBLAW_TRUNK_CHECKOUT or |
|---|
| 346 |
DieLog("unable to change directory to $LOBLAW_TRUNK_CHECKOUT: $!"); |
|---|
| 347 |
my @svnLog = read_from_process($SVN, '-r', $revision, 'log', $inputFile); |
|---|
| 348 |
my($tempFH, $tempFile) = mkstemps( "/tmp/hgcommitXXXXXX", ".log"); |
|---|
| 349 |
print $tempFH "From SVN Revision $revision:\n"; |
|---|
| 350 |
foreach my $line (@svnLog) { |
|---|
| 351 |
chomp $line; |
|---|
| 352 |
next if $line =~ /^\s*\-+\s*$/; |
|---|
| 353 |
$line =~ s/^(.*\|.*\|.*)(\|.*)$/$1/; |
|---|
| 354 |
print $tempFH "$line\n"; |
|---|
| 355 |
} |
|---|
| 356 |
$tempFH->close(); |
|---|
| 357 |
chdir $LOBLAW_TEST_HG_CHECKOUT or |
|---|
| 358 |
DieLog("unable to change directory to $LOBLAW_TEST_HG_CHECKOUT: $!"); |
|---|
| 359 |
my @vals = read_from_process($HG, 'commit', '-l', $tempFile, |
|---|
| 360 |
"$outputFile"); |
|---|
| 361 |
unlink($tempFile); |
|---|
| 362 |
DieLog("Unexpected output from commit: " . join("\n", @vals), |
|---|
| 363 |
$LOCK_CLEANUP_CODE) if (@vals > 0); |
|---|
| 364 |
$commitDone = 1; |
|---|
| 365 |
} |
|---|
| 366 |
chdir $LOBLAW_TEST_HG_CHECKOUT or |
|---|
| 367 |
DieLog("unable to change directory to $LOBLAW_TEST_HG_CHECKOUT: $!"); |
|---|
| 368 |
read_from_process($HG, 'push', '-f') if $commitDone; |
|---|
| 369 |
&$LOCK_CLEANUP_CODE; |
|---|
| 370 |
} |
|---|
| 371 |
############################################################################### |
|---|
| 372 |
sub MakePipe ($) { |
|---|
| 373 |
my($file) = @_; |
|---|
| 374 |
|
|---|
| 375 |
unless (-p $file) { # not a pipe |
|---|
| 376 |
POSIX::mkfifo($file, 0660) or die "can't mknod $file: $!"; |
|---|
| 377 |
} |
|---|
| 378 |
} |
|---|
| 379 |
############################################################################### |
|---|
| 380 |
my %FORK_STATUS; |
|---|
| 381 |
|
|---|
| 382 |
|
|---|
| 383 |
sub REAPER { |
|---|
| 384 |
my $child; |
|---|
| 385 |
# If a second child dies while in the signal handler caused by the |
|---|
| 386 |
# first death, we won't get another signal. So must loop here else |
|---|
| 387 |
# we will leave the unreaped child as a zombie. And the next time |
|---|
| 388 |
# two children die we get another zombie. And so on. |
|---|
| 389 |
while (($child = waitpid(-1,WNOHANG)) > 0) { |
|---|
| 390 |
$FORK_STATUS{$child}{exitCode} = $?; |
|---|
| 391 |
$FORK_STATUS{$child}{exitString} = $!; |
|---|
| 392 |
} |
|---|
| 393 |
$SIG{CHLD} = \&REAPER; |
|---|
| 394 |
} |
|---|
| 395 |
|
|---|
| 396 |
# Don't fear the reaper.... |
|---|
| 397 |
|
|---|
| 398 |
$SIG{CHLD} = \&REAPER; |
|---|
| 399 |
|
|---|
| 400 |
############################################################################### |
|---|
| 401 |
sub Execute { |
|---|
| 402 |
my %caughtRevisions; |
|---|
| 403 |
|
|---|
| 404 |
umask 0007; |
|---|
| 405 |
$| = 1; |
|---|
| 406 |
|
|---|
| 407 |
while (1) { |
|---|
| 408 |
foreach my $child (keys %FORK_STATUS) { |
|---|
| 409 |
if (defined $FORK_STATUS{$child}{exitCode}) { |
|---|
| 410 |
if ($FORK_STATUS{$child}{exitCode} == 0) { |
|---|
| 411 |
my $key = "$FORK_STATUS{$child}{repository}," . |
|---|
| 412 |
$FORK_STATUS{$child}{revision}; |
|---|
| 413 |
$CAUGHT_REVISIONS{$key} = time(); |
|---|
| 414 |
} else { |
|---|
| 415 |
DieLog("Non-zero exit status ($FORK_STATUS{$child}{exitCode}) " . |
|---|
| 416 |
"for $FORK_STATUS{$child}{repository} processing of " . |
|---|
| 417 |
"$FORK_STATUS{$child}{revision}: " . |
|---|
| 418 |
$FORK_STATUS{$child}{exitString}) |
|---|
| 419 |
} |
|---|
| 420 |
delete $FORK_STATUS{$child}; |
|---|
| 421 |
} |
|---|
| 422 |
} |
|---|
| 423 |
MakePipe($PIPE_FILE) unless -p $PIPE_FILE; |
|---|
| 424 |
unless (-p $PIPE_FILE) { |
|---|
| 425 |
DieLog("$PIPE_FILE just won't be a pipe."); sleep 60; next; |
|---|
| 426 |
} |
|---|
| 427 |
|
|---|
| 428 |
my $ii; |
|---|
| 429 |
for ($ii = 0, select(undef, undef, undef, 0.2); |
|---|
| 430 |
$ii < 25 and not open(FIFO, "<$PIPE_FILE"); $ii++) { |
|---|
| 431 |
select(undef, undef, undef, 0.3); # sleep fraction of second |
|---|
| 432 |
} |
|---|
| 433 |
if ($ii >= 25) { |
|---|
| 434 |
DieLog("Unable to read pipe in $PIPE_FILE after 25 tries," . |
|---|
| 435 |
"sleeping 4 seconds and trying again: $!"); |
|---|
| 436 |
sleep 4; next; |
|---|
| 437 |
} |
|---|
| 438 |
|
|---|
| 439 |
my(@lines) = <FIFO>; |
|---|
| 440 |
close FIFO; |
|---|
| 441 |
foreach my $line (@lines) { |
|---|
| 442 |
unless ($line =~ /^\s*REPOSITORY:\s*\"([^"]+)\"\s+ |
|---|
| 443 |
REVISION:\s+(\d+)\s*$/x) { |
|---|
| 444 |
DieLog("Invalid line from pipe: $line"); next; |
|---|
| 445 |
} |
|---|
| 446 |
my($repository, $revision) = ($1, $2); |
|---|
| 447 |
my $childPid; |
|---|
| 448 |
if (not defined($childPid = fork())) { |
|---|
| 449 |
DieLog("fork failed when processing $repository with " |
|---|
| 450 |
. "revision $revision: $!"); |
|---|
| 451 |
sleep 60; |
|---|
| 452 |
next; |
|---|
| 453 |
} elsif ($childPid) { |
|---|
| 454 |
# The parent just goes on... |
|---|
| 455 |
$FORK_STATUS{$childPid}{repository} = $repository; |
|---|
| 456 |
$FORK_STATUS{$childPid}{revision} = $revision; |
|---|
| 457 |
next; |
|---|
| 458 |
} else { |
|---|
| 459 |
# The child does the revision process. |
|---|
| 460 |
delete $SIG{CHLD}; |
|---|
| 461 |
$0 = "$0 handles $revision in $repository"; |
|---|
| 462 |
ProcessRevision($repository, $revision); |
|---|
| 463 |
exit 0; |
|---|
| 464 |
} |
|---|
| 465 |
} |
|---|
| 466 |
# give a chance for the FIFO to recover |
|---|
| 467 |
# DieLog("Error encountered closing $PIPE_FILE: $!") unless ($? == n0); |
|---|
| 468 |
} |
|---|
| 469 |
} |
|---|
| 470 |
############################################################################### |
|---|
| 471 |
|
|---|
| 472 |
open(PID, ">/home/hg/pids/loblaw-test-update.pid") or |
|---|
| 473 |
DieLog("Unable to open PID file: $!"); |
|---|
| 474 |
print PID "$$\n"; |
|---|
| 475 |
close(PID); |
|---|
| 476 |
DieLog("Unable to close PID file: $!") unless ($? == 0); |
|---|
| 477 |
Execute(); |
|---|
| 478 |
|
|---|
| 479 |
__END__ |
|---|
| 480 |
# Local variables: |
|---|
| 481 |
# compile-command: "perl -c update-loblaw-test.plx" |
|---|
| 482 |
# End: |
|---|