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 |
27 | static const char* log_levels[] = {"FATAL" , "ERROR" , "WARN" , "INFO" , "DEBUG" }; |
28 | |
29 | static 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 | |
34 | static wsrep_log_cb_t logger = default_logger; |
35 | |
36 | /************************************************************************** |
37 | * Library loader |
38 | **************************************************************************/ |
39 | |
40 | static 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 | |
56 | static 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 | |
109 | typedef int (*wsrep_loader_fun)(wsrep_t*); |
110 | |
111 | static 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 | |
121 | static 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 | |
130 | extern int wsrep_dummy_loader(wsrep_t *w); |
131 | |
132 | int 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 | |
198 | out: |
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 | |
214 | void 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 | |