2 tio.c - timed io functions
3 This file is part of the nss-pam-ldapd library.
5 Copyright (C) 2007-2014 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>
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 /* some older versions of Solaris don't provide CLOCK_MONOTONIC but do have
80 a CLOCK_HIGHRES that has the same properties we need */
81 #ifndef CLOCK_MONOTONIC
83 #define CLOCK_MONOTONIC CLOCK_HIGHRES
84 #endif /* CLOCK_HIGHRES */
85 #endif /* not CLOCK_MONOTONIC */
87 /* update the timeout to the value that is remaining before the deadline
88 returns the number of milliseconds before the deadline (or a negative
89 value of the deadline has expired) */
90 static inline int tio_time_remaining(struct timespec *deadline, int timeout)
93 /* if this is the first call, set the deadline and return the full time */
94 if ((deadline->tv_sec == 0) && (deadline->tv_nsec == 0))
96 if (clock_gettime(CLOCK_MONOTONIC, deadline) == 0)
98 deadline->tv_sec += timeout / 1000;
99 deadline->tv_nsec += (timeout % 1000) * 1000000;
103 /* get the current time (fall back to full time on error) */
104 if (clock_gettime(CLOCK_MONOTONIC, &tv))
106 /* calculate time remaining in milliseconds */
107 return (deadline->tv_sec - tv.tv_sec) * 1000 +
108 (deadline->tv_nsec - tv.tv_nsec) / 1000000;
111 /* open a new TFILE based on the file descriptor */
112 TFILE *tio_fdopen(int fd, int readtimeout, int writetimeout,
113 size_t initreadsize, size_t maxreadsize,
114 size_t initwritesize, size_t maxwritesize)
116 struct tio_fileinfo *fp;
117 fp = (struct tio_fileinfo *)malloc(sizeof(struct tio_fileinfo));
121 /* initialize read buffer */
122 fp->readbuffer.buffer = (uint8_t *)malloc(initreadsize);
123 if (fp->readbuffer.buffer == NULL)
128 fp->readbuffer.size = initreadsize;
129 fp->readbuffer.maxsize = maxreadsize;
130 fp->readbuffer.start = 0;
131 fp->readbuffer.len = 0;
132 /* initialize write buffer */
133 fp->writebuffer.buffer = (uint8_t *)malloc(initwritesize);
134 if (fp->writebuffer.buffer == NULL)
136 free(fp->readbuffer.buffer);
140 fp->writebuffer.size = initwritesize;
141 fp->writebuffer.maxsize = maxwritesize;
142 fp->writebuffer.start = 0;
143 fp->writebuffer.len = 0;
144 /* initialize other attributes */
145 fp->readtimeout = readtimeout;
146 fp->writetimeout = writetimeout;
147 fp->read_resettable = 0;
148 #ifdef DEBUG_TIO_STATS
149 fp->byteswritten = 0;
151 #endif /* DEBUG_TIO_STATS */
155 /* wait for any activity on the specified file descriptor using
156 the specified deadline */
157 static int tio_wait(int fd, short events, int timeout,
158 struct timespec *deadline)
161 struct pollfd fds[1];
166 fds[0].events = events;
167 /* figure out the time we need to wait */
168 if ((t = tio_time_remaining(deadline, timeout)) < 0)
173 /* sanitiy check for moving clock */
176 /* wait for activity */
177 rv = poll(fds, 1, t);
179 return 0; /* we have activity */
182 /* no file descriptors were available within the specified time */
186 else if ((errno != EINTR) && (errno != EAGAIN))
187 /* some error ocurred */
189 /* we just try again on EINTR or EAGAIN */
193 /* do a read on the file descriptor, returning the data in the buffer
194 if no data was read in the specified time an error is returned */
195 int tio_read(TFILE *fp, void *buf, size_t count)
197 struct timespec deadline = {0, 0};
202 /* have a more convenient storage type for the buffer */
203 uint8_t *ptr = (uint8_t *)buf;
204 /* loop until we have returned all the needed data */
207 /* check if we have enough data in the buffer */
208 if (fp->readbuffer.len >= count)
213 memcpy(ptr, fp->readbuffer.buffer + fp->readbuffer.start, count);
214 /* adjust buffer position */
215 fp->readbuffer.start += count;
216 fp->readbuffer.len -= count;
220 /* empty what we have and continue from there */
221 if (fp->readbuffer.len > 0)
225 memcpy(ptr, fp->readbuffer.buffer + fp->readbuffer.start,
227 ptr += fp->readbuffer.len;
229 count -= fp->readbuffer.len;
230 fp->readbuffer.start += fp->readbuffer.len;
231 fp->readbuffer.len = 0;
233 /* after this point until the read fp->readbuffer.len is 0 */
234 if (!fp->read_resettable)
236 /* the stream is not resettable, re-use the buffer */
237 fp->readbuffer.start = 0;
239 else if (fp->readbuffer.start >= (fp->readbuffer.size - 4))
241 /* buffer is running empty, try to grow buffer */
242 if (fp->readbuffer.size < fp->readbuffer.maxsize)
244 newsz = fp->readbuffer.size * 2;
245 if (newsz > fp->readbuffer.maxsize)
246 newsz = fp->readbuffer.maxsize;
247 tmp = realloc(fp->readbuffer.buffer, newsz);
250 fp->readbuffer.buffer = tmp;
251 fp->readbuffer.size = newsz;
254 /* if buffer still does not contain enough room, clear resettable */
255 if (fp->readbuffer.start >= (fp->readbuffer.size - 4))
257 fp->readbuffer.start = 0;
258 fp->read_resettable = 0;
261 /* wait until we have input */
262 if (tio_wait(fp->fd, POLLIN, fp->readtimeout, &deadline))
264 /* read the input in the buffer */
265 len = fp->readbuffer.size - fp->readbuffer.start;
269 #endif /* SSIZE_MAX */
270 rv = read(fp->fd, fp->readbuffer.buffer + fp->readbuffer.start, len);
271 /* check for errors */
277 else if ((rv < 0) && (errno != EINTR) && (errno != EAGAIN))
278 return -1; /* something went wrong with the read */
280 fp->readbuffer.len = rv; /* skip the read part in the buffer */
281 #ifdef DEBUG_TIO_STATS
283 #endif /* DEBUG_TIO_STATS */
287 /* Read and discard the specified number of bytes from the stream. */
288 int tio_skip(TFILE *fp, size_t count)
290 return tio_read(fp, NULL, count);
293 /* Read all available data from the stream and empty the read buffer. */
294 int tio_skipall(TFILE *fp, int timeout)
296 struct timespec deadline = {0, 0};
299 /* clear the read buffer */
300 fp->readbuffer.start = 0;
301 fp->readbuffer.len = 0;
302 fp->read_resettable = 0;
303 /* read until we can't read no more */
304 len = fp->readbuffer.size;
308 #endif /* SSIZE_MAX */
311 /* wait until we have input */
312 if (tio_wait(fp->fd, POLLIN, timeout, &deadline))
314 /* read data from the stream */
315 rv = read(fp->fd, fp->readbuffer.buffer, len);
317 return 0; /* end-of-file */
318 if ((rv < 0) && (errno == EWOULDBLOCK))
319 return 0; /* we've ready everything we can without blocking */
320 if ((rv < 0) && (errno != EINTR) && (errno != EAGAIN))
321 return -1; /* something went wrong with the read */
325 /* the caller has assured us that we can write to the file descriptor
326 and we give it a shot */
327 static int tio_writebuf(TFILE *fp)
330 /* write the buffer */
332 rv = send(fp->fd, fp->writebuffer.buffer + fp->writebuffer.start,
333 fp->writebuffer.len, MSG_NOSIGNAL);
334 #else /* not MSG_NOSIGNAL */
335 /* on platforms that cannot use send() with masked signals, we change the
336 signal mask and change it back after the write (note that there is a
337 race condition here) */
338 struct sigaction act, oldact;
339 /* set up sigaction */
340 memset(&act, 0, sizeof(struct sigaction));
341 act.sa_sigaction = NULL;
342 act.sa_handler = SIG_IGN;
343 sigemptyset(&act.sa_mask);
344 act.sa_flags = SA_RESTART;
346 if (sigaction(SIGPIPE, &act, &oldact) != 0)
347 return -1; /* error setting signal handler */
348 /* write the buffer */
349 rv = write(fp->fd, fp->writebuffer.buffer + fp->writebuffer.start,
350 fp->writebuffer.len);
351 /* restore the old handler for SIGPIPE */
352 if (sigaction(SIGPIPE, &oldact, NULL) != 0)
353 return -1; /* error restoring signal handler */
355 /* check for errors */
356 if ((rv == 0) || ((rv < 0) && (errno != EINTR) && (errno != EAGAIN)))
357 return -1; /* something went wrong with the write */
358 /* skip the written part in the buffer */
361 fp->writebuffer.start += rv;
362 fp->writebuffer.len -= rv;
363 #ifdef DEBUG_TIO_STATS
364 fp->byteswritten += rv;
365 #endif /* DEBUG_TIO_STATS */
366 /* reset start if len is 0 */
367 if (fp->writebuffer.len == 0)
368 fp->writebuffer.start = 0;
369 /* move contents of the buffer to the front if it will save enough room */
370 if (fp->writebuffer.start >= (fp->writebuffer.size / 4))
372 memmove(fp->writebuffer.buffer,
373 fp->writebuffer.buffer + fp->writebuffer.start,
374 fp->writebuffer.len);
375 fp->writebuffer.start = 0;
381 /* write all the data in the buffer to the stream */
382 int tio_flush(TFILE *fp)
384 struct timespec deadline = {0, 0};
385 /* loop until we have written our buffer */
386 while (fp->writebuffer.len > 0)
388 /* wait until we can write */
389 if (tio_wait(fp->fd, POLLOUT, fp->writetimeout, &deadline))
391 /* write one block */
392 if (tio_writebuf(fp))
398 /* try a single write of data in the buffer if the file descriptor
400 static int tio_flush_nonblock(TFILE *fp)
402 struct pollfd fds[1];
404 /* see if we can write without blocking */
406 fds[0].events = POLLOUT;
407 rv = poll(fds, 1, 0);
408 /* check if any file descriptors were ready (timeout) or we were
410 if ((rv == 0) || ((rv < 0) && ((errno == EINTR) || (errno == EAGAIN))))
412 /* any other errors? */
415 /* so file descriptor will accept writes */
416 return tio_writebuf(fp);
419 int tio_write(TFILE *fp, const void *buf, size_t count)
424 const uint8_t *ptr = (const uint8_t *)buf;
425 /* keep filling the buffer until we have bufferred everything */
428 /* figure out free size in buffer */
429 fr = fp->writebuffer.size - (fp->writebuffer.start + fp->writebuffer.len);
432 /* the data fits in the buffer */
433 memcpy(fp->writebuffer.buffer + fp->writebuffer.start +
434 fp->writebuffer.len, ptr, count);
435 fp->writebuffer.len += count;
440 /* fill the buffer with data that will fit */
441 memcpy(fp->writebuffer.buffer + fp->writebuffer.start +
442 fp->writebuffer.len, ptr, fr);
443 fp->writebuffer.len += fr;
447 /* try to flush some of the data that is in the buffer */
448 if (tio_flush_nonblock(fp))
450 /* if we have room now, try again */
451 if (fp->writebuffer.size > (fp->writebuffer.start + fp->writebuffer.len))
453 /* try to grow the buffer */
454 if (fp->writebuffer.size < fp->writebuffer.maxsize)
456 newsz = fp->writebuffer.size * 2;
457 if (newsz > fp->writebuffer.maxsize)
458 newsz = fp->writebuffer.maxsize;
459 tmp = realloc(fp->writebuffer.buffer, newsz);
462 fp->writebuffer.buffer = tmp;
463 fp->writebuffer.size = newsz;
464 continue; /* try again */
467 /* write the buffer to the stream */
474 int tio_close(TFILE *fp)
477 /* write any buffered data */
478 retv = tio_flush(fp);
479 #ifdef DEBUG_TIO_STATS
480 /* dump statistics to stderr */
481 fprintf(stderr, "DEBUG_TIO_STATS READ=%d WRITTEN=%d\n", fp->bytesread,
483 #endif /* DEBUG_TIO_STATS */
484 /* close file descriptor */
487 /* free any allocated buffers */
488 memset(fp->readbuffer.buffer, 0, fp->readbuffer.size);
489 memset(fp->writebuffer.buffer, 0, fp->writebuffer.size);
490 free(fp->readbuffer.buffer);
491 free(fp->writebuffer.buffer);
492 /* free the tio struct itself */
494 /* return the result of the earlier operations */
498 void tio_mark(TFILE *fp)
500 /* move any data in the buffer to the start of the buffer */
501 if ((fp->readbuffer.start > 0) && (fp->readbuffer.len > 0))
503 memmove(fp->readbuffer.buffer,
504 fp->readbuffer.buffer + fp->readbuffer.start, fp->readbuffer.len);
505 fp->readbuffer.start = 0;
507 /* mark the stream as resettable */
508 fp->read_resettable = 1;
511 int tio_reset(TFILE *fp)
513 /* check if the stream is (still) resettable */
514 if (!fp->read_resettable)
516 /* reset the buffer */
517 fp->readbuffer.len += fp->readbuffer.start;
518 fp->readbuffer.start = 0;