/***********************************************************************
*                                                                      *
*               This software is part of the ast package               *
*           Copyright (c) 1990-2006 AT&T Knowledge Ventures            *
*                      and is licensed under the                       *
*                  Common Public License, Version 1.0                  *
*                      by AT&T Knowledge Ventures                      *
*                                                                      *
*                A copy of the License is available at                 *
*            http://www.opensource.org/licenses/cpl1.0.txt             *
*         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
*                                                                      *
*              Information and Software Systems Research               *
*                            AT&T Research                             *
*                           Florham Park NJ                            *
*                                                                      *
*                 Glenn Fowler <gsf@research.att.com>                  *
*                                                                      *
***********************************************************************/
#pragma prototyped
                  
/*
 * Glenn Fowler
 * AT&T Research
 *
 * obsolete generic server state machine
 * <css.h> provides a discipline interface with multiple servers
 */

#include "csslib.h"

struct Server_s
{
	Cssdisc_t	disc;
	Css_t*		css;
	void*		handle;
	int		(*done)(void*, int);
	int		(*con)(void*, int, Csid_t*, int, char**);
	int		(*rd)(void*, int);
	int		(*wr)(void*, int);
	int		(*to)(void*);
};

static int
acceptf(Css_t* css, Cssfd_t* fp, Csid_t* ip, char** av, Cssdisc_t* disc)
{
	register Server_t*	server = (Server_t*)disc;

	return (*server->con)(server->handle, fp->fd, ip, 0, av) ? -1 : fp->fd;
}

static int
actionf(Css_t* css, Cssfd_t* fp, Cssdisc_t* disc)
{
	register Server_t*	server = (Server_t*)disc;

	switch (fp->status)
	{
	case CS_POLL_READ:
		if (server->rd)
			return (*server->rd)(server->handle, fp->fd) < 0 ? -1 : 1;
		break;
	case CS_POLL_WRITE:
		if (server->wr)
			return (*server->wr)(server->handle, fp->fd) < 0 ? -1 : 1;
		break;
	}
	return 0;
}

static int
exceptf(Css_t* css, unsigned long op, unsigned long arg, Cssdisc_t* disc)
{
	register Server_t*	server = (Server_t*)disc;

	switch (op)
	{
	case CSS_CLOSE:
		if (server->done)
			(*server->done)(server->handle, 0);
		return 0;
	case CSS_INTERRUPT:
		if (server->done && !(*server->done)(server->handle, EXIT_TERM(arg)))
			return 1;
		error(ERROR_SYSTEM|3, "%s: interrupt exit", fmtsignal(arg));
		return -1;
	case CSS_DORMANT:
	case CSS_TIMEOUT:
	case CSS_WAKEUP:
		return !server->to ? 0 : (*server->to)(server->handle) < 0 ? -1 : 1;
	}
	error(ERROR_SYSTEM|3, "poll error [op=%lu arg=%lu]", op, arg);
	return -1;
}

int
csfd(Cs_t* state, int fd, int op)
{
	return !state->server || cssfd(state->server->css, fd, op) ? -1 : 0;
}

/*
 * csserve() wakeup and timeout are mutually exclusive
 */

unsigned long
cstimeout(register Cs_t* state, unsigned long ms)
{
	unsigned long	rv;

	if (!state->server)
		return CS_NEVER;
	state->server->css->disc->wakeup = 0;
	rv = state->server->css->disc->timeout;
	state->server->css->disc->timeout = ms;
	return rv;
}

/*
 * csserve() wakeup and timeout are mutually exclusive
 */

unsigned long
cswakeup(Cs_t* state, unsigned long ms)
{
	unsigned long	rv;

	if (!state->server)
		return CS_NEVER;
	state->server->css->disc->timeout = 0;
	rv = state->server->css->disc->wakeup;
	state->server->css->disc->wakeup = ms;
	return rv;
}

/*
 * server state machine
 */

void
csserve(Cs_t* state, void* handle, const char* path, void* (*init)(void*, int), int (*done)(void*, int), int (*con)(void*, int, Csid_t*, int, char**), int (*rd)(void*, int), int (*wr)(void*, int), int (*to)(void*))
{
	register Server_t*	server;

	if (!con && !rd)
		error(ERROR_PANIC, "a connect or read handler must be supplied");
	if (!(server = newof(0, Server_t, 1, 0)))
		error(ERROR_SYSTEM|3, "out of space");
	state->server = server;
	server->disc.version = CSS_VERSION;
	server->disc.flags = CSS_DAEMON|CSS_LOG|CSS_CLOSE|CSS_ERROR|CSS_INTERRUPT|CSS_TIMEOUT|CSS_WAKEUP;
	server->handle = handle;
	server->con = con;
	if (server->con)
		server->disc.acceptf = acceptf;
	server->rd = rd;
	server->wr = wr;
	if (server->rd || server->wr)
		server->disc.actionf = actionf;
	server->to = to;
	server->disc.errorf = errorf;
	server->disc.exceptf = exceptf;
	server->done = done;
	close(0);
	if (!(server->css = cssopen(path, (Cssdisc_t*)server)))
		exit(1);
	error_info.id = server->css->service;
	state->id = server->css->id;
	state->cs = server->css->path;
	state->control = state->mount + (server->css->control - server->css->mount);
	strcpy(state->mount, server->css->mount);
#ifdef SIGCHLD
	if (!done)
		signal(SIGCHLD, SIG_DFL);
#endif

	/*
	 * we   the
	 *   are  
	 *         ser
	 *            ver
	 *
	 * in background
	 * no controlling tty
	 * pwd is the service data directory
	 * stdin is the connect stream
	 * stdout is /dev/null
	 * stderr is CS_MNT_LOG in the service data directory
	 * umask matches service mode
	 */

	if (init)
		server->handle = (*init)(server->handle, server->css->fdmax);
	csspoll(CS_NEVER, 0);
}

int
_cs_fd(int fd, int op)
{
	return csfd(&cs, fd, op);
}

void
_cs_serve(void* handle, const char* path, void* (*init)(void*, int), int (*done)(void*, int), int (*con)(void*, int, Csid_t*, int, char**), int (*rd)(void*, int), int (*wr)(void*, int), int (*to)(void*))
{
	csserve(&cs, handle, path, init, done, con, rd, wr, to);
}

unsigned long
_cs_timeout(unsigned long ms)
{
	return cstimeout(&cs, ms);
}

unsigned long
_cs_wakeup(unsigned long ms)
{
	return cswakeup(&cs, ms);
}
