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";
             }
 

Reply via email to