/*
 * Copyright (c) 1996, 1997, 1998, 1999 The University of Utah and
 * the Computer Systems Laboratory at the University of Utah (CSL).
 * All rights reserved.
 *
 * Permission to use, copy, modify and distribute this software is hereby
 * granted provided that (1) source code retains these copyright, permission,
 * and disclaimer notices, and (2) redistributions including binaries
 * reproduce the notices in supporting documentation.
 *
 * THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS
 * IS" CONDITION.  THE UNIVERSITY OF UTAH AND CSL DISCLAIM ANY LIABILITY OF
 * ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
 *
 * CSL requests users of this software to return to csl-dist@cs.utah.edu any
 * improvements that they make and grant CSL redistribution rights.
 */

#include "suntcp-link.h"

/* This stuff is used by Flick for transport independent
 * buffer management & message transmission.
 * This should NOT be used by user code - Flick's code is the only stuff
 * that should be using it.
 */

int
flick_server_get_request(FLICK_TARGET target,
			 FLICK_BUFFER *out_buf)
{
	return flick_read_buf(out_buf, target->socket_fd);
}

int
flick_server_send_reply(FLICK_TARGET target,
			FLICK_BUFFER *in_buf)
{
	return flick_write_buf(in_buf, target->socket_fd);
}

/* This one _can_ be used to register servers
   (if they want to build their own main function)
   */
typedef struct flick_server_list
{
	FLICK_SERVER_DESCRIPTOR server;
	FLICK_SERVER func;
	int sock;
	int listen;
	struct flick_server_list *next;
} flick_server_list;

static flick_server_list *lst = 0;

int
flick_server_register(FLICK_SERVER_DESCRIPTOR server, FLICK_SERVER func)
{
	flick_server_list *itm = (flick_server_list *)malloc(sizeof(flick_server_list));
	if (!itm)
		return 0;
	itm->next = lst;
	itm->server = server;
	itm->func = func;
	itm->listen = 1;
	itm->sock = 0;
	lst = itm;
	return 1;
}


static int
get_a_socket(void)
{
	int res = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	int socket_reuseaddr_p = 1;
	int val = SOCKET_BUF_SIZE;

	if (res < 0) {
		perror("TCP Socket creation problem");
		exit(1);
	}
	if (setsockopt(res,
		       SOL_SOCKET, SO_REUSEADDR,
		       ((void *) &socket_reuseaddr_p),
		       sizeof(socket_reuseaddr_p))
	    != 0) {
		perror("setsockopt");
		close(res);
		exit(1);
	}
	
#ifdef SO_SNDBUF
	printf("Server sendbuf: %d %d %d\n", setsockopt(res, SOL_SOCKET, SO_SNDBUF, ((void *) &val), sizeof(val)), errno, val);
#else
	printf("Server sendbuf: Using default settings\n");
#endif
#ifdef SO_RCVBUF
	printf("Server recvbuf: %d %d %d\n", setsockopt(res, SOL_SOCKET, SO_RCVBUF, ((void *) &val), sizeof(val)), errno, val);
#else
	printf("Server recvbuf: Using default settings\n");
#endif

	return res;
}

/* This one _can_ be used to begin grabbing messages
   (if they want to build their own main function)
   */
