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 */