CGI, Perl, GD and GD::Graph
I want to plot graphs of measurements made by a k8055
I/O board. The Perl language is primarily intended for text processing and has
no built-in graphics functionality. Graphics can be added by installing
additional modules.
- GD.pm is an interface library for perl, it allows a perl script to use
functions from GD.
- GD::Text.pm adds text drawing functions
- GD::Graph.pm draws charts and graphs from data supplied in arrays.
- GD is a library of graphics functions. Graphics are drawn into a buffer,
which is usually then stored as a file. The file may be in "GIF"
format or if LibPNG is available the file may be in "PNG" format.
- LibPNG is a library to enable the loading and saving of graphics in the
PNG (Portable Network Graphic) format. LibPNG Reqires zlib.
- Zlib is a data compression library.
- Freetype is an open source implementation of Truetype fonts. GD will use
it if it is installed.
I was disappointed to find that GD and GD.pm are not included in the current
openslug packages. I was further disappointed to find that GD.pm would
not install. The install failed due to some files being reported missing.
Before you can install the Perl GD module you will need to carry out the
steps in Perl Module
Also note that to perform this installation I had the entire "slugos-native"
package installed.
I downloaded and installed the following packages:
- zlib-1.2.3
- libpng-1.2.15
- freetype-2.2.1
- gd-2.0.33
Installing a source package typically consisted of six steps:
- Download the package in "tar.gz" form using wget
- unpack the package using tar -xzf <filename>
- Find and run the "Configure" script
- run make
- run make test
- run make install
Now you've replaced the missing files
and fixed the toolchain you can go ahead and install the perl modules:
- GD
- GD::Text
- GD::Graph
The GD::Graph examples create files in the samples directory.If you want to see a graph in your browser there is a crude example of a CGI
program returning a graph in http://tandem.bu.edu/classes/2005s/papers/perlgdgraph.pdf
and I've patched it a bit to run on my slug: gd_example.pl.
It uses a slightly dubious way to output the graph: it writes the graph to a
file then returns a reference to the file. The file always has the same name.
This technique will cause serious problems in a real web application* but it's just
about tolerable for a demo or home LAN based tool.
It's set up to create an image in /var/www/images so the images directory
must be writable. I used chmod 777 /var/www/images Also it assumes the server's
domain is 192.168.3.253. Edit this to match your system. I've added the
CGI::Carp module so you can see errors easily.
* For clarification: the problem is that each time you visit the site a new
graphics file is generated replacing the old one, and if you have simultaneous
users then they will often see the "wrong" file. If file locking isn't
implemented then they may even see corrupted downloads. Normally the simplest
way round it is to build and return an image URL containing the parameters of
the graph then have the image generated by a script in response to the
parameters. In the case of a data graph this could easily create an excessively
large URL though, and directly passing the data between scripts could require
session management.
I suppose an intermediate fix might be to embed a timestamp in the image
filename, and also clean up old images.
It is possible to compile Perl from source on the slug, however it is
demanding, and can only be done by adding a swapfile. I carried out the
following steps:
- Booted my PC with a "GParted" CD.
- Made up a small external hard drive using an old 6Gb drive and a Newlink
case.
- I used GParted to copy the SDA1 and SDA2 of my USB stick openslug onto the
hard drive, at the same time resizing them and leaving 512Mb free at the
end. I converted the free part into a swap partition.
- Booted the slug with no drive attached, added the hard drive and ran
turnup disk /dev/sda1 to switch over to the disk and rebooted. Note that
this step was unnessecery. All my test systems have the boot on the first
partition so all I have to do is shut down, change drive, restart.
- I used mkswap to convert /dev/sda3 into a swap partition for the slug
- I added a line to /etc/fstab to say that sda3 was swap
- I ran swapon -a to activate the swap partition.
I now had enough memory to build perl from source. After the build I did make
test and it failed one test, I still do not know if this is important. At the
end I was able to install GD.pm and run a simple test script.
I was left with a somewhat broken openslug in which the installed perl was
out of step with the package manager, but at least GD worked.
Complications with a CGI script using GD
The process of returning dynamic graphics is complicated as the graphics will
almost certainly be a part of a dynamically generated page. Unfortunately the
page and graphic will be transferred as separate operations. This means one of
several workarounds will be required to get the graphic to show the correct
content.
- Make the graphic generator self contained. Example: a web counter.
This really only works for extremely simple applications where the reader
does not interact with the script at all.
- Pass all the data needed to generate the graphic as parameters in the URL
This method should work well when the amount of data passed is relatively
small, for example a 12 column bar chart. Unfortunately there's a limit to
how much data you can pass this way.
- Pass parameters so the script that generates the graphic can look up the
required data.
This should work better than the previous method for larger data sets.
- Derive the data from the referrer information which should contain the URL
of the page, and therefore any parameters that were in the page's URL
This is unreliable, it would only work for "GET" type forms, not
"POST" and some browsers would break it.
- Pass a database query in the URL so the graphics script can perform the
query and make it into graphics.
Warning this risks opening a big security hole. Don't go there. If you do
you'll have to validate the query carefully to make sure its safe or you
risk a hacker writing a special url containing a database command.
- Set a per-session cookie on the browser or use a session URL, use the
session to pass data between scripts
This is the preferred method, though it's probably more work than passing
data in the URL.
- Have the script that generates the HTML also generate the graphics as
image files with fixed names
This is a quick, dirty and somewhat unsound way to generate CGI graphics
that will probably work well on a very limited number of applications. If
only one user browses the page things will probably work, although the
graphics may get cached by the browser preventing updates. As soon as two
users browse the site you risk a race condition with one user seeing the
other's graphics.
This method does have it's uses. One perfectly valid application would be
changing the "skin" of a site, where a script would recreate all
the buttons and banners for a site. In this case the script might not even
be cgi, it could be in a cron job set to change the site's appearance at
certain dates, and the web server would not have to support cgi. The script
might not be running on the web server, you could have a slug generate a
graph and write it to your webspace on your ISP's server via FTP.
- Have the script that generates the HTML also generate the graphics as
image files with changing filenames
This is probably a very bad idea. While it fixes the multiple user issue it
opens up the problem of cleaning up unwanted graphics files. If the files
can build up this enables a denial of service attack where the script is
called repeatedly to fill up local storage.
|