ruby-oci8 2.2.3 → 2.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/ChangeLog +100 -0
- data/NEWS +39 -0
- data/README.md +16 -8
- data/dist-files +2 -0
- data/docs/bind-array-to-in_cond.md +1 -1
- data/docs/conflicts-local-connections-and-processes.md +7 -4
- data/docs/hanging-after-inactivity.md +61 -0
- data/docs/install-binary-package.md +15 -11
- data/docs/install-full-client.md +18 -21
- data/docs/install-instant-client.md +44 -27
- data/docs/install-on-osx.md +13 -15
- data/docs/platform-specific-issues.md +17 -50
- data/docs/report-installation-issue.md +3 -0
- data/docs/timeout-parameters.md +3 -0
- data/ext/oci8/encoding.c +5 -5
- data/ext/oci8/env.c +8 -2
- data/ext/oci8/error.c +25 -2
- data/ext/oci8/hook_funcs.c +131 -6
- data/ext/oci8/oci8.c +18 -13
- data/ext/oci8/oci8.h +10 -2
- data/ext/oci8/oci8lib.c +129 -2
- data/ext/oci8/ocihandle.c +34 -34
- data/ext/oci8/oraconf.rb +26 -80
- data/ext/oci8/oradate.c +18 -18
- data/ext/oci8/plthook.h +10 -0
- data/ext/oci8/plthook_elf.c +83 -2
- data/ext/oci8/plthook_osx.c +31 -0
- data/ext/oci8/plthook_win32.c +9 -0
- data/ext/oci8/stmt.c +1 -1
- data/lib/oci8.rb +38 -1
- data/lib/oci8/cursor.rb +9 -8
- data/lib/oci8/oci8.rb +7 -3
- data/lib/oci8/properties.rb +22 -0
- data/lib/oci8/version.rb +1 -1
- data/ruby-oci8.gemspec +1 -5
- data/test/test_all.rb +1 -0
- data/test/test_bind_integer.rb +47 -0
- data/test/test_connstr.rb +29 -13
- data/test/test_oci8.rb +1 -1
- metadata +5 -3
data/ext/oci8/hook_funcs.c
CHANGED
@@ -9,14 +9,15 @@
|
|
9
9
|
#ifndef WIN32
|
10
10
|
#include <unistd.h>
|
11
11
|
#include <sys/socket.h>
|
12
|
+
#include <netinet/in.h>
|
13
|
+
#include <netinet/tcp.h>
|
12
14
|
#endif
|
13
15
|
|
14
|
-
#define DEBUG_HOOK_FUNCS 1
|
15
|
-
|
16
16
|
#ifdef WIN32
|
17
17
|
static CRITICAL_SECTION lock;
|
18
18
|
#define LOCK(lock) EnterCriticalSection(lock)
|
19
19
|
#define UNLOCK(lock) LeaveCriticalSection(lock)
|
20
|
+
typedef int socklen_t;
|
20
21
|
#else
|
21
22
|
static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
|
22
23
|
#define LOCK(lock) pthread_mutex_lock(lock)
|
@@ -25,6 +26,29 @@ static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
|
|
25
26
|
#define INVALID_SOCKET (-1)
|
26
27
|
#endif
|
27
28
|
|
29
|
+
#if defined(__APPLE__) && defined(TCP_KEEPALIVE) /* macOS */
|
30
|
+
#define USE_TCP_KEEPALIVE
|
31
|
+
#define SUPPORT_TCP_KEEPALIVE_TIME
|
32
|
+
#elif defined(__sun) && defined(TCP_KEEPALIVE_THRESHOLD) /* Solaris */
|
33
|
+
#define USE_TCP_KEEPALIVE_THRESHOLD
|
34
|
+
#define SUPPORT_TCP_KEEPALIVE_TIME
|
35
|
+
#elif defined(TCP_KEEPIDLE) /* Linux, etc */
|
36
|
+
#define USE_TCP_KEEPIDLE
|
37
|
+
#define SUPPORT_TCP_KEEPALIVE_TIME
|
38
|
+
#elif defined(WIN32) /* Windows */
|
39
|
+
#define SUPPORT_TCP_KEEPALIVE_TIME
|
40
|
+
#endif
|
41
|
+
|
42
|
+
int oci8_cancel_read_at_exit = 0;
|
43
|
+
|
44
|
+
#ifdef SUPPORT_TCP_KEEPALIVE_TIME
|
45
|
+
int oci8_tcp_keepalive_time = 0;
|
46
|
+
static int hook_setsockopt(SOCKET sockfd, int level, int optname, const void *optval, socklen_t optlen);
|
47
|
+
#else
|
48
|
+
int oci8_tcp_keepalive_time = -1;
|
49
|
+
#endif
|
50
|
+
|
51
|
+
|
28
52
|
typedef struct {
|
29
53
|
const char *func_name;
|
30
54
|
void *func_addr;
|
@@ -93,6 +117,16 @@ static int replace_functions(const char * const *files, hook_func_entry_t *funct
|
|
93
117
|
|
94
118
|
#ifdef WIN32
|
95
119
|
|
120
|
+
#ifndef _MSC_VER
|
121
|
+
/* setsockopt() in ws2_32.dll */
|
122
|
+
#define setsockopt rboci_setsockopt
|
123
|
+
typedef int (WSAAPI *setsockopt_t)(SOCKET, int, int, const void *, int);
|
124
|
+
static setsockopt_t setsockopt;
|
125
|
+
#endif
|
126
|
+
|
127
|
+
/* system-wide keepalive interval */
|
128
|
+
static DWORD keepalive_interval;
|
129
|
+
|
96
130
|
static int locK_is_initialized;
|
97
131
|
|
98
132
|
static int WSAAPI hook_WSARecv(SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount, LPDWORD lpNumberOfBytesRecvd, LPDWORD lpFlags, LPWSAOVERLAPPED lpOverlapped, LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine);
|
@@ -116,6 +150,7 @@ static const char * const tcp_func_files[] = {
|
|
116
150
|
|
117
151
|
static hook_func_entry_t tcp_functions[] = {
|
118
152
|
{"WSARecv", (void*)hook_WSARecv, NULL},
|
153
|
+
{"setsockopt", (void*)hook_setsockopt, NULL},
|
119
154
|
{NULL, NULL, NULL},
|
120
155
|
};
|
121
156
|
|
@@ -123,22 +158,63 @@ static hook_func_entry_t tcp_functions[] = {
|
|
123
158
|
static int WSAAPI hook_WSARecv(SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount, LPDWORD lpNumberOfBytesRecvd, LPDWORD lpFlags, LPWSAOVERLAPPED lpOverlapped, LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine)
|
124
159
|
{
|
125
160
|
socket_entry_t entry;
|
161
|
+
int enable_cancel = oci8_cancel_read_at_exit;
|
126
162
|
int rv;
|
127
163
|
|
128
|
-
|
164
|
+
if (enable_cancel > 0) {
|
165
|
+
socket_entry_set(&entry, s);
|
166
|
+
}
|
129
167
|
rv = WSARecv(s, lpBuffers, dwBufferCount, lpNumberOfBytesRecvd, lpFlags, lpOverlapped, lpCompletionRoutine);
|
130
|
-
|
168
|
+
if (enable_cancel > 0) {
|
169
|
+
socket_entry_clear(&entry);
|
170
|
+
}
|
131
171
|
return rv;
|
132
172
|
}
|
133
173
|
|
134
174
|
void oci8_install_hook_functions()
|
135
175
|
{
|
176
|
+
static int hook_functions_installed = 0;
|
177
|
+
HKEY hKey;
|
178
|
+
DWORD type;
|
179
|
+
DWORD data;
|
180
|
+
DWORD cbData = sizeof(data);
|
181
|
+
const char *reg_key = "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters";
|
182
|
+
|
183
|
+
if (hook_functions_installed) {
|
184
|
+
return;
|
185
|
+
}
|
186
|
+
|
136
187
|
InitializeCriticalSectionAndSpinCount(&lock, 5000);
|
137
188
|
locK_is_initialized = 1;
|
138
189
|
|
190
|
+
#ifndef _MSC_VER
|
191
|
+
/* Get setsockopt in ws2_32.dll.
|
192
|
+
* setsockopt used by mingw compiler isn't same with that in ws2_32.dll.
|
193
|
+
*/
|
194
|
+
setsockopt = (setsockopt_t)GetProcAddress(GetModuleHandleA("WS2_32.DLL"), "setsockopt");
|
195
|
+
if (setsockopt == NULL){
|
196
|
+
rb_raise(rb_eRuntimeError, "setsockopt isn't found in WS2_32.DLL");
|
197
|
+
}
|
198
|
+
#endif
|
199
|
+
|
200
|
+
/* Get system-wide keepalive interval parameter.
|
201
|
+
* https://technet.microsoft.com/en-us/library/cc957548.aspx
|
202
|
+
*/
|
203
|
+
if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, reg_key, 0, KEY_QUERY_VALUE, &hKey) != 0) {
|
204
|
+
rb_raise(rb_eRuntimeError, "failed to open the registry key HKLM\\%s", reg_key);
|
205
|
+
}
|
206
|
+
keepalive_interval = 1000; /* default value when the following entry isn't found. */
|
207
|
+
if (RegQueryValueEx(hKey, "KeepAliveInterval", NULL, &type, (LPBYTE)&data, &cbData) == 0) {
|
208
|
+
if (type == REG_DWORD) {
|
209
|
+
keepalive_interval = data;
|
210
|
+
}
|
211
|
+
}
|
212
|
+
RegCloseKey(hKey);
|
213
|
+
|
139
214
|
if (replace_functions(tcp_func_files, tcp_functions) != 0) {
|
140
215
|
rb_raise(rb_eRuntimeError, "No DLL is found to hook.");
|
141
216
|
}
|
217
|
+
hook_functions_installed = 1;
|
142
218
|
}
|
143
219
|
|
144
220
|
static void shutdown_socket(socket_entry_t *entry)
|
@@ -170,25 +246,39 @@ static const char * const files[] = {
|
|
170
246
|
|
171
247
|
static hook_func_entry_t functions[] = {
|
172
248
|
{"read", (void*)hook_read, NULL},
|
249
|
+
#ifdef SUPPORT_TCP_KEEPALIVE_TIME
|
250
|
+
{"setsockopt", (void*)hook_setsockopt, NULL},
|
251
|
+
#endif
|
173
252
|
{NULL, NULL, NULL},
|
174
253
|
};
|
175
254
|
|
176
255
|
static ssize_t hook_read(int fd, void *buf, size_t count)
|
177
256
|
{
|
178
257
|
socket_entry_t entry;
|
258
|
+
int enable_cancel = oci8_cancel_read_at_exit;
|
179
259
|
ssize_t rv;
|
180
260
|
|
181
|
-
|
261
|
+
if (enable_cancel > 0) {
|
262
|
+
socket_entry_set(&entry, fd);
|
263
|
+
}
|
182
264
|
rv = read(fd, buf, count);
|
183
|
-
|
265
|
+
if (enable_cancel > 0) {
|
266
|
+
socket_entry_clear(&entry);
|
267
|
+
}
|
184
268
|
return rv;
|
185
269
|
}
|
186
270
|
|
187
271
|
void oci8_install_hook_functions(void)
|
188
272
|
{
|
273
|
+
static int hook_functions_installed = 0;
|
274
|
+
|
275
|
+
if (hook_functions_installed) {
|
276
|
+
return;
|
277
|
+
}
|
189
278
|
if (replace_functions(files, functions) != 0) {
|
190
279
|
rb_raise(rb_eRuntimeError, "No shared library is found to hook.");
|
191
280
|
}
|
281
|
+
hook_functions_installed = 1;
|
192
282
|
}
|
193
283
|
|
194
284
|
static void shutdown_socket(socket_entry_t *entry)
|
@@ -197,6 +287,41 @@ static void shutdown_socket(socket_entry_t *entry)
|
|
197
287
|
}
|
198
288
|
#endif
|
199
289
|
|
290
|
+
#ifdef SUPPORT_TCP_KEEPALIVE_TIME
|
291
|
+
static int hook_setsockopt(SOCKET sockfd, int level, int optname, const void *optval, socklen_t optlen)
|
292
|
+
{
|
293
|
+
int rv = setsockopt(sockfd, level, optname, optval, optlen);
|
294
|
+
|
295
|
+
if (rv == 0 && level == SOL_SOCKET && optname == SO_KEEPALIVE
|
296
|
+
&& optlen == sizeof(int) && *(const int*)optval != 0) {
|
297
|
+
/* If Oracle client libraries enables keepalive by (ENABLE=BROKEN),
|
298
|
+
* set per-connection keepalive socket options to overwrite
|
299
|
+
* system-wide setting.
|
300
|
+
*/
|
301
|
+
if (oci8_tcp_keepalive_time > 0) {
|
302
|
+
#if defined(USE_TCP_KEEPALIVE) /* macOS */
|
303
|
+
setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPALIVE, &oci8_tcp_keepalive_time, sizeof(int));
|
304
|
+
#elif defined(USE_TCP_KEEPALIVE_THRESHOLD) /* Solaris */
|
305
|
+
unsigned int millisec = oci8_tcp_keepalive_time * 1000;
|
306
|
+
setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPALIVE_THRESHOLD, &millisec, sizeof(millisec));
|
307
|
+
#elif defined(USE_TCP_KEEPIDLE) /* Linux, etc */
|
308
|
+
setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPIDLE, &oci8_tcp_keepalive_time, sizeof(int));
|
309
|
+
#elif defined(WIN32) /* Windows */
|
310
|
+
struct tcp_keepalive vals;
|
311
|
+
DWORD dummy;
|
312
|
+
|
313
|
+
vals.onoff = 1;
|
314
|
+
vals.keepalivetime = oci8_tcp_keepalive_time * 1000;
|
315
|
+
vals.keepaliveinterval = keepalive_interval;
|
316
|
+
WSAIoctl(sockfd, SIO_KEEPALIVE_VALS, &vals, sizeof(vals), NULL, 0,
|
317
|
+
&dummy, NULL, NULL);
|
318
|
+
#endif
|
319
|
+
}
|
320
|
+
}
|
321
|
+
return rv;
|
322
|
+
}
|
323
|
+
#endif
|
324
|
+
|
200
325
|
void oci8_shutdown_sockets(void)
|
201
326
|
{
|
202
327
|
socket_entry_t *entry;
|
data/ext/oci8/oci8.c
CHANGED
@@ -286,7 +286,7 @@ static VALUE oci8_s_oracle_client_vernum(VALUE klass)
|
|
286
286
|
/*
|
287
287
|
* @overload OCI8.__get_prop(key)
|
288
288
|
*
|
289
|
-
* @param [
|
289
|
+
* @param [Integer] key 1, 2 or 3
|
290
290
|
* @return [Object] depends on +key+.
|
291
291
|
* @private
|
292
292
|
*/
|
@@ -306,16 +306,13 @@ static VALUE oci8_s_get_prop(VALUE klass, VALUE key)
|
|
306
306
|
/*
|
307
307
|
* @overload OCI8.__set_prop(key, value)
|
308
308
|
*
|
309
|
-
* @param [
|
309
|
+
* @param [Integer] key 1, 2 or 3
|
310
310
|
* @param [Object] value depends on +key+.
|
311
311
|
*
|
312
312
|
* @private
|
313
313
|
*/
|
314
314
|
static VALUE oci8_s_set_prop(VALUE klass, VALUE key, VALUE val)
|
315
315
|
{
|
316
|
-
#ifdef HAVE_PLTHOOK
|
317
|
-
static int hook_functions_installed = 0;
|
318
|
-
#endif
|
319
316
|
switch (NUM2INT(key)) {
|
320
317
|
case 1:
|
321
318
|
oci8_float_conversion_type_is_ruby = RTEST(val) ? 1 : 0;
|
@@ -331,13 +328,21 @@ static VALUE oci8_s_set_prop(VALUE klass, VALUE key, VALUE val)
|
|
331
328
|
oci8_env_mode = NUM2UINT(val);
|
332
329
|
break;
|
333
330
|
case 3:
|
331
|
+
if (oci8_cancel_read_at_exit == -1) {
|
332
|
+
rb_raise(rb_eNotImpError, "OCI8.properties[:cancel_read_at_exit] isn't available.");
|
333
|
+
}
|
334
334
|
#ifdef HAVE_PLTHOOK
|
335
|
-
|
336
|
-
|
337
|
-
|
335
|
+
oci8_install_hook_functions();
|
336
|
+
oci8_cancel_read_at_exit = RTEST(val) ? 1 : 0;
|
337
|
+
#endif
|
338
|
+
break;
|
339
|
+
case 4:
|
340
|
+
if (oci8_tcp_keepalive_time == -1) {
|
341
|
+
rb_raise(rb_eNotImpError, "OCI8.properties[:tcp_keepalive_time] isn't available.");
|
338
342
|
}
|
339
|
-
#
|
340
|
-
|
343
|
+
#ifdef HAVE_PLTHOOK
|
344
|
+
oci8_install_hook_functions();
|
345
|
+
oci8_tcp_keepalive_time = NIL_P(val) ? 0 : NUM2INT(val);
|
341
346
|
#endif
|
342
347
|
break;
|
343
348
|
default:
|
@@ -359,7 +364,7 @@ static VALUE oci8_s_set_prop(VALUE klass, VALUE key, VALUE val)
|
|
359
364
|
* # When NLS_LANG is FRENCH_FRANCE.AL32UTF8
|
360
365
|
* OCI8.error_message(1) # => "ORA-00001: violation de contrainte unique (%s.%s)"
|
361
366
|
*
|
362
|
-
* @param [
|
367
|
+
* @param [Integer] message_no Oracle error message number
|
363
368
|
* @return [String] Oracle error message
|
364
369
|
*/
|
365
370
|
static VALUE oci8_s_error_message(VALUE klass, VALUE msgid)
|
@@ -668,8 +673,8 @@ static VALUE oci8_server_attach(VALUE self, VALUE dbname, VALUE attach_mode)
|
|
668
673
|
*
|
669
674
|
* Begins the session by the OCI function OCISessionBegin().
|
670
675
|
*
|
671
|
-
* @param [
|
672
|
-
* @param [
|
676
|
+
* @param [Integer] cred
|
677
|
+
* @param [Integer] mode
|
673
678
|
* @private
|
674
679
|
*/
|
675
680
|
static VALUE oci8_session_begin(VALUE self, VALUE cred, VALUE mode)
|
data/ext/oci8/oci8.h
CHANGED
@@ -340,7 +340,8 @@ typedef struct {
|
|
340
340
|
} oci8_exec_sql_var_t;
|
341
341
|
|
342
342
|
#define oci8_raise(err, status, stmt) oci8_do_raise(err, status, stmt, __FILE__, __LINE__)
|
343
|
-
#define oci8_env_raise(
|
343
|
+
#define oci8_env_raise(env, status) oci8_do_env_raise(env, status, 0, __FILE__, __LINE__)
|
344
|
+
#define oci8_env_free_and_raise(env, status) oci8_do_env_raise(env, status, 1, __FILE__, __LINE__)
|
344
345
|
#define oci8_raise_init_error() oci8_do_raise_init_error(__FILE__, __LINE__)
|
345
346
|
#define oci8_raise_by_msgno(msgno, default_msg) oci8_do_raise_by_msgno(msgno, default_msg, __FILE__, __LINE__)
|
346
347
|
|
@@ -406,7 +407,7 @@ void *oci8_check_typeddata(VALUE obj, const oci8_handle_data_type_t *data_type,
|
|
406
407
|
extern VALUE eOCIException;
|
407
408
|
extern VALUE eOCIBreak;
|
408
409
|
void Init_oci8_error(void);
|
409
|
-
NORETURN(void oci8_do_env_raise(OCIEnv
|
410
|
+
NORETURN(void oci8_do_env_raise(OCIEnv *envhp, sword status, int free_envhp, const char *file, int line));
|
410
411
|
NORETURN(void oci8_do_raise_init_error(const char *file, int line));
|
411
412
|
sb4 oci8_get_error_code(OCIError *errhp);
|
412
413
|
VALUE oci8_get_error_message(ub4 msgno, const char *default_msg);
|
@@ -514,6 +515,13 @@ void Init_oci8_win32(VALUE cOCI8);
|
|
514
515
|
/* hook_funcs.c */
|
515
516
|
void oci8_install_hook_functions(void);
|
516
517
|
void oci8_shutdown_sockets(void);
|
518
|
+
#ifdef HAVE_PLTHOOK
|
519
|
+
extern int oci8_cancel_read_at_exit;
|
520
|
+
extern int oci8_tcp_keepalive_time;
|
521
|
+
#else
|
522
|
+
#define oci8_cancel_read_at_exit (-1)
|
523
|
+
#define oci8_tcp_keepalive_time (-1)
|
524
|
+
#endif
|
517
525
|
|
518
526
|
#define OCI8StringValue(v) do { \
|
519
527
|
StringValue(v); \
|
data/ext/oci8/oci8lib.c
CHANGED
@@ -7,6 +7,10 @@
|
|
7
7
|
#ifdef HAVE_RUBY_THREAD_H
|
8
8
|
#include <ruby/thread.h>
|
9
9
|
#endif
|
10
|
+
#if defined(HAVE_PLTHOOK) && !defined(WIN32)
|
11
|
+
#include <dlfcn.h>
|
12
|
+
#include "plthook.h"
|
13
|
+
#endif
|
10
14
|
|
11
15
|
ID oci8_id_at_last_error;
|
12
16
|
ID oci8_id_get;
|
@@ -67,6 +71,122 @@ static VALUE bind_base_alloc(VALUE klass)
|
|
67
71
|
rb_raise(rb_eNameError, "private method `new' called for %s:Class", rb_class2name(klass));
|
68
72
|
}
|
69
73
|
|
74
|
+
#if defined(HAVE_PLTHOOK) && !defined(WIN32)
|
75
|
+
static const char *find_libclntsh(void *handle)
|
76
|
+
{
|
77
|
+
void *symaddr = dlsym(handle, "OCIEnvCreate");
|
78
|
+
Dl_info info;
|
79
|
+
#ifdef __APPLE__
|
80
|
+
const char *basename = "libclntsh.dylib";
|
81
|
+
#else
|
82
|
+
const char *basename = "libclntsh.so";
|
83
|
+
#endif
|
84
|
+
const char *p;
|
85
|
+
|
86
|
+
if (symaddr == NULL) {
|
87
|
+
return NULL;
|
88
|
+
}
|
89
|
+
if (dladdr(symaddr, &info) == 0) {
|
90
|
+
return NULL;
|
91
|
+
}
|
92
|
+
if ((p = strrchr(info.dli_fname, '/')) == NULL) {
|
93
|
+
return NULL;
|
94
|
+
}
|
95
|
+
if (strncmp(p + 1, basename, strlen(basename)) != 0) {
|
96
|
+
return NULL;
|
97
|
+
}
|
98
|
+
return info.dli_fname;
|
99
|
+
}
|
100
|
+
|
101
|
+
/*
|
102
|
+
* Symbol prefix depends on the platform.
|
103
|
+
* Linux x86_64 - no prefix
|
104
|
+
* Linux x86_32 - "_"
|
105
|
+
* macOS - "@_"
|
106
|
+
*/
|
107
|
+
static const char *find_symbol_prefix(plthook_t *ph, size_t *len)
|
108
|
+
{
|
109
|
+
unsigned int pos = 0;
|
110
|
+
const char *name;
|
111
|
+
void **addr;
|
112
|
+
|
113
|
+
while (plthook_enum(ph, &pos, &name, &addr) == 0) {
|
114
|
+
const char *p = strstr(name, "OCIEnvCreate");
|
115
|
+
if (p != NULL) {
|
116
|
+
*len = p - name;
|
117
|
+
return name;
|
118
|
+
}
|
119
|
+
}
|
120
|
+
return NULL;
|
121
|
+
}
|
122
|
+
|
123
|
+
/*
|
124
|
+
* Fix PLT entries against function interposition.
|
125
|
+
* See: http://www.rubydoc.info/github/kubo/ruby-oci8/file/docs/ldap-auth-and-function-interposition.md
|
126
|
+
*/
|
127
|
+
static void rebind_internal_symbols(void)
|
128
|
+
{
|
129
|
+
const char *libfile;
|
130
|
+
void *handle;
|
131
|
+
int flags = RTLD_LAZY | RTLD_NOLOAD;
|
132
|
+
plthook_t *ph;
|
133
|
+
unsigned int pos = 0;
|
134
|
+
const char *name;
|
135
|
+
void **addr;
|
136
|
+
const char *prefix;
|
137
|
+
size_t prefix_len;
|
138
|
+
|
139
|
+
#ifdef RTLD_FIRST
|
140
|
+
flags |= RTLD_FIRST; /* for macOS */
|
141
|
+
#endif
|
142
|
+
|
143
|
+
libfile = find_libclntsh(RTLD_DEFAULT); /* normal case */
|
144
|
+
if (libfile == NULL) {
|
145
|
+
libfile = find_libclntsh(RTLD_NEXT); /* special case when OCIEnvCreate is hooked by LD_PRELOAD */
|
146
|
+
}
|
147
|
+
if (libfile == NULL) {
|
148
|
+
return;
|
149
|
+
}
|
150
|
+
handle = dlopen(libfile, flags);
|
151
|
+
if (handle == NULL) {
|
152
|
+
return;
|
153
|
+
}
|
154
|
+
if (plthook_open(&ph, libfile) != 0) {
|
155
|
+
dlclose(handle);
|
156
|
+
return;
|
157
|
+
}
|
158
|
+
prefix = find_symbol_prefix(ph, &prefix_len);
|
159
|
+
if (prefix == NULL) {
|
160
|
+
dlclose(handle);
|
161
|
+
plthook_close(ph);
|
162
|
+
return;
|
163
|
+
}
|
164
|
+
while (plthook_enum(ph, &pos, &name, &addr) == 0) {
|
165
|
+
void *funcaddr;
|
166
|
+
if (prefix_len != 0) {
|
167
|
+
if (strncmp(name, prefix, prefix_len) != 0) {
|
168
|
+
continue;
|
169
|
+
}
|
170
|
+
name += prefix_len;
|
171
|
+
}
|
172
|
+
if (strncmp(name, "OCI", 3) == 0) {
|
173
|
+
/* exclude functions starting with OCI not to prevent LD_PRELOAD hooking */
|
174
|
+
continue;
|
175
|
+
}
|
176
|
+
funcaddr = dlsym(handle, name);
|
177
|
+
if (funcaddr != NULL && *addr != funcaddr) {
|
178
|
+
/* If libclntsh.so exports and imports same functions, their
|
179
|
+
* PLT entries are forcedly modified to point to itself not
|
180
|
+
* to use functions in other libraries.
|
181
|
+
*/
|
182
|
+
*addr = funcaddr;
|
183
|
+
}
|
184
|
+
}
|
185
|
+
plthook_close(ph);
|
186
|
+
dlclose(handle);
|
187
|
+
}
|
188
|
+
#endif
|
189
|
+
|
70
190
|
#ifdef _WIN32
|
71
191
|
__declspec(dllexport)
|
72
192
|
#endif
|
@@ -74,7 +194,7 @@ void
|
|
74
194
|
Init_oci8lib()
|
75
195
|
{
|
76
196
|
VALUE cOCI8;
|
77
|
-
OCIEnv *envhp;
|
197
|
+
OCIEnv *envhp = NULL;
|
78
198
|
OCIError *errhp;
|
79
199
|
sword rv;
|
80
200
|
|
@@ -106,6 +226,9 @@ Init_oci8lib()
|
|
106
226
|
oracle_client_version = ORAVERNUM(major, minor, update, patch, port_update);
|
107
227
|
}
|
108
228
|
#endif
|
229
|
+
#if defined(HAVE_PLTHOOK) && !defined(WIN32)
|
230
|
+
rebind_internal_symbols();
|
231
|
+
#endif
|
109
232
|
|
110
233
|
oci8_id_at_last_error = rb_intern("@last_error");
|
111
234
|
oci8_id_get = rb_intern("get");
|
@@ -153,7 +276,11 @@ Init_oci8lib()
|
|
153
276
|
/* allocate a temporary errhp to pass Init_oci_number() */
|
154
277
|
rv = OCIEnvCreate(&envhp, oci8_env_mode, NULL, NULL, NULL, NULL, 0, NULL);
|
155
278
|
if (rv != OCI_SUCCESS) {
|
156
|
-
|
279
|
+
if (envhp != NULL) {
|
280
|
+
oci8_env_free_and_raise(envhp, rv);
|
281
|
+
} else {
|
282
|
+
oci8_raise_init_error();
|
283
|
+
}
|
157
284
|
}
|
158
285
|
rv = OCIHandleAlloc(envhp, (dvoid *)&errhp, OCI_HTYPE_ERROR, 0, NULL);
|
159
286
|
if (rv != OCI_SUCCESS)
|