blob: 95674014f096b1521394f5759e996f584f4cf8ed [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/*
* discovery_shm.c
*
* \date 26 Jul 2014
* \author <a href="mailto:dev@celix.apache.org">Apache Celix Project Team</a>
* \copyright Apache License, Version 2.0
*/
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/types.h>
#include <sys/sem.h>
#include <sys/shm.h>
#include <celix_errno.h>
#include <celix_threads.h>
#include "discovery_shm.h"
#define DISCOVERY_SHM_MEMSIZE 262144
#define DISCOVERY_SHM_FILENAME "/dev/null"
#define DISCOVERY_SHM_FTOK_ID 50
#define DISCOVERY_SEM_FILENAME "/dev/null"
#define DISCOVERY_SEM_FTOK_ID 54
struct shmEntry {
char key[SHM_ENTRY_MAX_KEY_LENGTH];
char value[SHM_ENTRY_MAX_VALUE_LENGTH];
time_t expires;
};
typedef struct shmEntry shmEntry;
struct shmData {
shmEntry entries[SHM_DATA_MAX_ENTRIES];
int numOfEntries;
int shmId;
celix_thread_mutex_t globalLock;
};
void* shmAdress;
static celix_status_t discoveryShm_removeWithIndex(shmData_pt data, int index);
/* returns the ftok key to identify shared memory*/
static key_t discoveryShm_getKey() {
return ftok(DISCOVERY_SHM_FILENAME, DISCOVERY_SHM_FTOK_ID);
}
/* creates a new shared memory block */
celix_status_t discoveryShm_create(shmData_pt* data) {
celix_status_t status;
shmData_pt shmData = calloc(1, sizeof(*shmData));
key_t shmKey = discoveryShm_getKey();
if (!shmData) {
status = CELIX_ENOMEM;
} else if ((shmData->shmId = shmget(shmKey, DISCOVERY_SHM_MEMSIZE, IPC_CREAT | 0666)) < 0) {
status = CELIX_BUNDLE_EXCEPTION;
} else if ((shmAdress = shmat(shmData->shmId, 0, 0)) == (char*) -1) {
status = CELIX_BUNDLE_EXCEPTION;
} else {
celix_thread_mutexattr_t threadAttr;
shmData->numOfEntries = 0;
status = celixThreadMutexAttr_create(&threadAttr);
#ifdef LINUX
if (status == CELIX_SUCCESS) {
// This is Linux specific
status = pthread_mutexattr_setrobust(&threadAttr, PTHREAD_MUTEX_ROBUST);
}
#endif
if (status == CELIX_SUCCESS) {
status = celixThreadMutex_create(&shmData->globalLock, &threadAttr);
}
if (status == CELIX_SUCCESS) {
memcpy(shmAdress, shmData, sizeof(struct shmData));
(*data) = shmAdress;
}
}
free(shmData);
return status;
}
celix_status_t discoveryShm_attach(shmData_pt* data) {
celix_status_t status = CELIX_SUCCESS;
key_t shmKey = ftok(DISCOVERY_SHM_FILENAME, DISCOVERY_SHM_FTOK_ID);
int shmId = -1;
if ((shmId = shmget(shmKey, DISCOVERY_SHM_MEMSIZE, 0666)) < 0) {
status = CELIX_BUNDLE_EXCEPTION;
}
if (((*data) = shmat(shmId, 0, 0)) < 0) {
status = CELIX_BUNDLE_EXCEPTION;
}
return status;
}
static celix_status_t discoveryShm_getwithIndex(shmData_pt data, char* key, char* value, int* index) {
celix_status_t status = CELIX_BUNDLE_EXCEPTION;
time_t currentTime = time(NULL);
unsigned int i;
for (i = 0; i < data->numOfEntries && status != CELIX_SUCCESS; i++) {
shmEntry entry = data->entries[i];
// check if entry is still valid
if (data->entries[i].expires < currentTime) {
discoveryShm_removeWithIndex(data, i);
} else if (strcmp(entry.key, key) == 0) {
if (value) {
strcpy(value, entry.value);
}
if (index) {
(*index) = i;
}
status = CELIX_SUCCESS;
}
}
return status;
}
celix_status_t discoveryShm_getKeys(shmData_pt data, char** keys, int* size) {
celix_status_t status;
status = celixThreadMutex_lock(&data->globalLock);
if (status == CELIX_SUCCESS) {
unsigned int i = 0;
for (i = 0; i < data->numOfEntries; i++) {
shmEntry entry = data->entries[i];
if (entry.key) {
snprintf(keys[i], SHM_ENTRY_MAX_KEY_LENGTH, "%s", entry.key);
}
}
(*size) = i;
celixThreadMutex_unlock(&data->globalLock);
}
return status;
}
celix_status_t discoveryShm_set(shmData_pt data, char *key, char* value) {
celix_status_t status;
int index = -1;
if (data->numOfEntries >= SHM_DATA_MAX_ENTRIES) {
status = CELIX_ILLEGAL_STATE;
} else {
status = celixThreadMutex_lock(&data->globalLock);
if (status == CELIX_SUCCESS) {
// check if key already there
status = discoveryShm_getwithIndex(data, key, NULL, &index);
if (status != CELIX_SUCCESS) {
index = data->numOfEntries;
snprintf(data->entries[index].key, SHM_ENTRY_MAX_KEY_LENGTH, "%s", key);
data->numOfEntries++;
status = CELIX_SUCCESS;
}
snprintf(data->entries[index].value, SHM_ENTRY_MAX_VALUE_LENGTH, "%s", value);
data->entries[index].expires = (time(NULL) + SHM_ENTRY_DEFAULT_TTL);
celixThreadMutex_unlock(&data->globalLock);
}
}
return status;
}
celix_status_t discoveryShm_get(shmData_pt data, char* key, char* value) {
celix_status_t status;
status = celixThreadMutex_lock(&data->globalLock);
if (status == CELIX_SUCCESS) {
status = discoveryShm_getwithIndex(data, key, value, NULL);
celixThreadMutex_unlock(&data->globalLock);
}
return status;
}
static celix_status_t discoveryShm_removeWithIndex(shmData_pt data, int index) {
celix_status_t status = CELIX_SUCCESS;
data->numOfEntries--;
if (index < data->numOfEntries) {
memcpy((void*) &data->entries[index], (void*) &data->entries[index + 1], ((data->numOfEntries - index) * sizeof(struct shmEntry)));
}
return status;
}
celix_status_t discoveryShm_remove(shmData_pt data, char* key) {
celix_status_t status;
int index = -1;
status = celixThreadMutex_lock(&data->globalLock);
if (status == CELIX_SUCCESS) {
status = discoveryShm_getwithIndex(data, key, NULL, &index);
if (status == CELIX_SUCCESS) {
status = discoveryShm_removeWithIndex(data, index);
}
celixThreadMutex_unlock(&data->globalLock);
}
return status;
}
celix_status_t discoveryShm_detach(shmData_pt data) {
celix_status_t status = CELIX_BUNDLE_EXCEPTION;
if (data->numOfEntries == 0) {
status = discoveryShm_destroy(data);
}
else if (shmdt(shmAdress) == 0) {
status = CELIX_SUCCESS;
}
return status;
}
celix_status_t discoveryShm_destroy(shmData_pt data) {
celix_status_t status = CELIX_BUNDLE_EXCEPTION;
if (shmctl(data->shmId, IPC_RMID, 0) == 0) {
status = CELIX_SUCCESS;
}
return status;
}