/* $Id: shlock.c 6124 2003-01-14 06:03:29Z rra $
**
** Produce reliable locks for shell scripts, by Peter Honeyman as told
** to Rich $alz.
*/
#include "config.h"
#include "clibrary.h"
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/stat.h>
#include "inn/messages.h"
static bool BinaryLock;
/*
** See if the process named in an existing lock still exists by
** sending it a null signal.
*/
static bool
ValidLock(char *name, bool JustChecking)
{
int fd;
int i;
pid_t pid;
char buff[BUFSIZ];
/* Open the file. */
if ((fd = open(name, O_RDONLY)) < 0) {
if (JustChecking)
return false;
syswarn("cannot open %s", name);
return true;
}
/* Read the PID that is written there. */
if (BinaryLock) {
if (read(fd, (char *)&pid, sizeof pid) != sizeof pid) {
close(fd);
return false;
}
}
else {
if ((i = read(fd, buff, sizeof buff - 1)) <= 0) {
close(fd);
return false;
}
buff[i] = '\0';
pid = (pid_t) atol(buff);
}
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.
*/
static void
UnlinkAndExit(char *name, int x)
{
if (unlink(name) < 0)
syswarn("cannot unlink %s", name);
exit(x);
}
/*
** Print a usage message and exit.
*/
static void
Usage(void)
{
fprintf(stderr, "Usage: shlock [-u|-b] -f file -p pid\n");
exit(1);
}
int
main(int ac, char *av[])
{
int i;
char *p;
int fd;
char tmp[BUFSIZ];
char buff[BUFSIZ];
char *name;
pid_t pid;
bool ok;
bool JustChecking;
/* Establish our identity. */
message_program_name = "shlock";
/* Set defaults. */
pid = 0;
name = NULL;
JustChecking = false;
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';
snprintf(tmp, sizeof(tmp), "%s/shlock%ld", name, (long)getpid());
*p = '/';
}
else
snprintf(tmp, sizeof(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. */
sysdie("cannot open %s", tmp);
case EEXIST:
/* If we can remove the old temporary, retry the open. */
if (unlink(tmp) < 0)
sysdie("cannot unlink %s", tmp);
break;
}
/* Write the process ID. */
if (BinaryLock)
ok = write(fd, &pid, sizeof pid) == sizeof pid;
else {
snprintf(buff, sizeof(buff), "%ld\n", (long) pid);
i = strlen(buff);
ok = write(fd, buff, i) == i;
}
if (!ok) {
syswarn("cannot write PID to %s", tmp);
close(fd);
UnlinkAndExit(tmp, 1);
}
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. */
syswarn("cannot link %s to %s", tmp, name);
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) {
syswarn("cannot unlink %s", name);
UnlinkAndExit(tmp, 1);
}
}
UnlinkAndExit(tmp, 0);
/* NOTREACHED */
return 1;
}
syntax highlighted by Code2HTML, v. 0.9.1