/* * 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); }