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