4.4BSD/usr/src/contrib/news/inn/backends/shlock.c
/* $Revision: 1.6 $
**
** Produce reliable locks for shell scripts, by Peter Honeyman as told
** to Rich $alz.
*/
#include "configdata.h"
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/types.h>
#include "clibrary.h"
STATIC BOOL BinaryLock;
STATIC char CANTUNLINK[] = "Can't unlink \"%s\", %s\n";
STATIC 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.
*/
STATIC BOOL
ValidLock(name, JustChecking)
char *name;
BOOL JustChecking;
{
register int fd;
register int i;
int pid;
char buff[BUFSIZ];
/* Open the file. */
if ((fd = open(name, O_RDONLY)) < 0) {
if (!JustChecking)
(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 = atoi(buff);
}
(void)close(fd);
if (pid <= 0)
return FALSE;
/* Send the signal. */
if (kill((PID_T)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 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.
*/
STATIC 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;
int 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 = atoi(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, "%d\n", 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 */
}