#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/wait.h>

#include "file_lock.h"

int main(int argc, char *const argv[]) {
    int wait_for_lock = 1;
    int exit_status_on_lock_failure = 1;
    int program_holds_lock = 1;
    int lock_fd;
    int option;
    const char *lock_file_name;
    int (*locker)(int);
    int child_pid;
    char *const *new_argv;
    int child_status;

    while ((option = getopt(argc, argv, "nNxXcC")) != -1) {
        switch (option) {
            case 'n':
                wait_for_lock = 0;
                break;
            case 'N':
                wait_for_lock = 1;
                break;
            case 'x':
                exit_status_on_lock_failure = 0;
                break;
            case 'X':
                exit_status_on_lock_failure = 1;
                break;
            case 'c':
                program_holds_lock = 0;
                break;
            case 'C':
                program_holds_lock = 1;
                break;
        }
    }

    if (optind + 1 >= argc) {
        fprintf(stderr,
            "Usage: %s lock_file command...\n", argv[0]);
        exit(1);
    }

    lock_file_name = argv[optind];
    new_argv = argv + optind + 1;

    lock_fd = open(lock_file_name, O_WRONLY|O_CREAT|O_NONBLOCK, 0600);
    if (lock_fd == -1) {
        perror("open of lock file failed");
        exit(exit_status_on_lock_failure);
    }

    /* Choose a lock function */
    locker = wait_for_lock ? file_lock : file_trylock;

    file_lock_init();
    if (locker(lock_fd) != 0) {
        perror("Could not lock file");
        exit(exit_status_on_lock_failure);
    }

    /* If we must prevent the program from holding the lock fd, we need
     * to fork-exec the process, close the fd, and wait for its
     * completion while we hold the lock */
    if (!program_holds_lock) {
        child_pid = fork();
        if (child_pid < 0) {
            perror("fork");
            exit(1);
        }
        else if (child_pid > 0) {
            /* Wait for the child to complete */
            if (waitpid(child_pid, &child_status, 0) < 0) {
                perror("Error waiting for child");
                exit(1);
            }
            exit(WEXITSTATUS(child_status));
        }
        else {
            /* I'm the lockless child. Close the lock and run the
             * program */
            close(lock_fd);
        }
    }
    execvp(*new_argv, new_argv);
    perror("execvp");
    exit(1);
}


syntax highlighted by Code2HTML, v. 0.9.1