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