/* Derived from lib/daemon.c by fastdaemon.sed */
/*-
 * Copyright (c) 1993, Trusted Information Systems, Incorporated
 * All rights reserved.
 *
 * Redistribution and use are governed by the terms detailed in the
 * license document ("LICENSE") included with the toolkit.
 */

/*
 * 10/27 pjc modified listen to use SOMAXCONN
 *           added sleep on bind failure.
 */

#include	<signal.h>
#include	<sys/signal.h>
#include	<syslog.h>
#include	<errno.h>
#include	<sys/types.h>
#include	<sys/stat.h>
#include	<sys/socket.h>
#ifdef SYSV
#include	<sys/sockio.h>
#endif
#include	<sys/wait.h>
#include	<netinet/in.h>
#include	<netdb.h>
#include	<arpa/inet.h>
#include	<sys/time.h>
#include	<sys/resource.h>
#ifndef AIX
#include	<sys/fcntl.h>
#endif
#include	<fcntl.h>
#include	<unistd.h>
#include	<stdlib.h>
#include	<string.h>

#include	"firewall.h"
#include	"libemfw.h"
#include	"fwstats.h"
extern struct fwstats proxy_stats;

extern void waitwaitwait();
extern void hup_exit();
extern void got_signal ();
extern char* ip2name(struct in_addr);

int do_fastdaemon(args)
struct fastdaemon_args *args;
{	struct sockaddr_in sa;
	int sock,sockl;
	pid_t pid;
	int boundok = 1;
	int reuse = 1;
	int devnull;

#ifdef	HAVE_SETSID
	(void) setsid();
#endif
	if ((pid = fork()) == -1)
		return (-1);
	if (pid)
{
keep_pid_file ();
exit(0);	/* parent exits */
}
write_pid_file ();
	devnull = open("/dev/null", O_RDWR, 0);
	if (devnull != -1) {
		(void) dup2(devnull, 0);
		(void) dup2(devnull, 1);
		(void) dup2(devnull, 2);
		if (devnull > 2)
			(void) close(devnull);
	}
	sa.sin_family = AF_INET;
fastdaemon_sockaddr (&sa, args);
	sock = socket(AF_INET, SOCK_STREAM, 0);
	if( sock < 0){
		syslog(LLEV,"Failed to create socket, %s", strerror(errno));
		exit(1);
	}
	setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse, sizeof(reuse));
	while( bind(sock, (struct sockaddr *)&sa, sizeof(sa))){
		syslog(LLEV,"Failed to bind port %d, %s", args->port, strerror(errno));
		syslog(LLEV,"Sleeping for 2 minutes");
		sleep(120);
		boundok=0;
	}
#ifndef SOMAXCONN
#define SOMAXCONN 4
#endif
	if( listen(sock, SOMAXCONN) < 0){
		syslog(LLEV,"Failed to listen, %s", strerror(errno));
		exit(1);
	}

	if( boundok == 0){
fastdaemon_ready (args);
	}
	signal(SIGCHLD,waitwaitwait);
	signal(SIGINT, hup_exit);
	signal(SIGTERM,hup_exit);

	while(1){
		socklen_t salen = sizeof(sa);
		sockl = accept(sock, (struct sockaddr *)&sa, &salen);
		if( sockl < 0){
if( errno == EINTR) { 
got_signal (); 
continue; 
}
			syslog(LLEV,"Accept failed, %s", strerror(errno));
			continue;
		}
		if ((proxy_stats.child_limit) &&
			(proxy_stats.child_count > proxy_stats.child_limit)) {
				syslog(LLEV,"deny host=%.100s/%.100s use of gateway: child limit exceeded",ip2name(sa.sin_addr),inet_ntoa(sa.sin_addr));
				close(sockl);
				continue;
		}
		pid = fork();
		if( pid < 0){
			syslog(LLEV,"Fork failed, %s", strerror(errno));
/* 9/8/95 pjc. Make fork failure non-fatal, just throw connection for now */
			close(sockl);
			continue;
		}
		if( pid == 0)
			break;	/* We are the child */
		close(sockl);
	}
#ifdef	HAVE_SETSID
	(void)setsid();
#endif
signal(SIGHUP, SIG_IGN);
	close(0);
	close(1);
	close(2);
	dup(sockl);
	dup(sockl);
	dup(sockl);
	close(sockl);
	close(sock);
keep_pid_file ();
return 0;
}
