summaryrefslogtreecommitdiffhomepage
path: root/src/core/lt_core.c
blob: 52c06d531c8f25542ba0d608d1f4f477d770cded (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
/*******************************************************************/
/*  sltdl: a surrogate ltdl implementation                         */
/*  Copyright (C) 2019 SysDeer Technologies, LLC                   */
/*  Released under the Standard MIT License; see COPYING.SLTDL.    */
/*******************************************************************/

#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(&lt_lock))
		return 1;

	lt_refs++;
	pthread_mutex_unlock(&lt_lock);

	return 0;
}

int lt_dlexit(void)
{
	if (pthread_mutex_lock(&lt_lock))
		return 1;

	if (!lt_refs) {
		lt_error = SLTDL_DLEXIT_REF_COUNT;
		pthread_mutex_unlock(&lt_lock);
		return 1;
	}

	lt_refs--;

	pthread_mutex_unlock(&lt_lock);

	return 0;
}

void lt_slock(void)
{
	int locked;

	do {
		locked = pthread_mutex_lock(&lt_lock);
	} while (locked);
}

int lt_sunlock(int ret, int error)
{
	if (error ==  0) {
		pthread_mutex_unlock(&lt_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(&lt_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;
}