ruby-oci8 2.2.0.2 → 2.2.12
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.yardopts +1 -6
- data/ChangeLog +600 -0
- data/NEWS +426 -35
- data/README.md +27 -9
- data/dist-files +13 -2
- data/docs/bind-array-to-in_cond.md +38 -0
- data/docs/conflicts-local-connections-and-processes.md +98 -0
- data/docs/hanging-after-inactivity.md +63 -0
- data/docs/install-binary-package.md +15 -11
- data/docs/install-full-client.md +18 -21
- data/docs/install-instant-client.md +45 -27
- data/docs/install-on-osx.md +31 -117
- data/docs/ldap-auth-and-function-interposition.md +123 -0
- data/docs/number-type-mapping.md +79 -0
- data/docs/platform-specific-issues.md +17 -50
- data/docs/report-installation-issue.md +11 -8
- data/docs/timeout-parameters.md +94 -0
- data/ext/oci8/apiwrap.c.tmpl +2 -5
- data/ext/oci8/apiwrap.rb +6 -1
- data/ext/oci8/apiwrap.yml +39 -143
- data/ext/oci8/attr.c +4 -2
- data/ext/oci8/bind.c +421 -9
- data/ext/oci8/connection_pool.c +3 -3
- data/ext/oci8/encoding.c +5 -5
- data/ext/oci8/env.c +8 -2
- data/ext/oci8/error.c +24 -16
- data/ext/oci8/extconf.rb +35 -63
- data/ext/oci8/hook_funcs.c +274 -61
- data/ext/oci8/lob.c +31 -75
- data/ext/oci8/metadata.c +8 -6
- data/ext/oci8/object.c +119 -29
- data/ext/oci8/oci8.c +46 -133
- data/ext/oci8/oci8.h +40 -123
- data/ext/oci8/oci8lib.c +178 -46
- data/ext/oci8/ocihandle.c +37 -37
- data/ext/oci8/ocinumber.c +24 -35
- data/ext/oci8/oraconf.rb +168 -337
- data/ext/oci8/oradate.c +19 -19
- data/ext/oci8/plthook.h +10 -0
- data/ext/oci8/plthook_elf.c +433 -268
- data/ext/oci8/plthook_osx.c +40 -9
- data/ext/oci8/plthook_win32.c +16 -1
- data/ext/oci8/stmt.c +52 -17
- data/ext/oci8/win32.c +4 -22
- data/lib/oci8/bindtype.rb +10 -17
- data/lib/oci8/check_load_error.rb +57 -10
- data/lib/oci8/compat.rb +5 -1
- data/lib/oci8/connection_pool.rb +74 -3
- data/lib/oci8/cursor.rb +70 -31
- data/lib/oci8/metadata.rb +9 -1
- data/lib/oci8/object.rb +14 -1
- data/lib/oci8/oci8.rb +184 -58
- data/lib/oci8/ocihandle.rb +0 -16
- data/lib/oci8/oracle_version.rb +11 -1
- data/lib/oci8/properties.rb +55 -0
- data/lib/oci8/version.rb +1 -1
- data/lib/oci8.rb +48 -4
- data/lib/ruby-oci8.rb +1 -0
- data/pre-distclean.rb +1 -3
- data/ruby-oci8.gemspec +4 -9
- data/setup.rb +11 -2
- data/test/README.md +37 -0
- data/test/config.rb +8 -1
- data/test/setup_test_object.sql +42 -14
- data/test/setup_test_package.sql +59 -0
- data/test/test_all.rb +4 -0
- data/test/test_bind_array.rb +70 -0
- data/test/test_bind_boolean.rb +99 -0
- data/test/test_bind_integer.rb +47 -0
- data/test/test_break.rb +11 -9
- data/test/test_clob.rb +5 -17
- data/test/test_connstr.rb +142 -0
- data/test/test_datetime.rb +8 -3
- data/test/test_metadata.rb +2 -1
- data/test/test_object.rb +99 -18
- data/test/test_oci8.rb +170 -46
- data/test/test_oranumber.rb +12 -6
- data/test/test_package_type.rb +17 -3
- data/test/test_properties.rb +17 -0
- metadata +45 -55
- data/docs/osx-install-dev-tools.png +0 -0
- data/test/README +0 -42
data/ext/oci8/extconf.rb
CHANGED
@@ -25,77 +25,44 @@ $CFLAGS += oraconf.cflags
|
|
25
25
|
saved_libs = $libs
|
26
26
|
$libs += oraconf.libs
|
27
27
|
|
28
|
-
oci_actual_client_version = 0x08000000
|
29
|
-
funcs = {}
|
30
|
-
YAML.load(open(File.dirname(__FILE__) + '/apiwrap.yml')).each do |key, val|
|
31
|
-
key = key[0..-4] if key[-3..-1] == '_nb'
|
32
|
-
ver = val[:version]
|
33
|
-
ver_major = (ver / 100)
|
34
|
-
ver_minor = (ver / 10) % 10
|
35
|
-
ver_update = ver % 10
|
36
|
-
ver = ((ver_major << 24) | (ver_minor << 20) | (ver_update << 12))
|
37
|
-
funcs[ver] ||= []
|
38
|
-
funcs[ver] << key
|
39
|
-
end
|
40
|
-
|
41
28
|
saved_defs = $defs.clone
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
29
|
+
fmt = "%s"
|
30
|
+
def fmt.%(x)
|
31
|
+
x ? super : "failed"
|
32
|
+
end
|
33
|
+
oci_major_version = checking_for 'OCI_MAJOR_VERSION in oci.h', fmt do
|
34
|
+
try_constant('OCI_MAJOR_VERSION', 'oci.h')
|
35
|
+
end
|
36
|
+
if oci_major_version
|
37
|
+
oci_minor_version = checking_for 'OCI_MINOR_VERSION in oci.h', fmt do
|
38
|
+
try_constant('OCI_MINOR_VERSION', 'oci.h')
|
39
|
+
end
|
40
|
+
else
|
41
|
+
if have_func('OCILobGetLength2')
|
42
|
+
oci_major_version = 10
|
43
|
+
oci_minor_version = 1
|
44
|
+
elsif have_func('OCIStmtPrepare2')
|
45
|
+
raise "Ruby-oci8 #{OCI8::VERSION} doesn't support Oracle 9iR2. Use ruby-oci8 2.1.x instead."
|
46
|
+
elsif have_func('OCILogon2')
|
47
|
+
raise "Ruby-oci8 #{OCI8::VERSION} doesn't support Oracle 9iR1. Use ruby-oci8 2.1.x instead."
|
48
|
+
elsif have_func('OCIEnvCreate')
|
49
|
+
raise "Ruby-oci8 #{OCI8::VERSION} doesn't support Oracle 8i. Use ruby-oci8 2.0.x instead."
|
50
|
+
else
|
51
|
+
raise "Ruby-oci8 #{OCI8::VERSION} doesn't support Oracle 8. Use ruby-oci8 2.0.x instead."
|
54
52
|
end
|
55
|
-
puts "checking for Oracle #{verstr} API - #{result}"
|
56
|
-
break if result == 'fail'
|
57
53
|
end
|
58
54
|
$defs = saved_defs
|
59
55
|
|
60
|
-
have_type('oratext', 'ociap.h')
|
61
|
-
have_type('OCIDateTime*', 'ociap.h')
|
62
|
-
have_type('OCIInterval*', 'ociap.h')
|
63
|
-
have_type('OCICallbackLobRead2', 'ociap.h')
|
64
|
-
have_type('OCICallbackLobWrite2', 'ociap.h')
|
65
|
-
have_type('OCIAdmin*', 'ociap.h')
|
66
|
-
have_type('OCIAuthInfo*', 'ociap.h')
|
67
|
-
have_type('OCIMsg*', 'ociap.h')
|
68
|
-
have_type('OCICPool*', 'ociap.h')
|
69
|
-
|
70
56
|
if with_config('oracle-version')
|
71
|
-
|
57
|
+
oraver = OCI8::OracleVersion.new(with_config('oracle-version'))
|
72
58
|
else
|
73
|
-
|
59
|
+
oraver = OCI8::OracleVersion.new(oci_major_version, oci_minor_version)
|
74
60
|
end
|
75
|
-
$defs << "-DORACLE_CLIENT_VERSION=#{format('0x%08x',
|
61
|
+
$defs << "-DORACLE_CLIENT_VERSION=#{format('0x%08x', oraver.to_i)}"
|
76
62
|
|
77
63
|
if with_config('runtime-check')
|
78
64
|
$defs << "-DRUNTIME_API_CHECK=1"
|
79
65
|
$libs = saved_libs
|
80
|
-
else
|
81
|
-
oraver = OCI8::OracleVersion.new(oci_client_version)
|
82
|
-
if oraver < OCI8::OracleVersion.new(10)
|
83
|
-
case "#{oraver.major}.#{oraver.minor}"
|
84
|
-
when "8.0"
|
85
|
-
ora_name = "Oracle 8"
|
86
|
-
oci8_ver = "2.0.x"
|
87
|
-
when "8.1"
|
88
|
-
ora_name = "Oracle 8i"
|
89
|
-
oci8_ver = "2.0.x"
|
90
|
-
when "9.1"
|
91
|
-
ora_name = "Oracle 9iR1"
|
92
|
-
oci8_ver = "2.1.x"
|
93
|
-
when "9.2"
|
94
|
-
ora_name = "Oracle 9iR2"
|
95
|
-
oci8_ver = "2.1.x"
|
96
|
-
end
|
97
|
-
raise "Ruby-oci8 #{OCI8::VERSION} doesn't support #{ora_name}. Use ruby-oci8 #{oci8_ver} instead."
|
98
|
-
end
|
99
66
|
end
|
100
67
|
|
101
68
|
$objs = ["oci8lib.o", "env.o", "error.o", "oci8.o", "ocihandle.o",
|
@@ -105,7 +72,7 @@ $objs = ["oci8lib.o", "env.o", "error.o", "oci8.o", "ocihandle.o",
|
|
105
72
|
"ocinumber.o", "ocidatetime.o", "object.o", "apiwrap.o",
|
106
73
|
"encoding.o", "oranumber_util.o", "thread_util.o", "util.o"]
|
107
74
|
|
108
|
-
if RUBY_PLATFORM =~ /mswin32|cygwin|
|
75
|
+
if RUBY_PLATFORM =~ /mswin32|cygwin|mingw/
|
109
76
|
$defs << "-DUSE_WIN32_C"
|
110
77
|
$objs << "win32.o"
|
111
78
|
end
|
@@ -172,6 +139,8 @@ when 'rbx'
|
|
172
139
|
so_basename += 'rbx'
|
173
140
|
when 'jruby'
|
174
141
|
raise "Ruby-oci8 doesn't support jruby because its C extension support is in development in jruby 1.6 and deprecated in jruby 1.7."
|
142
|
+
when 'truffleruby'
|
143
|
+
so_basename += 'truffleruby'
|
175
144
|
else
|
176
145
|
raise 'unsupported ruby engine: ' + RUBY_ENGINE
|
177
146
|
end
|
@@ -179,19 +148,22 @@ end
|
|
179
148
|
print "checking for plthook... "
|
180
149
|
STDOUT.flush
|
181
150
|
case RUBY_PLATFORM
|
182
|
-
when /mswin32|cygwin|
|
151
|
+
when /mswin32|cygwin|mingw/
|
183
152
|
plthook_src = "plthook_win32.c"
|
184
153
|
when /darwin/
|
185
154
|
plthook_src = "plthook_osx.c"
|
186
155
|
else
|
187
156
|
plthook_src = "plthook_elf.c"
|
188
157
|
end
|
189
|
-
|
190
|
-
|
158
|
+
FileUtils.copy(File.dirname(__FILE__) + "/" + plthook_src, CONFTEST_C)
|
159
|
+
if xsystem(cc_command(""))
|
160
|
+
FileUtils.rm_f("#{CONFTEST}.#{$OBJEXT}")
|
191
161
|
puts plthook_src
|
192
162
|
$objs << plthook_src.gsub(/\.c$/, '.o')
|
193
163
|
$objs << "hook_funcs.o"
|
194
164
|
$defs << "-DHAVE_PLTHOOK"
|
165
|
+
have_library('dbghelp', 'ImageDirectoryEntryToData', ['windows.h', 'dbghelp.h']) if RUBY_PLATFORM =~ /cygwin/
|
166
|
+
$libs += ' -lws2_32' if RUBY_PLATFORM =~ /cygwin/
|
195
167
|
else
|
196
168
|
puts "no"
|
197
169
|
end
|
data/ext/oci8/hook_funcs.c
CHANGED
@@ -2,29 +2,69 @@
|
|
2
2
|
/*
|
3
3
|
* hook.c
|
4
4
|
*
|
5
|
-
* Copyright (C) 2015 Kubo Takehiro <kubo@jiubao.org>
|
5
|
+
* Copyright (C) 2015-2018 Kubo Takehiro <kubo@jiubao.org>
|
6
6
|
*/
|
7
|
+
#if defined(_WIN32) || defined(__CYGWIN__)
|
8
|
+
#define WINDOWS
|
9
|
+
#include <winsock2.h>
|
10
|
+
#endif
|
7
11
|
#include "oci8.h"
|
8
12
|
#include "plthook.h"
|
9
|
-
#
|
13
|
+
#ifdef __CYGWIN__
|
14
|
+
#undef boolean /* boolean defined in oratypes.h coflicts with that in windows.h */
|
15
|
+
#define stricmp strcasecmp
|
16
|
+
#define strnicmp strncasecmp
|
17
|
+
#endif
|
18
|
+
#ifdef WINDOWS
|
19
|
+
#include <windows.h>
|
20
|
+
#include <mstcpip.h>
|
21
|
+
#include <tlhelp32.h>
|
22
|
+
#else
|
10
23
|
#include <unistd.h>
|
11
24
|
#include <sys/socket.h>
|
25
|
+
#include <netinet/in.h>
|
26
|
+
#include <netinet/tcp.h>
|
27
|
+
#include <dlfcn.h>
|
12
28
|
#endif
|
13
29
|
|
14
|
-
#
|
15
|
-
|
16
|
-
#ifdef WIN32
|
30
|
+
#ifdef WINDOWS
|
17
31
|
static CRITICAL_SECTION lock;
|
18
32
|
#define LOCK(lock) EnterCriticalSection(lock)
|
19
33
|
#define UNLOCK(lock) LeaveCriticalSection(lock)
|
34
|
+
typedef int socklen_t;
|
20
35
|
#else
|
21
36
|
static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
|
22
37
|
#define LOCK(lock) pthread_mutex_lock(lock)
|
23
38
|
#define UNLOCK(lock) pthread_mutex_unlock(lock)
|
24
39
|
#define SOCKET int
|
25
40
|
#define INVALID_SOCKET (-1)
|
41
|
+
#define WSAAPI
|
42
|
+
#endif
|
43
|
+
|
44
|
+
#if defined(__APPLE__) && defined(TCP_KEEPALIVE) /* macOS */
|
45
|
+
#define USE_TCP_KEEPALIVE
|
46
|
+
#define SUPPORT_TCP_KEEPALIVE_TIME
|
47
|
+
#elif defined(__sun) && defined(TCP_KEEPALIVE_THRESHOLD) /* Solaris */
|
48
|
+
#define USE_TCP_KEEPALIVE_THRESHOLD
|
49
|
+
#define SUPPORT_TCP_KEEPALIVE_TIME
|
50
|
+
#elif defined(TCP_KEEPIDLE) /* Linux, etc */
|
51
|
+
#define USE_TCP_KEEPIDLE
|
52
|
+
#define SUPPORT_TCP_KEEPALIVE_TIME
|
53
|
+
#elif defined(WINDOWS)
|
54
|
+
#define SUPPORT_TCP_KEEPALIVE_TIME
|
26
55
|
#endif
|
27
56
|
|
57
|
+
int oci8_cancel_read_at_exit = 0;
|
58
|
+
|
59
|
+
#ifdef SUPPORT_TCP_KEEPALIVE_TIME
|
60
|
+
int oci8_tcp_keepalive_time = 0;
|
61
|
+
static int WSAAPI hook_setsockopt(SOCKET sockfd, int level, int optname, const void *optval, socklen_t optlen);
|
62
|
+
#else
|
63
|
+
int oci8_tcp_keepalive_time = -1;
|
64
|
+
#endif
|
65
|
+
|
66
|
+
static char hook_errmsg[512];
|
67
|
+
|
28
68
|
typedef struct {
|
29
69
|
const char *func_name;
|
30
70
|
void *func_addr;
|
@@ -60,62 +100,56 @@ static void socket_entry_clear(socket_entry_t *entry)
|
|
60
100
|
UNLOCK(&lock);
|
61
101
|
}
|
62
102
|
|
63
|
-
static int replace_functions(
|
103
|
+
static int replace_functions(void *addr, const char *file, hook_func_entry_t *functions)
|
64
104
|
{
|
105
|
+
plthook_t *ph;
|
65
106
|
int i;
|
107
|
+
int rv = 0;
|
66
108
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
plthook_close(ph);
|
84
|
-
rb_raise(rb_eRuntimeError, "Could not replace function %s in %s", function->func_name, file);
|
85
|
-
}
|
109
|
+
if (plthook_open_by_address(&ph, addr) != 0) {
|
110
|
+
strncpy(hook_errmsg, plthook_error(), sizeof(hook_errmsg) - 1);
|
111
|
+
hook_errmsg[sizeof(hook_errmsg) - 1] = '\0';
|
112
|
+
return -1;
|
113
|
+
}
|
114
|
+
|
115
|
+
/* install hooks */
|
116
|
+
for (i = 0; functions[i].func_name != NULL ; i++) {
|
117
|
+
hook_func_entry_t *function = &functions[i];
|
118
|
+
rv = plthook_replace(ph, function->func_name, function->func_addr, &function->old_func_addr);
|
119
|
+
if (rv != 0) {
|
120
|
+
strncpy(hook_errmsg, plthook_error(), sizeof(hook_errmsg) - 1);
|
121
|
+
hook_errmsg[sizeof(hook_errmsg) - 1] = '\0';
|
122
|
+
while (--i >= 0) {
|
123
|
+
/*restore hooked fuction address */
|
124
|
+
plthook_replace(ph, functions[i].func_name, functions[i].old_func_addr, NULL);
|
86
125
|
}
|
87
|
-
|
88
|
-
|
126
|
+
snprintf(hook_errmsg, sizeof(hook_errmsg), "Could not replace function %s in %s", function->func_name, file);
|
127
|
+
break;
|
89
128
|
}
|
90
129
|
}
|
91
|
-
|
130
|
+
plthook_close(ph);
|
131
|
+
return rv;
|
92
132
|
}
|
93
133
|
|
94
|
-
#ifdef
|
134
|
+
#ifdef WINDOWS
|
135
|
+
|
136
|
+
#ifndef _MSC_VER
|
137
|
+
/* setsockopt() in ws2_32.dll */
|
138
|
+
#define setsockopt rboci_setsockopt
|
139
|
+
typedef int (WSAAPI *setsockopt_t)(SOCKET, int, int, const void *, int);
|
140
|
+
static setsockopt_t setsockopt;
|
141
|
+
#endif
|
142
|
+
|
143
|
+
/* system-wide keepalive interval */
|
144
|
+
static DWORD keepalive_interval;
|
95
145
|
|
96
146
|
static int locK_is_initialized;
|
97
147
|
|
98
148
|
static int WSAAPI hook_WSARecv(SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount, LPDWORD lpNumberOfBytesRecvd, LPDWORD lpFlags, LPWSAOVERLAPPED lpOverlapped, LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine);
|
99
149
|
|
100
|
-
static const char * const tcp_func_files[] = {
|
101
|
-
/* full client */
|
102
|
-
"orantcp12.dll",
|
103
|
-
"orantcp11.dll",
|
104
|
-
"orantcp10.dll",
|
105
|
-
"orantcp9.dll",
|
106
|
-
/* instant client basic */
|
107
|
-
"oraociei12.dll",
|
108
|
-
"oraociei11.dll",
|
109
|
-
"oraociei10.dll",
|
110
|
-
/* instant client basic lite */
|
111
|
-
"oraociicus12.dll",
|
112
|
-
"oraociicus11.dll",
|
113
|
-
"oraociicus10.dll",
|
114
|
-
NULL,
|
115
|
-
};
|
116
|
-
|
117
150
|
static hook_func_entry_t tcp_functions[] = {
|
118
151
|
{"WSARecv", (void*)hook_WSARecv, NULL},
|
152
|
+
{"setsockopt", (void*)hook_setsockopt, NULL},
|
119
153
|
{NULL, NULL, NULL},
|
120
154
|
};
|
121
155
|
|
@@ -123,22 +157,112 @@ static hook_func_entry_t tcp_functions[] = {
|
|
123
157
|
static int WSAAPI hook_WSARecv(SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount, LPDWORD lpNumberOfBytesRecvd, LPDWORD lpFlags, LPWSAOVERLAPPED lpOverlapped, LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine)
|
124
158
|
{
|
125
159
|
socket_entry_t entry;
|
160
|
+
int enable_cancel = oci8_cancel_read_at_exit;
|
126
161
|
int rv;
|
127
162
|
|
128
|
-
|
163
|
+
if (enable_cancel > 0) {
|
164
|
+
socket_entry_set(&entry, s);
|
165
|
+
}
|
129
166
|
rv = WSARecv(s, lpBuffers, dwBufferCount, lpNumberOfBytesRecvd, lpFlags, lpOverlapped, lpCompletionRoutine);
|
130
|
-
|
167
|
+
if (enable_cancel > 0) {
|
168
|
+
socket_entry_clear(&entry);
|
169
|
+
}
|
131
170
|
return rv;
|
132
171
|
}
|
133
172
|
|
173
|
+
static int is_target_dll(MODULEENTRY32 *me)
|
174
|
+
{
|
175
|
+
static const char *basenames[] = {
|
176
|
+
"orantcp", // ORACLE_HOME-based client
|
177
|
+
"oraociei", // instant client basic
|
178
|
+
"oraociicus", // instant client basic lite
|
179
|
+
NULL,
|
180
|
+
};
|
181
|
+
const char **basename = basenames;
|
182
|
+
while (*basename != NULL) {
|
183
|
+
if (strnicmp(me->szModule, *basename, strlen(*basename)) == 0) {
|
184
|
+
break;
|
185
|
+
}
|
186
|
+
basename++;
|
187
|
+
}
|
188
|
+
if (*basename == NULL) {
|
189
|
+
return 0;
|
190
|
+
}
|
191
|
+
// basename{zero_or_more_digits}.dll
|
192
|
+
const char *p = me->szModule + strlen(*basename);
|
193
|
+
while ('0' <= *p && *p <= '9') {
|
194
|
+
p++;
|
195
|
+
}
|
196
|
+
if (stricmp(p, ".dll") != 0) {
|
197
|
+
return 0;
|
198
|
+
}
|
199
|
+
if (GetProcAddress((HMODULE)me->modBaseAddr, "nttini") == NULL) {
|
200
|
+
return 0;
|
201
|
+
}
|
202
|
+
return 1;
|
203
|
+
}
|
204
|
+
|
134
205
|
void oci8_install_hook_functions()
|
135
206
|
{
|
207
|
+
static int hook_functions_installed = 0;
|
208
|
+
HKEY hKey;
|
209
|
+
DWORD type;
|
210
|
+
DWORD data;
|
211
|
+
DWORD cbData = sizeof(data);
|
212
|
+
const char *reg_key = "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters";
|
213
|
+
HANDLE hSnapshot;
|
214
|
+
MODULEENTRY32 me;
|
215
|
+
BOOL module_found = FALSE;
|
216
|
+
|
217
|
+
if (hook_functions_installed) {
|
218
|
+
return;
|
219
|
+
}
|
220
|
+
|
136
221
|
InitializeCriticalSectionAndSpinCount(&lock, 5000);
|
137
222
|
locK_is_initialized = 1;
|
138
223
|
|
139
|
-
|
224
|
+
#ifndef _MSC_VER
|
225
|
+
/* Get setsockopt in ws2_32.dll.
|
226
|
+
* setsockopt used by mingw compiler isn't same with that in ws2_32.dll.
|
227
|
+
*/
|
228
|
+
setsockopt = (setsockopt_t)GetProcAddress(GetModuleHandleA("WS2_32.DLL"), "setsockopt");
|
229
|
+
if (setsockopt == NULL){
|
230
|
+
rb_raise(rb_eRuntimeError, "setsockopt isn't found in WS2_32.DLL");
|
231
|
+
}
|
232
|
+
#endif
|
233
|
+
|
234
|
+
/* Get system-wide keepalive interval parameter.
|
235
|
+
* https://technet.microsoft.com/en-us/library/cc957548.aspx
|
236
|
+
*/
|
237
|
+
if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, reg_key, 0, KEY_QUERY_VALUE, &hKey) != 0) {
|
238
|
+
rb_raise(rb_eRuntimeError, "failed to open the registry key HKLM\\%s", reg_key);
|
239
|
+
}
|
240
|
+
keepalive_interval = 1000; /* default value when the following entry isn't found. */
|
241
|
+
if (RegQueryValueEx(hKey, "KeepAliveInterval", NULL, &type, (LPBYTE)&data, &cbData) == 0) {
|
242
|
+
if (type == REG_DWORD) {
|
243
|
+
keepalive_interval = data;
|
244
|
+
}
|
245
|
+
}
|
246
|
+
RegCloseKey(hKey);
|
247
|
+
|
248
|
+
hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, GetCurrentProcessId());
|
249
|
+
me.dwSize = sizeof(me);
|
250
|
+
if (Module32First(hSnapshot, &me)) {
|
251
|
+
do {
|
252
|
+
if (is_target_dll(&me)) {
|
253
|
+
module_found = TRUE;
|
254
|
+
if (replace_functions(me.modBaseAddr, me.szExePath, tcp_functions) != 0) {
|
255
|
+
CloseHandle(hSnapshot);
|
256
|
+
rb_raise(rb_eRuntimeError, "Hook error: %s", hook_errmsg);
|
257
|
+
}
|
258
|
+
}
|
259
|
+
} while (Module32Next(hSnapshot, &me));
|
260
|
+
}
|
261
|
+
CloseHandle(hSnapshot);
|
262
|
+
if (!module_found) {
|
140
263
|
rb_raise(rb_eRuntimeError, "No DLL is found to hook.");
|
141
264
|
}
|
265
|
+
hook_functions_installed = 1;
|
142
266
|
}
|
143
267
|
|
144
268
|
static void shutdown_socket(socket_entry_t *entry)
|
@@ -160,35 +284,89 @@ static ssize_t hook_read(int fd, void *buf, size_t count);
|
|
160
284
|
#define SO_EXT "so"
|
161
285
|
#endif
|
162
286
|
|
163
|
-
static const char * const files[] = {
|
164
|
-
"libclntsh." SO_EXT ".12.1",
|
165
|
-
"libclntsh." SO_EXT ".11.1",
|
166
|
-
"libclntsh." SO_EXT ".10.1",
|
167
|
-
"libclntsh." SO_EXT ".9.0",
|
168
|
-
NULL,
|
169
|
-
};
|
170
|
-
|
171
287
|
static hook_func_entry_t functions[] = {
|
172
288
|
{"read", (void*)hook_read, NULL},
|
289
|
+
#ifdef SUPPORT_TCP_KEEPALIVE_TIME
|
290
|
+
{"setsockopt", (void*)hook_setsockopt, NULL},
|
291
|
+
#endif
|
173
292
|
{NULL, NULL, NULL},
|
174
293
|
};
|
175
294
|
|
176
295
|
static ssize_t hook_read(int fd, void *buf, size_t count)
|
177
296
|
{
|
178
297
|
socket_entry_t entry;
|
298
|
+
int enable_cancel = oci8_cancel_read_at_exit;
|
179
299
|
ssize_t rv;
|
180
300
|
|
181
|
-
|
301
|
+
if (enable_cancel > 0) {
|
302
|
+
socket_entry_set(&entry, fd);
|
303
|
+
}
|
182
304
|
rv = read(fd, buf, count);
|
183
|
-
|
305
|
+
if (enable_cancel > 0) {
|
306
|
+
socket_entry_clear(&entry);
|
307
|
+
}
|
184
308
|
return rv;
|
185
309
|
}
|
186
310
|
|
311
|
+
static void *ocifunc_addr(void *dlsym_handle, const char **file)
|
312
|
+
{
|
313
|
+
void *addr = dlsym(dlsym_handle, "OCIEnvCreate");
|
314
|
+
Dl_info dli;
|
315
|
+
|
316
|
+
if (addr == NULL) {
|
317
|
+
return NULL;
|
318
|
+
}
|
319
|
+
if (dladdr(addr, &dli) == 0) {
|
320
|
+
return NULL;
|
321
|
+
}
|
322
|
+
if (strstr(dli.dli_fname, "/libclntsh." SO_EXT) == NULL) {
|
323
|
+
return NULL;
|
324
|
+
}
|
325
|
+
*file = dli.dli_fname;
|
326
|
+
return addr;
|
327
|
+
}
|
328
|
+
|
329
|
+
#ifdef __linux__
|
330
|
+
#include <link.h>
|
331
|
+
static void *ocifunc_addr_linux(const char **file)
|
332
|
+
{
|
333
|
+
struct link_map *lm;
|
334
|
+
for (lm = _r_debug.r_map; lm != NULL; lm = lm->l_next) {
|
335
|
+
if (strstr(lm->l_name, "/libclntsh." SO_EXT) != NULL) {
|
336
|
+
*file = lm->l_name;
|
337
|
+
return (void*)lm->l_addr;
|
338
|
+
}
|
339
|
+
}
|
340
|
+
return NULL;
|
341
|
+
}
|
342
|
+
#endif
|
343
|
+
|
187
344
|
void oci8_install_hook_functions(void)
|
188
345
|
{
|
189
|
-
|
346
|
+
static int hook_functions_installed = 0;
|
347
|
+
void *addr;
|
348
|
+
const char *file;
|
349
|
+
|
350
|
+
if (hook_functions_installed) {
|
351
|
+
return;
|
352
|
+
}
|
353
|
+
addr = ocifunc_addr(RTLD_DEFAULT, &file);
|
354
|
+
if (addr == NULL) {
|
355
|
+
/* OCI symbols may be hooked by LD_PRELOAD. */
|
356
|
+
addr = ocifunc_addr(RTLD_NEXT, &file);
|
357
|
+
}
|
358
|
+
#ifdef __linux__
|
359
|
+
if (addr == NULL) {
|
360
|
+
addr = ocifunc_addr_linux(&file);
|
361
|
+
}
|
362
|
+
#endif
|
363
|
+
if (addr == NULL) {
|
190
364
|
rb_raise(rb_eRuntimeError, "No shared library is found to hook.");
|
191
365
|
}
|
366
|
+
if (replace_functions(addr, file, functions) != 0) {
|
367
|
+
rb_raise(rb_eRuntimeError, "Hook error: %s", hook_errmsg);
|
368
|
+
}
|
369
|
+
hook_functions_installed = 1;
|
192
370
|
}
|
193
371
|
|
194
372
|
static void shutdown_socket(socket_entry_t *entry)
|
@@ -197,11 +375,46 @@ static void shutdown_socket(socket_entry_t *entry)
|
|
197
375
|
}
|
198
376
|
#endif
|
199
377
|
|
378
|
+
#ifdef SUPPORT_TCP_KEEPALIVE_TIME
|
379
|
+
static int WSAAPI hook_setsockopt(SOCKET sockfd, int level, int optname, const void *optval, socklen_t optlen)
|
380
|
+
{
|
381
|
+
int rv = setsockopt(sockfd, level, optname, optval, optlen);
|
382
|
+
|
383
|
+
if (rv == 0 && level == SOL_SOCKET && optname == SO_KEEPALIVE
|
384
|
+
&& optlen == sizeof(int) && *(const int*)optval != 0) {
|
385
|
+
/* If Oracle client libraries enables keepalive by (ENABLE=BROKEN),
|
386
|
+
* set per-connection keepalive socket options to overwrite
|
387
|
+
* system-wide setting.
|
388
|
+
*/
|
389
|
+
if (oci8_tcp_keepalive_time > 0) {
|
390
|
+
#if defined(USE_TCP_KEEPALIVE) /* macOS */
|
391
|
+
setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPALIVE, &oci8_tcp_keepalive_time, sizeof(int));
|
392
|
+
#elif defined(USE_TCP_KEEPALIVE_THRESHOLD) /* Solaris */
|
393
|
+
unsigned int millisec = oci8_tcp_keepalive_time * 1000;
|
394
|
+
setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPALIVE_THRESHOLD, &millisec, sizeof(millisec));
|
395
|
+
#elif defined(USE_TCP_KEEPIDLE) /* Linux, etc */
|
396
|
+
setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPIDLE, &oci8_tcp_keepalive_time, sizeof(int));
|
397
|
+
#elif defined(WINDOWS)
|
398
|
+
struct tcp_keepalive vals;
|
399
|
+
DWORD dummy;
|
400
|
+
|
401
|
+
vals.onoff = 1;
|
402
|
+
vals.keepalivetime = oci8_tcp_keepalive_time * 1000;
|
403
|
+
vals.keepaliveinterval = keepalive_interval;
|
404
|
+
WSAIoctl(sockfd, SIO_KEEPALIVE_VALS, &vals, sizeof(vals), NULL, 0,
|
405
|
+
&dummy, NULL, NULL);
|
406
|
+
#endif
|
407
|
+
}
|
408
|
+
}
|
409
|
+
return rv;
|
410
|
+
}
|
411
|
+
#endif
|
412
|
+
|
200
413
|
void oci8_shutdown_sockets(void)
|
201
414
|
{
|
202
415
|
socket_entry_t *entry;
|
203
416
|
|
204
|
-
#ifdef
|
417
|
+
#ifdef WINDOWS
|
205
418
|
if (!locK_is_initialized) {
|
206
419
|
return;
|
207
420
|
}
|