/* -------------------------------------------------------------------- */
/* SMS Client, send messages to mobile phones and pagers		*/
/*									*/
/* lock.c								*/
/*									*/
/*  Copyright (C) 1997,1998,1999 Angelo Masci				*/
/*									*/
/*  This library is free software; you can redistribute it and/or	*/
/*  modify it under the terms of the GNU Library General Public		*/
/*  License as published by the Free Software Foundation; either	*/
/*  version 2 of the License, or (at your option) any later version.	*/
/*									*/
/*  This library is distributed in the hope that it will be useful,	*/
/*  but WITHOUT ANY WARRANTY; without even the implied warranty of	*/
/*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU	*/
/*  Library General Public License for more details.			*/
/*									*/
/*  You should have received a copy of the GNU Library General Public	*/
/*  License along with this library; if not, write to the Free		*/
/*  Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.	*/
/*									*/
/*  You can contact the author at this e-mail address:			*/
/*									*/
/*  angelo@styx.demon.co.uk						*/
/*									*/
/* -------------------------------------------------------------------- */
/* $Id$
   -------------------------------------------------------------------- */

#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <stdio.h>
#include <signal.h>

#include "logfile/logfile.h"
#include "lock.h"
#include "common/common.h"

/* -------------------------------------------------------------------- */
/* -------------------------------------------------------------------- */
static pid_t resource_lock_pid(char *resource_lockfile)
{
	int 	lock_fd,
		i,
		eof;
	pid_t	pid;
	char	buf[512],
		c;


	lock_fd = open(resource_lockfile, O_RDONLY);
	if (lock_fd == -1)
	{	return(-1);
	}

	eof = FALSE;
	i = 0;
	while((i < 11) && (eof == FALSE))
	{
		switch (read(lock_fd, &c, 1))
		{
		case 1:
			buf[i] = c;
			i++;
			break;
		case -1:
			if (errno != EINTR)
			{	lprintf(LOG_ERROR, "Reading pid from lockfile '%s'\n", resource_lockfile);
				exit(-1);
			}
			break;
		case 0:
			eof = TRUE;		
		}
	}

	buf[i] = '\0';

	if (sscanf(buf, "%10d\n", &pid) != 1)
	{
		pid = -1;
	}

	close(lock_fd);
	return pid;
}

/* -------------------------------------------------------------------- */
/* -------------------------------------------------------------------- */
int resource_unlock(char *resource_lockfile)
{
	pid_t	pid;

	pid = resource_lock_pid(resource_lockfile);
	if (pid == getpid())
	{
		lprintf(LOG_VERBOSE, "Removing Lockfile '%s'\n", resource_lockfile);
		unlink(resource_lockfile);
	}

	return 0;
}


/* -------------------------------------------------------------------- */
/* -------------------------------------------------------------------- */
static int file_exists(char *filename)
{
	struct stat stat_buf;	


	if (stat(filename, &stat_buf) == -1)
	{	return -1;
	}

	if (! S_ISREG(stat_buf.st_mode))	
	{	return -1;
	}

	return 0;
}


/* -------------------------------------------------------------------- */
/* -------------------------------------------------------------------- */
static int directory_exists(char *filename)
{
	struct stat stat_buf;	


	if (stat(filename, &stat_buf) == -1)
	{	return -1;
	}

	if (! S_ISDIR(stat_buf.st_mode))	
	{	return -1;
	}

	return 0;
}


/* -------------------------------------------------------------------- */
/* -------------------------------------------------------------------- */
int resource_locked(char *resource_lockfile)
{
	pid_t	pid;

#if 0
	if (access(resource_lockfile, F_OK) == 0)
#else
	if (file_exists(resource_lockfile) == 0)
#endif
	{
		/* lockfile exists */

		pid = resource_lock_pid(resource_lockfile);
		if (pid != -1)
		{
			/* Is there a process with this pid? */

			if (kill(pid, 0) == 0)
			{	return -1;		/* Process exists */
			}
			else
			{	if (errno == EPERM)
				{	return -1;	/* Process exists */
				}
			}

			/* Process does not exist		*/
			/* we can unlink this lockfile as	*/
			/* it is stale.				*/

			lprintf(LOG_VERBOSE, "Removing Stale Lockfile '%s'\n", resource_lockfile);
			unlink(resource_lockfile);
			return 0;
		}
		else
		{	/* Could not determine PID of	*/
			/* owner of this lockfile!	*/

			return -1;
		}
	}

	/* No process holds the lock */
	return 0;
}

/* -------------------------------------------------------------------- */
/* -------------------------------------------------------------------- */
int resource_lock(char *resource_lockfile)
{	
	int 	lock_fd,
		i,
		len;
	char	buf[64];


	if (resource_locked(resource_lockfile) == 0)
	{
		lock_fd = open(resource_lockfile, O_CREAT|O_WRONLY|O_EXCL, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
		if (lock_fd == -1)
		{	if (errno == EEXIST)
			{		/* Could not create lockfile 	*/
					/* A lockfile already exists	*/
					/* The most likely reason for	*/
					/* this is another process	*/
					/* generating one after our	*/
					/* call to resource_locked()	*/

					return -1;
			}

			lprintf(LOG_ERROR, "Cannot create lockfile %s\n", resource_lockfile);
			exit(-1);
		}

		len = sms_snprintf(buf, 64, "%10d sms_client\n", (int)getpid());

		i = 0;
		while(i < len)
		{
			switch (write(lock_fd, &buf[i], 1))
			{
			case 1:
				i++;
				break;
			case -1:
				if (errno != EINTR)
				{	lprintf(LOG_ERROR, "Reading pid from lockfile '%s'\n", resource_lockfile);
					exit(-1);
				}
			}
		}

		chmod(resource_lockfile, S_IRUSR|S_IRGRP|S_IROTH);
		close(lock_fd);

		/* Created lockfile with this pid */


		lprintf(LOG_VERBOSE, "Created Lockfile '%s'\n", resource_lockfile);
		return 0;
	}

	/* Could not create lockfile */
	return -1;
}

/* -------------------------------------------------------------------- */
/* -------------------------------------------------------------------- */
int resource_wait(char *resource_lockfile, long delay)
{
	sms_usleep(delay);		/* Sleep between checks on the		*/
				/* lockfile.				*/
				/* What would be really nice would	*/
				/* be a way to block and return if	*/
				/* the attributes on the lockfile	*/
				/* change.				*/
	return 0;
}

/* -------------------------------------------------------------------- */
/* -------------------------------------------------------------------- */
void resource_check_lockdir(char *resource_lockfile)
{
	char 	lock_dir[512], 
		*dst, 
		*ptr;

	ptr = lock_dir;
	dst = lock_dir;
	while (*resource_lockfile != '\0')
	{
		if (*resource_lockfile == '/')
		{	ptr = dst;
		}

		*dst++ = *resource_lockfile++;
	}

	if (ptr == lock_dir)
	{	*ptr++ = '.';
	}

	*ptr = '\0';

#if 0
	if (access(lock_dir, R_OK|W_OK|X_OK) < 0)
#else
	if (directory_exists(lock_dir) == -1)
#endif
	{	lprintf(LOG_ERROR, "Cannot access lock directory '%s'\n", lock_dir);
		exit(-1); 
	}
}
