#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/file.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#ifndef errno
extern
int
errno
;
#endif
#include "sharelite.h"
#include "sharelite_shm.h"
#include "sharelite_sem.h"
#define CALL_NEEDS_SHARE(A) \
if
( share == NULL ) { \
errno
= EINVAL; \
return
(A); \
}
#define CALL_NEEDS_SHARE_AND_HEAD(A) \
if
( ( share == NULL ) \
|| ( share->head == NULL ) ) { \
errno
= EINVAL; \
return
(A); \
}
inline
Share *_sharelite_shmat( key_t key,
int
shmid ) {
Share *share;
if
( ( share = (Share *)
calloc
( 1,
sizeof
( Share ) ) ) == NULL )
return
NULL;
share->key = key;
share->shmid = shmid;
if
( ( shmid == -1 ) && ( key == IPC_PRIVATE ) ) {
errno
= EINVAL;
return
NULL;
}
if
( _sharelite_shm_attach( share ) < 0 ) {
free
( share );
return
NULL;
}
share->key = key;
share->lock = LOCK_UN;
share->flags = share->head->shminfo->seg_perms;
share->semid = share->head->shminfo->seg_semid;
share->size_data = share->head->shminfo->size_topseg
- (
sizeof
( Header ) +
sizeof
( Descriptor ) );
return
share;
}
Share *sharelite_shmat(
int
shmid ) {
return
_sharelite_shmat( IPC_PRIVATE, shmid );
}
Share *sharelite_attach( key_t key ) {
return
_sharelite_shmat( key, -1 );
}
Share *sharelite_create( key_t key,
int
segsize,
int
flags ) {
Share *share;
Node *node;
int
semid;
if
( segsize < SHARELITE_MIN_SIZE )
segsize = SHARELITE_SEG_SIZE;
if
( ( share =
calloc
( 1,
sizeof
( Share ) ) ) == NULL )
return
NULL;
if
( ( semid = _sharelite_sem_create( flags ) ) == -1 ) {
free
( share );
return
NULL;
}
share->key = key;
share->flags = flags & 0x01FF;
if
( _sharelite_shm_create( share, segsize ) < 0 ) {
_sharelite_sem_remove( semid );
free
( share );
return
NULL;
}
node = share->head;
share->lock = LOCK_EX;
share->semid = semid;
node->shminfo->seg_semid = semid;
return
share;
}
int
sharelite_shmdt( Share *share ) {
CALL_NEEDS_SHARE(-1);
if
( share->
remove
) {
REQ_EX_LOCK(share);
_sharelite_sem_remove( share->semid );
_sharelite_shm_remove( share, NULL );
}
else
{
_sharelite_shm_forget( share, NULL );
}
free
( share );
return
0;
}
int
sharelite_remove( Share *share ) {
CALL_NEEDS_SHARE(-1);
share->
remove
= 1;
return
0;
}
int
sharelite_lock(Share *share,
int
flags) {
int
nonblock, lockmode, rc;
CALL_NEEDS_SHARE(-1);
if
( ! flags ) {
errno
= EINVAL;
return
-1;
}
if
( ( ( flags & LOCK_EX ) && ( flags & LOCK_SH ) ) ||
( ( flags & LOCK_UN ) &&
( ( flags & LOCK_SH ) || ( flags & LOCK_EX ) ) ) ) {
errno
= EINVAL;
return
-1;
}
nonblock = ( flags & LOCK_NB ) == LOCK_NB;
lockmode = flags & ~ LOCK_NB;
if
( share->lock == lockmode )
return
0;
if
( lockmode & LOCK_UN ) {
if
( share->lock & LOCK_SH )
rc = REL_SH_LOCK(share);
else
rc = REL_EX_LOCK(share);
if
( rc == 0 )
share->lock = LOCK_UN;
return
rc;
}
if
( ! ( share->lock & LOCK_UN ) ) {
rc = 0;
if
( share->lock & LOCK_SH )
rc = REL_SH_LOCK(share);
else
rc = REL_EX_LOCK(share);
if
( rc < 0 )
return
-1;
}
if
( lockmode & LOCK_EX ) {
rc = nonblock
? GET_EX_LOCK_NB(share)
: GET_EX_LOCK(share);
}
else
if
( lockmode & LOCK_SH ) {
rc = nonblock
? GET_SH_LOCK_NB(share)
: GET_SH_LOCK(share);
}
else
{
errno
= EINVAL;
rc = -1;
}
if
( nonblock && ( rc < 0 ) && (
errno
== EAGAIN ) )
return
1;
if
( rc == 0 )
share->lock = lockmode;
return
rc;
}
int
sharelite_locked( Share *share,
int
flags ) {
CALL_NEEDS_SHARE(-1);
return
share->lock == flags;
}
int
sharelite_store( Share *share,
char
*data,
int
length ) {
char
*srcaddr;
char
*dstaddr;
Node *node;
int
left, size, chunk;
CALL_NEEDS_SHARE_AND_HEAD(-1);
REQ_EX_LOCK(share);
node = share->head;
node->shminfo->data_length = 0;
size = share->size_data;
chunk = node->shminfo->size_chunkseg -
sizeof
( Header );
dstaddr = node->shmdata;
srcaddr = data;
if
( length < size ) {
size = length;
left = 0;
}
else
left = length - size;
if
(
memcpy
( dstaddr, srcaddr, size ) == NULL ) {
END_EX_LOCK(share);
return
-1;
}
while
( left ) {
if
( node->next != NULL )
if
( node->next->shmid != node->shmhead->next_shmid )
if
( _sharelite_shm_forget( share, node ) == -1 ) {
END_EX_LOCK(share);
return
-1;
}
if
( node->next == NULL )
if
( _sharelite_shm_append( share ) == -1 ) {
END_EX_LOCK(share);
return
-1;
}
node = node->next;
dstaddr = node->shmdata;
srcaddr += size;
size = ( left > chunk ) ? chunk : left;
if
(
memcpy
( dstaddr, srcaddr, size ) == NULL ) {
END_EX_LOCK(share);
return
-1;
}
left -= size;
}
_sharelite_shm_remove( share, node );
node->shminfo->data_serial++;
node->shminfo->data_length = length;
END_EX_LOCK(share);
return
0;
}
int
sharelite_fetch( Share *share,
char
**data ) {
char
*srcaddr;
char
*dstaddr;
Node *node;
int
length, left, size, chunk;
CALL_NEEDS_SHARE_AND_HEAD(-1);
node = share->head;
length = node->shminfo->data_length;
if
( ( *data = dstaddr = (
char
*)
malloc
( length ) ) == NULL )
return
-1;
REQ_SH_LOCK(share);
size = share->size_data;
chunk = node->shminfo->size_chunkseg -
sizeof
( Header );
srcaddr = node->shmdata;
if
( length < size ) {
size = length;
left = 0;
}
else
left = length - size;
if
(
memcpy
( dstaddr, srcaddr, size ) == NULL ) {
END_SH_LOCK(share);
return
-1;
}
while
( left ) {
if
( node->next != NULL )
if
( node->next->shmid != node->shmhead->next_shmid )
if
( _sharelite_shm_forget( share, node ) == -1 ) {
END_SH_LOCK(share);
return
-1;
}
if
( node->next == NULL )
if
( _sharelite_shm_append( share ) == -1 ) {
END_SH_LOCK(share);
return
-1;
}
node = node->next;
srcaddr = node->shmdata;
dstaddr += size;
size = ( left > chunk ) ? chunk : left;
if
(
memcpy
( dstaddr, srcaddr, size ) == NULL ) {
END_SH_LOCK(share);
return
-1;
}
left -= size;
}
END_SH_LOCK(share);
return
length;
}
int
sharelite_key( Share *share ) {
CALL_NEEDS_SHARE(-1);
return
share->key;
}
int
sharelite_shmid( Share *share ) {
CALL_NEEDS_SHARE(-1);
return
share->shmid;
}
int
sharelite_flags( Share *share ) {
CALL_NEEDS_SHARE(-1);
return
share->flags;
}
int
sharelite_length( Share *share ) {
CALL_NEEDS_SHARE_AND_HEAD(-1);
return
share->head->shminfo->data_length;
}
int
sharelite_serial( Share *share ) {
CALL_NEEDS_SHARE_AND_HEAD(-1);
return
share->head->shminfo->data_serial;
}
int
sharelite_nsegments( Share *share ) {
CALL_NEEDS_SHARE_AND_HEAD(-1);
return
share->head->shminfo->data_chunks + 1;
}
int
sharelite_top_seg_size( Share *share ) {
CALL_NEEDS_SHARE_AND_HEAD(-1);
return
share->head->shminfo->size_topseg;
}
int
sharelite_chunk_seg_size( Share *share,
int
segsize ) {
CALL_NEEDS_SHARE_AND_HEAD(-1);
if
( segsize > 0 ) {
REQ_EX_LOCK(share);
if
( share->head->shminfo->data_length > share->size_data ) {
END_EX_LOCK(share);
errno
= EINVAL;
return
-1;
}
share->head->shminfo->size_chunkseg = segsize;
END_EX_LOCK(share);
}
return
share->head->shminfo->size_chunkseg;
}
int
sharelite_nrefs( Share *share ) {
CALL_NEEDS_SHARE_AND_HEAD(-1);
return
share->head->shminfo->nrefs;
}
int
sharelite_incref( Share *share ) {
CALL_NEEDS_SHARE_AND_HEAD(-1);
REQ_EX_LOCK(share);
share->head->shminfo->nrefs++;
END_EX_LOCK(share);
return
share->head->shminfo->nrefs;
}
int
sharelite_decref( Share *share ) {
CALL_NEEDS_SHARE_AND_HEAD(-1);
REQ_EX_LOCK(share);
share->head->shminfo->nrefs--;
END_EX_LOCK(share);
return
share->head->shminfo->nrefs;
}
int
sharelite_is_valid( Share *share ) {
CALL_NEEDS_SHARE(-1);
return
_sharelite_sem_access( share->semid ) == 0;
}