1/*
2 * Copyright (c) 2000-2002, 2004-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 *	$Id: local.h,v 1.58 2006/12/19 19:44:23 ca Exp $
15 */
16
17#pragma ident	"%Z%%M%	%I%	%E% SMI"
18
19/*
20**  Information local to this implementation of stdio,
21**  in particular, macros and private variables.
22*/
23
24#include <sm/time.h>
25#if !SM_CONF_MEMCHR
26# include <memory.h>
27#endif /* !SM_CONF_MEMCHR */
28#include <sm/heap.h>
29
30int	sm_flush __P((SM_FILE_T *, int *));
31SM_FILE_T	*smfp __P((void));
32int	sm_refill __P((SM_FILE_T *, int));
33void	sm_init __P((void));
34void	sm_cleanup __P((void));
35void	sm_makebuf __P((SM_FILE_T *));
36int	sm_whatbuf __P((SM_FILE_T *, size_t *, int *));
37int	sm_fwalk __P((int (*)(SM_FILE_T *, int *), int *));
38int	sm_wsetup __P((SM_FILE_T *));
39int	sm_flags __P((int));
40SM_FILE_T	*sm_fp __P((const SM_FILE_T *, const int, SM_FILE_T *));
41int	sm_vprintf __P((int, char const *, va_list));
42
43/* std io functions */
44ssize_t	sm_stdread __P((SM_FILE_T *, char *, size_t));
45ssize_t	sm_stdwrite __P((SM_FILE_T *, char const *, size_t));
46off_t	sm_stdseek __P((SM_FILE_T *, off_t, int));
47int	sm_stdclose __P((SM_FILE_T *));
48int	sm_stdopen __P((SM_FILE_T *, const void *, int, const void *));
49int	sm_stdfdopen __P((SM_FILE_T *, const void *, int, const void *));
50int	sm_stdsetinfo __P((SM_FILE_T *, int , void *));
51int	sm_stdgetinfo __P((SM_FILE_T *, int , void *));
52
53/* stdio io functions */
54ssize_t	sm_stdioread __P((SM_FILE_T *, char *, size_t));
55ssize_t	sm_stdiowrite __P((SM_FILE_T *, char const *, size_t));
56off_t	sm_stdioseek __P((SM_FILE_T *, off_t, int));
57int	sm_stdioclose __P((SM_FILE_T *));
58int	sm_stdioopen __P((SM_FILE_T *, const void *, int, const void *));
59int	sm_stdiosetinfo __P((SM_FILE_T *, int , void *));
60int	sm_stdiogetinfo __P((SM_FILE_T *, int , void *));
61
62/* string io functions */
63ssize_t	sm_strread __P((SM_FILE_T *, char *, size_t));
64ssize_t	sm_strwrite __P((SM_FILE_T *, char const *, size_t));
65off_t	sm_strseek __P((SM_FILE_T *, off_t, int));
66int	sm_strclose __P((SM_FILE_T *));
67int	sm_stropen __P((SM_FILE_T *, const void *, int, const void *));
68int	sm_strsetinfo __P((SM_FILE_T *, int , void *));
69int	sm_strgetinfo __P((SM_FILE_T *, int , void *));
70
71/* syslog io functions */
72ssize_t	sm_syslogread __P((SM_FILE_T *, char *, size_t));
73ssize_t	sm_syslogwrite __P((SM_FILE_T *, char const *, size_t));
74off_t	sm_syslogseek __P((SM_FILE_T *, off_t, int));
75int	sm_syslogclose __P((SM_FILE_T *));
76int	sm_syslogopen __P((SM_FILE_T *, const void *, int, const void *));
77int	sm_syslogsetinfo __P((SM_FILE_T *, int , void *));
78int	sm_sysloggetinfo __P((SM_FILE_T *, int , void *));
79
80extern bool Sm_IO_DidInit;
81
82/* Return true iff the given SM_FILE_T cannot be written now. */
83#define cantwrite(fp) \
84	((((fp)->f_flags & SMWR) == 0 || (fp)->f_bf.smb_base == NULL) && \
85	 sm_wsetup(fp))
86
87/*
88**  Test whether the given stdio file has an active ungetc buffer;
89**   release such a buffer, without restoring ordinary unread data.
90*/
91
92#define HASUB(fp) ((fp)->f_ub.smb_base != NULL)
93#define FREEUB(fp)					\
94{							\
95	if ((fp)->f_ub.smb_base != (fp)->f_ubuf)	\
96		sm_free((char *)(fp)->f_ub.smb_base);	\
97	(fp)->f_ub.smb_base = NULL;			\
98}
99
100extern const char SmFileMagic[];
101
102#define SM_ALIGN(p)	(((unsigned long)(p) + SM_ALIGN_BITS) & ~SM_ALIGN_BITS)
103
104#define sm_io_flockfile(fp)	((void) 0)
105#define sm_io_funlockfile(fp)	((void) 0)
106
107int sm_flags __P((int));
108
109#ifndef FDSET_CAST
110# define FDSET_CAST		/* empty cast for fd_set arg to select */
111#endif
112
113/*
114**  SM_CONVERT_TIME -- convert the API timeout flag for select() usage.
115**
116**	This takes a 'fp' (a file type pointer) and obtains the "raw"
117**	file descriptor (fd) if possible. The 'fd' is needed to possibly
118**	switch the mode of the file (blocking/non-blocking) to match
119**	the type of timeout. If timeout is SM_TIME_FOREVER then the
120**	timeout using select won't be needed and the file is best placed
121**	in blocking mode. If there is to be a finite timeout then the file
122**	is best placed in non-blocking mode. Then, if not enough can be
123**	written, select() can be used to test when something can be written
124**	yet still timeout if the wait is too long.
125**	If the mode is already in the correct state we don't change it.
126**	Iff (yes "iff") the 'fd' is "-1" in value then the mode change
127**	will not happen. This situation arises when a late-binding-to-disk
128**	file type is in use. An example of this is the sendmail buffered
129**	file type (in sendmail/bf.c).
130**
131**	Parameters
132**		fp -- the file pointer the timeout is for
133**		fd -- to become the file descriptor value from 'fp'
134**		val -- the timeout value to be converted
135**		time -- a struct timeval holding the converted value
136**
137**	Returns
138**		nothing, this is flow-through code
139**
140**	Side Effects:
141**		May or may not change the mode of a currently open file.
142**		The file mode may be changed to O_NONBLOCK or ~O_NONBLOCK
143**		(meaning block). This is done to best match the type of
144**		timeout and for (possible) use with select().
145*/
146
147# define SM_CONVERT_TIME(fp, fd, val, time) { \
148	if (((fd) = sm_io_getinfo(fp, SM_IO_WHAT_FD, NULL)) == -1) \
149	{ \
150		/* can't get an fd, likely internal 'fake' fp */ \
151		errno = 0; \
152	} \
153	if ((val) == SM_TIME_DEFAULT) \
154		(val) = (fp)->f_timeout; \
155	if ((val) == SM_TIME_IMMEDIATE || (val) == SM_TIME_FOREVER) \
156	{ \
157		(time)->tv_sec = 0; \
158		(time)->tv_usec = 0; \
159	} \
160	else \
161	{ \
162		(time)->tv_sec = (val) / 1000; \
163		(time)->tv_usec = ((val) - ((time)->tv_sec * 1000)) * 1000; \
164	} \
165	if ((val) == SM_TIME_FOREVER) \
166	{ \
167		if ((fp)->f_timeoutstate == SM_TIME_NONBLOCK && (fd) != -1) \
168		{ \
169			int ret; \
170			ret = fcntl((fd), F_GETFL, 0); \
171			if (ret == -1 || fcntl((fd), F_SETFL, \
172					       ret & ~O_NONBLOCK) == -1) \
173			{ \
174				/* errno should be set */ \
175				return SM_IO_EOF; \
176			} \
177			(fp)->f_timeoutstate = SM_TIME_BLOCK; \
178			if ((fp)->f_modefp != NULL) \
179				(fp)->f_modefp->f_timeoutstate = SM_TIME_BLOCK; \
180		} \
181	} \
182	else { \
183		if ((fp)->f_timeoutstate == SM_TIME_BLOCK && (fd) != -1) \
184		{ \
185			int ret; \
186			ret = fcntl((fd), F_GETFL, 0); \
187			if (ret == -1 || fcntl((fd), F_SETFL, \
188					       ret | O_NONBLOCK) == -1) \
189			{ \
190				/* errno should be set */ \
191				return SM_IO_EOF; \
192			} \
193			(fp)->f_timeoutstate = SM_TIME_NONBLOCK; \
194			if ((fp)->f_modefp != NULL) \
195				(fp)->f_modefp->f_timeoutstate = SM_TIME_NONBLOCK; \
196		} \
197	} \
198}
199
200/*
201**  SM_IO_WR_TIMEOUT -- setup the timeout for the write
202**
203**  This #define uses a select() to wait for the 'fd' to become writable.
204**  The select() can be active for up to 'to' time. The select may not
205**  use all of the the 'to' time. Hence, the amount of "wall-clock" time is
206**  measured to decide how much to subtract from 'to' to update it. On some
207**  BSD-based/like systems the timeout for a select is updated for the
208**  amount of time used. On many/most systems this does not happen. Therefore
209**  the updating of 'to' must be done ourselves; a copy of 'to' is passed
210**  since a BSD-like system will have updated it and we don't want to
211**  double the time used!
212**  Note: if a valid 'fd' doesn't exist yet, don't use this (e.g. the
213**  sendmail buffered file type in sendmail/bf.c; see fvwrite.c).
214**
215**	Parameters
216**		fd -- a file descriptor for doing select() with
217**		timeout -- the original user set value.
218**
219**	Returns
220**		nothing, this is flow through code
221**
222**	Side Effects:
223**		adjusts 'timeout' for time used
224*/
225
226#define SM_IO_WR_TIMEOUT(fp, fd, to) { \
227	struct timeval sm_io_to_before, sm_io_to_after, sm_io_to_diff; \
228	struct timeval sm_io_to; \
229	int sm_io_to_sel; \
230	fd_set sm_io_to_mask, sm_io_x_mask; \
231	errno = 0; \
232	if ((to) == SM_TIME_DEFAULT) \
233		(to) = (fp)->f_timeout; \
234	if ((to) == SM_TIME_IMMEDIATE) \
235	{ \
236		errno = EAGAIN; \
237		return SM_IO_EOF; \
238	} \
239	else if ((to) == SM_TIME_FOREVER) \
240	{ \
241		errno = EINVAL; \
242		return SM_IO_EOF; \
243	} \
244	else \
245	{ \
246		sm_io_to.tv_sec = (to) / 1000; \
247		sm_io_to.tv_usec = ((to) - (sm_io_to.tv_sec * 1000)) * 1000; \
248	} \
249	if (FD_SETSIZE > 0 && (fd) >= FD_SETSIZE) \
250	{ \
251		errno = EINVAL; \
252		return SM_IO_EOF; \
253	} \
254	FD_ZERO(&sm_io_to_mask); \
255	FD_SET((fd), &sm_io_to_mask); \
256	FD_ZERO(&sm_io_x_mask); \
257	FD_SET((fd), &sm_io_x_mask); \
258	if (gettimeofday(&sm_io_to_before, NULL) < 0) \
259		return SM_IO_EOF; \
260	do \
261	{	\
262		sm_io_to_sel = select((fd) + 1, NULL, &sm_io_to_mask, \
263					&sm_io_x_mask, &sm_io_to); \
264	} while (sm_io_to_sel < 0 && errno == EINTR); \
265	if (sm_io_to_sel < 0) \
266	{ \
267		/* something went wrong, errno set */ \
268		return SM_IO_EOF; \
269	} \
270	else if (sm_io_to_sel == 0) \
271	{ \
272		/* timeout */ \
273		errno = EAGAIN; \
274		return SM_IO_EOF; \
275	} \
276	/* else loop again */ \
277	if (gettimeofday(&sm_io_to_after, NULL) < 0) \
278		return SM_IO_EOF; \
279	timersub(&sm_io_to_after, &sm_io_to_before, &sm_io_to_diff); \
280	(to) -= (sm_io_to_diff.tv_sec * 1000); \
281	(to) -= (sm_io_to_diff.tv_usec / 1000); \
282	if ((to) < 0) \
283		(to) = 0; \
284}
285
286/*
287**  If there is no 'fd' just error (we can't timeout). If the timeout
288**  is SM_TIME_FOREVER then there is no need to do a timeout with
289**  select since this will be a real error.  If the error is not
290**  EAGAIN/EWOULDBLOCK (from a nonblocking) then it's a real error.
291**  Specify the condition here as macro so it can be used in several places.
292*/
293
294#define IS_IO_ERROR(fd, ret, to) \
295	((fd) < 0 ||	\
296	 ((ret) < 0 && errno != EAGAIN && errno != EWOULDBLOCK) ||	\
297	 (to) == SM_TIME_FOREVER)
298
299