aboutsummaryrefslogtreecommitdiff
path: root/contrib/sendmail/libsm/local.h
blob: 943321363c22450c8aeecf97bcb3c16ce0e65e05 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
/*
 * Copyright (c) 2000-2002 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.
 *
 *	$Id: local.h,v 1.51 2002/02/20 02:40:24 ca Exp $
 */

/*
**  Information local to this implementation of stdio,
**  in particular, macros and private variables.
*/

#include <sys/time.h>
#if !SM_CONF_MEMCHR
# include <memory.h>
#endif /* !SM_CONF_MEMCHR */
#include <sm/heap.h>

int	sm_flush __P((SM_FILE_T *, int *));
SM_FILE_T	*smfp __P((void));
int	sm_refill __P((SM_FILE_T *, int));
void	sm_init __P((void));
void	sm_cleanup __P((void));
void	sm_makebuf __P((SM_FILE_T *));
int	sm_whatbuf __P((SM_FILE_T *, size_t *, int *));
int	sm_fwalk __P((int (*)(SM_FILE_T *, int *), int *));
int	sm_wsetup __P((SM_FILE_T *));
int	sm_flags __P((int));
SM_FILE_T	*sm_fp __P((const SM_FILE_T *, const int, SM_FILE_T *));
int	sm_vprintf __P((int, char const *, va_list));
int	sm_vfscanf __P((SM_FILE_T *, int, char const *, va_list));

/* std io functions */
ssize_t	sm_stdread __P((SM_FILE_T *, char *, size_t));
ssize_t	sm_stdwrite __P((SM_FILE_T *, char const *, size_t));
off_t	sm_stdseek __P((SM_FILE_T *, off_t, int));
int	sm_stdclose __P((SM_FILE_T *));
int	sm_stdopen __P((SM_FILE_T *, const void *, int, const void *));
int	sm_stdfdopen __P((SM_FILE_T *, const void *, int, const void *));
int	sm_stdsetinfo __P((SM_FILE_T *, int , void *));
int	sm_stdgetinfo __P((SM_FILE_T *, int , void *));

/* stdio io functions */
ssize_t	sm_stdioread __P((SM_FILE_T *, char *, size_t));
ssize_t	sm_stdiowrite __P((SM_FILE_T *, char const *, size_t));
off_t	sm_stdioseek __P((SM_FILE_T *, off_t, int));
int	sm_stdioclose __P((SM_FILE_T *));
int	sm_stdioopen __P((SM_FILE_T *, const void *, int, const void *));
int	sm_stdiosetinfo __P((SM_FILE_T *, int , void *));
int	sm_stdiogetinfo __P((SM_FILE_T *, int , void *));

/* string io functions */
ssize_t	sm_strread __P((SM_FILE_T *, char *, size_t));
ssize_t	sm_strwrite __P((SM_FILE_T *, char const *, size_t));
off_t	sm_strseek __P((SM_FILE_T *, off_t, int));
int	sm_strclose __P((SM_FILE_T *));
int	sm_stropen __P((SM_FILE_T *, const void *, int, const void *));
int	sm_strsetinfo __P((SM_FILE_T *, int , void *));
int	sm_strgetinfo __P((SM_FILE_T *, int , void *));

/* syslog io functions */
ssize_t	sm_syslogread __P((SM_FILE_T *, char *, size_t));
ssize_t	sm_syslogwrite __P((SM_FILE_T *, char const *, size_t));
off_t	sm_syslogseek __P((SM_FILE_T *, off_t, int));
int	sm_syslogclose __P((SM_FILE_T *));
int	sm_syslogopen __P((SM_FILE_T *, const void *, int, const void *));
int	sm_syslogsetinfo __P((SM_FILE_T *, int , void *));
int	sm_sysloggetinfo __P((SM_FILE_T *, int , void *));

/* should be defined in sys/time.h */
#ifndef timersub
# define timersub(tvp, uvp, vvp)					\
	do								\
	{								\
		(vvp)->tv_sec = (tvp)->tv_sec - (uvp)->tv_sec;		\
		(vvp)->tv_usec = (tvp)->tv_usec - (uvp)->tv_usec;	\
		if ((vvp)->tv_usec < 0)					\
		{							\
			(vvp)->tv_sec--;				\
			(vvp)->tv_usec += 1000000;			\
		}							\
	} while (0)
#endif /* !timersub */

#ifndef timeradd
# define timeradd(tvp, uvp, vvp)					\
	do								\
	{								\
		(vvp)->tv_sec = (tvp)->tv_sec + (uvp)->tv_sec;		\
		(vvp)->tv_usec = (tvp)->tv_usec + (uvp)->tv_usec;	\
		if ((vvp)->tv_usec >= 1000000)				\
		{							\
			(vvp)->tv_sec++;				\
			(vvp)->tv_usec -= 1000000;			\
		}							\
	} while (0)
