/*
* Martin Balao (martin.uy) - Copyright 2020
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <errno.h>
#include <fcntl.h>
#include <libgen.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include "simplelib.h"
#include "simplemodule.h"
extern int init_module(void* module_image, unsigned long len, const char* param_values);
extern int delete_module(const char* name, int flags);
typedef enum module_state_t { UNLOADED = 0, LOADED } module_state_t;
static const char* get_path_to_library_directory(void);
static module_state_t module_loaded = UNLOADED;
static const char* path_to_library_directory = NULL;
// This is not Thread-Safe
__attribute__((constructor))
static void initialize_once(void) {
path_to_library_directory = get_path_to_library_directory();
if (path_to_library_directory == NULL) {
goto error;
}
goto success;
error:
printf("Error initializing simplelib\n");
exit(-1);
return;
success:
return;
}
const char* get_path_to_file_with_base(const char* file) {
const size_t path_to_file_with_base_length = strlen(path_to_library_directory) + 1 + strlen(file) + 1;
char* path_to_file_with_base = (char*)malloc(path_to_file_with_base_length);
if (path_to_file_with_base == NULL) {
goto end;
}
path_to_file_with_base[0] = 0;
strcat(path_to_file_with_base, path_to_library_directory);
strcat(path_to_file_with_base, "/");
strcat(path_to_file_with_base, file);
path_to_file_with_base[path_to_file_with_base_length-1] = 0;
end:
return path_to_file_with_base;
}
long load_module(void) {
long ret = SLIB_ERROR;
int cret = -1;
int simplemodule_image_fd = -1;
const char* path_to_simplemodule_image = NULL;
void* simplemodule_image_buf = NULL;
struct stat simplemodule_image_sb = {0x0};
if (module_loaded == LOADED) {
goto success;
}
path_to_simplemodule_image = get_path_to_file_with_base(SAMODULE_IMAGE);
if (path_to_simplemodule_image == NULL) {
goto error;
}
while((simplemodule_image_fd = open(path_to_simplemodule_image, 0, O_RDONLY)) == -1 && errno == EINTR);
if (simplemodule_image_fd < 0) {
goto error;
}
if (fstat(simplemodule_image_fd, &simplemodule_image_sb) == -1) {
goto error;
}
simplemodule_image_buf = mmap(0, simplemodule_image_sb.st_size, PROT_READ|PROT_EXEC, MAP_PRIVATE, simplemodule_image_fd, 0);
if (simplemodule_image_buf == NULL) {
goto error;
}
if (init_module(simplemodule_image_buf, simplemodule_image_sb.st_size, "") != 0 && errno != EEXIST) {
goto error;
}
goto success;
error:
ret = SLIB_ERROR;
goto cleanup;
success:
module_loaded = LOADED;
ret = SLIB_SUCCESS;
goto cleanup;
cleanup:
if (simplemodule_image_buf != NULL) {
munmap(simplemodule_image_buf, simplemodule_image_sb.st_size);
}
if (simplemodule_image_fd >= 0) {
while((cret = close(simplemodule_image_fd)) == -1 && errno == EINTR);
}
if (path_to_simplemodule_image != NULL) {
free((void*)path_to_simplemodule_image);
}
return ret;
}
long unload_module(void) {
long ret = SLIB_ERROR;
int cret = -1;
if (module_loaded == UNLOADED) {
goto success;
}
{
unsigned int remaining_tries = 5U;
while ((cret = delete_module(SAMODULE_NAME, O_NONBLOCK)) != 0 && (errno == EAGAIN || errno == EBUSY)
&& remaining_tries-- != 0U) {
sleep(1);
}
if (cret != 0 && errno != EWOULDBLOCK) {
goto error;
}
}
goto success;
error:
ret = SLIB_ERROR;
goto cleanup;
success:
module_loaded = UNLOADED;
ret = SLIB_SUCCESS;
goto cleanup;
cleanup:
return ret;
}
const char* get_path_to_library_directory(void) {
char* ret = NULL;
char* executable_full_path_ptr = NULL;
unsigned int executable_directory_length = 0;
ssize_t count = -1;
char* executable_full_path = (char*)malloc(PATH_MAX);
if (executable_full_path == NULL) {
goto cleanup;
}
count = readlink("/proc/self/exe", executable_full_path, PATH_MAX);
// Fail if we cannot read the link or if the name is too long
// and it was truncated by readlink
if (count == -1 || count == PATH_MAX) {
goto cleanup;
}
// man page readlink(2) says
// "readlink() does not append a null byte to buf"
// So we put an explict 0 here to be sure that we will not
// overrun the buffer later
//
executable_full_path[count] = 0;
executable_full_path_ptr = dirname(executable_full_path);
executable_directory_length = strlen(executable_full_path_ptr);
ret = (char*)malloc(executable_directory_length + 1);
if (ret == NULL) {
goto cleanup;
}
memcpy(ret, executable_full_path_ptr, executable_directory_length);
ret[executable_directory_length] = 0;
cleanup:
if (executable_full_path != NULL) {
free(executable_full_path);
executable_full_path = NULL;
}
return ret;
}