Compare commits

..

1 commit

Author SHA1 Message Date
444b3b6fec
allow custom daemon startup prevention
don't bother with /sbin/start-stop-daemon and /usr/sbin/policy-rc.d
if they're not a regular files (e.g. symlinks)

Signed-off-by: Konstantin Demin <rockdrilla@gmail.com>
2021-11-03 10:22:21 +03:00
3 changed files with 366 additions and 149 deletions

View file

@ -127,7 +127,7 @@ if [ ! -e shared/hooks/eatmydata/customize.sh ] || [ hooks/eatmydata/customize.s
fi fi
fi fi
starttime= starttime=
total=177 total=213
skipped=0 skipped=0
runtests=0 runtests=0
i=1 i=1
@ -2141,10 +2141,6 @@ tar -C /tmp/debian-chroot --one-file-system -c . | tar -t | sort > tar2.txt
ppc64el) ppc64el)
echo ./lib64; echo ./lib64;
;; ;;
s390x)
echo ./lib32;
echo ./usr/lib32/;
;;
esac esac
} | sort -u | diff -u - tar2.txt } | sort -u | diff -u - tar2.txt
rm -r /tmp/debian-chroot rm -r /tmp/debian-chroot
@ -3080,6 +3076,123 @@ END
done done
done done
# test all variants
for variant in essential apt required minbase buildd important debootstrap - standard; do
print_header "mode=root,variant=$variant: create tarball"
cat << END > shared/test.sh
#!/bin/sh
set -eu
export LC_ALL=C.UTF-8
$CMD --mode=root --variant=$variant $DEFAULT_DIST /tmp/debian-chroot.tar $mirror
tar -tf /tmp/debian-chroot.tar | sort > "$variant.txt"
rm /tmp/debian-chroot.tar
END
if [ "$HAVE_QEMU" = "yes" ]; then
./run_qemu.sh
runtests=$((runtests+1))
else
./run_null.sh SUDO
runtests=$((runtests+1))
fi
# check if the other modes produce the same result in each variant
for mode in unshare fakechroot proot; do
print_header "mode=$mode,variant=$variant: create tarball"
# fontconfig doesn't install reproducibly because differences
# in /var/cache/fontconfig/. See
# https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=864082
if [ "$variant" = standard ]; then
echo "skipping test because of #864082" >&2
skipped=$((skipped+1))
continue
fi
if [ "$mode" = "unshare" ] && [ "$HAVE_UNSHARE" != "yes" ]; then
echo "HAVE_UNSHARE != yes -- Skipping test..." >&2
skipped=$((skipped+1))
continue
fi
if [ "$mode" = "proot" ] && [ "$HAVE_PROOT" != "yes" ]; then
echo "HAVE_PROOT != yes -- Skipping test..." >&2
skipped=$((skipped+1))
continue
fi
cat << END > shared/test.sh
#!/bin/sh
set -eu
export LC_ALL=C.UTF-8
if [ "\$(id -u)" -eq 0 ] && ! id -u user > /dev/null 2>&1; then
if [ ! -e /mmdebstrap-testenv ]; then
echo "this test modifies the system and should only be run inside a container" >&2
exit 1
fi
adduser --gecos user --disabled-password user
fi
if [ "$mode" = unshare ]; then
if [ ! -e /mmdebstrap-testenv ]; then
echo "this test modifies the system and should only be run inside a container" >&2
exit 1
fi
sysctl -w kernel.unprivileged_userns_clone=1
fi
prefix=
[ "\$(id -u)" -eq 0 ] && prefix="runuser -u user --"
\$prefix $CMD --mode=$mode --variant=$variant $DEFAULT_DIST /tmp/debian-chroot.tar $mirror
tar -tf /tmp/debian-chroot.tar | sort | diff -u "./$variant.txt" -
rm /tmp/debian-chroot.tar
END
if [ "$HAVE_QEMU" = "yes" ]; then
./run_qemu.sh
runtests=$((runtests+1))
else
./run_null.sh
runtests=$((runtests+1))
fi
done
# some variants are equal and some are strict superset of the last
# special case of the buildd variant: nothing is a superset of it
case "$variant" in
essential) ;; # nothing to compare it to
apt)
[ $(comm -23 shared/essential.txt shared/apt.txt | wc -l) -eq 0 ]
[ $(comm -13 shared/essential.txt shared/apt.txt | wc -l) -gt 0 ]
rm shared/essential.txt
;;
required)
[ $(comm -23 shared/apt.txt shared/required.txt | wc -l) -eq 0 ]
[ $(comm -13 shared/apt.txt shared/required.txt | wc -l) -gt 0 ]
rm shared/apt.txt
;;
minbase) # equal to required
cmp shared/required.txt shared/minbase.txt
rm shared/required.txt
;;
buildd)
[ $(comm -23 shared/minbase.txt shared/buildd.txt | wc -l) -eq 0 ]
[ $(comm -13 shared/minbase.txt shared/buildd.txt | wc -l) -gt 0 ]
rm shared/buildd.txt # we need minbase.txt but not buildd.txt
;;
important)
[ $(comm -23 shared/minbase.txt shared/important.txt | wc -l) -eq 0 ]
[ $(comm -13 shared/minbase.txt shared/important.txt | wc -l) -gt 0 ]
rm shared/minbase.txt
;;
debootstrap) # equal to important
cmp shared/important.txt shared/debootstrap.txt
rm shared/important.txt
;;
-) # equal to debootstrap
cmp shared/debootstrap.txt shared/-.txt
rm shared/debootstrap.txt
;;
standard)
[ $(comm -23 shared/-.txt shared/standard.txt | wc -l) -eq 0 ]
[ $(comm -13 shared/-.txt shared/standard.txt | wc -l) -gt 0 ]
rm shared/-.txt shared/standard.txt
;;
*) exit 1;;
esac
done
# test extract variant also with chrootless mode # test extract variant also with chrootless mode
for mode in root unshare fakechroot proot chrootless; do for mode in root unshare fakechroot proot chrootless; do
print_header "mode=$mode,variant=extract: unpack doc-debian" print_header "mode=$mode,variant=extract: unpack doc-debian"

