
/*
    LinuxWare daemon - Netware like server for Linux

    Copyright (C) 1994, 1995  Ales Dryak <e-mail: A.Dryak@sh.cvut.cz>

    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.

*/
/*
std comments:
  hard wired
  dummy
*/

#include <stdlib.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include "global.h"
#include "ncp.h"
#include "sap2bind.h"
#include "wdog.h"
#include "timer.h"
#include "signals.h"
#include "fsystem.h"
#include "logging.h"
#include "msgops.h"
#include "bindauth.h"
#include "util.h"

#define SS_START	0
#define SS_INIT		1
#define SS_RUN		2
#define SS_SHUTDOWN	3
#define SS_DOWN		4

static server_state=SS_START;

void init_timezone()
{
	/* initialize zoneinfo prior to possible chroot */
	time_t t=0;
	localtime(&t);
	LPRINTF(LL_INFO,("timezone initialized\n"));
}

void done_timezone()
{
	LPRINTF(LL_INFO,("timezone shutdown\n"));
}

void init_log()
{
	FILE* log_file;
	log_file=fopen(LOG_FILENAME,"a");
	if (log_file==NULL) {LPRINTF(LL_FATAL,("lwared: open %s\n",strerror(errno)));exit(1);}
	log_set_file(log_file);
	log_set_level(cmdline.log_level);
	LPRINTF(LL_INFO,("logging initialized\n"));
}

void done_log()
{
	LPRINTF(LL_INFO,("logging shutdown\n"));
	fclose(log_set_file(stderr));
}

void init()
{
	init_log();
	daemonize();
	if (server_state!=SS_START)
	{
		LPRINTF(LL_FATAL,("init() called out of order (state=%i)\n",server_state));
		exit(1);
	}
	server_state=SS_INIT;
	LOG_ENTRY
	LPRINTF(LL_INFO,("initializing\n"));
	init_timezone();
	init_signals();
	init_timer();
	init_server_object();
	init_fsystem();
	init_connections();
	init_bindery();
	init_ncp();
	init_wdog();
	init_sap();
#if 0
	if (chroot("/lwfs")!=0)
	{
		LPRINTF(LL_FATAL,("chroot: %s\n",strerror(errno)));
		exit(1);
	}
#endif
}

