1 /* $NetBSD: tio.c,v 1.1.1.1 2010/12/12 15:19:12 adam 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 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 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 struct timeval readtimeout; 69 struct timeval 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 /* add the second timeval to the first modifing the first */ 80 static inline void tio_tv_add(struct timeval *tv1, const struct timeval *tv2) 81 { 82 /* BUG: we hope that this does not overflow */ 83 tv1->tv_usec+=tv2->tv_usec; 84 if (tv1->tv_usec>1000000) 85 { 86 tv1->tv_usec-=1000000; 87 tv1->tv_sec+=1; 88 } 89 tv1->tv_sec+=tv2->tv_sec; 90 } 91 92 /* build a timeval for comparison to when the operation should be finished */ 93 static inline void tio_tv_prepare(struct timeval *deadline, const struct timeval *timeout) 94 { 95 if (gettimeofday(deadline,NULL)) 96 { 97 /* just blank it in case of errors */ 98 deadline->tv_sec=0; 99 deadline->tv_usec=0; 100 return; 101 } 102 tio_tv_add(deadline,timeout); 103 } 104 105 /* update the timeval to the value that is remaining before deadline 106 returns non-zero if there is no more time before the deadline */ 107 static inline int tio_tv_remaining(struct timeval *tv, const struct timeval *deadline) 108 { 109 /* get the current time */ 110 if (gettimeofday(tv,NULL)) 111 { 112 /* 1 second default if gettimeofday() is broken */ 113 tv->tv_sec=1; 114 tv->tv_usec=0; 115 return 0; 116 } 117 /* check if we're too late */ 118 if ( (tv->tv_sec>deadline->tv_sec) || 119 ( (tv->tv_sec==deadline->tv_sec) && (tv->tv_usec>deadline->tv_usec) ) ) 120 return -1; 121 /* update tv */ 122 tv->tv_sec=deadline->tv_sec-tv->tv_sec; 123 if (tv->tv_usec<deadline->tv_usec) 124 tv->tv_usec=deadline->tv_usec-tv->tv_usec; 125 else 126 { 127 tv->tv_sec--; 128 tv->tv_usec=1000000+deadline->tv_usec-tv->tv_usec; 129 } 130 return 0; 131 } 132 133 /* open a new TFILE based on the file descriptor */ 134 TFILE *tio_fdopen(int fd,struct timeval *readtimeout,struct timeval *writetimeout, 135 size_t initreadsize,size_t maxreadsize, 136 size_t initwritesize,size_t maxwritesize) 137 { 138 struct tio_fileinfo *fp; 139 fp=(struct tio_fileinfo *)malloc(sizeof(struct tio_fileinfo)); 140 if (fp==NULL) 141 return NULL; 142 fp->fd=fd; 143 /* initialize read buffer */ 144 fp->readbuffer.buffer=(uint8_t *)malloc(initreadsize); 145 if (fp->readbuffer.buffer==NULL) 146 { 147 free(fp); 148 return NULL; 149 } 150 fp->readbuffer.size=initreadsize; 151 fp->readbuffer.maxsize=maxreadsize; 152 fp->readbuffer.start=0; 153 fp->readbuffer.len=0; 154 /* initialize write buffer */ 155 fp->writebuffer.buffer=(uint8_t *)malloc(initwritesize); 156 if (fp->writebuffer.buffer==NULL) 157 { 158 free(fp->readbuffer.buffer); 159 free(fp); 160 return NULL; 161 } 162 fp->writebuffer.size=initwritesize; 163 fp->writebuffer.maxsize=maxwritesize; 164 fp->writebuffer.start=0; 165 fp->writebuffer.len=0; 166 /* initialize other attributes */ 167 fp->readtimeout.tv_sec=readtimeout->tv_sec; 168 fp->readtimeout.tv_usec=readtimeout->tv_usec; 169 fp->writetimeout.tv_sec=writetimeout->tv_sec; 170 fp->writetimeout.tv_usec=writetimeout->tv_usec; 171 fp->read_resettable=0; 172 #ifdef DEBUG_TIO_STATS 173 fp->byteswritten=0; 174 fp->bytesread=0; 175 #endif /* DEBUG_TIO_STATS */ 176 return fp; 177 } 178 179 /* wait for any activity on the specified file descriptor using 180 the specified deadline */ 181 static int tio_select(TFILE *fp, int readfd, const struct timeval *deadline) 182 { 183 struct timeval tv; 184 fd_set fdset; 185 int rv; 186 while (1) 187 { 188 /* prepare our filedescriptorset */ 189 FD_ZERO(&fdset); 190 FD_SET(fp->fd,&fdset); 191 /* figure out the time we need to wait */ 192 if (tio_tv_remaining(&tv,deadline)) 193 { 194 errno=ETIME; 195 return -1; 196 } 197 /* wait for activity */ 198 if (readfd) 199 { 200 /* santiy check for moving clock */ 201 if (tv.tv_sec>fp->readtimeout.tv_sec) 202 tv.tv_sec=fp->readtimeout.tv_sec; 203 rv=select(FD_SETSIZE,&fdset,NULL,NULL,&tv); 204 } 205 else 206 { 207 /* santiy check for moving clock */ 208 if (tv.tv_sec>fp->writetimeout.tv_sec) 209 tv.tv_sec=fp->writetimeout.tv_sec; 210 rv=select(FD_SETSIZE,NULL,&fdset,NULL,&tv); 211 } 212 if (rv>0) 213 return 0; /* we have activity */ 214 else if (rv==0) 215 { 216 /* no file descriptors were available within the specified time */ 217 errno=ETIME; 218 return -1; 219 } 220 else if (errno!=EINTR) 221 /* some error ocurred */ 222 return -1; 223 /* we just try again on EINTR */ 224 } 225 } 226 227 /* do a read on the file descriptor, returning the data in the buffer 228 if no data was read in the specified time an error is returned */ 229 int tio_read(TFILE *fp, void *buf, size_t count) 230 { 231 struct timeval deadline; 232 int rv; 233 uint8_t *tmp; 234 size_t newsz; 235 /* have a more convenient storage type for the buffer */ 236 uint8_t *ptr=(uint8_t *)buf; 237 /* build a time by which we should be finished */ 238 /* TODO: probably only set up deadline if we have to do select() */ 239 tio_tv_prepare(&deadline,&(fp->readtimeout)); 240 /* loop until we have returned all the needed data */ 241 while (1) 242 { 243 /* check if we have enough data in the buffer */ 244 if (fp->readbuffer.len >= count) 245 { 246 if (count>0) 247 { 248 if (ptr!=NULL) 249 memcpy(ptr,fp->readbuffer.buffer+fp->readbuffer.start,count); 250 /* adjust buffer position */ 251 fp->readbuffer.start+=count; 252 fp->readbuffer.len-=count; 253 } 254 return 0; 255 } 256 /* empty what we have and continue from there */ 257 if (fp->readbuffer.len>0) 258 { 259 if (ptr!=NULL) 260 { 261 memcpy(ptr,fp->readbuffer.buffer+fp->readbuffer.start,fp->readbuffer.len); 262 ptr+=fp->readbuffer.len; 263 } 264 count-=fp->readbuffer.len; 265 fp->readbuffer.start+=fp->readbuffer.len; 266 fp->readbuffer.len=0; 267 } 268 /* after this point until the read fp->readbuffer.len is 0 */ 269 if (!fp->read_resettable) 270 { 271 /* the stream is not resettable, re-use the buffer */ 272 fp->readbuffer.start=0; 273 } 274 else if (fp->readbuffer.start>=(fp->readbuffer.size-4)) 275 { 276 /* buffer is running empty, try to grow buffer */ 277 if (fp->readbuffer.size<fp->readbuffer.maxsize) 278 { 279 newsz=fp->readbuffer.size*2; 280 if (newsz>fp->readbuffer.maxsize) 281 newsz=fp->readbuffer.maxsize; 282 tmp=realloc(fp->readbuffer.buffer,newsz); 283 if (tmp!=NULL) 284 { 285 fp->readbuffer.buffer=tmp; 286 fp->readbuffer.size=newsz; 287 } 288 } 289 /* if buffer still does not contain enough room, clear resettable */ 290 if (fp->readbuffer.start>=(fp->readbuffer.size-4)) 291 { 292 fp->readbuffer.start=0; 293 fp->read_resettable=0; 294 } 295 } 296 /* wait until we have input */ 297 if (tio_select(fp,1,&deadline)) 298 return -1; 299 /* read the input in the buffer */ 300 rv=read(fp->fd,fp->readbuffer.buffer+fp->readbuffer.start,fp->readbuffer.size-fp->readbuffer.start); 301 /* check for errors */ 302 if ((rv==0)||((rv<0)&&(errno!=EINTR)&&(errno!=EAGAIN))) 303 return -1; /* something went wrong with the read */ 304 /* skip the read part in the buffer */ 305 fp->readbuffer.len=rv; 306 #ifdef DEBUG_TIO_STATS 307 fp->bytesread+=rv; 308 #endif /* DEBUG_TIO_STATS */ 309 } 310 } 311 312 /* Read and discard the specified number of bytes from the stream. */ 313 int tio_skip(TFILE *fp, size_t count) 314 { 315 return tio_read(fp,NULL,count); 316 } 317 318 /* the caller has assured us that we can write to the file descriptor 319 and we give it a shot */ 320 static int tio_writebuf(TFILE *fp) 321 { 322 int rv; 323 /* write the buffer */ 324 #ifdef MSG_NOSIGNAL 325 rv=send(fp->fd,fp->writebuffer.buffer+fp->writebuffer.start,fp->writebuffer.len,MSG_NOSIGNAL); 326 #else /* not MSG_NOSIGNAL */ 327 /* on platforms that cannot use send() with masked signals, we change the 328 signal mask and change it back after the write (note that there is a 329 race condition here) */ 330 struct sigaction act,oldact; 331 /* set up sigaction */ 332 memset(&act,0,sizeof(struct sigaction)); 333 act.sa_sigaction=NULL; 334 act.sa_handler=SIG_IGN; 335 sigemptyset(&act.sa_mask); 336 act.sa_flags=SA_RESTART; 337 /* ignore SIGPIPE */ 338 if (sigaction(SIGPIPE,&act,&oldact)!=0) 339 return -1; /* error setting signal handler */ 340 /* write the buffer */ 341 rv=write(fp->fd,fp->writebuffer.buffer+fp->writebuffer.start,fp->writebuffer.len); 342 /* restore the old handler for SIGPIPE */ 343 if (sigaction(SIGPIPE,&oldact,NULL)!=0) 344 return -1; /* error restoring signal handler */ 345 #endif 346 /* check for errors */ 347 if ((rv==0)||((rv<0)&&(errno!=EINTR)&&(errno!=EAGAIN))) 348 return -1; /* something went wrong with the write */ 349 /* skip the written part in the buffer */ 350 if (rv>0) 351 { 352 fp->writebuffer.start+=rv; 353 fp->writebuffer.len-=rv; 354 #ifdef DEBUG_TIO_STATS 355 fp->byteswritten+=rv; 356 #endif /* DEBUG_TIO_STATS */ 357 /* reset start if len is 0 */ 358 if (fp->writebuffer.len==0) 359 fp->writebuffer.start=0; 360 /* move contents of the buffer to the front if it will save enough room */ 361 if (fp->writebuffer.start>=(fp->writebuffer.size/4)) 362 { 363 memmove(fp->writebuffer.buffer,fp->writebuffer.buffer+fp->writebuffer.start,fp->writebuffer.len); 364 fp->writebuffer.start=0; 365 } 366 } 367 return 0; 368 } 369 370 /* write all the data in the buffer to the stream */ 371 int tio_flush(TFILE *fp) 372 { 373 struct timeval deadline; 374 /* build a time by which we should be finished */ 375 tio_tv_prepare(&deadline,&(fp->writetimeout)); 376 /* loop until we have written our buffer */ 377 while (fp->writebuffer.len > 0) 378 { 379 /* wait until we can write */ 380 if (tio_select(fp,0,&deadline)) 381 return -1; 382 /* write one block */ 383 if (tio_writebuf(fp)) 384 return -1; 385 } 386 return 0; 387 } 388 389 /* try a single write of data in the buffer if the file descriptor 390 will accept data */ 391 static int tio_flush_nonblock(TFILE *fp) 392 { 393 struct timeval tv; 394 fd_set fdset; 395 int rv; 396 /* prepare our filedescriptorset */ 397 FD_ZERO(&fdset); 398 FD_SET(fp->fd,&fdset); 399 /* set the timeout to 0 to poll */ 400 tv.tv_sec=0; 401 tv.tv_usec=0; 402 /* wait for activity */ 403 rv=select(FD_SETSIZE,NULL,&fdset,NULL,&tv); 404 /* check if any file descriptors were ready (timeout) or we were 405 interrupted */ 406 if ((rv==0)||((rv<0)&&(errno==EINTR))) 407 return 0; 408 /* any other errors? */ 409 if (rv<0) 410 return -1; 411 /* so file descriptor will accept writes */ 412 return tio_writebuf(fp); 413 } 414 415 int tio_write(TFILE *fp, const void *buf, size_t count) 416 { 417 size_t fr; 418 uint8_t *tmp; 419 size_t newsz; 420 const uint8_t *ptr=(const uint8_t *)buf; 421 /* keep filling the buffer until we have bufferred everything */ 422 while (count>0) 423 { 424 /* figure out free size in buffer */ 425 fr=fp->writebuffer.size-(fp->writebuffer.start+fp->writebuffer.len); 426 if (count <= fr) 427 { 428 /* the data fits in the buffer */ 429 memcpy(fp->writebuffer.buffer+fp->writebuffer.start+fp->writebuffer.len,ptr,count); 430 fp->writebuffer.len+=count; 431 return 0; 432 } 433 else if (fr > 0) 434 { 435 /* fill the buffer with data that will fit */ 436 memcpy(fp->writebuffer.buffer+fp->writebuffer.start+fp->writebuffer.len,ptr,fr); 437 fp->writebuffer.len+=fr; 438 ptr+=fr; 439 count-=fr; 440 } 441 /* try to flush some of the data that is in the buffer */ 442 if (tio_flush_nonblock(fp)) 443 return -1; 444 /* if we have room now, try again */ 445 if (fp->writebuffer.size>(fp->writebuffer.start+fp->writebuffer.len)) 446 continue; 447 /* try to grow the buffer */ 448 if (fp->writebuffer.size<fp->writebuffer.maxsize) 449 { 450 newsz=fp->writebuffer.size*2; 451 if (newsz>fp->writebuffer.maxsize) 452 newsz=fp->writebuffer.maxsize; 453 tmp=realloc(fp->writebuffer.buffer,newsz); 454 if (tmp!=NULL) 455 { 456 fp->writebuffer.buffer=tmp; 457 fp->writebuffer.size=newsz; 458 continue; /* try again */ 459 } 460 } 461 /* write the buffer to the stream */ 462 if (tio_flush(fp)) 463 return -1; 464 } 465 return 0; 466 } 467 468 int tio_close(TFILE *fp) 469 { 470 int retv; 471 /* write any buffered data */ 472 retv=tio_flush(fp); 473 #ifdef DEBUG_TIO_STATS 474 /* dump statistics to stderr */ 475 fprintf(stderr,"DEBUG_TIO_STATS READ=%d WRITTEN=%d\n",fp->bytesread,fp->byteswritten); 476 #endif /* DEBUG_TIO_STATS */ 477 /* close file descriptor */ 478 if (close(fp->fd)) 479 retv=-1; 480 /* free any allocated buffers */ 481 free(fp->readbuffer.buffer); 482 free(fp->writebuffer.buffer); 483 /* free the tio struct itself */ 484 free(fp); 485 /* return the result of the earlier operations */ 486 return retv; 487 } 488 489 void tio_mark(TFILE *fp) 490 { 491 /* move any data in the buffer to the start of the buffer */ 492 if ((fp->readbuffer.start>0)&&(fp->readbuffer.len>0)) 493 { 494 memmove(fp->readbuffer.buffer,fp->readbuffer.buffer+fp->readbuffer.start,fp->readbuffer.len); 495 fp->readbuffer.start=0; 496 } 497 /* mark the stream as resettable */ 498 fp->read_resettable=1; 499 } 500 501 int tio_reset(TFILE *fp) 502 { 503 /* check if the stream is (still) resettable */ 504 if (!fp->read_resettable) 505 return -1; 506 /* reset the buffer */ 507 fp->readbuffer.len+=fp->readbuffer.start; 508 fp->readbuffer.start=0; 509 return 0; 510 } 511