Repository: ansible/ansible

Issue:
Chroot doesn't work with busybox
With busybox, `/bin/sh` can be a relative symlink, and won't resolve correctly prior to `chroot()`.

```
PLAY ***************************************************************************

TASK [setup] *******************************************************************
An exception occurred during task execution. To see the full traceback, use -vvv. The error was: OSError: [Errno 2] No such file or directory: '/rootfs/bin/sh'
fatal: [/rootfs]: FAILED! => {"failed": true, "stdout": ""}

NO MORE HOSTS LEFT *************************************************************

PLAY RECAP *********************************************************************
/rootfs                    : ok=0    changed=0    unreachable=0    failed=1   
```

```
# ls -la /rootfs/bin/sh
lrwxrwxrwx 1 root root 12 Jun  1 22:17 /rootfs/bin/sh -> /bin/busybox
```

A sensible solution for this would be through an option to override [the hardcoded shell path](https://github.com/ansible/ansible/blob/v2.0.0.1-1/lib/ansible/plugins/connection/chroot.py#L67-L68), or something like that.



Discussion:
`ansible_executable` host var or `executable` in ansible.cfg already do that, they just need to be added to the chroot plugin

I don't think ansible_executable can be used here since that's used from inside of the target and this is being used from outside of the target.

The code looks like it is just sanity checking that the chroot is going to be usable by us.  Perhaps we should change the check to simply pass if chroot + "/bin/sh" is a symlink instead of an executable.  ansible_executable could probably be added into the mix as well but it would be solving a separate problem than is being reported here.

This sounds right to me.

On Fri, 10 Jun 2016, 00:22 Toshio Kuratomi, notifications@github.com
wrote:

> The code looks like it is just sanity checking that the chroot is going to
> be usable by us. Perhaps we should change the check to simply pass if
> chroot + "/bin/sh" is a symlink instead of an executable.
> ansible_executable could probably be added into the mix as well but it
> would be solving a separate problem than is being reported here.
> 
> —
> You are receiving this because you authored the thread.
> Reply to this email directly, view it on GitHub
> https://github.com/ansible/ansible/issues/16097#issuecomment-225055392,
> or mute the thread
> https://github.com/notifications/unsubscribe/AAPWS3z2pY6i9GQTY1AEEjwadfd-6jmcks5qKKBRgaJpZM4IsdGz
> .

Thank you, Toshio.

On Fri, 10 Jun 2016, 00:24 Ilya Dmitrichenko, errordeveloper@gmail.com
wrote:

> This sounds right to me.
> 
> On Fri, 10 Jun 2016, 00:22 Toshio Kuratomi, notifications@github.com
> wrote:
> 
> > The code looks like it is just sanity checking that the chroot is going
> > to be usable by us. Perhaps we should change the check to simply pass if
> > chroot + "/bin/sh" is a symlink instead of an executable.
> > ansible_executable could probably be added into the mix as well but it
> > would be solving a separate problem than is being reported here.
> > 
> > —
> > You are receiving this because you authored the thread.
> > Reply to this email directly, view it on GitHub
> > https://github.com/ansible/ansible/issues/16097#issuecomment-225055392,
> > or mute the thread
> > https://github.com/notifications/unsubscribe/AAPWS3z2pY6i9GQTY1AEEjwadfd-6jmcks5qKKBRgaJpZM4IsdGz
> > .


Resolving patch:
diff --git a/lib/ansible/plugins/connection/chroot.py b/lib/ansible/plugins/connection/chroot.py
--- a/lib/ansible/plugins/connection/chroot.py
+++ b/lib/ansible/plugins/connection/chroot.py
@@ -63,7 +63,11 @@ def __init__(self, play_context, new_stdin, *args, **kwargs):
             raise AnsibleError("%s is not a directory" % self.chroot)
 
         chrootsh = os.path.join(self.chroot, 'bin/sh')
-        if not is_executable(chrootsh):
+        # Want to check for a usable bourne shell inside the chroot.
+        # is_executable() == True is sufficient.  For symlinks it
+        # gets really complicated really fast.  So we punt on finding that
+        # out.  As long as it's a symlink we assume that it will work
+        if not (is_executable(chrootsh) or (os.path.lexists(chrootsh) and os.path.islink(chrootsh))):
             raise AnsibleError("%s does not look like a chrootable dir (/bin/sh missing)" % self.chroot)
 
         self.chroot_cmd = distutils.spawn.find_executable('chroot')


Test patch: