/*********************************************************************\
    *  Copyright (c) 1991 by Wen-King Su (wen-king@vlsi.cs.caltech.edu)   *
    *  Copyright (c) 1993 by Phil Richards (pgr@prg.ox.ac.uk)             *
    *  Copyright (c) 2004 by Radim Kolar                                  *
    *                                                                     *
    *  You may copy or modify this file in any manner you wish, provided  *
    *  that this notice is always included, and that you hold the author  *
    *  harmless for any loss or damage resulting from the installation or *
    *  use of this software.                                              *
    \*********************************************************************/

#include "client.h"
#include "lock.h"
#include <stdlib.h>

#ifndef FSP_NOLOCKING

static char key_string[sizeof(FSP_KEY_PREFIX)+32];

static char code_str[] =
    "0123456789:_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";

#include <setjmp.h>

static jmp_buf my_interrupt_env;

static RETSIGTYPE (*old_intr_handler)(int);

static RETSIGTYPE
interrupted_lock(int sig)
{
    /* this prints the interrupt message *AND* resets the interrupt handler */
    (*old_intr_handler)(sig);

    if (client_intr_state > 1)
	longjmp(my_interrupt_env, 0);
}

static void
make_key_string(u_long server_addr, u_int server_port)
{
    u_long v1, v2;
    char *p;

    strcpy(key_string, FSP_KEY_PREFIX);
    for (p = key_string; *p; p++)
	;
    v1 = server_addr;
    v2 = server_port;

    *p++ = code_str[v1 & 0x3f]; v1 >>= 6;
    *p++ = code_str[v1 & 0x3f]; v1 >>= 6;
    *p++ = code_str[v1 & 0x3f]; v1 >>= 6; 
    v1 = v1 | (v2 << (32-3*6));
    *p++ = code_str[v1 & 0x3f]; v1 >>= 6;
    *p++ = code_str[v1 & 0x3f]; v1 >>= 6;
    *p++ = code_str[v1 & 0x3f]; v1 >>= 6;
    *p++ = code_str[v1 & 0x3f]; v1 >>= 6;
    *p++ = code_str[v1 & 0x3f]; v1 >>= 6;
    *p   = 0;
}

#endif

/********************************************************************/
/******* For those systems that has flock function call *************/
/********************************************************************/
#ifdef FSP_USE_FLOCK

/* this will cause an unfortunate redefinition of F_OK etc, but... */
#include <sys/file.h>

int key_persists = 1;
static int lock_fd;
static u_int okey;

u_int
client_get_key(void)
{
    if (!setjmp(my_interrupt_env))
    {
        old_intr_handler = signal(SIGINT, interrupted_lock);

	if (flock(lock_fd, LOCK_EX) < 0)
	{
	    perror("flock");
	    exit(1);
	}

	(void)signal(SIGINT, old_intr_handler);

	if (read(lock_fd, &okey, sizeof(okey)) < 0)
	{
	    perror("read");
	    exit(1);
	}

	if (lseek(lock_fd, 0L, 0) < 0)
	{
	    perror("seek");
	    exit(1);
	}

        return(okey);
    }

    ffprintf(STDERR, "?couldn't get key from lock\n");
    return -1;
}

void
client_put_key(u_int key)
{
    if (write(lock_fd, (char*)&key, sizeof(key)) < 0)
    {
	perror("write");
	exit(1);
    }

    if (lseek(lock_fd, 0L, 0) < 0)
    {
	perror("seek");
	exit(1);
    }

    if (flock(lock_fd, LOCK_UN) < 0)
    {
	perror("unflock");
	exit(1);
    }
}

void
client_init_key(u_long server_addr, u_int server_port, u_int key)
{
    mode_t omask;
    okey = key;

    make_key_string(server_addr, server_port);

    omask = umask(0);
    lock_fd = open(key_string, O_RDWR | O_CREAT, 0666);
    (void)umask(omask);
}

void
client_finish_key(void)
{
    (void)close(lock_fd);
}

#endif
/********************************************************************/
/******* For those systems that has lockf function call *************/
/********************************************************************/
#ifdef FSP_USE_LOCKF

