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, NULL)))
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, &params, &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