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