]> git.sur5r.net Git - openldap/blob - contrib/slapd-modules/nssov/nss-pam-ldapd/tio.c
31764c707c1676aa0ab64ec89fb52a183d7dcbe6
[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, 2008, 2010, 2011, 2012 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 "config.h"
24 #include "portable.h"
25
26 #ifdef HAVE_STDINT_H
27 #include <stdint.h>
28 #endif /* HAVE_STDINT_H */
29 #include <stdlib.h>
30 #include <unistd.h>
31 #include <sys/time.h>
32 #include <sys/types.h>
33 #include <sys/socket.h>
34 #include <errno.h>
35 #include <string.h>
36 #include <signal.h>
37 #include <stdio.h>
38 #include <limits.h>
39 #include <poll.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 /* build a timeval for comparison to when the operation should be finished */
80 static inline void tio_get_deadline(struct timeval *deadline,int timeout)
81 {
82   if (gettimeofday(deadline,NULL))
83   {
84     /* just blank it in case of errors */
85     deadline->tv_sec=0;
86     deadline->tv_usec=0;
87     return;
88   }
89   deadline->tv_sec+=timeout/1000;
90   deadline->tv_sec+=(timeout%1000)*1000;
91 }
92
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)
96 {
97   struct timeval tv;
98   /* get the current time */
99   if (gettimeofday(&tv,NULL))
100   {
101     /* 1 second default if gettimeofday() is broken */
102     return 1000;
103   }
104   /* calculate time remaining in miliseconds */
105   return (deadline->tv_sec-tv.tv_sec)*1000 + (deadline->tv_usec-tv.tv_usec)/1000;
106 }
107
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)
112 {
113   struct tio_fileinfo *fp;
114   fp=(struct tio_fileinfo *)malloc(sizeof(struct tio_fileinfo));
115   if (fp==NULL)
116     return NULL;
117   fp->fd=fd;
118   /* initialize read buffer */
119   fp->readbuffer.buffer=(uint8_t *)malloc(initreadsize);
120   if (fp->readbuffer.buffer==NULL)
121   {
122     free(fp);
123     return NULL;
124   }
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)
132   {
133     free(fp->readbuffer.buffer);
134     free(fp);
135     return NULL;
136   }
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
146   fp->byteswritten=0;
147   fp->bytesread=0;
148 #endif /* DEBUG_TIO_STATS */
149   return fp;
150 }
151
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)
155 {
156   int timeout;
157   struct pollfd fds[1];
158   int rv;
159   while (1)
160   {
161     /* figure out the time we need to wait */
162     if ((timeout=tio_time_remaining(deadline))<0)
163     {
164       errno=ETIME;
165       return -1;
166     }
167     /* wait for activity */
168     if (readfd)
169     {
170       fds[0].fd=fp->fd;
171       fds[0].events=POLLIN;
172       /* santiy check for moving clock */
173       if (timeout>fp->readtimeout)
174         timeout=fp->readtimeout;
175     }
176     else
177     {
178       fds[0].fd=fp->fd;
179       fds[0].events=POLLOUT;
180       /* santiy check for moving clock */
181       if (timeout>fp->writetimeout)
182         timeout=fp->writetimeout;
183     }
184     rv=poll(fds,1,timeout);
185     if (rv>0)
186       return 0; /* we have activity */
187     else if (rv==0)
188     {
189       /* no file descriptors were available within the specified time */
190       errno=ETIME;
191       return -1;
192     }
193     else if (errno!=EINTR)
194       /* some error ocurred */
195       return -1;
196     /* we just try again on EINTR */
197   }
198 }
199
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)
203 {
204   struct timeval deadline;
205   int rv;
206   uint8_t *tmp;
207   size_t newsz;
208   size_t len;
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 */
214   while (1)
215   {
216     /* check if we have enough data in the buffer */
217     if (fp->readbuffer.len >= count)
218     {
219       if (count>0)
220       {
221         if (ptr!=NULL)
222           memcpy(ptr,fp->readbuffer.buffer+fp->readbuffer.start,count);
223         /* adjust buffer position */
224         fp->readbuffer.start+=count;
225         fp->readbuffer.len-=count;
226       }
227       return 0;
228     }
229     /* empty what we have and continue from there */
230     if (fp->readbuffer.len>0)
231     {
232       if (ptr!=NULL)
233       {
234         memcpy(ptr,fp->readbuffer.buffer+fp->readbuffer.start,fp->readbuffer.len);
235         ptr+=fp->readbuffer.len;
236       }
237       count-=fp->readbuffer.len;
238       fp->readbuffer.start+=fp->readbuffer.len;
239       fp->readbuffer.len=0;
240     }
241     /* after this point until the read fp->readbuffer.len is 0 */
242     if (!fp->read_resettable)
243     {
244       /* the stream is not resettable, re-use the buffer */
245       fp->readbuffer.start=0;
246     }
247     else if (fp->readbuffer.start>=(fp->readbuffer.size-4))
248     {
249       /* buffer is running empty, try to grow buffer */
250       if (fp->readbuffer.size<fp->readbuffer.maxsize)
251       {
252         newsz=fp->readbuffer.size*2;
253         if (newsz>fp->readbuffer.maxsize)
254           newsz=fp->readbuffer.maxsize;
255         tmp=realloc(fp->readbuffer.buffer,newsz);
256         if (tmp!=NULL)
257         {
258           fp->readbuffer.buffer=tmp;
259           fp->readbuffer.size=newsz;
260         }
261       }
262       /* if buffer still does not contain enough room, clear resettable */
263       if (fp->readbuffer.start>=(fp->readbuffer.size-4))
264       {
265         fp->readbuffer.start=0;
266         fp->read_resettable=0;
267       }
268     }
269     /* wait until we have input */
270     if (tio_wait(fp,1,&deadline))
271       return -1;
272     /* read the input in the buffer */
273     len=fp->readbuffer.size-fp->readbuffer.start;
274 #ifdef SSIZE_MAX
275     if (len>SSIZE_MAX)
276       len=SSIZE_MAX;
277 #endif /* SSIZE_MAX */
278     rv=read(fp->fd,fp->readbuffer.buffer+fp->readbuffer.start,len);
279     /* check for errors */
280     if (rv==0)
281     {
282       errno=ECONNRESET;
283       return -1;
284     }
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
290     fp->bytesread+=rv;
291 #endif /* DEBUG_TIO_STATS */
292   }
293 }
294
295 /* Read and discard the specified number of bytes from the stream. */
296 int tio_skip(TFILE *fp, size_t count)
297 {
298   return tio_read(fp,NULL,count);
299 }
300
301 /* Read all available data from the stream and empty the read buffer. */
302 int tio_skipall(TFILE *fp)
303 {
304   struct pollfd fds[1];
305   int rv;
306   size_t len;
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;
313 #ifdef SSIZE_MAX
314   if (len>SSIZE_MAX)
315     len=SSIZE_MAX;
316 #endif /* SSIZE_MAX */
317   while (1)
318   {
319     /* see if any data is available */
320     fds[0].fd=fp->fd;
321     fds[0].events=POLLIN;
322     rv=poll(fds,1,0);
323     /* check the poll() result */
324     if (rv==0)
325       return 0; /* no file descriptor ready */
326     if ((rv<0)&&((errno==EINTR)||(errno==EAGAIN)))
327       continue; /* interrupted, try again */
328     if (rv<0)
329       return -1; /* something went wrong */
330     /* read data from the stream */
331     rv=read(fp->fd,fp->readbuffer.buffer,len);
332     if (rv==0)
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 */
338   }
339 }
340
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)
344 {
345   int rv;
346   /* write the buffer */
347 #ifdef MSG_NOSIGNAL
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;
360   /* ignore SIGPIPE */
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 */
368 #endif
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 */
373   if (rv>0)
374   {
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))
385     {
386       memmove(fp->writebuffer.buffer,fp->writebuffer.buffer+fp->writebuffer.start,fp->writebuffer.len);
387       fp->writebuffer.start=0;
388     }
389   }
390   return 0;
391 }
392
393 /* write all the data in the buffer to the stream */
394 int tio_flush(TFILE *fp)
395 {
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)
401   {
402     /* wait until we can write */
403     if (tio_wait(fp,0,&deadline))
404       return -1;
405     /* write one block */
406     if (tio_writebuf(fp))
407       return -1;
408   }
409   return 0;
410 }
411
412 /* try a single write of data in the buffer if the file descriptor
413    will accept data */
414 static int tio_flush_nonblock(TFILE *fp)
415 {
416   struct pollfd fds[1];
417   int rv;
418   /* wait for activity */
419   fds[0].fd=fp->fd;
420   fds[0].events=POLLOUT;
421   rv=poll(fds,1,0);
422   /* check if any file descriptors were ready (timeout) or we were
423      interrupted */
424   if ((rv==0)||((rv<0)&&(errno==EINTR)))
425     return 0;
426   /* any other errors? */
427   if (rv<0)
428     return -1;
429   /* so file descriptor will accept writes */
430   return tio_writebuf(fp);
431 }
432
433 int tio_write(TFILE *fp, const void *buf, size_t count)
434 {
435   size_t fr;
436   uint8_t *tmp;
437   size_t newsz;
438   const uint8_t *ptr=(const uint8_t *)buf;
439   /* keep filling the buffer until we have bufferred everything */
440   while (count>0)
441   {
442     /* figure out free size in buffer */
443     fr=fp->writebuffer.size-(fp->writebuffer.start+fp->writebuffer.len);
444     if (count <= fr)
445     {
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;
449       return 0;
450     }
451     else if (fr > 0)
452     {
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;
456       ptr+=fr;
457       count-=fr;
458     }
459     /* try to flush some of the data that is in the buffer */
460     if (tio_flush_nonblock(fp))
461       return -1;
462     /* if we have room now, try again */
463     if (fp->writebuffer.size>(fp->writebuffer.start+fp->writebuffer.len))
464       continue;
465     /* try to grow the buffer */
466     if (fp->writebuffer.size<fp->writebuffer.maxsize)
467     {
468       newsz=fp->writebuffer.size*2;
469       if (newsz>fp->writebuffer.maxsize)
470         newsz=fp->writebuffer.maxsize;
471       tmp=realloc(fp->writebuffer.buffer,newsz);
472       if (tmp!=NULL)
473       {
474         fp->writebuffer.buffer=tmp;
475         fp->writebuffer.size=newsz;
476         continue; /* try again */
477       }
478     }
479     /* write the buffer to the stream */
480     if (tio_flush(fp))
481       return -1;
482   }
483   return 0;
484 }
485
486 int tio_close(TFILE *fp)
487 {
488   int retv;
489   /* write any buffered data */
490   retv=tio_flush(fp);
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 */
496   if (close(fp->fd))
497     retv=-1;
498   /* free any allocated buffers */
499   free(fp->readbuffer.buffer);
500   free(fp->writebuffer.buffer);
501   /* free the tio struct itself */
502   free(fp);
503   /* return the result of the earlier operations */
504   return retv;
505 }
506
507 void tio_mark(TFILE *fp)
508 {
509   /* move any data in the buffer to the start of the buffer */
510   if ((fp->readbuffer.start>0)&&(fp->readbuffer.len>0))
511   {
512     memmove(fp->readbuffer.buffer,fp->readbuffer.buffer+fp->readbuffer.start,fp->readbuffer.len);
513     fp->readbuffer.start=0;
514   }
515   /* mark the stream as resettable */
516   fp->read_resettable=1;
517 }
518
519 int tio_reset(TFILE *fp)
520 {
521   /* check if the stream is (still) resettable */
522   if (!fp->read_resettable)
523     return -1;
524   /* reset the buffer */
525   fp->readbuffer.len+=fp->readbuffer.start;
526   fp->readbuffer.start=0;
527   return 0;
528 }