

#define LOCAL_DEBUG
#include "debug.h"

#include "config.h"
#include "meta.h"
#include "acfg.h"
#include "fileio.h"
#include "conserver.h"

#include <iostream>
using namespace MYSTD;

#include <cstdio>
#include <cstring>
#include <sys/time.h>
#include <signal.h>
#include <errno.h>

static void usage();
static void _TestCacheDir();
void term_handler(int signum);
void log_handler(int signum);
void check_algos();

void DispatchAndRunMaintTask(cmstring &cmd, int fd, const char *auth);

#ifndef HAVE_DAEMON // provide a poor but sufficient replacement
int daemon(int, int)
{
   chdir("/");
   int dummy=open("/dev/null", O_RDWR);
   if(0<=dup2(dummy, fileno(stdin))
		   && 0<=dup2(dummy, fileno(stdout))
		   && 0<=dup2(dummy, fileno(stderr)))
   {
	   switch(fork())
	   {
	   case 0: // this is child, good
		   return 0;
	   case -1: // bad...
		   return -1;
	   default: // in parent -> cleanup
		   setsid();
		   _exit(0);
	   }
   }
   return -1;
}
#endif

int main(int argc, char **argv)
{


// PLAYGROUND

/*
 * Let's be another csum tool...

	md5_state_s ctx;
	md5_init(&ctx);
	uint8_t buf[2000];
	while(!feof(stdin))
	{
		int n=fread(buf, sizeof(char), 2000, stdin);
		md5_append(&ctx, buf, n);
	}
	uint8_t csum[16];
	md5_finish(&ctx, csum);
	for(int i=0;i<16;i++)
		printf("%02x", csum[i]);
	printf("\n");
	exit(0);
*/
	/*
	uint8_t csum[16];
	string s(argv[1]);
	filereader::GetMd5Sum(s, csum);
	for(int i=0;i<16;i++)
			printf("%02x", csum[i]);
		printf("\n");
		if(CsEqual((unsigned char*) argv[2], csum))
			printf("Is OK\n");
		else
			printf("Differenz\n");
		exit(0);
	*/
	/*
	bool Bz2compressFile(const char *, const char*);
	return ! Bz2compressFile(argv[1], argv[2]);
	*/

	const char *toBase64=getenv("TOBASE64");
	if(toBase64)
	{
		MYSTD::cout << acfg::EncodeBase64Auth(toBase64);
		return 0;
	}

	check_algos();
	struct sigaction act;
	memset(&act, 0, sizeof(act));

	sigfillset(&act.sa_mask);
	act.sa_handler = &term_handler;
	sigaction(SIGTERM, &act, NULL);
	sigaction(SIGINT, &act, NULL);
	sigaction(SIGQUIT, &act, NULL);
	sigaction(SIGUSR2, &act, NULL);

	act.sa_handler = &log_handler;
	sigaction(SIGUSR1, &act, NULL);

	act.sa_handler = SIG_IGN;
	sigaction(SIGPIPE, &act, NULL);
#ifdef SIGIO
	sigaction(SIGIO, &act, NULL);
#endif
#ifdef SIGXFSZ
	sigaction(SIGXFSZ, &act, NULL);
#endif
	
	bool bForceCleanup(false);
    for (int i=1; i<argc; i++)
    {

		if (!strncmp(argv[i], "-h", 2))
			usage();

		if (!strncmp(argv[i], "-v", 2))
		{
			acfg::debug=acfg::debug|LOG_DEBUG|LOG_MORE;
			*(argv[i])=0x0; // ignore if ever checked
		}

		if (!strncmp(argv[i], "-e", 2))
		{
			bForceCleanup=true;
			*(argv[i])=0x0; // ignore if ever checked
		}
    }
    
    for (int i=1; i<argc; i++)
    {
    	
        if(!strcmp(argv[i], "-c"))
        {
        	i++;
        	if(i<argc)
        		acfg::ReadConfigDirectory(argv[i]);
        	else
        		usage();
		}
		else if(! *(argv[i]))
			continue;
		else
		{
			if(!acfg::SetOption(argv[i], false))
				usage();
		}

	}
    
    if(!aclog::open())
    {
        cerr << "Problem creating log files. Check permissions of the log directory, " << acfg::logdir<<endl;
        exit(1);
    }

	acfg::PostProcConfig();
    
    _TestCacheDir();

	conserver::Setup();
	
	if (bForceCleanup)
	{
		DispatchAndRunMaintTask(acfg::reportpage + "?abortOnErrors=aOe&doExpire=Start",
				fileno(stdout), acfg::adminauth.c_str());
		exit(0);
	}

	if (acfg::foreground)
		return conserver::Run();

	if (daemon(0, 0))
	{
		cerr << "Failed to change to daemon mode" << endl;
		exit(43);
	}

	if (!acfg::pidfile.empty())
	{
		mkbasedir(acfg::pidfile);
		FILE *PID_FILE = fopen(acfg::pidfile.c_str(), "w");
		if (PID_FILE != NULL)
		{
			fprintf(PID_FILE, "%d", getpid());
			checkForceFclose(PID_FILE);
		}
	}

	return conserver::Run();
	
}

static void usage() {
	cout <<"Usage: apt-cacher -h -c configdir <var=value ...>\n\n"
		"Options:\n"
		"-h: this help message\n"
		"-c: configuration directory\n"
		"-e: on startup, run expiration once\n\n"
		"Most interesting variables:\n"
		"ForeGround: Don't detach (default: 0)\n"
		"Port: TCP port number (default: 3142)\n"
		"CacheDir: /directory/for/storage\n"
		"LogDir: /directory/for/logfiles\n"
		"\n"
		"See configuration examples for all directives.\n\n";
	exit(0);
}


static void _TestCacheDir()
{
	struct timeval tv;
	gettimeofday(&tv, NULL);
	tSS buf;
	buf << acfg::cacheDirSlash << "testfile." <<42*tv.tv_usec*tv.tv_sec;
	mkbasedir(buf.c_str()); // try or force its directory creation
	int t=open( buf.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 00644);
	if (t>=0)
	{
		forceclose(t);
		if(0==unlink(buf.c_str()))
			return;
	}
	cerr << "Cache directory not writable. Check the permissions of "
		<< acfg::cachedir << "!" <<endl;
	exit(1);
}

void log_handler(int)
{
	aclog::close(true);
}

void term_handler(int signum)
{
	switch (signum)
	{
	case (SIGTERM):
	case (SIGINT):
	case (SIGQUIT):
		aclog::close(false);
	// and then terminate...
#if 0
		{
		char *crashMe(NULL);
		// work is done, commit suicide
		struct sigaction act;
		memset(&act, 0, sizeof(act));
		act.sa_handler = SIG_DFL;
		if (sigaction(SIGINT, &act, NULL))
			*crashMe = signum; // be sure not to end up in an infinite loop
		raise(SIGINT);
		}
#endif
#ifdef DEBUG
	case SIGUSR2:
#endif
		exit(1);
	default:
		return;
	}
}