void
flick_server_run()
{
	int maxconn = -1;
	fd_set fds;
	flick_server_list *tmp = lst;
	struct sockaddr_in server;
	short port = -1;
	int sock;
	int len = sizeof(struct sockaddr_in);
	FLICK_BUFFER the_buf, return_buf;
	
	the_buf.real_buf_start = the_buf.buf_read = the_buf.buf_start =
	         the_buf.buf_current = (void *)calloc(8192, 1);
	
	the_buf.real_buf_end = the_buf.buf_end = (((char *) the_buf.buf_start)
						  + 8188);
	
	return_buf.real_buf_start = return_buf.real_buf_end =
	      return_buf.buf_read = 0;
	return_buf.buf_start  = return_buf.buf_current = (void *)calloc(8192, 1);
	return_buf.buf_end = ((char *) return_buf.buf_start) + 8188;
	
	if(!the_buf.buf_start || !return_buf.buf_start) {
		perror("Insufficient memory for server buffers");
		exit(1);
	}
	
	FD_ZERO(&fds);
	
	/* Get the listening socket & port for this machine */
	while(tmp) {
		
		/* Get a socket for this skeleton function */
		sock = get_a_socket();
		memset((char *)&server, 0, sizeof(server));
		server.sin_family = AF_INET;
		
		/* Get a port for this skeleton function */
		server.sin_port = 0;
		if (bind(sock, (struct sockaddr *) &server, len) != 0) {
			perror("cannot `bind' to socket");
			exit(1);
		}
		if ((getsockname(sock, (struct sockaddr *) &server, &len) != 0)
		    || (listen(sock, 8) != 0)) {
			perror("cannot `getsockname' or `listen'");
			exit(1);
		}
		
		tmp->sock = sock;
		port = ntohs(server.sin_port);
		
		/* Register the port with the RPC port mapper & initialize the fd_set */
		pmap_unset(tmp->server.prog_num, tmp->server.vers_num);
		pmap_set(tmp->server.prog_num, tmp->server.vers_num, IPPROTO_TCP, port);
		FD_SET(sock, &fds);
		
		if (sock > maxconn)
			maxconn = sock;
		
		tmp = tmp->next;
	}
	
	/* Accept new clients, read client requests & send replies forever... */
	while (1) {
		int qty = select(maxconn + 1, &fds, 0, 0, 0);
		flick_server_list *last = 0;
		
		tmp = lst;
		while (qty > 0) {
			if (FD_ISSET(tmp->sock, &fds)) {
				if (tmp->listen) {
					flick_server_list *new_list = (flick_server_list *)malloc(sizeof(flick_server_list));
					if (!new_list)
						exit(1);
					new_list->next = lst;
					lst = new_list;
					lst->sock = accept(tmp->sock, 0, 0);
					lst->func = tmp->func;
					lst->server = tmp->server;
					lst->listen = 0;
					if (lst->sock > maxconn)
						maxconn = lst->sock;
				} else do {
					FLICK_TARGET_STRUCT ts;
					ts.socket_fd = tmp->sock;
					ts.port = port;
					
					if (flick_server_get_request(&ts, &the_buf)) {
						switch ((*tmp->func)(&the_buf, &return_buf)) {
						case FLICK_OPERATION_SUCCESS:{
							flick_server_send_reply(&ts, &return_buf);
							break;
						}
						case FLICK_OPERATION_SUCCESS_NOREPLY:{
							break;
						}
						default /* FLICK_OPERATION_FAILURE */ :{
							/* the socket has been closed */
							if (last) {
								last->next = tmp->next;
								free(tmp);
								tmp = last;
							} else {
								lst = tmp->next;
								free(tmp);
								tmp = 0;
							}
							break;
						}}
					} else {
						/* the socket has been closed */
						if (last) {
							last->next = tmp->next;
							free(tmp);
							tmp = last;
						} else {
							lst = tmp->next;
							free(tmp);
							tmp = 0;
						}
					}
				} while (flick_buffer_contains_more(&the_buf));
				qty--;
			}
			last = tmp;
			if (tmp)
				tmp = tmp->next;
			else
				tmp = lst;
		}
		if (qty < 0)
			fprintf(stderr,"Error %d in server\n", errno);
		
		/* reset the file descriptor list */
		FD_ZERO(&fds);
		tmp = lst;
		while (tmp) {
			FD_SET(tmp->sock, &fds);
			tmp = tmp->next;
		}
	}
	exit(1);
}

/* End of file. */



syntax highlighted by Code2HTML, v. 0.9.1