Update of /cvsroot/fink/scripts/buildfink In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv2535
Modified Files: filters.xml buildfink Log Message: Add configurable per-package build time and log size limits Index: filters.xml =================================================================== RCS file: /cvsroot/fink/scripts/buildfink/filters.xml,v retrieving revision 1.4 retrieving revision 1.5 diff -u -d -r1.4 -r1.5 --- filters.xml 11 Jan 2006 20:25:09 -0000 1.4 +++ filters.xml 13 Mar 2006 20:25:30 -0000 1.5 @@ -151,7 +151,10 @@ <filter name="failures/typeconflict/mbstate_t">error: conflicting types for '__mbstate_t'</filter> <filter name="failures/header/sys_signal_h">sys/signal.h:166</filter> - <filter name="failures/unanalyzed" flags="m">^Failed:</filter> + + <filter name="failures/unanalyzed/log_size_limit" flags="m">^Failed: Log size limit exceeded</filter> + <filter name="failures/unanalyzed/build_time_limit" flags="m">^Failed: Build process timed out</filter> + <filter name="failures/unanalyzed/other" flags="m">^Failed:</filter> <filter name="warnings/obsolete_headtail">(head|tail)\s+-[0-9]</filter> Index: buildfink =================================================================== RCS file: /cvsroot/fink/scripts/buildfink/buildfink,v retrieving revision 1.23 retrieving revision 1.24 diff -u -d -r1.23 -r1.24 --- buildfink 1 Mar 2006 20:17:31 -0000 1.23 +++ buildfink 13 Mar 2006 20:25:30 -0000 1.24 @@ -24,7 +24,7 @@ =head1 USAGE - buildfink [-n] [--infofilter SCRIPT] [--patchdir DIR] [--skip PACKAGE] [--skip PACKAGE] [--validate] [--build-as-nobody] FINKDIR OUTDIR + buildfink [-n] [--infofilter SCRIPT] [--patchdir DIR] [--skip PACKAGE] [--skip PACKAGE] [--validate] [--build-as-nobody] [--max-log-size [P:]N] [--max-build-time [P:]T] [FINKDIR OUTDIR buildfink [-r CHECKPOINT] C<buildfink> builds every package in Fink, taking care of things like avoiding repeated building @@ -58,6 +58,19 @@ it will try building as root if that fails and keep track of which packages only succeed when built as root. +buildfink places limits on the maximum size of the log file for an individual +build and the build time of an individual package to prevent runaway packages +from breaking the build. C<--max-log-size N> and C<--max-build-time T> can be +used to adjust these limits. An C<N> or C<T> of 0 will disable the +corresponding limit. C<N> is in bytes, unless it has a suffix of C<K>, C<M>, +or C<G> indicating kilobytes, megabytes, and gigabytes respsectively. +C<T> is in seconds, unless it has a suffix of C<m> or C<h> indicating, +respectively, minutes and hours. The defaults are 250 megabytes and 4 hours +respectively. Package-specific limits can be provided by specifying +C<packagename:> in front of the limit value. For instance, +C<--max-build-time openoffice.org:24h> would specify a 24-hour limit for +openoffice.org. + =head2 CHECKPOINTS Sometimes there will be system issues partway through a build. For instance, a recalcitrant @@ -74,7 +87,7 @@ use File::Copy; use File::Find; use Data::Dumper; -use POSIX qw(dup2); +use POSIX qw(dup2 setsid sys_wait_h); use Getopt::Long; use FindBin qw($Bin); use lib "$Bin"; @@ -83,6 +96,49 @@ our($Bin, $FinkConfig, $FinkDir, $rundir, $dryrun, $infofilter, $patchdir, @skiplist, $checkpoint, $DoValidate, $BuildNobody); our $VERSION = '$Revision$'; +our $max_build_time = 60*60*4; +our $max_log_size = 1024*1024*250; +our %max_build_time_forpkg = (); +our %max_log_size_forpkg = (); + +sub limit_arg { + my($arg, $val) = @_; + my $pkg; + + $pkg = $1 if $val =~ s/^(.*)://; + + if($val =~ s/([kmgh])b?$//i) { + my $suffix = $1; + if($suffix eq "k") { + $val *= 1024; + } elsif($suffix eq "m") { + if($arg =~ /time/) { + $val *= 60; + } else { + $val *= 1024*1024; + } + } elsif($suffix eq "h") { + $val *= 60*60; + } elsif($suffix eq "g") { + $val *= 1024*1024*1024; + } + } + + if($arg =~ /time/) { + if($pkg) { + $max_build_time_forpkg{$pkg} = $val; + } else { + $max_build_time = $val; + } + } else { + if($pkg) { + $max_log_size_forpkg{$pkg} = $val; + } else { + $max_log_size = $val; + } + } +} + my $opts_ok = GetOptions( "n" => \$dryrun, "infofilter=s" => \$infofilter, @@ -90,7 +146,9 @@ "skip=s" => [EMAIL PROTECTED], "r=s" => \$checkpoint, "validate" => \$DoValidate, - "build-as-nobody:s" => \$BuildNobody + "build-as-nobody:s" => \$BuildNobody, + "max-build-time=s" => \&limit_arg, + "max-log-size=s" => \&limit_arg ); if(@ARGV != 2 and not (($dryrun and @ARGV == 1) or $checkpoint)) { warn "You must specify a Fink directory and an output directory.\n"; @@ -352,6 +410,83 @@ } } +# Do the build command, logging to the package log and enforcing time and +# space limits. +sub doBuild { + my($buildcmd, $pkg) = @_; + my $logsize = 0; + + # Don't do the naive thing with || because we want to be able to + # disable limits for specific packages by specifying a limit of 0. + my $max_size = exists($max_log_size_forpkg{$pkg}) ? $max_log_size_forpkg{$pkg} : $max_log_size; + my $max_time = exists($max_build_time_forpkg{$pkg}) ? $max_build_time_forpkg{$pkg} : $max_build_time; + + local $SIG{PIPE} = 'IGNORE'; + my $pid = open(BUILD, "-|"); + if(!defined($pid)) { + doLog("Couldn't fork: $!\n"); + die "Couldn't fork: $!\n"; + } elsif($pid == 0) { + $| = 1; + + # We want to be able to kill this process and all its children. + # Making them be in their own process groups accomplishes this. + # TODO: Salt the earth so that nothing can grow in its place. + setsid(); + + exec($buildcmd) or die "Couldn't exec: $!\n"; + } + + open(BUILDLOG, ">", "$rundir/logs/$pkg.log") or do { + doLog("Couldn't open $rundir/logs/$pkg.log: $!"); + die "Couldn't open $rundir/logs/$pkg.log: $!\n"; + }; + + my($rin, $win, $ein) = ('', '', ''); + vec($rin, fileno(BUILD), 1) = 1; + $ein = $rin; + my($rout, $wout, $eout); + my $buff; + + eval { + local $SIG{ALRM} = sub { die "alarm\n"; }; + alarm($max_time) if $max_time; + + while(select($rout = $rin, $wout = $win, $eout = $ein, $max_time ? ($max_time + 5) : undef)) { + my $nbytes = sysread(BUILD, $buff, 4096); + $logsize += $nbytes; + last unless $nbytes; + print BUILDLOG $buff; + + if($max_size and $logsize > $max_size) { + alarm(0); + print BUILDLOG "\nFailed: Log size limit exceeded.\n"; + # Negative signals send to process group + kill(-15, $pid); + sleep(10) if kill(0, $pid); + kill(-9, $pid); + last; + } + } + }; + alarm(0); + if($@) { + die unless $@ eq "alarm\n"; + print BUILDLOG "\nFailed: Build process timed out.\n"; + kill(-15, $pid); + sleep(10); + kill(-9, $pid); + } + + close(BUILDLOG); + close(BUILD); + if(WIFEXITED($?)) { + return WEXITSTATUS($?); + } else { + return -1; + } +} + # Build all packages, skipping any with failed deps # pkglist is a list of package names to build in order. # skiplist is a list of package names to never build. @@ -422,14 +557,14 @@ my $buildcmd = "printf '\n\n' | fink --yes"; $buildcmd .= " --build-as-nobody" if $BuildNobody; - $buildcmd .= " rebuild $pkg > $rundir/logs/$pkg.log 2>&1"; - my $ret = system($buildcmd); + $buildcmd .= " rebuild $pkg 2>&1"; + my $ret = doBuild($buildcmd, $pkg); if($ret != 0 and defined($BuildNobody) and $BuildNobody eq "try") { $buildcmd =~ s/ --build-as-nobody//; move("$rundir/logs/$pkg.log", "$rundir/logs/$pkg.log.nobody"); doLog("Build of $pkg failed, trying as root..."); - $ret = system($buildcmd); + $ret = doBuild($buildcmd, $pkg); if($ret != 0) { unlink("$rundir/logs/$pkg.log.nobody"); } else { ------------------------------------------------------- This SF.Net email is sponsored by xPML, a groundbreaking scripting language that extends applications into web and mobile media. Attend the live webcast and join the prime developer group breaking into this new coding territory! http://sel.as-us.falkag.net/sel?cmd=lnk&kid=110944&bid=241720&dat=121642 _______________________________________________ Fink-commits mailing list Fink-commits@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/fink-commits