#endif /* !timeradd */

#ifndef timercmp
# define timercmp(tvp, uvp, cmp)					\
	(((tvp)->tv_sec == (uvp)->tv_sec) ?				\
	    ((tvp)->tv_usec cmp (uvp)->tv_usec) :			\
	    ((tvp)->tv_sec cmp (uvp)->tv_sec))
#endif /* !timercmp */

extern bool Sm_IO_DidInit;

/* Return true iff the given SM_FILE_T cannot be written now. */
#define cantwrite(fp) \
	((((fp)->f_flags & SMWR) == 0 || (fp)->f_bf.smb_base == NULL) && \
	 sm_wsetup(fp))

/*
**  Test whether the given stdio file has an active ungetc buffer;
**   release such a buffer, without restoring ordinary unread data.
*/

#define HASUB(fp) ((fp)->f_ub.smb_base != NULL)
#define FREEUB(fp)					\
{							\
	if ((fp)->f_ub.smb_base != (fp)->f_ubuf)	\
		sm_free((char *)(fp)->f_ub.smb_base);	\
	(fp)->f_ub.smb_base = NULL;			\
}

extern const char SmFileMagic[];

#define SM_ALIGN(p)	(((unsigned long)(p) + SM_ALIGN_BITS) & ~SM_ALIGN_BITS)

#define sm_io_flockfile(fp)	((void) 0)
#define sm_io_funlockfile(fp)	((void) 0)

#ifndef FDSET_CAST
# define FDSET_CAST		/* empty cast for fd_set arg to select */
#endif

/*
**  SM_CONVERT_TIME -- convert the API timeout flag for select() usage.
**
**	This takes a 'fp' (a file type pointer) and obtains the "raw"
**	file descriptor (fd) if possible. The 'fd' is needed to possibly
**	switch the mode of the file (blocking/non-blocking) to match
**	the type of timeout. If timeout is SM_TIME_FOREVER then the
**	timeout using select won't be needed and the file is best placed
**	in blocking mode. If there is to be a finite timeout then the file
**	is best placed in non-blocking mode. Then, if not enough can be
**	written, select() can be used to test when something can be written
**	yet still timeout if the wait is too long.
**	If the mode is already in the correct state we don't change it.
**	Iff (yes "iff") the 'fd' is "-1" in value then the mode change
**	will not happen. This situation arises when a late-binding-to-disk
**	file type is in use. An example of this is the sendmail buffered
**	file type (in sendmail/bf.c).
**
**	Parameters
**		fp -- the file pointer the timeout is for
**		fd -- to become the file descriptor value from 'fp'
**		val -- the timeout value to be converted
**		time -- a struct timeval holding the converted value
**
**	Returns
**		nothing, this is flow-through code
**
**	Side Effects:
**		May or may not change the mode of a currently open file.
**		The file mode may be changed to O_NONBLOCK or ~O_NONBLOCK
**		(meaning block). This is done to best match the type of
**		timeout and for (possible) use with select().
*/

# define SM_CONVERT_TIME(fp, fd, val, time) { \
	if (((fd) = sm_io_getinfo(fp, SM_IO_WHAT_FD, NULL)) == -1) \
	{ \
		/* can't get an fd, likely internal 'fake' fp */ \
		errno = 0; \
	} \
	if ((val) == SM_TIME_DEFAULT) \
		(val) = (fp)->f_timeout; \
	if ((val) == SM_TIME_IMMEDIATE || (val) == SM_TIME_FOREVER) \
	{ \
		(time)->tv_sec = 0; \
		(time)->tv_usec = 0; \
	} \
	else \
	{ \
		(time)->tv_sec = (val) / 1000; \
		(time)->tv_usec = ((val) - ((time)->tv_sec * 1000)) * 10; \
	} \
	if ((val) == SM_TIME_FOREVER) \
	{ \
		if ((fp)->f_timeoutstate == SM_TIME_NONBLOCK && (fd) != -1) \
		{ \
			int ret; \
			ret = fcntl((fd), F_GETFL, 0); \
			if (ret == -1 || fcntl((fd), F_SETFL, \
					       ret & ~O_NONBLOCK) == -1) \
			{ \
				/* errno should be set */ \
				return SM_IO_EOF; \
			} \
			(fp)->f_timeoutstate = SM_TIME_BLOCK; \
			if ((fp)->f_modefp != NULL) \
				(fp)->f_modefp->f_timeoutstate = SM_TIME_BLOCK; \
		} \
	} \
	else { \
		if ((fp)->f_timeoutstate == SM_TIME_BLOCK && (fd) != -1) \
		{ \
			int ret; \
			ret = fcntl((fd), F_GETFL, 0); \
			if (ret == -1 || fcntl((fd), F_SETFL, \
					       ret | O_NONBLOCK) == -1) \
			{ \
				/* errno should be set */ \
				return SM_IO_EOF; \
			} \
			(fp)->f_timeoutstate = SM_TIME_NONBLOCK; \
			if ((fp)->f_modefp != NULL) \
				(fp)->f_modefp->f_timeoutstate = SM_TIME_NONBLOCK; \
		} \
	} \
}