int key_persists = 1;
static u_int lock_fd;
static u_int okey;

u_int
client_get_key(void)
{
    if (!setjmp(my_interrupt_env))
    {
        old_intr_handler = signal(SIGINT, interrupted_lock);

	if (lockf(lock_fd, F_LOCK, sizeof(okey)) < 0)
	{
	    perror("lockf");
	    exit(1);
	}

        (void)signal(SIGINT, old_intr_handler);

	if (read(lock_fd, &okey, sizeof(okey)) < 0)
	{
	    perror("readlk");
	    exit(1);
	}

	if (lseek(lock_fd, 0L, 0) < 0)
	{
	    perror("seek");
	    exit(1);
	}

	return(okey);
    }

    ffprintf(STDERR, "?couldn't get key from lock\n");
    return -1;
}

void
client_put_key(u_int key)
{
    if (write(lock_fd, &key, sizeof(key)) < 0)
    {
	perror("write");
	exit(1);
    }
    if (lseek(lock_fd, 0L, 0) < 0)
    {
	perror("seek");
	exit(1);
    }
    if (lockf(lock_fd, F_ULOCK, sizeof(key)) < 0)
    {
	perror("unlockf");
	exit(1);
    }
}

void
client_init_key(u_long server_addr, u_int server_port, u_int key)
{
    mode_t omask;
    okey = key;

    make_key_string(server_addr, server_port);

    omask = umask(0);
    lock_fd = open(key_string, O_RDWR | O_CREAT, 0666);
    (void)umask(omask);
}

void
client_finish_key(void)
{
    (void)close(lock_fd);
}

#endif
/********************************************************************/
/******* For those systems that has SysV shared memory + lockf ******/
/********************************************************************/
#ifdef FSP_USE_SHAREMEM_AND_LOCKF

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>

int key_persists = 1;
static u_int *share_key;
static u_int lock_fd;
static int lock_shm;

u_int
client_get_key(void)
{
    if (!setjmp(my_interrupt_env))
    {
        old_intr_handler = signal(SIGINT, interrupted_lock);

	if (lockf(lock_fd, F_LOCK, 2) < 0)
	{
	    perror("lockf");
	    exit(1);
	}

	(void)signal(SIGINT, old_intr_handler);

	return(*share_key);
    }

    ffprintf(STDERR, "?couldn't get key from lock\n");
    return -1;
}

void
client_put_key(u_int key)
{
    *share_key = key;
    if (lockf(lock_fd, F_ULOCK, 2) < 0)
    {
	perror("unlockf");
	exit(1);
    }
}

void
client_init_key(u_long server_addr, u_int server_port, u_int key)
{
    mode_t omask;
    key_t lock_key;

    make_key_string(server_addr, server_port);

    omask = umask(0);
    lock_fd = open(key_string, O_RDWR | O_CREAT, 0666);
    umask(omask);

    if ((lock_key = ftok(key_string, 238)) < 0)
    {
	perror("ftok");
	exit(1);
    }
    if ((lock_shm = shmget(lock_key, 2*sizeof(u_int), IPC_CREAT | 0666)) < 0)
    {
	perror("shmget");
	exit(1);
    }
    if (!(share_key = (u_int *)shmat(lock_shm, 0, 0)))
    {
	perror("shmat");
	exit(1);
    }
}

/* I haven't got a clue what I'm doing here -- this is all guesswork! */
void
client_finish_key(void)
{
    if (shmdt((char *)share_key) < 0)
    {
	perror("shmdt");
	exit(1);
    }
    shmctl(lock_shm,IPC_RMID,NULL);

    (void)close(lock_fd);
}

#endif
/********************************************************************/
/******* For those who do not want to use locking *******************/
/********************************************************************/
#ifdef FSP_NOLOCKING

int key_persists = 0;
static u_int okey;

u_int
client_get_key(void)
{
    return (int)okey;
}

void
client_put_key(u_int key)
{
    okey = key;
}

void
client_init_key(u_long server_addr, u_int server_port, u_int key)
{
    okey = key;
}

void
client_finish_key(void)
{
    ;
}

