Installing Debian GNU/Linux in a chroot on a Samsung Galaxy S II
(Last updated 2011-11-26)
There are numerous how-tos on the web for installing a Debian chroot onto a smartphone running Android, but most of them either give you an image to install on your phone (which doesn't teach you anything) or have you set up the Debian system on your desktop and copy it to the phone. Neither of these options appeals to me, so I've done it my own way and written this document to light the way for others, too.
Assumptions made:
-
You have an Android smartphone with access to a root shell via SSH. I run
CyanogenMod 7.1 on my Galaxy S
II, but any Android system with root SSH access will do, provided it has
the following utilities:
- cat
- chroot
- dd
- ln
- mkdir
- mke2fs
- mount
- tar
- wget (used by cdebootstrap)
- You have a reasonably fast and high-capacity Internet connection available on your Android phone. I will not be held responsible for any excess usage charges you incur on your mobile Internet package while downloading Debian onto your phone. ;-)
- You have a basic understanding of Debian and its packaging system. This procedure does require manual invocation of dpkg if you run into problems -- if you don't know what dpkg is or how to use it, this method probably isn't for you.
If not all of these assumptions are true, please don't contact me just to complain if something doesn't go right for you when following this how-to. Of course, alternative suggestions are welcome!
Preparation
The first thing you need to do is install cdebootstrap on your phone. cdebootstrap is a tool for bootstrapping a Debian system, usually from another Debian system, but in this case we'll be doing it from Android.
So, to install cdebootstrap:
- Download the cdebootstrap-static package for your architecture. Both armel and armhf packages should work on recent Android phones; if you have an older phone, you may need to stick with armel. I'll be using armhf in this tutorial, but you are free to choose whichever architecture suits you best.
-
Now, copy the files needed for cdebootstrap to work across to
the phone. It doesn't matter where you put them, as long as you can
execute the cdebootstrap binary -- so don't put it in
/mnt/sdcard, as this is mounted with the noexec mount
option. These files are nicely ready for us in a tarball located at
./usr/lib/cdebootstrap/cdebootstrap_0.5.8+b1_armhf.tar.gz inside
the package, so you can just pipe the contents of this tarball to
ssh and extract it on the phone. I'm going to put it into
/data/local/bin/, because that's already in the default
$PATH so I don't have to care about it again.
$ mkdir /tmp/cdebootstrap && cd /tmp/cdebootstrap
Remember to replace $PHONE with the hostname of your phone.
$ ar x /path/to/cdebootstrap-static_0.5.8+b1_armhf.deb data.tar.gz
$ tar xzOf data.tar.gz ./usr/lib/cdebootstrap/cdebootstrap_0.5.8+b1_armhf.tar.gz | ssh root@$PHONE 'tar xzC /data/local/bin/' -
Unfortunately, the version of cdebootstrap included in the
tarball appears to segfault on Android systems. I don't know why this
happens, but replacing it with the binary provided in the package seems
to fix this issue:
$ tar xzOf data.tar.gz ./usr/bin/cdebootstrap-static | ssh root@$PHONE 'cat >/data/local/bin/cdebootstrap'
Bootstrapping the Debian system
Now that you have a working cdebootstrap on your phone, you can use it to bootstrap Debian. First, however, you will need to create a file system for Debian to live on. It may be possible to create such a file system directly on the SD card, but as I don't really understand Android partitioning, I decided not to try this and risk losing data. (If someone knows of a way to do this, please contact me and let me know!) The reason you need a separate file system for Debian is that Android mounts its own file systems with options like noexec, which would disallow executing all binaries placed on them (thus defeating the purpose of an operating system).
The easiest way to set up a file system is to simply create a file in /mnt/sdcard and format that -- but beware, if you make this file larger than 2 GiB, the FAT file system it lives on will complain and you will lose the image next time you reboot. This happened to me the first time I tried it, and I hadn't made a backup, so I had to do it all over again. :-(
So, ssh to your phone, and run the following commands:
# dd if=/dev/zero of=/mnt/sdcard/debian.img bs=1024 count=2000000
# mke2fs -Fj /mnt/sdcard/debian.img
# mkdir /debian
# mount -t ext3 -o noatime,errors=remount-ro /mnt/sdcard/debian.img /debian
# for fs in /dev /proc /sys; do mount -o bind "$fs" "/debian$fs"; done
One last thing you need to do before setting up Debian: cdebootstrap expects the system shell to be at /bin/sh and fails without telling you why if it isn't. Since Android's system shell is /system/bin/sh, you have to appease cdebootstrap like so:
# mkdir /bin && ln -s /system/bin/sh /bin/sh
Now comes the fun bit, where we actually set up a Debian system. Still on your phone, run cdebootstrap to set up a file system tree in /debian. You may want to tweak the options below; I'm using ftp.debian-ports.org as my mirror because (at the time of writing) the armhf architecture is not yet fully integrated into the official archive. If you're using armel, or if you happen to live in the future, you can use your normal Debian mirror for this. Don't forget to replace unstable with whichever suite you want to install, but be aware that (at the time of writing) armhf only exists in unstable -- if you want stable or testing, and you downloaded an armhf cdebootstrap binary, you will need to specify the -aarmel option to cdebootstrap.
# cdebootstrap --allow-unauthenticated -c/data/local/bin/cdebootstrap-support/ unstable /debian http://ftp.debian-ports.org/debian/
We have to use --allow-unauthenticated because gpgv isn't available to authenticate signatures on Android. This command can take a while to complete, so go and have a coffee or something while you wait for Debian to download and install.
Fixing broken packages
It was the case for me that some packages failed to install from cdebootstrap. Fortunately, it had at least progressed to the point where a shell and dpkg were installed and usable in the chroot, so now we can leave cdebootstrap behind and work in Debian:
# chroot /debian /bin/bash
# cd /var/cache/bootstrap
# dpkg -i *.deb
At this point, dpkg may run into some problems. As the package archive changes over time and different people install different suites, your problems will very likely not be the same as mine, so here's where your familiarity with the Debian package tools comes in. ;-)
The main issue I ran into here was circular dependencies. These can be fixed by the use of the --force-depends option to dpkg, to allow it to install and configure one of the packages in the dependency loop, so that you can then install the others normally. For example, I found that libc6 depends on libgcc1, which pre-depends on multiarch-support, which depends on libc6. After running:
# dpkg --force-depends --configure multiarch-support
this circular dependency was no longer a problem, although there were other remaining circular dependencies that could be fixed in a similar way.
Once you have resolved a few circular dependencies and want to try again, it is a good idea to run:
# dpkg --configure -a
before telling dpkg to try installing packages again, as the shell glob *.deb will simply present the packages in alphabetical order, which is usually not a sensible order in terms of satisyfing dependencies. dpkg --configure -a will make sure that packages which are already installed (and thus don't depend on any which aren't installed yet) get configured before those which aren't already installed (and might depend on installed packages being configured).
Installing a standard Debian package set
cdebootstrap will only install packages with a priority of required or important. While this will give you a powerful Unix-like base system, it doesn't include many utilities that you would get with a standard Debian system. Once all of your package dependency issues have been resolved, and everything in /var/cache/bootstrap/ is installed, you can use aptitude to install a standard Debian system:
# echo 'deb http://ftp.debian-ports.org/debian unstable main' >/etc/apt/sources.list
# echo 'nameserver 8.8.8.8' >/etc/resolv.conf
# aptitude update
# aptitude install debian-ports-archive-keyring
# aptitude install '~prequired|~pimportant|~pstandard'
What you choose to install beyond this depends on your wants and needs, and how you like your Debian. ;-)
Initialising the Debian system at boot time
I have written a couple of simple scripts to ease getting Debian up and running. They come in pairs; one of them lives in Android and chroots to Debian, and the other lives in Debian and gets run after the chroot. You can find a copy of these scripts in Gitweb, or clone the git repository with:
$ git clone git://git.steven-mcdonald.id.au/android-debian.git
For usage information, please refer to the README in that git repository.
Security considerations
Doing everything as root is less than desirable; however, Android seems to have prevented ordinary users from doing things such as resolving hostnames. When I have time, I'd certainly like to fix this properly, but for now, it seems a Debian chroot on Android must be used as root in order to be useful.