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