Ubuntu 12.04.1 LTS: Recovering from a Broken libc

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?

  1. 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.
  2. Still on the other system, unpack the contents into a subdirectory (preferrably, without adgering that second system in the process):

    mkdir foo
    cd foo
    dpkg-deb -x ../libc6-amd64_2.15-0ubuntu10.3_i386.deb

  3. Yet again on the other system, archive up the extracted files (and remove the unpacked version):

    tar cvf – . | bzip2 -v9 >../libc6.tar.bz2
    cd ..
    rm -rf foo

  4. Put the archive thus created on a handy Internet-accessible web server.
  5. On my broken system, filled with brokenness, which I broke, launch static-sh.
  6. On the broken system, use wget to fetch the archive and extract in the root directory:

    cd /
    wget http://mywebserver.example.org/some/path/libc6.tar.bz2
    bunzip2 -c libc6.tar.bz2 | tar xvf –

  7. 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!

Okay, whatever.

cd /var/cache/apt/archives
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.

cd /var/cache/apt/archives
mkdir libc6-amd64_2.15-0ubuntu10.3_i386
cd libc6-amd64_2.15-0ubuntu10.3_i386
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
cd ..
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.

By dhenke

Email: dhenke@mythopoeic.org

18 comments

  1. You, sir, are an absolute god-send. I was tearing my hair out before finding this!

    (One thing though, I don’t know about Ubuntu systems but on my Debian rig the last switch for dpkg-deb is -b, not -B)

  2. Scott @#3: Thanks! It’s good to hear that it helped somebody. You were absolutely right about the dpkg-deb option being -b not -B. I have fixed the original text.

  3. hi, dhenke, I came across the same problem
    “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.”
    But it doesn’t work by following your solution.
    Could you help me if you will?

  4. szpliman @#5: I will try to help you, but if what I described above didn’t work, I’m not sure I know what’s wrong.

    First, if your system is running and talking on the network, back up any data you care about before doing anything else. Seriously. Any of the stuff here can go wrong in ways that will necessitate a complete re-install from scratch (which is what you’ll eventually have to do anyway if you can’t solve this any other way).

    When you said you followed my solution, am I right in assuming you mean the part where you unpack the libc package with dpkg-deb -R, edit the DEBIAN/preinst script, then re-package and install?

    If so, what happened when you did so? (If you got an error message, what exactly did it say?)

  5. szpliman @#7: Wow, that’s a lot of information.

    One thing that stood out was in the dpkg -i output when you tried to install your modified package:

    dpkg: error processing /var/cache/apt/archives/libc6_2.15-0ubuntu10.4_amd64.deb (–unpack):
    subprocess new pre-installation script returned error exit status 1

    From that, I suspect a typo in the DEBIAN/preinst script you edited. If you put your original and modified DEBIAN/preinst files on dropbox and leave a link here, I’ll take a look and try to figure out specifically what’s wrong.

    Or, if it would be easier for you, email the files to me at dhenke@mythopoeic.org.

  6. dhenke @#8: I am afraid I don’t remember whether I edited the DEBIAN/preinst script file. And which script do you indicate exactly? Sorry :-(

  7. szpliman @#9: I re-downloaded the libc6_2.15-0ubuntu10.4_amd64.deb from http://security.ubuntu.com/ubuntu/pool/main/e/eglibc/libc6-amd64_2.15-0ubuntu10.3_i386.deb in order to create a before-and-after example of how I changed the DEBIAN/preinst script.

    What I found was that the preinst script has been replaced with a tiny (368-byte) version that doesn’t have the offending check for the C library in an unexpected location.

    So, the solution to your problem may be as simple as:

    wget http://security.ubuntu.com/ubuntu/pool/main/e/eglibc/libc6-amd64_2.15-0ubuntu10.3_i386.deb

    sudo dpkg -i libc6-amd64_2.15-0ubuntu10.3_i386.deb

  8. Sorry… I totally ignore the words.
    # edit DEBIAN/preinst to remove exit 1 following error quote above
    # and also to replace error text with vulgarity
    What a shame

  9. dhenke @#10: Eventually the problem is gone, you’re right. Thank you again. My fault is as #11 says.

  10. Haha, it seems your time is late. BTW, your website is wonderful, I put it in my bookmark :-)

  11. How is it even possible that I committed the same crime as you? I was completely unaware of that neato static-sh. Bless those ubuntu gnomes.

Leave a comment

Your email address will not be published. Required fields are marked *