View file

@ -224,9 +224,6 @@ Apt::Get::Download-Only true;
Acquire::Languages "none"; Acquire::Languages "none";
Dir::Etc::Trusted "/etc/apt/trusted.gpg"; Dir::Etc::Trusted "/etc/apt/trusted.gpg";
Dir::Etc::TrustedParts "/etc/apt/trusted.gpg.d"; Dir::Etc::TrustedParts "/etc/apt/trusted.gpg.d";
Acquire::http::Dl-Limit "1000";
Acquire::https::Dl-Limit "1000";
Acquire::Retries "5";
END END
> "$rootdir/var/lib/dpkg/status" > "$rootdir/var/lib/dpkg/status"
@ -469,17 +466,6 @@ cleanuptmpdir() {
export SOURCE_DATE_EPOCH=$(date --date="$(grep-dctrl -s Date -n '' "$newmirrordir/dists/$DEFAULT_DIST/Release")" +%s) export SOURCE_DATE_EPOCH=$(date --date="$(grep-dctrl -s Date -n '' "$newmirrordir/dists/$DEFAULT_DIST/Release")" +%s)
if [ "$HAVE_QEMU" = "yes" ]; then if [ "$HAVE_QEMU" = "yes" ]; then
case "$HOSTARCH" in
amd64|i386)
# okay
;;
*)
echo "qemu support is only available on amd64 and i386" >&2
echo "because syslinux is only available on those arches" >&2
exit 1
;;
esac
# We must not use any --dpkgopt here because any dpkg options still # We must not use any --dpkgopt here because any dpkg options still
# leak into the chroot with chrootless mode. # leak into the chroot with chrootless mode.
# We do not use our own package cache here because # We do not use our own package cache here because
@ -521,9 +507,6 @@ if [ "$HAVE_QEMU" = "yes" ]; then
arches=$HOSTARCH arches=$HOSTARCH
fi fi
$CMD --variant=apt --architectures=$arches --include="$pkgs" \ $CMD --variant=apt --architectures=$arches --include="$pkgs" \
--aptopt='Acquire::http::Dl-Limit "1000"' \
--aptopt='Acquire::https::Dl-Limit "1000"' \
--aptopt='Acquire::Retries "5"' \
$DEFAULT_DIST - "$mirror" > "$tmpdir/debian-chroot.tar" $DEFAULT_DIST - "$mirror" > "$tmpdir/debian-chroot.tar"
cat << END > "$tmpdir/extlinux.conf" cat << END > "$tmpdir/extlinux.conf"
@ -557,7 +540,7 @@ END
cat << 'END' > "$tmpdir/worker.sh" cat << 'END' > "$tmpdir/worker.sh"
#!/bin/sh #!/bin/sh
echo 'root:root' | chpasswd echo 'root:root' | chpasswd
mount -t 9p -o trans=virtio,access=any,msize=128k mmdebstrap /mnt mount -t 9p -o trans=virtio,access=any mmdebstrap /mnt
# need to restart mini-httpd because we mounted different content into www-root # need to restart mini-httpd because we mounted different content into www-root
systemctl restart mini-httpd systemctl restart mini-httpd

