From: oetiker Date: Wed, 23 May 2007 16:08:14 +0000 (+0000) Subject: new trunk based on current 1.2 X-Git-Url: https://git.tokkee.org/?a=commitdiff_plain;h=021678302eec7e4a122f9b128a79aea32b36ccee;p=rrdtool-all.git new trunk based on current 1.2 git-svn-id: svn://svn.oetiker.ch/rrdtool/trunk@1073 a5681a0c-68f1-0310-ab6d-d61299d08faa --- diff --git a/program/00README b/program/00README new file mode 100644 index 00000000..5f197b3f --- /dev/null +++ b/program/00README @@ -0,0 +1,6 @@ +Title: RRDtool +Date: 2005-04-04 +Owner: Tobias Oetiker +Group: Software + +Round Robin Database Tool diff --git a/program/CONTRIBUTORS b/program/CONTRIBUTORS new file mode 100644 index 00000000..aaf7718d --- /dev/null +++ b/program/CONTRIBUTORS @@ -0,0 +1,74 @@ +I would like to thank to following people for helping to +bring RRDtool into existence. + +Alan Lichty +Alan Milligan Python bindings +Alex van den Bogaerdt (rrd_resize.c and more) +Amos Shapira +Andreas Kroomaa +Andrew Turner (LAST consolidator) +Bernard Fischer 64bit stuff and --alt-autoscale-max +Bill Fenner +Blair Zajac +Bruce Campbell +Chin-A-Young +Christophe VG +Christophe Van Ginneken (--no-legend) +Dan Dunn +Dave Bodenstab AT style time in update, tclfixes +David Grimes SQRT/SORT/REV/SHIFT/TREND +David L. Barker xport function bug fixes +Frank Strauss TCL bindings +Henrik Storner functions for min/max values of data in graph +Hermann Hueni (SunOS porting) +Jakob Ilves HPUX 11 +Jeff R. Allen (autoconfigure, portability) +Jeremy Fischer (Makefile changes & RPM builds) +Jesús Couto Fandiño +Joel Becker AIX +Joey Miller php3 and php4 bindings +Jost.Krieger +Kai Siering +Larry Leszczynski +McCreary mccreary with xoanon.colorado.edu +Mike Mitchell +Mike Slifcak many rrdtool-1.1.x fixes +Oleg Cherevko +Otmar Lendl (lots of bugfixes) +Paul Joslin +Peter Speck eps/svg/pdf file format code in rrdtool-1.x +Peter Stamfest initial multi-thread support +Peter Breitenlohner many patches for rrdtool 1.2.x +Philippe.Simonet (NT porting) +Poul-Henning Kamp CDEF enhancements +REIBENSCHUH Alfred AIX +Radoslaw Karas +Rainer Bawidamann +Roman Hoogant +Ronan Mullally +Roger J. Meier (arbitrary linelength in rrdtool) +Russ Wright +Sean Summers (RPM .spec) +Selena M Brewington add_ds +Shane O'Donnell +Simon Leinen +Steen Linden +Stefan Mueller HPUX 11 +Steve Harris AIX portability +Steve Rader (rrd_cgi debugging and LAST) +Terminator rAT +Tobias Weingartner +Tom Crawley (GCC&HP configuration) +Travis Brown +Tuc +Ulf Lilleengen Python binding for 'rrdtool first' +Ulrich Schilling AIX +Wim Heirman --units=si option +Wolfgang Schrimm xport function +Wrolf Courtney (HP-UX) +hendrik visage +Philippe Simonet (Windows Binaries) +Alexander Lucke (lucke with dns-net.de) + of DNS:NET Internet Services (www.dns-net.de) http://rrdtool.org +Hedley Simons +Nicola Worthington diff --git a/program/COPYING b/program/COPYING new file mode 100644 index 00000000..37c6105c --- /dev/null +++ b/program/COPYING @@ -0,0 +1,280 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for non-commercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS diff --git a/program/COPYRIGHT b/program/COPYRIGHT new file mode 100644 index 00000000..ab5c6955 --- /dev/null +++ b/program/COPYRIGHT @@ -0,0 +1,90 @@ + RRDTOOL - Round Robin Database Tool + A tool for fast logging of numerical data graphical display + of this data. + + Copyright (c) 1998-2006 Tobias Oetiker + All rights reserved. + + GNU GPL License + =============== + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + + FLOSS License Exception + ======================= + (Adapted from http://www.mysql.com/company/legal/licensing/foss-exception.html) + + I want specified Free/Libre and Open Source Software ("FLOSS") + applications to be able to use specified GPL-licensed RRDtool + libraries (the "Program") despite the fact that not all FLOSS licenses are + compatible with version 2 of the GNU General Public License (the "GPL"). + + As a special exception to the terms and conditions of version 2.0 of the GPL: + + You are free to distribute a Derivative Work that is formed entirely from + the Program and one or more works (each, a "FLOSS Work") licensed under one + or more of the licenses listed below, as long as: + + 1. You obey the GPL in all respects for the Program and the Derivative + Work, except for identifiable sections of the Derivative Work which are + not derived from the Program, and which can reasonably be considered + independent and separate works in themselves, + + 2. all identifiable sections of the Derivative Work which are not derived + from the Program, and which can reasonably be considered independent and + separate works in themselves, + + 1. are distributed subject to one of the FLOSS licenses listed + below, and + + 2. the object code or executable form of those sections are + accompanied by the complete corresponding machine-readable source + code for those sections on the same medium and under the same FLOSS + license as the corresponding object code or executable forms of + those sections, and + + 3. any works which are aggregated with the Program or with a Derivative + Work on a volume of a storage or distribution medium in accordance with + the GPL, can reasonably be considered independent and separate works in + themselves which are not derivatives of either the Program, a Derivative + Work or a FLOSS Work. + + If the above conditions are not met, then the Program may only be copied, + modified, distributed or used under the terms and conditions of the GPL. + + FLOSS License List + ================== + License name Version(s)/Copyright Date + Academic Free License 2.0 + Apache Software License 1.0/1.1/2.0 + Apple Public Source License 2.0 + Artistic license From Perl 5.8.0 + BSD license "July 22 1999" + Common Public License 1.0 + GNU Library or "Lesser" General Public License (LGPL) 2.0/2.1 + IBM Public License, Version 1.0 + Jabber Open Source License 1.0 + MIT License (As listed in file MIT-License.txt) - + Mozilla Public License (MPL) 1.0/1.1 + Open Software License 2.0 + OpenSSL license (with original SSLeay license) "2003" ("1998") + PHP License 3.0 + Python license (CNRI Python License) - + Python Software Foundation License 2.1.1 + Sleepycat License "1999" + W3C License "2001" + X11 License "2001" + Zlib/libpng License - + Zope Public License 2.0 diff --git a/program/MakeMakefile b/program/MakeMakefile new file mode 100755 index 00000000..c936d002 --- /dev/null +++ b/program/MakeMakefile @@ -0,0 +1,70 @@ +#!/bin/sh +# +# Run this script after the first cvs checkout to build +# makefiles and friends + +PATH="/usr/sepp/bin:$PATH" +export PATH + +vcheck (){ + perl <= 1.5.14" + ERROR=1 +fi + +if vcheck 1.9.5 "automake --version" +then + if vcheck 1.9.5 "automake-1.9 --version" + then + echo "get a copy of GNU automake >= 1.9.5" + ERROR=1 + else + automake=automake-1.9 + aclocal="aclocal-1.9" + for d in /usr/pack/automake-1.9.5-to/share/aclocal-1.9 /usr/share/aclocal-1.9; do + [ -d $d ] && aclocal="$aclocal -I $d" + done + fi +else + automake="automake" + aclocal="aclocal" +# aclocal="aclocal -I /usr/pack/libtool-1.5.14-to/share/aclocal" +fi + +if vcheck 2.59 "autoconf --version" +then + echo "get a copy of GNU autoconf >= 2.59" + ERROR=1 +fi + +if [ $ERROR -ne 0 ] +then + exit 1 +fi + +# cleanup +set -x +find . -name Makefile | grep -v win32 | grep -v netware | xargs rm -f _UNKNONW_ +find . -name "*.la" | xargs rm -f _UNKNOWN_ +find . -name Makefile.in | xargs rm -f _UNKNONW_ +find . -name .libs | xargs rm -r +find . -name .debs | xargs rm -r + +$aclocal +libtoolize --copy --force +autoheader --force +$aclocal +$automake --foreign --add-missing --force-missing --copy +autoconf --force diff --git a/program/Makefile.am b/program/Makefile.am new file mode 100644 index 00000000..4bbab3e1 --- /dev/null +++ b/program/Makefile.am @@ -0,0 +1,50 @@ +## Process this file with automake to produce Makefile.in +RSYNC = rsync --rsh=ssh + +# build the following subdirectories +SUBDIRS = src doc examples bindings + + # the following files are not mentioned in any other Makefile +EXTRA_DIST = COPYRIGHT CHANGES WIN32-BUILD-TIPS.txt TODO CONTRIBUTORS THREADS \ + rrdtool.spec favicon.ico win32/config.h win32/rrd.dsp win32/rrd.vcproj \ + win32/rrdtool.dsp win32/rrdtool.dsw win32/rrdtool.vcproj win32/Makefile \ + win32/rrd_config.h.msvc netware/Makefile + + + +CLEANFILES = config.cache + +# use relaxed rules when building dists +AUTOMAKE_OPTIONS= foreign + +# where we keep local rules for automake + +ACLOCAL_M4= $(top_srcdir)/aclocal.m4 +#AUTOHEADER = @AUTOHEADER@ --localdir=$(top_srcdir)/config +#AUTOCONF = @AUTOCONF@ --localdir=$(top_srcdir)/config + +to-docs: to-versync + (cd doc && $(MAKE) clean && $(MAKE) && $(MAKE) pdf) + +to-dist: to-docs dist + mv $(PACKAGE)-$(VERSION).tar.gz archive + +to-scp: to-dist + cp CHANGES archive/$(PACKAGE)-$(VERSION).tar.gz /home/oetiker/public_html/webtools/rrdtool/pub/ + (cd /home/oetiker/public_html/webtools/rrdtool/pub; rm $(PACKAGE).tar.gz; ln -s $(PACKAGE)-$(VERSION).tar.gz $(PACKAGE).tar.gz) + +# $(RSYNC) CHANGES archive/$(PACKAGE)-$(VERSION).tar.gz tobi@ipn.caida.org:/ipn/web/Tools/RRDtool/pub/ + +site-perl-inst: site-perl-install + +site-perl-install: all bindings/perl-piped/Makefile bindings/perl-shared/Makefile + cd bindings/perl-piped && $(MAKE) install + cd bindings/perl-shared && $(MAKE) install + +site-tcl-install: all + cd bindings/tcl && $(MAKE) tcl-install + +site-python-install: all + cd bindings/python && $(PYTHON) setup.py install + +##END## diff --git a/program/NEWS b/program/NEWS new file mode 100644 index 00000000..156864e7 --- /dev/null +++ b/program/NEWS @@ -0,0 +1,62 @@ +RRDTOOL NEWS +============ +Major Changes between 1.0.x and 1.2.x + +Graphing +-------- + +* rewritten graphics generation based on libart. + - anti-aliased output + - alpha transparency support + - truetype fonts + +* additional graphics formats: EPS, PDF, SVG + +* extended multi-part documentation + +* VDEF support; define and use variables. Find, and use, the + maximum rate seen by rrdtool; compute and show the average + +* Sliding window (trend) analysis + Compute a smoother average, for instance over the last 6 CDPs + +* percentile (95th or other) + Remove peaks, 95 percent of all rates are at or below the + returned value + +Logging +------- +* a second logging interface: rrdtool updatev + Verbose updating of the database; show CPDs being created + +* Aberrant Behavior Detection with Holt-Winters Forecasting + Compare current data with expected data, detect and log when + the rates are outside expected levels + +* COMPUTE data type for artificial data-sources calculating their + input using RPN math and data from the other data-sources. + +Incompatibilities +----------------- +* Colons in COMMENT arguments to rrdtool graph must be escaped with a backslash + +* the --alt-y-mrtg option is gone or rather since 1.2.7 it is back but + without functionality. + +* In pipe mode, rrdtool answers with OK only if it was successful with the + command. Otherwhise the answer will be ERROR... + + +Behind the Scenes +----------------- +* In order to support Holt-Winters and Calculated Datasources, + the rrdtool data format has changed. While the new version of rrdtool can + read files created with rrdtool 1.0.x. It is not possible to read files + created by rrdtool-1.2.x with rrdtool-1.0.x + +* External libraries are not included with rrdtool anymore. This is in line + with todays trend of using shared libraries everywhere. With the exception + of the cgi library most things required by rrdtool will be found on every recent + system. + +* Memory Mapped IO support for faster logging. diff --git a/program/PROJECTS b/program/PROJECTS new file mode 100644 index 00000000..68a5fcfb --- /dev/null +++ b/program/PROJECTS @@ -0,0 +1,43 @@ +NEW RRD DATAFORMAT with Accessor Functions +========================================== + +Interested: + +Tobias Oetiker +Jake Brutlag +- updating rrd_update to use accessor fks + +Plan: + +Encapsulating access to the RRD files through +special accessor functions which provide +access to the datastructures within the +RRDfiles. + +pseudo code by Jake + +For example, here is a current code block from rrd_update.c: + + for (i=0;ids_cnt;i++) { + if(isnan(pdp_new[i])) + rrd.pdp_prep[i].scratch[PDP_unkn_sec_cnt].u_cnt += interval; + else + rrd.pdp_prep[i].scratch[PDP_val].u_val+= pdp_new[i]; + } + +This could read: + + for (i=0 ; i < getDSCount(&rrd) ;i++) { + if(isnan(pdp_new[i])) { + temp = getPDPParam(&rrd, i, PDP_unkn_sec_cnt); + temp.u_cnt += interval; + setPDPParam(&rrd, i, PDP_unkn_sec_cnt, temp); + } else { + temp = getPDPParam(&rrd, i, PDP_val); + temp.u_val += pdp_new[i]; + setPDPParam(&rrd, i, PDP_val, temp); + } + } + + + diff --git a/program/README b/program/README new file mode 100644 index 00000000..cba88fd2 --- /dev/null +++ b/program/README @@ -0,0 +1,75 @@ +Round Robin Database Tools +========================== + +It is pretty easy to gather status information from all sorts of things, +ranging from the temperature in your office to the number of octets which +have passed through the FDDI interface of your router. But it is not so +trivial to store this data in a efficient and systematic manner. This is +where RRDtool kicks in. It lets you log and analyze the data you gather from +all kinds of data-sources (DS). The data analysis part of RRDtool is based +on the ability to quickly generate graphical representations of the data +values collected over a definable time period. + + +To compile: +----------- + +check out the instructions in doc/rrdbuild.txt + +Getting Started: +---------------- + +Either after compiling or after installing you can try the example +RRDtool applications in the examples directory. + +To learn: +--------- + +Read the documentation in the doc directory. Start of with +RRDtool. All documents are available as html and as ASCII text. + +If you are looking for a more slow paced introduction, make sure to read +Alex van den Bogaerdt's rrdtutorial which is also available from the doc +directory. Also read his cdeftutorial and Steve Rader's rpntutorial. + +If you want to know about the format of the log files check +src/rrd_format.h there are a lot of comments in there ... + +How to make Tobi happy: +----------------------- + +If you want to show your appreciation for RRDtool you could make me happy +by going to http://tobi.oetiker.ch/wish and ordering a CD from +my CD wish list ... + + +How to keep in touch: +--------------------- + +There are 3 Mailing lists for RRDtool: + +rrd-announce LOW volume RRDtool Announcements List (Only Stable Releases) +rrd-users For discussion amongst people who use RRDtool in their applications +rrd-developers For people who actually HACK RRDtool code + +To subscribe to send a message with the subject 'subscribe' +to -request@lists.oetiker.ch + +Note, that postings to rrd-announce will always be cross-posted +to rrd-users and rrd-developers as well. + +To Contribute: +-------------- + +Contributed feature and bug patches are most welcome. But please +send complete patches. A complete patch patches the CODE as well +as the CHANGES, CONTRIBUTORS and the POD files. + +Use GNU diff --unified --recursive olddir newdir to build your patches. + +The latest Version: +------------------- +Is available from http://oss.oetiker.ch/rrdtool/ + + +Tobias Oetiker diff --git a/program/THREADS b/program/THREADS new file mode 100644 index 00000000..b0481638 --- /dev/null +++ b/program/THREADS @@ -0,0 +1,60 @@ +In order to use the librrd in multi-threaded programs you must: + + * Link with librrd_th instead of with librrd + * Use the *_r function instead or the *-functions + * Never use non *_r functions unless it is explicitly documented that the + function is tread-safe + +Every thread SHOULD call rrd_get_context() before the first call to +any librrd function in order to set up thread specific data. This is +not strictly required, but it is the only way to test if memory +allocation can be done by this function. Otherwise the program may die +with a SIGSEGV in a low-memory situation. + + +IMPORTANT NOTE FOR RRD CONTRIBUTORS: + +Some precautions must be followed when developing rrd from now on: + +* Only use thread-safe functions in library code. Many often used libc + functions aren't thread-safe. Take care if you want to use any of + the following functions: + + + direct strerror calls must be avoided: use rrd_strerror instead, + it provides a per-thread error message + + the getpw*, getgr*, gethost* function families (and some more get* + functions): use the *_r variants + + Time functions: asctime, ctime, gmtime, localtime: use *_r variants + + strtok: use strtok_r + + tmpnam: use tmpnam_r + + many other (lookup documentation) + +As an aide(?) a header file named "rrd_is_thread_safe.h" is provided +that works with the GNU C-preprocessor to "poison" some of the most +common non-thread-safe functions using the "#pragma GCC poison" +directive. Just include this header in source files you want to keep +thread-safe. + +* Do not introduce global variables! + + If you really, really have to use a global variable you may add a + new field to the rrd_context structure and modify rrd_error.c, + rrd_thread_safe.c and rrd_non_thread_safe.c + +* Do not use "getopt" or "getopt_long" in *_r (directly or indirectly) + + getopt uses global variables and behaves badly in a multithreaded + application when called concurrently. Instead provide a *_r function + taking all options as function parameters. You may provide argc and + **argv arguments for variable length argument lists. See + rrd_update_r as an example. + +* Do not use the parsetime function! + + It uses lots of global vars. You may use it in functions not + designed to be thread-safe like functions wrapping the _r version of some + operation (eg. rrd_create, but not in rrd_create_r) + +WIN32 Platform Note (added 04/01/03): + +Both rrdtool.vcproj (MSVC++ 7.0) and rrd.dsw (MSVC++ 6.0) are configured to compile with rrd_thread_safe_nt.c. diff --git a/program/TODO b/program/TODO new file mode 100644 index 00000000..baa64f7f --- /dev/null +++ b/program/TODO @@ -0,0 +1,68 @@ +What has to be done before the 1.2.0 Release +-------------------------------------------- +* Sync Docs with reality (there are things described which have not been + implemented) + +* Write Changes Document with all new features + +* Update Tutorials for new Release. + +Future Ideas +------------ +reverse order of stacked graph entries prior to plotting ... this is to make +plotting order more naturally fit with the ordering of the legend ... + +make it possible to define order of legend items independant of their order +on the commandline ... + +use XDR to write architecture independant date + +have drawing methode modifier for the LINE, AREA, STACK functions which allows +to select wether the data points should be presented as stepes (todays state) +or connected with lines. + +LINEx:ds[#color][/method]:description + +be more clever in the fetch function when no complete data coverage for the +desired area is possible. + +Let the user define the base resolution of the graph independently of +the pixel resolution. If it is smaller than one pixel it will simply +be ignored + +allow sub second precision for the step definition + +modularise consolidation and acquisition functions + +Allow the user to define vertical scaling of graphs in a more flexible way. +some thing like --inner-range 0-1000 and --outer-range 0-10000 which means +that the grapher can choose its vertical scaling in the given range ... +this would replace upper and lower limit + +make the inclusion of G?PRINT like output in to the lables of other graph +elements possible + +add axis on the right and on top of the graph ... + +add configurable counter wrap + +build derivatives while plotting + +show trend by building moving average and represent it as an arrow + +offer side by side tiny box plots for each succesive hour, or some other +means of seeing the variation rather than just the mean level + +circular periodic plots ... (polar) + +analyse data in non time domain ... + +add smothing functions to grapher, using rolling average ... + +make VRULE and HRULE with 123 versions ... + +have configurable role-over limits for counters + +align data points not to GMT but some free offset + +allow starting through symlinks called rrdcreate rrdtune and the like diff --git a/program/WIN32-BUILD-TIPS.txt b/program/WIN32-BUILD-TIPS.txt new file mode 100644 index 00000000..7d731c83 --- /dev/null +++ b/program/WIN32-BUILD-TIPS.txt @@ -0,0 +1,335 @@ +Compiling RRDtool 1.1.x on Win32 with Microsoft Visual C++: +--------------------------------------------------------------- +5/1/05 Tobi +to help windows deal with the reentrant versions of many unix +calls link with win32comp.c + +4/10/05 Tobi +The windows implementation of strftime does not seem to support +the ISO 8601 week number (%V) I have therfore included the file +strftime.[ch] which provides strftime_ ... if you compile rrdtool +with -Dstrftime=_strftime and link strftime.o then you will +get propper support for %V. + +7/29/04 Jake Brutlag + +As of Jan 2004, code for libraries utilized by rrdtool +(png, libart, freetype, and zlib) is no longer distributed with +rrdtool. This requires some changes to the compile process on +Win32. The solution described here is to compile rrdtool to +link against these libraries dynamically. There is an advantage +to this approach: namely the rrdtool distribution doesn't have to +worry about how to compile these libraries on Win32. In theory, +since others already provide and maintain Win32 binaries for these +libraries the users don't have to worry about how to compile them +either. The disadvantage of this approach is that the DLLs for +these libraries must be available on the hosts where rrdtool will run. + +Here are step by step instructions for compiling rrdtool.exe and +the perl shared library (RRDS.dll) with Microsoft Visual C++ 6.0. +(1) Download libraries rrdtool depends on from GnuWin32: +http://gnuwin32.sourceforge.net/ +For freetype, libpng, and zlib download the "Complete Package"; each of +these will be a self-extracting self-installing executable. +For libart, download both the "Binaries" and "Developer Files" packages. +Unfortunately at this time GnuWin32 doesn't provide the "Complete Package" +installer for libart. Perhaps by the time you are following these +instructions GnuWin32 will have a "Complete Package" for libart. +(2) Install the GnuWin32 libraries by running the executables for freetype, +libpng, and zlib. These instructions and the Visual C++ project files +distributed with rrdtool assume that you will use the default install +location: C:\Program Files\GnuWin32. Extract the two zip files for libart, +libart-2.3.3-bin.zip and libart-2.3.3-1-lib.zip into the GnuWin32 directory; +the appropriate libart files will be added to the include, lib, and bin +subdirectories. +(3) Add C:\Program Files\GnuWin32\bin to the PATH (Control Panel -> +System -> Advanced -> Environment Variables). +(4) Start Microsoft Visual C++ 6.0. Load the workspace file, rrdtool.dsw, +from the src subdirectory of your rrdtool code directory. +(5) Compile the Release build of the rrdtool project (since rrdtool depends +on the rrd project, the rrd library will also be compiled). At this +time, the compile will fail in zconf.h, a zlib header file. The problem +is a preprocessor directive that loads unistd.h. Open zconf.h in VC++ +(this file is in C:\Program Files\GnuWin32\include) and find the following +code block: + +#if 1 /* HAVE_UNISTD_H -- this line is updated by ./configure */ +# include /* for off_t */ +# include /* for SEEK_* and off_t */ +# ifdef VMS +# include /* for off_t */ +# endif +# define z_off_t off_t +#endif + +Change it to reads as follows (this is code from zlib-1.1.4): + +#if HAVE_UNISTD_H +# include /* for off_t */ +# include /* for SEEK_* and off_t */ +# ifdef VMS +# include /* for off_t */ +# endif +# define z_off_t off_t +#endif + +Note that it is actually just a one line change. Save the file and +recompile rrdtool. By the time you are following these instructions +this issue with zconf.h may be resolved. +(6) At this point, you can run the executable rrdtool.exe in the +src\toolrelease subdirectory. Note that if you wish to run rrdtool +on other machines, you will need the following DLLs installed (on the +path) on those machines: +zlib1.dll +libpng12.dll +libart_lgpl.dll +freetype6.dll +msvcrt.dll +The names of the first four DLLs might vary from what is listed here +depending on the versions of the packages you downloaded from GnuWin32. +The fifth DLL, msvcrt.dll, is a system DLL for most versions of Windows. +If you are running on old version of Windows, you can install/upgrade to +IE4.0 to get this DLL. +(7) To compile the perl-shared library, open a Command Prompt (DOS box) +and cd to the bindings\perl-shared subdirectory. +(8) Run vcvars32.bat; this batch file, in your vc98\bin directory will +set necessary environment options for command line compiling. +(9) In bindings\perl-shared, run +perl ntmake.pl +nmake +nmake test +If nmake test succeeds, you are good to go. RRDs.dll is in +blib\arch\auto\RRDs. If you plan to install via the Active State ppm +tool, tar and gzip the blib directory. You can use the RRDs.ppd file +in bindings\perl-shared directory. Remember that as in the case of +rrdtool.exe you will need the DLLs listed in (6) on the machine where +you are going to use RRDs.dll. + +Microsoft Visual C++ 7.1 (.NET 2003): + +Unfortunately, this is more difficult than with VC++ 6.0. The problem +is that by default the C runtime dll for VC++ 7.1 is msvcr71.dll rather +than msvcrt.dll. The GnuWin32 library binaries are all compiled +to use msvcrt.dll and you can't mix msvcr71.dll and msvcrt.dll in the +same process. One option is to download the source code for the libraries +(available from http://gnuwin32.sourceforge.net) recompile them with +VC++ 7.l. Then all the components will use msvcr71.dll. Once you are +going to go this route, you can also use static multi-threaded libraries +and use static linking between rrdtool (or RRDs.dll) and its dependencies. + +To use the GnuWin32 library binaries, you need to trick VC++ 7.1 into +compiling rrdtool to use the older msvcrt.dll. Follow steps (1) - (3) +as above, then: +(4) Obtain a different version of the msvcrt.lib import library that +is compatible with vc7 and points to msvcrt.dll: +msvcrtlib_for_vc7.zip from http://xchat.org/win32/testing +Backup msvcrt.lib in your vc7\lib directory +(\Program Files\Microsoft Visual Studio .NET 2003\vc7\lib) +Then extract the msvcrt.lib from the zip file into the vc7\lib directory. +WARNING: Use this msvcrt.lib at your own risk! This is not a Microsoft +supplied file nor a file supported by anyone associated with rrdtool. +(5) Start Microsoft Visual C++ 7.1. Load the solution file, rrdtool.sln, +from the src subdirectory of your rrdtool code directory. Edit zconf.h, +as needed, as described under (5) above. Compile the release build of +the rrdtool project. +Proceed with steps (6) - (9) as above, if you are using/picking up +the wrong msvcrt.lib import library then nmake test for perl-shared +will fail. + +Note: it is possible in the future that GnuWin32 will provide Win32 +binaries that utilize msvcr71.dll rather than msvcrt.dll. + +5/14/02 Jake Brutlag + +These notes share some insight I gained compiling 1.1.x with +MS Visual C++ 6.0 (using project files). This information may or +may not be accurate at the time you are reading this. + +(1) freetype and rrdtool cannot use precompiled headers (which are +enabled by default for MSVC++ projects). MSVC++ 6.0 does not +support precompiled headers if #include directives contain MACROS. +(2) Compile Release build with Default optimization, not the +Maximize Speed optimization. I encountered some strange errors +(related to argument processing for complex commands like graph-- +perhaps the getopt stuff is too blame) with Maximize Speed. +(3) libart relies upon config.h (ostensibly generated by the +configure script-- but of course not on Win32 platforms). ..\..\confignt +(which contains a static Win32 version of config.h) should be on +the include path. +(4) Fonts are located in the %windir%\fonts, so the default font +is c:\winnt\fonts\cour.ttf. (6/19/02) At Kerry Calvert's suggestion +this setting was moved to confignt\config.h. +(5) libart requires a custom build step to generate art_config.h; this +is done manually via the commands: +cl -I..\..\confignt gen_art_config.c +gen_art_config.exe > art_config.h + +Currently, to compile rrd.lib and rrdtool.exe using +the MSVC++ project files, first start MSVC++ 6.0. Open the rrdtool +workspace (rrdtool.dsw in the src directory). The active project/ +configuration should be rrdtool-Win32 Release. Select Rebuild All +from the Build menu. The static link library (rrd.lib) will +be generated in src\release directory and executable will be generated +in the src\toolrelease directory. + +Compiling RRDtool on NT ... work in progress +--------------------------------------------------------------- + by Tamas Kovacshazy (khazy@mit.bme.hu) + +Persisting Problems with the current NT port: + +Unfortunately, the RRD perl modules does not work with Perl +(ActivePerl) using the current distribution. + +The RRD shared perl module can be compiled after some +modification... + +Follow these steps: + +0. Install perl if you do not have it! + Visit http://www.ActiveState.com/pw32/ for a complete distribution. + +1. Copy ..\gd1.2\release\gd.lib to ..\gd1.2\ +2. Copy ..\src\release\rrd.lib to ..\src +3. perl Makefile.pl + +In this step the system complains about something I do not +understand. The error message is the following: + +Note (probably harmless): No library found for '-lm' + +Is a library missing? But it does not stop with an error... + +4. nmake test (You must have Visual C++ on the machine!) + +After these steps it generates the test files (svgs and rrds), +and they seem to be good. + +The real problem in the shared perl modul is the following: + +I do not know how this installation stuff works. The problem is +that the installation stuff looks for the gd.lib and the +rrd.lib in the ..\gd1.2 and ..\src directory. The UNIX compile +puts the files into these directories, but the NT compile does +not. + +It is all for today, + +khazy + +Tamas Kovacshazy E-mail: khazy@mit.bme.hu +WWW: http://www.mit.bme.hu/~khazy +Technical University of Budapest +Department of Measurement and Information Systems + + +Compiling RRDtool 1.2.x on Win32 with MingW32 gcc: +--------------------------------------------------------------- + +1. Obtain and install the current version of the MingW package. + + http://www.mingw.org/download.shtml + + In the MinGW set you will need the gcc and binutils as a minimum. + +2. Obtain either of the following awk versions and install in a directory + on your System Path: + + - awk.exe + + http://cm.bell-labs.com/cm/cs/awkbook/index.html + + Note: This version has no dependencies to other libs. + + - gawk.exe (GnuWin32 version) + + http://gnuwin32.sourceforge.net/packages/gawk.htm + + Note: Also fetch the dependant libraries for it from the same page. + +3. If you plan to create a 'distribution' release of the RRD Tools, the + Makefile.Win32 will copy all the needed files to an output directory and + then zip the entire directory. A suitable zip utility can be obtained here: + + http://www.info-zip.org/ + + Install in a directory on your System Path. + +4. Obtain the following libraries, ideally install them all under a common + directory: + + = zlib + + http://oss.oetiker.ch/rrdtool/pub/libs/zlib-1.2.3.tar.gz + http://www.zlib.net/ + + = libpng + + http://oss.oetiker.ch/rrdtool/pub/libs/libpng-1.2.12.tar.gz + http://libpng.sourceforge.net/ + + = freetype + + http://oss.oetiker.ch/rrdtool/pub/libs/freetype-2.2.1.tar.gz + http://freetype.sourceforge.net/index2.html + + = libart_lgpl + + http://oss.oetiker.ch/rrdtool/pub/libs/libart_lgpl-2.3.17.tar.gz + http://www.levien.com/libart/ + + Note: libart_lgpl needs a special tweak because the archive contains + only the base directory, but the libart headers are usually included with + a directory prefix; therefore create a subfolder 'libart_lgpl' and move + all files into this subfolder. + +5. Set up for DOS environment. + + Add MingW\bin and MSYS\bin directories to your System path. + + If the libraries share a common directory set the following environment var: + + set LIBBASE= + e.g set LIBBASE=C:\Libraries + + If the libraries are scattered, set the following environment vers: + + set ZLIBSDK= + e.g set ZLIBSDK=C:\mytest\zlib-1.2.3 + set LIBPNG= + set LIBFT2= + set LIBART= + + If using the Gnu Awk (gawk.exe), edit the Makefile.Win32 and change the line: + + AWK = awk + + to + + AWK = gawk + +6. Compile the project. + + All dependent libs are statically linked in. This has the benefit that the + binaries do not depend on any other DLLs. + In order to build the static freetype lib enter the freetype base directory + and type 'make'. If everything is fine a message appears that gcc is detected, + and that you should again type 'make'. Follow that in order to build freetype. + All other libs are build from the sources with the RRDTool Makefile.Win32. + + Switch to the RRDTOOL .\src directory. Then: + + make -f Makefile.Win32 help + + to see the build options, or + + make -f Makefile.Win32 all + + should build the entire package. + +6. Happy Graphing! + + +written by normw & gk. + + diff --git a/program/acinclude.m4 b/program/acinclude.m4 new file mode 100644 index 00000000..b71fe8e8 --- /dev/null +++ b/program/acinclude.m4 @@ -0,0 +1,548 @@ +dnl Helper Functions for the RRDtool configure.ac script +dnl +dnl this file gets included into aclocal.m4 when runnning aclocal +dnl +dnl +dnl +dnl Check for the presence of a particular library and its header files +dnl if this check fails set the environment variable EX_CHECK_ALL_ERR to YES +dnl and prints out a helful message +dnl +dnl +dnl EX_CHECK_ALL(library, function, header, pkgconf name, tested-version, homepage, cppflags) +dnl $1 $2 $3 $4 $5 $6 $7 +dnl +dnl +AC_DEFUN([EX_CHECK_ALL], +[ + AC_LANG_PUSH(C) + EX_CHECK_STATE=NO + ex_check_save_LIBS=${LIBS} + ex_check_save_CPPFLAGS=${CPPFLAGS} + ex_check_save_LDFLAGS=${LDFLAGS} + if test "x$7" != "x"; then + CPPFLAGS="$CPPFLAGS -I$7" + fi + dnl try compiling naked first + AC_CHECK_LIB($1,$2, [ + AC_CHECK_HEADER($3,[LIBS="-l$1 ${LIBS}";EX_CHECK_STATE=YES],[])],[]) + if test $EX_CHECK_STATE = NO; then + dnl now asking pkg-config for help + AC_CHECK_PROGS(PKGCONFIG,[pkg-config],no) + if test "$PKGCONFIG" != "no"; then + if $PKGCONFIG --exists $4; then + CPPFLAGS=${CPPFLAGS}" "`$PKGCONFIG --cflags $4` + LDFLAGS=${LDFLAGS}" "`$PKGCONFIG --libs-only-L $4` + LDFLAGS=${LDFLAGS}" "`$PKGCONFIG --libs-only-other $4` + LIBS=${LIBS}" "`$PKGCONFIG --libs-only-l $4` + dnl remove the cached value and test again + unset ac_cv_lib_$1_$2 + AC_CHECK_LIB($1,$2,[ + unset ac_cv_header_`echo $3 | sed ['s/[^a-zA-Z0-9_]/_/g;s/^[0-9]/_/']` + AC_CHECK_HEADER($3,[EX_CHECK_STATE=YES],[]) + ],[]) + else + AC_MSG_WARN([ +---------------------------------------------------------------------------- +* I found a copy of pkgconfig, but there is no $4.pc file around. + You may want to set the PKG_CONFIG_PATH variable to point to its + location. +---------------------------------------------------------------------------- + ]) + fi + fi + fi + + if test ${EX_CHECK_STATE} = NO; then + AC_MSG_WARN([ +---------------------------------------------------------------------------- +* I could not find a working copy of $4. Check config.log for hints on why + this is the case. Maybe you need to set LDFLAGS and CPPFLAGS appropriately + so that compiler and the linker can find lib$1 and its header files. If + you have not installed $4, you can get it either from its original home on + + $6 + + You can find also find an archive copy on + + http://oss.oetiker.ch/rrdtool/pub/libs + + The last tested version of $4 is $5. + + LIBS=$LIBS + LDFLAGS=$LDFLAGS + CPPFLAGS=$CPPFLAGS + +---------------------------------------------------------------------------- + ]) + EX_CHECK_ALL_ERR=YES + LIBS="${ex_check_save_LIBS}" + CPPFLAGS="${ex_check_save_CPPFLAGS}" + LDFLAGS="${ex_check_save_LDFLAGS}" + fi + AC_LANG_POP(C) +] +) + +dnl +dnl Ptherad check from http://autoconf-archive.cryp.to/acx_pthread.m4 +dnl +dnl @synopsis ACX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]]) +dnl +dnl This macro figures out how to build C programs using POSIX threads. +dnl It sets the PTHREAD_LIBS output variable to the threads library and +dnl linker flags, and the PTHREAD_CFLAGS output variable to any special +dnl C compiler flags that are needed. (The user can also force certain +dnl compiler flags/libs to be tested by setting these environment +dnl variables.) +dnl +dnl Also sets PTHREAD_CC to any special C compiler that is needed for +dnl multi-threaded programs (defaults to the value of CC otherwise). +dnl (This is necessary on AIX to use the special cc_r compiler alias.) +dnl +dnl NOTE: You are assumed to not only compile your program with these +dnl flags, but also link it with them as well. e.g. you should link +dnl with $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS +dnl $LIBS +dnl +dnl If you are only building threads programs, you may wish to use +dnl these variables in your default LIBS, CFLAGS, and CC: +dnl +dnl LIBS="$PTHREAD_LIBS $LIBS" +dnl CFLAGS="$CFLAGS $PTHREAD_CFLAGS" +dnl CC="$PTHREAD_CC" +dnl +dnl In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute +dnl constant has a nonstandard name, defines PTHREAD_CREATE_JOINABLE to +dnl that name (e.g. PTHREAD_CREATE_UNDETACHED on AIX). +dnl +dnl ACTION-IF-FOUND is a list of shell commands to run if a threads +dnl library is found, and ACTION-IF-NOT-FOUND is a list of commands to +dnl run it if it is not found. If ACTION-IF-FOUND is not specified, the +dnl default action will define HAVE_PTHREAD. +dnl +dnl Please let the authors know if this macro fails on any platform, or +dnl if you have any other suggestions or comments. This macro was based +dnl on work by SGJ on autoconf scripts for FFTW (www.fftw.org) (with +dnl help from M. Frigo), as well as ac_pthread and hb_pthread macros +dnl posted by Alejandro Forero Cuervo to the autoconf macro repository. +dnl We are also grateful for the helpful feedback of numerous users. +dnl +dnl @category InstalledPackages +dnl @author Steven G. Johnson +dnl @version 2005-01-14 +dnl @license GPLWithACException + +AC_DEFUN([ACX_PTHREAD], [ +AC_REQUIRE([AC_CANONICAL_HOST]) +AC_LANG_PUSH(C) +acx_pthread_ok=no + +# We used to check for pthread.h first, but this fails if pthread.h +# requires special compiler flags (e.g. on True64 or Sequent). +# It gets checked for in the link test anyway. + +# First of all, check if the user has set any of the PTHREAD_LIBS, +# etcetera environment variables, and if threads linking works using +# them: +if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then + save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + save_LIBS="$LIBS" + LIBS="$PTHREAD_LIBS $LIBS" + AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS]) + AC_TRY_LINK_FUNC(pthread_join, acx_pthread_ok=yes) + AC_MSG_RESULT($acx_pthread_ok) + if test x"$acx_pthread_ok" = xno; then + PTHREAD_LIBS="" + PTHREAD_CFLAGS="" + fi + LIBS="$save_LIBS" + CFLAGS="$save_CFLAGS" +fi + +# We must check for the threads library under a number of different +# names; the ordering is very important because some systems +# (e.g. DEC) have both -lpthread and -lpthreads, where one of the +# libraries is broken (non-POSIX). + +# Create a list of thread flags to try. Items starting with a "-" are +# C compiler flags, and other items are library names, except for "none" +# which indicates that we try without any flags at all, and "pthread-config" +# which is a program returning the flags for the Pth emulation library. + +acx_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config" + +# The ordering *is* (sometimes) important. Some notes on the +# individual items follow: + +# pthreads: AIX (must check this before -lpthread) +# none: in case threads are in libc; should be tried before -Kthread and +# other compiler flags to prevent continual compiler warnings +# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h) +# -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able) +# lthread: LinuxThreads port on FreeBSD (also preferred to -pthread) +# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads) +# -pthreads: Solaris/gcc +# -mthreads: Mingw32/gcc, Lynx/gcc +# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it +# doesn't hurt to check since this sometimes defines pthreads too; +# also defines -D_REENTRANT) +# pthread: Linux, etcetera +# --thread-safe: KAI C++ +# pthread-config: use pthread-config program (for GNU Pth library) + +case "${host_cpu}-${host_os}" in + *solaris*) + + # On Solaris (at least, for some versions), libc contains stubbed + # (non-functional) versions of the pthreads routines, so link-based + # tests will erroneously succeed. (We need to link with -pthread or + # -lpthread.) (The stubs are missing pthread_cleanup_push, or rather + # a function called by this macro, so we could check for that, but + # who knows whether they'll stub that too in a future libc.) So, + # we'll just look for -pthreads and -lpthread first: + + acx_pthread_flags="-pthread -pthreads pthread -mt $acx_pthread_flags" + ;; +esac + +if test x"$acx_pthread_ok" = xno; then +for flag in $acx_pthread_flags; do + + case $flag in + none) + AC_MSG_CHECKING([whether pthreads work without any flags]) + ;; + + -*) + AC_MSG_CHECKING([whether pthreads work with $flag]) + PTHREAD_CFLAGS="$flag" + ;; + + pthread-config) + AC_CHECK_PROG(acx_pthread_config, pthread-config, yes, no) + if test x"$acx_pthread_config" = xno; then continue; fi + PTHREAD_CFLAGS="`pthread-config --cflags`" + PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`" + ;; + + *) + AC_MSG_CHECKING([for the pthreads library -l$flag]) + PTHREAD_LIBS="-l$flag" + ;; + esac + + save_LIBS="$LIBS" + save_CFLAGS="$CFLAGS" + LIBS="$PTHREAD_LIBS $LIBS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + + # Check for various functions. We must include pthread.h, + # since some functions may be macros. (On the Sequent, we + # need a special flag -Kthread to make this header compile.) + # We check for pthread_join because it is in -lpthread on IRIX + # while pthread_create is in libc. We check for pthread_attr_init + # due to DEC craziness with -lpthreads. We check for + # pthread_cleanup_push because it is one of the few pthread + # functions on Solaris that doesn't have a non-functional libc stub. + # We try pthread_create on general principles. + AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include ]], [[pthread_t th; pthread_join(th, 0); + pthread_attr_init(0); pthread_cleanup_push(0, 0); + pthread_create(0,0,0,0); pthread_cleanup_pop(0); ]])],[acx_pthread_ok=yes],[]) + + LIBS="$save_LIBS" + CFLAGS="$save_CFLAGS" + + AC_MSG_RESULT($acx_pthread_ok) + if test "x$acx_pthread_ok" = xyes; then + break; + fi + + PTHREAD_LIBS="" + PTHREAD_CFLAGS="" +done +fi + +# Various other checks: +if test "x$acx_pthread_ok" = xyes; then + save_LIBS="$LIBS" + LIBS="$PTHREAD_LIBS $LIBS" + save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + + # Detect AIX lossage: JOINABLE attribute is called UNDETACHED. + AC_MSG_CHECKING([for joinable pthread attribute]) + attr_name=unknown + for attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do + AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include ]], [[int attr=$attr;]])],[attr_name=$attr; break],[]) + done + AC_MSG_RESULT($attr_name) + if test "$attr_name" != PTHREAD_CREATE_JOINABLE; then + AC_DEFINE_UNQUOTED(PTHREAD_CREATE_JOINABLE, $attr_name, + [Define to necessary symbol if this constant + uses a non-standard name on your system.]) + fi + + AC_MSG_CHECKING([if more special flags are required for pthreads]) + x_rflag=no + case "${host_cpu}-${host_os}" in + *-aix* | *-freebsd* | *-darwin*) x_rflag="-D_THREAD_SAFE";; + *solaris* | *-osf* | *-hpux*) x_rflag="-D_REENTRANT";; + *-linux*) + if test x"$PTHREAD_CFLAGS" = "x-pthread"; then + # For Linux/gcc "-pthread" implies "-lpthread". We need, however, to make this explicit + # in PTHREAD_LIBS such that a shared library to be built properly depends on libpthread. + PTHREAD_LIBS="-lpthread $PTHREAD_LIBS" + fi;; + esac + AC_MSG_RESULT(${x_rflag}) + if test "x$x_rflag" != xno; then + PTHREAD_CFLAGS="$x_rflag $PTHREAD_CFLAGS" + fi + + LIBS="$save_LIBS" + CFLAGS="$save_CFLAGS" + + # More AIX lossage: must compile with cc_r + AC_CHECK_PROG(PTHREAD_CC, cc_r, cc_r, ${CC}) +else + PTHREAD_CC="$CC" +fi + +AC_SUBST(PTHREAD_LIBS) +AC_SUBST(PTHREAD_CFLAGS) +AC_SUBST(PTHREAD_CC) + +# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: +if test x"$acx_pthread_ok" = xyes; then + ifelse([$1],,AC_DEFINE(HAVE_PTHREAD,1,[Define if you have POSIX threads libraries and header files.]),[$1]) + : +else + acx_pthread_ok=no + $2 +fi +AC_LANG_POP(C) +])dnl ACX_PTHREAD + + +dnl +dnl determine how to get IEEE math working +dnl AC_IEEE(MESSAGE, set rd_cv_ieee_[var] variable, INCLUDES, +dnl FUNCTION-BODY, [ACTION-IF-FOUND [,ACTION-IF-NOT-FOUND]]) +dnl + +dnl substitute them in all the files listed in AC_OUTPUT +AC_SUBST(PERLFLAGS) + +AC_DEFUN([AC_IEEE], [ +AC_MSG_CHECKING([if IEEE math works $1]) +AC_CACHE_VAL([rd_cv_ieee_$2], +[AC_RUN_IFELSE([AC_LANG_SOURCE([[$3 + +#if HAVE_MATH_H +# include +#endif + +#if HAVE_FLOAT_H +# include +#endif + +#if HAVE_IEEEFP_H +# include +#endif + +#if HAVE_FP_CLASS_H +# include +#endif + +/* Solaris */ +#if (! defined(HAVE_ISINF) && defined(HAVE_FPCLASS)) +# define HAVE_ISINF 1 +# define isinf(a) (fpclass(a) == FP_NINF || fpclass(a) == FP_PINF) +#endif + +/* solaris 10 it defines isnan such that only forte can compile it ... bad bad */ +#if (defined(HAVE_ISNAN) && defined(isnan) && defined(HAVE_FPCLASS)) +# undef isnan +# define isnan(a) (fpclass(a) == FP_SNAN || fpclass(a) == FP_QNAN) +#endif + +/* Digital UNIX */ +#if (! defined(HAVE_ISINF) && defined(HAVE_FP_CLASS) && defined(HAVE_FP_CLASS_H)) +# define HAVE_ISINF 1 +# define isinf(a) (fp_class(a) == FP_NEG_INF || fp_class(a) == FP_POS_INF) +#endif + +/* AIX */ +#if (! defined(HAVE_ISINF) && defined(HAVE_CLASS)) +# define HAVE_ISINF 1 +# define isinf(a) (class(a) == FP_MINUS_INF || class(a) == FP_PLUS_INF) +#endif + +#if (! defined(HAVE_ISINF) && defined(HAVE_FPCLASSIFY) && defined(FP_PLUS_INF) && defined(FP_MINUS_INF)) +# define HAVE_ISINF 1 +# define isinf(a) (fpclassify(a) == FP_MINUS_INF || fpclassify(a) == FP_PLUS_INF) +#endif + +#if (! defined(HAVE_ISINF) && defined(HAVE_FPCLASSIFY) && defined(FP_INFINITE)) +# define HAVE_ISINF 1 +# define isinf(a) (fpclassify(a) == FP_INFINITE) +#endif + +#include +int main(void){ + double rrdnan,rrdinf,rrdc,rrdzero; + $4; + /* some math to see if we get a floating point exception */ + rrdzero=sin(0.0); /* don't let the compiler optimize us away */ + rrdnan=0.0/rrdzero; /* especially here */ + rrdinf=1.0/rrdzero; /* and here. I want to know if it can do the magic */ + /* at run time without sig fpe */ + rrdc = rrdinf + rrdnan; + rrdc = rrdinf / rrdnan; + if (! isnan(rrdnan)) {printf ("not isnan(NaN) ... "); return 1;} + if (rrdnan == rrdnan) {printf ("nan == nan ... "); return 1;} + if (! isinf(rrdinf)) {printf ("not isinf(oo) ... "); return 1;} + if (! isinf(-rrdinf)) {printf ("not isinf(-oo) ... "); return 1;} + if (! rrdinf > 0) {printf ("not inf > 0 ... "); return 1;} + if (! -rrdinf < 0) {printf ("not -inf < 0 ... "); return 1;} + return 0; + }]])],[rd_cv_ieee_$2=yes],[rd_cv_ieee_$2=no],[:])]) +dnl these we run regardles is cached or not +if test x${rd_cv_ieee_$2} = "xyes"; then + AC_MSG_RESULT(yes) + $5 +else + AC_MSG_RESULT(no) + $6 +fi + +]) + +AC_DEFUN([AC_FULL_IEEE],[ +AC_LANG_PUSH(C) +_cflags=${CFLAGS} +AC_IEEE([out of the box], works, , , , + [CFLAGS="$_cflags -ieee" + AC_IEEE([with the -ieee switch], switch, , , , + [CFLAGS="$_cflags -qfloat=nofold" + AC_IEEE([with the -qfloat=nofold switch], nofold, , , , + [CFLAGS="$_cflags -w -qflttrap=enable:zerodivide" + AC_IEEE([with the -w -qflttrap=enable:zerodivide], flttrap, , , , + [CFLAGS="$_cflags -mieee" + AC_IEEE([with the -mieee switch], mswitch, , , , + [CFLAGS="$_cflags -q float=rndsngl" + AC_IEEE([with the -q float=rndsngl switch], qswitch, , , , + [CFLAGS="$_cflags -OPT:IEEE_NaN_inf=ON" + AC_IEEE([with the -OPT:IEEE_NaN_inf=ON switch], ieeenaninfswitch, , , , + [CFLAGS="$_cflags -OPT:IEEE_comparisons=ON" + AC_IEEE([with the -OPT:IEEE_comparisons=ON switch], ieeecmpswitch, , , , + [CFLAGS=$_cflags + AC_IEEE([with fpsetmask(0)], mask, + [#include ], [fpsetmask(0)], + [AC_DEFINE(MUST_DISABLE_FPMASK) + PERLFLAGS="CCFLAGS=-DMUST_DISABLE_FPMASK"], + [AC_IEEE([with signal(SIGFPE,SIG_IGN)], sigfpe, + [#include ], [signal(SIGFPE,SIG_IGN)], + [AC_DEFINE(MUST_DISABLE_SIGFPE) + PERLFLAGS="CCFLAGS=-DMUST_DISABLE_SIGFPE"], + AC_MSG_ERROR([ +Your Compiler does not do propper IEEE math ... Please find out how to +make IEEE math work with your compiler and let me know (tobi@oetiker.ch). +Check config.log to see what went wrong ... +]))])])])])])])])])]) + +AC_LANG_POP(C) + +]) + + +dnl a macro to check for ability to create python extensions +dnl AM_CHECK_PYTHON_HEADERS([ACTION-IF-POSSIBLE], [ACTION-IF-NOT-POSSIBLE]) +dnl function also defines PYTHON_INCLUDES +AC_DEFUN([AM_CHECK_PYTHON_HEADERS], +[AC_REQUIRE([AM_PATH_PYTHON]) +AC_MSG_CHECKING(for headers required to compile python extensions) +dnl deduce PYTHON_INCLUDES +py_prefix=`$PYTHON -c "import sys; print sys.prefix"` +py_exec_prefix=`$PYTHON -c "import sys; print sys.exec_prefix"` +PYTHON_INCLUDES="-I${py_prefix}/include/python${PYTHON_VERSION}" +if test "$py_prefix" != "$py_exec_prefix"; then + PYTHON_INCLUDES="$PYTHON_INCLUDES -I${py_exec_prefix}/include/python${PYTHON_VERSION}" +fi +AC_SUBST(PYTHON_INCLUDES) +dnl check if the headers exist: +save_CPPFLAGS="$CPPFLAGS" +CPPFLAGS="$CPPFLAGS $PYTHON_INCLUDES" +AC_TRY_CPP([#include ],dnl +[AC_MSG_RESULT(found) +$1],dnl +[AC_MSG_RESULT(not found) +$2]) +CPPFLAGS="$save_CPPFLAGS" +]) + +dnl a macro to add some color to the build process. +dnl CONFIGURE_PART(MESSAGE) + +AC_DEFUN([CONFIGURE_PART],[ +case $TERM in + # for the most important terminal types we directly know the sequences + xterm|xterm*|vt220|vt220*) + T_MD=`awk 'BEGIN { printf("%c%c%c%c", 27, 91, 49, 109); }' /dev/null` + T_ME=`awk 'BEGIN { printf("%c%c%c", 27, 91, 109); }' /dev/null` + ;; + vt100|vt100*|cygwin) + T_MD=`awk 'BEGIN { printf("%c%c%c%c%c%c", 27, 91, 49, 109, 0, 0); }' /dev/null` + T_ME=`awk 'BEGIN { printf("%c%c%c%c%c", 27, 91, 109, 0, 0); }' /dev/null` + ;; + *) + T_MD='' + T_ME='' + ;; +esac + AC_MSG_RESULT() + AC_MSG_RESULT([${T_MD}$1${T_ME}]) +]) + + +dnl --------------------------------------------------------------------------- +dnl CF_DISABLE_ECHO version: 10 updated: 2003/04/17 22:27:11 +dnl --------------- +dnl stolen from xterm aclocal.m4 +dnl +dnl You can always use "make -n" to see the actual options, but it's hard to +dnl pick out/analyze warning messages when the compile-line is long. +dnl +dnl Sets: +dnl ECHO_LT - symbol to control if libtool is verbose +dnl ECHO_LD - symbol to prefix "cc -o" lines +dnl RULE_CC - symbol to put before implicit "cc -c" lines (e.g., .c.o) +dnl SHOW_CC - symbol to put before explicit "cc -c" lines +dnl ECHO_CC - symbol to put before any "cc" line +dnl +AC_DEFUN([CF_DISABLE_ECHO],[ +AC_MSG_CHECKING(if you want to see long compiling messages) +CF_ARG_DISABLE(echo, + [ --disable-echo display "compiling" commands], + [ + ECHO_LT='--silent' + ECHO_LD='@echo linking [$]@;' + RULE_CC=' @echo compiling [$]<' + SHOW_CC=' @echo compiling [$]@' + ECHO_CC='@' +],[ + ECHO_LT='' + ECHO_LD='' + RULE_CC='# compiling' + SHOW_CC='# compiling' + ECHO_CC='' +]) +AC_MSG_RESULT($enableval) +AC_SUBST(ECHO_LT) +AC_SUBST(ECHO_LD) +AC_SUBST(RULE_CC) +AC_SUBST(SHOW_CC) +AC_SUBST(ECHO_CC) +])dnl diff --git a/program/bindings/Makefile.am b/program/bindings/Makefile.am new file mode 100644 index 00000000..a0e0907b --- /dev/null +++ b/program/bindings/Makefile.am @@ -0,0 +1,56 @@ +.PHONY: python ruby + +if BUILD_TCL +SUB_tcl = tcl +endif + +SUBDIRS = $(SUB_tcl) + +# the following files are not mentioned in any other Makefile +EXTRA_DIST = perl-piped/MANIFEST perl-piped/README perl-piped/Makefile.PL perl-piped/RRDp.pm perl-piped/t/base.t \ + perl-shared/ntmake.pl perl-shared/MANIFEST perl-shared/README perl-shared/Makefile.PL perl-shared/RRDs.pm perl-shared/RRDs.xs perl-shared/t/base.t \ + ruby/CHANGES ruby/README ruby/extconf.rb ruby/main.c ruby/test.rb \ + python/ACKNOWLEDGEMENT python/AUTHORS python/COPYING python/README python/rrd_extra.h python/rrdtoolmodule.c python/setup.py + + +# add the following to the all target +all-local: @COMP_PERL@ @COMP_RUBY@ @COMP_PYTHON@ + +install-data-local: + test -f perl-piped/Makefile && cd perl-piped && $(MAKE) install || true + test -f perl-shared/Makefile && cd perl-shared && $(MAKE) install || true + test -f ruby/Makefile && cd ruby && $(MAKE) EPREFIX=$(DESTDIR)$(exec_prefix) $(RUBY_MAKE_OPTIONS) install || true + test -d python/build && cd python && env BUILDLIBDIR=../../src/.libs $(PYTHON) setup.py install --skip-build --prefix=$(DESTDIR)$(prefix) --exec-prefix=$(DESTDIR)$(exec_prefix) || true + +# rules for buildung the ruby module +# RUBYARCHDIR= is to work around in a makefile quirk not sure +# it is is the right thing todo, but it makes rrdtool build on freebsd as well +ruby: + cd ruby && $(RUBY) extconf.rb && $(MAKE) EPREFIX=$(exec_prefix) $(RUBY_MAKE_OPTIONS) RUBYARCHDIR= + +# rules for buildung the pyton module +python: + cd python && env BUILDLIBDIR=../../src/.libs $(PYTHON) setup.py build_ext --rpath=$(libdir) && env LIBDIR=../../src/.libs $(PYTHON) setup.py build + +# rules for building the perl module +perl_piped: perl-piped/Makefile + cd perl-piped && $(MAKE) + +perl-piped/Makefile: perl-piped/Makefile.PL + cd perl-piped && $(PERL) Makefile.PL $(PERL_MAKE_OPTIONS) + +perl_shared: perl-shared/Makefile + cd perl-shared && $(MAKE) + +perl-shared/Makefile: perl-shared/Makefile.PL + cd perl-shared && $(PERL) Makefile.PL $(PERLFLAGS) $(PERL_MAKE_OPTIONS) RPATH=$(libdir) +# LIBS="$(LDFLAGS) $(LIBS)" $(PERLFLAGS) $(PERL_MAKE_OPTIONS) + +clean-local: + test -f perl-piped/Makefile && cd perl-piped && $(MAKE) clean || true + test -f perl-piped/Makefile && rm perl-piped/Makefile || true + test -f perl-shared/Makefile && cd perl-shared && $(MAKE) clean || true + test -f perl-shared/Makefile && rm -f perl-shared/Makefile || true + test -f ruby/Makefile && cd ruby && $(MAKE) clean && rm Makefile || true + test -d python/build && cd python && rm -rf build || true +##END## diff --git a/program/bindings/perl-piped/MANIFEST b/program/bindings/perl-piped/MANIFEST new file mode 100644 index 00000000..a530adec --- /dev/null +++ b/program/bindings/perl-piped/MANIFEST @@ -0,0 +1,5 @@ +MANIFEST +README +Makefile.PL +RRDp.pm +t/base.t diff --git a/program/bindings/perl-piped/Makefile.PL b/program/bindings/perl-piped/Makefile.PL new file mode 100644 index 00000000..5c1a98fc --- /dev/null +++ b/program/bindings/perl-piped/Makefile.PL @@ -0,0 +1,10 @@ +use ExtUtils::MakeMaker; + +# See lib/ExtUtils/MakeMaker.pm for details of how to influence +# the contents of the Makefile that is written. +WriteMakefile( + 'NAME' => 'RRDp', + 'VERSION' => '0.99.0', # finds $VERSION + 'linkext' => {LINKTYPE => ''}, + 'dist' => {COMPRESS=>'gzip', SUFFIX=>'gz'}, +); diff --git a/program/bindings/perl-piped/README b/program/bindings/perl-piped/README new file mode 100644 index 00000000..672061d1 --- /dev/null +++ b/program/bindings/perl-piped/README @@ -0,0 +1,5 @@ +This is a Perl module for using RRDtool process via a set of pipes. + +perl Makefile.PL +make test +make install diff --git a/program/bindings/perl-piped/RRDp.pm b/program/bindings/perl-piped/RRDp.pm new file mode 100644 index 00000000..e61a28be --- /dev/null +++ b/program/bindings/perl-piped/RRDp.pm @@ -0,0 +1,200 @@ +package RRDp; + +=head1 NAME + +RRDp - Attach RRDtool from within a perl script via a set of pipes; + +=head1 SYNOPSIS + +use B + +B I + +B I + +$answer = B + +$status = B + +B<$RRDp::user>, B<$RRDp::sys>, B<$RRDp::real>, B<$RRDp::error_mode>, B<$RRDp::error> + +=head1 DESCRIPTION + +With this module you can safely communicate with the RRDtool. + +After every B you have to issue an B command to get +Bs answer to your command. The answer is returned as a pointer, +in order to speed things up. If the last command did not return any +data, B will return an undefined variable. + +If you import the PERFORMANCE variables into your namespace, +you can access RRDtool's internal performance measurements. + +=over 8 + +=item use B + +Load the RRDp::pipe module. + +=item B I + +start RRDtool. The argument must be the path to the RRDtool executable + +=item B I + +pass commands on to RRDtool. check the RRDtool documentation for +more info on the RRDtool commands. + +=item $answer = B + +read RRDtool's response to your command. Note that the $answer variable will +only contain a pointer to the returned data. The reason for this is, that +RRDtool can potentially return quite excessive amounts of data +and we don't want to copy this around in memory. So when you want to +access the contents of $answer you have to use $$answer which dereferences +the variable. + +=item $status = B + +terminates RRDtool and returns RRDtool's status ... + +=item B<$RRDp::user>, B<$RRDp::sys>, B<$RRDp::real> + +these variables will contain totals of the user time, system time and +real time as seen by RRDtool. User time is the time RRDtool is +running, System time is the time spend in system calls and real time +is the total time RRDtool has been running. + +The difference between user + system and real is the time spent +waiting for things like the hard disk and new input from the perl +script. + +=item B<$RRDp::error_mode> and B<$RRDp::error> + +If you set the variable $RRDp::error_mode to the value 'catch' before you run RRDp::read a potential +ERROR message will not cause the program to abort but will be returned in this variable. If no error +occurs the variable will be empty. + + $RRDp::error_mode = 'catch'; + RRDp::cmd qw(info file.rrd); + print $RRDp::error if $RRDp::error; + +=back + + +=head1 EXAMPLE + + use RRDp; + RRDp::start "/usr/local/bin/rrdtool"; + RRDp::cmd qw(create demo.rrd --step 100 + DS:in:GAUGE:100:U:U + RRA:AVERAGE:0.5:1:10); + $answer = RRDp::read; + print $$answer; + ($usertime,$systemtime,$realtime) = ($RRDp::user,$RRDp::sys,$RRDp::real); + +=head1 SEE ALSO + +For more information on how to use RRDtool, check the manpages. + +=head1 AUTHOR + +Tobias Oetiker + +=cut +#' this is to make cperl.el happy + +use strict; +use Fcntl; +use Carp; +use IO::Handle; +use IPC::Open2; +use vars qw($Sequence $RRDpid $VERSION); +my $Sequence; +my $RRDpid; + +# Prototypes + +sub start ($); +sub cmd (@); +sub end (); +sub read (); + +$VERSION=1.2023; + +sub start ($){ + croak "rrdtool is already running" + if defined $Sequence; + $Sequence = 'S'; + my $rrdtool = shift @_; + $RRDpid = open2 \*RRDreadHand,\*RRDwriteHand, $rrdtool,"-" + or croak "Can't Start rrdtool: $!"; + RRDwriteHand->autoflush(); #flush after every write + fcntl RRDreadHand, F_SETFL,O_NONBLOCK|O_NDELAY; #make readhandle NON BLOCKING + return $RRDpid; +} + + +sub read () { + croak "RRDp::read can only be called after RRDp::cmd" + unless $Sequence eq 'C'; + $RRDp::error = undef; + $Sequence = 'R'; + my $inmask = 0; + my $srbuf; + my $minibuf; + my $buffer; + my $nfound; + my $timeleft; + my $ERR = 0; + vec($inmask,fileno(RRDreadHand),1) = 1; # setup select mask for Reader + while (1) { + my $rout; + $nfound = select($rout=$inmask,undef,undef,2); + if ($nfound == 0 ) { + # here, we could do something sensible ... + next; + } + sysread(RRDreadHand,$srbuf,4096); + $minibuf .= $srbuf; + while ($minibuf =~ s|^(.+?)\n||s) { + my $line = $1; + # print $line,"\n"; + $RRDp::error = undef; + if ($line =~ m|^ERROR|) { + $RRDp::error_mode eq 'catch' ? $RRDp::error = $line : croak $line; + $ERR = 1; + } + elsif ($line =~ m|^OK u:([\d\.]+) s:([\d\.]+) r:([\d\.]+)|){ + ($RRDp::sys,$RRDp::user,$RRDp::real)=($1,$2,$3); + return $ERR == 1 ? undef : \$buffer; + } else { + $buffer .= $line. "\n"; + } + } + } +} + +sub cmd (@){ + croak "RRDp::cmd can only be called after RRDp::read or RRDp::start" + unless $Sequence eq 'R' or $Sequence eq 'S'; + $Sequence = 'C'; + my $cmd = join " ", @_; + if ($Sequence ne 'S') { + } + $cmd =~ s/\n/ /gs; + $cmd =~ s/\s/ /gs; + print RRDwriteHand "$cmd\n"; +} + +sub end (){ + croak "RRDp::end can only be called after RRDp::start" + unless $Sequence; + close RRDwriteHand; + close RRDreadHand; + $Sequence = undef; + waitpid $RRDpid,0; + return $? +} + +1; diff --git a/program/bindings/perl-piped/leaktest.pl b/program/bindings/perl-piped/leaktest.pl new file mode 100644 index 00000000..cebf1c7f --- /dev/null +++ b/program/bindings/perl-piped/leaktest.pl @@ -0,0 +1,16 @@ +#!/bin/perl -w +$ENV{PATH}="/usr/ucb"; +use strict; +use RRDp; +my $rrdfile='/tmp/test.rrd'; +RRDp::start '/home/oetiker/data/projects/AABN-rrdtool/src/rrdtool'; +print grep /rrdtool/,`ps au`; +print grep /rrdtool/,`ps au`; +my $i=0; +while ($i<1000) { + RRDp::cmd 'info /tmp/test.rrd'; + $_ = RRDp::read; + $i++; +} +$_ = RRDp::end; +print grep /rrdtool/,`ps au`; diff --git a/program/bindings/perl-piped/rrdpl.dsp b/program/bindings/perl-piped/rrdpl.dsp new file mode 100644 index 00000000..bf3a15d6 --- /dev/null +++ b/program/bindings/perl-piped/rrdpl.dsp @@ -0,0 +1,115 @@ +# Microsoft Developer Studio Project File - Name="rrd" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 5.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=rrd - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "rrdpl.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "rrdpl.mak" CFG="rrd - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "rrd - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "rrd - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "rrd - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o NUL /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o NUL /win32 +# ADD BASE RSC /l 0x100c /d "NDEBUG" +# ADD RSC /l 0x100c /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386 + +!ELSEIF "$(CFG)" == "rrd - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "C:\perl\lib\site\auto\RRD\" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /FD /c +# ADD CPP /W3 /I "C:\perl\lib\CORE" /D "WIN32" /D VERSION=\"0.02\" /D XS_VERSION=\"0.02\" /D "_DEBUG" /D "_CONSOLE" /FR -I../src/ -I../gd1.2 RRD.c /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o NUL /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o NUL /win32 +# ADD BASE RSC /l 0x100c /d "_DEBUG" +# ADD RSC /l 0x100c /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386 /pdbtype:sept +# ADD LINK32 c:\perl\lib\core\perl.lib ..\src\debug\rrd.lib ..\gd1.2\debug\gd.lib /dll /incremental:no /debug /machine:IX86 /out:"C:\perl\lib\site\auto\RRD\rrd.dll" +# SUBTRACT LINK32 /pdb:none + +!ENDIF + +# Begin Target + +# Name "rrd - Win32 Release" +# Name "rrd - Win32 Debug" +# Begin Source File + +SOURCE=.\RRD.c +# End Source File +# Begin Source File + +SOURCE=.\RRD.xs + +!IF "$(CFG)" == "rrd - Win32 Release" + +!ELSEIF "$(CFG)" == "rrd - Win32 Debug" + +# Begin Custom Build +InputPath=.\RRD.xs + +"rrd.c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + C:\Perl\bin\perl -Ic:\perl\lib -Ic:\perl\lib C:\perl\lib\ExtUtils/xsubpp\ + -typemap C:\perl\lib\ExtUtils\typemap RRD.xs >RRD.tc && C:\Perl\bin\perl\ + -Ic:\perl\lib -Ic:\perl\lib -MExtUtils::Command -e mv RRD.tc RRD.c + +# End Custom Build + +!ENDIF + +# End Source File +# End Target +# End Project diff --git a/program/bindings/perl-piped/rrdpl.dsw b/program/bindings/perl-piped/rrdpl.dsw new file mode 100644 index 00000000..73296e1e --- /dev/null +++ b/program/bindings/perl-piped/rrdpl.dsw @@ -0,0 +1,29 @@ +Microsoft Developer Studio Workspace File, Format Version 5.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "rrd"=".\rrd.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/program/bindings/perl-piped/t/base.t b/program/bindings/perl-piped/t/base.t new file mode 100755 index 00000000..5f0b2aee --- /dev/null +++ b/program/bindings/perl-piped/t/base.t @@ -0,0 +1,42 @@ +#! /usr/bin/perl + +# this exercises just the perl module .. not RRDtool as such ... + +BEGIN { $| = 1; print "1..5\n"; } +END { + print "not ok 1\n" unless $loaded; + unlink "demo.rrd"; +} + +sub ok +{ + $ok_count++; + my($what, $result) = @_ ; + print "not " unless $result; + print "ok $ok_count $what\n"; +} + +use RRDp; + +$loaded = 1; +$ok_count = 1; + +print "ok 1 module load\n"; + +ok("RRDp::start", RRDp::start "../../src/rrdtool" > 0); + +$now=time(); +RRDp::cmd qw(create demo.rrd --start ), $now, qw(--step 100 ), + qw( DS:in:GAUGE:100:U:U RRA:AVERAGE:0.5:1:10 ); + +$answer = RRDp::read; +ok("RRDp::cmd", -s "demo.rrd" ); + +RRDp::cmd qw(last demo.rrd); +$answer = RRDp::read; + +ok("RRDp::read", $$answer =~ /$now/); + +$status = RRDp::end; + +ok("RRDp::end", $status == 0); diff --git a/program/bindings/perl-shared/MANIFEST b/program/bindings/perl-shared/MANIFEST new file mode 100644 index 00000000..664d2315 --- /dev/null +++ b/program/bindings/perl-shared/MANIFEST @@ -0,0 +1,7 @@ +ntmake.pl +MANIFEST +README +Makefile.PL +RRDs.pm +RRDs.xs +t/base.t diff --git a/program/bindings/perl-shared/Makefile.PL b/program/bindings/perl-shared/Makefile.PL new file mode 100644 index 00000000..863444c7 --- /dev/null +++ b/program/bindings/perl-shared/Makefile.PL @@ -0,0 +1,37 @@ +use ExtUtils::MakeMaker; +use Config; +# See lib/ExtUtils/MakeMaker.pm for details of how to influence +# the contents of the Makefile that is written. + +# if the last argument when calling Makefile.PL is RPATH=/... and ... is the +# path to librrd.so then the Makefile will be written such that RRDs.so knows +# where to find librrd.so later on ... +my $R=""; +if ($ARGV[-1] =~ /RPATH=(\S+)/){ + pop @ARGV; + my $rp = $1; + for ($^O){ + /linux/ && do{ $R = "-Wl,--rpath -Wl,$rp"}; + /hpux/ && do{ $R = "+b$rp"}; + /solaris/ && do{ $R = "-R$rp"}; + /aix/ && do{ $R = "-Wl,-blibpath:$rp"}; + } +} + +# darwin works without this because librrd contains its +# install_name which will includes the final location of the +# library after it is installed. This install_name gets transfered +# to the perl shared object. + +my $librrd = "-L../../src/.libs/ $R -lrrd"; + +WriteMakefile( + 'NAME' => 'RRDs', + 'VERSION_FROM' => 'RRDs.pm', # finds $VERSION + 'DEFINE' => "-DPERLPATCHLEVEL=$Config{PATCHLEVEL}", + 'INC' => '-I../../src', + # Perl will figure out which one is valid + 'dynamic_lib' => {'OTHERLDFLAGS' => "$librrd -lm"}, + 'realclean' => {FILES => 't/demo?.rrd t/demo?.png' } +); + diff --git a/program/bindings/perl-shared/README b/program/bindings/perl-shared/README new file mode 100644 index 00000000..0918ecf0 --- /dev/null +++ b/program/bindings/perl-shared/README @@ -0,0 +1,12 @@ +These are the Perl bindings for rrdtool as a shared library. To compile do +the following: + +perl Makefile.PL +make test + +(win32 users try perl ntmake.pl) + +* if dynamic linking does not work, try + +perl Makefile.PL LINKTYPE=static +make test diff --git a/program/bindings/perl-shared/RRDs.pm b/program/bindings/perl-shared/RRDs.pm new file mode 100644 index 00000000..95424210 --- /dev/null +++ b/program/bindings/perl-shared/RRDs.pm @@ -0,0 +1,145 @@ +package RRDs; + +use strict; +use vars qw(@ISA $VERSION); + +@ISA = qw(DynaLoader); + +require DynaLoader; + +$VERSION=1.2023; + +bootstrap RRDs $VERSION; + +1; +__END__ + +=head1 NAME + +RRDs - Access RRDtool as a shared module + +=head1 SYNOPSIS + + use RRDs; + RRDs::error + RRDs::last ... + RRDs::info ... + RRDs::create ... + RRDs::update ... + RRDs::updatev ... + RRDs::graph ... + RRDs::fetch ... + RRDs::tune ... + RRDs::times(start, end) + RRDs::dump ... + RRDs::restore ... + +=head1 DESCRIPTION + +=head2 Calling Sequence + +This module accesses RRDtool functionality directly from within perl. The +arguments to the functions listed in the SYNOPSIS are explained in the regular +RRDtool documentation. The commandline call + + rrdtool update mydemo.rrd --template in:out N:12:13 + +gets turned into + + RRDs::update ("mydemo.rrd", "--template", "in:out", "N:12:13"); + +Note that + + --template=in:out + +is also valid. + +The RRDs::times function takes two parameters: a "start" and "end" time. +These should be specified in the B format +used by RRDtool. See the B documentation for a detailed +explanation on how to specify time. + +=head2 Error Handling + +The RRD functions will not abort your program even when they can not make +sense out of the arguments you fed them. + +The function RRDs::error should be called to get the error status +after each function call. If RRDs::error does not return anything +then the previous function has completed its task successfully. + + use RRDs; + RRDs::update ("mydemo.rrd","N:12:13"); + my $ERR=RRDs::error; + die "ERROR while updating mydemo.rrd: $ERR\n" if $ERR; + +=head2 Return Values + +The functions RRDs::last, RRDs::graph, RRDs::info, RRDs::fetch and RRDs::times +return their findings. + +B returns a single INTEGER representing the last update time. + + $lastupdate = RRDs::last ... + +B returns an pointer to an ARRAY containing the x-size and y-size of the +created image and results of the PRINT arguments. + + ($averages,$xsize,$ysize) = RRDs::graph ... + print "Imagesize: ${xsize}x${ysize}\n"; + print "Averages: ", (join ", ", @$averages); + +B returns a pointer to a hash. The keys of the hash +represent the property names of the RRD and the values of the hash are +the values of the properties. + + $hash = RRDs::info "example.rrd"; + foreach my $key (keys %$hash){ + print "$key = $$hash{$key}\n"; + } + +B also returns a pointer to hash. The keys of the hash +are concatenated strings of a timestamp, RRA index, and data source name for +each consolidated data point (CDP) written to disk as a result of the +current update call. The hash values are CDP values. + +B is the most complex of +the pack regarding return values. There are 4 values. Two normal +integers, a pointer to an array and a pointer to a array of pointers. + + my ($start,$step,$names,$data) = RRDs::fetch ... + print "Start: ", scalar localtime($start), " ($start)\n"; + print "Step size: $step seconds\n"; + print "DS names: ", join (", ", @$names)."\n"; + print "Data points: ", $#$data + 1, "\n"; + print "Data:\n"; + foreach my $line (@$data) { + print " ", scalar localtime($start), " ($start) "; + $start += $step; + foreach my $val (@$line) { + printf "%12.1f ", $val; + } + print "\n"; + } + +B returns two integers which are the number of seconds since +epoch (1970-01-01) for the supplied "start" and "end" arguments, respectively. + +See the examples directory for more ways to use this extension. + +=head1 NOTE + +If you are manipulating the TZ variable you should also call the posixs +function tzset to initialize all internal state of the library for properly +operating in the timezone of your choice. + + use POSIX qw(tzset); + $ENV{TZ} = 'CET'; + POSIX::tzset(); + + +=head1 AUTHOR + +Tobias Oetiker Etobi@oetiker.chE + +=cut diff --git a/program/bindings/perl-shared/RRDs.ppd b/program/bindings/perl-shared/RRDs.ppd new file mode 100755 index 00000000..3a28cd9e --- /dev/null +++ b/program/bindings/perl-shared/RRDs.ppd @@ -0,0 +1,10 @@ + + RRDs + Round Robin Database Tool + Tobias Oetiker (tobi@oetiker.ch) + + + + + + diff --git a/program/bindings/perl-shared/RRDs.xs b/program/bindings/perl-shared/RRDs.xs new file mode 100644 index 00000000..f84efef6 --- /dev/null +++ b/program/bindings/perl-shared/RRDs.xs @@ -0,0 +1,433 @@ +#ifdef __cplusplus +extern "C" { +#endif + +#include "EXTERN.h" +#include "perl.h" +#include "XSUB.h" + +#ifdef __cplusplus +} +#endif + +/* + * rrd_tool.h includes config.h, but at least on Ubuntu Breezy Badger + * 5.10 with gcc 4.0.2, the C preprocessor picks up Perl's config.h + * which is included from the Perl includes and never reads rrdtool's + * config.h. Without including rrdtool's config.h, this module does + * not compile, so include it here with an explicit path. + * + * Because rrdtool's config.h redefines VERSION which is originally + * set via Perl's Makefile.PL and passed down to the C compiler's + * command line, save the original value and reset it after the + * includes. + */ +#define VERSION_SAVED VERSION +#undef VERSION +#include "../../rrd_config.h" +#include "../../src/rrd_tool.h" +#undef VERSION +#define VERSION VERSION_SAVED +#undef VERSION_SAVED + +/* perl 5.004 compatibility */ +#if PERLPATCHLEVEL < 5 +#define PL_sv_undef sv_undef +#endif + + +#define rrdcode(name) \ + argv = (char **) malloc((items+1)*sizeof(char *));\ + argv[0] = "dummy";\ + for (i = 0; i < items; i++) { \ + STRLEN len; \ + char *handle= SvPV(ST(i),len);\ + /* actually copy the data to make sure possible modifications \ + on the argv data does not backfire into perl */ \ + argv[i+1] = (char *) malloc((strlen(handle)+1)*sizeof(char)); \ + strcpy(argv[i+1],handle); \ + } \ + rrd_clear_error();\ + RETVAL=name(items+1,argv); \ + for (i=0; i < items; i++) {\ + free(argv[i+1]);\ + } \ + free(argv);\ + \ + if (rrd_test_error()) XSRETURN_UNDEF; + +#define hvs(VAL) hv_store_ent(hash, sv_2mortal(newSVpv(data->key,0)),VAL,0) + +#define rrdinfocode(name) \ + /* prepare argument list */ \ + argv = (char **) malloc((items+1)*sizeof(char *)); \ + argv[0] = "dummy"; \ + for (i = 0; i < items; i++) { \ + STRLEN len; \ + char *handle= SvPV(ST(i),len); \ + /* actually copy the data to make sure possible modifications \ + on the argv data does not backfire into perl */ \ + argv[i+1] = (char *) malloc((strlen(handle)+1)*sizeof(char)); \ + strcpy(argv[i+1],handle); \ + } \ + rrd_clear_error(); \ + data=name(items+1, argv); \ + for (i=0; i < items; i++) { \ + free(argv[i+1]); \ + } \ + free(argv); \ + if (rrd_test_error()) XSRETURN_UNDEF; \ + hash = newHV(); \ + while (data) { \ + save=data; \ + /* the newSV will get copied by hv so we create it as a mortal \ + to make sure it does not keep hanging round after the fact */ \ + switch (data->type) { \ + case RD_I_VAL: \ + if (isnan(data->value.u_val)) \ + hvs(&PL_sv_undef); \ + else \ + hvs(newSVnv(data->value.u_val)); \ + break; \ + case RD_I_INT: \ + hvs(newSViv(data->value.u_int)); \ + break; \ + case RD_I_CNT: \ + hvs(newSViv(data->value.u_cnt)); \ + break; \ + case RD_I_STR: \ + hvs(newSVpv(data->value.u_str,0)); \ + rrd_freemem(data->value.u_str); \ + break; \ + } \ + rrd_freemem(data->key); \ + data = data->next; \ + rrd_freemem(save); \ + } \ + rrd_freemem(data); \ + RETVAL = newRV_noinc((SV*)hash); + +/* + * should not be needed if libc is linked (see ntmake.pl) +#ifdef WIN32 + #define free free + #define malloc malloc + #define realloc realloc +#endif +*/ + + +MODULE = RRDs PACKAGE = RRDs PREFIX = rrd_ + +BOOT: +#ifdef MUST_DISABLE_SIGFPE + signal(SIGFPE,SIG_IGN); +#endif +#ifdef MUST_DISABLE_FPMASK + fpsetmask(0); +#endif + + +SV* +rrd_error() + CODE: + if (! rrd_test_error()) XSRETURN_UNDEF; + RETVAL = newSVpv(rrd_get_error(),0); + OUTPUT: + RETVAL + + +int +rrd_last(...) + PROTOTYPE: @ + PREINIT: + int i; + char **argv; + CODE: + rrdcode(rrd_last); + OUTPUT: + RETVAL + +int +rrd_first(...) + PROTOTYPE: @ + PREINIT: + int i; + char **argv; + CODE: + rrdcode(rrd_first); + OUTPUT: + RETVAL + + +int +rrd_create(...) + PROTOTYPE: @ + PREINIT: + int i; + char **argv; + CODE: + rrdcode(rrd_create); + RETVAL = 1; + OUTPUT: + RETVAL + + +int +rrd_update(...) + PROTOTYPE: @ + PREINIT: + int i; + char **argv; + CODE: + rrdcode(rrd_update); + RETVAL = 1; + OUTPUT: + RETVAL + + +int +rrd_tune(...) + PROTOTYPE: @ + PREINIT: + int i; + char **argv; + CODE: + rrdcode(rrd_tune); + RETVAL = 1; + OUTPUT: + RETVAL + + +void +rrd_graph(...) + PROTOTYPE: @ + PREINIT: + char **calcpr=NULL; + int i,xsize,ysize; + double ymin,ymax; + char **argv; + AV *retar; + PPCODE: + argv = (char **) malloc((items+1)*sizeof(char *)); + argv[0] = "dummy"; + for (i = 0; i < items; i++) { + STRLEN len; + char *handle = SvPV(ST(i),len); + /* actually copy the data to make sure possible modifications + on the argv data does not backfire into perl */ + argv[i+1] = (char *) malloc((strlen(handle)+1)*sizeof(char)); + strcpy(argv[i+1],handle); + } + rrd_clear_error(); + rrd_graph(items+1,argv,&calcpr,&xsize,&ysize,NULL,&ymin,&ymax); + for (i=0; i < items; i++) { + free(argv[i+1]); + } + free(argv); + + if (rrd_test_error()) { + if(calcpr) + for(i=0;calcpr[i];i++) + rrd_freemem(calcpr[i]); + XSRETURN_UNDEF; + } + retar=newAV(); + if(calcpr){ + for(i=0;calcpr[i];i++){ + av_push(retar,newSVpv(calcpr[i],0)); + rrd_freemem(calcpr[i]); + } + rrd_freemem(calcpr); + } + EXTEND(sp,4); + PUSHs(sv_2mortal(newRV_noinc((SV*)retar))); + PUSHs(sv_2mortal(newSViv(xsize))); + PUSHs(sv_2mortal(newSViv(ysize))); + +void +rrd_fetch(...) + PROTOTYPE: @ + PREINIT: + time_t start,end; + unsigned long step, ds_cnt,i,ii; + rrd_value_t *data,*datai; + char **argv; + char **ds_namv; + AV *retar,*line,*names; + PPCODE: + argv = (char **) malloc((items+1)*sizeof(char *)); + argv[0] = "dummy"; + for (i = 0; i < items; i++) { + STRLEN len; + char *handle= SvPV(ST(i),len); + /* actually copy the data to make sure possible modifications + on the argv data does not backfire into perl */ + argv[i+1] = (char *) malloc((strlen(handle)+1)*sizeof(char)); + strcpy(argv[i+1],handle); + } + rrd_clear_error(); + rrd_fetch(items+1,argv,&start,&end,&step,&ds_cnt,&ds_namv,&data); + for (i=0; i < items; i++) { + free(argv[i+1]); + } + free(argv); + if (rrd_test_error()) XSRETURN_UNDEF; + /* convert the ds_namv into perl format */ + names=newAV(); + for (ii = 0; ii < ds_cnt; ii++){ + av_push(names,newSVpv(ds_namv[ii],0)); + rrd_freemem(ds_namv[ii]); + } + rrd_freemem(ds_namv); + /* convert the data array into perl format */ + datai=data; + retar=newAV(); + for (i = start+step; i <= end; i += step){ + line = newAV(); + for (ii = 0; ii < ds_cnt; ii++){ + av_push(line,(isnan(*datai) ? &PL_sv_undef : newSVnv(*datai))); + datai++; + } + av_push(retar,newRV_noinc((SV*)line)); + } + rrd_freemem(data); + EXTEND(sp,5); + PUSHs(sv_2mortal(newSViv(start+step))); + PUSHs(sv_2mortal(newSViv(step))); + PUSHs(sv_2mortal(newRV_noinc((SV*)names))); + PUSHs(sv_2mortal(newRV_noinc((SV*)retar))); + +void +rrd_times(start, end) + char *start + char *end + PREINIT: + struct rrd_time_value start_tv, end_tv; + char *parsetime_error = NULL; + time_t start_tmp, end_tmp; + PPCODE: + rrd_clear_error(); + if( (parsetime_error = parsetime( start, &start_tv))) { + rrd_set_error( "start time: %s", parsetime_error); + XSRETURN_UNDEF; + } + if( (parsetime_error = parsetime( end, &end_tv))) { + rrd_set_error( "end time: %s", parsetime_error); + XSRETURN_UNDEF; + } + if( proc_start_end( &start_tv, &end_tv, &start_tmp, &end_tmp) == -1) { + XSRETURN_UNDEF; + } + EXTEND(sp,2); + PUSHs(sv_2mortal(newSVuv(start_tmp))); + PUSHs(sv_2mortal(newSVuv(end_tmp))); + +int +rrd_xport(...) + PROTOTYPE: @ + PREINIT: + time_t start,end; + int xsize; + unsigned long step, col_cnt,row_cnt,i,ii; + rrd_value_t *data,*ptr; + char **argv,**legend_v; + AV *retar,*line,*names; + PPCODE: + argv = (char **) malloc((items+1)*sizeof(char *)); + argv[0] = "dummy"; + for (i = 0; i < items; i++) { + STRLEN len; + char *handle = SvPV(ST(i),len); + /* actually copy the data to make sure possible modifications + on the argv data does not backfire into perl */ + argv[i+1] = (char *) malloc((strlen(handle)+1)*sizeof(char)); + strcpy(argv[i+1],handle); + } + rrd_clear_error(); + rrd_xport(items+1,argv,&xsize,&start,&end,&step,&col_cnt,&legend_v,&data); + for (i=0; i < items; i++) { + free(argv[i+1]); + } + free(argv); + if (rrd_test_error()) XSRETURN_UNDEF; + + /* convert the legend_v into perl format */ + names=newAV(); + for (ii = 0; ii < col_cnt; ii++){ + av_push(names,newSVpv(legend_v[ii],0)); + rrd_freemem(legend_v[ii]); + } + rrd_freemem(legend_v); + + /* convert the data array into perl format */ + ptr=data; + retar=newAV(); + for (i = start+step; i <= end; i += step){ + line = newAV(); + for (ii = 0; ii < col_cnt; ii++){ + av_push(line,(isnan(*ptr) ? &PL_sv_undef : newSVnv(*ptr))); + ptr++; + } + av_push(retar,newRV_noinc((SV*)line)); + } + rrd_freemem(data); + + EXTEND(sp,7); + PUSHs(sv_2mortal(newSViv(start+step))); + PUSHs(sv_2mortal(newSViv(end))); + PUSHs(sv_2mortal(newSViv(step))); + PUSHs(sv_2mortal(newSViv(col_cnt))); + PUSHs(sv_2mortal(newRV_noinc((SV*)names))); + PUSHs(sv_2mortal(newRV_noinc((SV*)retar))); + +SV* +rrd_info(...) + PROTOTYPE: @ + PREINIT: + info_t *data,*save; + int i; + char **argv; + HV *hash; + CODE: + rrdinfocode(rrd_info); + OUTPUT: + RETVAL + +SV* +rrd_updatev(...) + PROTOTYPE: @ + PREINIT: + info_t *data,*save; + int i; + char **argv; + HV *hash; + CODE: + rrdinfocode(rrd_update_v); + OUTPUT: + RETVAL + +int +rrd_dump(...) + PROTOTYPE: @ + PREINIT: + int i; + char **argv; + CODE: + rrdcode(rrd_dump); + RETVAL = 1; + OUTPUT: + RETVAL + +int +rrd_restore(...) + PROTOTYPE: @ + PREINIT: + int i; + char **argv; + CODE: + rrdcode(rrd_restore); + RETVAL = 1; + OUTPUT: + RETVAL + diff --git a/program/bindings/perl-shared/ntmake.pl b/program/bindings/perl-shared/ntmake.pl new file mode 100644 index 00000000..047b76f2 --- /dev/null +++ b/program/bindings/perl-shared/ntmake.pl @@ -0,0 +1,27 @@ +use ExtUtils::MakeMaker; +use Config; +# See lib/ExtUtils/MakeMaker.pm for details of how to influence +# the contents of the Makefile that is written. +# Run VCVARS32.BAT before generating makefile/compiling. +WriteMakefile( + 'NAME' => 'RRDs', + 'VERSION_FROM' => 'RRDs.pm', +# 'DEFINE' => "-DPERLPATCHLEVEL=$Config{PATCHLEVEL}", +# keep compatible w/ ActiveState 5xx builds + 'DEFINE' => "-DPERLPATCHLEVEL=5", + + 'INC' => '-I../../src/ "-I/Program Files/GnuWin32/include"', +# Since we are now using GnuWin32 libraries dynamically (instead of static +# complile with code redistributed with rrdtool), use /MD instead of /MT. +# Yes, this means we need msvcrt.dll but GnuWin32 dlls already require it +# and it is available on most versions of Windows. + 'OPTIMIZE' => '-O2 -MD', + 'LIBS' => '../../src/release/rrd.lib "/Program Files/GnuWin32/lib/libart_lgpl.lib" "/Program Files/GnuWin32/lib/libz.lib" "/Program Files/GnuWin32/lib/libpng.lib" "/Program Files/GnuWin32/lib/libfreetype.lib"', + 'realclean' => {FILES => 't/demo?.rrd t/demo?.png' }, + ($] ge '5.005') ? ( + 'AUTHOR' => 'Tobias Oetiker (tobi@oetiker.ch)', + 'ABSTRACT' => 'Round Robin Database Tool', + ) : () + + +); diff --git a/program/bindings/perl-shared/t/base.t b/program/bindings/perl-shared/t/base.t new file mode 100755 index 00000000..7b6c016a --- /dev/null +++ b/program/bindings/perl-shared/t/base.t @@ -0,0 +1,171 @@ +#! /usr/bin/perl + +BEGIN { $| = 1; print "1..7\n"; } +END { + print "not ok 1\n" unless $loaded; + unlink "demo.rrd"; +} + +sub ok +{ + my($what, $result) = @_ ; + $ok_count++; + print "not " unless $result; + print "ok $ok_count $what\n"; +} + +use strict; +use vars qw(@ISA $loaded); + +use RRDs; +$loaded = 1; +my $ok_count = 1; + +ok("loading",1); + +######################### End of black magic. + +my $STEP = 100; +my $RUNS = 500; +my $GRUNS = 4; +my $RRD1 = "demo1.rrd"; +my $RRD2 = "demo2.rrd"; +my $PNG1 = "demo1.png"; +my $PNG2 = "demo2.png"; +my $time = 30*int(time/30); +my $START = $time-$RUNS*$STEP; + +my @options = ("-b", $START, "-s", $STEP, + "DS:a:GAUGE:2000:U:U", + "DS:b:GAUGE:200:U:U", + "DS:c:GAUGE:200:U:U", + "DS:d:GAUGE:200:U:U", + "DS:e:DERIVE:200:U:U", + "RRA:AVERAGE:0.5:1:5000", + "RRA:AVERAGE:0.5:10:500"); + +print "* Creating RRD $RRD1 starting at $time.\n\n"; +RRDs::create $RRD1, @options; + +my $ERROR = RRDs::error; +ok("create 1", !$ERROR); # 2 +if ($ERROR) { + die "$0: unable to create `$RRD1': $ERROR\n"; +} + +print "* Creating RRD $RRD2 starting at $time.\n\n"; +RRDs::create $RRD2, @options; + +$ERROR= RRDs::error; +ok("create 2",!$ERROR); # 3 +if ($ERROR) { + die "$0: unable to create `$RRD2': $ERROR\n"; +} + +my $last = RRDs::last $RRD1; +if ($ERROR = RRDs::error) { + die "$0: unable to get last `$RRD1': $ERROR\n"; +} +ok("last 1", $last == $START); # 4 + +$last = RRDs::last $RRD2; +if ($ERROR = RRDs::error) { + die "$0: unable to get last `$RRD2': $ERROR\n"; +} +ok("last 2", $last == $START); # 5 + +print "* Filling $RRD1 and $RRD2 with $RUNS*5 values. One moment please ...\n"; +print "* If you are running over NFS this will take *MUCH* longer\n\n"; + +srand(int($time / 100)); + +@options = (); + +my $counter = 1e7; +for (my $t=$START+1; + $t<$START+$STEP*$RUNS; + $t+=$STEP+int((rand()-0.5)*7)){ + $counter += int(2500*sin($t/2000)*$STEP); + my $data = (1000+500*sin($t/1000)).":". + (1000+900*sin($t/2330)).":". + (2000*cos($t/1550)).":". + (3220*sin($t/3420)).":$counter"; + push(@options, "$t:$data"); + RRDs::update $RRD1, "$t:$data"; + if ($ERROR = RRDs::error) { + warn "$0: unable to update `$RRD1': $ERROR\n"; + } +} + +ok("update 1",!$ERROR); # 3 + +RRDs::update $RRD2, @options; + +ok("update 2",!$ERROR); # 3 + +if ($ERROR = RRDs::error) { + die "$0: unable to update `$RRD2': $ERROR\n"; +} + +print "* Creating $GRUNS graphs: $PNG1 & $PNG2\n\n"; +my $now = $time; +for (my $i=0;$i<$GRUNS;$i++) { + my @rrd_pngs = ($RRD1, $PNG1, $RRD2, $PNG2); + while (@rrd_pngs) { + my $RRD = shift(@rrd_pngs); + my $PNG = shift(@rrd_pngs); + my ($graphret,$xs,$ys) = RRDs::graph $PNG, "--title", 'Test GRAPH', + "--vertical-label", 'Dummy Units', "--start", (-$RUNS*$STEP), + "DEF:alpha=$RRD:a:AVERAGE", + "DEF:beta=$RRD:b:AVERAGE", + "DEF:gamma=$RRD:c:AVERAGE", + "DEF:delta=$RRD:d:AVERAGE", + "DEF:epsilon=$RRD:e:AVERAGE", + "CDEF:calc=alpha,beta,+,2,/", + "AREA:alpha#0022e9:Short", + "STACK:beta#00b871:Demo Text", + "LINE1:gamma#ff0000:Line 1", + "LINE2:delta#888800:Line 2", + "LINE3:calc#00ff44:Line 3", + "LINE3:epsilon#000000:Line 4", + "HRULE:1500#ff8800:Horizontal Line at 1500", + "PRINT:alpha:AVERAGE:Average Alpha %1.2lf", + "PRINT:alpha:MIN:Min Alpha %1.2lf %s", + "PRINT:alpha:MIN:Min Alpha %1.2lf", + "PRINT:alpha:MAX:Max Alpha %1.2lf", + "GPRINT:calc:AVERAGE:Average calc %1.2lf %s", + "GPRINT:calc:AVERAGE:Average calc %1.2lf", + "GPRINT:calc:MAX:Max calc %1.2lf", + "GPRINT:calc:MIN:Min calc %1.2lf", + "VRULE:".($now-3600)."#008877:60 Minutes ago", + "VRULE:".($now-7200)."#008877:120 Minutes ago"; + + if ($ERROR = RRDs::error) { + print "ERROR: $ERROR\n"; + } else { + print "Image Size: ${xs}x${ys}\n"; + print "Graph Return:\n",(join "\n", @$graphret),"\n\n"; + } + } +} + + + +my ($start,$step,$names,$array) = RRDs::fetch $RRD1, "AVERAGE"; +$ERROR = RRDs::error; +print "ERROR: $ERROR\n" if $ERROR ; +print "start=$start, step=$step\n"; +print " "; +map {printf("%12s",$_)} @$names ; +foreach my $line (@$array){ + print "".localtime($start)," "; + $start += $step; + foreach my $val (@$line) { + if (not defined $val){ + printf "%12s", "UNKNOWN"; + } else { + printf "%12.1f", $val; + } + } + print "\n"; +} diff --git a/program/bindings/python/ACKNOWLEDGEMENT b/program/bindings/python/ACKNOWLEDGEMENT new file mode 100644 index 00000000..8cf36589 --- /dev/null +++ b/program/bindings/python/ACKNOWLEDGEMENT @@ -0,0 +1,7 @@ +ACKNOWLEDGMENT +============== + +This is a list of people who have made contributions to py-rrdtool. + +Matthew W. Samsonoff +Brian E. Gallew diff --git a/program/bindings/python/AUTHORS b/program/bindings/python/AUTHORS new file mode 100644 index 00000000..b3b5713d --- /dev/null +++ b/program/bindings/python/AUTHORS @@ -0,0 +1 @@ +Hye-Shik Chang diff --git a/program/bindings/python/COPYING b/program/bindings/python/COPYING new file mode 100644 index 00000000..b1e3f5a2 --- /dev/null +++ b/program/bindings/python/COPYING @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/program/bindings/python/README b/program/bindings/python/README new file mode 100644 index 00000000..265244f9 --- /dev/null +++ b/program/bindings/python/README @@ -0,0 +1,28 @@ +Python-RRDtool 0.2.1 +-------------------- + +The python-rrdtool provides a interface to rrdtool, the wonderful +graphing and logging utility. This wrapper implementation has +worked from the scratch (without SWIG), and it's under LGPL. + +This module have not documented yet. Please refer pydoc. + + +Project +------- + +Homepage: http://www.nongnu.org/py-rrdtool/ + +Mailing Lists: + + CVS Checkins py-rrdtool-cvs@nongnu.org + Users & Hackers py-rrdtool-users@nongnu.org + + +Author +------ + +Hye-Shik Chang + +Any comments, suggestions, and/or patches are very welcome. +Thank you for using py-rrdtool! diff --git a/program/bindings/python/rrd_extra.h b/program/bindings/python/rrd_extra.h new file mode 100644 index 00000000..99b5aa07 --- /dev/null +++ b/program/bindings/python/rrd_extra.h @@ -0,0 +1,61 @@ +/* + * This file is part of RRDtool. + * + * RRDtool is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * RRDtool is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Foobar; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/***************************************************************************** + * RRDtool 1.0.37 Copyright Tobias Oetiker, 1997 - 2000 + ***************************************************************************** + * rrd_tool.h Common Header File + ***************************************************************************** + * Id: rrd_tool.h,v 1.1.1.1 2002/02/26 10:21:37 oetiker Exp + * Log: rrd_tool.h,v + * Revision 1.1.1.1 2002/02/26 10:21:37 oetiker + * Intial Import + * + *****************************************************************************/ +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef _RRD_EXTRA_H +#define _RRD_EXTRA_H + +#include "rrd_format.h" + +#ifndef WIN32 +#ifndef isnan /* POSIX */ +int isnan(double value); +#endif +#else /* Windows only */ +#include +#define isnan _isnan +#endif + +void rrd_free(rrd_t *rrd); +void rrd_init(rrd_t *rrd); + +int rrd_open(char *file_name, FILE **in_file, rrd_t *rrd, int rdwr); +int readfile(char *file, char **buffer, int skipfirst); + +#define RRD_READONLY 0 +#define RRD_READWRITE 1 + +#endif + +#ifdef __cplusplus +} +#endif diff --git a/program/bindings/python/rrdtoolmodule.c b/program/bindings/python/rrdtoolmodule.c new file mode 100644 index 00000000..6f5b36c5 --- /dev/null +++ b/program/bindings/python/rrdtoolmodule.c @@ -0,0 +1,542 @@ +/* + * rrdtoolmodule.c + * + * RRDTool Python binding + * + * Author : Hye-Shik Chang + * Date : $Date: 2003/02/22 07:41:19 $ + * Created : 23 May 2002 + * + * $Revision: 1.14 $ + * + * ========================================================================== + * This file is part of py-rrdtool. + * + * py-rrdtool is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * py-rrdtool is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Foobar; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifdef UNUSED +#elif defined(__GNUC__) +# define UNUSED(x) x __attribute__((unused)) +#elif defined(__LCLINT__) +# define UNUSED(x) /*@unused@*/ x +#else +# define UNUSED(x) x +#endif + +static const char *__version__ = "$Revision: 1.14 $"; + +#include "Python.h" +#include "rrd.h" +#include "rrd_extra.h" + +static PyObject *ErrorObject; +extern int optind; +extern int opterr; + +/* forward declaration to keep compiler happy */ +void initrrdtool(void); + +static int +create_args(char *command, PyObject *args, int *argc, char ***argv) +{ + PyObject *o; + int size, i; + + size = PyTuple_Size(args); + *argv = PyMem_New(char *, size + 1); + if (*argv == NULL) + return -1; + + for (i = 0; i < size; i++) { + o = PyTuple_GET_ITEM(args, i); + if (PyString_Check(o)) + (*argv)[i + 1] = PyString_AS_STRING(o); + else { + PyMem_Del(*argv); + PyErr_Format(PyExc_TypeError, "argument %d must be string", i); + return -1; + } + } + (*argv)[0] = command; + *argc = size + 1; + + /* reset getopt state */ + opterr = optind = 0; + + return 0; +} + +static void +destroy_args(char ***argv) +{ + PyMem_Del(*argv); + *argv = NULL; +} + +static char PyRRD_create__doc__[] = +"create(args..): Set up a new Round Robin Database\n\ + create filename [--start|-b start time] \ +[--step|-s step] [DS:ds-name:DST:heartbeat:min:max] \ +[RRA:CF:xff:steps:rows]"; + +static PyObject * +PyRRD_create(PyObject UNUSED(*self), PyObject *args) +{ + PyObject *r; + char **argv; + int argc; + + if (create_args("create", args, &argc, &argv) < 0) + return NULL; + + if (rrd_create(argc, argv) == -1) { + PyErr_SetString(ErrorObject, rrd_get_error()); + rrd_clear_error(); + r = NULL; + } else { + Py_INCREF(Py_None); + r = Py_None; + } + + destroy_args(&argv); + return r; +} + +static char PyRRD_update__doc__[] = +"update(args..): Store a new set of values into the rrd\n" +" update filename [--template|-t ds-name[:ds-name]...] " +"N|timestamp:value[:value...] [timestamp:value[:value...] ...]"; + +static PyObject * +PyRRD_update(PyObject UNUSED(*self), PyObject *args) +{ + PyObject *r; + char **argv; + int argc; + + if (create_args("update", args, &argc, &argv) < 0) + return NULL; + + if (rrd_update(argc, argv) == -1) { + PyErr_SetString(ErrorObject, rrd_get_error()); + rrd_clear_error(); + r = NULL; + } else { + Py_INCREF(Py_None); + r = Py_None; + } + + destroy_args(&argv); + return r; +} + +static char PyRRD_fetch__doc__[] = +"fetch(args..): fetch data from an rrd.\n" +" fetch filename CF [--resolution|-r resolution] " +"[--start|-s start] [--end|-e end]"; + +static PyObject * +PyRRD_fetch(PyObject UNUSED(*self), PyObject *args) +{ + PyObject *r; + rrd_value_t *data, *datai; + unsigned long step, ds_cnt; + time_t start, end; + int argc; + char **argv, **ds_namv; + + if (create_args("fetch", args, &argc, &argv) < 0) + return NULL; + + if (rrd_fetch(argc, argv, &start, &end, &step, + &ds_cnt, &ds_namv, &data) == -1) { + PyErr_SetString(ErrorObject, rrd_get_error()); + rrd_clear_error(); + r = NULL; + } else { + /* Return : + ((start, end, step), (name1, name2, ...), [(data1, data2, ..), ...]) */ + PyObject *range_tup, *dsnam_tup, *data_list, *t; + unsigned long i, j, row; + rrd_value_t dv; + + row = ((end - start) / step + 1); + + r = PyTuple_New(3); + range_tup = PyTuple_New(3); + dsnam_tup = PyTuple_New(ds_cnt); + data_list = PyList_New(row); + PyTuple_SET_ITEM(r, 0, range_tup); + PyTuple_SET_ITEM(r, 1, dsnam_tup); + PyTuple_SET_ITEM(r, 2, data_list); + + datai = data; + + PyTuple_SET_ITEM(range_tup, 0, PyInt_FromLong((long)start)); + PyTuple_SET_ITEM(range_tup, 1, PyInt_FromLong((long)end)); + PyTuple_SET_ITEM(range_tup, 2, PyInt_FromLong((long)step)); + + for (i = 0; i < ds_cnt; i++) + PyTuple_SET_ITEM(dsnam_tup, i, PyString_FromString(ds_namv[i])); + + for (i = 0; i < row; i ++) { + t = PyTuple_New(ds_cnt); + PyList_SET_ITEM(data_list, i, t); + + for (j = 0; j < ds_cnt; j++) { + dv = *(datai++); + if (isnan(dv)) { + PyTuple_SET_ITEM(t, j, Py_None); + Py_INCREF(Py_None); + } else { + PyTuple_SET_ITEM(t, j, PyFloat_FromDouble((double)dv)); + } + } + } + + for (i = 0; i < ds_cnt; i++) + free(ds_namv[i]); + free(ds_namv); /* rrdtool don't use PyMem_Malloc :) */ + free(data); + } + + destroy_args(&argv); + return r; +} + +static char PyRRD_graph__doc__[] = +"graph(args..): Create a graph based on data from one or several RRD\n" +" graph filename [-s|--start seconds] " +"[-e|--end seconds] [-x|--x-grid x-axis grid and label] " +"[-y|--y-grid y-axis grid and label] [--alt-y-grid] [--alt-y-mrtg] " +"[--alt-autoscale] [--alt-autoscale-max] [--units-exponent] value " +"[-v|--vertical-label text] [-w|--width pixels] [-h|--height pixels] " +"[-i|--interlaced] " +"[-f|--imginfo formatstring] [-a|--imgformat GIF|PNG|GD] " +"[-B|--background value] [-O|--overlay value] " +"[-U|--unit value] [-z|--lazy] [-o|--logarithmic] " +"[-u|--upper-limit value] [-l|--lower-limit value] " +"[-g|--no-legend] [-r|--rigid] [--step value] " +"[-b|--base value] [-c|--color COLORTAG#rrggbb] " +"[-t|--title title] [DEF:vname=rrd:ds-name:CF] " +"[CDEF:vname=rpn-expression] [PRINT:vname:CF:format] " +"[GPRINT:vname:CF:format] [COMMENT:text] " +"[HRULE:value#rrggbb[:legend]] [VRULE:time#rrggbb[:legend]] " +"[LINE{1|2|3}:vname[#rrggbb[:legend]]] " +"[AREA:vname[#rrggbb[:legend]]] " +"[STACK:vname[#rrggbb[:legend]]]"; + +static PyObject * +PyRRD_graph(PyObject UNUSED(*self), PyObject *args) +{ + PyObject *r; + char **argv, **calcpr; + int argc, xsize, ysize, i; + double ymin, ymax; + if (create_args("graph", args, &argc, &argv) < 0) + return NULL; + + if (rrd_graph(argc, argv, &calcpr, &xsize, &ysize, NULL, &ymin, &ymax) == -1) { + PyErr_SetString(ErrorObject, rrd_get_error()); + rrd_clear_error(); + r = NULL; + } else { + r = PyTuple_New(3); + + PyTuple_SET_ITEM(r, 0, PyInt_FromLong((long)xsize)); + PyTuple_SET_ITEM(r, 1, PyInt_FromLong((long)ysize)); + + if (calcpr) { + PyObject *e, *t; + + e = PyList_New(0); + PyTuple_SET_ITEM(r, 2, e); + + for(i = 0; calcpr[i]; i++) { + t = PyString_FromString(calcpr[i]); + PyList_Append(e, t); + Py_DECREF(t); + free(calcpr[i]); + } + free(calcpr); + } else { + Py_INCREF(Py_None); + PyTuple_SET_ITEM(r, 2, Py_None); + } + } + + destroy_args(&argv); + return r; +} + +static char PyRRD_tune__doc__[] = +"tune(args...): Modify some basic properties of a Round Robin Database\n" +" tune filename [--heartbeat|-h ds-name:heartbeat] " +"[--minimum|-i ds-name:min] [--maximum|-a ds-name:max] " +"[--data-source-type|-d ds-name:DST] [--data-source-rename|-r old-name:new-name]"; + +static PyObject * +PyRRD_tune(PyObject UNUSED(*self), PyObject *args) +{ + PyObject *r; + char **argv; + int argc; + + if (create_args("tune", args, &argc, &argv) < 0) + return NULL; + + if (rrd_tune(argc, argv) == -1) { + PyErr_SetString(ErrorObject, rrd_get_error()); + rrd_clear_error(); + r = NULL; + } else { + Py_INCREF(Py_None); + r = Py_None; + } + + destroy_args(&argv); + return r; +} + +static char PyRRD_first__doc__[] = +"first(filename): Return the timestamp of the first data sample in an RRD"; + +static PyObject * +PyRRD_first(PyObject UNUSED(*self), PyObject *args) +{ + PyObject *r; + int argc, ts; + char **argv; + + if (create_args("first", args, &argc, &argv) < 0) + return NULL; + + if ((ts = rrd_first(argc, argv)) == -1) { + PyErr_SetString(ErrorObject, rrd_get_error()); + rrd_clear_error(); + r = NULL; + } else + r = PyInt_FromLong((long)ts); + + destroy_args(&argv); + return r; +} + +static char PyRRD_last__doc__[] = +"last(filename): Return the timestamp of the last data sample in an RRD"; + +static PyObject * +PyRRD_last(PyObject UNUSED(*self), PyObject *args) +{ + PyObject *r; + int argc, ts; + char **argv; + + if (create_args("last", args, &argc, &argv) < 0) + return NULL; + + if ((ts = rrd_last(argc, argv)) == -1) { + PyErr_SetString(ErrorObject, rrd_get_error()); + rrd_clear_error(); + r = NULL; + } else + r = PyInt_FromLong((long)ts); + + destroy_args(&argv); + return r; +} + +static char PyRRD_resize__doc__[] = +"resize(args...): alters the size of an RRA.\n" +" resize filename rra-num GROW|SHRINK rows"; + +static PyObject * +PyRRD_resize(PyObject UNUSED(*self), PyObject *args) +{ + PyObject *r; + char **argv; + int argc, ts; + + if (create_args("resize", args, &argc, &argv) < 0) + return NULL; + + if ((ts = rrd_resize(argc, argv)) == -1) { + PyErr_SetString(ErrorObject, rrd_get_error()); + rrd_clear_error(); + r = NULL; + } else { + Py_INCREF(Py_None); + r = Py_None; + } + + destroy_args(&argv); + return r; +} + +static char PyRRD_info__doc__[] = +"info(filename): extract header information from an rrd"; + +static PyObject * +PyRRD_info(PyObject UNUSED(*self), PyObject *args) +{ + PyObject *r, *t, *ds; + rrd_t rrd; + FILE *in_file; + char *filename; + unsigned long i, j; + + if (! PyArg_ParseTuple(args, "s:info", &filename)) + return NULL; + + if (rrd_open(filename, &in_file, &rrd, RRD_READONLY) == -1) { + PyErr_SetString(ErrorObject, rrd_get_error()); + rrd_clear_error(); + return NULL; + } + fclose(in_file); + +#define DICTSET_STR(dict, name, value) \ + t = PyString_FromString(value); \ + PyDict_SetItemString(dict, name, t); \ + Py_DECREF(t); + +#define DICTSET_CNT(dict, name, value) \ + t = PyInt_FromLong((long)value); \ + PyDict_SetItemString(dict, name, t); \ + Py_DECREF(t); + +#define DICTSET_VAL(dict, name, value) \ + t = isnan(value) ? (Py_INCREF(Py_None), Py_None) : \ + PyFloat_FromDouble((double)value); \ + PyDict_SetItemString(dict, name, t); \ + Py_DECREF(t); + + r = PyDict_New(); + + DICTSET_STR(r, "filename", filename); + DICTSET_STR(r, "rrd_version", rrd.stat_head->version); + DICTSET_CNT(r, "step", rrd.stat_head->pdp_step); + DICTSET_CNT(r, "last_update", rrd.live_head->last_up); + + ds = PyDict_New(); + PyDict_SetItemString(r, "ds", ds); + Py_DECREF(ds); + + for (i = 0; i < rrd.stat_head->ds_cnt; i++) { + PyObject *d; + + d = PyDict_New(); + PyDict_SetItemString(ds, rrd.ds_def[i].ds_nam, d); + Py_DECREF(d); + + DICTSET_STR(d, "ds_name", rrd.ds_def[i].ds_nam); + DICTSET_STR(d, "type", rrd.ds_def[i].dst); + DICTSET_CNT(d, "minimal_heartbeat", rrd.ds_def[i].par[DS_mrhb_cnt].u_cnt); + DICTSET_VAL(d, "min", rrd.ds_def[i].par[DS_min_val].u_val); + DICTSET_VAL(d, "max", rrd.ds_def[i].par[DS_max_val].u_val); + DICTSET_STR(d, "last_ds", rrd.pdp_prep[i].last_ds); + DICTSET_VAL(d, "value", rrd.pdp_prep[i].scratch[PDP_val].u_val); + DICTSET_CNT(d, "unknown_sec", rrd.pdp_prep[i].scratch[PDP_unkn_sec_cnt].u_cnt); + } + + ds = PyList_New(rrd.stat_head->rra_cnt); + PyDict_SetItemString(r, "rra", ds); + Py_DECREF(ds); + + for (i = 0; i < rrd.stat_head->rra_cnt; i++) { + PyObject *d, *cdp; + + d = PyDict_New(); + PyList_SET_ITEM(ds, i, d); + + DICTSET_STR(d, "cf", rrd.rra_def[i].cf_nam); + DICTSET_CNT(d, "rows", rrd.rra_def[i].row_cnt); + DICTSET_CNT(d, "pdp_per_row", rrd.rra_def[i].pdp_cnt); + DICTSET_VAL(d, "xff", rrd.rra_def[i].par[RRA_cdp_xff_val].u_val); + + cdp = PyList_New(rrd.stat_head->ds_cnt); + PyDict_SetItemString(d, "cdp_prep", cdp); + Py_DECREF(cdp); + + for (j = 0; j < rrd.stat_head->ds_cnt; j++) { + PyObject *cdd; + + cdd = PyDict_New(); + PyList_SET_ITEM(cdp, j, cdd); + + DICTSET_VAL(cdd, "value", + rrd.cdp_prep[i*rrd.stat_head->ds_cnt+j].scratch[CDP_val].u_val); + DICTSET_CNT(cdd, "unknown_datapoints", + rrd.cdp_prep[i*rrd.stat_head->ds_cnt+j].scratch[CDP_unkn_pdp_cnt].u_cnt); + } + } + + rrd_free(&rrd); + + return r; +} + +/* List of methods defined in the module */ +#define meth(name, func, doc) {name, (PyCFunction)func, METH_VARARGS, doc} + +static PyMethodDef _rrdtool_methods[] = { + meth("create", PyRRD_create, PyRRD_create__doc__), + meth("update", PyRRD_update, PyRRD_update__doc__), + meth("fetch", PyRRD_fetch, PyRRD_fetch__doc__), + meth("graph", PyRRD_graph, PyRRD_graph__doc__), + meth("tune", PyRRD_tune, PyRRD_tune__doc__), + meth("first", PyRRD_first, PyRRD_first__doc__), + meth("last", PyRRD_last, PyRRD_last__doc__), + meth("resize", PyRRD_resize, PyRRD_resize__doc__), + meth("info", PyRRD_info, PyRRD_info__doc__), + {NULL, NULL,0,NULL} +}; + +#define SET_INTCONSTANT(dict, value) \ + t = PyInt_FromLong((long)value); \ + PyDict_SetItemString(dict, #value, t); \ + Py_DECREF(t); +#define SET_STRCONSTANT(dict, value) \ + t = PyString_FromString(value); \ + PyDict_SetItemString(dict, #value, t); \ + Py_DECREF(t); + +/* Initialization function for the module */ +void +initrrdtool(void) +{ + PyObject *m, *d, *t; + + /* Create the module and add the functions */ + m = Py_InitModule("rrdtool", _rrdtool_methods); + + /* Add some symbolic constants to the module */ + d = PyModule_GetDict(m); + + SET_STRCONSTANT(d, __version__); + ErrorObject = PyErr_NewException("rrdtool.error", NULL, NULL); + PyDict_SetItemString(d, "error", ErrorObject); + + /* Check for errors */ + if (PyErr_Occurred()) + Py_FatalError("can't initialize the rrdtool module"); +} + +/* + * $Id: _rrdtoolmodule.c,v 1.14 2003/02/22 07:41:19 perky Exp $ + * ex: ts=8 sts=4 et + */ diff --git a/program/bindings/python/setup.py b/program/bindings/python/setup.py new file mode 100644 index 00000000..7a41a113 --- /dev/null +++ b/program/bindings/python/setup.py @@ -0,0 +1,55 @@ +#! /usr/bin/env python +# +# setup.py +# +# py-rrdtool distutil setup +# +# Author : Hye-Shik Chang +# Date : $Date: 2003/02/14 02:38:16 $ +# Created : 24 May 2002 +# +# $Revision: 1.7 $ +# +# ========================================================================== +# This file is part of py-rrdtool. +# +# py-rrdtool is free software; you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# py-rrdtool is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Foobar; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +from distutils.core import setup, Extension +import sys, os + +RRDBASE = os.environ.get('LOCALBASE', '../../src') +library_dir = os.environ.get('BUILDLIBDIR', os.path.join(RRDBASE, 'lib')) +include_dir = os.environ.get('INCDIR', RRDBASE) + +setup(name = "py-rrdtool", + version = "0.2.1", + description = "Python Interface to RRDTool", + author = "Hye-Shik Chang", + author_email = "perky@fallin.lv", + license = "LGPL", + url = "http://oss.oetiker.ch/rrdtool", + #packages = ['rrdtool'], + ext_modules = [ + Extension( + "rrdtoolmodule", + ["rrdtoolmodule.c"], + libraries=['rrd'], + library_dirs=[library_dir], + include_dirs=[include_dir], + ) + ] +) diff --git a/program/bindings/ruby/CHANGES b/program/bindings/ruby/CHANGES new file mode 100644 index 00000000..c84ff293 --- /dev/null +++ b/program/bindings/ruby/CHANGES @@ -0,0 +1,3 @@ +2006-07-25 Loïs Lherbier + add y_min and y_max parameters to rrd_graph + update test.rb to send only strings to RRD.fetch diff --git a/program/bindings/ruby/README b/program/bindings/ruby/README new file mode 100644 index 00000000..888a0629 --- /dev/null +++ b/program/bindings/ruby/README @@ -0,0 +1,22 @@ +# +# ruby librrd bindings +# author: Miles Egan +# + +- Introduction + +This module provides ruby bindings for librrd, with functionality +comparable to the native perl bindings. See test.rb for a script that +exercises all ruby-librrd functionality. + +- Installation + +Installation is standard. Simply run: + +ruby extconf.rb +make +make install + +I hope this works for you. Please let me know if you have any +problems or suggestions. Someday when I'm feeling less lazy I'll +actually document this thing. Thanks to Tobi for rrdtool! diff --git a/program/bindings/ruby/extconf.rb b/program/bindings/ruby/extconf.rb new file mode 100644 index 00000000..2045e5a5 --- /dev/null +++ b/program/bindings/ruby/extconf.rb @@ -0,0 +1,18 @@ +# $Id: extconf.rb,v 1.2 2001/11/28 18:30:16 miles Exp $ +# Lost ticket pays maximum rate. + +require 'mkmf' + +if /linux/ =~ RUBY_PLATFORM + $LDFLAGS += '-Wl,--rpath -Wl,$(EPREFIX)/lib' +elsif /solaris/ =~ RUBY_PLATFORM + $LDFLAGS += '-R$(EPREFIX)/lib' +elsif /hpux/ =~ RUBY_PLATFORM + $LDFLAGS += '+b$(EPREFIX)/lib' +elsif /aix/ =~ RUBY_PLATFORM + $LDFLAGS += '-Wl,-blibpath:$(EPREFIX)/lib' +end + +dir_config("rrd","../../src","../../src/.libs") +have_library("rrd", "rrd_create") +create_makefile("RRD") diff --git a/program/bindings/ruby/main.c b/program/bindings/ruby/main.c new file mode 100644 index 00000000..b2eaa68e --- /dev/null +++ b/program/bindings/ruby/main.c @@ -0,0 +1,259 @@ +/* $Id$ + * Substantial penalty for early withdrawal. + */ + +#include +#include +#include + +typedef struct string_arr_t { + int len; + char **strings; +} string_arr; + +VALUE mRRD; +VALUE rb_eRRDError; + +typedef int (*RRDFUNC)(int argc, char ** argv); +#define RRD_CHECK_ERROR \ + if (rrd_test_error()) \ + rb_raise(rb_eRRDError, rrd_get_error()); \ + rrd_clear_error(); + +string_arr string_arr_new(VALUE rb_strings) +{ + string_arr a; + char buf[64]; + int i; + + Check_Type(rb_strings, T_ARRAY); + a.len = RARRAY(rb_strings)->len + 1; + + a.strings = malloc(a.len * sizeof(char *)); + a.strings[0] = "dummy"; /* first element is a dummy element */ + + for (i = 0; i < a.len - 1; i++) { + VALUE v = rb_ary_entry(rb_strings, i); + switch (TYPE(v)) { + case T_STRING: + a.strings[i + 1] = strdup(STR2CSTR(v)); + break; + case T_FIXNUM: + snprintf(buf, 63, "%d", FIX2INT(v)); + a.strings[i + 1] = strdup(buf); + break; + default: + rb_raise(rb_eTypeError, "invalid argument"); + break; + } + } + + return a; +} + +void string_arr_delete(string_arr a) +{ + int i; + + /* skip dummy first entry */ + for (i = 1; i < a.len; i++) { + free(a.strings[i]); + } + + free(a.strings); +} + +void reset_rrd_state() +{ + optind = 0; + opterr = 0; + rrd_clear_error(); +} + +VALUE rrd_call(RRDFUNC func, VALUE args) +{ + string_arr a; + + a = string_arr_new(args); + reset_rrd_state(); + func(a.len, a.strings); + string_arr_delete(a); + + RRD_CHECK_ERROR + + return Qnil; +} + +VALUE rb_rrd_create(VALUE self, VALUE args) +{ + return rrd_call(rrd_create, args); +} + +VALUE rb_rrd_dump(VALUE self, VALUE args) +{ + return rrd_call(rrd_dump, args); +} + +VALUE rb_rrd_fetch(VALUE self, VALUE args) +{ + string_arr a; + unsigned long i, j, k, step, ds_cnt; + rrd_value_t *raw_data; + char **raw_names; + VALUE data, names, result; + time_t start, end; + + a = string_arr_new(args); + reset_rrd_state(); + rrd_fetch(a.len, a.strings, &start, &end, &step, &ds_cnt, &raw_names, &raw_data); + string_arr_delete(a); + + RRD_CHECK_ERROR + + names = rb_ary_new(); + for (i = 0; i < ds_cnt; i++) { + rb_ary_push(names, rb_str_new2(raw_names[i])); + free(raw_names[i]); + } + free(raw_names); + + k = 0; + data = rb_ary_new(); + for (i = start; i <= end; i += step) { + VALUE line = rb_ary_new2(ds_cnt); + for (j = 0; j < ds_cnt; j++) { + rb_ary_store(line, j, rb_float_new(raw_data[k])); + k++; + } + rb_ary_push(data, line); + } + free(raw_data); + + result = rb_ary_new2(4); + rb_ary_store(result, 0, INT2FIX(start)); + rb_ary_store(result, 1, INT2FIX(end)); + rb_ary_store(result, 2, names); + rb_ary_store(result, 2, data); + return result; +} + +VALUE rb_rrd_graph(VALUE self, VALUE args) +{ + string_arr a; + char **calcpr, **p; + VALUE result, print_results; + int xsize, ysize; + double ymin, ymax; + + a = string_arr_new(args); + reset_rrd_state(); + rrd_graph(a.len, a.strings, &calcpr, &xsize, &ysize, NULL, &ymin, &ymax); + string_arr_delete(a); + + RRD_CHECK_ERROR + + result = rb_ary_new2(3); + print_results = rb_ary_new(); + p = calcpr; + for (p = calcpr; p && *p; p++) { + rb_ary_push(print_results, rb_str_new2(*p)); + free(*p); + } + free(calcpr); + rb_ary_store(result, 0, print_results); + rb_ary_store(result, 1, INT2FIX(xsize)); + rb_ary_store(result, 2, INT2FIX(ysize)); + return result; +} + +/* +VALUE rb_rrd_info(VALUE self, VALUE args) +{ + string_arr a; + info_t *p; + VALUE result; + + a = string_arr_new(args); + data = rrd_info(a.len, a.strings); + string_arr_delete(a); + + RRD_CHECK_ERROR + + result = rb_hash_new(); + while (data) { + VALUE key = rb_str_new2(data->key); + switch (data->type) { + case RD_I_VAL: + if (isnan(data->u_val)) { + rb_hash_aset(result, key, Qnil); + } + else { + rb_hash_aset(result, key, rb_float_new(data->u_val)); + } + break; + case RD_I_CNT: + rb_hash_aset(result, key, INT2FIX(data->u_cnt)); + break; + case RD_I_STR: + rb_hash_aset(result, key, rb_str_new2(data->u_str)); + free(data->u_str); + break; + } + p = data; + data = data->next; + free(p); + } + return result; +} +*/ + +VALUE rb_rrd_last(VALUE self, VALUE args) +{ + string_arr a; + time_t last; + + a = string_arr_new(args); + reset_rrd_state(); + last = rrd_last(a.len, a.strings); + string_arr_delete(a); + + RRD_CHECK_ERROR + + return rb_funcall(rb_cTime, rb_intern("at"), 1, INT2FIX(last)); +} + +VALUE rb_rrd_resize(VALUE self, VALUE args) +{ + return rrd_call(rrd_resize, args); +} + +VALUE rb_rrd_restore(VALUE self, VALUE args) +{ + return rrd_call(rrd_restore, args); +} + +VALUE rb_rrd_tune(VALUE self, VALUE args) +{ + return rrd_call(rrd_tune, args); +} + +VALUE rb_rrd_update(VALUE self, VALUE args) +{ + return rrd_call(rrd_update, args); +} + +void Init_RRD() +{ + mRRD = rb_define_module("RRD"); + rb_eRRDError = rb_define_class("RRDError", rb_eStandardError); + + rb_define_module_function(mRRD, "create", rb_rrd_create, -2); + rb_define_module_function(mRRD, "dump", rb_rrd_dump, -2); + rb_define_module_function(mRRD, "fetch", rb_rrd_fetch, -2); + rb_define_module_function(mRRD, "graph", rb_rrd_graph, -2); + rb_define_module_function(mRRD, "last", rb_rrd_last, -2); + rb_define_module_function(mRRD, "resize", rb_rrd_resize, -2); + rb_define_module_function(mRRD, "restore", rb_rrd_restore, -2); + rb_define_module_function(mRRD, "tune", rb_rrd_tune, -2); + rb_define_module_function(mRRD, "update", rb_rrd_update, -2); +} diff --git a/program/bindings/ruby/test.rb b/program/bindings/ruby/test.rb new file mode 100755 index 00000000..48533266 --- /dev/null +++ b/program/bindings/ruby/test.rb @@ -0,0 +1,52 @@ +#!/usr/bin/env ruby +# $Id: test.rb,v 1.2 2002/10/22 17:34:00 miles Exp $ +# Driver does not carry cash. + +$: << '/scratch/rrd12build/lib/ruby/1.8/i386-linux/' + +require "RRD" + +name = "test" +rrd = "#{name}.rrd" +start = Time.now.to_i + +puts "creating #{rrd}" +RRD.create( + rrd, + "--start", "#{start - 1}", + "--step", "300", + "DS:a:GAUGE:600:U:U", + "DS:b:GAUGE:600:U:U", + "RRA:AVERAGE:0.5:1:300") +puts + +puts "updating #{rrd}" +start.to_i.step(start.to_i + 300 * 300, 300) { |i| + RRD.update(rrd, "#{i}:#{rand(100)}:#{Math.sin(i / 800) * 50 + 50}") +} +puts + +puts "fetching data from #{rrd}" +(fstart, fend, data) = RRD.fetch(rrd, "--start", start.to_s, "--end", (start + 300 * 300).to_s, "AVERAGE") +puts "got #{data.length} data points from #{fstart} to #{fend}" +puts + +puts "generating graph #{name}.png" +RRD.graph( + "#{name}.png", + "--title", " RubyRRD Demo", + "--start", "#{start+3600}", + "--end", "start + 1000 min", + "--interlace", + "--imgformat", "PNG", + "--width=450", + "DEF:a=#{rrd}:a:AVERAGE", + "DEF:b=#{rrd}:b:AVERAGE", + "CDEF:line=TIME,2400,%,300,LT,a,UNKN,IF", + "AREA:b#00b6e4:beta", + "AREA:line#0022e9:alpha", + "LINE3:line#ff0000") +puts + +print "This script has created #{name}.png in the current directory\n"; +print "This demonstrates the use of the TIME and % RPN operators\n"; diff --git a/program/bindings/tcl/Makefile.am b/program/bindings/tcl/Makefile.am new file mode 100644 index 00000000..c73a6e2e --- /dev/null +++ b/program/bindings/tcl/Makefile.am @@ -0,0 +1,56 @@ + +EXTRA_DIST = README tclrrd.c + +VERSION = @VERSION@ + +AM_CFLAGS = @CFLAGS@ + +TCL_PREFIX = @TCL_PREFIX@ +TCL_SHLIB_LD = @TCL_SHLIB_LD@ +TCL_SHLIB_CFLAGS = @TCL_SHLIB_CFLAGS@ +TCL_SHLIB_SUFFIX = @TCL_SHLIB_SUFFIX@ +TCL_PACKAGE_PATH = @TCL_PACKAGE_PATH@ +TCL_LD_SEARCH_FLAGS = @TCL_LD_SEARCH_FLAGS@ +TCL_STUB_LIB_SPEC = @TCL_STUB_LIB_SPEC@ + +CLEANFILES = tclrrd.o tclrrd.so + +SRC_DIR = $(top_srcdir)/src +AM_CPPFLAGS = -I$(TCL_PREFIX)/include -I$(SRC_DIR) -DUSE_TCL_STUBS +LIBDIRS = -L$(top_builddir)/src/.libs -L$(top_builddir)/src -L$(libdir) +LIB_RUNTIME_DIR = $(libdir) + +if BUILD_TCL_SITE +tclpkgdir = @TCL_PACKAGE_DIR@ +tclpkg_DATA = pkgIndex.tcl +tclpkg_SCRIPTS = ifOctets.tcl +else +pkglib_DATA = pkgIndex.tcl +pkglib_SCRIPTS = ifOctets.tcl +endif + +# Automake doen't like `tclrrd$(VERSION)$(TCL_SHLIB_SUFFIX)' as +# library name. So we build and install this library `by hand'. +# +# We do, however, specify a lib_LIBRARIES target such that +# automake creates the directory (if neecessary). +# +TCL_RRD_LIB = tclrrd$(VERSION)$(TCL_SHLIB_SUFFIX) + +lib_LIBRARIES = + +all-local: $(TCL_RRD_LIB) + +$(TCL_RRD_LIB): tclrrd.o + $(TCL_SHLIB_LD) $(TCL_LD_SEARCH_FLAGS) $(LIBDIRS) $< -o $@ -lrrd_th -lm $(TCL_STUB_LIB_SPEC) $(LDFLAGS) $(LIBS) + +tclrrd.o: tclrrd.c + $(CC) $(AM_CFLAGS) $(CFLAGS) $(TCL_SHLIB_CFLAGS) $(AM_CPPFLAGS) -c $< -DVERSION=\"$(VERSION)\" + +pkgIndex.tcl: + echo "package ifneeded Rrd $(VERSION) \"load $(libdir)/tclrrd$(VERSION)[info sharedlibextension]\"" > $@ + +install-exec-local: $(TCL_RRD_LIB) + @$(NORMAL_INSTALL) + $(INSTALL_PROGRAM) $(TCL_RRD_LIB) $(DESTDIR)$(libdir)/$(TCL_RRD_LIB) + diff --git a/program/bindings/tcl/README b/program/bindings/tcl/README new file mode 100644 index 00000000..065a03aa --- /dev/null +++ b/program/bindings/tcl/README @@ -0,0 +1,31 @@ +TCLRRD -- A TCL interpreter extension to access the RRD library, + contributed to Tobias Oetiker's RRD tools. + +Copyright (c) 1999,2000 Frank Strauss, Technical University of Braunschweig. + +See the file "COPYING" for information on usage and redistribution +of this file, and for a DISCLAIMER OF ALL WARRANTIES. + +TCLRRD adds a dynamically loadable package to the Tcl 8.x interpreter +to access all RRD functions as of RRDtool 1.0.13. All command names +and arguments are equal to those of RRDtool. They are assigned to the +namespace `Rrd', e.g. `Rrd::create'. Return values are a bit +different from plain RRDtool behavior to enable more native Tcl +usage. Errors are mapped to the TCL_ERROR return code together with +the RRD error strings. + +TCLRRD makes it easy to combine RRD use with advanced SNMP functionality +of scotty (http://wwwsnmp.cs.utwente.nl/~schoenw/scotty/). E.g., it's easy +to use some scotty code to get the counters of some interfaces by their +interface name and then use Rrd::update to store the values. Furthermore, +data source types (see RRD::create documentation) and integer value ranges +could be easily retrieved from MIB information. + +TCLRRD has been written on a Linux system for use with Tcl 8.x. It should +work on many other platforms, although it has not been tested. There are +no fool proof installation procedures. Take a look at Makefile.am and +adapt it, if required. + +TCLRRD has been written for RRD 1.0.13. + + Frank Strauss , 09-Mar-2000 diff --git a/program/bindings/tcl/ifOctets.tcl.in b/program/bindings/tcl/ifOctets.tcl.in new file mode 100644 index 00000000..7a36397c --- /dev/null +++ b/program/bindings/tcl/ifOctets.tcl.in @@ -0,0 +1,45 @@ +#!/bin/sh +# the next line restarts using tclsh -*- tcl -*- \ +exec tclsh@TCL_VERSION@ "$0" "$@" + +#package require Tnm 3.0 +package require Rrd @VERSION@ + +set rrdfile "[lindex $argv 0]-[lindex $argv 1].rrd" + +# create rrdfile if not yet existent +if {[file exists $rrdfile] == 0} { + Rrd::create $rrdfile --step 5 \ + DS:inOctets:COUNTER:10:U:U DS:outOctets:COUNTER:10:U:U \ + RRA:AVERAGE:0.5:1:12 +} + +# get an snmp session context +set session [Tnm::snmp generator -address [lindex $argv 0]] + +# walk through the ifDescr column to find the right interface +$session walk descr IF-MIB!ifDescr { + + # is this the right interface? + if {"[Tnm::snmp value $descr 0]" == "[lindex $argv 1]"} { + + # get the instance part of this table row + set inst [lindex [Tnm::mib split [Tnm::snmp oid $descr 0]] 1] + + # get the two interface's octet counter values + set in [lindex [lindex [$session get IF-MIB!ifInOctets.$inst] 0] 2] + set out [lindex [lindex [$session get IF-MIB!ifOutOctets.$inst] 0] 2] + + # write the values to the rrd + puts "$in $out" + Rrd::update $rrdfile --template inOctets:outOctets N:$in:$out + + Rrd::graph gaga.png --title "gaga" \ + DEF:in=$rrdfile:inOctets:AVERAGE \ + DEF:out=$rrdfile:outOctets:AVERAGE \ + AREA:in#0000FF:inOctets \ + LINE2:out#00C000:outOctets + + #puts [Rrd::fetch $rrdfile AVERAGE] + } +} diff --git a/program/bindings/tcl/tclrrd.c b/program/bindings/tcl/tclrrd.c new file mode 100644 index 00000000..11d25dfa --- /dev/null +++ b/program/bindings/tcl/tclrrd.c @@ -0,0 +1,677 @@ +/* + * tclrrd.c -- A TCL interpreter extension to access the RRD library. + * + * Copyright (c) 1999,2000 Frank Strauss, Technical University of Braunschweig. + * + * Thread-safe code copyright (c) 2005 Oleg Derevenetz, CenterTelecom Voronezh ISP. + * + * See the file "COPYING" for information on usage and redistribution + * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + * + * $Id$ + */ + + + +#include +#include +#include +#include +#include +#include "../../src/rrd_tool.h" +#include "../../src/rrd_format.h" + +/* support pre-8.4 tcl */ + +#ifndef CONST84 +# define CONST84 +#endif + +extern int Tclrrd_Init(Tcl_Interp *interp); +extern int Tclrrd_SafeInit(Tcl_Interp *interp); + + +/* + * some rrd_XXX() and new thread-safe versions of Rrd_XXX() + * functions might modify the argv strings passed to it. + * Hence, we need to do some preparation before + * calling the rrd library functions. + */ +static char ** getopt_init(int argc, CONST84 char *argv[]) +{ + char **argv2; + int i; + + argv2 = calloc(argc, sizeof(char *)); + for (i = 0; i < argc; i++) { + argv2[i] = strdup(argv[i]); + } + return argv2; +} + +static void getopt_cleanup(int argc, char **argv2) +{ + int i; + + for (i = 0; i < argc; i++) { + if (argv2[i] != NULL) { + free(argv2[i]); + } + } + free(argv2); +} + +static void getopt_free_element(argv2, argn) + char *argv2[]; + int argn; +{ + if (argv2[argn] != NULL) { + free(argv2[argn]); + argv2[argn] = NULL; + } +} + +static void getopt_squieeze(argc, argv2) + int *argc; + char *argv2[]; +{ + int i, null_i = 0, argc_tmp = *argc; + + for (i = 0; i < argc_tmp; i++) { + if (argv2[i] == NULL) { + (*argc)--; + } else { + argv2[null_i++] = argv2[i]; + } + } +} + + + +/* Thread-safe version */ +static int +Rrd_Create(ClientData clientData, Tcl_Interp *interp, int argc, CONST84 char *argv[]) +{ + int argv_i; + char **argv2; + char *parsetime_error = NULL; + time_t last_up = time(NULL) - 10; + long int long_tmp; + unsigned long int pdp_step = 300; + struct rrd_time_value last_up_tv; + + argv2 = getopt_init(argc, argv); + + for (argv_i = 1; argv_i < argc; argv_i++) { + if (!strcmp(argv2[argv_i], "--start") || !strcmp(argv2[argv_i], "-b")) { + if (argv_i++>=argc) { + Tcl_AppendResult(interp, "RRD Error: option '", + argv2[argv_i - 1], "' needs an argument", (char *) NULL); + getopt_cleanup(argc, argv2); + return TCL_ERROR; + } + if ((parsetime_error = parsetime(argv2[argv_i], &last_up_tv))) { + Tcl_AppendResult(interp, "RRD Error: invalid time format: '", + argv2[argv_i], "'", (char *) NULL); + getopt_cleanup(argc, argv2); + return TCL_ERROR; + } + if (last_up_tv.type == RELATIVE_TO_END_TIME || + last_up_tv.type == RELATIVE_TO_START_TIME) { + Tcl_AppendResult(interp, "RRD Error: specifying time relative to the 'start' ", + "or 'end' makes no sense here", (char *) NULL); + getopt_cleanup(argc, argv2); + return TCL_ERROR; + } + last_up = mktime(&last_up_tv.tm) + last_up_tv.offset; + if (last_up < 3600*24*365*10) { + Tcl_AppendResult(interp, "RRD Error: the first entry to the RRD should be after 1980", + (char *) NULL); + getopt_cleanup(argc, argv2); + return TCL_ERROR; + } + getopt_free_element(argv2, argv_i - 1); + getopt_free_element(argv2, argv_i); + } else if (!strcmp(argv2[argv_i], "--step") || !strcmp(argv2[argv_i], "-s")) { + if (argv_i++>=argc) { + Tcl_AppendResult(interp, "RRD Error: option '", + argv2[argv_i - 1], "' needs an argument", (char *) NULL); + getopt_cleanup(argc, argv2); + return TCL_ERROR; + } + long_tmp = atol(argv2[argv_i]); + if (long_tmp < 1) { + Tcl_AppendResult(interp, "RRD Error: step size should be no less than one second", + (char *) NULL); + getopt_cleanup(argc, argv2); + return TCL_ERROR; + } + pdp_step = long_tmp; + getopt_free_element(argv2, argv_i - 1); + getopt_free_element(argv2, argv_i); + } else if (!strcmp(argv2[argv_i], "--")) { + getopt_free_element(argv2, argv_i); + break; + } else if (argv2[argv_i][0]=='-') { + Tcl_AppendResult(interp, "RRD Error: unknown option '", + argv2[argv_i], "'", (char *) NULL); + getopt_cleanup(argc, argv2); + return TCL_ERROR; + } + } + + getopt_squieeze(&argc, argv2); + + if (argc < 2) { + Tcl_AppendResult(interp, "RRD Error: needs rrd filename", + (char *) NULL); + getopt_cleanup(argc, argv2); + return TCL_ERROR; + } + + rrd_create_r(argv2[1], pdp_step, last_up, argc - 2, argv2 + 2); + + getopt_cleanup(argc, argv2); + + if (rrd_test_error()) { + Tcl_AppendResult(interp, "RRD Error: ", + rrd_get_error(), (char *) NULL); + rrd_clear_error(); + return TCL_ERROR; + } + + return TCL_OK; +} + + + +/* Thread-safe version */ +static int +Rrd_Dump(ClientData clientData, Tcl_Interp *interp, int argc, CONST84 char *argv[]) +{ + if (argc < 2) { + Tcl_AppendResult(interp, "RRD Error: needs rrd filename", + (char *) NULL); + return TCL_ERROR; + } + + rrd_dump_r(argv[1], NULL); + + /* NOTE: rrd_dump() writes to stdout. No interaction with TCL. */ + + if (rrd_test_error()) { + Tcl_AppendResult(interp, "RRD Error: ", + rrd_get_error(), (char *) NULL); + rrd_clear_error(); + return TCL_ERROR; + } + + return TCL_OK; +} + + + +/* Thread-safe version */ +static int +Rrd_Last(ClientData clientData, Tcl_Interp *interp, int argc, CONST84 char *argv[]) +{ + time_t t; + + if (argc < 2) { + Tcl_AppendResult(interp, "RRD Error: needs rrd filename", + (char *) NULL); + return TCL_ERROR; + } + + t = rrd_last_r(argv[1]); + + if (rrd_test_error()) { + Tcl_AppendResult(interp, "RRD Error: ", + rrd_get_error(), (char *) NULL); + rrd_clear_error(); + return TCL_ERROR; + } + + Tcl_SetIntObj(Tcl_GetObjResult(interp), t); + + return TCL_OK; +} + + + +/* Thread-safe version */ +static int +Rrd_Update(ClientData clientData, Tcl_Interp *interp, int argc, CONST84 char *argv[]) +{ + int argv_i; + char **argv2, *template = NULL; + + argv2 = getopt_init(argc, argv); + + for (argv_i = 1; argv_i < argc; argv_i++) { + if (!strcmp(argv2[argv_i], "--template") || !strcmp(argv2[argv_i], "-t")) { + if (argv_i++>=argc) { + Tcl_AppendResult(interp, "RRD Error: option '", + argv2[argv_i - 1], "' needs an argument", (char *) NULL); + if (template != NULL) { + free(template); + } + getopt_cleanup(argc, argv2); + return TCL_ERROR; + } + if (template != NULL) { + free(template); + } + template = strdup(argv2[argv_i]); + getopt_free_element(argv2, argv_i - 1); + getopt_free_element(argv2, argv_i); + } else if (!strcmp(argv2[argv_i], "--")) { + getopt_free_element(argv2, argv_i); + break; + } else if (argv2[argv_i][0]=='-') { + Tcl_AppendResult(interp, "RRD Error: unknown option '", + argv2[argv_i], "'", (char *) NULL); + if (template != NULL) { + free(template); + } + getopt_cleanup(argc, argv2); + return TCL_ERROR; + } + } + + getopt_squieeze(&argc, argv2); + + if (argc < 2) { + Tcl_AppendResult(interp, "RRD Error: needs rrd filename", + (char *) NULL); + if (template != NULL) { + free(template); + } + getopt_cleanup(argc, argv2); + return TCL_ERROR; + } + + rrd_update_r(argv2[1], template, argc - 2, argv2 + 2); + + if (template != NULL) { + free(template); + } + getopt_cleanup(argc, argv2); + + if (rrd_test_error()) { + Tcl_AppendResult(interp, "RRD Error: ", + rrd_get_error(), (char *) NULL); + rrd_clear_error(); + return TCL_ERROR; + } + + return TCL_OK; +} + +static int +Rrd_Lastupdate(ClientData clientData, Tcl_Interp *interp, int argc, CONST84 char *argv[]) +{ + time_t last_update; + char **argv2; + char **ds_namv; + char **last_ds; + char s[30]; + Tcl_Obj *listPtr; + unsigned long ds_cnt, i; + + argv2 = getopt_init(argc, argv); + if (rrd_lastupdate(argc-1, argv2, &last_update, + &ds_cnt, &ds_namv, &last_ds) == 0) { + listPtr = Tcl_GetObjResult(interp); + for (i=0; iis< a Tcl fileID + */ + if (!(mode & TCL_WRITABLE)) { + Tcl_AppendResult(interp, "channel \"", argv[1], + "\" wasn't opened for writing", (char *) NULL); + return TCL_ERROR; + } + /* + * Must flush channel to make sure any buffered data is written before + * rrd_graph() writes to the stream + */ + if (Tcl_Flush(channel) != TCL_OK) { + Tcl_AppendResult(interp, "flush failed for \"", argv[1], "\": ", + strerror(Tcl_GetErrno()), (char *) NULL); + return TCL_ERROR; + } + if (Tcl_GetChannelHandle(channel, TCL_WRITABLE, &fd1) != TCL_OK) { + Tcl_AppendResult(interp, "cannot get file descriptor associated with \"", + argv[1], "\"", (char *) NULL); + return TCL_ERROR; + } + /* + * Must dup() file descriptor so we can fclose(stream), otherwise the fclose() + * would close Tcl's file descriptor + */ + if ((fd2 = dup((int)fd1)) == -1) { + Tcl_AppendResult(interp, "dup() failed for file descriptor associated with \"", + argv[1], "\": ", strerror(errno), (char *) NULL); + return TCL_ERROR; + } + /* + * rrd_graph() wants a FILE* + */ + if ((stream = fdopen(fd2, "wb")) == NULL) { + Tcl_AppendResult(interp, "fdopen() failed for file descriptor associated with \"", + argv[1], "\": ", strerror(errno), (char *) NULL); + close(fd2); /* plug potential file descriptor leak */ + return TCL_ERROR; + } + + save = argv[1]; + argv[1] = "-"; + argv2 = getopt_init(argc, argv); + argv[1] = save; + } else { + Tcl_ResetResult(interp); /* clear error from Tcl_GetChannel() */ + argv2 = getopt_init(argc, argv); + } + + rc = rrd_graph(argc, argv2, &calcpr, &xsize, &ysize, stream, &ymin, &ymax); + getopt_cleanup(argc, argv2); + + if (stream != NULL) + fclose(stream); /* plug potential malloc & file descriptor leak */ + + if (rc != -1) { + sprintf(dimensions, "%d %d", xsize, ysize); + Tcl_AppendResult(interp, dimensions, (char *) NULL); + if (calcpr) { +#if 0 + int i; + + for(i = 0; calcpr[i]; i++){ + printf("%s\n", calcpr[i]); + free(calcpr[i]); + } +#endif + free(calcpr); + } + } + + if (rrd_test_error()) { + Tcl_AppendResult(interp, "RRD Error: ", + rrd_get_error(), (char *) NULL); + rrd_clear_error(); + return TCL_ERROR; + } + + return TCL_OK; +} + + + +static int +Rrd_Tune(ClientData clientData, Tcl_Interp *interp, int argc, CONST84 char *argv[]) +{ + char **argv2; + + argv2 = getopt_init(argc, argv); + rrd_tune(argc, argv2); + getopt_cleanup(argc, argv2); + + if (rrd_test_error()) { + Tcl_AppendResult(interp, "RRD Error: ", + rrd_get_error(), (char *) NULL); + rrd_clear_error(); + return TCL_ERROR; + } + + return TCL_OK; +} + + + +static int +Rrd_Resize(ClientData clientData, Tcl_Interp *interp, int argc, CONST84 char *argv[]) +{ + char **argv2; + + argv2 = getopt_init(argc, argv); + rrd_resize(argc, argv2); + getopt_cleanup(argc, argv2); + + if (rrd_test_error()) { + Tcl_AppendResult(interp, "RRD Error: ", + rrd_get_error(), (char *) NULL); + rrd_clear_error(); + return TCL_ERROR; + } + + return TCL_OK; +} + + + +static int +Rrd_Restore(ClientData clientData, Tcl_Interp *interp, int argc, CONST84 char *argv[]) +{ + char **argv2; + + argv2 = getopt_init(argc, argv); + rrd_restore(argc, argv2); + getopt_cleanup(argc, argv2); + + if (rrd_test_error()) { + Tcl_AppendResult(interp, "RRD Error: ", + rrd_get_error(), (char *) NULL); + rrd_clear_error(); + return TCL_ERROR; + } + + return TCL_OK; +} + + + +/* + * The following structure defines the commands in the Rrd extension. + */ + +typedef struct { + char *name; /* Name of the command. */ + Tcl_CmdProc *proc; /* Procedure for command. */ + int hide; /* Hide if safe interpreter */ +} CmdInfo; + +static CmdInfo rrdCmds[] = { + { "Rrd::create", Rrd_Create, 1 }, /* Thread-safe version */ + { "Rrd::dump", Rrd_Dump, 0 }, /* Thread-safe version */ + { "Rrd::last", Rrd_Last, 0 }, /* Thread-safe version */ + { "Rrd::lastupdate", Rrd_Lastupdate, 0 }, /* Thread-safe version */ + { "Rrd::update", Rrd_Update, 1 }, /* Thread-safe version */ + { "Rrd::fetch", Rrd_Fetch, 0 }, + { "Rrd::graph", Rrd_Graph, 1 }, /* Due to RRD's API, a safe + interpreter cannot create + a graph since it writes to + a filename supplied by the + caller */ + { "Rrd::tune", Rrd_Tune, 1 }, + { "Rrd::resize", Rrd_Resize, 1 }, + { "Rrd::restore", Rrd_Restore, 1 }, + { (char *) NULL, (Tcl_CmdProc *) NULL, 0 } +}; + + + +static int +init(Tcl_Interp *interp, int safe) +{ + CmdInfo *cmdInfoPtr; + Tcl_CmdInfo info; + + if ( Tcl_InitStubs(interp,TCL_VERSION,0) == NULL ) + return TCL_ERROR; + + if (Tcl_PkgRequire(interp, "Tcl", TCL_VERSION, 1) == NULL) { + return TCL_ERROR; + } + + /* + * Why a global array? In keeping with the Rrd:: namespace, why + * not simply create a normal variable Rrd::version and set it? + */ + Tcl_SetVar2(interp, "rrd", "version", VERSION, TCL_GLOBAL_ONLY); + + for (cmdInfoPtr = rrdCmds; cmdInfoPtr->name != NULL; cmdInfoPtr++) { + /* + * Check if the command already exists and return an error + * to ensure we detect name clashes while loading the Rrd + * extension. + */ + if (Tcl_GetCommandInfo(interp, cmdInfoPtr->name, &info)) { + Tcl_AppendResult(interp, "command \"", cmdInfoPtr->name, + "\" already exists", (char *) NULL); + return TCL_ERROR; + } + if (safe && cmdInfoPtr->hide) { +#if 0 + /* + * Turns out the one cannot hide a command in a namespace + * due to a limitation of Tcl, one can only hide global + * commands. Thus, if we created the commands without + * the Rrd:: namespace in a safe interpreter, then the + * "unsafe" commands could be hidden -- which would allow + * an owning interpreter either un-hiding them or doing + * an "interp invokehidden". If the Rrd:: namespace is + * used, then it's still possible for the owning interpreter + * to fake out the missing commands: + * + * # Make all Rrd::* commands available in master interperter + * package require Rrd + * set safe [interp create -safe] + * # Make safe Rrd::* commands available in safe interperter + * interp invokehidden $safe -global load ./tclrrd1.2.11.so + * # Provide the safe interpreter with the missing commands + * $safe alias Rrd::update do_update $safe + * proc do_update {which_interp $args} { + * # Do some checking maybe... + * : + * return [eval Rrd::update $args] + * } + * + * Our solution for now is to just not create the "unsafe" + * commands in a safe interpreter. + */ + if (Tcl_HideCommand(interp, cmdInfoPtr->name, cmdInfoPtr->name) != TCL_OK) + return TCL_ERROR; +#endif + } + else + Tcl_CreateCommand(interp, cmdInfoPtr->name, cmdInfoPtr->proc, + (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL); + } + + if (Tcl_PkgProvide(interp, "Rrd", VERSION) != TCL_OK) { + return TCL_ERROR; + } + + return TCL_OK; +} + +int +Tclrrd_Init(Tcl_Interp *interp) +{ + return init(interp, 0); +} + +/* + * See the comments above and note how few commands are considered "safe"... + * Using rrdtool in a safe interpreter has very limited functionality. It's + * tempting to just return TCL_ERROR and forget about it. + */ +int +Tclrrd_SafeInit(Tcl_Interp *interp) +{ + return init(interp, 1); +} diff --git a/program/configure.ac b/program/configure.ac new file mode 100644 index 00000000..09e02605 --- /dev/null +++ b/program/configure.ac @@ -0,0 +1,676 @@ +dnl RRDtool AutoConf script ... +dnl --------------------------- +dnl +dnl Created by Jeff Allen, Tobi Oetiker, Blair Zajac +dnl +dnl Inspiration from http://autoconf-archive.cryp.to + +dnl tell automake the this script is for rrdtool +dnl the official version number is +dnl a.b.c +AC_INIT([rrdtool],[1.2.23]) +dnl for testing a numberical version number comes handy +dnl the released version are +dnl a.bccc +dnl the devl versions will be something like +dnl a.b999yymmddhh +NUMVERS=1.2023 +AC_SUBST(NUMVERS) +AC_CANONICAL_TARGET +AM_INIT_AUTOMAKE +AC_CONFIG_HEADERS([rrd_config.h]) + +dnl all our local stuff like install scripts and include files +dnl is in there + + +dnl determine the type of system we are running on + +AC_SUBST(VERSION) + +AC_PREFIX_DEFAULT( /usr/local/rrdtool-$PACKAGE_VERSION ) + +dnl Minimum Autoconf version required. +AC_PREREQ(2.59) + +dnl At the TOP of the HEADER + +AH_TOP([ + +#ifndef RRD_CONFIG_H +#define RRD_CONFIG_H +/* IEEE can be prevented from raising signals with fpsetmask(0) */ +#undef MUST_DISABLE_FPMASK + +/* IEEE math only works if SIGFPE gets actively set to IGNORE */ + +#undef MUST_DISABLE_SIGFPE + +/* realloc does not support NULL as argument */ +#undef NO_NULL_REALLOC + + ]) + +AH_BOTTOM([ +/* enable posix_fadvise on linux */ +#if defined(HAVE_POSIX_FADVISE) && defined(HAVE_FCNTL_H) +#define _XOPEN_SOURCE 600 +#include +#endif + +/* define strrchr, strchr and memcpy, memmove in terms of bsd funcs + make sure you are NOT using bcopy, index or rindex in the code */ + +#ifdef STDC_HEADERS +# include +#else +# ifndef HAVE_STRCHR +# define strchr index +# define strrchr rindex +# endif +char *strchr (), *strrchr (); +# ifndef HAVE_MEMMOVE +# define memcpy(d, s, n) bcopy ((s), (d), (n)) +# define memmove(d, s, n) bcopy ((s), (d), (n)) +# endif +#endif + +#ifdef NO_NULL_REALLOC +# define rrd_realloc(a,b) ( (a) == NULL ? malloc( (b) ) : realloc( (a) , (b) )) +#else +# define rrd_realloc(a,b) realloc((a), (b)) +#endif + +#ifdef NEED_MALLOC_MALLOC_H +# include +#endif + +#ifdef HAVE_MATH_H +# include +#endif + +#ifdef HAVE_FLOAT_H +# include +#endif + +#ifdef HAVE_IEEEFP_H +# include +#endif + +#ifdef HAVE_FP_CLASS_H +# include +#endif + +/* for Solaris */ +#if (! defined(HAVE_ISINF) && defined(HAVE_FPCLASS)) +# define HAVE_ISINF 1 +# define isinf(a) (fpclass(a) == FP_NINF || fpclass(a) == FP_PINF) +#endif + +/* solaris 10 it defines isnan such that only forte can compile it ... bad bad */ +#if (defined(HAVE_ISNAN) && defined(isnan) && defined(HAVE_FPCLASS)) +# undef isnan +# define isnan(a) (fpclass(a) == FP_SNAN || fpclass(a) == FP_QNAN) +#endif + +/* for OSF1 Digital Unix */ +#if (! defined(HAVE_ISINF) && defined(HAVE_FP_CLASS) && defined(HAVE_FP_CLASS_H)) +# define HAVE_ISINF 1 +# define isinf(a) (fp_class(a) == FP_NEG_INF || fp_class(a) == FP_POS_INF) +#endif + +#if (! defined(HAVE_ISINF) && defined(HAVE_FPCLASSIFY) && defined(FP_PLUS_INF) && defined(FP_MINUS_INF)) +# define HAVE_ISINF 1 +# define isinf(a) (fpclassify(a) == FP_MINUS_INF || fpclassify(a) == FP_PLUS_INF) +#endif + +#if (! defined(HAVE_ISINF) && defined(HAVE_FPCLASSIFY) && defined(FP_INFINITE)) +# define HAVE_ISINF 1 +# define isinf(a) (fpclassify(a) == FP_INFINITE) +#endif + +/* for AIX */ +#if (! defined(HAVE_ISINF) && defined(HAVE_CLASS)) +# define HAVE_ISINF 1 +# define isinf(a) (class(a) == FP_MINUS_INF || class(a) == FP_PLUS_INF) +#endif + +#if (! defined (HAVE_FINITE) && defined (HAVE_ISFINITE)) +# define HAVE_FINITE 1 +# define finite(a) isfinite(a) +#endif + +#if (! defined(HAVE_FINITE) && defined(HAVE_ISNAN) && defined(HAVE_ISINF)) +# define HAVE_FINITE 1 +# define finite(a) (! isnan(a) && ! isinf(a)) +#endif + +#ifndef HAVE_FINITE +#error "Can't compile without finite function" +#endif + +#ifndef HAVE_ISINF +#error "Can't compile without isinf function" +#endif + +#endif /* RRD_CONFIG_H */ +]) + +dnl Process Special Options +dnl ----------------------------------- + +dnl How the vertical axis label is printed +AC_ARG_VAR(RRDGRAPH_YLEGEND_ANGLE, + [Vertical label angle: 90.0 (default) or 270.0]) +AC_DEFINE_UNQUOTED(RRDGRAPH_YLEGEND_ANGLE,${RRDGRAPH_YLEGEND_ANGLE:-90.0}, + [Vertical label angle: 90.0 (default) or 270.0]) + +AC_ARG_ENABLE(rrdcgi,[ --disable-rrdcgi disable building of rrdcgi], +[],[enable_rrdcgi=yes]) + +dnl Check if we run on a system that has fonts +AC_ARG_WITH(rrd-default-font, +[ --with-rrd-default-font=[OPTIONS] set the full path to your default font.], +[RRD_DEFAULT_FONT=$withval],[ + if test -d ${WINDIR:-nodir}/cour.ttf ; then + RRD_DEFAULT_FONT=`cd $WINDIR;pwd`/cour.ttf + else + RRD_DEFAULT_FONT='$(fontsdir)/$(fonts_DATA)' + fi +]) + +dnl Use mmap in rrd_update instead of seek+write +AC_ARG_ENABLE([mmap], +[ --disable-mmap disable mmap in rrd_update, use seek+write instead], +[], +[enable_mmap=yes]) + + + AC_ARG_ENABLE(pthread,[ --disable-pthread disable multithread support], +[],[enable_pthread=yes]) + + + +CONFIGURE_PART(Audit Compilation Environment) + + +dnl Check for the compiler and static/shared library creation. +AC_PROG_CC +AC_PROG_CPP +AC_PROG_LIBTOOL + +dnl which flags does the compile support? +if test "$GCC" = "yes"; then + for flag in -fno-strict-aliasing -Wall -std=gnu99 -pedantic -Wshadow -Wpointer-arith -Wcast-align -Wmissing-prototypes -Wmissing-declarations -Wnested-externs -Winline -W; do + oCFLAGS=$CFLAGS + CFLAGS="$CFLAGS $flag" + cachename=rd_cv_gcc_flag_`echo $flag|sed 's/[[^A-Za-z]]/_/g'` + AC_CACHE_CHECK([if gcc likes the $flag flag], $cachename, + [AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[return 0 ]])],[eval $cachename=yes],[eval $cachename=no])]) + if eval test \$$cachename = no; then + CFLAGS=$oCFLAGS + fi + done +fi + + + +AC_SUBST(RRD_DEFAULT_FONT) + +CONFIGURE_PART(Checking for Header Files) + +dnl Checks for header files. +AC_HEADER_STDC +AC_HEADER_DIRENT +AC_CHECK_HEADERS(sys/stat.h sys/types.h fcntl.h locale.h fp_class.h malloc.h unistd.h ieeefp.h math.h sys/times.h sys/param.h sys/resource.h float.h) + +dnl Checks for typedefs, structures, and compiler characteristics. +AC_C_CONST +AC_HEADER_TIME +AC_STRUCT_TM + +dnl Checks for libraries. +AC_CHECK_FUNC(acos, , AC_CHECK_LIB(m, acos)) + +dnl add pic flag in any case this makes sure all our code is relocatable +eval `./libtool --config | grep pic_flag` +CFLAGS="$CFLAGS $pic_flag" + +CONFIGURE_PART(Test Library Functions) + +dnl Checks for library functions. +AC_FUNC_STRFTIME +AC_FUNC_VPRINTF + +AC_C_BIGENDIAN + +dnl for each function found we get a definition in config.h +dnl of the form HAVE_FUNCTION + +AC_CHECK_FUNCS(tzset mbstowcs opendir readdir chdir chroot getuid setlocale strerror strerror_r snprintf vsnprintf fpclass class fp_class isnan memmove strchr mktime getrusage gettimeofday posix_fadvise madvise) + +AC_CHECK_DECLS(fdatasync, [], [], [#include ]) +AC_CHECK_DECLS(posix_fadvise, [], [], [#define _XOPEN_SOURCE 600 +#include ]) +AC_CHECK_DECLS(madvise, [], [], [#include ]) + +if test "x$enable_mmap" = xyes; then + case "$host" in + *cygwin*) + # the normal mmap test does not work in cygwin + AC_CHECK_FUNCS(mmap) + if [ "x${ac_cv_func_mmap}" = xyes ]; then + ac_cv_func_mmap_fixed_mapped=yes + fi + ;; + *) + AC_FUNC_MMAP + ;; + esac +fi + + +CONFIGURE_PART(IEEE Math Checks) + +dnl HP-UX 11.00 does not have finite but does have isfinite as a macro so we need +dnl actual code to check if this works +AC_CHECK_FUNCS(fpclassify, , + [AC_MSG_CHECKING(for fpclassify with ) + AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include +volatile int x;volatile float f; ]], [[x = fpclassify(f)]])],[AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_FPCLASSIFY)],[AC_MSG_RESULT(no)])]) +AC_CHECK_FUNCS(finite, , + [AC_CHECK_FUNCS(isfinite, , + [AC_MSG_CHECKING(for isfinite with ) + AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include +volatile int x;volatile float f; ]], [[x = isfinite(f)]])],[AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_ISFINITE)],[AC_MSG_RESULT(no)])])]) +AC_CHECK_FUNCS(isinf, , + [AC_MSG_CHECKING(for isinf with ) + AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include +volatile int x;volatile float f; ]], [[x = isinf(f)]])],[AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_ISINF)],[AC_MSG_RESULT(no)])]) + +AC_FULL_IEEE + +CONFIGURE_PART(Resolve Portability Issues) + +dnl what does realloc do if it gets called with a NULL pointer + +AC_CACHE_CHECK([if realloc can deal with NULL], rd_cv_null_realloc, +[AC_RUN_IFELSE([AC_LANG_SOURCE([[#include + int main(void){ + char *x = NULL; + x = realloc (x,10); + if (x==NULL) return 1; + return 0; + }]])],[rd_cv_null_realloc=yes],[rd_cv_null_realloc=nope],[:])]) + +if test x"$rd_cv_null_realloc" = xnope; then +AC_DEFINE(NO_NULL_REALLOC) +fi + +AC_LANG_PUSH(C) +dnl solaris has some odd defines it needs in order to propperly compile ctime_r +AC_MSG_CHECKING([if ctime_r need special care to act posixly correct]) +AC_LINK_IFELSE( + AC_LANG_PROGRAM( + [[#include ]], + [[ctime_r(NULL,NULL,0)]] + ), + [ CPPFLAGS="$CPPFLAGS -D_POSIX_PTHREAD_SEMANTICS" + AC_LINK_IFELSE( + AC_LANG_PROGRAM( + [[#include ]], + [[ctime_r(NULL,NULL)]] + ), + [AC_MSG_RESULT([yes, this seems to be solaris style])], + [AC_MSG_ERROR([Can't figure how to compile ctime_r])] + ) + ], + [ AC_LINK_IFELSE( + AC_LANG_PROGRAM( + [[#include ]], + [[ctime_r(NULL,NULL)]] + ), + [AC_MSG_RESULT(no)], + [AC_MSG_ERROR([Can't figure how to compile ctime_r])] + ) + ] +) +AC_LANG_POP(C) + +dnl Check for pthreads +dnl http://autoconf-archive.cryp.to/acx_pthread.m4 + +AC_SUBST(MULTITHREAD_CFLAGS) +AC_SUBST(MULTITHREAD_LDFLAGS) + +if test $enable_pthread != no; then + ACX_PTHREAD([ + MULTITHREAD_CFLAGS=$PTHREAD_CFLAGS + MULTITHREAD_LDFLAGS=$PTHREAD_LIBS + ], + []) +fi + +dnl since we use lots of *_r functions all over the code we better +dnl make sure they are known + +if test "x$x_rflag" != "xno"; then + CPPFLAGS="$CPPFLAGS $x_rflag" +fi + +AM_CONDITIONAL(BUILD_MULTITHREAD,[test $enable_pthread != no]) + +AC_LANG_PUSH(C) +dnl see if we have to include malloc/malloc.h +AC_MSG_CHECKING([do we need malloc/malloc.h]) +AC_LINK_IFELSE( + AC_LANG_PROGRAM( + [[#include ]], + [[malloc(1)]] + ), + [ AC_MSG_RESULT([nope, works out of the box]) ], + [ AC_LINK_IFELSE( + AC_LANG_PROGRAM( + [[#include + #include ]], + [[malloc(1)]] + ), + [AC_DEFINE(NEED_MALLOC_MALLOC_H) + AC_MSG_RESULT([yes we do])], + [AC_MSG_ERROR([Can not figure how to compile malloc])] + ) + ] +) +AC_LANG_POP(C) + +CONFIGURE_PART(Find 3rd-Party Libraries) + + +AM_CONDITIONAL(BUILD_RRDCGI,[test $enable_rrdcgi != no]) + +CORE_LIBS="$LIBS" + +EX_CHECK_ALL(art_lgpl_2, art_vpath_add_point, libart_lgpl/libart.h, libart-2.0, 2.3.17, ftp://ftp.gnome.org/pub/GNOME/sources/libart_lgpl/2.3/, /usr/include/libart-2.0) +EX_CHECK_ALL(z, zlibVersion, zlib.h, zlib, 1.2.3, http://www.gzip.org/zlib/, "") +EX_CHECK_ALL(png, png_access_version_number, png.h, libpng, 1.2.10, http://prdownloads.sourceforge.net/libpng/, "") +EX_CHECK_ALL(freetype, FT_Init_FreeType, ft2build.h, freetype2, 2.1.10, http://prdownloads.sourceforge.net/freetype/, /usr/include/freetype2) + +if test "$EX_CHECK_ALL_ERR" = "YES"; then + AC_MSG_ERROR([Please fix the library issues listed above and try again.]) +fi + +ALL_LIBS="$LIBS" +LIBS= + +AC_SUBST(CORE_LIBS) +AC_SUBST(ALL_LIBS) + +CONFIGURE_PART(Prep for Building Language Bindings) + +dnl Check for Perl. +AC_PATH_PROG(PERL, perl, no) + +AC_ARG_ENABLE(perl,[ --disable-perl do not build the perl modules], +[],[enable_perl=yes]) + + +AC_ARG_VAR(PERLCC, [[] C compiler for Perl modules]) +AC_ARG_VAR(PERLCCFLAGS, [[] CC flags for Perl modules]) +AC_ARG_VAR(PERLLD, [[same as PERLCC] Linker for Perl modules]) +AC_ARG_VAR(PERLLDFLAGS, [[] LD flags for Perl modules]) + +if test "x$PERL" = "xno" -o x$enable_perl = xno; then + COMP_PERL= +else + COMP_PERL="perl_piped perl_shared" + AC_MSG_CHECKING(for the perl version you are running) + PERL_VERSION=`$PERL -MConfig -e 'print $Config{version}'` + AC_MSG_RESULT($PERL_VERSION) + if test -z "$PERLCC"; then + AC_MSG_CHECKING(for the C compiler perl wants to use to build its modules) + perlcc=`$PERL -MConfig -e 'print $Config{cc}'` + AC_MSG_RESULT($perlcc) + if test ! -x "$perlcc"; then + AC_PATH_PROG(PERL_CC, ${perlcc}, no) + if test "$PERL_CC" = "no"; then + AC_MSG_WARN([ +I would not find the Compiler ($perlcc) that was originally used to compile +your perl binary. You should either make sure that this compiler is +available on your system, pick an other compiler and set PERLCC +appropriately, or use a different perl setup that was compiled locally. + +I will disable the compilation of the RRDs perl module for now. +]) + COMP_PERL="perl_piped" + fi + fi + fi +fi + +AC_MSG_CHECKING(Perl Modules to build) +AC_MSG_RESULT(${COMP_PERL:-No Perl Modules will be built}) + +# Options to pass when configuring perl module +ppref=$prefix +test "$ppref" = "NONE" && ppref=$ac_default_prefix + +PERL_MAKE_OPTIONS="PREFIX=$ppref LIB=$ppref/lib/perl/$PERL_VERSION" + +dnl pass additional perl options when generating Makefile from Makefile.PL +AC_ARG_ENABLE(perl-site-install, +[ --enable-perl-site-install by default the rrdtool perl modules are installed + together with rrdtool in $prefix/lib/perl. You have to + put a 'use lib qw($prefix/lib/perl)' into your scripts + when you want to use them. When you set this option + the perl modules will get installed wherever + your perl setup thinks it is best.], +[PERL_MAKE_OPTIONS=],[]) + +if test ! -z "$PERLCC"; then + PERL_MAKE_OPTIONS="$PERL_MAKE_OPTIONS CC=$PERLCC" + + if test ! -z "$PERLCCFLAGS"; then + PERL_MAKE_OPTIONS="$PERL_MAKE_OPTIONS CCFLAGS=$PERLCCFLAGS" + fi + + if test -z "$PERLLD"; then + PERLLD=$PERLCC + fi + PERL_MAKE_OPTIONS="$PERL_MAKE_OPTIONS LD=$PERLLD" + + if test ! -z "$PERLLDFLAGS"; then + PERL_MAKE_OPTIONS="$PERL_MAKE_OPTIONS LDFLAGS=$PERLLDFLAGS" + fi +fi + +AC_ARG_WITH(perl-options, +[ --with-perl-options=[OPTIONS] options to pass on command-line when + generating Makefile from Makefile.PL. If you set this + option, interesting things may happen unless you know + what you are doing!], +[PERL_MAKE_OPTIONS=$withval]) + +AC_SUBST(PERL_MAKE_OPTIONS) +AC_SUBST(PERL) +AC_SUBST(COMP_PERL) +AC_SUBST(PERL_VERSION) + +dnl Check for Ruby. +AC_PATH_PROG(RUBY, ruby, no) + +AC_ARG_ENABLE(ruby,[ --disable-ruby do not build the ruby modules], +[],[enable_ruby=yes]) + +AC_MSG_CHECKING(if ruby modules can be built) + +if test "x$RUBY" = "xno" -o x$enable_ruby = xno; then + COMP_RUBY= + AC_MSG_RESULT(No .. Ruby not found or disabled) +else + if $RUBY -e 'require "mkmf"' >/dev/null 2>&1; then + COMP_RUBY="ruby" + AC_MSG_RESULT(YES) + else + COMP_RUBY= + AC_MSG_RESULT(Ruby found but mkmf is missing! Install the -dev package) + fi +fi + + +dnl pass additional ruby options when generating Makefile from Makefile.PL +AC_ARG_ENABLE(ruby-site-install, +[ --enable-ruby-site-install by default the rrdtool ruby modules are installed + together with rrdtool in $prefix/lib/ruby. You have to + add $prefix/lib/ruby/$ruby_version/$sitearch to you $: variable + for ruby to find the RRD.so file.], +[RUBY_MAKE_OPTIONS=],[RUBY_MAKE_OPTIONS="sitedir=$prefix/lib/ruby"]) + + +AC_ARG_WITH(ruby-options, +[ --with-ruby-options=[OPTIONS] options to pass on command-line when + generating Makefile from extconf.rb. If you set this + option, interesting things may happen unless you know + what you are doing!], +[RUBY_MAKE_OPTIONS=$withval]) + +AC_SUBST(RUBY_MAKE_OPTIONS) +AC_SUBST(RUBY) +AC_SUBST(COMP_RUBY) + + +enable_tcl_site=no + +AC_ARG_ENABLE(tcl,[ --disable-tcl do not build the tcl modules], +[],[enable_tcl=yes]) + +if test "$enable_tcl" = "yes"; then + dnl Check for Tcl. + withval="" + AC_ARG_WITH(tcllib,[ --with-tcllib=DIR location of the tclConfig.sh]) + enable_tcl=no + for dir in $withval /usr/lib /usr/local/lib; do + AC_MSG_CHECKING(for tclConfig.sh in $dir) + if test -f "$dir/tclConfig.sh" ; then + tcl_config=$dir/tclConfig.sh + enable_tcl=yes + AC_MSG_RESULT(yes) + break + else + AC_MSG_RESULT(no) + fi + done + + if test "$enable_tcl" = "no"; then + AC_MSG_WARN([tclConfig.sh not found - Tcl interface won't be built]) + else + . $tcl_config + TCL_PACKAGE_DIR="$TCL_PACKAGE_PATH/tclrrd$VERSION" + fi + AC_ARG_ENABLE(tcl,[ --enable-tcl-site install the tcl extension in the tcl tree], + [],[enable_tcl_site=yes]) + +fi + +AM_CONDITIONAL(BUILD_TCL, test "$enable_tcl" = "yes" ) +AM_CONDITIONAL(BUILD_TCL_SITE, test "$enable_tcl_site" = "yes" ) + +AC_SUBST(TCL_PREFIX) +AC_SUBST(TCL_SHLIB_CFLAGS) +AC_SUBST(TCL_SHLIB_LD) +AC_SUBST(TCL_SHLIB_SUFFIX) +AC_SUBST(TCL_PACKAGE_PATH) +AC_SUBST(TCL_LD_SEARCH_FLAGS) +AC_SUBST(TCL_STUB_LIB_SPEC) +AC_SUBST(TCL_VERSION) +AC_SUBST(TCL_PACKAGE_DIR) + +AC_ARG_ENABLE(python,[ --disable-python do not build the python modules], +[],[enable_python=yes]) + +if test "$enable_python" = "yes"; then +dnl Check for python +AM_PATH_PYTHON(2.3,[],[enable_python=no]) +AM_CHECK_PYTHON_HEADERS(,[enable_python=no;AC_MSG_WARN(could not find Python headers)]) +fi + +if test x$enable_python = xno; then + COMP_PYTHON= +else + COMP_PYTHON="python" +fi + +AC_SUBST(COMP_PYTHON) + +dnl Check for nroff +AC_PATH_PROGS(NROFF, gnroff nroff) +AC_PATH_PROGS(TROFF, groff troff) + +AC_ARG_VAR(RRDDOCDIR, [[DATADIR/doc/PACKAGE-VERSION] Documentation directory]) +if test -z "$RRDDOCDIR"; then + RRDDOCDIR='${datadir}/doc/${PACKAGE}-${VERSION}'; fi + + +CONFIGURE_PART(Apply Configuration Information) + +AC_CONFIG_FILES([examples/shared-demo.pl]) +AC_CONFIG_FILES([examples/piped-demo.pl]) +AC_CONFIG_FILES([examples/stripes.pl]) +AC_CONFIG_FILES([examples/bigtops.pl]) +AC_CONFIG_FILES([examples/minmax.pl]) +AC_CONFIG_FILES([examples/4charts.pl]) +AC_CONFIG_FILES([examples/perftest.pl]) +AC_CONFIG_FILES([examples/Makefile]) +AC_CONFIG_FILES([doc/Makefile]) +AC_CONFIG_FILES([src/Makefile]) +AC_CONFIG_FILES([bindings/Makefile]) +AC_CONFIG_FILES([bindings/tcl/Makefile]) +AC_CONFIG_FILES([bindings/tcl/ifOctets.tcl]) +AC_CONFIG_FILES([Makefile]) + +AC_CONFIG_COMMANDS([default],[[ chmod +x examples/*.pl]],[[]]) +AC_OUTPUT + +AC_MSG_CHECKING(in) +AC_MSG_RESULT(and out again) + +echo $ECHO_N "ordering CD from http://tobi.oetiker.ch/wish $ECHO_C" 1>&6 +sleep 1 +echo $ECHO_N ".$ECHO_C" 1>&6 +sleep 1 +echo $ECHO_N ".$ECHO_C" 1>&6 +sleep 1 +echo $ECHO_N ".$ECHO_C" 1>&6 +sleep 1 +echo $ECHO_N ".$ECHO_C" 1>&6 +sleep 1 +AC_MSG_RESULT([ just kidding ;-)]) +echo +echo "----------------------------------------------------------------" +echo "Config is DONE!" +echo +echo " With MMAP IO: $ac_cv_func_mmap_fixed_mapped" +echo " Perl Modules: $COMP_PERL" +echo " Perl Binary: $PERL" +echo " Perl Version: $PERL_VERSION" +echo " Perl Options: $PERL_MAKE_OPTIONS" +echo " Ruby Modules: $COMP_RUBY" +echo " Ruby Binary: $RUBY" +echo " Ruby Options: $RUBY_MAKE_OPTIONS" +echo " Build Tcl Bindings: $enable_tcl" +echo " Build Python Bindings: $enable_python" +echo " Build rrdcgi: $enable_rrdcgi" +echo " Build librrd MT: $enable_pthread" +echo +echo +echo "Type 'make' to compile the software and use 'make install' to " +echo "install everything to: $prefix." +echo +echo " ... that wishlist is NO JOKE. If you find RRDtool useful" +echo "make me happy. Go to http://tobi.oetiker.ch/wish and" +echo "place an order." +echo +echo " -- Tobi Oetiker " +echo "----------------------------------------------------------------" diff --git a/program/debian/README.Debian b/program/debian/README.Debian new file mode 100644 index 00000000..ad7db631 --- /dev/null +++ b/program/debian/README.Debian @@ -0,0 +1,33 @@ +rrdtool for Debian +---------------------- + +The RRDtool distribution is split into several packages: + + +Package: rrdtool + Command-line utilities and documentation + +Package: librrd0 + Shared library + +Package: librrd0-dev + Static library + +Package: librrds-perl + Perl interface using the shared library (RRDs) + +Package: librrdp-perl + Perl interface using command pipes to the rrdtool program (RRDp) + +Package: rrdtool-tcl + TCL interface using the shared library (librrd) + + +Original by + + -- Matt Zimmerman , Fri, 10 Sep 1999 10:53:19 -0700 + +Hacked for 1.1.x by + + -- Mike Slifcak , Tue , 18 May 2004 20:38:40 +0200 + diff --git a/program/debian/build_freetype.sh b/program/debian/build_freetype.sh new file mode 100755 index 00000000..8edc6696 --- /dev/null +++ b/program/debian/build_freetype.sh @@ -0,0 +1,42 @@ +: +# Build freetype2 for rrdtool on Debian/Linux +# 12-May-2004 Mike Slifcak + +FOUND=`find /lib /usr/lib /usr/local/lib -name libfreetype.a | wc -l` +FOUND=`echo $FOUND` +if [ $FOUND -lt 1 ] ; then +######################################## +## Build the independent object freetype2 +######################################## +cd /tmp +rm -rf freetype*/ +if [ ! -e freetype*gz ] ; then + echo "get freetype-2.1.8 or stable from http://freetype.sf.net/" + exit 1 +fi +tar tzf freetype*gz > /dev/null 2>&1 +RC=$? +if [ $RC -ne 0 ] ; then + echo "Need one good freetype*gz. Just one. In /tmp. Thanks!" + exit 1 +fi + +echo -n "Testing " +ls freetype*gz +tar xzf freetype*gz +echo "Building freetype" +cd freetype-*/ +./configure --disable-shared > cfg.out 2>&1 +make > make.out 2>&1 +make install > inst.out 2>&1 +grep Error *.out +if [ $? -ne 1 ] ; then + echo "Building freetype failed. See `pwd`/*.out for details" + exit 1 +fi +cd .. +fi ## skip freetype build + +echo "Building freetype succeeded." + +exit 0 diff --git a/program/debian/changelog b/program/debian/changelog new file mode 100644 index 00000000..4d86445d --- /dev/null +++ b/program/debian/changelog @@ -0,0 +1,379 @@ +rrdtool (1.1.0-1) unstable; urgency=low + + * Fixed build dependencies + * Added libfreetype6-dev to build dependencies + * Changed rules file to compile against Debian version of freetype. + + -- Peter Hirdina Mon, 7 Jun 2004 17:10:10 +0200 + +rrdtool (1.1.0) unstable; urgency=low + + * grafted "debian" directory from 1.0.46 source + * no tcl package support due to build failures (see ##NO_TCL ) + + -- Mike Slifcak <> Wed, 12 May 2004 20:53:16 -0400 + +rrdtool (1.0.46-3) unstable; urgency=low + + * Add dependencies to librrd0-dev to ensure that static linking is + possible: libgd-gif1-dev, zlib1g-dev + * #include in rrd.h (Closes: #238849) + * Only link rrdcgi with -lcgi, not librrd + + -- Matt Zimmerman Fri, 19 Mar 2004 11:38:39 -0800 + +rrdtool (1.0.46-2) unstable; urgency=medium + + * Fix tcl module installation (Closes: #231171) + + -- Matt Zimmerman Wed, 4 Feb 2004 15:35:46 -0800 + +rrdtool (1.0.46-1) unstable; urgency=low + + * New upstream release + + -- Matt Zimmerman Mon, 12 Jan 2004 23:22:09 -0800 + +rrdtool (1.0.45-1) unstable; urgency=low + + * New upstream release + * Deal with changed tclrrd naming scheme + * Build with tcl8.4 + * Remove old dpkg-dev build-dependency; even woody is new enough + + -- Matt Zimmerman Wed, 17 Dec 2003 14:09:11 -0800 + +rrdtool (1.0.42-2) unstable; urgency=low + + * Build-Depends: libpng12-dev instead of libpng3-dev (when will the madness end?) + (Closes: #195224 + I hope this will also fix weird version mismatch problems (Closes: #194900) + + -- Matt Zimmerman Sat, 31 May 2003 15:06:42 -0400 + +rrdtool (1.0.42-1) unstable; urgency=low + + * New upstream release + * librrd0-dev Section: libdevel + * librrd[sp]-perl Section: perl + * Build with libpng3 (Closes: #189493) + + -- Matt Zimmerman Thu, 15 May 2003 19:44:37 -0400 + +rrdtool (1.0.40-2) unstable; urgency=low + + * Correctly suppress non-image output when writing image to stdout + (Closes: #182217) + + -- Matt Zimmerman Sat, 1 Mar 2003 16:59:39 -0500 + +rrdtool (1.0.40-1) unstable; urgency=low + + * New upstream release + * Remove unnecessary rrd_free to avoid crash on invalid option + (Closes: #166156) + * Clean example source code; some libtool cruft was getting installed in + /usr/share/doc/examples + + -- Matt Zimmerman Tue, 17 Dec 2002 22:11:46 -0500 + +rrdtool (1.0.39-2) unstable; urgency=low + + * Rebuild for perl 5.8 (Closes: #158728) + + -- Matt Zimmerman Sun, 25 Aug 2002 17:29:41 -0400 + +rrdtool (1.0.39-1) unstable; urgency=low + + * New upstream release + + -- Matt Zimmerman Wed, 31 Jul 2002 00:40:00 -0400 + +rrdtool (1.0.38-1) unstable; urgency=low + + * New upstream release (Closes: #148486) + + -- Matt Zimmerman Wed, 29 May 2002 12:28:28 -0400 + +rrdtool (1.0.35-2) unstable; urgency=low + + * Remove CVS directories with rm -rf. This broke autobuilds entirely. + (Closes: #140075) + + -- Matt Zimmerman Tue, 26 Mar 2002 17:15:01 -0500 + +rrdtool (1.0.35-1) unstable; urgency=low + + * New upstream release. + + -- Matt Zimmerman Sun, 24 Mar 2002 11:29:10 -0500 + +rrdtool (1.0.33-9) unstable; urgency=low + + * Call dh_shlibdeps with -l to allow it to find dependencies correctly + (Closes: #118629) + * Add call to ldconfig in postrm of librrd0 + * Remove .cvsignore files from installed examples + + -- Matt Zimmerman Sat, 10 Nov 2001 14:14:51 -0500 + +rrdtool (1.0.33-8) unstable; urgency=low + + * Use AM_MAINTAINER_MODE to keep the makefiles from trying to rebuild + autoconf/automake stuff during the build (Closes: #117599) + * Regenerated everything AGAIN. + * Use DESTDIR instead of prefix= during "make install" + + -- Matt Zimmerman Mon, 29 Oct 2001 22:22:07 -0500 + +rrdtool (1.0.33-7) unstable; urgency=low + + * Add an example to the rrddump documentation showing what is necessary + to transfer an RRD from one architecture to another. (Closes: #117147) + * Regenerated autoconf/automake configs + + -- Matt Zimmerman Mon, 29 Oct 2001 02:28:16 -0500 + +rrdtool (1.0.33-6) unstable; urgency=low + + * Spelling corrections from Martin Schulze (Closes: #110784) + + -- Matt Zimmerman Sat, 8 Sep 2001 13:13:22 -0400 + +rrdtool (1.0.33-5) unstable; urgency=low + + * Ship with pre-generated files from autoconf2.50/automake, rather than + pulling them in at build-time, in order to work around the current + libtool mess. This makes the diff less manageable, but should make + the build more stable. + * debian/control: remove build-dependencies on autoconf/automake/libtool + * debian/rules: don't call autoconf/automake + * Fix shlibs file (it was trying to use a substitution) + * configure.in: remove makefiles that no longer exist from AC_OUTPUT. + Apparently this is an error in autoconf 2.50. + * configure.in: Remove wacky logic to pull PIC flags out of libtool, and + let libtool do the right thing. + * debian/rules: shlibs.local hackery is no longer necessary, removed + + -- Matt Zimmerman Fri, 3 Aug 2001 17:36:43 -0400 + +rrdtool (1.0.33-4) unstable; urgency=low + + * Add build-dependencies on libtool and autoconf (Closes: #105655) + + -- Matt Zimmerman Tue, 17 Jul 2001 15:44:25 -0400 + +rrdtool (1.0.33-3) unstable; urgency=low + + * Update config.{sub,guess} from autotools-dev 20010702.1 + (Closes: #105037) + * Update ltmain.sh for good measure, from libtool 1.4-1 + * Build-Depend on autoconf, and run autoconf and aclocal from + debian/rules (in addition to automake) + + -- Matt Zimmerman Sat, 14 Jul 2001 03:19:22 -0400 + +rrdtool (1.0.33-2) unstable; urgency=low + + * Rebuild with latest tcl8.3-dev, to fix shared object extension + + -- Matt Zimmerman Sat, 3 Mar 2001 17:28:24 -0500 + +rrdtool (1.0.33-1) unstable; urgency=low + + * New upstream version. + * This is yet another patch release to fix some distribution/compilation + problems. There should be no visible changes for Debian users from + 1.0.32-3. + + -- Matt Zimmerman Thu, 1 Mar 2001 02:27:53 -0500 + +rrdtool (1.0.32-3) unstable; urgency=low + + * Fix the tcl extension module to build against tcl8.3, rather than + tcl8.0 (Closes: #87357) + * debian/control: Build-depend on tcl8.3-dev + * debian/rules: tclconfigdir = /usr/lib/tcl8.3 + * tcl/Makefile.am: (upstream) -I/usr/include/tcl@TCL_VERSION@ + * configure.in: (upstream) AC_SUBST(TCL_VERSION) + + -- Matt Zimmerman Sat, 24 Feb 2001 02:25:46 -0500 + +rrdtool (1.0.32-2) unstable; urgency=low + + * List changes to upstream source in the copyright file (as well as the + changelog) + * Link against libgd-gif instead of libgd, to restore GIF support + + -- Matt Zimmerman Fri, 23 Feb 2001 15:27:37 -0500 + +rrdtool (1.0.32-1) unstable; urgency=low + + * New upstream version. Hopefully this one will last more than 24 + hours (Closes: #75771) + + -- Matt Zimmerman Wed, 21 Feb 2001 20:13:09 -0500 + +rrdtool (1.0.27-7) unstable; urgency=low + + * debian/rules: Updated for new Perl policy + * debian/rules: Don't use install-stamp; it causes more problems than it + prevents + * debian/rules: Don't call dh_suidregister; we didn't use it anyway + * debian/control: Build-depend on the new debhelper for dh_perl + * debian/control: rrdtool-tcl only seems to work with 8.0, so depend on + tcl8.0 + * debian/control: Build-depend on perl 5.6.0+ + * debian/librrd?-perl: Updated for new Perl filesystem layout + + -- Matt Zimmerman Thu, 15 Feb 2001 19:04:11 -0500 + +rrdtool (1.0.27-6) unstable; urgency=low + + * debian/rules: Handle DEB_BUILD_OPTIONS debug, nostrip + * debian/rules: Separate out "configure" target + * debian/rules: Miscellaneous cleanup + * Recompile librrds-perl against perl-5.6 + + -- Matt Zimmerman Tue, 19 Dec 2000 21:23:55 -0500 + +rrdtool (1.0.27-5) unstable; urgency=low + + * Fix Build-Depends to reflect our dependency on dpkg-dev >= 1.7.0 | + librrd0. The old dpkg can only calculate our dependencies correctly + if the runtime library is installed on the system, so potato users + must either install the potato librrd0 or the new dpkg-dev + (Closes: #77479) + + -- Matt Zimmerman Mon, 20 Nov 2000 20:39:55 -0500 + +rrdtool (1.0.27-4) unstable; urgency=low + + * Updated to comply with policy 3.2.1.0: + - Don't build with -g by default + * Build-Depend on tcl8.0-dev (not tcl-dev), since that is the only + version we currently work with. + * Added automake to Build-Depends (oops) + + -- Matt Zimmerman Sat, 18 Nov 2000 22:29:02 -0500 + +rrdtool (1.0.27-3) unstable; urgency=low + + * Big packaging cleanup release. + * Fixed perl module compilation to link with the shared library, rather + than including a copy of the static library inside the .so (also fixes + a lintian error, shlib-with-non-pic-code in librrds-perl) + * Fixed tcl module compilation to link with the shared library, rather + than including a copy of the static library inside the .so (also fixes + a lintian error, shlib-with-non-pic-code in rrdtool-tcl) + * The above shrunk the size of the perl and tcl library packages from + 60k each to about 20k (perl) and 16k (tcl) + * We can now use shlibdeps to calculate the dependencies for + librrds-perl, so those will be more correct now + * Rather than include Makefile.in changes in the .diff.gz, just keep our + modifications to Makefile.am and run automake in debian/rules. This + makes the .diff.gz much smaller and cleaner + * Fixed shared library dependencies all around to be smarter + * rrdtool package now Suggests librrds-perl (rather than depending on + perl), as it is only needed for an auxiliary script for converting + mrtg data + + -- Matt Zimmerman Fri, 10 Nov 2000 19:34:05 -0500 + +rrdtool (1.0.27-2) unstable; urgency=low + + * Updated maintainer email address (now official maintainer) + + -- Matt Zimmerman Wed, 1 Nov 2000 22:44:41 -0500 + +rrdtool (1.0.27-1) unstable; urgency=low + + * New upstream version. + + -- Matt Zimmerman Wed, 13 Sep 2000 13:10:14 -0400 + +rrdtool (1.0.26-1) unstable; urgency=low + + * New upstream version. + + -- Matt Zimmerman Sun, 10 Sep 2000 16:34:08 -0400 + +rrdtool (1.0.25-1) unstable; urgency=low + + * New upstream version. + + -- Matt Zimmerman Sun, 23 Jul 2000 02:23:53 -0400 + +rrdtool (1.0.24-2) unstable; urgency=low + + * Updated for libgd 1.8.3 (build depends, recompile) + + -- Matt Zimmerman Fri, 7 Jul 2000 19:33:29 -0400 + +rrdtool (1.0.24-1) unstable; urgency=low + + * New upstream version (Closes: #61997). + + -- Matt Zimmerman Fri, 30 Jun 2000 13:53:27 -0400 + +rrdtool (1.0.17-1) unstable; urgency=low + + * New upstream version. + * Now creates rrdtool-tcl, containing tcl bindings. + + -- Matt Zimmerman Wed, 26 Apr 2000 13:16:24 -0700 + +rrdtool (1.0.16-1) unstable; urgency=low + + * New upstream version. + * Not released. + + -- Matt Zimmerman Wed, 26 Apr 2000 10:53:36 -0700 + +rrdtool (1.0.13-2) unstable; urgency=low + + * Build-Depends: cgilib, zlib1g-dev, libpng2-dev, libgd1g-dev, groff, + debhelper. Hopefully that's everything. + + -- Matt Zimmerman Fri, 7 Apr 2000 14:16:55 -0700 + +rrdtool (1.0.13-1) unstable; urgency=low + + * New upstream version (Closes: #58583) + * Added Build-Depends for cgilib (Closes: #55270) + + -- Matt Zimmerman Thu, 23 Mar 2000 15:55:37 -0800 + +rrdtool (1.0.10-1) unstable; urgency=low + + * New upstream version. + + -- Matt Zimmerman Mon, 10 Jan 2000 16:46:33 -0800 + +rrdtool (1.0.7-3) unstable; urgency=low + + * Non-i386 fixes, thanks to Christopher C Chimelis : + * Changed 'clean' target in debian/rules to remove the auto-generated + Makefiles for the Perl modules + * Changed Depends: for librrds-perl to use shlibdeps + + -- Matt Zimmerman Mon, 22 Nov 1999 10:47:13 -0800 + +rrdtool (1.0.7-2) unstable; urgency=low + + * Linked RRDs.so against libpng, closes: #49701 + + -- Matt Zimmerman Tue, 9 Nov 1999 15:02:40 -0800 + +rrdtool (1.0.7-1) unstable; urgency=low + + * Initial Release. + * Upstream source ships with cgilib-0.4, gd-1.3, libpng-1.0.3, and + zlib-1.3.3 (!). Build process is modified to skip building these, and + instead link with the Debian versions. + * Modifications for gd 1.6.1 included removal of GIF support (no longer + included with gd as of 1.6) + + -- Matt Zimmerman Sat, 11 Sep 1999 13:29:29 -0700 + + diff --git a/program/debian/control b/program/debian/control new file mode 100644 index 00000000..3a80bc55 --- /dev/null +++ b/program/debian/control @@ -0,0 +1,98 @@ +Source: rrdtool +Section: utils +Priority: extra +Maintainer: Matt Zimmerman +Standards-Version: 3.2.1 +Build-Depends: debhelper (>= 3.0.5), cgilib (>= 0.5), zlib1g-dev (>= 1.2.1), tcl8.4-dev, perl (>= 5.8.0), libart-2.0-dev (>= 2.3.16), libpng12-dev (>= 1.2.5), libfreetype6-dev (>= 2.1.7) + +Package: rrdtool +Architecture: any +Depends: ${shlibs:Depends} +Suggests: librrds-perl +Description: Time-series data storage and display system (programs) + RRD is the Acronym for Round Robin Database. RRD is a system to store and + display time-series data (i.e. network bandwidth, machine-room temperature, + server load average). It stores the data in a very compact way that will + not expand over time, and it presents useful graphs by processing the data + to enforce a certain data density. It can be used either via simple wrapper + scripts (from shell or Perl) or via frontends that poll network devices and + put friendly user interface on it. + . + This package contains command line programs used to access and manipulate + RRDs. + +Package: librrd0 +Architecture: any +Section: libs +Priority: optional +Depends: ${shlibs:Depends} +Description: Time-series data storage and display system (runtime) + RRD is the Acronym for Round Robin Database. RRD is a system to store and + display time-series data (i.e. network bandwidth, machine-room temperature, + server load average). It stores the data in a very compact way that will + not expand over time, and it presents useful graphs by processing the data + to enforce a certain data density. It can be used either via simple wrapper + scripts (from shell or Perl) or via frontends that poll network devices and + put friendly user interface on it. + . + This package contains shared libraries used to access and manipulate RRDs. + +Package: librrd0-dev +Architecture: any +Section: libdevel +Depends: librrd0 (= ${Source-Version}), ${shlibs:Depends} +Description: Time-series data storage and display system (development) + RRD is the Acronym for Round Robin Database. RRD is a system to store and + display time-series data (i.e. network bandwidth, machine-room temperature, + server load average). It stores the data in a very compact way that will + not expand over time, and it presents useful graphs by processing the data + to enforce a certain data density. It can be used either via simple wrapper + scripts (from shell or Perl) or via frontends that poll network devices and + put friendly user interface on it. + . + This package contains libraries used to develop software that uses RRDs. + +Package: librrds-perl +Architecture: any +Section: perl +Depends: ${perl:Depends}, ${shlibs:Depends} +Description: Time-series data storage and display system (perl-shared) + RRD is the Acronym for Round Robin Database. RRD is a system to store and + display time-series data (i.e. network bandwidth, machine-room temperature, + server load average). It stores the data in a very compact way that will + not expand over time, and it presents useful graphs by processing the data + to enforce a certain data density. It can be used either via simple wrapper + scripts (from shell or Perl) or via frontends that poll network devices and + put friendly user interface on it. + . + This package contains a Perl interface to RRDs using a shared library. + +Package: librrdp-perl +Architecture: all +Section: perl +Depends: ${perl:Depends}, rrdtool +Description: Time-series data storage and display system (perl-piped) + RRD is the Acronym for Round Robin Database. RRD is a system to store and + display time-series data (i.e. network bandwidth, machine-room temperature, + server load average). It stores the data in a very compact way that will + not expand over time, and it presents useful graphs by processing the data + to enforce a certain data density. It can be used either via simple wrapper + scripts (from shell or Perl) or via frontends that poll network devices and + put friendly user interface on it. + . + This package contains a Perl interface to RRDs using command pipes. + +#NO_TCL Package: rrdtool-tcl +#NO_TCL Architecture: any +#NO_TCL Section: utils +#NO_TCL Depends: ${shlibs:Depends}, tcl8.4 +#NO_TCL Description: Time-series data storage and display system (tcl) +#NO_TCL RRD is the Acronym for Round Robin Database. RRD is a system to store and +#NO_TCL display time-series data (i.e. network bandwidth, machine-room temperature, +#NO_TCL server load average). It stores the data in a very compact way that will +#NO_TCL not expand over time, and it presents useful graphs by processing the data +#NO_TCL to enforce a certain data density. It can be used either via simple wrapper +#NO_TCL scripts (from shell or Perl) or via frontends that poll network devices and +#NO_TCL put friendly user interface on it. +#NO_TCL . +#NO_TCL This package contains a tcl interface to RRDs. diff --git a/program/debian/copyright b/program/debian/copyright new file mode 100644 index 00000000..4af7fb0b --- /dev/null +++ b/program/debian/copyright @@ -0,0 +1,30 @@ +This package was debianized by Matt Zimmerman on +Fri, 10 Sep 1999 10:53:19 -0700. + +Copied from 1.0.46 to 1.1.0 snapshot by Mike Slifcak on Wed May 12 20:53:16 EDT 2004 + +The source package was downloaded from + http://oss.oetiker.ch/rrdtool/pub/ + +Upstream Author(s): Tobias Oetiker + +Modifications to upstream source: + - Build and install shared libraries + - Link everything dynamically + - Link with Debian versions of libraries rather than using the + supplied (old) versions + +Copyright: + + RRDTOOL - Round Robin Database Tool + A tool for fast logging of numerical data graphical display + of this data. + + Copyright (c) 1998, 1999 Tobias Oetiker + All rights reserved. + + RRDtool is distributed under the terms of the GNU General Public License, + version 2. + + On Debian GNU/Linux systems, you can find a copy of the GNU General Public + License in `/usr/share/common-licenses/GPL'. diff --git a/program/debian/librrd0-dev.files b/program/debian/librrd0-dev.files new file mode 100644 index 00000000..957ce463 --- /dev/null +++ b/program/debian/librrd0-dev.files @@ -0,0 +1,5 @@ +usr/lib/librrd.a +usr/lib/librrd.so +usr/lib/librrd_th.a +usr/lib/librrd_th.so +usr/include/* diff --git a/program/debian/librrd0.files b/program/debian/librrd0.files new file mode 100644 index 00000000..c198f710 --- /dev/null +++ b/program/debian/librrd0.files @@ -0,0 +1,6 @@ +usr/lib/librrd.so.1.0.0 +usr/lib/librrd.so.1 +usr/lib/librrd.la +usr/lib/librrd_th.so.1.0.0 +usr/lib/librrd_th.so.1 +usr/lib/librrd_th.la diff --git a/program/debian/librrd0.postinst b/program/debian/librrd0.postinst new file mode 100644 index 00000000..8b92694b --- /dev/null +++ b/program/debian/librrd0.postinst @@ -0,0 +1,49 @@ +#! /bin/sh +# postinst script for #PACKAGE# +# +# see: dh_installdeb(1) + +set -e + +# summary of how this script can be called: +# * `configure' +# * `abort-upgrade' +# * `abort-remove' `in-favour' +# +# * `abort-deconfigure' `in-favour' +# `removing' +# +# for details, see /usr/doc/packaging-manual/ +# +# quoting from the policy: +# Any necessary prompting should almost always be confined to the +# post-installation script, and should be protected with a conditional +# so that unnecessary prompting doesn't happen if a package's +# installation fails and the `postinst' is called with `abort-upgrade', +# `abort-remove' or `abort-deconfigure'. + +case "$1" in + configure) + + ldconfig + + ;; + + abort-upgrade|abort-remove|abort-deconfigure) + + ;; + + *) + echo "postinst called with unknown argument \`$1'" >&2 + exit 0 + ;; +esac + +# dh_installdeb will replace this with shell code automatically +# generated by other debhelper scripts. + +#DEBHELPER# + +exit 0 + + diff --git a/program/debian/librrd0.postrm b/program/debian/librrd0.postrm new file mode 100644 index 00000000..a828228a --- /dev/null +++ b/program/debian/librrd0.postrm @@ -0,0 +1,37 @@ +#! /bin/sh +# +# see: dh_installdeb(1) + +set -e + +# summary of how this script can be called: +# * `remove' +# * `purge' +# * `upgrade' +# * `failed-upgrade' +# * `abort-install' +# * `abort-install' +# * `abort-upgrade' +# * `disappear' overwrit>r> +# for details, see /usr/share/doc/packaging-manual/ + +case "$1" in + remove) + ldconfig + ;; + + purge|upgrade|failed-upgrade|abort-install|abort-upgrade|disappear) + ;; + + *) + echo "postrm called with unknown argument \`$1'" >&2 + exit 0 + +esac + +# dh_installdeb will replace this with shell code automatically +# generated by other debhelper scripts. + +#DEBHELPER# + + diff --git a/program/debian/librrd0.shlibs b/program/debian/librrd0.shlibs new file mode 100644 index 00000000..d0e06dd4 --- /dev/null +++ b/program/debian/librrd0.shlibs @@ -0,0 +1 @@ +librrd 0 librrd0 (>= 1.0.10-1) diff --git a/program/debian/librrdp-perl.files b/program/debian/librrdp-perl.files new file mode 100644 index 00000000..75adff66 --- /dev/null +++ b/program/debian/librrdp-perl.files @@ -0,0 +1,2 @@ +usr/lib/perl5/RRDp.pm +usr/share/man/man3/RRDp.3pm diff --git a/program/debian/librrds-perl.files b/program/debian/librrds-perl.files new file mode 100644 index 00000000..5e50edb8 --- /dev/null +++ b/program/debian/librrds-perl.files @@ -0,0 +1,3 @@ +usr/lib/perl5/auto +usr/lib/perl5/RRDs.pm +usr/share/man/man3/RRDs.3pm diff --git a/program/debian/rrdtool-tcl.files b/program/debian/rrdtool-tcl.files new file mode 100644 index 00000000..af83bcac --- /dev/null +++ b/program/debian/rrdtool-tcl.files @@ -0,0 +1 @@ +usr/lib/tclrrd* diff --git a/program/debian/rrdtool.files b/program/debian/rrdtool.files new file mode 100644 index 00000000..b08aa5d9 --- /dev/null +++ b/program/debian/rrdtool.files @@ -0,0 +1,4 @@ +usr/bin +usr/share/man/man1 +usr/share/doc/rrdtool +usr/share/rrdtool diff --git a/program/debian/rules b/program/debian/rules new file mode 100755 index 00000000..6ddfafeb --- /dev/null +++ b/program/debian/rules @@ -0,0 +1,178 @@ +#!/usr/bin/make -f +#-*- makefile -*- +# Made with the aid of dh_make, by Craig Small +# Sample debian/rules that uses debhelper. GNU copyright 1997 by Joey Hess. +# This version is for a hypothetical package that builds an +# architecture-dependant package, as well as an architecture-independant +# package. + +package:=rrdtool + +top_srcdir:=$(shell pwd) +tmp:=$(top_srcdir)/debian/tmp + +# TCL installation stuff (must match tcl/Makefile.am) +tclconfigdir = /usr/lib/tcl8.4 + +# Uncomment this to turn on verbose mode. +#export DH_VERBOSE=1 + +# This is the debhelper compatability version to use. +export DH_COMPAT=2 + +# Defaults +CFLAGS := -O2 + +# Handle DEB_BUILD_OPTIONS +ifneq "$(findstring debug,$(DEB_BUILD_OPTIONS))" "" +CFLAGS += -g +endif + +# Whack the flags to compile additional dependent pieces +CFLAGS += -I/usr/include/libart-2.0 -I/usr/include/freetype2 + +##NO_TCL CFLAGS += -I/usr/include/tcl8.4 + +configure: configure-stamp +configure-stamp: + dh_testdir + # Make sure we can find tcl stuff (configure won't fail) + test -f $(tclconfigdir)/tclConfig.sh + + # match the configure script to the build environment, as needed + #aclocal + #automake + #autoconf + + # Configure C and Tcl stuff +##NO_TCL CFLAGS="$(CFLAGS)" ./configure --prefix=/usr --enable-shared=yes --enable-static=yes --with-tcllib=$(tclconfigdir) --enable-local-libpng=yes --enable-local-zlib=yes + CFLAGS="$(CFLAGS)" ./configure --prefix=/usr --enable-shared=yes --enable-static=yes --enable-local-libpng=yes --enable-local-zlib=yes + + # Configure Perl stuff + cd bindings/perl-piped && \ + rm -f Makefile && \ + perl Makefile.PL INSTALLDIRS=vendor + + cd bindings/perl-shared && \ + rm -f Makefile && \ + perl Makefile.PL INSTALLDIRS=vendor + + touch configure-stamp + +build: build-stamp +build-stamp: configure-stamp + dh_testdir + + # Build most stuff + $(MAKE) + + # We now build perl-piped and perl-shared separately + cd bindings/perl-piped && make OPTIMIZE="$(CFLAGS)" + cd bindings/perl-shared && make OPTIMIZE="$(CFLAGS)" + + touch build-stamp + +clean: + dh_testdir + dh_testroot + rm -f build-stamp configure-stamp + + # Add here commands to clean up after the build process. + -$(MAKE) distclean + + -rm bindings/perl*/Makefile.old bindings/perl*/Makefile + + dh_clean + +install: build-stamp + dh_testdir + dh_testroot + dh_clean -k + dh_installdirs + + # Add here commands to install the package into debian/tmp. +##NO_TCL $(MAKE) install site-tcl-install DESTDIR=$(tmp) + $(MAKE) install DESTDIR=$(tmp) + + mkdir -p $(tmp)/usr/share/doc + mv $(tmp)/usr/doc $(tmp)/usr/share/doc/rrdtool + mkdir -p $(tmp)/usr/share/rrdtool + mv $(tmp)/usr/examples $(tmp)/usr/share/rrdtool + mv $(tmp)/usr/html $(tmp)/usr/share/rrdtool + + ## touch-up perl docs + mv $(tmp)/usr/man $(tmp)/usr/share + mkdir -p $(tmp)/usr/share/man/man3 + mv $(tmp)/usr/share/man/man1/RRDs.1 $(tmp)/usr/share/man/man3/RRDs.3pm + mv $(tmp)/usr/share/man/man1/RRDp.1 $(tmp)/usr/share/man/man3/RRDp.3pm + mv $(tmp)/usr/lib/perl $(tmp)/usr/lib/perl5 + + ## trim off the doc sources + find $(tmp) -name "*.pod" | xargs rm -f + + dh_movefiles + +# Build architecture-independent files here. +binary-indep: build install +# dh_testversion + dh_testdir -i + dh_testroot -i + dh_installdocs -i +# dh_installexamples -i +# dh_installmenu -i +# dh_installemacsen -i +# dh_installpam -i +# dh_installinit -i +# dh_installcron -i +# dh_installmanpages -i +# dh_installinfo -i +# dh_undocumented +#LATER dh_installchangelogs -i ChangeLog + dh_link -i + dh_compress -i + dh_fixperms -i + # You may want to make some executables suid here. +# dh_suidregister -i + dh_installdeb -i + dh_perl -i + dh_gencontrol -i + dh_md5sums -i + dh_builddeb -i + +# Build architecture-dependent files here. +binary-arch: build install +# dh_testversion + dh_testdir -a + dh_testroot -a + dh_installdocs -a +# dh_installexamples -a +# dh_installmenu -a +# dh_installemacsen -a +# dh_installpam -a +# dh_installinit -a +# dh_installcron -a +# dh_installmanpages -a +# dh_installinfo -a +# dh_undocumented +#LATER dh_installchangelogs -a ChangeLog +ifeq "$(findstring nostrip,$(DEB_BUILD_OPTIONS))" "" + dh_strip -a +endif + dh_link -a + dh_compress -a -Xexamples/ + dh_fixperms -a + # You may want to make some executables suid here. +# dh_suidregister -a +# dh_makeshlibs -a + dh_installdeb -a + dh_perl -a + dh_shlibdeps -a -ldebian/librrd0/usr/lib + dh_gencontrol -a + dh_md5sums -a + dh_builddeb -a + +source diff: + @echo >&2 'source and diff are obsolete - use dpkg-source -b'; false + +binary: binary-indep binary-arch +.PHONY: build clean binary-indep binary-arch binary install diff --git a/program/debian/watch b/program/debian/watch new file mode 100644 index 00000000..bf1b626c --- /dev/null +++ b/program/debian/watch @@ -0,0 +1 @@ +http://oss.oetiker.ch /rrdtool/pub rrdtool-(.*)\.tar\.gz debian diff --git a/program/doc/Makefile.am b/program/doc/Makefile.am new file mode 100644 index 00000000..70f456c2 --- /dev/null +++ b/program/doc/Makefile.am @@ -0,0 +1,72 @@ +## Process this file with automake to produce Makefile.in + +SUFFIXES = .pod .1 .man .html .txt .pm .pdf .inc + +#AUTOMAKE_OPTIONS = foreign + +#ACLOCAL_M4 = $(top_srcdir)/config/aclocal.m4 + +CLEANFILES = *.1 *.html *.txt *-dircache RRD?.pod *.pdf *~ core *itemcache *.rej *.orig *.tmp + +POD = bin_dec_hex.pod rrddump.pod rrdgraph_examples.pod rrdrestore.pod rrdupdate.pod \ + cdeftutorial.pod rrdfetch.pod rrdgraph_graph.pod rrdthreads.pod rrdxport.pod \ + rpntutorial.pod rrdfirst.pod rrdgraph_rpn.pod rrdtool.pod \ + rrd-beginners.pod rrdinfo.pod rrdtune.pod rrdbuild.pod \ + rrdcgi.pod rrdgraph.pod rrdlast.pod rrdlastupdate.pod \ + rrdcreate.pod rrdgraph_data.pod rrdresize.pod rrdtutorial.pod + + +PMP = RRDs.pod RRDp.pod + +MAN = $(POD:.pod=.1) +TXT = $(MAN:.1=.txt) +HTML = $(POD:.pod=.html) $(PMP:.pod=.html) +PDF = $(MAN:.1=.pdf) + +# what should go into the distribution +EXTRA_DIST= $(POD) $(HTML) $(MAN) $(TXT) rrdtool-dump.dtd rrdtool-xport.dtd + +idocdir = $(RRDDOCDIR)/txt +idoc_DATA = $(POD) $(TXT) +ihtmldir = $(RRDDOCDIR)/html +ihtml_DATA = $(HTML) +imandir = $(mandir)/man1 +iman_DATA = $(MAN) + +all-local: link txt man html-local + +.src.pod: + perl -n -e 'if (/^=include\s+(\S+)/){open F,"$$1.inc" || die $$?;print ; close F} else {print}' $< > $@ + +.pod.1 .pm.1 .pl.1: + pod2man --release=$(VERSION) --center=rrdtool $< > $@ + +.1.txt: + GROFF_NO_SGR=1 @NROFF@ -man -Tlp $< > $@ + +.1.pdf: + @TROFF@ -man $< | ps2pdf - $@ + +.pm.html .pod.html .pl.html: + pod2html --infile=$< --outfile=$@ --noindex --htmlroot=. --podpath=. --title=$* + +RRDs.pod: + $(LN_S) $(top_srcdir)/bindings/perl-shared/RRDs.pm RRDs.pod + +RRDp.pod: + $(LN_S) $(top_srcdir)/bindings/perl-piped/RRDp.pm RRDp.pod + +link: RRDp.pod RRDs.pod + +man: $(MAN) + +html-local: $(HTML) + +txt: $(TXT) + +pdf-local: $(PDF) + +pod: $(POD) + +install-data-hook: + cd $(DESTDIR)$(ihtmldir) && rm -f index.html && $(LN_S) rrdtool.html index.html diff --git a/program/doc/bin_dec_hex.pod b/program/doc/bin_dec_hex.pod new file mode 100644 index 00000000..7a2adf5d --- /dev/null +++ b/program/doc/bin_dec_hex.pod @@ -0,0 +1,371 @@ +=head1 NAME + +bin_dec_hex - How to use binary, decimal, and hexadecimal notation. + +=for html
PDF version.
+ +=head1 DESCRIPTION + +Most people use the decimal numbering system. This system uses ten +symbols to represent numbers. When those ten symbols are used up, they +start all over again and increment the position to the left. The +digit 0 is only shown if it is the only symbol in the sequence, or if +it is not the first one. + +If this sounds cryptic to you, this is what I've just said in numbers: + + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + +and so on. + +Each time the digit nine is incremented, it is reset to 0 and the +position before (to the left) is incremented (from 0 to 1). Then +number 9 can be seen as "00009" and when we should increment 9, we +reset it to zero and increment the digit just before the 9 so the +number becomes "00010". Leading zeros we don't write except if it is +the only digit (number 0). And of course, we write zeros if they occur +anywhere inside or at the end of a number: + + "00010" -> " 0010" -> " 010" -> " 10", but not " 1 ". + +This was pretty basic, you already knew this. Why did I tell it? +Well, computers usually do not represent numbers with 10 different +digits. They only use two different symbols, namely "0" and "1". Apply +the same rules to this set of digits and you get the binary numbering +system: + + 0 + 1 + 10 + 11 + 100 + 101 + 110 + 111 + 1000 + 1001 + 1010 + 1011 + 1100 + 1101 + +and so on. + +If you count the number of rows, you'll see that these are again 14 +different numbers. The numbers are the same and mean the same as in +the first list, we just used a different representation. This means +that you have to know the representation used, or as it is called the +numbering system or base. Normally, if we do not explicitly specify +the numbering system used, we implicitly use the decimal system. If we +want to use any other numbering system, we'll have to make that +clear. There are a few widely adopted methods to do so. One common +form is to write 1010(2) which means that you wrote down a number in +its binary representation. It is the number ten. If you would write +1010 without specifying the base, the number is interpreted as one +thousand and ten using base 10. + +In books, another form is common. It uses subscripts (little +characters, more or less in between two rows). You can leave out the +parentheses in that case and write down the number in normal +characters followed by a little two just behind it. + +As the numbering system used is also called the base, we talk of the +number 1100 base 2, the number 12 base 10. + +Within the binary system, it is common to write leading zeros. The +numbers are written down in series of four, eight or sixteen depending +on the context. + +We can use the binary form when talking to computers +(...programming...), but the numbers will have large +representations. The number 65'535 (often in the decimal system a ' is +used to separate blocks of three digits for readability) would be +written down as 1111111111111111(2) which is 16 times the digit 1. +This is difficult and prone to errors. Therefore, we usually would use +another base, called hexadecimal. It uses 16 different symbols. First +the symbols from the decimal system are used, thereafter we continue +with alphabetic characters. We get 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, +A, B, C, D, E and F. This system is chosen because the hexadecimal +form can be converted into the binary system very easily (and back). + +There is yet another system in use, called the octal system. This was +more common in the old days, but is not used very often anymore. As +you might find it in use sometimes, you should get used to it and +we'll show it below. It's the same story as with the other +representations, but with eight different symbols. + + Binary (2) + Octal (8) + Decimal (10) + Hexadecimal (16) + + (2) (8) (10) (16) + 00000 0 0 0 + 00001 1 1 1 + 00010 2 2 2 + 00011 3 3 3 + 00100 4 4 4 + 00101 5 5 5 + 00110 6 6 6 + 00111 7 7 7 + 01000 10 8 8 + 01001 11 9 9 + 01010 12 10 A + 01011 13 11 B + 01100 14 12 C + 01101 15 13 D + 01110 16 14 E + 01111 17 15 F + 10000 20 16 10 + 10001 21 17 11 + 10010 22 18 12 + 10011 23 19 13 + 10100 24 20 14 + 10101 25 21 15 + +Most computers used nowadays are using bytes of eight bits. This means +that they store eight bits at a time. You can see why the octal system +is not the most practical for that: You'd need three digits to represent +the eight bits and this means that you'd have to use one complete digit +to represent only two bits (2+3+3=8). This is a waste. For hexadecimal +digits, you need only two digits which are used completely: + + (2) (8) (10) (16) + 11111111 377 255 FF + +You can see why binary and hexadecimal can be converted quickly: For +each hexadecimal digit there are exactly four binary digits. Take a +binary number: take four digits from the right and make a hexadecimal +digit from it (see the table above). Repeat this until there are no +more digits. And the other way around: Take a hexadecimal number. For +each digit, write down its binary equivalent. + +Computers (or rather the parsers running on them) would have a hard +time converting a number like 1234(16). Therefore hexadecimal numbers +are specified with a prefix. This prefix depends on the language +you're writing in. Some of the prefixes are "0x" for C, "$" for +Pascal, "#" for HTML. It is common to assume that if a number starts +with a zero, it is octal. It does not matter what is used as long as +you know what it is. I will use "0x" for hexadecimal, "%" for binary +and "0" for octal. The following numbers are all the same, just their represenatation (base) is different: 021 0x11 17 %00010001 + +To do arithmetics and conversions you need to understand one more thing. +It is something you already know but perhaps you do not "see" it yet: + +If you write down 1234, (no prefix, so it is decimal) you are talking +about the number one thousand, two hundred and thirty four. In sort of +a formula: + + 1 * 1000 = 1000 + 2 * 100 = 200 + 3 * 10 = 30 + 4 * 1 = 4 + +This can also be written as: + + 1 * 10^3 + 2 * 10^2 + 3 * 10^1 + 4 * 10^0 + +where ^ means "to the power of". + +We are using the base 10, and the positions 0,1,2 and 3. +The right-most position should NOT be multiplied with 10. The second +from the right should be multiplied one time with 10. The third from +the right is multiplied with 10 two times. This continues for whatever +positions are used. + +It is the same in all other representations: + +0x1234 will be + + 1 * 16^3 + 2 * 16^2 + 3 * 16^1 + 4 * 16^0 + +01234 would be + + 1 * 8^3 + 2 * 8^2 + 3 * 8^1 + 4 * 8^0 + +This example can not be done for binary as that system only uses two +symbols. Another example: + +%1010 would be + + 1 * 2^3 + 0 * 2^2 + 1 * 2^1 + 0 * 2^0 + +It would have been easier to convert it to its hexadecimal form and +just translate %1010 into 0xA. After a while you get used to it. You will +not need to do any calculations anymore, but just know that 0xA means 10. + +To convert a decimal number into a hexadecimal you could use the next +method. It will take some time to be able to do the estimates, but it +will be easier when you use the system more frequently. We'll look at +yet another way afterwards. + +First you need to know how many positions will be used in the other +system. To do so, you need to know the maximum numbers you'll be +using. Well, that's not as hard as it looks. In decimal, the maximum +number that you can form with two digits is "99". The maximum for +three: "999". The next number would need an extra position. Reverse +this idea and you will see that the number can be found by taking 10^3 +(10*10*10 is 1000) minus 1 or 10^2 minus one. + +This can be done for hexadecimal as well: + + 16^4 = 0x10000 = 65536 + 16^3 = 0x1000 = 4096 + 16^2 = 0x100 = 256 + 16^1 = 0x10 = 16 + +If a number is smaller than 65'536 it will fit in four positions. +If the number is bigger than 4'095, you must use position 4. +How many times you can subtract 4'096 from the number without going below +zero is the first digit you write down. This will always be a number +from 1 to 15 (0x1 to 0xF). Do the same for the other positions. + +Let's try with 41'029. It is smaller than 16^4 but bigger than 16^3-1. This +means that we have to use four positions. +We can subtract 16^3 from 41'029 ten times without going below zero. +The left-most digit will therefore be "A", so we have 0xA????. +The number is reduced to 41'029 - 10*4'096 = 41'029-40'960 = 69. +69 is smaller than 16^3 but not bigger than 16^2-1. The second digit +is therefore "0" and we now have 0xA0??. +69 is smaller than 16^2 and bigger than 16^1-1. We can subtract 16^1 +(which is just plain 16) four times and write down "4" to get 0xA04?. +Subtract 64 from 69 (69 - 4*16) and the last digit is 5 --> 0xA045. + +The other method builds ub the number from the right. Let's try 41'029 +again. Divide by 16 and do not use fractions (only whole numbers). + + 41'029 / 16 is 2'564 with a remainder of 5. Write down 5. + 2'564 / 16 is 160 with a remainder of 4. Write the 4 before the 5. + 160 / 16 is 10 with no remainder. Prepend 45 with 0. + 10 / 16 is below one. End here and prepend 0xA. End up with 0xA045. + +Which method to use is up to you. Use whatever works for you. I use +them both without being able to tell what method I use in each case, +it just depends on the number, I think. Fact is, some numbers will +occur frequently while programming. If the number is close to one I am +familiar with, then I will use the first method (like 32'770 which is +into 32'768 + 2 and I just know that it is 0x8000 + 0x2 = 0x8002). + +For binary the same approach can be used. The base is 2 and not 16, +and the number of positions will grow rapidly. Using the second method +has the advantage that you can see very easily if you should write down +a zero or a one: if you divide by two the remainder will be zero if it +is an even number and one if it is an odd number: + + 41029 / 2 = 20514 remainder 1 + 20514 / 2 = 10257 remainder 0 + 10257 / 2 = 5128 remainder 1 + 5128 / 2 = 2564 remainder 0 + 2564 / 2 = 1282 remainder 0 + 1282 / 2 = 641 remainder 0 + 641 / 2 = 320 remainder 1 + 320 / 2 = 160 remainder 0 + 160 / 2 = 80 remainder 0 + 80 / 2 = 40 remainder 0 + 40 / 2 = 20 remainder 0 + 20 / 2 = 10 remainder 0 + 10 / 2 = 5 remainder 0 + 5 / 2 = 2 remainder 1 + 2 / 2 = 1 remainder 0 + 1 / 2 below 0 remainder 1 + +Write down the results from right to left: %1010000001000101 + +Group by four: + + %1010000001000101 + %101000000100 0101 + %10100000 0100 0101 + %1010 0000 0100 0101 + +Convert into hexadecimal: 0xA045 + +Group %1010000001000101 by three and convert into octal: + + %1010000001000101 + %1010000001000 101 + %1010000001 000 101 + %1010000 001 000 101 + %1010 000 001 000 101 + %1 010 000 001 000 101 + %001 010 000 001 000 101 + 1 2 0 1 0 5 --> 0120105 + + So: %1010000001000101 = 0120105 = 0xA045 = 41029 + Or: 1010000001000101(2) = 120105(8) = A045(16) = 41029(10) + Or: 1010000001000101(2) = 120105(8) = A045(16) = 41029 + + +At first while adding numbers, you'll convert them to their decimal +form and then back into their original form after doing the addition. +If you use the other numbering system often, you will see that you'll +be able to do arithmetics directly in the base that is used. +In any representation it is the same, add the numbers on the right, +write down the right-most digit from the result, remember the other +digits and use them in the next round. Continue with the second digit +from the right and so on: + + %1010 + %0111 --> 10 + 7 --> 17 --> %00010001 + +will become + + %1010 + %0111 + + |||| + |||+-- add 0 + 1, result is 1, nothing to remember + ||+--- add 1 + 1, result is %10, write down 0 and remember 1 + |+---- add 0 + 1 + 1(remembered), result = 0, remember 1 + +----- add 1 + 0 + 1(remembered), result = 0, remember 1 + nothing to add, 1 remembered, result = 1 + -------- + %10001 is the result, I like to write it as %00010001 + +For low values, try to do the calculations yourself, then check them with +a calculator. The more you do the calculations yourself, the more you'll +find that you didn't make mistakes. In the end, you'll do calculi in +other bases as easily as you do them in decimal. + +When the numbers get bigger, you'll have to realize that a computer is +not called a computer just to have a nice name. There are many +different calculators available, use them. For Unix you could use "bc" +which is short for Binary Calculator. It calculates not only in +decimal, but in all bases you'll ever want to use (among them Binary). + +For people on Windows: +Start the calculator (start->programs->accessories->calculator) +and if necessary click view->scientific. You now have a scientific +calculator and can compute in binary or hexadecimal. + +=head1 AUTHOR + +I hope you enjoyed the examples and their descriptions. If you do, help +other people by pointing them to this document when they are asking +basic questions. They will not only get their answer, but at the same +time learn a whole lot more. + +Alex van den Bogaerdt Ealex@ergens.op.het.netE diff --git a/program/doc/cdeftutorial.pod b/program/doc/cdeftutorial.pod new file mode 100644 index 00000000..6a45ef4a --- /dev/null +++ b/program/doc/cdeftutorial.pod @@ -0,0 +1,889 @@ +=head1 NAME + +cdeftutorial - Alex van den Bogaerdt's CDEF tutorial + +=head1 DESCRIPTION + +Intention of this document: to provide some examples of the commonly +used parts of RRDtool's CDEF language. + +If you think some important feature is not explained properly, and if +adding it to this document would benefit most users, please do ask me +to add it. I will then try to provide an answer in the next release +of this tutorial. No feedback equals no changes! Additions to +this document are also welcome. -- Alex van den Bogaerdt +Ealex@ergens.op.het.netE + +=head2 Why this tutorial? + +One of the powerful parts of RRDtool is its ability to do all sorts +of calculations on the data retrieved from its databases. However, +RRDtool's many options and syntax make it difficult for the average +user to understand. The manuals are good at explaining what these +options do; however they do not (and should not) explain in detail +why they are useful. As with my RRDtool tutorial: if you want a +simple document in simple language you should read this tutorial. +If you are happy with the official documentation, you may find this +document too simple or even boring. If you do choose to read this +tutorial, I also expect you to have read and fully understand my +other tutorial. + +=head2 More reading + +If you have difficulties with the way I try to explain it please read +Steve Rader's L. It may help you understand how this all works. + +=head1 What are CDEFs? + +When retrieving data from an RRD, you are using a "DEF" to work with +that data. Think of it as a variable that changes over time (where +time is the x-axis). The value of this variable is what is found in +the database at that particular time and you can't do any +modifications on it. This is what CDEFs are for: they takes values +from DEFs and perform calculations on them. + +=head1 Syntax + + DEF:var_name_1=some.rrd:ds_name:CF + CDEF:var_name_2=RPN_expression + +You first define "var_name_1" to be data collected from data source +"ds_name" found in RRD "some.rrd" with consolidation function "CF". + +Assume the ifInOctets SNMP counter is saved in mrtg.rrd as the DS "in". +Then the following DEF defines a variable for the average of that +data source: + + DEF:inbytes=mrtg.rrd:in:AVERAGE + +Say you want to display bits per second (instead of bytes per second +as stored in the database.) You have to define a calculation +(hence "CDEF") on variable "inbytes" and use that variable (inbits) +instead of the original: + + CDEF:inbits=inbytes,8,* + +This tells RRDtool to multiply inbytes by eight to get inbits. I'll +explain later how this works. In the graphing or printing functions, +you can now use inbits where you would use inbytes otherwise. + +Note that the variable name used in the CDEF (inbits) must not be the +same as the variable named in the DEF (inbytes)! + +=head1 RPN-expressions + +RPN is short-hand for Reverse Polish Notation. It works as follows. +You put the variables or numbers on a stack. You also put operations +(things-to-do) on the stack and this stack is then processed. The result +will be placed on the stack. At the end, there should be exactly one +number left: the outcome of the series of operations. If there is not +exactly one number left, RRDtool will complain loudly. + +Above multiplication by eight will look like: + +=over 4 + +=item 1. + +Start with an empty stack + +=item 2. + +Put the content of variable inbytes on the stack + +=item 3. + +Put the number eight on the stack + +=item 4. + +Put the operation multiply on the stack + +=item 5. + +Process the stack + +=item 6. + +Retrieve the value from the stack and put it in variable inbits + +=back + +We will now do an example with real numbers. Suppose the variable +inbytes would have value 10, the stack would be: + +=over 4 + +=item 1. + +|| + +=item 2. + +|10| + +=item 3. + +|10|8| + +=item 4. + +|10|8|*| + +=item 5. + +|80| + +=item 6. + +|| + +=back + +Processing the stack (step 5) will retrieve one value from the stack +(from the right at step 4). This is the operation multiply and this +takes two values off the stack as input. The result is put back on the +stack (the value 80 in this case). For multiplication the order doesn't +matter, but for other operations like subtraction and division it does. +Generally speaking you have the following order: + + y = A - B --> y=minus(A,B) --> CDEF:y=A,B,- + +This is not very intuitive (at least most people don't think so). For +the function f(A,B) you reverse the position of "f", but you do not +reverse the order of the variables. + +=head1 Converting your wishes to RPN + +First, get a clear picture of what you want to do. Break down the problem +in smaller portions until they cannot be split anymore. Then it is rather +simple to convert your ideas into RPN. + +Suppose you have several RRDs and would like to add up some counters in +them. These could be, for instance, the counters for every WAN link you +are monitoring. + +You have: + + router1.rrd with link1in link2in + router2.rrd with link1in link2in + router3.rrd with link1in link2in + +Suppose you would like to add up all these counters, except for link2in +inside router2.rrd. You need to do: + +(in this example, "router1.rrd:link1in" means the DS link1in inside the +RRD router1.rrd) + + router1.rrd:link1in + router1.rrd:link2in + router2.rrd:link1in + router3.rrd:link1in + router3.rrd:link2in + -------------------- + + (outcome of the sum) + +As a mathematical function, this could be written: + +C + +With RRDtool and RPN, first, define the inputs: + + DEF:a=router1.rrd:link1in:AVERAGE + DEF:b=router1.rrd:link2in:AVERAGE + DEF:c=router2.rrd:link1in:AVERAGE + DEF:d=router3.rrd:link1in:AVERAGE + DEF:e=router3.rrd:link2in:AVERAGE + +Now, the mathematical function becomes: C + +In RPN, there's no operator that sums more than two values so you need +to do several additions. You add a and b, add c to the result, add d +to the result and add e to the result. + + push a: a stack contains the value of a + push b and add: b,+ stack contains the result of a+b + push c and add: c,+ stack contains the result of a+b+c + push d and add: d,+ stack contains the result of a+b+c+d + push e and add: e,+ stack contains the result of a+b+c+d+e + +What was calculated here would be written down as: + + ( ( ( (a+b) + c) + d) + e) > + +This is in RPN: C + +This is correct but it can be made more clear to humans. It does +not matter if you add a to b and then add c to the result or first +add b to c and then add a to the result. This makes it possible to +rewrite the RPN into C which is +evaluated differently: + + push value of variable a on the stack: a + push value of variable b on the stack: a b + push value of variable c on the stack: a b c + push value of variable d on the stack: a b c d + push value of variable e on the stack: a b c d e + push operator + on the stack: a b c d e + + and process it: a b c P (where P == d+e) + push operator + on the stack: a b c P + + and process it: a b Q (where Q == c+P) + push operator + on the stack: a b Q + + and process it: a R (where R == b+Q) + push operator + on the stack: a R + + and process it: S (where S == a+R) + +As you can see the RPN expression C will evaluate in +C<((((d+e)+c)+b)+a)> and it has the same outcome as C. +This is called the commutative law of addition, +but you may forget this right away, as long as you remember what it +means. + +Now look at an expression that contains a multiplication: + +First in normal math: C. In this case you can't +choose the order yourself, you have to start with the multiplication +and then add a to it. You may alter the position of b and c, you must +not alter the position of a and b. + +You have to take this in consideration when converting this expression +into RPN. Read it as: "Add the outcome of b*c to a" and then it is +easy to write the RPN expression: C +Another expression that would return the same: C + +In normal math, you may encounter something like "a*(b+c)" and this +can also be converted into RPN. The parenthesis just tell you to first +add b and c, and then multiply a with the result. Again, now it is +easy to write it in RPN: C. Note that this is very +similar to one of the expressions in the previous paragraph, only the +multiplication and the addition changed places. + +When you have problems with RPN or when RRDtool is complaining, it's +usually a good thing to write down the stack on a piece of paper +and see what happens. Have the manual ready and pretend to be RRDtool. +Just do all the math by hand to see what happens, I'm sure this will +solve most, if not all, problems you encounter. + +=head1 Some special numbers + +=head2 The unknown value + +Sometimes collecting your data will fail. This can be very common, +especially when querying over busy links. RRDtool can be configured +to allow for one (or even more) unknown value(s) and calculate the missing +update. You can, for instance, query your device every minute. This is +creating one so called PDP or primary data point per minute. If you +defined your RRD to contain an RRA that stores 5-minute values, you need +five of those PDPs to create one CDP (consolidated data point). +These PDPs can become unknown in two cases: + +=over 4 + +=item 1. + +The updates are too far apart. This is tuned using the "heartbeat" setting. + +=item 2. + +The update was set to unknown on purpose by inserting no value (using the +template option) or by using "U" as the value to insert. + +=back + +When a CDP is calculated, another mechanism determines if this CDP is valid +or not. If there are too many PDPs unknown, the CDP is unknown as well. +This is determined by the xff factor. Please note that one unknown counter +update can result in two unknown PDPs! If you only allow for one unknown +PDP per CDP, this makes the CDP go unknown! + +Suppose the counter increments with one per second and you retrieve it +every minute: + + counter value resulting rate + 10'000 + 10'060 1; (10'060-10'000)/60 == 1 + 10'120 1; (10'120-10'060)/60 == 1 + unknown unknown; you don't know the last value + 10'240 unknown; you don't know the previous value + 10'300 1; (10'300-10'240)/60 == 1 + +If the CDP was to be calculated from the last five updates, it would get +two unknown PDPs and three known PDPs. If xff would have been set to 0.5 +which by the way is a commonly used factor, the CDP would have a known +value of 1. If xff would have been set to 0.2 then the resulting CDP +would be unknown. + +You have to decide the proper values for heartbeat, number of PDPs per +CDP and the xff factor. As you can see from the previous text they define +the behavior of your RRA. + +=head2 Working with unknown data in your database + +As you have read in the previous chapter, entries in an RRA can be +set to the unknown value. If you do calculations with this type of +value, the result has to be unknown too. This means that an expression +such as C will be unknown if either a or b is unknown. +It would be wrong to just ignore the unknown value and return the value +of the other parameter. By doing so, you would assume "unknown" means "zero" +and this is not true. + +There has been a case where somebody was collecting data for over a year. +A new piece of equipment was installed, a new RRD was created and the +scripts were changed to add a counter from the old database and a counter +from the new database. The result was disappointing, a large part of +the statistics seemed to have vanished mysteriously ... +They of course didn't, values from the old database (known values) were +added to values from the new database (unknown values) and the result was +unknown. + +In this case, it is fairly reasonable to use a CDEF that alters unknown +data into zero. The counters of the device were unknown (after all, it +wasn't installed yet!) but you know that the data rate through the device +had to be zero (because of the same reason: it was not installed). + +There are some examples below that make this change. + +=head2 Infinity + +Infinite data is another form of a special number. It cannot be +graphed because by definition you would never reach the infinite +value. You can think of positive and negative infinity depending on +the position relative to zero. + +RRDtool is capable of representing (-not- graphing!) infinity by stopping +at its current maximum (for positive infinity) or minimum (for negative +infinity) without knowing this maximum (minimum). + +Infinity in RRDtool is mostly used to draw an AREA without knowing its +vertical dimensions. You can think of it as drawing an AREA with an +infinite height and displaying only the part that is visible in the +current graph. This is probably a good way to approximate infinity +and it sure allows for some neat tricks. See below for examples. + +=head2 Working with unknown data and infinity + +Sometimes you would like to discard unknown data and pretend it is zero +(or any other value for that matter) and sometimes you would like to +pretend that known data is unknown (to discard known-to-be-wrong data). +This is why CDEFs have support for unknown data. There are also examples +available that show unknown data by using infinity. + +=head1 Some examples + +=head2 Example: using a recently created RRD + +You are keeping statistics on your router for over a year now. Recently +you installed an extra router and you would like to show the combined +throughput for these two devices. + +If you just add up the counters from router.rrd and router2.rrd, you +will add known data (from router.rrd) to unknown data (from router2.rrd) for +the bigger part of your stats. You could solve this in a few ways: + +=over 4 + +=item * + +While creating the new database, fill it with zeros from the start to now. +You have to make the database start at or before the least recent time in +the other database. + +=item * + +Alternatively, you could use CDEF and alter unknown data to zero. + +=back + +Both methods have their pros and cons. The first method is troublesome and +if you want to do that you have to figure it out yourself. It is not +possible to create a database filled with zeros, you have to put them in +manually. Implementing the second method is described next: + +What we want is: "if the value is unknown, replace it with zero". This +could be written in pseudo-code as: if (value is unknown) then (zero) +else (value). When reading the L manual you notice the "UN" +function that returns zero or one. You also notice the "IF" function +that takes zero or one as input. + +First look at the "IF" function. It takes three values from the stack, +the first value is the decision point, the second value is returned to +the stack if the evaluation is "true" and if not, the third value is +returned to the stack. We want the "UN" function to decide what happens +so we combine those two functions in one CDEF. + +Lets write down the two possible paths for the "IF" function: + + if true return a + if false return b + +In RPN: C where "x" is either true or false. + +Now we have to fill in "x", this should be the "(value is unknown)" part +and this is in RPN: C + +We now combine them: C and when we fill in the +appropriate things for "a" and "b" we're finished: + +C + +You may want to read Steve Rader's RPN guide if you have difficulties +with the way I explained this last example. + +If you want to check this RPN expression, just mimic RRDtool behavior: + + For any known value, the expression evaluates as follows: + CDEF:result=value,UN,0,value,IF (value,UN) is not true so it becomes 0 + CDEF:result=0,0,value,IF "IF" will return the 3rd value + CDEF:result=value The known value is returned + + For the unknown value, this happens: + CDEF:result=value,UN,0,value,IF (value,UN) is true so it becomes 1 + CDEF:result=1,0,value,IF "IF" sees 1 and returns the 2nd value + CDEF:result=0 Zero is returned + +Of course, if you would like to see another value instead of zero, you +can use that other value. + +Eventually, when all unknown data is removed from the RRD, you may want +to remove this rule so that unknown data is properly displayed. + +=head2 Example: better handling of unknown data, by using time + +The above example has one drawback. If you do log unknown data in +your database after installing your new equipment, it will also be +translated into zero and therefore you won't see that there was a +problem. This is not good and what you really want to do is: + +=over 4 + +=item * + +If there is unknown data, look at the time that this sample was taken. + +=item * + +If the unknown value is before time xxx, make it zero. + +=item * + +If it is after time xxx, leave it as unknown data. + +=back + +This is doable: you can compare the time that the sample was taken +to some known time. Assuming you started to monitor your device on +Friday September 17, 1999, 00:35:57 MET DST. Translate this time in seconds +since 1970-01-01 and it becomes 937'521'357. If you process unknown values +that were received after this time, you want to leave them unknown and +if they were "received" before this time, you want to translate them +into zero (so you can effectively ignore them while adding them to your +other routers counters). + +Translating Friday September 17, 1999, 00:35:57 MET DST into 937'521'357 can +be done by, for instance, using gnu date: + + date -d "19990917 00:35:57" +%s + +You could also dump the database and see where the data starts to be +known. There are several other ways of doing this, just pick one. + +Now we have to create the magic that allows us to process unknown +values different depending on the time that the sample was taken. +This is a three step process: + +=over 4 + +=item 1. + +If the timestamp of the value is after 937'521'357, leave it as is. + +=item 2. + +If the value is a known value, leave it as is. + +=item 3. + +Change the unknown value into zero. + +=back + +Lets look at part one: + + if (true) return the original value + +We rewrite this: + + if (true) return "a" + if (false) return "b" + +We need to calculate true or false from step 1. There is a function +available that returns the timestamp for the current sample. It is +called, how surprisingly, "TIME". This time has to be compared to +a constant number, we need "GT". The output of "GT" is true or false +and this is good input to "IF". We want "if (time > 937521357) then +(return a) else (return b)". + +This process was already described thoroughly in the previous chapter +so lets do it quick: + + if (x) then a else b + where x represents "time>937521357" + where a represents the original value + where b represents the outcome of the previous example + + time>937521357 --> TIME,937521357,GT + + if (x) then a else b --> x,a,b,IF + substitute x --> TIME,937521357,GT,a,b,IF + substitute a --> TIME,937521357,GT,value,b,IF + substitute b --> TIME,937521357,GT,value,value,UN,0,value,IF,IF + +We end up with: +C + +This looks very complex, however, as you can see, it was not too hard to +come up with. + +=head2 Example: Pretending weird data isn't there + +Suppose you have a problem that shows up as huge spikes in your graph. +You know this happens and why, so you decide to work around the problem. +Perhaps you're using your network to do a backup at night and by doing +so you get almost 10mb/s while the rest of your network activity does +not produce numbers higher than 100kb/s. + +There are two options: + +=over 4 + +=item 1. + +If the number exceeds 100kb/s it is wrong and you want it masked out +by changing it into unknown. + +=item 2. + +You don't want the graph to show more than 100kb/s. + +=back + +Pseudo code: if (number > 100) then unknown else number +or +Pseudo code: if (number > 100) then 100 else number. + +The second "problem" may also be solved by using the rigid option of +RRDtool graph, however this has not the same result. In this example +you can end up with a graph that does autoscaling. Also, if you use +the numbers to display maxima they will be set to 100kb/s. + +We use "IF" and "GT" again. "if (x) then (y) else (z)" is written +down as "CDEF:result=x,y,z,IF"; now fill in x, y and z. +For x you fill in "number greater than 100kb/s" becoming +"number,100000,GT" (kilo is 1'000 and b/s is what we measure!). +The "z" part is "number" in both cases and the "y" part is either +"UNKN" for unknown or "100000" for 100kb/s. + +The two CDEF expressions would be: + + CDEF:result=number,100000,GT,UNKN,number,IF + CDEF:result=number,100000,GT,100000,number,IF + +=head2 Example: working on a certain time span + +If you want a graph that spans a few weeks, but would only want to +see some routers' data for one week, you need to "hide" the rest of +the time frame. Don't ask me when this would be useful, it's just +here for the example :) + +We need to compare the time stamp to a begin date and an end date. +Comparing isn't difficult: + + TIME,begintime,GE + TIME,endtime,LE + +These two parts of the CDEF produce either 0 for false or 1 for true. +We can now check if they are both 0 (or 1) using a few IF statements +but, as Wataru Satoh pointed out, we can use the "*" or "+" functions +as logical AND and logical OR. + +For "*", the result will be zero (false) if either one of the two +operators is zero. For "+", the result will only be false (0) when +two false (0) operators will be added. Warning: *any* number not +equal to 0 will be considered "true". This means that, for instance, +"-1,1,+" (which should be "true or true") will become FALSE ... +In other words, use "+" only if you know for sure that you have positive +numbers (or zero) only. + +Let's compile the complete CDEF: + + DEF:ds0=router1.rrd:AVERAGE + CDEF:ds0modified=TIME,begintime,GE,TIME,endtime,LE,*,UNKN,ds0,IF + +This will return the value of ds0 if both comparisons return true. You +could also do it the other way around: + + DEF:ds0=router1.rrd:AVERAGE + CDEF:ds0modified=TIME,begintime,LT,TIME,endtime,GT,+,UNKN,ds0,IF + +This will return an UNKNOWN if either comparison returns true. + +=head2 Example: You suspect to have problems and want to see unknown data. + +Suppose you add up the number of active users on several terminal servers. +If one of them doesn't give an answer (or an incorrect one) you get "NaN" +in the database ("Not a Number") and NaN is evaluated as Unknown. + +In this case, you would like to be alerted to it and the sum of the +remaining values is of no value to you. + +It would be something like: + + DEF:users1=location1.rrd:onlineTS1:LAST + DEF:users2=location1.rrd:onlineTS2:LAST + DEF:users3=location2.rrd:onlineTS1:LAST + DEF:users4=location2.rrd:onlineTS2:LAST + CDEF:allusers=users1,users2,users3,users4,+,+,+ + +If you now plot allusers, unknown data in one of users1..users4 will +show up as a gap in your graph. You want to modify this to show a +bright red line, not a gap. + +Define an extra CDEF that is unknown if all is okay and is infinite if +there is an unknown value: + + CDEF:wrongdata=allusers,UN,INF,UNKN,IF + +"allusers,UN" will evaluate to either true or false, it is the (x) part +of the "IF" function and it checks if allusers is unknown. +The (y) part of the "IF" function is set to "INF" (which means infinity) +and the (z) part of the function returns "UNKN". + +The logic is: if (allusers == unknown) then return INF else return UNKN. + +You can now use AREA to display this "wrongdata" in bright red. If it +is unknown (because allusers is known) then the red AREA won't show up. +If the value is INF (because allusers is unknown) then the red AREA will +be filled in on the graph at that particular time. + + AREA:allusers#0000FF:combined user count + AREA:wrongdata#FF0000:unknown data + +=head2 Same example useful with STACKed data: + +If you use stack in the previous example (as I would do) then you don't +add up the values. Therefore, there is no relationship between the +four values and you don't get a single value to test. +Suppose users3 would be unknown at one point in time: users1 is plotted, +users2 is stacked on top of users1, users3 is unknown and therefore +nothing happens, users4 is stacked on top of users2. +Add the extra CDEFs anyway and use them to overlay the "normal" graph: + + DEF:users1=location1.rrd:onlineTS1:LAST + DEF:users2=location1.rrd:onlineTS2:LAST + DEF:users3=location2.rrd:onlineTS1:LAST + DEF:users4=location2.rrd:onlineTS2:LAST + CDEF:allusers=users1,users2,users3,users4,+,+,+ + CDEF:wrongdata=allusers,UN,INF,UNKN,IF + AREA:users1#0000FF:users at ts1 + STACK:users2#00FF00:users at ts2 + STACK:users3#00FFFF:users at ts3 + STACK:users4#FFFF00:users at ts4 + AREA:wrongdata#FF0000:unknown data + +If there is unknown data in one of users1..users4, the "wrongdata" AREA +will be drawn and because it starts at the X-axis and has infinite height +it will effectively overwrite the STACKed parts. + +You could combine the two CDEF lines into one (we don't use "allusers") +if you like. But there are good reasons for writing two CDEFS: + +=over 4 + +=item * + +It improves the readability of the script. + +=item * + +It can be used inside GPRINT to display the total number of users. + +=back + +If you choose to combine them, you can substitute the "allusers" in the +second CDEF with the part after the equal sign from the first line: + + CDEF:wrongdata=users1,users2,users3,users4,+,+,+,UN,INF,UNKN,IF + +If you do so, you won't be able to use these next GPRINTs: + + COMMENT:"Total number of users seen" + GPRINT:allusers:MAX:"Maximum: %6.0lf" + GPRINT:allusers:MIN:"Minimum: %6.0lf" + GPRINT:allusers:AVERAGE:"Average: %6.0lf" + GPRINT:allusers:LAST:"Current: %6.0lf\n" + +=head1 The examples from the RRD graph manual page + +=head2 Degrees Celsius vs. Degrees Fahrenheit + +To convert Celsius into Fahrenheit use the formula +F=9/5*C+32 + + rrdtool graph demo.png --title="Demo Graph" \ + DEF:cel=demo.rrd:exhaust:AVERAGE \ + CDEF:far=9,5,/,cel,*,32,+ \ + LINE2:cel#00a000:"D. Celsius" \ + LINE2:far#ff0000:"D. Fahrenheit\c" + +This example gets the DS called "exhaust" from database "demo.rrd" +and puts the values in variable "cel". The CDEF used is evaluated +as follows: + + CDEF:far=9,5,/,cel,*,32,+ + 1. push 9, push 5 + 2. push function "divide" and process it + the stack now contains 9/5 + 3. push variable "cel" + 4. push function "multiply" and process it + the stack now contains 9/5*cel + 5. push 32 + 6. push function "plus" and process it + the stack contains now the temperature in Fahrenheit + +=head2 Changing unknown into zero + + rrdtool graph demo.png --title="Demo Graph" \ + DEF:idat1=interface1.rrd:ds0:AVERAGE \ + DEF:idat2=interface2.rrd:ds0:AVERAGE \ + DEF:odat1=interface1.rrd:ds1:AVERAGE \ + DEF:odat2=interface2.rrd:ds1:AVERAGE \ + CDEF:agginput=idat1,UN,0,idat1,IF,idat2,UN,0,idat2,IF,+,8,* \ + CDEF:aggoutput=odat1,UN,0,odat1,IF,odat2,UN,0,odat2,IF,+,8,* \ + AREA:agginput#00cc00:Input Aggregate \ + LINE1:aggoutput#0000FF:Output Aggregate + +These two CDEFs are built from several functions. It helps to split +them when viewing what they do. Starting with the first CDEF we would +get: + + idat1,UN --> a + 0 --> b + idat1 --> c + if (a) then (b) else (c) + +The result is therefore "0" if it is true that "idat1" equals "UN". +If not, the original value of "idat1" is put back on the stack. +Lets call this answer "d". The process is repeated for the next +five items on the stack, it is done the same and will return answer +"h". The resulting stack is therefore "d,h". +The expression has been simplified to "d,h,+,8,*" and it will now be +easy to see that we add "d" and "h", and multiply the result with eight. + +The end result is that we have added "idat1" and "idat2" and in the +process we effectively ignored unknown values. The result is multiplied +by eight, most likely to convert bytes/s to bits/s. + +=head2 Infinity demo + + rrdtool graph example.png --title="INF demo" \ + DEF:val1=some.rrd:ds0:AVERAGE \ + DEF:val2=some.rrd:ds1:AVERAGE \ + DEF:val3=some.rrd:ds2:AVERAGE \ + DEF:val4=other.rrd:ds0:AVERAGE \ + CDEF:background=val4,POP,TIME,7200,%,3600,LE,INF,UNKN,IF \ + CDEF:wipeout=val1,val2,val3,val4,+,+,+,UN,INF,UNKN,IF \ + AREA:background#F0F0F0 \ + AREA:val1#0000FF:Value1 \ + STACK:val2#00C000:Value2 \ + STACK:val3#FFFF00:Value3 \ + STACK:val4#FFC000:Value4 \ + AREA:whipeout#FF0000:Unknown + +This demo demonstrates two ways to use infinity. It is a bit tricky +to see what happens in the "background" CDEF. + + "val4,POP,TIME,7200,%,3600,LE,INF,UNKN,IF" + +This RPN takes the value of "val4" as input and then immediately +removes it from the stack using "POP". The stack is now empty but +as a side effect we now know the time that this sample was taken. +This time is put on the stack by the "TIME" function. + +"TIME,7200,%" takes the modulo of time and 7'200 (which is two hours). +The resulting value on the stack will be a number in the range from +0 to 7199. + +For people who don't know the modulo function: it is the remainder +after an integer division. If you divide 16 by 3, the answer would +be 5 and the remainder would be 1. So, "16,3,%" returns 1. + +We have the result of "TIME,7200,%" on the stack, lets call this +"a". The start of the RPN has become "a,3600,LE" and this checks +if "a" is less or equal than "3600". It is true half of the time. +We now have to process the rest of the RPN and this is only a simple +"IF" function that returns either "INF" or "UNKN" depending on the +time. This is returned to variable "background". + +The second CDEF has been discussed earlier in this document so we +won't do that here. + +Now you can draw the different layers. Start with the background +that is either unknown (nothing to see) or infinite (the whole +positive part of the graph gets filled). + +Next you draw the data on top of this background, it will overlay +the background. Suppose one of val1..val4 would be unknown, in that +case you end up with only three bars stacked on top of each other. +You don't want to see this because the data is only valid when all +four variables are valid. This is why you use the second CDEF, it +will overlay the data with an AREA so the data cannot be seen anymore. + +If your data can also have negative values you also need to overwrite +the other half of your graph. This can be done in a relatively simple +way: what you need is the "wipeout" variable and place a negative +sign before it: "CDEF:wipeout2=wipeout,-1,*" + +=head2 Filtering data + +You may do some complex data filtering: + + MEDIAN FILTER: filters shot noise + + DEF:var=database.rrd:traffic:AVERAGE + CDEF:prev1=PREV(var) + CDEF:prev2=PREV(prev1) + CDEF:prev3=PREV(prev2) + CDEF:median=prev1,prev2,prev3,+,+,3,/ + LINE3:median#000077:filtered + LINE1:prev2#007700:'raw data' + + + DERIVATE: + + DEF:var=database.rrd:traffic:AVERAGE + CDEF:prev1=PREV(var) + CDEF:time=TIME + CDEF:prevtime=PREV(time) + CDEF:derivate=var,prev1,-,time,prevtime,-,/ + LINE3:derivate#000077:derivate + LINE1:var#007700:'raw data' + + +=head1 Out of ideas for now + +This document was created from questions asked by either myself or by +other people on the RRDtool mailing list. Please let me know if you +find errors in it or if you have trouble understanding it. If you +think there should be an addition, mail me: +Ealex@ergens.op.het.netE + +Remember: B + +=head1 SEE ALSO + +The RRDtool manpages + +=head1 AUTHOR + +Alex van den Bogaerdt +Ealex@ergens.op.het.netE diff --git a/program/doc/name.inc b/program/doc/name.inc new file mode 100644 index 00000000..dffcae4e --- /dev/null +++ b/program/doc/name.inc @@ -0,0 +1,11 @@ +=head1 NAME + +=cut + +WARNING: DO NOT EDIT THE POD FILES. THEY ARE AUTO-GENERATED + +=pod + +rrdtool graph - Round Robin Database tool grapher functions + +Documentation for version 1.2.0 diff --git a/program/doc/rpntutorial.pod b/program/doc/rpntutorial.pod new file mode 100644 index 00000000..b3beac1d --- /dev/null +++ b/program/doc/rpntutorial.pod @@ -0,0 +1,198 @@ +=head1 NAME + +rpntutorial - Reading RRDtool RPN Expressions by Steve Rader + +=head1 DESCRIPTION + +This tutorial should help you get to grips with RRDtool RPN expressions +as seen in CDEF arguments of RRDtool graph. + +=head1 Reading Comparison Operators + +The LT, LE, GT, GE and EQ RPN logic operators are not as tricky as +they appear. These operators act on the two values on the stack +preceding them (to the left). Read these two values on the stack +from left to right inserting the operator in the middle. If the +resulting statement is true, then replace the three values from the +stack with "1". If the statement if false, replace the three values +with "0". + +For example, think about "2,1,GT". This RPN expression could be +read as "is two greater than one?" The answer to that question is +"true". So the three values should be replaced with "1". Thus the +RPN expression 2,1,GT evaluates to 1. + +Now consider "2,1,LE". This RPN expression could be read as "is +two less than or equal to one?". The natural response is "no" +and thus the RPN expression 2,1,LE evaluates to 0. + +=head1 Reading the IF Operator + +The IF RPN logic operator can be straightforward also. The key +to reading IF operators is to understand that the condition part +of the traditional "if X than Y else Z" notation has *already* +been evaluated. So the IF operator acts on only one value on the +stack: the third value to the left of the IF value. The second +value to the left of the IF corresponds to the true ("Y") branch. +And the first value to the left of the IF corresponds to the false +("Z") branch. Read the RPN expression "X,Y,Z,IF" from left to +right like so: "if X then Y else Z". + +For example, consider "1,10,100,IF". It looks bizarre to me. +But when I read "if 1 then 10 else 100" it's crystal clear: 1 is true +so the answer is 10. Note that only zero is false; all other values +are true. "2,20,200,IF" ("if 2 then 20 else 200") evaluates to 20. +And "0,1,2,IF" ("if 0 then 1 else 2) evaluates to 2. + + +Notice that none of the above examples really simulate the whole +"if X then Y else Z" statement. This is because computer programmers +read this statement as "if Some Condition then Y else Z". So it's +important to be able to read IF operators along with the LT, LE, +GT, GE and EQ operators. + +=head1 Some Examples + +While compound expressions can look overly complex, they can be +considered elegantly simple. To quickly comprehend RPN expressions, +you must know the the algorithm for evaluating RPN expressions: +iterate searches from the left to the right looking for an operator. +When it's found, apply that operator by popping the operator and some +number of values (and by definition, not operators) off the stack. + +For example, the stack "1,2,3,+,+" gets "2,3,+" evaluated (as "2+3") +during the first iteration and is replaced by 5. This results in +the stack "1,5,+". Finally, "1,5,+" is evaluated resulting in the +answer 6. For convenience, it's useful to write this set of +operations as: + + 1) 1,2,3,+,+ eval is 2,3,+ = 5 result is 1,5,+ + 2) 1,5,+ eval is 1,5,+ = 6 result is 6 + 3) 6 + +Let's use that notation to conveniently solve some complex RPN expressions +with multiple logic operators: + + 1) 20,10,GT,10,20,IF eval is 20,10,GT = 1 result is 1,10,20,IF + +read the eval as pop "20 is greater than 10" so push 1 + + 2) 1,10,20,IF eval is 1,10,20,IF = 10 result is 10 + +read pop "if 1 then 10 else 20" so push 10. Only 10 is left so +10 is the answer. + +Let's read a complex RPN expression that also has the traditional +multiplication operator: + + 1) 128,8,*,7000,GT,7000,128,8,*,IF eval 128,8,* result is 1024 + 2) 1024,7000,GT,7000,128,8,*,IF eval 1024,7000,GT result is 0 + 3) 0,128,8,*,IF eval 128,8,* result is 1024 + 4) 0,7000,1024,IF result is 1024 + + +Now let's go back to the first example of multiple logic operators, +but replace the value 20 with the variable "input": + + 1) input,10,GT,10,input,IF eval is input,10,GT ( lets call this A ) + +Read eval as "if input > 10 then true" and replace "input,10,GT" +with "A": + + 2) A,10,input,IF eval is A,10,input,IF + +read "if A then 10 else input". Now replace A with it's verbose +description againg and--voila!--you have a easily readable description +of the expression: + + if input > 10 then 10 else input + +Finally, let's go back to the first most complex example and replace +the value 128 with "input": + + 1) input,8,*,7000,GT,7000,input,8,*,IF eval input,8,* result is A + +where A is "input * 8" + + 2) A,7000,GT,7000,input,8,*,IF eval is A,7000,GT result is B + +where B is "if ((input * 8) > 7000) then true" + + 3) B,7000,input,8,*,IF eval is input,8,* result is C + +where C is "input * 8" + + 4) B,7000,C,IF + +At last we have a readable decoding of the complex RPN expression with +a variable: + + if ((input * 8) > 7000) then 7000 else (input * 8) + +=head1 Exercises + +Exercise 1: + +Compute "3,2,*,1,+ and "3,2,1,+,*" by hand. Rewrite them in +traditional notation. Explain why they have different answers. + +Answer 1: + + 3*2+1 = 7 and 3*(2+1) = 9. These expressions have + different answers because the altering of the plus and + times operators alter the order of their evaluation. + + +Exercise 2: + +One may be tempted to shorten the expression + + input,8,*,56000,GT,56000,input,*,8,IF + +by removing the redundant use of "input,8,*" like so: + + input,56000,GT,56000,input,IF,8,* + +Use traditional notation to show these expressions are not the same. +Write an expression that's equivalent to the first expression, but +uses the LE and DIV operators. + +Answer 2: + + if (input <= 56000/8 ) { input*8 } else { 56000 } + input,56000,8,DIV,LT,input,8,*,56000,IF + + +Exercise 3: + +Briefly explain why traditional mathematic notation requires the +use of parentheses. Explain why RPN notation does not require +the use of parentheses. + +Answer 3: + + Traditional mathematic expressions are evaluated by + doing multiplication and division first, then addition and + subtraction. Parentheses are used to force the evaluation of + addition before multiplication (etc). RPN does not require + parentheses because the ordering of objects on the stack + can force the evaluation of addition before multiplication. + + +Exercise 4: + +Explain why it was desirable for the RRDtool developers to implement +RPN notation instead of traditional mathematical notation. + +Answer 4: + + The algorithm that implements traditional mathematical + notation is more complex then algorithm used for RPN. + So implementing RPN allowed Tobias Oetiker to write less + code! (The code is also less complex and therefore less + likely to have bugs.) + + +=head1 AUTHOR + +Steve Rader Erader@wiscnet.netE diff --git a/program/doc/rrd-beginners.pod b/program/doc/rrd-beginners.pod new file mode 100644 index 00000000..5ac79973 --- /dev/null +++ b/program/doc/rrd-beginners.pod @@ -0,0 +1,326 @@ +=head1 NAME + +rrd-beginners - RRDtool Beginners' Guide + +=head1 SYNOPSIS + +Helping new RRDtool users to understand the basics of RRDtool + +=head1 DESCRIPTION + +This manual is an attempt to assist beginners in understanding the concepts +of RRDtool. It sheds a light on differences between RRDtool and other +databases. With help of an example, it explains the structure of RRDtool +database. This is followed by an overview of the "graph" feature of RRDtool. +At the end, it has sample scripts that illustrate the +usage/wrapping of RRDtool within Shell or Perl scripts. + +=head2 What makes RRDtool so special? + +RRDtool is GNU licensed software developed by Tobias Oetiker, a system +manager at the Swiss Federal Institute of Technology. Though it is a +database, there are distinct differences between RRDtool databases and other +databases as listed below: + +=over + +=item * + +RRDtool stores data; that makes it a back-end tool. The RRDtool command set +allows the creation of graphs; that makes it a front-end tool as well. Other +databases just store data and can not create graphs. + +=item * + +In case of linear databases, new data gets appended at the bottom of +the database table. Thus its size keeps on increasing, whereas the size of +an RRDtool database is determined at creation time. Imagine an RRDtool +database as the perimeter of a circle. Data is added along the +perimeter. When new data reaches the starting point, it overwrites +existing data. This way, the size of an RRDtool database always +remains constant. The name "Round Robin" stems from this behavior. + +=item * + +Other databases store the values as supplied. RRDtool can be configured to +calculate the rate of change from the previous to the current value and +store this information instead. + +=item * + +Other databases get updated when values are supplied. The RRDtool database +is structured in such a way that it needs data at predefined time +intervals. If it does not get a new value during the interval, it stores an +UNKNOWN value for that interval. So, when using the RRDtool database, it is +imperative to use scripts that run at regular intervals to ensure a constant +data flow to update the RRDtool database. + +=back + +RRDtool is designed to store time series of data. With every data +update, an associated time stamp is stored. Time is always expressed +in seconds passed since epoch (01-01-1970). RRDtool can be installed +on Unix as well as Windows. It comes with a command set to carry out +various operations on RRD databases. This command set can be accessed +from the command line, as well as from Shell or Perl scripts. The +scripts act as wrappers for accessing data stored in RRDtool +databases. + +=head2 Understanding by an example + +The structure of an RRD database is different than other linear databases. +Other databases define tables with columns, and many other parameters. These +definitions sometimes are very complex, especially in large databases. +RRDtool databases are primarily used for monitoring purposes and +hence are very simple in structure. The parameters +that need to be defined are variables that hold values and archives of those +values. Being time sensitive, a couple of time related parameters are also +defined. Because of its structure, the definition of an RRDtool database also +includes a provision to specify specific actions to take in the absence of +update values. Data Source (DS), heartbeat, Date Source Type (DST), Round +Robin Archive (RRA), and Consolidation Function (CF) are some of the +terminologies related to RRDtool databases. + +The structure of a database and the terminology associated with it can be +best explained with an example. + + rrdtool create target.rrd \ + --start 1023654125 \ + --step 300 \ + DS:mem:GAUGE:600:0:671744 \ + RRA:AVERAGE:0.5:12:24 \ + RRA:AVERAGE:0.5:288:31 + +This example creates a database named F. Start time +(1'023'654'125) is specified in total number of seconds since epoch +(time in seconds since 01-01-1970). While updating the database, the +update time is also specified. This update time MUST be large (later) +then start time and MUST be in seconds since epoch. + +The step of 300 seconds indicates that database expects new values every +300 seconds. The wrapper script should be scheduled to run every B +seconds so that it updates the database every B seconds. + +DS (Data Source) is the actual variable which relates to the parameter on +the device that is monitored. Its syntax is + + DS:variable_name:DST:heartbeat:min:max + +B is a key word. C is a name under which the parameter is +saved in the database. There can be as many DSs in a database as needed. After +every step interval, a new value of DS is supplied to update the database. +This value is also called Primary Data Point B<(PDP)>. In our example +mentioned above, a new PDP is generated every 300 seconds. + +Note, that if you do NOT supply new datapoints exactly every 300 seconds, +this is not a problem, RRDtool will interpolate the data accordingly. + +B (Data Source Type) defines the type of the DS. It can be +COUNTER, DERIVE, ABSOLUTE, GAUGE. A DS declared as COUNTER will save +the rate of change of the value over a step period. This assumes that +the value is always increasing (the difference between the current and +the previous value is greater than 0). Traffic counters on a router +are an ideal candidate for using COUNTER as DST. DERIVE is the same as +COUNTER, but it allows negative values as well. If you want to see the +rate of I in free diskspace on your server, then you might +want to use the DERIVE data type. ABSOLUTE also saves the rate of +change, but it assumes that the previous value is set to 0. The +difference between the current and the previous value is always equal +to the current value. Thus it just stores the current value divided by +the step interval (300 seconds in our example). GAUGE does not save +the rate of change. It saves the actual value itself. There are no +divisions or calculations. Memory consumption in a server is a typical +example of gauge. The difference between the different types DSTs can be +explained better with the following example: + + Values = 300, 600, 900, 1200 + Step = 300 seconds + COUNTER DS = 1, 1, 1, 1 + DERIVE DS = 1, 1, 1, 1 + ABSOLUTE DS = 1, 2, 3, 4 + GAUGE DS = 300, 600, 900, 1200 + +The next parameter is B. In our example, heartbeat is 600 +seconds. If the database does not get a new PDP within 300 seconds, it +will wait for another 300 seconds (total 600 seconds). If it doesn't +receive any PDP within 600 seconds, it will save an UNKNOWN value into +the database. This UNKNOWN value is a special feature of RRDtool - it +is much better than to assume a missing value was 0 (zero) or any +other number which might also be a valid data value. For example, the +traffic flow counter on a router keeps increasing. Lets say, a value +is missed for an interval and 0 is stored instead of UNKNOWN. Now when +the next value becomes available, it will calculate the difference +between the current value and the previous value (0) which is not +correct. So, inserting the value UNKNOWN makes much more sense here. + +The next two parameters are the minimum and maximum value, +respectively. If the variable to be stored has predictable maximum and +minimum values, this should be specified here. Any update value +falling out of this range will be stored as UNKNOWN. + +The next line declares a round robin archive (RRA). The syntax for +declaring an RRA is + + RRA:CF:xff:step:rows + +RRA is the keyword to declare RRAs. The consolidation function (CF) +can be AVERAGE, MINIMUM, MAXIMUM, and LAST. The concept of the +consolidated data point (CDP) comes into the picture here. A CDP is +CFed (averaged, maximum/minimum value or last value) from I +number of PDPs. This RRA will hold I CDPs. + +Lets have a look at the example above. For the first RRA, 12 (steps) +PDPs (DS variables) are AVERAGEed (CF) to form one CDP. 24 (rows) of +theses CDPs are archived. Each PDP occurs at 300 seconds. 12 PDPs +represent 12 times 300 seconds which is 1 hour. It means 1 CDP (which +is equal to 12 PDPs) represents data worth 1 hour. 24 such CDPs +represent 1 day (1 hour times 24 CDPs). This means, this RRA is an +archive for one day. After 24 CDPs, CDP number 25 will replace the 1st +CDP. The second RRA saves 31 CDPs; each CPD represents an AVERAGE +value for a day (288 PDPs, each covering 300 seconds = 24 +hours). Therefore this RRA is an archive for one month. A single +database can have many RRAs. If there are multiple DSs, each +individual RRA will save data for all the DSs in the database. For +example, if a database has 3 DSs and daily, weekly, monthly, and +yearly RRAs are declared, then each RRA will hold data from all 3 data +sources. + +=head2 Graphical Magic + +Another important feature of RRDtool is its ability to create +graphs. The "graph" command uses the "fetch" command internally to +retrieve values from the database. With the retrieved values it draws +graphs as defined by the parameters supplied on the command line. A +single graph can show different DS (Data Sources) from a database. It +is also possible to show the values from more than one database in a +single graph. Often, it is necessary to perform some math on the +values retrieved from the database before plotting them. For example, +in SNMP replies, memory consumption values are usually specified in +KBytes and traffic flow on interfaces is specified in Bytes. Graphs +for these values will be more meaningful if values are represented in +MBytes and mbps. The RRDtool graph command allows to define such +conversions. Apart from mathematical calculations, it is also possible +to perform logical operations such as greater than, less than, and +if/then/else. If a database contains more than one RRA archive, then a +question may arise - how does RRDtool decide which RRA archive to use +for retrieving the values? RRDtool looks at several things when making +its choice. First it makes sure that the RRA covers as much of the +graphing time frame as possible. Second it looks at the resolution of +the RRA compared to the resolution of the graph. It tries to find one +which has the same or higher better resolution. With the "-r" option +you can force RRDtool to assume a different resolution than the one +calculated from the pixel width of the graph. + +Values of different variables can be presented in 5 different shapes +in a graph - AREA, LINE1, LINE2, LINE3, and STACK. AREA is represented +by a solid colored area with values as the boundary of this +area. LINE1/2/3 (increasing width) are just plain lines representing +the values. STACK is also an area but it is "stack"ed on top AREA or +LINE1/2/3. Another important thing to note is that variables are +plotted in the order they are defined in the graph command. Therefore +care must be taken to define STACK only after defining AREA/LINE. It +is also possible to put formatted comments within the graph. Detailed +instructions can be found in the graph manual. + +=head2 Wrapping RRDtool within Shell/Perl script + +After understanding RRDtool it is now a time to actually use RRDtool +in scripts. Tasks involved in network management are data collection, +data storage, and data retrieval. In the following example, the +previously created target.rrd database is used. Data collection and +data storage is done using Shell scripts. Data retrieval and report +generation is done using Perl scripts. These scripts are shown below: + +=head3 Shell script (collects data, updates database) + + #!/bin/sh + a=0 + while [ "$a" == 0 ]; do + snmpwalk -c public 192.168.1.250 hrSWRunPerfMem > snmp_reply + total_mem=`awk 'BEGIN {tot_mem=0} + { if ($NF == "KBytes") + {tot_mem=tot_mem+$(NF-1)} + } + END {print tot_mem}' snmp_reply` + # I can use N as a replacement for the current time + rrdtool update target.rrd N:$total_mem + # sleep until the next 300 seconds are full + perl -e 'sleep 300 - time % 300' + done # end of while loop + +=head3 Perl script (retrieves data from database and generates graphs and statistics) + + #!/usr/bin/perl -w + # This script fetches data from target.rrd, creates a graph of memory + # consumption on the target (Dual P3 Processor 1 GHz, 656 MB RAM) + + # call the RRD perl module + use lib qw( /usr/local/rrdtool-1.0.41/lib/perl ../lib/perl ); + use RRDs; + my $cur_time = time(); # set current time + my $end_time = $cur_time - 86400; # set end time to 24 hours ago + my $start_time = $end_time - 2592000; # set start 30 days in the past + + # fetch average values from the RRD database between start and end time + my ($start,$step,$ds_names,$data) = + RRDs::fetch("target.rrd", "AVERAGE", + "-r", "600", "-s", "$start_time", "-e", "$end_time"); + # save fetched values in a 2-dimensional array + my $rows = 0; + my $columns = 0; + my $time_variable = $start; + foreach $line (@$data) { + $vals[$rows][$columns] = $time_variable; + $time_variable = $time_variable + $step; + foreach $val (@$line) { + $vals[$rows][++$columns] = $val;} + $rows++; + $columns = 0; + } + my $tot_time = 0; + my $count = 0; + # save the values from the 2-dimensional into a 1-dimensional array + for $i ( 0 .. $#vals ) { + $tot_mem[$count] = $vals[$i][1]; + $count++; + } + my $tot_mem_sum = 0; + # calculate the total of all values + for $i ( 0 .. ($count-1) ) { + $tot_mem_sum = $tot_mem_sum + $tot_mem[$i]; + } + # calculate the average of the array + my $tot_mem_ave = $tot_mem_sum/($count); + # create the graph + RRDs::graph ("/images/mem_$count.png", \ + "--title= Memory Usage", \ + "--vertical-label=Memory Consumption (MB)", \ + "--start=$start_time", \ + "--end=$end_time", \ + "--color=BACK#CCCCCC", \ + "--color=CANVAS#CCFFFF", \ + "--color=SHADEB#9999CC", \ + "--height=125", \ + "--upper-limit=656", \ + "--lower-limit=0", \ + "--rigid", \ + "--base=1024", \ + "DEF:tot_mem=target.rrd:mem:AVERAGE", \ + "CDEF:tot_mem_cor=tot_mem,0,671744,LIMIT,UN,0,tot_mem,IF,1024,/",\ + "CDEF:machine_mem=tot_mem,656,+,tot_mem,-",\ + "COMMENT:Memory Consumption between $start_time",\ + "COMMENT: and $end_time ",\ + "HRULE:656#000000:Maximum Available Memory - 656 MB",\ + "AREA:machine_mem#CCFFFF:Memory Unused", \ + "AREA:tot_mem_cor#6699CC:Total memory consumed in MB"); + my $err=RRDs::error; + if ($err) {print "problem generating the graph: $err\n";} + # print the output + print "Average memory consumption is "; + printf "%5.2f",$tot_mem_ave/1024; + print " MB. Graphical representation can be found at /images/mem_$count.png."; + +=head1 AUTHOR + +Ketan Patel Ek2pattu@yahoo.comE + diff --git a/program/doc/rrdbuild.pod b/program/doc/rrdbuild.pod new file mode 100644 index 00000000..7a24a823 --- /dev/null +++ b/program/doc/rrdbuild.pod @@ -0,0 +1,205 @@ +=head1 NAME + +rrdbuild - Instructions for building RRDtool + +=head1 DESCRIPTION + +=head2 Overview + +If you downloaded the source of rrdtool you have to compile it. This +document will give some information on how this is done. + +RRDtool relies on services of thrid part libraries. Some of these libraries +may already be installed on your system. You have to compile copies of the other +ones before you can build RRDtool. + +This document will tell you about all the necessary steps to get going. + +=head2 Building + +Before you start to build RRDtool, you have to decide two things: + +=over + +=item 1. + +In which directory you want to build the software. + +=item 2. + +Where you want to install the software. + +=back + +Once you have decided. Save the two locations into environment variables. +Depending on the shell you are using, you can do either (bash,zsh): + + BUILD_DIR=/tmp/rrdbuild + INSTALL_DIR=/usr/local/rrdtool-1.2.23 + +Or if you run tcsh: + + set BUILD_DIR=/tmp/rrdbuild + set INSTALL_DIR=/usr/local/rrdtool-1.2.23 + +If your F is mounted with the option noexec (RHEL seems todo that) you have to choose +a different directory! + +Now make sure the BUILD_DIR exists and go there: + + mkdir -p $BUILD_DIR + cd $BUILD_DIR + +Lets first assume you already have all the necessary libraries +pre-installed. Note that these instructions assume that your copies of +B and B are actually B and B respectively. It +could be that they are installed as B and B on your system. + + wget http://oss.oetiker.ch/rrdtool/pub/rrdtool-1.2.23.tar.gz + tar zxf rrdtool-1.2.23.tar.gz + cd rrdtool-1.2.23 + ./configure --prefix=$INSTALL_DIR && make && make install + +Ok, this was very optimistic. This try will probably have ended with +B complaining about several missing libraries. If you are on a +Linux or *bsd system you may want to just install the missing bits from your +software repository. When you do that, make sure you also get the B<-dev> +package for each library you install. Once you have the missing bits on +board, just re-run the last line of the instructions above. + +But again this may have been too optimistic, and you actually have to +compile your own copies of the required libraries. + +=head3 Build Tipps for AIX + +If you are woking with AIX, you may find the the B<--disable-shared> option +will cause things to break for you. In that case you may have to install the +shared libraries into the rrdtool PREFIX and work with B<--disable-static> +instead. + +Another hint to get rrdtool working on AIX is to use the IBM XL C Compiler: + + export CC=/usr/vac/bin/cc + export PERLCC=$CC + +(Better instructions for AIX welcome!) + +=head2 Building Libraries + +In order to build the libraries you need a compiler on your system. +Unfortunately compilers are not all alike. This has an effect on the CFLAGS +you want to set. The examples below are for the popular GCC compiler suite. +If you have an other compile you have to use the following settings: + +=over + +=item Sun Forte + + CFLAGS="-xO3 -kPIC" + +=back + +=over + +=item Building zlib + + cd $BUILD_DIR + wget http://oss.oetiker.ch/rrdtool/pub/libs/zlib-1.2.3.tar.gz + tar zxf zlib-1.2.3.tar.gz + cd zlib-1.2.3 + env CFLAGS="-O3 -fPIC" ./configure --prefix=$BUILD_DIR/lb + make + make install + +=item Building libpng + +Libpng itself requires zlib to build, so we need to help a bit. If you +already have a copy of zlib on your system (which is very likley) you can +drop the settings of LDFLAGS and CPPFLAGS. Note that the backslash (\) at +the end of line 4 means that line 4 and line 5 are on one line. + + cd $BUILD_DIR + wget http://oss.oetiker.ch/rrdtool/pub/libs/libpng-1.2.10.tar.gz + tar zxvf libpng-1.2.10.tar.gz + cd libpng-1.2.10 + env CPPFLAGS="-I$BUILD_DIR/lb/include" LDFLAGS="-L$BUILD_DIR/lb/lib" CFLAGS="-O3 -fPIC" \ + ./configure --disable-shared --prefix=$BUILD_DIR/lb + make + make install + +=item Building freetype + + cd $BUILD_DIR + wget http://oss.oetiker.ch/rrdtool/pub/libs/freetype-2.1.10.tar.bz2 + tar jxvf freetype-2.1.10.tar.bz2 + cd freetype-2.1.10 + env CPPFLAGS="-I$BUILD_DIR/lb/include" LDFLAGS="-L$BUILD_DIR/lb/lib" CFLAGS="-O3 -fPIC" \ + ./configure --disable-shared --prefix=$BUILD_DIR/lb + make + make install + +If you run into problems building freetype on Solaris, you may want to try to +add the following at the end of the configure line: + + GNUMAKE=gmake EGREP=egrep + +=item Building libart_lgpl + + cd $BUILD_DIR + wget http://oss.oetiker.ch/rrdtool/pub/libs/libart_lgpl-2.3.17.tar.gz + tar zxvf libart_lgpl-2.3.17.tar.gz + cd libart_lgpl-2.3.17 + env CFLAGS="-O3 -fPIC" ./configure --disable-shared --prefix=$BUILD_DIR/lb + make + make install + +=back + +Now all the dependent libraries are built and you can try again. Since these +are static libraries, you may have to use F to make them accessible. +Especially BSD systems like Mac OS X may require this, Linux and Solaris +will do just fine without since their F command does ranlibs job as well. + + ranlib $BUILD_DIR/lb/lib/*.a + +This time you tell configure where it should be looking for libraries and +include files. This is done via environment variables. Depending on the +shell you are running, the syntax for setting environment variables is +different. Under csh/tcsh you use: + + set IR=-I$BUILD_DIR/lb/include + setenv CPPFLAGS "$IR $IR/libart-2.0 $IR/freetype2 $IR/libpng" + setenv LDFLAGS -L$BUILD_DIR/lb/lib + setenv CFLAGS -O3 + +If you are running bash/sh/ash/ksh/zsh use this: + + IR=-I$BUILD_DIR/lb/include + CPPFLAGS="$IR $IR/libart-2.0 $IR/freetype2 $IR/libpng" + LDFLAGS="-L$BUILD_DIR/lb/lib" + CFLAGS=-O3 + export CPPFLAGS LDFLAGS CFLAGS + +And finally try building again. We disable the python and tcl bindings +because it seems that a fair number of people have ill configured python and +tcl setups that would prevent rrdtool from building if they are included in +their current state. + + cd $BUILD_DIR/rrdtool-1.2.23 + ./configure --prefix=$INSTALL_DIR --disable-python --disable-tcl + make clean + make + make install + +SOLARIS HINT: if you want to build the perl module for the native perl (the +one shipping with solaris) you will need the sun forte compiler +installed on your box or you have to hand-tune bindings/perl-shared/Makefile +while building! + +Now go to I<$INSTALL_DIR>B and run them to see if your +build has been successful. + +=head1 AUTHOR + +Tobias Oetiker Etobi@oetiker.chE + diff --git a/program/doc/rrdcgi.pod b/program/doc/rrdcgi.pod new file mode 100644 index 00000000..4c81e018 --- /dev/null +++ b/program/doc/rrdcgi.pod @@ -0,0 +1,225 @@ +=head1 NAME + +rrdcgi - Create web pages containing RRD graphs based on templates + +=head1 SYNOPSIS + +C<#!/path/to/>B S<[B<--filter>]> + +=head1 DESCRIPTION + +B is a sort of very limited script interpreter. Its purpose +is to run as a cgi-program and parse a web page template containing special +ERRD:: tags. B will interpret and act according to these tags. +In the end it will printout a web page including the necessary CGI headers. + +B parses the contents of the template in 3 steps. In each step it looks +only for a subset of tags. This allows nesting of tags. + +The argument parser uses the same semantics as you are used from your C-shell. + +=over 8 + +=item B<--filter> + +Assume that rrdcgi is run as a filter and not as a cgi. + +=back + +=head2 Keywords + +=over 8 + +=item RRD::CV I + +Inserts the CGI variable of the given name. + +=item RRD::CV::QUOTE I + +Inserts the CGI variable of the given name but quotes it, ready for +use as an argument in another RRD:: tag. So even when there are spaces in the +value of the CGI variable it will still be considered to be one argument. + +=item RRD::CV::PATH I + +Inserts the CGI variable of the given name, quotes it and makes sure +it starts neither with a '/' nor contains '..'. This is to make +sure that no problematic pathnames can be introduced through the +CGI interface. + +=item RRD::GETENV I + +Get the value of an environment variable. + + + +might give you the name of the remote user given you are using +some sort of access control on the directory. + + +=item RRD::GOODFOR I + +Specify the number of seconds this page should remain valid. This will prompt +the rrdcgi to output a Last-Modified, an Expire and if the number of +seconds is I a Refresh header. + +=item RRD::INCLUDE I + +Include the contents of the specified file into the page returned from the cgi. + +=item RRD::SETENV I I + +If you want to present your graphs in another time zone than your own, you +could use + + + +to make sure everything is presented in Universal Time. Note that the +values permitted to TZ depend on your OS. + +=item RRD::SETVAR I I + +Analog to SETENV but for local variables. + +=item RRD::GETVAR I + +Analog to GETENV but for local variables. + +=item RRD::TIME::LAST I I + +This gets replaced by the last modification time of the selected RRD. The +time is I-formatted with the string specified in the second argument. + +=item RRD::TIME::NOW I + +This gets replaced by the current time of day. The time is +I-formatted with the string specified in the argument. + +Note that if you return : (colons) from your strftime format you may +have to escape them using \ if the time is to be used as an argument +to a GRAPH command. + +=item RRD::TIME::STRFTIME I I I I + +This gets replaced by a strftime-formatted time using the format +I on either I or I depending on +whether I or I is specified. Both I and I +must be supplied as either could be relative to the other. This is intended +to allow pretty titles on graphs with times that are easier for non RRDtool +folks to figure out than "-2weeks". + +Note that again, if you return : (colon) from your strftime format, +you may have to escape them using \ if the time is to be used as an +argument to a GRAPH command. + +=item RRD::GRAPH I + +This tag creates the RRD graph defined by its argument and then is +replaced by an appropriate EIMG ... E tag referring to the graph. +The B<--lazy> option in RRD graph can be used to make sure that graphs +are only regenerated when they are out of date. The arguments +to the B tag work as described in the B manual page. + +Use the B<--lazy> option in your RRD::GRAPH tags, to reduce the load +on your server. This option makes sure that graphs are only regenerated when +the old ones are out of date. + +If you do not specify your own B<--imginfo> format, the following will +be used: + + + +Note that %s stands for the filename part of the graph generated, all +directories given in the PNG file argument will get dropped. + +=item RRD::PRINT I + +If the preceding B tag contained and B arguments, +then you can access their output with this tag. The I argument refers to the +number of the B argument. This first B has I 0. + +=item RRD::INTERNAL + +This tag gets replaced by an internal var. Currently these vars are known: +VERSION, COMPILETIME. +These vars represent the compiled-in values. + +=back + +=head1 EXAMPLE 1 + +The example below creates a web pages with a single RRD graph. + + #!/usr/local/bin/rrdcgi + + RRDCGI Demo + +

RRDCGI Example Page

+

+ + +

+ + + +=head1 EXAMPLE 2 + +This script is slightly more elaborate, it allows you to run it from +a form which sets RRD_NAME. RRD_NAME is then used to select which RRD +you want to use as source for your graph. + + #!/usr/local/bin/rrdcgi + + RRDCGI Demo + +

RRDCGI Example Page for

+

Selection

+
Room A, + Room B. +
+

Graph

+

+ .png --lazy + --title "Temperatures for " + DEF:cel=.rrd:exhaust:AVERAGE + LINE2:cel#00a000:"D. Celsius"> + +

+ + + +=head1 EXAMPLE 3 + +This example shows how to handle the case where the RRD, graphs and +cgi-bins are seperate directories + + #!/.../bin/rrdcgi + + RRDCGI Demo + +

RRDCGI test Page

+ ' + --lazy --start -1d --end now + DEF:http_src=/.../rrds/test.rrd:http_src:AVERAGE + AREA:http_src#00ff00:http_src + > + + + +Note 1: Replace /.../ with the relevant directories + +Note 2: The SRC=/.../pngs should be paths from the view of the +webserver/browser + +=head1 AUTHOR + +Tobias Oetiker Etobi@oetiker.chE + + + + + diff --git a/program/doc/rrdcreate.pod b/program/doc/rrdcreate.pod new file mode 100644 index 00000000..27ef702a --- /dev/null +++ b/program/doc/rrdcreate.pod @@ -0,0 +1,553 @@ +=head1 NAME + +rrdcreate - Set up a new Round Robin Database + +=head1 SYNOPSIS + +B B I +S<[B<--start>|B<-b> I]> +S<[B<--step>|B<-s> I]> +S<[BIB<:>IB<:>I]> +S<[BIB<:>I]> + +=head1 DESCRIPTION + +The create function of RRDtool lets you set up new Round Robin +Database (B) files. The file is created at its final, full size +and filled with I<*UNKNOWN*> data. + +=over 8 + +=item I + +The name of the B you want to create. B files should end +with the extension F<.rrd>. However, B will accept any +filename. + +=item B<--start>|B<-b> I (default: now - 10s) + +Specifies the time in seconds since 1970-01-01 UTC when the first +value should be added to the B. B will not accept +any data timed before or at the time specified. + +See also AT-STYLE TIME SPECIFICATION section in the +I documentation for other ways to specify time. + +=item B<--step>|B<-s> I (default: 300 seconds) + +Specifies the base interval in seconds with which data will be fed +into the B. + +=item BIB<:>IB<:>I + +A single B can accept input from several data sources (B), +for example incoming and outgoing traffic on a specific communication +line. With the B configuration option you must define some basic +properties of each data source you want to store in the B. + +I is the name you will use to reference this particular data +source from an B. A I must be 1 to 19 characters long in +the characters [a-zA-Z0-9_]. + +I defines the Data Source Type. The remaining arguments of a +data source entry depend on the data source type. For GAUGE, COUNTER, +DERIVE, and ABSOLUTE the format for a data source entry is: + +BIB<:>IB<:>IB<:>IB<:>I + +For COMPUTE data sources, the format is: + +BIB<:>IB<:>I + +In order to decide which data source type to use, review the +definitions that follow. Also consult the section on "HOW TO MEASURE" +for further insight. + +=over 4 + +=item B + +is for things like temperatures or number of people in a room or the +value of a RedHat share. + +=item B + +is for continuous incrementing counters like the ifInOctets counter in +a router. The B data source assumes that the counter never +decreases, except when a counter overflows. The update function takes +the overflow into account. The counter is stored as a per-second +rate. When the counter overflows, RRDtool checks if the overflow +happened at the 32bit or 64bit border and acts accordingly by adding +an appropriate value to the result. + +=item B + +will store the derivative of the line going from the last to the +current value of the data source. This can be useful for gauges, for +example, to measure the rate of people entering or leaving a +room. Internally, derive works exactly like COUNTER but without +overflow checks. So if your counter does not reset at 32 or 64 bit you +might want to use DERIVE and combine it with a MIN value of 0. + +=over + +=item NOTE on COUNTER vs DERIVE + +by Don Baarda Edon.baarda@baesystems.comE + +If you cannot tolerate ever mistaking the occasional counter reset for a +legitimate counter wrap, and would prefer "Unknowns" for all legitimate +counter wraps and resets, always use DERIVE with min=0. Otherwise, using +COUNTER with a suitable max will return correct values for all legitimate +counter wraps, mark some counter resets as "Unknown", but can mistake some +counter resets for a legitimate counter wrap. + +For a 5 minute step and 32-bit counter, the probability of mistaking a +counter reset for a legitimate wrap is arguably about 0.8% per 1Mbps of +maximum bandwidth. Note that this equates to 80% for 100Mbps interfaces, so +for high bandwidth interfaces and a 32bit counter, DERIVE with min=0 is +probably preferable. If you are using a 64bit counter, just about any max +setting will eliminate the possibility of mistaking a reset for a counter +wrap. + +=back + +=item B + +is for counters which get reset upon reading. This is used for fast counters +which tend to overflow. So instead of reading them normally you reset them +after every read to make sure you have a maximum time available before the +next overflow. Another usage is for things you count like number of messages +since the last update. + +=item B + +is for storing the result of a formula applied to other data sources +in the B. This data source is not supplied a value on update, but +rather its Primary Data Points (PDPs) are computed from the PDPs of +the data sources according to the rpn-expression that defines the +formula. Consolidation functions are then applied normally to the PDPs +of the COMPUTE data source (that is the rpn-expression is only applied +to generate PDPs). In database software, such data sets are referred +to as "virtual" or "computed" columns. + +=back + +I defines the maximum number of seconds that may pass +between two updates of this data source before the value of the +data source is assumed to be I<*UNKNOWN*>. + +I and I define the expected range values for data supplied by a +data source. If I and/or I any value outside the defined range +will be regarded as I<*UNKNOWN*>. If you do not know or care about min and +max, set them to U for unknown. Note that min and max always refer to the +processed values of the DS. For a traffic-B type DS this would be +the maximum and minimum data-rate expected from the device. + +I + +I defines the formula used to compute the PDPs of a +COMPUTE data source from other data sources in the same . It is +similar to defining a B argument for the graph command. Please +refer to that manual page for a list and description of RPN operations +supported. For COMPUTE data sources, the following RPN operations are +not supported: COUNT, PREV, TIME, and LTIME. In addition, in defining +the RPN expression, the COMPUTE data source may only refer to the +names of data source listed previously in the create command. This is +similar to the restriction that Bs must refer only to Bs +and Bs previously defined in the same graph command. + +=item BIB<:>I + + +The purpose of an B is to store data in the round robin archives +(B). An archive consists of a number of data values or statistics for +each of the defined data-sources (B) and is defined with an B line. + +When data is entered into an B, it is first fit into time slots +of the length defined with the B<-s> option, thus becoming a I. + +The data is also processed with the consolidation function (I) of +the archive. There are several consolidation functions that +consolidate primary data points via an aggregate function: B, +B, B, B. The format of B line for these +consolidation functions is: + +BIB<:>IB<:>IB<:>I + +I The xfiles factor defines what part of a consolidation interval may +be made up from I<*UNKNOWN*> data while the consolidated value is still +regarded as known. It is given as the ratio of allowed I<*UNKNOWN*> PDPs +to the number of PDPs in the interval. Thus, it ranges from 0 to 1 (exclusive). + + +I defines how many of these I are used to build +a I which then goes into the archive. + +I defines how many generations of data values are kept in an B. + +=back + +=head1 Aberrant Behavior Detection with Holt-Winters Forecasting + +In addition to the aggregate functions, there are a set of specialized +functions that enable B to provide data smoothing (via the +Holt-Winters forecasting algorithm), confidence bands, and the +flagging aberrant behavior in the data source time series: + +=over + +=item * + +BIB<:>IB<:>IB<:>IB<:>I[B<:>I] + +=item * + +BIB<:>IB<:>IB<:>I + +=item * + +BIB<:>IB<:>IB<:>I + +=item * + +BIB<:>IB<:>I + +=item * + +BIB<:>IB<:>IB<:>IB<:>I + +=back + +These B differ from the true consolidation functions in several ways. +First, each of the Bs is updated once for every primary data point. +Second, these B are interdependent. To generate real-time confidence +bounds, a matched set of HWPREDICT, SEASONAL, DEVSEASONAL, and +DEVPREDICT must exist. Generating smoothed values of the primary data points +requires both a HWPREDICT B and SEASONAL B. Aberrant behavior +detection requires FAILURES, HWPREDICT, DEVSEASONAL, and SEASONAL. + +The actual predicted, or smoothed, values are stored in the HWPREDICT +B. The predicted deviations are stored in DEVPREDICT (think a standard +deviation which can be scaled to yield a confidence band). The FAILURES +B stores binary indicators. A 1 marks the indexed observation as +failure; that is, the number of confidence bounds violations in the +preceding window of observations met or exceeded a specified threshold. An +example of using these B to graph confidence bounds and failures +appears in L. + +The SEASONAL and DEVSEASONAL B store the seasonal coefficients for the +Holt-Winters forecasting algorithm and the seasonal deviations, respectively. +There is one entry per observation time point in the seasonal cycle. For +example, if primary data points are generated every five minutes and the +seasonal cycle is 1 day, both SEASONAL and DEVSEASONAL will have 288 rows. + +In order to simplify the creation for the novice user, in addition to +supporting explicit creation of the HWPREDICT, SEASONAL, DEVPREDICT, +DEVSEASONAL, and FAILURES B, the B create command supports +implicit creation of the other four when HWPREDICT is specified alone and +the final argument I is omitted. + +I specifies the length of the B prior to wrap around. Remember +that there is a one-to-one correspondence between primary data points and +entries in these RRAs. For the HWPREDICT CF, I should be larger than +the I. If the DEVPREDICT B is implicitly created, the +default number of rows is the same as the HWPREDICT I argument. If the +FAILURES B is implicitly created, I will be set to the I argument of the HWPREDICT B. Of course, the B +I command is available if these defaults are not sufficient and the +creator wishes to avoid explicit creations of the other specialized function +B. + +I specifies the number of primary data points in a seasonal +cycle. If SEASONAL and DEVSEASONAL are implicitly created, this argument for +those B is set automatically to the value specified by HWPREDICT. If +they are explicitly created, the creator should verify that all three +I arguments agree. + +I is the adaption parameter of the intercept (or baseline) +coefficient in the Holt-Winters forecasting algorithm. See L for a +description of this algorithm. I must lie between 0 and 1. A value +closer to 1 means that more recent observations carry greater weight in +predicting the baseline component of the forecast. A value closer to 0 means +that past history carries greater weight in predicting the baseline +component. + +I is the adaption parameter of the slope (or linear trend) coefficient +in the Holt-Winters forecasting algorithm. I must lie between 0 and 1 +and plays the same role as I with respect to the predicted linear +trend. + +I is the adaption parameter of the seasonal coefficients in the +Holt-Winters forecasting algorithm (HWPREDICT) or the adaption parameter in +the exponential smoothing update of the seasonal deviations. It must lie +between 0 and 1. If the SEASONAL and DEVSEASONAL B are created +implicitly, they will both have the same value for I: the value +specified for the HWPREDICT I argument. Note that because there is +one seasonal coefficient (or deviation) for each time point during the +seasonal cycle, the adaptation rate is much slower than the baseline. Each +seasonal coefficient is only updated (or adapts) when the observed value +occurs at the offset in the seasonal cycle corresponding to that +coefficient. + +If SEASONAL and DEVSEASONAL B are created explicitly, I need not +be the same for both. Note that I can also be changed via the +B I command. + +I provides the links between related B. If HWPREDICT is +specified alone and the other B are created implicitly, then +there is no need to worry about this argument. If B are created +explicitly, then carefully pay attention to this argument. For each +B which includes this argument, there is a dependency between +that B and another B. The I argument is the 1-based +index in the order of B creation (that is, the order they appear +in the I command). The dependent B for each B +requiring the I argument is listed here: + +=over + +=item * + +HWPREDICT I is the index of the SEASONAL B. + +=item * + +SEASONAL I is the index of the HWPREDICT B. + +=item * + +DEVPREDICT I is the index of the DEVSEASONAL B. + +=item * + +DEVSEASONAL I is the index of the HWPREDICT B. + +=item * + +FAILURES I is the index of the DEVSEASONAL B. + +=back + +I is the minimum number of violations (observed values outside +the confidence bounds) within a window that constitutes a failure. If the +FAILURES B is implicitly created, the default value is 7. + +I is the number of time points in the window. Specify an +integer greater than or equal to the threshold and less than or equal to 28. +The time interval this window represents depends on the interval between +primary data points. If the FAILURES B is implicitly created, the +default value is 9. + +=head1 The HEARTBEAT and the STEP + +Here is an explanation by Don Baarda on the inner workings of RRDtool. +It may help you to sort out why all this *UNKNOWN* data is popping +up in your databases: + +RRDtool gets fed samples at arbitrary times. From these it builds Primary +Data Points (PDPs) at exact times on every "step" interval. The PDPs are +then accumulated into RRAs. + +The "heartbeat" defines the maximum acceptable interval between +samples. If the interval between samples is less than "heartbeat", +then an average rate is calculated and applied for that interval. If +the interval between samples is longer than "heartbeat", then that +entire interval is considered "unknown". Note that there are other +things that can make a sample interval "unknown", such as the rate +exceeding limits, or even an "unknown" input sample. + +The known rates during a PDP's "step" interval are used to calculate +an average rate for that PDP. Also, if the total "unknown" time during +the "step" interval exceeds the "heartbeat", the entire PDP is marked +as "unknown". This means that a mixture of known and "unknown" sample +times in a single PDP "step" may or may not add up to enough "unknown" +time to exceed "heartbeat" and hence mark the whole PDP "unknown". So +"heartbeat" is not only the maximum acceptable interval between +samples, but also the maximum acceptable amount of "unknown" time per +PDP (obviously this is only significant if you have "heartbeat" less +than "step"). + +The "heartbeat" can be short (unusual) or long (typical) relative to +the "step" interval between PDPs. A short "heartbeat" means you +require multiple samples per PDP, and if you don't get them mark the +PDP unknown. A long heartbeat can span multiple "steps", which means +it is acceptable to have multiple PDPs calculated from a single +sample. An extreme example of this might be a "step" of 5 minutes and a +"heartbeat" of one day, in which case a single sample every day will +result in all the PDPs for that entire day period being set to the +same average rate. I<-- Don Baarda Edon.baarda@baesystems.comE> + + time| + axis| + begin__|00| + |01| + u|02|----* sample1, restart "hb"-timer + u|03| / + u|04| / + u|05| / + u|06|/ "hbt" expired + u|07| + |08|----* sample2, restart "hb" + |09| / + |10| / + u|11|----* sample3, restart "hb" + u|12| / + u|13| / + step1_u|14| / + u|15|/ "swt" expired + u|16| + |17|----* sample4, restart "hb", create "pdp" for step1 = + |18| / = unknown due to 10 "u" labled secs > "hb" + |19| / + |20| / + |21|----* sample5, restart "hb" + |22| / + |23| / + |24|----* sample6, restart "hb" + |25| / + |26| / + |27|----* sample7, restart "hb" + step2__|28| / + |22| / + |23|----* sample8, restart "hb", create "pdp" for step1, create "cdp" + |24| / + |25| / + +graphics by I. + + +=head1 HOW TO MEASURE + +Here are a few hints on how to measure: + +=over + + +=item Temperature + +Usually you have some type of meter you can read to get the temperature. +The temperature is not really connected with a time. The only connection is +that the temperature reading happened at a certain time. You can use the +B data source type for this. RRDtool will then record your reading +together with the time. + +=item Mail Messages + +Assume you have a method to count the number of messages transported by +your mailserver in a certain amount of time, giving you data like '5 +messages in the last 65 seconds'. If you look at the count of 5 like an +B data type you can simply update the RRD with the number 5 and the +end time of your monitoring period. RRDtool will then record the number of +messages per second. If at some later stage you want to know the number of +messages transported in a day, you can get the average messages per second +from RRDtool for the day in question and multiply this number with the +number of seconds in a day. Because all math is run with Doubles, the +precision should be acceptable. + +=item It's always a Rate + +RRDtool stores rates in amount/second for COUNTER, DERIVE and ABSOLUTE +data. When you plot the data, you will get on the y axis +amount/second which you might be tempted to convert to an absolute +amount by multiplying by the delta-time between the points. RRDtool +plots continuous data, and as such is not appropriate for plotting +absolute amounts as for example "total bytes" sent and received in a +router. What you probably want is plot rates that you can scale to +bytes/hour, for example, or plot absolute amounts with another tool +that draws bar-plots, where the delta-time is clear on the plot for +each point (such that when you read the graph you see for example GB +on the y axis, days on the x axis and one bar for each day). + +=back + + +=head1 EXAMPLE + + rrdtool create temperature.rrd --step 300 \ + DS:temp:GAUGE:600:-273:5000 \ + RRA:AVERAGE:0.5:1:1200 \ + RRA:MIN:0.5:12:2400 \ + RRA:MAX:0.5:12:2400 \ + RRA:AVERAGE:0.5:12:2400 + +This sets up an B called F which accepts one +temperature value every 300 seconds. If no new data is supplied for +more than 600 seconds, the temperature becomes I<*UNKNOWN*>. The +minimum acceptable value is -273 and the maximum is 5'000. + +A few archive areas are also defined. The first stores the +temperatures supplied for 100 hours (1'200 * 300 seconds = 100 +hours). The second RRA stores the minimum temperature recorded over +every hour (12 * 300 seconds = 1 hour), for 100 days (2'400 hours). The +third and the fourth RRA's do the same for the maximum and +average temperature, respectively. + +=head1 EXAMPLE 2 + + rrdtool create monitor.rrd --step 300 \ + DS:ifOutOctets:COUNTER:1800:0:4294967295 \ + RRA:AVERAGE:0.5:1:2016 \ + RRA:HWPREDICT:1440:0.1:0.0035:288 + +This example is a monitor of a router interface. The first B tracks the +traffic flow in octets; the second B generates the specialized +functions B for aberrant behavior detection. Note that the I +argument of HWPREDICT is missing, so the other B will implicitly be +created with default parameter values. In this example, the forecasting +algorithm baseline adapts quickly; in fact the most recent one hour of +observations (each at 5 minute intervals) accounts for 75% of the baseline +prediction. The linear trend forecast adapts much more slowly. Observations +made during the last day (at 288 observations per day) account for only +65% of the predicted linear trend. Note: these computations rely on an +exponential smoothing formula described in the LISA 2000 paper. + +The seasonal cycle is one day (288 data points at 300 second intervals), and +the seasonal adaption parameter will be set to 0.1. The RRD file will store 5 +days (1'440 data points) of forecasts and deviation predictions before wrap +around. The file will store 1 day (a seasonal cycle) of 0-1 indicators in +the FAILURES B. + +The same RRD file and B are created with the following command, +which explicitly creates all specialized function B. + + rrdtool create monitor.rrd --step 300 \ + DS:ifOutOctets:COUNTER:1800:0:4294967295 \ + RRA:AVERAGE:0.5:1:2016 \ + RRA:HWPREDICT:1440:0.1:0.0035:288:3 \ + RRA:SEASONAL:288:0.1:2 \ + RRA:DEVPREDICT:1440:5 \ + RRA:DEVSEASONAL:288:0.1:2 \ + RRA:FAILURES:288:7:9:5 + +Of course, explicit creation need not replicate implicit create, a +number of arguments could be changed. + +=head1 EXAMPLE 3 + + rrdtool create proxy.rrd --step 300 \ + DS:Total:DERIVE:1800:0:U \ + DS:Duration:DERIVE:1800:0:U \ + DS:AvgReqDur:COMPUTE:Duration,Requests,0,EQ,1,Requests,IF,/ \ + RRA:AVERAGE:0.5:1:2016 + +This example is monitoring the average request duration during each 300 sec +interval for requests processed by a web proxy during the interval. +In this case, the proxy exposes two counters, the number of requests +processed since boot and the total cumulative duration of all processed +requests. Clearly these counters both have some rollover point, but using the +DERIVE data source also handles the reset that occurs when the web proxy is +stopped and restarted. + +In the B, the first data source stores the requests per second rate +during the interval. The second data source stores the total duration of all +requests processed during the interval divided by 300. The COMPUTE data source +divides each PDP of the AccumDuration by the corresponding PDP of +TotalRequests and stores the average request duration. The remainder of the +RPN expression handles the divide by zero case. + +=head1 AUTHOR + +Tobias Oetiker Etobi@oetiker.chE diff --git a/program/doc/rrddump.pod b/program/doc/rrddump.pod new file mode 100644 index 00000000..2e4cd4b2 --- /dev/null +++ b/program/doc/rrddump.pod @@ -0,0 +1,62 @@ +=head1 NAME + +rrddump - dump the contents of an RRD to XML format + +=head1 SYNOPSIS + +B B I E I + +or + +B B I I + +=head1 DESCRIPTION + +The B function writes the contents of an B in human +readable (?) XML format to a file or to stdout. This format can +be read by rrdrestore. Together they allow you to transfer your +files from one computer architecture to another as well to +manipulate the contents of an B file in a somewhat more +convenient manner. + + + +=over 8 + +=item I + +The name of the B you want to dump. + +=item I + +The (optional) filename that you want to write the XML output to. +If not specified, the XML will be printed to stdout. + +=back + +=head1 EXAMPLES + +To transfer an RRD between architectures, follow these steps: + +=over 4 + +=item 1. + +On the same system where the RRD was created, use B B +to export the data to XML format. + +=item 2. + +Transfer the XML dump to the target system. + +=item 3. + +Run B B to create a new RRD from the XML dump. See +B for details. + +=back + +=head1 AUTHOR + +Tobias Oetiker Etobi@oetiker.chE + diff --git a/program/doc/rrdfetch.pod b/program/doc/rrdfetch.pod new file mode 100644 index 00000000..51b5ccd9 --- /dev/null +++ b/program/doc/rrdfetch.pod @@ -0,0 +1,262 @@ +=head1 NAME + +rrdfetch - Fetch data from an RRD. + +=head1 SYNOPSIS + +B B I I +S<[B<--resolution>|B<-r> I]> +S<[B<--start>|B<-s> I]> +S<[B<--end>|B<-e> I]> + +=head1 DESCRIPTION + +The B function is normally used internally by the graph +function to get data from Bs. B will analyze the B +and try to retrieve the data in the resolution requested. +The data fetched is printed to stdout. I<*UNKNOWN*> data is often +represented by the string "NaN" depending on your OS's printf +function. + +=over 8 + +=item I + +the name of the B you want to fetch the data from. + +=item I + +the consolidation function that is applied to the data you +want to fetch (AVERAGE,MIN,MAX,LAST) + +=item B<--resolution>|B<-r> I (default is the highest resolution) + +the interval you want the values to have (seconds per +value). B will try to match your request, but it will return +data even if no absolute match is possible. B See note below. + +=item B<--start>|B<-s> I (default end-1day) + +start of the time series. A time in seconds since epoch (1970-01-01) +is required. Negative numbers are relative to the current time. By default, +one day worth of data will be fetched. See also AT-STYLE TIME SPECIFICATION +section for a detailed explanation on ways to specify the start time. + +=item B<--end>|B<-e> I (default now) + +the end of the time series in seconds since epoch. See also AT-STYLE +TIME SPECIFICATION section for a detailed explanation of how to +specify the end time. + +=back + +=head2 RESOLUTION INTERVAL + +In order to get RRDtool to fetch anything other than the finest resolution RRA +B the start and end time must be specified on boundaries that are +multiples of the desired resolution. Consider the following example: + + rrdtool create subdata.rrd -s 10 DS:ds0:GAUGE:300:0:U \ + RRA:AVERAGE:0.5:30:3600 \ + RRA:AVERAGE:0.5:90:1200 \ + RRA:AVERAGE:0.5:360:1200 \ + RRA:MAX:0.5:360:1200 \ + RRA:AVERAGE:0.5:8640:600 \ + RRA:MAX:0.5:8640:600 + +This RRD collects data every 10 seconds and stores its averages over 5 +minutes, 15 minutes, 1 hour, and 1 day, as well as the maxima for 1 hour +and 1 day. + +Consider now that you want to fetch the 15 minute average data for the +last hour. You might try + + rrdtool fetch subdata.rrd AVERAGE -r 900 -s -1h + +However, this will almost always result in a time series that is +B in the 15 minute RRA. Therefore, the highest resolution RRA, +i.e. 5 minute averages, will be chosen which in this case is not +what you want. + +Hence, make sure that + +=over 3 + +=item 1. + +both start and end time are a multiple of 900 + +=item 2. + +both start and end time are within the desired RRA + +=back + +So, if time now is called "t", do + + end time == int(t/900)*900, + start time == end time - 1hour, + resolution == 900. + +Using the bash shell, this could look be: + + TIME=$(date +%s) + RRDRES=900 + rrdtool fetch subdata.rrd AVERAGE -r $RRDRES \ + -e $(($TIME/$RRDRES*$RRDRES)) -s e-1h + +Or in Perl: + + perl -e '$ctime = time; $rrdres = 900; \ + system "rrdtool fetch subdata.rrd AVERAGE \ + -r $rrdres -e @{[int($ctime/$rrdres)*$rrdres]} -s e-1h"' + + +=head2 AT-STYLE TIME SPECIFICATION + +Apart from the traditional I, RRDtool does also +understand at-style time specification. The specification is called +"at-style" after the Unix command at(1) that has moderately complex +ways to specify time to run your job at a certain date and time. The +at-style specification consists of two parts: the B