1/*
2 * Copyright (c) 2000-2002, 2006 Sendmail, Inc. and its suppliers.
3 *      All rights reserved.
4 * Copyright (c) 1990, 1993
5 *	The Regents of the University of California.  All rights reserved.
6 *
7 * This code is derived from software contributed to Berkeley by
8 * Chris Torek.
9 *
10 * By using this file, you agree to the terms and conditions set
11 * forth in the LICENSE file which can be found at the top level of
12 * the sendmail distribution.
13 */
14
15#pragma ident	"%Z%%M%	%I%	%E% SMI"
16
17#include <sm/gen.h>
18SM_RCSID("@(#)$Id: findfp.c,v 1.67 2006/08/28 21:24:46 ca Exp $")
19#include <stdlib.h>
20#include <unistd.h>
21#include <sys/param.h>
22#include <errno.h>
23#include <string.h>
24#include <syslog.h>
25#include <sm/io.h>
26#include <sm/assert.h>
27#include <sm/heap.h>
28#include <sm/string.h>
29#include <sm/conf.h>
30#include "local.h"
31#include "glue.h"
32
33bool	Sm_IO_DidInit;	/* IO system has been initialized? */
34
35const char SmFileMagic[] = "sm_file";
36
37/* An open type to map to fopen()-like behavior */
38SM_FILE_T SmFtStdio_def =
39    {SmFileMagic, 0, 0, 0, (SMRW|SMFBF), -1, {0, 0}, 0, 0, 0,
40	sm_stdclose, sm_stdread, sm_stdseek, sm_stdwrite,
41	sm_stdopen, sm_stdsetinfo, sm_stdgetinfo, SM_TIME_FOREVER,
42	SM_TIME_BLOCK, "stdio" };
43
44/* An open type to map to fdopen()-like behavior */
45SM_FILE_T SmFtStdiofd_def =
46    {SmFileMagic, 0, 0, 0, (SMRW|SMFBF), -1, {0, 0}, 0, 0, 0,
47	sm_stdclose, sm_stdread, sm_stdseek, sm_stdwrite,
48	sm_stdfdopen, sm_stdsetinfo, sm_stdgetinfo, SM_TIME_FOREVER,
49	SM_TIME_BLOCK, "stdiofd" };
50
51/* A string file type */
52SM_FILE_T SmFtString_def =
53    {SmFileMagic, 0, 0, 0, (SMRW|SMNBF), -1, {0, 0}, 0, 0, 0,
54	sm_strclose, sm_strread, sm_strseek, sm_strwrite,
55	sm_stropen, sm_strsetinfo, sm_strgetinfo, SM_TIME_FOREVER,
56	SM_TIME_BLOCK, "string" };
57
58#if 0
59/* A file type for syslog communications */
60SM_FILE_T SmFtSyslog_def =
61    {SmFileMagic, 0, 0, 0, (SMRW|SMNBF), -1, {0, 0}, 0, 0, 0,
62	sm_syslogclose, sm_syslogread, sm_syslogseek, sm_syslogwrite,
63	sm_syslogopen, sm_syslogsetinfo, sm_sysloggetinfo, SM_TIME_FOREVER,
64	SM_TIME_BLOCK, "syslog" };
65#endif /* 0 */
66
67#define NDYNAMIC 10		/* add ten more whenever necessary */
68
69#define smio(flags, file, name)						\
70    {SmFileMagic, 0, 0, 0, flags, file, {0}, 0, SmIoF+file, 0,		\
71	sm_stdclose, sm_stdread, sm_stdseek, sm_stdwrite,		\
72	sm_stdopen, sm_stdsetinfo, sm_stdgetinfo, SM_TIME_FOREVER,	\
73	SM_TIME_BLOCK, name}
74
75/* sm_magic p r w flags file bf lbfsize cookie ival */
76#define smstd(flags, file, name)					\
77    {SmFileMagic, 0, 0, 0, flags, -1, {0}, 0, 0, file,			\
78	sm_stdioclose, sm_stdioread, sm_stdioseek, sm_stdiowrite,	\
79	sm_stdioopen, sm_stdiosetinfo, sm_stdiogetinfo, SM_TIME_FOREVER,\
80	SM_TIME_BLOCK, name}
81
82/* A file type for interfacing to stdio FILE* streams. */
83SM_FILE_T SmFtRealStdio_def = smstd(SMRW|SMNBF, -1, "realstdio");
84
85				/* the usual - (stdin + stdout + stderr) */
86static SM_FILE_T usual[SM_IO_OPEN_MAX - 3];
87static struct sm_glue smuglue = { 0, SM_IO_OPEN_MAX - 3, usual };
88
89/* List of builtin automagically already open file pointers */
90SM_FILE_T SmIoF[6] =
91{
92	smio(SMRD|SMLBF, SMIOIN_FILENO, "smioin"),	/* smioin */
93	smio(SMWR|SMLBF, SMIOOUT_FILENO, "smioout"),	/* smioout */
94	smio(SMWR|SMNBF, SMIOERR_FILENO, "smioerr"),	/* smioerr */
95	smstd(SMRD|SMNBF, SMIOIN_FILENO, "smiostdin"),	/* smiostdin */
96	smstd(SMWR|SMNBF, SMIOOUT_FILENO, "smiostdout"),/* smiostdout */
97	smstd(SMWR|SMNBF, SMIOERR_FILENO, "smiostderr") /* smiostderr */
98};
99
100/* Structure containing list of currently open file pointers */
101struct sm_glue smglue = { &smuglue, 3, SmIoF };
102
103/*
104**  SM_MOREGLUE -- adds more space for open file pointers
105**
106**	Parameters:
107**		n -- number of new spaces for file pointers
108**
109**	Returns:
110**		Raises an exception if no more memory.
111**		Otherwise, returns a pointer to new spaces.
112*/
113
114static struct sm_glue *sm_moreglue_x __P((int));
115static SM_FILE_T empty;
116
117static struct sm_glue *
118sm_moreglue_x(n)
119	register int n;
120{
121	register struct sm_glue *g;
122	register SM_FILE_T *p;
123
124	g = (struct sm_glue *) sm_pmalloc_x(sizeof(*g) + SM_ALIGN_BITS +
125					    n * sizeof(SM_FILE_T));
126	p = (SM_FILE_T *) SM_ALIGN(g + 1);
127	g->gl_next = NULL;
128	g->gl_niobs = n;
129	g->gl_iobs = p;
130	while (--n >= 0)
131		*p++ = empty;
132	return g;
133}
134
135/*
136**  SM_FP -- allocate and initialize an SM_FILE structure
137**
138**	Parameters:
139**		t -- file type requested to be opened.
140**		flags -- control flags for file type behavior
141**		oldfp -- file pointer to reuse if available (optional)
142**
143**	Returns:
144**		Raises exception on memory exhaustion.
145**		Aborts if type is invalid.
146**		Otherwise, returns file pointer for requested file type.
147*/
148
149SM_FILE_T *
150sm_fp(t, flags, oldfp)
151	const SM_FILE_T *t;
152	const int flags;
153	SM_FILE_T *oldfp;
154{
155	register SM_FILE_T *fp;
156	register int n;
157	register struct sm_glue *g;
158
159	SM_REQUIRE(t->f_open && t->f_close && (t->f_read || t->f_write));
160
161	if (!Sm_IO_DidInit)
162		sm_init();
163
164	if (oldfp != NULL)
165	{
166		fp = oldfp;
167		goto found; /* for opening reusing an 'fp' */
168	}
169
170	for (g = &smglue;; g = g->gl_next)
171	{
172		for (fp = g->gl_iobs, n = g->gl_niobs; --n >= 0; fp++)
173			if (fp->sm_magic == NULL)
174				goto found;
175		if (g->gl_next == NULL)
176			g->gl_next = sm_moreglue_x(NDYNAMIC);
177	}
178found:
179	fp->sm_magic = SmFileMagic; /* 'fp' now valid and in-use */
180	fp->f_p = NULL;		/* no current pointer */
181	fp->f_w = 0;		/* nothing to write */
182	fp->f_r = 0;		/* nothing to read */
183	fp->f_flags = flags;
184	fp->f_file = -1;		/* no file */
185	fp->f_bf.smb_base = NULL;	/* no buffer */
186	fp->f_bf.smb_size = 0;	/* no buffer size with no buffer */
187	fp->f_lbfsize = 0;	/* not line buffered */
188	fp->f_flushfp = NULL;	/* no associated flush file */
189
190	fp->f_cookie = fp;	/* default: *open* overrides cookie setting */
191	fp->f_close = t->f_close;	/* assign close function */
192	fp->f_read = t->f_read;		/* assign read function */
193	fp->f_seek = t->f_seek;		/* assign seek function */
194	fp->f_write = t->f_write;	/* assign write function */
195	fp->f_open = t->f_open;		/* assign open function */
196	fp->f_setinfo = t->f_setinfo;	/* assign setinfo function */
197	fp->f_getinfo = t->f_getinfo;	/* assign getinfo function */
198	fp->f_type = t->f_type;		/* file type */
199
200	fp->f_ub.smb_base = NULL;	/* no ungetc buffer */
201	fp->f_ub.smb_size = 0;		/* no size for no ungetc buffer */
202
203	if (fp->f_timeout == SM_TIME_DEFAULT)
204		fp->f_timeout = SM_TIME_FOREVER;
205	else
206		fp->f_timeout = t->f_timeout; /* traditional behavior */
207	fp->f_timeoutstate = SM_TIME_BLOCK; /* by default */
208
209	return fp;
210}
211
212/*
213**  SM_CLEANUP -- cleanup function when exit called.
214**
215**	This function is registered via atexit().
216**
217**	Parameters:
218**		none
219**
220**	Returns:
221**		nothing.
222**
223**	Side Effects:
224**		flushes open files before they are forced closed
225*/
226
227void
228sm_cleanup()
229{
230	int timeout = SM_TIME_DEFAULT;
231
232	(void) sm_fwalk(sm_flush, &timeout); /* `cheating' */
233}
234
235/*
236**  SM_INIT -- called whenever sm_io's internal variables must be set up.
237**
238**	Parameters:
239**		none
240**
241**	Returns:
242**		none
243**
244**	Side Effects:
245**		Registers sm_cleanup() using atexit().
246*/
247
248void
249sm_init()
250{
251	if (Sm_IO_DidInit)	/* paranoia */
252		return;
253
254	/* more paranoia: initialize pointers in a static variable */
255	empty.f_type = NULL;
256	empty.sm_magic = NULL;
257
258	/* make sure we clean up on exit */
259	atexit(sm_cleanup);		/* conservative */
260	Sm_IO_DidInit = true;
261}
262
263/*
264**  SM_IO_SETINFO -- change info for an open file type (fp)
265**
266**	The generic SM_IO_WHAT_VECTORS is auto supplied for all file types.
267**	If the request is to set info other than SM_IO_WHAT_VECTORS then
268**	the request is passed on to the file type's specific setinfo vector.
269**	WARNING: this is working on an active/open file type.
270**
271**	Parameters:
272**		fp -- file to make the setting on
273**		what -- type of information to set
274**		valp -- structure to obtain info from
275**
276**	Returns:
277**		0 on success
278**		-1 on error and sets errno:
279**			- when what != SM_IO_WHAT_VECTORS and setinfo vector
280**				not set
281**			- when vectored setinfo returns -1
282*/
283
284int
285sm_io_setinfo(fp, what, valp)
286	SM_FILE_T *fp;
287	int what;
288	void *valp;
289{
290	SM_FILE_T *v = (SM_FILE_T *) valp;
291
292	SM_REQUIRE_ISA(fp, SmFileMagic);
293	switch (what)
294	{
295	  case SM_IO_WHAT_VECTORS:
296
297		/*
298		**  This is the "generic" available for all.
299		**  This allows the function vectors to be replaced
300		**  while the file type is active.
301		*/
302
303		fp->f_close = v->f_close;
304		fp->f_read = v->f_read;
305		fp->f_seek = v->f_seek;
306		fp->f_write = v->f_write;
307		fp->f_open = v->f_open;
308		fp->f_setinfo = v->f_setinfo;
309		fp->f_getinfo = v->f_getinfo;
310		sm_free(fp->f_type);
311		fp->f_type = sm_strdup_x(v->f_type);
312		return 0;
313	  case SM_IO_WHAT_TIMEOUT:
314		fp->f_timeout = *((int *)valp);
315		return 0;
316	}
317
318	/* Otherwise the vector will check it out */
319	if (fp->f_setinfo == NULL)
320	{
321		errno = EINVAL;
322		return -1;
323	}
324	else
325		return (*fp->f_setinfo)(fp, what, valp);
326}
327
328/*
329**  SM_IO_GETINFO -- get information for an active file type (fp)
330**
331**  This function supplies for all file types the answers for the
332**		three requests SM_IO_WHAT_VECTORS, SM_IO_WHAT_TYPE and
333**		SM_IO_WHAT_ISTYPE. Other requests are handled by the getinfo
334**		vector if available for the open file type.
335**	SM_IO_WHAT_VECTORS returns information for the file pointer vectors.
336**	SM_IO_WHAT_TYPE returns the type identifier for the file pointer
337**	SM_IO_WHAT_ISTYPE returns >0 if the passed in type matches the
338**		file pointer's type.
339**	SM_IO_IS_READABLE returns 1 if there is data available for reading,
340**		0 otherwise.
341**
342**	Parameters:
343**		fp -- file pointer for active file type
344**		what -- type of information request
345**		valp -- structure to place obtained info into
346**
347**	Returns:
348**		-1 on error and sets errno:
349**			- when valp==NULL and request expects otherwise
350**			- when request is not SM_IO_WHAT_VECTORS and not
351**				SM_IO_WHAT_TYPE and not SM_IO_WHAT_ISTYPE
352**				and getinfo vector is NULL
353**			- when getinfo type vector returns -1
354**		>=0 on success
355*/
356
357int
358sm_io_getinfo(fp, what, valp)
359	SM_FILE_T *fp;
360	int what;
361	void *valp;
362{
363	SM_FILE_T *v = (SM_FILE_T *) valp;
364
365	SM_REQUIRE_ISA(fp, SmFileMagic);
366
367	switch (what)
368	{
369	  case SM_IO_WHAT_VECTORS:
370		if (valp == NULL)
371		{
372			errno = EINVAL;
373			return -1;
374		}
375
376		/* This is the "generic" available for all */
377		v->f_close = fp->f_close;
378		v->f_read = fp->f_read;
379		v->f_seek = fp->f_seek;
380		v->f_write = fp->f_write;
381		v->f_open = fp->f_open;
382		v->f_setinfo = fp->f_setinfo;
383		v->f_getinfo = fp->f_getinfo;
384		v->f_type = fp->f_type;
385		return 0;
386
387	  case SM_IO_WHAT_TYPE:
388		if (valp == NULL)
389		{
390			errno = EINVAL;
391			return -1;
392		}
393		valp = sm_strdup_x(fp->f_type);
394		return 0;
395
396	  case SM_IO_WHAT_ISTYPE:
397		if (valp == NULL)
398		{
399			errno = EINVAL;
400			return -1;
401		}
402		return strcmp(fp->f_type, valp) == 0;
403
404	  case SM_IO_IS_READABLE:
405
406		/* if there is data in the buffer, it must be readable */
407		if (fp->f_r > 0)
408			return 1;
409
410		/* otherwise query the underlying file */
411		break;
412
413	   case SM_IO_WHAT_TIMEOUT:
414		*((int *) valp) = fp->f_timeout;
415		return 0;
416
417	  case SM_IO_WHAT_FD:
418		if (fp->f_file > -1)
419			return fp->f_file;
420
421		/* try the file type specific getinfo to see if it knows */
422		break;
423	}
424
425	/* Otherwise the vector will check it out */
426	if (fp->f_getinfo == NULL)
427	{
428		errno = EINVAL;
429		return -1;
430	}
431	return (*fp->f_getinfo)(fp, what, valp);
432}
433