ee2da812b2
git-svn-id: http://emdebian.org/svn/current@6659 563faec7-e20c-0410-992a-a66f704d0ccd
781 lines
23 KiB
Perl
Executable file
781 lines
23 KiB
Perl
Executable file
#!/usr/bin/perl
|
|
|
|
# Copyright (C) 2009 Neil Williams <codehelp@debian.org>
|
|
#
|
|
# This package is free software; you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation; either version 3 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
use IO::File;
|
|
use Config::Auto;
|
|
use File::Basename;
|
|
use Parse::Debian::Packages;
|
|
use strict;
|
|
use warnings;
|
|
use vars qw/ $progname $ourversion $dstrap $extra @aptsources
|
|
@archives $deb $cachedir $config_str %packages $retval $str $retries
|
|
$dir $include $arch $foreign $suite $url $unpack $sourcedir
|
|
@e $sourcesname $libdir $dpkgdir @debootstrap %suites $mirror $etcdir
|
|
$repo @dirs @touch %sources $section %keys $host $key $value $type
|
|
$file $config $tidy $noauth $keyring %keyrings $deflist /;
|
|
$progname = basename($0);
|
|
$ourversion = "0.0.7";
|
|
$unpack = "true";
|
|
|
|
while( @ARGV ) {
|
|
$_= shift( @ARGV );
|
|
last if m/^--$/;
|
|
if (!/^-/) {
|
|
unshift(@ARGV,$_);
|
|
last;
|
|
}
|
|
elsif (/^(-\?|-h|--help|--version)$/) {
|
|
&usageversion();
|
|
exit( 0 );
|
|
}
|
|
elsif (/^(-f|--file)$/) {
|
|
$file = shift(@ARGV);
|
|
}
|
|
elsif (/^(-a|--arch)$/) {
|
|
$arch = shift(@ARGV);
|
|
}
|
|
elsif (/^(-d|--dir)$/) {
|
|
$dir = shift(@ARGV);
|
|
$dir .= ($dir =~ m:/$:) ? '' : "/";
|
|
}
|
|
elsif (/^(--tidy-up)$/) {
|
|
$tidy++;
|
|
}
|
|
elsif (/^(--source-dir)$/) {
|
|
$sourcedir = shift (@ARGV);
|
|
$sourcedir .= ($sourcedir =~ m:/$:) ? '' : "/";
|
|
$sourcedir = (-d $sourcedir) ? $sourcedir : undef;
|
|
}
|
|
elsif (/^(--no-auth)$/) {
|
|
$noauth++;
|
|
}
|
|
else {
|
|
die "$progname: Unknown option $_.\n";
|
|
}
|
|
}
|
|
die ("Need a configuration file - use $progname -f\n")
|
|
if (not defined $file);
|
|
$config = Config::Auto::parse("$file");
|
|
%keys=();
|
|
foreach $key (%$config)
|
|
{
|
|
$type = lc($key) if (ref $key ne "HASH");
|
|
$value = $key if (ref $key eq "HASH");
|
|
$keys{$type} = $value;
|
|
}
|
|
%sources=();
|
|
%packages=();
|
|
%suites=();
|
|
%keyrings=();
|
|
@aptsources=();
|
|
foreach $section (sort keys %keys)
|
|
{
|
|
if ($section eq "general")
|
|
{
|
|
$arch = $keys{$section}{'arch'} if (not defined $arch);
|
|
$dir = $keys{$section}{'directory'} if (not defined $dir);
|
|
# support the original value but replace by new value.
|
|
$unpack = lc($keys{$section}{'unpack'})
|
|
if (defined $keys{$section}{'forceunpack'});
|
|
$unpack = lc($keys{$section}{'unpack'})
|
|
if (defined $keys{$section}{'unpack'});
|
|
$tidy++ if ((defined $keys{$section}{'cleanup'}) and
|
|
($keys{$section}{'cleanup'} eq "true"));
|
|
$noauth++ if ((defined $keys{$section}{'noauth'}) and
|
|
($keys{$section}{'noauth'} eq "true"));
|
|
$sourcedir = $keys{$section}{'retainsources'} if
|
|
((defined $keys{$section}{'retainsources'}) and
|
|
(-d $keys{$section}{'retainsources'}));
|
|
@debootstrap = split(' ', lc($keys{$section}{'debootstrap'}));
|
|
@aptsources = split (' ', lc($keys{$section}{'aptsources'}));
|
|
}
|
|
else
|
|
{
|
|
$sources{$section}=$keys{$section}{'source'};
|
|
$packages{$section}=$keys{$section}{'packages'};
|
|
$suites{$section}=$keys{$section}{'suite'};
|
|
$keyrings{$section}=$keys{$section}{'keyring'};
|
|
}
|
|
}
|
|
print "$progname $ourversion using $file\n";
|
|
$host = `dpkg-architecture -qDEB_BUILD_ARCH`;
|
|
chomp ($host);
|
|
if (not defined $arch)
|
|
{
|
|
$arch = $host;
|
|
printf ("Defaulting architecture to native: %s\n",$arch);
|
|
}
|
|
else
|
|
{
|
|
printf ("Using foreign architecture: %s\n", $arch);
|
|
}
|
|
$foreign++ if ($host ne $arch);
|
|
|
|
unless (keys %sources and @aptsources)
|
|
{
|
|
my $msg = sprintf("No sources defined for a foreign multistrap
|
|
Using your existing apt sources. To use different sources,
|
|
and list them with aptsources= in '%s'.", $file);
|
|
warn ("$progname: $msg\n");
|
|
$deflist = prepare_sources_list();
|
|
}
|
|
|
|
printf ("%s building %s multistrap on '%s'\n", $progname, $arch, $host);
|
|
$cachedir = "var/cache/apt/"; # archives
|
|
$libdir = "var/lib/apt/"; # lists
|
|
$etcdir = "etc/apt/"; # sources
|
|
$dpkgdir = "var/lib/dpkg/"; # state
|
|
|
|
mkdir ("$dir") if (not -d "$dir");
|
|
system ("mkdir -p ${dir}${cachedir}") if (not -d "${dir}${cachedir}");
|
|
system ("mkdir -p ${dir}${libdir}") if (not -d "${dir}${libdir}");
|
|
system ("mkdir -p ${dir}${dpkgdir}") if (not -d "${dir}${dpkgdir}");
|
|
system ("mkdir -p ${dir}etc/apt/sources.list.d/")
|
|
if (not -d "${dir}etc/apt/sources.list.d/");
|
|
system ("mkdir -p ${dir}etc/apt/preferences.d/")
|
|
if (not -d "${dir}etc/apt/preferences.d/");
|
|
@dirs = qw/ alternatives info parts updates/;
|
|
@touch = qw/ diversions statoverride status lock/;
|
|
foreach my $dpkgd (@dirs) {
|
|
if (not -d "${dir}${dpkgdir}$dpkgd") {
|
|
mkdir "${dir}${dpkgdir}$dpkgd";
|
|
}
|
|
}
|
|
foreach my $file (@touch) {
|
|
utime(time, time, "${dir}${dpkgdir}/$file") or (
|
|
open(F, ">${dir}${dpkgdir}/$file") && close F );
|
|
}
|
|
utime(time, time, "${dir}etc/shells") or
|
|
(open(F, ">${dir}etc/shells") && close F );
|
|
|
|
if (not -d "${dir}etc/network") {
|
|
mkdir "${dir}etc/network";
|
|
}
|
|
|
|
if (not -d "${dir}dev") {
|
|
mkdir "${dir}dev";
|
|
}
|
|
|
|
# prevent the absolute symlink in libc6 from allowing
|
|
# writes outside the multistrap root dir. See: #553599
|
|
if (-l "${dir}lib64" ) {
|
|
my $r = readlink "${dir}lib64";
|
|
if ($r =~ m:^/:)
|
|
{
|
|
my $old = `pwd`;
|
|
chomp ($old);
|
|
unlink "${dir}lib64";
|
|
chdir ("$dir");
|
|
print "INF: ./lib64 -> /lib symbolic link reset to ./lib.\n";
|
|
symlink "./lib", "lib64";
|
|
chdir ("${old}");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
my $old = `pwd`;
|
|
chomp ($old);
|
|
chdir ("$dir");
|
|
print "INF: Setting ./lib64 -> ./lib symbolic link.\n";
|
|
symlink "./lib", "lib64";
|
|
chdir ("${old}");
|
|
}
|
|
|
|
unlink ("${dir}etc/apt/sources.list.d/multistrap.sources.list")
|
|
if (-f "${dir}etc/apt/sources.list.d/multistrap.sources.list");
|
|
unlink ("${dir}etc/apt/sources.list")
|
|
if (-f "${dir}etc/apt/sources.list");
|
|
|
|
foreach $repo (sort keys %suites)
|
|
{
|
|
if (not -e "${dir}${cachedir}") {
|
|
mkdir "${dir}${cachedir}";
|
|
}
|
|
if (not -e "$dir/${libdir}lists") {
|
|
mkdir "$dir/${libdir}lists";
|
|
}
|
|
if (not -e "$dir/${libdir}lists/partial") {
|
|
mkdir "$dir/${libdir}lists/partial";
|
|
}
|
|
if (not -e "$dir/${cachedir}archives") {
|
|
mkdir "$dir/${cachedir}archives";
|
|
}
|
|
if (not -e "$dir/${cachedir}archives/partial") {
|
|
mkdir "$dir/${cachedir}archives/partial";
|
|
}
|
|
}
|
|
foreach my $aptsrc (@aptsources)
|
|
{
|
|
if (defined $deflist)
|
|
{
|
|
open (SOURCES, ">>${dir}etc/apt/sources.list.d/multistrap.sources.list")
|
|
or die "Cannot open sources list $!";
|
|
print SOURCES $deflist;
|
|
close SOURCES;
|
|
}
|
|
elsif (-d "${dir}etc/apt/")
|
|
{
|
|
open (SOURCES, ">>${dir}etc/apt/sources.list.d/multistrap.sources.list")
|
|
or die _g("Cannot open sources list"). $!;
|
|
$mirror = $sources{$aptsrc};
|
|
$suite = $suites{$aptsrc};
|
|
print SOURCES<<END;
|
|
deb $mirror $suite main
|
|
deb-src $mirror $suite main
|
|
END
|
|
close SOURCES;
|
|
}
|
|
}
|
|
my $k;
|
|
foreach my $pkg (values %keyrings)
|
|
{
|
|
next if (not defined $pkg);
|
|
my $status = `LC_ALL=C dpkg -s $pkg`;
|
|
next if $status =~ /Status: install ok installed/;
|
|
$k .= "$pkg ";
|
|
}
|
|
if (defined $k)
|
|
{
|
|
my $e=`LC_ALL=C printenv`;
|
|
my $str = ($e =~ /\nUSER=root\n/) ? "" : "sudo ";
|
|
$str = (-f "/usr/bin/sudo") ? "$str" : "";
|
|
print "I: Installing $k\n";
|
|
system ("$str apt-get install $k");
|
|
}
|
|
|
|
$config_str = '';
|
|
$config_str .= " -o Apt::Architecture=$arch";
|
|
$config_str .= " -o Apt::Get::AllowUnauthenticated=true"
|
|
if (defined $noauth);
|
|
$config_str .= " -o Apt::Get::Download-Only=true";
|
|
$config_str .= " -o Apt::Install-Recommends=false";
|
|
$config_str .= " -o Dir=$dir";
|
|
$config_str .= " -o Dir::Etc=${dir}${etcdir}";
|
|
$sourcesname = "sources.list.d/multistrap.sources.list";
|
|
$config_str .= " -o Dir::Etc::SourceList=${dir}${etcdir}$sourcesname";
|
|
$config_str .= " -o Dir::State=${dir}${libdir}";
|
|
$config_str .= " -o Dir::State::Status=${dir}${dpkgdir}status";
|
|
$config_str .= " -o Dir::Cache=${dir}${cachedir}";
|
|
printf ("Getting package lists: apt-get %s update\n", $config_str);
|
|
$retval = system ("apt-get $config_str update");
|
|
die (sprintf("apt update failed. Exit value: ").($retval/256)."\n")
|
|
if ($retval != 0);
|
|
$str = join (' ', values %packages) . " ";
|
|
chomp($str);
|
|
$str .= join (' ', values %keyrings) . " ";
|
|
chomp($str);
|
|
my $required = &get_required_debs;
|
|
$str .= join (' ', @$required);
|
|
chomp($str);
|
|
print "apt-get -y $config_str install $str\n";
|
|
$retval = system ("apt-get -y $config_str install $str");
|
|
die ("apt download failed. Exit value: ".($retval/256)."\n")
|
|
if ($retval != 0);
|
|
|
|
&force_unpack if ($unpack eq "true");
|
|
system ("touch ${dir}${libdir}lists/lock");
|
|
&native if (not defined ($foreign));
|
|
(not defined $tidy) ? system ("apt-get $config_str update") : &tidy_apt;
|
|
if (-l "${dir}lib64" ) {
|
|
my $r = readlink "${dir}lib64";
|
|
if ($r =~ m:^/:)
|
|
{
|
|
print "ERR: ./lib64 -> /lib symbolic link reset to ./lib after unpacking.\n";
|
|
printf ("ERR: Some files may have been unpacked outside %s!\n", $dir);
|
|
}
|
|
else
|
|
{
|
|
printf ("\nMultistrap system installed successfully in %s.\n\n", $dir);
|
|
}
|
|
}
|
|
exit 0;
|
|
|
|
sub force_unpack
|
|
{
|
|
my %unpack=();
|
|
opendir (DEBS, "${dir}${cachedir}archives/")
|
|
or die ("Cannot read apt archives directory.\n");
|
|
@archives=grep(/.*\.deb$/, readdir DEBS);
|
|
closedir (DEBS);
|
|
print "I: Calculating obsolete packages\n";
|
|
foreach $deb (sort @archives)
|
|
{
|
|
my $version = `LC_ALL=C dpkg -f ${dir}${cachedir}archives/$deb Version`;
|
|
my $package = `LC_ALL=C dpkg -f ${dir}${cachedir}archives/$deb Package`;
|
|
chomp ($version);
|
|
chomp ($package);
|
|
if (exists $unpack{$package})
|
|
{
|
|
my $test=system("dpkg --compare-versions $unpack{$package} '<<' $version");
|
|
$test /= 256;
|
|
# unlink version in $unpack if 0
|
|
# unlink $deb (current one) if 1
|
|
if ($test == 0)
|
|
{
|
|
my $old = $deb;
|
|
$old =~ s/$version/$unpack{$package}/;
|
|
printf ("I: Removing %s\n", $old);
|
|
unlink "${dir}${cachedir}archives/$old";
|
|
next;
|
|
}
|
|
else
|
|
{
|
|
printf ("I: Removing %s\n", $deb);
|
|
unlink "${dir}${cachedir}archives/$deb";
|
|
}
|
|
}
|
|
$unpack{$package}=$version;
|
|
}
|
|
open (LOCK, ">${dir}${libdir}lists/lock");
|
|
close (LOCK);
|
|
opendir (DEBS, "${dir}${cachedir}archives/")
|
|
or die ("Cannot read apt archives directory.\n");
|
|
@archives=grep(/.*\.deb$/, readdir DEBS);
|
|
closedir (DEBS);
|
|
my $old = `pwd`;
|
|
chomp ($old);
|
|
chdir ("${dir}");
|
|
foreach $deb (sort @archives)
|
|
{
|
|
printf ("I: Extracting %s...\n", $deb);
|
|
system ("ar -p \"./${cachedir}archives/$deb\" data.tar.gz | zcat | tar -xf -");
|
|
my $ver=`LC_ALL=C dpkg -f ./${cachedir}archives/$deb Version`;
|
|
my $pkg=`LC_ALL=C dpkg -f ./${cachedir}archives/$deb Package`;
|
|
chomp ($ver);
|
|
chomp ($pkg);
|
|
mkdir ("./tmp");
|
|
my $tmpdir = `mktemp -p ./tmp -d -t multistrap.XXXXXX`;
|
|
chomp ($tmpdir);
|
|
mkdir ("./${tmpdir}/listing");
|
|
system ("ar -p \"./${cachedir}archives/$deb\" data.tar.gz > ./${tmpdir}/listing/data.tar.gz");
|
|
my $datatar = `tar -tzf ./${tmpdir}/listing/data.tar.gz`;
|
|
my @lines = split("\n", $datatar);
|
|
open (LIST, ">>./${dpkgdir}info/${pkg}.list");
|
|
foreach my $l (@lines)
|
|
{
|
|
chomp ($l);
|
|
$l =~ s:^\.::;
|
|
$l =~ s:^/$:/\.:;
|
|
$l =~ s:/$::;
|
|
print LIST "$l\n";
|
|
}
|
|
close (LIST);
|
|
system ("rm -rf ./${tmpdir}/listing");
|
|
system ("dpkg -e ./${cachedir}archives/$deb ${tmpdir}/");
|
|
opendir (MAINT, "./${tmpdir}");
|
|
my @maint=grep(!m:\.\.?:, readdir (MAINT));
|
|
closedir (MAINT);
|
|
open (AVAIL, ">>./${dpkgdir}available");
|
|
open (STATUS, ">>./${dpkgdir}status");
|
|
foreach my $mscript (@maint)
|
|
{
|
|
rename "./${tmpdir}/$mscript", "./${dpkgdir}info/$pkg.$mscript";
|
|
if ( $mscript eq "control" )
|
|
{
|
|
open (MSCRIPT, "./${dpkgdir}info/$pkg.$mscript");
|
|
my @scr=<MSCRIPT>;
|
|
close (MSCRIPT);
|
|
my @avail = grep(!/^$/, @scr);
|
|
print AVAIL @avail;
|
|
print STATUS @avail;
|
|
print AVAIL "\n";
|
|
print STATUS "Status: install ok unpacked\n";
|
|
unlink ("./${dpkgdir}info/$mscript");
|
|
}
|
|
}
|
|
close (AVAIL);
|
|
if ( -f "./${dpkgdir}info/$pkg.conffiles")
|
|
{
|
|
print STATUS "Conffiles:\n";
|
|
printf (" -> Processing conffiles for %s\n", $pkg);
|
|
open (CONF, "./${dpkgdir}info/$pkg.conffiles");
|
|
my @lines=<CONF>;
|
|
close (CONF);
|
|
foreach my $line (@lines)
|
|
{
|
|
chomp ($line);
|
|
my $md5=`md5sum ./$line | cut -d" " -f1`;
|
|
chomp ($md5);
|
|
print STATUS " $line $md5\n";
|
|
}
|
|
}
|
|
print STATUS "\n";
|
|
close (STATUS);
|
|
system ("rm -rf ./${tmpdir}");
|
|
if (-l "${dir}lib64" ) {
|
|
my $r = readlink "${dir}lib64";
|
|
if ($r =~ m:^/:)
|
|
{
|
|
my $old = `pwd`;
|
|
chomp ($old);
|
|
printf ("ERR: lib64 -> ./lib symbolic link clobbered by %s\n", $pkg);
|
|
unlink "${dir}lib64";
|
|
chdir ("$dir");
|
|
print "INF: lib64 -> /lib symbolic link reset to ./lib.\n";
|
|
symlink "./lib", "lib64";
|
|
chdir ("${old}");
|
|
}
|
|
}
|
|
}
|
|
chdir ("$old");
|
|
print "I: Unpacking complete.\n";
|
|
}
|
|
|
|
sub tidy_apt
|
|
{
|
|
print "I: Tidying up apt cache and list data.\n";
|
|
unlink ("${dir}etc/apt/sources.list")
|
|
if (-f "${dir}etc/apt/sources.list");
|
|
opendir (DEBS, "${dir}${libdir}lists/")
|
|
or die ("Cannot read apt lists directory.\n");
|
|
my @lists=grep(!m:\.\.?$:, readdir DEBS);
|
|
closedir (DEBS);
|
|
foreach my $file (@lists)
|
|
{
|
|
next if (-d $file);
|
|
unlink ("${dir}${libdir}lists/$file");
|
|
}
|
|
opendir (DEBS, "${dir}${cachedir}/")
|
|
or die ("Cannot read apt cache directory/.\n");
|
|
my @files=grep(!m:\.\.?$:, readdir DEBS);
|
|
closedir (DEBS);
|
|
foreach my $file (@files)
|
|
{
|
|
next if (-d $file);
|
|
next unless ($file =~ /\.bin$/);
|
|
unlink ("${dir}${cachedir}$file");
|
|
}
|
|
if ($unpack eq "true")
|
|
{
|
|
opendir (DEBS, "${dir}${cachedir}/archives/")
|
|
or die ("Cannot read apt archives directory/.\n");
|
|
my @files=grep(!m:\.\.?$:, readdir DEBS);
|
|
closedir (DEBS);
|
|
foreach my $file (@files)
|
|
{
|
|
next if (-d $file);
|
|
next unless ($file =~ /\.deb$/);
|
|
(defined $sourcedir) ?
|
|
system ("mv ${dir}${cachedir}archives/$file $sourcedir/$file") :
|
|
unlink ("${dir}${cachedir}archives/$file");
|
|
;
|
|
}
|
|
}
|
|
}
|
|
|
|
# if native arch, do a few tasks just because we can and probably should.
|
|
sub native
|
|
{
|
|
print "I: Native mode - configuring unpacked packages . . .\n";
|
|
my $e=`LC_ALL=C printenv`;
|
|
my $str = ($e =~ /\nUSER=root\n/) ? "" : "sudo";
|
|
$str = (-f "/usr/bin/sudo") ? "$str" : "";
|
|
my $env = "DEBIAN_FRONTEND=noninteractive ".
|
|
"DEBCONF_NONINTERACTIVE_SEEN=true ".
|
|
"LC_ALL=C LANGUAGE=C LANG=C";
|
|
print "I: dpkg configuration settings:\n\t$env\n";
|
|
system ("$str $env chroot $dir dpkg --configure -a");
|
|
}
|
|
|
|
sub get_required_debs
|
|
{
|
|
# emulate required="$(get_debs Priority: required)"
|
|
# from debootstrap/functions
|
|
# needs to be run after the first apt-get install so that
|
|
# Packages files exist
|
|
my @required=();
|
|
my @debs=();
|
|
opendir (PKGS, "${dir}${libdir}lists/")
|
|
or die sprintf("Cannot open %s directory. %s\n","${dir}${libdir}lists/", $!);
|
|
my @lists=grep(/_Packages$/, readdir (PKGS));
|
|
closedir (PKGS);
|
|
foreach my $strap (@debootstrap)
|
|
{
|
|
my $s = lc($strap);
|
|
foreach my $l (@lists)
|
|
{
|
|
next unless ($l =~ /$s/);
|
|
push (@required, $l);
|
|
}
|
|
}
|
|
foreach my $file (@required)
|
|
{
|
|
my $fh = IO::File->new("${dir}${libdir}lists/$file");
|
|
my $parser = Parse::Debian::Packages->new( $fh );
|
|
while (my %package = $parser->next)
|
|
{
|
|
next unless $package{'Priority'} eq "required";
|
|
push @debs, $package{'Package'};
|
|
}
|
|
}
|
|
return \@debs;
|
|
}
|
|
|
|
# inherited from apt-cross
|
|
sub prepare_sources_list
|
|
{
|
|
my @source_list=();
|
|
# collate all available/configured sources into one list
|
|
if (-e "/etc/apt/sources.list") {
|
|
open (SOURCES, "/etc/apt/sources.list")
|
|
or die _g("cannot open apt sources list. %s",$!);
|
|
@source_list = <SOURCES>;
|
|
close (SOURCES);
|
|
}
|
|
if (-d "/etc/apt/sources.list.d/") {
|
|
opendir (FILES, "/etc/apt/sources.list.d/")
|
|
or die _g("cannot open apt sources.list directory %s\n",$!);
|
|
my @files = grep(!/^\.\.?$/, readdir FILES);
|
|
foreach my $f (@files) {
|
|
next if ($f =~ /\.ucf-old$/);
|
|
open (SOURCES, "/etc/apt/sources.list.d/$f") or
|
|
die _g("cannot open /etc/apt/sources.list.d/%s %s",$f, $!);
|
|
while(<SOURCES>) {
|
|
push @source_list, $_;
|
|
}
|
|
close (SOURCES);
|
|
}
|
|
closedir (FILES);
|
|
}
|
|
return \@source_list;
|
|
}
|
|
|
|
sub usageversion {
|
|
printf STDERR ("
|
|
%s version %s
|
|
|
|
Usage:
|
|
%s [-a ARCH] [-d DIR] -f CONFIG_FILE
|
|
%s -?|-h|--help|--version
|
|
|
|
Command:
|
|
-f|--file CONFIG_FILE: path the the multistrap configuration file.
|
|
|
|
Options:
|
|
-a|--arch ARCHITECTURE: override the configuration file architecture.
|
|
-d|--dir PATH: override the configuration file directory.
|
|
--no-auth: do not use Secure Apt for any repositories
|
|
--tidy-up: remove apt cache data and downloaded archives.
|
|
-?|-h|--help: print this usage message and exit
|
|
--version: print this usage message and exit
|
|
|
|
%s extends debootstrap to provide support for multiple
|
|
repositories, using a configuration file to specify the relevant suites,
|
|
architecture, extra packages and the mirror to use for each repository.
|
|
|
|
Example configuration:
|
|
[General]
|
|
arch=armel
|
|
directory=/opt/multistrap/
|
|
# same as --tidy-up option if set to true
|
|
cleanup=true
|
|
# same as --no-auth option if set to true
|
|
# keyring packages listed in each debootstrap will
|
|
# still be installed.
|
|
noauth=false
|
|
# extract all downloaded archives (default is true)
|
|
unpack=true
|
|
# the order of sections is not important.
|
|
# the debootstrap option determines which repository
|
|
# is used to calculate the list of Priority: required packages.
|
|
debootstrap=Debian
|
|
|
|
[Debian]
|
|
packages=
|
|
source=http://ftp.uk.debian.org/debian
|
|
keyring=debian-archive-keyring
|
|
suite=lenny
|
|
|
|
This will result in a completely normal debootstrap of Debian lenny from
|
|
the specified mirror, for armel in /opt/multistrap/.
|
|
|
|
'Architecture' and 'directory' can be overridden on the command line.
|
|
|
|
Specify a package to extend the debootstap to include that package and
|
|
all dependencies. Dependencies will be calculated by apt so as to use
|
|
only the most recent suitable version from all configured repositories.
|
|
|
|
General settings:
|
|
|
|
'directory' specifies the top level directory where the debootstrap
|
|
will be created - it is not packed into a .tgz once complete.
|
|
|
|
", $progname, $ourversion, $progname, $progname, $progname)
|
|
or die "$progname: "."failed to write usage:". "$!\n";
|
|
}
|
|
|
|
=pod
|
|
|
|
=head1 Name
|
|
|
|
em_multistrap - extends debootstrap for multiple repository support
|
|
|
|
=head1 Synopsis
|
|
|
|
em_multistrap [-a ARCH] [-d DIR] -f CONFIG_FILE
|
|
em_multistrap -?|-h|--help|--version
|
|
|
|
=head1 Options
|
|
|
|
(These options can also be set in the configuration file.)
|
|
|
|
--tidy-up - remove apt cache data, downloaded Packages files and
|
|
the apt package cache. Same as cleanup=true.
|
|
|
|
--no-auth - allow the use of unauthenticated repositories. Same
|
|
as noauth=true
|
|
|
|
=head1 Description
|
|
|
|
em_multistrap provides a debootstrap-like method based on apt and
|
|
extended to provide support for multiple repositories, using a
|
|
configuration file to specify the relevant suites, architecture,
|
|
extra packages and the mirror to use for each debootstrap.
|
|
|
|
The aim is to create a complete debootstrap with all packages
|
|
installed and configured, instead of just the base system.
|
|
|
|
Example configuration:
|
|
|
|
[General]
|
|
arch=armel
|
|
directory=/opt/multistrap/
|
|
# same as --tidy-up option if set to true
|
|
cleanup=true
|
|
# same as --no-auth option if set to true
|
|
# keyring packages listed in each debootstrap will
|
|
# still be installed.
|
|
noauth=false
|
|
# extract all downloaded archives (default is true)
|
|
unpack=true
|
|
# the order of sections is not important.
|
|
# the debootstrap option determines which repository
|
|
# is used to calculate the list of Priority: required packages.
|
|
debootstrap=Debian
|
|
|
|
[Debian]
|
|
packages=
|
|
source=http://ftp.uk.debian.org/debian
|
|
keyring=debian-archive-keyring
|
|
suite=lenny
|
|
|
|
This will result in a completely normal debootstrap of Debian lenny from
|
|
the specified mirror, for armel in '/opt/multistrap/'.
|
|
|
|
Specify a package to extend the multistrap to include that package and
|
|
all dependencies.
|
|
|
|
Specify more debootstraps by adding new sections. Section names are used
|
|
in the debootstrap general option.
|
|
|
|
Section names are case-insensitive.
|
|
|
|
e.g. change
|
|
|
|
debootstrap=Debian
|
|
|
|
to
|
|
|
|
debootstrap=Grip
|
|
|
|
then add the new section for Grip:
|
|
|
|
[Grip]
|
|
packages=locales
|
|
keyring=emdebian-archive-keyring
|
|
source=http://www.emdebian.org/grip
|
|
suite=lenny
|
|
|
|
Setting Grip instead of Debian in the debootstrap option, as above,
|
|
will provide a base system from Emdebian Grip 1.0 and locate any
|
|
missing dependencies in Debian 5.0 Lenny, allowing you to add any
|
|
package(s) you need from Debian that are not yet in Emdebian Grip.
|
|
|
|
All dependencies are resolved only by apt, using all configured
|
|
repositories, to use only the most recent and most suitable
|
|
dependencies. Note that multistrap turns off Install-Recommends
|
|
so if the multistrap needs a package that is only a Recommended
|
|
dependency, the recommended package needs to be specified in the
|
|
packages line explicitly.
|
|
|
|
'Architecture' and 'directory' can be overridden on the command line.
|
|
Other general options have command line options, except debootstrap
|
|
itself.
|
|
|
|
=head1 General settings:
|
|
|
|
'directory' specifies the top level directory where the debootstrap
|
|
will be created - it is not packed into a .tgz once complete.
|
|
|
|
As with debootstrap, em_multistrap will continue after errors.
|
|
|
|
em_multistrap does not currently implement the machine:variant support
|
|
used in Emdebian but the build directory is not packed up at the
|
|
end of the run so other scripts can be used to implement customisations.
|
|
|
|
=head1 Secure Apt
|
|
|
|
To use authenticated apt repositories, multistrap either needs to be
|
|
able to install an appropriate keyring package from the existing apt
|
|
sources *outside the multistrap environment* or have the relevant keys
|
|
already configured using apt-key *on the host system*.
|
|
|
|
If relevant packages exist, specify them in the 'keyring' option for
|
|
each repository. em_multistrap will then check that apt has already
|
|
installed this package so that the repository can be authenticated
|
|
before any packages are downloaded from it.
|
|
|
|
Note that *all* repositories to be used with multistrap must be
|
|
authenticated or apt will fail. Similarly, secure apt can only be
|
|
disabled for all repositories (by using the --no-auth command line
|
|
option or setting the general noauth option in the configuration
|
|
file), even if only one repository does not have a suitable keyring
|
|
available. Not all packages need keyring packages, if you configure
|
|
apt-key appropriately.
|
|
|
|
The keyring package(s) will also be installed inside the multistrap
|
|
environment to match the installed apt sources for the multistrap.
|
|
|
|
All configuration of apt-key needs to be done for the machine
|
|
running multistrap itself.
|
|
|
|
=head1 State
|
|
|
|
multistrap is stateless - if the directory exists, it will simply
|
|
proceed as normal and apt will try to pick up where it left off.
|
|
|
|
=head1 Native mode - multistrap
|
|
|
|
em_multistrap was not intended for native support, it was developed for
|
|
cross architecture support. In order for multiple repositories to be
|
|
used, em_multistrap only unpacks the packages selected by apt.
|
|
|
|
In native mode, various post-multistrap operations are likely to be
|
|
needed that debootstrap would do for you:
|
|
|
|
1. copy /etc/hosts into the chroot
|
|
2. clean the environment to unset LANGUAGE, LC_ALL and LANG
|
|
to silence nuisance perl warnings that obscure other errors
|
|
|
|
(An alternative to unset the localisation variables is to add
|
|
locales to your multistrap configuration file in the 'packages'
|
|
option.
|
|
|
|
Eventually, multistrap will either replace em_multistrap or call
|
|
em_multistrap with --arch and take over native duties.
|
|
|
|
=cut
|