1 #ifdef __cplusplus
2 extern "C" {
3 #endif
4 
5 #include "EXTERN.h"
6 #include "perl.h"
7 #include "XSUB.h"
8 
9 #ifdef __cplusplus
10 }
11 #endif
12 
13 /*
14  * rrd_tool.h includes config.h, but at least on Ubuntu Breezy Badger
15  * 5.10 with gcc 4.0.2, the C preprocessor picks up Perl's config.h
16  * which is included from the Perl includes and never reads rrdtool's
17  * config.h.  Without including rrdtool's config.h, this module does
18  * not compile, so include it here with an explicit path.
19  *
20  * Because rrdtool's config.h redefines VERSION which is originally
21  * set via Perl's Makefile.PL and passed down to the C compiler's
22  * command line, save the original value and reset it after the
23  * includes.
24  */
25 #define VERSION_SAVED VERSION
26 #undef VERSION
27 #ifndef _WIN32
28 #include "rrd_config.h"
29 #endif
30 #include "rrd_tool.h"
31 #undef VERSION
32 #define VERSION VERSION_SAVED
33 #undef VERSION_SAVED
34 
35 #define rrdcode(name) \
36 		argv = (char **) malloc((items+1)*sizeof(char *));\
37 		argv[0] = "dummy";\
38 		for (i = 0; i < items; i++) { \
39 		    STRLEN len; \
40 		    char *handle= SvPV(ST(i),len);\
41 		    /* actually copy the data to make sure possible modifications \
42 		       on the argv data does not backfire into perl */ \
43 		    argv[i+1] = (char *) malloc((strlen(handle)+1)*sizeof(char)); \
44 		    strcpy(argv[i+1],handle); \
45  	        } \
46 		rrd_clear_error();\
47 		RETVAL=name(items+1,argv); \
48 		for (i=0; i < items; i++) {\
49 		    free(argv[i+1]);\
50 		} \
51 		free(argv);\
52 		\
53 		if (rrd_test_error()) XSRETURN_UNDEF;
54 
55 #define hvs(VAL) hv_store_ent(hash, sv_2mortal(newSVpv(data->key,0)),VAL,0)
56 
57 #define rrdinfocode(name) \
58 		/* prepare argument list */ \
59 		argv = (char **) malloc((items+1)*sizeof(char *)); \
60 		argv[0] = "dummy"; \
61 		for (i = 0; i < items; i++) { \
62 		    STRLEN len; \
63 		    char *handle= SvPV(ST(i),len); \
64 		    /* actually copy the data to make sure possible modifications \
65 		       on the argv data does not backfire into perl */ \
66 		    argv[i+1] = (char *) malloc((strlen(handle)+1)*sizeof(char)); \
67 		    strcpy(argv[i+1],handle); \
68  	        } \
69                 rrd_clear_error(); \
70                 data=name(items+1, argv); \
71                 for (i=0; i < items; i++) { \
72 		    free(argv[i+1]); \
73 		} \
74 		free(argv); \
75                 if (rrd_test_error()) XSRETURN_UNDEF; \
76                 hash = newHV(); \
77    	        save=data; \
78                 while (data) { \
79 		/* the newSV will get copied by hv so we create it as a mortal \
80            to make sure it does not keep hanging round after the fact */ \
81 		    switch (data->type) { \
82 		    case RD_I_VAL: \
83 			if (isnan(data->value.u_val)) \
84 			    hvs(newSV(0)); \
85 			else \
86 			    hvs(newSVnv(data->value.u_val)); \
87 			break; \
88 			case RD_I_INT: \
89 			hvs(newSViv(data->value.u_int)); \
90 			break; \
91 		    case RD_I_CNT: \
92 			hvs(newSViv(data->value.u_cnt)); \
93 			break; \
94 		    case RD_I_STR: \
95 			hvs(newSVpv(data->value.u_str,0)); \
96 			break; \
97 		    case RD_I_BLO: \
98 			hvs(newSVpv((char *)data->value.u_blo.ptr,data->value.u_blo.size)); \
99 			break; \
100 		    } \
101 		    data = data->next; \
102 	        } \
103             rrd_info_free(save); \
104             RETVAL = newRV_noinc((SV*)hash);
105 
106 /*
107  * should not be needed if libc is linked (see ntmake.pl)
108 #ifdef _WIN32
109  #define free free
110  #define malloc malloc
111  #define realloc realloc
112 #endif
113 */
114 
115 
116 static SV * rrd_fetch_cb_svptr = (SV*)NULL;
117 
rrd_fetch_cb_wrapper(const char * filename,enum cf_en cf_idx,time_t * start,time_t * end,unsigned long * step,unsigned long * ds_cnt,char *** ds_namv,rrd_value_t ** data)118 static int rrd_fetch_cb_wrapper(
119     const char     *filename,  /* name of the rrd */
120     enum cf_en     cf_idx, /* consolidation function */
121     time_t         *start,
122     time_t         *end,       /* which time frame do you want ?
123                                 * will be changed to represent reality */
124     unsigned long  *step,      /* which stepsize do you want?
125                                 * will be changed to represent reality */
126     unsigned long  *ds_cnt,    /* number of data sources in file */
127     char           ***ds_namv, /* names of data_sources */
128     rrd_value_t    **data)     /* two dimensional array containing the data */
129   {
130     dSP;
131     HV *callHV;
132     SV *retSV;
133     HV *retHV;
134     HE *retHE;
135     AV *retAV;
136     time_t new_start;
137     char *cfStr = NULL;
138     unsigned long i,ii;
139     unsigned long rowCount = 0;
140     if (!rrd_fetch_cb_svptr){
141         rrd_set_error("Use RRDs::register_fetch_cb to register a fetch callback.");
142         return -1;
143     }
144 
145     ENTER;
146     SAVETMPS;
147     /* prepare the arguments */
148     callHV = newHV();
149     hv_store_ent(callHV, sv_2mortal(newSVpv("filename",0)),newSVpv(filename,0),0);
150     switch(cf_idx){
151         case CF_AVERAGE:
152             cfStr = "AVERAGE";
153             break;
154         case CF_MINIMUM:
155             cfStr = "MIN";
156             break;
157         case CF_MAXIMUM:
158             cfStr = "MAX";
159             break;
160         case CF_LAST:
161             cfStr = "LAST";
162         default:
163             break;
164     }
165     hv_store_ent(callHV, sv_2mortal(newSVpv("cd",0)),newSVpv(cfStr,0),0);
166     hv_store_ent(callHV, sv_2mortal(newSVpv("start",0)),newSVuv(*start),0);
167     hv_store_ent(callHV, sv_2mortal(newSVpv("end",0)),newSVuv(*end),0);
168     hv_store_ent(callHV, sv_2mortal(newSVpv("step",0)),newSVuv(*step),0);
169     PUSHMARK(SP);
170     XPUSHs(newRV_noinc((SV *)callHV));
171     PUTBACK;
172     /* Call the Perl sub to process the callback */
173     call_sv(rrd_fetch_cb_svptr , G_EVAL|G_SCALAR);
174     SPAGAIN;
175     /* Check the eval first */
176     if (SvTRUE(ERRSV)) {
177         rrd_set_error("perl callback failed: %s",SvPV_nolen(ERRSV));
178         POPs; /* there is undef on top of the stack when there is an error
179                  and call_sv was initiated with G_EVAL|G_SCALER */
180         goto error_out;
181     }
182     retSV = POPs;
183     if (!SvROK(retSV)){
184         rrd_set_error("Expected the perl callback function to return a reference");
185         goto error_out;
186     }
187     retHV = (HV*)SvRV(retSV);
188     if (SvTYPE(retHV) != SVt_PVHV) {
189         rrd_set_error("Expected the perl callback function to return a hash reference");
190         goto error_out;
191     }
192 
193 #define loadRet(hashKey) \
194     if (( retHE = hv_fetch_ent(retHV,sv_2mortal(newSVpv(hashKey,0)),0,0)) == NULL){ \
195         rrd_set_error("Expected the perl callback function to return a '" hashKey "' value"); \
196         goto error_out; }
197 
198     loadRet("step");
199     *step = SvIV(HeVAL(retHE));
200     if (*step <= 0){
201         rrd_set_error("Expected the perl callback function to return a valid step value");
202         goto error_out;
203     }
204 
205     loadRet("start");
206     new_start = SvIV(HeVAL(retHE));
207     if (new_start == 0 || new_start > *start){
208         rrd_set_error("Expected the perl callback function to return a start value equal or earlier than %lld but got %lld",(long long int)(*start),(long long int)(new_start));
209         goto error_out;
210     }
211 
212     *start = new_start;
213 /*    rowCount = ((*end - *start) / *step); */
214 
215     /* figure out how long things are so that we can allocate the memory */
216     loadRet("data");
217     retSV = HeVAL(retHE);
218     if (!SvROK(retSV)){
219         rrd_set_error("Expected the perl callback function to return a valid data element");
220         goto error_out;
221     }
222     retHV = (HV*)SvRV(retSV);
223     if (SvTYPE(retHV) != SVt_PVHV){
224         rrd_set_error("Expected the perl callback function to return data element pointing to a hash");
225         goto error_out;
226     }
227 
228     *ds_cnt = hv_iterinit(retHV);
229 
230     if (((*ds_namv) = (char **) calloc( *ds_cnt , sizeof(char *))) == NULL) {
231         rrd_set_error("Failed to allocate memory for ds_namev when returning from perl callback");
232         goto error_out;
233     }
234     for (i=0;i<*ds_cnt;i++){
235         char *retKey;
236         I32 retKeyLen;
237         HE* hash_entry;
238         hash_entry = hv_iternext(retHV);
239         retKey = hv_iterkey(hash_entry,&retKeyLen);
240 	if (strlen(retKey) >= DS_NAM_SIZE){
241             rrd_set_error("Key '%s' longer than the allowed maximum of %d byte",retKey,DS_NAM_SIZE-1);
242             goto error_out;
243 	}
244 
245         if ((((*ds_namv)[i]) = (char*)malloc(sizeof(char) * DS_NAM_SIZE)) == NULL) {
246             rrd_set_error("malloc fetch ds_namv entry");
247             goto error_out_free_ds_namv;
248         }
249 
250         strncpy((*ds_namv)[i], retKey, DS_NAM_SIZE - 1);
251         (*ds_namv)[i][DS_NAM_SIZE - 1] = '\0';
252         retSV = hv_iterval(retHV,hash_entry);
253         if (!SvROK(retSV)){
254             rrd_set_error("Expected the perl callback function to return an array pointer for {data}->{%s}",(*ds_namv)[i]);
255             goto error_out_free_ds_namv;
256         }
257         retAV = (AV*)SvRV(retSV);
258         if (SvTYPE(retAV) != SVt_PVAV){
259             rrd_set_error("Expected the perl callback function to return an array pointer for {data}->{%s}",(*ds_namv)[i]);
260             goto error_out_free_ds_namv;
261         }
262         if (av_len(retAV)+1 > rowCount)
263             rowCount = av_len(retAV)+1;
264     }
265 
266     *end = *start + *step * rowCount;
267 
268     if (((*data) = (rrd_value_t*)malloc(*ds_cnt * rowCount * sizeof(rrd_value_t))) == NULL) {
269         rrd_set_error("malloc fetch data area");
270         goto error_out_free_ds_namv;
271     }
272 
273     for (i=0;i<*ds_cnt;i++){
274         retAV = (AV*)SvRV(HeVAL(hv_fetch_ent(retHV,sv_2mortal(newSVpv((*ds_namv)[i],0)),0,0)));
275         for (ii=0;ii<rowCount;ii++){
276             SV** valP = av_fetch(retAV,ii,0);
277             SV* val = valP ? *valP : &PL_sv_undef;
278             (*data)[i + ii * (*ds_cnt)] = SvNIOK(val) ? SvNVx(val) : DNAN;
279         }
280     }
281 
282 
283     PUTBACK;
284     FREETMPS;
285     LEAVE;
286     return 1;
287 
288     error_out_free_ds_namv:
289 
290     for (i = 0; i < *ds_cnt; ++i){
291         if ((*ds_namv)[i]){
292             free((*ds_namv)[i]);
293         }
294     }
295 
296     free(*ds_namv);
297 
298     error_out:
299     PUTBACK;
300     FREETMPS;
301     LEAVE;
302     return -1;
303 
304     /* prepare return data */
305 }
306 
307 
308 MODULE = RRDs	PACKAGE = RRDs	PREFIX = rrd_
309 
310 BOOT:
311 #ifdef MUST_DISABLE_SIGFPE
312 	signal(SIGFPE,SIG_IGN);
313 #endif
314 #ifdef MUST_DISABLE_FPMASK
315 	fpsetmask(0);
316 #endif
317 
318 SV*
319 rrd_error()
320 	CODE:
321 		if (! rrd_test_error()) XSRETURN_UNDEF;
322                 RETVAL = newSVpv(rrd_get_error(),0);
323 	OUTPUT:
324 		RETVAL
325 
326 int
327 rrd_last(...)
328       PROTOTYPE: @
329       PREINIT:
330       int i;
331       char **argv;
332       CODE:
333               rrdcode(rrd_last);
334       OUTPUT:
335             RETVAL
336 
337 int
338 rrd_first(...)
339       PROTOTYPE: @
340       PREINIT:
341       int i;
342       char **argv;
343       CODE:
344               rrdcode(rrd_first);
345       OUTPUT:
346             RETVAL
347 
348 int
349 rrd_create(...)
350 	PROTOTYPE: @
351 	PREINIT:
352         int i;
353 	char **argv;
354 	CODE:
355 		rrdcode(rrd_create);
356 	        RETVAL = 1;
357         OUTPUT:
358 		RETVAL
359 
360 int
361 rrd_update(...)
362 	PROTOTYPE: @
363 	PREINIT:
364         int i;
365 	char **argv;
366 	CODE:
367 		rrdcode(rrd_update);
368        	        RETVAL = 1;
369 	OUTPUT:
370 		RETVAL
371 
372 int
373 rrd_tune(...)
374 	PROTOTYPE: @
375 	PREINIT:
376         int i;
377 	char **argv;
378 	CODE:
379 		rrdcode(rrd_tune);
380        	        RETVAL = 1;
381 	OUTPUT:
382 		RETVAL
383 
384 #ifdef HAVE_RRD_GRAPH
385 
386 SV *
387 rrd_graph(...)
388 	PROTOTYPE: @
389 	PREINIT:
390 	char **calcpr=NULL;
391 	int i,xsize,ysize;
392 	double ymin,ymax;
393 	char **argv;
394 	AV *retar;
395 	PPCODE:
396 		argv = (char **) malloc((items+1)*sizeof(char *));
397 		argv[0] = "dummy";
398 		for (i = 0; i < items; i++) {
399 		    STRLEN len;
400 		    char *handle = SvPV(ST(i),len);
401 		    /* actually copy the data to make sure possible modifications
402 		       on the argv data does not backfire into perl */
403 		    argv[i+1] = (char *) malloc((strlen(handle)+1)*sizeof(char));
404 		    strcpy(argv[i+1],handle);
405  	        }
406 		rrd_clear_error();
407 		rrd_graph(items+1,argv,&calcpr,&xsize,&ysize,NULL,&ymin,&ymax);
408 		for (i=0; i < items; i++) {
409 		    free(argv[i+1]);
410 		}
411 		free(argv);
412 
413 		if (rrd_test_error()) {
414 			if(calcpr) {
415 			   for(i=0;calcpr[i];i++)
416 				rrd_freemem(calcpr[i]);
417                            rrd_freemem(calcpr);
418                         }
419 			XSRETURN_UNDEF;
420 		}
421 		retar=newAV();
422 		if(calcpr){
423 			for(i=0;calcpr[i];i++){
424 				 av_push(retar,newSVpv(calcpr[i],0));
425 				 rrd_freemem(calcpr[i]);
426 			}
427 			rrd_freemem(calcpr);
428 		}
429 		EXTEND(sp,4);
430 		PUSHs(sv_2mortal(newRV_noinc((SV*)retar)));
431 		PUSHs(sv_2mortal(newSViv(xsize)));
432 		PUSHs(sv_2mortal(newSViv(ysize)));
433 
434 #endif /* HAVE_RRD_GRAPH */
435 
436 SV *
437 rrd_fetch(...)
438 	PROTOTYPE: @
439 	PREINIT:
440 		time_t        start,end;
441 		unsigned long step, ds_cnt,i,ii;
442 		rrd_value_t   *data,*datai;
443 		char **argv;
444 		char **ds_namv;
445 		AV *retar,*line,*names;
446 	PPCODE:
447 		argv = (char **) malloc((items+1)*sizeof(char *));
448 		argv[0] = "dummy";
449 		for (i = 0; i < items; i++) {
450 		    STRLEN len;
451 		    char *handle= SvPV(ST(i),len);
452 		    /* actually copy the data to make sure possible modifications
453 		       on the argv data does not backfire into perl */
454 		    argv[i+1] = (char *) malloc((strlen(handle)+1)*sizeof(char));
455 		    strcpy(argv[i+1],handle);
456  	        }
457 		rrd_clear_error();
458 		rrd_fetch(items+1,argv,&start,&end,&step,&ds_cnt,&ds_namv,&data);
459 		for (i=0; i < items; i++) {
460 		    free(argv[i+1]);
461 		}
462 		free(argv);
463 		if (rrd_test_error()) XSRETURN_UNDEF;
464                 /* convert the ds_namv into perl format */
465 		names=newAV();
466 		for (ii = 0; ii < ds_cnt; ii++){
467 		    av_push(names,newSVpv(ds_namv[ii],0));
468 		    rrd_freemem(ds_namv[ii]);
469 		}
470 		rrd_freemem(ds_namv);
471 		/* convert the data array into perl format */
472 		datai=data;
473 		retar=newAV();
474 		for (i = start+step; i <= end; i += step){
475 			line = newAV();
476 			for (ii = 0; ii < ds_cnt; ii++){
477  			  av_push(line,(isnan(*datai) ? newSV(0) : newSVnv(*datai)));
478 			  datai++;
479 			}
480 			av_push(retar,newRV_noinc((SV*)line));
481 		}
482 		rrd_freemem(data);
483 		EXTEND(sp,5);
484 		PUSHs(sv_2mortal(newSViv(start+step)));
485 		PUSHs(sv_2mortal(newSViv(step)));
486 		PUSHs(sv_2mortal(newRV_noinc((SV*)names)));
487 		PUSHs(sv_2mortal(newRV_noinc((SV*)retar)));
488 
489 SV *
490 rrd_fetch_cb_register(cb)
491     SV * cb
492     CODE:
493         if (rrd_fetch_cb_svptr == (SV*)NULL )
494             rrd_fetch_cb_svptr = newSVsv(cb);
495         else
496             SvSetSV(rrd_fetch_cb_svptr,cb);
497         rrd_fetch_cb_register(rrd_fetch_cb_wrapper);
498 
499 SV *
500 rrd_times(start, end)
501 	  char *start
502 	  char *end
503 	PREINIT:
504 		rrd_time_value_t start_tv, end_tv;
505 		char    *parsetime_error = NULL;
506 		time_t	start_tmp, end_tmp;
507 	PPCODE:
508 		rrd_clear_error();
509 		if ((parsetime_error = rrd_parsetime(start, &start_tv))) {
510 			rrd_set_error("start time: %s", parsetime_error);
511 			XSRETURN_UNDEF;
512 		}
513 		if ((parsetime_error = rrd_parsetime(end, &end_tv))) {
514 			rrd_set_error("end time: %s", parsetime_error);
515 			XSRETURN_UNDEF;
516 		}
517 		if (rrd_proc_start_end(&start_tv, &end_tv, &start_tmp, &end_tmp) == -1) {
518 			XSRETURN_UNDEF;
519 		}
520 		EXTEND(sp,2);
521 		PUSHs(sv_2mortal(newSVuv(start_tmp)));
522 		PUSHs(sv_2mortal(newSVuv(end_tmp)));
523 
524 int
525 rrd_xport(...)
526 	PROTOTYPE: @
527 	PREINIT:
528                 time_t start,end;
529                 int xsize;
530 		unsigned long step, col_cnt,i,ii;
531 		rrd_value_t *data,*ptr;
532                 char **argv,**legend_v;
533 		AV *retar,*line,*names;
534 	PPCODE:
535 		argv = (char **) malloc((items+1)*sizeof(char *));
536 		argv[0] = "dummy";
537 		for (i = 0; i < items; i++) {
538 		    STRLEN len;
539 		    char *handle = SvPV(ST(i),len);
540 		    /* actually copy the data to make sure possible modifications
541 		       on the argv data does not backfire into perl */
542 		    argv[i+1] = (char *) malloc((strlen(handle)+1)*sizeof(char));
543 		    strcpy(argv[i+1],handle);
544  	        }
545 		rrd_clear_error();
546 		rrd_xport(items+1,argv,&xsize,&start,&end,&step,&col_cnt,&legend_v,&data);
547 		for (i=0; i < items; i++) {
548 		    free(argv[i+1]);
549 		}
550 		free(argv);
551 		if (rrd_test_error()) XSRETURN_UNDEF;
552 
553                 /* convert the legend_v into perl format */
554 		names=newAV();
555 		for (ii = 0; ii < col_cnt; ii++){
556 		    av_push(names,newSVpv(legend_v[ii],0));
557 		    rrd_freemem(legend_v[ii]);
558 		}
559 		rrd_freemem(legend_v);
560 
561 		/* convert the data array into perl format */
562 		ptr=data;
563 		retar=newAV();
564 		for (i = start+step; i <= end; i += step){
565 			line = newAV();
566 			for (ii = 0; ii < col_cnt; ii++){
567  			  av_push(line,(isnan(*ptr) ? newSV(0) : newSVnv(*ptr)));
568 			  ptr++;
569 			}
570 			av_push(retar,newRV_noinc((SV*)line));
571 		}
572 		rrd_freemem(data);
573 
574 		EXTEND(sp,7);
575 		PUSHs(sv_2mortal(newSViv(start+step)));
576 		PUSHs(sv_2mortal(newSViv(end)));
577 		PUSHs(sv_2mortal(newSViv(step)));
578 		PUSHs(sv_2mortal(newSViv(col_cnt)));
579 		PUSHs(sv_2mortal(newRV_noinc((SV*)names)));
580 		PUSHs(sv_2mortal(newRV_noinc((SV*)retar)));
581 
582 SV*
583 rrd_info(...)
584 	PROTOTYPE: @
585 	PREINIT:
586 		rrd_info_t *data,*save;
587                 int i;
588                 char **argv;
589 		HV *hash;
590 	CODE:
591 		rrdinfocode(rrd_info);
592     OUTPUT:
593 	   RETVAL
594 
595 SV*
596 rrd_updatev(...)
597 	PROTOTYPE: @
598 	PREINIT:
599 		rrd_info_t *data,*save;
600                 int i;
601                 char **argv;
602 		HV *hash;
603 	CODE:
604 		rrdinfocode(rrd_update_v);
605     OUTPUT:
606 	   RETVAL
607 
608 #ifdef HAVE_RRD_GRAPH
609 
610 SV*
611 rrd_graphv(...)
612 	PROTOTYPE: @
613 	PREINIT:
614 		rrd_info_t *data,*save;
615                 int i;
616                 char **argv;
617 		HV *hash;
618 	CODE:
619 		rrdinfocode(rrd_graph_v);
620     OUTPUT:
621 	   RETVAL
622 
623 #endif /* HAVE_RRD_GRAPH */
624 
625 int
626 rrd_dump(...)
627        PROTOTYPE: @
628        PREINIT:
629         int i;
630        char **argv;
631        CODE:
632                rrdcode(rrd_dump);
633                        RETVAL = 1;
634        OUTPUT:
635                RETVAL
636 
637 int
638 rrd_restore(...)
639        PROTOTYPE: @
640        PREINIT:
641         int i;
642        char **argv;
643        CODE:
644                rrdcode(rrd_restore);
645                        RETVAL = 1;
646        OUTPUT:
647                RETVAL
648 
649 int
650 rrd_flushcached(...)
651 	PROTOTYPE: @
652 	PREINIT:
653 	int i;
654 	char **argv;
655 	CODE:
656 		rrdcode(rrd_flushcached);
657 	OUTPUT:
658 		RETVAL
659 
660 SV*
661 rrd_list(...)
662 	PROTOTYPE: @
663 	PREINIT:
664                 char *data;
665                 char *ptr, *end;
666                 int i;
667                 char **argv;
668 		AV *list;
669 	PPCODE:
670 		argv = (char **) malloc((items+1)*sizeof(char *));
671 		argv[0] = "dummy";
672 
673 		for (i = 0; i < items; i++) {
674 		    STRLEN len;
675 		    char *handle= SvPV(ST(i),len);
676 		    /* actually copy the data to make sure possible modifications
677 		       on the argv data does not backfire into perl */
678 		    argv[i+1] = (char *) malloc((strlen(handle)+1)*sizeof(char));
679 		    strcpy(argv[i+1],handle);
680 		}
681 
682                 rrd_clear_error();
683 
684 		data = rrd_list(items+1, argv);
685 
686                 for (i=0; i < items; i++) {
687 		    free(argv[i+1]);
688 		}
689 		free(argv);
690 
691                 if (rrd_test_error())
692 		    XSRETURN_UNDEF;
693 
694 		list = newAV();
695 
696 		ptr = data;
697 		end = strchr(ptr, '\n');
698 
699 		while (end) {
700 		    *end = '\0';
701 		    av_push(list, newSVpv(ptr, 0));
702 		    ptr = end + 1;
703 
704 		    if (strlen(ptr) == 0)
705 			    break;
706 
707 		    end = strchr(ptr, '\n');
708 		}
709 
710 		rrd_freemem(data);
711 
712 		XPUSHs(sv_2mortal(newRV_noinc((SV*)list)));
713