#!/usr/bin/python

import sys
import os
import tempfile
import subprocess
import shutil
import errno
import fcntl

def rm_fr(d):
    try:
        shutil.rmtree(d)
    except OSError, e:
        if e.errno != errno.ENOENT:
            raise

def install(storage, tarball):
    # Make a randomly named directory within the storage directory.
    if not os.path.exists(storage):
        os.makedirs(storage)
    dst = tempfile.mkdtemp(dir=storage)
    os.chmod(dst, 0755)
    os.chdir(dst)

    # Untar the includes to the directory.
    bzip2 = subprocess.Popen(['bzip2', '-cd', tarball], stdout=subprocess.PIPE)
    tar = subprocess.Popen(['tar', 'xvf', '-'], stdin=bzip2.stdout)
    bzip2.stdout.close()
    x = bzip2.wait()
    if x != 0:
        sys.stderr.write('bzip2 exited with exit code %d\n' % x)
    x = tar.wait()
    if x != 0:
        sys.stderr.write('tar exited with exit code %d\n' % x)

    # Find all directories named 'include', but not those which have a parent
    # directory named include.  Make symlinks where the compiler expects header
    # files.
    for (dirname, dirs, files) in os.walk('.'):
        if dirname[dirname.find('include'):] == 'include':
            # Figure out the symlink target and name.
            dirname = dirname[len('./'):]
            dst_path = os.path.join(dst, dirname)
            usr_path = os.path.join('/usr', dirname)
            # Delete an existing symlink.
            try:
                os.unlink(usr_path)
            except OSError, e:
                if e.errno != errno.ENOENT:
                    raise
            # Make the symlink.
            os.symlink(dst_path, usr_path)

def lock(tarball):
    # XXX: On FreeBSD, write access is required or LOCK_EX is converted into
    # LOCK_SH. :(
    tarball_f = file(tarball, 'r+')
    try:
        fcntl.lockf(tarball_f.fileno(), fcntl.LOCK_EX | fcntl.LOCK_NB)
    except IOError, e:
        if e.errno == errno.EACCES or e.errno == errno.EAGAIN:
            return None
    return tarball_f

def unlock(lockobj):
    lockobj.close()

def main():
    # Constants
    storage = '/var/crash/isi_include'
    tarball = '/var/crash/tools/include.tar.bz2'
    
    lockobj = lock(tarball)
    if not lockobj:
        sys.stderr.write('Multiple isi_includes are running!\n')
        sys.exit(0)
    
    if os.path.exists('/usr/include'):
        sys.stdout.write('/usr/include is already installed.\n')
    else:
        rm_fr(storage)
        install(storage, tarball)
    
    unlock(lockobj)

if __name__ == '__main__':
    main()
