
/* fcntl.c  -- Defines mutexes in terms of read/write locks on files. 
 *             (code is mostly from Stevens: Advanced Programming in the
 *             Unix environment. See from page 367 on.
 *             filerwlock, filerwlockCreate, filelockDelete,
 *             filereadlock, filelongreadlock, filelongunlock, filelockUnlock
 */

#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>

#define MODE (S_IWUSR | S_IRUSR | S_IWGRP | S_IRGRP | S_IWOTH | S_IROTH)
#define FLAGS (O_RDWR | O_CREAT)

int		lock_reg(int, int, int, off_t, int, off_t);

#define	read_lock(fd, offset, whence, len) \
			lock_reg(fd, F_SETLK, F_RDLCK, offset, whence, len)
#define	readw_lock(fd, offset, whence, len) \
			lock_reg(fd, F_SETLKW, F_RDLCK, offset, whence, len)
#define	write_lock(fd, offset, whence, len) \
			lock_reg(fd, F_SETLK, F_WRLCK, offset, whence, len)
#define	writew_lock(fd, offset, whence, len) \
			lock_reg(fd, F_SETLKW, F_WRLCK, offset, whence, len)
#define	un_lock(fd, offset, whence, len) \
			lock_reg(fd, F_SETLK, F_UNLCK, offset, whence, len)

pid_t	lock_test(int, int , off_t , int , off_t );

#define	is_readlock(fd, offset, whence, len) \
			lock_test(fd, F_RDLCK, offset, whence, len)
#define	is_writelock(fd, offset, whence, len) \
			lock_test(fd, F_WRLCK, offset, whence, len)


int lock_reg(int fd, int cmd, int type, off_t offset, int whence, off_t len)
{
  struct flock *lock;
  lock = (struct flock *)malloc(sizeof(struct flock));
  lock->l_type = type;     /* F_RDLCK, F_WRLCK, F_UNLCK */
  lock->l_start = offset;  /* byte offset relative to l_whence */
  lock->l_whence = whence; /* SEEK_SET, SEEK_CUR, SEEK_END */
  lock->l_len = len;       /* #bytes (0 means to EOF) */
  return (fcntl(fd, cmd, lock));
}


pid_t	lock_test(int fd, int type, off_t offset, int whence, off_t len) 
{
  struct flock lock;
  lock.l_type = type;     /* F_RDLCK or F_WRLCK */
  lock.l_start = offset;  /* byte offset relative to l_whence */
  lock.l_whence = whence; /* SEEK_SET, SEEK_CUR, SEEK_END */
  lock.l_len = len;       /* #bytes (0 means to EOF) */
  if (fcntl(fd,F_GETLK,&lock) < 0){
    perror("fcntl"); exit(1);}
  if (lock.l_type == F_UNLCK) 
    return (0);        /* false, region is not locked by another process */
  return (lock.l_pid); /* true, return pid of lock owner */
}

typedef struct {
    int fd;
    int n;} filerwlock;

/* Create N read/write locks and returns the id of this cluster of locks. */
filerwlock * filerwlockCreate(char *filename, int n) {
   filerwlock *fl = (filerwlock *)malloc(sizeof(filerwlock));
   if (((fl->fd) = open(filename, FLAGS, MODE)) < 0) {
      perror("filerwlockCreate:open");
      exit(1);}
   fl->n = n;
   return fl;
 }

/* Delete the cluster of read/write locks associated with fl. */
int filerwlockDelete(filerwlock *fl) {
   if (close(fl->fd) < 0) {
     perror("filerwlockCreate:oclose");
     exit(1);}
   return free(fl);
 }

/* Given the read/write lock cluster fl, lock its ith element */
int filereadlock(filerwlock *fl, int i) {
   if ((i < 0) | (i >= fl->n)) {
     printf("filerwlockLock needs i in range 0 .. %d\n", (fl->n)-1);
     exit(0);}
   readw_lock(fl->fd, i, SEEK_SET, 1);
 }

int filewritelock(filerwlock *fl, int i) {
   if ((i < 0) | (i >= fl->n)) {
     printf("filerwlockLock needs i in range 0 .. %d\n", (fl->n)-1);
     exit(0);}
   writew_lock(fl->fd, i, SEEK_SET, 1);
 }

/* Given the lock cluster fl, unlock its ith element */
int filerwunlock(filerwlock *fl, int i){
   if ((i < 0) | (i >= fl->n)) {
     printf("filelockUnlock needs i in range 0 .. %d\n", (fl->n)-1);
     exit(0);}
   un_lock(fl->fd, i, SEEK_SET, 1);
 }

/* Given the lock cluster fl, it read locks all its elements */
int filelongreadlock(filerwlock *fl) {
  readw_lock(fl->fd, 0, SEEK_SET, fl->n);
}

/* Given the lock cluster fl, it read locks all its elements */
int filelongunlock(filerwlock *fl) {
  un_lock(fl->fd, 0, SEEK_SET, fl->n);
}
