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 
31 #include "alerter.h"
32 
33 #define	ALARM_ACTION_TIMEOUT	40
34 
35 extern unsigned char	process_type, program_type;
36 extern int		server_num, process_num;
37 
38 /******************************************************************************
39  *                                                                            *
40  * Function: execute_action                                                   *
41  *                                                                            *
42  * Purpose: execute an action depending on mediatype                          *
43  *                                                                            *
44  * Parameters: alert - alert details                                          *
45  *             mediatype - media details                                      *
46  *                                                                            *
47  * Return value: SUCCESS - action executed successfully                       *
48  *               FAIL - otherwise, error will contain error message           *
49  *                                                                            *
50  * Author: Alexei Vladishev                                                   *
51  *                                                                            *
52  ******************************************************************************/
execute_action(DB_ALERT * alert,DB_MEDIATYPE * mediatype,char * error,int max_error_len)53 int	execute_action(DB_ALERT *alert, DB_MEDIATYPE *mediatype, char *error, int max_error_len)
54 {
55 	const char	*__function_name = "execute_action";
56 
57 	int		res = FAIL;
58 
59 	zabbix_log(LOG_LEVEL_DEBUG, "In %s(): alertid [" ZBX_FS_UI64 "] mediatype [%d]",
60 			__function_name, alert->alertid, mediatype->type);
61 
62 	if (MEDIA_TYPE_EMAIL == mediatype->type)
63 	{
64 		res = send_email(mediatype->smtp_server, mediatype->smtp_port, mediatype->smtp_helo,
65 				mediatype->smtp_email, alert->sendto, alert->subject, alert->message,
66 				mediatype->smtp_security, mediatype->smtp_verify_peer, mediatype->smtp_verify_host,
67 				mediatype->smtp_authentication, mediatype->username, mediatype->passwd,
68 				ALARM_ACTION_TIMEOUT, error, max_error_len);
69 	}
70 #ifdef HAVE_JABBER
71 	else if (MEDIA_TYPE_JABBER == mediatype->type)
72 	{
73 		/* Jabber uses its own timeouts */
74 		res = send_jabber(mediatype->username, mediatype->passwd,
75 				alert->sendto, alert->subject, alert->message, error, max_error_len);
76 	}
77 #endif
78 	else if (MEDIA_TYPE_SMS == mediatype->type)
79 	{
80 		/* SMS uses its own timeouts */
81 		res = send_sms(mediatype->gsm_modem, alert->sendto, alert->message, error, max_error_len);
82 	}
83 	else if (MEDIA_TYPE_EZ_TEXTING == mediatype->type)
84 	{
85 		/* Ez Texting uses its own timeouts */
86 		res = send_ez_texting(mediatype->username, mediatype->passwd,
87 				alert->sendto, alert->message, mediatype->exec_path, error, max_error_len);
88 	}
89 	else if (MEDIA_TYPE_EXEC == mediatype->type)
90 	{
91 		char	*cmd = NULL, *output = NULL;
92 		size_t	cmd_alloc = ZBX_KIBIBYTE, cmd_offset = 0;
93 
94 		cmd = zbx_malloc(cmd, cmd_alloc);
95 
96 		zbx_snprintf_alloc(&cmd, &cmd_alloc, &cmd_offset, "%s/%s",
97 				CONFIG_ALERT_SCRIPTS_PATH, mediatype->exec_path);
98 
99 		if (0 == access(cmd, X_OK))
100 		{
101 			char	*pstart, *pend;
102 
103 			for (pstart = mediatype->exec_params; NULL != (pend = strchr(pstart, '\n')); pstart = pend + 1)
104 			{
105 				char	*param_esc, *param = NULL;
106 				size_t	param_alloc = 0, param_offset = 0;
107 
108 				zbx_strncpy_alloc(&param, &param_alloc, &param_offset, pstart, pend - pstart);
109 
110 				substitute_simple_macros(NULL, NULL, NULL, NULL, NULL, NULL, NULL, alert, &param,
111 						MACRO_TYPE_ALERT, NULL, 0);
112 
113 				param_esc = zbx_dyn_escape_shell_single_quote(param);
114 
115 				zbx_snprintf_alloc(&cmd, &cmd_alloc, &cmd_offset, " '%s'", param_esc);
116 
117 				zbx_free(param_esc);
118 				zbx_free(param);
119 			}
120 
121 
122 			if (SUCCEED == (res = zbx_execute(cmd, &output, error, max_error_len, ALARM_ACTION_TIMEOUT)))
123 			{
124 				zabbix_log(LOG_LEVEL_DEBUG, "%s output:\n%s", mediatype->exec_path, output);
125 				zbx_free(output);
126 			}
127 			else
128 				res = FAIL;
129 		}
130 		else
131 			zbx_snprintf(error, max_error_len, "%s: %s", cmd, zbx_strerror(errno));
132 
133 		zbx_free(cmd);
134 	}
135 	else
136 	{
137 		zbx_snprintf(error, max_error_len, "unsupported media type [%d]", mediatype->type);
138 		zabbix_log(LOG_LEVEL_ERR, "alert ID [" ZBX_FS_UI64 "]: %s", alert->alertid, error);
139 	}
140 
141 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __function_name, zbx_result_string(res));
142 
143 	return res;
144 }
145 
146 /******************************************************************************
147  *                                                                            *
148  * Function: main_alerter_loop                                                *
149  *                                                                            *
150  * Purpose: periodically check table alerts and send notifications if needed  *
151  *                                                                            *
152  * Author: Alexei Vladishev                                                   *
153  *                                                                            *
154  ******************************************************************************/
ZBX_THREAD_ENTRY(alerter_thread,args)155 ZBX_THREAD_ENTRY(alerter_thread, args)
156 {
157 	char		error[MAX_STRING_LEN], *error_esc;
158 	int		res, alerts_success, alerts_fail;
159 	double		sec;
160 	DB_RESULT	result;
161 	DB_ROW		row;
162 	DB_ALERT	alert;
163 	DB_MEDIATYPE	mediatype;
164 
165 	process_type = ((zbx_thread_args_t *)args)->process_type;
166 	server_num = ((zbx_thread_args_t *)args)->server_num;
167 	process_num = ((zbx_thread_args_t *)args)->process_num;
168 
169 	zabbix_log(LOG_LEVEL_INFORMATION, "%s #%d started [%s #%d]", get_program_type_string(program_type),
170 			server_num, get_process_type_string(process_type), process_num);
171 
172 	zbx_setproctitle("%s [connecting to the database]", get_process_type_string(process_type));
173 
174 	DBconnect(ZBX_DB_CONNECT_NORMAL);
175 
176 	for (;;)
177 	{
178 		zbx_handle_log();
179 
180 		zbx_setproctitle("%s [sending alerts]", get_process_type_string(process_type));
181 
182 		sec = zbx_time();
183 
184 		alerts_success = alerts_fail = 0;
185 
186 		result = DBselect(
187 				"select a.alertid,a.mediatypeid,a.sendto,a.subject,a.message,a.status,mt.mediatypeid,"
188 					"mt.type,mt.description,mt.smtp_server,mt.smtp_helo,mt.smtp_email,mt.exec_path,"
189 					"mt.gsm_modem,mt.username,mt.passwd,mt.smtp_port,mt.smtp_security,"
190 					"mt.smtp_verify_peer,mt.smtp_verify_host,mt.smtp_authentication,mt.exec_params,"
191 					"a.retries"
192 				" from alerts a,media_type mt"
193 				" where a.mediatypeid=mt.mediatypeid"
194 					" and a.status=%d"
195 					" and a.alerttype=%d"
196 				" order by a.alertid",
197 				ALERT_STATUS_NOT_SENT,
198 				ALERT_TYPE_MESSAGE);
199 
200 		while (NULL != (row = DBfetch(result)))
201 		{
202 			ZBX_STR2UINT64(alert.alertid, row[0]);
203 			ZBX_STR2UINT64(alert.mediatypeid, row[1]);
204 			alert.sendto = row[2];
205 			alert.subject = row[3];
206 			alert.message = row[4];
207 			alert.status = atoi(row[5]);
208 
209 			ZBX_STR2UINT64(mediatype.mediatypeid, row[6]);
210 			mediatype.type = atoi(row[7]);
211 			mediatype.description = row[8];
212 			mediatype.smtp_server = row[9];
213 			mediatype.smtp_helo = row[10];
214 			mediatype.smtp_email = row[11];
215 			mediatype.exec_path = row[12];
216 			mediatype.exec_params = row[21];
217 			mediatype.gsm_modem = row[13];
218 			mediatype.username = row[14];
219 			mediatype.passwd = row[15];
220 			mediatype.smtp_port = (unsigned short)atoi(row[16]);
221 			ZBX_STR2UCHAR(mediatype.smtp_security, row[17]);
222 			ZBX_STR2UCHAR(mediatype.smtp_verify_peer, row[18]);
223 			ZBX_STR2UCHAR(mediatype.smtp_verify_host, row[19]);
224 			ZBX_STR2UCHAR(mediatype.smtp_authentication, row[20]);
225 
226 			alert.retries = atoi(row[22]);
227 
228 			*error = '\0';
229 			res = execute_action(&alert, &mediatype, error, sizeof(error));
230 
231 			if (SUCCEED == res)
232 			{
233 				zabbix_log(LOG_LEVEL_DEBUG, "alert ID [" ZBX_FS_UI64 "] was sent successfully",
234 						alert.alertid);
235 				DBexecute("update alerts set status=%d,error='' where alertid=" ZBX_FS_UI64,
236 						ALERT_STATUS_SENT, alert.alertid);
237 				alerts_success++;
238 			}
239 			else
240 			{
241 				zabbix_log(LOG_LEVEL_DEBUG, "error sending alert ID [" ZBX_FS_UI64 "]", alert.alertid);
242 
243 				error_esc = DBdyn_escape_field("alerts", "error", error);
244 
245 				alert.retries++;
246 
247 				if (ALERT_MAX_RETRIES > alert.retries)
248 				{
249 					DBexecute("update alerts set retries=%d,error='%s' where alertid=" ZBX_FS_UI64,
250 							alert.retries, error_esc, alert.alertid);
251 				}
252 				else
253 				{
254 					DBexecute("update alerts set status=%d,retries=%d,error='%s' where alertid=" ZBX_FS_UI64,
255 							ALERT_STATUS_FAILED, alert.retries, error_esc, alert.alertid);
256 				}
257 
258 				zbx_free(error_esc);
259 
260 				alerts_fail++;
261 			}
262 
263 		}
264 		DBfree_result(result);
265 
266 		sec = zbx_time() - sec;
267 
268 		zbx_setproctitle("%s [sent alerts: %d success, %d fail in " ZBX_FS_DBL " sec, idle %d sec]",
269 				get_process_type_string(process_type), alerts_success, alerts_fail, sec,
270 				CONFIG_SENDER_FREQUENCY);
271 
272 		zbx_sleep_loop(CONFIG_SENDER_FREQUENCY);
273 
274 #if !defined(_WINDOWS) && defined(HAVE_RESOLV_H)
275 		zbx_update_resolver_conf();	/* handle /etc/resolv.conf update */
276 #endif
277 	}
278 }
279