#endif
/********************************************************************/
/******* For those systems that has SysV shared memory + semop ******/
/********************************************************************/
#ifdef FSP_USE_SHAREMEM_AND_SEMOP

#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>

#ifdef _SEM_SEMUN_UNDEFINED
union semun
{
  int val;
  struct semid_ds *buf;
  unsigned short int *array;
  struct seminfo *__buf;
};
#endif

int key_persists = 1;
static u_int *share_key;
static int   lock_shm;
static int   lock_sem;

u_int client_get_key (void)
{
  struct sembuf sem;
  sem.sem_num = 0;
  sem.sem_op = -1;
  sem.sem_flg = SEM_UNDO;
  
    if (!setjmp(my_interrupt_env))
    {
          old_intr_handler = signal(SIGINT, interrupted_lock);
	  if(semop(lock_sem,&sem,1) == -1 )
	  {
	      perror("semop");
	      exit(1);
	  }
	  (void)signal(SIGINT, old_intr_handler);

	  return(*share_key);
    }

    ffprintf(STDERR, "?couldn't get key from lock\n");
    return -1;
}

void client_put_key (u_int key)
{
  struct sembuf sem;

  sem.sem_num = 0;
  sem.sem_op = 1;
  sem.sem_flg = SEM_UNDO;

  *share_key = key;
  if(semop(lock_sem,&sem,1) == -1) {
    perror("semop");
    exit(1);
  }
}

void client_init_key (u_long server_addr,
	              u_int server_port,
		    u_int key)
{
  mode_t omask;
  key_t lock_key;
  int fd;
  union semun sun;
  struct sembuf sem;

  make_key_string(server_addr,server_port);

  omask = umask(0);
  fd = open(key_string,O_RDWR|O_CREAT,0666);
  umask(omask);
  close(fd);

  if((lock_key = ftok(key_string,238)) == -1) {
    perror("ftok");
    exit(1);
  }
  if((lock_shm = shmget(lock_key,2*sizeof(u_int),IPC_CREAT|0666)) == -1) {
    perror("shmget");
    exit(1);
  }
  if(!(share_key = (unsigned int *) shmat(lock_shm,(char*)0,0))) {
    perror("shmat");
    exit(1);
  }

  if((lock_sem = semget(lock_key,0,0)) == -1) {
      /* create a new semaphore and init it */
      if((lock_sem = semget(lock_key,2,IPC_CREAT|0666)) == -1) {
	  perror("semget");
      }
      /* we need to init this semaphore */
      sun.val=1;
      if(semctl(lock_sem,0,SETVAL,sun) == -1)
      {
	  perror("semctl setval");
	  exit(1);
      }
      *share_key = key;
  }

  /* increase in use counter */
  sem.sem_num = 1;
  sem.sem_op = 1;
  sem.sem_flg = SEM_UNDO;

  if(semop(lock_sem,&sem,1) == -1) {
      perror("semop");
      exit(1);
  }
}

void client_finish_key(void)
{
    int rc;
    struct sembuf sem;
    
    if (shmdt((char *)share_key) < 0)
    {
	perror("shmdt");
	exit(1);
    }
    /* check if we are only one process holding lock */
    rc = semctl(lock_sem,1,GETVAL);
    if (rc == -1)
    {
	perror("semctl");
	exit(1);
    }
    if (rc == 1)
    {
	
	/* safe to destroy */
	if (
	     (semctl(lock_sem,0,IPC_RMID) < 0) ||
	     (shmctl(lock_shm,IPC_RMID,NULL) < 0) ||
	     (unlink(key_string) < 0) )
	    rc++;/* ignore cleanup errors */
    } else
	if(rc > 1)
	{
	      /* we need to decrease sem. */
	      sem.sem_num = 1;
	      sem.sem_op = -1;
	      sem.sem_flg = SEM_UNDO;

	      if(semop(lock_sem,&sem,1) == -1) {
		  perror("semop");
	      }
	}
}
#endif
/********************************************************************/
/********************************************************************/
/********************************************************************/


syntax highlighted by Code2HTML, v. 0.9.1