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
28 #endif /* HAVE_STDINT_H */
32 #include <sys/types.h>
33 #include <sys/socket.h>
43 /* for platforms that don't have ETIME use ETIMEDOUT */
45 #define ETIME ETIMEDOUT
48 /* structure that holds a buffer
49 the buffer contains the data that is between the application and the
50 file descriptor that is used for efficient transfer
51 the buffer is built up as follows:
57 size_t size; /* the size of the buffer */
58 size_t maxsize; /* the maximum size of the buffer */
59 size_t start; /* the start of the data (before start is unused) */
60 size_t len; /* size of the data (from the start) */
63 /* structure that holds all the state for files */
66 struct tio_buffer readbuffer;
67 struct tio_buffer writebuffer;
70 int read_resettable; /* whether the tio_reset() function can be called */
71 #ifdef DEBUG_TIO_STATS
72 /* this is used to collect statistics on the use of the streams
73 and can be used to tune the buffer sizes */
76 #endif /* DEBUG_TIO_STATS */
79 /* build a timeval for comparison to when the operation should be finished */
80 static inline void tio_get_deadline(struct timeval *deadline,int timeout)
82 if (gettimeofday(deadline,NULL))
84 /* just blank it in case of errors */
89 deadline->tv_sec+=timeout/1000;
90 deadline->tv_sec+=(timeout%1000)*1000;
93 /* update the timeout to the value that is remaining before deadline
94 returns non-zero if there is no more time before the deadline */
95 static inline int tio_time_remaining(const struct timeval *deadline)
98 /* get the current time */
99 if (gettimeofday(&tv,NULL))
101 /* 1 second default if gettimeofday() is broken */
104 /* calculate time remaining in miliseconds */
105 return (deadline->tv_sec-tv.tv_sec)*1000 + (deadline->tv_usec-tv.tv_usec)/1000;
108 /* open a new TFILE based on the file descriptor */
109 TFILE *tio_fdopen(int fd,int readtimeout,int writetimeout,
110 size_t initreadsize,size_t maxreadsize,
111 size_t initwritesize,size_t maxwritesize)
113 struct tio_fileinfo *fp;
114 fp=(struct tio_fileinfo *)malloc(sizeof(struct tio_fileinfo));
118 /* initialize read buffer */
119 fp->readbuffer.buffer=(uint8_t *)malloc(initreadsize);
120 if (fp->readbuffer.buffer==NULL)
125 fp->readbuffer.size=initreadsize;
126 fp->readbuffer.maxsize=maxreadsize;
127 fp->readbuffer.start=0;
128 fp->readbuffer.len=0;
129 /* initialize write buffer */
130 fp->writebuffer.buffer=(uint8_t *)malloc(initwritesize);
131 if (fp->writebuffer.buffer==NULL)
133 free(fp->readbuffer.buffer);
137 fp->writebuffer.size=initwritesize;
138 fp->writebuffer.maxsize=maxwritesize;
139 fp->writebuffer.start=0;
140 fp->writebuffer.len=0;
141 /* initialize other attributes */
142 fp->readtimeout=readtimeout;
143 fp->writetimeout=writetimeout;
144 fp->read_resettable=0;
145 #ifdef DEBUG_TIO_STATS
148 #endif /* DEBUG_TIO_STATS */
152 /* wait for any activity on the specified file descriptor using
153 the specified deadline */
154 static int tio_wait(TFILE *fp,int readfd,const struct timeval *deadline)
157 struct pollfd fds[1];
161 /* figure out the time we need to wait */
162 if ((timeout=tio_time_remaining(deadline))<0)
167 /* wait for activity */
171 fds[0].events=POLLIN;
172 /* santiy check for moving clock */
173 if (timeout>fp->readtimeout)
174 timeout=fp->readtimeout;
179 fds[0].events=POLLOUT;
180 /* santiy check for moving clock */
181 if (timeout>fp->writetimeout)
182 timeout=fp->writetimeout;
184 rv=poll(fds,1,timeout);
186 return 0; /* we have activity */
189 /* no file descriptors were available within the specified time */
193 else if (errno!=EINTR)
194 /* some error ocurred */
196 /* we just try again on EINTR */
200 /* do a read on the file descriptor, returning the data in the buffer
201 if no data was read in the specified time an error is returned */
202 int tio_read(TFILE *fp, void *buf, size_t count)
204 struct timeval deadline;
209 /* have a more convenient storage type for the buffer */
210 uint8_t *ptr=(uint8_t *)buf;
211 /* build a time by which we should be finished */
212 tio_get_deadline(&deadline,fp->readtimeout);
213 /* loop until we have returned all the needed data */
216 /* check if we have enough data in the buffer */
217 if (fp->readbuffer.len >= count)
222 memcpy(ptr,fp->readbuffer.buffer+fp->readbuffer.start,count);
223 /* adjust buffer position */
224 fp->readbuffer.start+=count;
225 fp->readbuffer.len-=count;
229 /* empty what we have and continue from there */
230 if (fp->readbuffer.len>0)
234 memcpy(ptr,fp->readbuffer.buffer+fp->readbuffer.start,fp->readbuffer.len);
235 ptr+=fp->readbuffer.len;
237 count-=fp->readbuffer.len;
238 fp->readbuffer.start+=fp->readbuffer.len;
239 fp->readbuffer.len=0;
241 /* after this point until the read fp->readbuffer.len is 0 */
242 if (!fp->read_resettable)
244 /* the stream is not resettable, re-use the buffer */
245 fp->readbuffer.start=0;
247 else if (fp->readbuffer.start>=(fp->readbuffer.size-4))
249 /* buffer is running empty, try to grow buffer */
250 if (fp->readbuffer.size<fp->readbuffer.maxsize)
252 newsz=fp->readbuffer.size*2;
253 if (newsz>fp->readbuffer.maxsize)
254 newsz=fp->readbuffer.maxsize;
255 tmp=realloc(fp->readbuffer.buffer,newsz);
258 fp->readbuffer.buffer=tmp;
259 fp->readbuffer.size=newsz;
262 /* if buffer still does not contain enough room, clear resettable */
263 if (fp->readbuffer.start>=(fp->readbuffer.size-4))
265 fp->readbuffer.start=0;
266 fp->read_resettable=0;
269 /* wait until we have input */
270 if (tio_wait(fp,1,&deadline))
272 /* read the input in the buffer */
273 len=fp->readbuffer.size-fp->readbuffer.start;
277 #endif /* SSIZE_MAX */
278 rv=read(fp->fd,fp->readbuffer.buffer+fp->readbuffer.start,len);
279 /* check for errors */
285 else if ((rv<0)&&(errno!=EINTR)&&(errno!=EAGAIN))
286 return -1; /* something went wrong with the read */
287 /* skip the read part in the buffer */
288 fp->readbuffer.len=rv;
289 #ifdef DEBUG_TIO_STATS
291 #endif /* DEBUG_TIO_STATS */
295 /* Read and discard the specified number of bytes from the stream. */
296 int tio_skip(TFILE *fp, size_t count)
298 return tio_read(fp,NULL,count);
301 /* Read all available data from the stream and empty the read buffer. */
302 int tio_skipall(TFILE *fp)
304 struct pollfd fds[1];
307 /* clear the read buffer */
308 fp->readbuffer.start=0;
309 fp->readbuffer.len=0;
310 fp->read_resettable=0;
311 /* read until we can't read no more */
312 len=fp->readbuffer.size;
316 #endif /* SSIZE_MAX */
319 /* see if any data is available */
321 fds[0].events=POLLIN;
323 /* check the poll() result */
325 return 0; /* no file descriptor ready */
326 if ((rv<0)&&((errno==EINTR)||(errno==EAGAIN)))
327 continue; /* interrupted, try again */
329 return -1; /* something went wrong */
330 /* read data from the stream */
331 rv=read(fp->fd,fp->readbuffer.buffer,len);
333 return 0; /* end-of-file */
334 if ((rv<0)&&(errno==EWOULDBLOCK))
335 return 0; /* we've ready everything we can without blocking */
336 if ((rv<0)&&(errno!=EINTR)&&(errno!=EAGAIN))
337 return -1; /* something went wrong with the read */
341 /* the caller has assured us that we can write to the file descriptor
342 and we give it a shot */
343 static int tio_writebuf(TFILE *fp)
346 /* write the buffer */
348 rv=send(fp->fd,fp->writebuffer.buffer+fp->writebuffer.start,fp->writebuffer.len,MSG_NOSIGNAL);
349 #else /* not MSG_NOSIGNAL */
350 /* on platforms that cannot use send() with masked signals, we change the
351 signal mask and change it back after the write (note that there is a
352 race condition here) */
353 struct sigaction act,oldact;
354 /* set up sigaction */
355 memset(&act,0,sizeof(struct sigaction));
356 act.sa_sigaction=NULL;
357 act.sa_handler=SIG_IGN;
358 sigemptyset(&act.sa_mask);
359 act.sa_flags=SA_RESTART;
361 if (sigaction(SIGPIPE,&act,&oldact)!=0)
362 return -1; /* error setting signal handler */
363 /* write the buffer */
364 rv=write(fp->fd,fp->writebuffer.buffer+fp->writebuffer.start,fp->writebuffer.len);
365 /* restore the old handler for SIGPIPE */
366 if (sigaction(SIGPIPE,&oldact,NULL)!=0)
367 return -1; /* error restoring signal handler */
369 /* check for errors */
370 if ((rv==0)||((rv<0)&&(errno!=EINTR)&&(errno!=EAGAIN)))
371 return -1; /* something went wrong with the write */
372 /* skip the written part in the buffer */
375 fp->writebuffer.start+=rv;
376 fp->writebuffer.len-=rv;
377 #ifdef DEBUG_TIO_STATS
378 fp->byteswritten+=rv;
379 #endif /* DEBUG_TIO_STATS */
380 /* reset start if len is 0 */
381 if (fp->writebuffer.len==0)
382 fp->writebuffer.start=0;
383 /* move contents of the buffer to the front if it will save enough room */
384 if (fp->writebuffer.start>=(fp->writebuffer.size/4))
386 memmove(fp->writebuffer.buffer,fp->writebuffer.buffer+fp->writebuffer.start,fp->writebuffer.len);
387 fp->writebuffer.start=0;
393 /* write all the data in the buffer to the stream */
394 int tio_flush(TFILE *fp)
396 struct timeval deadline;
397 /* build a time by which we should be finished */
398 tio_get_deadline(&deadline,fp->writetimeout);
399 /* loop until we have written our buffer */
400 while (fp->writebuffer.len > 0)
402 /* wait until we can write */
403 if (tio_wait(fp,0,&deadline))
405 /* write one block */
406 if (tio_writebuf(fp))
412 /* try a single write of data in the buffer if the file descriptor
414 static int tio_flush_nonblock(TFILE *fp)
416 struct pollfd fds[1];
418 /* wait for activity */
420 fds[0].events=POLLOUT;
422 /* check if any file descriptors were ready (timeout) or we were
424 if ((rv==0)||((rv<0)&&(errno==EINTR)))
426 /* any other errors? */
429 /* so file descriptor will accept writes */
430 return tio_writebuf(fp);
433 int tio_write(TFILE *fp, const void *buf, size_t count)
438 const uint8_t *ptr=(const uint8_t *)buf;
439 /* keep filling the buffer until we have bufferred everything */
442 /* figure out free size in buffer */
443 fr=fp->writebuffer.size-(fp->writebuffer.start+fp->writebuffer.len);
446 /* the data fits in the buffer */
447 memcpy(fp->writebuffer.buffer+fp->writebuffer.start+fp->writebuffer.len,ptr,count);
448 fp->writebuffer.len+=count;
453 /* fill the buffer with data that will fit */
454 memcpy(fp->writebuffer.buffer+fp->writebuffer.start+fp->writebuffer.len,ptr,fr);
455 fp->writebuffer.len+=fr;
459 /* try to flush some of the data that is in the buffer */
460 if (tio_flush_nonblock(fp))
462 /* if we have room now, try again */
463 if (fp->writebuffer.size>(fp->writebuffer.start+fp->writebuffer.len))
465 /* try to grow the buffer */
466 if (fp->writebuffer.size<fp->writebuffer.maxsize)
468 newsz=fp->writebuffer.size*2;
469 if (newsz>fp->writebuffer.maxsize)
470 newsz=fp->writebuffer.maxsize;
471 tmp=realloc(fp->writebuffer.buffer,newsz);
474 fp->writebuffer.buffer=tmp;
475 fp->writebuffer.size=newsz;
476 continue; /* try again */
479 /* write the buffer to the stream */
486 int tio_close(TFILE *fp)
489 /* write any buffered data */
491 #ifdef DEBUG_TIO_STATS
492 /* dump statistics to stderr */
493 fprintf(stderr,"DEBUG_TIO_STATS READ=%d WRITTEN=%d\n",fp->bytesread,fp->byteswritten);
494 #endif /* DEBUG_TIO_STATS */
495 /* close file descriptor */
498 /* free any allocated buffers */
499 free(fp->readbuffer.buffer);
500 free(fp->writebuffer.buffer);
501 /* free the tio struct itself */
503 /* return the result of the earlier operations */
507 void tio_mark(TFILE *fp)
509 /* move any data in the buffer to the start of the buffer */
510 if ((fp->readbuffer.start>0)&&(fp->readbuffer.len>0))
512 memmove(fp->readbuffer.buffer,fp->readbuffer.buffer+fp->readbuffer.start,fp->readbuffer.len);
513 fp->readbuffer.start=0;
515 /* mark the stream as resettable */
516 fp->read_resettable=1;
519 int tio_reset(TFILE *fp)
521 /* check if the stream is (still) resettable */
522 if (!fp->read_resettable)
524 /* reset the buffer */
525 fp->readbuffer.len+=fp->readbuffer.start;
526 fp->readbuffer.start=0;