December 2005

Using sed for UNIX Portability Part I

There is a program called service which can be used on multiple UNIX systems to control Operating System services. One of the problems faced in the design of the service utility was the path to where system startup scripts (or rc scripts) resided. This series examines how using sed can easily mitigate multi-platform scripting problems. The first part will look at how the utility program works.

The service Utility

The service utility is a generic front end to UNIX init scripts. Writing a service clone for a particular distribution, slackware for instance, is pretty simple:

/etc/rc.d/rc.$1 $2

In order to make a multi-UNIX version of service a bit more is required, also, the one line shell script does not have the capability of listing services.

A More Robust Version of Service

A Perl version was written instead, initially for the Slackware Linux distribution. The first parts of the program to examine are the global variables and the input parser:


$SIG{'INT' } = 'interrupt';  $SIG{'QUIT'} = 'interrupt';
$SIG{'HUP' } = 'interrupt';  $SIG{'TRAP'} = 'interrupt';
$SIG{'ABRT'} = 'interrupt';  $SIG{'STOP'} = 'interrupt';

$RC_CTL="/etc/rc.d/rc.";

$PROG    = "service";                                   
$AUTHOR  = "Fubu "; 
$VERSION = "0.5";                                       

$vflag = 0; # Verbosity flag

for ($i = 0; $i < @ARGV; $i++) {
        if ($ARGV[$i] =~ /^-version|-V$/) {
                printinfo();
                exit 0;
        } elsif ($ARGV[$i] =~ /^-verbose|-v$/) {
                $vflag = 1;
        } elsif ($ARGV[$i] eq '-usage') {
                usage();
                exit 0;
        } elsif ($ARGV[$i] eq '-show') {
                listall($RC_CTL);
                exit 0;
        } elsif ($ARGV[$i] =~ /^restart|start|stop$/) {
                $OPERATION = $ARGV[$i];
        } else {
                $SERVICE = $ARGV[$i];
        }
}


Most of the code above is pretty self explanatory. The parser looks for one of 5 possible choices:

  • -version: Print the version and author information then exit.
  • -verbose: While performing the operation, print out as much information as possible. Set the verbose flag (vflag) to 1.
  • -usage: A usage printout helper message then exit.
  • -show: List the rc files in the main rc directory then exit.
  • service_name command: perform command on the given service name.

Note the $RC_CTLvariable which points to where the rc scripts reside. The logic uses fall through to fetch the actual service name. The code also is looking for specific commands which are detailed later.

Helper Functions

There are a few helper functions associated with the main part of the program.


sub interrupt {
        print "caught @_ exiting\n";
        die;
}

sub listall {
        my $rcfiles = shift;

        system("ls $rcfiles*");
}

sub printinfo {
        print "$PROG $VERSION by $AUTHOR\n";
}

sub usage {
        print "Usage:   $PROG [option]\n";
        print "Usage:   $PROG   [option]\n";
        print "Options:\n";
        print "  -show         List the services and exit\n";
        print "  -usage        Print usage message and exit\n";
        print "  -verbose -v   Run in verbose mode\n";
        print "  -version -V   Print version and author information\n";
        print "Examples:\n";
        print "  Stop then start dhcpd\n";
        print "   $PROG dhcpd stop\n";
        print "   $PROG dhcpd start -v\n";
        print "  Restart NFSD\n";
        print "   $PROG nfsd restart\n";
}

The above functions do the following:

  • interrupt(): A routine to catch interrupts.
  • listall: A call to list the files in the rc directory. The function was made so adding better formatting later would be easier.
  • printinfo: Print out the version and author information.
  • usage: A simple help print.

With the helpers out of the way, the rest of the main can be examined along with the key sub routine of the portable service utility.

The Rest of the Program

The remaining part of the main program checks to see if there is a specified operation, calls the passthru() subroutine and exits.


if ($SERVICE) {
        if ($OPERATION !~ /^restart|start|stop$/) {
                print "Error: Illegal or no operation specified\n";
                usage();
                exit 1;
        }
}

passthru ($SERVICE, $OPERATION, $RC_CTL); # Send it through

exit 0;

The key subroutine is passthru() which passes through the operation to the specified rc script. The passthru() subroutine also checks to see if $vflag is set.


sub passthru {
        my ($service, $operation, $rcstring) = @_;

        if ($vflag) {
                print "going to $operation $service\n";
                print "running $rcstring$service $operation\n";
        }
        if (system("$rcstring$service $operation") == 0) {
                if ($do_version) {
                        print "$operation of $service successful\n";
                }
        } else {
                print "$operation of $service returned a non zero status\n";
        }
}

In addition to just performing (or attempting to perform) the operation, the service utility also checks the return value for success.

With the operation of the program now explained, the next text will look at some of the ways to make it portable among different UNIXs and the method that was employed.

 

Digg!
Submit site
news to Digg!

Slashdot Slashdot It!
Delicious Bookmark on Delicious