/*
 * Copyright (c) 2000-2005 Sendmail, Inc. and its suppliers.
 *      All rights reserved.
 * Copyright (c) 1990, 1993
 *	The Regents of the University of California.  All rights reserved.
 *
 * This code is derived from software contributed to Berkeley by
 * Chris Torek.
 *
 * By using this file, you agree to the terms and conditions set
 * forth in the LICENSE file which can be found at the top level of
 * the sendmail distribution.
 */

#include "sm/generic.h"
SM_RCSID("@(#)$Id: findfp.c,v 1.36 2006/10/05 04:27:37 ca Exp $")
#include "sm/param.h"
#include "sm/error.h"
#include "sm/string.h"
#include "sm/time.h"
#include "sm/io.h"
#include "sm/assert.h"
#include "sm/heap.h"
#include "sm/rpool.h"
#include "sm/string.h"
#include "io-int.h"
#include "io-glue.h"
#if MTA_USE_PTHREADS
# include "sm/pthread.h"
#endif

#define smio(flags, fileno, name)	\
	SM_FILE_STRUCT(name, fileno, sm_stdopen, sm_stdclose, \
		sm_stdread, sm_stdwrite, NULL, NULL, sm_stdseek, \
		sm_stdgetinfo, sm_stdsetinfo, flags, SM_TIME_FOREVER)

/* An open type to map to fopen()-like behavior */
sm_file_T SmFtStdio_def = smio((SMRW|SMFBF), -1, "stdio");

/* An open type to map to fdopen()-like behavior */
sm_file_T SmFtStdiofd_def =
	SM_FILE_STRUCT("stdiofd", -1, sm_stdfdopen, sm_stdclose, sm_stdread, \
		sm_stdwrite, NULL, NULL, sm_stdseek, sm_stdgetinfo, \
		sm_stdsetinfo, (SMRW|SMFBF), SM_TIME_FOREVER);

#if 0
/* A string file type */
sm_file_T _SmFtString_def =
	SM_FILE_STRUCT("strings", -1, sm_stropen, sm_strclose, sm_strread, \
		sm_strwrite, NULL, NULL, sm_strseek, sm_strgetinfo, \
		sm_strsetinfo, (SMRW|SMNBF), SM_TIME_FOREVER);
#endif /* 0 */

#if 0
/* A file type for syslog communications */
/* change this to use ISC or sm_log_* logging?? */
sm_file_T SmFtSyslog_def =
	SM_FILE_STRUCT("syslog", -1, sm_syslogopen, sm_syslogclose, sm_syslogread, sm_syslogwrite, NULL, NULL, sm_syslogseek, sm_sysloggetinfo, sm_syslogsetinfo, (SMRW|SMNBF), SM_TIME_FOREVER);
#endif /* 0 */

#define SM_FNDYNAMIC 10		/* add ten more whenever necessary */

#if 0
/* sm_magic p r w flags file bf lbfsize cookie ival */
#define smstd(flags, file, name)			\
	SM_FILE_STRUCT(name, file, sm_stdioopen, sm_stdioclose, \
		sm_stdioread, sm_stdiowrite, NULL, NULL, sm_stdioseek, \
		sm_stdiogetinfo, sm_stdiosetinfo, flags, SM_TIME_FOREVER)

/* A file type for interfacing to stdio FILE* streams. */
sm_file_T SmFtRealStdio_def = smstd(SMRW|SMNBF, -1, "realstdio");
#endif /* 0 */

/*
**  This data must be protected in a multi-threaded system!
*/

				/* the usual - (stdin + stdout + stderr) */
static sm_file_T usual[SM_IO_OPEN_MAX - SMIO_FILES];
static sm_glue_T smuglue = { 0, SM_IO_OPEN_MAX - SMIO_FILES, usual };

/* List of builtin automagically already open file pointers */
sm_file_T SmIoF[] =
{
	smio(SMRD, SMIOIN_FILENO, "smioin"),	/* smioin */
	smio(SMWR, SMIOOUT_FILENO, "smioout"),	/* smioout */
	smio(SMWR|SMNBF, SMIOERR_FILENO, "smioerr"),	/* smioerr */
};

/* Structure containing list of currently open file pointers */
sm_glue_T smglue = { &smuglue, 3, SmIoF };
#if MTA_USE_PTHREADS
static pthread_mutex_t sm_io_mutex = PTHREAD_MUTEX_INITIALIZER;
# define LOCK_FILES()						\
	do							\
	{							\
		int r;						\
								\
		r = pthread_mutex_lock(&sm_io_mutex);		\
		if (r != 0)					\
		{						\
			/* COMPLAIN */				\
			return sm_error_temp(SM_EM_IO, r);			\
		}						\
	} while (0)

# define UNLOCK_FILES()						\
	do							\
	{							\
		int r;						\
								\
		r = pthread_mutex_unlock(&sm_io_mutex);		\
		if (r != 0)					\
		{						\
			/* COMPLAIN */				\
			SM_ASSERT(r == 0);			\
		}						\
	} while (0)

