/*
* 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