Hi. I tried to do that apt pinning today, as you suggested. It still fails in the same way as before:
$ mmdebstrap .... .... I: installing remaining packages inside the chroot... .... Some packages could not be installed. This may mean that you have requested an impossible situation or if you are using the unstable distribution that some required packages have not yet been created or been moved out of Incoming. The following information may help to resolve the situation: The following packages have unmet dependencies: ros-noetic-cv-bridge : Depends: libopencv-dev but it is not installable ros-noetic-grid-map-filters : Depends: libopencv-dev but it is not installable ros-noetic-image-geometry : Depends: libopencv-dev but it is not installable E: Unable to correct problems, you have held broken packages. The preferences file is there, but it isn't obviously doing anything. I did some debugging just now to try to figure out why, and I'm reminded of https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1036929 Ideally the workflow I would like is 1. mmdebstrap does stuff. When it runs a command, it prints out EXACTLY what it does, in a way that I can copy/paste it, and get the same output 2. If it fails to do something, it drops me into a shell. Where I can paste the command to reproduce the problem, and then poke around to fix it This probably isn't 100% possible here; but how would you debug this otherwise? I'm attaching a patch that does some of (1.) above. This uses String::ShellQuote to quote all the arguments so that the command string can be pasted. I'm guessing you'd want to do some of that differently, so it isn't super thorough. OK. Then I gave myself a shell in a spot that (I think?) sits right before the failing "apt-get install": mmdebstrap \ --essential-hook 'echo $$1; bash -i' \ .... And pasting the command didn't work as I had hoped: root@fatty:# apt-get -o Dir::Bin::dpkg=env -o DPkg::Options::=--unset=TMPDIR -o DPkg::Options::=dpkg -o DPkg::Chroot-Directory=/tmp/mmdebstrap.i1wpW0WLMS --yes install -oDpkg::Use-Pty=false tst-libopoencv '?narrow(?or(?archive(^focal$),?codename(^focal$)),?architecture(arm64),?and(?or(?priority(required),?priority(important)),?not(?essential)))' E: Could not open lock file /var/lib/dpkg/lock-frontend - open (13: Permission denied) E: Unable to acquire the dpkg frontend lock (/var/lib/dpkg/lock-frontend), are you root? Any idea what the difference is? Here I'm running the external "apt-get". Is the "apt-get install" supposed to happen inside the chroot? There is no "apt-get" binary there yet. If I can quickly and repeatedly reproduce the failure, I can "apt-cache policy libopencv-dev", and I can see if it's trying to use the preferences file and such. Maybe I mistyped something. If the above diagnostic sequence cannot work, what would? Thanks much
diff --git a/debian/control b/debian/control index c82cdb3..e3f56a8 100644 --- a/debian/control +++ b/debian/control @@ -17,6 +17,7 @@ Architecture: all Depends: apt (>= 2.3.14), python3 (>= 3.10), + libstring-shellquote-perl, ${misc:Depends}, ${perl:Depends}, Recommends: diff --git a/mmdebstrap b/mmdebstrap index 0abdfc3..d52496d 100755 --- a/mmdebstrap +++ b/mmdebstrap @@ -46,6 +46,7 @@ use Socket; use Time::HiRes; use Math::BigInt; use Text::ParseWords; +use String::ShellQuote qw(shell_quote); use version; ## no critic (InputOutput::RequireBriefOpen) @@ -815,8 +816,6 @@ sub run_progress { info "run_progress() received signal $_[0]: waiting for child..."; }; - debug("run_progress: exec " . (join ' ', ($get_exec->('${FD}')))); - # delay signals so that we can fork and change behaviour of the signal # handler in parent and child without getting interrupted my $sigset = POSIX::SigSet->new(SIGINT, SIGHUP, SIGPIPE, SIGTERM); @@ -845,6 +844,8 @@ sub run_progress { # redirect stderr to stdout so that we can capture it open(STDERR, '>&', STDOUT) or error "cannot open STDOUT: $!"; my @execargs = $get_exec->($fd); + my $cmd_string = shell_quote(@execargs); + # before apt 1.5, "apt-get update" attempted to chdir() into the # working directory. This will fail if the current working directory # is not accessible by the user (for example in unshare mode). See @@ -853,8 +854,11 @@ sub run_progress { chdir $chdir or error "failed chdir() to $chdir: $!"; } eval { Devel::Cover::set_coverage("none") } if $is_covering; + + info("run_progress: running $cmd_string"); + exec { $execargs[0] } @execargs - or error 'cannot exec() ' . (join ' ', @execargs); + or error 'cannot exec() $cmd_string'; } close $wfh; @@ -947,7 +951,7 @@ sub run_progress { if ($verbosity_level >= 1) { print STDERR $output; } - error((join ' ', $get_exec->('<$fd>')) . ' failed'); + error(shell_quote($get_exec->('<$fd>')) . ' failed'); } return; } @@ -1678,8 +1682,6 @@ sub run_hooks { $script = $script->[1]; if ($type eq "pivoted") { - info "running --chrooted-$name-hook in shell: sh -c " - . "'$script'"; my $pid = fork() // error "fork() failed: $!"; if ($pid == 0) { # child @@ -1710,8 +1712,11 @@ sub run_hooks { } else { error "unknown mode: $options->{mode}"; } - 0 == system(@cmdprefix, 'env', @env_opts, 'sh', '-c', - $script) + + my @cmd = (@cmdprefix, 'env', @env_opts, 'sh', '-c', + $script); + info "running --chrooted-$name-hook in shell: " . shell_quote(@cmd); + 0 == system(@cmd) or error "command failed: $script"; exit 0; } @@ -1731,7 +1736,9 @@ sub run_hooks { |sync-in|sync-out )\ /x ) { - info "running special hook: $script"; + my @args = shellwords $script; + my @cmd = @args; + info "running special hook: " . shell_quote(@cmd); if ((any { $_ eq $options->{variant} } ('extract', 'custom')) and $options->{mode} eq 'fakechroot' and $name ne 'setup') { @@ -1762,18 +1769,19 @@ sub run_hooks { waitpid($pid, 0); $? == 0 or error "special hook failed with exit code $?"; } elsif (-x $script || $script !~ m/[^\w@\%+=:,.\/-]/a) { - info "running --$name-hook directly: $script $options->{root}"; + my @cmd = ('env', @env_opts, $script, $options->{root}); + info "running --$name-hook directly: " . shell_quote(@cmd); # execute it directly if it's an executable file # or if it there are no shell metacharacters # (the /a regex modifier makes \w match only ASCII) - 0 == system('env', @env_opts, $script, $options->{root}) + 0 == system(@cmd) or error "command failed: $script"; } else { - info "running --$name-hook in shell: sh -c '$script' exec" - . " $options->{root}"; + my @cmd = ('env', @env_opts, + 'sh', '-c', $script, 'exec', $options->{root}); + info "running --$name-hook in shell: " . shell_quote(@cmd); # otherwise, wrap everything in sh -c - 0 == system('env', @env_opts, - 'sh', '-c', $script, 'exec', $options->{root}) + 0 == system(@cmd) or error "command failed: $script"; }