split up setup() into multiple functions

This commit is contained in:
Johannes 'josch' Schauer 2020-04-09 23:07:02 +02:00
parent 895c388ede
commit 1076e9a78d
Signed by untrusted user: josch
GPG key ID: F2CBA5C78FBD83E1

View file

@ -1191,6 +1191,49 @@ sub setup {
warning "cannot read $options->{apttrustedparts}"; warning "cannot read $options->{apttrustedparts}";
} }
run_setup($options);
run_hooks('setup', $options);
run_update($options);
(my $pkgs_to_install, my $essential_pkgs) = run_download($options);
if ( $options->{mode} ne 'chrootless'
or $options->{variant} eq 'extract') {
# We have to extract the packages from @essential_pkgs either if we run
# in chrootless mode and extract variant or in any other mode. In
# other words, the only scenario in which the @essential_pkgs are not
# extracted are in chrootless mode in any other than the extract
# variant.
run_extract($options, $essential_pkgs);
}
run_hooks('extract', $options);
if ($options->{variant} ne 'extract') {
my $chrootcmd = [];
if ($options->{mode} ne 'chrootless') {
$chrootcmd = run_prepare($options);
}
run_essential($options, $essential_pkgs, $chrootcmd);
run_hooks('essential', $options);
run_install($options, $pkgs_to_install, $chrootcmd);
run_hooks('customize', $options);
}
run_cleanup($options);
return;
}
sub run_setup() {
my $options = shift;
{ {
my @directories = ( my @directories = (
'/etc/apt/apt.conf.d', '/etc/apt/sources.list.d', '/etc/apt/apt.conf.d', '/etc/apt/sources.list.d',
@ -1580,8 +1623,11 @@ sub setup {
$ENV{PATH} = "/usr/sbin:/usr/bin:/sbin:/bin"; $ENV{PATH} = "/usr/sbin:/usr/bin:/sbin:/bin";
} }
# run setup hooks return;
run_hooks('setup', $options); }
sub run_update() {
my $options = shift;
info "running apt-get update..."; info "running apt-get update...";
run_apt_progress({ run_apt_progress({
@ -1607,6 +1653,12 @@ sub setup {
} }
} }
return;
}
sub run_download() {
my $options = shift;
my @pkgs_to_install; my @pkgs_to_install;
for my $incl (@{ $options->{include} }) { for my $incl (@{ $options->{include} }) {
for my $pkg (split /[,\s]+/, $incl) { for my $pkg (split /[,\s]+/, $incl) {
@ -1827,21 +1879,23 @@ sub setup {
} }
} }
# We have to extract the packages from @essential_pkgs either if we run in return (\@pkgs_to_install, \@essential_pkgs);
# chrootless mode and extract variant or in any other mode. }
# In other words, the only scenario in which the @essential_pkgs are not
# extracted are in chrootless mode in any other than the extract variant. sub run_extract() {
if ( $options->{mode} eq 'chrootless' my $options = shift;
and $options->{variant} ne 'extract') { my $essential_pkgs = shift;
# nothing to do
} elsif ($options->{dryrun}) { if ($options->{dryrun}) {
info "skip extracting packages because of --dry-run"; info "skip extracting packages because of --dry-run";
} else { return;
}
info "extracting archives..."; info "extracting archives...";
print_progress 0.0; print_progress 0.0;
my $counter = 0; my $counter = 0;
my $total = scalar @essential_pkgs; my $total = scalar @{$essential_pkgs};
foreach my $deb (@essential_pkgs) { foreach my $deb (@{$essential_pkgs}) {
$counter += 1; $counter += 1;
# not using dpkg-deb --extract as that would replace the # not using dpkg-deb --extract as that would replace the
# merged-usr symlinks with plain directories # merged-usr symlinks with plain directories
@ -1869,81 +1923,13 @@ sub setup {
print_progress($counter / $total * 100); print_progress($counter / $total * 100);
} }
print_progress "done"; print_progress "done";
}
run_hooks('extract', $options); return;
}
if ($options->{mode} eq 'chrootless') { sub run_prepare {
if ($options->{dryrun}) { my $options = shift;
info "simulate installing packages...";
} else {
info "installing packages...";
}
# FIXME: the dpkg config from the host is parsed before the command
# line arguments are parsed and might break this mode
# Example: if the host has --path-exclude set, then this will also
# affect the chroot.
my @chrootless_opts = (
'-oDPkg::Options::=--force-not-root',
'-oDPkg::Options::=--force-script-chrootless',
'-oDPkg::Options::=--root=' . $options->{root},
'-oDPkg::Options::=--log=' . "$options->{root}/var/log/dpkg.log",
$options->{dryrun} ? '-oAPT::Get::Simulate=true' : (),
);
if (defined $options->{qemu}) {
# The binfmt support on the outside is used, so qemu needs to know
# where it has to look for shared libraries
if (defined $ENV{QEMU_LD_PREFIX}
&& $ENV{QEMU_LD_PREFIX} ne "") {
## no critic (Variables::RequireLocalizedPunctuationVars)
$ENV{QEMU_LD_PREFIX} = "$ENV{QEMU_LD_PREFIX}:$options->{root}";
} else {
## no critic (Variables::RequireLocalizedPunctuationVars)
$ENV{QEMU_LD_PREFIX} = $options->{root};
}
}
if ($options->{variant} eq 'extract') {
# nothing to do
} else {
run_apt_progress({
ARGV => ['apt-get', '--yes', @chrootless_opts, 'install'],
PKGS => [map { "$options->{root}/$_" } @essential_pkgs],
});
}
if (any { $_ eq $options->{variant} } ('extract', 'custom')) {
# nothing to do
} elsif (
any { $_ eq $options->{variant} } (
'essential', 'apt', 'standard', 'important',
'required', 'buildd', 'minbase'
)
) {
# run essential hooks
run_hooks('essential', $options);
if (scalar @pkgs_to_install > 0) {
run_apt_progress({
ARGV =>
['apt-get', '--yes', @chrootless_opts, 'install'],
PKGS => [@pkgs_to_install],
});
}
} else {
error "unknown variant: $options->{variant}";
}
} elsif (
any { $_ eq $options->{mode} }
('root', 'unshare', 'fakechroot', 'proot')
) {
if (any { $_ eq $options->{variant} } ('extract')) {
# nothing to do
} elsif (
any { $_ eq $options->{variant} } (
'custom', 'essential', 'apt', 'standard',
'important', 'required', 'buildd', 'minbase'
)
) {
if ($options->{mode} eq 'fakechroot') { if ($options->{mode} eq 'fakechroot') {
# this borrows from and extends # this borrows from and extends
# /etc/fakechroot/debootstrap.env and # /etc/fakechroot/debootstrap.env and
@ -1951,18 +1937,15 @@ sub setup {
{ {
my @fakechrootsubst = (); my @fakechrootsubst = ();
foreach my $d ('/usr/sbin', '/usr/bin', '/sbin', '/bin') { foreach my $d ('/usr/sbin', '/usr/bin', '/sbin', '/bin') {
push @fakechrootsubst, push @fakechrootsubst, "$d/chroot=/usr/sbin/chroot.fakechroot";
"$d/chroot=/usr/sbin/chroot.fakechroot";
push @fakechrootsubst, "$d/mkfifo=/bin/true"; push @fakechrootsubst, "$d/mkfifo=/bin/true";
push @fakechrootsubst, "$d/ldconfig=/bin/true"; push @fakechrootsubst, "$d/ldconfig=/bin/true";
push @fakechrootsubst, push @fakechrootsubst, "$d/ldd=/usr/bin/ldd.fakechroot";
"$d/ldd=/usr/bin/ldd.fakechroot";
push @fakechrootsubst, "$d/ischroot=/bin/true"; push @fakechrootsubst, "$d/ischroot=/bin/true";
} }
if (defined $ENV{FAKECHROOT_CMD_SUBST} if (defined $ENV{FAKECHROOT_CMD_SUBST}
&& $ENV{FAKECHROOT_CMD_SUBST} ne "") { && $ENV{FAKECHROOT_CMD_SUBST} ne "") {
push @fakechrootsubst, split /:/, push @fakechrootsubst, split /:/, $ENV{FAKECHROOT_CMD_SUBST};
$ENV{FAKECHROOT_CMD_SUBST};
} }
## no critic (Variables::RequireLocalizedPunctuationVars) ## no critic (Variables::RequireLocalizedPunctuationVars)
$ENV{FAKECHROOT_CMD_SUBST} = join ':', @fakechrootsubst; $ENV{FAKECHROOT_CMD_SUBST} = join ':', @fakechrootsubst;
@ -2054,15 +2037,13 @@ sub setup {
if (($? != 0) or (!$deb_host_multiarch)) { if (($? != 0) or (!$deb_host_multiarch)) {
error "dpkg-architecture failed: $?"; error "dpkg-architecture failed: $?";
} }
my $fakechrootdir my $fakechrootdir = "/usr/lib/$deb_host_multiarch/fakechroot";
= "/usr/lib/$deb_host_multiarch/fakechroot";
if (!-e "$fakechrootdir/libfakechroot.so") { if (!-e "$fakechrootdir/libfakechroot.so") {
error "$fakechrootdir/libfakechroot.so doesn't exist." error "$fakechrootdir/libfakechroot.so doesn't exist."
. " Install libfakechroot:$options->{nativearch}" . " Install libfakechroot:$options->{nativearch}"
. " outside the chroot"; . " outside the chroot";
} }
my $fakerootdir my $fakerootdir = "/usr/lib/$deb_host_multiarch/libfakeroot";
= "/usr/lib/$deb_host_multiarch/libfakeroot";
if (!-e "$fakerootdir/libfakeroot-sysv.so") { if (!-e "$fakerootdir/libfakeroot-sysv.so") {
error "$fakerootdir/libfakeroot-sysv.so doesn't exist." error "$fakerootdir/libfakeroot-sysv.so doesn't exist."
. " Install libfakeroot:$options->{nativearch}" . " Install libfakeroot:$options->{nativearch}"
@ -2081,8 +2062,7 @@ sub setup {
# to know where it has to look for shared libraries # to know where it has to look for shared libraries
if (defined $ENV{QEMU_LD_PREFIX} if (defined $ENV{QEMU_LD_PREFIX}
&& $ENV{QEMU_LD_PREFIX} ne "") { && $ENV{QEMU_LD_PREFIX} ne "") {
$ENV{QEMU_LD_PREFIX} $ENV{QEMU_LD_PREFIX} = "$ENV{QEMU_LD_PREFIX}:$options->{root}";
= "$ENV{QEMU_LD_PREFIX}:$options->{root}";
} else { } else {
$ENV{QEMU_LD_PREFIX} = $options->{root}; $ENV{QEMU_LD_PREFIX} = $options->{root};
} }
@ -2120,19 +2100,16 @@ sub setup {
. " work under proot"; . " work under proot";
info "this is likely due to missing support for" info "this is likely due to missing support for"
. " renameat2 in proot"; . " renameat2 in proot";
info info "see https://github.com/proot-me/PRoot/issues/147";
"see https://github.com/proot-me/PRoot/issues/147";
} else { } else {
info "the /bin/mv binary inside the chroot doesn't" info "the /bin/mv binary inside the chroot doesn't"
. " work under fakechroot"; . " work under fakechroot";
info "with certain versions of coreutils and glibc," info "with certain versions of coreutils and glibc,"
. " this is due to missing support for renameat2 in" . " this is due to missing support for renameat2 in"
. " fakechroot"; . " fakechroot";
info info "see https://github.com/dex4er/fakechroot/issues/60";
"see https://github.com/dex4er/fakechroot/issues/60";
} }
info info "expect package post installation scripts not to work";
"expect package post installation scripts not to work";
rmdir "$options->{root}/000-move-me" rmdir "$options->{root}/000-move-me"
or error "cannot rmdir: $!"; or error "cannot rmdir: $!";
} else { } else {
@ -2141,6 +2118,51 @@ sub setup {
} }
} }
return \@chrootcmd;
}
sub run_essential() {
my $options = shift;
my $essential_pkgs = shift;
my $chrootcmd = shift;
if ($options->{mode} eq 'chrootless') {
if ($options->{dryrun}) {
info "simulate installing packages...";
} else {
info "installing packages...";
}
# FIXME: the dpkg config from the host is parsed before the command
# line arguments are parsed and might break this mode
# Example: if the host has --path-exclude set, then this will also
# affect the chroot.
my @chrootless_opts = (
'-oDPkg::Options::=--force-not-root',
'-oDPkg::Options::=--force-script-chrootless',
'-oDPkg::Options::=--root=' . $options->{root},
'-oDPkg::Options::=--log=' . "$options->{root}/var/log/dpkg.log",
$options->{dryrun} ? '-oAPT::Get::Simulate=true' : (),
);
if (defined $options->{qemu}) {
# The binfmt support on the outside is used, so qemu needs to know
# where it has to look for shared libraries
if (defined $ENV{QEMU_LD_PREFIX}
&& $ENV{QEMU_LD_PREFIX} ne "") {
## no critic (Variables::RequireLocalizedPunctuationVars)
$ENV{QEMU_LD_PREFIX} = "$ENV{QEMU_LD_PREFIX}:$options->{root}";
} else {
## no critic (Variables::RequireLocalizedPunctuationVars)
$ENV{QEMU_LD_PREFIX} = $options->{root};
}
}
run_apt_progress({
ARGV => ['apt-get', '--yes', @chrootless_opts, 'install'],
PKGS => [map { "$options->{root}/$_" } @{$essential_pkgs}],
});
} elsif (
any { $_ eq $options->{mode} }
('root', 'unshare', 'fakechroot', 'proot')
) {
# install the extracted packages properly # install the extracted packages properly
# we need --force-depends because dpkg does not take Pre-Depends # we need --force-depends because dpkg does not take Pre-Depends
# into account and thus doesn't install them in the right order # into account and thus doesn't install them in the right order
@ -2153,11 +2175,11 @@ sub setup {
sub { sub {
run_dpkg_progress({ run_dpkg_progress({
ARGV => [ ARGV => [
@chrootcmd, 'env', @{$chrootcmd}, 'env',
'--unset=TMPDIR', 'dpkg', '--unset=TMPDIR', 'dpkg',
'--install', '--force-depends' '--install', '--force-depends'
], ],
PKGS => \@essential_pkgs, PKGS => $essential_pkgs,
}); });
}, },
$options $options
@ -2181,11 +2203,11 @@ sub setup {
sub { sub {
run_dpkg_progress({ run_dpkg_progress({
ARGV => [ ARGV => [
@chrootcmd, 'env', @{$chrootcmd}, 'env',
'--unset=TMPDIR', 'dpkg', '--unset=TMPDIR', 'dpkg',
'--install', '--force-depends' '--install', '--force-depends'
], ],
PKGS => \@essential_pkgs, PKGS => $essential_pkgs,
}); });
}, },
$options $options
@ -2193,18 +2215,48 @@ sub setup {
} }
} }
foreach my $deb (@essential_pkgs) { } else {
error "unknown mode: $options->{mode}";
}
foreach my $deb (@{$essential_pkgs}) {
unlink "$options->{root}/$deb" unlink "$options->{root}/$deb"
or error "cannot unlink $deb: $!"; or error "cannot unlink $deb: $!";
} }
return;
}
sub run_install() {
my $options = shift;
my $pkgs_to_install = shift;
my $chrootcmd = shift;
if ($options->{mode} eq 'chrootless') {
if (scalar @{$pkgs_to_install} > 0) {
my @chrootless_opts = (
'-oDPkg::Options::=--force-not-root',
'-oDPkg::Options::=--force-script-chrootless',
'-oDPkg::Options::=--root=' . $options->{root},
'-oDPkg::Options::=--log='
. "$options->{root}/var/log/dpkg.log",
$options->{dryrun} ? '-oAPT::Get::Simulate=true' : (),
);
run_apt_progress({
ARGV => ['apt-get', '--yes', @chrootless_opts, 'install'],
PKGS => $pkgs_to_install,
});
}
} elsif (
any { $_ eq $options->{mode} }
('root', 'unshare', 'fakechroot', 'proot')
) {
# run essential hooks # run essential hooks
if ($options->{variant} ne 'custom') { if ($options->{variant} ne 'custom') {
run_hooks('essential', $options);
} }
if ($options->{variant} ne 'custom' if ($options->{variant} ne 'custom'
and scalar @pkgs_to_install > 0) { and scalar @{$pkgs_to_install} > 0) {
# some packages have to be installed from the outside before # some packages have to be installed from the outside before
# anything can be installed from the inside. # anything can be installed from the inside.
# #
@ -2233,15 +2285,13 @@ sub setup {
while (my $uri = <$pipe_apt>) { while (my $uri = <$pipe_apt>) {
if ($uri =~ /^https:\/\//) { if ($uri =~ /^https:\/\//) {
# FIXME: support for https is part of apt >= 1.5 # FIXME: support for https is part of apt >= 1.5
push @pkgs_to_install_from_outside, push @pkgs_to_install_from_outside, 'apt-transport-https';
'apt-transport-https';
push @pkgs_to_install_from_outside, 'ca-certificates'; push @pkgs_to_install_from_outside, 'ca-certificates';
last; last;
} elsif ($uri =~ /^tor(\+[a-z]+)*:\/\//) { } elsif ($uri =~ /^tor(\+[a-z]+)*:\/\//) {
# tor URIs can be tor+http://, tor+https:// or even # tor URIs can be tor+http://, tor+https:// or even
# tor+mirror+file:// # tor+mirror+file://
push @pkgs_to_install_from_outside, push @pkgs_to_install_from_outside, 'apt-transport-tor';
'apt-transport-tor';
last; last;
} }
} }
@ -2288,19 +2338,17 @@ sub setup {
} }
close $dh; close $dh;
if (scalar @debs_to_install == 0) { if (scalar @debs_to_install == 0) {
warning warning "nothing got downloaded -- maybe the packages"
"nothing got downloaded -- maybe the packages"
. " were already installed?"; . " were already installed?";
} else { } else {
# we need --force-depends because dpkg does not take # we need --force-depends because dpkg does not take
# Pre-Depends into account and thus doesn't install # Pre-Depends into account and thus doesn't install
# them in the right order # them in the right order
info 'installing ' info 'installing '
. (join ', ', @pkgs_to_install_from_outside) . (join ', ', @pkgs_to_install_from_outside) . "...";
. "...";
run_dpkg_progress({ run_dpkg_progress({
ARGV => [ ARGV => [
@chrootcmd, 'env', @{$chrootcmd}, 'env',
'--unset=TMPDIR', 'dpkg', '--unset=TMPDIR', 'dpkg',
'--install', '--force-depends' '--install', '--force-depends'
], ],
@ -2321,15 +2369,12 @@ sub setup {
. " chroot..."; . " chroot...";
run_apt_progress({ run_apt_progress({
ARGV => [ ARGV => [
@chrootcmd, @{$chrootcmd}, 'env',
'env', '--unset=APT_CONFIG', '--unset=TMPDIR',
'--unset=APT_CONFIG', 'apt-get', '--yes',
'--unset=TMPDIR',
'apt-get',
'--yes',
'install' 'install'
], ],
PKGS => [@pkgs_to_install], PKGS => $pkgs_to_install,
}); });
}, },
$options $options
@ -2342,18 +2387,19 @@ sub setup {
'apt-get', '--yes', 'apt-get', '--yes',
'-oAPT::Get::Simulate=true', 'install' '-oAPT::Get::Simulate=true', 'install'
], ],
PKGS => [@pkgs_to_install], PKGS => $pkgs_to_install,
}); });
} }
} }
} else {
error "unknown variant: $options->{variant}";
}
} else { } else {
error "unknown mode: $options->{mode}"; error "unknown mode: $options->{mode}";
} }
run_hooks('customize', $options); return;
}
sub run_cleanup() {
my $options = shift;
# clean up temporary configuration file # clean up temporary configuration file
unlink "$options->{root}/etc/apt/apt.conf.d/00mmdebstrap" unlink "$options->{root}/etc/apt/apt.conf.d/00mmdebstrap"
@ -2369,7 +2415,10 @@ sub setup {
}); });
run_apt_progress( run_apt_progress(
{ ARGV => ['apt-get', 'clean'], CHDIR => $options->{root} }); { ARGV => ['apt-get', 'clean'], CHDIR => $options->{root} });
unlink $tmpfile or error "failed to unlink $tmpfile: $!"; if (defined $ENV{APT_CONFIG} && -e $ENV{APT_CONFIG}) {
unlink $ENV{APT_CONFIG}
or error "failed to unlink $ENV{APT_CONFIG}: $!";
}
# apt since 1.6 creates the auxfiles directory. If apt inside the chroot # apt since 1.6 creates the auxfiles directory. If apt inside the chroot
# is older than that, then it will not know how to clean it. # is older than that, then it will not know how to clean it.