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 */ }