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 		return DCget_nextid(tablename, num);
848 
849 	return DBget_nextid(tablename, num);
850 }
851 
852 /******************************************************************************
853  *                                                                            *
854  * Function: DBcheck_capabilities                                             *
855  *                                                                            *
856  * Purpose: checks DBMS for optional features and adjusting configuration     *
857  *                                                                            *
858  ******************************************************************************/
DBcheck_capabilities(void)859 void	DBcheck_capabilities(void)
860 {
861 #ifdef HAVE_POSTGRESQL
862 	int	compression_available = OFF;
863 
864 	DBconnect(ZBX_DB_CONNECT_NORMAL);
865 
866 	/* Timescale compression feature is available in PostgreSQL 10.2 and TimescaleDB 1.5.0 */
867 	if (100002 <= zbx_dbms_get_version())
868 	{
869 		DB_RESULT	result;
870 		DB_ROW		row;
871 		int		major, minor, patch, version;
872 
873 		if (NULL == (result = DBselect("select extversion from pg_extension where extname = 'timescaledb'")))
874 			goto out;
875 
876 		if (NULL == (row = DBfetch(result)))
877 			goto clean;
878 
879 		zabbix_log(LOG_LEVEL_DEBUG, "TimescaleDB version: %s", (char*)row[0]);
880 
881 		sscanf((const char*)row[0], "%d.%d.%d", &major, &minor, &patch);
882 		version = major * 10000;
883 		version += minor * 100;
884 		version += patch;
885 
886 		if (10500 <= version)
887 			compression_available = ON;
888 clean:
889 		DBfree_result(result);
890 	}
891 out:
892 	DBexecute("update config set compression_availability=%d", compression_available);
893 
894 	DBclose();
895 #endif
896 }
897 
898 #define MAX_EXPRESSIONS	950
899 
900 #ifdef HAVE_ORACLE
901 #define MIN_NUM_BETWEEN	5	/* minimum number of consecutive values for using "between <id1> and <idN>" */
902 
903 /******************************************************************************
904  *                                                                            *
905  * Function: DBadd_condition_alloc_btw                                        *
906  *                                                                            *
907  * Purpose: Takes an initial part of SQL query and appends a generated        *
908  *          WHERE condition. The WHERE condition is generated from the given  *
909  *          list of values as a mix of <fieldname> BETWEEN <id1> AND <idN>"   *
910  *                                                                            *
911  * Parameters: sql        - [IN/OUT] buffer for SQL query construction        *
912  *             sql_alloc  - [IN/OUT] size of the 'sql' buffer                 *
913  *             sql_offset - [IN/OUT] current position in the 'sql' buffer     *
914  *             fieldname  - [IN] field name to be used in SQL WHERE condition *
915  *             values     - [IN] array of numerical values sorted in          *
916  *                               ascending order to be included in WHERE      *
917  *             num        - [IN] number of elements in 'values' array         *
918  *             seq_len    - [OUT] - array of sequential chains                *
919  *             seq_num    - [OUT] - length of seq_len                         *
920  *             in_num     - [OUT] - number of id for 'IN'                     *
921  *             between_num- [OUT] - number of sequential chains for 'BETWEEN' *
922  *                                                                            *
923  ******************************************************************************/
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)924 static void	DBadd_condition_alloc_btw(char **sql, size_t *sql_alloc, size_t *sql_offset, const char *fieldname,
925 		const zbx_uint64_t *values, const int num, int **seq_len, int *seq_num, int *in_num, int *between_num)
926 {
927 	int		i, len, first, start;
928 	zbx_uint64_t	value;
929 
930 	/* Store lengths of consecutive sequences of values in a temporary array 'seq_len'. */
931 	/* An isolated value is represented as a sequence with length 1. */
932 	*seq_len = (int *)zbx_malloc(*seq_len, num * sizeof(int));
933 
934 	for (i = 1, *seq_num = 0, value = values[0], len = 1; i < num; i++)
935 	{
936 		if (values[i] != ++value)
937 		{
938 			if (MIN_NUM_BETWEEN <= len)
939 				(*between_num)++;
940 			else
941 				*in_num += len;
942 
943 			(*seq_len)[(*seq_num)++] = len;
944 			len = 1;
945 			value = values[i];
946 		}
947 		else
948 			len++;
949 	}
950 
951 	if (MIN_NUM_BETWEEN <= len)
952 		(*between_num)++;
953 	else
954 		*in_num += len;
955 
956 	(*seq_len)[(*seq_num)++] = len;
957 
958 	if (MAX_EXPRESSIONS < *in_num || 1 < *between_num || (0 < *in_num && 0 < *between_num))
959 		zbx_chrcpy_alloc(sql, sql_alloc, sql_offset, '(');
960 
961 	/* compose "between"s */
962 	for (i = 0, first = 1, start = 0; i < *seq_num; i++)
963 	{
964 		if (MIN_NUM_BETWEEN <= (*seq_len)[i])
965 		{
966 			if (1 != first)
967 			{
968 					zbx_strcpy_alloc(sql, sql_alloc, sql_offset, " or ");
969 			}
970 			else
971 				first = 0;
972 
973 			zbx_snprintf_alloc(sql, sql_alloc, sql_offset, "%s between " ZBX_FS_UI64 " and " ZBX_FS_UI64,
974 					fieldname, values[start], values[start + (*seq_len)[i] - 1]);
975 		}
976 
977 		start += (*seq_len)[i];
978 	}
979 
980 	if (0 < *in_num && 0 < *between_num)
981 	{
982 		zbx_strcpy_alloc(sql, sql_alloc, sql_offset, " or ");
983 	}
984 }
985 #endif
986 
987 /******************************************************************************
988  *                                                                            *
989  * Function: DBadd_condition_alloc                                            *
990  *                                                                            *
991  * Purpose: Takes an initial part of SQL query and appends a generated        *
992  *          WHERE condition. The WHERE condition is generated from the given  *
993  *          list of values as a mix of <fieldname> BETWEEN <id1> AND <idN>"   *
994  *          and "<fieldname> IN (<id1>,<id2>,...,<idN>)" elements.            *
995  *                                                                            *
996  * Parameters: sql        - [IN/OUT] buffer for SQL query construction        *
997  *             sql_alloc  - [IN/OUT] size of the 'sql' buffer                 *
998  *             sql_offset - [IN/OUT] current position in the 'sql' buffer     *
999  *             fieldname  - [IN] field name to be used in SQL WHERE condition *
1000  *             values     - [IN] array of numerical values sorted in          *
1001  *                               ascending order to be included in WHERE      *
1002  *             num        - [IN] number of elements in 'values' array         *
1003  *                                                                            *
1004  ******************************************************************************/
DBadd_condition_alloc(char ** sql,size_t * sql_alloc,size_t * sql_offset,const char * fieldname,const zbx_uint64_t * values,const int num)1005 void	DBadd_condition_alloc(char **sql, size_t *sql_alloc, size_t *sql_offset, const char *fieldname,
1006 		const zbx_uint64_t *values, const int num)
1007 {
1008 #ifdef HAVE_ORACLE
1009 	int		start, between_num = 0, in_num = 0, seq_num;
1010 	int		*seq_len = NULL;
1011 #endif
1012 	int		i, in_cnt;
1013 #if defined(HAVE_SQLITE3)
1014 	int		expr_num, expr_cnt = 0;
1015 #endif
1016 	if (0 == num)
1017 		return;
1018 
1019 	zbx_chrcpy_alloc(sql, sql_alloc, sql_offset, ' ');
1020 #ifdef HAVE_ORACLE
1021 	DBadd_condition_alloc_btw(sql, sql_alloc, sql_offset, fieldname, values, num, &seq_len, &seq_num, &in_num,
1022 			&between_num);
1023 
1024 	if (1 < in_num)
1025 		zbx_snprintf_alloc(sql, sql_alloc, sql_offset, "%s in (", fieldname);
1026 
1027 	/* compose "in"s */
1028 	for (i = 0, in_cnt = 0, start = 0; i < seq_num; i++)
1029 	{
1030 		if (MIN_NUM_BETWEEN > seq_len[i])
1031 		{
1032 			if (1 == in_num)
1033 #else
1034 	if (MAX_EXPRESSIONS < num)
1035 		zbx_chrcpy_alloc(sql, sql_alloc, sql_offset, '(');
1036 
1037 #if	defined(HAVE_SQLITE3)
1038 	expr_num = (num + MAX_EXPRESSIONS - 1) / MAX_EXPRESSIONS;
1039 
1040 	if (MAX_EXPRESSIONS < expr_num)
1041 		zbx_chrcpy_alloc(sql, sql_alloc, sql_offset, '(');
1042 #endif
1043 
1044 	if (1 < num)
1045 		zbx_snprintf_alloc(sql, sql_alloc, sql_offset, "%s in (", fieldname);
1046 
1047 	/* compose "in"s */
1048 	for (i = 0, in_cnt = 0; i < num; i++)
1049 	{
1050 			if (1 == num)
1051 #endif
1052 			{
1053 				zbx_snprintf_alloc(sql, sql_alloc, sql_offset, "%s=" ZBX_FS_UI64, fieldname,
1054 #ifdef HAVE_ORACLE
1055 						values[start]);
1056 #else
1057 						values[i]);
1058 #endif
1059 				break;
1060 			}
1061 			else
1062 			{
1063 #ifdef HAVE_ORACLE
1064 				do
1065 				{
1066 #endif
1067 					if (MAX_EXPRESSIONS == in_cnt)
1068 					{
1069 						in_cnt = 0;
1070 						(*sql_offset)--;
1071 #if defined(HAVE_SQLITE3)
1072 						if (MAX_EXPRESSIONS == ++expr_cnt)
1073 						{
1074 							zbx_snprintf_alloc(sql, sql_alloc, sql_offset, ")) or (%s in (",
1075 									fieldname);
1076 							expr_cnt = 0;
1077 						}
1078 						else
1079 						{
1080 #endif
1081 							zbx_snprintf_alloc(sql, sql_alloc, sql_offset, ") or %s in (",
1082 									fieldname);
1083 #if defined(HAVE_SQLITE3)
1084 						}
1085 #endif
1086 					}
1087 
1088 					in_cnt++;
1089 					zbx_snprintf_alloc(sql, sql_alloc, sql_offset, ZBX_FS_UI64 ",",
1090 #ifdef HAVE_ORACLE
1091 							values[start++]);
1092 				}
1093 				while (0 != --seq_len[i]);
1094 			}
1095 		}
1096 		else
1097 			start += seq_len[i];
1098 	}
1099 
1100 	zbx_free(seq_len);
1101 
1102 	if (1 < in_num)
1103 #else
1104 							values[i]);
1105 			}
1106 	}
1107 
1108 	if (1 < num)
1109 #endif
1110 	{
1111 		(*sql_offset)--;
1112 		zbx_chrcpy_alloc(sql, sql_alloc, sql_offset, ')');
1113 	}
1114 
1115 #if defined(HAVE_SQLITE3)
1116 	if (MAX_EXPRESSIONS < expr_num)
1117 		zbx_chrcpy_alloc(sql, sql_alloc, sql_offset, ')');
1118 #endif
1119 #ifdef HAVE_ORACLE
1120 	if (MAX_EXPRESSIONS < in_num || 1 < between_num || (0 < in_num && 0 < between_num))
1121 #else
1122 	if (MAX_EXPRESSIONS < num)
1123 #endif
1124 		zbx_chrcpy_alloc(sql, sql_alloc, sql_offset, ')');
1125 
1126 #undef MAX_EXPRESSIONS
1127 #ifdef HAVE_ORACLE
1128 #undef MIN_NUM_BETWEEN
1129 #endif
1130 }
1131 
1132 /******************************************************************************
1133  *                                                                            *
1134  * Function: DBadd_str_condition_alloc                                        *
1135  *                                                                            *
1136  * Purpose: This function is similar to DBadd_condition_alloc(), except it is *
1137  *          designed for generating WHERE conditions for strings. Hence, this *
1138  *          function is simpler, because only IN condition is possible.       *
1139  *                                                                            *
1140  * Parameters: sql        - [IN/OUT] buffer for SQL query construction        *
1141  *             sql_alloc  - [IN/OUT] size of the 'sql' buffer                 *
1142  *             sql_offset - [IN/OUT] current position in the 'sql' buffer     *
1143  *             fieldname  - [IN] field name to be used in SQL WHERE condition *
1144  *             values     - [IN] array of string values                       *
1145  *             num        - [IN] number of elements in 'values' array         *
1146  *                                                                            *
1147  * Comments: To support Oracle empty values are checked separately (is null   *
1148  *           for Oracle and ='' for the other databases).                     *
1149  *                                                                            *
1150  ******************************************************************************/
DBadd_str_condition_alloc(char ** sql,size_t * sql_alloc,size_t * sql_offset,const char * fieldname,const char ** values,const int num)1151 void	DBadd_str_condition_alloc(char **sql, size_t *sql_alloc, size_t *sql_offset, const char *fieldname,
1152 		const char **values, const int num)
1153 {
1154 #define MAX_EXPRESSIONS	950
1155 
1156 	int	i, cnt = 0;
1157 	char	*value_esc;
1158 	int	values_num = 0, empty_num = 0;
1159 
1160 	if (0 == num)
1161 		return;
1162 
1163 	zbx_chrcpy_alloc(sql, sql_alloc, sql_offset, ' ');
1164 
1165 	for (i = 0; i < num; i++)
1166 	{
1167 		if ('\0' == *values[i])
1168 			empty_num++;
1169 		else
1170 			values_num++;
1171 	}
1172 
1173 	if (MAX_EXPRESSIONS < values_num || (0 != values_num && 0 != empty_num))
1174 		zbx_chrcpy_alloc(sql, sql_alloc, sql_offset, '(');
1175 
1176 	if (0 != empty_num)
1177 	{
1178 		zbx_snprintf_alloc(sql, sql_alloc, sql_offset, "%s" ZBX_SQL_STRCMP, fieldname, ZBX_SQL_STRVAL_EQ(""));
1179 
1180 		if (0 == values_num)
1181 			return;
1182 
1183 		zbx_strcpy_alloc(sql, sql_alloc, sql_offset, " or ");
1184 	}
1185 
1186 	if (1 == values_num)
1187 	{
1188 		for (i = 0; i < num; i++)
1189 		{
1190 			if ('\0' == *values[i])
1191 				continue;
1192 
1193 			value_esc = DBdyn_escape_string(values[i]);
1194 			zbx_snprintf_alloc(sql, sql_alloc, sql_offset, "%s='%s'", fieldname, value_esc);
1195 			zbx_free(value_esc);
1196 		}
1197 
1198 		if (0 != empty_num)
1199 			zbx_chrcpy_alloc(sql, sql_alloc, sql_offset, ')');
1200 		return;
1201 	}
1202 
1203 	zbx_strcpy_alloc(sql, sql_alloc, sql_offset, fieldname);
1204 	zbx_strcpy_alloc(sql, sql_alloc, sql_offset, " in (");
1205 
1206 	for (i = 0; i < num; i++)
1207 	{
1208 		if ('\0' == *values[i])
1209 			continue;
1210 
1211 		if (MAX_EXPRESSIONS == cnt)
1212 		{
1213 			cnt = 0;
1214 			(*sql_offset)--;
1215 			zbx_strcpy_alloc(sql, sql_alloc, sql_offset, ") or ");
1216 			zbx_strcpy_alloc(sql, sql_alloc, sql_offset, fieldname);
1217 			zbx_strcpy_alloc(sql, sql_alloc, sql_offset, " in (");
1218 		}
1219 
1220 		value_esc = DBdyn_escape_string(values[i]);
1221 		zbx_chrcpy_alloc(sql, sql_alloc, sql_offset, '\'');
1222 		zbx_strcpy_alloc(sql, sql_alloc, sql_offset, value_esc);
1223 		zbx_strcpy_alloc(sql, sql_alloc, sql_offset, "',");
1224 		zbx_free(value_esc);
1225 
1226 		cnt++;
1227 	}
1228 
1229 	(*sql_offset)--;
1230 	zbx_chrcpy_alloc(sql, sql_alloc, sql_offset, ')');
1231 
1232 	if (MAX_EXPRESSIONS < values_num || 0 != empty_num)
1233 		zbx_chrcpy_alloc(sql, sql_alloc, sql_offset, ')');
1234 
1235 #undef MAX_EXPRESSIONS
1236 }
1237 
1238 static char	buf_string[640];
1239 
1240 /******************************************************************************
1241  *                                                                            *
1242  * Function: zbx_host_string                                                  *
1243  *                                                                            *
1244  * Return value: <host> or "???" if host not found                            *
1245  *                                                                            *
1246  * Author: Alexander Vladishev                                                *
1247  *                                                                            *
1248  ******************************************************************************/
zbx_host_string(zbx_uint64_t hostid)1249 const char	*zbx_host_string(zbx_uint64_t hostid)
1250 {
1251 	DB_RESULT	result;
1252 	DB_ROW		row;
1253 
1254 	result = DBselect(
1255 			"select host"
1256 			" from hosts"
1257 			" where hostid=" ZBX_FS_UI64,
1258 			hostid);
1259 
1260 	if (NULL != (row = DBfetch(result)))
1261 		zbx_snprintf(buf_string, sizeof(buf_string), "%s", row[0]);
1262 	else
1263 		zbx_snprintf(buf_string, sizeof(buf_string), "???");
1264 
1265 	DBfree_result(result);
1266 
1267 	return buf_string;
1268 }
1269 
1270 /******************************************************************************
1271  *                                                                            *
1272  * Function: zbx_host_key_string                                              *
1273  *                                                                            *
1274  * Return value: <host>:<key> or "???" if item not found                      *
1275  *                                                                            *
1276  * Author: Alexander Vladishev                                                *
1277  *                                                                            *
1278  ******************************************************************************/
zbx_host_key_string(zbx_uint64_t itemid)1279 const char	*zbx_host_key_string(zbx_uint64_t itemid)
1280 {
1281 	DB_RESULT	result;
1282 	DB_ROW		row;
1283 
1284 	result = DBselect(
1285 			"select h.host,i.key_"
1286 			" from hosts h,items i"
1287 			" where h.hostid=i.hostid"
1288 				" and i.itemid=" ZBX_FS_UI64,
1289 			itemid);
1290 
1291 	if (NULL != (row = DBfetch(result)))
1292 		zbx_snprintf(buf_string, sizeof(buf_string), "%s:%s", row[0], row[1]);
1293 	else
1294 		zbx_snprintf(buf_string, sizeof(buf_string), "???");
1295 
1296 	DBfree_result(result);
1297 
1298 	return buf_string;
1299 }
1300 
1301 /******************************************************************************
1302  *                                                                            *
1303  * Function: zbx_check_user_permissions                                       *
1304  *                                                                            *
1305  * Purpose: check if user has access rights to information - full name,       *
1306  *          alias, Email, SMS, etc                                            *
1307  *                                                                            *
1308  * Parameters: userid           - [IN] user who owns the information          *
1309  *             recipient_userid - [IN] user who will receive the information  *
1310  *                                     can be NULL for remote command         *
1311  *                                                                            *
1312  * Return value: SUCCEED - if information receiving user has access rights    *
1313  *               FAIL    - otherwise                                          *
1314  *                                                                            *
1315  * Comments: Users has access rights or can view personal information only    *
1316  *           about themselves and other user who belong to their group.       *
1317  *           "Zabbix Super Admin" can view and has access rights to           *
1318  *           information about any user.                                      *
1319  *                                                                            *
1320  ******************************************************************************/
zbx_check_user_permissions(const zbx_uint64_t * userid,const zbx_uint64_t * recipient_userid)1321 int	zbx_check_user_permissions(const zbx_uint64_t *userid, const zbx_uint64_t *recipient_userid)
1322 {
1323 	DB_RESULT	result;
1324 	DB_ROW		row;
1325 	int		user_type = -1, ret = SUCCEED;
1326 
1327 	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
1328 
1329 	if (NULL == recipient_userid || *userid == *recipient_userid)
1330 		goto out;
1331 
1332 	result = DBselect("select type from users where userid=" ZBX_FS_UI64, *recipient_userid);
1333 
1334 	if (NULL != (row = DBfetch(result)) && FAIL == DBis_null(row[0]))
1335 		user_type = atoi(row[0]);
1336 	DBfree_result(result);
1337 
1338 	if (-1 == user_type)
1339 	{
1340 		zabbix_log(LOG_LEVEL_DEBUG, "%s() cannot check permissions", __func__);
1341 		ret = FAIL;
1342 		goto out;
1343 	}
1344 
1345 	if (USER_TYPE_SUPER_ADMIN != user_type)
1346 	{
1347 		/* check if users are from the same user group */
1348 		result = DBselect(
1349 				"select null"
1350 				" from users_groups ug1"
1351 				" where ug1.userid=" ZBX_FS_UI64
1352 					" and exists (select null"
1353 						" from users_groups ug2"
1354 						" where ug1.usrgrpid=ug2.usrgrpid"
1355 							" and ug2.userid=" ZBX_FS_UI64
1356 					")",
1357 				*userid, *recipient_userid);
1358 
1359 		if (NULL == DBfetch(result))
1360 			ret = FAIL;
1361 		DBfree_result(result);
1362 	}
1363 out:
1364 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret));
1365 
1366 	return ret;
1367 }
1368 
1369 /******************************************************************************
1370  *                                                                            *
1371  * Function: zbx_user_string                                                  *
1372  *                                                                            *
1373  * Return value: "Name Surname (Alias)" or "unknown" if user not found        *
1374  *                                                                            *
1375  * Author: Alexander Vladishev                                                *
1376  *                                                                            *
1377  ******************************************************************************/
zbx_user_string(zbx_uint64_t userid)1378 const char	*zbx_user_string(zbx_uint64_t userid)
1379 {
1380 	DB_RESULT	result;
1381 	DB_ROW		row;
1382 
1383 	result = DBselect("select name,surname,alias from users where userid=" ZBX_FS_UI64, userid);
1384 
1385 	if (NULL != (row = DBfetch(result)))
1386 		zbx_snprintf(buf_string, sizeof(buf_string), "%s %s (%s)", row[0], row[1], row[2]);
1387 	else
1388 		zbx_snprintf(buf_string, sizeof(buf_string), "unknown");
1389 
1390 	DBfree_result(result);
1391 
1392 	return buf_string;
1393 }
1394 
1395 /******************************************************************************
1396  *                                                                            *
1397  * Function: DBget_user_names                                                 *
1398  *                                                                            *
1399  * Purpose: get user alias, name and surname                                  *
1400  *                                                                            *
1401  * Parameters: userid - [IN] user id                                          *
1402  *             alias   - [OUT] user alias                                     *
1403  *             name    - [OUT] user name                                      *
1404  *             surname - [OUT] user surname                                   *
1405  *                                                                            *
1406  * Return value: SUCCEED or FAIL                                              *
1407  *                                                                            *
1408  ******************************************************************************/
DBget_user_names(zbx_uint64_t userid,char ** alias,char ** name,char ** surname)1409 int	DBget_user_names(zbx_uint64_t userid, char **alias, char **name, char **surname)
1410 {
1411 	int		ret = FAIL;
1412 	DB_RESULT	result;
1413 	DB_ROW		row;
1414 
1415 	if (NULL == (result = DBselect(
1416 			"select alias,name,surname"
1417 			" from users"
1418 			" where userid=" ZBX_FS_UI64, userid)))
1419 	{
1420 		goto out;
1421 	}
1422 
1423 	if (NULL == (row = DBfetch(result)))
1424 		goto out;
1425 
1426 	*alias = zbx_strdup(NULL, row[0]);
1427 	*name = zbx_strdup(NULL, row[1]);
1428 	*surname = zbx_strdup(NULL, row[2]);
1429 
1430 	ret = SUCCEED;
1431 out:
1432 	DBfree_result(result);
1433 
1434 	return ret;
1435 }
1436 
1437 /******************************************************************************
1438  *                                                                            *
1439  * Function: DBsql_id_cmp                                                     *
1440  *                                                                            *
1441  * Purpose: construct where condition                                         *
1442  *                                                                            *
1443  * Return value: "=<id>" if id not equal zero,                                *
1444  *               otherwise " is null"                                         *
1445  *                                                                            *
1446  * Author: Alexander Vladishev                                                *
1447  *                                                                            *
1448  * Comments: NB! Do not use this function more than once in same SQL query    *
1449  *                                                                            *
1450  ******************************************************************************/
DBsql_id_cmp(zbx_uint64_t id)1451 const char	*DBsql_id_cmp(zbx_uint64_t id)
1452 {
1453 	static char		buf[22];	/* 1 - '=', 20 - value size, 1 - '\0' */
1454 	static const char	is_null[9] = " is null";
1455 
1456 	if (0 == id)
1457 		return is_null;
1458 
1459 	zbx_snprintf(buf, sizeof(buf), "=" ZBX_FS_UI64, id);
1460 
1461 	return buf;
1462 }
1463 
1464 /******************************************************************************
1465  *                                                                            *
1466  * Function: DBregister_host                                                  *
1467  *                                                                            *
1468  * Purpose: register unknown host and generate event                          *
1469  *                                                                            *
1470  * Parameters: host - host name                                               *
1471  *                                                                            *
1472  * Author: Alexander Vladishev                                                *
1473  *                                                                            *
1474  ******************************************************************************/
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)1475 void	DBregister_host(zbx_uint64_t proxy_hostid, const char *host, const char *ip, const char *dns,
1476 		unsigned short port, unsigned int connection_type, const char *host_metadata, unsigned short flag,
1477 		int now)
1478 {
1479 	zbx_vector_ptr_t	autoreg_hosts;
1480 
1481 	zbx_vector_ptr_create(&autoreg_hosts);
1482 
1483 	DBregister_host_prepare(&autoreg_hosts, host, ip, dns, port, connection_type, host_metadata, flag, now);
1484 	DBregister_host_flush(&autoreg_hosts, proxy_hostid);
1485 
1486 	DBregister_host_clean(&autoreg_hosts);
1487 	zbx_vector_ptr_destroy(&autoreg_hosts);
1488 }
1489 
DBregister_host_active(void)1490 static int	DBregister_host_active(void)
1491 {
1492 	DB_RESULT	result;
1493 	int		ret = SUCCEED;
1494 
1495 	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
1496 
1497 	result = DBselect(
1498 			"select null"
1499 			" from actions"
1500 			" where eventsource=%d"
1501 				" and status=%d",
1502 			EVENT_SOURCE_AUTOREGISTRATION,
1503 			ACTION_STATUS_ACTIVE);
1504 
1505 	if (NULL == DBfetch(result))
1506 		ret = FAIL;
1507 
1508 	DBfree_result(result);
1509 
1510 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret));
1511 
1512 	return ret;
1513 }
1514 
autoreg_host_free(zbx_autoreg_host_t * autoreg_host)1515 static void	autoreg_host_free(zbx_autoreg_host_t *autoreg_host)
1516 {
1517 	zbx_free(autoreg_host->host);
1518 	zbx_free(autoreg_host->ip);
1519 	zbx_free(autoreg_host->dns);
1520 	zbx_free(autoreg_host->host_metadata);
1521 	zbx_free(autoreg_host);
1522 }
1523 
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)1524 void	DBregister_host_prepare(zbx_vector_ptr_t *autoreg_hosts, const char *host, const char *ip, const char *dns,
1525 		unsigned short port, unsigned int connection_type, const char *host_metadata, unsigned short flag,
1526 		int now)
1527 {
1528 	zbx_autoreg_host_t	*autoreg_host;
1529 	int 			i;
1530 
1531 	for (i = 0; i < autoreg_hosts->values_num; i++)	/* duplicate check */
1532 	{
1533 		autoreg_host = (zbx_autoreg_host_t *)autoreg_hosts->values[i];
1534 
1535 		if (0 == strcmp(host, autoreg_host->host))
1536 		{
1537 			zbx_vector_ptr_remove(autoreg_hosts, i);
1538 			autoreg_host_free(autoreg_host);
1539 			break;
1540 		}
1541 	}
1542 
1543 	autoreg_host = (zbx_autoreg_host_t *)zbx_malloc(NULL, sizeof(zbx_autoreg_host_t));
1544 	autoreg_host->autoreg_hostid = autoreg_host->hostid = 0;
1545 	autoreg_host->host = zbx_strdup(NULL, host);
1546 	autoreg_host->ip = zbx_strdup(NULL, ip);
1547 	autoreg_host->dns = zbx_strdup(NULL, dns);
1548 	autoreg_host->port = port;
1549 	autoreg_host->connection_type = connection_type;
1550 	autoreg_host->host_metadata = zbx_strdup(NULL, host_metadata);
1551 	autoreg_host->flag = flag;
1552 	autoreg_host->now = now;
1553 
1554 	zbx_vector_ptr_append(autoreg_hosts, autoreg_host);
1555 }
1556 
autoreg_get_hosts(zbx_vector_ptr_t * autoreg_hosts,zbx_vector_str_t * hosts)1557 static void	autoreg_get_hosts(zbx_vector_ptr_t *autoreg_hosts, zbx_vector_str_t *hosts)
1558 {
1559 	int	i;
1560 
1561 	for (i = 0; i < autoreg_hosts->values_num; i++)
1562 	{
1563 		zbx_autoreg_host_t	*autoreg_host = (zbx_autoreg_host_t *)autoreg_hosts->values[i];
1564 
1565 		zbx_vector_str_append(hosts, autoreg_host->host);
1566 	}
1567 }
1568 
process_autoreg_hosts(zbx_vector_ptr_t * autoreg_hosts,zbx_uint64_t proxy_hostid)1569 static void	process_autoreg_hosts(zbx_vector_ptr_t *autoreg_hosts, zbx_uint64_t proxy_hostid)
1570 {
1571 	DB_RESULT		result;
1572 	DB_ROW			row;
1573 	zbx_vector_str_t	hosts;
1574 	zbx_uint64_t		current_proxy_hostid;
1575 	char			*sql = NULL;
1576 	size_t			sql_alloc = 256, sql_offset;
1577 	zbx_autoreg_host_t	*autoreg_host;
1578 	int			i;
1579 
1580 	sql = (char *)zbx_malloc(sql, sql_alloc);
1581 	zbx_vector_str_create(&hosts);
1582 
1583 	if (0 != proxy_hostid)
1584 	{
1585 		autoreg_get_hosts(autoreg_hosts, &hosts);
1586 
1587 		/* delete from vector if already exist in hosts table */
1588 		sql_offset = 0;
1589 		zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset,
1590 				"select h.host,h.hostid,h.proxy_hostid,a.host_metadata,a.listen_ip,a.listen_dns,"
1591 					"a.listen_port,a.flags,a.autoreg_hostid"
1592 				" from hosts h"
1593 				" left join autoreg_host a"
1594 					" on a.proxy_hostid=h.proxy_hostid and a.host=h.host"
1595 				" where");
1596 		DBadd_str_condition_alloc(&sql, &sql_alloc, &sql_offset, "h.host",
1597 				(const char **)hosts.values, hosts.values_num);
1598 
1599 		result = DBselect("%s", sql);
1600 
1601 		while (NULL != (row = DBfetch(result)))
1602 		{
1603 			for (i = 0; i < autoreg_hosts->values_num; i++)
1604 			{
1605 				autoreg_host = (zbx_autoreg_host_t *)autoreg_hosts->values[i];
1606 
1607 				if (0 != strcmp(autoreg_host->host, row[0]))
1608 					continue;
1609 
1610 				ZBX_STR2UINT64(autoreg_host->hostid, row[1]);
1611 				ZBX_DBROW2UINT64(current_proxy_hostid, row[2]);
1612 
1613 				if (current_proxy_hostid != proxy_hostid || SUCCEED == DBis_null(row[8]) ||
1614 						0 != strcmp(autoreg_host->host_metadata, row[3]) ||
1615 						autoreg_host->flag != atoi(row[7]))
1616 				{
1617 					break;
1618 				}
1619 
1620 				/* process with autoregistration if the connection type was forced and */
1621 				/* is different from the last registered connection type               */
1622 				if (ZBX_CONN_DEFAULT != autoreg_host->flag)
1623 				{
1624 					unsigned short	port;
1625 
1626 					if (FAIL == is_ushort(row[6], &port) || port != autoreg_host->port)
1627 						break;
1628 
1629 					if (ZBX_CONN_IP == autoreg_host->flag && 0 != strcmp(row[4], autoreg_host->ip))
1630 						break;
1631 
1632 					if (ZBX_CONN_DNS == autoreg_host->flag && 0 != strcmp(row[5], autoreg_host->dns))
1633 						break;
1634 				}
1635 
1636 				zbx_vector_ptr_remove(autoreg_hosts, i);
1637 				autoreg_host_free(autoreg_host);
1638 
1639 				break;
1640 			}
1641 
1642 		}
1643 		DBfree_result(result);
1644 
1645 		hosts.values_num = 0;
1646 	}
1647 
1648 	if (0 != autoreg_hosts->values_num)
1649 	{
1650 		autoreg_get_hosts(autoreg_hosts, &hosts);
1651 
1652 		/* update autoreg_id in vector if already exists in autoreg_host table */
1653 		sql_offset = 0;
1654 		zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset,
1655 				"select autoreg_hostid,host"
1656 				" from autoreg_host"
1657 				" where");
1658 		DBadd_str_condition_alloc(&sql, &sql_alloc, &sql_offset, "host",
1659 				(const char **)hosts.values, hosts.values_num);
1660 
1661 		result = DBselect("%s", sql);
1662 
1663 		while (NULL != (row = DBfetch(result)))
1664 		{
1665 			for (i = 0; i < autoreg_hosts->values_num; i++)
1666 			{
1667 				autoreg_host = (zbx_autoreg_host_t *)autoreg_hosts->values[i];
1668 
1669 				if (0 == autoreg_host->autoreg_hostid && 0 == strcmp(autoreg_host->host, row[1]))
1670 				{
1671 					ZBX_STR2UINT64(autoreg_host->autoreg_hostid, row[0]);
1672 					break;
1673 				}
1674 			}
1675 		}
1676 		DBfree_result(result);
1677 
1678 		hosts.values_num = 0;
1679 	}
1680 
1681 	zbx_vector_str_destroy(&hosts);
1682 	zbx_free(sql);
1683 }
1684 
compare_autoreg_host_by_hostid(const void * d1,const void * d2)1685 static int	compare_autoreg_host_by_hostid(const void *d1, const void *d2)
1686 {
1687 	const zbx_autoreg_host_t	*p1 = *(const zbx_autoreg_host_t **)d1;
1688 	const zbx_autoreg_host_t	*p2 = *(const zbx_autoreg_host_t **)d2;
1689 
1690 	ZBX_RETURN_IF_NOT_EQUAL(p1->hostid, p2->hostid);
1691 
1692 	return 0;
1693 }
1694 
DBregister_host_flush(zbx_vector_ptr_t * autoreg_hosts,zbx_uint64_t proxy_hostid)1695 void	DBregister_host_flush(zbx_vector_ptr_t *autoreg_hosts, zbx_uint64_t proxy_hostid)
1696 {
1697 	zbx_autoreg_host_t	*autoreg_host;
1698 	zbx_uint64_t		autoreg_hostid;
1699 	zbx_db_insert_t		db_insert;
1700 	int			i, create = 0, update = 0;
1701 	char			*sql = NULL, *ip_esc, *dns_esc, *host_metadata_esc;
1702 	size_t			sql_alloc = 256, sql_offset = 0;
1703 	zbx_timespec_t		ts = {0, 0};
1704 
1705 	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
1706 
1707 	if (SUCCEED != DBregister_host_active())
1708 		goto exit;
1709 
1710 	process_autoreg_hosts(autoreg_hosts, proxy_hostid);
1711 
1712 	for (i = 0; i < autoreg_hosts->values_num; i++)
1713 	{
1714 		autoreg_host = (zbx_autoreg_host_t *)autoreg_hosts->values[i];
1715 
1716 		if (0 == autoreg_host->autoreg_hostid)
1717 			create++;
1718 	}
1719 
1720 	if (0 != create)
1721 	{
1722 		autoreg_hostid = DBget_maxid_num("autoreg_host", create);
1723 
1724 		zbx_db_insert_prepare(&db_insert, "autoreg_host", "autoreg_hostid", "proxy_hostid", "host", "listen_ip",
1725 				"listen_dns", "listen_port", "tls_accepted", "host_metadata", "flags", NULL);
1726 	}
1727 
1728 	if (0 != (update = autoreg_hosts->values_num - create))
1729 	{
1730 		sql = (char *)zbx_malloc(sql, sql_alloc);
1731 		DBbegin_multiple_update(&sql, &sql_alloc, &sql_offset);
1732 	}
1733 
1734 	zbx_vector_ptr_sort(autoreg_hosts, ZBX_DEFAULT_UINT64_PTR_COMPARE_FUNC);
1735 
1736 	for (i = 0; i < autoreg_hosts->values_num; i++)
1737 	{
1738 		autoreg_host = (zbx_autoreg_host_t *)autoreg_hosts->values[i];
1739 
1740 		if (0 == autoreg_host->autoreg_hostid)
1741 		{
1742 			autoreg_host->autoreg_hostid = autoreg_hostid++;
1743 
1744 			zbx_db_insert_add_values(&db_insert, autoreg_host->autoreg_hostid, proxy_hostid,
1745 					autoreg_host->host, autoreg_host->ip, autoreg_host->dns,
1746 					(int)autoreg_host->port, (int)autoreg_host->connection_type,
1747 					autoreg_host->host_metadata, autoreg_host->flag);
1748 		}
1749 		else
1750 		{
1751 			ip_esc = DBdyn_escape_string(autoreg_host->ip);
1752 			dns_esc = DBdyn_escape_string(autoreg_host->dns);
1753 			host_metadata_esc = DBdyn_escape_string(autoreg_host->host_metadata);
1754 
1755 			zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset,
1756 					"update autoreg_host"
1757 					" set listen_ip='%s',"
1758 						"listen_dns='%s',"
1759 						"listen_port=%hu,"
1760 						"host_metadata='%s',"
1761 						"tls_accepted='%u',"
1762 						"flags=%hu,"
1763 						"proxy_hostid=%s"
1764 					" where autoreg_hostid=" ZBX_FS_UI64 ";\n",
1765 				ip_esc, dns_esc, autoreg_host->port, host_metadata_esc, autoreg_host->connection_type,
1766 				autoreg_host->flag, DBsql_id_ins(proxy_hostid), autoreg_host->autoreg_hostid);
1767 
1768 			zbx_free(host_metadata_esc);
1769 			zbx_free(dns_esc);
1770 			zbx_free(ip_esc);
1771 		}
1772 	}
1773 
1774 	if (0 != create)
1775 	{
1776 		zbx_db_insert_execute(&db_insert);
1777 		zbx_db_insert_clean(&db_insert);
1778 	}
1779 
1780 	if (0 != update)
1781 	{
1782 		DBend_multiple_update(&sql, &sql_alloc, &sql_offset);
1783 		DBexecute("%s", sql);
1784 		zbx_free(sql);
1785 	}
1786 
1787 	zbx_vector_ptr_sort(autoreg_hosts, compare_autoreg_host_by_hostid);
1788 
1789 	for (i = 0; i < autoreg_hosts->values_num; i++)
1790 	{
1791 		autoreg_host = (zbx_autoreg_host_t *)autoreg_hosts->values[i];
1792 
1793 		ts.sec = autoreg_host->now;
1794 		zbx_add_event(EVENT_SOURCE_AUTOREGISTRATION, EVENT_OBJECT_ZABBIX_ACTIVE, autoreg_host->autoreg_hostid,
1795 				&ts, TRIGGER_VALUE_PROBLEM, NULL, NULL, NULL, 0, 0, NULL, 0, NULL, 0, NULL, NULL);
1796 	}
1797 
1798 	zbx_process_events(NULL, NULL);
1799 	zbx_clean_events();
1800 exit:
1801 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__);
1802 }
1803 
DBregister_host_clean(zbx_vector_ptr_t * autoreg_hosts)1804 void	DBregister_host_clean(zbx_vector_ptr_t *autoreg_hosts)
1805 {
1806 	zbx_vector_ptr_clear_ext(autoreg_hosts, (zbx_mem_free_func_t)autoreg_host_free);
1807 }
1808 
1809 /******************************************************************************
1810  *                                                                            *
1811  * Function: DBproxy_register_host                                            *
1812  *                                                                            *
1813  * Purpose: register unknown host                                             *
1814  *                                                                            *
1815  * Parameters: host - host name                                               *
1816  *                                                                            *
1817  * Author: Alexander Vladishev                                                *
1818  *                                                                            *
1819  ******************************************************************************/
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)1820 void	DBproxy_register_host(const char *host, const char *ip, const char *dns, unsigned short port,
1821 		unsigned int connection_type, const char *host_metadata, unsigned short flag)
1822 {
1823 	char	*host_esc, *ip_esc, *dns_esc, *host_metadata_esc;
1824 
1825 	host_esc = DBdyn_escape_field("proxy_autoreg_host", "host", host);
1826 	ip_esc = DBdyn_escape_field("proxy_autoreg_host", "listen_ip", ip);
1827 	dns_esc = DBdyn_escape_field("proxy_autoreg_host", "listen_dns", dns);
1828 	host_metadata_esc = DBdyn_escape_field("proxy_autoreg_host", "host_metadata", host_metadata);
1829 
1830 	DBexecute("insert into proxy_autoreg_host"
1831 			" (clock,host,listen_ip,listen_dns,listen_port,tls_accepted,host_metadata,flags)"
1832 			" values"
1833 			" (%d,'%s','%s','%s',%d,%u,'%s',%d)",
1834 			(int)time(NULL), host_esc, ip_esc, dns_esc, (int)port, connection_type, host_metadata_esc,
1835 			(int)flag);
1836 
1837 	zbx_free(host_metadata_esc);
1838 	zbx_free(dns_esc);
1839 	zbx_free(ip_esc);
1840 	zbx_free(host_esc);
1841 }
1842 
1843 /******************************************************************************
1844  *                                                                            *
1845  * Function: DBexecute_overflowed_sql                                         *
1846  *                                                                            *
1847  * Purpose: execute a set of SQL statements IF it is big enough               *
1848  *                                                                            *
1849  * Author: Dmitry Borovikov                                                   *
1850  *                                                                            *
1851  ******************************************************************************/
DBexecute_overflowed_sql(char ** sql,size_t * sql_alloc,size_t * sql_offset)1852 int	DBexecute_overflowed_sql(char **sql, size_t *sql_alloc, size_t *sql_offset)
1853 {
1854 	int	ret = SUCCEED;
1855 
1856 	if (ZBX_MAX_OVERFLOW_SQL_SIZE < *sql_offset)
1857 	{
1858 #ifdef HAVE_MULTIROW_INSERT
1859 		if (',' == (*sql)[*sql_offset - 1])
1860 		{
1861 			(*sql_offset)--;
1862 			zbx_strcpy_alloc(sql, sql_alloc, sql_offset, ";\n");
1863 		}
1864 #endif
1865 #if defined(HAVE_ORACLE) && 0 == ZBX_MAX_OVERFLOW_SQL_SIZE
1866 		/* make sure we are not called twice without */
1867 		/* putting a new sql into the buffer first */
1868 		if (*sql_offset <= ZBX_SQL_EXEC_FROM)
1869 		{
1870 			THIS_SHOULD_NEVER_HAPPEN;
1871 			return ret;
1872 		}
1873 
1874 		/* Oracle fails with ORA-00911 if it encounters ';' w/o PL/SQL block */
1875 		zbx_rtrim(*sql, ZBX_WHITESPACE ";");
1876 #else
1877 		DBend_multiple_update(sql, sql_alloc, sql_offset);
1878 #endif
1879 		/* For Oracle with max_overflow_sql_size == 0, jump over "begin\n" */
1880 		/* before execution. ZBX_SQL_EXEC_FROM is 0 for all other cases. */
1881 		if (ZBX_DB_OK > DBexecute("%s", *sql + ZBX_SQL_EXEC_FROM))
1882 			ret = FAIL;
1883 
1884 		*sql_offset = 0;
1885 
1886 		DBbegin_multiple_update(sql, sql_alloc, sql_offset);
1887 	}
1888 
1889 	return ret;
1890 }
1891 
1892 /******************************************************************************
1893  *                                                                            *
1894  * Function: DBget_unique_hostname_by_sample                                  *
1895  *                                                                            *
1896  * Purpose: construct a unique host name by the given sample                  *
1897  *                                                                            *
1898  * Parameters: host_name_sample - a host name to start constructing from      *
1899  *             field_name       - field name for host or host visible name    *
1900  *                                                                            *
1901  * Return value: unique host name which does not exist in the database        *
1902  *                                                                            *
1903  * Author: Dmitry Borovikov                                                   *
1904  *                                                                            *
1905  * Comments: the sample cannot be empty                                       *
1906  *           constructs new by adding "_$(number+1)", where "number"          *
1907  *           shows count of the sample itself plus already constructed ones   *
1908  *           host_name_sample is not modified, allocates new memory!          *
1909  *                                                                            *
1910  ******************************************************************************/
DBget_unique_hostname_by_sample(const char * host_name_sample,const char * field_name)1911 char	*DBget_unique_hostname_by_sample(const char *host_name_sample, const char *field_name)
1912 {
1913 	DB_RESULT		result;
1914 	DB_ROW			row;
1915 	int			full_match = 0, i;
1916 	char			*host_name_temp = NULL, *host_name_sample_esc;
1917 	zbx_vector_uint64_t	nums;
1918 	zbx_uint64_t		num = 2;	/* produce alternatives starting from "2" */
1919 	size_t			sz;
1920 
1921 	assert(host_name_sample && *host_name_sample);
1922 
1923 	zabbix_log(LOG_LEVEL_DEBUG, "In %s() sample:'%s'", __func__, host_name_sample);
1924 
1925 	zbx_vector_uint64_create(&nums);
1926 	zbx_vector_uint64_reserve(&nums, 8);
1927 
1928 	sz = strlen(host_name_sample);
1929 	host_name_sample_esc = DBdyn_escape_like_pattern(host_name_sample);
1930 
1931 	result = DBselect(
1932 			"select %s"
1933 			" from hosts"
1934 			" where %s like '%s%%' escape '%c'"
1935 				" and flags<>%d"
1936 				" and status in (%d,%d,%d)",
1937 				field_name, field_name, host_name_sample_esc, ZBX_SQL_LIKE_ESCAPE_CHAR,
1938 			ZBX_FLAG_DISCOVERY_PROTOTYPE,
1939 			HOST_STATUS_MONITORED, HOST_STATUS_NOT_MONITORED, HOST_STATUS_TEMPLATE);
1940 
1941 	zbx_free(host_name_sample_esc);
1942 
1943 	while (NULL != (row = DBfetch(result)))
1944 	{
1945 		zbx_uint64_t	n;
1946 		const char	*p;
1947 
1948 		if (0 != strncmp(row[0], host_name_sample, sz))
1949 			continue;
1950 
1951 		p = row[0] + sz;
1952 
1953 		if ('\0' == *p)
1954 		{
1955 			full_match = 1;
1956 			continue;
1957 		}
1958 
1959 		if ('_' != *p || FAIL == is_uint64(p + 1, &n))
1960 			continue;
1961 
1962 		zbx_vector_uint64_append(&nums, n);
1963 	}
1964 	DBfree_result(result);
1965 
1966 	zbx_vector_uint64_sort(&nums, ZBX_DEFAULT_UINT64_COMPARE_FUNC);
1967 
1968 	if (0 == full_match)
1969 	{
1970 		host_name_temp = zbx_strdup(host_name_temp, host_name_sample);
1971 		goto clean;
1972 	}
1973 
1974 	for (i = 0; i < nums.values_num; i++)
1975 	{
1976 		if (num > nums.values[i])
1977 			continue;
1978 
1979 		if (num < nums.values[i])	/* found, all other will be bigger */
1980 			break;
1981 
1982 		num++;
1983 	}
1984 
1985 	host_name_temp = zbx_dsprintf(host_name_temp, "%s_" ZBX_FS_UI64, host_name_sample, num);
1986 clean:
1987 	zbx_vector_uint64_destroy(&nums);
1988 
1989 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s():'%s'", __func__, host_name_temp);
1990 
1991 	return host_name_temp;
1992 }
1993 
1994 /******************************************************************************
1995  *                                                                            *
1996  * Function: DBsql_id_ins                                                     *
1997  *                                                                            *
1998  * Purpose: construct insert statement                                        *
1999  *                                                                            *
2000  * Return value: "<id>" if id not equal zero,                                 *
2001  *               otherwise "null"                                             *
2002  *                                                                            *
2003  * Author: Alexander Vladishev                                                *
2004  *                                                                            *
2005  ******************************************************************************/
DBsql_id_ins(zbx_uint64_t id)2006 const char	*DBsql_id_ins(zbx_uint64_t id)
2007 {
2008 	static unsigned char	n = 0;
2009 	static char		buf[4][21];	/* 20 - value size, 1 - '\0' */
2010 	static const char	null[5] = "null";
2011 
2012 	if (0 == id)
2013 		return null;
2014 
2015 	n = (n + 1) & 3;
2016 
2017 	zbx_snprintf(buf[n], sizeof(buf[n]), ZBX_FS_UI64, id);
2018 
2019 	return buf[n];
2020 }
2021 
2022 /******************************************************************************
2023  *                                                                            *
2024  * Function: DBget_inventory_field                                            *
2025  *                                                                            *
2026  * Purpose: get corresponding host_inventory field name                       *
2027  *                                                                            *
2028  * Parameters: inventory_link - [IN] field link 1..HOST_INVENTORY_FIELD_COUNT *
2029  *                                                                            *
2030  * Return value: field name or NULL if value of inventory_link is incorrect   *
2031  *                                                                            *
2032  * Author: Alexander Vladishev                                                *
2033  *                                                                            *
2034  ******************************************************************************/
DBget_inventory_field(unsigned char inventory_link)2035 const char	*DBget_inventory_field(unsigned char inventory_link)
2036 {
2037 	static const char	*inventory_fields[HOST_INVENTORY_FIELD_COUNT] =
2038 	{
2039 		"type", "type_full", "name", "alias", "os", "os_full", "os_short", "serialno_a", "serialno_b", "tag",
2040 		"asset_tag", "macaddress_a", "macaddress_b", "hardware", "hardware_full", "software", "software_full",
2041 		"software_app_a", "software_app_b", "software_app_c", "software_app_d", "software_app_e", "contact",
2042 		"location", "location_lat", "location_lon", "notes", "chassis", "model", "hw_arch", "vendor",
2043 		"contract_number", "installer_name", "deployment_status", "url_a", "url_b", "url_c", "host_networks",
2044 		"host_netmask", "host_router", "oob_ip", "oob_netmask", "oob_router", "date_hw_purchase",
2045 		"date_hw_install", "date_hw_expiry", "date_hw_decomm", "site_address_a", "site_address_b",
2046 		"site_address_c", "site_city", "site_state", "site_country", "site_zip", "site_rack", "site_notes",
2047 		"poc_1_name", "poc_1_email", "poc_1_phone_a", "poc_1_phone_b", "poc_1_cell", "poc_1_screen",
2048 		"poc_1_notes", "poc_2_name", "poc_2_email", "poc_2_phone_a", "poc_2_phone_b", "poc_2_cell",
2049 		"poc_2_screen", "poc_2_notes"
2050 	};
2051 
2052 	if (1 > inventory_link || inventory_link > HOST_INVENTORY_FIELD_COUNT)
2053 		return NULL;
2054 
2055 	return inventory_fields[inventory_link - 1];
2056 }
2057 
DBtxn_status(void)2058 int	DBtxn_status(void)
2059 {
2060 	return 0 == zbx_db_txn_error() ? SUCCEED : FAIL;
2061 }
2062 
DBtxn_ongoing(void)2063 int	DBtxn_ongoing(void)
2064 {
2065 	return 0 == zbx_db_txn_level() ? FAIL : SUCCEED;
2066 }
2067 
DBtable_exists(const char * table_name)2068 int	DBtable_exists(const char *table_name)
2069 {
2070 	char		*table_name_esc;
2071 	DB_RESULT	result;
2072 	int		ret;
2073 
2074 	table_name_esc = DBdyn_escape_string(table_name);
2075 
2076 #if defined(HAVE_MYSQL)
2077 	result = DBselect("show tables like '%s'", table_name_esc);
2078 #elif defined(HAVE_ORACLE)
2079 	result = DBselect(
2080 			"select 1"
2081 			" from tab"
2082 			" where tabtype='TABLE'"
2083 				" and lower(tname)='%s'",
2084 			table_name_esc);
2085 #elif defined(HAVE_POSTGRESQL)
2086 	result = DBselect(
2087 			"select 1"
2088 			" from information_schema.tables"
2089 			" where table_name='%s'"
2090 				" and table_schema='%s'",
2091 			table_name_esc, zbx_db_get_schema_esc());
2092 #elif defined(HAVE_SQLITE3)
2093 	result = DBselect(
2094 			"select 1"
2095 			" from sqlite_master"
2096 			" where tbl_name='%s'"
2097 				" and type='table'",
2098 			table_name_esc);
2099 #endif
2100 
2101 	zbx_free(table_name_esc);
2102 
2103 	ret = (NULL == DBfetch(result) ? FAIL : SUCCEED);
2104 
2105 	DBfree_result(result);
2106 
2107 	return ret;
2108 }
2109 
DBfield_exists(const char * table_name,const char * field_name)2110 int	DBfield_exists(const char *table_name, const char *field_name)
2111 {
2112 	DB_RESULT	result;
2113 #if defined(HAVE_MYSQL)
2114 	char		*field_name_esc;
2115 	int		ret;
2116 #elif defined(HAVE_ORACLE)
2117 	char		*table_name_esc, *field_name_esc;
2118 	int		ret;
2119 #elif defined(HAVE_POSTGRESQL)
2120 	char		*table_name_esc, *field_name_esc;
2121 	int		ret;
2122 #elif defined(HAVE_SQLITE3)
2123 	char		*table_name_esc;
2124 	DB_ROW		row;
2125 	int		ret = FAIL;
2126 #endif
2127 
2128 #if defined(HAVE_MYSQL)
2129 	field_name_esc = DBdyn_escape_string(field_name);
2130 
2131 	result = DBselect("show columns from %s like '%s'",
2132 			table_name, field_name_esc);
2133 
2134 	zbx_free(field_name_esc);
2135 
2136 	ret = (NULL == DBfetch(result) ? FAIL : SUCCEED);
2137 
2138 	DBfree_result(result);
2139 #elif defined(HAVE_ORACLE)
2140 	table_name_esc = DBdyn_escape_string(table_name);
2141 	field_name_esc = DBdyn_escape_string(field_name);
2142 
2143 	result = DBselect(
2144 			"select 1"
2145 			" from col"
2146 			" where lower(tname)='%s'"
2147 				" and lower(cname)='%s'",
2148 			table_name_esc, field_name_esc);
2149 
2150 	zbx_free(field_name_esc);
2151 	zbx_free(table_name_esc);
2152 
2153 	ret = (NULL == DBfetch(result) ? FAIL : SUCCEED);
2154 
2155 	DBfree_result(result);
2156 #elif defined(HAVE_POSTGRESQL)
2157 	table_name_esc = DBdyn_escape_string(table_name);
2158 	field_name_esc = DBdyn_escape_string(field_name);
2159 
2160 	result = DBselect(
2161 			"select 1"
2162 			" from information_schema.columns"
2163 			" where table_name='%s'"
2164 				" and column_name='%s'"
2165 				" and table_schema='%s'",
2166 			table_name_esc, field_name_esc, zbx_db_get_schema_esc());
2167 
2168 	zbx_free(field_name_esc);
2169 	zbx_free(table_name_esc);
2170 
2171 	ret = (NULL == DBfetch(result) ? FAIL : SUCCEED);
2172 
2173 	DBfree_result(result);
2174 #elif defined(HAVE_SQLITE3)
2175 	table_name_esc = DBdyn_escape_string(table_name);
2176 
2177 	result = DBselect("PRAGMA table_info('%s')", table_name_esc);
2178 
2179 	zbx_free(table_name_esc);
2180 
2181 	while (NULL != (row = DBfetch(result)))
2182 	{
2183 		if (0 != strcmp(field_name, row[1]))
2184 			continue;
2185 
2186 		ret = SUCCEED;
2187 		break;
2188 	}
2189 	DBfree_result(result);
2190 #endif
2191 
2192 	return ret;
2193 }
2194 
2195 #ifndef HAVE_SQLITE3
DBindex_exists(const char * table_name,const char * index_name)2196 int	DBindex_exists(const char *table_name, const char *index_name)
2197 {
2198 	char		*table_name_esc, *index_name_esc;
2199 	DB_RESULT	result;
2200 	int		ret;
2201 
2202 	table_name_esc = DBdyn_escape_string(table_name);
2203 	index_name_esc = DBdyn_escape_string(index_name);
2204 
2205 #if defined(HAVE_MYSQL)
2206 	result = DBselect(
2207 			"show index from %s"
2208 			" where key_name='%s'",
2209 			table_name_esc, index_name_esc);
2210 #elif defined(HAVE_ORACLE)
2211 	result = DBselect(
2212 			"select 1"
2213 			" from user_indexes"
2214 			" where lower(table_name)='%s'"
2215 				" and lower(index_name)='%s'",
2216 			table_name_esc, index_name_esc);
2217 #elif defined(HAVE_POSTGRESQL)
2218 	result = DBselect(
2219 			"select 1"
2220 			" from pg_indexes"
2221 			" where tablename='%s'"
2222 				" and indexname='%s'"
2223 				" and schemaname='%s'",
2224 			table_name_esc, index_name_esc, zbx_db_get_schema_esc());
2225 #endif
2226 
2227 	ret = (NULL == DBfetch(result) ? FAIL : SUCCEED);
2228 
2229 	DBfree_result(result);
2230 
2231 	zbx_free(table_name_esc);
2232 	zbx_free(index_name_esc);
2233 
2234 	return ret;
2235 }
2236 #endif
2237 
2238 /******************************************************************************
2239  *                                                                            *
2240  * Function: DBselect_uint64                                                  *
2241  *                                                                            *
2242  * Parameters: sql - [IN] sql statement                                       *
2243  *             ids - [OUT] sorted list of selected uint64 values              *
2244  *                                                                            *
2245  ******************************************************************************/
DBselect_uint64(const char * sql,zbx_vector_uint64_t * ids)2246 void	DBselect_uint64(const char *sql, zbx_vector_uint64_t *ids)
2247 {
2248 	DB_RESULT	result;
2249 	DB_ROW		row;
2250 	zbx_uint64_t	id;
2251 
2252 	result = DBselect("%s", sql);
2253 
2254 	while (NULL != (row = DBfetch(result)))
2255 	{
2256 		ZBX_STR2UINT64(id, row[0]);
2257 
2258 		zbx_vector_uint64_append(ids, id);
2259 	}
2260 	DBfree_result(result);
2261 
2262 	zbx_vector_uint64_sort(ids, ZBX_DEFAULT_UINT64_COMPARE_FUNC);
2263 }
2264 
DBexecute_multiple_query(const char * query,const char * field_name,zbx_vector_uint64_t * ids)2265 int	DBexecute_multiple_query(const char *query, const char *field_name, zbx_vector_uint64_t *ids)
2266 {
2267 #define ZBX_MAX_IDS	950
2268 	char	*sql = NULL;
2269 	size_t	sql_alloc = ZBX_KIBIBYTE, sql_offset = 0;
2270 	int	i, ret = SUCCEED;
2271 
2272 	sql = (char *)zbx_malloc(sql, sql_alloc);
2273 
2274 	DBbegin_multiple_update(&sql, &sql_alloc, &sql_offset);
2275 
2276 	for (i = 0; i < ids->values_num; i += ZBX_MAX_IDS)
2277 	{
2278 		zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset, query);
2279 		DBadd_condition_alloc(&sql, &sql_alloc, &sql_offset, field_name,
2280 				&ids->values[i], MIN(ZBX_MAX_IDS, ids->values_num - i));
2281 		zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset, ";\n");
2282 
2283 		if (SUCCEED != (ret = DBexecute_overflowed_sql(&sql, &sql_alloc, &sql_offset)))
2284 			break;
2285 	}
2286 
2287 	if (SUCCEED == ret && sql_offset > 16)	/* in ORACLE always present begin..end; */
2288 	{
2289 		DBend_multiple_update(&sql, &sql_alloc, &sql_offset);
2290 
2291 		if (ZBX_DB_OK > DBexecute("%s", sql))
2292 			ret = FAIL;
2293 	}
2294 
2295 	zbx_free(sql);
2296 
2297 	return ret;
2298 }
2299 
2300 #if defined(HAVE_MYSQL) || defined(HAVE_POSTGRESQL)
zbx_warn_char_set(const char * db_name,const char * char_set)2301 static void	zbx_warn_char_set(const char *db_name, const char *char_set)
2302 {
2303 	zabbix_log(LOG_LEVEL_WARNING, "Zabbix supports only \"" ZBX_SUPPORTED_DB_CHARACTER_SET "\" character set(s)."
2304 			" Database \"%s\" has default character set \"%s\"", db_name, char_set);
2305 }
2306 #endif
2307 
2308 #if defined(HAVE_MYSQL) || defined(HAVE_POSTGRESQL) || defined(HAVE_ORACLE)
zbx_warn_no_charset_info(const char * db_name)2309 static void	zbx_warn_no_charset_info(const char *db_name)
2310 {
2311 	zabbix_log(LOG_LEVEL_WARNING, "Cannot get database \"%s\" character set", db_name);
2312 }
2313 #endif
2314 
2315 #if defined(HAVE_MYSQL)
db_strlist_quote(const char * strlist,char delimiter)2316 static char	*db_strlist_quote(const char *strlist, char delimiter)
2317 {
2318 	const char	*delim;
2319 	char		*str = NULL;
2320 	size_t		str_alloc = 0, str_offset = 0;
2321 
2322 	while (NULL != (delim = strchr(strlist, delimiter)))
2323 	{
2324 		zbx_snprintf_alloc(&str, &str_alloc, &str_offset, "'%.*s',", (int)(delim - strlist), strlist);
2325 		strlist = delim + 1;
2326 	}
2327 
2328 	zbx_snprintf_alloc(&str, &str_alloc, &str_offset, "'%s'", strlist);
2329 
2330 	return str;
2331 }
2332 #endif
2333 
DBcheck_character_set(void)2334 void	DBcheck_character_set(void)
2335 {
2336 #if defined(HAVE_MYSQL)
2337 	char		*database_name_esc, *charset_list, *collation_list;
2338 	DB_RESULT	result;
2339 	DB_ROW		row;
2340 
2341 	database_name_esc = DBdyn_escape_string(CONFIG_DBNAME);
2342 	DBconnect(ZBX_DB_CONNECT_NORMAL);
2343 
2344 	result = DBselect(
2345 			"select default_character_set_name,default_collation_name"
2346 			" from information_schema.SCHEMATA"
2347 			" where schema_name='%s'", database_name_esc);
2348 
2349 	if (NULL == result || NULL == (row = DBfetch(result)))
2350 	{
2351 		zbx_warn_no_charset_info(CONFIG_DBNAME);
2352 	}
2353 	else
2354 	{
2355 		char	*char_set = row[0];
2356 		char	*collation = row[1];
2357 
2358 		if (SUCCEED != str_in_list(ZBX_SUPPORTED_DB_CHARACTER_SET, char_set, ZBX_DB_STRLIST_DELIM))
2359 			zbx_warn_char_set(CONFIG_DBNAME, char_set);
2360 
2361 		if (SUCCEED != str_in_list(ZBX_SUPPORTED_DB_COLLATION, collation, ZBX_DB_STRLIST_DELIM))
2362 		{
2363 			zabbix_log(LOG_LEVEL_WARNING, "Zabbix supports only \"%s\" collation(s)."
2364 					" Database \"%s\" has default collation \"%s\"", ZBX_SUPPORTED_DB_COLLATION,
2365 					CONFIG_DBNAME, collation);
2366 		}
2367 	}
2368 
2369 	DBfree_result(result);
2370 
2371 	charset_list = db_strlist_quote(ZBX_SUPPORTED_DB_CHARACTER_SET, ZBX_DB_STRLIST_DELIM);
2372 	collation_list = db_strlist_quote(ZBX_SUPPORTED_DB_COLLATION, ZBX_DB_STRLIST_DELIM);
2373 
2374 	result = DBselect(
2375 			"select count(*)"
2376 			" from information_schema.`COLUMNS`"
2377 			" where table_schema='%s'"
2378 				" and data_type in ('text','varchar','longtext')"
2379 				" and (character_set_name not in (%s) or collation_name not in (%s))",
2380 			database_name_esc, charset_list, collation_list);
2381 
2382 	zbx_free(collation_list);
2383 	zbx_free(charset_list);
2384 
2385 	if (NULL == result || NULL == (row = DBfetch(result)))
2386 	{
2387 		zabbix_log(LOG_LEVEL_WARNING, "cannot get character set of database \"%s\" tables", CONFIG_DBNAME);
2388 	}
2389 	else if (0 != strcmp("0", row[0]))
2390 	{
2391 		zabbix_log(LOG_LEVEL_WARNING, "character set name or collation name that is not supported by Zabbix"
2392 				" found in %s column(s) of database \"%s\"", row[0], CONFIG_DBNAME);
2393 		zabbix_log(LOG_LEVEL_WARNING, "only character set(s) \"%s\" and corresponding collation(s) \"%s\""
2394 				" should be used in database", ZBX_SUPPORTED_DB_CHARACTER_SET,
2395 				ZBX_SUPPORTED_DB_COLLATION);
2396 	}
2397 
2398 	DBfree_result(result);
2399 	DBclose();
2400 	zbx_free(database_name_esc);
2401 #elif defined(HAVE_ORACLE)
2402 	DB_RESULT	result;
2403 	DB_ROW		row;
2404 
2405 	DBconnect(ZBX_DB_CONNECT_NORMAL);
2406 	result = DBselect(
2407 			"select parameter,value"
2408 			" from NLS_DATABASE_PARAMETERS"
2409 			" where parameter in ('NLS_CHARACTERSET','NLS_NCHAR_CHARACTERSET')");
2410 
2411 	if (NULL == result)
2412 	{
2413 		zbx_warn_no_charset_info(CONFIG_DBNAME);
2414 	}
2415 	else
2416 	{
2417 		while (NULL != (row = DBfetch(result)))
2418 		{
2419 			const char	*parameter = row[0];
2420 			const char	*value = row[1];
2421 
2422 			if (NULL == parameter || NULL == value)
2423 			{
2424 				continue;
2425 			}
2426 			else if (0 == strcasecmp("NLS_CHARACTERSET", parameter) ||
2427 					(0 == strcasecmp("NLS_NCHAR_CHARACTERSET", parameter)))
2428 			{
2429 				if (0 != strcasecmp(ZBX_ORACLE_UTF8_CHARSET, value) &&
2430 						0 != strcasecmp(ZBX_ORACLE_CESU8_CHARSET, value))
2431 				{
2432 					zabbix_log(LOG_LEVEL_WARNING, "database \"%s\" parameter \"%s\" has value"
2433 							" \"%s\". Zabbix supports only \"%s\" or \"%s\" character sets",
2434 							CONFIG_DBNAME, parameter, value,
2435 							ZBX_ORACLE_UTF8_CHARSET, ZBX_ORACLE_CESU8_CHARSET);
2436 				}
2437 			}
2438 		}
2439 	}
2440 
2441 	DBfree_result(result);
2442 	DBclose();
2443 #elif defined(HAVE_POSTGRESQL)
2444 #define OID_LENGTH_MAX		20
2445 
2446 	char		*database_name_esc, oid[OID_LENGTH_MAX];
2447 	DB_RESULT	result;
2448 	DB_ROW		row;
2449 
2450 	database_name_esc = DBdyn_escape_string(CONFIG_DBNAME);
2451 
2452 	DBconnect(ZBX_DB_CONNECT_NORMAL);
2453 	result = DBselect(
2454 			"select pg_encoding_to_char(encoding)"
2455 			" from pg_database"
2456 			" where datname='%s'",
2457 			database_name_esc);
2458 
2459 	if (NULL == result || NULL == (row = DBfetch(result)))
2460 	{
2461 		zbx_warn_no_charset_info(CONFIG_DBNAME);
2462 		goto out;
2463 	}
2464 	else if (strcasecmp(row[0], ZBX_SUPPORTED_DB_CHARACTER_SET))
2465 	{
2466 		zbx_warn_char_set(CONFIG_DBNAME, row[0]);
2467 		goto out;
2468 
2469 	}
2470 
2471 	DBfree_result(result);
2472 
2473 	result = DBselect(
2474 			"select oid"
2475 			" from pg_namespace"
2476 			" where nspname='%s'",
2477 			zbx_db_get_schema_esc());
2478 
2479 	if (NULL == result || NULL == (row = DBfetch(result)) || '\0' == **row)
2480 	{
2481 		zabbix_log(LOG_LEVEL_WARNING, "cannot get character set of database \"%s\" fields", CONFIG_DBNAME);
2482 		goto out;
2483 	}
2484 
2485 	strscpy(oid, *row);
2486 
2487 	DBfree_result(result);
2488 
2489 	result = DBselect(
2490 			"select count(*)"
2491 			" from pg_attribute as a"
2492 				" left join pg_class as c"
2493 					" on c.relfilenode=a.attrelid"
2494 				" left join pg_collation as l"
2495 					" on l.oid=a.attcollation"
2496 			" where atttypid in (25,1043)"
2497 				" and c.relnamespace=%s"
2498 				" and c.relam=0"
2499 				" and l.collname<>'default'",
2500 			oid);
2501 
2502 	if (NULL == result || NULL == (row = DBfetch(result)))
2503 	{
2504 		zabbix_log(LOG_LEVEL_WARNING, "cannot get character set of database \"%s\" fields", CONFIG_DBNAME);
2505 	}
2506 	else if (0 != strcmp("0", row[0]))
2507 	{
2508 		zabbix_log(LOG_LEVEL_WARNING, "database has %s fields with unsupported character set. Zabbix supports"
2509 				" only \"%s\" character set", row[0], ZBX_SUPPORTED_DB_CHARACTER_SET);
2510 	}
2511 
2512 	DBfree_result(result);
2513 
2514 	result = DBselect("show client_encoding");
2515 
2516 	if (NULL == result || NULL == (row = DBfetch(result)))
2517 	{
2518 		zabbix_log(LOG_LEVEL_WARNING, "cannot get info about database \"%s\" client encoding", CONFIG_DBNAME);
2519 	}
2520 	else if (0 != strcasecmp(row[0], ZBX_SUPPORTED_DB_CHARACTER_SET))
2521 	{
2522 		zabbix_log(LOG_LEVEL_WARNING, "client_encoding for database \"%s\" is \"%s\". Zabbix supports only"
2523 				" \"%s\"", CONFIG_DBNAME, row[0], ZBX_SUPPORTED_DB_CHARACTER_SET);
2524 	}
2525 
2526 	DBfree_result(result);
2527 
2528 	result = DBselect("show server_encoding");
2529 
2530 	if (NULL == result || NULL == (row = DBfetch(result)))
2531 	{
2532 		zabbix_log(LOG_LEVEL_WARNING, "cannot get info about database \"%s\" server encoding", CONFIG_DBNAME);
2533 	}
2534 	else if (0 != strcasecmp(row[0], ZBX_SUPPORTED_DB_CHARACTER_SET))
2535 	{
2536 		zabbix_log(LOG_LEVEL_WARNING, "server_encoding for database \"%s\" is \"%s\". Zabbix supports only"
2537 				" \"%s\"", CONFIG_DBNAME, row[0], ZBX_SUPPORTED_DB_CHARACTER_SET);
2538 	}
2539 out:
2540 	DBfree_result(result);
2541 	DBclose();
2542 	zbx_free(database_name_esc);
2543 #endif
2544 }
2545 
2546 #ifdef HAVE_ORACLE
2547 /******************************************************************************
2548  *                                                                            *
2549  * Function: zbx_db_format_values                                             *
2550  *                                                                            *
2551  * Purpose: format bulk operation (insert, update) value list                 *
2552  *                                                                            *
2553  * Parameters: fields     - [IN] the field list                               *
2554  *             values     - [IN] the corresponding value list                 *
2555  *             values_num - [IN] the number of values to format               *
2556  *                                                                            *
2557  * Return value: the formatted value list <value1>,<value2>...                *
2558  *                                                                            *
2559  * Comments: The returned string is allocated by this function and must be    *
2560  *           freed by the caller later.                                       *
2561  *                                                                            *
2562  ******************************************************************************/
zbx_db_format_values(ZBX_FIELD ** fields,const zbx_db_value_t * values,int values_num)2563 static char	*zbx_db_format_values(ZBX_FIELD **fields, const zbx_db_value_t *values, int values_num)
2564 {
2565 	int	i;
2566 	char	*str = NULL;
2567 	size_t	str_alloc = 0, str_offset = 0;
2568 
2569 	for (i = 0; i < values_num; i++)
2570 	{
2571 		ZBX_FIELD		*field = fields[i];
2572 		const zbx_db_value_t	*value = &values[i];
2573 
2574 		if (0 < i)
2575 			zbx_chrcpy_alloc(&str, &str_alloc, &str_offset, ',');
2576 
2577 		switch (field->type)
2578 		{
2579 			case ZBX_TYPE_CHAR:
2580 			case ZBX_TYPE_TEXT:
2581 			case ZBX_TYPE_SHORTTEXT:
2582 			case ZBX_TYPE_LONGTEXT:
2583 				zbx_snprintf_alloc(&str, &str_alloc, &str_offset, "'%s'", value->str);
2584 				break;
2585 			case ZBX_TYPE_FLOAT:
2586 				zbx_snprintf_alloc(&str, &str_alloc, &str_offset, ZBX_FS_DBL64, value->dbl);
2587 				break;
2588 			case ZBX_TYPE_ID:
2589 			case ZBX_TYPE_UINT:
2590 				zbx_snprintf_alloc(&str, &str_alloc, &str_offset, ZBX_FS_UI64, value->ui64);
2591 				break;
2592 			case ZBX_TYPE_INT:
2593 				zbx_snprintf_alloc(&str, &str_alloc, &str_offset, "%d", value->i32);
2594 				break;
2595 			default:
2596 				zbx_strcpy_alloc(&str, &str_alloc, &str_offset, "(unknown type)");
2597 				break;
2598 		}
2599 	}
2600 
2601 	return str;
2602 }
2603 #endif
2604 
2605 /******************************************************************************
2606  *                                                                            *
2607  * Function: zbx_db_insert_clean                                              *
2608  *                                                                            *
2609  * Purpose: releases resources allocated by bulk insert operations            *
2610  *                                                                            *
2611  * Parameters: self        - [IN] the bulk insert data                        *
2612  *                                                                            *
2613  ******************************************************************************/
zbx_db_insert_clean(zbx_db_insert_t * self)2614 void	zbx_db_insert_clean(zbx_db_insert_t *self)
2615 {
2616 	int	i, j;
2617 
2618 	for (i = 0; i < self->rows.values_num; i++)
2619 	{
2620 		zbx_db_value_t	*row = (zbx_db_value_t *)self->rows.values[i];
2621 
2622 		for (j = 0; j < self->fields.values_num; j++)
2623 		{
2624 			ZBX_FIELD	*field = (ZBX_FIELD *)self->fields.values[j];
2625 
2626 			switch (field->type)
2627 			{
2628 				case ZBX_TYPE_CHAR:
2629 				case ZBX_TYPE_TEXT:
2630 				case ZBX_TYPE_SHORTTEXT:
2631 				case ZBX_TYPE_LONGTEXT:
2632 					zbx_free(row[j].str);
2633 			}
2634 		}
2635 
2636 		zbx_free(row);
2637 	}
2638 
2639 	zbx_vector_ptr_destroy(&self->rows);
2640 
2641 	zbx_vector_ptr_destroy(&self->fields);
2642 }
2643 
2644 /******************************************************************************
2645  *                                                                            *
2646  * Function: zbx_db_insert_prepare_dyn                                        *
2647  *                                                                            *
2648  * Purpose: prepare for database bulk insert operation                        *
2649  *                                                                            *
2650  * Parameters: self        - [IN] the bulk insert data                        *
2651  *             table       - [IN] the target table name                       *
2652  *             fields      - [IN] names of the fields to insert               *
2653  *             fields_num  - [IN] the number of items in fields array         *
2654  *                                                                            *
2655  * Comments: The operation fails if the target table does not have the        *
2656  *           specified fields defined in its schema.                          *
2657  *                                                                            *
2658  *           Usage example:                                                   *
2659  *             zbx_db_insert_t ins;                                           *
2660  *                                                                            *
2661  *             zbx_db_insert_prepare(&ins, "history", "id", "value");         *
2662  *             zbx_db_insert_add_values(&ins, (zbx_uint64_t)1, 1.0);          *
2663  *             zbx_db_insert_add_values(&ins, (zbx_uint64_t)2, 2.0);          *
2664  *               ...                                                          *
2665  *             zbx_db_insert_execute(&ins);                                   *
2666  *             zbx_db_insert_clean(&ins);                                     *
2667  *                                                                            *
2668  ******************************************************************************/
zbx_db_insert_prepare_dyn(zbx_db_insert_t * self,const ZBX_TABLE * table,const ZBX_FIELD ** fields,int fields_num)2669 void	zbx_db_insert_prepare_dyn(zbx_db_insert_t *self, const ZBX_TABLE *table, const ZBX_FIELD **fields, int fields_num)
2670 {
2671 	int	i;
2672 
2673 	if (0 == fields_num)
2674 	{
2675 		THIS_SHOULD_NEVER_HAPPEN;
2676 		exit(EXIT_FAILURE);
2677 	}
2678 
2679 	self->autoincrement = -1;
2680 
2681 	zbx_vector_ptr_create(&self->fields);
2682 	zbx_vector_ptr_create(&self->rows);
2683 
2684 	self->table = table;
2685 
2686 	for (i = 0; i < fields_num; i++)
2687 		zbx_vector_ptr_append(&self->fields, (ZBX_FIELD *)fields[i]);
2688 }
2689 
2690 /******************************************************************************
2691  *                                                                            *
2692  * Function: zbx_db_insert_prepare                                            *
2693  *                                                                            *
2694  * Purpose: prepare for database bulk insert operation                        *
2695  *                                                                            *
2696  * Parameters: self  - [IN] the bulk insert data                              *
2697  *             table - [IN] the target table name                             *
2698  *             ...   - [IN] names of the fields to insert                     *
2699  *             NULL  - [IN] terminating NULL pointer                          *
2700  *                                                                            *
2701  * Comments: This is a convenience wrapper for zbx_db_insert_prepare_dyn()    *
2702  *           function.                                                        *
2703  *                                                                            *
2704  ******************************************************************************/
zbx_db_insert_prepare(zbx_db_insert_t * self,const char * table,...)2705 void	zbx_db_insert_prepare(zbx_db_insert_t *self, const char *table, ...)
2706 {
2707 	zbx_vector_ptr_t	fields;
2708 	va_list			args;
2709 	char			*field;
2710 	const ZBX_TABLE		*ptable;
2711 	const ZBX_FIELD		*pfield;
2712 
2713 	/* find the table and fields in database schema */
2714 	if (NULL == (ptable = DBget_table(table)))
2715 	{
2716 		THIS_SHOULD_NEVER_HAPPEN;
2717 		exit(EXIT_FAILURE);
2718 	}
2719 
2720 	va_start(args, table);
2721 
2722 	zbx_vector_ptr_create(&fields);
2723 
2724 	while (NULL != (field = va_arg(args, char *)))
2725 	{
2726 		if (NULL == (pfield = DBget_field(ptable, field)))
2727 		{
2728 			zabbix_log(LOG_LEVEL_ERR, "Cannot locate table \"%s\" field \"%s\" in database schema",
2729 					table, field);
2730 			THIS_SHOULD_NEVER_HAPPEN;
2731 			exit(EXIT_FAILURE);
2732 		}
2733 		zbx_vector_ptr_append(&fields, (ZBX_FIELD *)pfield);
2734 	}
2735 
2736 	va_end(args);
2737 
2738 	zbx_db_insert_prepare_dyn(self, ptable, (const ZBX_FIELD **)fields.values, fields.values_num);
2739 
2740 	zbx_vector_ptr_destroy(&fields);
2741 }
2742 
2743 /******************************************************************************
2744  *                                                                            *
2745  * Function: zbx_db_insert_add_values_dyn                                     *
2746  *                                                                            *
2747  * Purpose: adds row values for database bulk insert operation                *
2748  *                                                                            *
2749  * Parameters: self        - [IN] the bulk insert data                        *
2750  *             values      - [IN] the values to insert                        *
2751  *             fields_num  - [IN] the number of items in values array         *
2752  *                                                                            *
2753  * Comments: The values must be listed in the same order as the field names   *
2754  *           for insert preparation functions.                                *
2755  *                                                                            *
2756  ******************************************************************************/
zbx_db_insert_add_values_dyn(zbx_db_insert_t * self,const zbx_db_value_t ** values,int values_num)2757 void	zbx_db_insert_add_values_dyn(zbx_db_insert_t *self, const zbx_db_value_t **values, int values_num)
2758 {
2759 	int		i;
2760 	zbx_db_value_t	*row;
2761 
2762 	if (values_num != self->fields.values_num)
2763 	{
2764 		THIS_SHOULD_NEVER_HAPPEN;
2765 		exit(EXIT_FAILURE);
2766 	}
2767 
2768 	row = (zbx_db_value_t *)zbx_malloc(NULL, self->fields.values_num * sizeof(zbx_db_value_t));
2769 
2770 	for (i = 0; i < self->fields.values_num; i++)
2771 	{
2772 		ZBX_FIELD		*field = (ZBX_FIELD *)self->fields.values[i];
2773 		const zbx_db_value_t	*value = values[i];
2774 
2775 		switch (field->type)
2776 		{
2777 			case ZBX_TYPE_LONGTEXT:
2778 			case ZBX_TYPE_CHAR:
2779 			case ZBX_TYPE_TEXT:
2780 			case ZBX_TYPE_SHORTTEXT:
2781 #ifdef HAVE_ORACLE
2782 				row[i].str = DBdyn_escape_field_len(field, value->str, ESCAPE_SEQUENCE_OFF);
2783 #else
2784 				row[i].str = DBdyn_escape_field_len(field, value->str, ESCAPE_SEQUENCE_ON);
2785 #endif
2786 				break;
2787 			default:
2788 				row[i] = *value;
2789 				break;
2790 		}
2791 	}
2792 
2793 	zbx_vector_ptr_append(&self->rows, row);
2794 }
2795 
2796 /******************************************************************************
2797  *                                                                            *
2798  * Function: zbx_db_insert_add_values                                         *
2799  *                                                                            *
2800  * Purpose: adds row values for database bulk insert operation                *
2801  *                                                                            *
2802  * Parameters: self - [IN] the bulk insert data                               *
2803  *             ...  - [IN] the values to insert                               *
2804  *                                                                            *
2805  * Comments: This is a convenience wrapper for zbx_db_insert_add_values_dyn() *
2806  *           function.                                                        *
2807  *           Note that the types of the passed values must conform to the     *
2808  *           corresponding field types.                                       *
2809  *                                                                            *
2810  ******************************************************************************/
zbx_db_insert_add_values(zbx_db_insert_t * self,...)2811 void	zbx_db_insert_add_values(zbx_db_insert_t *self, ...)
2812 {
2813 	zbx_vector_ptr_t	values;
2814 	va_list			args;
2815 	int			i;
2816 	ZBX_FIELD		*field;
2817 	zbx_db_value_t		*value;
2818 
2819 	va_start(args, self);
2820 
2821 	zbx_vector_ptr_create(&values);
2822 
2823 	for (i = 0; i < self->fields.values_num; i++)
2824 	{
2825 		field = (ZBX_FIELD *)self->fields.values[i];
2826 
2827 		value = (zbx_db_value_t *)zbx_malloc(NULL, sizeof(zbx_db_value_t));
2828 
2829 		switch (field->type)
2830 		{
2831 			case ZBX_TYPE_CHAR:
2832 			case ZBX_TYPE_TEXT:
2833 			case ZBX_TYPE_SHORTTEXT:
2834 			case ZBX_TYPE_LONGTEXT:
2835 				value->str = va_arg(args, char *);
2836 				break;
2837 			case ZBX_TYPE_INT:
2838 				value->i32 = va_arg(args, int);
2839 				break;
2840 			case ZBX_TYPE_FLOAT:
2841 				value->dbl = va_arg(args, double);
2842 				break;
2843 			case ZBX_TYPE_UINT:
2844 			case ZBX_TYPE_ID:
2845 				value->ui64 = va_arg(args, zbx_uint64_t);
2846 				break;
2847 			default:
2848 				THIS_SHOULD_NEVER_HAPPEN;
2849 				exit(EXIT_FAILURE);
2850 		}
2851 
2852 		zbx_vector_ptr_append(&values, value);
2853 	}
2854 
2855 	va_end(args);
2856 
2857 	zbx_db_insert_add_values_dyn(self, (const zbx_db_value_t **)values.values, values.values_num);
2858 
2859 	zbx_vector_ptr_clear_ext(&values, zbx_ptr_free);
2860 	zbx_vector_ptr_destroy(&values);
2861 }
2862 
2863 /******************************************************************************
2864  *                                                                            *
2865  * Function: zbx_db_insert_execute                                            *
2866  *                                                                            *
2867  * Purpose: executes the prepared database bulk insert operation              *
2868  *                                                                            *
2869  * Parameters: self - [IN] the bulk insert data                               *
2870  *                                                                            *
2871  * Return value: Returns SUCCEED if the operation completed successfully or   *
2872  *               FAIL otherwise.                                              *
2873  *                                                                            *
2874  ******************************************************************************/
zbx_db_insert_execute(zbx_db_insert_t * self)2875 int	zbx_db_insert_execute(zbx_db_insert_t *self)
2876 {
2877 	int		ret = FAIL, i, j;
2878 	const ZBX_FIELD	*field;
2879 	char		*sql_command, delim[2] = {',', '('};
2880 	size_t		sql_command_alloc = 512, sql_command_offset = 0;
2881 
2882 #ifndef HAVE_ORACLE
2883 	char		*sql;
2884 	size_t		sql_alloc = 16 * ZBX_KIBIBYTE, sql_offset = 0;
2885 
2886 #	ifdef HAVE_MYSQL
2887 	char		*sql_values = NULL;
2888 	size_t		sql_values_alloc = 0, sql_values_offset = 0;
2889 #	endif
2890 #else
2891 	zbx_db_bind_context_t	*contexts;
2892 	int			rc, tries = 0;
2893 #endif
2894 
2895 	if (0 == self->rows.values_num)
2896 		return SUCCEED;
2897 
2898 	/* process the auto increment field */
2899 	if (-1 != self->autoincrement)
2900 	{
2901 		zbx_uint64_t	id;
2902 
2903 		id = DBget_maxid_num(self->table->table, self->rows.values_num);
2904 
2905 		for (i = 0; i < self->rows.values_num; i++)
2906 		{
2907 			zbx_db_value_t	*values = (zbx_db_value_t *)self->rows.values[i];
2908 
2909 			values[self->autoincrement].ui64 = id++;
2910 		}
2911 	}
2912 
2913 #ifndef HAVE_ORACLE
2914 	sql = (char *)zbx_malloc(NULL, sql_alloc);
2915 #endif
2916 	sql_command = (char *)zbx_malloc(NULL, sql_command_alloc);
2917 
2918 	/* create sql insert statement command */
2919 
2920 	zbx_strcpy_alloc(&sql_command, &sql_command_alloc, &sql_command_offset, "insert into ");
2921 	zbx_strcpy_alloc(&sql_command, &sql_command_alloc, &sql_command_offset, self->table->table);
2922 	zbx_chrcpy_alloc(&sql_command, &sql_command_alloc, &sql_command_offset, ' ');
2923 
2924 	for (i = 0; i < self->fields.values_num; i++)
2925 	{
2926 		field = (ZBX_FIELD *)self->fields.values[i];
2927 
2928 		zbx_chrcpy_alloc(&sql_command, &sql_command_alloc, &sql_command_offset, delim[0 == i]);
2929 		zbx_strcpy_alloc(&sql_command, &sql_command_alloc, &sql_command_offset, field->name);
2930 	}
2931 
2932 #ifdef HAVE_MYSQL
2933 	/* MySQL workaround - explicitly add missing text fields with '' default value */
2934 	for (field = (const ZBX_FIELD *)self->table->fields; NULL != field->name; field++)
2935 	{
2936 		switch (field->type)
2937 		{
2938 			case ZBX_TYPE_BLOB:
2939 			case ZBX_TYPE_TEXT:
2940 			case ZBX_TYPE_SHORTTEXT:
2941 			case ZBX_TYPE_LONGTEXT:
2942 				if (FAIL != zbx_vector_ptr_search(&self->fields, (void *)field,
2943 						ZBX_DEFAULT_PTR_COMPARE_FUNC))
2944 				{
2945 					continue;
2946 				}
2947 
2948 				zbx_chrcpy_alloc(&sql_command, &sql_command_alloc, &sql_command_offset, ',');
2949 				zbx_strcpy_alloc(&sql_command, &sql_command_alloc, &sql_command_offset, field->name);
2950 
2951 				zbx_strcpy_alloc(&sql_values, &sql_values_alloc, &sql_values_offset, ",''");
2952 				break;
2953 		}
2954 	}
2955 #endif
2956 	zbx_strcpy_alloc(&sql_command, &sql_command_alloc, &sql_command_offset, ") values ");
2957 
2958 #ifdef HAVE_ORACLE
2959 	for (i = 0; i < self->fields.values_num; i++)
2960 	{
2961 		zbx_chrcpy_alloc(&sql_command, &sql_command_alloc, &sql_command_offset, delim[0 == i]);
2962 		zbx_snprintf_alloc(&sql_command, &sql_command_alloc, &sql_command_offset, ":%d", i + 1);
2963 	}
2964 	zbx_chrcpy_alloc(&sql_command, &sql_command_alloc, &sql_command_offset, ')');
2965 
2966 	contexts = (zbx_db_bind_context_t *)zbx_malloc(NULL, sizeof(zbx_db_bind_context_t) * self->fields.values_num);
2967 
2968 retry_oracle:
2969 	DBstatement_prepare(sql_command);
2970 
2971 	for (j = 0; j < self->fields.values_num; j++)
2972 	{
2973 		field = (ZBX_FIELD *)self->fields.values[j];
2974 
2975 		if (ZBX_DB_OK > zbx_db_bind_parameter_dyn(&contexts[j], j, field->type,
2976 				(zbx_db_value_t **)self->rows.values, self->rows.values_num))
2977 		{
2978 			for (i = 0; i < j; i++)
2979 				zbx_db_clean_bind_context(&contexts[i]);
2980 
2981 			goto out;
2982 		}
2983 	}
2984 
2985 	if (SUCCEED == ZBX_CHECK_LOG_LEVEL(LOG_LEVEL_DEBUG))
2986 	{
2987 		for (i = 0; i < self->rows.values_num; i++)
2988 		{
2989 			zbx_db_value_t	*values = (zbx_db_value_t *)self->rows.values[i];
2990 			char	*str;
2991 
2992 			str = zbx_db_format_values((ZBX_FIELD **)self->fields.values, values, self->fields.values_num);
2993 			zabbix_log(LOG_LEVEL_DEBUG, "insert [txnlev:%d] [%s]", zbx_db_txn_level(), str);
2994 			zbx_free(str);
2995 		}
2996 	}
2997 
2998 	rc = zbx_db_statement_execute(self->rows.values_num);
2999 
3000 	for (j = 0; j < self->fields.values_num; j++)
3001 		zbx_db_clean_bind_context(&contexts[j]);
3002 
3003 	if (ZBX_DB_DOWN == rc)
3004 	{
3005 		if (0 < tries++)
3006 		{
3007 			zabbix_log(LOG_LEVEL_ERR, "database is down: retrying in %d seconds", ZBX_DB_WAIT_DOWN);
3008 			connection_failure = 1;
3009 			sleep(ZBX_DB_WAIT_DOWN);
3010 		}
3011 
3012 		DBclose();
3013 		DBconnect(ZBX_DB_CONNECT_NORMAL);
3014 
3015 		goto retry_oracle;
3016 	}
3017 
3018 	ret = (ZBX_DB_OK <= rc ? SUCCEED : FAIL);
3019 
3020 #else
3021 	DBbegin_multiple_update(&sql, &sql_alloc, &sql_offset);
3022 
3023 	for (i = 0; i < self->rows.values_num; i++)
3024 	{
3025 		zbx_db_value_t	*values = (zbx_db_value_t *)self->rows.values[i];
3026 
3027 #	ifdef HAVE_MULTIROW_INSERT
3028 		if (16 > sql_offset)
3029 			zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset, sql_command);
3030 #	else
3031 		zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset, sql_command);
3032 #	endif
3033 		for (j = 0; j < self->fields.values_num; j++)
3034 		{
3035 			const zbx_db_value_t	*value = &values[j];
3036 
3037 			field = (const ZBX_FIELD *)self->fields.values[j];
3038 
3039 			zbx_chrcpy_alloc(&sql, &sql_alloc, &sql_offset, delim[0 == j]);
3040 
3041 			switch (field->type)
3042 			{
3043 				case ZBX_TYPE_CHAR:
3044 				case ZBX_TYPE_TEXT:
3045 				case ZBX_TYPE_SHORTTEXT:
3046 				case ZBX_TYPE_LONGTEXT:
3047 					zbx_chrcpy_alloc(&sql, &sql_alloc, &sql_offset, '\'');
3048 					zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset, value->str);
3049 					zbx_chrcpy_alloc(&sql, &sql_alloc, &sql_offset, '\'');
3050 					break;
3051 				case ZBX_TYPE_INT:
3052 					zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, "%d", value->i32);
3053 					break;
3054 				case ZBX_TYPE_FLOAT:
3055 					zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, ZBX_FS_DBL64_SQL, value->dbl);
3056 					break;
3057 				case ZBX_TYPE_UINT:
3058 					zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, ZBX_FS_UI64,
3059 							value->ui64);
3060 					break;
3061 				case ZBX_TYPE_ID:
3062 					zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset,
3063 							DBsql_id_ins(value->ui64));
3064 					break;
3065 				default:
3066 					THIS_SHOULD_NEVER_HAPPEN;
3067 					exit(EXIT_FAILURE);
3068 			}
3069 		}
3070 #	ifdef HAVE_MYSQL
3071 		if (NULL != sql_values)
3072 			zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset, sql_values);
3073 #	endif
3074 
3075 		zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset, ")" ZBX_ROW_DL);
3076 
3077 		if (SUCCEED != (ret = DBexecute_overflowed_sql(&sql, &sql_alloc, &sql_offset)))
3078 			goto out;
3079 	}
3080 
3081 	if (16 < sql_offset)
3082 	{
3083 #	ifdef HAVE_MULTIROW_INSERT
3084 		if (',' == sql[sql_offset - 1])
3085 		{
3086 			sql_offset--;
3087 			zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset, ";\n");
3088 		}
3089 #	endif
3090 		DBend_multiple_update(sql, sql_alloc, sql_offset);
3091 
3092 		if (ZBX_DB_OK > DBexecute("%s", sql))
3093 			ret = FAIL;
3094 	}
3095 #endif
3096 
3097 out:
3098 	zbx_free(sql_command);
3099 
3100 #ifndef HAVE_ORACLE
3101 	zbx_free(sql);
3102 
3103 #	ifdef HAVE_MYSQL
3104 	zbx_free(sql_values);
3105 #	endif
3106 #else
3107 	zbx_free(contexts);
3108 #endif
3109 	return ret;
3110 }
3111 
3112 /******************************************************************************
3113  *                                                                            *
3114  * Function: zbx_db_insert_autoincrement                                      *
3115  *                                                                            *
3116  * Purpose: executes the prepared database bulk insert operation              *
3117  *                                                                            *
3118  * Parameters: self - [IN] the bulk insert data                               *
3119  *                                                                            *
3120  ******************************************************************************/
zbx_db_insert_autoincrement(zbx_db_insert_t * self,const char * field_name)3121 void	zbx_db_insert_autoincrement(zbx_db_insert_t *self, const char *field_name)
3122 {
3123 	int	i;
3124 
3125 	for (i = 0; i < self->fields.values_num; i++)
3126 	{
3127 		ZBX_FIELD	*field = (ZBX_FIELD *)self->fields.values[i];
3128 
3129 		if (ZBX_TYPE_ID == field->type && 0 == strcmp(field_name, field->name))
3130 		{
3131 			self->autoincrement = i;
3132 			return;
3133 		}
3134 	}
3135 
3136 	THIS_SHOULD_NEVER_HAPPEN;
3137 	exit(EXIT_FAILURE);
3138 }
3139 
3140 /******************************************************************************
3141  *                                                                            *
3142  * Function: zbx_db_get_database_type                                         *
3143  *                                                                            *
3144  * Purpose: determine is it a server or a proxy database                      *
3145  *                                                                            *
3146  * Return value: ZBX_DB_SERVER - server database                              *
3147  *               ZBX_DB_PROXY - proxy database                                *
3148  *               ZBX_DB_UNKNOWN - an error occurred                           *
3149  *                                                                            *
3150  ******************************************************************************/
zbx_db_get_database_type(void)3151 int	zbx_db_get_database_type(void)
3152 {
3153 	const char	*result_string;
3154 	DB_RESULT	result;
3155 	int		ret = ZBX_DB_UNKNOWN;
3156 
3157 	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
3158 
3159 	DBconnect(ZBX_DB_CONNECT_NORMAL);
3160 
3161 	if (NULL == (result = DBselectN("select userid from users", 1)))
3162 	{
3163 		zabbix_log(LOG_LEVEL_DEBUG, "cannot select records from \"users\" table");
3164 		goto out;
3165 	}
3166 
3167 	if (NULL != DBfetch(result))
3168 	{
3169 		zabbix_log(LOG_LEVEL_DEBUG, "there is at least 1 record in \"users\" table");
3170 		ret = ZBX_DB_SERVER;
3171 	}
3172 	else
3173 	{
3174 		zabbix_log(LOG_LEVEL_DEBUG, "no records in \"users\" table");
3175 		ret = ZBX_DB_PROXY;
3176 	}
3177 
3178 	DBfree_result(result);
3179 out:
3180 	DBclose();
3181 
3182 	switch (ret)
3183 	{
3184 		case ZBX_DB_SERVER:
3185 			result_string = "ZBX_DB_SERVER";
3186 			break;
3187 		case ZBX_DB_PROXY:
3188 			result_string = "ZBX_DB_PROXY";
3189 			break;
3190 		case ZBX_DB_UNKNOWN:
3191 			result_string = "ZBX_DB_UNKNOWN";
3192 			break;
3193 	}
3194 
3195 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, result_string);
3196 
3197 	return ret;
3198 }
3199 
3200 /******************************************************************************
3201  *                                                                            *
3202  * Function: DBlock_record                                                    *
3203  *                                                                            *
3204  * Purpose: locks a record in a table by its primary key and an optional      *
3205  *          constraint field                                                  *
3206  *                                                                            *
3207  * Parameters: table     - [IN] the target table                              *
3208  *             id        - [IN] primary key value                             *
3209  *             add_field - [IN] additional constraint field name (optional)   *
3210  *             add_id    - [IN] constraint field value                        *
3211  *                                                                            *
3212  * Return value: SUCCEED - the record was successfully locked                 *
3213  *               FAIL    - the table does not contain the specified record    *
3214  *                                                                            *
3215  ******************************************************************************/
DBlock_record(const char * table,zbx_uint64_t id,const char * add_field,zbx_uint64_t add_id)3216 int	DBlock_record(const char *table, zbx_uint64_t id, const char *add_field, zbx_uint64_t add_id)
3217 {
3218 	DB_RESULT	result;
3219 	const ZBX_TABLE	*t;
3220 	int		ret;
3221 
3222 	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
3223 
3224 	if (0 == zbx_db_txn_level())
3225 		zabbix_log(LOG_LEVEL_DEBUG, "%s() called outside of transaction", __func__);
3226 
3227 	t = DBget_table(table);
3228 
3229 	if (NULL == add_field)
3230 	{
3231 		result = DBselect("select null from %s where %s=" ZBX_FS_UI64 ZBX_FOR_UPDATE, table, t->recid, id);
3232 	}
3233 	else
3234 	{
3235 		result = DBselect("select null from %s where %s=" ZBX_FS_UI64 " and %s=" ZBX_FS_UI64 ZBX_FOR_UPDATE,
3236 				table, t->recid, id, add_field, add_id);
3237 	}
3238 
3239 	if (NULL == DBfetch(result))
3240 		ret = FAIL;
3241 	else
3242 		ret = SUCCEED;
3243 
3244 	DBfree_result(result);
3245 
3246 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret));
3247 
3248 	return ret;
3249 }
3250 
3251 /******************************************************************************
3252  *                                                                            *
3253  * Function: DBlock_records                                                   *
3254  *                                                                            *
3255  * Purpose: locks a records in a table by its primary key                     *
3256  *                                                                            *
3257  * Parameters: table     - [IN] the target table                              *
3258  *             ids       - [IN] primary key values                            *
3259  *                                                                            *
3260  * Return value: SUCCEED - one or more of the specified records were          *
3261  *                         successfully locked                                *
3262  *               FAIL    - the table does not contain any of the specified    *
3263  *                         records                                            *
3264  *                                                                            *
3265  ******************************************************************************/
DBlock_records(const char * table,const zbx_vector_uint64_t * ids)3266 int	DBlock_records(const char *table, const zbx_vector_uint64_t *ids)
3267 {
3268 	DB_RESULT	result;
3269 	const ZBX_TABLE	*t;
3270 	int		ret;
3271 	char		*sql = NULL;
3272 	size_t		sql_alloc = 0, sql_offset = 0;
3273 
3274 	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
3275 
3276 	if (0 == zbx_db_txn_level())
3277 		zabbix_log(LOG_LEVEL_DEBUG, "%s() called outside of transaction", __func__);
3278 
3279 	t = DBget_table(table);
3280 
3281 	zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, "select null from %s where", table);
3282 	DBadd_condition_alloc(&sql, &sql_alloc, &sql_offset, t->recid, ids->values, ids->values_num);
3283 
3284 	result = DBselect("%s" ZBX_FOR_UPDATE, sql);
3285 
3286 	zbx_free(sql);
3287 
3288 	if (NULL == DBfetch(result))
3289 		ret = FAIL;
3290 	else
3291 		ret = SUCCEED;
3292 
3293 	DBfree_result(result);
3294 
3295 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret));
3296 
3297 	return ret;
3298 }
3299 
3300 /******************************************************************************
3301  *                                                                            *
3302  * Function: DBlock_ids                                                       *
3303  *                                                                            *
3304  * Purpose: locks a records in a table by field name                          *
3305  *                                                                            *
3306  * Parameters: table      - [IN] the target table                             *
3307  *             field_name - [IN] field name                                   *
3308  *             ids        - [IN/OUT] IN - sorted array of IDs to lock         *
3309  *                                   OUT - resulting array of locked IDs      *
3310  *                                                                            *
3311  * Return value: SUCCEED - one or more of the specified records were          *
3312  *                         successfully locked                                *
3313  *               FAIL    - no records were locked                             *
3314  *                                                                            *
3315  ******************************************************************************/
DBlock_ids(const char * table_name,const char * field_name,zbx_vector_uint64_t * ids)3316 int	DBlock_ids(const char *table_name, const char *field_name, zbx_vector_uint64_t *ids)
3317 {
3318 	char		*sql = NULL;
3319 	size_t		sql_alloc = 0, sql_offset = 0;
3320 	zbx_uint64_t	id;
3321 	int		i;
3322 	DB_RESULT	result;
3323 	DB_ROW		row;
3324 
3325 	if (0 == ids->values_num)
3326 		return FAIL;
3327 
3328 	zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, "select %s from %s where", field_name, table_name);
3329 	DBadd_condition_alloc(&sql, &sql_alloc, &sql_offset, field_name, ids->values, ids->values_num);
3330 	zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, " order by %s" ZBX_FOR_UPDATE, field_name);
3331 	result = DBselect("%s", sql);
3332 	zbx_free(sql);
3333 
3334 	for (i = 0; NULL != (row = DBfetch(result)); i++)
3335 	{
3336 		ZBX_STR2UINT64(id, row[0]);
3337 
3338 		while (id != ids->values[i])
3339 			zbx_vector_uint64_remove(ids, i);
3340 	}
3341 	DBfree_result(result);
3342 
3343 	while (i != ids->values_num)
3344 		zbx_vector_uint64_remove_noorder(ids, i);
3345 
3346 	return (0 != ids->values_num ? SUCCEED : FAIL);
3347 }
3348 
3349 /******************************************************************************
3350  *                                                                            *
3351  * Function: zbx_sql_add_host_availability                                    *
3352  *                                                                            *
3353  * Purpose: adds host availability update to sql statement                    *
3354  *                                                                            *
3355  * Parameters: sql        - [IN/OUT] the sql statement                        *
3356  *             sql_alloc  - [IN/OUT] the number of bytes allocated for sql    *
3357  *                                   statement                                *
3358  *             sql_offset - [IN/OUT] the number of bytes used in sql          *
3359  *                                   statement                                *
3360  *             ha           [IN] the host availability data                   *
3361  *                                                                            *
3362  ******************************************************************************/
zbx_sql_add_host_availability(char ** sql,size_t * sql_alloc,size_t * sql_offset,const zbx_host_availability_t * ha)3363 int	zbx_sql_add_host_availability(char **sql, size_t *sql_alloc, size_t *sql_offset,
3364 		const zbx_host_availability_t *ha)
3365 {
3366 	const char	*field_prefix[ZBX_AGENT_MAX] = {"", "snmp_", "ipmi_", "jmx_"};
3367 	char		delim = ' ';
3368 	int		i;
3369 
3370 	if (FAIL == zbx_host_availability_is_set(ha))
3371 		return FAIL;
3372 
3373 	zbx_strcpy_alloc(sql, sql_alloc, sql_offset, "update hosts set");
3374 
3375 	for (i = 0; i < ZBX_AGENT_MAX; i++)
3376 	{
3377 		if (0 != (ha->agents[i].flags & ZBX_FLAGS_AGENT_STATUS_AVAILABLE))
3378 		{
3379 			zbx_snprintf_alloc(sql, sql_alloc, sql_offset, "%c%savailable=%d", delim, field_prefix[i],
3380 					(int)ha->agents[i].available);
3381 			delim = ',';
3382 		}
3383 
3384 		if (0 != (ha->agents[i].flags & ZBX_FLAGS_AGENT_STATUS_ERROR))
3385 		{
3386 			char	*error_esc;
3387 
3388 			error_esc = DBdyn_escape_field("hosts", "error", ha->agents[i].error);
3389 			zbx_snprintf_alloc(sql, sql_alloc, sql_offset, "%c%serror='%s'", delim, field_prefix[i],
3390 					error_esc);
3391 			zbx_free(error_esc);
3392 			delim = ',';
3393 		}
3394 
3395 		if (0 != (ha->agents[i].flags & ZBX_FLAGS_AGENT_STATUS_ERRORS_FROM))
3396 		{
3397 			zbx_snprintf_alloc(sql, sql_alloc, sql_offset, "%c%serrors_from=%d", delim, field_prefix[i],
3398 					ha->agents[i].errors_from);
3399 			delim = ',';
3400 		}
3401 
3402 		if (0 != (ha->agents[i].flags & ZBX_FLAGS_AGENT_STATUS_DISABLE_UNTIL))
3403 		{
3404 			zbx_snprintf_alloc(sql, sql_alloc, sql_offset, "%c%sdisable_until=%d", delim, field_prefix[i],
3405 					ha->agents[i].disable_until);
3406 			delim = ',';
3407 		}
3408 	}
3409 
3410 	zbx_snprintf_alloc(sql, sql_alloc, sql_offset, " where hostid=" ZBX_FS_UI64, ha->hostid);
3411 
3412 	return SUCCEED;
3413 }
3414 
3415 /******************************************************************************
3416  *                                                                            *
3417  * Function: DBget_user_by_active_session                                     *
3418  *                                                                            *
3419  * Purpose: validate that session is active and get associated user data      *
3420  *                                                                            *
3421  * Parameters: sessionid - [IN] the session id to validate                    *
3422  *             user      - [OUT] user information                             *
3423  *                                                                            *
3424  * Return value:  SUCCEED - session is active and user data was retrieved     *
3425  *                FAIL    - otherwise                                         *
3426  *                                                                            *
3427  ******************************************************************************/
DBget_user_by_active_session(const char * sessionid,zbx_user_t * user)3428 int	DBget_user_by_active_session(const char *sessionid, zbx_user_t *user)
3429 {
3430 	char		*sessionid_esc;
3431 	int		ret = FAIL;
3432 	DB_RESULT	result;
3433 	DB_ROW		row;
3434 
3435 	zabbix_log(LOG_LEVEL_DEBUG, "In %s() sessionid:%s", __func__, sessionid);
3436 
3437 	sessionid_esc = DBdyn_escape_string(sessionid);
3438 
3439 	if (NULL == (result = DBselect(
3440 			"select u.userid,u.type"
3441 				" from sessions s,users u"
3442 			" where s.userid=u.userid"
3443 				" and s.sessionid='%s'"
3444 				" and s.status=%d",
3445 			sessionid_esc, ZBX_SESSION_ACTIVE)))
3446 	{
3447 		goto out;
3448 	}
3449 
3450 	if (NULL == (row = DBfetch(result)))
3451 		goto out;
3452 
3453 	ZBX_STR2UINT64(user->userid, row[0]);
3454 	user->type = atoi(row[1]);
3455 
3456 	ret = SUCCEED;
3457 out:
3458 	DBfree_result(result);
3459 	zbx_free(sessionid_esc);
3460 
3461 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret));
3462 
3463 	return ret;
3464 }
3465 
3466 /******************************************************************************
3467  *                                                                            *
3468  * Function: zbx_db_mock_field_init                                           *
3469  *                                                                            *
3470  * Purpose: initializes mock field                                            *
3471  *                                                                            *
3472  * Parameters: field      - [OUT] the field data                              *
3473  *             field_type - [IN] the field type in database schema            *
3474  *             field_len  - [IN] the field size in database schema            *
3475  *                                                                            *
3476  ******************************************************************************/
zbx_db_mock_field_init(zbx_db_mock_field_t * field,int field_type,int field_len)3477 void	zbx_db_mock_field_init(zbx_db_mock_field_t *field, int field_type, int field_len)
3478 {
3479 	switch (field_type)
3480 	{
3481 		case ZBX_TYPE_CHAR:
3482 #if defined(HAVE_ORACLE)
3483 			field->chars_num = field_len;
3484 			field->bytes_num = 4000;
3485 #else
3486 			field->chars_num = field_len;
3487 			field->bytes_num = -1;
3488 #endif
3489 			return;
3490 	}
3491 
3492 	THIS_SHOULD_NEVER_HAPPEN;
3493 
3494 	field->chars_num = 0;
3495 	field->bytes_num = 0;
3496 }
3497 
3498 /******************************************************************************
3499  *                                                                            *
3500  * Function: zbx_db_mock_field_append                                         *
3501  *                                                                            *
3502  * Purpose: 'appends' text to the field, if successful the character/byte     *
3503  *           limits are updated                                               *
3504  *                                                                            *
3505  * Parameters: field - [IN/OUT] the mock field                                *
3506  *             text  - [IN] the text to append                                *
3507  *                                                                            *
3508  * Return value: SUCCEED - the field had enough space to append the text      *
3509  *               FAIL    - otherwise                                          *
3510  *                                                                            *
3511  ******************************************************************************/
zbx_db_mock_field_append(zbx_db_mock_field_t * field,const char * text)3512 int	zbx_db_mock_field_append(zbx_db_mock_field_t *field, const char *text)
3513 {
3514 	int	bytes_num, chars_num;
3515 
3516 	if (-1 != field->bytes_num)
3517 	{
3518 		bytes_num = strlen(text);
3519 		if (bytes_num > field->bytes_num)
3520 			return FAIL;
3521 	}
3522 	else
3523 		bytes_num = 0;
3524 
3525 	if (-1 != field->chars_num)
3526 	{
3527 		chars_num = zbx_strlen_utf8(text);
3528 		if (chars_num > field->chars_num)
3529 			return FAIL;
3530 	}
3531 	else
3532 		chars_num = 0;
3533 
3534 	field->bytes_num -= bytes_num;
3535 	field->chars_num -= chars_num;
3536 
3537 	return SUCCEED;
3538 }
3539 
3540 /******************************************************************************
3541  *                                                                            *
3542  * Function: zbx_db_check_instanceid                                          *
3543  *                                                                            *
3544  * Purpose: checks instanceid value in config table and generates new         *
3545  *          instance id if its empty                                          *
3546  *                                                                            *
3547  * Return value: SUCCEED - valid instance id either exists or was created     *
3548  *               FAIL    - no valid instance id exists and could not create   *
3549  *                         one                                                *
3550  *                                                                            *
3551  ******************************************************************************/
zbx_db_check_instanceid(void)3552 int	zbx_db_check_instanceid(void)
3553 {
3554 	DB_RESULT	result;
3555 	DB_ROW		row;
3556 	int		ret = SUCCEED;
3557 
3558 	DBconnect(ZBX_DB_CONNECT_NORMAL);
3559 
3560 	result = DBselect("select configid,instanceid from config order by configid");
3561 	if (NULL != (row = DBfetch(result)))
3562 	{
3563 		if (SUCCEED == DBis_null(row[1]) || '\0' == *row[1])
3564 		{
3565 			char	*token;
3566 
3567 			token = zbx_create_token(0);
3568 			if (ZBX_DB_OK > DBexecute("update config set instanceid='%s' where configid=%s", token, row[0]))
3569 			{
3570 				zabbix_log(LOG_LEVEL_ERR, "cannot update instance id in database");
3571 				ret = FAIL;
3572 			}
3573 			zbx_free(token);
3574 		}
3575 	}
3576 	else
3577 	{
3578 		zabbix_log(LOG_LEVEL_ERR, "cannot read instance id from database");
3579 		ret = FAIL;
3580 	}
3581 	DBfree_result(result);
3582 
3583 	DBclose();
3584 
3585 	return ret;
3586 }
3587 
3588 #if defined(HAVE_POSTGRESQL)
3589 /******************************************************************************
3590  *                                                                            *
3591  * Function: zbx_db_get_schema_esc                                            *
3592  *                                                                            *
3593  * Purpose: returns escaped DB schema name                                    *
3594  *                                                                            *
3595  ******************************************************************************/
zbx_db_get_schema_esc(void)3596 char	*zbx_db_get_schema_esc(void)
3597 {
3598 	static char	*name;
3599 
3600 	if (NULL == name)
3601 	{
3602 		name = DBdyn_escape_string(NULL == CONFIG_DBSCHEMA || '\0' == *CONFIG_DBSCHEMA ?
3603 				"public" : CONFIG_DBSCHEMA);
3604 	}
3605 
3606 	return name;
3607 }
3608 #endif
3609