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