View file

@ -1510,7 +1510,8 @@ sub setup {
run_update($options); run_update($options);
} }
(my $essential_pkgs, my $cached_debs) = run_download($options); (my $pkgs_to_install, my $essential_pkgs, my $cached_debs)
= run_download($options);
# in theory, we don't have to extract the packages in chrootless mode # in theory, we don't have to extract the packages in chrootless mode
# but we do it anyways because otherwise directory creation timestamps # but we do it anyways because otherwise directory creation timestamps
@ -1532,7 +1533,7 @@ sub setup {
run_hooks('essential', $options); run_hooks('essential', $options);
run_install($options, $chrootcmd); run_install($options, $pkgs_to_install, $chrootcmd);
run_hooks('customize', $options); run_hooks('customize', $options);
} }
@ -1991,6 +1992,22 @@ sub run_update() {
sub run_download() { sub run_download() {
my $options = shift; my $options = shift;
my %pkgs_to_install;
for my $incl (@{ $options->{include} }) {
for my $pkg (split /[,\s]+/, $incl) {
# strip leading and trailing whitespace
$pkg =~ s/^\s+|\s+$//g;
# skip if the remainder is an empty string
if ($pkg eq '') {
next;
}
$pkgs_to_install{$pkg} = ();
}
}
if ($options->{variant} eq 'buildd') {
$pkgs_to_install{'build-essential'} = ();
}
# We use /var/cache/apt/archives/ to figure out which packages apt chooses # We use /var/cache/apt/archives/ to figure out which packages apt chooses
# to install. That's why the directory must be empty if: # to install. That's why the directory must be empty if:
# - /var/cache/apt/archives exists, and # - /var/cache/apt/archives exists, and
@ -2014,7 +2031,7 @@ sub run_download() {
if ( if (
!$options->{dryrun} !$options->{dryrun}
&& ((none { $_ eq $options->{variant} } ('extract', 'custom')) && ((none { $_ eq $options->{variant} } ('extract', 'custom'))
|| scalar @{ $options->{include} } != 0) || scalar keys %pkgs_to_install != 0)
&& -d "$options->{root}/var/cache/apt/archives/" && -d "$options->{root}/var/cache/apt/archives/"
) { ) {
my $apt_archives = "/var/cache/apt/archives/"; my $apt_archives = "/var/cache/apt/archives/";
@ -2047,22 +2064,10 @@ sub run_download() {
# (essential variant) then we have to compute the package set ourselves. # (essential variant) then we have to compute the package set ourselves.
# Same if we want to install priority based variants. # Same if we want to install priority based variants.
if (any { $_ eq $options->{variant} } ('extract', 'custom')) { if (any { $_ eq $options->{variant} } ('extract', 'custom')) {
if (scalar @{ $options->{include} } == 0) { if (scalar keys %pkgs_to_install == 0) {
info "nothing to download -- skipping..."; info "nothing to download -- skipping...";
return ([], []); return ([], []);
} }
my %pkgs_to_install;
for my $incl (@{ $options->{include} }) {
for my $pkg (split /[,\s]+/, $incl) {
# strip leading and trailing whitespace
$pkg =~ s/^\s+|\s+$//g;
# skip if the remainder is an empty string
if ($pkg eq '') {
next;
}
$pkgs_to_install{$pkg} = ();
}
}
my %result = (); my %result = ();
if ($options->{dryrun}) { if ($options->{dryrun}) {
@ -2121,10 +2126,7 @@ sub run_download() {
], ],
%result %result
}); });
} elsif ( } elsif ($options->{variant} eq 'essential') {
any { $_ eq $options->{variant} }
('essential', 'standard', 'important', 'required', 'buildd')
) {
# 2021-06-07, #debian-apt on OFTC, times in UTC+2 # 2021-06-07, #debian-apt on OFTC, times in UTC+2
# 17:27 < DonKult> (?essential includes 'apt' through) # 17:27 < DonKult> (?essential includes 'apt' through)
# 17:30 < josch> DonKult: no, because pkgCacheGen::ForceEssential ","; # 17:30 < josch> DonKult: no, because pkgCacheGen::ForceEssential ",";
@ -2149,7 +2151,7 @@ sub run_download() {
'install', 'install',
'?narrow(' '?narrow('
. ( . (
length($options->{suite}) defined($options->{suite})
? '?archive(' . $options->{suite} . '),' ? '?archive(' . $options->{suite} . '),'
: '' : ''
) )
@ -2159,6 +2161,209 @@ sub run_download() {
], ],
%result %result
}); });
} elsif (
any { $_ eq $options->{variant} }
('standard', 'important', 'required', 'minbase', 'buildd')
) {
# In the future, after bug https://bugs.debian.org/989558 is fixed, we
# want to use apt patterns to select the packages to install:
#
# ?narrow(?archive(unstable),?architecture(amd64),?priority(important))
#
# Once this is possible, we can append above statement to the apt-get
# install call in run_download() instead of assembling the package list
# here and passing it through all the way. Then this function only
# takes care of retrieving the essential packages.
#
# https://salsa.debian.org/apt-team/apt/-/merge_requests/185
my %ess_pkgs;
my %ess_pkgs_target;
my %pkgs_to_install_target = %pkgs_to_install;
my $num_suite_matches = 0;
my $num_suite_mismatch = 0;
open(
my $pipe_apt,
'-|',
'apt-get',
'indextargets',
'--format',
('$(CODENAME)' . "\t" . '$(SUITE)' . "\t" . '$(FILENAME)'),
'Created-By: Packages'
) or error "cannot start apt-get indextargets: $!";
while (my $line = <$pipe_apt>) {
chomp $line;
my ($codename, $suite, $fname) = split /\t/, $line, 3;
debug "processing indextarget output for $codename $suite $fname";
my $suite_matches = 0;
if (
defined $options->{suite}
and
($options->{suite} eq $codename or $options->{suite} eq $suite)
) {
$suite_matches = 1;
$num_suite_matches++;
} else {
$num_suite_mismatch++;
}
open(my $pipe_cat, '-|', '/usr/lib/apt/apt-helper', 'cat-file',
$fname)
or error "cannot start apt-helper cat-file: $!";
my $pkgname;
my $ess = '';
my $prio = 'optional';
my $arch = '';
while (my $line = <$pipe_cat>) {
chomp $line;
# Dpkg::Index takes 10 seconds to parse a typical Packages
# file. Thus we instead use a simple parser that just retrieve
# the information we need.
if ($line ne "") {
if ($line =~ /^Package: (.*)/) {
$pkgname = $1;
} elsif ($line =~ /^Essential: yes$/) {
$ess = 'yes';
} elsif ($line =~ /^Priority: (.*)/) {
$prio = $1;
} elsif ($line =~ /^Architecture: (.*)/) {
$arch = $1;
}
next;
}
# we are only interested of packages of native architecture or
# Architecture:all
if ($arch eq $options->{nativearch} or $arch eq 'all') {
# the line is empty, thus a package stanza just finished
# processing and we can handle it now
if ($ess eq 'yes') {
$ess_pkgs{$pkgname} = ();
if ($suite_matches) {
$ess_pkgs_target{$pkgname} = ();
}
} elsif ($options->{variant} eq 'essential') {
# for this variant we are only interested in the
# essential packages
} elsif (
any { $_ eq $options->{variant} } (
'standard', 'important', 'required', 'buildd',
'minbase'
)
) {
if ($prio eq 'optional' or $prio eq 'extra') {
# always ignore packages of priority optional and
# extra
} elsif ($prio eq 'standard') {
if (
none { $_ eq $options->{variant} }
('important', 'required', 'buildd', 'minbase')
) {
$pkgs_to_install{$pkgname} = ();
if ($suite_matches) {
$pkgs_to_install_target{$pkgname} = ();
}
}
} elsif ($prio eq 'important') {
if (
none { $_ eq $options->{variant} }
('required', 'buildd', 'minbase')
) {
$pkgs_to_install{$pkgname} = ();
if ($suite_matches) {
$pkgs_to_install_target{$pkgname} = ();
}
}
} elsif ($prio eq 'required') {
# required packages are part of all sets except
# essential and apt
$pkgs_to_install{$pkgname} = ();
if ($suite_matches) {
$pkgs_to_install_target{$pkgname} = ();
}
} else {
error "unknown priority: $prio";
}
} else {
error "unknown variant: $options->{variant}";
}
}
# reset values
undef $pkgname;
$ess = '';
$prio = 'optional';
$arch = '';
}
close $pipe_cat;
$? == 0 or error "apt-helper cat-file failed: $?";
}
close $pipe_apt;
$? == 0 or error "apt-get indextargets failed: $?";
# We now have two package sets, %pkgs_to_install and
# %pkgs_to_install_target, where the latter was only filled if the
# suite matched the codename or the suite name of one of the given
# apt indices.
# We only need to bother with this distinction if one or more of the
# indices matched and one or more of the indices mismatched. If either
# nothing matched or all matched, then we can just use %pkgs_to_install
if ( defined $options->{suite}
and $num_suite_matches > 0
and $num_suite_mismatch > 0) {
# Now we know that some matched and some didn't. But we only
# replace the results from all indices with the results from those
# indices that matched if the results are actually different and
# if there is more than zero packages from the indices that matched
if (scalar keys %ess_pkgs_target > 0
and keys %ess_pkgs != %ess_pkgs_target) {
info( "multiple sources defined, using those matching "
. "'$options->{suite}' to find essential packages");
%ess_pkgs = %ess_pkgs_target;
}
if (scalar keys %pkgs_to_install_target > 0
and keys %pkgs_to_install != keys %pkgs_to_install_target) {
if ($options->{variant} eq 'essential') {
error "logic error";
} elsif (
any { $_ eq $options->{variant} }
('standard', 'important', 'required', 'buildd', 'minbase')
) {
info( "multiple sources defined -- using those matching "
. "'$options->{suite}' to find packages for variant "
. "'$options->{variant}'");
%pkgs_to_install = %pkgs_to_install_target;
} else {
error "unknown variant: $options->{variant}";
}
}
}
debug "Identified the following Essential:yes packages:";
foreach my $pkg (sort keys %ess_pkgs) {
debug " $pkg";
}
my %result = ();
if ($options->{dryrun}) {
info "simulate downloading packages with apt...";
} else {
# if there are already packages in /var/cache/apt/archives/, we
# need to use our proxysolver to obtain the solution chosen by apt
if (scalar @cached_debs > 0) {
$result{EDSP_RES} = \@dl_debs;
}
info "downloading packages with apt...";
}
run_apt_progress({
ARGV => [
'apt-get',
'--yes',
'-oApt::Get::Download-Only=true',
$options->{dryrun} ? '-oAPT::Get::Simulate=true' : (),
'install'
],
PKGS => [keys %ess_pkgs],
%result
});
} else { } else {
error "unknown variant: $options->{variant}"; error "unknown variant: $options->{variant}";
} }
@ -2228,7 +2433,7 @@ sub run_download() {
# list before returning it. # list before returning it.
@essential_pkgs = sort @essential_pkgs; @essential_pkgs = sort @essential_pkgs;
return (\@essential_pkgs, \@cached_debs); return ([keys %pkgs_to_install], \@essential_pkgs, \@cached_debs);
} }
sub run_extract() { sub run_extract() {
@ -2670,51 +2875,12 @@ sub run_essential() {
} }
sub run_install() { sub run_install() {
my $options = shift; my $options = shift;
my $chrootcmd = shift; my $pkgs_to_install = shift;
my $chrootcmd = shift;
my %pkgs_to_install;
for my $incl (@{ $options->{include} }) {
for my $pkg (split /[,\s]+/, $incl) {
# strip leading and trailing whitespace
$pkg =~ s/^\s+|\s+$//g;
# skip if the remainder is an empty string
if ($pkg eq '') {
next;
}
$pkgs_to_install{$pkg} = ();
}
}
if ($options->{variant} eq 'buildd') {
$pkgs_to_install{'build-essential'} = ();
}
if (
any { $_ eq $options->{variant} }
('required', 'important', 'standard', 'buildd')
) {
my $priority;
if (any { $_ eq $options->{variant} } ('required', 'buildd')) {
$priority = '?priority(required)';
} elsif ($options->{variant} eq 'important') {
$priority = '?or(?priority(required),?priority(important))';
} elsif ($options->{variant} eq 'standard') {
$priority = '?or(~prequired,~pimportant,~pstandard)';
}
$pkgs_to_install{
"?narrow("
. (
length($options->{suite})
? '?archive(' . $options->{suite} . '),'
: ''
)
. "?architecture($options->{nativearch}),"
. "$priority)"
} = ();
}
my @pkgs_to_install = keys %pkgs_to_install;
if ($options->{mode} eq 'chrootless') { if ($options->{mode} eq 'chrootless') {
if (scalar @pkgs_to_install > 0) { if (scalar @{$pkgs_to_install} > 0) {
my @chrootless_opts = ( my @chrootless_opts = (
'-oDPkg::Options::=--force-not-root', '-oDPkg::Options::=--force-not-root',
'-oDPkg::Options::=--force-script-chrootless', '-oDPkg::Options::=--force-script-chrootless',
@ -2725,7 +2891,7 @@ sub run_install() {
); );
run_apt_progress({ run_apt_progress({
ARGV => ['apt-get', '--yes', @chrootless_opts, 'install'], ARGV => ['apt-get', '--yes', @chrootless_opts, 'install'],
PKGS => [@pkgs_to_install], PKGS => $pkgs_to_install,
}); });
} }
} elsif ( } elsif (
@ -2733,7 +2899,7 @@ sub run_install() {
('root', 'unshare', 'fakechroot', 'proot') ('root', 'unshare', 'fakechroot', 'proot')
) { ) {
if ($options->{variant} ne 'custom' if ($options->{variant} ne 'custom'
and scalar @pkgs_to_install > 0) { and scalar @{$pkgs_to_install} > 0) {
# Advantage of running apt on the outside instead of inside the # Advantage of running apt on the outside instead of inside the
# chroot: # chroot:
# #
@ -2779,7 +2945,7 @@ sub run_install() {
'--yes', '--yes',
'install' 'install'
], ],
PKGS => [@pkgs_to_install], PKGS => $pkgs_to_install,
}); });
}, },
$options $options
@ -2792,7 +2958,7 @@ sub run_install() {
'apt-get', '--yes', 'apt-get', '--yes',
'-oAPT::Get::Simulate=true', 'install' '-oAPT::Get::Simulate=true', 'install'
], ],
PKGS => [@pkgs_to_install], PKGS => $pkgs_to_install,
}); });
} }
} }
@ -3794,9 +3960,6 @@ sub get_keyring_by_suite {
my $debianvendor; my $debianvendor;
my $ubuntuvendor; my $ubuntuvendor;
# make $@ local, so we don't print "Can't locate Dpkg/Vendor/Debian.pm"
# in other parts where we evaluate $@
local $@ = '';
eval { eval {
require Dpkg::Vendor::Debian; require Dpkg::Vendor::Debian;
require Dpkg::Vendor::Ubuntu; require Dpkg::Vendor::Ubuntu;
@ -3916,9 +4079,6 @@ sub get_sourceslist_by_suite {
# https://lists.debian.org/87r26wqr2a.fsf@43-1.org # https://lists.debian.org/87r26wqr2a.fsf@43-1.org
my $bullseye_or_later = 0; my $bullseye_or_later = 0;
my $distro_info = '/usr/share/distro-info/debian.csv'; my $distro_info = '/usr/share/distro-info/debian.csv';
# make $@ local, so we don't print "Can't locate Debian/DistroInfo.pm"
# in other parts where we evaluate $@
local $@ = '';
eval { require Debian::DistroInfo; }; eval { require Debian::DistroInfo; };
if (!$@) { if (!$@) {
# libdistro-info-perl is installed # libdistro-info-perl is installed
@ -4145,20 +4305,9 @@ sub main() {
'h|help' => sub { pod2usage(-exitval => 0, -verbose => 1) }, 'h|help' => sub { pod2usage(-exitval => 0, -verbose => 1) },
'man' => sub { pod2usage(-exitval => 0, -verbose => 2) }, 'man' => sub { pod2usage(-exitval => 0, -verbose => 2) },
'version' => sub { print STDOUT "mmdebstrap $VERSION\n"; exit 0; }, 'version' => sub { print STDOUT "mmdebstrap $VERSION\n"; exit 0; },
'components=s@' => \$options->{components}, 'components=s@' => \$options->{components},
'variant=s' => \$options->{variant}, 'variant=s' => \$options->{variant},
'include=s' => sub { 'include=s@' => \$options->{include},
my ($opt_name, $opt_value) = @_;
for my $pkg (split /[,\s]+/, $opt_value) {
# strip leading and trailing whitespace
$pkg =~ s/^\s+|\s+$//g;
# skip if the remainder is an empty string
if ($pkg eq '') {
next;
}
push @{ $options->{include} }, $pkg;
}
},
'architectures=s@' => \$options->{architectures}, 'architectures=s@' => \$options->{architectures},
'mode=s' => \$options->{mode}, 'mode=s' => \$options->{mode},
'dpkgopt=s@' => \$options->{dpkgopts}, 'dpkgopt=s@' => \$options->{dpkgopts},
@ -4281,10 +4430,6 @@ sub main() {
if (any { $_ eq $options->{variant} } ('-', 'debootstrap')) { if (any { $_ eq $options->{variant} } ('-', 'debootstrap')) {
$options->{variant} = 'important'; $options->{variant} = 'important';
} }
# minbase is an alias for required
if ($options->{variant} eq 'minbase') {
$options->{variant} = 'required';
}
# fakeroot is an alias for fakechroot # fakeroot is an alias for fakechroot
if ($options->{mode} eq 'fakeroot') { if ($options->{mode} eq 'fakeroot') {
@ -4353,7 +4498,7 @@ sub main() {
# the --robot option was introduced in 1.20.0 but until 1.20.2 the # the --robot option was introduced in 1.20.0 but until 1.20.2 the
# output contained a string after the version, separated by a # output contained a string after the version, separated by a
# whitespace -- since then, it's only the version # whitespace -- since then, it's only the version
if ($? == 0 and $content =~ /^([0-9.]+).*$/) { if ($? == 0 and $content =~ /^([0-9.]+)( .*)?$/) {
# dpkg is new enough for the --robot option # dpkg is new enough for the --robot option
$dpkgversion = version->new($1); $dpkgversion = version->new($1);
} }
@ -4374,8 +4519,8 @@ sub main() {
and $content =~ /^apt ([0-9]+\.[0-9]+\.[0-9]+) \([a-z0-9-]+\)$/m) { and $content =~ /^apt ([0-9]+\.[0-9]+\.[0-9]+) \([a-z0-9-]+\)$/m) {
$aptversion = version->new($1); $aptversion = version->new($1);
} }
if ($aptversion < "2.3.10") { if ($aptversion < "2.3.7") {
error "need apt >= 2.3.10 but have $aptversion"; error "need apt >= 2.3.7 but have $aptversion";
} }
} }
@ -6007,9 +6152,9 @@ option depends on the selected variant. The B<extract> and B<custom> variants
install no packages by default, so for these variants, the packages specified install no packages by default, so for these variants, the packages specified
by this option will be the only ones that get either extracted or installed by by this option will be the only ones that get either extracted or installed by
dpkg, respectively. For all other variants, apt is used to install the dpkg, respectively. For all other variants, apt is used to install the
additional packages. Package names are directly passed to apt and thus, you additional packages. Package names are directly passed to
can use apt features like C<pkg/suite>, C<pkg=version>, C<pkg->, use a glob or apt and thus, you can use apt features like C<pkg/suite>, C<pkg=version>,
regex for C<pkg> or use apt patterns. See apt(8) for the supported C<pkg-> or use a glob or regex for C<pkg>. See apt(8) for the supported
syntax. The option can be specified multiple times and the packages are syntax. The option can be specified multiple times and the packages are
concatenated in the order in which they are given on the command line. If concatenated in the order in which they are given on the command line. If
later list items are repeated, then they get dropped so that the resulting later list items are repeated, then they get dropped so that the resulting
@ -6316,10 +6461,8 @@ All package sets also include the direct and indirect hard dependencies (but
not recommends) of the selected package sets. The variants B<minbase>, not recommends) of the selected package sets. The variants B<minbase>,
B<buildd> and B<->, resemble the package sets that debootstrap would install B<buildd> and B<->, resemble the package sets that debootstrap would install
with the same I<--variant> argument. The release with a name matching the with the same I<--variant> argument. The release with a name matching the
I<SUITE> argument as well as the native architecture will be used to determine I<SUITE> argument will be used to determine the C<Essential:yes> and priority
the C<Essential:yes> and priority values. To select packages with matching values.
priority from any suite, specify the empty string for I<SUITE>. The default
variant is B<debootstrap>.
=over 8 =over 8
@ -6346,30 +6489,19 @@ The B<essential> set plus apt.
=item B<required>, B<minbase> =item B<required>, B<minbase>
The B<essential> set plus all packages with Priority:required and apt. The B<essential> set plus all packages with Priority:required and apt.
It is roughly equivalent to running mmdebstrap with
--variant=essential --include="?priority(required)"
=item B<buildd> =item B<buildd>
The B<minbase> set plus build-essential. The B<minbase> set plus build-essential.
It is roughly equivalent to running mmdebstrap with
--variant=essential --include="?priority(required),build-essential"
=item B<important>, B<debootstrap>, B<-> =item B<important>, B<debootstrap>, B<->
The B<required> set plus all packages with Priority:important. This is the The B<required> set plus all packages with Priority:important. This is the
default of debootstrap. It is roughly equivalent to running mmdebstrap with default of debootstrap.
--variant=essential --include="~prequired|~pimportant"
=item B<standard> =item B<standard>
The B<important> set plus all packages with Priority:standard. The B<important> set plus all packages with Priority:standard.
It is roughly equivalent to running mmdebstrap with
--variant=essential --include="~prequired|~pimportant|~pstandard"
=back =back
@ -6847,17 +6979,6 @@ Create a system that can be used with podman:
root root
$ podman rmi debian $ podman rmi debian
As a docker/podman replacement:
$ mmdebstrap unstable | mmtarfilter --path-exclude='/dev/*' > chroot.tar
[...]
$ mmdebstrap --variant=custom --skip=update \
--setup-hook='tar-in chroot.tar /' \
--customize-hook='chroot "$1" whoami' unstable /dev/null
[...]
root
$ rm chroot.tar
=head1 ENVIRONMENT VARIABLES =head1 ENVIRONMENT VARIABLES
=over 8 =over 8