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 envied 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 "log.h"
21 #include "zbxembed.h"
22 
23 #include "httprequest.h"
24 #include "zabbix.h"
25 #include "global.h"
26 #include "console.h"
27 #include "xml.h"
28 #include "embed.h"
29 
30 #define ZBX_ES_MEMORY_LIMIT	(1024 * 1024 * 64)
31 #define ZBX_ES_TIMEOUT		10
32 
33 #define ZBX_ES_STACK_LIMIT	1000
34 
35 /* maximum number of consequent runtime errors after which it's treated as fatal error */
36 #define ZBX_ES_MAX_CONSEQUENT_RT_ERROR	3
37 
38 #define ZBX_ES_SCRIPT_HEADER	"function(value){"
39 #define ZBX_ES_SCRIPT_FOOTER	"\n}"
40 
41 /******************************************************************************
42  *                                                                            *
43  * Function: es_handle_error                                                  *
44  *                                                                            *
45  * Purpose: fatal error handler                                               *
46  *                                                                            *
47  ******************************************************************************/
es_handle_error(void * udata,const char * msg)48 static void	es_handle_error(void *udata, const char *msg)
49 {
50 	zbx_es_env_t	*env = (zbx_es_env_t *)udata;
51 
52 	zabbix_log(LOG_LEVEL_WARNING, "Cannot process javascript, fatal error: %s", msg);
53 
54 	env->fatal_error = 1;
55 	env->error = zbx_strdup(env->error, msg);
56 	longjmp(env->loc, 1);
57 }
58 
59 /*
60  * Memory allocation routines to track and limit script memory usage.
61  */
62 
es_malloc(void * udata,duk_size_t size)63 static void	*es_malloc(void *udata, duk_size_t size)
64 {
65 	zbx_es_env_t	*env = (zbx_es_env_t *)udata;
66 	uint64_t	*uptr;
67 
68 	if (env->total_alloc + size + 8 > ZBX_ES_MEMORY_LIMIT)
69 	{
70 		if (NULL == env->ctx)
71 			env->error = zbx_strdup(env->error, "cannot allocate memory");
72 
73 		return NULL;
74 	}
75 
76 	env->total_alloc += (size + 8);
77 	uptr = zbx_malloc(NULL, size + 8);
78 	*uptr++ = size;
79 
80 	return uptr;
81 }
82 
es_realloc(void * udata,void * ptr,duk_size_t size)83 static void	*es_realloc(void *udata, void *ptr, duk_size_t size)
84 {
85 	zbx_es_env_t	*env = (zbx_es_env_t *)udata;
86 	uint64_t	*uptr = ptr;
87 	size_t		old_size;
88 
89 	if (NULL != uptr)
90 	{
91 		--uptr;
92 		old_size = *uptr + 8;
93 	}
94 	else
95 		old_size = 0;
96 
97 	if (env->total_alloc + size + 8 - old_size > ZBX_ES_MEMORY_LIMIT)
98 	{
99 		if (NULL == env->ctx)
100 			env->error = zbx_strdup(env->error, "cannot allocate memory");
101 
102 		return NULL;
103 	}
104 
105 	env->total_alloc += size + 8 - old_size;
106 	uptr = zbx_realloc(uptr, size + 8);
107 	*uptr++ = size;
108 
109 	return uptr;
110 }
111 
es_free(void * udata,void * ptr)112 static void	es_free(void *udata, void *ptr)
113 {
114 	zbx_es_env_t	*env = (zbx_es_env_t *)udata;
115 	uint64_t	*uptr = ptr;
116 
117 	if (NULL != ptr)
118 	{
119 		env->total_alloc -= (*(--uptr) + 8);
120 		zbx_free(uptr);
121 	}
122 }
123 
124 /******************************************************************************
125  *                                                                            *
126  * Function: zbx_es_check_timeout                                             *
127  *                                                                            *
128  * Purpose: timeout checking callback                                         *
129  *                                                                            *
130  ******************************************************************************/
zbx_es_check_timeout(void * udata)131 int	zbx_es_check_timeout(void *udata)
132 {
133 	zbx_es_env_t	*env = (zbx_es_env_t *)udata;
134 
135 	if (time(NULL) - env->start_time.sec > env->timeout)
136 		return 1;
137 
138 	return 0;
139 }
140 
141 /******************************************************************************
142  *                                                                            *
143  * Function: zbx_es_init                                                      *
144  *                                                                            *
145  * Purpose: initializes embedded scripting engine                             *
146  *                                                                            *
147  ******************************************************************************/
zbx_es_init(zbx_es_t * es)148 void	zbx_es_init(zbx_es_t *es)
149 {
150 	es->env = NULL;
151 }
152 
153 /******************************************************************************
154  *                                                                            *
155  * Function: zbx_es_destroy                                                   *
156  *                                                                            *
157  * Purpose: destroys embedded scripting engine                                *
158  *                                                                            *
159  ******************************************************************************/
zbx_es_destroy(zbx_es_t * es)160 void	zbx_es_destroy(zbx_es_t *es)
161 {
162 	char	*error = NULL;
163 
164 	if (SUCCEED != zbx_es_destroy_env(es, &error))
165 	{
166 		zabbix_log(LOG_LEVEL_WARNING, "Cannot destroy embedded scripting engine environment: %s", error);
167 	}
168 }
169 
170 /******************************************************************************
171  *                                                                            *
172  * Function: zbx_es_init_env                                                  *
173  *                                                                            *
174  * Purpose: initializes embedded scripting engine environment                 *
175  *                                                                            *
176  * Parameters: es    - [IN] the embedded scripting engine                     *
177  *             error - [OUT] the error message                                *
178  *                                                                            *
179  * Return value: SUCCEED                                                      *
180  *               FAIL                                                         *
181  *                                                                            *
182  ******************************************************************************/
zbx_es_init_env(zbx_es_t * es,char ** error)183 int	zbx_es_init_env(zbx_es_t *es, char **error)
184 {
185 	volatile int	ret = FAIL;
186 
187 	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
188 
189 	es->env = zbx_malloc(NULL, sizeof(zbx_es_env_t));
190 	memset(es->env, 0, sizeof(zbx_es_env_t));
191 
192 	if (0 != setjmp(es->env->loc))
193 	{
194 		*error = zbx_strdup(*error, es->env->error);
195 		goto out;
196 	}
197 
198 	if (NULL == (es->env->ctx = duk_create_heap(es_malloc, es_realloc, es_free, es->env, es_handle_error)))
199 	{
200 		*error = zbx_strdup(*error, "cannot create context");
201 		goto out;
202 	}
203 
204 	/* initialize Zabbix object */
205 	zbx_es_init_zabbix(es, error);
206 
207 	/* initialize console object */
208 	zbx_es_init_console(es, error);
209 
210 	/* remove Duktape object */
211 	duk_push_global_object(es->env->ctx);
212 	duk_del_prop_string(es->env->ctx, -1, "Duktape");
213 	duk_pop(es->env->ctx);
214 
215 	es_init_global_functions(es);
216 
217 	/* put environment object to be accessible from duktape C calls */
218 	duk_push_global_stash(es->env->ctx);
219 	duk_push_pointer(es->env->ctx, (void *)es->env);
220 	if (1 != duk_put_prop_string(es->env->ctx, -2, "\xff""\xff""zbx_env"))
221 	{
222 		*error = zbx_strdup(*error, duk_safe_to_string(es->env->ctx, -1));
223 		duk_pop(es->env->ctx);
224 		return FAIL;
225 	}
226 
227 	/* initialize HttpRequest and CurlHttpRequest prototypes */
228 	if (FAIL == zbx_es_init_httprequest(es, error))
229 		goto out;
230 
231 	if (FAIL == zbx_es_init_xml(es, error))
232 		goto out;
233 
234 	es->env->timeout = ZBX_ES_TIMEOUT;
235 	ret = SUCCEED;
236 out:
237 	if (SUCCEED != ret)
238 	{
239 		zbx_es_debug_disable(es);
240 		zbx_free(es->env->error);
241 		zbx_free(es->env);
242 	}
243 
244 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s %s", __func__, zbx_result_string(ret),
245 			ZBX_NULL2EMPTY_STR(*error));
246 
247 	return ret;
248 }
249 
250 /******************************************************************************
251  *                                                                            *
252  * Function: zbx_es_destroy_env                                               *
253  *                                                                            *
254  * Purpose: destroys initialized embedded scripting engine environment        *
255  *                                                                            *
256  * Parameters: es    - [IN] the embedded scripting engine                     *
257  *             error - [OUT] the error message                                *
258  *                                                                            *
259  * Return value: SUCCEED                                                      *
260  *               FAIL                                                         *
261  *                                                                            *
262  ******************************************************************************/
zbx_es_destroy_env(zbx_es_t * es,char ** error)263 int	zbx_es_destroy_env(zbx_es_t *es, char **error)
264 {
265 	int	ret;
266 
267 	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
268 
269 	if (0 != setjmp(es->env->loc))
270 	{
271 		ret = FAIL;
272 		*error = zbx_strdup(*error, es->env->error);
273 		goto out;
274 	}
275 
276 	duk_destroy_heap(es->env->ctx);
277 	zbx_es_debug_disable(es);
278 	zbx_free(es->env->error);
279 	zbx_free(es->env);
280 
281 	ret = SUCCEED;
282 out:
283 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s %s", __func__, zbx_result_string(ret),
284 		ZBX_NULL2EMPTY_STR(*error));
285 
286 	return ret;
287 }
288 
289 /******************************************************************************
290  *                                                                            *
291  * Function: zbx_es_ready                                                     *
292  *                                                                            *
293  * Purpose: checks if the scripting engine environment is initialized         *
294  *                                                                            *
295  * Parameters: es    - [IN] the embedded scripting engine                     *
296  *                                                                            *
297  * Return value: SUCCEED - the scripting engine is initialized                *
298  *               FAIL - otherwise                                             *
299  *                                                                            *
300  ******************************************************************************/
zbx_es_is_env_initialized(zbx_es_t * es)301 int	zbx_es_is_env_initialized(zbx_es_t *es)
302 {
303 	return (NULL == es->env ? FAIL : SUCCEED);
304 }
305 
306 /******************************************************************************
307  *                                                                            *
308  * Function: zbx_es_fatal_error                                               *
309  *                                                                            *
310  * Purpose: checks if fatal error has occurred                                *
311  *                                                                            *
312  * Comments: Fatal error may put the scripting engine in unknown state, it's  *
313  *           safer to destroy it instead of continuing to work with it.       *
314  *                                                                            *
315  ******************************************************************************/
zbx_es_fatal_error(zbx_es_t * es)316 int	zbx_es_fatal_error(zbx_es_t *es)
317 {
318 	if (0 != es->env->fatal_error || ZBX_ES_MAX_CONSEQUENT_RT_ERROR < es->env->rt_error_num)
319 		return SUCCEED;
320 
321 	if (ZBX_ES_STACK_LIMIT < duk_get_top(es->env->ctx))
322 	{
323 		zabbix_log(LOG_LEVEL_WARNING, "embedded scripting engine stack exceeded limits,"
324 				" resetting scripting environment");
325 		return SUCCEED;
326 	}
327 
328 	return FAIL;
329 }
330 
331 /******************************************************************************
332  *                                                                            *
333  * Function: zbx_es_compile                                                   *
334  *                                                                            *
335  * Purpose: compiles script into bytecode                                     *
336  *                                                                            *
337  * Parameters: es     - [IN] the embedded scripting engine                    *
338  *             script - [IN] the script to compile                            *
339  *             code   - [OUT] the bytecode                                    *
340  *             size   - [OUT] the size of compiled bytecode                   *
341  *             error  - [OUT] the error message                               *
342  *                                                                            *
343  * Return value: SUCCEED                                                      *
344  *               FAIL                                                         *
345  *                                                                            *
346  * Comments: The this function allocates the bytecode array, which must be    *
347  *           freed by the caller after being used.                            *
348  *                                                                            *
349  ******************************************************************************/
zbx_es_compile(zbx_es_t * es,const char * script,char ** code,int * size,char ** error)350 int	zbx_es_compile(zbx_es_t *es, const char *script, char **code, int *size, char **error)
351 {
352 	unsigned char	*buffer;
353 	duk_size_t	sz;
354 	size_t		len;
355 	char		* volatile func = NULL, *ptr;
356 	volatile int	ret = FAIL;
357 
358 	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
359 
360 	if (SUCCEED == zbx_es_fatal_error(es))
361 	{
362 		*error = zbx_strdup(*error, "cannot continue javascript processing after fatal scripting engine error");
363 		goto out;
364 	}
365 
366 	if (0 != setjmp(es->env->loc))
367 	{
368 		*error = zbx_strdup(*error, es->env->error);
369 		goto out;
370 	}
371 
372 	/* wrap the code block into a function: function(value){<code>\n} */
373 	len = strlen(script);
374 	ptr = func = zbx_malloc(NULL, len + ZBX_CONST_STRLEN(ZBX_ES_SCRIPT_HEADER) +
375 			ZBX_CONST_STRLEN(ZBX_ES_SCRIPT_FOOTER) + 1);
376 	memcpy(ptr, ZBX_ES_SCRIPT_HEADER, ZBX_CONST_STRLEN(ZBX_ES_SCRIPT_HEADER));
377 	ptr += ZBX_CONST_STRLEN(ZBX_ES_SCRIPT_HEADER);
378 	memcpy(ptr, script, len);
379 	ptr += len;
380 	memcpy(ptr, ZBX_ES_SCRIPT_FOOTER, ZBX_CONST_STRLEN(ZBX_ES_SCRIPT_FOOTER));
381 	ptr += ZBX_CONST_STRLEN(ZBX_ES_SCRIPT_FOOTER);
382 	*ptr = '\0';
383 
384 	duk_push_lstring(es->env->ctx, func, ptr - func);
385 	duk_push_lstring(es->env->ctx, "function", ZBX_CONST_STRLEN("function"));
386 
387 	if (0 != duk_pcompile(es->env->ctx, DUK_COMPILE_FUNCTION))
388 	{
389 		*error = zbx_strdup(*error, duk_safe_to_string(es->env->ctx, -1));
390 		duk_pop(es->env->ctx);
391 		goto out;
392 	}
393 
394 	duk_dump_function(es->env->ctx);
395 
396 	if (NULL != (buffer = (unsigned char *)duk_get_buffer(es->env->ctx, -1, &sz)))
397 	{
398 		*size = sz;
399 		*code = zbx_malloc(NULL, sz);
400 		memcpy(*code, buffer, sz);
401 		ret = SUCCEED;
402 	}
403 	else
404 		*error = zbx_strdup(*error, "empty function compilation result");
405 
406 	duk_pop(es->env->ctx);
407 out:
408 	zbx_free(func);
409 
410 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s %s", __func__, zbx_result_string(ret), ZBX_NULL2EMPTY_STR(*error));
411 
412 	return ret;
413 }
414 
415 /******************************************************************************
416  *                                                                            *
417  * Function: zbx_es_execute                                                   *
418  *                                                                            *
419  * Purpose: executes script                                                   *
420  *                                                                            *
421  * Parameters: es         - [IN] the embedded scripting engine                *
422  *             script     - [IN] the script to execute                        *
423  *             code       - [IN] the precompiled bytecode                     *
424  *             size       - [IN] the size of precompiled bytecode             *
425  *             param      - [IN] the parameter to pass to the script          *
426  *             script_ret - [OUT] the result value                            *
427  *             error      - [OUT] the error message                           *
428  *                                                                            *
429  * Return value: SUCCEED                                                      *
430  *               FAIL                                                         *
431  *                                                                            *
432  * Comments: Some scripting engines cannot compile into bytecode, but can     *
433  *           cache some compilation data that can be reused for the next      *
434  *           compilation. Because of that execute function accepts script and *
435  *           bytecode parameters.                                             *
436  *                                                                            *
437  ******************************************************************************/
zbx_es_execute(zbx_es_t * es,const char * script,const char * code,int size,const char * param,char ** script_ret,char ** error)438 int	zbx_es_execute(zbx_es_t *es, const char *script, const char *code, int size, const char *param, char **script_ret,
439 	char **error)
440 {
441 	void		*buffer;
442 	volatile int	ret = FAIL;
443 
444 	zabbix_log(LOG_LEVEL_DEBUG, "In %s() param:%s", __func__, param);
445 
446 	zbx_timespec(&es->env->start_time);
447 
448 	if (NULL != es->env->json)
449 	{
450 		zbx_json_clean(es->env->json);
451 		zbx_json_addarray(es->env->json, "logs");
452 	}
453 
454 	if (SUCCEED == zbx_es_fatal_error(es))
455 	{
456 		*error = zbx_strdup(*error, "cannot continue javascript processing after fatal scripting engine error");
457 		goto out;
458 	}
459 
460 	ZBX_UNUSED(script);
461 
462 	if (0 != setjmp(es->env->loc))
463 	{
464 		*error = zbx_strdup(*error, es->env->error);
465 		goto out;
466 	}
467 
468 	buffer = duk_push_fixed_buffer(es->env->ctx, size);
469 	memcpy(buffer, code, size);
470 	duk_load_function(es->env->ctx);
471 	duk_push_string(es->env->ctx, param);
472 
473 	if (DUK_EXEC_SUCCESS != duk_pcall(es->env->ctx, 1))
474 	{
475 		duk_small_int_t	rc = 0;
476 
477 		es->env->rt_error_num++;
478 
479 		if (0 != duk_is_object(es->env->ctx, -1))
480 		{
481 			/* try to get 'stack' property of the object on stack, assuming it's an Error object */
482 			if (0 != (rc = duk_get_prop_string(es->env->ctx, -1, "stack")))
483 				*error = zbx_strdup(*error, duk_get_string(es->env->ctx, -1));
484 
485 			duk_pop(es->env->ctx);
486 		}
487 
488 		/* If the object does not have stack property, return the object itself as error. */
489 		/* This allows to simply throw "error message" from scripts                       */
490 		if (0 == rc)
491 			*error = zbx_strdup(*error, duk_safe_to_string(es->env->ctx, -1));
492 
493 		duk_pop(es->env->ctx);
494 
495 		goto out;
496 	}
497 
498 	if (NULL != script_ret || SUCCEED == ZBX_CHECK_LOG_LEVEL(LOG_LEVEL_DEBUG))
499 	{
500 		if (0 == duk_check_type(es->env->ctx, -1, DUK_TYPE_UNDEFINED))
501 		{
502 			if (0 != duk_check_type(es->env->ctx, -1, DUK_TYPE_NULL))
503 			{
504 				ret = SUCCEED;
505 
506 				if (NULL != script_ret)
507 					*script_ret = NULL;
508 
509 				zabbix_log(LOG_LEVEL_DEBUG, "%s() output: null", __func__);
510 			}
511 			else
512 			{
513 				char	*output = NULL;
514 
515 				if (SUCCEED != (ret = zbx_cesu8_to_utf8(duk_safe_to_string(es->env->ctx, -1), &output)))
516 					*error = zbx_strdup(*error, "could not convert return value to utf8");
517 				else
518 					zabbix_log(LOG_LEVEL_DEBUG, "%s() output:'%s'", __func__, output);
519 
520 				if (SUCCEED == ret && NULL != script_ret)
521 					*script_ret = output;
522 				else
523 					zbx_free(output);
524 			}
525 		}
526 		else
527 		{
528 			if (NULL == script_ret)
529 			{
530 				zabbix_log(LOG_LEVEL_DEBUG, "%s(): undefined return value", __func__);
531 				ret = SUCCEED;
532 			}
533 			else
534 				*error = zbx_strdup(*error, "undefined return value");
535 		}
536 	}
537 	else
538 		ret = SUCCEED;
539 
540 	duk_pop(es->env->ctx);
541 	es->env->rt_error_num = 0;
542 out:
543 	if (NULL != es->env->json)
544 	{
545 		zbx_json_close(es->env->json);
546 		zbx_json_adduint64(es->env->json, "ms", zbx_get_duration_ms(&es->env->start_time));
547 	}
548 
549 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s %s", __func__, zbx_result_string(ret), ZBX_NULL2EMPTY_STR(*error));
550 
551 	return ret;
552 }
553 
554 /******************************************************************************
555  *                                                                            *
556  * Function: zbx_es_set_timeout                                               *
557  *                                                                            *
558  * Purpose: sets script execution timeout                                     *
559  *                                                                            *
560  * Parameters: es      - [IN] the embedded scripting engine                   *
561  *             timeout - [IN] the script execution timeout in seconds         *
562  *                                                                            *
563  ******************************************************************************/
zbx_es_set_timeout(zbx_es_t * es,int timeout)564 void	zbx_es_set_timeout(zbx_es_t *es, int timeout)
565 {
566 	es->env->timeout = timeout;
567 }
568 
zbx_es_debug_enable(zbx_es_t * es)569 void	zbx_es_debug_enable(zbx_es_t *es)
570 {
571 	if (NULL == es->env->json)
572 	{
573 		es->env->json = zbx_malloc(NULL, sizeof(struct zbx_json));
574 		zbx_json_init(es->env->json, ZBX_JSON_STAT_BUF_LEN);
575 	}
576 }
577 
zbx_es_debug_info(const zbx_es_t * es)578 const char	*zbx_es_debug_info(const zbx_es_t *es)
579 {
580 	if (NULL == es->env->json)
581 		return NULL;
582 
583 	return es->env->json->buffer;
584 }
585 
zbx_es_debug_disable(zbx_es_t * es)586 void	zbx_es_debug_disable(zbx_es_t *es)
587 {
588 	if (NULL == es->env->json)
589 		return;
590 
591 	zbx_json_free(es->env->json);
592 	zbx_free(es->env->json);
593 }
594 
595 /******************************************************************************
596  *                                                                            *
597  * Function: zbx_es_execute_command                                           *
598  *                                                                            *
599  * Purpose: executes command (script in form of a text)                       *
600  *                                                                            *
601  * Parameters: command       - [IN] the command in form of a text             *
602  *             param         - [IN] the script parameters                     *
603  *             timeout       - [IN] the timeout for the execution (seconds)   *
604  *             result        - [OUT] the result of an execution               *
605  *             error         - [OUT] the error message                        *
606  *             max_error_len - [IN] the maximum length of an error            *
607  *             debug         - [OUT] the debug data (optional)                *
608  *                                                                            *
609  * Return value: SUCCEED                                                      *
610  *               FAIL                                                         *
611  *                                                                            *
612  ******************************************************************************/
zbx_es_execute_command(const char * command,const char * param,int timeout,char ** result,char * error,size_t max_error_len,char ** debug)613 int	zbx_es_execute_command(const char *command, const char *param, int timeout, char **result,
614 		char *error, size_t max_error_len, char **debug)
615 {
616 	int		size, ret = SUCCEED;
617 	char		*code = NULL, *errmsg = NULL;
618 	zbx_es_t	es;
619 
620 	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
621 
622 	zbx_es_init(&es);
623 	if (FAIL == zbx_es_init_env(&es, &errmsg))
624 	{
625 		zbx_snprintf(error, max_error_len, "cannot initialize scripting environment: %s", errmsg);
626 		zbx_free(errmsg);
627 		ret = FAIL;
628 		goto failure;
629 	}
630 
631 	if (NULL != debug)
632 		zbx_es_debug_enable(&es);
633 
634 	if (FAIL == zbx_es_compile(&es, command, &code, &size, &errmsg))
635 	{
636 		zbx_snprintf(error, max_error_len, "cannot compile script: %s", errmsg);
637 		zbx_free(errmsg);
638 		ret = FAIL;
639 		goto out;
640 	}
641 
642 	if (0 != timeout)
643 		zbx_es_set_timeout(&es, timeout);
644 
645 	if (FAIL == zbx_es_execute(&es, NULL, code, size, param, result, &errmsg))
646 	{
647 		zbx_snprintf(error, max_error_len, "cannot execute script: %s", errmsg);
648 		zbx_free(errmsg);
649 		ret = FAIL;
650 		goto out;
651 	}
652 out:
653 	if (NULL != debug)
654 		*debug = zbx_strdup(NULL, zbx_es_debug_info(&es));
655 
656 	if (FAIL == zbx_es_destroy_env(&es, &errmsg))
657 	{
658 		zabbix_log(LOG_LEVEL_WARNING, "cannot destroy embedded scripting engine environment: %s", errmsg);
659 		zbx_free(errmsg);
660 	}
661 
662 	zbx_free(code);
663 	zbx_free(errmsg);
664 failure:
665 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret));
666 
667 	return ret;
668 }
669