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
22 #include "cfg.h"
23 #include "db.h"
24 #include "log.h"
25 #include "daemon.h"
26 #include "zbxmedia.h"
27 #include "zbxserver.h"
28 #include "zbxself.h"
29 #include "zbxexec.h"
30 #include "zbxipcservice.h"
31
32 #include "alerter.h"
33 #include "alerter_protocol.h"
34 #include "alert_manager.h"
35 #include "zbxembed.h"
36
37 #define ALARM_ACTION_TIMEOUT 40
38
39 extern unsigned char process_type, program_type;
40 extern int server_num, process_num;
41
42 static zbx_es_t es_engine;
43
44 /******************************************************************************
45 * *
46 * Function: execute_script_alert *
47 * *
48 * Purpose: execute script alert type *
49 * *
50 ******************************************************************************/
execute_script_alert(const char * command,char * error,size_t max_error_len)51 static int execute_script_alert(const char *command, char *error, size_t max_error_len)
52 {
53 char *output = NULL;
54 int ret = FAIL;
55
56 if (SUCCEED == (ret = zbx_execute(command, &output, error, max_error_len, ALARM_ACTION_TIMEOUT,
57 ZBX_EXIT_CODE_CHECKS_ENABLED)))
58 {
59 zabbix_log(LOG_LEVEL_DEBUG, "%s output:\n%s", command, output);
60 zbx_free(output);
61 }
62
63 return ret;
64 }
65
66 /******************************************************************************
67 * *
68 * Function: alerter_register *
69 * *
70 * Purpose: registers alerter with alert manager *
71 * *
72 * Parameters: socket - [IN] the connections socket *
73 * *
74 ******************************************************************************/
alerter_register(zbx_ipc_socket_t * socket)75 static void alerter_register(zbx_ipc_socket_t *socket)
76 {
77 pid_t ppid;
78
79 ppid = getppid();
80
81 zbx_ipc_socket_write(socket, ZBX_IPC_ALERTER_REGISTER, (unsigned char *)&ppid, sizeof(ppid));
82 }
83
84 /******************************************************************************
85 * *
86 * Function: alerter_send_result *
87 * *
88 * Purpose: sends alert sending result to alert manager *
89 * *
90 * Parameters: socket - [IN] the connections socket *
91 * errcode - [IN] the error code *
92 * value - [IN] the value or error message *
93 * debug - [IN] debug message *
94 * *
95 ******************************************************************************/
alerter_send_result(zbx_ipc_socket_t * socket,const char * value,int errcode,const char * error,const char * debug)96 static void alerter_send_result(zbx_ipc_socket_t *socket, const char *value, int errcode, const char *error,
97 const char *debug)
98 {
99 unsigned char *data;
100 zbx_uint32_t data_len;
101
102 data_len = zbx_alerter_serialize_result(&data, value, errcode, error, debug);
103 zbx_ipc_socket_write(socket, ZBX_IPC_ALERTER_RESULT, data, data_len);
104
105 zbx_free(data);
106 }
107
108 /******************************************************************************
109 * *
110 * Function: create_email_inreplyto *
111 * *
112 * Purpose: create email In-Reply_To field value to group related messages *
113 * *
114 ******************************************************************************/
create_email_inreplyto(zbx_uint64_t mediatypeid,const char * sendto,zbx_uint64_t eventid)115 static char *create_email_inreplyto(zbx_uint64_t mediatypeid, const char *sendto, zbx_uint64_t eventid)
116 {
117 const char *hex = "0123456789abcdef";
118 char *str = NULL;
119 md5_state_t state;
120 md5_byte_t hash[MD5_DIGEST_SIZE];
121 int i;
122 size_t str_alloc = 0, str_offset = 0;
123
124 zbx_md5_init(&state);
125 zbx_md5_append(&state, (const md5_byte_t *)sendto, strlen(sendto));
126 zbx_md5_finish(&state, hash);
127
128 zbx_snprintf_alloc(&str, &str_alloc, &str_offset, ZBX_FS_UI64 ".", eventid);
129
130 for (i = 0; i < MD5_DIGEST_SIZE; i++)
131 {
132 zbx_chrcpy_alloc(&str, &str_alloc, &str_offset, hex[hash[i] >> 4]);
133 zbx_chrcpy_alloc(&str, &str_alloc, &str_offset, hex[hash[i] & 15]);
134 }
135
136 zbx_snprintf_alloc(&str, &str_alloc, &str_offset, "." ZBX_FS_UI64 ".%s@zabbix.com", mediatypeid,
137 zbx_dc_get_instanceid());
138
139 return str;
140 }
141
142 /******************************************************************************
143 * *
144 * Function: alerter_process_email *
145 * *
146 * Purpose: processes email alert *
147 * *
148 * Parameters: socket - [IN] the connections socket *
149 * ipc_message - [IN] the ipc message with media type and alert *
150 * data *
151 * *
152 ******************************************************************************/
alerter_process_email(zbx_ipc_socket_t * socket,zbx_ipc_message_t * ipc_message)153 static void alerter_process_email(zbx_ipc_socket_t *socket, zbx_ipc_message_t *ipc_message)
154 {
155 zbx_uint64_t alertid, mediatypeid, eventid;
156 char *sendto, *subject, *message, *smtp_server, *smtp_helo, *smtp_email, *username, *password,
157 *inreplyto;
158 unsigned short smtp_port;
159 unsigned char smtp_security, smtp_verify_peer, smtp_verify_host, smtp_authentication, content_type;
160 int ret;
161 char error[MAX_STRING_LEN];
162
163
164 zbx_alerter_deserialize_email(ipc_message->data, &alertid, &mediatypeid, &eventid, &sendto, &subject, &message,
165 &smtp_server, &smtp_port, &smtp_helo, &smtp_email, &smtp_security, &smtp_verify_peer,
166 &smtp_verify_host, &smtp_authentication, &username, &password, &content_type);
167
168 inreplyto = create_email_inreplyto(mediatypeid, sendto, eventid);
169 ret = send_email(smtp_server, smtp_port, smtp_helo, smtp_email, sendto, inreplyto, subject, message,
170 smtp_security, smtp_verify_peer, smtp_verify_host, smtp_authentication, username, password,
171 content_type, ALARM_ACTION_TIMEOUT, error, sizeof(error));
172
173 alerter_send_result(socket, NULL, ret, (SUCCEED == ret ? NULL : error), NULL);
174
175 zbx_free(inreplyto);
176 zbx_free(sendto);
177 zbx_free(subject);
178 zbx_free(message);
179 zbx_free(smtp_server);
180 zbx_free(smtp_helo);
181 zbx_free(smtp_email);
182 zbx_free(username);
183 zbx_free(password);
184 }
185
186 /******************************************************************************
187 * *
188 * Function: alerter_process_sms *
189 * *
190 * Purpose: processes SMS alert *
191 * *
192 * Parameters: socket - [IN] the connections socket *
193 * ipc_message - [IN] the ipc message with media type and alert *
194 * data *
195 * *
196 ******************************************************************************/
alerter_process_sms(zbx_ipc_socket_t * socket,zbx_ipc_message_t * ipc_message)197 static void alerter_process_sms(zbx_ipc_socket_t *socket, zbx_ipc_message_t *ipc_message)
198 {
199 zbx_uint64_t alertid;
200 char *sendto, *message, *gsm_modem;
201 int ret;
202 char error[MAX_STRING_LEN];
203
204 zbx_alerter_deserialize_sms(ipc_message->data, &alertid, &sendto, &message, &gsm_modem);
205
206 /* SMS uses its own timeouts */
207 ret = send_sms(gsm_modem, sendto, message, error, sizeof(error));
208 alerter_send_result(socket, NULL, ret, (SUCCEED == ret ? NULL : error), NULL);
209
210 zbx_free(sendto);
211 zbx_free(message);
212 zbx_free(gsm_modem);
213 }
214
215 /******************************************************************************
216 * *
217 * Function: alerter_process_exec *
218 * *
219 * Purpose: processes script alert *
220 * *
221 * Parameters: socket - [IN] the connections socket *
222 * ipc_message - [IN] the ipc message with media type and alert *
223 * data *
224 * *
225 ******************************************************************************/
alerter_process_exec(zbx_ipc_socket_t * socket,zbx_ipc_message_t * ipc_message)226 static void alerter_process_exec(zbx_ipc_socket_t *socket, zbx_ipc_message_t *ipc_message)
227 {
228 zbx_uint64_t alertid;
229 char *command;
230 int ret;
231 char error[MAX_STRING_LEN];
232
233 zbx_alerter_deserialize_exec(ipc_message->data, &alertid, &command);
234
235 ret = execute_script_alert(command, error, sizeof(error));
236 alerter_send_result(socket, NULL, ret, (SUCCEED == ret ? NULL : error), NULL);
237
238 zbx_free(command);
239 }
240
241 /******************************************************************************
242 * *
243 * Function: alerter_process_webhook *
244 * *
245 * Purpose: processes webhook alert *
246 * *
247 * Parameters: socket - [IN] the connections socket *
248 * ipc_message - [IN] the ipc message with media type and alert *
249 * data *
250 * *
251 ******************************************************************************/
alerter_process_webhook(zbx_ipc_socket_t * socket,zbx_ipc_message_t * ipc_message)252 static void alerter_process_webhook(zbx_ipc_socket_t *socket, zbx_ipc_message_t *ipc_message)
253 {
254 char *script_bin = NULL, *params = NULL, *error = NULL, *output = NULL;
255 int script_bin_sz, ret, timeout;
256 unsigned char debug;
257
258 zbx_alerter_deserialize_webhook(ipc_message->data, &script_bin, &script_bin_sz, &timeout, ¶ms, &debug);
259
260 if (SUCCEED != (ret = zbx_es_is_env_initialized(&es_engine)))
261 ret = zbx_es_init_env(&es_engine, &error);
262
263 if (SUCCEED == ret)
264 {
265 zbx_es_set_timeout(&es_engine, timeout);
266
267 if (ZBX_ALERT_DEBUG == debug)
268 zbx_es_debug_enable(&es_engine);
269
270 ret = zbx_es_execute(&es_engine, NULL, script_bin, script_bin_sz, params, &output, &error);
271 }
272
273 if (ZBX_ALERT_DEBUG == debug && SUCCEED == zbx_es_is_env_initialized(&es_engine))
274 {
275 alerter_send_result(socket, output, ret, error, zbx_es_debug_info(&es_engine));
276 zbx_es_debug_disable(&es_engine);
277 }
278 else
279 alerter_send_result(socket, output, ret, error, NULL);
280
281 if (SUCCEED == zbx_es_fatal_error(&es_engine))
282 {
283 char *errmsg = NULL;
284 if (SUCCEED != zbx_es_destroy_env(&es_engine, &errmsg))
285 {
286 zabbix_log(LOG_LEVEL_WARNING,
287 "Cannot destroy embedded scripting engine environment: %s", errmsg);
288 zbx_free(errmsg);
289 }
290 }
291
292 zbx_free(output);
293 zbx_free(error);
294 zbx_free(params);
295 zbx_free(script_bin);
296 }
297
298 /******************************************************************************
299 * *
300 * Function: main_alerter_loop *
301 * *
302 * Purpose: periodically check table alerts and send notifications if needed *
303 * *
304 * Author: Alexei Vladishev *
305 * *
306 ******************************************************************************/
ZBX_THREAD_ENTRY(alerter_thread,args)307 ZBX_THREAD_ENTRY(alerter_thread, args)
308 {
309 #define STAT_INTERVAL 5 /* if a process is busy and does not sleep then update status not faster than */
310 /* once in STAT_INTERVAL seconds */
311
312 char *error = NULL;
313 int success_num = 0, fail_num = 0;
314 zbx_ipc_socket_t alerter_socket;
315 zbx_ipc_message_t message;
316 double time_stat, time_idle = 0, time_now, time_read;
317
318 process_type = ((zbx_thread_args_t *)args)->process_type;
319 server_num = ((zbx_thread_args_t *)args)->server_num;
320 process_num = ((zbx_thread_args_t *)args)->process_num;
321
322 zabbix_log(LOG_LEVEL_INFORMATION, "%s #%d started [%s #%d]", get_program_type_string(program_type),
323 server_num, get_process_type_string(process_type), process_num);
324
325 update_selfmon_counter(ZBX_PROCESS_STATE_BUSY);
326
327 zbx_setproctitle("%s [connecting to the database]", get_process_type_string(process_type));
328
329 zbx_es_init(&es_engine);
330
331 zbx_ipc_message_init(&message);
332
333 if (FAIL == zbx_ipc_socket_open(&alerter_socket, ZBX_IPC_SERVICE_ALERTER, SEC_PER_MIN, &error))
334 {
335 zabbix_log(LOG_LEVEL_CRIT, "cannot connect to alert manager service: %s", error);
336 zbx_free(error);
337 exit(EXIT_FAILURE);
338 }
339
340 alerter_register(&alerter_socket);
341
342 time_stat = zbx_time();
343
344 zbx_setproctitle("%s #%d started", get_process_type_string(process_type), process_num);
345
346 update_selfmon_counter(ZBX_PROCESS_STATE_BUSY);
347
348 while (ZBX_IS_RUNNING())
349 {
350 time_now = zbx_time();
351
352 if (STAT_INTERVAL < time_now - time_stat)
353 {
354 zbx_setproctitle("%s #%d [sent %d, failed %d alerts, idle " ZBX_FS_DBL " sec during "
355 ZBX_FS_DBL " sec]", get_process_type_string(process_type), process_num,
356 success_num, fail_num, time_idle, time_now - time_stat);
357
358 time_stat = time_now;
359 time_idle = 0;
360 success_num = 0;
361 fail_num = 0;
362 }
363
364 update_selfmon_counter(ZBX_PROCESS_STATE_IDLE);
365
366 if (SUCCEED != zbx_ipc_socket_read(&alerter_socket, &message))
367 {
368 zabbix_log(LOG_LEVEL_CRIT, "cannot read alert manager service request");
369 exit(EXIT_FAILURE);
370 }
371
372 update_selfmon_counter(ZBX_PROCESS_STATE_BUSY);
373
374 time_read = zbx_time();
375 time_idle += time_read - time_now;
376 zbx_update_env(time_read);
377
378 switch (message.code)
379 {
380 case ZBX_IPC_ALERTER_EMAIL:
381 alerter_process_email(&alerter_socket, &message);
382 break;
383 case ZBX_IPC_ALERTER_SMS:
384 alerter_process_sms(&alerter_socket, &message);
385 break;
386 case ZBX_IPC_ALERTER_EXEC:
387 alerter_process_exec(&alerter_socket, &message);
388 break;
389 case ZBX_IPC_ALERTER_WEBHOOK:
390 alerter_process_webhook(&alerter_socket, &message);
391 break;
392 }
393
394 zbx_ipc_message_clean(&message);
395 }
396
397 zbx_setproctitle("%s #%d [terminated]", get_process_type_string(process_type), process_num);
398
399 while (1)
400 zbx_sleep(SEC_PER_MIN);
401
402 zbx_ipc_socket_close(&alerter_socket);
403 }
404