/**********************************************************************
 
	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	"machine/err.h"
#include	<stdlib.h>
#include	"memory_debug.h"
#include	"server.h"
#include	"task.h"

int accept_test;
int accept_tid;



extern SEM stream_lock;
extern int now_open_files;
extern int max_fid;

typedef struct accept_t {
	int			fid;
	struct sockaddr* 	client;
	int *			lenp;
	int			ret;
	int			err;
} ACCEPT_T;

void
do_accept(ACCEPT_T * arg)
{
	errno = 0;
	arg->ret = accept(arg->fid,
			arg->client,
			arg->lenp);
	arg->err = errno;
}


ACCESS_KEY *
accept_connection(STREAM * sd)
{
char buf[100];
int len;
int len2;
int id;
struct sockaddr_in client;
ACCESS_KEY * key;
extern S_TABLE s_file_table,s_accept_table;
int f;
S_FILE_THREAD t;
ACCEPT_T a;

	lock_task(stream_lock);
	if ( sd == 0 ) {
		unlock_task(stream_lock,"s_write");
		return 0;
	}
	if ( sd->h.tbl == 0 ) {
		unlock_task(stream_lock,"s_write");
		return 0;
	}
	if ( sd->h.tbl != &s_accept_table ) {
		unlock_task(stream_lock,"s_write");
		return 0;
	}
	_s_insert_thread(sd,&t);
	unlock_task(stream_lock,"s_write");



	for ( ; ; ) {
		len2 = sizeof(client);
		f = 0;
	retry:
		errno = 0;
		a.fid = sd->accept.fid;
		a.client = (struct sockaddr*)&client;
		a.lenp = &len2;
		ss_do((void(*)(void*))do_accept,&a);
		id = a.ret;
		if ( a.ret == -1) {
			if ( f = 0 && a.err == EMFILE && stream_gc ) {
				f = 1;
				if ( stream_gc )
					(*stream_gc)();
				goto retry;
			}
			if ( a.err == EINTR ) {
				if ( sd->h.tbl )
					goto retry;
				key = 0;
				goto end;
			}
			if ( a.err != ESYS_AGAIN ) {
				key = 0;
				goto end;
			}
			continue;
		}
		break;
	}
	fcntl(id,F_SETFL,O_RDWR);
	key = d_alloc(sizeof(*key));
	key->s = d_alloc(sizeof(S_FILE));
	key->s->h.tbl = &s_file_table;
	key->s->h.thread = 0;
	key->s->file.fid = id;
	key->s->file.filename = 0;
	lock_task(stream_lock);
	_s_open(key->s,O_RDWR);
	now_open_files ++;
	unlock_task(stream_lock,"accept_connection");
	key->ip = ntohl(client.sin_addr.s_addr);
	key->work = 0;
	if ( max_fid < id )
		max_fid = id;

if ( accept_tid == get_tid() )
accept_test = 115;

end:

	s_delete_thread(sd,&t);

if ( accept_tid == get_tid() )
accept_test = 116;

	return key;
}

