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 "dbcache.h"
23 #include "log.h"
24 #include "zbxserver.h"
25 #include "zbxicmpping.h"
26 #include "daemon.h"
27 #include "zbxself.h"
28 #include "preproc.h"
29 
30 #include "pinger.h"
31 
32 /* defines for `fping' and `fping6' to successfully process pings */
33 #define MIN_COUNT	1
34 #define MAX_COUNT	10000
35 #define MIN_INTERVAL	20
36 #define MIN_SIZE	24
37 #define MAX_SIZE	65507
38 #define MIN_TIMEOUT	50
39 
40 extern unsigned char	process_type, program_type;
41 extern int		server_num, process_num;
42 
43 /******************************************************************************
44  *                                                                            *
45  * Function: process_value                                                    *
46  *                                                                            *
47  * Purpose: process new item value                                            *
48  *                                                                            *
49  * Parameters:                                                                *
50  *                                                                            *
51  * Return value:                                                              *
52  *                                                                            *
53  * Author: Alexei Vladishev, Alexander Vladishev                              *
54  *                                                                            *
55  * Comments:                                                                  *
56  *                                                                            *
57  ******************************************************************************/
process_value(zbx_uint64_t itemid,zbx_uint64_t * value_ui64,double * value_dbl,zbx_timespec_t * ts,int ping_result,char * error)58 static void	process_value(zbx_uint64_t itemid, zbx_uint64_t *value_ui64, double *value_dbl,	zbx_timespec_t *ts,
59 		int ping_result, char *error)
60 {
61 	const char	*__function_name = "process_value";
62 	DC_ITEM		item;
63 	int		errcode;
64 	AGENT_RESULT	value;
65 
66 	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __function_name);
67 
68 	DCconfig_get_items_by_itemids(&item, &itemid, &errcode, 1);
69 
70 	if (SUCCEED != errcode)
71 		goto clean;
72 
73 	if (ITEM_STATUS_ACTIVE != item.status)
74 		goto clean;
75 
76 	if (HOST_STATUS_MONITORED != item.host.status)
77 		goto clean;
78 
79 	if (NOTSUPPORTED == ping_result)
80 	{
81 		item.state = ITEM_STATE_NOTSUPPORTED;
82 		zbx_preprocess_item_value(item.itemid, item.value_type, item.flags, NULL, ts, item.state, error);
83 	}
84 	else
85 	{
86 		init_result(&value);
87 
88 		if (NULL != value_ui64)
89 			SET_UI64_RESULT(&value, *value_ui64);
90 		else
91 			SET_DBL_RESULT(&value, *value_dbl);
92 
93 		item.state = ITEM_STATE_NORMAL;
94 		zbx_preprocess_item_value(item.itemid, item.value_type, item.flags, &value, ts, item.state, NULL);
95 
96 		free_result(&value);
97 	}
98 clean:
99 	DCrequeue_items(&item.itemid, &item.state, &ts->sec, &errcode, 1);
100 
101 	DCconfig_clean_items(&item, &errcode, 1);
102 
103 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __function_name);
104 }
105 
106 /******************************************************************************
107  *                                                                            *
108  * Function: process_values                                                   *
109  *                                                                            *
110  * Purpose: process new item values                                           *
111  *                                                                            *
112  * Parameters:                                                                *
113  *                                                                            *
114  * Return value:                                                              *
115  *                                                                            *
116  * Author: Alexei Vladishev, Alexander Vladishev                              *
117  *                                                                            *
118  * Comments:                                                                  *
119  *                                                                            *
120  ******************************************************************************/
process_values(icmpitem_t * items,int first_index,int last_index,ZBX_FPING_HOST * hosts,int hosts_count,zbx_timespec_t * ts,int ping_result,char * error)121 static void	process_values(icmpitem_t *items, int first_index, int last_index, ZBX_FPING_HOST *hosts,
122 		int hosts_count, zbx_timespec_t *ts, int ping_result, char *error)
123 {
124 	const char	*__function_name = "process_values";
125 	int		i, h;
126 	zbx_uint64_t	value_uint64;
127 	double		value_dbl;
128 
129 	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __function_name);
130 
131 	for (h = 0; h < hosts_count; h++)
132 	{
133 		const ZBX_FPING_HOST	*host = &hosts[h];
134 
135 		if (NOTSUPPORTED == ping_result)
136 		{
137 			zabbix_log(LOG_LEVEL_DEBUG, "host [%s] %s", host->addr, error);
138 		}
139 		else
140 		{
141 			zabbix_log(LOG_LEVEL_DEBUG, "host [%s] cnt=%d rcv=%d"
142 					" min=" ZBX_FS_DBL " max=" ZBX_FS_DBL " sum=" ZBX_FS_DBL,
143 					host->addr, host->cnt, host->rcv, host->min, host->max, host->sum);
144 		}
145 
146 		for (i = first_index; i < last_index; i++)
147 		{
148 			const icmpitem_t	*item = &items[i];
149 
150 			if (0 != strcmp(item->addr, host->addr))
151 				continue;
152 
153 			if (NOTSUPPORTED == ping_result)
154 			{
155 				process_value(item->itemid, NULL, NULL, ts, NOTSUPPORTED, error);
156 				continue;
157 			}
158 
159 			if (0 == host->cnt)
160 			{
161 				process_value(item->itemid, NULL, NULL, ts, NOTSUPPORTED,
162 						(char *)"Cannot send ICMP ping packets to this host.");
163 				continue;
164 			}
165 
166 			switch (item->icmpping)
167 			{
168 				case ICMPPING:
169 					value_uint64 = (0 != host->rcv ? 1 : 0);
170 					process_value(item->itemid, &value_uint64, NULL, ts, SUCCEED, NULL);
171 					break;
172 				case ICMPPINGSEC:
173 					switch (item->type)
174 					{
175 						case ICMPPINGSEC_MIN:
176 							value_dbl = host->min;
177 							break;
178 						case ICMPPINGSEC_MAX:
179 							value_dbl = host->max;
180 							break;
181 						case ICMPPINGSEC_AVG:
182 							value_dbl = (0 != host->rcv ? host->sum / host->rcv : 0);
183 							break;
184 					}
185 
186 					if (0 < value_dbl && ZBX_FLOAT_PRECISION > value_dbl)
187 						value_dbl = ZBX_FLOAT_PRECISION;
188 
189 					process_value(item->itemid, NULL, &value_dbl, ts, SUCCEED, NULL);
190 					break;
191 				case ICMPPINGLOSS:
192 					value_dbl = (100 * (host->cnt - host->rcv)) / (double)host->cnt;
193 					process_value(item->itemid, NULL, &value_dbl, ts, SUCCEED, NULL);
194 					break;
195 			}
196 		}
197 	}
198 
199 	zbx_preprocessor_flush();
200 
201 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __function_name);
202 }
203 
parse_key_params(const char * key,const char * host_addr,icmpping_t * icmpping,char ** addr,int * count,int * interval,int * size,int * timeout,icmppingsec_type_t * type,char * error,int max_error_len)204 static int	parse_key_params(const char *key, const char *host_addr, icmpping_t *icmpping, char **addr, int *count,
205 		int *interval, int *size, int *timeout, icmppingsec_type_t *type, char *error, int max_error_len)
206 {
207 	const char	*tmp;
208 	int		ret = NOTSUPPORTED;
209 	AGENT_REQUEST	request;
210 
211 	init_request(&request);
212 
213 	if (SUCCEED != parse_item_key(key, &request))
214 	{
215 		zbx_snprintf(error, max_error_len, "Invalid item key format.");
216 		goto out;
217 	}
218 
219 	if (0 == strcmp(get_rkey(&request), SERVER_ICMPPING_KEY))
220 	{
221 		*icmpping = ICMPPING;
222 	}
223 	else if (0 == strcmp(get_rkey(&request), SERVER_ICMPPINGLOSS_KEY))
224 	{
225 		*icmpping = ICMPPINGLOSS;
226 	}
227 	else if (0 == strcmp(get_rkey(&request), SERVER_ICMPPINGSEC_KEY))
228 	{
229 		*icmpping = ICMPPINGSEC;
230 	}
231 	else
232 	{
233 		zbx_snprintf(error, max_error_len, "Unsupported pinger key.");
234 		goto out;
235 	}
236 
237 	if (6 < get_rparams_num(&request) || (ICMPPINGSEC != *icmpping && 5 < get_rparams_num(&request)))
238 	{
239 		zbx_snprintf(error, max_error_len, "Too many arguments.");
240 		goto out;
241 	}
242 
243 	if (NULL == (tmp = get_rparam(&request, 1)) || '\0' == *tmp)
244 	{
245 		*count = 3;
246 	}
247 	else if (FAIL == is_uint31(tmp, count) || MIN_COUNT > *count || *count > MAX_COUNT)
248 	{
249 		zbx_snprintf(error, max_error_len, "Number of packets \"%s\" is not between %d and %d.",
250 				tmp, MIN_COUNT, MAX_COUNT);
251 		goto out;
252 	}
253 
254 	if (NULL == (tmp = get_rparam(&request, 2)) || '\0' == *tmp)
255 	{
256 		*interval = 0;
257 	}
258 	else if (FAIL == is_uint31(tmp, interval) || MIN_INTERVAL > *interval)
259 	{
260 		zbx_snprintf(error, max_error_len, "Interval \"%s\" should be at least %d.", tmp, MIN_INTERVAL);
261 		goto out;
262 	}
263 
264 	if (NULL == (tmp = get_rparam(&request, 3)) || '\0' == *tmp)
265 	{
266 		*size = 0;
267 	}
268 	else if (FAIL == is_uint31(tmp, size) || MIN_SIZE > *size || *size > MAX_SIZE)
269 	{
270 		zbx_snprintf(error, max_error_len, "Packet size \"%s\" is not between %d and %d.",
271 				tmp, MIN_SIZE, MAX_SIZE);
272 		goto out;
273 	}
274 
275 	if (NULL == (tmp = get_rparam(&request, 4)) || '\0' == *tmp)
276 	{
277 		*timeout = 0;
278 	}
279 	else if (FAIL == is_uint31(tmp, timeout) || MIN_TIMEOUT > *timeout)
280 	{
281 		zbx_snprintf(error, max_error_len, "Timeout \"%s\" should be at least %d.", tmp, MIN_TIMEOUT);
282 		goto out;
283 	}
284 
285 	if (NULL == (tmp = get_rparam(&request, 5)) || '\0' == *tmp)
286 	{
287 		*type = ICMPPINGSEC_AVG;
288 	}
289 	else
290 	{
291 		if (0 == strcmp(tmp, "min"))
292 		{
293 			*type = ICMPPINGSEC_MIN;
294 		}
295 		else if (0 == strcmp(tmp, "avg"))
296 		{
297 			*type = ICMPPINGSEC_AVG;
298 		}
299 		else if (0 == strcmp(tmp, "max"))
300 		{
301 			*type = ICMPPINGSEC_MAX;
302 		}
303 		else
304 		{
305 			zbx_snprintf(error, max_error_len, "Mode \"%s\" is not supported.", tmp);
306 			goto out;
307 		}
308 	}
309 
310 	if (NULL == (tmp = get_rparam(&request, 0)) || '\0' == *tmp)
311 		*addr = strdup(host_addr);
312 	else
313 		*addr = strdup(tmp);
314 
315 	ret = SUCCEED;
316 out:
317 	free_request(&request);
318 
319 	return ret;
320 }
321 
get_icmpping_nearestindex(icmpitem_t * items,int items_count,int count,int interval,int size,int timeout)322 static int	get_icmpping_nearestindex(icmpitem_t *items, int items_count, int count, int interval, int size, int timeout)
323 {
324 	int		first_index, last_index, index;
325 	icmpitem_t	*item;
326 
327 	if (items_count == 0)
328 		return 0;
329 
330 	first_index = 0;
331 	last_index = items_count - 1;
332 	while (1)
333 	{
334 		index = first_index + (last_index - first_index) / 2;
335 		item = &items[index];
336 
337 		if (item->count == count && item->interval == interval && item->size == size && item->timeout == timeout)
338 			return index;
339 		else if (last_index == first_index)
340 		{
341 			if (item->count < count ||
342 					(item->count == count && item->interval < interval) ||
343 					(item->count == count && item->interval == interval && item->size < size) ||
344 					(item->count == count && item->interval == interval && item->size == size && item->timeout < timeout))
345 				index++;
346 			return index;
347 		}
348 		else if (item->count < count ||
349 				(item->count == count && item->interval < interval) ||
350 				(item->count == count && item->interval == interval && item->size < size) ||
351 				(item->count == count && item->interval == interval && item->size == size && item->timeout < timeout))
352 			first_index = index + 1;
353 		else
354 			last_index = index;
355 	}
356 }
357 
add_icmpping_item(icmpitem_t ** items,int * items_alloc,int * items_count,int count,int interval,int size,int timeout,zbx_uint64_t itemid,char * addr,icmpping_t icmpping,icmppingsec_type_t type)358 static void	add_icmpping_item(icmpitem_t **items, int *items_alloc, int *items_count, int count, int interval,
359 		int size, int timeout, zbx_uint64_t itemid, char *addr, icmpping_t icmpping, icmppingsec_type_t type)
360 {
361 	const char	*__function_name = "add_icmpping_item";
362 	int		index;
363 	icmpitem_t	*item;
364 	size_t		sz;
365 
366 	zabbix_log(LOG_LEVEL_DEBUG, "In %s() addr:'%s' count:%d interval:%d size:%d timeout:%d",
367 			__function_name, addr, count, interval, size, timeout);
368 
369 	index = get_icmpping_nearestindex(*items, *items_count, count, interval, size, timeout);
370 
371 	if (*items_alloc == *items_count)
372 	{
373 		*items_alloc += 4;
374 		sz = *items_alloc * sizeof(icmpitem_t);
375 		*items = (icmpitem_t *)zbx_realloc(*items, sz);
376 	}
377 
378 	memmove(&(*items)[index + 1], &(*items)[index], sizeof(icmpitem_t) * (*items_count - index));
379 
380 	item = &(*items)[index];
381 	item->count	= count;
382 	item->interval	= interval;
383 	item->size	= size;
384 	item->timeout	= timeout;
385 	item->itemid	= itemid;
386 	item->addr	= addr;
387 	item->icmpping	= icmpping;
388 	item->type	= type;
389 
390 	(*items_count)++;
391 
392 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __function_name);
393 }
394 
395 /******************************************************************************
396  *                                                                            *
397  * Function: get_pinger_hosts                                                 *
398  *                                                                            *
399  * Purpose: creates buffer which contains list of hosts to ping               *
400  *                                                                            *
401  * Parameters:                                                                *
402  *                                                                            *
403  * Return value: SUCCEED - the file was created successfully                  *
404  *               FAIL - otherwise                                             *
405  *                                                                            *
406  * Author: Alexei Vladishev, Alexander Vladishev                              *
407  *                                                                            *
408  * Comments:                                                                  *
409  *                                                                            *
410  ******************************************************************************/
get_pinger_hosts(icmpitem_t ** icmp_items,int * icmp_items_alloc,int * icmp_items_count)411 static void	get_pinger_hosts(icmpitem_t **icmp_items, int *icmp_items_alloc, int *icmp_items_count)
412 {
413 	const char		*__function_name = "get_pinger_hosts";
414 	DC_ITEM			item, *items;
415 	int			i, num, count, interval, size, timeout, rc, errcode = SUCCEED;
416 	char			error[MAX_STRING_LEN], *addr = NULL;
417 	icmpping_t		icmpping;
418 	icmppingsec_type_t	type;
419 
420 	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __function_name);
421 
422 	items = &item;
423 	num = DCconfig_get_poller_items(ZBX_POLLER_TYPE_PINGER, &items);
424 
425 	for (i = 0; i < num; i++)
426 	{
427 		ZBX_STRDUP(items[i].key, items[i].key_orig);
428 		rc = substitute_key_macros(&items[i].key, NULL, &items[i], NULL, MACRO_TYPE_ITEM_KEY,
429 				error, sizeof(error));
430 
431 		if (SUCCEED == rc)
432 		{
433 			rc = parse_key_params(items[i].key, items[i].interface.addr, &icmpping, &addr, &count,
434 					&interval, &size, &timeout, &type, error, sizeof(error));
435 		}
436 
437 		if (SUCCEED == rc)
438 		{
439 			add_icmpping_item(icmp_items, icmp_items_alloc, icmp_items_count, count, interval, size,
440 				timeout, items[i].itemid, addr, icmpping, type);
441 		}
442 		else
443 		{
444 			zbx_timespec_t	ts;
445 
446 			zbx_timespec(&ts);
447 
448 			items[i].state = ITEM_STATE_NOTSUPPORTED;
449 			zbx_preprocess_item_value(items[i].itemid, items[i].value_type, items[i].flags, NULL, &ts,
450 					items[i].state, error);
451 
452 			DCrequeue_items(&items[i].itemid, &items[i].state, &ts.sec, &errcode, 1);
453 		}
454 
455 		zbx_free(items[i].key);
456 	}
457 
458 	DCconfig_clean_items(items, NULL, num);
459 
460 	if (items != &item)
461 		zbx_free(items);
462 
463 	zbx_preprocessor_flush();
464 
465 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%d", __function_name, *icmp_items_count);
466 }
467 
free_hosts(icmpitem_t ** items,int * items_count)468 static void	free_hosts(icmpitem_t **items, int *items_count)
469 {
470 	int	i;
471 
472 	for (i = 0; i < *items_count; i++)
473 		zbx_free((*items)[i].addr);
474 
475 	*items_count = 0;
476 }
477 
add_pinger_host(ZBX_FPING_HOST ** hosts,int * hosts_alloc,int * hosts_count,char * addr)478 static void	add_pinger_host(ZBX_FPING_HOST **hosts, int *hosts_alloc, int *hosts_count, char *addr)
479 {
480 	const char	*__function_name = "add_pinger_host";
481 
482 	int		i;
483 	size_t		sz;
484 	ZBX_FPING_HOST	*h;
485 
486 	zabbix_log(LOG_LEVEL_DEBUG, "In %s() addr:'%s'", __function_name, addr);
487 
488 	for (i = 0; i < *hosts_count; i++)
489 	{
490 		if (0 == strcmp(addr, (*hosts)[i].addr))
491 			return;
492 	}
493 
494 	(*hosts_count)++;
495 
496 	if (*hosts_alloc < *hosts_count)
497 	{
498 		*hosts_alloc += 4;
499 		sz = *hosts_alloc * sizeof(ZBX_FPING_HOST);
500 		*hosts = (ZBX_FPING_HOST *)zbx_realloc(*hosts, sz);
501 	}
502 
503 	h = &(*hosts)[*hosts_count - 1];
504 	memset(h, 0, sizeof(ZBX_FPING_HOST));
505 	h->addr = addr;
506 
507 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __function_name);
508 }
509 
510 /******************************************************************************
511  *                                                                            *
512  * Function: process_pinger_hosts                                             *
513  *                                                                            *
514  * Purpose:                                                                   *
515  *                                                                            *
516  * Parameters:                                                                *
517  *                                                                            *
518  * Return value:                                                              *
519  *                                                                            *
520  * Author: Alexander Vladishev                                                *
521  *                                                                            *
522  * Comments:                                                                  *
523  *                                                                            *
524  ******************************************************************************/
process_pinger_hosts(icmpitem_t * items,int items_count)525 static void	process_pinger_hosts(icmpitem_t *items, int items_count)
526 {
527 	const char		*__function_name = "process_pinger_hosts";
528 	int			i, first_index = 0, ping_result;
529 	char			error[ITEM_ERROR_LEN_MAX];
530 	static ZBX_FPING_HOST	*hosts = NULL;
531 	static int		hosts_alloc = 4;
532 	int			hosts_count = 0;
533 	zbx_timespec_t		ts;
534 
535 	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __function_name);
536 
537 	if (NULL == hosts)
538 		hosts = (ZBX_FPING_HOST *)zbx_malloc(hosts, sizeof(ZBX_FPING_HOST) * hosts_alloc);
539 
540 	for (i = 0; i < items_count && ZBX_IS_RUNNING(); i++)
541 	{
542 		add_pinger_host(&hosts, &hosts_alloc, &hosts_count, items[i].addr);
543 
544 		if (i == items_count - 1 || items[i].count != items[i + 1].count || items[i].interval != items[i + 1].interval ||
545 				items[i].size != items[i + 1].size || items[i].timeout != items[i + 1].timeout)
546 		{
547 			zbx_setproctitle("%s #%d [pinging hosts]", get_process_type_string(process_type), process_num);
548 
549 			zbx_timespec(&ts);
550 
551 			ping_result = zbx_ping(hosts, hosts_count,
552 						items[i].count, items[i].interval, items[i].size, items[i].timeout,
553 						error, sizeof(error));
554 
555 			process_values(items, first_index, i + 1, hosts, hosts_count, &ts, ping_result, error);
556 
557 			hosts_count = 0;
558 			first_index = i + 1;
559 		}
560 	}
561 
562 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __function_name);
563 }
564 
565 /******************************************************************************
566  *                                                                            *
567  * Function: main_pinger_loop                                                 *
568  *                                                                            *
569  * Purpose: periodically perform ICMP pings                                   *
570  *                                                                            *
571  * Parameters:                                                                *
572  *                                                                            *
573  * Return value:                                                              *
574  *                                                                            *
575  * Author: Alexei Vladishev                                                   *
576  *                                                                            *
577  * Comments: never returns                                                    *
578  *                                                                            *
579  ******************************************************************************/
ZBX_THREAD_ENTRY(pinger_thread,args)580 ZBX_THREAD_ENTRY(pinger_thread, args)
581 {
582 	int			nextcheck, sleeptime, items_count = 0, itc;
583 	double			sec;
584 	static icmpitem_t	*items = NULL;
585 	static int		items_alloc = 4;
586 
587 	process_type = ((zbx_thread_args_t *)args)->process_type;
588 	server_num = ((zbx_thread_args_t *)args)->server_num;
589 	process_num = ((zbx_thread_args_t *)args)->process_num;
590 
591 	zabbix_log(LOG_LEVEL_INFORMATION, "%s #%d started [%s #%d]", get_program_type_string(program_type),
592 			server_num, get_process_type_string(process_type), process_num);
593 
594 	update_selfmon_counter(ZBX_PROCESS_STATE_BUSY);
595 
596 	if (NULL == items)
597 		items = (icmpitem_t *)zbx_malloc(items, sizeof(icmpitem_t) * items_alloc);
598 
599 	while (ZBX_IS_RUNNING())
600 	{
601 		sec = zbx_time();
602 		zbx_update_env(sec);
603 
604 		zbx_setproctitle("%s #%d [getting values]", get_process_type_string(process_type), process_num);
605 
606 		get_pinger_hosts(&items, &items_alloc, &items_count);
607 		process_pinger_hosts(items, items_count);
608 		sec = zbx_time() - sec;
609 		itc = items_count;
610 
611 		free_hosts(&items, &items_count);
612 
613 		nextcheck = DCconfig_get_poller_nextcheck(ZBX_POLLER_TYPE_PINGER);
614 		sleeptime = calculate_sleeptime(nextcheck, POLLER_DELAY);
615 
616 		zbx_setproctitle("%s #%d [got %d values in " ZBX_FS_DBL " sec, idle %d sec]",
617 				get_process_type_string(process_type), process_num, itc, sec, sleeptime);
618 
619 		zbx_sleep_loop(sleeptime);
620 	}
621 
622 	zbx_setproctitle("%s #%d [terminated]", get_process_type_string(process_type), process_num);
623 
624 	while (1)
625 		zbx_sleep(SEC_PER_MIN);
626 }
627