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 "db.h"
21 #include "log.h"
22 #include "common.h"
23 #include "events.h"
24 #include "threads.h"
25 #include "zbxserver.h"
26 #include "dbcache.h"
27 #include "zbxalgo.h"
28 #include "cfg.h"
29 
30 #if defined(HAVE_POSTGRESQL)
31 #	define ZBX_SUPPORTED_DB_CHARACTER_SET	"utf8"
32 #elif defined(HAVE_ORACLE)
33 #	define ZBX_ORACLE_UTF8_CHARSET "AL32UTF8"
34 #	define ZBX_ORACLE_CESU8_CHARSET "UTF8"
35 #elif defined(HAVE_MYSQL)
36 #	define ZBX_DB_STRLIST_DELIM		','
37 #	define ZBX_SUPPORTED_DB_CHARACTER_SET	"utf8,utf8mb3"
38 #	define ZBX_SUPPORTED_DB_COLLATION	"utf8_bin,utf8mb3_bin"
39 #endif
40 
41 typedef struct
42 {
43 	zbx_uint64_t	autoreg_hostid;
44 	zbx_uint64_t	hostid;
45 	char		*host;
46 	char		*ip;
47 	char		*dns;
48 	char		*host_metadata;
49 	int		now;
50 	unsigned short	port;
51 	unsigned short	flag;
52 	unsigned int	connection_type;
53 }
54 zbx_autoreg_host_t;
55 
56 #if defined(HAVE_POSTGRESQL)
57 extern char	ZBX_PG_ESCAPE_BACKSLASH;
58 #endif
59 
60 static int	connection_failure;
61 extern unsigned char	program_type;
62 
DBclose(void)63 void	DBclose(void)
64 {
65 	zbx_db_close();
66 }
67 
zbx_db_validate_config_features(void)68 int	zbx_db_validate_config_features(void)
69 {
70 	int	err = 0;
71 
72 #if !(defined(HAVE_MYSQL_TLS) || defined(HAVE_MARIADB_TLS) || defined(HAVE_POSTGRESQL))
73 	err |= (FAIL == check_cfg_feature_str("DBTLSConnect", CONFIG_DB_TLS_CONNECT, "PostgreSQL or MySQL library"
74 			" version that support TLS"));
75 	err |= (FAIL == check_cfg_feature_str("DBTLSCAFile", CONFIG_DB_TLS_CA_FILE,"PostgreSQL or MySQL library"
76 			" version that support TLS"));
77 	err |= (FAIL == check_cfg_feature_str("DBTLSCertFile", CONFIG_DB_TLS_CERT_FILE, "PostgreSQL or MySQL library"
78 			" version that support TLS"));
79 	err |= (FAIL == check_cfg_feature_str("DBTLSKeyFile", CONFIG_DB_TLS_KEY_FILE, "PostgreSQL or MySQL library"
80 			" version that support TLS"));
81 #endif
82 
83 #if !(defined(HAVE_MYSQL_TLS) || defined(HAVE_POSTGRESQL))
84 	if (NULL != CONFIG_DB_TLS_CONNECT && 0 == strcmp(CONFIG_DB_TLS_CONNECT, ZBX_DB_TLS_CONNECT_VERIFY_CA_TXT))
85 	{
86 		zbx_error("\"DBTLSConnect\" configuration parameter value '%s' cannot be used: Zabbix %s was compiled"
87 			" without PostgreSQL or MySQL library version that support this value",
88 			ZBX_DB_TLS_CONNECT_VERIFY_CA_TXT, get_program_type_string(program_type));
89 		err |= 1;
90 	}
91 #endif
92 
93 #if !(defined(HAVE_MYSQL_TLS) || defined(HAVE_MARIADB_TLS))
94 	err |= (FAIL == check_cfg_feature_str("DBTLSCipher", CONFIG_DB_TLS_CIPHER, "MySQL library version that support"
95 			" configuration of cipher"));
96 #endif
97 
98 #if !defined(HAVE_MYSQL_TLS_CIPHERSUITES)
99 	err |= (FAIL == check_cfg_feature_str("DBTLSCipher13", CONFIG_DB_TLS_CIPHER_13, "MySQL library version that"
100 			" support configuration of TLSv1.3 ciphersuites"));
101 #endif
102 
103 	return 0 != err ? FAIL : SUCCEED;
104 }
105 
106 #if defined(HAVE_MYSQL) || defined(HAVE_POSTGRESQL)
check_cfg_empty_str(const char * parameter,const char * value)107 static void	check_cfg_empty_str(const char *parameter, const char *value)
108 {
109 	if (NULL != value && 0 == strlen(value))
110 	{
111 		zabbix_log(LOG_LEVEL_CRIT, "configuration parameter \"%s\" is defined but empty", parameter);
112 		exit(EXIT_FAILURE);
113 	}
114 }
115 
zbx_db_validate_config(void)116 void	zbx_db_validate_config(void)
117 {
118 	check_cfg_empty_str("DBTLSConnect", CONFIG_DB_TLS_CONNECT);
119 	check_cfg_empty_str("DBTLSCertFile", CONFIG_DB_TLS_CERT_FILE);
120 	check_cfg_empty_str("DBTLSKeyFile", CONFIG_DB_TLS_KEY_FILE);
121 	check_cfg_empty_str("DBTLSCAFile", CONFIG_DB_TLS_CA_FILE);
122 	check_cfg_empty_str("DBTLSCipher", CONFIG_DB_TLS_CIPHER);
123 	check_cfg_empty_str("DBTLSCipher13", CONFIG_DB_TLS_CIPHER_13);
124 
125 	if (NULL != CONFIG_DB_TLS_CONNECT &&
126 			0 != strcmp(CONFIG_DB_TLS_CONNECT, ZBX_DB_TLS_CONNECT_REQUIRED_TXT) &&
127 			0 != strcmp(CONFIG_DB_TLS_CONNECT, ZBX_DB_TLS_CONNECT_VERIFY_CA_TXT) &&
128 			0 != strcmp(CONFIG_DB_TLS_CONNECT, ZBX_DB_TLS_CONNECT_VERIFY_FULL_TXT))
129 	{
130 		zabbix_log(LOG_LEVEL_CRIT, "invalid \"DBTLSConnect\" configuration parameter: '%s'",
131 				CONFIG_DB_TLS_CONNECT);
132 		exit(EXIT_FAILURE);
133 	}
134 
135 	if (NULL != CONFIG_DB_TLS_CONNECT &&
136 			(0 == strcmp(ZBX_DB_TLS_CONNECT_VERIFY_CA_TXT, CONFIG_DB_TLS_CONNECT) ||
137 			0 == strcmp(ZBX_DB_TLS_CONNECT_VERIFY_FULL_TXT, CONFIG_DB_TLS_CONNECT)) &&
138 			NULL == CONFIG_DB_TLS_CA_FILE)
139 	{
140 		zabbix_log(LOG_LEVEL_CRIT, "parameter \"DBTLSConnect\" value \"%s\" requires \"DBTLSCAFile\", but it"
141 				" is not defined", CONFIG_DB_TLS_CONNECT);
142 		exit(EXIT_FAILURE);
143 	}
144 
145 	if ((NULL != CONFIG_DB_TLS_CERT_FILE || NULL != CONFIG_DB_TLS_KEY_FILE) &&
146 			(NULL == CONFIG_DB_TLS_CERT_FILE || NULL == CONFIG_DB_TLS_KEY_FILE ||
147 			NULL == CONFIG_DB_TLS_CA_FILE))
148 	{
149 		zabbix_log(LOG_LEVEL_CRIT, "parameter \"DBTLSKeyFile\" or \"DBTLSCertFile\" is defined, but"
150 				" \"DBTLSKeyFile\", \"DBTLSCertFile\" or \"DBTLSCAFile\" is not defined");
151 		exit(EXIT_FAILURE);
152 	}
153 }
154 #endif
155 
156 /******************************************************************************
157  *                                                                            *
158  * Function: DBinit_autoincrement_options                                     *
159  *                                                                            *
160  * Purpose: specify the autoincrement options when connecting to the database *
161  *                                                                            *
162  ******************************************************************************/
DBinit_autoincrement_options(void)163 void	DBinit_autoincrement_options(void)
164 {
165 	zbx_db_init_autoincrement_options();
166 }
167 
168 /******************************************************************************
169  *                                                                            *
170  * Function: DBconnect                                                        *
171  *                                                                            *
172  * Purpose: connect to the database                                           *
173  *                                                                            *
174  * Parameters: flag - ZBX_DB_CONNECT_ONCE (try once and return the result),   *
175  *                    ZBX_DB_CONNECT_EXIT (exit on failure) or                *
176  *                    ZBX_DB_CONNECT_NORMAL (retry until connected)           *
177  *                                                                            *
178  * Return value: same as zbx_db_connect()                                     *
179  *                                                                            *
180  ******************************************************************************/
DBconnect(int flag)181 int	DBconnect(int flag)
182 {
183 	int	err;
184 
185 	zabbix_log(LOG_LEVEL_DEBUG, "In %s() flag:%d", __func__, flag);
186 
187 	while (ZBX_DB_OK != (err = zbx_db_connect(CONFIG_DBHOST, CONFIG_DBUSER, CONFIG_DBPASSWORD,
188 			CONFIG_DBNAME, CONFIG_DBSCHEMA, CONFIG_DBSOCKET, CONFIG_DBPORT, CONFIG_DB_TLS_CONNECT,
189 			CONFIG_DB_TLS_CERT_FILE, CONFIG_DB_TLS_KEY_FILE, CONFIG_DB_TLS_CA_FILE, CONFIG_DB_TLS_CIPHER,
190 			CONFIG_DB_TLS_CIPHER_13)))
191 	{
192 		if (ZBX_DB_CONNECT_ONCE == flag)
193 			break;
194 
195 		if (ZBX_DB_FAIL == err || ZBX_DB_CONNECT_EXIT == flag)
196 		{
197 			zabbix_log(LOG_LEVEL_CRIT, "Cannot connect to the database. Exiting...");
198 			exit(EXIT_FAILURE);
199 		}
200 
201 		zabbix_log(LOG_LEVEL_ERR, "database is down: reconnecting in %d seconds", ZBX_DB_WAIT_DOWN);
202 		connection_failure = 1;
203 		zbx_sleep(ZBX_DB_WAIT_DOWN);
204 	}
205 
206 	if (0 != connection_failure)
207 	{
208 		zabbix_log(LOG_LEVEL_ERR, "database connection re-established");
209 		connection_failure = 0;
210 	}
211 
212 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%d", __func__, err);
213 
214 	return err;
215 }
216 
217 /******************************************************************************
218  *                                                                            *
219  * Function: DBinit                                                           *
220  *                                                                            *
221  * Author: Alexander Vladishev                                                *
222  *                                                                            *
223  ******************************************************************************/
DBinit(char ** error)224 int	DBinit(char **error)
225 {
226 	return zbx_db_init(CONFIG_DBNAME, db_schema, error);
227 }
228 
DBdeinit(void)229 void	DBdeinit(void)
230 {
231 	zbx_db_deinit();
232 }
233 
234 /******************************************************************************
235  *                                                                            *
236  * Function: DBtxn_operation                                                  *
237  *                                                                            *
238  * Purpose: helper function to loop transaction operation while DB is down    *
239  *                                                                            *
240  * Author: Eugene Grigorjev, Vladimir Levijev                                 *
241  *                                                                            *
242  ******************************************************************************/
DBtxn_operation(int (* txn_operation)(void))243 static void	DBtxn_operation(int (*txn_operation)(void))
244 {
245 	int	rc;
246 
247 	rc = txn_operation();
248 
249 	while (ZBX_DB_DOWN == rc)
250 	{
251 		DBclose();
252 		DBconnect(ZBX_DB_CONNECT_NORMAL);
253 
254 		if (ZBX_DB_DOWN == (rc = txn_operation()))
255 		{
256 			zabbix_log(LOG_LEVEL_ERR, "database is down: retrying in %d seconds", ZBX_DB_WAIT_DOWN);
257 			connection_failure = 1;
258 			sleep(ZBX_DB_WAIT_DOWN);
259 		}
260 	}
261 }
262 
263 /******************************************************************************
264  *                                                                            *
265  * Function: DBbegin                                                          *
266  *                                                                            *
267  * Purpose: start a transaction                                               *
268  *                                                                            *
269  * Author: Eugene Grigorjev, Vladimir Levijev                                 *
270  *                                                                            *
271  * Comments: do nothing if DB does not support transactions                   *
272  *                                                                            *
273  ******************************************************************************/
DBbegin(void)274 void	DBbegin(void)
275 {
276 	DBtxn_operation(zbx_db_begin);
277 }
278 
279 /******************************************************************************
280  *                                                                            *
281  * Function: DBcommit                                                         *
282  *                                                                            *
283  * Purpose: commit a transaction                                              *
284  *                                                                            *
285  * Author: Eugene Grigorjev, Vladimir Levijev                                 *
286  *                                                                            *
287  * Comments: do nothing if DB does not support transactions                   *
288  *                                                                            *
289  ******************************************************************************/
DBcommit(void)290 int	DBcommit(void)
291 {
292 	if (ZBX_DB_OK > zbx_db_commit())
293 	{
294 		zabbix_log(LOG_LEVEL_DEBUG, "commit called on failed transaction, doing a rollback instead");
295 		DBrollback();
296 	}
297 
298 	return zbx_db_txn_end_error();
299 }
300 
301 /******************************************************************************
302  *                                                                            *
303  * Function: DBrollback                                                       *
304  *                                                                            *
305  * Purpose: rollback a transaction                                            *
306  *                                                                            *
307  * Author: Eugene Grigorjev, Vladimir Levijev                                 *
308  *                                                                            *
309  * Comments: do nothing if DB does not support transactions                   *
310  *                                                                            *
311  ******************************************************************************/
DBrollback(void)312 void	DBrollback(void)
313 {
314 	if (ZBX_DB_OK > zbx_db_rollback())
315 	{
316 		zabbix_log(LOG_LEVEL_WARNING, "cannot perform transaction rollback, connection will be reset");
317 
318 		DBclose();
319 		DBconnect(ZBX_DB_CONNECT_NORMAL);
320 	}
321 }
322 
323 /******************************************************************************
324  *                                                                            *
325  * Function: DBend                                                            *
326  *                                                                            *
327  * Purpose: commit or rollback a transaction depending on a parameter value   *
328  *                                                                            *
329  * Comments: do nothing if DB does not support transactions                   *
330  *                                                                            *
331  ******************************************************************************/
DBend(int ret)332 int	DBend(int ret)
333 {
334 	if (SUCCEED == ret)
335 		return ZBX_DB_OK == DBcommit() ? SUCCEED : FAIL;
336 
337 	DBrollback();
338 
339 	return FAIL;
340 }
341 
342 #ifdef HAVE_ORACLE
343 /******************************************************************************
344  *                                                                            *
345  * Function: DBstatement_prepare                                              *
346  *                                                                            *
347  * Purpose: prepares a SQL statement for execution                            *
348  *                                                                            *
349  * Comments: retry until DB is up                                             *
350  *                                                                            *
351  ******************************************************************************/
DBstatement_prepare(const char * sql)352 void	DBstatement_prepare(const char *sql)
353 {
354 	int	rc;
355 
356 	rc = zbx_db_statement_prepare(sql);
357 
358 	while (ZBX_DB_DOWN == rc)
359 	{
360 		DBclose();
361 		DBconnect(ZBX_DB_CONNECT_NORMAL);
362 
363 		if (ZBX_DB_DOWN == (rc = zbx_db_statement_prepare(sql)))
364 		{
365 			zabbix_log(LOG_LEVEL_ERR, "database is down: retrying in %d seconds", ZBX_DB_WAIT_DOWN);
366 			connection_failure = 1;
367 			sleep(ZBX_DB_WAIT_DOWN);
368 		}
369 	}
370 }
371 #endif
372 
373 /******************************************************************************
374  *                                                                            *
375  * Function: __zbx_DBexecute                                                  *
376  *                                                                            *
377  * Purpose: execute a non-select statement                                    *
378  *                                                                            *
379  * Comments: retry until DB is up                                             *
380  *                                                                            *
381  ******************************************************************************/
DBexecute(const char * fmt,...)382 int	DBexecute(const char *fmt, ...)
383 {
384 	va_list	args;
385 	int	rc;
386 
387 	va_start(args, fmt);
388 
389 	rc = zbx_db_vexecute(fmt, args);
390 
391 	while (ZBX_DB_DOWN == rc)
392 	{
393 		DBclose();
394 		DBconnect(ZBX_DB_CONNECT_NORMAL);
395 
396 		if (ZBX_DB_DOWN == (rc = zbx_db_vexecute(fmt, args)))
397 		{
398 			zabbix_log(LOG_LEVEL_ERR, "database is down: retrying in %d seconds", ZBX_DB_WAIT_DOWN);
399 			connection_failure = 1;
400 			sleep(ZBX_DB_WAIT_DOWN);
401 		}
402 	}
403 
404 	va_end(args);
405 
406 	return rc;
407 }
408 
409 /******************************************************************************
410  *                                                                            *
411  * Function: __zbx_DBexecute_once                                             *
412  *                                                                            *
413  * Purpose: execute a non-select statement                                    *
414  *                                                                            *
415  * Comments: don't retry if DB is down                                        *
416  *                                                                            *
417  ******************************************************************************/
DBexecute_once(const char * fmt,...)418 int	DBexecute_once(const char *fmt, ...)
419 {
420 	va_list	args;
421 	int	rc;
422 
423 	va_start(args, fmt);
424 
425 	rc = zbx_db_vexecute(fmt, args);
426 
427 	va_end(args);
428 
429 	return rc;
430 }
431 
432 /******************************************************************************
433  *                                                                            *
434  * Function: DBis_null                                                        *
435  *                                                                            *
436  * Purpose: check if numeric field value is null                              *
437  *                                                                            *
438  * Parameters: field - [IN] field value to be checked                         *
439  *                                                                            *
440  * Return value: SUCCEED - field value is null                                *
441  *               FAIL    - otherwise                                          *
442  *                                                                            *
443  * Comments: ATTENTION! This function should only be used with numeric fields *
444  *           since on Oracle empty string is returned instead of NULL and it  *
445  *           is not possible to differentiate empty string from NULL string   *
446  *                                                                            *
447  ******************************************************************************/
DBis_null(const char * field)448 int	DBis_null(const char *field)
449 {
450 	return zbx_db_is_null(field);
451 }
452 
DBfetch(DB_RESULT result)453 DB_ROW	DBfetch(DB_RESULT result)
454 {
455 	return zbx_db_fetch(result);
456 }
457 
458 /******************************************************************************
459  *                                                                            *
460  * Function: DBselect_once                                                    *
461  *                                                                            *
462  * Purpose: execute a select statement                                        *
463  *                                                                            *
464  ******************************************************************************/
DBselect_once(const char * fmt,...)465 DB_RESULT	DBselect_once(const char *fmt, ...)
466 {
467 	va_list		args;
468 	DB_RESULT	rc;
469 
470 	va_start(args, fmt);
471 
472 	rc = zbx_db_vselect(fmt, args);
473 
474 	va_end(args);
475 
476 	return rc;
477 }
478 
479 /******************************************************************************
480  *                                                                            *
481  * Function: DBselect                                                         *
482  *                                                                            *
483  * Purpose: execute a select statement                                        *
484  *                                                                            *
485  * Comments: retry until DB is up                                             *
486  *                                                                            *
487  ******************************************************************************/
DBselect(const char * fmt,...)488 DB_RESULT	DBselect(const char *fmt, ...)
489 {
490 	va_list		args;
491 	DB_RESULT	rc;
492 
493 	va_start(args, fmt);
494 
495 	rc = zbx_db_vselect(fmt, args);
496 
497 	while ((DB_RESULT)ZBX_DB_DOWN == rc)
498 	{
499 		DBclose();
500 		DBconnect(ZBX_DB_CONNECT_NORMAL);
501 
502 		if ((DB_RESULT)ZBX_DB_DOWN == (rc = zbx_db_vselect(fmt, args)))
503 		{
504 			zabbix_log(LOG_LEVEL_ERR, "database is down: retrying in %d seconds", ZBX_DB_WAIT_DOWN);
505 			connection_failure = 1;
506 			sleep(ZBX_DB_WAIT_DOWN);
507 		}
508 	}
509 
510 	va_end(args);
511 
512 	return rc;
513 }
514 
515 /******************************************************************************
516  *                                                                            *
517  * Function: DBselectN                                                        *
518  *                                                                            *
519  * Purpose: execute a select statement and get the first N entries            *
520  *                                                                            *
521  * Comments: retry until DB is up                                             *
522  *                                                                            *
523  ******************************************************************************/
DBselectN(const char * query,int n)524 DB_RESULT	DBselectN(const char *query, int n)
525 {
526 	DB_RESULT	rc;
527 
528 	rc = zbx_db_select_n(query, n);
529 
530 	while ((DB_RESULT)ZBX_DB_DOWN == rc)
531 	{
532 		DBclose();
533 		DBconnect(ZBX_DB_CONNECT_NORMAL);
534 
535 		if ((DB_RESULT)ZBX_DB_DOWN == (rc = zbx_db_select_n(query, n)))
536 		{
537 			zabbix_log(LOG_LEVEL_ERR, "database is down: retrying in %d seconds", ZBX_DB_WAIT_DOWN);
538 			connection_failure = 1;
539 			sleep(ZBX_DB_WAIT_DOWN);
540 		}
541 	}
542 
543 	return rc;
544 }
545 
DBget_row_count(const char * table_name)546 int	DBget_row_count(const char *table_name)
547 {
548 	int		count = 0;
549 	DB_RESULT	result;
550 	DB_ROW		row;
551 
552 	zabbix_log(LOG_LEVEL_DEBUG, "In %s() table_name:'%s'", __func__, table_name);
553 
554 	result = DBselect("select count(*) from %s", table_name);
555 
556 	if (NULL != (row = DBfetch(result)))
557 		count = atoi(row[0]);
558 	DBfree_result(result);
559 
560 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%d", __func__, count);
561 
562 	return count;
563 }
564 
DBget_proxy_lastaccess(const char * hostname,int * lastaccess,char ** error)565 int	DBget_proxy_lastaccess(const char *hostname, int *lastaccess, char **error)
566 {
567 	DB_RESULT	result;
568 	DB_ROW		row;
569 	char		*host_esc;
570 	int		ret = FAIL;
571 
572 	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
573 
574 	host_esc = DBdyn_escape_string(hostname);
575 	result = DBselect("select lastaccess from hosts where host='%s' and status in (%d,%d)",
576 			host_esc, HOST_STATUS_PROXY_ACTIVE, HOST_STATUS_PROXY_PASSIVE);
577 	zbx_free(host_esc);
578 
579 	if (NULL != (row = DBfetch(result)))
580 	{
581 		*lastaccess = atoi(row[0]);
582 		ret = SUCCEED;
583 	}
584 	else
585 		*error = zbx_dsprintf(*error, "Proxy \"%s\" does not exist.", hostname);
586 	DBfree_result(result);
587 
588 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret));
589 
590 	return ret;
591 }
592 
593 #ifdef HAVE_MYSQL
get_string_field_size(unsigned char type)594 static size_t	get_string_field_size(unsigned char type)
595 {
596 	switch(type)
597 	{
598 		case ZBX_TYPE_LONGTEXT:
599 			return ZBX_SIZE_T_MAX;
600 		case ZBX_TYPE_CHAR:
601 		case ZBX_TYPE_TEXT:
602 		case ZBX_TYPE_SHORTTEXT:
603 			return 65535u;
604 		default:
605 			THIS_SHOULD_NEVER_HAPPEN;
606 			exit(EXIT_FAILURE);
607 	}
608 }
609 #elif defined(HAVE_ORACLE)
get_string_field_size(unsigned char type)610 static size_t	get_string_field_size(unsigned char type)
611 {
612 	switch(type)
613 	{
614 		case ZBX_TYPE_LONGTEXT:
615 		case ZBX_TYPE_TEXT:
616 			return ZBX_SIZE_T_MAX;
617 		case ZBX_TYPE_CHAR:
618 		case ZBX_TYPE_SHORTTEXT:
619 			return 4000u;
620 		default:
621 			THIS_SHOULD_NEVER_HAPPEN;
622 			exit(EXIT_FAILURE);
623 	}
624 }
625 #endif
626 
627 /******************************************************************************
628  *                                                                            *
629  * Function: DBdyn_escape_string_len                                          *
630  *                                                                            *
631  ******************************************************************************/
DBdyn_escape_string_len(const char * src,size_t length)632 char	*DBdyn_escape_string_len(const char *src, size_t length)
633 {
634 	return zbx_db_dyn_escape_string(src, ZBX_SIZE_T_MAX, length, ESCAPE_SEQUENCE_ON);
635 }
636 
637 /******************************************************************************
638  *                                                                            *
639  * Function: DBdyn_escape_string                                              *
640  *                                                                            *
641  ******************************************************************************/
DBdyn_escape_string(const char * src)642 char	*DBdyn_escape_string(const char *src)
643 {
644 	return zbx_db_dyn_escape_string(src, ZBX_SIZE_T_MAX, ZBX_SIZE_T_MAX, ESCAPE_SEQUENCE_ON);
645 }
646 
647 /******************************************************************************
648  *                                                                            *
649  * Function: DBdyn_escape_field_len                                           *
650  *                                                                            *
651  ******************************************************************************/
DBdyn_escape_field_len(const ZBX_FIELD * field,const char * src,zbx_escape_sequence_t flag)652 static char	*DBdyn_escape_field_len(const ZBX_FIELD *field, const char *src, zbx_escape_sequence_t flag)
653 {
654 	size_t	length;
655 
656 	if (ZBX_TYPE_LONGTEXT == field->type && 0 == field->length)
657 		length = ZBX_SIZE_T_MAX;
658 	else
659 		length = field->length;
660 
661 #if defined(HAVE_MYSQL) || defined(HAVE_ORACLE)
662 	return zbx_db_dyn_escape_string(src, get_string_field_size(field->type), length, flag);
663 #else
664 	return zbx_db_dyn_escape_string(src, ZBX_SIZE_T_MAX, length, flag);
665 #endif
666 }
667 
668 /******************************************************************************
669  *                                                                            *
670  * Function: DBdyn_escape_field                                               *
671  *                                                                            *
672  ******************************************************************************/
DBdyn_escape_field(const char * table_name,const char * field_name,const char * src)673 char	*DBdyn_escape_field(const char *table_name, const char *field_name, const char *src)
674 {
675 	const ZBX_TABLE	*table;
676 	const ZBX_FIELD	*field;
677 
678 	if (NULL == (table = DBget_table(table_name)) || NULL == (field = DBget_field(table, field_name)))
679 	{
680 		zabbix_log(LOG_LEVEL_CRIT, "invalid table: \"%s\" field: \"%s\"", table_name, field_name);
681 		exit(EXIT_FAILURE);
682 	}
683 
684 	return DBdyn_escape_field_len(field, src, ESCAPE_SEQUENCE_ON);
685 }
686 
687 /******************************************************************************
688  *                                                                            *
689  * Function: DBdyn_escape_like_pattern                                        *
690  *                                                                            *
691  ******************************************************************************/
DBdyn_escape_like_pattern(const char * src)692 char	*DBdyn_escape_like_pattern(const char *src)
693 {
694 	return zbx_db_dyn_escape_like_pattern(src);
695 }
696 
DBget_table(const char * tablename)697 const ZBX_TABLE	*DBget_table(const char *tablename)
698 {
699 	int	t;
700 
701 	for (t = 0; NULL != tables[t].table; t++)
702 	{
703 		if (0 == strcmp(tables[t].table, tablename))
704 			return &tables[t];
705 	}
706 
707 	return NULL;
708 }
709 
DBget_field(const ZBX_TABLE * table,const char * fieldname)710 const ZBX_FIELD	*DBget_field(const ZBX_TABLE *table, const char *fieldname)
711 {
712 	int	f;
713 
714 	for (f = 0; NULL != table->fields[f].name; f++)
715 	{
716 		if (0 == strcmp(table->fields[f].name, fieldname))
717 			return &table->fields[f];
718 	}
719 
720 	return NULL;
721 }
722 
723 /******************************************************************************
724  *                                                                            *
725  * Function: DBget_nextid                                                     *
726  *                                                                            *
727  * Purpose: gets a new identifier(s) for a specified table                    *
728  *                                                                            *
729  * Parameters: tablename - [IN] the name of a table                           *
730  *             num       - [IN] the number of reserved records                *
731  *                                                                            *
732  * Return value: first reserved identifier                                    *
733  *                                                                            *
734  ******************************************************************************/
DBget_nextid(const char * tablename,int num)735 static zbx_uint64_t	DBget_nextid(const char *tablename, int num)
736 {
737 	DB_RESULT	result;
738 	DB_ROW		row;
739 	zbx_uint64_t	ret1, ret2;
740 	zbx_uint64_t	min = 0, max = ZBX_DB_MAX_ID;
741 	int		found = FAIL, dbres;
742 	const ZBX_TABLE	*table;
743 
744 	zabbix_log(LOG_LEVEL_DEBUG, "In %s() tablename:'%s'", __func__, tablename);
745 
746 	table = DBget_table(tablename);
747 
748 	while (FAIL == found)
749 	{
750 		/* avoid eternal loop within failed transaction */
751 		if (0 < zbx_db_txn_level() && 0 != zbx_db_txn_error())
752 		{
753 			zabbix_log(LOG_LEVEL_DEBUG, "End of %s() transaction failed", __func__);
754 			return 0;
755 		}
756 
757 		result = DBselect("select nextid from ids where table_name='%s' and field_name='%s'",
758 				table->table, table->recid);
759 
760 		if (NULL == (row = DBfetch(result)))
761 		{
762 			DBfree_result(result);
763 
764 			result = DBselect("select max(%s) from %s where %s between " ZBX_FS_UI64 " and " ZBX_FS_UI64,
765 					table->recid, table->table, table->recid, min, max);
766 
767 			if (NULL == (row = DBfetch(result)) || SUCCEED == DBis_null(row[0]))
768 			{
769 				ret1 = min;
770 			}
771 			else
772 			{
773 				ZBX_STR2UINT64(ret1, row[0]);
774 				if (ret1 >= max)
775 				{
776 					zabbix_log(LOG_LEVEL_CRIT, "maximum number of id's exceeded"
777 							" [table:%s, field:%s, id:" ZBX_FS_UI64 "]",
778 							table->table, table->recid, ret1);
779 					exit(EXIT_FAILURE);
780 				}
781 			}
782 
783 			DBfree_result(result);
784 
785 			dbres = DBexecute("insert into ids (table_name,field_name,nextid)"
786 					" values ('%s','%s'," ZBX_FS_UI64 ")",
787 					table->table, table->recid, ret1);
788 
789 			if (ZBX_DB_OK > dbres)
790 			{
791 				/* solving the problem of an invisible record created in a parallel transaction */
792 				DBexecute("update ids set nextid=nextid+1 where table_name='%s' and field_name='%s'",
793 						table->table, table->recid);
794 			}
795 
796 			continue;
797 		}
798 		else
799 		{
800 			ZBX_STR2UINT64(ret1, row[0]);
801 			DBfree_result(result);
802 
803 			if (ret1 < min || ret1 >= max)
804 			{
805 				DBexecute("delete from ids where table_name='%s' and field_name='%s'",
806 						table->table, table->recid);
807 				continue;
808 			}
809 
810 			DBexecute("update ids set nextid=nextid+%d where table_name='%s' and field_name='%s'",
811 					num, table->table, table->recid);
812 
813 			result = DBselect("select nextid from ids where table_name='%s' and field_name='%s'",
814 					table->table, table->recid);
815 
816 			if (NULL != (row = DBfetch(result)) && SUCCEED != DBis_null(row[0]))
817 			{
818 				ZBX_STR2UINT64(ret2, row[0]);
819 
820 				if (ret1 + num == ret2)
821 					found = SUCCEED;
822 			}
823 			else
824 				THIS_SHOULD_NEVER_HAPPEN;
825 
826 			DBfree_result(result);
827 		}
828 	}
829 
830 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s():" ZBX_FS_UI64 " table:'%s' recid:'%s'",
831 			__func__, ret2 - num + 1, table->table, table->recid);
832 
833 	return ret2 - num + 1;
834 }
835 
DBget_maxid_num(const char * tablename,int num)836 zbx_uint64_t	DBget_maxid_num(const char *tablename, int num)
837 {
838 	if (0 == strcmp(tablename, "events") ||
839 			0 == strcmp(tablename, "event_tag") ||
840 			0 == strcmp(tablename, "problem_tag") ||
841 			0 == strcmp(tablename, "dservices") ||
842 			0 == strcmp(tablename, "dhosts") ||
843 			0 == strcmp(tablename, "alerts") ||
844 			0 == strcmp(tablename, "escalations") ||
845 			0 == strcmp(tablename, "autoreg_host") ||
846 			0 == strcmp(tablename, "event_suppress") ||
847 			0 == strcmp(tablename, "trigger_queue"))
848 		return DCget_nextid(tablename, num);
849 
850 	return DBget_nextid(tablename, num);
851 }
852 
853 /******************************************************************************
854  *                                                                            *
855  * Function: DBextract_version                                                *
856  *                                                                            *
857  * Purpose: connects to DB and tries to detect DB version                     *
858  *                                                                            *
859  ******************************************************************************/
DBextract_version(struct zbx_json * json)860 zbx_uint32_t	DBextract_version(struct zbx_json *json)
861 {
862 	zbx_uint32_t	ret;
863 
864 	DBconnect(ZBX_DB_CONNECT_NORMAL);
865 	ret = zbx_dbms_version_extract(json);
866 	DBclose();
867 
868 	return ret;
869 }
870 
871 /******************************************************************************
872  *                                                                            *
873  * Function: DBflush_version_requirements                                     *
874  *                                                                            *
875  * Purpose: writes a json entry in DB with the result for the front-end       *
876  *                                                                            *
877  * Parameters: version - [IN] entry of DB versions                            *
878  *                                                                            *
879  ******************************************************************************/
DBflush_version_requirements(const char * version)880 void	DBflush_version_requirements(const char *version)
881 {
882 	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
883 
884 	DBconnect(ZBX_DB_CONNECT_NORMAL);
885 
886 	if (ZBX_DB_OK > DBexecute("update config set dbversion_status='%s'", version))
887 		zabbix_log(LOG_LEVEL_CRIT, "Failed to set dbversion_status");
888 
889 	DBclose();
890 
891 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__);
892 }
893 
894 /******************************************************************************
895  *                                                                            *
896  * Function: DBcheck_capabilities                                             *
897  *                                                                            *
898  * Purpose: checks DBMS for optional features and exit if is not suitable     *
899  *                                                                            *
900  * Parameters: db_version - [IN] version of DB                                *
901  *                                                                            *
902  * Return value: SUCCEED - if optional features were checked successfully     *
903  *               FAIL    - otherwise                                          *
904  *                                                                            *
905  ******************************************************************************/
DBcheck_capabilities(zbx_uint32_t db_version)906 int	DBcheck_capabilities(zbx_uint32_t db_version)
907 {
908 	int	ret = SUCCEED;
909 #ifdef HAVE_POSTGRESQL
910 
911 #define MIN_POSTGRESQL_VERSION_WITH_TIMESCALEDB	100002
912 #define MIN_TIMESCALEDB_VERSION			10500
913 	int		timescaledb_version;
914 	DB_RESULT	result;
915 	DB_ROW		row;
916 
917 	DBconnect(ZBX_DB_CONNECT_NORMAL);
918 
919 	if (FAIL == DBfield_exists("config", "db_extension"))
920 		goto out;
921 
922 	if (NULL == (result = DBselect("select db_extension from config")))
923 		goto out;
924 
925 	if (NULL == (row = DBfetch(result)))
926 		goto clean;
927 
928 	if (0 != zbx_strcmp_null(row[0], ZBX_CONFIG_DB_EXTENSION_TIMESCALE))
929 		goto clean;
930 
931 	ret = FAIL;	/* In case of major upgrade, db_extension may be missing */
932 
933 	/* Timescale compression feature is available in PostgreSQL 10.2 and TimescaleDB 1.5.0 */
934 	if (MIN_POSTGRESQL_VERSION_WITH_TIMESCALEDB > db_version)
935 	{
936 		zabbix_log(LOG_LEVEL_CRIT, "PostgreSQL version %lu is not supported with TimescaleDB, minimum is %d",
937 				(unsigned long)db_version, MIN_POSTGRESQL_VERSION_WITH_TIMESCALEDB);
938 		goto clean;
939 	}
940 
941 	if (0 == (timescaledb_version = zbx_tsdb_get_version()))
942 	{
943 		zabbix_log(LOG_LEVEL_CRIT, "Cannot determine TimescaleDB version");
944 		goto clean;
945 	}
946 
947 	zabbix_log(LOG_LEVEL_INFORMATION, "TimescaleDB version: %d", timescaledb_version);
948 
949 	if (MIN_TIMESCALEDB_VERSION > timescaledb_version)
950 	{
951 		zabbix_log(LOG_LEVEL_CRIT, "TimescaleDB version %d is not supported, minimum is %d",
952 				timescaledb_version, MIN_TIMESCALEDB_VERSION);
953 		goto clean;
954 	}
955 
956 	ret = SUCCEED;
957 clean:
958 	DBfree_result(result);
959 out:
960 	DBclose();
961 #else
962 	ZBX_UNUSED(db_version);
963 #endif
964 	return ret;
965 }
966 
967 #define MAX_EXPRESSIONS	950
968 
969 #ifdef HAVE_ORACLE
970 #define MIN_NUM_BETWEEN	5	/* minimum number of consecutive values for using "between <id1> and <idN>" */
971 
972 /******************************************************************************
973  *                                                                            *
974  * Function: DBadd_condition_alloc_btw                                        *
975  *                                                                            *
976  * Purpose: Takes an initial part of SQL query and appends a generated        *
977  *          WHERE condition. The WHERE condition is generated from the given  *
978  *          list of values as a mix of <fieldname> BETWEEN <id1> AND <idN>"   *
979  *                                                                            *
980  * Parameters: sql        - [IN/OUT] buffer for SQL query construction        *
981  *             sql_alloc  - [IN/OUT] size of the 'sql' buffer                 *
982  *             sql_offset - [IN/OUT] current position in the 'sql' buffer     *
983  *             fieldname  - [IN] field name to be used in SQL WHERE condition *
984  *             values     - [IN] array of numerical values sorted in          *
985  *                               ascending order to be included in WHERE      *
986  *             num        - [IN] number of elements in 'values' array         *
987  *             seq_len    - [OUT] - array of sequential chains                *
988  *             seq_num    - [OUT] - length of seq_len                         *
989  *             in_num     - [OUT] - number of id for 'IN'                     *
990  *             between_num- [OUT] - number of sequential chains for 'BETWEEN' *
991  *                                                                            *
992  ******************************************************************************/
DBadd_condition_alloc_btw(char ** sql,size_t * sql_alloc,size_t * sql_offset,const char * fieldname,const zbx_uint64_t * values,const int num,int ** seq_len,int * seq_num,int * in_num,int * between_num)993 static void	DBadd_condition_alloc_btw(char **sql, size_t *sql_alloc, size_t *sql_offset, const char *fieldname,
994 		const zbx_uint64_t *values, const int num, int **seq_len, int *seq_num, int *in_num, int *between_num)
995 {
996 	int		i, len, first, start;
997 	zbx_uint64_t	value;
998 
999 	/* Store lengths of consecutive sequences of values in a temporary array 'seq_len'. */
1000 	/* An isolated value is represented as a sequence with length 1. */
1001 	*seq_len = (int *)zbx_malloc(*seq_len, num * sizeof(int));
1002 
1003 	for (i = 1, *seq_num = 0, value = values[0], len = 1; i < num; i++)
1004 	{
1005 		if (values[i] != ++value)
1006 		{
1007 			if (MIN_NUM_BETWEEN <= len)
1008 				(*between_num)++;
1009 			else
1010 				*in_num += len;
1011 
1012 			(*seq_len)[(*seq_num)++] = len;
1013 			len = 1;
1014 			value = values[i];
1015 		}
1016 		else
1017 			len++;
1018 	}
1019 
1020 	if (MIN_NUM_BETWEEN <= len)
1021 		(*between_num)++;
1022 	else
1023 		*in_num += len;
1024 
1025 	(*seq_len)[(*seq_num)++] = len;
1026 
1027 	if (MAX_EXPRESSIONS < *in_num || 1 < *between_num || (0 < *in_num && 0 < *between_num))
1028 		zbx_chrcpy_alloc(sql, sql_alloc, sql_offset, '(');
1029 
1030 	/* compose "between"s */
1031 	for (i = 0, first = 1, start = 0; i < *seq_num; i++)
1032 	{
1033 		if (MIN_NUM_BETWEEN <= (*seq_len)[i])
1034 		{
1035 			if (1 != first)
1036 			{
1037 					zbx_strcpy_alloc(sql, sql_alloc, sql_offset, " or ");
1038 			}
1039 			else
1040 				first = 0;
1041 
1042 			zbx_snprintf_alloc(sql, sql_alloc, sql_offset, "%s between " ZBX_FS_UI64 " and " ZBX_FS_UI64,
1043 					fieldname, values[start], values[start + (*seq_len)[i] - 1]);
1044 		}
1045 
1046 		start += (*seq_len)[i];
1047 	}
1048 
1049 	if (0 < *in_num && 0 < *between_num)
1050 	{
1051 		zbx_strcpy_alloc(sql, sql_alloc, sql_offset, " or ");
1052 	}
1053 }
1054 #endif
1055 
1056 /******************************************************************************
1057  *                                                                            *
1058  * Function: DBadd_condition_alloc                                            *
1059  *                                                                            *
1060  * Purpose: Takes an initial part of SQL query and appends a generated        *
1061  *          WHERE condition. The WHERE condition is generated from the given  *
1062  *          list of values as a mix of <fieldname> BETWEEN <id1> AND <idN>"   *
1063  *          and "<fieldname> IN (<id1>,<id2>,...,<idN>)" elements.            *
1064  *                                                                            *
1065  * Parameters: sql        - [IN/OUT] buffer for SQL query construction        *
1066  *             sql_alloc  - [IN/OUT] size of the 'sql' buffer                 *
1067  *             sql_offset - [IN/OUT] current position in the 'sql' buffer     *
1068  *             fieldname  - [IN] field name to be used in SQL WHERE condition *
1069  *             values     - [IN] array of numerical values sorted in          *
1070  *                               ascending order to be included in WHERE      *
1071  *             num        - [IN] number of elements in 'values' array         *
1072  *                                                                            *
1073  ******************************************************************************/
DBadd_condition_alloc(char ** sql,size_t * sql_alloc,size_t * sql_offset,const char * fieldname,const zbx_uint64_t * values,const int num)1074 void	DBadd_condition_alloc(char **sql, size_t *sql_alloc, size_t *sql_offset, const char *fieldname,
1075 		const zbx_uint64_t *values, const int num)
1076 {
1077 #ifdef HAVE_ORACLE
1078 	int		start, between_num = 0, in_num = 0, seq_num;
1079 	int		*seq_len = NULL;
1080 #endif
1081 	int		i, in_cnt;
1082 #if defined(HAVE_SQLITE3)
1083 	int		expr_num, expr_cnt = 0;
1084 #endif
1085 	if (0 == num)
1086 		return;
1087 
1088 	zbx_chrcpy_alloc(sql, sql_alloc, sql_offset, ' ');
1089 #ifdef HAVE_ORACLE
1090 	DBadd_condition_alloc_btw(sql, sql_alloc, sql_offset, fieldname, values, num, &seq_len, &seq_num, &in_num,
1091 			&between_num);
1092 
1093 	if (1 < in_num)
1094 		zbx_snprintf_alloc(sql, sql_alloc, sql_offset, "%s in (", fieldname);
1095 
1096 	/* compose "in"s */
1097 	for (i = 0, in_cnt = 0, start = 0; i < seq_num; i++)
1098 	{
1099 		if (MIN_NUM_BETWEEN > seq_len[i])
1100 		{
1101 			if (1 == in_num)
1102 #else
1103 	if (MAX_EXPRESSIONS < num)
1104 		zbx_chrcpy_alloc(sql, sql_alloc, sql_offset, '(');
1105 
1106 #if	defined(HAVE_SQLITE3)
1107 	expr_num = (num + MAX_EXPRESSIONS - 1) / MAX_EXPRESSIONS;
1108 
1109 	if (MAX_EXPRESSIONS < expr_num)
1110 		zbx_chrcpy_alloc(sql, sql_alloc, sql_offset, '(');
1111 #endif
1112 
1113 	if (1 < num)
1114 		zbx_snprintf_alloc(sql, sql_alloc, sql_offset, "%s in (", fieldname);
1115 
1116 	/* compose "in"s */
1117 	for (i = 0, in_cnt = 0; i < num; i++)
1118 	{
1119 			if (1 == num)
1120 #endif
1121 			{
1122 				zbx_snprintf_alloc(sql, sql_alloc, sql_offset, "%s=" ZBX_FS_UI64, fieldname,
1123 #ifdef HAVE_ORACLE
1124 						values[start]);
1125 #else
1126 						values[i]);
1127 #endif
1128 				break;
1129 			}
1130 			else
1131 			{
1132 #ifdef HAVE_ORACLE
1133 				do
1134 				{
1135 #endif
1136 					if (MAX_EXPRESSIONS == in_cnt)
1137 					{
1138 						in_cnt = 0;
1139 						(*sql_offset)--;
1140 #if defined(HAVE_SQLITE3)
1141 						if (MAX_EXPRESSIONS == ++expr_cnt)
1142 						{
1143 							zbx_snprintf_alloc(sql, sql_alloc, sql_offset, ")) or (%s in (",
1144 									fieldname);
1145 							expr_cnt = 0;
1146 						}
1147 						else
1148 						{
1149 #endif
1150 							zbx_snprintf_alloc(sql, sql_alloc, sql_offset, ") or %s in (",
1151 									fieldname);
1152 #if defined(HAVE_SQLITE3)
1153 						}
1154 #endif
1155 					}
1156 
1157 					in_cnt++;
1158 					zbx_snprintf_alloc(sql, sql_alloc, sql_offset, ZBX_FS_UI64 ",",
1159 #ifdef HAVE_ORACLE
1160 							values[start++]);
1161 				}
1162 				while (0 != --seq_len[i]);
1163 			}
1164 		}
1165 		else
1166 			start += seq_len[i];
1167 	}
1168 
1169 	zbx_free(seq_len);
1170 
1171 	if (1 < in_num)
1172 #else
1173 							values[i]);
1174 			}
1175 	}
1176 
1177 	if (1 < num)
1178 #endif
1179 	{
1180 		(*sql_offset)--;
1181 		zbx_chrcpy_alloc(sql, sql_alloc, sql_offset, ')');
1182 	}
1183 
1184 #if defined(HAVE_SQLITE3)
1185 	if (MAX_EXPRESSIONS < expr_num)
1186 		zbx_chrcpy_alloc(sql, sql_alloc, sql_offset, ')');
1187 #endif
1188 #ifdef HAVE_ORACLE
1189 	if (MAX_EXPRESSIONS < in_num || 1 < between_num || (0 < in_num && 0 < between_num))
1190 #else
1191 	if (MAX_EXPRESSIONS < num)
1192 #endif
1193 		zbx_chrcpy_alloc(sql, sql_alloc, sql_offset, ')');
1194 
1195 #undef MAX_EXPRESSIONS
1196 #ifdef HAVE_ORACLE
1197 #undef MIN_NUM_BETWEEN
1198 #endif
1199 }
1200 
1201 /******************************************************************************
1202  *                                                                            *
1203  * Function: DBadd_str_condition_alloc                                        *
1204  *                                                                            *
1205  * Purpose: This function is similar to DBadd_condition_alloc(), except it is *
1206  *          designed for generating WHERE conditions for strings. Hence, this *
1207  *          function is simpler, because only IN condition is possible.       *
1208  *                                                                            *
1209  * Parameters: sql        - [IN/OUT] buffer for SQL query construction        *
1210  *             sql_alloc  - [IN/OUT] size of the 'sql' buffer                 *
1211  *             sql_offset - [IN/OUT] current position in the 'sql' buffer     *
1212  *             fieldname  - [IN] field name to be used in SQL WHERE condition *
1213  *             values     - [IN] array of string values                       *
1214  *             num        - [IN] number of elements in 'values' array         *
1215  *                                                                            *
1216  * Comments: To support Oracle empty values are checked separately (is null   *
1217  *           for Oracle and ='' for the other databases).                     *
1218  *                                                                            *
1219  ******************************************************************************/
DBadd_str_condition_alloc(char ** sql,size_t * sql_alloc,size_t * sql_offset,const char * fieldname,const char ** values,const int num)1220 void	DBadd_str_condition_alloc(char **sql, size_t *sql_alloc, size_t *sql_offset, const char *fieldname,
1221 		const char **values, const int num)
1222 {
1223 #define MAX_EXPRESSIONS	950
1224 
1225 	int	i, cnt = 0;
1226 	char	*value_esc;
1227 	int	values_num = 0, empty_num = 0;
1228 
1229 	if (0 == num)
1230 		return;
1231 
1232 	zbx_chrcpy_alloc(sql, sql_alloc, sql_offset, ' ');
1233 
1234 	for (i = 0; i < num; i++)
1235 	{
1236 		if ('\0' == *values[i])
1237 			empty_num++;
1238 		else
1239 			values_num++;
1240 	}
1241 
1242 	if (MAX_EXPRESSIONS < values_num || (0 != values_num && 0 != empty_num))
1243 		zbx_chrcpy_alloc(sql, sql_alloc, sql_offset, '(');
1244 
1245 	if (0 != empty_num)
1246 	{
1247 		zbx_snprintf_alloc(sql, sql_alloc, sql_offset, "%s" ZBX_SQL_STRCMP, fieldname, ZBX_SQL_STRVAL_EQ(""));
1248 
1249 		if (0 == values_num)
1250 			return;
1251 
1252 		zbx_strcpy_alloc(sql, sql_alloc, sql_offset, " or ");
1253 	}
1254 
1255 	if (1 == values_num)
1256 	{
1257 		for (i = 0; i < num; i++)
1258 		{
1259 			if ('\0' == *values[i])
1260 				continue;
1261 
1262 			value_esc = DBdyn_escape_string(values[i]);
1263 			zbx_snprintf_alloc(sql, sql_alloc, sql_offset, "%s='%s'", fieldname, value_esc);
1264 			zbx_free(value_esc);
1265 		}
1266 
1267 		if (0 != empty_num)
1268 			zbx_chrcpy_alloc(sql, sql_alloc, sql_offset, ')');
1269 		return;
1270 	}
1271 
1272 	zbx_strcpy_alloc(sql, sql_alloc, sql_offset, fieldname);
1273 	zbx_strcpy_alloc(sql, sql_alloc, sql_offset, " in (");
1274 
1275 	for (i = 0; i < num; i++)
1276 	{
1277 		if ('\0' == *values[i])
1278 			continue;
1279 
1280 		if (MAX_EXPRESSIONS == cnt)
1281 		{
1282 			cnt = 0;
1283 			(*sql_offset)--;
1284 			zbx_strcpy_alloc(sql, sql_alloc, sql_offset, ") or ");
1285 			zbx_strcpy_alloc(sql, sql_alloc, sql_offset, fieldname);
1286 			zbx_strcpy_alloc(sql, sql_alloc, sql_offset, " in (");
1287 		}
1288 
1289 		value_esc = DBdyn_escape_string(values[i]);
1290 		zbx_chrcpy_alloc(sql, sql_alloc, sql_offset, '\'');
1291 		zbx_strcpy_alloc(sql, sql_alloc, sql_offset, value_esc);
1292 		zbx_strcpy_alloc(sql, sql_alloc, sql_offset, "',");
1293 		zbx_free(value_esc);
1294 
1295 		cnt++;
1296 	}
1297 
1298 	(*sql_offset)--;
1299 	zbx_chrcpy_alloc(sql, sql_alloc, sql_offset, ')');
1300 
1301 	if (MAX_EXPRESSIONS < values_num || 0 != empty_num)
1302 		zbx_chrcpy_alloc(sql, sql_alloc, sql_offset, ')');
1303 
1304 #undef MAX_EXPRESSIONS
1305 }
1306 
1307 static char	buf_string[640];
1308 
1309 /******************************************************************************
1310  *                                                                            *
1311  * Function: zbx_host_string                                                  *
1312  *                                                                            *
1313  * Return value: <host> or "???" if host not found                            *
1314  *                                                                            *
1315  * Author: Alexander Vladishev                                                *
1316  *                                                                            *
1317  ******************************************************************************/
zbx_host_string(zbx_uint64_t hostid)1318 const char	*zbx_host_string(zbx_uint64_t hostid)
1319 {
1320 	DB_RESULT	result;
1321 	DB_ROW		row;
1322 
1323 	result = DBselect(
1324 			"select host"
1325 			" from hosts"
1326 			" where hostid=" ZBX_FS_UI64,
1327 			hostid);
1328 
1329 	if (NULL != (row = DBfetch(result)))
1330 		zbx_snprintf(buf_string, sizeof(buf_string), "%s", row[0]);
1331 	else
1332 		zbx_snprintf(buf_string, sizeof(buf_string), "???");
1333 
1334 	DBfree_result(result);
1335 
1336 	return buf_string;
1337 }
1338 
1339 /******************************************************************************
1340  *                                                                            *
1341  * Function: zbx_host_key_string                                              *
1342  *                                                                            *
1343  * Return value: <host>:<key> or "???" if item not found                      *
1344  *                                                                            *
1345  * Author: Alexander Vladishev                                                *
1346  *                                                                            *
1347  ******************************************************************************/
zbx_host_key_string(zbx_uint64_t itemid)1348 const char	*zbx_host_key_string(zbx_uint64_t itemid)
1349 {
1350 	DB_RESULT	result;
1351 	DB_ROW		row;
1352 
1353 	result = DBselect(
1354 			"select h.host,i.key_"
1355 			" from hosts h,items i"
1356 			" where h.hostid=i.hostid"
1357 				" and i.itemid=" ZBX_FS_UI64,
1358 			itemid);
1359 
1360 	if (NULL != (row = DBfetch(result)))
1361 		zbx_snprintf(buf_string, sizeof(buf_string), "%s:%s", row[0], row[1]);
1362 	else
1363 		zbx_snprintf(buf_string, sizeof(buf_string), "???");
1364 
1365 	DBfree_result(result);
1366 
1367 	return buf_string;
1368 }
1369 
1370 /******************************************************************************
1371  *                                                                            *
1372  * Function: zbx_check_user_permissions                                       *
1373  *                                                                            *
1374  * Purpose: check if user has access rights to information - full name,       *
1375  *          alias, Email, SMS, etc                                            *
1376  *                                                                            *
1377  * Parameters: userid           - [IN] user who owns the information          *
1378  *             recipient_userid - [IN] user who will receive the information  *
1379  *                                     can be NULL for remote command         *
1380  *                                                                            *
1381  * Return value: SUCCEED - if information receiving user has access rights    *
1382  *               FAIL    - otherwise                                          *
1383  *                                                                            *
1384  * Comments: Users has access rights or can view personal information only    *
1385  *           about themselves and other user who belong to their group.       *
1386  *           "Zabbix Super Admin" can view and has access rights to           *
1387  *           information about any user.                                      *
1388  *                                                                            *
1389  ******************************************************************************/
zbx_check_user_permissions(const zbx_uint64_t * userid,const zbx_uint64_t * recipient_userid)1390 int	zbx_check_user_permissions(const zbx_uint64_t *userid, const zbx_uint64_t *recipient_userid)
1391 {
1392 	DB_RESULT	result;
1393 	DB_ROW		row;
1394 	int		user_type = -1, ret = SUCCEED;
1395 
1396 	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
1397 
1398 	if (NULL == recipient_userid || *userid == *recipient_userid)
1399 		goto out;
1400 
1401 	result = DBselect("select r.type from users u,role r where u.roleid=r.roleid and"
1402 			" userid=" ZBX_FS_UI64, *recipient_userid);
1403 
1404 	if (NULL != (row = DBfetch(result)) && FAIL == DBis_null(row[0]))
1405 		user_type = atoi(row[0]);
1406 	DBfree_result(result);
1407 
1408 	if (-1 == user_type)
1409 	{
1410 		zabbix_log(LOG_LEVEL_DEBUG, "%s() cannot check permissions", __func__);
1411 		ret = FAIL;
1412 		goto out;
1413 	}
1414 
1415 	if (USER_TYPE_SUPER_ADMIN != user_type)
1416 	{
1417 		/* check if users are from the same user group */
1418 		result = DBselect(
1419 				"select null"
1420 				" from users_groups ug1"
1421 				" where ug1.userid=" ZBX_FS_UI64
1422 					" and exists (select null"
1423 						" from users_groups ug2"
1424 						" where ug1.usrgrpid=ug2.usrgrpid"
1425 							" and ug2.userid=" ZBX_FS_UI64
1426 					")",
1427 				*userid, *recipient_userid);
1428 
1429 		if (NULL == DBfetch(result))
1430 			ret = FAIL;
1431 		DBfree_result(result);
1432 	}
1433 out:
1434 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret));
1435 
1436 	return ret;
1437 }
1438 
1439 /******************************************************************************
1440  *                                                                            *
1441  * Function: zbx_user_string                                                  *
1442  *                                                                            *
1443  * Return value: "Name Surname (Alias)" or "unknown" if user not found        *
1444  *                                                                            *
1445  * Author: Alexander Vladishev                                                *
1446  *                                                                            *
1447  ******************************************************************************/
zbx_user_string(zbx_uint64_t userid)1448 const char	*zbx_user_string(zbx_uint64_t userid)
1449 {
1450 	DB_RESULT	result;
1451 	DB_ROW		row;
1452 
1453 	result = DBselect("select name,surname,username from users where userid=" ZBX_FS_UI64, userid);
1454 
1455 	if (NULL != (row = DBfetch(result)))
1456 		zbx_snprintf(buf_string, sizeof(buf_string), "%s %s (%s)", row[0], row[1], row[2]);
1457 	else
1458 		zbx_snprintf(buf_string, sizeof(buf_string), "unknown");
1459 
1460 	DBfree_result(result);
1461 
1462 	return buf_string;
1463 }
1464 
1465 /******************************************************************************
1466  *                                                                            *
1467  * Function: DBget_user_names                                                 *
1468  *                                                                            *
1469  * Purpose: get user username, name and surname                               *
1470  *                                                                            *
1471  * Parameters: userid     - [IN] user id                                      *
1472  *             username   - [OUT] user alias                                  *
1473  *             name       - [OUT] user name                                   *
1474  *             surname    - [OUT] user surname                                *
1475  *                                                                            *
1476  * Return value: SUCCEED or FAIL                                              *
1477  *                                                                            *
1478  ******************************************************************************/
DBget_user_names(zbx_uint64_t userid,char ** username,char ** name,char ** surname)1479 int	DBget_user_names(zbx_uint64_t userid, char **username, char **name, char **surname)
1480 {
1481 	int		ret = FAIL;
1482 	DB_RESULT	result;
1483 	DB_ROW		row;
1484 
1485 	if (NULL == (result = DBselect(
1486 			"select username,name,surname"
1487 			" from users"
1488 			" where userid=" ZBX_FS_UI64, userid)))
1489 	{
1490 		goto out;
1491 	}
1492 
1493 	if (NULL == (row = DBfetch(result)))
1494 		goto out;
1495 
1496 	*username = zbx_strdup(NULL, row[0]);
1497 	*name = zbx_strdup(NULL, row[1]);
1498 	*surname = zbx_strdup(NULL, row[2]);
1499 
1500 	ret = SUCCEED;
1501 out:
1502 	DBfree_result(result);
1503 
1504 	return ret;
1505 }
1506 
1507 /******************************************************************************
1508  *                                                                            *
1509  * Function: DBsql_id_cmp                                                     *
1510  *                                                                            *
1511  * Purpose: construct where condition                                         *
1512  *                                                                            *
1513  * Return value: "=<id>" if id not equal zero,                                *
1514  *               otherwise " is null"                                         *
1515  *                                                                            *
1516  * Author: Alexander Vladishev                                                *
1517  *                                                                            *
1518  * Comments: NB! Do not use this function more than once in same SQL query    *
1519  *                                                                            *
1520  ******************************************************************************/
DBsql_id_cmp(zbx_uint64_t id)1521 const char	*DBsql_id_cmp(zbx_uint64_t id)
1522 {
1523 	static char		buf[22];	/* 1 - '=', 20 - value size, 1 - '\0' */
1524 	static const char	is_null[9] = " is null";
1525 
1526 	if (0 == id)
1527 		return is_null;
1528 
1529 	zbx_snprintf(buf, sizeof(buf), "=" ZBX_FS_UI64, id);
1530 
1531 	return buf;
1532 }
1533 
1534 /******************************************************************************
1535  *                                                                            *
1536  * Function: DBregister_host                                                  *
1537  *                                                                            *
1538  * Purpose: register unknown host and generate event                          *
1539  *                                                                            *
1540  * Parameters: host - host name                                               *
1541  *                                                                            *
1542  * Author: Alexander Vladishev                                                *
1543  *                                                                            *
1544  ******************************************************************************/
DBregister_host(zbx_uint64_t proxy_hostid,const char * host,const char * ip,const char * dns,unsigned short port,unsigned int connection_type,const char * host_metadata,unsigned short flag,int now)1545 void	DBregister_host(zbx_uint64_t proxy_hostid, const char *host, const char *ip, const char *dns,
1546 		unsigned short port, unsigned int connection_type, const char *host_metadata, unsigned short flag,
1547 		int now)
1548 {
1549 	zbx_vector_ptr_t	autoreg_hosts;
1550 
1551 	zbx_vector_ptr_create(&autoreg_hosts);
1552 
1553 	DBregister_host_prepare(&autoreg_hosts, host, ip, dns, port, connection_type, host_metadata, flag, now);
1554 	DBregister_host_flush(&autoreg_hosts, proxy_hostid);
1555 
1556 	DBregister_host_clean(&autoreg_hosts);
1557 	zbx_vector_ptr_destroy(&autoreg_hosts);
1558 }
1559 
DBregister_host_active(void)1560 static int	DBregister_host_active(void)
1561 {
1562 	DB_RESULT	result;
1563 	int		ret = SUCCEED;
1564 
1565 	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
1566 
1567 	result = DBselect(
1568 			"select null"
1569 			" from actions"
1570 			" where eventsource=%d"
1571 				" and status=%d",
1572 			EVENT_SOURCE_AUTOREGISTRATION,
1573 			ACTION_STATUS_ACTIVE);
1574 
1575 	if (NULL == DBfetch(result))
1576 		ret = FAIL;
1577 
1578 	DBfree_result(result);
1579 
1580 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret));
1581 
1582 	return ret;
1583 }
1584 
autoreg_host_free(zbx_autoreg_host_t * autoreg_host)1585 static void	autoreg_host_free(zbx_autoreg_host_t *autoreg_host)
1586 {
1587 	zbx_free(autoreg_host->host);
1588 	zbx_free(autoreg_host->ip);
1589 	zbx_free(autoreg_host->dns);
1590 	zbx_free(autoreg_host->host_metadata);
1591 	zbx_free(autoreg_host);
1592 }
1593 
DBregister_host_prepare(zbx_vector_ptr_t * autoreg_hosts,const char * host,const char * ip,const char * dns,unsigned short port,unsigned int connection_type,const char * host_metadata,unsigned short flag,int now)1594 void	DBregister_host_prepare(zbx_vector_ptr_t *autoreg_hosts, const char *host, const char *ip, const char *dns,
1595 		unsigned short port, unsigned int connection_type, const char *host_metadata, unsigned short flag,
1596 		int now)
1597 {
1598 	zbx_autoreg_host_t	*autoreg_host;
1599 	int 			i;
1600 
1601 	for (i = 0; i < autoreg_hosts->values_num; i++)	/* duplicate check */
1602 	{
1603 		autoreg_host = (zbx_autoreg_host_t *)autoreg_hosts->values[i];
1604 
1605 		if (0 == strcmp(host, autoreg_host->host))
1606 		{
1607 			zbx_vector_ptr_remove(autoreg_hosts, i);
1608 			autoreg_host_free(autoreg_host);
1609 			break;
1610 		}
1611 	}
1612 
1613 	autoreg_host = (zbx_autoreg_host_t *)zbx_malloc(NULL, sizeof(zbx_autoreg_host_t));
1614 	autoreg_host->autoreg_hostid = autoreg_host->hostid = 0;
1615 	autoreg_host->host = zbx_strdup(NULL, host);
1616 	autoreg_host->ip = zbx_strdup(NULL, ip);
1617 	autoreg_host->dns = zbx_strdup(NULL, dns);
1618 	autoreg_host->port = port;
1619 	autoreg_host->connection_type = connection_type;
1620 	autoreg_host->host_metadata = zbx_strdup(NULL, host_metadata);
1621 	autoreg_host->flag = flag;
1622 	autoreg_host->now = now;
1623 
1624 	zbx_vector_ptr_append(autoreg_hosts, autoreg_host);
1625 }
1626 
autoreg_get_hosts(zbx_vector_ptr_t * autoreg_hosts,zbx_vector_str_t * hosts)1627 static void	autoreg_get_hosts(zbx_vector_ptr_t *autoreg_hosts, zbx_vector_str_t *hosts)
1628 {
1629 	int	i;
1630 
1631 	for (i = 0; i < autoreg_hosts->values_num; i++)
1632 	{
1633 		zbx_autoreg_host_t	*autoreg_host = (zbx_autoreg_host_t *)autoreg_hosts->values[i];
1634 
1635 		zbx_vector_str_append(hosts, autoreg_host->host);
1636 	}
1637 }
1638 
process_autoreg_hosts(zbx_vector_ptr_t * autoreg_hosts,zbx_uint64_t proxy_hostid)1639 static void	process_autoreg_hosts(zbx_vector_ptr_t *autoreg_hosts, zbx_uint64_t proxy_hostid)
1640 {
1641 	DB_RESULT		result;
1642 	DB_ROW			row;
1643 	zbx_vector_str_t	hosts;
1644 	zbx_uint64_t		current_proxy_hostid;
1645 	char			*sql = NULL;
1646 	size_t			sql_alloc = 256, sql_offset;
1647 	zbx_autoreg_host_t	*autoreg_host;
1648 	int			i;
1649 
1650 	sql = (char *)zbx_malloc(sql, sql_alloc);
1651 	zbx_vector_str_create(&hosts);
1652 
1653 	if (0 != proxy_hostid)
1654 	{
1655 		autoreg_get_hosts(autoreg_hosts, &hosts);
1656 
1657 		/* delete from vector if already exist in hosts table */
1658 		sql_offset = 0;
1659 		zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset,
1660 				"select h.host,h.hostid,h.proxy_hostid,a.host_metadata,a.listen_ip,a.listen_dns,"
1661 					"a.listen_port,a.flags,a.autoreg_hostid"
1662 				" from hosts h"
1663 				" left join autoreg_host a"
1664 					" on a.proxy_hostid=h.proxy_hostid and a.host=h.host"
1665 				" where");
1666 		DBadd_str_condition_alloc(&sql, &sql_alloc, &sql_offset, "h.host",
1667 				(const char **)hosts.values, hosts.values_num);
1668 
1669 		result = DBselect("%s", sql);
1670 
1671 		while (NULL != (row = DBfetch(result)))
1672 		{
1673 			for (i = 0; i < autoreg_hosts->values_num; i++)
1674 			{
1675 				autoreg_host = (zbx_autoreg_host_t *)autoreg_hosts->values[i];
1676 
1677 				if (0 != strcmp(autoreg_host->host, row[0]))
1678 					continue;
1679 
1680 				ZBX_STR2UINT64(autoreg_host->hostid, row[1]);
1681 				ZBX_DBROW2UINT64(current_proxy_hostid, row[2]);
1682 
1683 				if (current_proxy_hostid != proxy_hostid || SUCCEED == DBis_null(row[8]) ||
1684 						0 != strcmp(autoreg_host->host_metadata, row[3]) ||
1685 						autoreg_host->flag != atoi(row[7]))
1686 				{
1687 					break;
1688 				}
1689 
1690 				/* process with autoregistration if the connection type was forced and */
1691 				/* is different from the last registered connection type               */
1692 				if (ZBX_CONN_DEFAULT != autoreg_host->flag)
1693 				{
1694 					unsigned short	port;
1695 
1696 					if (FAIL == is_ushort(row[6], &port) || port != autoreg_host->port)
1697 						break;
1698 
1699 					if (ZBX_CONN_IP == autoreg_host->flag && 0 != strcmp(row[4], autoreg_host->ip))
1700 						break;
1701 
1702 					if (ZBX_CONN_DNS == autoreg_host->flag && 0 != strcmp(row[5], autoreg_host->dns))
1703 						break;
1704 				}
1705 
1706 				zbx_vector_ptr_remove(autoreg_hosts, i);
1707 				autoreg_host_free(autoreg_host);
1708 
1709 				break;
1710 			}
1711 
1712 		}
1713 		DBfree_result(result);
1714 
1715 		hosts.values_num = 0;
1716 	}
1717 
1718 	if (0 != autoreg_hosts->values_num)
1719 	{
1720 		autoreg_get_hosts(autoreg_hosts, &hosts);
1721 
1722 		/* update autoreg_id in vector if already exists in autoreg_host table */
1723 		sql_offset = 0;
1724 		zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset,
1725 				"select autoreg_hostid,host"
1726 				" from autoreg_host"
1727 				" where");
1728 		DBadd_str_condition_alloc(&sql, &sql_alloc, &sql_offset, "host",
1729 				(const char **)hosts.values, hosts.values_num);
1730 
1731 		result = DBselect("%s", sql);
1732 
1733 		while (NULL != (row = DBfetch(result)))
1734 		{
1735 			for (i = 0; i < autoreg_hosts->values_num; i++)
1736 			{
1737 				autoreg_host = (zbx_autoreg_host_t *)autoreg_hosts->values[i];
1738 
1739 				if (0 == autoreg_host->autoreg_hostid && 0 == strcmp(autoreg_host->host, row[1]))
1740 				{
1741 					ZBX_STR2UINT64(autoreg_host->autoreg_hostid, row[0]);
1742 					break;
1743 				}
1744 			}
1745 		}
1746 		DBfree_result(result);
1747 
1748 		hosts.values_num = 0;
1749 	}
1750 
1751 	zbx_vector_str_destroy(&hosts);
1752 	zbx_free(sql);
1753 }
1754 
compare_autoreg_host_by_hostid(const void * d1,const void * d2)1755 static int	compare_autoreg_host_by_hostid(const void *d1, const void *d2)
1756 {
1757 	const zbx_autoreg_host_t	*p1 = *(const zbx_autoreg_host_t **)d1;
1758 	const zbx_autoreg_host_t	*p2 = *(const zbx_autoreg_host_t **)d2;
1759 
1760 	ZBX_RETURN_IF_NOT_EQUAL(p1->hostid, p2->hostid);
1761 
1762 	return 0;
1763 }
1764 
DBregister_host_flush(zbx_vector_ptr_t * autoreg_hosts,zbx_uint64_t proxy_hostid)1765 void	DBregister_host_flush(zbx_vector_ptr_t *autoreg_hosts, zbx_uint64_t proxy_hostid)
1766 {
1767 	zbx_autoreg_host_t	*autoreg_host;
1768 	zbx_uint64_t		autoreg_hostid;
1769 	zbx_db_insert_t		db_insert;
1770 	int			i, create = 0, update = 0;
1771 	char			*sql = NULL, *ip_esc, *dns_esc, *host_metadata_esc;
1772 	size_t			sql_alloc = 256, sql_offset = 0;
1773 	zbx_timespec_t		ts = {0, 0};
1774 
1775 	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
1776 
1777 	if (SUCCEED != DBregister_host_active())
1778 		goto exit;
1779 
1780 	process_autoreg_hosts(autoreg_hosts, proxy_hostid);
1781 
1782 	for (i = 0; i < autoreg_hosts->values_num; i++)
1783 	{
1784 		autoreg_host = (zbx_autoreg_host_t *)autoreg_hosts->values[i];
1785 
1786 		if (0 == autoreg_host->autoreg_hostid)
1787 			create++;
1788 	}
1789 
1790 	if (0 != create)
1791 	{
1792 		autoreg_hostid = DBget_maxid_num("autoreg_host", create);
1793 
1794 		zbx_db_insert_prepare(&db_insert, "autoreg_host", "autoreg_hostid", "proxy_hostid", "host", "listen_ip",
1795 				"listen_dns", "listen_port", "tls_accepted", "host_metadata", "flags", NULL);
1796 	}
1797 
1798 	if (0 != (update = autoreg_hosts->values_num - create))
1799 	{
1800 		sql = (char *)zbx_malloc(sql, sql_alloc);
1801 		DBbegin_multiple_update(&sql, &sql_alloc, &sql_offset);
1802 	}
1803 
1804 	zbx_vector_ptr_sort(autoreg_hosts, ZBX_DEFAULT_UINT64_PTR_COMPARE_FUNC);
1805 
1806 	for (i = 0; i < autoreg_hosts->values_num; i++)
1807 	{
1808 		autoreg_host = (zbx_autoreg_host_t *)autoreg_hosts->values[i];
1809 
1810 		if (0 == autoreg_host->autoreg_hostid)
1811 		{
1812 			autoreg_host->autoreg_hostid = autoreg_hostid++;
1813 
1814 			zbx_db_insert_add_values(&db_insert, autoreg_host->autoreg_hostid, proxy_hostid,
1815 					autoreg_host->host, autoreg_host->ip, autoreg_host->dns,
1816 					(int)autoreg_host->port, (int)autoreg_host->connection_type,
1817 					autoreg_host->host_metadata, autoreg_host->flag);
1818 		}
1819 		else
1820 		{
1821 			ip_esc = DBdyn_escape_string(autoreg_host->ip);
1822 			dns_esc = DBdyn_escape_string(autoreg_host->dns);
1823 			host_metadata_esc = DBdyn_escape_string(autoreg_host->host_metadata);
1824 
1825 			zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset,
1826 					"update autoreg_host"
1827 					" set listen_ip='%s',"
1828 						"listen_dns='%s',"
1829 						"listen_port=%hu,"
1830 						"host_metadata='%s',"
1831 						"tls_accepted='%u',"
1832 						"flags=%hu,"
1833 						"proxy_hostid=%s"
1834 					" where autoreg_hostid=" ZBX_FS_UI64 ";\n",
1835 				ip_esc, dns_esc, autoreg_host->port, host_metadata_esc, autoreg_host->connection_type,
1836 				autoreg_host->flag, DBsql_id_ins(proxy_hostid), autoreg_host->autoreg_hostid);
1837 
1838 			zbx_free(host_metadata_esc);
1839 			zbx_free(dns_esc);
1840 			zbx_free(ip_esc);
1841 		}
1842 	}
1843 
1844 	if (0 != create)
1845 	{
1846 		zbx_db_insert_execute(&db_insert);
1847 		zbx_db_insert_clean(&db_insert);
1848 	}
1849 
1850 	if (0 != update)
1851 	{
1852 		DBend_multiple_update(&sql, &sql_alloc, &sql_offset);
1853 		DBexecute("%s", sql);
1854 		zbx_free(sql);
1855 	}
1856 
1857 	zbx_vector_ptr_sort(autoreg_hosts, compare_autoreg_host_by_hostid);
1858 
1859 	for (i = 0; i < autoreg_hosts->values_num; i++)
1860 	{
1861 		autoreg_host = (zbx_autoreg_host_t *)autoreg_hosts->values[i];
1862 
1863 		ts.sec = autoreg_host->now;
1864 		zbx_add_event(EVENT_SOURCE_AUTOREGISTRATION, EVENT_OBJECT_ZABBIX_ACTIVE, autoreg_host->autoreg_hostid,
1865 				&ts, TRIGGER_VALUE_PROBLEM, NULL, NULL, NULL, 0, 0, NULL, 0, NULL, 0, NULL, NULL, NULL);
1866 	}
1867 
1868 	zbx_process_events(NULL, NULL);
1869 	zbx_clean_events();
1870 exit:
1871 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__);
1872 }
1873 
DBregister_host_clean(zbx_vector_ptr_t * autoreg_hosts)1874 void	DBregister_host_clean(zbx_vector_ptr_t *autoreg_hosts)
1875 {
1876 	zbx_vector_ptr_clear_ext(autoreg_hosts, (zbx_mem_free_func_t)autoreg_host_free);
1877 }
1878 
1879 /******************************************************************************
1880  *                                                                            *
1881  * Function: DBproxy_register_host                                            *
1882  *                                                                            *
1883  * Purpose: register unknown host                                             *
1884  *                                                                            *
1885  * Parameters: host - host name                                               *
1886  *                                                                            *
1887  * Author: Alexander Vladishev                                                *
1888  *                                                                            *
1889  ******************************************************************************/
DBproxy_register_host(const char * host,const char * ip,const char * dns,unsigned short port,unsigned int connection_type,const char * host_metadata,unsigned short flag)1890 void	DBproxy_register_host(const char *host, const char *ip, const char *dns, unsigned short port,
1891 		unsigned int connection_type, const char *host_metadata, unsigned short flag)
1892 {
1893 	char	*host_esc, *ip_esc, *dns_esc, *host_metadata_esc;
1894 
1895 	host_esc = DBdyn_escape_field("proxy_autoreg_host", "host", host);
1896 	ip_esc = DBdyn_escape_field("proxy_autoreg_host", "listen_ip", ip);
1897 	dns_esc = DBdyn_escape_field("proxy_autoreg_host", "listen_dns", dns);
1898 	host_metadata_esc = DBdyn_escape_field("proxy_autoreg_host", "host_metadata", host_metadata);
1899 
1900 	DBexecute("insert into proxy_autoreg_host"
1901 			" (clock,host,listen_ip,listen_dns,listen_port,tls_accepted,host_metadata,flags)"
1902 			" values"
1903 			" (%d,'%s','%s','%s',%d,%u,'%s',%d)",
1904 			(int)time(NULL), host_esc, ip_esc, dns_esc, (int)port, connection_type, host_metadata_esc,
1905 			(int)flag);
1906 
1907 	zbx_free(host_metadata_esc);
1908 	zbx_free(dns_esc);
1909 	zbx_free(ip_esc);
1910 	zbx_free(host_esc);
1911 }
1912 
1913 /******************************************************************************
1914  *                                                                            *
1915  * Function: DBexecute_overflowed_sql                                         *
1916  *                                                                            *
1917  * Purpose: execute a set of SQL statements IF it is big enough               *
1918  *                                                                            *
1919  * Author: Dmitry Borovikov                                                   *
1920  *                                                                            *
1921  ******************************************************************************/
DBexecute_overflowed_sql(char ** sql,size_t * sql_alloc,size_t * sql_offset)1922 int	DBexecute_overflowed_sql(char **sql, size_t *sql_alloc, size_t *sql_offset)
1923 {
1924 	int	ret = SUCCEED;
1925 
1926 	if (ZBX_MAX_OVERFLOW_SQL_SIZE < *sql_offset)
1927 	{
1928 #ifdef HAVE_MULTIROW_INSERT
1929 		if (',' == (*sql)[*sql_offset - 1])
1930 		{
1931 			(*sql_offset)--;
1932 			zbx_strcpy_alloc(sql, sql_alloc, sql_offset, ";\n");
1933 		}
1934 #endif
1935 #if defined(HAVE_ORACLE) && 0 == ZBX_MAX_OVERFLOW_SQL_SIZE
1936 		/* make sure we are not called twice without */
1937 		/* putting a new sql into the buffer first */
1938 		if (*sql_offset <= ZBX_SQL_EXEC_FROM)
1939 		{
1940 			THIS_SHOULD_NEVER_HAPPEN;
1941 			return ret;
1942 		}
1943 
1944 		/* Oracle fails with ORA-00911 if it encounters ';' w/o PL/SQL block */
1945 		zbx_rtrim(*sql, ZBX_WHITESPACE ";");
1946 #else
1947 		DBend_multiple_update(sql, sql_alloc, sql_offset);
1948 #endif
1949 		/* For Oracle with max_overflow_sql_size == 0, jump over "begin\n" */
1950 		/* before execution. ZBX_SQL_EXEC_FROM is 0 for all other cases. */
1951 		if (ZBX_DB_OK > DBexecute("%s", *sql + ZBX_SQL_EXEC_FROM))
1952 			ret = FAIL;
1953 
1954 		*sql_offset = 0;
1955 
1956 		DBbegin_multiple_update(sql, sql_alloc, sql_offset);
1957 	}
1958 
1959 	return ret;
1960 }
1961 
1962 /******************************************************************************
1963  *                                                                            *
1964  * Function: DBget_unique_hostname_by_sample                                  *
1965  *                                                                            *
1966  * Purpose: construct a unique host name by the given sample                  *
1967  *                                                                            *
1968  * Parameters: host_name_sample - a host name to start constructing from      *
1969  *             field_name       - field name for host or host visible name    *
1970  *                                                                            *
1971  * Return value: unique host name which does not exist in the database        *
1972  *                                                                            *
1973  * Author: Dmitry Borovikov                                                   *
1974  *                                                                            *
1975  * Comments: the sample cannot be empty                                       *
1976  *           constructs new by adding "_$(number+1)", where "number"          *
1977  *           shows count of the sample itself plus already constructed ones   *
1978  *           host_name_sample is not modified, allocates new memory!          *
1979  *                                                                            *
1980  ******************************************************************************/
DBget_unique_hostname_by_sample(const char * host_name_sample,const char * field_name)1981 char	*DBget_unique_hostname_by_sample(const char *host_name_sample, const char *field_name)
1982 {
1983 	DB_RESULT		result;
1984 	DB_ROW			row;
1985 	int			full_match = 0, i;
1986 	char			*host_name_temp = NULL, *host_name_sample_esc;
1987 	zbx_vector_uint64_t	nums;
1988 	zbx_uint64_t		num = 2;	/* produce alternatives starting from "2" */
1989 	size_t			sz;
1990 
1991 	assert(host_name_sample && *host_name_sample);
1992 
1993 	zabbix_log(LOG_LEVEL_DEBUG, "In %s() sample:'%s'", __func__, host_name_sample);
1994 
1995 	zbx_vector_uint64_create(&nums);
1996 	zbx_vector_uint64_reserve(&nums, 8);
1997 
1998 	sz = strlen(host_name_sample);
1999 	host_name_sample_esc = DBdyn_escape_like_pattern(host_name_sample);
2000 
2001 	result = DBselect(
2002 			"select %s"
2003 			" from hosts"
2004 			" where %s like '%s%%' escape '%c'"
2005 				" and flags<>%d"
2006 				" and status in (%d,%d,%d)",
2007 				field_name, field_name, host_name_sample_esc, ZBX_SQL_LIKE_ESCAPE_CHAR,
2008 			ZBX_FLAG_DISCOVERY_PROTOTYPE,
2009 			HOST_STATUS_MONITORED, HOST_STATUS_NOT_MONITORED, HOST_STATUS_TEMPLATE);
2010 
2011 	zbx_free(host_name_sample_esc);
2012 
2013 	while (NULL != (row = DBfetch(result)))
2014 	{
2015 		zbx_uint64_t	n;
2016 		const char	*p;
2017 
2018 		if (0 != strncmp(row[0], host_name_sample, sz))
2019 			continue;
2020 
2021 		p = row[0] + sz;
2022 
2023 		if ('\0' == *p)
2024 		{
2025 			full_match = 1;
2026 			continue;
2027 		}
2028 
2029 		if ('_' != *p || FAIL == is_uint64(p + 1, &n))
2030 			continue;
2031 
2032 		zbx_vector_uint64_append(&nums, n);
2033 	}
2034 	DBfree_result(result);
2035 
2036 	zbx_vector_uint64_sort(&nums, ZBX_DEFAULT_UINT64_COMPARE_FUNC);
2037 
2038 	if (0 == full_match)
2039 	{
2040 		host_name_temp = zbx_strdup(host_name_temp, host_name_sample);
2041 		goto clean;
2042 	}
2043 
2044 	for (i = 0; i < nums.values_num; i++)
2045 	{
2046 		if (num > nums.values[i])
2047 			continue;
2048 
2049 		if (num < nums.values[i])	/* found, all other will be bigger */
2050 			break;
2051 
2052 		num++;
2053 	}
2054 
2055 	host_name_temp = zbx_dsprintf(host_name_temp, "%s_" ZBX_FS_UI64, host_name_sample, num);
2056 clean:
2057 	zbx_vector_uint64_destroy(&nums);
2058 
2059 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s():'%s'", __func__, host_name_temp);
2060 
2061 	return host_name_temp;
2062 }
2063 
2064 /******************************************************************************
2065  *                                                                            *
2066  * Function: DBsql_id_ins                                                     *
2067  *                                                                            *
2068  * Purpose: construct insert statement                                        *
2069  *                                                                            *
2070  * Return value: "<id>" if id not equal zero,                                 *
2071  *               otherwise "null"                                             *
2072  *                                                                            *
2073  * Author: Alexander Vladishev                                                *
2074  *                                                                            *
2075  ******************************************************************************/
DBsql_id_ins(zbx_uint64_t id)2076 const char	*DBsql_id_ins(zbx_uint64_t id)
2077 {
2078 	static unsigned char	n = 0;
2079 	static char		buf[4][21];	/* 20 - value size, 1 - '\0' */
2080 	static const char	null[5] = "null";
2081 
2082 	if (0 == id)
2083 		return null;
2084 
2085 	n = (n + 1) & 3;
2086 
2087 	zbx_snprintf(buf[n], sizeof(buf[n]), ZBX_FS_UI64, id);
2088 
2089 	return buf[n];
2090 }
2091 
2092 /******************************************************************************
2093  *                                                                            *
2094  * Function: DBget_inventory_field                                            *
2095  *                                                                            *
2096  * Purpose: get corresponding host_inventory field name                       *
2097  *                                                                            *
2098  * Parameters: inventory_link - [IN] field link 1..HOST_INVENTORY_FIELD_COUNT *
2099  *                                                                            *
2100  * Return value: field name or NULL if value of inventory_link is incorrect   *
2101  *                                                                            *
2102  * Author: Alexander Vladishev                                                *
2103  *                                                                            *
2104  ******************************************************************************/
DBget_inventory_field(unsigned char inventory_link)2105 const char	*DBget_inventory_field(unsigned char inventory_link)
2106 {
2107 	static const char	*inventory_fields[HOST_INVENTORY_FIELD_COUNT] =
2108 	{
2109 		"type", "type_full", "name", "alias", "os", "os_full", "os_short", "serialno_a", "serialno_b", "tag",
2110 		"asset_tag", "macaddress_a", "macaddress_b", "hardware", "hardware_full", "software", "software_full",
2111 		"software_app_a", "software_app_b", "software_app_c", "software_app_d", "software_app_e", "contact",
2112 		"location", "location_lat", "location_lon", "notes", "chassis", "model", "hw_arch", "vendor",
2113 		"contract_number", "installer_name", "deployment_status", "url_a", "url_b", "url_c", "host_networks",
2114 		"host_netmask", "host_router", "oob_ip", "oob_netmask", "oob_router", "date_hw_purchase",
2115 		"date_hw_install", "date_hw_expiry", "date_hw_decomm", "site_address_a", "site_address_b",
2116 		"site_address_c", "site_city", "site_state", "site_country", "site_zip", "site_rack", "site_notes",
2117 		"poc_1_name", "poc_1_email", "poc_1_phone_a", "poc_1_phone_b", "poc_1_cell", "poc_1_screen",
2118 		"poc_1_notes", "poc_2_name", "poc_2_email", "poc_2_phone_a", "poc_2_phone_b", "poc_2_cell",
2119 		"poc_2_screen", "poc_2_notes"
2120 	};
2121 
2122 	if (1 > inventory_link || inventory_link > HOST_INVENTORY_FIELD_COUNT)
2123 		return NULL;
2124 
2125 	return inventory_fields[inventory_link - 1];
2126 }
2127 
DBtxn_status(void)2128 int	DBtxn_status(void)
2129 {
2130 	return 0 == zbx_db_txn_error() ? SUCCEED : FAIL;
2131 }
2132 
DBtxn_ongoing(void)2133 int	DBtxn_ongoing(void)
2134 {
2135 	return 0 == zbx_db_txn_level() ? FAIL : SUCCEED;
2136 }
2137 
DBtable_exists(const char * table_name)2138 int	DBtable_exists(const char *table_name)
2139 {
2140 	char		*table_name_esc;
2141 	DB_RESULT	result;
2142 	int		ret;
2143 
2144 	table_name_esc = DBdyn_escape_string(table_name);
2145 
2146 #if defined(HAVE_MYSQL)
2147 	result = DBselect("show tables like '%s'", table_name_esc);
2148 #elif defined(HAVE_ORACLE)
2149 	result = DBselect(
2150 			"select 1"
2151 			" from tab"
2152 			" where tabtype='TABLE'"
2153 				" and lower(tname)='%s'",
2154 			table_name_esc);
2155 #elif defined(HAVE_POSTGRESQL)
2156 	result = DBselect(
2157 			"select 1"
2158 			" from information_schema.tables"
2159 			" where table_name='%s'"
2160 				" and table_schema='%s'",
2161 			table_name_esc, zbx_db_get_schema_esc());
2162 #elif defined(HAVE_SQLITE3)
2163 	result = DBselect(
2164 			"select 1"
2165 			" from sqlite_master"
2166 			" where tbl_name='%s'"
2167 				" and type='table'",
2168 			table_name_esc);
2169 #endif
2170 
2171 	zbx_free(table_name_esc);
2172 
2173 	ret = (NULL == DBfetch(result) ? FAIL : SUCCEED);
2174 
2175 	DBfree_result(result);
2176 
2177 	return ret;
2178 }
2179 
DBfield_exists(const char * table_name,const char * field_name)2180 int	DBfield_exists(const char *table_name, const char *field_name)
2181 {
2182 	DB_RESULT	result;
2183 #if defined(HAVE_MYSQL)
2184 	char		*field_name_esc;
2185 	int		ret;
2186 #elif defined(HAVE_ORACLE)
2187 	char		*table_name_esc, *field_name_esc;
2188 	int		ret;
2189 #elif defined(HAVE_POSTGRESQL)
2190 	char		*table_name_esc, *field_name_esc;
2191 	int		ret;
2192 #elif defined(HAVE_SQLITE3)
2193 	char		*table_name_esc;
2194 	DB_ROW		row;
2195 	int		ret = FAIL;
2196 #endif
2197 
2198 #if defined(HAVE_MYSQL)
2199 	field_name_esc = DBdyn_escape_string(field_name);
2200 
2201 	result = DBselect("show columns from %s like '%s'",
2202 			table_name, field_name_esc);
2203 
2204 	zbx_free(field_name_esc);
2205 
2206 	ret = (NULL == DBfetch(result) ? FAIL : SUCCEED);
2207 
2208 	DBfree_result(result);
2209 #elif defined(HAVE_ORACLE)
2210 	table_name_esc = DBdyn_escape_string(table_name);
2211 	field_name_esc = DBdyn_escape_string(field_name);
2212 
2213 	result = DBselect(
2214 			"select 1"
2215 			" from col"
2216 			" where lower(tname)='%s'"
2217 				" and lower(cname)='%s'",
2218 			table_name_esc, field_name_esc);
2219 
2220 	zbx_free(field_name_esc);
2221 	zbx_free(table_name_esc);
2222 
2223 	ret = (NULL == DBfetch(result) ? FAIL : SUCCEED);
2224 
2225 	DBfree_result(result);
2226 #elif defined(HAVE_POSTGRESQL)
2227 	table_name_esc = DBdyn_escape_string(table_name);
2228 	field_name_esc = DBdyn_escape_string(field_name);
2229 
2230 	result = DBselect(
2231 			"select 1"
2232 			" from information_schema.columns"
2233 			" where table_name='%s'"
2234 				" and column_name='%s'"
2235 				" and table_schema='%s'",
2236 			table_name_esc, field_name_esc, zbx_db_get_schema_esc());
2237 
2238 	zbx_free(field_name_esc);
2239 	zbx_free(table_name_esc);
2240 
2241 	ret = (NULL == DBfetch(result) ? FAIL : SUCCEED);
2242 
2243 	DBfree_result(result);
2244 #elif defined(HAVE_SQLITE3)
2245 	table_name_esc = DBdyn_escape_string(table_name);
2246 
2247 	result = DBselect("PRAGMA table_info('%s')", table_name_esc);
2248 
2249 	zbx_free(table_name_esc);
2250 
2251 	while (NULL != (row = DBfetch(result)))
2252 	{
2253 		if (0 != strcmp(field_name, row[1]))
2254 			continue;
2255 
2256 		ret = SUCCEED;
2257 		break;
2258 	}
2259 	DBfree_result(result);
2260 #endif
2261 
2262 	return ret;
2263 }
2264 
2265 #ifndef HAVE_SQLITE3
DBindex_exists(const char * table_name,const char * index_name)2266 int	DBindex_exists(const char *table_name, const char *index_name)
2267 {
2268 	char		*table_name_esc, *index_name_esc;
2269 	DB_RESULT	result;
2270 	int		ret;
2271 
2272 	table_name_esc = DBdyn_escape_string(table_name);
2273 	index_name_esc = DBdyn_escape_string(index_name);
2274 
2275 #if defined(HAVE_MYSQL)
2276 	result = DBselect(
2277 			"show index from %s"
2278 			" where key_name='%s'",
2279 			table_name_esc, index_name_esc);
2280 #elif defined(HAVE_ORACLE)
2281 	result = DBselect(
2282 			"select 1"
2283 			" from user_indexes"
2284 			" where lower(table_name)='%s'"
2285 				" and lower(index_name)='%s'",
2286 			table_name_esc, index_name_esc);
2287 #elif defined(HAVE_POSTGRESQL)
2288 	result = DBselect(
2289 			"select 1"
2290 			" from pg_indexes"
2291 			" where tablename='%s'"
2292 				" and indexname='%s'"
2293 				" and schemaname='%s'",
2294 			table_name_esc, index_name_esc, zbx_db_get_schema_esc());
2295 #endif
2296 
2297 	ret = (NULL == DBfetch(result) ? FAIL : SUCCEED);
2298 
2299 	DBfree_result(result);
2300 
2301 	zbx_free(table_name_esc);
2302 	zbx_free(index_name_esc);
2303 
2304 	return ret;
2305 }
2306 #endif
2307 
2308 /******************************************************************************
2309  *                                                                            *
2310  * Function: DBselect_uint64                                                  *
2311  *                                                                            *
2312  * Parameters: sql - [IN] sql statement                                       *
2313  *             ids - [OUT] sorted list of selected uint64 values              *
2314  *                                                                            *
2315  ******************************************************************************/
DBselect_uint64(const char * sql,zbx_vector_uint64_t * ids)2316 void	DBselect_uint64(const char *sql, zbx_vector_uint64_t *ids)
2317 {
2318 	DB_RESULT	result;
2319 	DB_ROW		row;
2320 	zbx_uint64_t	id;
2321 
2322 	result = DBselect("%s", sql);
2323 
2324 	while (NULL != (row = DBfetch(result)))
2325 	{
2326 		ZBX_STR2UINT64(id, row[0]);
2327 
2328 		zbx_vector_uint64_append(ids, id);
2329 	}
2330 	DBfree_result(result);
2331 
2332 	zbx_vector_uint64_sort(ids, ZBX_DEFAULT_UINT64_COMPARE_FUNC);
2333 }
2334 
DBprepare_multiple_query(const char * query,const char * field_name,zbx_vector_uint64_t * ids,char ** sql,size_t * sql_alloc,size_t * sql_offset)2335 int	DBprepare_multiple_query(const char *query, const char *field_name, zbx_vector_uint64_t *ids, char **sql,
2336 		size_t	*sql_alloc, size_t *sql_offset)
2337 {
2338 #define ZBX_MAX_IDS	950
2339 	int	i, ret = SUCCEED;
2340 
2341 	for (i = 0; i < ids->values_num; i += ZBX_MAX_IDS)
2342 	{
2343 		zbx_strcpy_alloc(sql, sql_alloc, sql_offset, query);
2344 		DBadd_condition_alloc(sql, sql_alloc, sql_offset, field_name, &ids->values[i],
2345 				MIN(ZBX_MAX_IDS, ids->values_num - i));
2346 		zbx_strcpy_alloc(sql, sql_alloc, sql_offset, ";\n");
2347 
2348 		if (SUCCEED != (ret = DBexecute_overflowed_sql(sql, sql_alloc, sql_offset)))
2349 			break;
2350 	}
2351 
2352 	return ret;
2353 }
2354 
DBexecute_multiple_query(const char * query,const char * field_name,zbx_vector_uint64_t * ids)2355 int	DBexecute_multiple_query(const char *query, const char *field_name, zbx_vector_uint64_t *ids)
2356 {
2357 	char	*sql = NULL;
2358 	size_t	sql_alloc = ZBX_KIBIBYTE, sql_offset = 0;
2359 	int	ret = SUCCEED;
2360 
2361 	sql = (char *)zbx_malloc(sql, sql_alloc);
2362 
2363 	DBbegin_multiple_update(&sql, &sql_alloc, &sql_offset);
2364 
2365 	ret = DBprepare_multiple_query(query, field_name, ids, &sql, &sql_alloc, &sql_offset);
2366 
2367 	if (SUCCEED == ret && sql_offset > 16)	/* in ORACLE always present begin..end; */
2368 	{
2369 		DBend_multiple_update(&sql, &sql_alloc, &sql_offset);
2370 
2371 		if (ZBX_DB_OK > DBexecute("%s", sql))
2372 			ret = FAIL;
2373 	}
2374 
2375 	zbx_free(sql);
2376 
2377 	return ret;
2378 }
2379 
2380 #if defined(HAVE_MYSQL) || defined(HAVE_POSTGRESQL)
zbx_warn_char_set(const char * db_name,const char * char_set)2381 static void	zbx_warn_char_set(const char *db_name, const char *char_set)
2382 {
2383 	zabbix_log(LOG_LEVEL_WARNING, "Zabbix supports only \"" ZBX_SUPPORTED_DB_CHARACTER_SET "\" character set(s)."
2384 			" Database \"%s\" has default character set \"%s\"", db_name, char_set);
2385 }
2386 #endif
2387 
2388 #if defined(HAVE_MYSQL) || defined(HAVE_POSTGRESQL) || defined(HAVE_ORACLE)
zbx_warn_no_charset_info(const char * db_name)2389 static void	zbx_warn_no_charset_info(const char *db_name)
2390 {
2391 	zabbix_log(LOG_LEVEL_WARNING, "Cannot get database \"%s\" character set", db_name);
2392 }
2393 #endif
2394 
2395 #if defined(HAVE_MYSQL)
db_strlist_quote(const char * strlist,char delimiter)2396 static char	*db_strlist_quote(const char *strlist, char delimiter)
2397 {
2398 	const char	*delim;
2399 	char		*str = NULL;
2400 	size_t		str_alloc = 0, str_offset = 0;
2401 
2402 	while (NULL != (delim = strchr(strlist, delimiter)))
2403 	{
2404 		zbx_snprintf_alloc(&str, &str_alloc, &str_offset, "'%.*s',", (int)(delim - strlist), strlist);
2405 		strlist = delim + 1;
2406 	}
2407 
2408 	zbx_snprintf_alloc(&str, &str_alloc, &str_offset, "'%s'", strlist);
2409 
2410 	return str;
2411 }
2412 #endif
2413 
DBcheck_character_set(void)2414 void	DBcheck_character_set(void)
2415 {
2416 #if defined(HAVE_MYSQL)
2417 	char		*database_name_esc, *charset_list, *collation_list;
2418 	DB_RESULT	result;
2419 	DB_ROW		row;
2420 
2421 	database_name_esc = DBdyn_escape_string(CONFIG_DBNAME);
2422 	DBconnect(ZBX_DB_CONNECT_NORMAL);
2423 
2424 	result = DBselect(
2425 			"select default_character_set_name,default_collation_name"
2426 			" from information_schema.SCHEMATA"
2427 			" where schema_name='%s'", database_name_esc);
2428 
2429 	if (NULL == result || NULL == (row = DBfetch(result)))
2430 	{
2431 		zbx_warn_no_charset_info(CONFIG_DBNAME);
2432 	}
2433 	else
2434 	{
2435 		char	*char_set = row[0];
2436 		char	*collation = row[1];
2437 
2438 		if (SUCCEED != str_in_list(ZBX_SUPPORTED_DB_CHARACTER_SET, char_set, ZBX_DB_STRLIST_DELIM))
2439 			zbx_warn_char_set(CONFIG_DBNAME, char_set);
2440 
2441 		if (SUCCEED != str_in_list(ZBX_SUPPORTED_DB_COLLATION, collation, ZBX_DB_STRLIST_DELIM))
2442 		{
2443 			zabbix_log(LOG_LEVEL_WARNING, "Zabbix supports only \"%s\" collation(s)."
2444 					" Database \"%s\" has default collation \"%s\"", ZBX_SUPPORTED_DB_COLLATION,
2445 					CONFIG_DBNAME, collation);
2446 		}
2447 	}
2448 
2449 	DBfree_result(result);
2450 
2451 	charset_list = db_strlist_quote(ZBX_SUPPORTED_DB_CHARACTER_SET, ZBX_DB_STRLIST_DELIM);
2452 	collation_list = db_strlist_quote(ZBX_SUPPORTED_DB_COLLATION, ZBX_DB_STRLIST_DELIM);
2453 
2454 	result = DBselect(
2455 			"select count(*)"
2456 			" from information_schema.`COLUMNS`"
2457 			" where table_schema='%s'"
2458 				" and data_type in ('text','varchar','longtext')"
2459 				" and (character_set_name not in (%s) or collation_name not in (%s))",
2460 			database_name_esc, charset_list, collation_list);
2461 
2462 	zbx_free(collation_list);
2463 	zbx_free(charset_list);
2464 
2465 	if (NULL == result || NULL == (row = DBfetch(result)))
2466 	{
2467 		zabbix_log(LOG_LEVEL_WARNING, "cannot get character set of database \"%s\" tables", CONFIG_DBNAME);
2468 	}
2469 	else if (0 != strcmp("0", row[0]))
2470 	{
2471 		zabbix_log(LOG_LEVEL_WARNING, "character set name or collation name that is not supported by Zabbix"
2472 				" found in %s column(s) of database \"%s\"", row[0], CONFIG_DBNAME);
2473 		zabbix_log(LOG_LEVEL_WARNING, "only character set(s) \"%s\" and corresponding collation(s) \"%s\""
2474 				" should be used in database", ZBX_SUPPORTED_DB_CHARACTER_SET,
2475 				ZBX_SUPPORTED_DB_COLLATION);
2476 	}
2477 
2478 	DBfree_result(result);
2479 	DBclose();
2480 	zbx_free(database_name_esc);
2481 #elif defined(HAVE_ORACLE)
2482 	DB_RESULT	result;
2483 	DB_ROW		row;
2484 
2485 	DBconnect(ZBX_DB_CONNECT_NORMAL);
2486 	result = DBselect(
2487 			"select parameter,value"
2488 			" from NLS_DATABASE_PARAMETERS"
2489 			" where parameter in ('NLS_CHARACTERSET','NLS_NCHAR_CHARACTERSET')");
2490 
2491 	if (NULL == result)
2492 	{
2493 		zbx_warn_no_charset_info(CONFIG_DBNAME);
2494 	}
2495 	else
2496 	{
2497 		while (NULL != (row = DBfetch(result)))
2498 		{
2499 			const char	*parameter = row[0];
2500 			const char	*value = row[1];
2501 
2502 			if (NULL == parameter || NULL == value)
2503 			{
2504 				continue;
2505 			}
2506 			else if (0 == strcasecmp("NLS_CHARACTERSET", parameter) ||
2507 					(0 == strcasecmp("NLS_NCHAR_CHARACTERSET", parameter)))
2508 			{
2509 				if (0 != strcasecmp(ZBX_ORACLE_UTF8_CHARSET, value) &&
2510 						0 != strcasecmp(ZBX_ORACLE_CESU8_CHARSET, value))
2511 				{
2512 					zabbix_log(LOG_LEVEL_WARNING, "database \"%s\" parameter \"%s\" has value"
2513 							" \"%s\". Zabbix supports only \"%s\" or \"%s\" character sets",
2514 							CONFIG_DBNAME, parameter, value,
2515 							ZBX_ORACLE_UTF8_CHARSET, ZBX_ORACLE_CESU8_CHARSET);
2516 				}
2517 			}
2518 		}
2519 	}
2520 
2521 	DBfree_result(result);
2522 	DBclose();
2523 #elif defined(HAVE_POSTGRESQL)
2524 #define OID_LENGTH_MAX		20
2525 
2526 	char		*database_name_esc, oid[OID_LENGTH_MAX];
2527 	DB_RESULT	result;
2528 	DB_ROW		row;
2529 
2530 	database_name_esc = DBdyn_escape_string(CONFIG_DBNAME);
2531 
2532 	DBconnect(ZBX_DB_CONNECT_NORMAL);
2533 	result = DBselect(
2534 			"select pg_encoding_to_char(encoding)"
2535 			" from pg_database"
2536 			" where datname='%s'",
2537 			database_name_esc);
2538 
2539 	if (NULL == result || NULL == (row = DBfetch(result)))
2540 	{
2541 		zbx_warn_no_charset_info(CONFIG_DBNAME);
2542 		goto out;
2543 	}
2544 	else if (strcasecmp(row[0], ZBX_SUPPORTED_DB_CHARACTER_SET))
2545 	{
2546 		zbx_warn_char_set(CONFIG_DBNAME, row[0]);
2547 		goto out;
2548 
2549 	}
2550 
2551 	DBfree_result(result);
2552 
2553 	result = DBselect(
2554 			"select oid"
2555 			" from pg_namespace"
2556 			" where nspname='%s'",
2557 			zbx_db_get_schema_esc());
2558 
2559 	if (NULL == result || NULL == (row = DBfetch(result)) || '\0' == **row)
2560 	{
2561 		zabbix_log(LOG_LEVEL_WARNING, "cannot get character set of database \"%s\" fields", CONFIG_DBNAME);
2562 		goto out;
2563 	}
2564 
2565 	strscpy(oid, *row);
2566 
2567 	DBfree_result(result);
2568 
2569 	result = DBselect(
2570 			"select count(*)"
2571 			" from pg_attribute as a"
2572 				" left join pg_class as c"
2573 					" on c.relfilenode=a.attrelid"
2574 				" left join pg_collation as l"
2575 					" on l.oid=a.attcollation"
2576 			" where atttypid in (25,1043)"
2577 				" and c.relnamespace=%s"
2578 				" and c.relam=0"
2579 				" and l.collname<>'default'",
2580 			oid);
2581 
2582 	if (NULL == result || NULL == (row = DBfetch(result)))
2583 	{
2584 		zabbix_log(LOG_LEVEL_WARNING, "cannot get character set of database \"%s\" fields", CONFIG_DBNAME);
2585 	}
2586 	else if (0 != strcmp("0", row[0]))
2587 	{
2588 		zabbix_log(LOG_LEVEL_WARNING, "database has %s fields with unsupported character set. Zabbix supports"
2589 				" only \"%s\" character set", row[0], ZBX_SUPPORTED_DB_CHARACTER_SET);
2590 	}
2591 
2592 	DBfree_result(result);
2593 
2594 	result = DBselect("show client_encoding");
2595 
2596 	if (NULL == result || NULL == (row = DBfetch(result)))
2597 	{
2598 		zabbix_log(LOG_LEVEL_WARNING, "cannot get info about database \"%s\" client encoding", CONFIG_DBNAME);
2599 	}
2600 	else if (0 != strcasecmp(row[0], ZBX_SUPPORTED_DB_CHARACTER_SET))
2601 	{
2602 		zabbix_log(LOG_LEVEL_WARNING, "client_encoding for database \"%s\" is \"%s\". Zabbix supports only"
2603 				" \"%s\"", CONFIG_DBNAME, row[0], ZBX_SUPPORTED_DB_CHARACTER_SET);
2604 	}
2605 
2606 	DBfree_result(result);
2607 
2608 	result = DBselect("show server_encoding");
2609 
2610 	if (NULL == result || NULL == (row = DBfetch(result)))
2611 	{
2612 		zabbix_log(LOG_LEVEL_WARNING, "cannot get info about database \"%s\" server encoding", CONFIG_DBNAME);
2613 	}
2614 	else if (0 != strcasecmp(row[0], ZBX_SUPPORTED_DB_CHARACTER_SET))
2615 	{
2616 		zabbix_log(LOG_LEVEL_WARNING, "server_encoding for database \"%s\" is \"%s\". Zabbix supports only"
2617 				" \"%s\"", CONFIG_DBNAME, row[0], ZBX_SUPPORTED_DB_CHARACTER_SET);
2618 	}
2619 out:
2620 	DBfree_result(result);
2621 	DBclose();
2622 	zbx_free(database_name_esc);
2623 #endif
2624 }
2625 
2626 #ifdef HAVE_ORACLE
2627 /******************************************************************************
2628  *                                                                            *
2629  * Function: zbx_db_format_values                                             *
2630  *                                                                            *
2631  * Purpose: format bulk operation (insert, update) value list                 *
2632  *                                                                            *
2633  * Parameters: fields     - [IN] the field list                               *
2634  *             values     - [IN] the corresponding value list                 *
2635  *             values_num - [IN] the number of values to format               *
2636  *                                                                            *
2637  * Return value: the formatted value list <value1>,<value2>...                *
2638  *                                                                            *
2639  * Comments: The returned string is allocated by this function and must be    *
2640  *           freed by the caller later.                                       *
2641  *                                                                            *
2642  ******************************************************************************/
zbx_db_format_values(ZBX_FIELD ** fields,const zbx_db_value_t * values,int values_num)2643 static char	*zbx_db_format_values(ZBX_FIELD **fields, const zbx_db_value_t *values, int values_num)
2644 {
2645 	int	i;
2646 	char	*str = NULL;
2647 	size_t	str_alloc = 0, str_offset = 0;
2648 
2649 	for (i = 0; i < values_num; i++)
2650 	{
2651 		ZBX_FIELD		*field = fields[i];
2652 		const zbx_db_value_t	*value = &values[i];
2653 
2654 		if (0 < i)
2655 			zbx_chrcpy_alloc(&str, &str_alloc, &str_offset, ',');
2656 
2657 		switch (field->type)
2658 		{
2659 			case ZBX_TYPE_CHAR:
2660 			case ZBX_TYPE_TEXT:
2661 			case ZBX_TYPE_SHORTTEXT:
2662 			case ZBX_TYPE_LONGTEXT:
2663 				zbx_snprintf_alloc(&str, &str_alloc, &str_offset, "'%s'", value->str);
2664 				break;
2665 			case ZBX_TYPE_FLOAT:
2666 				zbx_snprintf_alloc(&str, &str_alloc, &str_offset, ZBX_FS_DBL64, value->dbl);
2667 				break;
2668 			case ZBX_TYPE_ID:
2669 			case ZBX_TYPE_UINT:
2670 				zbx_snprintf_alloc(&str, &str_alloc, &str_offset, ZBX_FS_UI64, value->ui64);
2671 				break;
2672 			case ZBX_TYPE_INT:
2673 				zbx_snprintf_alloc(&str, &str_alloc, &str_offset, "%d", value->i32);
2674 				break;
2675 			default:
2676 				zbx_strcpy_alloc(&str, &str_alloc, &str_offset, "(unknown type)");
2677 				break;
2678 		}
2679 	}
2680 
2681 	return str;
2682 }
2683 #endif
2684 
2685 /******************************************************************************
2686  *                                                                            *
2687  * Function: zbx_db_insert_clean                                              *
2688  *                                                                            *
2689  * Purpose: releases resources allocated by bulk insert operations            *
2690  *                                                                            *
2691  * Parameters: self        - [IN] the bulk insert data                        *
2692  *                                                                            *
2693  ******************************************************************************/
zbx_db_insert_clean(zbx_db_insert_t * self)2694 void	zbx_db_insert_clean(zbx_db_insert_t *self)
2695 {
2696 	int	i, j;
2697 
2698 	for (i = 0; i < self->rows.values_num; i++)
2699 	{
2700 		zbx_db_value_t	*row = (zbx_db_value_t *)self->rows.values[i];
2701 
2702 		for (j = 0; j < self->fields.values_num; j++)
2703 		{
2704 			ZBX_FIELD	*field = (ZBX_FIELD *)self->fields.values[j];
2705 
2706 			switch (field->type)
2707 			{
2708 				case ZBX_TYPE_CHAR:
2709 				case ZBX_TYPE_TEXT:
2710 				case ZBX_TYPE_SHORTTEXT:
2711 				case ZBX_TYPE_LONGTEXT:
2712 					zbx_free(row[j].str);
2713 			}
2714 		}
2715 
2716 		zbx_free(row);
2717 	}
2718 
2719 	zbx_vector_ptr_destroy(&self->rows);
2720 
2721 	zbx_vector_ptr_destroy(&self->fields);
2722 }
2723 
2724 /******************************************************************************
2725  *                                                                            *
2726  * Function: zbx_db_insert_prepare_dyn                                        *
2727  *                                                                            *
2728  * Purpose: prepare for database bulk insert operation                        *
2729  *                                                                            *
2730  * Parameters: self        - [IN] the bulk insert data                        *
2731  *             table       - [IN] the target table name                       *
2732  *             fields      - [IN] names of the fields to insert               *
2733  *             fields_num  - [IN] the number of items in fields array         *
2734  *                                                                            *
2735  * Comments: The operation fails if the target table does not have the        *
2736  *           specified fields defined in its schema.                          *
2737  *                                                                            *
2738  *           Usage example:                                                   *
2739  *             zbx_db_insert_t ins;                                           *
2740  *                                                                            *
2741  *             zbx_db_insert_prepare(&ins, "history", "id", "value");         *
2742  *             zbx_db_insert_add_values(&ins, (zbx_uint64_t)1, 1.0);          *
2743  *             zbx_db_insert_add_values(&ins, (zbx_uint64_t)2, 2.0);          *
2744  *               ...                                                          *
2745  *             zbx_db_insert_execute(&ins);                                   *
2746  *             zbx_db_insert_clean(&ins);                                     *
2747  *                                                                            *
2748  ******************************************************************************/
zbx_db_insert_prepare_dyn(zbx_db_insert_t * self,const ZBX_TABLE * table,const ZBX_FIELD ** fields,int fields_num)2749 void	zbx_db_insert_prepare_dyn(zbx_db_insert_t *self, const ZBX_TABLE *table, const ZBX_FIELD **fields, int fields_num)
2750 {
2751 	int	i;
2752 
2753 	if (0 == fields_num)
2754 	{
2755 		THIS_SHOULD_NEVER_HAPPEN;
2756 		exit(EXIT_FAILURE);
2757 	}
2758 
2759 	self->autoincrement = -1;
2760 
2761 	zbx_vector_ptr_create(&self->fields);
2762 	zbx_vector_ptr_create(&self->rows);
2763 
2764 	self->table = table;
2765 
2766 	for (i = 0; i < fields_num; i++)
2767 		zbx_vector_ptr_append(&self->fields, (ZBX_FIELD *)fields[i]);
2768 }
2769 
2770 /******************************************************************************
2771  *                                                                            *
2772  * Function: zbx_db_insert_prepare                                            *
2773  *                                                                            *
2774  * Purpose: prepare for database bulk insert operation                        *
2775  *                                                                            *
2776  * Parameters: self  - [IN] the bulk insert data                              *
2777  *             table - [IN] the target table name                             *
2778  *             ...   - [IN] names of the fields to insert                     *
2779  *             NULL  - [IN] terminating NULL pointer                          *
2780  *                                                                            *
2781  * Comments: This is a convenience wrapper for zbx_db_insert_prepare_dyn()    *
2782  *           function.                                                        *
2783  *                                                                            *
2784  ******************************************************************************/
zbx_db_insert_prepare(zbx_db_insert_t * self,const char * table,...)2785 void	zbx_db_insert_prepare(zbx_db_insert_t *self, const char *table, ...)
2786 {
2787 	zbx_vector_ptr_t	fields;
2788 	va_list			args;
2789 	char			*field;
2790 	const ZBX_TABLE		*ptable;
2791 	const ZBX_FIELD		*pfield;
2792 
2793 	/* find the table and fields in database schema */
2794 	if (NULL == (ptable = DBget_table(table)))
2795 	{
2796 		THIS_SHOULD_NEVER_HAPPEN;
2797 		exit(EXIT_FAILURE);
2798 	}
2799 
2800 	va_start(args, table);
2801 
2802 	zbx_vector_ptr_create(&fields);
2803 
2804 	while (NULL != (field = va_arg(args, char *)))
2805 	{
2806 		if (NULL == (pfield = DBget_field(ptable, field)))
2807 		{
2808 			zabbix_log(LOG_LEVEL_ERR, "Cannot locate table \"%s\" field \"%s\" in database schema",
2809 					table, field);
2810 			THIS_SHOULD_NEVER_HAPPEN;
2811 			exit(EXIT_FAILURE);
2812 		}
2813 		zbx_vector_ptr_append(&fields, (ZBX_FIELD *)pfield);
2814 	}
2815 
2816 	va_end(args);
2817 
2818 	zbx_db_insert_prepare_dyn(self, ptable, (const ZBX_FIELD **)fields.values, fields.values_num);
2819 
2820 	zbx_vector_ptr_destroy(&fields);
2821 }
2822 
2823 /******************************************************************************
2824  *                                                                            *
2825  * Function: zbx_db_insert_add_values_dyn                                     *
2826  *                                                                            *
2827  * Purpose: adds row values for database bulk insert operation                *
2828  *                                                                            *
2829  * Parameters: self        - [IN] the bulk insert data                        *
2830  *             values      - [IN] the values to insert                        *
2831  *             fields_num  - [IN] the number of items in values array         *
2832  *                                                                            *
2833  * Comments: The values must be listed in the same order as the field names   *
2834  *           for insert preparation functions.                                *
2835  *                                                                            *
2836  ******************************************************************************/
zbx_db_insert_add_values_dyn(zbx_db_insert_t * self,const zbx_db_value_t ** values,int values_num)2837 void	zbx_db_insert_add_values_dyn(zbx_db_insert_t *self, const zbx_db_value_t **values, int values_num)
2838 {
2839 	int		i;
2840 	zbx_db_value_t	*row;
2841 
2842 	if (values_num != self->fields.values_num)
2843 	{
2844 		THIS_SHOULD_NEVER_HAPPEN;
2845 		exit(EXIT_FAILURE);
2846 	}
2847 
2848 	row = (zbx_db_value_t *)zbx_malloc(NULL, self->fields.values_num * sizeof(zbx_db_value_t));
2849 
2850 	for (i = 0; i < self->fields.values_num; i++)
2851 	{
2852 		ZBX_FIELD		*field = (ZBX_FIELD *)self->fields.values[i];
2853 		const zbx_db_value_t	*value = values[i];
2854 
2855 		switch (field->type)
2856 		{
2857 			case ZBX_TYPE_LONGTEXT:
2858 			case ZBX_TYPE_CHAR:
2859 			case ZBX_TYPE_TEXT:
2860 			case ZBX_TYPE_SHORTTEXT:
2861 #ifdef HAVE_ORACLE
2862 				row[i].str = DBdyn_escape_field_len(field, value->str, ESCAPE_SEQUENCE_OFF);
2863 #else
2864 				row[i].str = DBdyn_escape_field_len(field, value->str, ESCAPE_SEQUENCE_ON);
2865 #endif
2866 				break;
2867 			default:
2868 				row[i] = *value;
2869 				break;
2870 		}
2871 	}
2872 
2873 	zbx_vector_ptr_append(&self->rows, row);
2874 }
2875 
2876 /******************************************************************************
2877  *                                                                            *
2878  * Function: zbx_db_insert_add_values                                         *
2879  *                                                                            *
2880  * Purpose: adds row values for database bulk insert operation                *
2881  *                                                                            *
2882  * Parameters: self - [IN] the bulk insert data                               *
2883  *             ...  - [IN] the values to insert                               *
2884  *                                                                            *
2885  * Comments: This is a convenience wrapper for zbx_db_insert_add_values_dyn() *
2886  *           function.                                                        *
2887  *           Note that the types of the passed values must conform to the     *
2888  *           corresponding field types.                                       *
2889  *                                                                            *
2890  ******************************************************************************/
zbx_db_insert_add_values(zbx_db_insert_t * self,...)2891 void	zbx_db_insert_add_values(zbx_db_insert_t *self, ...)
2892 {
2893 	zbx_vector_ptr_t	values;
2894 	va_list			args;
2895 	int			i;
2896 	ZBX_FIELD		*field;
2897 	zbx_db_value_t		*value;
2898 
2899 	va_start(args, self);
2900 
2901 	zbx_vector_ptr_create(&values);
2902 
2903 	for (i = 0; i < self->fields.values_num; i++)
2904 	{
2905 		field = (ZBX_FIELD *)self->fields.values[i];
2906 
2907 		value = (zbx_db_value_t *)zbx_malloc(NULL, sizeof(zbx_db_value_t));
2908 
2909 		switch (field->type)
2910 		{
2911 			case ZBX_TYPE_CHAR:
2912 			case ZBX_TYPE_TEXT:
2913 			case ZBX_TYPE_SHORTTEXT:
2914 			case ZBX_TYPE_LONGTEXT:
2915 				value->str = va_arg(args, char *);
2916 				break;
2917 			case ZBX_TYPE_INT:
2918 				value->i32 = va_arg(args, int);
2919 				break;
2920 			case ZBX_TYPE_FLOAT:
2921 				value->dbl = va_arg(args, double);
2922 				break;
2923 			case ZBX_TYPE_UINT:
2924 			case ZBX_TYPE_ID:
2925 				value->ui64 = va_arg(args, zbx_uint64_t);
2926 				break;
2927 			default:
2928 				THIS_SHOULD_NEVER_HAPPEN;
2929 				exit(EXIT_FAILURE);
2930 		}
2931 
2932 		zbx_vector_ptr_append(&values, value);
2933 	}
2934 
2935 	va_end(args);
2936 
2937 	zbx_db_insert_add_values_dyn(self, (const zbx_db_value_t **)values.values, values.values_num);
2938 
2939 	zbx_vector_ptr_clear_ext(&values, zbx_ptr_free);
2940 	zbx_vector_ptr_destroy(&values);
2941 }
2942 
2943 /******************************************************************************
2944  *                                                                            *
2945  * Function: zbx_db_insert_execute                                            *
2946  *                                                                            *
2947  * Purpose: executes the prepared database bulk insert operation              *
2948  *                                                                            *
2949  * Parameters: self - [IN] the bulk insert data                               *
2950  *                                                                            *
2951  * Return value: Returns SUCCEED if the operation completed successfully or   *
2952  *               FAIL otherwise.                                              *
2953  *                                                                            *
2954  ******************************************************************************/
zbx_db_insert_execute(zbx_db_insert_t * self)2955 int	zbx_db_insert_execute(zbx_db_insert_t *self)
2956 {
2957 	int		ret = FAIL, i, j;
2958 	const ZBX_FIELD	*field;
2959 	char		*sql_command, delim[2] = {',', '('};
2960 	size_t		sql_command_alloc = 512, sql_command_offset = 0;
2961 
2962 #ifndef HAVE_ORACLE
2963 	char		*sql;
2964 	size_t		sql_alloc = 16 * ZBX_KIBIBYTE, sql_offset = 0;
2965 
2966 #	ifdef HAVE_MYSQL
2967 	char		*sql_values = NULL;
2968 	size_t		sql_values_alloc = 0, sql_values_offset = 0;
2969 #	endif
2970 #else
2971 	zbx_db_bind_context_t	*contexts;
2972 	int			rc, tries = 0;
2973 #endif
2974 
2975 	if (0 == self->rows.values_num)
2976 		return SUCCEED;
2977 
2978 	/* process the auto increment field */
2979 	if (-1 != self->autoincrement)
2980 	{
2981 		zbx_uint64_t	id;
2982 
2983 		id = DBget_maxid_num(self->table->table, self->rows.values_num);
2984 
2985 		for (i = 0; i < self->rows.values_num; i++)
2986 		{
2987 			zbx_db_value_t	*values = (zbx_db_value_t *)self->rows.values[i];
2988 
2989 			values[self->autoincrement].ui64 = id++;
2990 		}
2991 	}
2992 
2993 #ifndef HAVE_ORACLE
2994 	sql = (char *)zbx_malloc(NULL, sql_alloc);
2995 #endif
2996 	sql_command = (char *)zbx_malloc(NULL, sql_command_alloc);
2997 
2998 	/* create sql insert statement command */
2999 
3000 	zbx_strcpy_alloc(&sql_command, &sql_command_alloc, &sql_command_offset, "insert into ");
3001 	zbx_strcpy_alloc(&sql_command, &sql_command_alloc, &sql_command_offset, self->table->table);
3002 	zbx_chrcpy_alloc(&sql_command, &sql_command_alloc, &sql_command_offset, ' ');
3003 
3004 	for (i = 0; i < self->fields.values_num; i++)
3005 	{
3006 		field = (ZBX_FIELD *)self->fields.values[i];
3007 
3008 		zbx_chrcpy_alloc(&sql_command, &sql_command_alloc, &sql_command_offset, delim[0 == i]);
3009 		zbx_strcpy_alloc(&sql_command, &sql_command_alloc, &sql_command_offset, field->name);
3010 	}
3011 
3012 #ifdef HAVE_MYSQL
3013 	/* MySQL workaround - explicitly add missing text fields with '' default value */
3014 	for (field = (const ZBX_FIELD *)self->table->fields; NULL != field->name; field++)
3015 	{
3016 		switch (field->type)
3017 		{
3018 			case ZBX_TYPE_BLOB:
3019 			case ZBX_TYPE_TEXT:
3020 			case ZBX_TYPE_SHORTTEXT:
3021 			case ZBX_TYPE_LONGTEXT:
3022 				if (FAIL != zbx_vector_ptr_search(&self->fields, (void *)field,
3023 						ZBX_DEFAULT_PTR_COMPARE_FUNC))
3024 				{
3025 					continue;
3026 				}
3027 
3028 				zbx_chrcpy_alloc(&sql_command, &sql_command_alloc, &sql_command_offset, ',');
3029 				zbx_strcpy_alloc(&sql_command, &sql_command_alloc, &sql_command_offset, field->name);
3030 
3031 				zbx_strcpy_alloc(&sql_values, &sql_values_alloc, &sql_values_offset, ",''");
3032 				break;
3033 		}
3034 	}
3035 #endif
3036 	zbx_strcpy_alloc(&sql_command, &sql_command_alloc, &sql_command_offset, ") values ");
3037 
3038 #ifdef HAVE_ORACLE
3039 	for (i = 0; i < self->fields.values_num; i++)
3040 	{
3041 		zbx_chrcpy_alloc(&sql_command, &sql_command_alloc, &sql_command_offset, delim[0 == i]);
3042 		zbx_snprintf_alloc(&sql_command, &sql_command_alloc, &sql_command_offset, ":%d", i + 1);
3043 	}
3044 	zbx_chrcpy_alloc(&sql_command, &sql_command_alloc, &sql_command_offset, ')');
3045 
3046 	contexts = (zbx_db_bind_context_t *)zbx_malloc(NULL, sizeof(zbx_db_bind_context_t) * self->fields.values_num);
3047 
3048 retry_oracle:
3049 	DBstatement_prepare(sql_command);
3050 
3051 	for (j = 0; j < self->fields.values_num; j++)
3052 	{
3053 		field = (ZBX_FIELD *)self->fields.values[j];
3054 
3055 		if (ZBX_DB_OK > zbx_db_bind_parameter_dyn(&contexts[j], j, field->type,
3056 				(zbx_db_value_t **)self->rows.values, self->rows.values_num))
3057 		{
3058 			for (i = 0; i < j; i++)
3059 				zbx_db_clean_bind_context(&contexts[i]);
3060 
3061 			goto out;
3062 		}
3063 	}
3064 
3065 	if (SUCCEED == ZBX_CHECK_LOG_LEVEL(LOG_LEVEL_DEBUG))
3066 	{
3067 		for (i = 0; i < self->rows.values_num; i++)
3068 		{
3069 			zbx_db_value_t	*values = (zbx_db_value_t *)self->rows.values[i];
3070 			char	*str;
3071 
3072 			str = zbx_db_format_values((ZBX_FIELD **)self->fields.values, values, self->fields.values_num);
3073 			zabbix_log(LOG_LEVEL_DEBUG, "insert [txnlev:%d] [%s]", zbx_db_txn_level(), str);
3074 			zbx_free(str);
3075 		}
3076 	}
3077 
3078 	rc = zbx_db_statement_execute(self->rows.values_num);
3079 
3080 	for (j = 0; j < self->fields.values_num; j++)
3081 		zbx_db_clean_bind_context(&contexts[j]);
3082 
3083 	if (ZBX_DB_DOWN == rc)
3084 	{
3085 		if (0 < tries++)
3086 		{
3087 			zabbix_log(LOG_LEVEL_ERR, "database is down: retrying in %d seconds", ZBX_DB_WAIT_DOWN);
3088 			connection_failure = 1;
3089 			sleep(ZBX_DB_WAIT_DOWN);
3090 		}
3091 
3092 		DBclose();
3093 		DBconnect(ZBX_DB_CONNECT_NORMAL);
3094 
3095 		goto retry_oracle;
3096 	}
3097 
3098 	ret = (ZBX_DB_OK <= rc ? SUCCEED : FAIL);
3099 
3100 #else
3101 	DBbegin_multiple_update(&sql, &sql_alloc, &sql_offset);
3102 
3103 	for (i = 0; i < self->rows.values_num; i++)
3104 	{
3105 		zbx_db_value_t	*values = (zbx_db_value_t *)self->rows.values[i];
3106 
3107 #	ifdef HAVE_MULTIROW_INSERT
3108 		if (16 > sql_offset)
3109 			zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset, sql_command);
3110 #	else
3111 		zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset, sql_command);
3112 #	endif
3113 		for (j = 0; j < self->fields.values_num; j++)
3114 		{
3115 			const zbx_db_value_t	*value = &values[j];
3116 
3117 			field = (const ZBX_FIELD *)self->fields.values[j];
3118 
3119 			zbx_chrcpy_alloc(&sql, &sql_alloc, &sql_offset, delim[0 == j]);
3120 
3121 			switch (field->type)
3122 			{
3123 				case ZBX_TYPE_CHAR:
3124 				case ZBX_TYPE_TEXT:
3125 				case ZBX_TYPE_SHORTTEXT:
3126 				case ZBX_TYPE_LONGTEXT:
3127 					zbx_chrcpy_alloc(&sql, &sql_alloc, &sql_offset, '\'');
3128 					zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset, value->str);
3129 					zbx_chrcpy_alloc(&sql, &sql_alloc, &sql_offset, '\'');
3130 					break;
3131 				case ZBX_TYPE_INT:
3132 					zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, "%d", value->i32);
3133 					break;
3134 				case ZBX_TYPE_FLOAT:
3135 					zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, ZBX_FS_DBL64_SQL, value->dbl);
3136 					break;
3137 				case ZBX_TYPE_UINT:
3138 					zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, ZBX_FS_UI64,
3139 							value->ui64);
3140 					break;
3141 				case ZBX_TYPE_ID:
3142 					zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset,
3143 							DBsql_id_ins(value->ui64));
3144 					break;
3145 				default:
3146 					THIS_SHOULD_NEVER_HAPPEN;
3147 					exit(EXIT_FAILURE);
3148 			}
3149 		}
3150 #	ifdef HAVE_MYSQL
3151 		if (NULL != sql_values)
3152 			zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset, sql_values);
3153 #	endif
3154 
3155 		zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset, ")" ZBX_ROW_DL);
3156 
3157 		if (SUCCEED != (ret = DBexecute_overflowed_sql(&sql, &sql_alloc, &sql_offset)))
3158 			goto out;
3159 	}
3160 
3161 	if (16 < sql_offset)
3162 	{
3163 #	ifdef HAVE_MULTIROW_INSERT
3164 		if (',' == sql[sql_offset - 1])
3165 		{
3166 			sql_offset--;
3167 			zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset, ";\n");
3168 		}
3169 #	endif
3170 		DBend_multiple_update(sql, sql_alloc, sql_offset);
3171 
3172 		if (ZBX_DB_OK > DBexecute("%s", sql))
3173 			ret = FAIL;
3174 	}
3175 #endif
3176 
3177 out:
3178 	zbx_free(sql_command);
3179 
3180 #ifndef HAVE_ORACLE
3181 	zbx_free(sql);
3182 
3183 #	ifdef HAVE_MYSQL
3184 	zbx_free(sql_values);
3185 #	endif
3186 #else
3187 	zbx_free(contexts);
3188 #endif
3189 	return ret;
3190 }
3191 
3192 /******************************************************************************
3193  *                                                                            *
3194  * Function: zbx_db_insert_autoincrement                                      *
3195  *                                                                            *
3196  * Purpose: executes the prepared database bulk insert operation              *
3197  *                                                                            *
3198  * Parameters: self - [IN] the bulk insert data                               *
3199  *                                                                            *
3200  ******************************************************************************/
zbx_db_insert_autoincrement(zbx_db_insert_t * self,const char * field_name)3201 void	zbx_db_insert_autoincrement(zbx_db_insert_t *self, const char *field_name)
3202 {
3203 	int	i;
3204 
3205 	for (i = 0; i < self->fields.values_num; i++)
3206 	{
3207 		ZBX_FIELD	*field = (ZBX_FIELD *)self->fields.values[i];
3208 
3209 		if (ZBX_TYPE_ID == field->type && 0 == strcmp(field_name, field->name))
3210 		{
3211 			self->autoincrement = i;
3212 			return;
3213 		}
3214 	}
3215 
3216 	THIS_SHOULD_NEVER_HAPPEN;
3217 	exit(EXIT_FAILURE);
3218 }
3219 
3220 /******************************************************************************
3221  *                                                                            *
3222  * Function: zbx_db_get_database_type                                         *
3223  *                                                                            *
3224  * Purpose: determine is it a server or a proxy database                      *
3225  *                                                                            *
3226  * Return value: ZBX_DB_SERVER - server database                              *
3227  *               ZBX_DB_PROXY - proxy database                                *
3228  *               ZBX_DB_UNKNOWN - an error occurred                           *
3229  *                                                                            *
3230  ******************************************************************************/
zbx_db_get_database_type(void)3231 int	zbx_db_get_database_type(void)
3232 {
3233 	const char	*result_string;
3234 	DB_RESULT	result;
3235 	int		ret = ZBX_DB_UNKNOWN;
3236 
3237 	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
3238 
3239 	DBconnect(ZBX_DB_CONNECT_NORMAL);
3240 
3241 	if (NULL == (result = DBselectN("select userid from users", 1)))
3242 	{
3243 		zabbix_log(LOG_LEVEL_DEBUG, "cannot select records from \"users\" table");
3244 		goto out;
3245 	}
3246 
3247 	if (NULL != DBfetch(result))
3248 	{
3249 		zabbix_log(LOG_LEVEL_DEBUG, "there is at least 1 record in \"users\" table");
3250 		ret = ZBX_DB_SERVER;
3251 	}
3252 	else
3253 	{
3254 		zabbix_log(LOG_LEVEL_DEBUG, "no records in \"users\" table");
3255 		ret = ZBX_DB_PROXY;
3256 	}
3257 
3258 	DBfree_result(result);
3259 out:
3260 	DBclose();
3261 
3262 	switch (ret)
3263 	{
3264 		case ZBX_DB_SERVER:
3265 			result_string = "ZBX_DB_SERVER";
3266 			break;
3267 		case ZBX_DB_PROXY:
3268 			result_string = "ZBX_DB_PROXY";
3269 			break;
3270 		case ZBX_DB_UNKNOWN:
3271 			result_string = "ZBX_DB_UNKNOWN";
3272 			break;
3273 	}
3274 
3275 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, result_string);
3276 
3277 	return ret;
3278 }
3279 
3280 /******************************************************************************
3281  *                                                                            *
3282  * Function: DBlock_record                                                    *
3283  *                                                                            *
3284  * Purpose: locks a record in a table by its primary key and an optional      *
3285  *          constraint field                                                  *
3286  *                                                                            *
3287  * Parameters: table     - [IN] the target table                              *
3288  *             id        - [IN] primary key value                             *
3289  *             add_field - [IN] additional constraint field name (optional)   *
3290  *             add_id    - [IN] constraint field value                        *
3291  *                                                                            *
3292  * Return value: SUCCEED - the record was successfully locked                 *
3293  *               FAIL    - the table does not contain the specified record    *
3294  *                                                                            *
3295  ******************************************************************************/
DBlock_record(const char * table,zbx_uint64_t id,const char * add_field,zbx_uint64_t add_id)3296 int	DBlock_record(const char *table, zbx_uint64_t id, const char *add_field, zbx_uint64_t add_id)
3297 {
3298 	DB_RESULT	result;
3299 	const ZBX_TABLE	*t;
3300 	int		ret;
3301 
3302 	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
3303 
3304 	if (0 == zbx_db_txn_level())
3305 		zabbix_log(LOG_LEVEL_DEBUG, "%s() called outside of transaction", __func__);
3306 
3307 	t = DBget_table(table);
3308 
3309 	if (NULL == add_field)
3310 	{
3311 		result = DBselect("select null from %s where %s=" ZBX_FS_UI64 ZBX_FOR_UPDATE, table, t->recid, id);
3312 	}
3313 	else
3314 	{
3315 		result = DBselect("select null from %s where %s=" ZBX_FS_UI64 " and %s=" ZBX_FS_UI64 ZBX_FOR_UPDATE,
3316 				table, t->recid, id, add_field, add_id);
3317 	}
3318 
3319 	if (NULL == DBfetch(result))
3320 		ret = FAIL;
3321 	else
3322 		ret = SUCCEED;
3323 
3324 	DBfree_result(result);
3325 
3326 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret));
3327 
3328 	return ret;
3329 }
3330 
3331 /******************************************************************************
3332  *                                                                            *
3333  * Function: DBlock_records                                                   *
3334  *                                                                            *
3335  * Purpose: locks a records in a table by its primary key                     *
3336  *                                                                            *
3337  * Parameters: table     - [IN] the target table                              *
3338  *             ids       - [IN] primary key values                            *
3339  *                                                                            *
3340  * Return value: SUCCEED - one or more of the specified records were          *
3341  *                         successfully locked                                *
3342  *               FAIL    - the table does not contain any of the specified    *
3343  *                         records                                            *
3344  *                                                                            *
3345  ******************************************************************************/
DBlock_records(const char * table,const zbx_vector_uint64_t * ids)3346 int	DBlock_records(const char *table, const zbx_vector_uint64_t *ids)
3347 {
3348 	DB_RESULT	result;
3349 	const ZBX_TABLE	*t;
3350 	int		ret;
3351 	char		*sql = NULL;
3352 	size_t		sql_alloc = 0, sql_offset = 0;
3353 
3354 	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
3355 
3356 	if (0 == zbx_db_txn_level())
3357 		zabbix_log(LOG_LEVEL_DEBUG, "%s() called outside of transaction", __func__);
3358 
3359 	t = DBget_table(table);
3360 
3361 	zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, "select null from %s where", table);
3362 	DBadd_condition_alloc(&sql, &sql_alloc, &sql_offset, t->recid, ids->values, ids->values_num);
3363 
3364 	result = DBselect("%s" ZBX_FOR_UPDATE, sql);
3365 
3366 	zbx_free(sql);
3367 
3368 	if (NULL == DBfetch(result))
3369 		ret = FAIL;
3370 	else
3371 		ret = SUCCEED;
3372 
3373 	DBfree_result(result);
3374 
3375 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret));
3376 
3377 	return ret;
3378 }
3379 
3380 /******************************************************************************
3381  *                                                                            *
3382  * Function: DBlock_ids                                                       *
3383  *                                                                            *
3384  * Purpose: locks a records in a table by field name                          *
3385  *                                                                            *
3386  * Parameters: table      - [IN] the target table                             *
3387  *             field_name - [IN] field name                                   *
3388  *             ids        - [IN/OUT] IN - sorted array of IDs to lock         *
3389  *                                   OUT - resulting array of locked IDs      *
3390  *                                                                            *
3391  * Return value: SUCCEED - one or more of the specified records were          *
3392  *                         successfully locked                                *
3393  *               FAIL    - no records were locked                             *
3394  *                                                                            *
3395  ******************************************************************************/
DBlock_ids(const char * table_name,const char * field_name,zbx_vector_uint64_t * ids)3396 int	DBlock_ids(const char *table_name, const char *field_name, zbx_vector_uint64_t *ids)
3397 {
3398 	char		*sql = NULL;
3399 	size_t		sql_alloc = 0, sql_offset = 0;
3400 	zbx_uint64_t	id;
3401 	int		i;
3402 	DB_RESULT	result;
3403 	DB_ROW		row;
3404 
3405 	if (0 == ids->values_num)
3406 		return FAIL;
3407 
3408 	zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, "select %s from %s where", field_name, table_name);
3409 	DBadd_condition_alloc(&sql, &sql_alloc, &sql_offset, field_name, ids->values, ids->values_num);
3410 	zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, " order by %s" ZBX_FOR_UPDATE, field_name);
3411 	result = DBselect("%s", sql);
3412 	zbx_free(sql);
3413 
3414 	for (i = 0; NULL != (row = DBfetch(result)); i++)
3415 	{
3416 		ZBX_STR2UINT64(id, row[0]);
3417 
3418 		while (id != ids->values[i])
3419 			zbx_vector_uint64_remove(ids, i);
3420 	}
3421 	DBfree_result(result);
3422 
3423 	while (i != ids->values_num)
3424 		zbx_vector_uint64_remove_noorder(ids, i);
3425 
3426 	return (0 != ids->values_num ? SUCCEED : FAIL);
3427 }
3428 
3429 /******************************************************************************
3430  *                                                                            *
3431  * Function: zbx_sql_add_interface_availability                               *
3432  *                                                                            *
3433  * Purpose: adds interface availability update to sql statement               *
3434  *                                                                            *
3435  * Parameters: ia           [IN] the interface availability data              *
3436  *             sql        - [IN/OUT] the sql statement                        *
3437  *             sql_alloc  - [IN/OUT] the number of bytes allocated for sql    *
3438  *                                   statement                                *
3439  *             sql_offset - [IN/OUT] the number of bytes used in sql          *
3440  *                                   statement                                *
3441  *                                                                            *
3442  * Return value: SUCCEED - sql statement is created                           *
3443  *               FAIL    - no interface availability is set                   *
3444  *                                                                            *
3445  ******************************************************************************/
zbx_sql_add_interface_availability(const zbx_interface_availability_t * ia,char ** sql,size_t * sql_alloc,size_t * sql_offset)3446 static int	zbx_sql_add_interface_availability(const zbx_interface_availability_t *ia, char **sql,
3447 		size_t *sql_alloc, size_t *sql_offset)
3448 {
3449 	char		delim = ' ';
3450 
3451 	if (FAIL == zbx_interface_availability_is_set(ia))
3452 		return FAIL;
3453 
3454 	zbx_strcpy_alloc(sql, sql_alloc, sql_offset, "update interface set");
3455 
3456 	if (0 != (ia->agent.flags & ZBX_FLAGS_AGENT_STATUS_AVAILABLE))
3457 	{
3458 		zbx_snprintf_alloc(sql, sql_alloc, sql_offset, "%cavailable=%d", delim, (int)ia->agent.available);
3459 		delim = ',';
3460 	}
3461 
3462 	if (0 != (ia->agent.flags & ZBX_FLAGS_AGENT_STATUS_ERROR))
3463 	{
3464 		char	*error_esc;
3465 
3466 		error_esc = DBdyn_escape_field("interface", "error", ia->agent.error);
3467 		zbx_snprintf_alloc(sql, sql_alloc, sql_offset, "%cerror='%s'", delim, error_esc);
3468 		zbx_free(error_esc);
3469 		delim = ',';
3470 	}
3471 
3472 	if (0 != (ia->agent.flags & ZBX_FLAGS_AGENT_STATUS_ERRORS_FROM))
3473 	{
3474 		zbx_snprintf_alloc(sql, sql_alloc, sql_offset, "%cerrors_from=%d", delim, ia->agent.errors_from);
3475 		delim = ',';
3476 	}
3477 
3478 	if (0 != (ia->agent.flags & ZBX_FLAGS_AGENT_STATUS_DISABLE_UNTIL))
3479 		zbx_snprintf_alloc(sql, sql_alloc, sql_offset, "%cdisable_until=%d", delim, ia->agent.disable_until);
3480 
3481 	zbx_snprintf_alloc(sql, sql_alloc, sql_offset, " where interfaceid=" ZBX_FS_UI64, ia->interfaceid);
3482 
3483 	return SUCCEED;
3484 }
3485 
3486 /******************************************************************************
3487  *                                                                            *
3488  * Function: zbx_db_update_interface_availabilities                           *
3489  *                                                                            *
3490  * Purpose: sync interface availabilities updates into database               *
3491  *                                                                            *
3492  * Parameters: interface_availabilities [IN] the interface availability data  *
3493  *                                                                            *
3494  ******************************************************************************/
zbx_db_update_interface_availabilities(const zbx_vector_availability_ptr_t * interface_availabilities)3495 void	zbx_db_update_interface_availabilities(const zbx_vector_availability_ptr_t *interface_availabilities)
3496 {
3497 	int	txn_error;
3498 	char	*sql = NULL;
3499 	size_t	sql_alloc = 4 * ZBX_KIBIBYTE;
3500 	int	i;
3501 
3502 	sql = (char *)zbx_malloc(sql, sql_alloc);
3503 
3504 	do
3505 	{
3506 		size_t	sql_offset = 0;
3507 
3508 		DBbegin();
3509 		DBbegin_multiple_update(&sql, &sql_alloc, &sql_offset);
3510 
3511 		for (i = 0; i < interface_availabilities->values_num; i++)
3512 		{
3513 			if (SUCCEED != zbx_sql_add_interface_availability(interface_availabilities->values[i], &sql,
3514 					&sql_alloc, &sql_offset))
3515 			{
3516 				continue;
3517 			}
3518 
3519 			zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset, ";\n");
3520 			DBexecute_overflowed_sql(&sql, &sql_alloc, &sql_offset);
3521 		}
3522 
3523 		DBend_multiple_update(&sql, &sql_alloc, &sql_offset);
3524 
3525 		if (16 < sql_offset)
3526 			DBexecute("%s", sql);
3527 
3528 		txn_error = DBcommit();
3529 	}
3530 	while (ZBX_DB_DOWN == txn_error);
3531 
3532 	zbx_free(sql);
3533 }
3534 
3535 /******************************************************************************
3536  *                                                                            *
3537  * Function: DBget_user_by_active_session                                     *
3538  *                                                                            *
3539  * Purpose: validate that session is active and get associated user data      *
3540  *                                                                            *
3541  * Parameters: sessionid - [IN] the session id to validate                    *
3542  *             user      - [OUT] user information                             *
3543  *                                                                            *
3544  * Return value:  SUCCEED - session is active and user data was retrieved     *
3545  *                FAIL    - otherwise                                         *
3546  *                                                                            *
3547  ******************************************************************************/
DBget_user_by_active_session(const char * sessionid,zbx_user_t * user)3548 int	DBget_user_by_active_session(const char *sessionid, zbx_user_t *user)
3549 {
3550 	char		*sessionid_esc;
3551 	int		ret = FAIL;
3552 	DB_RESULT	result;
3553 	DB_ROW		row;
3554 
3555 	zabbix_log(LOG_LEVEL_DEBUG, "In %s() sessionid:%s", __func__, sessionid);
3556 
3557 	sessionid_esc = DBdyn_escape_string(sessionid);
3558 
3559 	if (NULL == (result = DBselect(
3560 			"select u.userid,u.roleid,r.type"
3561 				" from sessions s,users u,role r"
3562 			" where s.userid=u.userid"
3563 				" and s.sessionid='%s'"
3564 				" and s.status=%d"
3565 				" and u.roleid=r.roleid",
3566 			sessionid_esc, ZBX_SESSION_ACTIVE)))
3567 	{
3568 		goto out;
3569 	}
3570 
3571 	if (NULL == (row = DBfetch(result)))
3572 		goto out;
3573 
3574 	ZBX_STR2UINT64(user->userid, row[0]);
3575 	ZBX_STR2UINT64(user->roleid, row[1]);
3576 	user->type = atoi(row[2]);
3577 
3578 	ret = SUCCEED;
3579 out:
3580 	DBfree_result(result);
3581 	zbx_free(sessionid_esc);
3582 
3583 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret));
3584 
3585 	return ret;
3586 }
3587 
3588 /******************************************************************************
3589  *                                                                            *
3590  * Function: DBget_user_by_auth_token                                         *
3591  *                                                                            *
3592  * Purpose: validate that token is not expired and is active and then get     *
3593  *          associated user data                                              *
3594  *                                                                            *
3595  * Parameters: formatted_auth_token_hash - [IN] auth token to validate        *
3596  *             user                      - [OUT] user information             *
3597  *                                                                            *
3598  * Return value:  SUCCEED - token is valid and user data was retrieved        *
3599  *                FAIL    - otherwise                                         *
3600  *                                                                            *
3601  ******************************************************************************/
DBget_user_by_auth_token(const char * formatted_auth_token_hash,zbx_user_t * user)3602 int	DBget_user_by_auth_token(const char *formatted_auth_token_hash, zbx_user_t *user)
3603 {
3604 	int		ret = FAIL;
3605 	DB_RESULT	result;
3606 	DB_ROW		row;
3607 
3608 	zabbix_log(LOG_LEVEL_DEBUG, "In %s() auth token:%s", __func__, formatted_auth_token_hash);
3609 
3610 	if (NULL == (result = DBselect(
3611 			"select u.userid,u.roleid,r.type"
3612 				" from token t,users u,role r"
3613 			" where t.userid=u.userid"
3614 				" and t.token='%s'"
3615 				" and u.roleid=r.roleid"
3616 				" and t.status=%d"
3617 				" and (t.expires_at=%d or t.expires_at > %lu)",
3618 			formatted_auth_token_hash, ZBX_AUTH_TOKEN_ENABLED, ZBX_AUTH_TOKEN_NEVER_EXPIRES, time(NULL))))
3619 	{
3620 		goto out;
3621 	}
3622 
3623 	if (NULL == (row = DBfetch(result)))
3624 		goto out;
3625 
3626 	ZBX_STR2UINT64(user->userid, row[0]);
3627 	ZBX_STR2UINT64(user->roleid, row[1]);
3628 	user->type = atoi(row[2]);
3629 	ret = SUCCEED;
3630 out:
3631 	DBfree_result(result);
3632 
3633 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret));
3634 
3635 	return ret;
3636 }
3637 
3638 /******************************************************************************
3639  *                                                                            *
3640  * Function: zbx_db_mock_field_init                                           *
3641  *                                                                            *
3642  * Purpose: initializes mock field                                            *
3643  *                                                                            *
3644  * Parameters: field      - [OUT] the field data                              *
3645  *             field_type - [IN] the field type in database schema            *
3646  *             field_len  - [IN] the field size in database schema            *
3647  *                                                                            *
3648  ******************************************************************************/
zbx_db_mock_field_init(zbx_db_mock_field_t * field,int field_type,int field_len)3649 void	zbx_db_mock_field_init(zbx_db_mock_field_t *field, int field_type, int field_len)
3650 {
3651 	switch (field_type)
3652 	{
3653 		case ZBX_TYPE_CHAR:
3654 #if defined(HAVE_ORACLE)
3655 			field->chars_num = field_len;
3656 			field->bytes_num = 4000;
3657 #else
3658 			field->chars_num = field_len;
3659 			field->bytes_num = -1;
3660 #endif
3661 			return;
3662 	}
3663 
3664 	THIS_SHOULD_NEVER_HAPPEN;
3665 
3666 	field->chars_num = 0;
3667 	field->bytes_num = 0;
3668 }
3669 
3670 /******************************************************************************
3671  *                                                                            *
3672  * Function: zbx_db_mock_field_append                                         *
3673  *                                                                            *
3674  * Purpose: 'appends' text to the field, if successful the character/byte     *
3675  *           limits are updated                                               *
3676  *                                                                            *
3677  * Parameters: field - [IN/OUT] the mock field                                *
3678  *             text  - [IN] the text to append                                *
3679  *                                                                            *
3680  * Return value: SUCCEED - the field had enough space to append the text      *
3681  *               FAIL    - otherwise                                          *
3682  *                                                                            *
3683  ******************************************************************************/
zbx_db_mock_field_append(zbx_db_mock_field_t * field,const char * text)3684 int	zbx_db_mock_field_append(zbx_db_mock_field_t *field, const char *text)
3685 {
3686 	int	bytes_num, chars_num;
3687 
3688 	if (-1 != field->bytes_num)
3689 	{
3690 		bytes_num = strlen(text);
3691 		if (bytes_num > field->bytes_num)
3692 			return FAIL;
3693 	}
3694 	else
3695 		bytes_num = 0;
3696 
3697 	if (-1 != field->chars_num)
3698 	{
3699 		chars_num = zbx_strlen_utf8(text);
3700 		if (chars_num > field->chars_num)
3701 			return FAIL;
3702 	}
3703 	else
3704 		chars_num = 0;
3705 
3706 	field->bytes_num -= bytes_num;
3707 	field->chars_num -= chars_num;
3708 
3709 	return SUCCEED;
3710 }
3711 
3712 /******************************************************************************
3713  *                                                                            *
3714  * Function: zbx_db_check_instanceid                                          *
3715  *                                                                            *
3716  * Purpose: checks instanceid value in config table and generates new         *
3717  *          instance id if its empty                                          *
3718  *                                                                            *
3719  * Return value: SUCCEED - valid instance id either exists or was created     *
3720  *               FAIL    - no valid instance id exists and could not create   *
3721  *                         one                                                *
3722  *                                                                            *
3723  ******************************************************************************/
zbx_db_check_instanceid(void)3724 int	zbx_db_check_instanceid(void)
3725 {
3726 	DB_RESULT	result;
3727 	DB_ROW		row;
3728 	int		ret = SUCCEED;
3729 
3730 	DBconnect(ZBX_DB_CONNECT_NORMAL);
3731 
3732 	result = DBselect("select configid,instanceid from config order by configid");
3733 	if (NULL != (row = DBfetch(result)))
3734 	{
3735 		if (SUCCEED == DBis_null(row[1]) || '\0' == *row[1])
3736 		{
3737 			char	*token;
3738 
3739 			token = zbx_create_token(0);
3740 			if (ZBX_DB_OK > DBexecute("update config set instanceid='%s' where configid=%s", token, row[0]))
3741 			{
3742 				zabbix_log(LOG_LEVEL_ERR, "cannot update instance id in database");
3743 				ret = FAIL;
3744 			}
3745 			zbx_free(token);
3746 		}
3747 	}
3748 	else
3749 	{
3750 		zabbix_log(LOG_LEVEL_ERR, "cannot read instance id from database");
3751 		ret = FAIL;
3752 	}
3753 	DBfree_result(result);
3754 
3755 	DBclose();
3756 
3757 	return ret;
3758 }
3759 
3760 #if defined(HAVE_POSTGRESQL)
3761 /******************************************************************************
3762  *                                                                            *
3763  * Function: zbx_db_get_schema_esc                                            *
3764  *                                                                            *
3765  * Purpose: returns escaped DB schema name                                    *
3766  *                                                                            *
3767  ******************************************************************************/
zbx_db_get_schema_esc(void)3768 char	*zbx_db_get_schema_esc(void)
3769 {
3770 	static char	*name;
3771 
3772 	if (NULL == name)
3773 	{
3774 		name = DBdyn_escape_string(NULL == CONFIG_DBSCHEMA || '\0' == *CONFIG_DBSCHEMA ?
3775 				"public" : CONFIG_DBSCHEMA);
3776 	}
3777 
3778 	return name;
3779 }
3780 #endif
3781