/* $Cambridge: hermes/src/prayer/lib/mymutex_file.c,v 1.3 2008/09/16 09:59:57 dpc22 Exp $ */

/************************************************
 *    Prayer - a Webmail Interface              *
 ************************************************/

/* Copyright (c) University of Cambridge 2000 - 2008 */
/* See the file NOTICE for conditions of use and distribution. */

struct mymutex {
    struct pool *pool;
    char *lockname;
    int   lockfd;
};

struct mymutex *
mymutex_create(struct pool *pool, char *lockdir, char *name)
{
    struct mymutex *mymutex = pool_alloc(pool, sizeof(struct mymutex));

    mymutex->pool   = pool;
    mymutex->lockfd = -1;

    if (lockdir && (name[0] != '/'))
        mymutex->lockname = pool_strcat3(pool, lockdir, "/", name);
    else
        mymutex->lockname = pool_strdup(pool, name);

    return(mymutex);
}

void
mymutex_free(struct mymutex *mymutex)
{
    if (mymutex->lockfd >= 0) close(mymutex->lockfd);
    mymutex->lockfd = (-1);

    if (!mymutex->pool) {
        free(mymutex->lockname);
        free(mymutex);
    }
}

BOOL
mymutex_slave_init(struct mymutex *mymutex)
{

    if ((mymutex->lockfd=open(mymutex->lockname, O_CREAT|O_WRONLY, 0660)) < 0)
        log_fatal("Couldn't open lock file: \"%s\": %s",
                  mymutex->lockname, strerror(errno));

    return(T);
}

void
mymutex_slave_cleanup(struct mymutex *mymutex)
{
    if (mymutex->lockfd >= 0) close(mymutex->lockfd);
    mymutex->lockfd = -1;
}

static BOOL mymutex_alarm_value = NIL;

static void
mymutex_alarm(int arg)
{
    mymutex_alarm_value = T;
}

BOOL
mymutex_on(struct mymutex *mymutex, int timeout)
{
    int rc;

    if (timeout > 0) {
        if (!os_signal_alarm_init(mymutex_alarm))
            log_fatal("[mymutex_on] Failed to set alarm signal");

        alarm(timeout);
    }

    mymutex_alarm_value = NIL;
    do {
        rc = os_lock_exclusive_allow_break(mymutex->lockfd);
    } while (!mymutex_alarm_value && (rc == NIL) && (errno == EINTR));

    if (timeout) {
        alarm(0);
        os_signal_alarm_clear();
    }

    if ((rc == NIL) && (errno != EINTR))
        log_fatal("[mymutex_on()] failed to release lock: %s",
                  strerror(errno));

    return((mymutex_alarm_value) ? NIL : T);
}

BOOL
mymutex_off(struct mymutex *mymutex)
{
    if (!os_lock_release(mymutex->lockfd))
        log_fatal("[mymutex_off()] failed to release lock: %s",
                  strerror(errno));

    return(T);
}

