/* flock.c: File locking interceptor, Michael McTernan
 *
 * This can be compiled with the following command:
 *   gcc -o flock.so flock.c -shared -fpic -ldl
 *
 * This produces a shared library that can be pre-linked with other
 * excutables to intercept calls to flock():
 *
 *  LD_PRELOAD=flock.so <command>
 *
 * This source code is released as public domain.
 * This means that there is no copyright and anyone is able to take a
 * copy for free and use it as they wish, with or without modifications,
 * and in any context they like, commercially or otherwise.
 *
 * The only limitation is that I don't guarantee that the software is
 * fit for any purpose or accept any liability for it's use or misuse -
 * the software is without warranty.
 *
 */

/* Declare GNU_SOURCE to provide RTLD_NEXT from dlfcn.h */
#define _GNU_SOURCE

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <fcntl.h>
#include <sys/types.h>
#include <unistd.h>
#include <dlfcn.h>


#define MAX_FDS  1024

int fdList[MAX_FDS];
int fdListLen = 0;

static int (*real_flock)(int fd, int operation) = NULL;
static int (*real_open)(const char *pathname, int flags, ...) = NULL;
static int (*real_open64)(const char *pathname, int flags, ...) = NULL;
static int (*real_close)(int fd);

static void addFd(int fd)
{
  if(fdListLen < MAX_FDS)
  {
    fdList[fdListLen] = fd;
    fdListLen++;
  }
}

static int findFd(int fd)
{
  int t = fdListLen;

  while(t > 0)
  {
    t--;
    if(fdList[t] == fd)
    {
      return t;
    }
  }
  return -1;
}

static void remFd(int fd)
{
  int u, t = findFd(fd);

  if(t != -1)
  {
     for(u = fdListLen - 1; u > t; u--)
     {
       fdList[u - 1] = fdList[u];
     }
     fdListLen--;
  }
}

static void *find(const char *sym)
{
  void *f = dlsym(RTLD_NEXT, sym);

  if(!f)
  {
    fprintf(stderr, "Failed to find '%s'\n", sym);
    abort();
  }

  return f;
}

int open64(const char *pathname, int flags, ...)
{
  int r;

  if(!real_open64) real_open64 = find("open64");

  if (flags & O_CREAT)
  {
    va_list arg_list;
    mode_t  mode;

    va_start(arg_list, flags);

    mode = va_arg(arg_list, mode_t);

    va_end(arg_list);

    r = real_open64(pathname, flags, mode);
  }
  else
  {
    r = real_open64(pathname, flags);
  }

  if(strncmp("db.", pathname, 3) == 0)
  {
    printf("%d: open64 %s  = %d\n", getpid(), pathname, r);
    addFd(r);
  }

  return r;
}

int open(const char *pathname, int flags, ...)
{
  int r;

  if(!real_open) real_open = find("open");

  if (flags & O_CREAT)
  {
     va_list arg_list;
     mode_t  mode;

     va_start(arg_list, flags);

     mode = va_arg(arg_list, mode_t);

     va_end(arg_list);

     r = real_open(pathname, flags, mode);
   }
   else
   {
      r = real_open(pathname, flags);
   }

   if(strncmp("db.", pathname, 3) == 0)
   {
     printf("%d: open %s = %d\n", getpid(), pathname, r);
     addFd(r);
   }

   return r;
}

int close(int fd)
{
  if(!real_close) real_close = find("close");

  if(findFd(fd) != -1)
  {
    printf("%d: Close %d\n", getpid(), fd);
    remFd(fd);
  }

  return real_close(fd);
}

int flock(int fd, int operation)
{
  if(!real_flock) real_flock = find("flock");

  if(findFd(fd) != -1)
  {
    printf("%d: Ignoring flock %d, %d!\n", getpid(), fd, operation);
    return 0;
  }
  else
  {
    return real_flock(fd, operation);
  }
}

/* END OF FILE */


