USING PERL TO ADMINISTER LINUX SORCERER
    I am a confirmed Sorcerer Linux SA ( see <http://sorcerer.wox.org> ).
    For several years now I have been steadily building up my base of
    Sorcerer installations ( eat your heart out Billy G. ), and have found
    it to be fast, lean, reliable, and down right fun to maintain. My users
    are happy, so I am happy.

    I have been seriously using Linux for about four years now, having come
    from the Microsoft world. Before I started using and administrating
    Linux I was one of Microsoft's minions and thought if the software
    didn't cost a lot of money and have the funny 'window' picture on the
    box, it was really just for amateurs and geeks. Not so, as I and many of
    you have discovered.

    Perl, for me, is a different story. I have been a 'serious' perl monger
    for almost ten years, reaching back to my Windows days. I am still more
    productive in perl than bash. When I started writing perl scripts to
    help me administer Sorcerer, I ran into the problem that the environment
    variables on which Sorcerer so heavily depends, are 'sourced' into the
    bash environment from /etc/sorcery/config. It is very hard to access
    them from perl. And variables that are bash arrays, good luck! Try this:

      # . /etc/sorcery/config && \
            perl -e 'print "$_ => $ENV{$_}\n" for sort keys %ENV'

    Look at the result. Yep, nary a Sorcerer variable appears. Now try:

      # . /etc/sorcery/config
      # export GRIMOIRE
      # perl -e 'print "$_ => $ENV{$_}\n" for sort keys %ENV'

    Yep, GRIMORE appears in the output. So I had to write bash scripts to
    source /etc/sorcery/config, export the variables I wanted to use, and
    run my perl script. That got old quickly. And I still didn't have a good
    way to get to bash arrays.

    I have developed a solution, based on the CPAN module Env::Bash. Please
    take a moment to glance at the documentation before continuing.

A SIMPLE EXAMPLE
    I have written a small, more or less useless, perl script using
    Env::Bash with Sorcerer. The source is scripts/show-spell.pl in the
    module's distribution. The script can list the spells in the grimoire
    and/or display spell details:

      perl scripts/show-spell [-d] [-l] [<spell> ...]

    where:

      -d      shows internal debugging information
      -l      lists spells in the grimoire
      <spell> display details of one or more spells.

    If the script is started without any arguments, spell linux is
    displayed.

    Here is a blow-by-blow description of show-spell.pl

  Starting
      #!/usr/bin/perl
  
      use warnings;
      use strict;
      use Env::Bash;
      use Data::Dumper;
      use Getopt::Std;
  
      my %opt;
      unless (getopts('dl', \%opt)) {
          usage();
      }

    Pretty standard; options are handled too.

  tie a HASH to the Environment
      # tie a hash to /etc/sorcery/config, no ForceArray
      my %env = ();
      tie %env, "Env::Bash", Source => "/etc/sorcery/config",
          Debug => $opt{d};

    This is the easiest way to interface to Env::Bash ( there is a simple,
    standard interface and an oo interface which are fully discussed in the
    module's documentation ). The tie statement says to interface to the
    environment through hash %env, with the option Source ( the script or
    list of scripts to source ), and conditionally set internal debugging.
    More on ForceArray below.

  Find the GRIMOIRE directory
      # find the GRIMOIRE directory
      my $grimoire = $env{GRIMOIRE} || die "cannot find GRIMOIRE\n";

    This is the first real use of the module; the grimoire directory (
    defined in /etc/sorcery/config ), is returned.

  Perl code to list the GRIMOIRE and display spells
      # display spells in the grimoire if option -l
      if( $opt{l} ) {
          print "---spells in grimoire-------------------------\n";
          my @spells = ();
          for my $spell( <$grimoire/*> ) {
              $spell =~ s,.*/,,;
              push @spells, $spell;
          }
          print "$_\n" for sort @spells;
      }
  
      # show spells on command line, or linux if none given
      show_spell( $_ ) for @ARGV;
      show_spell( 'linux' ) unless @ARGV || $opt{l};

    Just perl code.

  show-spell subroutine
      sub show_spell
      {
          my $spell = shift;
    
          # find the spell and DETAILS
          unless( -e "$grimoire/$spell" ) {
              warn "Spell '$spell' not found.\n";
              return;
          }
          my $details = -d _ ? "$grimoire/$spell/DETAILS" : "$grimoire/$spell";
          unless( -e "$details" ) {
              warn "Spell '$spell' DETAILS not found.\n";
              return;
          }
  
          # tie a hash to /etc/sorcery/config and DETAILS w/ForceArray
          my %env = ();
          tie %env, "Env::Bash",
          [],
          Source => [ "/etc/sorcery/config", $details ],
          Debug => $opt{d};
  
          print "---$spell-------------------------------------\n";
          show_detail( VERSION   => \%env );
          show_detail( CATEGORY  => \%env );
          show_detail( ATTRIBUTE => \%env );
          show_detail( SOURCE    => \%env );
          show_detail( URL       => \%env );
          show_detail( HOMEPAGE  => \%env );
          show_detail( REQ       => \%env );
          show_detail( PROTECT   => \%env );
          show_detail( ESTIMATE  => \%env );
          show_detail( DESC      => \%env );
      }

    Here we do another tie for two reasons: 1) we want to source the spell's
    DETAILS script as well as /etc/sorcery/config, and 2) we want to be able
    to access any variables that are bash arrays - like VERSION. The Source
    option to the tie is a list reference - that's how Env::Bash knows how
    to use more than one source script. To access the environment, the
    module constructs a mini bash script, which in this case would be
    something like:

      #!/bin/sh
      . /etc/sorcery/config;. /var/state/sorcery/grimoire/<spell>/DETAILS;set

    The script is run by forking via backtics and the set output is parsed
    to get a list of environment names. Turn on debugging ( -d ) if you
    really want to see the the script.

    To access bash arrays, the ForceArray option ( shortcut: [] ) is
    specified. The module again creates and runs a bash script that returns
    all elements of any bash arrays; the results are stored in the tied hash
    as an array reference, whether or not the variable is a bash array.
    Again, for the more curious, look at what's happening by turning on
    debugging.

  Show spell details
      sub show_detail
      {
          my( $name, $env ) = @_;
  
          # get the requested detail ( return is an array because the
          # the $env hash was tied with ForceArray ( [] ).
          my $values = $env->{$name};
  
          # print each detail
          my $eq = '=';
          for my $value( @$values ) {
              $value = join( "\n".' ' x 14, split /\n/, $value )
                  if $value =~ /\n/s;
              printf "%12s%1s\"%s\"\n", $name, $eq, $value;
              $name = $eq = '';
          }
      }

    Now we get the variable from the tied hash. As mentioned above, each
    hash element is an array reference ( because ForceArray was specified ).
    The resulting values are manipulated and printed.

  usage subroutine
      sub usage
      {
          my $progname = $0;
          $progname =~ s,.*/,,;    # only basename left in progname
          die "Usage: $progname [-d] [-l] <spell> [<spell> ...]\n";
      }

  Summary
    OK, so you now have a script the demonstrates the use of Env::Bash. I
    admit it's slower and much harder than a few bash ls and cat commands,
    but that's not the point. Env::Bash has proved to open up Sorcerer to
    perl; I have written some perl scripts that are, in fact, useful; I will
    try to get them posted my my home page ( currently not available as I
    write this - December 2004 ) and let you know on the Sorcerer list. I
    hope you can find some use for Env::Bash.

AUTHOR
    Beau E. Cox, <beaucox@hawaii.rr.com>.

COPYRIGHT AND LICENSE
    Copyright (C) 2004 by Beau E. Cox.

    This library is free software; you can redistribute it and/or modify it
    under the same terms as Perl itself, either Perl version 5.8.6 or, at
    your option, any later version of Perl 5 you may have available.

