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