1package RRDp;
2
3=head1 NAME
4
5RRDp - Attach RRDtool from within a perl script via a set of pipes;
6
7=head1 SYNOPSIS
8
9use B<RRDp>
10
11B<RRDp::start> I<path to RRDtool executable>
12
13B<RRDp::cmd>  I<rrdtool commandline>
14
15$answer = B<RRD::read>
16
17$status = B<RRD::end>
18
19B<$RRDp::user>,  B<$RRDp::sys>, B<$RRDp::real>, B<$RRDp::error_mode>, B<$RRDp::error>
20
21=head1 DESCRIPTION
22
23With this module you can safely communicate with the RRDtool.
24
25After every B<RRDp::cmd> you have to issue an B<RRDp::read> command to get
26B<RRDtool>s answer to your command. The answer is returned as a pointer,
27in order to speed things up. If the last command did not return any
28data, B<RRDp::read> will return an undefined variable.
29
30If you import the PERFORMANCE variables into your namespace,
31you can access RRDtool's internal performance measurements.
32
33=over 8
34
35=item  use B<RRDp>
36
37Load the RRDp::pipe module.
38
39=item B<RRDp::start> I<path to RRDtool executable>
40
41start RRDtool. The argument must be the path to the RRDtool executable
42
43=item B<RRDp::cmd> I<rrdtool commandline>
44
45pass commands on to RRDtool. check the RRDtool documentation for
46more info on the RRDtool commands.
47
48=item $answer = B<RRDp::read>
49
50read RRDtool's response to your command. Note that the $answer variable will
51only contain a pointer to the returned data. The reason for this is, that
52RRDtool can potentially return quite excessive amounts of data
53and we don't want to copy this around in memory. So when you want to
54access the contents of $answer you have to use $$answer which dereferences
55the variable.
56
57=item $status = B<RRDp::end>
58
59terminates RRDtool and returns RRDtool's status ...
60
61=item B<$RRDp::user>,  B<$RRDp::sys>, B<$RRDp::real>
62
63these variables will contain totals of the user time, system time and
64real time as seen by RRDtool.  User time is the time RRDtool is
65running, System time is the time spend in system calls and real time
66is the total time RRDtool has been running.
67
68The difference between user + system and real is the time spent
69waiting for things like the hard disk and new input from the perl
70script.
71
72=item B<$RRDp::error_mode> and B<$RRDp::error>
73
74If you set the variable $RRDp::error_mode to the value 'catch' before you run RRDp::read a potential
75ERROR message will not cause the program to abort but will be returned in this variable. If no error
76occurs the variable will be empty.
77
78 $RRDp::error_mode = 'catch';
79 RRDp::cmd qw(info file.rrd);
80 print $RRDp::error if $RRDp::error;
81
82=back
83
84
85=head1 EXAMPLE
86
87 use RRDp;
88 RRDp::start "/usr/local/bin/rrdtool";
89 RRDp::cmd   qw(create demo.rrd --step 100
90               DS:in:GAUGE:100:U:U
91	       RRA:AVERAGE:0.5:1:10);
92 $answer = RRDp::read;
93 print $$answer;
94 ($usertime,$systemtime,$realtime) =  ($RRDp::user,$RRDp::sys,$RRDp::real);
95
96=head1 SEE ALSO
97
98For more information on how to use RRDtool, check the manpages.
99
100=head1 AUTHOR
101
102Tobias Oetiker <tobi@oetiker.ch>
103
104=cut
105#'  this is to make cperl.el happy
106
107use strict;
108use Fcntl;
109use Carp;
110use IO::Handle;
111use IPC::Open2;
112use vars qw($Sequence $RRDpid $VERSION);
113my $Sequence;
114my $RRDpid;
115
116# Prototypes
117
118sub start ($);
119sub cmd (@);
120sub end ();
121sub read ();
122
123$VERSION=1.2030;
124
125sub start ($){
126  croak "rrdtool is already running"
127    if defined $Sequence;
128  $Sequence = 'S';
129  my $rrdtool = shift @_;
130  $RRDpid = open2 \*RRDreadHand,\*RRDwriteHand, $rrdtool,"-"
131    or croak "Can't Start rrdtool: $!";
132  RRDwriteHand->autoflush(); #flush after every write
133  fcntl RRDreadHand, F_SETFL,O_NONBLOCK|O_NDELAY; #make readhandle NON BLOCKING
134  return $RRDpid;
135}
136
137
138sub read () {
139  croak "RRDp::read can only be called after RRDp::cmd"
140    unless $Sequence eq 'C';
141  $RRDp::error = undef;
142  $Sequence = 'R';
143  my $inmask = 0;
144  my $srbuf;
145  my $minibuf;
146  my $buffer;
147  my $nfound;
148  my $timeleft;
149  my $ERR = 0;
150  vec($inmask,fileno(RRDreadHand),1) = 1; # setup select mask for Reader
151  while (1) {
152    my $rout;
153    $nfound = select($rout=$inmask,undef,undef,2);
154    if ($nfound == 0 ) {
155      # here, we could do something sensible ...
156      next;
157    }
158    sysread(RRDreadHand,$srbuf,4096);
159    $minibuf .= $srbuf;
160    while ($minibuf =~ s|^(.+?)\n||s) {
161      my $line = $1;
162      # print $line,"\n";
163      $RRDp::error = undef;
164      if ($line =~  m|^ERROR|) {
165	$RRDp::error_mode eq 'catch' ? $RRDp::error = $line : croak $line;
166	$ERR = 1;
167      }
168      elsif ($line =~ m|^OK u:([\d\.]+) s:([\d\.]+) r:([\d\.]+)|){
169	($RRDp::sys,$RRDp::user,$RRDp::real)=($1,$2,$3);
170	return $ERR == 1 ? undef : \$buffer;
171      } else {
172	$buffer .= $line. "\n";
173      }
174    }
175  }
176}
177
178sub cmd (@){
179  croak "RRDp::cmd can only be called after RRDp::read or RRDp::start"
180    unless $Sequence eq 'R' or $Sequence eq 'S';
181  $Sequence = 'C';
182  my $cmd = join " ", @_;
183  if ($Sequence ne 'S') {
184  }
185  $cmd =~ s/\n/ /gs;
186  $cmd =~ s/\s/ /gs;
187  print RRDwriteHand "$cmd\n";
188}
189
190sub end (){
191  croak "RRDp::end can only be called after RRDp::start"
192    unless $Sequence;
193  close RRDwriteHand;
194  close RRDreadHand;
195  $Sequence = undef;
196  waitpid $RRDpid,0;
197  return $?
198}
199
2001;
201