void run()
{
	int size;
	char buf[BUF_SIZE];
	struct timeval tmv;
	int ticks_to_down=DOWN_DELAY;

	if (server_state!=SS_INIT)
	{
		LPRINTF(LL_FATAL,("run() called out of order (state=%i)\n",server_state));
		exit(1);
	}
	server_state=SS_RUN;
	
	tmv.tv_sec=1;
	tmv.tv_usec=0;
	
	LOG_ENTRY
	LPRINTF(LL_INFO,("initialization complete\n"));

	while(1)
	{
		fd_set rd_fds,wr_fds,ex_fds;
		int sres;

		LOG_ENTRY
		
		FD_ZERO(&rd_fds);
		FD_ZERO(&wr_fds);
		FD_ZERO(&ex_fds);
		FD_SET(ncp_sock,&rd_fds);
		FD_SET(sap_sock,&rd_fds);
		FD_SET(wdog_sock,&rd_fds);
		
		LPRINTF(LL_DEBUG,("selecting...\n"));
		sres=select(getdtablesize(),&rd_fds,&wr_fds,&ex_fds,server_state==SS_RUN?NULL:&tmv);
		LPRINTF(LL_DEBUG,("selected %i filedes\n",sres));
		if (sres==-1 && errno!=EINTR)
		{
			LPRINTF(LL_ERROR,("select: %s\n",strerror(errno)));
		}
		if (int_request)
		{
			int_request=0;
			LPRINTF(LL_INFO,("received signal 2 (SIGINT) - ignored\n"));
		}
		if (terminate_request!=SG_CONTINUE && server_state==SS_RUN)
		{
			switch (terminate_request)
			{
				case SG_TERMINATE:
					LPRINTF(LL_INFO,("received signal 15 (SIGTERM) - exiting\n"));
					break;
				case SG_HANGUP:
					LPRINTF(LL_INFO,("received signal 1 (SIGHUP) - exiting\n"));
					break;
				default:
					LPRINTF(LL_ERROR,("unknown terminate request - exiting\n"));
					
			}
			{
				char buf[200];
				sprintf(buf,"Server %s is going down",SERVER_NAME);
				LPRINTF(LL_INFO,("%s\n",buf));
				send_broadcast_message(buf);
				server_state=SS_SHUTDOWN;
			}
		}
		if (timer_request)
		{
			handle_timer();
			timer_request=0;
		}
		if (server_state==SS_SHUTDOWN && tmv.tv_sec==0 && tmv.tv_usec==0)
		{
			if ((--ticks_to_down)==0) return;
			notify_message_all();
			tmv.tv_sec=1;
			tmv.tv_usec=0;
		}
		if (sres==-1) 
		{
			continue;
		}
		
		if (FD_ISSET(ncp_sock,&rd_fds))
		{
			struct sockaddr_ipx source;
			int sz=sizeof(source);

			sres--;
			size=recvfrom(ncp_sock,(void*)buf,sizeof(buf),0,(struct sockaddr*)&source,&sz);
			if(size!=-1) 
			{
				LOG_START(LL_DEBUG)
				log_printf("NCP size: %d from ",size);
				ipx_fprint_saddr(log_file,&source);
				log_printf("\n");
				ipx_ncp_fdump(log_file,buf,size);
				LOG_END

				handle_ncp(buf,size,&source);
			}
			else
			{
				LPRINTF(LL_ERROR,("recvfrom NCP socket: %s\n",strerror(errno)));
			}
		}
		if (FD_ISSET(sap_sock,&rd_fds))
		{
			struct sockaddr_ipx source;
			int sz=sizeof(source);

			sres--;
			size=recvfrom(sap_sock,(void*)buf,sizeof(buf),0,(struct sockaddr*)&source,&sz);
			if(size!=-1) 
			{
				LOG_START(LL_DEBUG)
				log_printf("SAP size: %d from ",size);
				ipx_fprint_saddr(log_file,&source);
				log_printf("\n");
				ipx_sap_fdump(log_file,(SapPacket*)buf,size);
				LOG_END

				handle_sap((SapPacket*)buf,size,&source);
			}
			else
			{
				LPRINTF(LL_ERROR,("recvfrom SAP socket: %s\n",strerror(errno)));
			}
		}
		if (FD_ISSET(wdog_sock,&rd_fds))
		{
			struct sockaddr_ipx source;
			int sz=sizeof(source);

			sres--;
			size=recvfrom(wdog_sock,(void*)buf,sizeof(buf),0,(struct sockaddr*)&source,&sz);
			if(size!=-1) 
			{
				LOG_START(LL_DEBUG)
				log_printf("WDOG size: %d from ",size);
				ipx_fprint_saddr(log_file,&source);
				log_printf("\n");
				ipx_wdog_fdump(log_file,(WatchDogPacket*)buf,size);
				LOG_END

				handle_wdog((WatchDogPacket*)buf,size,&source);
			}
			else
			{
				LPRINTF(LL_ERROR,("recvfrom WDOG socket: %s\n",strerror(errno)));
			}
		}
		if (sres!=0) LPRINTF(LL_ERROR,("inconsistence: sres: %i != 0\n",sres));
	}
}

void done()
{
	if (server_state!=SS_SHUTDOWN)
	{
		LPRINTF(LL_FATAL,("done() called out of order (state=%i)\n",server_state));
		exit(1);
	}
	
	done_sap();
	done_wdog();
	done_ncp();
	done_bindery();
	done_connections();
	done_fsystem();
	done_server_object();
	done_timer();
	done_signals();
	done_timezone();
	LPRINTF(LL_INFO,("server is down\n"));
	server_state=SS_DOWN;
	done_log();
}

int main(int argc,char **argv)
{
	parse_cmdline(argc,argv);
	init();
	run();
	done();
	return 0;
}
