/*
 * Un faux daemon.
 *
 * Copyright (c) 1999 Association CRANS.
 *
 * Auteur: Olivier Saut <Olivier.Saut@crans.ens-cachan.fr> 
 *
 *
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <signal.h>

#include <syslog.h>
#include <varargs.h>
#include <netdb.h>

#ifndef LINUX
#include <libutil.h>
#endif


#define kZamok_Tourne 		1
#define kPORT 			514 	/* Port de rshd */
#define kMaxDaemonChildren 	10

int nbrFils;


int daemon_init(void) {
	pid_t pid;

	if((pid = fork())<0)
		return (-1);
	else if (pid !=0) {
		(void)fprintf(stdout,"beastie launched : %d\n",pid);
		exit(0);
	}
	setsid();
	chdir("/");
	umask(0);
	return(0);
}

/* Attention subtil (waitpid plutot que wait) :-) */
void zombie(int signo) {
	pid_t pid;
	int stat;
	
	while ((pid = waitpid(-1, &stat, WNOHANG)) > 0) 
		nbrFils--;

	return;
}

void sigterm(int signo) {
	syslog(LOG_NOTICE,"beastie killed by SIGTERM.");
	closelog();
	exit(EXIT_SUCCESS);
}


int main(int argc, char *argv[]){
int serv_sock, client_sock, cli_len;	
struct	sockaddr_in serv_addr, cli_addr;
struct hostent *cli_ent;
char *clientname,*message;
int PORT;
pid_t pid;
sig_t previous_handler;
char buffer[1024];

	if(argc>1)
		PORT=atoi(argv[1]);
	else
		PORT=kPORT;

	if(setuid(getuid())) {
		perror("Setuid.");
		exit(EXIT_FAILURE);
	}

	/* On se daemonize */
	if(daemon_init()) {
		perror("Initialize as daemon.");
		exit(EXIT_FAILURE);
	}

	/* Pour éviter les zombies (vade retro...) */
	previous_handler = signal(SIGCHLD,(sig_t)zombie);
	if(previous_handler==SIG_ERR) {
		perror("Installing SIGCHLD handler");
    		exit(EXIT_FAILURE);
  	}

	/* Pour détecter le SIGTERM */
	previous_handler = signal(SIGTERM,(sig_t)sigterm);
	if(previous_handler==SIG_ERR) {
		perror("Installing SIGTERM handler");
		exit(EXIT_FAILURE);
	}
	
	nbrFils=0;
	
#ifndef LINUX
	setproctitle("ecoute le port %d",PORT);
#endif	

	openlog("beastie", LOG_PID|LOG_CONS,LOG_LOCAL2);

	serv_sock = socket(AF_INET, SOCK_STREAM, 0);
	if (serv_sock < 0)
	{
		perror("Creating server socket.");
		exit(EXIT_FAILURE);
	}

	/* On remplit la structure */
	bzero((char *) &serv_addr, sizeof (serv_addr));
	serv_addr.sin_family = AF_INET;
	serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    	serv_addr.sin_port = htons(PORT);

    	/* Et on lie la socket au port */
	if (bind(serv_sock, (struct sockaddr *) &serv_addr, sizeof (serv_addr)) < 0)
	{
		perror("Binding server socket.");
		exit(EXIT_FAILURE);
    	}
	
	listen(serv_sock,5);

	while(kZamok_Tourne) {
		
        cli_len = sizeof (cli_addr);
        bzero((char *) &cli_addr, sizeof (cli_addr));
        
        client_sock = accept(serv_sock, (struct sockaddr *) &cli_addr, &cli_len);
        if (client_sock < 0)
        {
            perror("Accepting connection on server socket.");
            break;
        }
       
	/* On forke, on traite la requete dans le fils */
	/* Evaluation paresseuse */
	if((nbrFils++ < kMaxDaemonChildren) && ((pid=fork())==0) ) {
		close(serv_sock);
		clientname=inet_ntoa(cli_addr.sin_addr); /* On recupere le nom */
	 	/* Hehe t'es loggue mon pote */
		if(strcmp(clientname, "127.0.0.1")) {
			if (!((cli_ent= gethostbyaddr((char *)&cli_addr.sin_addr.s_addr,
                	    sizeof (u_long), AF_INET)) == (struct hostent *)0)) { 
				snprintf(buffer,1023, "Tentative de connexion de %s au port %d.",cli_ent->h_name,PORT);
				syslog(LOG_NOTICE,buffer );
			}
			else {
				snprintf(buffer,1023, "Tentative de connexion de %s au port %d.",clientname,PORT);
				syslog(LOG_NOTICE,buffer);
			}
		}
		close(client_sock);
		exit(0);
	}
	/* Fin du fork */
	
        close(client_sock);

	}

	closelog();
	close(serv_sock);
	exit( 0);
}
