1 /*
2 ** Zabbix
3 ** Copyright (C) 2001-2021 Zabbix SIA
4 **
5 ** This program is free software; you can redistribute it and/or modify
6 ** it under the terms of the GNU General Public License as published by
7 ** the Free Software Foundation; either version 2 of the License, or
8 ** (at your option) any later version.
9 **
10 ** This program is distributed in the hope that it will be useful,
11 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
12 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 ** GNU General Public License for more details.
14 **
15 ** You should have received a copy of the GNU General Public License
16 ** along with this program; if not, write to the Free Software
17 ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 **/
19
20 #include "common.h"
21 #include "md5.h"
22 #include "log.h"
23 #include "logfiles.h"
24 #include "persistent_state.h"
25 #include "zbxjson.h"
26 #include "../../libs/zbxalgo/vectorimpl.h"
27
28 /* tags for agent persistent storage files */
29 #define ZBX_PERSIST_TAG_FILENAME "filename"
30 #define ZBX_PERSIST_TAG_MTIME "mtime"
31 #define ZBX_PERSIST_TAG_SEQ "seq"
32 #define ZBX_PERSIST_TAG_INCOMPLETE "incomplete"
33 #define ZBX_PERSIST_TAG_COPY_OF "copy_of"
34 #define ZBX_PERSIST_TAG_DEVICE "dev"
35 #define ZBX_PERSIST_TAG_INODE_LO "ino_lo"
36 #define ZBX_PERSIST_TAG_INODE_HI "ino_hi"
37 #define ZBX_PERSIST_TAG_SIZE "size"
38 #define ZBX_PERSIST_TAG_PROCESSED_SIZE "processed_size"
39 #define ZBX_PERSIST_TAG_MD5_BLOCK_SIZE "md5_block_size"
40 #define ZBX_PERSIST_TAG_FIRST_BLOCK_MD5 "first_block_md5"
41 #define ZBX_PERSIST_TAG_LAST_BLOCK_OFFSET "last_block_offset"
42 #define ZBX_PERSIST_TAG_LAST_BLOCK_MD5 "last_block_md5"
43
ZBX_VECTOR_IMPL(pre_persistent,zbx_pre_persistent_t)44 ZBX_VECTOR_IMPL(pre_persistent, zbx_pre_persistent_t)
45 ZBX_VECTOR_IMPL(persistent_inactive, zbx_persistent_inactive_t)
46
47 #if !defined(_WINDOWS) && !defined(__MINGW32__)
48 static int zbx_persistent_inactive_compare_func(const void *d1, const void *d2)
49 {
50 return strcmp(((const zbx_persistent_inactive_t *)d1)->key_orig,
51 ((const zbx_persistent_inactive_t *)d2)->key_orig);
52 }
53
54 /******************************************************************************
55 * *
56 * Function: str2file_name_part *
57 * *
58 * Purpose: for the specified string get the part of persistent storage path *
59 * *
60 * Parameters: *
61 * str - [IN] string (e.g. host, item key) *
62 * *
63 * Return value: dynamically allocated string with the part of path *
64 * corresponding to the input string *
65 * *
66 * Comments: the part of persistent storage path is built of md5 sum of the *
67 * input string with the length of input string appended *
68 * *
69 * Example: for input string *
70 * "logrt[/home/zabbix/test.log,,,,,,,,/home/zabbix/agent_private]" *
71 * the function returns *
72 * "147bd30c08995022fbe78de3c26c082962" *
73 * \/ - length of input string *
74 * \------------------------------/ - md5 sum of input string *
75 * *
76 ******************************************************************************/
str2file_name_part(const char * str)77 static char *str2file_name_part(const char *str)
78 {
79 md5_state_t state;
80 md5_byte_t md5[MD5_DIGEST_SIZE];
81 char size_buf[21]; /* 20 - max size of printed 'size_t' value, 1 - '\0' */
82 char *md5_text = NULL;
83 size_t str_sz, md5_text_sz, size_buf_len;
84
85 str_sz = strlen(str);
86
87 zbx_md5_init(&state);
88 zbx_md5_append(&state, (const md5_byte_t *)str, (int)str_sz);
89 zbx_md5_finish(&state, md5);
90
91 size_buf_len = zbx_snprintf(size_buf, sizeof(size_buf), ZBX_FS_SIZE_T, (zbx_fs_size_t)str_sz);
92
93 md5_text_sz = MD5_DIGEST_SIZE * 2 + size_buf_len + 1;
94 md5_text = (char *)zbx_malloc(NULL, md5_text_sz);
95
96 zbx_md5buf2str(md5, md5_text);
97 memcpy(md5_text + MD5_DIGEST_SIZE * 2, size_buf, size_buf_len + 1);
98
99 return md5_text;
100 }
101
102 /******************************************************************************
103 * *
104 * Function: active_server2dir_name_part *
105 * *
106 * Purpose: calculate the part of persistent storage path for the specified *
107 * server/port pair where the agent is sending active check data *
108 * *
109 * Parameters: *
110 * server - [IN] server string *
111 * port - [IN] port number *
112 * *
113 * Return value: dynamically allocated string with the part of path *
114 * *
115 * Comments: the part of persistent storage path is built of md5 sum of the *
116 * input string with the length of input string appended *
117 * *
118 * Example: for server "127.0.0.1" and port 10091 the function returns *
119 * "8392b6ea687188e70feac24517d2f9d715" *
120 * \/ - length of "127.0.0.1:10091" *
121 * \------------------------------/ - md5 sum of "127.0.0.1:10091" *
122 * *
123 ******************************************************************************/
active_server2dir_name_part(const char * server,unsigned short port)124 static char *active_server2dir_name_part(const char *server, unsigned short port)
125 {
126 char *endpoint, *dir_name;
127
128 endpoint = zbx_dsprintf(NULL, "%s:%hu", server, port);
129 dir_name = str2file_name_part(endpoint);
130 zbx_free(endpoint);
131
132 return dir_name;
133 }
134
135 /******************************************************************************
136 * *
137 * Function: make_persistent_server_directory_name *
138 * *
139 * Purpose: make the name of persistent storage directory for the specified *
140 * server/proxy and port *
141 * *
142 * Parameters: *
143 * base_path - [IN] initial part of pathname *
144 * server - [IN] server string *
145 * port - [IN] port number *
146 * *
147 * Return value: dynamically allocated string with the name of directory *
148 * *
149 * Example: for base_path "/var/tmp/", server "127.0.0.1", port 10091 the *
150 * function returns "/var/tmp/8392b6ea687188e70feac24517d2f9d715" *
151 * *
152 ******************************************************************************/
make_persistent_server_directory_name(const char * base_path,const char * server,unsigned short port)153 static char *make_persistent_server_directory_name(const char *base_path, const char *server, unsigned short port)
154 {
155 char *server_part, *file_name;
156
157 server_part = active_server2dir_name_part(server, port);
158 file_name = zbx_dsprintf(NULL, "%s/%s", base_path, server_part);
159 zbx_free(server_part);
160
161 return file_name;
162 }
163
164 /******************************************************************************
165 * *
166 * Function: check_persistent_directory_exists *
167 * *
168 * Purpose: check if the directory exists *
169 * *
170 * Parameters: *
171 * pathname - [IN] full pathname of directory *
172 * error - [OUT] error message *
173 * *
174 * Return value: SUCCEED or FAIL (with dynamically allocated 'error') *
175 * *
176 ******************************************************************************/
check_persistent_directory_exists(const char * pathname,char ** error)177 static int check_persistent_directory_exists(const char *pathname, char **error)
178 {
179 zbx_stat_t status;
180
181 if (0 != lstat(pathname, &status))
182 {
183 *error = zbx_dsprintf(*error, "cannot obtain directory information: %s", zbx_strerror(errno));
184 return FAIL;
185 }
186
187 if (0 == S_ISDIR(status.st_mode))
188 {
189 *error = zbx_dsprintf(*error, "file exists but is not a directory");
190 return FAIL;
191 }
192
193 return SUCCEED;
194 }
195
196 /******************************************************************************
197 * *
198 * Function: create_persistent_directory *
199 * *
200 * Purpose: create directory if it does not exist or check access if it *
201 * exists *
202 * *
203 * Parameters: *
204 * pathname - [IN] full pathname of directory *
205 * error - [OUT] error message *
206 * *
207 * Return value: SUCCEED - directory created or already exists *
208 * FAIL - cannot create directory or it has insufficient *
209 * access rights (see dynamically allocated 'error') *
210 * *
211 ******************************************************************************/
create_persistent_directory(const char * pathname,char ** error)212 static int create_persistent_directory(const char *pathname, char **error)
213 {
214 if (0 != mkdir(pathname, S_IRWXU))
215 {
216 if (EEXIST == errno)
217 return check_persistent_directory_exists(pathname, error);
218
219 *error = zbx_dsprintf(*error, "%s(): cannot create directory \"%s\": %s", __func__, pathname,
220 zbx_strerror(errno));
221 return FAIL;
222 }
223
224 zabbix_log(LOG_LEVEL_DEBUG, "%s(): created directory:[%s]", __func__, pathname);
225
226 return SUCCEED;
227 }
228
229 /******************************************************************************
230 * *
231 * Function: create_base_path_directories *
232 * *
233 * Purpose: create all subdirectories in the pathname if they do not exist *
234 * *
235 * Parameters: *
236 * pathname - [IN] full pathname of directory, must consist of only *
237 * ASCII characters *
238 * error - [OUT] error message *
239 * *
240 * Return value: SUCCEED - directories created or already exist *
241 * FAIL - cannot create directory or it has insufficient *
242 * access rights (see dynamically allocated 'error') *
243 * *
244 ******************************************************************************/
create_base_path_directories(const char * pathname,char ** error)245 static int create_base_path_directories(const char *pathname, char **error)
246 {
247 char *path, *p;
248
249 zabbix_log(LOG_LEVEL_DEBUG, "%s(): pathname:[%s]", __func__, pathname);
250
251 if ('/' != pathname[0])
252 {
253 *error = zbx_dsprintf(*error, "persistent directory name is not an absolute path,"
254 " it does not start with '/'");
255 return FAIL;
256 }
257
258 path = zbx_strdup(NULL, pathname); /* mutable copy */
259
260 for (p = path + 1; ; ++p)
261 {
262 if ('/' == *p)
263 {
264 *p = '\0';
265
266 zabbix_log(LOG_LEVEL_DEBUG, "%s(): checking directory:[%s]", __func__, path);
267
268 if (SUCCEED != create_persistent_directory(path, error))
269 {
270 zbx_free(path);
271 return FAIL;
272 }
273
274 *p = '/';
275 }
276 else if ('\0' == *p)
277 {
278 zabbix_log(LOG_LEVEL_DEBUG, "%s(): checking directory:[%s]", __func__, path);
279
280 if (SUCCEED != create_persistent_directory(path, error))
281 {
282 zbx_free(path);
283 return FAIL;
284 }
285
286 break;
287 }
288 }
289
290 zbx_free(path);
291
292 return SUCCEED;
293 }
294
295 /******************************************************************************
296 * *
297 * Function: zbx_create_persistent_server_directory *
298 * *
299 * Purpose: create directory if it does not exist or check access if it *
300 * exists. Directory name is derived from host and port. *
301 * *
302 * Parameters: *
303 * base_path - [IN] initial part of pathname, must consist of only *
304 * ASCII characters *
305 * host - [IN] host string *
306 * port - [IN] port number *
307 * error - [OUT] error message *
308 * *
309 * Return value: on success - dynamically allocated string with the name of *
310 * directory, *
311 * on error - NULL (with dynamically allocated 'error') *
312 * *
313 ******************************************************************************/
zbx_create_persistent_server_directory(const char * base_path,const char * host,unsigned short port,char ** error)314 char *zbx_create_persistent_server_directory(const char *base_path, const char *host, unsigned short port,
315 char **error)
316 {
317 char *server_dir_name, *err_msg = NULL;
318
319 /* persistent storage top-level directory */
320 if (SUCCEED != create_base_path_directories(base_path, error))
321 return NULL;
322
323 /* directory for server/proxy where the current active check process will be sending data */
324 server_dir_name = make_persistent_server_directory_name(base_path, host, port);
325
326 if (SUCCEED != create_persistent_directory(server_dir_name, &err_msg))
327 {
328 *error = zbx_dsprintf(*error, "cannot create directory \"%s\": %s", server_dir_name, err_msg);
329 zbx_free(server_dir_name);
330 zbx_free(err_msg);
331 return NULL;
332 }
333
334 if (0 != access(server_dir_name, R_OK | W_OK | X_OK))
335 {
336 if (NULL != server_dir_name) /* only to silence GCC warning "directive argument is null" */
337 {
338 *error = zbx_dsprintf(*error, "insufficient access rights to directory \"%s\"",
339 server_dir_name);
340 }
341 else
342 THIS_SHOULD_NEVER_HAPPEN;
343
344 zbx_free(server_dir_name);
345 return NULL;
346 }
347
348 return server_dir_name;
349 }
350
351 /******************************************************************************
352 * *
353 * Function: zbx_make_persistent_file_name *
354 * *
355 * Purpose: make the name of persistent storage directory or file *
356 * *
357 * Parameters: *
358 * persistent_server_dir - [IN] initial part of pathname *
359 * item_key - [IN] item key (not NULL) *
360 * *
361 * Return value: dynamically allocated string with the name of file *
362 * *
363 ******************************************************************************/
zbx_make_persistent_file_name(const char * persistent_server_dir,const char * item_key)364 char *zbx_make_persistent_file_name(const char *persistent_server_dir, const char *item_key)
365 {
366 char *file_name, *item_part;
367
368 item_part = str2file_name_part(item_key);
369 file_name = zbx_dsprintf(NULL, "%s/%s", persistent_server_dir, item_part);
370 zbx_free(item_part);
371
372 return file_name;
373 }
374
375 /******************************************************************************
376 * *
377 * Function: zbx_write_persistent_file *
378 * *
379 * Purpose: write metric info into persistent file *
380 * *
381 * Parameters: *
382 * filename - [IN] file name *
383 * data - [IN] file content *
384 * error - [OUT] error message *
385 * *
386 * Return value: SUCCEED - file was successfully written *
387 * FAIL - cannot write the file (see dynamically allocated *
388 * 'error' message) *
389 * *
390 ******************************************************************************/
zbx_write_persistent_file(const char * filename,const char * data,char ** error)391 static int zbx_write_persistent_file(const char *filename, const char *data, char **error)
392 {
393 FILE *fp;
394 size_t sz, alloc_bytes = 0, offset = 0;
395 int ret = SUCCEED;
396
397 zabbix_log(LOG_LEVEL_DEBUG, "%s(): filename:[%s] data:[%s]", __func__, filename, data);
398
399 if (NULL == (fp = fopen(filename, "w")))
400 {
401 zbx_snprintf_alloc(error, &alloc_bytes, &offset, "cannot open file: %s", zbx_strerror(errno));
402 return FAIL;
403 }
404
405 sz = strlen(data);
406
407 if (sz != fwrite(data, 1, sz, fp))
408 {
409 zbx_snprintf_alloc(error, &alloc_bytes, &offset, "cannot write to file: %s", zbx_strerror(errno));
410 ret = FAIL;
411 }
412
413 if (0 != fclose(fp))
414 {
415 zbx_snprintf_alloc(error, &alloc_bytes, &offset, "%scannot close file: %s",
416 (FAIL == ret) ? "; ": "" , zbx_strerror(errno));
417 ret = FAIL;
418 }
419
420 return ret;
421 }
422
423 /******************************************************************************
424 * *
425 * Function: zbx_read_persistent_file *
426 * *
427 * Purpose: read metric info from persistent file. One line is read. *
428 * *
429 * Parameters: *
430 * filename - [IN] file name *
431 * buf - [OUT] buffer to read file into *
432 * buf_size - [IN] buffer size *
433 * err_msg - [OUT] error message *
434 * *
435 * Return value: SUCCEED - file was successfully read *
436 * FAIL - cannot read the file (see dynamically allocated *
437 * 'err_msg' message) *
438 * *
439 ******************************************************************************/
zbx_read_persistent_file(const char * filename,char * buf,size_t buf_size,char ** err_msg)440 int zbx_read_persistent_file(const char *filename, char *buf, size_t buf_size, char **err_msg)
441 {
442 int ret = FAIL;
443 FILE *f;
444
445 if (NULL == (f = fopen(filename, "r")))
446 {
447 *err_msg = zbx_dsprintf(*err_msg, "cannot open file \"%s\": %s", filename, zbx_strerror(errno));
448 return FAIL;
449 }
450
451 if (NULL == fgets(buf, (int)buf_size, f))
452 {
453 *err_msg = zbx_dsprintf(*err_msg, "cannot read from file \"%s\" or file empty", filename);
454 goto out;
455 }
456
457 buf[strcspn(buf, "\r\n")] = '\0'; /* discard newline at the end of string */
458
459 ret = SUCCEED;
460 out:
461 if (0 != fclose(f))
462 { /* only log, cannot do anything in case of error */
463 zabbix_log(LOG_LEVEL_CRIT, "cannot close file \"%s\": %s", filename, zbx_strerror(errno));
464 }
465
466 return ret;
467 }
468
469 /******************************************************************************
470 * *
471 * Function: zbx_remove_persistent_file *
472 * *
473 * Purpose: remove the specified file *
474 * *
475 * Parameters: *
476 * pathname - [IN] file name *
477 * error - [OUT] error message *
478 * *
479 * Return value: SUCCEED - file was removed or does not exist *
480 * FAIL - cannot remove the specified file (see dynamically *
481 * allocated 'error' message) *
482 * *
483 ******************************************************************************/
zbx_remove_persistent_file(const char * pathname,char ** error)484 int zbx_remove_persistent_file(const char *pathname, char **error)
485 {
486 zabbix_log(LOG_LEVEL_DEBUG, "%s(): removing persistent file '%s'", __func__, pathname);
487
488 if (0 == unlink(pathname) || ENOENT == errno)
489 return SUCCEED;
490
491 *error = zbx_strdup(*error, zbx_strerror(errno));
492
493 return FAIL;
494 }
495
zbx_write_persistent_files(zbx_vector_pre_persistent_t * prep_vec)496 void zbx_write_persistent_files(zbx_vector_pre_persistent_t *prep_vec)
497 {
498 int i;
499
500 for (i = 0; i < prep_vec->values_num; i++)
501 {
502 char *error = NULL;
503 struct zbx_json json;
504
505 /* prepare JSON */
506 zbx_json_init(&json, ZBX_JSON_STAT_BUF_LEN);
507
508 if (NULL != prep_vec->values[i].filename)
509 {
510 zbx_json_addstring(&json, ZBX_PERSIST_TAG_FILENAME, prep_vec->values[i].filename,
511 ZBX_JSON_TYPE_STRING);
512 }
513
514 zbx_json_adduint64(&json, ZBX_PERSIST_TAG_MTIME, (zbx_uint64_t)prep_vec->values[i].mtime);
515
516 zbx_json_adduint64(&json, ZBX_PERSIST_TAG_PROCESSED_SIZE, prep_vec->values[i].processed_size);
517
518 if (NULL != prep_vec->values[i].filename)
519 {
520 char buf[ZBX_MD5_PRINT_BUF_LEN];
521
522 zbx_json_adduint64(&json, ZBX_PERSIST_TAG_SEQ, (zbx_uint64_t)prep_vec->values[i].seq);
523 zbx_json_adduint64(&json, ZBX_PERSIST_TAG_INCOMPLETE,
524 (zbx_uint64_t)prep_vec->values[i].incomplete);
525
526 zbx_json_addint64(&json, ZBX_PERSIST_TAG_COPY_OF, prep_vec->values[i].copy_of);
527 zbx_json_adduint64(&json, ZBX_PERSIST_TAG_DEVICE, prep_vec->values[i].dev);
528 zbx_json_adduint64(&json, ZBX_PERSIST_TAG_INODE_LO, prep_vec->values[i].ino_lo);
529 zbx_json_adduint64(&json, ZBX_PERSIST_TAG_INODE_HI, prep_vec->values[i].ino_hi);
530 zbx_json_adduint64(&json, ZBX_PERSIST_TAG_SIZE, prep_vec->values[i].size);
531
532 zbx_json_adduint64(&json, ZBX_PERSIST_TAG_MD5_BLOCK_SIZE,
533 (zbx_uint64_t)prep_vec->values[i].md5_block_size);
534
535 zbx_md5buf2str(prep_vec->values[i].first_block_md5, buf);
536 zbx_json_addstring(&json, ZBX_PERSIST_TAG_FIRST_BLOCK_MD5, buf, ZBX_JSON_TYPE_STRING);
537
538 zbx_json_adduint64(&json, ZBX_PERSIST_TAG_LAST_BLOCK_OFFSET,
539 prep_vec->values[i].last_block_offset);
540
541 zbx_md5buf2str(prep_vec->values[i].last_block_md5, buf);
542 zbx_json_addstring(&json, ZBX_PERSIST_TAG_LAST_BLOCK_MD5, buf, ZBX_JSON_TYPE_STRING);
543 }
544
545 zbx_json_close(&json);
546
547 if (SUCCEED != zbx_write_persistent_file(prep_vec->values[i].persistent_file_name, json.buffer, &error))
548 {
549 zabbix_log(LOG_LEVEL_WARNING, "cannot write persistent file \"%s\": %s",
550 prep_vec->values[i].persistent_file_name, error);
551 zbx_free(error);
552 }
553
554 zbx_json_free(&json);
555 }
556 }
557
zbx_clean_pre_persistent_elements(zbx_vector_pre_persistent_t * prep_vec)558 void zbx_clean_pre_persistent_elements(zbx_vector_pre_persistent_t *prep_vec)
559 {
560 int i;
561
562 /* clean only element data and number of elements, do not reduce vector size */
563
564 for (i = 0; i < prep_vec->values_num; i++)
565 {
566 zbx_free(prep_vec->values[i].key_orig);
567 zbx_free(prep_vec->values[i].persistent_file_name);
568 zbx_free(prep_vec->values[i].filename);
569 }
570
571 zbx_vector_pre_persistent_clear(prep_vec);
572 }
573
zbx_add_to_persistent_inactive_list(zbx_vector_persistent_inactive_t * inactive_vec,char * key,const char * filename)574 void zbx_add_to_persistent_inactive_list(zbx_vector_persistent_inactive_t *inactive_vec, char *key,
575 const char *filename)
576 {
577 zbx_persistent_inactive_t el;
578
579 el.key_orig = key;
580
581 if (FAIL == zbx_vector_persistent_inactive_search(inactive_vec, el,
582 zbx_persistent_inactive_compare_func))
583 {
584 /* create and initialize a new vector element */
585
586 el.key_orig = zbx_strdup(NULL, key);
587 el.not_received_time = time(NULL);
588 el.persistent_file_name = zbx_strdup(NULL, filename);
589
590 zbx_vector_persistent_inactive_append(inactive_vec, el);
591
592 zabbix_log(LOG_LEVEL_DEBUG, "%s(): added element %d with key '%s' for file '%s'", __func__,
593 inactive_vec->values_num - 1, key, filename);
594 }
595 }
596
zbx_remove_from_persistent_inactive_list(zbx_vector_persistent_inactive_t * inactive_vec,char * key)597 void zbx_remove_from_persistent_inactive_list(zbx_vector_persistent_inactive_t *inactive_vec, char *key)
598 {
599 zbx_persistent_inactive_t el;
600 int idx;
601
602 el.key_orig = key;
603
604 if (FAIL == (idx = zbx_vector_persistent_inactive_search(inactive_vec, el,
605 zbx_persistent_inactive_compare_func)))
606 {
607 return;
608 }
609
610 zbx_free(inactive_vec->values[idx].key_orig);
611 zbx_free(inactive_vec->values[idx].persistent_file_name);
612 zbx_vector_persistent_inactive_remove(inactive_vec, idx);
613
614 zabbix_log(LOG_LEVEL_DEBUG, "%s(): removed element %d with key '%s'", __func__, idx, key);
615 }
616
zbx_remove_inactive_persistent_files(zbx_vector_persistent_inactive_t * inactive_vec)617 void zbx_remove_inactive_persistent_files(zbx_vector_persistent_inactive_t *inactive_vec)
618 {
619 int i;
620 time_t now;
621
622 now = time(NULL);
623
624 for (i = 0; i < inactive_vec->values_num; i++)
625 {
626 zbx_persistent_inactive_t *el = inactive_vec->values + i;
627
628 if (ZBX_PERSIST_INACTIVITY_PERIOD <= now - el->not_received_time)
629 {
630 char *err_msg = NULL;
631
632 zabbix_log(LOG_LEVEL_DEBUG, "%s(): removing element %d with key '%s'", __func__, i,
633 el->key_orig);
634
635 if (SUCCEED != zbx_remove_persistent_file(el->persistent_file_name, &err_msg))
636 {
637 zabbix_log(LOG_LEVEL_WARNING, "cannot remove persistent file \"%s\": %s",
638 el->persistent_file_name, err_msg);
639 zbx_free(err_msg);
640 }
641
642 zbx_free(el->key_orig);
643 zbx_free(el->persistent_file_name);
644 zbx_vector_persistent_inactive_remove(inactive_vec, i);
645 }
646 }
647 }
648
zbx_pre_persistent_compare_func(const void * d1,const void * d2)649 static int zbx_pre_persistent_compare_func(const void *d1, const void *d2)
650 {
651 return strcmp(((const zbx_pre_persistent_t *)d1)->key_orig, ((const zbx_pre_persistent_t *)d2)->key_orig);
652 }
653
654 /******************************************************************************
655 * *
656 * Function: zbx_find_or_create_prep_vec_element *
657 * *
658 * Purpose: search preparation vector to find element with the specified key. *
659 * If not found then create the element. *
660 * *
661 * Parameters: *
662 * prep_vec - [IN/OUT] preparation vector for persistent data *
663 * files *
664 * key - [IN] log*[] item key *
665 * persistent_file_name - [IN] file name for copying into new element *
666 * *
667 ******************************************************************************/
zbx_find_or_create_prep_vec_element(zbx_vector_pre_persistent_t * prep_vec,const char * key,const char * persistent_file_name)668 int zbx_find_or_create_prep_vec_element(zbx_vector_pre_persistent_t *prep_vec, const char *key,
669 const char *persistent_file_name)
670 {
671 zbx_pre_persistent_t prep_element;
672 int prep_vec_idx;
673
674 prep_element.key_orig = (char *)key;
675
676 if (FAIL == (prep_vec_idx = zbx_vector_pre_persistent_search(prep_vec, prep_element,
677 zbx_pre_persistent_compare_func)))
678 {
679 /* create and initialize a new vector element */
680 memset(&prep_element, 0, sizeof(prep_element));
681
682 zbx_vector_pre_persistent_append(prep_vec, prep_element);
683 prep_vec_idx = prep_vec->values_num - 1;
684
685 /* fill in 'key_orig' and 'persistent_file_name' values - they never change for the specified */
686 /* log*[] item (otherwise it is not the same item anymore) */
687 prep_vec->values[prep_vec_idx].key_orig = zbx_strdup(NULL, key);
688 prep_vec->values[prep_vec_idx].persistent_file_name = zbx_strdup(NULL, persistent_file_name);
689
690 zabbix_log(LOG_LEVEL_DEBUG, "%s(): key:[%s] created element %d", __func__, key, prep_vec_idx);
691 }
692 else
693 zabbix_log(LOG_LEVEL_DEBUG, "%s(): key:[%s] found element %d", __func__, key, prep_vec_idx);
694
695 return prep_vec_idx;
696 }
697
zbx_init_prep_vec_data(const struct st_logfile * logfile,zbx_pre_persistent_t * prep_vec_elem)698 void zbx_init_prep_vec_data(const struct st_logfile *logfile, zbx_pre_persistent_t *prep_vec_elem)
699 {
700 /* copy attributes which are stable within one check of the specified log file but */
701 /* may change in the next check */
702
703 if (NULL == prep_vec_elem->filename || 0 != strcmp(prep_vec_elem->filename, logfile->filename))
704 prep_vec_elem->filename = zbx_strdup(prep_vec_elem->filename, logfile->filename);
705
706 prep_vec_elem->mtime = logfile->mtime;
707 prep_vec_elem->seq = logfile->seq;
708 prep_vec_elem->copy_of = logfile->copy_of;
709 prep_vec_elem->dev = logfile->dev;
710 prep_vec_elem->ino_lo = logfile->ino_lo;
711 prep_vec_elem->ino_hi = logfile->ino_hi;
712 prep_vec_elem->size = logfile->size;
713
714 prep_vec_elem->md5_block_size = logfile->md5_block_size;
715 memcpy(prep_vec_elem->first_block_md5, logfile->first_block_md5, sizeof(logfile->first_block_md5));
716
717 prep_vec_elem->last_block_offset = logfile->last_block_offset;
718 memcpy(prep_vec_elem->last_block_md5, logfile->last_block_md5, sizeof(logfile->last_block_md5));
719 }
720
zbx_update_prep_vec_data(const struct st_logfile * logfile,zbx_uint64_t processed_size,zbx_pre_persistent_t * prep_vec_elem)721 void zbx_update_prep_vec_data(const struct st_logfile *logfile, zbx_uint64_t processed_size,
722 zbx_pre_persistent_t *prep_vec_elem)
723 {
724 /* copy attributes which (may) change with every log file record */
725 prep_vec_elem->processed_size = processed_size;
726 prep_vec_elem->incomplete = logfile->incomplete;
727 }
728
729 /******************************************************************************
730 * *
731 * Function: zbx_restore_file_details *
732 * *
733 * Purpose: create the 'old log file list' and restore log file attributes *
734 * from JSON string which was read from persistent file *
735 * *
736 * Parameters: *
737 * str - [IN] JSON string *
738 * logfiles - [OUT] log file list (vector) *
739 * logfiles_num - [OUT] number of elements in the log file list *
740 * processed_size - [OUT] processed size attribute *
741 * mtime - [OUT] mtime *
742 * err_msg - [OUT] dynamically allocated error message *
743 * *
744 * Return value: SUCCEED or FAIL *
745 * *
746 * Examples of valid JSON 'str' (one text line but here it is split for *
747 * readability): *
748 * {"mtime":1623174047, *
749 * "processed_size":0} *
750 * or *
751 * {"filename":"/home/zabbix/test.log", *
752 * "mtime":0, *
753 * "processed_size":324, *
754 * "seq":0, *
755 * "incomplete":0, *
756 * "copy_of":-1, *
757 * "dev":65027, *
758 * "ino_lo":17043636, *
759 * "ino_hi":0, *
760 * "size":635, *
761 * "md5_block_size":512, *
762 * "first_block_md5":"7f2d0cf871384671c51359ce8c90e475", *
763 * "last_block_offset":123, *
764 * "last_block_md5":"86985e105f79b95d6bc918fb45ec7727"} *
765 ******************************************************************************/
zbx_restore_file_details(const char * str,struct st_logfile ** logfiles,int * logfiles_num,zbx_uint64_t * processed_size,int * mtime,char ** err_msg)766 int zbx_restore_file_details(const char *str, struct st_logfile **logfiles, int *logfiles_num,
767 zbx_uint64_t *processed_size, int *mtime, char **err_msg)
768 {
769 struct zbx_json_parse jp;
770 /* temporary variables before filling in 'st_logfile' elements */
771 char filename[MAX_STRING_LEN];
772 int mtime_tmp = 0;
773 int seq = 0;
774 int incomplete = 0;
775 int copy_of = 0;
776 zbx_uint64_t dev;
777 zbx_uint64_t ino_lo;
778 zbx_uint64_t ino_hi;
779 zbx_uint64_t size;
780 zbx_uint64_t processed_size_tmp = 0;
781 int md5_block_size = 0;
782 md5_byte_t first_block_md5[MD5_DIGEST_SIZE];
783 zbx_uint64_t last_block_offset = 0;
784 md5_byte_t last_block_md5[MD5_DIGEST_SIZE];
785 /* flags to check missing attributes */
786 int got_filename = 0, got_mtime = 0, got_seq = 0, got_incomplete = 0, got_copy_of = 0, got_dev = 0,
787 got_ino_lo = 0, got_ino_hi = 0, got_size = 0, got_processed_size = 0, got_md5_block_size = 0,
788 got_first_block_md5 = 0, got_last_block_offset = 0, got_last_block_md5 = 0, sum;
789 char tmp[MAX_STRING_LEN];
790
791 if (0 != *logfiles_num || NULL != *logfiles)
792 {
793 THIS_SHOULD_NEVER_HAPPEN;
794 *err_msg = zbx_dsprintf(*err_msg, "%s(): non-empty log file list, logfiles_num:%d",
795 __func__, *logfiles_num);
796 zabbix_log(LOG_LEVEL_WARNING, "%s(): non-empty log file list, logfiles_num:%d",
797 __func__, *logfiles_num);
798 return FAIL;
799 }
800
801 if (SUCCEED != zbx_json_open(str, &jp))
802 {
803 *err_msg = zbx_dsprintf(*err_msg, "cannot parse persistent data: %s", zbx_json_strerror());
804 return FAIL;
805 }
806
807 if (SUCCEED == zbx_json_value_by_name(&jp, ZBX_PERSIST_TAG_FILENAME, filename, sizeof(filename), NULL))
808 got_filename = 1;
809
810 if (SUCCEED == zbx_json_value_by_name(&jp, ZBX_PERSIST_TAG_MTIME, tmp, sizeof(tmp), NULL))
811 {
812 mtime_tmp = atoi(tmp);
813 got_mtime = 1;
814 }
815
816 if (SUCCEED == zbx_json_value_by_name(&jp, ZBX_PERSIST_TAG_SEQ, tmp, sizeof(tmp), NULL))
817 {
818 seq = atoi(tmp);
819 got_seq = 1;
820 }
821
822 if (SUCCEED == zbx_json_value_by_name(&jp, ZBX_PERSIST_TAG_INCOMPLETE, tmp, sizeof(tmp), NULL))
823 {
824 incomplete = atoi(tmp);
825 got_incomplete = 1;
826 }
827
828 if (SUCCEED == zbx_json_value_by_name(&jp, ZBX_PERSIST_TAG_COPY_OF, tmp, sizeof(tmp), NULL))
829 {
830 copy_of = atoi(tmp);
831 got_copy_of = 1;
832 }
833
834 if (SUCCEED == zbx_json_value_by_name(&jp, ZBX_PERSIST_TAG_DEVICE, tmp, sizeof(tmp), NULL))
835 {
836 if (SUCCEED == is_uint64(tmp, &dev))
837 got_dev = 1;
838 }
839
840 if (SUCCEED == zbx_json_value_by_name(&jp, ZBX_PERSIST_TAG_INODE_LO, tmp, sizeof(tmp), NULL))
841 {
842 if (SUCCEED == is_uint64(tmp, &ino_lo))
843 got_ino_lo = 1;
844 }
845
846 if (SUCCEED == zbx_json_value_by_name(&jp, ZBX_PERSIST_TAG_INODE_HI, tmp, sizeof(tmp), NULL))
847 {
848 if (SUCCEED == is_uint64(tmp, &ino_hi))
849 got_ino_hi = 1;
850 }
851
852 if (SUCCEED == zbx_json_value_by_name(&jp, ZBX_PERSIST_TAG_SIZE, tmp, sizeof(tmp), NULL))
853 {
854 if (SUCCEED == is_uint64(tmp, &size))
855 got_size = 1;
856 }
857
858 if (SUCCEED == zbx_json_value_by_name(&jp, ZBX_PERSIST_TAG_PROCESSED_SIZE, tmp, sizeof(tmp), NULL))
859 {
860 if (SUCCEED == is_uint64(tmp, &processed_size_tmp))
861 got_processed_size = 1;
862 }
863
864 if (SUCCEED == zbx_json_value_by_name(&jp, ZBX_PERSIST_TAG_MD5_BLOCK_SIZE, tmp, sizeof(tmp), NULL))
865 {
866 md5_block_size = atoi(tmp);
867 got_md5_block_size = 1;
868 }
869
870 if (SUCCEED == zbx_json_value_by_name(&jp, ZBX_PERSIST_TAG_FIRST_BLOCK_MD5, tmp, sizeof(tmp), NULL))
871 {
872 if (sizeof(first_block_md5) == zbx_hex2bin((const unsigned char *)tmp, first_block_md5,
873 sizeof(first_block_md5)))
874 {
875 got_first_block_md5 = 1;
876 }
877 }
878
879 if (SUCCEED == zbx_json_value_by_name(&jp, ZBX_PERSIST_TAG_LAST_BLOCK_OFFSET, tmp, sizeof(tmp), NULL))
880 {
881 if (SUCCEED == is_uint64(tmp, &last_block_offset))
882 got_last_block_offset = 1;
883 }
884
885 if (SUCCEED == zbx_json_value_by_name(&jp, ZBX_PERSIST_TAG_LAST_BLOCK_MD5, tmp, sizeof(tmp), NULL))
886 {
887 if (sizeof(last_block_md5) == zbx_hex2bin((const unsigned char *)tmp, last_block_md5,
888 sizeof(last_block_md5)))
889 {
890 got_last_block_md5 = 1;
891 }
892 }
893
894 /* 'mtime' and 'processed_size' should always be present */
895 if (0 == got_mtime || 0 == got_processed_size)
896 {
897 *err_msg = zbx_dsprintf(*err_msg, "corrupted data: 'mtime' or 'processed_size' attribute missing");
898 return FAIL;
899 }
900
901 /* all other 12 variables should be present or none of them */
902 sum = got_filename + got_seq + got_incomplete + got_copy_of + got_dev + got_ino_lo + got_ino_hi + got_size +
903 got_md5_block_size + got_first_block_md5 + got_last_block_offset + got_last_block_md5;
904
905 if (0 == sum) /* got only metadata 'mtime' and 'processed_size' */
906 {
907 *mtime = mtime_tmp;
908 *processed_size = processed_size_tmp;
909 return SUCCEED;
910 }
911
912 if (12 != sum)
913 {
914 *err_msg = zbx_dsprintf(*err_msg, "present/missing attributes: filename:%d seq:%d incomplete:%d"
915 " copy_of:%d dev:%d ino_lo:%d ino_hi:%d size:%d md5_block_size:%d first_block_md5:%d"
916 " last_block_offset:%d last_block_md5:%d", got_filename, got_seq,
917 got_incomplete, got_copy_of, got_dev, got_ino_lo, got_ino_hi, got_size,
918 got_md5_block_size, got_first_block_md5, got_last_block_offset, got_last_block_md5);
919 return FAIL;
920 }
921
922 /* All attributes present. Create log file list with one element. It will be used as the 'old log file list', */
923 /* it does not need to be resizable. */
924 *logfiles = (struct st_logfile *)zbx_malloc(NULL, sizeof(struct st_logfile));
925 *logfiles_num = 1;
926
927 (*logfiles)[0].filename = zbx_strdup(NULL, filename);
928 (*logfiles)[0].mtime = mtime_tmp;
929 (*logfiles)[0].seq = seq;
930 (*logfiles)[0].retry = 0;
931 (*logfiles)[0].incomplete = incomplete;
932 (*logfiles)[0].copy_of = copy_of;
933 (*logfiles)[0].dev = dev;
934 (*logfiles)[0].ino_lo = ino_lo;
935 (*logfiles)[0].ino_hi = ino_hi;
936 (*logfiles)[0].size = size;
937 (*logfiles)[0].processed_size = processed_size_tmp;
938
939 (*logfiles)[0].md5_block_size = md5_block_size;
940 memcpy((*logfiles)[0].first_block_md5, first_block_md5, sizeof(first_block_md5));
941
942 (*logfiles)[0].last_block_offset = last_block_offset;
943 memcpy((*logfiles)[0].last_block_md5, last_block_md5, sizeof(last_block_md5));
944
945 *mtime = mtime_tmp;
946 *processed_size = processed_size_tmp;
947
948 return SUCCEED;
949 }
950 #endif /* not WINDOWS */
951