tdnf/common/lock.c

262 lines
4.4 KiB
C

/*
* Copyright (C) 2021-2023 VMware, Inc. All Rights Reserved.
*
* Licensed under the GNU Lesser General Public License v2.1 (the "License");
* you may not use this file except in compliance with the License. The terms
* of the License are located in the COPYING file of this distribution.
*/
#include "includes.h"
static void tdnflock_free(tdnflock lock);
static void tdnflock_release(tdnflock lock);
static int tdnflock_acquire(tdnflock lock, int mode);
static tdnflock tdnflock_new(const char *lock_path, const char *descr);
static tdnflock tdnflock_new(const char *lock_path, const char *descr)
{
mode_t oldmask;
uint32_t dwErr = 0;
tdnflock lock = NULL;
if (IsNullOrEmptyString(lock_path) || IsNullOrEmptyString(descr))
{
goto error;
}
dwErr = TDNFAllocateMemory(1, sizeof(*lock), (void *)&lock);
if (dwErr)
{
goto error;
}
oldmask = umask(022);
lock->fd = open(lock_path, O_RDWR|O_CREAT, 0644);
(void) umask(oldmask);
if (lock->fd < 0)
{
if (errno == EACCES)
{
lock->fd = open(lock_path, O_RDONLY);
}
if (lock->fd < 0)
{
TDNF_SAFE_FREE_MEMORY(lock);
return NULL;
}
else
{
lock->openmode = TDNFLOCK_READ;
}
}
else
{
lock->openmode = TDNFLOCK_WRITE | TDNFLOCK_READ;
/* Write out PID into lock file */
char pid_buffer[128] = {0};
int rsnpf;
rsnpf = snprintf(pid_buffer, sizeof(pid_buffer), "%ld\n", (long) getpid());
if (rsnpf > 0)
{
int wr;
wr = write(lock->fd, pid_buffer, strlen(pid_buffer));
if (wr == 0)
{
sync();
}
}
}
lock->fdrefs = 1;
dwErr = TDNFAllocateString(lock_path, &lock->path);
BAIL_ON_TDNF_ERROR(dwErr);
dwErr = TDNFAllocateString(descr, &lock->descr);
BAIL_ON_TDNF_ERROR(dwErr);
return lock;
error:
tdnflock_free(lock);
return lock;
}
static void tdnflock_free(tdnflock lock)
{
if (lock && --lock->fdrefs == 0)
{
TDNF_SAFE_FREE_MEMORY(lock->path);
TDNF_SAFE_FREE_MEMORY(lock->descr);
if (lock->fd >= 0)
{
(void) close(lock->fd);
}
TDNF_SAFE_FREE_MEMORY(lock);
}
}
static int tdnflock_acquire(tdnflock lock, int mode)
{
int res = 0;
if (!lock || mode < 0)
{
goto end;
}
if (!(mode & lock->openmode))
return res;
if (lock->fdrefs > 1) {
res = 1;
} else {
int cmd;
struct flock info;
if (mode & TDNFLOCK_WAIT)
{
cmd = F_SETLKW;
}
else
{
cmd = F_SETLK;
}
if (mode & TDNFLOCK_READ)
{
info.l_type = F_RDLCK;
}
else
{
info.l_type = F_WRLCK;
}
info.l_whence = SEEK_SET;
info.l_start = 0;
info.l_len = 0;
info.l_pid = 0;
if (fcntl(lock->fd, cmd, &info) != -1)
{
res = 1;
}
}
lock->fdrefs += res;
end:
return res;
}
static void tdnflock_release(tdnflock lock)
{
if (!lock || lock->fdrefs <= 1)
{
return;
}
if (--lock->fdrefs == 1)
{
struct flock info;
info.l_type = F_UNLCK;
info.l_whence = SEEK_SET;
info.l_start = 0;
info.l_len = 0;
info.l_pid = 0;
(void) fcntl(lock->fd, F_SETLK, &info);
}
}
/* External interface */
tdnflock tdnflockNew(const char *lock_path, const char *descr)
{
tdnflock lock = NULL;
if (IsNullOrEmptyString(lock_path) || IsNullOrEmptyString(descr))
{
goto end;
}
lock = tdnflock_new(lock_path, descr);
if (!lock)
{
pr_err("can't create %s lock on %s (%s)\n",
descr, lock_path, strerror(errno));
}
end:
return lock;
}
int tdnflockAcquire(tdnflock lock)
{
int locked = 0; /* assume failure */
if (!lock)
{
return locked;
}
locked = tdnflock_acquire(lock, TDNFLOCK_WRITE);
if (!locked && (lock->openmode & TDNFLOCK_WRITE))
{
pr_crit("waiting for %s lock on %s\n", lock->descr, lock->path);
locked = tdnflock_acquire(lock, (TDNFLOCK_WRITE|TDNFLOCK_WAIT));
}
if (!locked)
{
pr_err("can't create %s lock on %s (%s)\n", lock->descr, lock->path,
strerror(errno));
}
return locked;
}
void tdnflockRelease(tdnflock lock)
{
if (lock)
{
tdnflock_release(lock);
}
}
tdnflock tdnflockNewAcquire(const char *lock_path, const char *descr)
{
tdnflock lock = NULL;
if (IsNullOrEmptyString(lock_path) || IsNullOrEmptyString(descr))
{
goto end;
}
lock = tdnflockNew(lock_path, descr);
if (!tdnflockAcquire(lock))
{
lock = tdnflockFree(lock);
}
end:
return lock;
}
tdnflock tdnflockFree(tdnflock lock)
{
if (lock)
{
tdnflock_release(lock);
tdnflock_free(lock);
}
if (remove(TDNF_INSTANCE_LOCK_FILE))
{
pr_err("WARNING: Unable to remove lockfile(%s)\n",
TDNF_INSTANCE_LOCK_FILE);
}
return NULL;
}