rpm/lib/rpmlock.c

160 lines
3.3 KiB
C

#include "system.h"
#include <rpmlib.h>
#include <rpmmacro.h>
#include "rpmts.h"
#include "rpmlock.h"
#include "debug.h"
/* Internal interface */
#define RPMLOCK_PATH LOCALSTATEDIR "/lock/rpm/transaction"
/*@unchecked@*/ /*@observer@*/
static const char * rpmlock_path_default = "%{?_rpmlock_path}";
/*@unchecked@*/
static const char * rpmlock_path = NULL;
enum {
RPMLOCK_READ = 1 << 0,
RPMLOCK_WRITE = 1 << 1,
RPMLOCK_WAIT = 1 << 2,
};
typedef struct {
int fd;
int openmode;
} * rpmlock;
/*@null@*/
static rpmlock rpmlock_new(/*@unused@*/ const char *rootdir)
/*@globals fileSystem @*/
/*@modifies fileSystem @*/
{
rpmlock lock = (rpmlock) malloc(sizeof(*lock));
/* XXX oneshot to determine path for fcntl lock. */
if (rpmlock_path == NULL) {
const char * t = rpmGenPath(rootdir, rpmlock_path_default, NULL);
if (t == NULL || *t == '\0' || *t == '%')
t = strdup(RPMLOCK_PATH);
rpmlock_path = xstrdup(t);
t = _free(t);
}
if (lock != NULL) {
mode_t oldmask = umask(022);
lock->fd = open(rpmlock_path, O_RDWR|O_CREAT, 0644);
(void) umask(oldmask);
/*@-branchstate@*/
if (lock->fd == -1) {
lock->fd = open(rpmlock_path, O_RDONLY);
if (lock->fd == -1) {
free(lock);
lock = NULL;
} else {
/*@-nullderef@*/ /* XXX splint on crack */
lock->openmode = RPMLOCK_READ;
/*@=nullderef@*/
}
} else {
/*@-nullderef@*/ /* XXX splint on crack */
lock->openmode = RPMLOCK_WRITE | RPMLOCK_READ;
/*@=nullderef@*/
}
/*@=branchstate@*/
}
/*@-compdef@*/
return lock;
/*@=compdef@*/
}
static void rpmlock_free(/*@only@*/ /*@null@*/ rpmlock lock)
/*@globals fileSystem, internalState @*/
/*@modifies lock, fileSystem, internalState @*/
{
if (lock) {
(void) close(lock->fd);
free(lock);
}
}
static int rpmlock_acquire(/*@null@*/ rpmlock lock, int mode)
/*@*/
{
int res = 0;
if (lock && (mode & lock->openmode)) {
struct flock info;
int cmd;
if (mode & RPMLOCK_WAIT)
cmd = F_SETLKW;
else
cmd = F_SETLK;
if (mode & RPMLOCK_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;
}
return res;
}
static void rpmlock_release(/*@null@*/ rpmlock lock)
/*@globals internalState @*/
/*@modifies internalState @*/
{
if (lock) {
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 */
void *rpmtsAcquireLock(rpmts ts)
{
const char *rootDir = rpmtsRootDir(ts);
rpmlock lock;
if (!rootDir || rpmtsChrootDone(ts))
rootDir = "/";
lock = rpmlock_new(rootDir);
/*@-branchstate@*/
if (!lock) {
rpmMessage(RPMMESS_ERROR, _("can't create transaction lock on %s\n"), rpmlock_path);
} else if (!rpmlock_acquire(lock, RPMLOCK_WRITE)) {
if (lock->openmode & RPMLOCK_WRITE)
rpmMessage(RPMMESS_WARNING,
_("waiting for transaction lock on %s\n"), rpmlock_path);
if (!rpmlock_acquire(lock, RPMLOCK_WRITE|RPMLOCK_WAIT)) {
rpmMessage(RPMMESS_ERROR,
_("can't create transaction lock on %s\n"), rpmlock_path);
rpmlock_free(lock);
lock = NULL;
}
}
/*@=branchstate@*/
return lock;
}
void rpmtsFreeLock(void *lock)
{
rpmlock_release((rpmlock)lock); /* Not really needed here. */
rpmlock_free((rpmlock)lock);
}