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