1 /**
2  * PHP bindings to the rrdtool
3  *
4  * This source file is subject to the BSD license that is bundled
5  * with this package in the file LICENSE.
6  * ---------------------------------------------------------------
7  *  Author: Miroslav Kubelik <koubel@php.net>
8  * ---------------------------------------------------------------
9  */
10 #ifdef HAVE_CONFIG_H
11 #include "config.h"
12 #endif
13 
14 #include "php.h"
15 #include "ext/standard/php_smart_string.h"
16 #include "ext/standard/php_array.h"
17 #include "ext/standard/info.h"
18 
19 #include <rrd.h>
20 #ifdef HAVE_RRDC_DISCONNECT
21 #include <rrd_client.h>
22 #endif
23 
24 #include "php_rrd.h"
25 #include "rrd_graph.h"
26 #include "rrd_create.h"
27 #include "rrd_update.h"
28 #include "rrd_info.h"
29 
30 /* {{{ proto string rrd_error()
31 Get the error message set by the last rrd tool function call, this function
32 clear error buffer also.
33 */
PHP_FUNCTION(rrd_error)34 PHP_FUNCTION(rrd_error)
35 {
36 	if (zend_parse_parameters_none() == FAILURE) {
37 		return;
38 	}
39 
40 	if (!rrd_test_error()) RETURN_FALSE;
41 
42 	RETVAL_STRING(rrd_get_error());
43 	rrd_clear_error();
44 }
45 /* }}} */
46 
47 /* {{{ proto array rrd_fetch(string file, array options)
48 Fetch data from RRD in requested resolution.
49 */
PHP_FUNCTION(rrd_fetch)50 PHP_FUNCTION(rrd_fetch)
51 {
52 	char *filename;
53 	size_t filename_length;
54 	zval *zv_arr_options;
55 	rrd_args *argv;
56 	/* returned values if rrd_fetch doesn't fail */
57 	time_t start, end;
58 	u_long step,
59 	ds_cnt; /* count of data sources */
60 	char **ds_namv; /* list of data source names */
61 	rrd_value_t *ds_data; /* all data from all sources */
62 
63 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "pa", &filename,
64 		&filename_length, &zv_arr_options) == FAILURE) {
65 		return;
66 	}
67 
68 	if (php_check_open_basedir(filename)) RETURN_FALSE;
69 
70 	argv = rrd_args_init_by_phparray("fetch", filename, zv_arr_options);
71 	if (!argv) {
72 		zend_error(E_WARNING, "cannot allocate arguments options");
73 		RETURN_FALSE;
74 	}
75 
76 	if (rrd_test_error()) rrd_clear_error();
77 
78 	/* call rrd_fetch and test if fails */
79 	if (rrd_fetch(argv->count - 1, &argv->args[1], &start, &end, &step, &ds_cnt,
80 		&ds_namv, &ds_data) == -1 ) {
81 		rrd_args_free(argv);
82 		RETURN_FALSE;
83 	}
84 
85 	/* making return array */
86 	array_init(return_value);
87 	add_assoc_long(return_value, "start", start);
88 	add_assoc_long(return_value, "end", end);
89 	add_assoc_long(return_value, "step", step);
90 
91 	/* add "ds_namv" and "data" array into return values if there is any
92 	 * result data
93 	 */
94 	if (!ds_data || !ds_namv || !ds_cnt) {
95 		add_assoc_null(return_value, "data");
96 	} else {
97 		rrd_value_t *datap = ds_data;
98 		uint timestamp, ds_counter;
99 		/* final array for all data from all data sources */
100 		zval zv_data_array;
101 
102 		array_init(&zv_data_array);
103 
104 		/* add arrays for each data source, each array will be filled with
105 		 * retrieved data for a particular data source
106 		 */
107 		for (ds_counter = 0; ds_counter < ds_cnt; ds_counter++) {
108 			zval zv_ds_data_array;
109 			array_init(&zv_ds_data_array);
110 
111 			add_assoc_zval(&zv_data_array, ds_namv[ds_counter], &zv_ds_data_array);
112 		}
113 
114 		for (timestamp = start + step; timestamp <= end; timestamp += step) {
115 			/* try to find current data source result array in the
116 			 * zv_data_array
117 			 */
118 			zend_hash_internal_pointer_reset(Z_ARRVAL(zv_data_array));
119 			for (ds_counter = 0; ds_counter < ds_cnt; ds_counter++) {
120 				/* pointer for one data source retrieved data */
121 				zval *ds_data_array;
122 				/* value for key (timestamp) in data array */
123 				char str_timestamp[11];
124 				ZEND_LTOA((zend_ulong)timestamp, str_timestamp, sizeof(str_timestamp));
125 
126 				/* gets pointer for data source result array */
127 				ds_data_array = zend_hash_get_current_data(Z_ARRVAL(zv_data_array));
128 
129 				add_assoc_double(ds_data_array, str_timestamp, *(datap++));
130 				zend_hash_move_forward(Z_ARRVAL(zv_data_array));
131 			}
132 		}
133 		add_assoc_zval(return_value, "data", &zv_data_array);
134 
135 		/* free data from rrd_fetch */
136 		free(ds_data);
137 		for (ds_counter = 0; ds_counter < ds_cnt; ds_counter++) {
138 			free(ds_namv[ds_counter]);
139 		}
140 		free(ds_namv);
141 	}
142 
143 	rrd_args_free(argv);
144 }
145 /* }}} */
146 
147 /* {{{ proto int rrd_first(string file [, int rraindex = 0])
148 	Gets first update time of an RRD file
149 */
PHP_FUNCTION(rrd_first)150 PHP_FUNCTION(rrd_first)
151 {
152 	char *filename;
153 	size_t filename_length;
154 	long rraindex = 0;
155 	/* return value from rrd_first_r call */
156 	time_t rrd_first_return_val;
157 
158 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "p|l", &filename,
159 		&filename_length, &rraindex) == FAILURE) {
160 		return;
161 	}
162 
163 	if (rraindex < 0) {
164 		rrd_set_error("invalid rraindex number, rraindex must be >= 0");
165 		RETURN_FALSE;
166 	}
167 
168 	if (php_check_open_basedir(filename)) {
169 		RETURN_FALSE;
170 	}
171 
172 	if (rrd_test_error()) rrd_clear_error();
173 
174 	/* call rrd_first and test if fails */
175 	rrd_first_return_val = rrd_first_r(filename, rraindex);
176 	if (rrd_first_return_val == -1) {
177 		RETURN_FALSE;
178 	}
179 	RETURN_LONG(rrd_first_return_val);
180 }
181 /* }}} */
182 
183 /* {{{ proto int rrd_last(string file)
184 	Gets last update time of an RRD file
185 */
PHP_FUNCTION(rrd_last)186 PHP_FUNCTION(rrd_last)
187 {
188 	char *filename;
189 	size_t filename_length;
190 	/* return value from rrd_first_r call */
191 	time_t rrd_last_return_val;
192 
193 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "p", &filename,
194 		&filename_length) == FAILURE) {
195 		return;
196 	}
197 
198 	if (php_check_open_basedir(filename)) {
199 		RETURN_FALSE;
200 	}
201 
202 	if (rrd_test_error()) rrd_clear_error();
203 
204 	/* call rrd_last and test if fails */
205 	rrd_last_return_val = rrd_last_r(filename);
206 	if (rrd_last_return_val == -1) {
207 		RETURN_FALSE;
208 	}
209 	RETURN_LONG(rrd_last_return_val);
210 }
211 /* }}} */
212 
213 /* {{{ proto int rrd_lastupdate(string file)
214 	Gets last update details of an RRD file */
PHP_FUNCTION(rrd_lastupdate)215 PHP_FUNCTION(rrd_lastupdate)
216 {
217 	char *filename;
218 	size_t filename_length;
219 	/* list of arguments for rrd_lastupdate call, it's more efficient then
220 	 * usage of rrd_args, because there isn't array of arguments in parameters
221 	 */
222 	char *argv[3];
223 	/* return values from rrd_lastupdate_r function */
224 	time_t last_update;
225 	unsigned long ds_cnt;
226 	char **ds_namv;
227 	char **last_ds;
228 
229 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "p", &filename,
230 		&filename_length) == FAILURE) {
231 		return;
232 	}
233 
234 	if (php_check_open_basedir(filename)) {
235 		RETURN_FALSE;
236 	}
237 
238 	argv[0] = "dummy";
239 	argv[1] = estrdup("lastupdate");
240 	argv[2] = estrndup(filename, filename_length);
241 
242 	if (rrd_test_error()) rrd_clear_error();
243 
244 #ifdef HAVE_RRD_LASTUPDATE_R
245 	if (rrd_lastupdate_r(argv[2], &last_update, &ds_cnt, &ds_namv,
246 		&last_ds) == -1) {
247 #else
248  	if (rrd_lastupdate(2, &argv[1], &last_update, &ds_cnt, &ds_namv,
249  		&last_ds) == -1) {
250 #endif
251 		efree(argv[2]); efree(argv[1]);
252 		RETURN_FALSE;
253 	}
254 
255 	efree(argv[2]); efree(argv[1]);
256 
257 	/* making return array*/
258 	array_init(return_value);
259 	add_assoc_long(return_value, "last_update", last_update);
260 	add_assoc_long(return_value, "ds_cnt", ds_cnt);
261 
262 	/* "ds_navm" return array or null, if no available */
263 	if (!ds_namv || !ds_cnt) {
264 		add_assoc_null(return_value, "ds_namv");
265 	} else {
266 		uint i;
267 		zval zv_ds_namv_array;
268 		array_init(&zv_ds_namv_array);
269 
270 		for (i = 0; i < ds_cnt; i++) {
271 			add_next_index_string(&zv_ds_namv_array, ds_namv[i]);
272 			free(ds_namv[i]);
273 		}
274 		free(ds_namv);
275 		add_assoc_zval(return_value, "ds_navm", &zv_ds_namv_array);
276 	}
277 
278 	/* "data" return array or null, if no available */
279 	if (!last_ds || !ds_cnt) {
280 		add_assoc_null(return_value, "data");
281 	} else {
282 		uint i;
283 		zval zv_data_array;
284 		array_init(&zv_data_array);
285 
286 		/* simple array for "data" is enough, data source names and timestamps are
287 		 * available under other return value keys
288 		 */
289 		for (i = 0; i < ds_cnt; i++) {
290 			add_next_index_string(&zv_data_array, last_ds[i]);
291 			free(last_ds[i]);
292 		}
293 		free(last_ds);
294 		add_assoc_zval(return_value, "data", &zv_data_array);
295 	}
296 }
297 
298 /* {{{ proto array rrd_restore(string xmlFile, string rrdFile [, array options])
299 	Restores an RRD file from a XML dump */
300 PHP_FUNCTION(rrd_restore)
301 {
302 	char *xml_filename, *rrd_filename;
303 	size_t xml_filename_length, rrd_filename_length;
304 	zval *zv_arr_options = NULL;
305 	/* this is merge of options and rrd_filename. This is needed because
306 	 * rrd_args_init_by_phparray allows only one filename as argument, so
307 	 * rrd_filename mugst be part of array of arguments
308 	 */
309 	zval zv_options;
310 	rrd_args *argv;
311 
312 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "pp|a", &xml_filename,
313 		&xml_filename_length, &rrd_filename, &rrd_filename_length,
314 		&zv_arr_options) == FAILURE) {
315 		return;
316 	}
317 
318 	if (php_check_open_basedir(xml_filename) ||
319 		php_check_open_basedir(rrd_filename)) {
320 		RETURN_FALSE;
321 	}
322 
323 	/* zv_options = merge rrd_filename and zv_arr_options content */
324 	array_init(&zv_options);
325 	add_next_index_string(&zv_options, rrd_filename);
326 	if (zv_arr_options && Z_TYPE_P(zv_arr_options) == IS_ARRAY) {
327 		php_array_merge(Z_ARRVAL(zv_options), Z_ARRVAL_P(zv_arr_options));
328 	}
329 
330 	argv = rrd_args_init_by_phparray("restore", xml_filename, &zv_options);
331 	if (!argv) {
332 		zend_error(E_WARNING, "cannot allocate arguments options");
333 		zval_dtor(&zv_options);
334 		RETURN_FALSE;
335 	}
336 
337 	if (rrd_test_error()) rrd_clear_error();
338 
339 	/* call rrd_ restore and test if fails */
340 	if (rrd_restore(argv->count-1, &argv->args[1]) == -1) {
341 		RETVAL_FALSE;
342 	} else {
343 		RETVAL_TRUE;
344 	}
345 	zval_dtor(&zv_options);
346 	rrd_args_free(argv);
347 }
348 /* }}} */
349 
350 /* {{{ proto bool rrd_tune(string file, array options)
351 	Tune an RRD file with the options passed (passed via array) */
352 PHP_FUNCTION(rrd_tune)
353 {
354 	char *filename;
355 	size_t filename_length;
356 	zval *zv_arr_options;
357 	rrd_args *argv;
358 
359 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "pa", &filename,
360 		&filename_length, &zv_arr_options) == FAILURE) {
361 		return;
362 	}
363 
364 	if (!zend_hash_num_elements(Z_ARRVAL_P(zv_arr_options))) {
365 		zend_error(E_WARNING, "options array mustn't be empty");
366 		RETURN_FALSE;
367 	}
368 
369 	if (php_check_open_basedir(filename)) RETURN_FALSE;
370 
371 	argv = rrd_args_init_by_phparray("tune", filename, zv_arr_options);
372 	if (!argv) {
373 		zend_error(E_WARNING, "cannot allocate arguments options");
374 		RETURN_FALSE;
375 	}
376 
377 	if (rrd_test_error()) rrd_clear_error();
378 
379 	/* call rrd_tune and test if fails */
380 	if (rrd_tune(argv->count-1, &argv->args[1]) == -1 ) {
381 		RETVAL_FALSE;
382 	} else {
383 		RETVAL_TRUE;
384 	}
385 	rrd_args_free(argv);
386 }
387 /* }}} */
388 
389 /* {{{ proto array rrd_xport(array options)
390  * Creates a graph based on options passed via an array
391  */
392 PHP_FUNCTION(rrd_xport)
393 {
394 	zval *zv_arr_options;
395 	rrd_args *argv;
396 	/* return values from rrd_xport */
397 	int xxsize;
398 	time_t start, end, time_index;
399 	u_long step, outvar_count;
400 	char **legend_v;
401 	rrd_value_t *data, *data_ptr;
402 	zval zv_data;
403 	u_long outvar_index;
404 
405 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "a", &zv_arr_options) == FAILURE) {
406 		return;
407 	}
408 
409 	argv = rrd_args_init_by_phparray("xport", "", zv_arr_options);
410 	if (!argv) {
411 		zend_error(E_WARNING, "cannot allocate arguments options");
412 		RETURN_FALSE;
413 	}
414 
415 	if (rrd_test_error()) rrd_clear_error();
416 
417 	/* call rrd_xport and test if fails */
418 	if (rrd_xport(argv->count-1, &argv->args[1], &xxsize, &start, &end, &step,
419 		&outvar_count, &legend_v, &data) == -1) {
420 		php_printf("rrd_xport failed");
421 		rrd_args_free(argv);
422 		RETURN_FALSE;
423 	}
424 
425 	rrd_args_free(argv);
426 
427 	/* fill rrd_xport return values into array */
428 	array_init(return_value);
429 
430 	add_assoc_long(return_value, "start", start + step);
431 	add_assoc_long(return_value, "end", end);
432 	add_assoc_long(return_value, "step", step);
433 
434 	/* no data available */
435 	if (!data) {
436 		add_assoc_null(return_value, "data");
437 		return;
438 	}
439 
440 	array_init(&zv_data);
441 
442 	for (outvar_index = 0; outvar_index < outvar_count; outvar_index++) {
443 		/* array for a whole one output variable data, it contains indexes
444 		 * array(
445 		 *  "legend" => "variable legend",
446 		 *  "data" => array(
447 		 *    920807400 => 0.14,
448 		 *    920807800 => 21,
449 		 *    ...
450 		 *  ))
451 		 */
452 		zval zv_var_data, time_data;
453 		array_init(&zv_var_data);
454 		array_init(&time_data);
455 
456 		add_assoc_string(&zv_var_data, "legend", legend_v[outvar_index]);
457 		free(legend_v[outvar_index]);
458 
459 		data_ptr = data + outvar_index;
460 		for (time_index = start + step; time_index <= end; time_index += step) {
461 			/* value for key (timestamp) in data array */
462 			char str_timestamp[11];
463 			ZEND_LTOA((zend_ulong)time_index, str_timestamp, sizeof(str_timestamp));
464 
465 			add_assoc_double(&time_data, str_timestamp, *data_ptr);
466 			data_ptr += outvar_count;
467 		}
468 		add_assoc_zval(&zv_var_data, "data", &time_data);
469 		add_next_index_zval(&zv_data, &zv_var_data);
470 	}
471 	add_assoc_zval(return_value, "data", &zv_data);
472 	free(legend_v);
473 	free(data);
474 }
475 /* }}} */
476 
477 #ifdef HAVE_RRDC_DISCONNECT
478 /* {{{ proto void rrdc_disconnect()
479  * Close any outstanding connection to rrd cache daemon.
480  */
481 PHP_FUNCTION(rrdc_disconnect)
482 {
483 	if (zend_parse_parameters_none() == FAILURE) {
484 		return;
485 	}
486 
487 	rrdc_disconnect();
488 }
489 #endif
490 
491 /* {{{ proto string rrd_version()
492  * Gets version of underlying librrd.
493  */
494 PHP_FUNCTION(rrd_version)
495 {
496 	if (zend_parse_parameters_none() == FAILURE) {
497 		return;
498 	}
499 
500 	RETVAL_STRING(rrd_strversion());
501 }
502 
503 /* {{{ arguments */
504 ZEND_BEGIN_ARG_INFO(arginfo_rrd_fetch, 0)
505 	ZEND_ARG_INFO(0, file)
506 	ZEND_ARG_INFO(0, options)
507 ZEND_END_ARG_INFO()
508 
509 ZEND_BEGIN_ARG_INFO_EX(arginfo_rrd_first, 0, 0, 1)
510 	ZEND_ARG_INFO(0, file)
511 	ZEND_ARG_INFO(0, raaindex)
512 ZEND_END_ARG_INFO()
513 
514 ZEND_BEGIN_ARG_INFO(arginfo_rrd_last, 0)
515 	ZEND_ARG_INFO(0, file)
516 ZEND_END_ARG_INFO()
517 
518 ZEND_BEGIN_ARG_INFO(arginfo_rrd_lastupdate, 0)
519 	ZEND_ARG_INFO(0, file)
520 ZEND_END_ARG_INFO()
521 
522 ZEND_BEGIN_ARG_INFO_EX(arginfo_rrd_restore, 0, 0, 2)
523 	ZEND_ARG_INFO(0, xml_file)
524 	ZEND_ARG_INFO(0, rrd_file)
525 	ZEND_ARG_INFO(0, options)
526 ZEND_END_ARG_INFO()
527 
528 ZEND_BEGIN_ARG_INFO(arginfo_rrd_tune, 0)
529 	ZEND_ARG_INFO(0, file)
530 	ZEND_ARG_INFO(0, options)
531 ZEND_END_ARG_INFO()
532 
533 ZEND_BEGIN_ARG_INFO(arginfo_rrd_xport, 0)
534 	ZEND_ARG_INFO(0, options)
535 ZEND_END_ARG_INFO()
536 
537 ZEND_BEGIN_ARG_INFO(arginfo_rrd_info, 0)
538 	ZEND_ARG_INFO(0, file)
539 ZEND_END_ARG_INFO()
540 
541 ZEND_BEGIN_ARG_INFO(arginfo_rrd_graph, 0)
542 	ZEND_ARG_INFO(0, file)
543 	ZEND_ARG_INFO(0, options)
544 ZEND_END_ARG_INFO()
545 
546 ZEND_BEGIN_ARG_INFO(arginfo_rrd_create, 0)
547 	ZEND_ARG_INFO(0, filename)
548 	ZEND_ARG_INFO(0, options)
549 ZEND_END_ARG_INFO()
550 
551 ZEND_BEGIN_ARG_INFO(arginfo_rrd_update, 0)
552 	ZEND_ARG_INFO(0, file)
553 	ZEND_ARG_INFO(0, options)
554 ZEND_END_ARG_INFO()
555 /* }}} */
556 
557 /* {{{ */
558 static zend_function_entry rrd_functions[] = {
559 	PHP_FE(rrd_update, arginfo_rrd_update)
560 	PHP_FE(rrd_create, arginfo_rrd_create)
561 	PHP_FE(rrd_graph, arginfo_rrd_graph)
562 	PHP_FE(rrd_error, NULL)
563 	PHP_FE(rrd_fetch, arginfo_rrd_fetch)
564 	PHP_FE(rrd_first, arginfo_rrd_first)
565 	PHP_FE(rrd_info, arginfo_rrd_info)
566 	PHP_FE(rrd_last, arginfo_rrd_last)
567 	PHP_FE(rrd_lastupdate, arginfo_rrd_lastupdate)
568 	PHP_FE(rrd_restore, arginfo_rrd_restore)
569 	PHP_FE(rrd_tune, arginfo_rrd_tune)
570 	PHP_FE(rrd_xport, arginfo_rrd_xport)
571 #ifdef HAVE_RRDC_DISCONNECT
572 	PHP_FE(rrdc_disconnect, NULL)
573 #endif
574 	PHP_FE(rrd_version, NULL)
575 	PHP_FE_END
576 };
577 /* }}} */
578 
579 #ifdef COMPILE_DL_RRD
580 	ZEND_GET_MODULE(rrd)
581 #endif
582 
583 /* {{{ PHP_MINIT_FUNCTION */
584 static PHP_MINIT_FUNCTION(rrd)
585 {
586 	rrd_graph_minit();
587 	rrd_create_minit();
588 	rrd_update_minit();
589 	return SUCCESS;
590 }
591 /* }}} */
592 
593 /* {{{ PHP_MINFO_FUNCTION */
594 static PHP_MINFO_FUNCTION(rrd)
595 {
596 	php_info_print_table_start();
597 	php_info_print_table_header(2, "rrd tool module", "enabled");
598 	php_info_print_table_row(2, "rrd tool module version", PHP_RRD_VERSION);
599 	php_info_print_table_row(2, "rrdtool library version", rrd_strversion());
600 	php_info_print_table_end();
601 }
602 /* }}} */
603 
604 
605 /* {{{ PHP_MSHUTDOWN_FUNCTION */
606 static PHP_MSHUTDOWN_FUNCTION(rrd)
607 {
608 #ifdef HAVE_RRDC_DISCONNECT
609 	/* ensure that any connection to rrd cache deamon will be closed */
610 	rrdc_disconnect();
611 #endif
612 	return SUCCESS;
613 }
614 /* }}} */
615 
616 /* {{{ rrd module_entry */
617 zend_module_entry rrd_module_entry = {
618 	STANDARD_MODULE_HEADER,
619 	"rrd",
620 	rrd_functions,
621 	PHP_MINIT(rrd),
622 	PHP_MSHUTDOWN(rrd),
623 	NULL, /* PHP_RINIT(rrd) */
624 	NULL, /* PHP_RSHUTDOWN(rrd) */
625 	PHP_MINFO(rrd),
626 	PHP_RRD_VERSION,
627 	STANDARD_MODULE_PROPERTIES
628 };
629 /* }}} */
630 
631 /* {{{ Inits rrd arguments object for a particular rrd command by php array
632  * filename paremeter is optional.
633  */
634 rrd_args *rrd_args_init_by_phparray(const char *command_name, const char *filename,
635 	const zval *options)
636 {
637 	uint i, option_count, args_counter = 2;
638 	rrd_args *result;
639 
640 	if (Z_TYPE_P(options) != IS_ARRAY) return NULL;
641 	option_count = zend_hash_num_elements(Z_ARRVAL_P(options));
642 	if (!option_count) return NULL;
643 	if (!strlen(command_name)) return NULL;
644 
645 	result = (rrd_args *)emalloc(sizeof(rrd_args));
646 	/* "dummy" + command_name + filename if presented */
647 	result->count = option_count + (strlen(filename) ? 3 : 2);
648 	result->args = (char **)safe_emalloc(result->count, sizeof(char *), 0);
649 
650 	/* "dummy" and command_name are always needed */
651 	result->args[0] = "dummy";
652 	result->args[1] = estrdup(command_name);
653 
654 	/* append filename if it's presented */
655 	if (strlen(filename)) result->args[args_counter++] = estrdup(filename);
656 
657 	zend_hash_internal_pointer_reset(Z_ARRVAL_P(options));
658 	for (i=0; i < option_count; i++) {
659 		zval *item;
660 		smart_string option = {0}; /* one argument option */
661 
662 		/* force using strings as array items */
663 		item = zend_hash_get_current_data(Z_ARRVAL_P(options));
664 		if (Z_TYPE_P(item) != IS_STRING) convert_to_string(item);
665 		smart_string_appendl(&option, Z_STRVAL_P(item), Z_STRLEN_P(item));
666 		smart_string_0(&option);
667 
668 		result->args[args_counter++] = estrdup(option.c);
669 		smart_string_free(&option);
670 
671 		zend_hash_move_forward(Z_ARRVAL_P(options));
672 	}
673 
674 	return result;
675 }
676 /* }}} */
677 
678 /* {{{ Frees all memory for arguments object
679  */
680 void rrd_args_free(rrd_args *args)
681 {
682 	int i;
683 	if (!args || !args->args) return;
684 
685 	for (i = 1; i < args->count; i++) {
686 		efree(args->args[i]);
687 	}
688 
689 	efree(args->args);
690 	efree(args);
691 }
692 /* }}} */
693