#else /* MTA_USE_PTHREADS */
# define LOCK_FILES()
# define UNLOCK_FILES()
#endif /* MTA_USE_PTHREADS */

/*
**  SM_MOREGLUE -- adds more space for open file pointers
**
**	Parameters:
**		n -- number of new spaces for file pointers
**
**	Returns:
**		NULL if no more memory.
**		Otherwise, returns a pointer to new spaces.
**		ToDo: Should return better error description.
**		However, that requires that sm_malloc() returns it too.
*/

static sm_file_T empty;

static sm_glue_T *
sm_moreglue(int n)
{
	sm_glue_T *g;
	sm_file_T *p;

	g = (sm_glue_T *) sm_malloc(sizeof(*g) + SM_ALIGN_BITS +
					    n * sizeof(sm_file_T));
	if (g == NULL)
		return NULL;
	p = (sm_file_T *) SM_ALIGN(g + 1);
	g->gl_next = NULL;
	g->gl_niobs = n;
	g->gl_iobs = p;
	while (--n >= 0)
		*p++ = empty;
	return g;
}

/*
**  SM_FP -- allocate and initialize an SM_FILE structure
**
**	Parameters:
**		t -- file type requested to be opened.
**		flags -- control flags for file type behavior
**		oldfp -- file pointer to reuse if available (optional)
**		newfp -- new file pointer.
**
**	Returns:
**		usual sm return type.
*/

sm_ret_T
sm_fp(const sm_stream_T *t, const sm_f_flags_T flags, sm_file_T *oldfp,
	sm_file_T **newfp)
{
	sm_file_T *fp;
	int n;
	sm_glue_T *g;

	/* get/putbuf?? */
	SM_REQUIRE(t->fs_open != NULL && t->fs_close != NULL &&
		   (t->fs_read != NULL || t->fs_write != NULL));

	if (oldfp != NULL)
	{
		fp = oldfp;
		goto found; /* for opening reusing an 'fp' */
	}

	LOCK_FILES();
	for (g = &smglue;; g = g->gl_next)
	{
		for (fp = g->gl_iobs, n = g->gl_niobs; --n >= 0; fp++)
		{
			if (fp->sm_magic == SM_MAGIC_NULL)
				goto found;
		}
		if (g->gl_next == NULL)
		{
			g->gl_next = sm_moreglue(SM_FNDYNAMIC);
			if (g->gl_next == NULL)
			{
				UNLOCK_FILES();
				return sm_error_temp(SM_EM_IO, ENOMEM);
			}
		}
	}
found:
	/* why not memzero() the structure?? */

	/* can we really set this so early?? */
	fp->sm_magic = SM_FILE_MAGIC; /* 'fp' now in-use */
	UNLOCK_FILES();		/* hence we can unlock it */
	f_p(*fp) = NULL;	/* no current pointer */
	f_w(*fp) = 0;		/* nothing to write */
	f_r(*fp) = 0;		/* nothing to read */
	f_flags(*fp) = flags;
	f_fd(*fp) = -1;		/* no file */
	f_bfbase(*fp) = NULL;	/* no buffer */
	f_bfsize(*fp) = 0;	/* no buffer size with no buffer */
	f_rd_bfbase(*fp) = NULL;
	f_wr_bfbase(*fp) = NULL;
	f_rd_bfsize(*fp) = 0;
	f_wr_bfsize(*fp) = 0;
	fp->f_cookie = fp;	/* default: *open* overrides cookie setting */
	fp->f_stream = *t;

	if (fp->f_timeout == SM_TIME_DEFAULT)
		fp->f_timeout = SM_TIME_FOREVER;
#if 0
	else
		fp->f_timeout = t->fs_timeout; /* traditional behavior */
#endif

	*newfp = fp;
	return SM_SUCCESS;
}

/*
**  SM_IO_SETINFO -- change info for an open file type (fp)
**
**	The generic SM_IO_WHAT_VECTORS is auto supplied for all file types.
**	If the request is to set info other than SM_IO_WHAT_VECTORS then
**	the request is passed on to the file type's specific setinfo vector.
**	WARNING: this is working on an active/open file type.
**
**	Parameters:
**		fp -- file to make the setting on
**		what -- type of information to set
**		valp -- structure to obtain info from
**
**	Returns:
**		usual sm_error code
*/

