1 /*
2 ** Zabbix
3 ** Copyright (C) 2001-2021 Zabbix SIA
4 **
5 ** This program is free software; you can redistribute it and/or modify
6 ** it under the terms of the GNU General Public License as published by
7 ** the Free Software Foundation; either version 2 of the License, or
8 ** (at your option) any later version.
9 **
10 ** This program is distributed in the hope that it will be useful,
11 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
12 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 ** GNU General Public License for more details.
14 **
15 ** You should have received a copy of the GNU General Public License
16 ** along with this program; if not, write to the Free Software
17 ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 **/
19
20 #include "common.h"
21 #include "log.h"
22
23 #ifdef HAVE_UNIXODBC
24
25 #include "zbxodbc.h"
26
27 #define CALLODBC(fun, rc, h_type, h, msg) (SQL_SUCCESS != (rc = (fun)) && 0 == odbc_Diag((h_type), (h), rc, (msg)))
28
29 #define ODBC_ERR_MSG_LEN 255
30
31 static char zbx_last_odbc_strerror[ODBC_ERR_MSG_LEN];
32
get_last_odbc_strerror(void)33 const char *get_last_odbc_strerror(void)
34 {
35 return zbx_last_odbc_strerror;
36 }
37
38 #ifdef HAVE___VA_ARGS__
39 # define set_last_odbc_strerror(fmt, ...) __zbx_set_last_odbc_strerror(ZBX_CONST_STRING(fmt), ##__VA_ARGS__)
40 #else
41 # define set_last_odbc_strerror __zbx_set_last_odbc_strerror
42 #endif
__zbx_set_last_odbc_strerror(const char * fmt,...)43 static void __zbx_set_last_odbc_strerror(const char *fmt, ...)
44 {
45 va_list args;
46
47 va_start(args, fmt);
48
49 zbx_vsnprintf(zbx_last_odbc_strerror, sizeof(zbx_last_odbc_strerror), fmt, args);
50
51 va_end(args);
52 }
53
54 #define clean_odbc_strerror() zbx_last_odbc_strerror[0] = '\0'
55
odbc_free_row_data(ZBX_ODBC_DBH * pdbh)56 static void odbc_free_row_data(ZBX_ODBC_DBH *pdbh)
57 {
58 SQLSMALLINT i;
59
60 if (NULL != pdbh->row_data)
61 {
62 for (i = 0; i < pdbh->col_num; i++)
63 zbx_free(pdbh->row_data[i]);
64
65 zbx_free(pdbh->row_data);
66 }
67
68 pdbh->col_num = 0;
69 }
70
odbc_Diag(SQLSMALLINT h_type,SQLHANDLE h,SQLRETURN sql_rc,const char * msg)71 static int odbc_Diag(SQLSMALLINT h_type, SQLHANDLE h, SQLRETURN sql_rc, const char *msg)
72 {
73 const char *__function_name = "odbc_Diag";
74 SQLCHAR sql_state[SQL_SQLSTATE_SIZE + 1], err_msg[128];
75 SQLINTEGER native_err_code = 0;
76 int rec_nr = 1;
77 char rc_msg[40]; /* the longest message is "%d (unknown SQLRETURN code)" */
78 char diag_msg[ODBC_ERR_MSG_LEN];
79 size_t offset = 0;
80
81 *sql_state = '\0';
82 *err_msg = '\0';
83 *diag_msg = '\0';
84
85 switch (sql_rc)
86 {
87 case SQL_ERROR:
88 zbx_strlcpy(rc_msg, "SQL_ERROR", sizeof(rc_msg));
89 break;
90 case SQL_SUCCESS_WITH_INFO:
91 zbx_strlcpy(rc_msg, "SQL_SUCCESS_WITH_INFO", sizeof(rc_msg));
92 break;
93 case SQL_NO_DATA:
94 zbx_strlcpy(rc_msg, "SQL_NO_DATA", sizeof(rc_msg));
95 break;
96 case SQL_INVALID_HANDLE:
97 zbx_strlcpy(rc_msg, "SQL_INVALID_HANDLE", sizeof(rc_msg));
98 break;
99 case SQL_STILL_EXECUTING:
100 zbx_strlcpy(rc_msg, "SQL_STILL_EXECUTING", sizeof(rc_msg));
101 break;
102 case SQL_NEED_DATA:
103 zbx_strlcpy(rc_msg, "SQL_NEED_DATA", sizeof(rc_msg));
104 break;
105 case SQL_SUCCESS:
106 zbx_strlcpy(rc_msg, "SQL_SUCCESS", sizeof(rc_msg));
107 break;
108 default:
109 zbx_snprintf(rc_msg, sizeof(rc_msg), "%d (unknown SQLRETURN code)", (int)sql_rc);
110 }
111
112 if (SQL_ERROR == sql_rc || SQL_SUCCESS_WITH_INFO == sql_rc)
113 {
114 while (0 != SQL_SUCCEEDED(SQLGetDiagRec(h_type, h, (SQLSMALLINT)rec_nr, sql_state, &native_err_code,
115 err_msg, sizeof(err_msg), NULL)))
116 {
117 zabbix_log(LOG_LEVEL_DEBUG, "%s(): rc_msg:'%s' rec_nr:%d sql_state:'%s' native_err_code:%ld "
118 "err_msg:'%s'", __function_name, rc_msg, rec_nr, sql_state,
119 (long)native_err_code, err_msg);
120 if (sizeof(diag_msg) > offset)
121 {
122 offset += zbx_snprintf(diag_msg + offset, sizeof(diag_msg) - offset, "[%s][%ld][%s]|",
123 sql_state, (long)native_err_code, err_msg);
124 }
125 rec_nr++;
126 }
127 *(diag_msg + offset) = '\0';
128 }
129 set_last_odbc_strerror("%s:[%s]:%s", msg, rc_msg, diag_msg);
130
131 return (int)SQL_SUCCEEDED(sql_rc);
132 }
133
odbc_DBclose(ZBX_ODBC_DBH * pdbh)134 void odbc_DBclose(ZBX_ODBC_DBH *pdbh)
135 {
136 if (NULL == pdbh)
137 return;
138
139 if (NULL != pdbh->hstmt)
140 {
141 SQLFreeHandle(SQL_HANDLE_STMT, pdbh->hstmt);
142 pdbh->hstmt = NULL;
143 }
144
145 if (NULL != pdbh->hdbc)
146 {
147 if (pdbh->connected)
148 SQLDisconnect(pdbh->hdbc);
149
150 SQLFreeHandle(SQL_HANDLE_DBC, pdbh->hdbc);
151 pdbh->hdbc = NULL;
152 }
153
154 if (NULL != pdbh->henv)
155 {
156 SQLFreeHandle(SQL_HANDLE_ENV, pdbh->henv);
157 pdbh->henv = NULL;
158 }
159
160 odbc_free_row_data(pdbh);
161 }
162
odbc_DBconnect(ZBX_ODBC_DBH * pdbh,char * db_dsn,char * user,char * pass,int login_timeout)163 int odbc_DBconnect(ZBX_ODBC_DBH *pdbh, char *db_dsn, char *user, char *pass, int login_timeout)
164 {
165 const char *__function_name = "odbc_DBconnect";
166 int ret = FAIL;
167 SQLRETURN rc;
168
169 zabbix_log(LOG_LEVEL_DEBUG, "In %s() db_dsn:'%s' user:'%s'", __function_name, db_dsn, user);
170
171 clean_odbc_strerror();
172
173 memset(pdbh, 0, sizeof(ZBX_ODBC_DBH));
174
175 /* allocate environment handle */
176 if (0 == SQL_SUCCEEDED(SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &(pdbh->henv))))
177 {
178 set_last_odbc_strerror("%s", "Cannot create ODBC environment handle.");
179 goto end;
180 }
181
182 /* set the ODBC version environment attribute */
183 if (0 != CALLODBC(SQLSetEnvAttr(pdbh->henv, SQL_ATTR_ODBC_VERSION, (void*)SQL_OV_ODBC3, 0), rc, SQL_HANDLE_ENV,
184 pdbh->henv, "Cannot set ODBC version"))
185 {
186 goto end;
187 }
188
189 /* allocate connection handle */
190 if (0 != CALLODBC(SQLAllocHandle(SQL_HANDLE_DBC, pdbh->henv, &(pdbh->hdbc)), rc, SQL_HANDLE_ENV, pdbh->henv,
191 "Cannot create ODBC connection handle"))
192 {
193 goto end;
194 }
195
196 /* set login timeout */
197 if (0 != CALLODBC(SQLSetConnectAttr(pdbh->hdbc, (SQLINTEGER)SQL_LOGIN_TIMEOUT,
198 (SQLPOINTER)(intptr_t)login_timeout, (SQLINTEGER)0), rc, SQL_HANDLE_DBC, pdbh->hdbc,
199 "Cannot set ODBC login timeout"))
200 {
201 goto end;
202 }
203
204 /* connect to data source */
205 if (0 != CALLODBC(SQLConnect(pdbh->hdbc, (SQLCHAR *)db_dsn, SQL_NTS, (SQLCHAR *)user, SQL_NTS, (SQLCHAR *)pass,
206 SQL_NTS), rc, SQL_HANDLE_DBC, pdbh->hdbc, "Cannot connect to ODBC DSN"))
207 {
208 goto end;
209 }
210
211 /* allocate statement handle */
212 if (0 != CALLODBC(SQLAllocHandle(SQL_HANDLE_STMT, pdbh->hdbc, &(pdbh->hstmt)), rc, SQL_HANDLE_DBC, pdbh->hdbc,
213 "Cannot create ODBC statement handle."))
214 {
215 goto end;
216 }
217
218 if (SUCCEED == zabbix_check_log_level(LOG_LEVEL_DEBUG))
219 {
220 char driver_name[MAX_STRING_LEN + 1], driver_ver[MAX_STRING_LEN + 1], db_name[MAX_STRING_LEN + 1],
221 db_ver[MAX_STRING_LEN + 1];
222
223 if (0 != CALLODBC(SQLGetInfo(pdbh->hdbc, SQL_DRIVER_NAME, driver_name, MAX_STRING_LEN, NULL),
224 rc, SQL_HANDLE_DBC, pdbh->hdbc, "Cannot obtain driver name"))
225 {
226 zbx_strlcpy(driver_name, "unknown", sizeof(driver_name));
227 }
228
229 if (0 != CALLODBC(SQLGetInfo(pdbh->hdbc, SQL_DRIVER_VER, driver_ver, MAX_STRING_LEN, NULL),
230 rc, SQL_HANDLE_DBC, pdbh->hdbc, "Cannot obtain driver version"))
231 {
232 zbx_strlcpy(driver_ver, "unknown", sizeof(driver_ver));
233 }
234
235 if (0 != CALLODBC(SQLGetInfo(pdbh->hdbc, SQL_DBMS_NAME, db_name, MAX_STRING_LEN, NULL),
236 rc, SQL_HANDLE_DBC, pdbh->hdbc, "Cannot obtain database name"))
237 {
238 zbx_strlcpy(db_name, "unknown", sizeof(db_name));
239 }
240
241 if (0 != CALLODBC(SQLGetInfo(pdbh->hdbc, SQL_DBMS_VER, db_ver, MAX_STRING_LEN, NULL),
242 rc, SQL_HANDLE_DBC, pdbh->hdbc, "Cannot obtain database version"))
243 {
244 zbx_strlcpy(db_ver, "unknown", sizeof(db_ver));
245 }
246
247 zabbix_log(LOG_LEVEL_DEBUG, "%s() connected to %s(%s) using %s(%s)", __function_name,
248 db_name, db_ver, driver_name, driver_ver);
249 }
250
251 pdbh->connected = 1;
252
253 ret = SUCCEED;
254 end:
255 if (SUCCEED != ret)
256 odbc_DBclose(pdbh);
257
258 zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __function_name, zbx_result_string(ret));
259
260 return ret;
261 }
262
odbc_DBfetch(ZBX_ODBC_RESULT pdbh)263 ZBX_ODBC_ROW odbc_DBfetch(ZBX_ODBC_RESULT pdbh)
264 {
265 const char *__function_name = "odbc_DBfetch";
266 SQLRETURN retcode;
267 SQLSMALLINT i;
268 ZBX_ODBC_ROW result_row = NULL;
269
270 zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __function_name);
271
272 clean_odbc_strerror();
273
274 if (NULL == pdbh)
275 {
276 set_last_odbc_strerror("cannot fetch row on an empty connection handle");
277 goto end;
278 }
279
280 if (SQL_NO_DATA == (retcode = SQLFetch(pdbh->hstmt)))
281 {
282 /* end of rows */
283 goto end;
284 }
285
286 if (SQL_SUCCESS != retcode && 0 == odbc_Diag(SQL_HANDLE_STMT, pdbh->hstmt, retcode, "cannot fetch row"))
287 goto end;
288
289 for (i = 0; i < pdbh->col_num; i++)
290 {
291 size_t alloc = 0, offset = 0;
292 char buffer[MAX_STRING_LEN + 1];
293 SQLLEN len;
294
295 zbx_free(pdbh->row_data[i]);
296
297 /* force len to integer value for DB2 compatibility */
298 do
299 {
300 retcode = SQLGetData(pdbh->hstmt, i + 1, SQL_C_CHAR, buffer, MAX_STRING_LEN, &len);
301
302 if (0 == SQL_SUCCEEDED(retcode))
303 {
304 odbc_Diag(SQL_HANDLE_STMT, pdbh->hstmt, retcode, "Cannot get column data");
305 goto end;
306 }
307
308 if (SQL_NULL_DATA == (int)len)
309 break;
310
311 zbx_strcpy_alloc(&pdbh->row_data[i], &alloc, &offset, buffer);
312 }
313 while (SQL_SUCCESS != retcode);
314
315 if (NULL != pdbh->row_data[i])
316 zbx_rtrim(pdbh->row_data[i], " ");
317
318 zabbix_log(LOG_LEVEL_DEBUG, "%s() fetched [%i col]: '%s'", __function_name, i,
319 NULL == pdbh->row_data[i] ? "NULL" : pdbh->row_data[i]);
320 }
321
322 result_row = pdbh->row_data;
323 end:
324 zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __function_name);
325
326 return result_row;
327 }
328
odbc_DBselect(ZBX_ODBC_DBH * pdbh,char * query)329 ZBX_ODBC_RESULT odbc_DBselect(ZBX_ODBC_DBH *pdbh, char *query)
330 {
331 const char *__function_name = "odbc_DBselect";
332 ZBX_ODBC_RESULT result = NULL;
333 SQLRETURN rc;
334
335 zabbix_log(LOG_LEVEL_DEBUG, "In %s() query:'%s'", __function_name, query);
336
337 clean_odbc_strerror();
338
339 odbc_free_row_data(pdbh);
340
341 if (0 != CALLODBC(SQLExecDirect(pdbh->hstmt, (SQLCHAR *)query, SQL_NTS), rc, SQL_HANDLE_STMT, pdbh->hstmt,
342 "Cannot execute ODBC query"))
343 {
344 goto end;
345 }
346
347 if (0 != CALLODBC(SQLNumResultCols(pdbh->hstmt, &pdbh->col_num), rc, SQL_HANDLE_STMT, pdbh->hstmt,
348 "Cannot get number of columns in ODBC result"))
349 {
350 goto end;
351 }
352
353 pdbh->row_data = zbx_malloc(pdbh->row_data, sizeof(char *) * (size_t)pdbh->col_num);
354 memset(pdbh->row_data, 0, sizeof(char *) * (size_t)pdbh->col_num);
355
356 zabbix_log(LOG_LEVEL_DEBUG, "%s() selected %i columns", __function_name, pdbh->col_num);
357
358 result = (ZBX_ODBC_RESULT)pdbh;
359 end:
360 zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __function_name);
361
362 return result;
363 }
364
365 #endif /* HAVE_UNIXODBC */
366