#!/usr/bin/env python
from __future__ import with_statement
import sys, os, subprocess
import fcntl, errno
import argparse

def touch(fname): 
    open(fname, 'wa').close()

def main():
    parser = argparse.ArgumentParser(description='Add locking or timeouts to scripts.')
    parser.add_argument('-l', '--lock', dest='lock_file', action='store', help='file to lock', required=True)
    parser.add_argument('-e', '--exit', dest='exit_code', action='store', type=int, choices=xrange(0,255), metavar="EXIT_CODE", help="exit code to return if lock can't be granted", default=1)
    parser.add_argument('-s', '--shell', dest='shell', action='store_true', help="execute commands via shell !!IS DANGEROUS!!", default=False)
    parser.add_argument('-v', '--verbose', dest='verbose', action='store_true', help="verbose errors", default=False)
    parser.add_argument('-w', '--wait', dest='wait', action='store_true', help="wait to secure lock", default=False)
    parser.add_argument('command', action='store', nargs='*', help='command to run')

    args = parser.parse_args()

    try:
        touch(args.lock_file)

        with open(args.lock_file, "r") as f:
            if args.wait:
                flags = fcntl.LOCK_EX
            else:
                flags = fcntl.LOCK_EX | fcntl.LOCK_NB

            fcntl.flock(f.fileno(), flags)

            if args.shell:
                command = subprocess.list2cmdline(args.command)
            else:
                command = args.command

            subprocess.call(command, shell=args.shell)

            fcntl.flock(f.fileno(), fcntl.LOCK_UN)
    except IOError, e:
        print e
        if e.errno == 35:
            if args.verbose: print >> sys.stderr, "Can't secure lock file: '%s'" % args.lock_file
            return args.exit_code
        elif e.errno == errno.EACCES:
            if args.verbose: print >> sys.stderr, "EACCES: Incorrect permissions to modify lockfile '%s'." % args.lock_file
            return 255
        elif e.errno == errno.ENOENT:
            if args.verbose: print >> sys.stderr, "ENOENT: Can't access lock file's directory '%s'." % os.path.dirname(args.lock_file)
            return 255
        elif e.errno == errno.EISDIR:
            if args.verbose: print >> sys.stderr, "EISDIR: Can't lock directories '%s'." % os.path.dirname(args.lock_file)
            return 255
        else:
            if args.verbose: print >> sys.stderr, e
            return 255

    return 0


if __name__ == "__main__":
    sys.exit(main())
