2 * (c) Copyright 1992, 1993 by Panagiotis Tsirigotis
3 * All rights reserved. The file named COPYRIGHT specifies the terms
4 * and conditions for redistribution.
23 int __sio_n_descriptors = 0 ;
24 __sio_descriptor_t *__sio_descriptors = NULL ;
26 static sio_status_e setup_read_buffer( __sio_id_t *idp, unsigned buf_size );
29 #define MAP_FAILED ((void *)-1)
33 * Code for finalization
35 #ifdef HAVE_FINALIZATION_FUNCTION
36 static int finalizer_installed ;
38 SIO_DEFINE_FIN( sio_cleanup )
40 (void) Sflush( SIO_FLUSH_ALL ) ;
42 #endif /* HAVE_FINALIZATION_FUNCTION */
48 #define CHAR_NULL ((char *)0)
51 * PAGES_MAPPED gives the size of each map unit in pages
53 #define PAGES_MAPPED 2
55 static size_t map_unit_size = 0 ; /* bytes */
56 static size_t page_size = 0 ; /* bytes */
58 static mapd_s *mmap_descriptors = NULL ;
60 #define MDP( fd ) ( mmap_descriptors + (fd) )
64 * NOTES ON MEMORY MAPPING:
66 * 1. Memory mapping works only for file descriptors opened for input
67 * 2. Mapping an object to a part of the address space where another
68 * object is mapped will cause the old mapping to disappear (i.e. mmap
71 * Memory mapping interface:
72 * SIO_MMAP : maps a file into a portion of the address space.
73 * SIO_MUNMAP: unmap a portion of the address space
74 * SIO_MNEED: indicate to the OS that we will need a portion of
77 * The map_unit_size variable defines how much of the file is mapped at
78 * a time. It is a multiple of the operating system page size. It is
79 * not less than SIO_BUFFER_SIZE unless SIO_BUFFER_SIZE is not a
80 * multiple of the page size (so the SIO_BUFFER_SIZE overrides
83 * NOTE: All memory mapping code is in this file only
88 * Macros used by the memory mapping code
90 #define FIRST_TIME( dp ) ( dp->buf == NULL )
93 * Functions to support memory mapping:
103 * try_memory_mapping attempts to setup the specified descriptor
104 * for memory mapping.
105 * It returns FAILURE if it fails and SUCCESS if it is successful.
106 * If HAVE_MMAP is not defined, the function is defined to be FAILURE.
109 * memory_mapped: TRUE or FALSE
111 * Also sets the following fields if memory_mapped is TRUE:
112 * file_offset, file_size, buffer_size
115 static sio_status_e try_memory_mapping( int fd, __sio_id_t *idp, const struct stat *stp )
120 * Do not try memory mapping if:
121 * 1) The file is not a regular file
122 * 2) The file is a regular file but has zero-length
123 * 3) The file pointer is not positioned at the beginning of the file
124 * 4) The fcntl to obtain the file descriptor flags fails
125 * 5) The access mode is not O_RDONLY or O_RDWR
127 * The operations are done in this order to avoid the system calls
130 if ( ( ( stp->st_mode & S_IFMT ) != S_IFREG ) ||
131 ( stp->st_size == 0 ) ||
132 ( lseek( fd, (long)0, 1 ) != 0 ) ||
133 ( ( access_f = fcntl( fd, F_GETFL, 0 ) ) == -1 ) ||
134 ( ( access_f &= 0x3 ) != O_RDONLY && access_f != O_RDWR ) )
136 idp->memory_mapped = FALSE ;
141 * Determine page_size and map_unit_size.
142 * Note that the code works even if PAGES_MAPPED is 0.
144 if ( page_size == 0 )
146 page_size = getpagesize() ;
147 map_unit_size = page_size * PAGES_MAPPED ;
148 if ( map_unit_size < SIO_BUFFER_SIZE )
150 if ( map_unit_size != 0 && SIO_BUFFER_SIZE % map_unit_size == 0 )
151 map_unit_size = SIO_BUFFER_SIZE ;
153 map_unit_size = page_size ;
157 MDP(fd)->file_offset = 0 ;
158 MDP(fd)->file_size = stp->st_size ;
159 idp->buffer_size = map_unit_size ;
160 idp->buf = CHAR_NULL ;
161 idp->memory_mapped = TRUE ;
168 * Copy the current_unit to the primary buffer
170 * Sets fields: start, end, nextb
171 * Also sets the file pointer
173 static void buffer_setup( __sio_id_t *idp, int fd, const struct map_unit *mu_cur, const struct map_unit *mu_next )
177 sio_memcopy( mu_cur->addr, idp->buf, mu_cur->valid_bytes ) ;
178 idp->start = idp->buf ;
179 idp->end = idp->buf + mu_cur->valid_bytes ;
180 idp->nextb = idp->buf + ( idp->nextb - mu_cur->addr ) ;
182 if ( mu_next->addr != CHAR_NULL )
183 new_offset = MDP(fd)->file_offset - mu_next->valid_bytes ;
185 new_offset = MDP(fd)->file_offset ;
186 (void) lseek( fd, new_offset, 0 ) ;
190 * Switch from memory mapping to buffered I/O
191 * If any mapping has occurred, then the current unit is
192 * copied into the buffer that is allocated.
193 * Any data in the next unit is ignored.
194 * We rely on idp->buf to identify the current unit (so it
195 * better be equal to the address of one of the units).
200 sio_status_e __sio_switch( __sio_id_t *idp, int fd )
202 mapd_s *mdp = MDP( fd ) ;
203 struct map_unit *mu_cur, *mu_next ;
204 unsigned buffer_size = idp->buffer_size ;
205 char *buf_addr = idp->buf ;
206 int first_time = FIRST_TIME( idp ) ;
209 * Initialize stream for buffering
211 if ( setup_read_buffer( idp, buffer_size ) == FAILURE )
217 * Find current, next unit
219 if ( buf_addr == mdp->first_unit.addr )
221 mu_cur = &mdp->first_unit ;
222 mu_next = &mdp->second_unit ;
226 mu_cur = &mdp->second_unit ;
227 mu_next = &mdp->first_unit ;
230 buffer_setup( idp, fd, mu_cur, mu_next ) ;
232 * Destroy all mappings
234 (void) SIO_MUNMAP( mu_cur->addr, mu_cur->mapped_bytes ) ;
235 if ( mu_next->addr != NULL )
236 (void) SIO_MUNMAP( mu_next->addr, mu_next->mapped_bytes ) ;
239 idp->start = idp->end = idp->nextb = idp->buf ;
241 idp->memory_mapped = FALSE ;
247 * initial_map does the first memory map on the file descriptor.
248 * It attempts to map both units.
249 * The mapping always starts at file offset 0.
252 * first_unit.*, second_unit.*
256 * number of bytes mapped in first_unit
258 * 0 to indicate that mmap failed.
260 static int initial_map( mapd_s *mdp, int fd )
263 size_t requested_length = 2 * map_unit_size ;
264 size_t mapped_length = MIN( (size_t)mdp->file_size, requested_length ) ;
266 size_t bytes_in_unit ;
268 addr = SIO_MMAP( CHAR_NULL, mapped_length, fd, 0 ) ;
269 if ( addr == MAP_FAILED )
272 SIO_MNEED( addr, mapped_length ) ;
275 * Map as much as possible in the first unit
277 bytes_in_unit = MIN( mapped_length, map_unit_size ) ;
278 mdp->first_unit.addr = addr ;
279 mdp->first_unit.mapped_bytes = bytes_in_unit ;
280 mdp->first_unit.valid_bytes = bytes_in_unit ;
283 * If there is more, map it in the second unit.
285 bytes_left = mapped_length - bytes_in_unit ;
286 if ( bytes_left != 0 )
288 mdp->second_unit.addr = addr + bytes_in_unit ;
289 mdp->second_unit.mapped_bytes = bytes_left ;
290 mdp->second_unit.valid_bytes = bytes_left ;
293 mdp->second_unit.addr = CHAR_NULL ;
295 mdp->file_offset = mapped_length ;
297 return( mdp->first_unit.valid_bytes ) ;
304 * if ( there are more bytes in the file )
306 * map them at the given unit
313 static sio_status_e map_unit( mapd_s *mdp, int fd, struct map_unit *mup )
315 size_t bytes_left = mdp->file_size - mdp->file_offset ;
316 size_t bytes_to_map = MIN( bytes_left, map_unit_size ) ;
318 if ( bytes_to_map != 0 )
320 if ( SIO_MMAP( mup->addr, bytes_to_map,
321 fd, mdp->file_offset ) == MAP_FAILED )
322 return( FAILURE ) ; /* XXX: need to do more ? */
324 mup->valid_bytes = bytes_to_map ;
325 ASSERT( mup->valid_bytes <= mup->mapped_bytes ) ;
326 mdp->file_offset += bytes_to_map ;
327 SIO_MNEED( mup->addr, mup->valid_bytes ) ;
331 (void) SIO_MUNMAP( mup->addr, mup->mapped_bytes ) ;
332 mup->addr = CHAR_NULL ;
339 #define try_memory_mapping( x, y, z ) FAILURE
341 #endif /* HAVE_MMAP */
344 static sio_status_e setup_read_buffer( __sio_id_t *idp, unsigned buf_size )
349 * First allocate space for 2 buffers: primary and auxiliary
351 buf = malloc( buf_size * 2 ) ;
356 * The descriptor buf field should point to the start of the main buffer
358 idp->buf = buf + buf_size ;
359 idp->buffer_size = buf_size ;
364 static sio_status_e init_input_stream( __sio_id_t *idp, int fd, const struct stat *stp )
367 * First initialize the fields relevant to buffering: buf, buffer_size
369 if ( try_memory_mapping( fd, idp, stp ) == FAILURE )
372 * Try to use normal buffering
374 unsigned buf_size = (unsigned)
375 ( stp->st_blksize ? stp->st_blksize : SIO_BUFFER_SIZE ) ;
377 if ( setup_read_buffer( idp, buf_size ) == FAILURE )
382 * Initialize remaining descriptor fields
384 idp->max_line_length = 2 * idp->buffer_size - 1 ;
385 idp->start = idp->end = idp->nextb = idp->buf ;
386 idp->tied_fd = SIO_NO_TIED_FD ;
392 static sio_status_e init_output_stream( __sio_od_t *odp, int fd,
393 const struct stat *stp )
398 buf_size = (unsigned)
399 ( stp->st_blksize ? stp->st_blksize : SIO_BUFFER_SIZE ) ;
400 buf = malloc( buf_size ) ;
405 * Initialize buffering fields
408 odp->buffer_size = buf_size ;
409 odp->buf_end = odp->buf + buf_size ;
412 * Initialize remaining fields
414 odp->start = odp->nextb = odp->buf ;
416 odp->buftype = SIO_LINEBUF ;
419 odp->buftype = SIO_NOBUF ;
431 static int isatty( int fd )
435 if ( ioctl( fd, TCGETA, &t ) == -1 && errno == ENOTTY )
440 #endif /* HAVE_SYSVTTY */
446 static int isatty( int fd )
450 if ( ioctl( fd, TIOCGETP, &s ) == -1 && errno == ENOTTY )
455 #endif /* HAVE_BSDTTY */
457 #endif /* ! HAVE_ISATTY */
461 * Initialize stream I/O for a file descriptor.
464 * fd: file descriptor
465 * dp: descriptor pointer
466 * stream_type: either __SIO_INPUT_STREAM or __SIO_OUTPUT_STREAM
470 * SIO_ERR if the file descriptor is not valid (sets errno)
471 * exits if stream_type is not __SIO_INPUT_STREAM or __SIO_OUTPUT_STREAM
473 int __sio_init( __sio_descriptor_t *dp, int fd, enum __sio_stream stream_type )
477 memset(dp, 0, sizeof(__sio_descriptor_t));
478 if ( fd >= __sio_n_descriptors )
484 if ( fstat( fd, &st ) == -1 )
487 switch ( stream_type )
489 case __SIO_INPUT_STREAM:
490 if ( init_input_stream( IDP( dp ), fd, &st ) == FAILURE )
494 case __SIO_OUTPUT_STREAM:
495 if ( init_output_stream( ODP( dp ), fd, &st ) == FAILURE )
500 terminate( "SIO __sio_init: bad stream type (internal error).\n" ) ;
503 dp->stream_type = stream_type ;
504 dp->initialized = TRUE ;
506 #ifdef HAVE_FINALIZATION_FUNCTION
507 if ( ! finalizer_installed )
509 if ( ! SIO_FINALIZE( sio_cleanup ) )
511 char *s = "SIO __sio_init: finalizer installation failed\n" ;
513 (void) write( 2, s, strlen( s ) ) ;
516 finalizer_installed = TRUE ;
518 #endif /* HAVE_FINALIZATION_FUNCTION */
525 * __sio_writef writes the data in the buffer to the file descriptor.
527 * It tries to write as much data as possible until either all data
528 * are written or an error occurs. EINTR is the only error that is
530 * In case an error occurs but some data were written, that number
531 * is returned instead of SIO_ERR.
534 * When successful: start, nextb
535 * When not successful: start
538 * Number of bytes written
539 * SIO_ERR, if write(2) fails and no data were written
541 int __sio_writef( __sio_od_t *odp, int fd )
547 * Make sure we don't exceed the buffer limits
548 * Maybe we should log this ? XXX
550 if ( odp->nextb > odp->buf_end )
551 odp->nextb = odp->buf_end ;
553 b_in_buffer = odp->nextb - odp->start ;
555 if ( b_in_buffer == 0 )
562 cc = write( fd, odp->start, b_in_buffer ) ;
563 if ( cc == b_in_buffer )
565 odp->start = odp->nextb = odp->buf ;
571 if ( errno == EINTR )
575 * If some bytes were written, return that number, otherwise
578 return( ( cc_total != 0 ) ? cc_total : SIO_ERR ) ;
580 else /* some bytes were written */
582 odp->start += cc ; /* advance start of buffer */
583 b_in_buffer -= cc ; /* decrease number bytes left in buffer */
584 cc_total += cc ; /* count the bytes that were written */
592 * __sio_readf reads data from the file descriptor into the buffer.
593 * Unlike __sio_writef it does NOT try to read as much data as will fit
594 * in the buffer. It ignores EINTR.
596 * Returns: # of bytes read or SIO_ERR
599 * If it does not return SIO_ERR, it sets start, nextb, end
600 * If it returns SIO_ERR, it does not change anything
602 static int __sio_readf( __sio_id_t *idp, int fd )
607 * First check for a tied fd and flush the stream if necessary
609 * XXX the return value of __sio_writef is not checked.
612 if ( idp->tied_fd != SIO_NO_TIED_FD )
613 (void) __sio_writef( &__SIO_OD( idp->tied_fd ), idp->tied_fd ) ;
616 if ( idp->memory_mapped )
618 mapd_s *mdp = MDP( fd ) ;
621 * The functions initial_map and map_unit may fail.
622 * In either case, we switch to buffered I/O.
623 * If initial_map fails, we have read no data, so we
624 * should perform a read(2).
625 * If map_unit fails (for the next unit), we still have
626 * the data in the current unit, so we can return.
628 if ( FIRST_TIME( idp ) )
630 cc = initial_map( mdp, fd ) ;
632 idp->buf = mdp->first_unit.addr ;
635 if ( __sio_switch( idp, fd ) == FAILURE )
642 struct map_unit *mu_cur, *mu_next ;
644 if ( idp->buf == mdp->first_unit.addr )
646 mu_cur = &mdp->first_unit ;
647 mu_next = &mdp->second_unit ;
651 mu_cur = &mdp->second_unit ;
652 mu_next = &mdp->first_unit ;
655 if ( mu_next->addr != NULL )
657 idp->buf = mu_next->addr ;
658 cc = mu_next->valid_bytes ;
660 * XXX: Here we may return SIO_ERR even though there
661 * are data in the current unit because the switch
662 * fails (possibly because malloc failed).
664 if ( map_unit( mdp, fd, mu_cur ) == FAILURE &&
665 __sio_switch( idp, fd ) == FAILURE )
673 idp->end = idp->buf + cc ;
674 idp->start = idp->nextb = idp->buf ;
678 #endif /* HAVE_MMAP */
682 cc = read( fd, idp->buf, (int) idp->buffer_size ) ;
684 if ( errno == EINTR )
692 idp->end = idp->buf + cc ;
693 idp->start = idp->nextb = idp->buf ;
699 * __sio_extend_buffer is used by Srdline to extend the buffer
700 * If successful, it returns the number of bytes that have been read.
701 * If it fails (because of end-of-file or I/O error), it returns 0 or -1.
704 * idp->start points to the start of the buffer area (which is in the
706 * Also, if successful, idp->nextb is set to idp->buf, idp->end is modified.
708 int __sio_extend_buffer( __sio_id_t *idp, int fd, int b_left )
713 * copy to auxiliary buffer
716 sio_memcopy( idp->nextb, idp->buf - b_left, b_left ) ;
717 b_read = __sio_readf( idp, fd ) ;
718 idp->start = idp->buf - b_left ;
724 * __sio_more tries to read more data from the given file descriptor iff
725 * there is free space in the buffer.
726 * __sio_more is used only by Srdline and only AFTER __sio_extend_buffer
727 * has been called. This implies that
728 * a) this is not a memory mapped file
729 * b) __sio_readf has been called (so we don't need to check for tied fd's
731 * Fields modified (only if successful):
734 * Return value: the number of bytes read.
736 int __sio_more( __sio_id_t *idp, int fd )
738 int b_left = &idp->buf[ idp->buffer_size ] - idp->end ;
746 cc = read( fd, idp->end, b_left ) ;
753 if ( errno == EINTR )
762 * Finalize a buffer by unmapping the file or freeing the malloc'ed memory.
763 * This function is only called by Sclose. We always free memory even if
764 * SIO_ERR is returned as long as the descriptor was initialized.
768 __sio_descriptor_t *dp ;
771 if ( fd < 0 || fd >= __sio_n_descriptors )
777 dp = &__sio_descriptors[ fd ] ;
778 if ( ! DESCRIPTOR_INITIALIZED( dp ) )
784 switch ( dp->stream_type )
786 case __SIO_INPUT_STREAM:
788 __sio_id_t *idp = IDP( dp ) ;
791 if ( idp->memory_mapped )
793 mapd_s *mdp = MDP( fd ) ;
795 if ( mdp->first_unit.addr != CHAR_NULL )
796 (void) SIO_MUNMAP( mdp->first_unit.addr,
797 mdp->first_unit.mapped_bytes ) ;
798 if ( mdp->second_unit.addr != CHAR_NULL )
799 (void) SIO_MUNMAP( mdp->second_unit.addr,
800 mdp->second_unit.mapped_bytes ) ;
801 idp->memory_mapped = FALSE ;
804 #endif /* HAVE_MMAP */
805 free( idp->buf - idp->buffer_size ) ;
806 idp->nextb = idp->end = NULL ;
810 case __SIO_OUTPUT_STREAM:
812 __sio_od_t *odp = ODP( dp ) ;
814 if ( Sflush( fd ) == SIO_ERR )
817 odp->nextb = odp->buf_end = NULL ;
822 terminate( "SIO Sdone: bad stream type\n" ) ;
825 memset( dp, 0, sizeof(__sio_descriptor_t) );
826 dp->initialized = FALSE ;
831 static char *sioexpand( char *area, unsigned old_size, unsigned new_size, int is_static )
837 if ( ( new_area = malloc( new_size ) ) == NULL )
839 sio_memcopy( area, new_area, old_size ) ;
842 if ( ( new_area = realloc( area, new_size ) ) == NULL )
850 * Expand the descriptor array (and if we use memory mapping the
851 * memory mapping descriptors). We first expand the memory mapping
853 * There is no problem if the expansion of the SIO descriptors fails
854 * (i.e. there is no need to undo anything).
860 unsigned new_size, old_size ;
861 int n_fds = 4; /* Let's bump 4 at a time for hysteresis */
863 /* If the fd is out of range of the proposed size, make n_fds big enough */
864 if (fd >= (__sio_n_descriptors+n_fds))
865 n_fds += fd - __sio_n_descriptors;
868 old_size = __sio_n_descriptors * sizeof( mapd_s ) ;
869 new_size = n_fds * sizeof( mapd_s ) ;
870 new_size += old_size;
871 is_static = ( mmap_descriptors == NULL ) ;
872 p = sioexpand( (char *)mmap_descriptors, old_size, new_size, is_static ) ;
875 memset(p+old_size, 0, new_size-old_size);
876 mmap_descriptors = (mapd_s *) p ;
877 #endif /* HAVE_MMAP */
879 old_size = __sio_n_descriptors * sizeof( __sio_descriptor_t ) ;
880 new_size = n_fds * sizeof( __sio_descriptor_t ) ;
881 new_size += old_size;
882 is_static = ( __sio_descriptors == NULL ) ;
883 p = sioexpand( (char *)__sio_descriptors, old_size, new_size, is_static ) ;
886 memset(p+old_size, 0, new_size-old_size);
887 __sio_descriptors = (__sio_descriptor_t *) p ;
889 __sio_n_descriptors += n_fds ;
893 void terminate(const char *msg)
895 syslog(LOG_CRIT, "%s", msg);
897 _exit( 1 ) ; /* NOT REACHED */