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 "db.h"
23 #include "log.h"
24 #include "daemon.h"
25 #include "zbxself.h"
26 #include "zbxalgo.h"
27 
28 #include "../alerter/alerter.h"
29 
30 #include "watchdog.h"
31 
32 #define STR_REPLACE(str1, str2)	if (NULL == str1 || 0 != strcmp(str1, str2)) str1 = zbx_strdup(str1, str2)
33 
34 #define ALERT_FREQUENCY		(15 * SEC_PER_MIN)
35 #define DB_PING_FREQUENCY	SEC_PER_MIN
36 
37 typedef struct
38 {
39 	DB_ALERT	alert;
40 	DB_MEDIATYPE	mediatype;
41 }
42 ZBX_RECIPIENT;
43 
44 static zbx_vector_ptr_t	recipients;
45 static int		lastsent = 0;
46 
47 extern int		CONFIG_CONFSYNCER_FREQUENCY;
48 extern unsigned char	process_type, program_type;
49 extern int		server_num, process_num;
50 
51 /******************************************************************************
52  *                                                                            *
53  * Function: send_alerts                                                      *
54  *                                                                            *
55  * Purpose: send warning message to all interested                            *
56  *                                                                            *
57  * Author: Alexei Vladishev                                                   *
58  *                                                                            *
59  * Comments: messages are sent only every ALERT_FREQUENCY seconds             *
60  *                                                                            *
61  ******************************************************************************/
send_alerts(void)62 static void	send_alerts(void)
63 {
64 	int	i, now;
65 	char	error[MAX_STRING_LEN];
66 
67 	now = time(NULL);
68 
69 	if (now > lastsent + ALERT_FREQUENCY)
70 	{
71 		for (i = 0; i < recipients.values_num; i++)
72 		{
73 			execute_action(&((ZBX_RECIPIENT *)recipients.values[i])->alert,
74 					&((ZBX_RECIPIENT *)recipients.values[i])->mediatype, error, sizeof(error));
75 		}
76 
77 		lastsent = now;
78 	}
79 }
80 
81 /******************************************************************************
82  *                                                                            *
83  * Function: sync_config                                                      *
84  *                                                                            *
85  * Purpose: sync list of medias to send notifications in case if DB is down   *
86  *                                                                            *
87  * Author: Alexei Vladishev, Rudolfs Kreicbergs                               *
88  *                                                                            *
89  ******************************************************************************/
sync_config(void)90 static void	sync_config(void)
91 {
92 	const char	*__function_name = "sync_config";
93 
94 	DB_RESULT	result;
95 	DB_ROW		row;
96 	ZBX_RECIPIENT	*recipient;
97 	int		count = 0, old_count;
98 	static int	no_recipients = 0;
99 
100 	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __function_name);
101 
102 	result = DBselect_once(
103 			"select mt.mediatypeid,mt.type,mt.description,mt.smtp_server,mt.smtp_helo,mt.smtp_email,"
104 				"mt.exec_path,mt.gsm_modem,mt.username,mt.passwd,mt.smtp_port,mt.smtp_security,"
105 				"mt.smtp_verify_peer,mt.smtp_verify_host,mt.smtp_authentication,mt.exec_params,m.sendto"
106 			" from media m,users_groups u,config c,media_type mt"
107 			" where m.userid=u.userid"
108 				" and u.usrgrpid=c.alert_usrgrpid"
109 				" and m.mediatypeid=mt.mediatypeid"
110 				" and m.active=%d"
111 				" and mt.status=%d",
112 			MEDIA_STATUS_ACTIVE,
113 			MEDIA_TYPE_STATUS_ACTIVE);
114 
115 	if (NULL == result || (DB_RESULT)ZBX_DB_DOWN == result)
116 	{
117 		zabbix_log(LOG_LEVEL_WARNING, "watchdog: database is down");
118 		send_alerts();
119 		goto exit;
120 	}
121 
122 	old_count = recipients.values_num;
123 
124 	while (NULL != (row = DBfetch(result)))
125 	{
126 		/* add the recipients to the list */
127 
128 		if (count >= recipients.values_num)
129 		{
130 			recipient = zbx_calloc(NULL, 1, sizeof(ZBX_RECIPIENT));
131 			zbx_vector_ptr_append(&recipients, recipient);
132 		}
133 		else
134 			recipient = recipients.values[count];
135 
136 		ZBX_STR2UINT64(recipient->mediatype.mediatypeid, row[0]);
137 		recipient->mediatype.type = atoi(row[1]);
138 
139 		/* the recipients are likely to be the same, change only what's different */
140 
141 		STR_REPLACE(recipient->mediatype.description, row[2]);
142 		STR_REPLACE(recipient->mediatype.smtp_server, row[3]);
143 		STR_REPLACE(recipient->mediatype.smtp_helo, row[4]);
144 		STR_REPLACE(recipient->mediatype.smtp_email, row[5]);
145 		STR_REPLACE(recipient->mediatype.exec_path, row[6]);
146 		STR_REPLACE(recipient->mediatype.exec_params, row[15]);
147 		STR_REPLACE(recipient->mediatype.gsm_modem, row[7]);
148 		STR_REPLACE(recipient->mediatype.username, row[8]);
149 		STR_REPLACE(recipient->mediatype.passwd, row[9]);
150 		recipient->mediatype.smtp_port = (unsigned short)atoi(row[10]);
151 		ZBX_STR2UCHAR(recipient->mediatype.smtp_security, row[11]);
152 		ZBX_STR2UCHAR(recipient->mediatype.smtp_verify_peer, row[12]);
153 		ZBX_STR2UCHAR(recipient->mediatype.smtp_verify_host, row[13]);
154 		ZBX_STR2UCHAR(recipient->mediatype.smtp_authentication, row[14]);
155 
156 		STR_REPLACE(recipient->alert.sendto, row[16]);
157 
158 		if (NULL == recipient->alert.subject)
159 			recipient->alert.message = recipient->alert.subject = zbx_strdup(NULL, "Zabbix database is down.");
160 
161 		count++;
162 	}
163 	DBfree_result(result);
164 
165 	if (0 < old_count && 0 == count)
166 	{
167 		zabbix_log(LOG_LEVEL_WARNING, "watchdog: no recipients found for database down messages");
168 		no_recipients = 1;
169 	}
170 	else if (1 == no_recipients && 0 < count)
171 	{
172 		zabbix_log(LOG_LEVEL_WARNING, "watchdog: %d recipient(s) found for database down messages", count);
173 		no_recipients = 0;
174 	}
175 
176 	recipients.values_num = count;
177 
178 	while (count < old_count)
179 	{
180 		/* some recipients have been deleted, free the older entries */
181 
182 		recipient = recipients.values[count++];
183 
184 		zbx_free(recipient->mediatype.description);
185 		zbx_free(recipient->mediatype.smtp_server);
186 		zbx_free(recipient->mediatype.smtp_helo);
187 		zbx_free(recipient->mediatype.smtp_email);
188 		zbx_free(recipient->mediatype.exec_path);
189 		zbx_free(recipient->mediatype.gsm_modem);
190 		zbx_free(recipient->mediatype.username);
191 		zbx_free(recipient->mediatype.passwd);
192 		zbx_free(recipient->alert.sendto);
193 		zbx_free(recipient->alert.subject);
194 
195 		zbx_free(recipient);
196 	}
197 exit:
198 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s() values_num:%d", __function_name, recipients.values_num);
199 }
200 
201 /******************************************************************************
202  *                                                                            *
203  * Function: main_watchdog_loop                                               *
204  *                                                                            *
205  * Purpose: check database availability every DB_PING_FREQUENCY seconds and   *
206  *          alert admins if it is down                                        *
207  *                                                                            *
208  * Author: Alexei Vladishev, Rudolfs Kreicbergs                               *
209  *                                                                            *
210  ******************************************************************************/
ZBX_THREAD_ENTRY(watchdog_thread,args)211 ZBX_THREAD_ENTRY(watchdog_thread, args)
212 {
213 	int	now, nextsync = 0, action;
214 	double	sec;
215 
216 	process_type = ((zbx_thread_args_t *)args)->process_type;
217 	server_num = ((zbx_thread_args_t *)args)->server_num;
218 	process_num = ((zbx_thread_args_t *)args)->process_num;
219 
220 	zabbix_log(LOG_LEVEL_INFORMATION, "%s #%d started [%s #%d]", get_program_type_string(program_type),
221 			server_num, get_process_type_string(process_type), process_num);
222 
223 	zbx_vector_ptr_create(&recipients);
224 
225 	for (;;)
226 	{
227 		zbx_handle_log();
228 
229 		zbx_setproctitle("%s [pinging database]", get_process_type_string(process_type));
230 
231 		sec = zbx_time();
232 		action = 0;
233 
234 		if (ZBX_DB_OK != DBconnect(ZBX_DB_CONNECT_ONCE))
235 		{
236 			zabbix_log(LOG_LEVEL_WARNING, "watchdog: database is down");
237 			send_alerts();
238 			action = 1;
239 		}
240 		else if (nextsync <= (now = (int)time(NULL)))
241 		{
242 			zbx_setproctitle("%s [syncing configuration]", get_process_type_string(process_type));
243 
244 			sync_config();
245 
246 			nextsync = now + CONFIG_CONFSYNCER_FREQUENCY;
247 
248 			action = 2;
249 		}
250 
251 		DBclose();
252 
253 		sec = zbx_time() - sec;
254 
255 		if (1 == action)
256 		{
257 			zbx_setproctitle("%s [database is down, checking took " ZBX_FS_DBL " sec, idle %d sec]",
258 					get_process_type_string(process_type), sec, (int)DB_PING_FREQUENCY);
259 		}
260 		else if (2 == action)
261 		{
262 			zbx_setproctitle("%s [synced alerts config in " ZBX_FS_DBL " sec, idle %d sec]",
263 					get_process_type_string(process_type), sec, (int)DB_PING_FREQUENCY);
264 		}
265 
266 		zbx_sleep_loop(DB_PING_FREQUENCY);
267 
268 #if !defined(_WINDOWS) && defined(HAVE_RESOLV_H)
269 		zbx_update_resolver_conf();	/* handle /etc/resolv.conf update */
270 #endif
271 	}
272 }
273