File: //scripts.20110531.215904.25158/RcsRecord.pm
package RcsRecord;
# cpanel - RcsRecord.pm Copyright(c) 2010 cPanel, Inc.
# All rights Reserved.
# copyright@cpanel.net http://cpanel.net
# This code is subject to the cPanel license. Unauthorized copying is prohibited
use Cpanel::SafeFile ();
use Cpanel::FindBin ();
use IPC::Open3 ();
our (@ISA,@EXPORT,$VERSION);
require Exporter;
@ISA = qw(Exporter);
@EXPORT = qw(rcsrecord);
$VERSION = '4.0';
sub rcsrecord {
my ( $file, $description, $skip_lock ) = @_;
if ( !$description ) {
$description = '';
}
my ( $mode, $uid, $gid, $size, $mtime ) = ( stat($file) )[ 2, 4, 5, 7, 9 ];
if ( !$mtime ) { return; }
my $maxsize = ( $size * 25 );
if ( $maxsize < 32768 ) { $maxsize = 32768; }
my @DIR = split( /\//, $file );
my $truefile = pop(@DIR);
my $dir = '';
if ( $#DIR > -1 ) {
$dir = join( '/', @DIR );
}
if ( !$dir ) {
$dir = '.';
}
if ( $uid != $> ) {
my $dir_uid = ( stat($dir) )[4];
if ( $dir_uid != $> ) {
warn "rcsrecord skipped on $file because we do not own it or the directory it is in";
return;
}
}
my $rcslock;
if (!$skip_lock) {
$rcslock = Cpanel::SafeFile::safelock( $file );
if ( !$rcslock ) {
#warn "Could not read from $file\n";
return;
}
}
if ( -e "$dir/,$truefile," ) {
my $tmp_mtime = ( stat("$dir/,$truefile,") )[9];
if ( ( time() - $tmp_mtime ) > 360 ) { #remove temp files after 5 minutes
unlink("$dir/,$truefile,");
}
}
my $rcs_bin;
# This will mask all of the bits that weren't set in the original file (except user r/w)
# RCS commands create temporary files that are 0444 by default
my $orig_umask = umask( ( ( $mode | 0600 ) ^ 0777 ) & 0777 );
if ( !-e "$dir/$truefile,v" ) {
$rcs_bin = Cpanel::FindBin::findbin('rcs');
if ($rcs_bin) {
my $rcs_pid = IPC::Open3::open3( my $wtr_rcs, ">&STDERR", ">&STDERR", $rcs_bin, '-q', '-i', $file );
if ($rcs_pid) {
close($wtr_rcs);
waitpid( $rcs_pid, 0 );
}
else {
umask($orig_umask);
if ($rcslock) { Cpanel::SafeFile::safeunlock( $rcslock ); }
return;
}
}
else {
umask($orig_umask);
if ($rcslock) { Cpanel::SafeFile::safeunlock( $rcslock ); }
return;
}
}
my $ci_bin = Cpanel::FindBin::findbin('ci');
if ($ci_bin) {
my $safe_msg = "Modified by $0 $description";
$safe_msg =~ s/\"//g;
my $rdr_ci;
my $ci_pid = IPC::Open3::open3( my $wtr_ci, $rdr_ci, $rdr_ci, $ci_bin, '-l', qq{-m"$safe_msg"} , $file );
if ($ci_pid) {
close($wtr_ci);
my $buffer;
while ( sysread( $rdr_ci, $buffer, 32768 ) ) { } #prevent sig pipe
waitpid( $ci_pid, 0 );
}
else {
umask($orig_umask);
if ($rcslock) { Cpanel::SafeFile::safeunlock( $rcslock ); }
return;
}
}
else {
umask($orig_umask);
if ($rcslock) { Cpanel::SafeFile::safeunlock( $rcslock ); }
return;
}
if ( ( stat("$dir/$truefile,v") )[7] > $maxsize ) {
my $rlog_bin = Cpanel::FindBin::findbin('rlog');
my @REVS;
if ($rlog_bin) {
my $head;
my $rlog_pid = IPC::Open3::open3( my $wtr_rlog, my $rdr_rlog, ">&STDERR", $rlog_bin, $file );
if ($rlog_pid) {
close($wtr_rlog);
while ( readline($rdr_rlog) ) {
if (/revision[\s\t]+([\d\.]+)/) {
push @REVS, $1;
}
}
close($rdr_rlog);
waitpid( $rlog_pid, 0 );
}
else {
umask($orig_umask);
if ($rcslock) { Cpanel::SafeFile::safeunlock( $rcslock ); }
return;
}
}
else {
umask($orig_umask);
if ($rcslock) { Cpanel::SafeFile::safeunlock( $rcslock ); }
return;
}
if (@REVS) {
my $rev = $REVS[ int( $#REVS / 2 ) ]; #remove 50% of revisions
if ( !$rcs_bin ) { $rcs_bin = Cpanel::FindBin::findbin('rcs'); }
if ( !$rcs_bin ) {
umask($orig_umask);
if ($rcslock) { Cpanel::SafeFile::safeunlock( $rcslock ); }
return;
}
my $rcs_pid = IPC::Open3::open3( my $wtr_rcs, ">&STDERR", ">&STDERR", $rcs_bin, '-q', '-o:' . $rev, $file );
if ($rcs_pid) {
close($wtr_rcs);
waitpid( $rcs_pid, 0 );
}
else {
umask($orig_umask);
if ($rcslock) { Cpanel::SafeFile::safeunlock( $rcslock ); }
return;
}
}
}
umask($orig_umask);
if ($rcslock) { Cpanel::SafeFile::safeunlock( $rcslock ); }
chown $uid, $gid, $file;
}
1;