Repository: borgbackup/borg

Issue:
borg binary: Exception when trying to find/load libc
I was reticent to mention this, since it's a very specific situation, but somebody in IRC suggested opening a bug. Anyhow, I'm attempting to use Borg via a bootable ISO based off of CentOS 6 using the Relax and Recover disaster recovery system.

When attempting to run either 0.28.2 or 0.29.0 from inside of the bootable recovery system I get the Traceback mentioned in the title. I ran a stack trace to see if I was missing a library, but I didn't see anything amiss off-hand. Also running ldd against borg doesn't seem to turn up any missing items.

I don't know much about Python, so I'm not sure what you need in order to assist, but here's the last couple of lines from the Traceback:

```
File "<string>", line 121, in __init__
File "/home/vagrant/.pyenv/versions/3.5.1/lib/python3.5/posixpath.py", line 139, in basename
AttributeError: 'NoneType' object has no attribute 'rfind'
__main__ returned -1
```

More Traceback info can be found here:
[borg_0.29.0_traceback.txt](https://github.com/borgbackup/borg/files/63456/borg_0.29.0_traceback.txt)

Before I found Borg, I was using this setup with Attic and that binary seemed to work okay.



Discussion:
The xattr.py line 31 that causes the fail is:

```
libc = CDLL(find_library('c'), use_errno=True)
```

So it seems it has trouble finding your (g)libc.

I need the following things:
- output of `python3.x /usr/lib/python3.x/ctypes/util.py` (choose x from 5,4,3,2 - whatever works) on that machine
- precise `/sbin/ldconfig -p` output on that machine in text form (no screenshot!). I need that to throw a regex match at it.
- output of `uname` and `uname -a` on that machine
- try to invoke `gcc` and `cc` and `objdump` on that machine - do the commands work?
- the original traceback attached or included in text form in the top post

Please note that dropbox or other imagebin links are bad because they can go away before this bug is solved and thus would render your bug report incomplete.

Also try on that machine (using python 2.5/6/7 or 3.2/3/4/5):

```
$ python
Python 2.7.6 (default, Jun 22 2015, 17:58:13) 
[GCC 4.8.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from ctypes import *
>>> from ctypes.util import find_library

>>> find_library('c')
'libc.so.6'

>>> CDLL('libc.so.6')      #  <-- use the name it outputs above
<CDLL 'libc.so.6', handle 7f5292511a88 at 7f529239cc90>

>>> CDLL(None)
???
```

This is a minimal bootable system for Disaster Recovery, based off of a full CentOS 6 system, so there's no python, cc, gcc, ldconfig or objdump installed on it.

I apologize for the dropbox links, I'll attach the traceback to my original post.

```
ldd /usr/local/bin/borg
    linux-vdso.so.1 =>  (0x00007ffd6b060000)
    libdl.so.2 => /lib64/libdl.so.2 (0x00007f45d6b17000)
    libz.so.1 => /lib64/libz.so.1 (0x00007f45d6901000)
    libc.so.6 => /lib64/libc.so.6 (0x00007f45d656c000)
    /lib64/ld-linux-x86-64.so.2 (0x00007f45d6d1e000)
```

My suspicion is that it might be a bit too minimal then to be generally useful for running stuff.
As /sbin/ldconfig isn't big, maybe it should be just included.

Maybe first try to copy it onto that system manually and check if borg works then. If so, file a ticket at "rear" project.

I can easily include anything else I need to, I just need a little hand holding to figure out what that might be. I thought when I ran "ldd" against borg it would just show me the missing library, but that doesn't seem to be the case.

The binary uses some bootstrap loader to get things going, what ldd shows you is likely what the loader needs. The loader then unpacks all the stuff to /tmp/_ME\* (a directory including all needed python and binary library files) and then invokes the borg main program there. Then, python imports stuff and python's "ctypes" fails to find your libc.

Okay, that makes sense, let me see if I can use pmap or something to figure out what all the requirements are.

I believe I've found the culprit (which you already mentioned), it was simply a matter of _/sbin/ldconfig_ missing. In order to figure this out, I used **strace -f -e trace=process borg** The **-f** was key, in order to capture child process info, which may not have dawned on me without your input above.

Well, we maybe can do a "last resort" trick for that case:

I bet the function that usually finds the library name returns None in such a case.
But if we can do a good guess about the library name (like "libc.so.6" for linux) we could just try that in case we got None and in case we are on Linux.

Whatever you think is appropriate. I just updated my build script to include _/sbin/ldconfig_ in the recovery ISO and all seems well now for my use case.

OS X: libc.dylib (works without abs. path)
netbsd / freebsd: libc.so
openbsd: does not work, no symlinks
linux: libc.so.6


Resolving patch:
diff --git a/borg/xattr.py b/borg/xattr.py
--- a/borg/xattr.py
+++ b/borg/xattr.py
@@ -7,6 +7,9 @@
 from ctypes import CDLL, create_string_buffer, c_ssize_t, c_size_t, c_char_p, c_int, c_uint32, get_errno
 from ctypes.util import find_library
 
+from .logger import create_logger
+logger = create_logger()
+
 
 def is_enabled(path=None):
     """Determine if xattr is enabled on the filesystem
@@ -27,8 +30,28 @@ def get_all(path, follow_symlinks=True):
         if e.errno in (errno.ENOTSUP, errno.EPERM):
             return {}
 
+libc_name = find_library('c')
+if libc_name is None:
+    # find_library didn't work, maybe we are on some minimal system that misses essential
+    # tools used by find_library, like ldconfig, gcc/cc, objdump.
+    # so we can only try some "usual" names for the C library:
+    if sys.platform.startswith('linux'):
+        libc_name = 'libc.so.6'
+    elif sys.platform.startswith(('freebsd', 'netbsd')):
+        libc_name = 'libc.so'
+    elif sys.platform == 'darwin':
+        libc_name = 'libc.dylib'
+    else:
+        msg = "Can't find C library. No fallback known. Try installing ldconfig, gcc/cc or objdump."
+        logger.error(msg)
+        raise Exception(msg)
 
-libc = CDLL(find_library('c'), use_errno=True)
+try:
+    libc = CDLL(libc_name, use_errno=True)
+except OSError as e:
+    msg = "Can't find C library [%s]. Try installing ldconfig, gcc/cc or objdump." % e
+    logger.error(msg)
+    raise Exception(msg)
 
 
 def _check(rv, path=None):


Test patch: