/*
  $Id: server.c 1889 2005-09-27 09:01:54Z paul $
 Copyright (C) 1999-2004 IC & S  dbmail@ic-s.nl

 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.
*/

/* 
 * server.c
 *
 * code to implement a network server
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "db.h"
#include "debug.h"
#include "server.h"
#include "pool.h"
#include "serverchild.h"
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <signal.h>


int GeneralStopRequested = 0;
int Restart = 0;
pid_t ParentPID = 0;
ChildInfo_t childinfo;

/* some extra prototypes (defintions are below) */
static void ParentSigHandler(int sig, siginfo_t * info, void *data);
static int SetParentSigHandler(void);

int SetParentSigHandler()
{
	struct sigaction act;

	/* init & install signal handlers */
	memset(&act, 0, sizeof(act));

	act.sa_sigaction = ParentSigHandler;
	sigemptyset(&act.sa_mask);
	act.sa_flags = SA_SIGINFO | SA_NOCLDSTOP;

	sigaction(SIGCHLD, &act, 0);
	sigaction(SIGINT, &act, 0);
	sigaction(SIGQUIT, &act, 0);
	sigaction(SIGILL, &act, 0);
	sigaction(SIGBUS, &act, 0);
	sigaction(SIGFPE, &act, 0);
	sigaction(SIGSEGV, &act, 0);
	sigaction(SIGTERM, &act, 0);
	sigaction(SIGHUP, &act, 0);

	return 0;
}


int StartServer(serverConfig_t * conf)
{
	int stopped = 0;
	if (!conf)
		trace(TRACE_FATAL, "StartServer(): NULL configuration");

	trace(TRACE_DEBUG, "StartServer(): init");
	
	/* make sure we can talk to the db before we spawn */
	if (db_connect() != 0) 
		return -1;
	if (db_check_version() != 0)
		return -1;
		
	ParentPID = getpid();
	Restart = 0;
	GeneralStopRequested = 0;
	SetParentSigHandler();
	
	childinfo.maxConnect = conf->childMaxConnect;
	childinfo.listenSocket = conf->listenSocket;
	childinfo.timeout = conf->timeout;
	childinfo.ClientHandler = conf->ClientHandler;
	childinfo.timeoutMsg = conf->timeoutMsg;
	childinfo.resolveIP = conf->resolveIP;
 	
 	trace(TRACE_DEBUG, "StartServer(): init ok. Creating children..");
 	scoreboard_new(conf);
 	manage_start_children();
 	manage_spare_children();
  
 	trace(TRACE_DEBUG, "StartServer(): children created, starting main service loop");
 	while (!GeneralStopRequested) {
		if (db_check_connection() != 0) {
			
			if (! stopped) 
				manage_stop_children();
			
			stopped=1;
			sleep(10);
			
		} else {
			if (stopped) { 
				manage_restart_children();
				stopped=0;
			}

			manage_spare_children();
			sleep(1);
		}
	}

 	manage_stop_children();
 	scoreboard_delete();
 
	return Restart;
}


void ParentSigHandler(int sig, siginfo_t * info, void *data)
{
	pid_t chpid;
	int saved_errno = errno;
	
	/* this call is for a child but it's handler is not yet installed */
	if (ParentPID != getpid()) 
		active_child_sig_handler(sig, info, data); 

	switch (sig) {
 
	case SIGCHLD:
		/* ignore, wait for child in main loop */
	        /* but we need to catch zombie */
		if ((chpid = waitpid(-1,&sig,WNOHANG)) > 0)
			scoreboard_release(chpid);
		
		break;		

	case SIGHUP:
		Restart = 1;
		/* fall-through */
		
	default:
		GeneralStopRequested = 1;
	}

	errno = saved_errno;
}


int CreateSocket(serverConfig_t * conf)
{
	int sock, r, len;
	struct sockaddr_in saServer;
	int so_reuseaddress = 1;
			   /**< reuseaddr to 1, so address will be reused */

	/* make a tcp/ip socket */
	sock = socket(PF_INET, SOCK_STREAM, 0);
	if (sock == -1)
		trace(TRACE_FATAL,
		      "CreateSocket(): socket creation failed [%s]",
		      strerror(errno));

	trace(TRACE_DEBUG, "CreateSocket(): socket created");

	/* make an (socket)address */
	memset(&saServer, 0, sizeof(saServer));

	saServer.sin_family = AF_INET;
	saServer.sin_port = htons(conf->port);

	if (conf->ip[0] == '*')
		saServer.sin_addr.s_addr = htonl(INADDR_ANY);
	else {
		r = inet_aton(conf->ip, &saServer.sin_addr);
		if (!r) {
			close(sock);
			trace(TRACE_FATAL,
			      "CreateSocket(): invalid IP [%s]", conf->ip);
		}
	}

	trace(TRACE_DEBUG, "CreateSocket(): socket IP requested [%s] OK",
	      conf->ip);

	/* set socket option: reuse address */
	setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &so_reuseaddress,
		   sizeof(so_reuseaddress));

	/* bind the address */
	len = sizeof(saServer);
	r = bind(sock, (struct sockaddr *) &saServer, len);

	if (r == -1) {
		close(sock);
		trace(TRACE_FATAL,
		      "CreateSocket(): could not bind address to socket");
	}

	trace(TRACE_DEBUG, "CreateSocket(): IP bound to socket");

	r = listen(sock, BACKLOG);
	if (r == -1) {
		close(sock);
		trace(TRACE_FATAL,
		      "CreateSocket(): error making socket listen [%s]",
		      strerror(errno));
	}

	trace(TRACE_INFO, "CreateSocket(): socket creation complete");
	conf->listenSocket = sock;

	return 0;
}



syntax highlighted by Code2HTML, v. 0.9.1