/*
 * Copyright (C) 1995 Lars Fenneberg
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */
 
#include <stdio.h> 
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include "ipquota.h"
 
static FILE *database; 
static int locked = 0;
static int write_ok = 0;

void lock_database(char *filename)
{
	char linkfile[1024], lockfile[1024];
	FILE *f;
	int tries = 0;
	int pid;
	int time_to_wait = 1;	

	sprintf(linkfile,"%s.lock.%i", filename, getpid());
	sprintf(lockfile,"%s.lock", filename);
	
	if ((f = fopen(linkfile,"w")) == NULL)
	{
		log(my_perror(linkfile));
		exit(1);
	}
	fprintf(f,"%i\n", getpid());
	fclose(f);
	
	while (tries < MAX_TRIES && locked == 0)
	{
		if (link(linkfile, lockfile) != 0)
		{
			if (errno != EEXIST)
			{
				remove(linkfile);
				log(my_perror("lockfile rename"));
				exit(1);
			}
			
			if ((f = fopen(lockfile,"r")) == NULL)
			{
				remove(linkfile);
				log(my_perror(lockfile));
				exit(1);
			}
			fscanf(f,"%i\n", &pid);
			fclose(f);

			log("%s locked by process %i", filename, pid);
			
			if (kill(pid,0) != 0 && errno == ESRCH)
			{
				log("dead process %i - removing old lockfile", pid);
				if (remove(lockfile) != 0)
				{
					remove(linkfile);
					log(my_perror(lockfile));
					exit(1);
				}
			}
			
			sleep(time_to_wait);
			time_to_wait *= 2;
			tries++;
		}
		else
		{
			remove(linkfile);
			locked = 1;
		}
	}
	if (!locked) 
	{
		remove(linkfile);
		log("max tries exhausted while trying to create lockfile");
		exit(1);
	}
}

void unlock_database(char *filename)
{
	char temp[1024];
	
	sprintf(temp,"%s.lock", filename);
	if (locked) remove(temp);
}

 
void init_database(char *filename)
{
	uid_t uid;
	unsigned int quota;
	int flags;

	lock_database(filename);

	if ((database = fopen(filename,"r")) == NULL)
	{
		if (errno == ENOENT)
		{
			if ((database = fopen(filename,"w")) != NULL)
			{
				log("Warning: database %s created, please check permissions",
					 database);
				fclose(database);
				write_ok = 1;
				return;
			}
		} 
		log(my_perror(filename));
		unlock_database(filename);
		exit(1);
	}

	while (fscanf(database,"%hi %i %i\n", &uid, &quota, &flags) != EOF)
		add_user(uid, quota, flags);

	fclose(database);

	write_ok = 1;
}

void close_database(char *filename)
{
	char temp[1024];
	struct user_hash_s *p;
	int i;

	if (!write_ok)
	{
		log("not updating database %s because of error", filename);
		return;
	}

	sprintf(temp,"%s.%i.temp", filename, getpid());
	
	if ((database = fopen(temp,"w")) == NULL)
	{
		log(my_perror(filename));
		return;
	}

	for(i=0; i<HASH_TABLE_SIZE; i++)
	{
		p = hash_table[i];
		while (p)
		{
			fprintf(database,"%6hi %15li %3i\n", p->uid, p->quota, p->flags);
			p = p->next;
		}
	}

	fclose(database);
	if (rename(temp, filename)<0)
	{
		log("failed to replace database: %s", my_perror(filename));
		log("temporary database is %s", temp);
		return;
	}
	
	unlock_database(filename);
}