sm_ret_T
sm_io_setinfo(sm_file_T *fp, int what, void *valp)
{
	sm_file_T *v = (sm_file_T *) valp;

	SM_IS_FP(fp);
	switch (what)
	{
	  case SM_IO_WHAT_VECTORS:

		/*
		**  This is the "generic" available for all.
		**  This allows the function vectors to be replaced
		**  while the file type is active.
		*/

		SM_FILE_FUNCT_ASSIGN(fp, v);
		return SM_SUCCESS;

	  case SM_IO_WHAT_TIMEOUT:
		fp->f_timeout = *((int *)valp);
		return SM_SUCCESS;

	  case SM_IO_DOUBLE:
		if (!sm_io_double(fp))
		{
			sm_io_setdouble(fp);
			fp->f_wrbuf.smb_flags = f_flags(*fp);
			fp->f_rdbuf.smb_flags = f_flags(*fp);
			fp->f_wrbuf.smb_fd = f_fd(*fp);
			fp->f_rdbuf.smb_fd = f_fd(*fp);
		}
		return SM_SUCCESS;

	  case SM_IO_WHAT_RD_FD:
		if (!sm_io_double(fp))
			return sm_error_perm(SM_EM_IO, EBADF);
		f_rd_fd(*fp) = *((int *)valp);
		return SM_SUCCESS;

	  case SM_IO_WHAT_WR_FD:
		if (!sm_io_double(fp))
			return sm_error_perm(SM_EM_IO, EBADF);
		f_wr_fd(*fp) = *((int *)valp);
		return SM_SUCCESS;
	}

	/* Otherwise the vector will check it out */
	if (fp->f_stream.fs_setinfo == NULL)
		return sm_error_perm(SM_EM_IO, EINVAL);
	return (*fp->f_stream.fs_setinfo)(fp, what, valp);
}

/*
**  SM_IO_GETINFO -- get information for an active file type (fp)
**
**  This function supplies for all file types the answers for the
**		three requests SM_IO_WHAT_VECTORS, SM_IO_WHAT_TYPE and
**		SM_IO_WHAT_ISTYPE. Other requests are handled by the getinfo
**		vector if available for the open file type.
**	SM_IO_WHAT_VECTORS returns information for the file pointer vectors.
**	SM_IO_WHAT_TYPE returns the type identifier for the file pointer
**	SM_IO_WHAT_ISTYPE returns >0 if the passed in type matches the
**		file pointer's type.
**	SM_IO_IS_READABLE returns 1 if there is data available for reading,
**		0 otherwise.
**
**	Parameters:
**		fp -- file pointer for active file type
**		what -- type of information request
**		valp -- structure to place obtained info into
**
**	Returns:
**		usual sm_error code
**			- when valp==NULL and request expects otherwise
**			- when request is not SM_IO_WHAT_VECTORS and not
**				SM_IO_WHAT_TYPE and not SM_IO_WHAT_ISTYPE
**				and getinfo vector is NULL
**			- when getinfo type vector returns -1
**		>=0 on success
*/

sm_ret_T
sm_io_getinfo(sm_file_T *fp, int what, void *valp)
{

	SM_IS_FP(fp);

	switch (what)
	{
#if 0
	  case SM_IO_WHAT_VECTORS:
		{
		sm_file_T *v = (sm_file_T *) valp;

		/* This is the "generic" available for all */
		SM_FILE_FUNCT_ASSIGN(v, fp);
		return SM_SUCCESS;
		}

	  case SM_IO_WHAT_TYPE:
		if (valp == NULL)
			return sm_error_perm(SM_EM_IO, EINVAL);
		else
		{
			char *r;

			r = (char *) valp;
			return SM_SUCCESS;
		}
		/* NOTREACHED */
		break;
#endif /* 0 */

#if 0
	  case SM_IO_WHAT_ISTYPE:
		if (valp == NULL)
			return sm_error_perm(SM_EM_IO, EINVAL);
#endif /* 0 */

	  case SM_IO_IS_READABLE:

		/* if there is data in the buffer, it must be readable */
		if (f_r(*fp) > 0)
			return 1;

		/*
		**  if there is data in the read buffer, and the read buffer
		**  is not active (not in RD mode), it must be readable
		*/

		if (sm_io_double(fp) && (f_flags(*fp) & SMRD) == 0
		    && f_rd_r(*fp) > 0)
			return 1;

		/* check whether file is readable */
		if ((f_flags(*fp) & (SMRD|SMRW)) == 0)
			return sm_error_perm(SM_EM_IO, EBADF);

		/* otherwise query the underlying file */
		break;

	   case SM_IO_WHAT_TIMEOUT:
		*((sm_intvl_T *) valp) = fp->f_timeout;
		return 0;

	  case SM_IO_WHAT_FD:
		if (f_fd(*fp) > -1)
			return f_fd(*fp);
		break;

	  case SM_IO_WHAT_RD_FD:
		if (!sm_io_double(fp))
			return sm_error_perm(SM_EM_IO, EBADF);
		if (f_rd_fd(*fp) > -1)
			return f_rd_fd(*fp);
		break;

	  case SM_IO_WHAT_WR_FD:
		if (!sm_io_double(fp))
			return sm_error_perm(SM_EM_IO, EBADF);
		if (f_wr_fd(*fp) > -1)
			return f_wr_fd(*fp);
		break;
	}


	/* Otherwise the vector will check it out */
	if (fp->f_stream.fs_getinfo == NULL)
		return sm_error_perm(SM_EM_IO, EINVAL);
	return (*fp->f_stream.fs_getinfo)(fp, what, valp);
}


syntax highlighted by Code2HTML, v. 0.9.1