On Wed, Aug 17, 2005, Ralf S. Engelschall wrote:
>On Wed, Aug 17, 2005, Bill Campbell wrote:
...
>> I had posted a suggestion several month ago recommending that we implement
>> processing locking on programs like findutils which may run for extended
>> periods of time.  We often use the ``shlock'' program from the inn news
>> software to handle this locking in shell scripts.
>>
>> Using lock files like %{l_prefix}/var/$package/run/hourly.lock with shlock
>> is reasonably safe given the minimum time interval is fifteen minutes for
>> quarterly cron jobs.
>
>Good idea. Is there a portable and stand-alone implementation of shlock?
>If not we can hand-craft it with a few lines of C code, of course.
>But perhaps there is already an implementation (outside of INN) which
>has the necessary Autoconf stuff in place to be able to use fcntl(2),
>flock(2) and as a fallback UUCP-style lock files.

I'm attaching the version that I'm using which is very slightly modified
from inn to get it to compile standalone.

This doesn't use fancy locking, but creates a standard file with the suffix
.lock containing the pid of the locking process.  It is designed to be used
from standard shell scripts, and I've never had any problems with it on
cron jobs which typically run infrequently enough that they don't have to
worry about race conditions.  It also is compatible with other locking
programs including the perl LockFile::Simple from CPAN.

Bill
--
INTERNET:   [EMAIL PROTECTED]  Bill Campbell; Celestial Systems, Inc.
UUCP:               camco!bill  PO Box 820; 6641 E. Mercer Way
FAX:            (206) 232-9186  Mercer Island, WA 98040-0820; (206) 236-1676
URL: http://www.celestial.com/

A government which robs Peter to pay Paul can always depend on the support
of Paul -- George Bernard Shaw
/*  $Revision: 3.3 $
**
**  Produce reliable locks for shell scripts, by Peter Honeyman as told
**  to Rich $alz.
*/
#include <strings.h>
/* #include "configdata.h" */
#include <errno.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <getopt.h>

#define PID_T int
#define POINTER void*
#define SIZE_T size_t
#define NORETURN void

private bool    BinaryLock;
private char    CANTUNLINK[] = "Can't unlink \"%s\", %s\n";
private char    CANTOPEN[] = "Can't open \"%s\", %s\n";


/*
**  See if the process named in an existing lock still exists by
**  sending it a null signal.
*/
private bool
ValidLock(name, JustChecking)
    char                *name;
    bool                JustChecking;
{
    register int        fd;
    register int        i;
    PID_T               pid;
    char                buff[BUFSIZ];

    /* Open the file. */
    if ((fd = open(name, O_RDONLY)) < 0) {
        if (JustChecking)
            return FALSE;
        (void)fprintf(stderr, CANTOPEN, name, strerror(errno));
        return TRUE;
    }

    /* Read the PID that is written there. */
    if (BinaryLock) {
        if (read(fd, (char *)&pid, sizeof pid) != sizeof pid) {
            (void)close(fd);
            return FALSE;
        }
    }
    else {
        if ((i = read(fd, buff, sizeof buff - 1)) <= 0) {
            (void)close(fd);
            return FALSE;
        }
        buff[i] = '\0';
        pid = (PID_T) atol(buff);
    }
    (void)close(fd);
    if (pid <= 0)
        return FALSE;

    /* Send the signal. */
    if (kill(pid, 0) < 0 && errno == ESRCH)
        return FALSE;

    /* Either the kill worked, or we're optimistic about the error code. */
    return TRUE;
}


/*
**  Unlink a file, print a message on error, and exit.
*/
private NORETURN
UnlinkAndExit(name, x)
    char        *name;
    int         x;
{
    if (unlink(name) < 0)
        (void)fprintf(stderr, CANTUNLINK, name, strerror(errno));
    exit(x);
}


/*
**  Print a usage message and exit.
*/
private NORETURN
Usage()
{
    (void)fprintf(stderr, "Usage: shlock [-u|-b] -f file -p pid\n");
    exit(1);
    /* NOTREACHED */
}


int
main(ac, av)
    int                 ac;
    char                *av[];
{
    register int        i;
    register char       *p;
    register int        fd;
    char                tmp[BUFSIZ];
    char                buff[BUFSIZ];
    char                *name;
    PID_T               pid;
    bool                ok;
    bool                JustChecking;

    /* Set defaults. */
    pid = 0;
    name = NULL;
    JustChecking = FALSE;
    /* (void)umask(NEWSUMASK); */

    /* Parse JCL. */
    while ((i = getopt(ac, av, "bcup:f:")) != EOF)
        switch (i) {
        default:
            Usage();
            /* NOTREACHED */
        case 'b':
        case 'u':
            BinaryLock = TRUE;
            break;
        case 'c':
            JustChecking = TRUE;
            break;
        case 'p':
            pid = (PID_T) atol(optarg);
            break;
        case 'f':
            name = optarg;
            break;
        }
    ac -= optind;
    av += optind;
    if (ac || pid == 0 || name == NULL)
        Usage();

    /* Create the temp file in the same directory as the destination. */
    if ((p = strrchr(name, '/')) != NULL) {
        *p = '\0';
        (void)sprintf(tmp, "%s/shlock%ld", name, (long)getpid());
        *p = '/';
    }
    else
        (void)sprintf(tmp, "shlock%ld", (long)getpid());

    /* Loop until we can open the file. */
    while ((fd = open(tmp, O_RDWR | O_CREAT | O_EXCL, 0644)) < 0)
        switch (errno) {
        default:
            /* Unknown error -- give up. */
            (void)fprintf(stderr, CANTOPEN, tmp, strerror(errno));
            exit(1);
        case EEXIST:
            /* If we can remove the old temporary, retry the open. */
            if (unlink(tmp) < 0) {
                (void)fprintf(stderr, CANTUNLINK, tmp, strerror(errno));
                exit(1);
            }
            break;
        }

    /* Write the process ID. */
    if (BinaryLock)
        ok = write(fd, (POINTER)&pid, (SIZE_T)sizeof pid) == sizeof pid;
    else {
        (void)sprintf(buff, "%ld\n", (long) pid);
        i = strlen(buff);
        ok = write(fd, (POINTER)buff, (SIZE_T)i) == i;
    }
    if (!ok) {
        (void)fprintf(stderr, "Can't write PID to \"%s\", %s\n",
            tmp, strerror(errno));
        (void)close(fd);
        UnlinkAndExit(tmp, 1);
    }

    (void)close(fd);

    /* Handle the "-c" flag. */
    if (JustChecking) {
        if (ValidLock(name, TRUE))
            UnlinkAndExit(tmp, 1);
        UnlinkAndExit(tmp, 0);
    }

    /* Try to link the temporary to the lockfile. */
    while (link(tmp, name) < 0)
        switch (errno) {
        default:
            /* Unknown error -- give up. */
            (void)fprintf(stderr, "Can't link \"%s\" to \"%s\", %s\n",
                    tmp, name, strerror(errno));
            UnlinkAndExit(tmp, 1);
            /* NOTREACHED */
        case EEXIST:
            /* File exists; if lock is valid, give up. */
            if (ValidLock(name, FALSE))
                UnlinkAndExit(tmp, 1);
            if (unlink(name) < 0) {
                (void)fprintf(stderr, CANTUNLINK, name, strerror(errno));
                UnlinkAndExit(tmp, 1);
            }
        }

    UnlinkAndExit(tmp, 0);
    /* NOTREACHED */
}

Reply via email to