#include	<stdio.h>
#include	<string.h>
#include	<ctype.h>
#include	<stdlib.h>
#include	<sys/socket.h>
#include	<netinet/in.h>
#include	<netinet/tcp.h>
#include	<netdb.h>
#include	<signal.h>
#include	<errno.h>
#include	<fcntl.h>

/*
#define	DB(x)	x
*/
#define	DB(x)

#define	VERSION	"Server version 0.1"

int	Soc=0;

void main()
{
	/* バージョンの表示 */
	PrintVersion();

	/* パラメータ設定 */
	InitParam();

	/* ソケットの初期設定 */
	if(InitSocket()==-1){
		exit(-1);
	}

	/* シグナルの設定 */
	InitSignal();

	/* メインループ */
	MainLoop();

	/* ソケットのクローズ */
	CloseSocket();
}

int PrintVersion()
{
char	buf[80];

	DB(fprintf(stderr,"PrintVersion()\n"));

	VersionString(buf);
	printf("%s\n",buf);

	return(0);
}

int VersionString(char *buf)
{
	strcpy(buf,VERSION);

	return(0);
}

int InitParam()
{
	DB(fprintf(stderr,"InitParam()\n"));

	return(0);
}

int InitSocket()
{
char	hostname[MAXHOSTNAMELEN];
struct hostent	*myhost;
struct servent	*se;
struct sockaddr_in	me;
int	opt;

	DB(fprintf(stderr,"InitSocket()\n"));

	if(gethostname(hostname,MAXHOSTNAMELEN)==-1){
		perror("gethostname");
		return(-1);
	}
	DB(fprintf(stderr,"gethostname:%s\n",hostname));

	if((se=getservbyname("TestServer","tcp"))==NULL){
		perror("getservbyname");
		return(-1);
	}
	DB(fprintf(stderr,"getservbyname:%d\n",se->s_port));

	if((myhost=gethostbyname(hostname))==NULL){
		perror("gethostbyname");
		return(-1);
	}
	DB(fprintf(stderr,"gethostbyname:%d.%d.%d.%d\n",(unsigned char)myhost->h_addr_list[0][0],
					       		(unsigned char)myhost->h_addr_list[0][1],
					       		(unsigned char)myhost->h_addr_list[0][2],
					       		(unsigned char)myhost->h_addr_list[0][3]));

	if((Soc=socket(AF_INET,SOCK_STREAM,0))<0){
		perror("socket");
		return(-1);
	}
	DB(fprintf(stderr,"socket:%d\n",Soc));

	opt=1;
	if(setsockopt(Soc,SOL_SOCKET,SO_REUSEADDR,(char *)&opt,sizeof(int))!=0){
		perror("setsockopt");
		return(-1);
	}
	DB(fprintf(stderr,"setsockopt:OK\n"));

	memset((char *)&me,0,sizeof(me));
	me.sin_family=AF_INET;
	me.sin_port=se->s_port;
	if(bind(Soc,(struct sockaddr *)&me,sizeof(me))==-1){
		perror("bind");
		return(-1);
	}
	DB(fprintf(stderr,"bind:OK\n"));

	return(0);
}

void EndSignal(int sig)
{
	DB(fprintf(stderr,"EndSignal()\n"));

	CloseSocket();

	exit(0);
}

void EndChild(int sig)
{
int	pid;

	pid=wait(0);
	DB(fprintf(stderr,"wait:pid=%d\n",pid));

	signal(SIGCLD,EndChild);
}

int InitSignal()
{
	DB(fprintf(stderr,"InitSignal()\n"));

	signal(SIGINT,EndSignal);
	signal(SIGTERM,EndSignal);
	signal(SIGCLD,EndChild);

	return(0);
}

int MainLoop()
{
int	acc;
struct sockaddr_in	from;
int	len;
char	client_address[80];
int	pid;

	DB(fprintf(stderr,"MainLoop()\n"));

	listen(Soc,SOMAXCONN);
	while(1){
		len=sizeof(from);
		acc=accept(Soc,(struct sockaddr *)&from,&len);
		if(acc<0){
			if(errno!=EINTR){
				perror("accept");
			}
			continue;
		}
		sprintf(client_address,"%d.%d.%d.%d",from.sin_addr.S_un.S_un_b.s_b1,
						       from.sin_addr.S_un.S_un_b.s_b2,
						       from.sin_addr.S_un.S_un_b.s_b3,
						       from.sin_addr.S_un.S_un_b.s_b4);
		DB(fprintf(stderr,"accept:%s\n",client_address));
		if((pid=fork())==0){
			/* 子プロセス */
			close(Soc);
			Soc=0;
			ChildLoop(acc,client_address);
			exit(0);
		}
		close(acc);
		acc=0;
	}

	return(0);
}

int CloseSocket()
{
	DB(fprintf(stderr,"CloseSocket()\n"));

	if(Soc>0){
		close(Soc);
	}
	Soc=0;

	return(0);
}

int SetBlock(int fd,int flag)
{
int	flags;

	if((flags=fcntl(fd,F_GETFL,0))==-1){
		perror("fcntl");
		return(-1);
	}
	if(flag==0){
		fcntl(fd,F_SETFL,flags|O_NDELAY);
	}
	else if(flag==1){
		fcntl(fd,F_SETFL,flags&~O_NDELAY);
	}
	return (0);
}
