]> git.sur5r.net Git - openldap/blob - contrib/slapd-modules/nssov/nss-pam-ldapd/tio.c
ITS#8527 - Add additional debug logging on consumer/provider state when the consumer...
[openldap] / contrib / slapd-modules / nssov / nss-pam-ldapd / tio.c
1 /*
2    tio.c - timed io functions
3    This file is part of the nss-pam-ldapd library.
4
5    Copyright (C) 2007-2014 Arthur de Jong
6
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.
11
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.
16
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
20    02110-1301 USA
21 */
22
23 #include "portable.h"
24
25 #ifdef HAVE_STDINT_H
26 #include <stdint.h>
27 #endif /* HAVE_STDINT_H */
28 #include <stdlib.h>
29 #include <unistd.h>
30 #include <sys/time.h>
31 #include <sys/types.h>
32 #include <sys/socket.h>
33 #include <errno.h>
34 #include <string.h>
35 #include <signal.h>
36 #include <stdio.h>
37 #include <limits.h>
38 #include <poll.h>
39 #include <time.h>
40
41 #include "tio.h"
42
43 /* for platforms that don't have ETIME use ETIMEDOUT */
44 #ifndef ETIME
45 #define ETIME ETIMEDOUT
46 #endif /* ETIME */
47
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:
52    |.....********......|
53          ^start        ^size
54          ^--len--^           */
55 struct tio_buffer {
56   uint8_t *buffer;
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) */
61 };
62
63 /* structure that holds all the state for files */
64 struct tio_fileinfo {
65   int fd;
66   struct tio_buffer readbuffer;
67   struct tio_buffer writebuffer;
68   int readtimeout;
69   int writetimeout;
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 */
74   size_t byteswritten;
75   size_t bytesread;
76 #endif /* DEBUG_TIO_STATS */
77 };
78
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
82 #ifdef CLOCK_HIGHRES
83 #define CLOCK_MONOTONIC CLOCK_HIGHRES
84 #endif /* CLOCK_HIGHRES */
85 #endif /* not CLOCK_MONOTONIC */
86
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)
91 {
92   struct timespec tv;
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))
95   {
96     if (clock_gettime(CLOCK_MONOTONIC, deadline) == 0)
97     {
98       deadline->tv_sec += timeout / 1000;
99       deadline->tv_nsec += (timeout % 1000) * 1000000;
100     }
101     return timeout;
102   }
103   /* get the current time (fall back to full time on error) */
104   if (clock_gettime(CLOCK_MONOTONIC, &tv))
105     return timeout;
106   /* calculate time remaining in milliseconds */
107   return (deadline->tv_sec - tv.tv_sec) * 1000 +
108          (deadline->tv_nsec - tv.tv_nsec) / 1000000;
109 }
110
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)
115 {
116   struct tio_fileinfo *fp;
117   fp = (struct tio_fileinfo *)malloc(sizeof(struct tio_fileinfo));
118   if (fp == NULL)
119     return NULL;
120   fp->fd = fd;
121   /* initialize read buffer */
122   fp->readbuffer.buffer = (uint8_t *)malloc(initreadsize);
123   if (fp->readbuffer.buffer == NULL)
124   {
125     free(fp);
126     return NULL;
127   }
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)
135   {
136     free(fp->readbuffer.buffer);
137     free(fp);
138     return NULL;
139   }
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;
150   fp->bytesread = 0;
151 #endif /* DEBUG_TIO_STATS */
152   return fp;
153 }
154
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)
159 {
160   int t;
161   struct pollfd fds[1];
162   int rv;
163   while (1)
164   {
165     fds[0].fd = fd;
166     fds[0].events = events;
167     /* figure out the time we need to wait */
168     if ((t = tio_time_remaining(deadline, timeout)) < 0)
169     {
170       errno = ETIME;
171       return -1;
172     }
173     /* sanitiy check for moving clock */
174     if (t > timeout)
175       t = timeout;
176     /* wait for activity */
177     rv = poll(fds, 1, t);
178     if (rv > 0)
179       return 0; /* we have activity */
180     else if (rv == 0)
181     {
182       /* no file descriptors were available within the specified time */
183       errno = ETIME;
184       return -1;
185     }
186     else if ((errno != EINTR) && (errno != EAGAIN))
187       /* some error ocurred */
188       return -1;
189     /* we just try again on EINTR or EAGAIN */
190   }
191 }
192
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)
196 {
197   struct timespec deadline = {0, 0};
198   int rv;
199   uint8_t *tmp;
200   size_t newsz;
201   size_t len;
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 */
205   while (1)
206   {
207     /* check if we have enough data in the buffer */
208     if (fp->readbuffer.len >= count)
209     {
210       if (count > 0)
211       {
212         if (ptr != NULL)
213           memcpy(ptr, fp->readbuffer.buffer + fp->readbuffer.start, count);
214         /* adjust buffer position */
215         fp->readbuffer.start += count;
216         fp->readbuffer.len -= count;
217       }
218       return 0;
219     }
220     /* empty what we have and continue from there */
221     if (fp->readbuffer.len > 0)
222     {
223       if (ptr != NULL)
224       {
225         memcpy(ptr, fp->readbuffer.buffer + fp->readbuffer.start,
226                fp->readbuffer.len);
227         ptr += fp->readbuffer.len;
228       }
229       count -= fp->readbuffer.len;
230       fp->readbuffer.start += fp->readbuffer.len;
231       fp->readbuffer.len = 0;
232     }
233     /* after this point until the read fp->readbuffer.len is 0 */
234     if (!fp->read_resettable)
235     {
236       /* the stream is not resettable, re-use the buffer */
237       fp->readbuffer.start = 0;
238     }
239     else if (fp->readbuffer.start >= (fp->readbuffer.size - 4))
240     {
241       /* buffer is running empty, try to grow buffer */
242       if (fp->readbuffer.size < fp->readbuffer.maxsize)
243       {
244         newsz = fp->readbuffer.size * 2;
245         if (newsz > fp->readbuffer.maxsize)
246           newsz = fp->readbuffer.maxsize;
247         tmp = realloc(fp->readbuffer.buffer, newsz);
248         if (tmp != NULL)
249         {
250           fp->readbuffer.buffer = tmp;
251           fp->readbuffer.size = newsz;
252         }
253       }
254       /* if buffer still does not contain enough room, clear resettable */
255       if (fp->readbuffer.start >= (fp->readbuffer.size - 4))
256       {
257         fp->readbuffer.start = 0;
258         fp->read_resettable = 0;
259       }
260     }
261     /* wait until we have input */
262     if (tio_wait(fp->fd, POLLIN, fp->readtimeout, &deadline))
263       return -1;
264     /* read the input in the buffer */
265     len = fp->readbuffer.size - fp->readbuffer.start;
266 #ifdef SSIZE_MAX
267     if (len > SSIZE_MAX)
268       len = SSIZE_MAX;
269 #endif /* SSIZE_MAX */
270     rv = read(fp->fd, fp->readbuffer.buffer + fp->readbuffer.start, len);
271     /* check for errors */
272     if (rv == 0)
273     {
274       errno = ECONNRESET;
275       return -1;
276     }
277     else if ((rv < 0) && (errno != EINTR) && (errno != EAGAIN))
278       return -1;        /* something went wrong with the read */
279     else if (rv > 0)
280       fp->readbuffer.len = rv;  /* skip the read part in the buffer */
281 #ifdef DEBUG_TIO_STATS
282     fp->bytesread += rv;
283 #endif /* DEBUG_TIO_STATS */
284   }
285 }
286
287 /* Read and discard the specified number of bytes from the stream. */
288 int tio_skip(TFILE *fp, size_t count)
289 {
290   return tio_read(fp, NULL, count);
291 }
292
293 /* Read all available data from the stream and empty the read buffer. */
294 int tio_skipall(TFILE *fp, int timeout)
295 {
296   struct timespec deadline = {0, 0};
297   int rv;
298   size_t len;
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;
305 #ifdef SSIZE_MAX
306   if (len > SSIZE_MAX)
307     len = SSIZE_MAX;
308 #endif /* SSIZE_MAX */
309   while (1)
310   {
311     /* wait until we have input */
312     if (tio_wait(fp->fd, POLLIN, timeout, &deadline))
313       return -1;
314     /* read data from the stream */
315     rv = read(fp->fd, fp->readbuffer.buffer, len);
316     if (rv == 0)
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 */
322   }
323 }
324
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)
328 {
329   int rv;
330   /* write the buffer */
331 #ifdef MSG_NOSIGNAL
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;
345   /* ignore SIGPIPE */
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 */
354 #endif
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 */
359   if (rv > 0)
360   {
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))
371     {
372       memmove(fp->writebuffer.buffer,
373               fp->writebuffer.buffer + fp->writebuffer.start,
374               fp->writebuffer.len);
375       fp->writebuffer.start = 0;
376     }
377   }
378   return 0;
379 }
380
381 /* write all the data in the buffer to the stream */
382 int tio_flush(TFILE *fp)
383 {
384   struct timespec deadline = {0, 0};
385   /* loop until we have written our buffer */
386   while (fp->writebuffer.len > 0)
387   {
388     /* wait until we can write */
389     if (tio_wait(fp->fd, POLLOUT, fp->writetimeout, &deadline))
390       return -1;
391     /* write one block */
392     if (tio_writebuf(fp))
393       return -1;
394   }
395   return 0;
396 }
397
398 /* try a single write of data in the buffer if the file descriptor
399    will accept data */
400 static int tio_flush_nonblock(TFILE *fp)
401 {
402   struct pollfd fds[1];
403   int rv;
404   /* see if we can write without blocking */
405   fds[0].fd = fp->fd;
406   fds[0].events = POLLOUT;
407   rv = poll(fds, 1, 0);
408   /* check if any file descriptors were ready (timeout) or we were
409      interrupted */
410   if ((rv == 0) || ((rv < 0) && ((errno == EINTR) || (errno == EAGAIN))))
411     return 0;
412   /* any other errors? */
413   if (rv < 0)
414     return -1;
415   /* so file descriptor will accept writes */
416   return tio_writebuf(fp);
417 }
418
419 int tio_write(TFILE *fp, const void *buf, size_t count)
420 {
421   size_t fr;
422   uint8_t *tmp;
423   size_t newsz;
424   const uint8_t *ptr = (const uint8_t *)buf;
425   /* keep filling the buffer until we have bufferred everything */
426   while (count > 0)
427   {
428     /* figure out free size in buffer */
429     fr = fp->writebuffer.size - (fp->writebuffer.start + fp->writebuffer.len);
430     if (count <= fr)
431     {
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;
436       return 0;
437     }
438     else if (fr > 0)
439     {
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;
444       ptr += fr;
445       count -= fr;
446     }
447     /* try to flush some of the data that is in the buffer */
448     if (tio_flush_nonblock(fp))
449       return -1;
450     /* if we have room now, try again */
451     if (fp->writebuffer.size > (fp->writebuffer.start + fp->writebuffer.len))
452       continue;
453     /* try to grow the buffer */
454     if (fp->writebuffer.size < fp->writebuffer.maxsize)
455     {
456       newsz = fp->writebuffer.size * 2;
457       if (newsz > fp->writebuffer.maxsize)
458         newsz = fp->writebuffer.maxsize;
459       tmp = realloc(fp->writebuffer.buffer, newsz);
460       if (tmp != NULL)
461       {
462         fp->writebuffer.buffer = tmp;
463         fp->writebuffer.size = newsz;
464         continue; /* try again */
465       }
466     }
467     /* write the buffer to the stream */
468     if (tio_flush(fp))
469       return -1;
470   }
471   return 0;
472 }
473
474 int tio_close(TFILE *fp)
475 {
476   int retv;
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,
482           fp->byteswritten);
483 #endif /* DEBUG_TIO_STATS */
484   /* close file descriptor */
485   if (close(fp->fd))
486     retv = -1;
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 */
493   free(fp);
494   /* return the result of the earlier operations */
495   return retv;
496 }
497
498 void tio_mark(TFILE *fp)
499 {
500   /* move any data in the buffer to the start of the buffer */
501   if ((fp->readbuffer.start > 0) && (fp->readbuffer.len > 0))
502   {
503     memmove(fp->readbuffer.buffer,
504             fp->readbuffer.buffer + fp->readbuffer.start, fp->readbuffer.len);
505     fp->readbuffer.start = 0;
506   }
507   /* mark the stream as resettable */
508   fp->read_resettable = 1;
509 }
510
511 int tio_reset(TFILE *fp)
512 {
513   /* check if the stream is (still) resettable */
514   if (!fp->read_resettable)
515     return -1;
516   /* reset the buffer */
517   fp->readbuffer.len += fp->readbuffer.start;
518   fp->readbuffer.start = 0;
519   return 0;
520 }