freebsd-src/contrib/sendmail/libsmutil/t-lockfile.c
Gregory Neil Shapiro d39bd2c138 Merge commit '850ef5ae11d69ea3381bd310f564f025fc8caea3'
Merge vendor sendmail 8.18.1 into HEAD
2024-01-31 23:53:48 +00:00

434 lines
7.6 KiB
C

/*
* Copyright (c) 2005 Proofpoint, Inc. and its suppliers.
* All rights reserved.
*
* By using this file, you agree to the terms and conditions set
* forth in the LICENSE file which can be found at the top level of
* the sendmail distribution.
*
*/
#include <sm/gen.h>
SM_IDSTR(id, "@(#)$Id: t-lockfile.c,v 1.2 2013-11-22 20:51:50 ca Exp $")
#include <stdlib.h>
#include <stdio.h>
#include <sendmail.h>
#define IOBUFSZ 64
char iobuf[IOBUFSZ];
#define FIRSTLINE "first line\n"
#define LASTLINE "last line\n"
static int noio, chk;
static pid_t pid;
/*
** OPENFILE -- open a file
**
** Parameters:
** owner -- create file?
** filename -- name of file.
** flags -- flags for open(2)
**
** Returns:
** >=0 fd
** <0 on failure.
*/
static int
openfile(owner, filename, flags)
int owner;
char *filename;
int flags;
{
int fd;
if (owner)
flags |= O_CREAT;
fd = open(filename, flags, 0640);
if (fd >= 0)
return fd;
fprintf(stderr, "%d: %ld: owner=%d, open(%s) failed\n",
(int) pid, (long) time(NULL), owner, filename);
return -1;
}
/*
** WRBUF -- write iobuf to fd
**
** Parameters:
** fd -- file descriptor.
**
** Returns:
** ==0 write was ok
** !=0 on failure.
*/
static int
wrbuf(fd)
int fd;
{
int r;
if (noio)
return 0;
r = write(fd, iobuf, sizeof(iobuf));
if (sizeof(iobuf) == r)
return 0;
fprintf(stderr, "%d: %ld: owner=1, write(%s)=fail\n",
(int) pid, (long) time(NULL), iobuf);
return 1;
}
/*
** RDBUF -- read from fd
**
** Parameters:
** fd -- file descriptor.
** xbuf -- expected content.
**
** Returns:
** ==0 read was ok and content matches
** !=0 otherwise
*/
static int
rdbuf(fd, xbuf)
int fd;
const char *xbuf;
{
int r;
if (noio)
return 0;
r = read(fd, iobuf, sizeof(iobuf));
if (sizeof(iobuf) != r)
{
fprintf(stderr, "%d: %ld: owner=0, read()=fail\n",
(int) pid, (long) time(NULL));
return 1;
}
if (strncmp(iobuf, xbuf, strlen(xbuf)))
{
fprintf(stderr, "%d: %ld: owner=0, read=%s expected=%s\n",
(int) pid, (long) time(NULL), iobuf, xbuf);
return 1;
}
return 0;
}
/*
** LOCKTESTWR -- test WR/EX file locking
**
** Parameters:
** owner -- create file?
** filename -- name of file.
** flags -- flags for open(2)
** delay -- how long to keep file locked?
**
** Returns:
** 0 on success
** != 0 on failure.
*/
#define DBGPRINTR(str) \
do \
{ \
fprintf(stderr, "%d: %ld: owner=0, ", (int) pid, \
(long) time(NULL)); \
fprintf(stderr, str, filename, shared ? "RD" : "EX"); \
} while (0)
static int
locktestwr(filename, flags, delay)
char *filename;
int flags;
int delay;
{
int fd;
bool locked;
fd = openfile(1, filename, flags);
if (fd < 0)
return errno;
locked = lockfile(fd, filename, "[owner]", LOCK_EX);
if (!locked)
{
fprintf(stderr, "%d: %ld: owner=1, lock(%s) failed\n",
(int) pid, (long) time(NULL), filename);
return 1;
}
else
fprintf(stderr, "%d: %ld: owner=1, lock(%s) ok\n",
(int) pid, (long) time(NULL), filename);
sm_strlcpy(iobuf, FIRSTLINE, sizeof(iobuf));
if (wrbuf(fd))
return 1;
if (delay > 0)
sleep(delay);
sm_strlcpy(iobuf, LASTLINE, sizeof(iobuf));
if (wrbuf(fd))
return 1;
locked = lockfile(fd, filename, "[owner]", LOCK_UN);
if (!locked)
{
fprintf(stderr, "%d: %ld: owner=1, unlock(%s) failed\n",
(int) pid, (long) time(NULL), filename);
return 1;
}
fprintf(stderr, "%d: %ld: owner=1, unlock(%s) done\n",
(int) pid, (long) time(NULL), filename);
if (fd > 0)
{
close(fd);
fd = -1;
}
return 0;
}
/*
** CHKLCK -- check whether fd is locked (only for fcntl())
**
** Parameters:
** owner -- create file?
** filename -- name of file.
** flags -- flags for open(2)
** delay -- how long to keep file locked?
**
** Returns:
** 0 if not locked
** >0 pid of process which holds a WR lock
** <0 error
*/
static long
chklck(fd)
int fd;
{
#if !HASFLOCK
int action, i;
struct flock lfd;
(void) memset(&lfd, '\0', sizeof lfd);
lfd.l_type = F_RDLCK;
action = F_GETLK;
while ((i = fcntl(fd, action, &lfd)) < 0 && errno == EINTR)
continue;
if (i < 0)
return (long)i;
if (F_WRLCK == lfd.l_type)
return (long)lfd.l_pid;
return 0L;
#else /* !HASFLOCK */
fprintf(stderr, "%d: %ld: flock(): no lock test\n",
(int) pid, (long) time(NULL));
return -1L;
#endif /* !HASFLOCK */
}
/*
** LOCKTESTRD -- test file locking for reading
**
** Parameters:
** filename -- name of file.
** flags -- flags for open(2)
** delay -- how long is file locked by owner?
** shared -- LOCK_{EX/SH}
**
** Returns:
** 0 on success
** != 0 on failure.
*/
static int
locktestrd(filename, flags, delay, shared)
char *filename;
int flags;
int delay;
int shared;
{
int fd, cnt;
int lt;
bool locked;
fd = openfile(0, filename, flags);
if (fd < 0)
return errno;
if (chk)
{
long locked;
locked = chklck(fd);
if (locked > 0)
fprintf(stderr, "%d: %ld: file=%s status=locked pid=%ld\n",
(int) pid, (long) time(NULL), filename, locked);
else if (0 == locked)
fprintf(stderr, "%d: %ld: file=%s status=not_locked\n",
(int) pid, (long) time(NULL), filename);
else
fprintf(stderr, "%d: %ld: file=%s status=unknown\n",
(int) pid, (long) time(NULL), filename);
goto end;
}
if (shared)
lt = LOCK_SH;
else
lt = LOCK_EX;
for (cnt = 0; cnt < delay - 2; cnt++)
{
/* try to get lock: should fail (nonblocking) */
locked = lockfile(fd, filename, "[client]", lt|LOCK_NB);
if (locked)
{
DBGPRINTR("lock(%s)=%s succeeded\n");
return 1;
}
sleep(1);
}
if (delay > 0)
sleep(2);
locked = lockfile(fd, filename, "[client]", lt);
if (!locked)
{
DBGPRINTR("lock(%s)=%s failed\n");
return 1;
}
DBGPRINTR("lock(%s)=%s ok\n");
if (rdbuf(fd, FIRSTLINE))
return 1;
if (rdbuf(fd, LASTLINE))
return 1;
sleep(1);
locked = lockfile(fd, filename, "[client]", LOCK_UN);
if (!locked)
{
DBGPRINTR("unlock(%s)=%s failed\n");
return 1;
}
DBGPRINTR("unlock(%s)=%s done\n");
end:
if (fd > 0)
{
close(fd);
fd = -1;
}
return 0;
}
/*
** USAGE -- show usage
**
** Parameters:
** prg -- name of program
**
** Returns:
** nothing.
*/
static void
usage(prg)
const char *prg;
{
fprintf(stderr, "usage: %s [options]\n"
"-f filename use filename\n"
"-i do not perform I/O\n"
"-n do not try non-blocking locking first\n"
"-R only start reader process\n"
"-r use shared locking for reader\n"
"-s delay sleep delay seconds before unlocking\n"
"-W only start writer process\n"
#if !HASFLOCK
"uses fcntl()\n"
#else
"uses flock()\n"
#endif
, prg);
}
int
main(argc, argv)
int argc;
char *argv[];
{
int ch, delay, r, status, flags, shared, nb, reader, writer;
char *filename;
pid_t fpid;
extern char *optarg;
delay = 5;
filename = "testlock";
flags = O_RDWR;
shared = nb = noio = reader = writer = chk = 0;
#define OPTIONS "cf:inRrs:W"
while ((ch = getopt(argc, argv, OPTIONS)) != -1)
{
switch ((char) ch)
{
case 'c':
chk = 1;
break;
case 'f':
filename = optarg;
break;
case 'i':
noio = 1;
break;
case 'n':
nb = 0;
break;
case 'R':
reader = 1;
break;
case 'r':
shared = 1;
break;
case 's':
delay = atoi(optarg);
break;
case 'W':
writer = 1;
break;
default:
usage(argv[0]);
exit(69);
break;
}
}
fpid = -1;
if (0 == reader && 0 == writer && (fpid = fork()) < 0)
{
perror("fork failed\n");
return 1;
}
r = 0;
if (reader || fpid == 0)
{
/* give the parent the chance to set up data */
pid = getpid();
sleep(1);
r = locktestrd(filename, flags, nb ? delay : 0, shared);
}
if (writer || fpid > 0)
{
fpid = getpid();
r = locktestwr(filename, flags, delay);
(void) wait(&status);
}
/* (void) unlink(filename); */
return r;
}