ruby-oci8 2.2.3 → 2.2.4
Sign up to get free protection for your applications and to get access to all the features.
- 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)
|