1/* Copyright (C) 2009-2011 Codership Oy <info@codersihp.com>
2
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; version 2 of the License.
6
7 This program is distributed in the hope that it will be useful,
8 but WITHOUT ANY WARRANTY; without even the implied warranty of
9 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 GNU General Public License for more details.
11
12 You should have received a copy of the GNU General Public License
13 along with this program; if not, write to the Free Software
14 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
15 */
16
17/*! @file wsrep implementation loader */
18
19#include <dlfcn.h>
20#include <errno.h>
21#include <string.h>
22#include <stdio.h>
23
24#include "wsrep_api.h"
25
26// Logging stuff for the loader
27static const char* log_levels[] = {"FATAL", "ERROR", "WARN", "INFO", "DEBUG"};
28
29static void default_logger (wsrep_log_level_t lvl, const char* msg)
30{
31 fprintf (stderr, "wsrep loader: [%s] %s\n", log_levels[lvl], msg);
32}
33
34static wsrep_log_cb_t logger = default_logger;
35
36/**************************************************************************
37 * Library loader
38 **************************************************************************/
39
40static int wsrep_check_iface_version(const char* found, const char* iface_ver)
41{
42 const size_t msg_len = 128;
43 char msg[128];
44
45 if (strcmp(found, iface_ver)) {
46 snprintf (msg, msg_len,
47 "provider interface version mismatch: need '%s', found '%s'",
48 iface_ver, found);
49 logger (WSREP_LOG_ERROR, msg);
50 return EINVAL;
51 }
52
53 return 0;
54}
55
56static int verify(const wsrep_t *wh, const char *iface_ver)
57{
58 char msg[128];
59
60#define VERIFY(_p) if (!(_p)) { \
61 snprintf(msg, sizeof(msg), "wsrep_load(): verify(): %s\n", # _p); \
62 logger (WSREP_LOG_ERROR, msg); \
63 return EINVAL; \
64 }
65
66 VERIFY(wh);
67 VERIFY(wh->version);
68
69 if (wsrep_check_iface_version(wh->version, iface_ver))
70 return EINVAL;
71
72 VERIFY(wh->init);
73 VERIFY(wh->options_set);
74 VERIFY(wh->options_get);
75 VERIFY(wh->connect);
76 VERIFY(wh->disconnect);
77 VERIFY(wh->recv);
78 VERIFY(wh->pre_commit);
79 VERIFY(wh->post_commit);
80 VERIFY(wh->post_rollback);
81 VERIFY(wh->replay_trx);
82 VERIFY(wh->abort_pre_commit);
83 VERIFY(wh->append_key);
84 VERIFY(wh->append_data);
85 VERIFY(wh->free_connection);
86 VERIFY(wh->to_execute_start);
87 VERIFY(wh->to_execute_end);
88 VERIFY(wh->preordered_collect);
89 VERIFY(wh->preordered_commit);
90 VERIFY(wh->sst_sent);
91 VERIFY(wh->sst_received);
92 VERIFY(wh->stats_get);
93 VERIFY(wh->stats_free);
94 VERIFY(wh->stats_reset);
95 VERIFY(wh->pause);
96 VERIFY(wh->resume);
97 VERIFY(wh->desync);
98 VERIFY(wh->resync);
99 VERIFY(wh->lock);
100 VERIFY(wh->unlock);
101 VERIFY(wh->is_locked);
102 VERIFY(wh->provider_name);
103 VERIFY(wh->provider_version);
104 VERIFY(wh->provider_vendor);
105 VERIFY(wh->free);
106 return 0;
107}
108
109typedef int (*wsrep_loader_fun)(wsrep_t*);
110
111static wsrep_loader_fun wsrep_dlf(void *dlh, const char *sym)
112{
113 union {
114 wsrep_loader_fun dlfun;
115 void *obj;
116 } alias;
117 alias.obj = dlsym(dlh, sym);
118 return alias.dlfun;
119}
120
121static int wsrep_check_version_symbol(void *dlh)
122{
123 char** dlversion = NULL;
124 dlversion = (char**) dlsym(dlh, "wsrep_interface_version");
125 if (dlversion == NULL)
126 return 0;
127 return wsrep_check_iface_version(*dlversion, WSREP_INTERFACE_VERSION);
128}
129
130extern int wsrep_dummy_loader(wsrep_t *w);
131
132int wsrep_load(const char *spec, wsrep_t **hptr, wsrep_log_cb_t log_cb)
133{
134 int ret = 0;
135 void *dlh = NULL;
136 wsrep_loader_fun dlfun;
137 char msg[1025];
138 msg[sizeof(msg)-1] = 0;
139
140 if (NULL != log_cb)
141 logger = log_cb;
142
143 if (!(spec && hptr))
144 return EINVAL;
145
146 snprintf (msg, sizeof(msg)-1,
147 "wsrep_load(): loading provider library '%s'", spec);
148 logger (WSREP_LOG_INFO, msg);
149
150 if (!(*hptr = malloc(sizeof(wsrep_t)))) {
151 logger (WSREP_LOG_FATAL, "wsrep_load(): out of memory");
152 return ENOMEM;
153 }
154
155 if (!spec || strcmp(spec, WSREP_NONE) == 0) {
156 if ((ret = wsrep_dummy_loader(*hptr)) != 0) {
157 free (*hptr);
158 *hptr = NULL;
159 }
160 return ret;
161 }
162
163 if (!(dlh = dlopen(spec, RTLD_NOW | RTLD_LOCAL))) {
164 snprintf(msg, sizeof(msg)-1, "wsrep_load(): dlopen(): %s", dlerror());
165 logger (WSREP_LOG_ERROR, msg);
166 ret = EINVAL;
167 goto out;
168 }
169
170 if (!(dlfun = wsrep_dlf(dlh, "wsrep_loader"))) {
171 ret = EINVAL;
172 goto out;
173 }
174
175 if (wsrep_check_version_symbol(dlh) != 0) {
176 ret = EINVAL;
177 goto out;
178 }
179
180 if ((ret = (*dlfun)(*hptr)) != 0) {
181 snprintf(msg, sizeof(msg)-1, "wsrep_load(): loader failed: %s",
182 strerror(ret));
183 logger (WSREP_LOG_ERROR, msg);
184 goto out;
185 }
186
187 if ((ret = verify(*hptr, WSREP_INTERFACE_VERSION)) != 0) {
188 snprintf (msg, sizeof(msg)-1,
189 "wsrep_load(): interface version mismatch: my version %s, "
190 "provider version %s", WSREP_INTERFACE_VERSION,
191 (*hptr)->version);
192 logger (WSREP_LOG_ERROR, msg);
193 goto out;
194 }
195
196 (*hptr)->dlh = dlh;
197
198out:
199 if (ret != 0) {
200 if (dlh) dlclose(dlh);
201 free(*hptr);
202 *hptr = NULL;
203 } else {
204 snprintf (msg, sizeof(msg)-1,
205 "wsrep_load(): %s %s by %s loaded successfully.",
206 (*hptr)->provider_name, (*hptr)->provider_version,
207 (*hptr)->provider_vendor);
208 logger (WSREP_LOG_INFO, msg);
209 }
210
211 return ret;
212}
213
214void wsrep_unload(wsrep_t *hptr)
215{
216 if (!hptr) {
217 logger (WSREP_LOG_WARN, "wsrep_unload(): null pointer.");
218 } else {
219 if (hptr->free)
220 hptr->free(hptr);
221 if (hptr->dlh)
222 dlclose(hptr->dlh);
223 free(hptr);
224 }
225}
226
227