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(¶m, ¶m_alloc, ¶m_offset, pstart, pend - pstart);
109
110 substitute_simple_macros(NULL, NULL, NULL, NULL, NULL, NULL, NULL, alert, ¶m,
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