| |
| |
| |
| |
| |
| |
| #include <dlfcn.h> |
| #include <errno.h> |
| #include <limits.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <pthread.h> |
| |
| #include <sltdl/sltdl.h> |
| #include "sltdl_core.h" |
| |
| static const char * lt_dlerror_desc[] = { |
| [SLTDL_OK] = 0, |
| [SLTDL_SYSTEM_ERROR] = 0, |
| [SLTDL_DLFCN_ERROR] = 0, |
| [SLTDL_SLTDL_ERROR] = "sltdl internal error", |
| [SLTDL_DLEXIT_REF_COUNT] = "lt_dlexit() reference count already zero", |
| [SLTDL_MODULE_REF_COUNT] = "module reference count already zero", |
| [SLTDL_MODULE_PTR_INVALID] = "module handle invalid", |
| [SLTDL_PATH_INVALID_FIRST_CHAR] = "invalid path (does not begin with a forward slash)", |
| [SLTDL_PATH_INVALID_SEPARATTOR_CHAR] = "invalid path (separator character is not a colon)", |
| [SLTDL_PATH_INVALID_MARK] = "invalid path (mark not within range)", |
| [SLTDL_PATH_INVALID_LEN] = "invalid path (string too long)", |
| [SLTDL_PATH_NO_ENTRY] = "invalid path (not found)", |
| }; |
| |
| static int lt_refs = 0; |
| static int lt_error = 0; |
| static int lt_errno = 0; |
| static char * lt_dlerr = 0; |
| static pthread_mutex_t lt_lock = PTHREAD_MUTEX_INITIALIZER; |
| |
| int lt_dlinit(void) |
| { |
| if (pthread_mutex_lock(<_lock)) |
| return 1; |
| |
| lt_refs++; |
| pthread_mutex_unlock(<_lock); |
| |
| return 0; |
| } |
| |
| int lt_dlexit(void) |
| { |
| if (pthread_mutex_lock(<_lock)) |
| return 1; |
| |
| if (!lt_refs) { |
| lt_error = SLTDL_DLEXIT_REF_COUNT; |
| pthread_mutex_unlock(<_lock); |
| return 1; |
| } |
| |
| lt_refs--; |
| |
| pthread_mutex_unlock(<_lock); |
| |
| return 0; |
| } |
| |
| void lt_slock(void) |
| { |
| int locked; |
| |
| do { |
| locked = pthread_mutex_lock(<_lock); |
| } while (locked); |
| } |
| |
| int lt_sunlock(int ret, int error) |
| { |
| if (error == 0) { |
| pthread_mutex_unlock(<_lock); |
| return 0; |
| } |
| |
| if ((error < 0) || (error >= SLTDL_ERROR_CAP)) { |
| error = SLTDL_SLTDL_ERROR; |
| |
| } else if (error == SLTDL_SYSTEM_ERROR) { |
| lt_errno = errno; |
| |
| } else if (error == SLTDL_DLFCN_ERROR) { |
| if (lt_dlerr) |
| free(lt_dlerr); |
| |
| lt_dlerr = strdup(dlerror()); |
| } |
| |
| lt_error = error; |
| pthread_mutex_unlock(<_lock); |
| return ret; |
| } |
| |
| const char * lt_dlerror(void) |
| { |
| const char * errdesc; |
| |
| lt_slock(); |
| |
| switch (lt_error) { |
| case SLTDL_OK: |
| errdesc = 0; |
| break; |
| |
| case SLTDL_SYSTEM_ERROR: |
| errdesc = strerror(lt_errno); |
| break; |
| |
| case SLTDL_DLFCN_ERROR: |
| errdesc = lt_dlerr; |
| break; |
| |
| default: |
| errdesc = lt_dlerror_desc[lt_error]; |
| break; |
| } |
| |
| lt_error = 0; |
| lt_sunlock(0,0); |
| |
| return errdesc; |
| } |