/*
 * This code is part of Screws project.
 * Copylefted by pancake@phreaker.net at 2003
 */

#include "Engine.h"
#include "Signal.h"
#include "Args.h"
#include "Accept.h"

int forks=0; /* Initial Forks value */

/* Set Process Priority */
int
setProcPrio(n)
	int n;
{
	return (setpriority(PRIO_PROCESS,0,n) )?ERROR_PRIO:0;
}

/* Set Process Limits */
int
setProcLimi(cpu,mem)
	int cpu,mem;
{
	struct rlimit rl;
	int err=0;

	rl.rlim_cur=rl.rlim_max=cpu;
	err|=setrlimit(RLIMIT_CPU, &rl);
	rl.rlim_cur=rl.rlim_max=mem;
	err|=setrlimit(RLIMIT_STACK, &rl);
	err|=setrlimit(RLIMIT_DATA, &rl);
#if __solaris__
	err|=setrlimit(RLIMIT_VMEM,&rl);
#else
	err|=setrlimit(RLIMIT_RSS, &rl);
#endif
	return 0; // ignore errors here XXX
	return err;
}

/* Set Process Permisions */
int
setProcPerm(uid,euid,gid,egid)
	uid_t uid, euid;
	gid_t gid, egid;
{
	int ret=0;
	ret|=setegid(egid);
	ret|=setgid(gid);
	ret|=seteuid(euid);
	ret|=setuid(uid);
	return (ret)?ERROR_SUID:0;
}

/* Set Process Directories */
int
setProcDirs(dir,root)
	char *dir,
	     *root;
{
	int ret=0;
	
	if (strcmp(root,"/")) 
	{
		ret|=chdir(root);
		ret|=chroot("."); 
	} 
	ret|=chdir (dir);
	return (ret)?ERROR_CHDIR:0;
}

/* Set Process Options */
int
setProcOptions(n)
	int n;
{
	int err;
	if ((err=	setProcPerm(	Svr.Lis[n].V[_UID],
					Svr.Lis[n].V[_EUID],
					Svr.Lis[n].V[_GID],
					Svr.Lis[n].V[_EGID] ) ))   {}else
	if ((err=	setProcDirs(	Svr.Lis[n].S[_CHDIR],
					Svr.Lis[n].S[_CHROOT] )))  {}else
	if ((err=	setProcLimi(	Svr.Lis[n].V[_MAXCPU],
					Svr.Lis[n].V[_MAXMEM] )))  {}else
	if ((err=	setProcPrio(	Svr.Lis[n].V[_PRIO])  ))   {}else
	return 0;
	return err;
}

/* Set Children options */
int
setChildOptions(n)
	int n;
{
	int err;
	char *dir=Svr.Lis[n].S2[_CHDIR];
	if (dir[0]!='/'&&dir[0]!='.') //||strcmp(".",Svr.Lis[n].S2[_CHDIR]))
	{
		// TODO SURE?!?! :/
		//chdir(EXEC_PATH);
	}
	if ((err=	setProcPerm(	Svr.Lis[n].V2[_UID],
					Svr.Lis[n].V2[_EUID],
					Svr.Lis[n].V2[_GID],
					Svr.Lis[n].V2[_EGID] ) ))   {}else
	if ((err=	setProcDirs(	Svr.Lis[n].S2[_CHDIR],
					Svr.Lis[n].S2[_CHROOT] )))  {}else
	if ((err=	setProcLimi(	Svr.Lis[n].V2[_MAXCPU],
					Svr.Lis[n].V2[_MAXMEM] )))  {}else
	if ((err=	setProcPrio(	Svr.Lis[n].V2[_PRIO])  ))   {}else
		return 0;
	return err;
}


/* == */
/* Main Thread launching. just fork and dup. */
/* == */
pid_t
launchListen(n)
	int n;
{
	pid_t pid;
	int ok,err;
	
	/* Create fork thread */
	pid=fork();
	switch(pid)
	{
	  case -1: return 0;
	  case  0: break;
	  default: return pid;
	}
	
	/* Change process name */
	sprintf(av[0],"[%d] %s\0",n,Svr.Lis[n].name);
	av[1]=0;
	av[2]=0;

	/* Set process options */
	err=Error(Svr.Lis[n].name,setProcOptions(n));
	if (err>ERROR_NULL)
	{
		exit( Error(Svr.Lis[n].name,err) );
	}
	
	/* Catch signals */
	if ( Error(Svr.Lis[n].name, catchSignals(WHO_CHILDREN)) )
		exit(1);

	/* Listen Socket */
	Svr.Lis[n].s = screws_socket_listen(
		Svr.Lis[n].family,
		Svr.Lis[n].ip,
		Svr.Lis[n].port);

		if (Svr.Lis[n].s==-1)
		{
		exit(Error(Svr.Lis[n].name,ERROR_LISTEN));
		}
		printf("[o] Listen thread '%s' at port %d is ok.\n",
			Svr.Lis[n].name,Svr.Lis[n].port);

	/* Init logfile */
	ok = screws_logs_init(n); // TODO catch FALSE!
	if (!ok)
	{
		printf("[e] Error initializing logs\n");
	}

	/* Init SSL stuff */
	screws_ssl_init(n); // TODO catch FALSE!

	setParentEnv(n);
	do
	{
		err=Accept(n);
		if (err==-1) // errlog?
			printf("Accept error(-1)\n");
		if (waitpid(-2,0,WNOHANG)>=0) /* rip dead childs */
		{
			/* TODO debug forks  (CATCH SEGF) */
			// NEVER HAPPENS
			printf("RIP!\n");
			forks--;
		}

	} while(err!=-1);

	/* End Of Life */
	exit(0);
}

