xref: /netbsd-src/external/bsd/openldap/dist/contrib/slapd-modules/nssov/nss-pam-ldapd/tio.c (revision b7b7574d3bf8eeb51a1fa3977b59142ec6434a55)
1 /*	$NetBSD: tio.c,v 1.1.1.2 2014/05/28 09:58:28 tron Exp $	*/
2 
3 /*
4    tio.c - timed io functions
5    This file is part of the nss-pam-ldapd library.
6 
7    Copyright (C) 2007, 2008, 2010, 2011, 2012 Arthur de Jong
8 
9    This library is free software; you can redistribute it and/or
10    modify it under the terms of the GNU Lesser General Public
11    License as published by the Free Software Foundation; either
12    version 2.1 of the License, or (at your option) any later version.
13 
14    This library is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17    Lesser General Public License for more details.
18 
19    You should have received a copy of the GNU Lesser General Public
20    License along with this library; if not, write to the Free Software
21    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
22    02110-1301 USA
23 */
24 
25 //#include "config.h"
26 #include "portable.h"
27 
28 #ifdef HAVE_STDINT_H
29 #include <stdint.h>
30 #endif /* HAVE_STDINT_H */
31 #include <stdlib.h>
32 #include <unistd.h>
33 #include <sys/time.h>
34 #include <sys/types.h>
35 #include <sys/socket.h>
36 #include <errno.h>
37 #include <string.h>
38 #include <signal.h>
39 #include <stdio.h>
40 #include <limits.h>
41 #include <poll.h>
42 
43 #include "tio.h"
44 
45 /* for platforms that don't have ETIME use ETIMEDOUT */
46 #ifndef ETIME
47 #define ETIME ETIMEDOUT
48 #endif /* ETIME */
49 
50 /* structure that holds a buffer
51    the buffer contains the data that is between the application and the
52    file descriptor that is used for efficient transfer
53    the buffer is built up as follows:
54    |.....********......|
55          ^start        ^size
56          ^--len--^           */
57 struct tio_buffer {
58   uint8_t *buffer;
59   size_t size;      /* the size of the buffer */
60   size_t maxsize;   /* the maximum size of the buffer */
61   size_t start;     /* the start of the data (before start is unused) */
62   size_t len;       /* size of the data (from the start) */
63 };
64 
65 /* structure that holds all the state for files */
66 struct tio_fileinfo {
67   int fd;
68   struct tio_buffer readbuffer;
69   struct tio_buffer writebuffer;
70   int readtimeout;
71   int writetimeout;
72   int read_resettable; /* whether the tio_reset() function can be called */
73 #ifdef DEBUG_TIO_STATS
74   /* this is used to collect statistics on the use of the streams
75      and can be used to tune the buffer sizes */
76   size_t byteswritten;
77   size_t bytesread;
78 #endif /* DEBUG_TIO_STATS */
79 };
80 
81 /* build a timeval for comparison to when the operation should be finished */
82 static inline void tio_get_deadline(struct timeval *deadline,int timeout)
83 {
84   if (gettimeofday(deadline,NULL))
85   {
86     /* just blank it in case of errors */
87     deadline->tv_sec=0;
88     deadline->tv_usec=0;
89     return;
90   }
91   deadline->tv_sec+=timeout/1000;
92   deadline->tv_sec+=(timeout%1000)*1000;
93 }
94 
95 /* update the timeout to the value that is remaining before deadline
96    returns non-zero if there is no more time before the deadline */
97 static inline int tio_time_remaining(const struct timeval *deadline)
98 {
99   struct timeval tv;
100   /* get the current time */
101   if (gettimeofday(&tv,NULL))
102   {
103     /* 1 second default if gettimeofday() is broken */
104     return 1000;
105   }
106   /* calculate time remaining in miliseconds */
107   return (deadline->tv_sec-tv.tv_sec)*1000 + (deadline->tv_usec-tv.tv_usec)/1000;
108 }
109 
110 /* open a new TFILE based on the file descriptor */
111 TFILE *tio_fdopen(int fd,int readtimeout,int writetimeout,
112                   size_t initreadsize,size_t maxreadsize,
113                   size_t initwritesize,size_t maxwritesize)
114 {
115   struct tio_fileinfo *fp;
116   fp=(struct tio_fileinfo *)malloc(sizeof(struct tio_fileinfo));
117   if (fp==NULL)
118     return NULL;
119   fp->fd=fd;
120   /* initialize read buffer */
121   fp->readbuffer.buffer=(uint8_t *)malloc(initreadsize);
122   if (fp->readbuffer.buffer==NULL)
123   {
124     free(fp);
125     return NULL;
126   }
127   fp->readbuffer.size=initreadsize;
128   fp->readbuffer.maxsize=maxreadsize;
129   fp->readbuffer.start=0;
130   fp->readbuffer.len=0;
131   /* initialize write buffer */
132   fp->writebuffer.buffer=(uint8_t *)malloc(initwritesize);
133   if (fp->writebuffer.buffer==NULL)
134   {
135     free(fp->readbuffer.buffer);
136     free(fp);
137     return NULL;
138   }
139   fp->writebuffer.size=initwritesize;
140   fp->writebuffer.maxsize=maxwritesize;
141   fp->writebuffer.start=0;
142   fp->writebuffer.len=0;
143   /* initialize other attributes */
144   fp->readtimeout=readtimeout;
145   fp->writetimeout=writetimeout;
146   fp->read_resettable=0;
147 #ifdef DEBUG_TIO_STATS
148   fp->byteswritten=0;
149   fp->bytesread=0;
150 #endif /* DEBUG_TIO_STATS */
151   return fp;
152 }
153 
154 /* wait for any activity on the specified file descriptor using
155    the specified deadline */
156 static int tio_wait(TFILE *fp,int readfd,const struct timeval *deadline)
157 {
158   int timeout;
159   struct pollfd fds[1];
160   int rv;
161   while (1)
162   {
163     /* figure out the time we need to wait */
164     if ((timeout=tio_time_remaining(deadline))<0)
165     {
166       errno=ETIME;
167       return -1;
168     }
169     /* wait for activity */
170     if (readfd)
171     {
172       fds[0].fd=fp->fd;
173       fds[0].events=POLLIN;
174       /* santiy check for moving clock */
175       if (timeout>fp->readtimeout)
176         timeout=fp->readtimeout;
177     }
178     else
179     {
180       fds[0].fd=fp->fd;
181       fds[0].events=POLLOUT;
182       /* santiy check for moving clock */
183       if (timeout>fp->writetimeout)
184         timeout=fp->writetimeout;
185     }
186     rv=poll(fds,1,timeout);
187     if (rv>0)
188       return 0; /* we have activity */
189     else if (rv==0)
190     {
191       /* no file descriptors were available within the specified time */
192       errno=ETIME;
193       return -1;
194     }
195     else if (errno!=EINTR)
196       /* some error ocurred */
197       return -1;
198     /* we just try again on EINTR */
199   }
200 }
201 
202 /* do a read on the file descriptor, returning the data in the buffer
203    if no data was read in the specified time an error is returned */
204 int tio_read(TFILE *fp, void *buf, size_t count)
205 {
206   struct timeval deadline;
207   int rv;
208   uint8_t *tmp;
209   size_t newsz;
210   size_t len;
211   /* have a more convenient storage type for the buffer */
212   uint8_t *ptr=(uint8_t *)buf;
213   /* build a time by which we should be finished */
214   tio_get_deadline(&deadline,fp->readtimeout);
215   /* loop until we have returned all the needed data */
216   while (1)
217   {
218     /* check if we have enough data in the buffer */
219     if (fp->readbuffer.len >= count)
220     {
221       if (count>0)
222       {
223         if (ptr!=NULL)
224           memcpy(ptr,fp->readbuffer.buffer+fp->readbuffer.start,count);
225         /* adjust buffer position */
226         fp->readbuffer.start+=count;
227         fp->readbuffer.len-=count;
228       }
229       return 0;
230     }
231     /* empty what we have and continue from there */
232     if (fp->readbuffer.len>0)
233     {
234       if (ptr!=NULL)
235       {
236         memcpy(ptr,fp->readbuffer.buffer+fp->readbuffer.start,fp->readbuffer.len);
237         ptr+=fp->readbuffer.len;
238       }
239       count-=fp->readbuffer.len;
240       fp->readbuffer.start+=fp->readbuffer.len;
241       fp->readbuffer.len=0;
242     }
243     /* after this point until the read fp->readbuffer.len is 0 */
244     if (!fp->read_resettable)
245     {
246       /* the stream is not resettable, re-use the buffer */
247       fp->readbuffer.start=0;
248     }
249     else if (fp->readbuffer.start>=(fp->readbuffer.size-4))
250     {
251       /* buffer is running empty, try to grow buffer */
252       if (fp->readbuffer.size<fp->readbuffer.maxsize)
253       {
254         newsz=fp->readbuffer.size*2;
255         if (newsz>fp->readbuffer.maxsize)
256           newsz=fp->readbuffer.maxsize;
257         tmp=realloc(fp->readbuffer.buffer,newsz);
258         if (tmp!=NULL)
259         {
260           fp->readbuffer.buffer=tmp;
261           fp->readbuffer.size=newsz;
262         }
263       }
264       /* if buffer still does not contain enough room, clear resettable */
265       if (fp->readbuffer.start>=(fp->readbuffer.size-4))
266       {
267         fp->readbuffer.start=0;
268         fp->read_resettable=0;
269       }
270     }
271     /* wait until we have input */
272     if (tio_wait(fp,1,&deadline))
273       return -1;
274     /* read the input in the buffer */
275     len=fp->readbuffer.size-fp->readbuffer.start;
276 #ifdef SSIZE_MAX
277     if (len>SSIZE_MAX)
278       len=SSIZE_MAX;
279 #endif /* SSIZE_MAX */
280     rv=read(fp->fd,fp->readbuffer.buffer+fp->readbuffer.start,len);
281     /* check for errors */
282     if (rv==0)
283     {
284       errno=ECONNRESET;
285       return -1;
286     }
287     else if ((rv<0)&&(errno!=EINTR)&&(errno!=EAGAIN))
288       return -1; /* something went wrong with the read */
289     /* skip the read part in the buffer */
290     fp->readbuffer.len=rv;
291 #ifdef DEBUG_TIO_STATS
292     fp->bytesread+=rv;
293 #endif /* DEBUG_TIO_STATS */
294   }
295 }
296 
297 /* Read and discard the specified number of bytes from the stream. */
298 int tio_skip(TFILE *fp, size_t count)
299 {
300   return tio_read(fp,NULL,count);
301 }
302 
303 /* Read all available data from the stream and empty the read buffer. */
304 int tio_skipall(TFILE *fp)
305 {
306   struct pollfd fds[1];
307   int rv;
308   size_t len;
309   /* clear the read buffer */
310   fp->readbuffer.start=0;
311   fp->readbuffer.len=0;
312   fp->read_resettable=0;
313   /* read until we can't read no more */
314   len=fp->readbuffer.size;
315 #ifdef SSIZE_MAX
316   if (len>SSIZE_MAX)
317     len=SSIZE_MAX;
318 #endif /* SSIZE_MAX */
319   while (1)
320   {
321     /* see if any data is available */
322     fds[0].fd=fp->fd;
323     fds[0].events=POLLIN;
324     rv=poll(fds,1,0);
325     /* check the poll() result */
326     if (rv==0)
327       return 0; /* no file descriptor ready */
328     if ((rv<0)&&((errno==EINTR)||(errno==EAGAIN)))
329       continue; /* interrupted, try again */
330     if (rv<0)
331       return -1; /* something went wrong */
332     /* read data from the stream */
333     rv=read(fp->fd,fp->readbuffer.buffer,len);
334     if (rv==0)
335       return 0; /* end-of-file */
336     if ((rv<0)&&(errno==EWOULDBLOCK))
337       return 0; /* we've ready everything we can without blocking */
338     if ((rv<0)&&(errno!=EINTR)&&(errno!=EAGAIN))
339       return -1; /* something went wrong with the read */
340   }
341 }
342 
343 /* the caller has assured us that we can write to the file descriptor
344    and we give it a shot */
345 static int tio_writebuf(TFILE *fp)
346 {
347   int rv;
348   /* write the buffer */
349 #ifdef MSG_NOSIGNAL
350   rv=send(fp->fd,fp->writebuffer.buffer+fp->writebuffer.start,fp->writebuffer.len,MSG_NOSIGNAL);
351 #else /* not MSG_NOSIGNAL */
352   /* on platforms that cannot use send() with masked signals, we change the
353      signal mask and change it back after the write (note that there is a
354      race condition here) */
355   struct sigaction act,oldact;
356   /* set up sigaction */
357   memset(&act,0,sizeof(struct sigaction));
358   act.sa_sigaction=NULL;
359   act.sa_handler=SIG_IGN;
360   sigemptyset(&act.sa_mask);
361   act.sa_flags=SA_RESTART;
362   /* ignore SIGPIPE */
363   if (sigaction(SIGPIPE,&act,&oldact)!=0)
364     return -1; /* error setting signal handler */
365   /* write the buffer */
366   rv=write(fp->fd,fp->writebuffer.buffer+fp->writebuffer.start,fp->writebuffer.len);
367   /* restore the old handler for SIGPIPE */
368   if (sigaction(SIGPIPE,&oldact,NULL)!=0)
369     return -1; /* error restoring signal handler */
370 #endif
371   /* check for errors */
372   if ((rv==0)||((rv<0)&&(errno!=EINTR)&&(errno!=EAGAIN)))
373     return -1; /* something went wrong with the write */
374   /* skip the written part in the buffer */
375   if (rv>0)
376   {
377     fp->writebuffer.start+=rv;
378     fp->writebuffer.len-=rv;
379 #ifdef DEBUG_TIO_STATS
380     fp->byteswritten+=rv;
381 #endif /* DEBUG_TIO_STATS */
382     /* reset start if len is 0 */
383     if (fp->writebuffer.len==0)
384       fp->writebuffer.start=0;
385     /* move contents of the buffer to the front if it will save enough room */
386     if (fp->writebuffer.start>=(fp->writebuffer.size/4))
387     {
388       memmove(fp->writebuffer.buffer,fp->writebuffer.buffer+fp->writebuffer.start,fp->writebuffer.len);
389       fp->writebuffer.start=0;
390     }
391   }
392   return 0;
393 }
394 
395 /* write all the data in the buffer to the stream */
396 int tio_flush(TFILE *fp)
397 {
398   struct timeval deadline;
399   /* build a time by which we should be finished */
400   tio_get_deadline(&deadline,fp->writetimeout);
401   /* loop until we have written our buffer */
402   while (fp->writebuffer.len > 0)
403   {
404     /* wait until we can write */
405     if (tio_wait(fp,0,&deadline))
406       return -1;
407     /* write one block */
408     if (tio_writebuf(fp))
409       return -1;
410   }
411   return 0;
412 }
413 
414 /* try a single write of data in the buffer if the file descriptor
415    will accept data */
416 static int tio_flush_nonblock(TFILE *fp)
417 {
418   struct pollfd fds[1];
419   int rv;
420   /* wait for activity */
421   fds[0].fd=fp->fd;
422   fds[0].events=POLLOUT;
423   rv=poll(fds,1,0);
424   /* check if any file descriptors were ready (timeout) or we were
425      interrupted */
426   if ((rv==0)||((rv<0)&&(errno==EINTR)))
427     return 0;
428   /* any other errors? */
429   if (rv<0)
430     return -1;
431   /* so file descriptor will accept writes */
432   return tio_writebuf(fp);
433 }
434 
435 int tio_write(TFILE *fp, const void *buf, size_t count)
436 {
437   size_t fr;
438   uint8_t *tmp;
439   size_t newsz;
440   const uint8_t *ptr=(const uint8_t *)buf;
441   /* keep filling the buffer until we have bufferred everything */
442   while (count>0)
443   {
444     /* figure out free size in buffer */
445     fr=fp->writebuffer.size-(fp->writebuffer.start+fp->writebuffer.len);
446     if (count <= fr)
447     {
448       /* the data fits in the buffer */
449       memcpy(fp->writebuffer.buffer+fp->writebuffer.start+fp->writebuffer.len,ptr,count);
450       fp->writebuffer.len+=count;
451       return 0;
452     }
453     else if (fr > 0)
454     {
455       /* fill the buffer with data that will fit */
456       memcpy(fp->writebuffer.buffer+fp->writebuffer.start+fp->writebuffer.len,ptr,fr);
457       fp->writebuffer.len+=fr;
458       ptr+=fr;
459       count-=fr;
460     }
461     /* try to flush some of the data that is in the buffer */
462     if (tio_flush_nonblock(fp))
463       return -1;
464     /* if we have room now, try again */
465     if (fp->writebuffer.size>(fp->writebuffer.start+fp->writebuffer.len))
466       continue;
467     /* try to grow the buffer */
468     if (fp->writebuffer.size<fp->writebuffer.maxsize)
469     {
470       newsz=fp->writebuffer.size*2;
471       if (newsz>fp->writebuffer.maxsize)
472         newsz=fp->writebuffer.maxsize;
473       tmp=realloc(fp->writebuffer.buffer,newsz);
474       if (tmp!=NULL)
475       {
476         fp->writebuffer.buffer=tmp;
477         fp->writebuffer.size=newsz;
478         continue; /* try again */
479       }
480     }
481     /* write the buffer to the stream */
482     if (tio_flush(fp))
483       return -1;
484   }
485   return 0;
486 }
487 
488 int tio_close(TFILE *fp)
489 {
490   int retv;
491   /* write any buffered data */
492   retv=tio_flush(fp);
493 #ifdef DEBUG_TIO_STATS
494   /* dump statistics to stderr */
495   fprintf(stderr,"DEBUG_TIO_STATS READ=%d WRITTEN=%d\n",fp->bytesread,fp->byteswritten);
496 #endif /* DEBUG_TIO_STATS */
497   /* close file descriptor */
498   if (close(fp->fd))
499     retv=-1;
500   /* free any allocated buffers */
501   free(fp->readbuffer.buffer);
502   free(fp->writebuffer.buffer);
503   /* free the tio struct itself */
504   free(fp);
505   /* return the result of the earlier operations */
506   return retv;
507 }
508 
509 void tio_mark(TFILE *fp)
510 {
511   /* move any data in the buffer to the start of the buffer */
512   if ((fp->readbuffer.start>0)&&(fp->readbuffer.len>0))
513   {
514     memmove(fp->readbuffer.buffer,fp->readbuffer.buffer+fp->readbuffer.start,fp->readbuffer.len);
515     fp->readbuffer.start=0;
516   }
517   /* mark the stream as resettable */
518   fp->read_resettable=1;
519 }
520 
521 int tio_reset(TFILE *fp)
522 {
523   /* check if the stream is (still) resettable */
524   if (!fp->read_resettable)
525     return -1;
526   /* reset the buffer */
527   fp->readbuffer.len+=fp->readbuffer.start;
528   fp->readbuffer.start=0;
529   return 0;
530 }
531