/**********************************************************************
 
	Copyright (C) 2003 Hirohisa MORI <joshua@nichibun.ac.jp>
 
	This program is free software; you can redistribute it 
	and/or modify it under the terms of the GLOBALBASE 
	Library General Public License (G-LGPL) as published by 

	http://www.globalbase.org/
 
	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.

**********************************************************************/


#define STREAM_LIB

#include        <stdio.h>
#include        <sys/types.h>
#include        <sys/socket.h>
#include        <netinet/in.h>
#include	<fcntl.h>
#include	<stdlib.h>
#include	<errno.h>

#include	"memory_debug.h"
#include	"server.h"
#include	"task.h"

extern SEM stream_lock;
extern int max_fid,now_open_files;

int s_close_accept();
int s_error();
STREAM * s_error_stream();
STREAM * s_open_accept_desc;

S_TABLE s_accept_table = {
	'a',
	0,
	{0,0},
	0,
	s_error_stream,
	s_close_accept,
	s_error,
	s_error,
	s_error,
       	s_error,
	s_error_stream
};



STREAM *
init_server(int * open_port,int base_port,int limit)
{
int id; /* accept の返すファイル記述子 */
struct sockaddr_in server;
int len;
int len2;
int i,on;
int sd; /* socket の返すファイル記述子 */
int p;
STREAM * ret;
int f;

	/* socket の初期化 (UDP = 17)*/
	f = 0;
retry:
	errno = 0;
	if ((sd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
		if ( f == 0 && errno == EMFILE && stream_gc ) {
			f = 1;
			(*stream_gc)();
			goto retry;
		}
		return 0;
	}
	on = 1;
	if (setsockopt(sd,
			SOL_SOCKET,
			SO_REUSEADDR, (char *)&on, sizeof(int)) < 0) {
		close(sd);
        	return 0;
	}

	fcntl(sd,F_SETFL,O_RDWR);

	for ( p = base_port ; p < base_port + limit ; p ++ ) {
		server.sin_family = AF_INET;
		server.sin_addr.s_addr = INADDR_ANY;
		server.sin_port = htons((unsigned short)p);
		len = sizeof(server);
		if (bind(sd, (struct sockaddr*)&server, len) == -1) {
			continue;
		}
		break;
	}
	if ( p == base_port + limit ) {
		close(sd);
        	return 0;
	}

	if (listen(sd, 5) == -1) {
		close(sd);
        	return 0;
	}
	*open_port = p;
	ret = d_alloc(sizeof(S_ACCEPT));
	ret->h.tbl = &s_accept_table;
	ret->h.thread = 0;
	ret->accept.fid = sd;
	lock_task(stream_lock);
	_s_open(ret,O_RDONLY);
	now_open_files ++;
	if ( max_fid < sd )
		max_fid = sd;
	unlock_task(stream_lock,"init_server");
	return ret;
}

int
s_close_accept(STREAM * s)
{
	_s_close_sync(s,s->accept.fid);
	close(s->accept.fid);
	now_open_files --;
	return 0;
}
