#include <sys/cdefs.h>
#ifndef lint
__RCSID("$NetBSD: dotlock.c,v 1.14 2021/02/17 21:09:39 christos Exp $");
#endif
#include "rcv.h"
#include "extern.h"
#include "sig.h"
#ifndef O_SYNC
#define O_SYNC 0
#endif
static int create_exclusive(const char *);
static int
create_exclusive(const char *fname)
{
char path[MAXPATHLEN], hostname[MAXHOSTNAMELEN + 1];
const char *ptr;
struct timeval tv;
pid_t pid;
size_t ntries, cookie;
int fd, serrno;
struct stat st;
(void)gettimeofday(&tv, NULL);
(void)gethostname(hostname, sizeof(hostname));
hostname[sizeof(hostname) - 1] = '\0';
pid = getpid();
cookie = pid ^ tv.tv_usec;
if ((ptr = strrchr(fname, '/')) == NULL)
ptr = fname;
else
ptr++;
(void)snprintf(path, sizeof(path), "%.*s.%s.%lx",
(int)(ptr - fname), fname, hostname, (u_long)cookie);
for (ntries = 0; ; ntries++) {
fd = open(path,
O_WRONLY|O_CREAT|O_TRUNC|O_EXCL|O_SYNC|O_CLOEXEC, 0);
if (fd != -1) {
(void)close(fd);
break;
}
else if (errno == EEXIST && ntries < 5)
continue;
else
return -1;
}
if (link(path, fname) == -1)
goto bad;
if (stat(path, &st) == -1)
goto bad;
(void)unlink(path);
if (st.st_nlink != 2) {
errno = EEXIST;
return -1;
}
return 0;
bad:
serrno = errno;
(void)unlink(path);
errno = serrno;
return -1;
}
PUBLIC int
dot_lock(const char *fname, int pollinterval, FILE *fp, const char *msg)
{
char path[MAXPATHLEN];
sigset_t nset, oset;
int retval;
(void)sigemptyset(&nset);
(void)sigaddset(&nset, SIGHUP);
(void)sigaddset(&nset, SIGINT);
(void)sigaddset(&nset, SIGQUIT);
(void)sigaddset(&nset, SIGTERM);
(void)sigaddset(&nset, SIGTTIN);
(void)sigaddset(&nset, SIGTTOU);
(void)sigaddset(&nset, SIGTSTP);
(void)sigaddset(&nset, SIGCHLD);
(void)snprintf(path, sizeof(path), "%s.lock", fname);
retval = -1;
for (;;) {
sig_check();
(void)sigprocmask(SIG_BLOCK, &nset, &oset);
if (create_exclusive(path) != -1) {
(void)sigprocmask(SIG_SETMASK, &oset, NULL);
retval = 0;
break;
}
else
(void)sigprocmask(SIG_SETMASK, &oset, NULL);
if (errno != EEXIST)
break;
if (fp && msg)
(void)fputs(msg, fp);
if (pollinterval) {
if (pollinterval == -1) {
errno = EEXIST;
break;
}
(void)sleep((unsigned int)pollinterval);
}
}
sig_check();
return retval;
}
PUBLIC void
dot_unlock(const char *fname)
{
char path[MAXPATHLEN];
(void)snprintf(path, sizeof(path), "%s.lock", fname);
(void)unlink(path);
}