2
tio.c - timed io functions
3
This file is part of the nss-pam-ldapd library.
5
Copyright (C) 2007, 2008, 2010, 2011, 2012 Arthur de Jong
7
This library is free software; you can redistribute it and/or
8
modify it under the terms of the GNU Lesser General Public
9
License as published by the Free Software Foundation; either
10
version 2.1 of the License, or (at your option) any later version.
12
This library is distributed in the hope that it will be useful,
13
but WITHOUT ANY WARRANTY; without even the implied warranty of
14
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15
Lesser General Public License for more details.
17
You should have received a copy of the GNU Lesser General Public
18
License along with this library; if not, write to the Free Software
19
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
27
#endif /* HAVE_STDINT_H */
31
#include <sys/types.h>
32
#include <sys/socket.h>
41
/* for platforms that don't have ETIME use ETIMEDOUT */
43
#define ETIME ETIMEDOUT
46
/* structure that holds a buffer
47
the buffer contains the data that is between the application and the
48
file descriptor that is used for efficient transfer
49
the buffer is built up as follows:
55
size_t size; /* the size of the buffer */
56
size_t maxsize; /* the maximum size of the buffer */
57
size_t start; /* the start of the data (before start is unused) */
58
size_t len; /* size of the data (from the start) */
61
/* structure that holds all the state for files */
64
struct tio_buffer readbuffer;
65
struct tio_buffer writebuffer;
66
struct timeval readtimeout;
67
struct timeval writetimeout;
68
int read_resettable; /* whether the tio_reset() function can be called */
69
#ifdef DEBUG_TIO_STATS
70
/* this is used to collect statistics on the use of the streams
71
and can be used to tune the buffer sizes */
74
#endif /* DEBUG_TIO_STATS */
77
/* add the second timeval to the first modifing the first */
78
static inline void tio_tv_add(struct timeval *tv1, const struct timeval *tv2)
80
/* BUG: we hope that this does not overflow */
81
tv1->tv_usec+=tv2->tv_usec;
82
if (tv1->tv_usec>=1000000)
84
tv1->tv_usec-=1000000;
87
tv1->tv_sec+=tv2->tv_sec;
90
/* build a timeval for comparison to when the operation should be finished */
91
static inline void tio_tv_prepare(struct timeval *deadline, const struct timeval *timeout)
93
if (gettimeofday(deadline,NULL))
95
/* just blank it in case of errors */
100
tio_tv_add(deadline,timeout);
103
/* update the timeval to the value that is remaining before deadline
104
returns non-zero if there is no more time before the deadline */
105
static inline int tio_tv_remaining(struct timeval *tv, const struct timeval *deadline)
107
/* get the current time */
108
if (gettimeofday(tv,NULL))
110
/* 1 second default if gettimeofday() is broken */
115
/* check if we're too late */
116
if ( (tv->tv_sec>deadline->tv_sec) ||
117
( (tv->tv_sec==deadline->tv_sec) && (tv->tv_usec>deadline->tv_usec) ) )
120
tv->tv_sec=deadline->tv_sec-tv->tv_sec;
121
if (tv->tv_usec<=deadline->tv_usec)
122
tv->tv_usec=deadline->tv_usec-tv->tv_usec;
126
tv->tv_usec=1000000+deadline->tv_usec-tv->tv_usec;
131
/* open a new TFILE based on the file descriptor */
132
TFILE *tio_fdopen(int fd,struct timeval *readtimeout,struct timeval *writetimeout,
133
size_t initreadsize,size_t maxreadsize,
134
size_t initwritesize,size_t maxwritesize)
136
struct tio_fileinfo *fp;
137
fp=(struct tio_fileinfo *)malloc(sizeof(struct tio_fileinfo));
141
/* initialize read buffer */
142
fp->readbuffer.buffer=(uint8_t *)malloc(initreadsize);
143
if (fp->readbuffer.buffer==NULL)
148
fp->readbuffer.size=initreadsize;
149
fp->readbuffer.maxsize=maxreadsize;
150
fp->readbuffer.start=0;
151
fp->readbuffer.len=0;
152
/* initialize write buffer */
153
fp->writebuffer.buffer=(uint8_t *)malloc(initwritesize);
154
if (fp->writebuffer.buffer==NULL)
156
free(fp->readbuffer.buffer);
160
fp->writebuffer.size=initwritesize;
161
fp->writebuffer.maxsize=maxwritesize;
162
fp->writebuffer.start=0;
163
fp->writebuffer.len=0;
164
/* initialize other attributes */
165
fp->readtimeout.tv_sec=readtimeout->tv_sec;
166
fp->readtimeout.tv_usec=readtimeout->tv_usec;
167
fp->writetimeout.tv_sec=writetimeout->tv_sec;
168
fp->writetimeout.tv_usec=writetimeout->tv_usec;
169
fp->read_resettable=0;
170
#ifdef DEBUG_TIO_STATS
173
#endif /* DEBUG_TIO_STATS */
177
/* wait for any activity on the specified file descriptor using
178
the specified deadline */
179
static int tio_select(TFILE *fp, int readfd, const struct timeval *deadline)
186
/* prepare our filedescriptorset */
188
FD_SET(fp->fd,&fdset);
189
/* figure out the time we need to wait */
190
if (tio_tv_remaining(&tv,deadline))
195
/* wait for activity */
198
/* santiy check for moving clock */
199
if (tv.tv_sec>fp->readtimeout.tv_sec)
200
tv.tv_sec=fp->readtimeout.tv_sec;
201
rv=select(FD_SETSIZE,&fdset,NULL,NULL,&tv);
205
/* santiy check for moving clock */
206
if (tv.tv_sec>fp->writetimeout.tv_sec)
207
tv.tv_sec=fp->writetimeout.tv_sec;
208
rv=select(FD_SETSIZE,NULL,&fdset,NULL,&tv);
211
return 0; /* we have activity */
214
/* no file descriptors were available within the specified time */
218
else if (errno!=EINTR)
219
/* some error ocurred */
221
/* we just try again on EINTR */
225
/* do a read on the file descriptor, returning the data in the buffer
226
if no data was read in the specified time an error is returned */
227
int tio_read(TFILE *fp, void *buf, size_t count)
229
struct timeval deadline;
234
/* have a more convenient storage type for the buffer */
235
uint8_t *ptr=(uint8_t *)buf;
236
/* build a time by which we should be finished */
237
tio_tv_prepare(&deadline,&(fp->readtimeout));
238
/* loop until we have returned all the needed data */
241
/* check if we have enough data in the buffer */
242
if (fp->readbuffer.len >= count)
247
memcpy(ptr,fp->readbuffer.buffer+fp->readbuffer.start,count);
248
/* adjust buffer position */
249
fp->readbuffer.start+=count;
250
fp->readbuffer.len-=count;
254
/* empty what we have and continue from there */
255
if (fp->readbuffer.len>0)
259
memcpy(ptr,fp->readbuffer.buffer+fp->readbuffer.start,fp->readbuffer.len);
260
ptr+=fp->readbuffer.len;
262
count-=fp->readbuffer.len;
263
fp->readbuffer.start+=fp->readbuffer.len;
264
fp->readbuffer.len=0;
266
/* after this point until the read fp->readbuffer.len is 0 */
267
if (!fp->read_resettable)
269
/* the stream is not resettable, re-use the buffer */
270
fp->readbuffer.start=0;
272
else if (fp->readbuffer.start>=(fp->readbuffer.size-4))
274
/* buffer is running empty, try to grow buffer */
275
if (fp->readbuffer.size<fp->readbuffer.maxsize)
277
newsz=fp->readbuffer.size*2;
278
if (newsz>fp->readbuffer.maxsize)
279
newsz=fp->readbuffer.maxsize;
280
tmp=realloc(fp->readbuffer.buffer,newsz);
283
fp->readbuffer.buffer=tmp;
284
fp->readbuffer.size=newsz;
287
/* if buffer still does not contain enough room, clear resettable */
288
if (fp->readbuffer.start>=(fp->readbuffer.size-4))
290
fp->readbuffer.start=0;
291
fp->read_resettable=0;
294
/* wait until we have input */
295
if (tio_select(fp,1,&deadline))
297
/* read the input in the buffer */
298
len=fp->readbuffer.size-fp->readbuffer.start;
302
#endif /* SSIZE_MAX */
303
rv=read(fp->fd,fp->readbuffer.buffer+fp->readbuffer.start,len);
304
/* check for errors */
310
else if ((rv<0)&&(errno!=EINTR)&&(errno!=EAGAIN))
311
return -1; /* something went wrong with the read */
312
/* skip the read part in the buffer */
313
fp->readbuffer.len=rv;
314
#ifdef DEBUG_TIO_STATS
316
#endif /* DEBUG_TIO_STATS */
320
/* Read and discard the specified number of bytes from the stream. */
321
int tio_skip(TFILE *fp, size_t count)
323
return tio_read(fp,NULL,count);
326
/* Read all available data from the stream and empty the read buffer. */
327
int tio_skipall(TFILE *fp)
333
/* clear the read buffer */
334
fp->readbuffer.start=0;
335
fp->readbuffer.len=0;
336
fp->read_resettable=0;
337
/* read until we can't read no more */
338
len=fp->readbuffer.size;
342
#endif /* SSIZE_MAX */
345
/* prepare our file descriptor set */
347
FD_SET(fp->fd,&fdset);
348
/* prepare the time to wait */
351
/* see if any data is available */
352
rv=select(FD_SETSIZE,&fdset,NULL,NULL,&tv);
354
return 0; /* no file descriptor ready */
355
if ((rv<0)&&((errno==EINTR)||(errno==EAGAIN)))
356
continue; /* interrupted, try again */
358
return -1; /* something went wrong */
359
/* read data from the stream */
360
rv=read(fp->fd,fp->readbuffer.buffer,len);
362
return 0; /* end-of-file */
363
if ((rv<0)&&(errno==EWOULDBLOCK))
364
return 0; /* we've ready everything we can without blocking */
365
if ((rv<0)&&(errno!=EINTR)&&(errno!=EAGAIN))
366
return -1; /* something went wrong with the read */
370
/* the caller has assured us that we can write to the file descriptor
371
and we give it a shot */
372
static int tio_writebuf(TFILE *fp)
375
/* write the buffer */
377
rv=send(fp->fd,fp->writebuffer.buffer+fp->writebuffer.start,fp->writebuffer.len,MSG_NOSIGNAL);
378
#else /* not MSG_NOSIGNAL */
379
/* on platforms that cannot use send() with masked signals, we change the
380
signal mask and change it back after the write (note that there is a
381
race condition here) */
382
struct sigaction act,oldact;
383
/* set up sigaction */
384
memset(&act,0,sizeof(struct sigaction));
385
act.sa_sigaction=NULL;
386
act.sa_handler=SIG_IGN;
387
sigemptyset(&act.sa_mask);
388
act.sa_flags=SA_RESTART;
390
if (sigaction(SIGPIPE,&act,&oldact)!=0)
391
return -1; /* error setting signal handler */
392
/* write the buffer */
393
rv=write(fp->fd,fp->writebuffer.buffer+fp->writebuffer.start,fp->writebuffer.len);
394
/* restore the old handler for SIGPIPE */
395
if (sigaction(SIGPIPE,&oldact,NULL)!=0)
396
return -1; /* error restoring signal handler */
398
/* check for errors */
399
if ((rv==0)||((rv<0)&&(errno!=EINTR)&&(errno!=EAGAIN)))
400
return -1; /* something went wrong with the write */
401
/* skip the written part in the buffer */
404
fp->writebuffer.start+=rv;
405
fp->writebuffer.len-=rv;
406
#ifdef DEBUG_TIO_STATS
407
fp->byteswritten+=rv;
408
#endif /* DEBUG_TIO_STATS */
409
/* reset start if len is 0 */
410
if (fp->writebuffer.len==0)
411
fp->writebuffer.start=0;
412
/* move contents of the buffer to the front if it will save enough room */
413
if (fp->writebuffer.start>=(fp->writebuffer.size/4))
415
memmove(fp->writebuffer.buffer,fp->writebuffer.buffer+fp->writebuffer.start,fp->writebuffer.len);
416
fp->writebuffer.start=0;
422
/* write all the data in the buffer to the stream */
423
int tio_flush(TFILE *fp)
425
struct timeval deadline;
426
/* build a time by which we should be finished */
427
tio_tv_prepare(&deadline,&(fp->writetimeout));
428
/* loop until we have written our buffer */
429
while (fp->writebuffer.len > 0)
431
/* wait until we can write */
432
if (tio_select(fp,0,&deadline))
434
/* write one block */
435
if (tio_writebuf(fp))
441
/* try a single write of data in the buffer if the file descriptor
443
static int tio_flush_nonblock(TFILE *fp)
448
/* prepare our filedescriptorset */
450
FD_SET(fp->fd,&fdset);
451
/* set the timeout to 0 to poll */
454
/* wait for activity */
455
rv=select(FD_SETSIZE,NULL,&fdset,NULL,&tv);
456
/* check if any file descriptors were ready (timeout) or we were
458
if ((rv==0)||((rv<0)&&(errno==EINTR)))
460
/* any other errors? */
463
/* so file descriptor will accept writes */
464
return tio_writebuf(fp);
467
int tio_write(TFILE *fp, const void *buf, size_t count)
472
const uint8_t *ptr=(const uint8_t *)buf;
473
/* keep filling the buffer until we have bufferred everything */
476
/* figure out free size in buffer */
477
fr=fp->writebuffer.size-(fp->writebuffer.start+fp->writebuffer.len);
480
/* the data fits in the buffer */
481
memcpy(fp->writebuffer.buffer+fp->writebuffer.start+fp->writebuffer.len,ptr,count);
482
fp->writebuffer.len+=count;
487
/* fill the buffer with data that will fit */
488
memcpy(fp->writebuffer.buffer+fp->writebuffer.start+fp->writebuffer.len,ptr,fr);
489
fp->writebuffer.len+=fr;
493
/* try to flush some of the data that is in the buffer */
494
if (tio_flush_nonblock(fp))
496
/* if we have room now, try again */
497
if (fp->writebuffer.size>(fp->writebuffer.start+fp->writebuffer.len))
499
/* try to grow the buffer */
500
if (fp->writebuffer.size<fp->writebuffer.maxsize)
502
newsz=fp->writebuffer.size*2;
503
if (newsz>fp->writebuffer.maxsize)
504
newsz=fp->writebuffer.maxsize;
505
tmp=realloc(fp->writebuffer.buffer,newsz);
508
fp->writebuffer.buffer=tmp;
509
fp->writebuffer.size=newsz;
510
continue; /* try again */
513
/* write the buffer to the stream */
520
int tio_close(TFILE *fp)
523
/* write any buffered data */
525
#ifdef DEBUG_TIO_STATS
526
/* dump statistics to stderr */
527
fprintf(stderr,"DEBUG_TIO_STATS READ=%d WRITTEN=%d\n",fp->bytesread,fp->byteswritten);
528
#endif /* DEBUG_TIO_STATS */
529
/* close file descriptor */
532
/* free any allocated buffers */
533
free(fp->readbuffer.buffer);
534
free(fp->writebuffer.buffer);
535
/* free the tio struct itself */
537
/* return the result of the earlier operations */
541
void tio_mark(TFILE *fp)
543
/* move any data in the buffer to the start of the buffer */
544
if ((fp->readbuffer.start>0)&&(fp->readbuffer.len>0))
546
memmove(fp->readbuffer.buffer,fp->readbuffer.buffer+fp->readbuffer.start,fp->readbuffer.len);
547
fp->readbuffer.start=0;
549
/* mark the stream as resettable */
550
fp->read_resettable=1;
553
int tio_reset(TFILE *fp)
555
/* check if the stream is (still) resettable */
556
if (!fp->read_resettable)
558
/* reset the buffer */
559
fp->readbuffer.len+=fp->readbuffer.start;
560
fp->readbuffer.start=0;