2 tio.c - timed io functions
3 This file is part of the nss-pam-ldapd library.
5 Copyright (C) 2007, 2008 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>
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;
233 /* have a more convenient storage type for the buffer */
234 uint8_t *ptr=(uint8_t *)buf;
235 /* build a time by which we should be finished */
236 /* TODO: probably only set up deadline if we have to do select() */
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 rv=read(fp->fd,fp->readbuffer.buffer+fp->readbuffer.start,fp->readbuffer.size-fp->readbuffer.start);
299 /* check for errors */
300 if ((rv==0)||((rv<0)&&(errno!=EINTR)&&(errno!=EAGAIN)))
301 return -1; /* something went wrong with the read */
302 /* skip the read part in the buffer */
303 fp->readbuffer.len=rv;
304 #ifdef DEBUG_TIO_STATS
306 #endif /* DEBUG_TIO_STATS */
310 /* Read and discard the specified number of bytes from the stream. */
311 int tio_skip(TFILE *fp, size_t count)
313 return tio_read(fp,NULL,count);
316 /* the caller has assured us that we can write to the file descriptor
317 and we give it a shot */
318 static int tio_writebuf(TFILE *fp)
321 /* write the buffer */
323 rv=send(fp->fd,fp->writebuffer.buffer+fp->writebuffer.start,fp->writebuffer.len,MSG_NOSIGNAL);
324 #else /* not MSG_NOSIGNAL */
325 /* on platforms that cannot use send() with masked signals, we change the
326 signal mask and change it back after the write (note that there is a
327 race condition here) */
328 struct sigaction act,oldact;
329 /* set up sigaction */
330 memset(&act,0,sizeof(struct sigaction));
331 act.sa_sigaction=NULL;
332 act.sa_handler=SIG_IGN;
333 sigemptyset(&act.sa_mask);
334 act.sa_flags=SA_RESTART;
336 if (sigaction(SIGPIPE,&act,&oldact)!=0)
337 return -1; /* error setting signal handler */
338 /* write the buffer */
339 rv=write(fp->fd,fp->writebuffer.buffer+fp->writebuffer.start,fp->writebuffer.len);
340 /* restore the old handler for SIGPIPE */
341 if (sigaction(SIGPIPE,&oldact,NULL)!=0)
342 return -1; /* error restoring signal handler */
344 /* check for errors */
345 if ((rv==0)||((rv<0)&&(errno!=EINTR)&&(errno!=EAGAIN)))
346 return -1; /* something went wrong with the write */
347 /* skip the written part in the buffer */
350 fp->writebuffer.start+=rv;
351 fp->writebuffer.len-=rv;
352 #ifdef DEBUG_TIO_STATS
353 fp->byteswritten+=rv;
354 #endif /* DEBUG_TIO_STATS */
355 /* reset start if len is 0 */
356 if (fp->writebuffer.len==0)
357 fp->writebuffer.start=0;
358 /* move contents of the buffer to the front if it will save enough room */
359 if (fp->writebuffer.start>=(fp->writebuffer.size/4))
361 memmove(fp->writebuffer.buffer,fp->writebuffer.buffer+fp->writebuffer.start,fp->writebuffer.len);
362 fp->writebuffer.start=0;
368 /* write all the data in the buffer to the stream */
369 int tio_flush(TFILE *fp)
371 struct timeval deadline;
372 /* build a time by which we should be finished */
373 tio_tv_prepare(&deadline,&(fp->writetimeout));
374 /* loop until we have written our buffer */
375 while (fp->writebuffer.len > 0)
377 /* wait until we can write */
378 if (tio_select(fp,0,&deadline))
380 /* write one block */
381 if (tio_writebuf(fp))
387 /* try a single write of data in the buffer if the file descriptor
389 static int tio_flush_nonblock(TFILE *fp)
394 /* prepare our filedescriptorset */
396 FD_SET(fp->fd,&fdset);
397 /* set the timeout to 0 to poll */
400 /* wait for activity */
401 rv=select(FD_SETSIZE,NULL,&fdset,NULL,&tv);
402 /* check if any file descriptors were ready (timeout) or we were
404 if ((rv==0)||((rv<0)&&(errno==EINTR)))
406 /* any other errors? */
409 /* so file descriptor will accept writes */
410 return tio_writebuf(fp);
413 int tio_write(TFILE *fp, const void *buf, size_t count)
418 const uint8_t *ptr=(const uint8_t *)buf;
419 /* keep filling the buffer until we have bufferred everything */
422 /* figure out free size in buffer */
423 fr=fp->writebuffer.size-(fp->writebuffer.start+fp->writebuffer.len);
426 /* the data fits in the buffer */
427 memcpy(fp->writebuffer.buffer+fp->writebuffer.start+fp->writebuffer.len,ptr,count);
428 fp->writebuffer.len+=count;
433 /* fill the buffer with data that will fit */
434 memcpy(fp->writebuffer.buffer+fp->writebuffer.start+fp->writebuffer.len,ptr,fr);
435 fp->writebuffer.len+=fr;
439 /* try to flush some of the data that is in the buffer */
440 if (tio_flush_nonblock(fp))
442 /* if we have room now, try again */
443 if (fp->writebuffer.size>(fp->writebuffer.start+fp->writebuffer.len))
445 /* try to grow the buffer */
446 if (fp->writebuffer.size<fp->writebuffer.maxsize)
448 newsz=fp->writebuffer.size*2;
449 if (newsz>fp->writebuffer.maxsize)
450 newsz=fp->writebuffer.maxsize;
451 tmp=realloc(fp->writebuffer.buffer,newsz);
454 fp->writebuffer.buffer=tmp;
455 fp->writebuffer.size=newsz;
456 continue; /* try again */
459 /* write the buffer to the stream */
466 int tio_close(TFILE *fp)
469 /* write any buffered data */
471 #ifdef DEBUG_TIO_STATS
472 /* dump statistics to stderr */
473 fprintf(stderr,"DEBUG_TIO_STATS READ=%d WRITTEN=%d\n",fp->bytesread,fp->byteswritten);
474 #endif /* DEBUG_TIO_STATS */
475 /* close file descriptor */
478 /* free any allocated buffers */
479 free(fp->readbuffer.buffer);
480 free(fp->writebuffer.buffer);
481 /* free the tio struct itself */
483 /* return the result of the earlier operations */
487 void tio_mark(TFILE *fp)
489 /* move any data in the buffer to the start of the buffer */
490 if ((fp->readbuffer.start>0)&&(fp->readbuffer.len>0))
492 memmove(fp->readbuffer.buffer,fp->readbuffer.buffer+fp->readbuffer.start,fp->readbuffer.len);
493 fp->readbuffer.start=0;
495 /* mark the stream as resettable */
496 fp->read_resettable=1;
499 int tio_reset(TFILE *fp)
501 /* check if the stream is (still) resettable */
502 if (!fp->read_resettable)
504 /* reset the buffer */
505 fp->readbuffer.len+=fp->readbuffer.start;
506 fp->readbuffer.start=0;