Note: Reasonably heavy UNIX-geekery ahead. Mostly Linux-specific, somewhat Debian-specific and a little bit Ubuntu-specific. Skip if that isn't your cup of ichor.
I recently did something incredibly ill-considered while logged in (as root, natch) to my Ubuntu server box at home. In essence, I told the package manager to uninstall libc.
On a scale from good to bad, this is bad.
Now, I could have booted up from a rescue disk and fixed it. I could have re-installed easily enough (using the trick where you just keep your existing partitions and don't format them -- though this would have inevitably led to some fallout as various config files and customizations got clobbered). Heck, I even had a reasonably recent backup at hand. None of these sounded especially fun, mostly because the machine was in a place where it's a hassle to stick a head on it. I had three things working in my favor: an open root-privileged shell prompt, Internet connectivity and my native cunning.
Read on after the jump for the full tale.
So, the first and most obvious problem (other than "If you didn't want to do that, then why did you type the command?") was that, while I had a shell prompt, most of the usual commands are dynamically linked and therefore busted. I had the stuff in /sbin, /usr/sbin and the shell builtins. OK, so I can use "echo *" instead of "ls". But there's no static apt-get or dpkg (or wget or tar or ftp or cat or dd).
Fortunately for me, the Ubuntu gnomes had kindly provided a static-sh command. This is a busybox with all kinds of treats built in, and not a dynamic library in sight. All the basic command line stuff seemed to be there, as well as such things as tar and bzip2 and wget. (Aside: Though I linked to the page for the full-blown GNU wget, the version in busybox is a work-vaguely-alike stripped down to the bare essentials.)
So, first problem: How do I get my system back to a somewhat-working state?
- On another system, get the appropriate libc6 package (.deb) from packages.ubuntu.com. In my case, it was libc6-amd64_2.15-0ubuntu10.3_i386.deb.
- Still on the other system, unpack the contents into a subdirectory (preferrably, without adgering that second system in the process):
dpkg-deb -x ../libc6-amd64_2.15-0ubuntu10.3_i386.deb
- Yet again on the other system, archive up the extracted files (and remove the unpacked version):
tar cvf - . | bzip2 -v9 >../libc6.tar.bz2
rm -rf foo
- Put the archive thus created on a handy Internet-accessible web server.
- On my broken system, filled with brokenness, which I broke, launch static-sh.
- On the broken system, use wget to fetch the archive and extract in the root directory:
bunzip2 -c libc6.tar.bz2 | tar xvf -
- Exit static-sh and bask in the power of a vaguely usable system.
That was obviously an improvement. All the usual shell commands worked again. I could ssh in from outside and sudo. ldconfig -v did something reasonable. I could even reboot the system and it would come back up and work.
The remaining problem was that the package manager was not best pleased. As far as it could tell, libc6 was uninstalled and deselected, and every dependency known to man was broken. Everything is ruined forever!
dpkg -i libc6-amd64_2.15-0ubuntu10.3_i386.deb
would fix it, right? Right?
Not so fast.
A copy of the C library was found in an unexpected directory: '/lib/x86_64-linux-gnu/libc-2.15.so' It is not safe to upgrade the C library in this situation; please remove that copy of the C library or get it out of '/lib/x86_64-linux-gnu' and try again.
Update: As of 24 April, 2014, the libc6-amd64_2.15-0ubuntu10.3_i386.deb package appears to have an updated preinstall script which does not contain the check that produces the error quoted above. If you have this problem, please re-download the package and try installing again, before you bother trying the more complex method outlined below.
Now, I get what the package maintainer was thinking here. Screwing this up could turn a working (or even partly-working) system into a doorstop. But I had already completely removed libc and lived to tell the tale. What if I really truly did want to JFDI and accept the consequences? (Not having a working package manager is not a viable long-term solution. If I couldn't fix this, I'd have to re-install anyway...)
I tried various promising things that a reasonable and prudent person might think would work. Here is a partial list:
- dpkg -i --force-all
- dpkg --unpack
- performing the installation in a chroot-ed jail
- actually following the suggestion in the error text and removing the offending file
- making a temp directory somewhere, adding it to /etc/ld.so.conf, and moving offending files there one at a time, running ldconfig -v after each
(Though it is perhaps worth noting that the second-to-last of these put the system back in the original broken state. Yay!)
Welp. Brute force and ignorance got me into this mess in the first place... perhaps they can get me out.
dpkg-deb -R ../libc6-amd64_2.15-0ubuntu10.3_i386.deb .
# edit DEBIAN/preinst to remove exit 1 following error quote above
# and also to replace error text with vulgarity
mv libc6-amd64_2.15-0ubuntu10.3_i386.deb libc6-amd64_2.15-0ubuntu10.3_i386.deb.real
dpkg-deb -b libc6-amd64_2.15-0ubuntu10.3_i386
rm -rf libc6-amd64_2.15-0ubuntu10.3_i386
dpkg -i libc6-amd64_2.15-0ubuntu10.3_i386.deb
Against all odds, that seemed to work.
Updated 2012-Feb-01 by DGH: The dpkg-deb command has no -B option. Corrected to -b. Thanks, Scott.