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