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 "log.h"
22 #include "export.h"
23 
24 #define ZBX_OPTION_EXPTYPE_EVENTS	"events"
25 #define ZBX_OPTION_EXPTYPE_HISTORY	"history"
26 #define ZBX_OPTION_EXPTYPE_TRENDS	"trends"
27 
28 extern char		*CONFIG_EXPORT_DIR;
29 extern char		*CONFIG_EXPORT_TYPE;
30 extern zbx_uint64_t	CONFIG_EXPORT_FILE_SIZE;
31 
32 typedef struct
33 {
34 	char	*name;
35 	FILE	*file;
36 	int	missing;
37 }
38 zbx_export_file_t;
39 
40 static zbx_export_file_t	*history_file;
41 static zbx_export_file_t	*trends_file;
42 static zbx_export_file_t	*problems_file;
43 
44 static char	*export_dir;
45 
46 /******************************************************************************
47  *                                                                            *
48  * Function: zbx_validate_export_type                                         *
49  *                                                                            *
50  * Purpose: validate export type                                              *
51  *                                                                            *
52  * Parameters:  export_type - [in] list of export types                       *
53  *              export_mask - [out] export types mask (if SUCCEED)            *
54  *                                                                            *
55  * Return value: SUCCEED - valid configuration                                *
56  *               FAIL    - otherwise                                          *
57  *                                                                            *
58  ******************************************************************************/
zbx_validate_export_type(char * export_type,uint32_t * export_mask)59 int	zbx_validate_export_type(char *export_type, uint32_t *export_mask)
60 {
61 	int		ret = SUCCEED;
62 	char		*start = export_type;
63 	uint32_t	mask;
64 	char		*types[] = {
65 				ZBX_OPTION_EXPTYPE_EVENTS,
66 				ZBX_OPTION_EXPTYPE_HISTORY,
67 				ZBX_OPTION_EXPTYPE_TRENDS,
68 				NULL};
69 	size_t		lengths[] = {
70 				ZBX_CONST_STRLEN(ZBX_OPTION_EXPTYPE_EVENTS),
71 				ZBX_CONST_STRLEN(ZBX_OPTION_EXPTYPE_HISTORY),
72 				ZBX_CONST_STRLEN(ZBX_OPTION_EXPTYPE_TRENDS),
73 				0};
74 
75 	if (NULL != start)
76 	{
77 		mask = 0;
78 
79 		do
80 		{
81 			int	i;
82 			char	*end;
83 
84 			end = strchr(start, ',');
85 
86 			for (i = 0; NULL != types[i]; i++)
87 			{
88 				if ((NULL != end && lengths[i] == (size_t)(end - start) &&
89 						0 == strncmp(start, types[i], lengths[i])) ||
90 						(NULL == end && 0 == strcmp(start, types[i])))
91 				{
92 					mask |= (uint32_t)(1 << i);
93 					break;
94 				}
95 			}
96 
97 			if (NULL == types[i])
98 			{
99 				ret = FAIL;
100 				break;
101 			}
102 
103 			start = end;
104 		} while (NULL != start++);
105 	}
106 	else
107 		mask = ZBX_FLAG_EXPTYPE_EVENTS | ZBX_FLAG_EXPTYPE_HISTORY | ZBX_FLAG_EXPTYPE_TRENDS;
108 
109 	if (NULL != export_mask)
110 		*export_mask = mask;
111 
112 	return ret;
113 }
114 
115 /******************************************************************************
116  *                                                                            *
117  * Function: zbx_is_export_enabled                                            *
118  *                                                                            *
119  * Purpose: checks if export is enabled for given type(s)                     *
120  *                                                                            *
121  * Parameters: flag - ZBX_FLAG_EXPTYPE_EVENTS events are enabled              *
122  *                    ZBX_FLAG_EXPTYPE_HISTORY history is enabled             *
123  *                    ZBX_FLAG_EXPTYPE_TRENDS trends are enabled              *
124  *                                                                            *
125  * Return value: SUCCEED - export enabled                                     *
126  *               FAIL    - otherwise                                          *
127  *                                                                            *
128  ******************************************************************************/
zbx_is_export_enabled(uint32_t flags)129 int	zbx_is_export_enabled(uint32_t flags)
130 {
131 	int			ret = FAIL;
132 	static uint32_t		export_types;
133 
134 	if (NULL == CONFIG_EXPORT_DIR)
135 		return ret;
136 
137 	if (NULL != CONFIG_EXPORT_TYPE)
138 	{
139 		if (0 == export_types)
140 			zbx_validate_export_type(CONFIG_EXPORT_TYPE, &export_types);
141 
142 		if (0 != (export_types & flags))
143 			ret = SUCCEED;
144 	}
145 	else
146 		ret = SUCCEED;
147 
148 	return ret;
149 }
150 
zbx_export_init(char ** error)151 int	zbx_export_init(char **error)
152 {
153 	struct stat	fs;
154 
155 	if (FAIL == zbx_is_export_enabled(ZBX_FLAG_EXPTYPE_EVENTS | ZBX_FLAG_EXPTYPE_TRENDS | ZBX_FLAG_EXPTYPE_HISTORY))
156 	{
157 		if (NULL != CONFIG_EXPORT_TYPE)
158 		{
159 			*error = zbx_dsprintf(*error, "Misconfiguration: \"ExportType\" is set to '%s' "
160 					"while \"ExportDir\" is unset.", CONFIG_EXPORT_TYPE);
161 			return FAIL;
162 		}
163 		return SUCCEED;
164 	}
165 
166 	if (NULL == CONFIG_EXPORT_TYPE)
167 	{
168 		CONFIG_EXPORT_TYPE = zbx_dsprintf(CONFIG_EXPORT_TYPE, "%s,%s,%s", ZBX_OPTION_EXPTYPE_EVENTS,
169 				ZBX_OPTION_EXPTYPE_HISTORY, ZBX_OPTION_EXPTYPE_TRENDS);
170 	}
171 
172 	if (0 != stat(CONFIG_EXPORT_DIR, &fs))
173 	{
174 		*error = zbx_dsprintf(*error, "Failed to stat the specified path \"%s\": %s.", CONFIG_EXPORT_DIR,
175 				zbx_strerror(errno));
176 		return FAIL;
177 	}
178 
179 	if (0 == S_ISDIR(fs.st_mode))
180 	{
181 		*error = zbx_dsprintf(*error, "The specified path \"%s\" is not a directory.", CONFIG_EXPORT_DIR);
182 		return FAIL;
183 	}
184 
185 	if (0 != access(CONFIG_EXPORT_DIR, W_OK | R_OK))
186 	{
187 		*error = zbx_dsprintf(*error, "Cannot access path \"%s\": %s.", CONFIG_EXPORT_DIR, zbx_strerror(errno));
188 		return FAIL;
189 	}
190 
191 	export_dir = zbx_strdup(NULL, CONFIG_EXPORT_DIR);
192 
193 	if ('/' == export_dir[strlen(export_dir) - 1])
194 		export_dir[strlen(export_dir) - 1] = '\0';
195 
196 	return SUCCEED;
197 }
198 
open_export_file(zbx_export_file_t * file,char ** error)199 static int	open_export_file(zbx_export_file_t *file, char **error)
200 {
201 	if (NULL == (file->file = fopen(file->name, "a")))
202 	{
203 		*error = zbx_dsprintf(*error, "cannot open export file '%s': %s", file->name, zbx_strerror(errno));
204 		return FAIL;
205 	}
206 
207 	zabbix_log(LOG_LEVEL_DEBUG, "successfully created export file '%s'", file->name);
208 
209 	return SUCCEED;
210 }
211 
export_init(zbx_export_file_t * file,const char * process_type,const char * process_name,int process_num)212 static zbx_export_file_t	*export_init(zbx_export_file_t *file, const char *process_type, const char
213 				*process_name,	int process_num)
214 {
215 	char	*error = NULL;
216 
217 	file = (zbx_export_file_t *)zbx_malloc(NULL, sizeof(zbx_export_file_t));
218 	file->name = zbx_dsprintf(NULL, "%s/%s-%s-%d.ndjson", export_dir, process_type, process_name, process_num);
219 
220 	if (FAIL == open_export_file(file, &error))
221 	{
222 		zabbix_log(LOG_LEVEL_CRIT, "%s", error);
223 		exit(EXIT_FAILURE);
224 	}
225 
226 	file->missing = 0;
227 
228 	return file;
229 }
230 
zbx_history_export_init(const char * process_name,int process_num)231 void	zbx_history_export_init(const char *process_name, int process_num)
232 {
233 	history_file = export_init(history_file, "history", process_name, process_num);
234 }
235 
zbx_trends_export_init(const char * process_name,int process_num)236 void	zbx_trends_export_init(const char *process_name, int process_num)
237 {
238 	trends_file = export_init(trends_file, "trends", process_name, process_num);
239 }
240 
zbx_problems_export_init(const char * process_name,int process_num)241 void	zbx_problems_export_init(const char *process_name, int process_num)
242 {
243 	problems_file = export_init(problems_file, "problems", process_name, process_num);
244 }
245 
file_write(const char * buf,size_t count,zbx_export_file_t * file)246 static void	file_write(const char *buf, size_t count, zbx_export_file_t *file)
247 {
248 #define ZBX_LOGGING_SUSPEND_TIME	10
249 
250 	static time_t	last_log_time = 0;
251 	time_t		now;
252 	char		*error_msg = NULL;
253 	long		file_offset;
254 
255 	if (0 == file->missing && 0 != access(file->name, F_OK))
256 	{
257 		if (NULL != file->file && 0 != fclose(file->file))
258 			zabbix_log(LOG_LEVEL_DEBUG, "cannot close export file '%s': %s",file->name, zbx_strerror(errno));
259 
260 		file->file = NULL;
261 	}
262 
263 	if (NULL == file->file && FAIL == open_export_file(file, &error_msg))
264 	{
265 		file->missing = 1;
266 		goto error;
267 	}
268 
269 	if (1 == file->missing)
270 	{
271 		file->missing = 0;
272 		zabbix_log(LOG_LEVEL_ERR, "regained access to export file '%s'", file->name);
273 	}
274 
275 	if (-1 == (file_offset = ftell(file->file)))
276 	{
277 		error_msg = zbx_dsprintf(error_msg, "cannot get current position in export file '%s': %s",
278 				file->name, zbx_strerror(errno));
279 		goto error;
280 	}
281 
282 	if (CONFIG_EXPORT_FILE_SIZE <= count + (size_t)file_offset + 1)
283 	{
284 		char	filename_old[MAX_STRING_LEN];
285 
286 		strscpy(filename_old, file->name);
287 		zbx_strlcat(filename_old, ".old", MAX_STRING_LEN);
288 
289 		if (0 == access(filename_old, F_OK) && 0 != remove(filename_old))
290 		{
291 			error_msg = zbx_dsprintf(error_msg, "cannot remove export file '%s': %s",
292 					filename_old, zbx_strerror(errno));
293 			goto error;
294 		}
295 
296 		if (0 != fclose(file->file))
297 		{
298 			error_msg = zbx_dsprintf(error_msg, "cannot close export file %s': %s",
299 					file->name, zbx_strerror(errno));
300 			file->file = NULL;
301 			goto error;
302 		}
303 		file->file = NULL;
304 
305 		if (0 != rename(file->name, filename_old))
306 		{
307 			error_msg = zbx_dsprintf(error_msg, "cannot rename export file '%s': %s",
308 					file->name, zbx_strerror(errno));
309 			goto error;
310 		}
311 
312 		if (FAIL == open_export_file(file, &error_msg))
313 			goto error;
314 	}
315 
316 	if (count != fwrite(buf, 1, count, file->file) || '\n' != fputc('\n', file->file))
317 	{
318 		error_msg = zbx_dsprintf(error_msg, "cannot write to export file '%s': %s", file->name,
319 				zbx_strerror(errno));
320 		goto error;
321 	}
322 
323 	return;
324 error:
325 	if (NULL != file->file && 0 != fclose(file->file))
326 	{
327 		error_msg = zbx_dsprintf(error_msg, "%s; cannot close export file %s': %s",
328 				error_msg, file->name, zbx_strerror(errno));
329 	}
330 
331 	file->file = NULL;
332 	now = time(NULL);
333 
334 	if (ZBX_LOGGING_SUSPEND_TIME < now - last_log_time)
335 	{
336 		zabbix_log(LOG_LEVEL_ERR, "%s", error_msg);
337 		last_log_time = now;
338 	}
339 
340 	zbx_free(error_msg);
341 
342 #undef ZBX_LOGGING_SUSPEND_TIME
343 }
344 
zbx_problems_export_write(const char * buf,size_t count)345 void	zbx_problems_export_write(const char *buf, size_t count)
346 {
347 	file_write(buf, count, problems_file);
348 }
349 
zbx_history_export_write(const char * buf,size_t count)350 void	zbx_history_export_write(const char *buf, size_t count)
351 {
352 	file_write(buf, count, history_file);
353 }
354 
zbx_trends_export_write(const char * buf,size_t count)355 void	zbx_trends_export_write(const char *buf, size_t count)
356 {
357 	file_write(buf, count, trends_file);
358 }
359 
zbx_flush(FILE * file,const char * file_name)360 static void	zbx_flush(FILE *file, const char *file_name)
361 {
362 	if (0 != fflush(file))
363 		zabbix_log(LOG_LEVEL_ERR, "cannot flush export file '%s': %s", file_name, zbx_strerror(errno));
364 }
365 
zbx_problems_export_flush(void)366 void	zbx_problems_export_flush(void)
367 {
368 	if (NULL != problems_file->file)
369 		zbx_flush(problems_file->file, problems_file->name);
370 }
371 
zbx_history_export_flush(void)372 void	zbx_history_export_flush(void)
373 {
374 	if (NULL != history_file->file)
375 		zbx_flush(history_file->file, history_file->name);
376 }
377 
zbx_trends_export_flush(void)378 void	zbx_trends_export_flush(void)
379 {
380 	if (NULL != trends_file->file)
381 		zbx_flush(trends_file->file, trends_file->name);
382 }
383