All pastes #2048636 Raw Edit

aginies

public text v1 · immutable
#2048636 ·published 2011-04-20 13:27 UTC
rendered paste body
package Iurt::Chroot;use strict;use base qw(Exporter);use MDV::Distribconf::Build;use MDK::Common;use Iurt::Process qw(clean sudo);use Iurt::Config qw(dump_cache_par);use Iurt::Util qw(plog);use File::Temp 'mktemp';use File::Path 'mkpath';our @EXPORT = qw(    clean_chroot_tmp    clean_unionfs    clean_all_chroot_tmp    clean_all_unionfs    clean_chroot    update_chroot    dump_rpmmacros    add_local_user    create_temp_chroot    remove_chroot    create_chroot    create_build_chroot    check_chroot    check_build_chroot);    my $sudo = '/usr/bin/sudo';=head2 clean_chroot($chroot, $run, $only_clean)Create or clean a chrootI<$chroot> chroot pathI<$run> is the running environmentI<%only_clean> only clean the chroot, do not create a new oneReturn true.=cutsub clean_chroot {    my ($chroot, $chroot_tar, $run, $config, $o_only_clean, $o_only_tar) = @_;    plog('DEBUG', "clean chroot");    if (-d $chroot && !$o_only_tar) {	system("$sudo umount $chroot/proc &> /dev/null");	system("$sudo umount $chroot/dev/pts &> /dev/null");	if ($run->{icecream}) {	    system("$sudo umount $chroot/var/cache/icecream &> /dev/null");	}	if (-d "$chroot/urpmi_medias/") {	    system("$sudo umount $chroot/urpmi_medias");	}	# Do not run rm if there is something still mounted there	open(my $FP, "/proc/mounts") or die $!;	my @list = grep { /$chroot/ } <$FP>;	close($FP);	if ($#list >= 0) {	    # Still referenced	    return 1;	}	sudo($run, $config, '--rm', '-r', $chroot);    }    return 1 if $o_only_clean;    mkdir $chroot;    # various integrity checking    if ($o_only_tar	&& -f "$chroot/home/builder/.rpmmacros"	&& -d "$chroot/home/builder"	&& -d "$chroot/proc") {	return 1;    }     # First try    if (system($sudo, 'tar', '-C', $chroot, '-xf', $chroot_tar)) {	create_build_chroot($chroot, $chroot_tar, $run, $config);    }    # <mrl> 20071106 Second try?    if (!-d "$chroot/proc" || !-d "$chroot/home/builder") {	create_build_chroot($chroot, $chroot_tar, $run, $config);    }    if (!dump_rpmmacros($run, $config, "$chroot/home/builder/.rpmmacros")) {	plog('ERROR', "Failed to dump macros");	return;    }    if (system("$sudo mount none -t proc $chroot/proc")) {	plog('ERROR', "Failed to mount proc");	return;    }    if (system("$sudo mount none -t devpts $chroot/dev/pts")) {	plog('ERROR', "Failed to mount dev/pts");	return;    }    if ($run->{icecream}) {	system("$sudo mkdir -p $chroot/var/cache/icecream");	if (system("$sudo mount -o bind /var/cache/icecream $chroot/var/cache/icecream")) {	    plog('ERROR', "Failed to mount var/cache/icecream");	    return;	}    }    if ($run->{additional_media} && $run->{additional_media}{repository}) {	plog('DEBUG', "add the additional media $run->{additional_media}{repository}"); 	plog('DEBUG', "$sudo $chroot /usr/sbin/urpmi.addmedia additional $run->{additional_media}{repository}");	system("$sudo chroot $chroot /usr/sbin/urpmi.addmedia additional $run->{additional_media}{repository}");    }    1;}  =head2 update_chroot($chroot, $run, $only_clean)Updates chrootI<$chroot> chroot pathI<$run> is the running environmentI<%only_clean> only clean the chroot, do not create a new oneReturn true.=cutsub update_chroot {    my ($_chroot, $_chroot_tar, $_run, $_config, $_only_clean, $_only_tar) = @_;    plog('DEBUG', "update chroot");#    my $urpmi = $run->{urpmi};#    $urpmi->auto_select($chroot);}sub dump_rpmmacros {    my ($run, $config, $file) = @_;    my $f;    #plog("adding rpmmacros to $file");    if (!open $f, qq(| $sudo sh -c "cat > $file")) {	plog("ERROR: could not open $file ($!)");	return 0;    }    my $packager = $run->{packager} || $config->{packager};    print $f qq(\%_topdir                \%(echo \$HOME)/rpm\%_tmppath               \%(echo \$HOME)/rpm/tmp/\%distribution           $config->{distribution}\%vendor                 $config->{vendor}\%packager               $packager);    # need to be root for permission}sub add_local_user {    my ($chroot_tmp, $run, $config, $luser, $uid) = @_;    my $program_name = $run->{program_name};    # change the builder user to the local user id    # FIXME it seems that unionfs does not handle well the change of the    #       uid of files    # if (system(qq|sudo chroot $chroot_tmp usermod -u $run->{uid} builder|)) {        # this should not be necessary as the builder user is supposed to have    # the macros already    foreach my $p ('RPMS', 'BUILD', 'SPECS', 'SRPMS', 'SOURCES') {	-d "$chroot_tmp/home/builder/rpm/$p" and next;	sudo($run, $config, "--mkdir", "$chroot_tmp/home/builder/rpm/$p");    }     dump_rpmmacros($run, $config, "$chroot_tmp/home/builder/.rpmmacros") or return;    if ($uid) {	if (system($sudo, 'chroot', $chroot_tmp, 'useradd', '-M', '-u', $uid, $luser) ||	    system("$sudo chroot $chroot_tmp id $luser >/dev/null 2>&1")) {	    plog('ERR', "ERROR: setting userid $uid to $luser in " .		"$chroot_tmp failed, checking the chroot");	    check_build_chroot($run->{chroot_path}, $run->{chroot_tar}, $run,		$config) or return;	}    } else {	# the program has been launch as root, setting the home to /home/root for compatibility	system($sudo, 'chroot', $chroot_tmp, 'usermod', '-d', "/home/$luser", '-u', $uid, '-o', '-l', $luser, 'root');    }    if (system(qq($sudo chroot $chroot_tmp cp -R /home/builder /home/$luser))) {	plog("ERROR: could not initialized $luser directory");	return;    }    if (system(qq($sudo chroot $chroot_tmp chown -R $uid /home/$luser))) {	die "ERROR $program_name: could not initialized $luser directory\n";     }    1;}sub create_temp_chroot {    my ($run, $config, $cache, $union_id, $chroot_tmp, $chroot_tar, $o_srpm) = @_;    my $home = $config->{local_home};    my $debug_tag = $run->{debug_tag};    my $unionfs_dir = $run->{unionfs_dir};    if ($run->{unionfs_tmp}) {	my $mount_point = "$unionfs_dir/unionfs.$run->{run}.$union_id";	plog(2, "cleaning temp chroot $mount_point");	if (!clean_mnt($run, $mount_point, $run->{verbose})) {	    dump_cache_par($run);	    die "FATAL: can't kill remaining processes acceding $mount_point";	}	my $tmpfs;	# we cannont just rm -rf $tmpfs, this create defunct processes	# afterwards (and lock particularly hard the urpmi database)	#	$union_id = clean_unionfs($unionfs_dir, $run, $run->{run}, $union_id);	$tmpfs = "$unionfs_dir/tmpfs.$run->{run}.$union_id";	$chroot_tmp = "$unionfs_dir/unionfs.$run->{run}.$union_id";	if (!-d $tmpfs) {	    if (!mkpath($tmpfs)) {		plog("ERROR: Could not create $tmpfs ($!)");		return;	    }	}	if (! -d $chroot_tmp) {	    if (!mkpath($chroot_tmp)) {		plog("ERROR: Could not create $chroot_tmp ($!)");		return;	    }	}	if ($cache->{no_unionfs}{$o_srpm}) {	    $run->{unionfs_tmp} = 0;	    clean_chroot($chroot_tmp, $chroot_tar, $run, $config);	} else {	    # if the previous package has been built without unionfs, chroot need to be cleaned	    if (!$run->{unionfs_tmp}) {		clean_chroot($chroot_tmp, $chroot_tar, $run, $config);	    } else {		# only detar the chroot if not already		clean_chroot($chroot_tmp, $chroot_tar, $run, $config, 0, 1);	    }	    $run->{unionfs_tmp} = 1;	    if (system(qq($sudo mount -t tmpfs none $tmpfs &>/dev/null))) {		plog("ERROR: can't mount $tmpfs ($!)"); 		return;	    }	    if (system(qq($sudo mount -o dirs=$tmpfs=rw:$home/chroot_$run->{distro_tag}$debug_tag=ro -t unionfs none $chroot_tmp &>/dev/null))) {		plog("ERROR: can't mount $tmpfs and $home/chroot_$run->{distro_tag}$debug_tag with unionfs ($!)");		return;	    }	    if (system("$sudo mount -t proc none $chroot_tmp/proc &>/dev/null")) {		plog("ERROR: can't mount /proc in chroot $chroot_tmp ($!)");		return;	    }	    if (!-d "$chroot_tmp/dev/pts") {		if (sudo($run, $config, "--mkdir", "$chroot_tmp/dev/pts")) {		    plog("ERROR: can't create /dev/pts in chroot $chroot_tmp ($!)");		    return;		}		if (system($sudo, "mount", "-t", "devpts", "none", "$chroot_tmp/dev/pts &>/dev/null")) {		    plog("ERROR: can't mount /dev/pts in the chroot $chroot_tmp ($!)");		    return;		}	    }	}    } else {	plog("Install new chroot");	plog('DEBUG', "... in $chroot_tmp");	clean_chroot($chroot_tmp, $chroot_tar, $run, $config);	update_chroot($chroot_tmp, $run, $config);    }    $union_id, $chroot_tmp;}sub remove_chroot {    my ($run, $dir, $func, $prefix) = @_;    plog("Remove existing chroot");    plog('DEBUG', "... dir $dir all $run->{clean_all} prefix $prefix");    if ($run->{clean_all}) {	opendir my $chroot_dir, $dir;	foreach (readdir $chroot_dir) {	    next if !-d "$dir/$_" || /\.{1,2}/;	    plog("cleaning old chroot for $_ in $dir");	    $func->($run, "$dir/$_", $prefix);	}    } else {	foreach my $user (@{$run->{clean}}) {	    plog("cleaning old chroot for $user in $dir");	    $func->($run, "$dir/$user", $prefix);	}    }} sub clean_mnt {    my ($run, $mount_point, $verbose) = @_;    return clean($run, $mount_point, "/sbin/fuser", "$sudo /sbin/fuser -k", $verbose);}sub clean_all_chroot_tmp {    my ($run, $chroot_dir, $prefix) = @_;    plog(1, "cleaning all old chroot remaining dir in $chroot_dir");    my $dir;    if (!opendir $dir, $chroot_dir) { 	plog("ERROR: can't open $chroot_dir ($!)");	return;    }    foreach (readdir($dir)) {	/$prefix/ or next;	clean_chroot_tmp($run, $chroot_dir, $_);    }    closedir $dir;}sub clean_unionfs {    my ($unionfs_dir, $_run, $r, $union_id) = @_;    -d "$unionfs_dir/unionfs.$r.$union_id" or return $union_id;    plog(2, "cleaning unionfs $unionfs_dir/unionfs.$r.$union_id");    my $nok = 1;    my $path = "$unionfs_dir/unionfs.$r.$union_id";    while ($nok) {	$nok = 0;	foreach my $fs ([ 'proc', 'proc' ], [ 'dev/pts', 'devpts' ]) {	    my ($dir, $type) = @$fs;	    if (-d "$path/$dir" && check_mounted("$path/$dir", $type)) {		plog(1, "clean_unionfs: umounting $path/$dir\n");		if (system("$sudo umount $path/$dir &>/dev/null")) { 		    plog("ERROR: could not umount $path/$dir");		}	    }	}	foreach my $t ('unionfs', 'tmpfs') {	    # unfortunately quite oftem the unionfs is busy and could not	    # be unmounted	    my $d = "$unionfs_dir/$t.$r.$union_id";	    if (-d $d && check_mounted($d, $t)) {		$nok = 1;		system("$sudo /sbin/fuser -k $d &> /dev/null");		plog(3, "umounting $d");		if (system(qq($sudo umount $d &> /dev/null))) {		    plog(2, "WARNING: could not umount $d ($!)");		    return $union_id + 1;		}	    }	}    }    foreach my $t ('unionfs', 'tmpfs') {	my $d = "$unionfs_dir/$t.$r.$union_id";	plog(2, "removing $d");	if (system($sudo, 'rm', '-rf', $d)) {	    plog("ERROR: removing $d failed ($!)");	    return $union_id + 1;	}    }    $union_id;}sub clean_chroot_tmp {    my ($run, $chroot_dir, $dir) = @_;    my $d = "$chroot_dir/$dir";    foreach my $m ('proc', 'dev/pts', 'urpmi_medias', 'var/cache/icecream') {	if (system("$sudo umount $d/$m &>/dev/null") && $run->{verbose} > 1) { 	    plog("ERROR: could not umount /$m in $d/");	    # FIXME: <mrl> We can't go on, otherelse we will remove something	    # that we shouldn't. But for that, we should:	    #  a) Check for all mount-points inside the chroot	    #  b) Try to unmount only the needed ones, otherelse the errors	    # can be misleading.	}    }    plog(1, "cleaning $d");    system("$sudo /sbin/fuser -k $d &> /dev/null");    plog(1, "removing $d");    system($sudo, 'rm', '-rf', $d);}sub check_mounted {    my ($mount_point, $type) = @_;    my $mount;    if (!open $mount, '/proc/mounts') {	plog("ERROR: could not open /proc/mounts");	return;    }    $mount_point =~ s,//+,/,g;    local $_;    while (<$mount>) {	return 1 if /^\w+ $mount_point $type /;    }    0;}sub create_build_chroot {    my ($chroot, $chroot_tar, $run, $config) = @_;    create_chroot($chroot, $chroot_tar, $run, $config,			{ packages => $config->{basesystem_packages} });}sub create_chroot {    my ($chroot, $chroot_tar, $run, $config, $opt) = @_;    my $tmp_tar = mktemp("$chroot_tar.tmp.XXXXXX");    my $tmp_chroot = mktemp("$chroot.tmp.XXXXXX");    my $rebuild;    my $clean = sub {	plog("Remove temporary chroot tarball");	sudo($run, $config, '--rm', '-r', $tmp_chroot, $tmp_tar);    };    plog('NOTIFY', "creating chroot");    plog('DEBUG', "... with packages " . join(', ', @{$opt->{packages}}));    if (mkdir($tmp_chroot) && (!-f $chroot_tar || link $chroot_tar, $tmp_tar)) {	if (!-f $chroot_tar) {	    plog("rebuild chroot tarball");	    $rebuild = 1;	    if (!build_chroot($run, $config, $tmp_chroot, $chroot_tar, $opt)) {		plog('NOTIFY', "creating chroot failed.");		$clean->();		sudo($run, $config, '--rm', '-r', $chroot, $chroot_tar);		return;	    }	} else {	    plog('DEBUG', "decompressing /var/log/qa from $chroot_tar in $tmp_chroot");	    system($sudo, 'tar', 'xf', $chroot_tar, '-C', $tmp_chroot, "./var/log/qa");	    my $qa;	    if (open $qa, "$tmp_chroot/var/log/qa") {		my $ok;		my $f;		while (!$ok && ($f = <$qa>)) {		    chomp $f;		    if (!-f "$config->{basesystem_media_root}/media/$config->{basesystem_media}/$f") {			plog('DEBUG', "$f has changed");			plog('NOTIFY', "Rebuilding chroot tarball");			$rebuild = 1;			sudo($run, $config, '--rm', '-r', $tmp_chroot);			mkdir $tmp_chroot;			if (!build_chroot($run, $config, $tmp_chroot, $chroot_tar, $opt)) { 			    plog('NOTIFY', "creating chroot failed.");			    $clean->();			    return;			}			$ok = 1;		    }		}	    } else {		plog('DEBUG', "can't open $tmp_chroot/var/log/qa");		plog('ERR', "can't check chroot, recreating");		if (!build_chroot($run, $config, $tmp_chroot, $chroot_tar, $opt)) {		    plog('NOTIFY', "creating chroot failed.");		    $clean->();       		    return;		}	    }	}	link $tmp_tar, $chroot_tar;    } else {	die "FATAL: could not initialize chroot ($!)\n";    }    if (!-d $chroot || $rebuild) {	plog('DEBUG', "recreate chroot $chroot");	plog('NOTIFY', "recreate chroot");	my $urpmi = $run->{urpmi};	$urpmi->clean_urpmi_process($chroot);	sudo($run, $config, '--rm', '-r', $chroot, $tmp_tar);	mkdir_p $chroot;	system($sudo, 'tar', 'xf', $chroot_tar, '-C', $chroot);	plog('NOTIFY', "chroot recreated in $chroot_tar (live in $chroot)");    }        $clean->();    1;}sub build_chroot {    my ($run, $config, $tmp_chroot, $chroot_tar, $opt) = @_;    plog('DEBUG', "building the chroot with "			. join(', ', @{$opt->{packages}}));    sudo($run, $config, "--mkdir", "-p", "$tmp_chroot/dev/pts",		"$tmp_chroot/etc/sysconfig", "$tmp_chroot/proc",	        "$tmp_chroot/var/lib/rpm");    # create empty files    foreach ('/etc/mtab') {	system($sudo, 'touch', "$tmp_chroot$_");    }    system($sudo, 'mknod', "$tmp_chroot/dev/null", 'c', 1, 3);    system("$sudo chmod a+rw $tmp_chroot/dev/null");    #system(qq($sudo sh -c "echo 127.0.0.1 localhost > $tmp_chroot/etc/hosts"));    # warly some program perform a gethostbyname(hostname) and in the cluster the     # name are not resolved via DNS but via /etc/hosts    print "$run $config, --cp, /etc/hosts, $tmp_chroot/etc/\n";    sudo($run, $config, '--cp', "/etc/hosts", "$tmp_chroot/etc/");    sudo($run, $config, '--cp', "/etc/resolv.conf", "$tmp_chroot/etc/");    # install chroot    my $urpmi = $run->{urpmi};    if ($urpmi->{use__urpmi_root}) {	if (!$urpmi->add_media__urpmi_root($tmp_chroot)) {	    plog('ERROR', "urpmi.addmedia --urpmi-root failed");	    return 0;	}    }    $urpmi->set_command($tmp_chroot);    # 20060826 warly urpmi --root does not work properly    $urpmi->install_packages(	"chroot",	$tmp_chroot,	$run->{local_spool},	{},	'initialize',	"[ADMIN] creation of initial chroot failed on $run->{my_arch}",	{ maintainer => $config->{admin} },	@{$opt->{packages}}    );    # Yes, /usr/lib/rpm/rpmb even for x86_64    if (! -f "$tmp_chroot/bin/rpm") {	plog('ERROR', "Base packages missing in genenrated chroot.");	return 0;    }    # remove files used by --urpmi-root    sudo($run, $config, "--rm", "$tmp_chroot/etc/urpmi/urpmi.cfg");    sudo($run, $config, "--rm", "$tmp_chroot/var/lib/urpmi/*");    system($sudo, 'sh', '-c', "chroot $tmp_chroot rpm -qa --qf '\%{NAME}-\%{VERSION}-\%{RELEASE}.\%{ARCH}.rpm\n' | sort > $tmp_chroot/var/log/qa");    #    # CM: Choose a sub-500 uid to prevent collison with $luser    #    system($sudo, 'chroot', $tmp_chroot, 'adduser', '-o', '--uid', 499, 'builder');    sudo($run, $config, "--mkdir", "-p", "$tmp_chroot/home/builder/rpm");    foreach my $p (qw(RPMS BUILD SPECS SRPMS SOURCES tmp)) {	-d "$tmp_chroot/home/builder/rpm/$p" and next;	sudo($run, $config, "--mkdir", "$tmp_chroot/home/builder/rpm/$p");    }    system($sudo, 'chown', '-R', 499, "$tmp_chroot/home/builder");    # FIXME: <mrl> Be careful! Damn ugly hack right below!    sudo($run, $config, "--rm", "$tmp_chroot/var/lib/rpm/__db*");    system("$sudo umount $tmp_chroot/proc &> /dev/null");    system("$sudo umount $tmp_chroot/dev/pts &> /dev/null");    if ($run->{icecream}) {	system("$sudo umount $tmp_chroot/var/cache/icecream &> /dev/null");    }    if (-d "$tmp_chroot/urpmi_medias/") {	system("$sudo umount $tmp_chroot/urpmi_medias");    }    return !system($sudo, 'tar', 'czf', $chroot_tar, '-C', $tmp_chroot, '.');}sub check_build_chroot {    my ($chroot, $chroot_tar, $run, $config) = @_;    check_chroot($chroot, $chroot_tar, $run, $config,		{ packages => $config->{basesystem_packages} });}sub check_chroot {    my ($chroot, $chroot_tar, $run, $config, $opt) = @_;    plog('DEBUG', "checking basesystem tar");        my (@stat) = stat $chroot_tar;    if (time -$stat[9] > 604800) {	plog('WARN', "chroot tarball too old, force rebuild");	sudo($run, $config, '--rm', '-r', $chroot, $chroot_tar);    }    create_chroot($chroot, $chroot_tar, $run, $config, $opt);}sub clean_all_unionfs {    my ($run, $unionfs_dir) = @_;    plog(2, "Cleaning old unionfs remaining dir in $unionfs_dir");    my $dir;    if (!opendir $dir, $unionfs_dir) {	plog(0, "FATAL could not open $unionfs_dir ($!)");	return;    }    foreach (readdir $dir) {	/unionfs\.((?:0\.)?\d+)\.(\d*)$/ or next;	clean_unionfs($unionfs_dir, $run, $1, $2);    }    closedir $dir;}1;