Add chrootless mode and extract and custom variants
This commit is contained in:
parent
07f0e53081
commit
7534a7607f
1 changed files with 447 additions and 366 deletions
219
mmdebstrap
219
mmdebstrap
|
@ -586,12 +586,23 @@ sub setup {
|
|||
print $conf "Dir::Etc::TrustedParts \"/etc/apt/trusted.gpg.d\";\n";
|
||||
close $conf;
|
||||
|
||||
foreach my $dir ('/etc/apt/apt.conf.d', '/etc/apt/sources.list.d',
|
||||
{
|
||||
my @directories = ('/etc/apt/apt.conf.d', '/etc/apt/sources.list.d',
|
||||
'/etc/apt/preferences.d', '/var/cache/apt',
|
||||
'/var/lib/apt/lists/partial', '/var/lib/dpkg',
|
||||
'/etc/dpkg/dpkg.cfg.d/') {
|
||||
'/etc/dpkg/dpkg.cfg.d/');
|
||||
# if dpkg and apt operate from the outside we need some more
|
||||
# directories because dpkg and apt might not even be installed inside
|
||||
# the chroot
|
||||
if ($options->{mode} eq 'chrootless') {
|
||||
push @directories, ('/var/log/apt', '/var/lib/dpkg/triggers',
|
||||
'/var/lib/dpkg/info', '/var/lib/dpkg/alternatives',
|
||||
'/var/lib/dpkg/updates');
|
||||
}
|
||||
foreach my $dir (@directories) {
|
||||
make_path("$options->{root}/$dir") or die "failed to create $dir: $!";
|
||||
}
|
||||
}
|
||||
|
||||
# We put certain configuration items in their own configuration file
|
||||
# because they have to be valid for apt invocation from outside as well as
|
||||
|
@ -638,6 +649,8 @@ sub setup {
|
|||
}
|
||||
|
||||
if (scalar @{$options->{dpkgopts}} > 0) {
|
||||
# FIXME: in chrootless mode, dpkg will only read the configuration
|
||||
# from the host
|
||||
open my $fh, '>', "$options->{root}/etc/dpkg/dpkg.cfg.d/99mmdebstrap" or die "cannot open /etc/dpkg/dpkg.cfg.d/99mmdebstrap: $!";
|
||||
foreach my $opt (@{$options->{dpkgopts}}) {
|
||||
if (-r $opt) {
|
||||
|
@ -673,6 +686,36 @@ sub setup {
|
|||
close $fh;
|
||||
}
|
||||
|
||||
# allow network access from within
|
||||
copy("/etc/resolv.conf", "$options->{root}/etc/resolv.conf") or die "cannot copy /etc/resolv.conf: $!";
|
||||
copy("/etc/hostname", "$options->{root}/etc/hostname") or die "cannot copy /etc/hostname: $!";
|
||||
|
||||
if ($options->{havemknod}) {
|
||||
foreach my $file (@devfiles) {
|
||||
my ($fname, $mode, $type, $linkname, $devmajor, $devminor) = @{$file};
|
||||
if ($type == 0) { # normal file
|
||||
die "type 0 not implemented";
|
||||
} elsif ($type == 1) { # hardlink
|
||||
die "type 1 not implemented";
|
||||
} elsif ($type == 2) { # symlink
|
||||
symlink $linkname, "$options->{root}/$fname" or die "cannot create symlink $fname";
|
||||
next; # chmod cannot work on symlinks
|
||||
} elsif ($type == 3) { # character special
|
||||
0 == system('mknod', "$options->{root}/$fname", 'c', $devmajor, $devminor) or die "mknod failed: $?";
|
||||
} elsif ($type == 4) { # block special
|
||||
0 == system('mknod', "$options->{root}/$fname", 'b', $devmajor, $devminor) or die "mknod failed: $?";
|
||||
} elsif ($type == 5) { # directory
|
||||
make_path "$options->{root}/$fname", { error => \my $err };
|
||||
if (@$err) {
|
||||
die "cannot create $fname";
|
||||
}
|
||||
} else {
|
||||
die "unsupported type: $type";
|
||||
}
|
||||
chmod $mode, "$options->{root}/$fname" or die "cannot chmod $fname: $!";
|
||||
}
|
||||
}
|
||||
|
||||
# we tell apt about the configuration via a config file passed via the
|
||||
# APT_CONFIG environment variable instead of using the --option command
|
||||
# line arguments because configuration settings like Dir::Etc have already
|
||||
|
@ -713,7 +756,30 @@ sub setup {
|
|||
# apt and libapt treats apt as essential. If we want to install less
|
||||
# (essential variant) then we have to compute the package set ourselves.
|
||||
# Same if we want to install priority based variants.
|
||||
if (any { $_ eq $options->{variant} } ('essential', 'standard', 'important', 'required', 'buildd', 'minbase')) {
|
||||
if (any { $_ eq $options->{variant} } ('extract', 'custom')) {
|
||||
print STDERR "I: downloading packages with apt...\n";
|
||||
run_apt_progress ('apt-get', '--yes',
|
||||
'-oApt::Get::Download-Only=true',
|
||||
'install', keys %pkgs_to_install);
|
||||
} elsif ($options->{variant} eq 'apt') {
|
||||
# if we just want to install Essential:yes packages, apt and their
|
||||
# dependencies then we can make use of libapt treating apt as
|
||||
# implicitly essential. An upgrade with the (currently) empty status
|
||||
# file will trigger an installation of the essential packages plus apt.
|
||||
#
|
||||
# 2018-09-02, #debian-dpkg on OFTC, times in UTC+2
|
||||
# 23:39 < josch> I'll just put it in my script and if it starts
|
||||
# breaking some time I just say it's apt's fault. :P
|
||||
# 23:42 < DonKult> that is how it usually works, so yes, do that :P (<-
|
||||
# and please add that line next to it so you can
|
||||
# remind me in 5+ years that I said that after I wrote
|
||||
# in the bugreport: "Are you crazy?!? Nobody in his
|
||||
# right mind would even suggest depending on it!")
|
||||
print STDERR "I: downloading packages with apt...\n";
|
||||
run_apt_progress ('apt-get', '--yes',
|
||||
'-oApt::Get::Download-Only=true',
|
||||
'dist-upgrade');
|
||||
} elsif (any { $_ eq $options->{variant} } ('essential', 'standard', 'important', 'required', 'buildd', 'minbase')) {
|
||||
my %ess_pkgs;
|
||||
open(my $pipe_apt, '-|', 'apt-get', 'indextargets', '--format', '$(FILENAME)', 'Created-By: Packages') or die "cannot start apt-get indextargets: $!";
|
||||
while (my $fname = <$pipe_apt>) {
|
||||
|
@ -790,24 +856,6 @@ sub setup {
|
|||
run_apt_progress ('apt-get', '--yes',
|
||||
'-oApt::Get::Download-Only=true',
|
||||
'install', keys %ess_pkgs);
|
||||
} elsif ($options->{variant} eq 'apt') {
|
||||
# if we just want to install Essential:yes packages, apt and their
|
||||
# dependencies then we can make use of libapt treating apt as
|
||||
# implicitly essential. An upgrade with the (currently) empty status
|
||||
# file will trigger an installation of the essential packages plus apt.
|
||||
#
|
||||
# 2018-09-02, #debian-dpkg on OFTC, times in UTC+2
|
||||
# 23:39 < josch> I'll just put it in my script and if it starts
|
||||
# breaking some time I just say it's apt's fault. :P
|
||||
# 23:42 < DonKult> that is how it usually works, so yes, do that :P (<-
|
||||
# and please add that line next to it so you can
|
||||
# remind me in 5+ years that I said that after I wrote
|
||||
# in the bugreport: "Are you crazy?!? Nobody in his
|
||||
# right mind would even suggest depending on it!")
|
||||
print STDERR "I: downloading packages with apt...\n";
|
||||
run_apt_progress ('apt-get', '--yes',
|
||||
'-oApt::Get::Download-Only=true',
|
||||
'dist-upgrade');
|
||||
} else {
|
||||
die "unknown variant: $options->{variant}";
|
||||
}
|
||||
|
@ -841,6 +889,28 @@ sub setup {
|
|||
die "nothing got downloaded";
|
||||
}
|
||||
|
||||
if ($options->{mode} eq 'chrootless') {
|
||||
print STDERR "I: installing packages...\n";
|
||||
# FIXME: the dpkg config from the host is parsed before the command
|
||||
# line arguments are parsed and might break this mode
|
||||
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");
|
||||
run_apt_progress ('apt-get', '--yes', @chrootless_opts,
|
||||
'install', (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')) {
|
||||
if (%pkgs_to_install) {
|
||||
run_apt_progress ('apt-get', '--yes', @chrootless_opts,
|
||||
'install', keys %pkgs_to_install);
|
||||
}
|
||||
} else {
|
||||
die "unknown variant: $options->{variant}";
|
||||
}
|
||||
} elsif (any { $_ eq $options->{mode} } ('root', 'unshare', 'fakechroot', 'proot')) {
|
||||
print STDERR "I: extracting archives...\n";
|
||||
print_progress 0.0;
|
||||
my $counter = 0;
|
||||
|
@ -868,6 +938,9 @@ sub setup {
|
|||
}
|
||||
print_progress "done";
|
||||
|
||||
if ($options->{variant} eq 'extract') {
|
||||
# nothing else to do
|
||||
} elsif (any { $_ eq $options->{variant} } ('custom', 'essential', 'apt', 'standard', 'important', 'required', 'buildd', 'minbase')) {
|
||||
if ($options->{mode} eq 'fakechroot') {
|
||||
# FIXME: if trouble arises, look into /etc/fakechroot/*.env for
|
||||
# more interesting variables to set
|
||||
|
@ -931,32 +1004,6 @@ sub setup {
|
|||
}
|
||||
}
|
||||
|
||||
if ($options->{havemknod}) {
|
||||
foreach my $file (@devfiles) {
|
||||
my ($fname, $mode, $type, $linkname, $devmajor, $devminor) = @{$file};
|
||||
if ($type == 0) { # normal file
|
||||
die "type 0 not implemented";
|
||||
} elsif ($type == 1) { # hardlink
|
||||
die "type 1 not implemented";
|
||||
} elsif ($type == 2) { # symlink
|
||||
symlink $linkname, "$options->{root}/$fname" or die "cannot create symlink $fname";
|
||||
next; # chmod cannot work on symlinks
|
||||
} elsif ($type == 3) { # character special
|
||||
0 == system('mknod', "$options->{root}/$fname", 'c', $devmajor, $devminor) or die "mknod failed: $?";
|
||||
} elsif ($type == 4) { # block special
|
||||
0 == system('mknod', "$options->{root}/$fname", 'b', $devmajor, $devminor) or die "mknod failed: $?";
|
||||
} elsif ($type == 5) { # directory
|
||||
make_path "$options->{root}/$fname", { error => \my $err };
|
||||
if (@$err) {
|
||||
die "cannot create $fname";
|
||||
}
|
||||
} else {
|
||||
die "unsupported type: $type";
|
||||
}
|
||||
chmod $mode, "$options->{root}/$fname" or die "cannot chmod $fname: $!";
|
||||
}
|
||||
}
|
||||
|
||||
# install the extracted packages properly
|
||||
# we need --force-depends because dpkg does not take Pre-Depends into
|
||||
# account and thus doesn't install them in the right order
|
||||
|
@ -1116,10 +1163,6 @@ sub setup {
|
|||
chmod 0755, "$options->{root}/sbin/start-stop-daemon" or die "cannot chmod start-stop-daemon: $!";
|
||||
}
|
||||
|
||||
# allow network access from within
|
||||
copy("/etc/resolv.conf", "$options->{root}/etc/resolv.conf") or die "cannot copy /etc/resolv.conf: $!";
|
||||
copy("/etc/hostname", "$options->{root}/etc/hostname") or die "cannot copy /etc/hostname: $!";
|
||||
|
||||
print STDERR "I: installing remaining packages inside the chroot...\n";
|
||||
run_apt_progress @chrootcmd, 'apt-get', '--yes', 'install', keys %pkgs_to_install;
|
||||
|
||||
|
@ -1181,6 +1224,12 @@ sub setup {
|
|||
die "unknown mode: $options->{mode}";
|
||||
}
|
||||
}
|
||||
} else {
|
||||
die "unknown variant: $options->{variant}";
|
||||
}
|
||||
} else {
|
||||
die "unknown mode: $options->{mode}";
|
||||
}
|
||||
|
||||
# clean up temporary configuration file
|
||||
unlink "$options->{root}/etc/apt/apt.conf.d/00mmdebstrap" or die "failed to unlink /etc/apt/apt.conf.d/00mmdebstrap: $!";
|
||||
|
@ -1246,8 +1295,8 @@ sub main() {
|
|||
'aptopt=s@' => \$options->{aptopts},
|
||||
) or pod2usage(-exitval => 2, -verbose => 1);
|
||||
|
||||
my @valid_variants = ('essential', 'apt', 'required', 'minbase', 'buildd',
|
||||
'important', 'debootstrap', '-', 'standard');
|
||||
my @valid_variants = ('extract', 'custom', 'essential', 'apt', 'required',
|
||||
'minbase', 'buildd', 'important', 'debootstrap', '-', 'standard');
|
||||
if (none { $_ eq $options->{variant}} @valid_variants) {
|
||||
die "invalid variant. Choose from " . (join ', ', @valid_variants);
|
||||
}
|
||||
|
@ -1264,7 +1313,8 @@ sub main() {
|
|||
if ($options->{mode} eq 'sudo') {
|
||||
$options->{mode} = 'root';
|
||||
}
|
||||
my @valid_modes = ('auto', 'root', 'unshare', 'fakechroot', 'proot');
|
||||
my @valid_modes = ('auto', 'root', 'unshare', 'fakechroot', 'proot',
|
||||
'chrootless');
|
||||
if (none { $_ eq $options->{mode} } @valid_modes) {
|
||||
die "invalid mode. Choose from " . (join ', ', @valid_modes);
|
||||
}
|
||||
|
@ -1492,6 +1542,8 @@ sub main() {
|
|||
}
|
||||
exit 1;
|
||||
}
|
||||
} elsif ($options->{mode} eq 'chrootless') {
|
||||
# nothing to do
|
||||
} else {
|
||||
die "unknown mode: $options->{mode}";
|
||||
}
|
||||
|
@ -1502,6 +1554,9 @@ sub main() {
|
|||
$options->{maketar} = 0;
|
||||
if (scalar @tar_compress_opts > 0 or $options->{target} =~ /\.tar$/ or $options->{target} eq '-') {
|
||||
$options->{maketar} = 1;
|
||||
if (any { $_ eq $options->{variant} } ('extract', 'custom') and $options->{mode} eq 'fakechroot') {
|
||||
print STDERR "I: creating a tarball in fakechroot mode might fail in extract and custom variants because there might be no tar inside the chroot\n";
|
||||
}
|
||||
}
|
||||
|
||||
if ($options->{maketar}) {
|
||||
|
@ -1572,7 +1627,7 @@ sub main() {
|
|||
} \@idmap;
|
||||
waitpid $pid, 0;
|
||||
$? == 0 or die "havemknod failed";
|
||||
} elsif (any { $_ eq $options->{mode} } ('root', 'fakechroot', 'proot')) {
|
||||
} elsif (any { $_ eq $options->{mode} } ('root', 'fakechroot', 'proot', 'chrootless')) {
|
||||
$options->{havemknod} = havemknod($options->{root});
|
||||
} else {
|
||||
die "unknown mode: $options->{mode}";
|
||||
|
@ -1639,7 +1694,7 @@ sub main() {
|
|||
|
||||
exit 0;
|
||||
} \@idmap;
|
||||
} elsif (any { $_ eq $options->{mode} } ('root', 'fakechroot', 'proot')) {
|
||||
} elsif (any { $_ eq $options->{mode} } ('root', 'fakechroot', 'proot', 'chrootless')) {
|
||||
$pid = fork() // die "fork() failed: $!";
|
||||
if ($pid == 0) {
|
||||
close $rfh;
|
||||
|
@ -1668,7 +1723,7 @@ sub main() {
|
|||
# proot requires tar to run inside proot or otherwise
|
||||
# permissions will be completely off
|
||||
0 == system('proot', '--root-id', "--rootfs=$options->{root}", '--cwd=/', 'tar', @taropts, '--exclude=./dev', '-C', '/', '.') or die "tar failed: $?";
|
||||
} elsif (any { $_ eq $options->{mode} } ('root')) {
|
||||
} elsif (any { $_ eq $options->{mode} } ('root', 'chrootless')) {
|
||||
0 == system('tar', @taropts, '-C', $options->{root}, '.') or die "tar failed: $?";
|
||||
} else {
|
||||
die "unknown mode: $options->{mode}";
|
||||
|
@ -1719,7 +1774,7 @@ sub main() {
|
|||
} \@idmap;
|
||||
waitpid $pid, 0;
|
||||
$? == 0 or die "remove_tree failed";
|
||||
} elsif (any { $_ eq $options->{mode} } ('root', 'fakechroot', 'proot')) {
|
||||
} elsif (any { $_ eq $options->{mode} } ('root', 'fakechroot', 'proot', 'chrootless')) {
|
||||
# without unshare, we use the system's rm to recursively remove the
|
||||
# temporary directory just to make sure that we do not accidentally
|
||||
# remove more than we should by using --one-file-system.
|
||||
|
@ -1797,10 +1852,10 @@ Print this help text and exit.
|
|||
|
||||
=item B<--variant>
|
||||
|
||||
Choose which package set to install. Valid variant names are B<essential>,
|
||||
B<apt>, B<required>, B<minbase>, B<buildd>, B<important>, B<debootstrap>,
|
||||
B<->, and B<standard>. The default variant is B<required>. See the section
|
||||
B<VARIANTS> for more information.
|
||||
Choose which package set to install. Valid variant names are B<extract>,
|
||||
B<custom>, B<essential>, B<apt>, B<required>, B<minbase>, B<buildd>,
|
||||
B<important>, B<debootstrap>, B<->, and B<standard>. The default variant is
|
||||
B<required>. See the section B<VARIANTS> for more information.
|
||||
|
||||
=item B<--mode>
|
||||
|
||||
|
@ -1837,9 +1892,15 @@ Example: --dpkgopt="path-exclude=/usr/share/man/*"
|
|||
=item B<--include>
|
||||
|
||||
Comma separated list of packages which will be installed in addition to the
|
||||
packages installed by the specified variant. This option is incompatible with
|
||||
the essential variant because apt inside the chroot is needed to install extra
|
||||
packages.
|
||||
packages installed by the specified variant. The direct and indirect hard
|
||||
dependencies will also be installed. The behaviour of this 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 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 additional packages. The
|
||||
B<essential> variant does not include apt and thus, the include option will
|
||||
only work when the B<chrootless> mode is selected and thus apt from the outside
|
||||
can be used.
|
||||
|
||||
=item B<--components>
|
||||
|
||||
|
@ -1897,17 +1958,37 @@ permissions are only retained while proot is still running, this will lead to
|
|||
wrong permissions in the final directory and tarball. This mode is useful if
|
||||
you plan to use the chroot with proot.
|
||||
|
||||
=item B<chrootless>
|
||||
|
||||
Uses the dpkg option C<--force-script-chrootless> to install packages into
|
||||
B<TARGET> without dpkg and apt inside B<target> but using apt and dpkg from
|
||||
the machine running mmdebstrap. Maintainer scripts are run without chrooting
|
||||
into B<TARGET> and rely on their dependencies being installed on the machine
|
||||
running mmdebstrap.
|
||||
|
||||
=back
|
||||
|
||||
=head1 VARIANTS
|
||||
|
||||
All package sets also include the hard dependencies (but not recommends) of
|
||||
the selected package sets. The variants B<minbase>, B<buildd> and B<->,
|
||||
resemble the package sets that debootstrap would install with the same
|
||||
I<--variant> argument.
|
||||
All package sets also include the direct and indirect hard dependencies (but
|
||||
not recommends) of the selected package sets. The variants B<minbase>,
|
||||
B<buildd> and B<->, resemble the package sets that debootstrap would install
|
||||
with the same I<--variant> argument.
|
||||
|
||||
=over 8
|
||||
|
||||
=item B<extract>
|
||||
|
||||
Installs nothing by default (not even C<Essential:yes> packages). Packages
|
||||
given by the C<--include> option are extracted but will not be installed.
|
||||
|
||||
=item B<custom>
|
||||
|
||||
Installs nothing by default (not even C<Essential:yes> packages). Packages
|
||||
given by the C<--include> option will be installed. If another mode than
|
||||
B<chrootless> was selected and dpkg was not part of the included package set,
|
||||
then this variant will fail because it cannot configure the packages.
|
||||
|
||||
=item B<essential>
|
||||
|
||||
C<Essential:yes> packages.
|
||||
|
|
Loading…
Reference in a new issue