| Chess | Tools | Data | Blog | Poetry | Why? | Wiki | Admin | Logout |

Download


#!/usr/bin/perl -w
# $Revision: 1.17 $
# $Date: 2007-03-10 22:30:11 $
# Luis Mondesi < lemsx1@gmail.com >
#
# DESCRIPTION: Advanced drop-in replacement for "service" command line utility found on Redhat systems. Use to start/stop or removes services in Linux. Works on all Linux distributions.
# USAGE: service <start|stop|restart|remove|reload|status|on|off> <service1 [service2 ... serviceN]>
# LICENSE: GPL
# BUGS:
# - A service cannot be called: start, stop, restart, remove, reload, off, on, status
# - script will do first action found in list (start stop reload...) all other actions are removed

=pod

=head1 NAME

service - service script for starting, stoping adding and removing services in Linux

=head1 SYNOPSIS

B<service>  [-v,--version]
            [-D,--debug] 
            [-h,--help]
            <[--list-all] |
            [--list-running] |
            [--list-runlevel] |
            [--list-on] |
            [--list-off]> | <start|stop|restart|reset|remove|off|on|reload>
                <service1 service2 ... serviceN>

=head1 EXAMPLES

    # new style like:

    service stop apache2 squid gnump3d shorewall

    # or classic Redhat style:

    service apache2 stop

    service off fingerd telnetd wu-ftpd

    service --list-all

    service --list-running 

    service --list-runlevel # is the same as service --list-on --list-off

=head1 DESCRIPTION 

    This script is used for sending commands to a given service or to remove/add a service to the SysV style startup script directories. This script should work well for all Linux distributions. Tested on: Redhat/Fedora, SuSE, Ubuntu and Debian.
    This is a drop-in replacement for the "service" command line utility on Redhat-based systems.
    
    Adding more distributions is as simple as:
    
    * take the distribution id from the command: lsb_release -i
    
    * add the distribution to the %distros HASH

=cut

use strict;
use File::Basename qw/ basename /;
$|++;

$ENV{'PATH'} .= ":/sbin:/usr/sbin";

my $revision = '1.17';    # version

# service is the template for the command to execute
# /etc/init.d/service_name action
my %redhat_commands = (
                       'on'      => 'chkconfig %s on',
                       'off'     => 'chkconfig %s off',
                       'service' => '/etc/init.d/%s %s'
                      );
my %debian_commands = (
                       'on'      => 'update-rc.d %s defaults',
                       'off'     => 'update-rc.d -f %s remove',
                       'service' => '/etc/init.d/%s %s'
                      );

# now assign commands accordingly
# NOTE: key 1 is output of lsb_release -i
my %distros = (
               'redhat'     => \%redhat_commands,
               'suselinux'  => \%redhat_commands,
               'fedora'     => \%redhat_commands,
               'fedoracore'     => \%redhat_commands,
               'debian'     => \%debian_commands,
               'ubuntu'     => \%debian_commands,
              );

#---------------------------------------------------------------------------#
# standard Perl modules
use Getopt::Long;
Getopt::Long::Configure('bundling');
use File::Spec::Functions;    # abs2rel() and other dir/filename specific

=pod

=head1 OPTIONS

=over 8

=item -v,--version

prints version and exits

=item -D,--debug

enables debug mode

=item -h,--help

prints this help and exits

=item start,stop,restart,status,reload

sends these arguments to the given services

=item remove,off

removes the initrc scripts from the system using chkconfig or update-rc.d depending on the distribution

=item add,on

adds the initrc scripts to the system using chkconfig or update-rc.d depending on the distribution

=item reset

same as remove and add. This ensures that the proper links are created for a given run level

=item --list-all