/*
**  SM_IO_WR_TIMEOUT -- setup the timeout for the write
**
**  This #define uses a select() to wait for the 'fd' to become writable.
**  The select() can be active for up to 'to' time. The select may not
**  use all of the the 'to' time. Hence, the amount of "wall-clock" time is
**  measured to decide how much to subtract from 'to' to update it. On some
**  BSD-based/like systems the timeout for a select is updated for the
**  amount of time used. On many/most systems this does not happen. Therefore
**  the updating of 'to' must be done ourselves; a copy of 'to' is passed
**  since a BSD-like system will have updated it and we don't want to
**  double the time used!
**  Note: if a valid 'fd' doesn't exist yet, don't use this (e.g. the
**  sendmail buffered file type in sendmail/bf.c; see fvwrite.c).
**
**	Parameters
**		fd -- a file descriptor for doing select() with
**		timeout -- the original user set value.
**
**	Returns
**		nothing, this is flow through code
**
**	Side Effects:
**		adjusts 'timeout' for time used
*/

#define SM_IO_WR_TIMEOUT(fp, fd, to) { \
	struct timeval sm_io_to_before, sm_io_to_after, sm_io_to_diff; \
	struct timeval sm_io_to; \
	int sm_io_to_sel; \
	fd_set sm_io_to_mask, sm_io_x_mask; \
	errno = 0; \
	if ((to) == SM_TIME_DEFAULT) \
		(to) = (fp)->f_timeout; \
	if ((to) == SM_TIME_IMMEDIATE) \
	{ \
		errno = EAGAIN; \
		return SM_IO_EOF; \
	} \
	else if ((to) == SM_TIME_FOREVER) \
	{ \
		errno = EINVAL; \
		return SM_IO_EOF; \
	} \
	else \
	{ \
		sm_io_to.tv_sec = (to) / 1000; \
		sm_io_to.tv_usec = ((to) - (sm_io_to.tv_sec * 1000)) * 10; \
	} \
	FD_ZERO(&sm_io_to_mask); \
	FD_SET((fd), &sm_io_to_mask); \
	FD_ZERO(&sm_io_x_mask); \
	FD_SET((fd), &sm_io_x_mask); \
	if (gettimeofday(&sm_io_to_before, NULL) < 0) \
		return SM_IO_EOF; \
	sm_io_to_sel = select((fd) + 1, NULL, &sm_io_to_mask, &sm_io_x_mask, \
			      &sm_io_to); \
	if (sm_io_to_sel < 0) \
	{ \
		/* something went wrong, errno set */ \
		return SM_IO_EOF; \
	} \
	else if (sm_io_to_sel == 0) \
	{ \
		/* timeout */ \
		errno = EAGAIN; \
		return SM_IO_EOF; \
	} \
	/* else loop again */ \
	if (gettimeofday(&sm_io_to_after, NULL) < 0) \
		return SM_IO_EOF; \
	timersub(&sm_io_to_before, &sm_io_to_after, &sm_io_to_diff); \
	timersub(&sm_io_to, &sm_io_to_diff, &sm_io_to); \
	(to) -= (sm_io_to.tv_sec * 1000); \
	(to) -= (sm_io_to.tv_usec / 10); \
	if ((to) < 0) \
		(to) = 0; \
}

/*
**  If there is no 'fd' just error (we can't timeout). If the timeout
**  is SM_TIME_FOREVER then there is no need to do a timeout with
**  select since this will be a real error.  If the error is not
**  EAGAIN/EWOULDBLOCK (from a nonblocking) then it's a real error.
**  Specify the condition here as macro so it can be used in several places.
*/

#define IS_IO_ERROR(fd, ret, to) \
	((fd) < 0 ||	\
	 ((ret) < 0 && errno != EAGAIN && errno != EWOULDBLOCK) ||	\
	 (to) == SM_TIME_FOREVER)