List all services in /etc/init.d/*

=item --list-on

List all services set to start in our current run level

=item --list-off

List all services set to stop in our current run level

=item --list-running

List all processes running which have startup scripts in /etc/init.d/*

=item --list-runlevel

Same as --list-off --list-on

=cut

# Args:
my $PVERSION = 0;
my $HELP     = 0;
my $DEBUG    = 0;
my $ACTION   = undef;
my $LIST_ALL = undef;
my $LIST_RUN = undef;
my $LIST_LEV = undef;
my $LIST_ON  = undef;
my $LIST_OFF = undef;

# get options
GetOptions(

    # flags
    'v|version'     => \$PVERSION,
    'h|help'        => \$HELP,
    'D|debug'       => \$DEBUG,
    'list-all'      => \$LIST_ALL,
    'list-running'  => \$LIST_RUN,
    'list-runlevel' => sub { $LIST_LEV++; $LIST_ON++; $LIST_OFF++; },
    'list-on'       => sub { $LIST_LEV++; $LIST_ON++; },
    'list-off'      => sub { $LIST_LEV++; $LIST_OFF++; },
);# and $ACTION = shift;

if ($HELP)
{
    use Pod::Text;
    my $parser = Pod::Text->new(sentence => 0, width => 78);
    $parser->parse_from_file($0, \*STDOUT);
    exit 0;
}

if ($PVERSION) { print STDOUT ($revision, "
"); exit 0; }

# what distro are we?
open(LSB,"</etc/lsb-release") or open(LSB,"lsb_release -i|") 
    or die("Cannot get distributor id ",$!,"
");

my $release_info = undef;

while(<LSB>)
{
    chomp;
    if (/\s*distrib(utor)*[[:blank:]\_]*ID\s*[\:\=]{1}\s*([^[:blank:]]+)/i)
    {
        $release_info = lc($2);
        $release_info =~ s/\s+//;
        last;
    }
}
print "Distributor ID: ",$release_info, "
" if ($DEBUG);

if ($DEBUG)
{
    use Data::Dumper;
    print Dumper(%distros);
}

# main()
if ($LIST_ALL or $LIST_RUN or $LIST_LEV or $LIST_ON or $LIST_OFF)
{
    if ($LIST_ALL)
    {
        print "All services
";

        # TODO use distro specific

        my $DIR = "/etc/init.d";    # LSB
                                    # TODO use distro specific
        opendir(DIR, $DIR) or die("Cannot open $DIR. $!
");
        while (my $file = readdir(DIR))
        {
            next if (-d "$DIR/$file");
            next if (!-x "$DIR/$file");
            next if ($file =~ /\.dpkg-old$/);
            next if ($file =~ /\.rpmsave$/);

            print "$DIR/", $file, "
";
        }
    }

    if ($LIST_RUN)
    {
        print "All services running
";

        # TODO use distro specific

        my $DIR = "/etc/init.d";    # LSB
                                    # TODO use distro specific

        # TODO attempt to find some names by reading the initrc files... 
        # will take a lot of time to finish.
        open(PROC,"ps ax --format \%c | sort | uniq|") or die($!,"
");
        while (my $file = <PROC>)
        {
            chomp($file);
            next if (!-x "$DIR/$file");

            print "$DIR/$file
";
        }
    }

    if ($LIST_LEV)
    {
        my $RUNLEVEL = undef;

        # parse inittab
        open(INITTAB, "</etc/inittab") or die($! . "
");
        while (<INITTAB>)
        {
            if (/^\s*id\s*:\s*(\d+)\s*:\s*initdefault\s*:\s*$/)
            {
                $RUNLEVEL = $1;
                last;
            }
        }

        print "All services for this runlevel [$RUNLEVEL]
";
        my $DIR = "/etc/rc$RUNLEVEL.d";    # LSB
                                           # TODO use distro specific
        opendir(DIR, $DIR) or die("Cannot open $DIR. $!
");

        print '-'x64,"
";
        print sprintf("\%s\t%32s\t%s
","Pri","Service Name","Status");
        print '-'x64,"
";

        while (my $file = readdir(DIR))
        {
            next if (-d "$DIR/$file");
            next if (!-x "$DIR/$file");
            next if ($file =~ /\.dpkg-old$/);
            next if ($file =~ /\.rpmsave$/);

            if ($LIST_ON and $file =~ /^S(\d+)([^[:blank:]]+)/)
            {
                print sprintf("\%s\t%32s\t%s
",$1,$2,"On");
            }
            elsif ($LIST_OFF and $file =~ /^K(\d+)([^[:blank:]]+)/)
            {
                print sprintf("\%s\t%32s\t%s
",$1,$2,"Off");
            }
        }
    }
    exit(0);
}

my $_args = join(",",@ARGV);

if ( $_args =~ /(\bstart\b|\bstop\b|\breload\b|\bstatus\b|\boff\b|\bon\b|\breset\b|\bremove\b|\badd\b)/)
{
    $ACTION = $1;
}

# sanity checks before action is taken
if (not defined($ACTION)
        or $ACTION !~ /^(start|stop|reload|status|off|on|reset|remove|add)$/)
{
    use Pod::Usage;
    pod2usage(1);
    exit 0;
}

# remove all other actions from $_args
$_args =~ s/,?(\bstart\b|\bstop\b|\breload\b|\bstatus\b|\boff\b|\bon\b|\breset\b|\bremove\b|\badd\b)//g;

print "Remaining arguments: ",$_args,"
" if ($DEBUG);

my $KEY = $ACTION;

# deal with synonyms
# we allow as our first argument: remove, off, on, add, and all the service conventions: start, stop, restart, reload, condrestart etc..
if ($ACTION =~ /start|stop|reload|status/)
{
    $KEY = "service";
}
elsif ($ACTION =~ /remove/)
{
    $KEY = "off";
}
elsif ($ACTION =~ /add/)
{
    $KEY = "on";
}

foreach (split(",",$_args))
{
    print "_argument: '$_'
" if ($DEBUG);
    next if (/^[[:blank:]]*$/);
    if ($ACTION =~ /\breset\b/
      )    # reset means to make sure the service has the proper initrc links
    {
        my $error = 0;
        system(sprintf($distros{$release_info}->{'off'}, basename($_), $ACTION));
        $error++ if ($? != 0);
        system(sprintf($distros{$release_info}->{'on'}, basename($_), $ACTION));
        $error++ if ($? != 0);
        if (!$error)
        {
            print "$_ service was reset
";
        }
        else
        {
            print "ERROR: $_ service was not reset
";
        }
        next;
    }
    elsif ($ACTION =~ /\bremove\b/ or $ACTION =~ /\boff\b/)
    {

        # when removing a service we turn it off as well
        print "Turning off service $_
" if ($DEBUG);
        system(sprintf($distros{$release_info}->{$KEY}, basename($_), 'stop'));
    }
     
# TODO sanity check: see if command exists
#     my $service_cmd = sprintf($distros{$release_info}->{$KEY}, basename($_), $ACTION);
#     $service_cmd =~ s/^(\S+).*$/$1/;
#     if ( ! -x $service_cmd )
#     {
#         print STDERR "No such command $service_cmd
";
#         next;
#     } 

    my $system_cmd = sprintf($distros{$release_info}->{$KEY}, basename($_), $ACTION);
    print "Executing cmd: $system_cmd
" if ($DEBUG);
    system($system_cmd);
    if ($? != 0)
    {
        print "ERROR: $_ service was not $ACTION" . "ed
";
    }
}

=pod

=back

=head1 BUGS

* Service names cannot be any of: start stop reload status off on reset remove add

* First action found in list (start stop reload...) will be executed and all other actions are removed

=head1 AUTHORS

Luis Mondesi <lemsx1@gmail.com>

=cut


Advertisement