#! perl -w

# ############################################################################ #
#
# FILE          : ServiceAdmin.pl
#
# PURPOSE       : To Continue, Install, Pause, Remove, Start, Stop or Query the
#                 properties of one or more installed services.
#
# LAST MODIFIED : 22nd August 2000
#
# AUTHOR        : Basil Dias.
#
# Maintained By : K.P.Harihara Subramanian.
#
# ############################################################################ #

use strict;
BEGIN
{
	if( &IsWmiModuleInstalled() )
	{
		require W2RK::WMI; import W2RK::WMI; # WMI library
	}
	else
	{
		print STDERR "ERROR: Wmi.pm is required to run the script.\n       Copy Wmi.pm from the Resource Kit directory to /Perl/site/lib/W2RK.\n";
		exit( 0 );
	}

	sub IsWmiModuleInstalled
	{
		foreach ( @INC )
		{
			my $dir = $_."/W2RK/Wmi.pm";
			if( -e $dir )
			{
				require W2RK::WMI; import W2RK::WMI; # WMI library
				return 1;
			}
		}
		return 0;
	}
}


# General Error Message
use constant MAX_ITERATION  => 5;
use constant IFLAG          => 48;

# Syntax Error Messages.
use constant USAGE_MAIN     => "The syntax of the command is incorrect.\n".
                               "Type \"ServiceAdmin.pl -?\" for usage information.\n"; # Error no 8
use constant USAGE_CONTINUE => "The syntax of the command is incorrect.\n".
                               "Type \'ServiceAdmin.pl -continue -?\' for usage information.\n"; # Error no 8
use constant USAGE_INSTALL  => "The syntax of the command is incorrect.\n".
                               "Type \'ServiceAdmin.pl -install -?\' for usage information.\n"; # Error no 8
use constant USAGE_PAUSE    => "The syntax of the command is incorrect.\n".
                               "Type \'ServiceAdmin.pl -pause -?\' for usage information.\n"; # Error no 8
use constant USAGE_REMOVE   => "The syntax of the command is incorrect.\n".
                               "Type \'ServiceAdmin.pl -remove -?\' for usage information.\n"; # Error no 8
use constant USAGE_START    => "The syntax of the command is incorrect.\n".
                               "Type \'ServiceAdmin.pl -start -?\' for usage information.\n"; # Error no 8
use constant USAGE_STOP     => "The syntax of the command is incorrect.\n".
                               "Type \'ServiceAdmin.pl -stop -?\' for usage information.\n"; # Error no 8
use constant USAGE_QUERY    => "The syntax of the command is incorrect.\n".
                               "Type \'ServiceAdmin.pl -query -?\' for usage information.\n"; # Error no 8

# Error Messages.
use constant ERROR_NO_SERVICE         => "ERROR: Service '%s' does not exist.\n"; # Error no 2302
use constant ERROR_ALREADY_RUNNING    => "ERROR: '%s' service is already running.\n"; # Error No 21
use constant ERROR_NOT_STARTED        => "       This service is not started.\n";
use constant ERROR_ALREADY_PAUSED     => "ERROR: '%s' service is already paused.\n"; # Error No 21
use constant ERROR_NOINFO             => "ERROR: Unable to retrieve Service Information.\n"; # Error no 19
use constant ERROR_ALREADY_STARTED    => "ERROR: '%s' service is already started.\n"; # Error No 21
use constant ERROR_INVALID_OPTION     => "ERROR: Invalid value '%s' for the '%s' option. \n"; # Error no 7
use constant ERROR_CANNOT_NULL        => "%s cannot be a null string.\n"; # No Exit Level. Part of other Message.
use constant ERROR_NAME_EXISTS        => "ERROR: A service with name '%s' already exists.\n"; # Error No 20
use constant ERROR_DISPNAME_EXISTS    => "ERROR: A service with display name '%s' already exists.\n"; # Error No 20
use constant ERROR_INVALID_USERFORMAT => "ERROR: Invalid user format '%s' specified for '%s' option. \n"; # Error No 7
use constant ERROR_INVALID_USER       => "ERROR: Invalid user '%s' specified with '%s' option.\n"; # Error No 7
use constant ERROR_SERVICE_OWNPROC    => "ERROR: Service type should be 'OWNPROC' for a non-local system account.\n"; # Error no 2305
use constant ERROR_INTERACT_NO        => "ERROR: Desktop interact should be 'NO' for a non-local system account.\n";  # Error no 2306
use constant ERROR_NO_MULTPATH        => "ERROR: Multiple paths cannot be set up for a service.\n"; 
use constant ERROR_INVALID_PATH       => "ERROR: Invalid file path '%s' specified for service.\n"; # Error No 3
use constant ERROR_DEP_ARGS           => "ERROR: Option -dependencies requires an argument\n"; # Similar to ProcessOptions Error. Error No 255

# Could not and Operational Failed Messages.

# Resume Error Message.
use constant ERROR_NOTRESUME_INTERNAL => "ERROR: '%s' service may not have been resumed.\n".
                                         "An internal Windows/Service error occurred.\n"; # Error no 255
use constant ERROR_COULDNOT_RESUME    => "ERROR: Unable to resume the service '%s'.\n"; # Error No 254

# Pause Error Messages.
use constant ERROR_NOTPAUSE_INTERNAL  => "ERROR: '%s' service may not have been paused.\n".
                                         "An internal Windows/Service error occurred.\n"; # Error no 255
use constant ERROR_COULDNOT_PAUSE     => "ERROR: Unable to pause the service '%s'.\n"; # Error No 254
use constant ERROR_CANNOT_PAUSE       => "ERROR: '%s' service cannot not be paused.\n".
                                         "The requested pause is not valid for this service.\n"; # Error No 254
# Remove Error Messages.
use constant ERROR_COULDNOT_REMOVE    => "ERROR: Unable to remove the service '%s'.\n"; # Error No 254;

# Start Error Messages.
use constant ERROR_NOTSTART_INTERNAL  => "ERROR: '%s' service may not have been started.\n". 
                                         "An internal Windows/Service error occurred.\n"; # Error No 255
use constant ERROR_COULDNOT_START     => "ERROR: Unable to start the service '%s'.\n"; # Error No 254

# Stop Error Messages
use constant ERROR_NOTSTOP_INTERNAL   => "ERROR: '%s' service may not have been stopped.\n".
                                         "An internal Windows/Service error occurred.\n"; # Error No 255
use constant ERROR_COULDNOT_STOP      => "ERROR: Unable to stop the service '%s'.\n"; # Error No 254
use constant ERROR_CANNOT_STOP        => "ERROR: '%s' service cannot not be stopped.\n". 
                                         "The requested stop is not valid for this service.\n"; # Error No 254

# Install Error Messages.
use constant ERROR_INSTALL_UNABLE     => "ERROR: Unable to install the new service '%s'.\n"; # Error No 254

# Info Messages
use constant INFO_NORESULT_FILTER      => "INFO: No Information retrieved after performing filter Operation. \n"; # Error No 30
use constant ERROR_NORESULT            => "ERROR: Unable to retreive service information.\n";

# Status Messages.
use constant STATUS_CONTINUE       => "STATUS: '%s' service is continuing.\n";
use constant STATUS_PAUSE          => "STATUS: '%s' service is pausing.\n";
use constant STATUS_START          => "STATUS: '%s' service is starting.\n";
use constant STATUS_STOP           => "STATUS: '%s' service is stopping.\n";

# Success Messages.
use constant MSG_CONTINUED => "SUCCESS: '%s' service is continued successfully.\n";  # Error No 0
use constant MSG_PAUSED    => "SUCCESS: '%s' service is paused successfully.\n"; # Error No 0
use constant MSG_REMOVE    => "SUCCESS: '%s' service is removed successfully.\n"; # Error No 0
use constant MSG_START     => "SUCCESS: '%s' service is started successfully.\n"; # Error No 0
use constant MSG_STOP      => "SUCCESS: '%s' service is stopped successfully.\n"; # Error No 0
use constant MSG_INSTALL   => "SUCCESS: '%s' service is installed successfully.\n"; # Error No 0

my (%Error, @dependencies);
my $errorlevel = 0;
MAIN:
{
	my ($continue, $install, $pause, $remove, $start, $stop, $query);
	my ($name, $display, $type, $errorcontrol, $startmode, $interact);
	my ($path, $runas, $runaspw, $loadorder, $loadorderdepend, $delfile);
	my ($format, $verbose, $help, $wmi);
	my @filter;

	$SIG{__WARN__} = sub { fail @_; };
	$wmi = W2RK::WMI->new;
	$wmi->ProcessOptions('continue'       => \$continue,
	                     'install'        => \$install,
	                     'pause'          => \$pause,
	                     'remove'         => \$remove,
	                     'start'          => \$start,
	                     'stop'           => \$stop,
	                     'query'          => \$query,
	                     'name=s'         => \$name,
	                     'display=s'      => \$display,
	                     'errorcontrol=s' => \$errorcontrol,
	                     'type=s'         => \$type,
	                     'startmode=s'    => \$startmode,
	                     'interact=s'     => \$interact,
	                     'runas=s'        => \$runas,
	                     'runaspw=s'      => \$runaspw,
	                     'loadorder=s'    => \$loadorder,
	                     'loadorderdepend=s' => \$loadorderdepend,
	                     'dependencies=s'    => \@dependencies,
	                     'filter=s'       => \@filter,
	                     'delfile'        => \$delfile,
	                     'format=s'       => \$format,
	                     'verbose'        => \$verbose,
	                     'h|?'            => \$help,
	                    );

	%Error = ( 2  => 'The user did not have the necessary access.',
	          3  => 'The service cannot be stopped because other services that are running are dependent on it.',
	          4  => 'The requested control code is not valid, or it is unacceptable to the service.',
	          5  => 'The requested control code cannot be sent to the service because the state of the service (Win32_BaseService:State) is equal to 0, 1, or 2.',
	          6  => 'The service has not been started.',
	          7  => 'The service did not respond to the start request in a timely fashion.',
	          8  => 'Unknown failure when starting the service.',
	          9  => 'The directory path to the service executable was not found.',
	          10 => 'The service is already running.',
	          11 => 'The database to add a new service is locked.',
	          12 => 'A dependency for which this service relies on has been removed from the system.',
	          13 => 'The service failed to find the service needed from a dependent service.',
	          14 => 'The service has been disabled from the system.',
	          15 => 'The service does not have the correct authentication to run on the system.',
	          16 => 'This service is being removed from the system.',
	          17 => 'There is no execution thread for the service.',
	          18 => 'There are circular dependencies when starting the service.',
	          19 => 'There is a service running under the same name.',
	          20 => 'There are invalid characters in the name of the service.',
	          21 => 'Invalid parameters have been passed to the service.',
	          22 => 'The account which this service is to run under is either invalid or lacks the permissions to run the service.',
	          23 => 'The service exists in the database of services available from the system.',
	          24 => 'The service is currently paused in the system.'
	        );

	if($continue && !$install && !$pause && !$remove && !$start && !$stop && !$query)
	{
		# Check whether the rest of options is defined.
		my $rest = 0;
		$rest = 1
			if( defined $name || defined $display || defined $type || defined $errorcontrol || 
			    defined $startmode || defined $interact || defined $runas || defined $runaspw ||
			    defined $loadorder || defined $loadorderdepend || defined @dependencies ||
			    defined $delfile || defined @filter || defined $format || defined $verbose );

		if( $help )
		{
			fail( USAGE_CONTINUE, 8 ) if( $rest == 1 or @ARGV );
			&ContinueHelp();
		}
		fail ( USAGE_CONTINUE, 8 ) if( $rest or @ARGV == 0 );
		fail ( USAGE_CONTINUE, 8 ) 
			if( ( defined $wmi->{username} and !defined $wmi->{server} ) or
			    ( defined $wmi->{password} and !defined $wmi->{username}) ); 

		# Connect to WMI and trap the Error.
		eval { $wmi->Connect(); };
		fail ( $@, 9 ) if($@);

		# Continue the Specified Services.
		&ContinueService($wmi, \@ARGV);
	}
	elsif(!$continue && $install && !$pause && !$remove && !$start && !$stop && !$query)
	{
		my $rest = 0;
		$rest = 1
			if ( defined $delfile || @filter || defined $format || defined $verbose );

		if( $help )
		{
			fail ( USAGE_INSTALL, 8 )
				if( $rest or @ARGV or defined $name or defined $display or defined $type or
				    defined $errorcontrol or defined $startmode or defined $interact or defined $runas or
				    defined $runaspw or defined $loadorder or defined $loadorderdepend or
				    @dependencies or defined $path);
			&InstallHelp;
		}

		fail ( USAGE_INSTALL, 8 )
			if (!defined $name or $rest or @ARGV == 0);

		# Connect to WMI and trap the Error.
		eval { $wmi->Connect(); };
		fail ( $@, 9 ) if($@);

		&ValidateInstallArgs($wmi, \$name, \$display, \$type, \$errorcontrol,
		                     \$startmode, \$interact, \$runas, \$runaspw, \$loadorder,
		                     \$loadorderdepend, \@dependencies, \@ARGV, \$path);

		&InstallService($wmi, $name, $display, $path, $type, $errorcontrol,
		                $startmode, $interact, $runas, $runaspw, $loadorder,
		                $loadorderdepend, \@dependencies);
	}
	elsif(!$continue && !$install && $pause && !$remove && !$start && !$stop && !$query)
	{
		my $rest = 0;
		$rest = 1
			if( defined $name || defined $display || defined $type || defined $errorcontrol ||
			    defined $startmode || defined $interact || defined $runas || defined $runaspw ||
			    defined $loadorder || defined $loadorderdepend || defined @dependencies || 
			    defined $delfile || defined @filter || defined $format || defined $verbose );

		if( $help )
		{
			fail( USAGE_PAUSE , 8 ) if( $rest == 1 or @ARGV );
			&PauseHelp;
		}
		fail ( USAGE_PAUSE, 8 ) if( $rest or @ARGV == 0 );
		fail ( USAGE_PAUSE, 8 ) 
			if( ( defined $wmi->{username} and !defined $wmi->{server} ) or
			    ( defined $wmi->{password} and !defined $wmi->{username}) ); 

		eval { $wmi->Connect(); };
		fail ( $@, 9 ) if($@);

		&PauseService($wmi, \@ARGV);
	}
	elsif(!$continue && !$install && !$pause && $remove && !$start && !$stop && !$query)
	{
		my $rest = 0;
		$rest = 1 
			if( defined $name || defined $display || defined $type || defined $errorcontrol || 
			    defined $startmode || defined $interact || defined $runas || defined $runaspw ||
			    defined $loadorder || defined $loadorderdepend || defined @dependencies || 
			    defined @filter || defined $format || defined $verbose );

		if( $help )
		{
			fail( USAGE_REMOVE, 8 ) if( $rest == 1 or defined $delfile or @ARGV );
			&RemoveHelp;
		}
		fail( USAGE_REMOVE, 8 ) if( $rest == 1 or @ARGV == 0 );
		fail ( USAGE_REMOVE, 8 ) 
			if( ( defined $wmi->{username} and !defined $wmi->{server} ) or
			    ( defined $wmi->{password} and !defined $wmi->{username}) ); 

		eval { $wmi->Connect(); };
		fail ( $@, 9 ) if($@);

		&RemoveService($wmi, $delfile, \@ARGV);
	}
	elsif(!$continue && !$install && !$pause && !$remove && $start && !$stop && !$query)
	{
		my $rest = 0;
		$rest = 1
			if (defined $name || defined $display || defined $type || defined $errorcontrol || 
			    defined $startmode || defined $interact || defined $runas || defined $runaspw ||
				defined $loadorder || defined $loadorderdepend || defined @dependencies ||
				defined $delfile || defined @filter || defined $format || defined $verbose );

		if( $help )
		{
			fail( USAGE_START, 8 ) if( $rest or @ARGV );
			&StartHelp;
		}

		fail( USAGE_START , 8) if( $rest or @ARGV == 0 );
		fail ( USAGE_START, 8 ) 
			if( ( defined $wmi->{username} and !defined $wmi->{server} ) or
			    ( defined $wmi->{password} and !defined $wmi->{username}) ); 

		eval { $wmi->Connect(); };
		fail ( $@, 9 ) if($@);

		&StartService($wmi, \@ARGV);
	}
	elsif(!$continue && !$install && !$pause && !$remove && !$start && $stop && !$query)
	{
		my $rest = 0;
		$rest = 1
			if (defined $name || defined $display || defined $type || defined $errorcontrol || 
			    defined $startmode || defined $interact || defined $runas || defined $runaspw ||
				defined $loadorder || defined $loadorderdepend || defined @dependencies ||
				defined $delfile || defined @filter || defined $format || defined $verbose );

		if( $help )
		{
			fail( USAGE_STOP, 8 ) if( $rest or @ARGV );
			&StopHelp;
		}

		fail( USAGE_STOP, 8 ) if( $rest or @ARGV == 0 );
		fail ( USAGE_STOP, 8 ) 
			if( ( defined $wmi->{username} and !defined $wmi->{server} ) or
			    ( defined $wmi->{password} and !defined $wmi->{username}) ); 

		eval { $wmi->Connect(); };
		fail ( $@, 9 ) if($@);

		&StopService($wmi, \@ARGV);
	}
	elsif(!$continue && !$install && !$pause && !$remove && !$start && !$stop && $query)
	{
		my $rest = 0;
		$rest = 1
			if (defined $name || defined $display || defined $type || defined $errorcontrol || 
			    defined $startmode || defined $runas || defined $runaspw || defined $interact || 
			    defined $loadorder || defined $loadorderdepend || defined @dependencies ||
			    defined $delfile);

		if( $help )
		{
			fail ( USAGE_QUERY, 8 ) if( $rest or @ARGV or @filter or defined $format or defined $verbose );
			&QueryHelp;
		}

		if(defined $format)
		{
			fail( sprintf( ERROR_INVALID_OPTION, $format, 'format'), 7 )
				if($format !~ /^TABLE$|^LIST$|^CSV$/i);
			$format = lc($format);
			$format = 'normal' if($format eq 'table');
		}
		else
		{
			$format = 'normal';
		}

		my %FilterCfg = (
		                "Name"            => { Operators  => ['!','='] },
		                "DisplayName"     => { Operators  => ['!','='] },
		                "State"           => { Operators  => ['!','='],
		                                      ValueType  => ['RUNNING', 'STOPPED', 'PAUSED'] },
		                "ErrorControl"    => { Operators  => ['!','='],
		                                      ValueType  => ['IGNORE', 'NORMAL', 'SEVERE', 'CRITICAL'] },
		                "AcceptPause"     => { Operators  => ['!','='],
		                                      ValueType  => ['YES', 'NO'] },
		                "AcceptStop"      => { Operators  => ['!','='],
		                                      ValueType  => ['YES', 'NO'] },
		                "StartMode"       => { Operators  => ['!','='],
		                                      ValueType  => ['AUTO', 'MANUAL', 'DISABLED', 'BOOT'] },
		                "Interact"        => { Operators  => ['!','='],
		                                      ValueType  => ['ON', 'OFF'] },
		                "RunAs"           => { Operators  => ['!','='] },
		                "LoadOrderGroup"  => { Operators  => ['!','='] },
		                "LoadOrderNum"    => { Operators  => ['!','=', '<', '>'],
		                                      ValueType  => 'Number' },
		                "Dependencies"    => { Operators  => ['!','='] },
		                "Path"            => { Operators  => ['!','='] },
		                "Type"            => { Operators  => ['!','='],
		                                       ValueType  => ['OWNPROC', 'SHAREDPROC'] }
		                );

		if( @filter )
		{
			my $errfilter = W2RK::WMI->ValidateFilter( \@filter, \%FilterCfg );
			if( defined $errfilter )
			{
				fail( sprintf( ERROR_INVALID_OPTION, $errfilter, 'filter' ), 7 );
			}
		}

		fail( USAGE_QUERY, 8 ) if( $rest);
		fail( USAGE_QUERY, 8 ) 
			if( ( defined $wmi->{username} and !defined $wmi->{server} ) or
			    ( defined $wmi->{password} and !defined $wmi->{username}) ); 

		eval { $wmi->Connect(); };
		fail ( $@, 9 ) if($@);

		&QueryService($wmi, \@filter, $format, $verbose, \@ARGV, \%FilterCfg);
	}
	elsif(!$continue && !$install && !$pause && !$remove && !$start && !$stop && !$query)
	{
		if( $help )
		{
			if (defined $name || defined $display || defined $type || defined $errorcontrol || 
			    defined $startmode || defined $interact || defined $runas || defined $runaspw || 
			    defined $interact || defined $loadorder || defined $loadorderdepend || 
			    defined @dependencies || @filter || defined $delfile || defined $format || defined $verbose || @ARGV )
			{
				fail( USAGE_MAIN, 8 );
			}
			else
			{
				&usage();
			}
		}
		else
		{
			fail( USAGE_MAIN, 8 );
		}
	}
	else
	{
		fail( USAGE_MAIN, 8 );
	}
	exit($errorlevel);
}

# ######################################################################### #
#
# PURPOSE	: Retrieves multiple arguments to -dependencies option.
#
# INPUTS	: Command line string following -dependencies option.
#
# RETURN VALUE	: None.
#
# ######################################################################### #
sub GetDependencies
{
	unshift(@ARGV,$_[1]);
	fail ( ERROR_DEP_ARGS, 255 ) if($ARGV[0] =~ /^-/i);
	push(@dependencies, shift(@ARGV)) while(@ARGV && ($ARGV[0] !~ /^-/i));
}


# ######################################################################### #
#
# PURPOSE	: Validate arguments passed with the install option.
#
# INPUTS	: Valid arguments for the -install option.
#
# RETURN VALUE	: None.
#
# ######################################################################### #
sub ValidateInstallArgs
{
	my ($wmi, $name, $display, $type, $errorcontrol, $startmode, $interact, $runas,
	    $runaspw, $loadorder, $loadorderdepend, $dependencies, $args, $path) = @_;

	fail( USAGE_INSTALL, 8 ) if(@$args != 1);

	if($$name eq "")
	{
		printf STDERR ERROR_INVALID_OPTION, $$name, 'name';
		fail( sprintf( ERROR_CANNOT_NULL,'Service name') , 7);
	}

	my $obj;
	eval{ $obj = $wmi->services->Get("Win32_Service.Name='$$name'"); };
	fail ( sprintf(ERROR_NAME_EXISTS, $$name), 20 ) if( !$@ and defined $obj );

	if(defined $$display)
	{
		if($$display eq "")
		{
			printf STDERR ERROR_INVALID_OPTION, $$display, 'display';
			fail( sprintf( ERROR_CANNOT_NULL,'Display name') , 7);
		}
		my $coll;
		eval { $coll = $wmi->ExecQuery("SELECT * FROM Win32_Service WHERE DisplayName='$$display'", IFLAG); };
		fail ( sprintf(ERROR_DISPNAME_EXISTS, $$display), 20 ) if( !$@ and defined $coll );
	}
	else
	{
		$$display = $$name;
	}

	if(defined $$type)
	{
		fail( sprintf( ERROR_INVALID_OPTION, $$type , 'type' ), 7 )
			if($$type !~ /^OWNPROC$|^SHAREDPROC$/i);
		$$type = ($$type =~ /^OWNPROC$/i) ? 16 : 32;
	}
	else
	{
		$$type = 16;
	}

	if(defined $$errorcontrol)
	{
		fail( sprintf( ERROR_INVALID_OPTION, $$errorcontrol , 'errorcontrol' ) , 7)
			if($$errorcontrol !~ /^IGNORE$|^NORMAL$|^SEVERE$|^CRITICAL$/i);

		if($$errorcontrol =~ /^IGNORE$/i)
		{
			$$errorcontrol = 0;
		}
		elsif($$errorcontrol =~ /^NORMAL$/i)
		{
			$$errorcontrol = 1;
		}
		elsif($$errorcontrol =~ /^SEVERE$/i)
		{
			$$errorcontrol = 2;
		}
		elsif($$errorcontrol =~ /^CRITICAL$/i)
		{
			$$errorcontrol = 3;
		}
	}
	else
	{
		$$errorcontrol = 1;
	}

	if(defined $$startmode)
	{
		fail( sprintf( ERROR_INVALID_OPTION, $$startmode , 'startmode' ) , 7)
			if($$startmode !~ /^AUTO$|^MANUAL$|^DISABLED$/i);

		$$startmode = "automatic" if($$startmode =~ /^AUTO$/i);
		$$startmode = lc($$startmode);
	}
	else
	{
		$$startmode = "manual";
	}

	if(defined $$interact)
	{
		fail( sprintf( ERROR_INVALID_OPTION, $$interact , 'interact' ), 7 )
			if($$interact !~ /^YES$|^NO$/i);

		$$interact = ($$interact =~ /^YES$/i) ? 1 : 0;
	}
	else
	{
		$$interact = 0;
	}

	if(defined $$runas)
	{
		my ($domain, $user);
		fail( sprintf( ERROR_INVALID_USERFORMAT, $$runas, 'runas' ), 7)
			if($$runas !~ /\w+\\?\w+/i);

		if($$runas =~ /^LOCALSYSTEM$/i)
		{
			$$runas = "LOCALSYSTEM";
			$$runaspw = undef;
		}
		else
		{
			my $temp = $$runas;
			if($$runas =~ /\\/)
			{
				($domain, $user) = split /\\/, $$runas;
			}
			else
			{
				$domain = ".";
				$user = $$runas;
			}
			if($domain eq ".")
			{
				my $CSInstances;
				eval{ $CSInstances = $wmi->InstancesOf("Win32_ComputerSystem", IFLAG ); };
				if( !$@ and defined $CSInstances )
				{
					my @CS = in $CSInstances;
					$domain = $CS[0]->{Name};
					$$runas = ".\\$user";
				}
			}
			my $obj;
			eval{ $obj = $wmi->services->Get("Win32_UserAccount.Domain=\"$domain\",Name=\"$user\""); };
			fail ( sprintf( ERROR_INVALID_USER, $temp, 'runas' ), 7 ) if( $@ or !defined $obj );

			$$runaspw = "" unless(defined $$runaspw);
			fail ( ERROR_SERVICE_OWNPROC, 2305) if($$type == 32);
			fail ( ERROR_INTERACT_NO, 2306 ) if($$interact);
			$$interact = undef;
		}
	}
	else
	{
		$$runas = "LOCALSYSTEM";
	}

	if(defined $$loadorder)
	{
		my $obj;
		eval{ $obj = $wmi->services->Get("Win32_LoadOrderGroup.Name=\"$$loadorder\""); };
		fail( sprintf( ERROR_INVALID_OPTION, $$loadorder , 'loadorder' ), 7 )
			if($@ or !$obj);
	}

	if(defined $$loadorderdepend)
	{
		eval{ my $obj = $wmi->services->Get("Win32_LoadOrderGroup.Name=\"$$loadorderdepend\""); };
		fail( sprintf( ERROR_INVALID_OPTION, $$loadorderdepend , 'loadorderdepend' ), 7 )
			if($@ or !$obj);
	}

	if(defined @$dependencies)
	{
		my $instances = &GetInstances($wmi, "Win32_Service");
		my @services = &CheckService($dependencies, $instances);
		exit($errorlevel) if(@$dependencies);

		@$dependencies = ();
		push (@$dependencies, $$_[0]->{Name})
			foreach ( @services );
	}
	else
	{
		push(@$dependencies, "");
	}

#	fail( ERROR_NO_MULTPATH )
#		if(@$args != 1);

	$$path = $$args[0];
	my $temp = $wmi->ValidateABSPath($$path, 1)
		or fail( sprintf( ERROR_INVALID_PATH, $$path ), 3);

	$$path = $temp;
}


# ######################################################################### #
#
# PURPOSE   : Retrieves the instances of a specified class.
#
# INPUTS    : $wmi   - Reference to WMI object.
#             $class - Class name for whcih to retrieve instances of.
#
# RETURN VALUE: $instances - Instances of the specified class.
#
# ######################################################################### #
sub GetInstances
{
	my ($wmi, $class) = @_;
	my $instances;
	eval { $instances = $wmi->InstancesOf($class, IFLAG); };
	return undef if( $@ or !defined $instances );
	return $instances;
}


# ######################################################################### #
#
# PURPOSE	: Checks for valid service names as entered through
#		  command line.
#
# INPUTS	: $services - Reference to services array.
#		  $instances - Instances of Win32_Services.
#
# RETURN VALUE	: Array @newservices containing matched services instances.
#
# ######################################################################### #
sub CheckService
{
	my ($services, $instances) = @_;
	my @newservices;
	my ($dumy, $flag);

	my $servicelist = "";
	foreach (@$services)
	{
		$dumy = $_;
		$dumy =~ s/\*+/\*/g;	$dumy =~ s/\[/\\\[/g;
		$dumy =~ s/\(/\\\(/g;	$dumy =~ s/\)/\\\)/g;
		$dumy =~ s/\$/\\\$/g;	$dumy =~ s/\|/\\\|/g;
		$dumy =~ s/\+/\\\+/g;	$dumy =~ s/\./\\./g;
		$dumy =~ s/\^/\\^/g;	$dumy =~ s/\?/\\?/g;
		$dumy =~ s/\*/.\*/g;
		$servicelist .= "^$dumy\$|";
	}
	chop($servicelist);
	for my $instance (in $instances )
	{
		if($instance->{Name} =~ /$servicelist/i ||
		   $instance->{DisplayName} =~ /$servicelist/i )
		{
			my @temp = ($instance, $&);
			push(@newservices,\@temp);
			&ValidateEntry($services, $&) if(@$services > 0);
		}
	}
	foreach(@$services)
	{
		printf STDERR ERROR_NO_SERVICE, $_;
		$errorlevel = 2302;
	}
	return @newservices;
}


# ############################################################################ #
#
# PURPOSE	: To check whether an entry was matched in the service list
#
# INPUTS	: $Array - Reference of an array in which to match.
#		  $Value - The entry value to match.
#
# RETURN VALUES : None.
#
# ############################################################################ #
sub ValidateEntry
{
	my ($Array, $Value) = @_;
	my $temp;
	for(my $index=0; $index < @$Array; $index++ )
	{
		$temp = $$Array[$index];
		$temp =~ s/\*+/\*/g;	$temp =~ s/\[/\\\[/g;
		$temp =~ s/\(/\\\(/g;	$temp =~ s/\)/\\\)/g;
		$temp =~ s/\$/\\\$/g;	$temp =~ s/\|/\\\|/g;
		$temp =~ s/\+/\\\+/g;	$temp =~ s/\./\\./g;
		$temp =~ s/\^/\\^/g;	$temp =~ s/\?/\\?/g;
		$temp =~ s/\*/.\*/g;
		if($Value =~ /^$temp$/i)
		{
			splice(@$Array,$index,1);
			$index--;
		}
	}
}


# ############################################################################ #
#
# PURPOSE   : Continues a currently paused service.
#
# INPUTS    : $wmi - WMI object.
#             $args - Reference to Default arguments.
#
# RETURN VALUES : None.
#
# ############################################################################ #
sub ContinueService
{
	my ($wmi, $args) = @_;

	# Get the Instance of Win32_Service.
	my $instances = &GetInstances($wmi, "Win32_Service");
	fail ( ERROR_NOINFO, 19 ) if( !defined $instances );

	# Check Whether the Services are existing or not. The args are formed as a pattern
	# and the checked against it. The New set of service list is returned.
	@$args = &CheckService($args, $instances);
	exit($errorlevel) unless(@$args);

	foreach (@$args)
	{
		# Check for the existing condition of the Service.
		if($$_[0]->{State} =~ /RUNNING/i)
		{
			printf STDERR ERROR_ALREADY_RUNNING, $$_[1];
			$errorlevel = 21;
			next;
		}
		elsif($$_[0]->{State} =~ /^STOPPED$/i)
		{
			printf STDERR ERROR_COULDNOT_RESUME, $$_[1];
			print STDERR ERROR_NOT_STARTED;
			$errorlevel = 254;
			next;
		}

		# Now resume the Service.
		printf STATUS_CONTINUE, $$_[1];
		my $Ret = $$_[0]->ResumeService();

		if(defined $Ret && $Ret == 0)
		{
			my $incr = 100;
			while(1)
			{
				my $check;
				eval{$check = $wmi->services->Get("Win32_Service.Name='$$_[0]->{Name}'"); };
				last if( $@ or !defined $check);
				last if($incr == $check->{CheckPoint});
				$incr = $check->{CheckPoint};
				sleep(1);
			}
			my $check;
			eval{ $check = $wmi->services->Get("Win32_Service.Name='$$_[0]->{Name}'"); };
			if(!$@ && $check && $check->{State} =~ /^RUNNING$/i)
			{
				printf MSG_CONTINUED, $$_[1];
			}
			else
			{
				printf STDERR ERROR_NOTRESUME_INTERNAL, $$_[1];
				$errorlevel = 255;
			}
		}
		else
		{
			printf STDERR ERROR_COULDNOT_RESUME, $$_[1];
			printf STDERR "       ",$Error{ $Ret},"\n"  if($Ret && defined $Error{$Ret});
			$errorlevel = 254;
		}
	}
}


# ############################################################################ #
#
# PURPOSE	: Installs a new service.
#
# INPUTS	: $wmi		- WMI Object.
#		  Valid arguments required for installing new service.
#
# RETURN VALUES : None.
#
# ############################################################################ #
sub InstallService
{
	my ($wmi, $name, $display, $path, $type, $errorcontrol, $startmode, $interact,
		$runas, $runaspw, $loadorder, $loadorderdepend, $dependencies ) = @_;

	my $obj;
	eval{ $obj = $wmi->services->Get("Win32_Service.Name=\"$name\""); };
	fail ( sprintf(ERROR_NAME_EXISTS, $name), 20 ) if( !$@ and defined $obj);

	my @loaddepend = ();
	push(@loaddepend, $loadorderdepend) if(defined $loadorderdepend);

	my $objService;
	eval{$objService = $wmi->services->Get("Win32_Service"); };
	fail ( ERROR_NOINFO, 19) if( $@ or !defined $objService );;

	my $Ret = $objService->Create( $name,
	                               $display,
	                               $path,
	                               $type,
	                               $errorcontrol,
	                               $startmode,
	                               $interact,
	                               $runas,
	                               $runaspw,
	                               $loadorder,
	                               \@loaddepend,
	                               $dependencies );

	if(defined $Ret && $Ret == 0)
	{
		printf MSG_INSTALL, $name; 
	}
	else
	{
		printf STDERR ERROR_INSTALL_UNABLE, $name;
		printf STDERR "       ",$Error{$Ret},"\n" if($Ret && defined $Error{$Ret});
		$errorlevel = 254;
	}
}


# ############################################################################ #
#
# PURPOSE    : Pauses a currently running service.
#
# INPUTS     : $wmi - WMI object.
#              $args - Reference to Default arguments.
#
# RETURN VALUES : None.
#
# ############################################################################ #
sub PauseService
{
	my ($wmi, $args) =@_;

	# Get the Instances of Win32_Service.
	my $instances = &GetInstances($wmi, "Win32_Service");
	fail ( ERROR_NOINFO, 19 ) if( !defined $instances );

	# Check whether the service exists or not.
	@$args = &CheckService($args, $instances);
	exit($errorlevel) unless(@$args);

	foreach (@$args)
	{
		if($$_[0]->{State} =~ /^PAUSED$/i)
		{
			printf STDERR ERROR_ALREADY_PAUSED, $$_[1];
			$errorlevel = 21;
			next;
		}
		elsif($$_[0]->{State} =~ /^STOPPED$/i)
		{
			printf STDERR ERROR_COULDNOT_PAUSE, $$_[1];
			printf STDERR ERROR_NOT_STARTED, $$_[1];
			$errorlevel = 254;
			next;
		}

		if($$_[0]->{AcceptPause})
		{
			printf STATUS_PAUSE, $$_[1];
			my $Ret=$$_[0]->PauseService();
			if(defined $Ret && $Ret == 0)
			{
				my $incr = 100;
				while(1)
				{
					my $check;
					eval{ $check = $wmi->services->Get("Win32_Service.Name='$$_[0]->{Name}'"); };
					last if($@ or !defined $check);
					last if($incr == $check->{CheckPoint});
					$incr = $check->{CheckPoint};
					sleep(1);
				}
				my $check;
				eval{ $check = $wmi->services->Get("Win32_Service.Name='$$_[0]->{Name}'"); };
				if(!$@ && $check && $check->{State}  =~ /^PAUSED$/i)
				{
					printf MSG_PAUSED, $$_[1];
				}
				else
				{
					printf STDERR ERROR_NOTPAUSE_INTERNAL, $$_[1];
					$errorlevel = 255;
				}
			}
			else
			{
				printf STDERR ERROR_COULDNOT_PAUSE, $$_[1];
				printf STDERR "       ",$Error{ $Ret},"\n"  if($Ret && defined $Error{$Ret});
				$errorlevel = 254;
			}
		}
		else
		{
			printf STDERR ERROR_CANNOT_PAUSE, $$_[1];
			$errorlevel = 254;
		}
	}
}


# ############################################################################ #
#
# PURPOSE	: Removes a installed service.
#
# INPUTS	: $wmi	- WMI object.
#		  $args - Reference to Default arguments.
#
# RETURN VALUES : None.
#
# ############################################################################ #
sub RemoveService
{
	my ($wmi, $delfile, $args) = @_;
	my $query = "SELECT * FROM Win32_Service WHERE ";
	$query .= "(Name='$_' OR DisplayName='$_') OR"
		foreach (@$args);

	substr($query, -3, 3) = "";

	my $instances;
	eval { $instances = $wmi->ExecQuery($query, IFLAG) };
	if($@ or !defined $instances )
	{
		foreach(@$args)
		{
			printf STDERR ERROR_NO_SERVICE, $_;
			$errorlevel = 2302;
		}
		exit( $errorlevel );
	}

	my $found;
	for my $entry (@$args)
	{
		$found = 0;
		foreach (in $instances)
		{
			if(uc($_->{Name}) eq uc($entry) || uc($_->{DisplayName}) eq uc($entry))
			{
				my $dobj;
				eval{$dobj = $wmi->services->Get("Win32_Service.Name='$_->{Name}'"); };
				if(!$@ && defined $dobj)
				{
					$found = 1;
					my $Ret=$dobj->Delete();
					if(defined $Ret && $Ret == 0)
					{
						$dobj->InterrogateService();
						printf MSG_REMOVE, $entry;
					}
					else
					{
						printf STDERR ERROR_COULDNOT_REMOVE, $entry;
						print STDERR "       ",$Error{ $Ret },"\n" if($Ret && defined $Error{$Ret});
						$errorlevel = 254;
					}
				}
			}
		}
		unless($found)
		{
			printf STDERR ERROR_NO_SERVICE, $entry;
			$errorlevel = 2302;
		}
	}
}

# ############################################################################ #
#
# PURPOSE   : Starts a currently stopped service.
#
# INPUTS    : $wmi  - WMI object.
#             $args - Reference to Default arguments.
#
# RETURN VALUES : None.
#
# ############################################################################ #
sub StartService
{
	my ($wmi, $args) =@_;

	# Get the Instances of Win32_Service
	my $instances = &GetInstances($wmi, "Win32_Service");

	# Check for the Services.
	@$args = &CheckService($args, $instances);
	exit($errorlevel) unless(@$args);

	foreach (@$args)
	{
		if($$_[0]->{State} =~ /^RUNNING$|^PAUSED$/i)
		{
			printf STDERR ERROR_ALREADY_STARTED, $$_[1];
			$errorlevel = 21;
			next;
		}

		printf STATUS_START, $$_[1];
		my $Ret=$$_[0]->StartService();

		if(defined $Ret && $Ret == 0)
		{
			my $incr = 100;
			while(1)
			{
				my $check;
				eval{ $check = $wmi->services->Get("Win32_Service.Name='$$_[0]->{Name}'"); };
				last if( $@ or !defined $check);
				last if($incr == $check->{CheckPoint});
				$incr = $check->{CheckPoint};
				sleep(1);
			}
			my $check;
			eval{ $check = $wmi->services->Get("Win32_Service.Name='$$_[0]->{Name}'"); };
			if(!$@ && $check && $check->{State}  =~ /^RUNNING$/i)
			{
				printf MSG_START, $$_[1];
			}
			else
			{
				printf STDERR ERROR_NOTSTART_INTERNAL, $$_[1];
				$errorlevel = 255;
			}
		}
		else
		{
			printf STDERR ERROR_COULDNOT_START, $$_[1];
			print STDERR "       ",$Error{$Ret},"\n" if($Ret && defined $Error{$Ret});
			$errorlevel = 254;
		}
	}
}

# ############################################################################ #
#
# PURPOSE   : Stops a currently running service.
#
# INPUTS    : $wmi  - WMI object.
#             $args - Reference to Default arguments.
#
# RETURN VALUES : None.
#
# ############################################################################ #
sub StopService
{
	my ($wmi, $args) =@_;


	# Get the Instance of the Win32 Service Class.
	my $instances = &GetInstances($wmi, "Win32_Service");

	# Check for the service whether it is valid or not.
	@$args = &CheckService($args, $instances);
	exit($errorlevel) unless(@$args);

	foreach (@$args)
	{
		if($$_[0]->{State} =~ /STOPPED/i)
		{
			printf STDERR ERROR_COULDNOT_STOP, $$_[1];
			printf STDERR ERROR_NOT_STARTED, $$_[1];
			$errorlevel = 254;
			next;
		}

		if($$_[0]->{AcceptStop})
		{
			printf STATUS_STOP, $$_[1];
			my $Ret=$$_[0]->StopService();
			if(defined $Ret && $Ret == 0)
			{
				my $incr = 100;
				while(1)
				{
					my $check;
					eval{$check = $wmi->services->Get("Win32_Service.Name='$$_[0]->{Name}'"); };
					last if($@ or !defined $check);
					last if($incr == $check->{CheckPoint});
					$incr = $check->{CheckPoint};
					sleep(2);
				}
				my $check;
				eval{$check = $wmi->services->Get("Win32_Service.Name='$$_[0]->{Name}'"); };
				if($check && $check->{State} =~ /^STOPPED$/i)
				{
					printf MSG_STOP, $$_[1];
				}
				else
				{
					printf STDERR ERROR_NOTSTOP_INTERNAL, $$_[1];
					$errorlevel = 255;
				}
			}
			else
			{
				printf STDERR ERROR_COULDNOT_STOP, $$_[1];
				printf STDERR "       ",$Error{$Ret},"\n" if($Ret && defined $Error{$Ret});
				$errorlevel = 254;
			}
		}
		else
		{
			printf STDERR ERROR_CANNOT_STOP, $$_[1];
			$errorlevel = 254;
		}
	}
}

# ############################################################################ #
#
# PURPOSE   : Queries and displays the service properties/settings.
#
# INPUTS    : $wmi      - WMI object.
#             $filter   - Reference to filter array.
#             $format   - Format of display.
#             $verbose  - Verbose flag.
#             $args     - Reference to Default arguments.
#             $FilterCfg - Referecen to FilterConfigruation.
#
# RETURN VALUES : None.
#
# ############################################################################ #
sub QueryService
{
	my ($wmi, $filter, $format, $verbose, $args, $FilterCfg) = @_;

	# Get the Instance of the Win32 Services 
	my $instances = &GetInstances($wmi, "Win32_Service");
	push(@$args, "*") if(@$args == 0);

	# Check for the Validity of the Arguments.
	my @Instances = &CheckService($args, $instances);
	exit($errorlevel) unless(@Instances);

	my @table;
	my @temp = ('Name', 'State', 'DisplayName', 'RunAs', 'StartMode',
	            'Interact', 'ErrorControl', 'Type', 'AcceptStop',
	            'AcceptPause', 'LoadOrderGroup', 'LoadOrderNum',
	            'Dependencies', 'Path');
	push(@table, \@temp);

	my $size = 0;
	my $displayed = 0;

	foreach (@Instances)
	{
		my @temp =( $$_[0]->{Name},
		            $$_[0]->{State},
		            $$_[0]->{DisplayName},
		            $$_[0]->{StartName},
		            $$_[0]->{StartMode},
		            $$_[0]->{DesktopInteract} ? "ON" : "OFF",
		            $$_[0]->{ErrorControl},
		            (lc( $$_[0]->{ServiceType} ) eq 'own process' ) ? "OWNPROC" : "SHAREDPROC",
		            $$_[0]->{AcceptStop} ? "YES" : "NO",
		            $$_[0]->{AcceptPause} ? "YES" : "NO");

		for( my $i = 0 ; $i <= $#temp; $i++ )
		{
			$temp[ $i ] = 'N/A' if( !defined $temp[ $i ] );
		}

		my @dservice = ();
		my $path = $$_[0]->Path_()->{Path};
		$path =~ s/\\/\\\\/g;
		$path =~ s/\"/\\\"/g;

		my ( $blnLoadOrder , $blnDependencies ) = &CheckAssocReqd( $verbose, $filter );
		if( $blnLoadOrder )
		{
			my $LDGrp;
			eval{ $LDGrp = $$_[0]->Associators_("", "Win32_LoadOrderGroup", "", "", undef, undef , "", "", IFLAG, undef); };
			if($@ or !defined $LDGrp)
			{
				push(@temp, 'N/A', 'N/A');
			}
			else
			{
				my @InLDGrp = in $LDGrp;
				push(@temp, $InLDGrp[0]->{Name}, $InLDGrp[0]->{GroupOrder});
			}
		}
		else
		{
			push( @temp, 'N/A', 'N/A' );
		}

		if( $blnDependencies )
		{
			my $coll;
			eval{ $coll = $wmi->ExecQuery("SELECT * FROM Win32_DependentService WHERE Dependent = \"$path\"", IFLAG); };
			if(!$@ && defined $coll )
			{
				foreach (in $coll)
				{
					my $depend;
					eval{ $depend = $wmi->services->Get($_->{Antecedent}); };
					push(@dservice, $depend->{Name}) if(!$@ && defined $depend);
				}
			}
			push(@temp, \@dservice, $$_[0]->{PathName});
		}
		else
		{
			push( @temp, [ 'N/A' ], 'N/A' );
		}
		push(@table, \@temp);

		if( $size == MAX_ITERATION )
		{
			&FilterAndDisplayResults( $wmi, \@table, $filter, $FilterCfg, $format, $verbose, \$displayed );
			@table = $table[0];
			$size = 0;
		}
		else
		{
			$size++;
		}
	}

	if( @table > 1 )
	{
		&FilterAndDisplayResults( $wmi, \@table, $filter, $FilterCfg, $format, $verbose, \$displayed );
	}

	fail( INFO_NORESULT_FILTER, 30 ) if(@$filter && $displayed == 0 );
	fail( ERROR_NOINFO, 19 ) if( $displayed == 0 );
}

# ############################################################################ #
#
# PURPOSE   : Filters And displays the result which is in the table 
#
# INPUTS    : $table    - Table to be filtered and displayed
#             $filter   - Reference to filter array.
#             $filterCfg - Configruation of the filter to be applied
#             $format   - Format of display.
#             $verbose  - Verbose flag.
#             $displayed - A Flag that specifies whether the table is displayed or not
#                          ( Reference ).
#
# RETURN VALUES : None.
#
# ############################################################################ #
sub FilterAndDisplayResults
{
	my ( $wmi, $table, $filter, $FilterCfg, $format, $verbose, $displayed ) = @_;

	$wmi->FilterResults($table,$filter,$FilterCfg )
		if(@$filter > 0);

	if(@$table > 1)
	{
		$wmi->ShowTable( $table,
		                 { Format       => $format,
		                   HeaderDashes => ($$displayed == 0) ? 1 : 0,
		                   NoHeader     => ($$displayed) ? 1 : 0},
		                   Name         => { Name => 'Name', Align => 'left', MinWidth => 20},
		                   State        => { Name => 'State', Align => 'left', MinWidth => 8},
		                   DisplayName  => { Name => 'Display Name', Align => 'left', MinWidth => 40 },
		                   RunAs        => { Name => 'Log On As', Align => 'left', MinWidth => 40 },
		                   StartMode    => { Name => 'Start Mode', Align => 'left', MinWidth => 9} ,
		                   Interact     => { Name => 'Interactive', Align => 'left' },
		                   ErrorControl => { Name => 'Error Control', Align => 'left',
		                                     Hide => ($verbose ? 0 : 1) , MinWidth => 10},
		                   Type         => { Name => 'Service Type', Align => 'left',
		                                     Hide => ($verbose ? 0 : 1),
		                                     Formatter => sub {($_[0] eq "OWNPROC") ? "Own Process" : "Share Process"} , MinWidth => 14},
		                   AcceptStop   => { Name => 'Accept Stop', Align => 'left',
		                                     Hide => ($verbose ? 0 : 1) },
		                   AcceptPause  => { Name => 'Accept Pause', Align => 'left',
		                                     Hide => ($verbose ? 0 : 1) },
		                   LoadOrderGroup => { Name => 'Load Order Group', Align => 'left',
		                                       Hide => ($verbose ? 0 : 1),
		                                       Formatter => sub { defined $_[0] ? $_[0]:"N/A"} , MinWidth => 25},
		                   LoadOrderNum => { Name => 'Load Order Number', Align => 'left',
		                                     Hide => ($verbose ? 0 : 1),
		                                     Formatter => sub { defined $_[0] ? $_[0]:"N/A"}, MinWidth => 10 },
		                   Dependencies => { Name => 'Dependencies', Align => 'left',
		                                     Hide => ($verbose ? 0 : 1),
		                                     Formatter => sub { (@{$_[0]} > 0) ? join(",", @{$_[0]}) : "None"} , MinWidth => 40},
		                   Path         => { Name => 'Path Name', Align => 'left',
		                                     Hide => ($verbose ? 0 : 1),
		                                     Formatter => sub { defined $_[0] ? $_[0]:"N/A"} }
		               );
		$$displayed = 1;
	}
}

sub CheckAssocReqd
{
	my ( $verbose, $filter ) = @_;
	my( $blnLoadOrder, $blnDependencies );

	return ( 1, 1 ) if( $verbose );
	foreach my $fltr (@$filter)
	{
		if( $fltr =~ /<|>|=|!/ )
		{
			my $name = $`;
			$blnLoadOrder    = 1 if( $name =~ /^LoadOrderGroup$|^LoadOrderNum$/i );
			$blnDependencies = 1 if( lc( $name ) eq "dependencies" );
		}
		return( 1, 1 ) if( $blnLoadOrder and $blnDependencies );
	}
	return( $blnLoadOrder, $blnDependencies );
}

# ######################################################################### #
#
# PURPOSE	: Prints usage to STDOUT and exits.
#
# ######################################################################### #
sub usage
{
print <<EOF;

ServiceAdmin.pl -Operation [Arguments]

Description:
    Continue, Install, Pause, Remove, Start, Stop or Query the
    properties of one or more installed services.

Operation List:
    -continue   Continue a service that is currently paused.

    -install    Install and register a new service with the
                Service Control Manager(SC).

    -pause      Pause a servcie that is currently running.

    -remove     Uninstall a service registered with the
                Service Control Manager(SC).

    -start      Start a service that is currently stopped.

    -stop       Stop a service that is currently started.

    -query      Query and display the status and properties/settings 
                for one or more services.

For detailed usage:
    ServiceAdmin.pl -continue -?
    ServiceAdmin.pl -install  -?
    ServiceAdmin.pl -pause    -?
    ServiceAdmin.pl -remove   -?
    ServiceAdmin.pl -start    -?
    ServiceAdmin.pl -stop     -?
    ServiceAdmin.pl -query    -?
EOF
exit(0);
}

# ######################################################################### #
#
# PURPOSE	: Prints usage for -continue option to STDOUT and exits.
#
# ######################################################################### #
sub ContinueHelp
{

print <<EOF;

ServiceAdmin.pl -continue [-s machine [-u user [-p password]]]
                servicename...

Description:
    Continue service(s) that are currently paused.

Parameter List:
    -s          machine      Specifies the remote System to connect to.

    -u          user         Specifies the username (user context)
                             to run the scripts commands under.

    -p          password     Specifies the user password.

    default                  Specifies the service(s) to continue.
                             Can specify either Display name or service name
                             of the service. Also Wildcard * may be used.

Examples:
    ServiceAdmin.pl -continue Alerter M*
    ServiceAdmin.pl -continue utilman messanger
    ServiceAdmin.pl -continue -s machine -u user -p password Dns*

EOF
exit(0);
}


# ######################################################################### #
#
# PURPOSE	: Prints usage for -install option to STDOUT and exits.
#
# ######################################################################### #
sub InstallHelp
{
print <<EOF;

ServiceAdmin.pl -install [-s machine [-u user [-p password]]]
                -name servicename
                [-display displayname]
                [-type type]
                [-errorcontrol errctrltype]
                [-startmode mode]
                [-interact switch]
                [-runas user]
                [-runaspw password]
                [-loadorder group]
                [-loadorderdepend group]
                [-dependencies services]
                executablepath

Description:
    Install and register a new service with the Service Control
    Manager(SC).

Parameter List:
    -s               machine         Specifies the remote System to 
                                     connect to.

    -u               user            Specifies the username (user context)
                                     to run the scripts commands under.

    -p               password        Specifies the user password.

    -name            servicename     Specifies the name of the service to
                                     be installed.

    -display         displayname     Specifies the display name of the
                                     service being installed. Defaults
                                     to name specified with -name.

    -type            type            Specifies the type of service to be
                                     installed. Valid types include:
                                     OWNPROC or SHAREDPROC. Defaults to
                                     OWNPROC.

    -errorcontrol    errctrltype     Specifies the severity of the error
                                     if the service fails to start during
                                     startup. Valid values to be passed
                                     with the switch include: IGNORE,
                                     NORMAL, SEVERE or CRITICAL.
                                     Defaults to NORMAL.

    -startmode       mode            Specifies the start mode to use for
                                     the service. Valid start modes
                                     include: AUTO, MANUAL or DISABLED.
                                     Defaults to MANUAL.

    -interact        switch          Specifies whether the service
                                     should interact with the Desktop.
                                     Valid values to be passed with the
                                     switch include: YES or NO.
                                     Defaults to NO.

    -runas           user            Specifies the user context that the
                                     service should run under. Defaults
                                     to LocalSystem.

    -runaspw         password        Specifies the password of the user
                                     specified in -runas.

    -loadorder       group           Specifies the load order group in which
                                     this service should become a member.

    -loadorderdepend group           Specifies a load order group on which
                                     the service depends before starting.

    -dependencies    service(s)      Specifies the list of services that
                                     must start before this service starts.

    default                          Specifies the path to the executable
                                     service to install.

Examples:
    ServiceAdmin.pl -install -name "Fresh Service" c:\\Winnt\\Services.exe
    ServiceAdmin.pl -install -name "New Service" -startmode MANUAL
                    -dependencies dns -dependencies Dnscache
                     c:\\Winnt\\Services.exe
    ServiceAdmin.pl -install -name "New Service1" -display "My Service"
                    -type SHAREDPROC -errorcontrol IGNORE -startmode AUTO
                    -interact NO -runas LocalSystem -loadorder "Filter"
                    -dependencies Serv1 C:\\Winnt\\Newservice.exe
    ServiceAdmin.pl -install -s machine -u user -p password -name "AntiSrv"
                    -display "Anti Service" -type OWNPROC 
                    -errorcontrol CRITICAL -startmode MANUAL -interact NO 
                    -runas user1 C:\\Winnt\\system32\\antisrv.exe

EOF
exit(0);
}

# ######################################################################### #
#
# PURPOSE   : Prints usage for -pause option to STDOUT and exits.
#
# ######################################################################### #
sub PauseHelp
{

print <<EOF;

ServiceAdmin.pl -pause [-s machine [-u user [-p password]]]
                servicename...

Description:
    Pause service(s) that are currently running.

Parameter List:
    -s         machine      Specifies the remote System to connect to.

    -u         user         Specifies the username (user context)
                            to run the scripts commands under.

    -p         password     Specifies the user password.

    default                 Specifies the service(s) to Pause.
                            Can specify either display name or service name
                            of the service. Also Wildcard * may be used.

Examples:
    ServiceAdmin.pl -pause Alerter M*
    ServiceAdmin.pl -pause utilman messanger
    ServiceAdmin.pl -pause -s machine -u user -p password Dns*

EOF
exit(0);
}

# ######################################################################### #
#
# PURPOSE   : Prints usage for -remove option to STDOUT and exits.
#
# ######################################################################### #
sub RemoveHelp
{
print <<EOF;

ServiceAdmin.pl -remove [-s machine [-u user [-p password]]]
                servicename...

Description:
    Uninstall service(s) registered with the Service Control Manager (SC).

Parameter List:
    -s         machine      Specifies the remote system to connect to.

    -u         user         Specifies the username (user context)
                            to run the scripts commands under.

    -p         password     Specifies the user password.

    default                 Specifies service(s) to remove from the 
                            Service Control Manager(SC). 
                            Can specify either Display name or service name
                            of the service. Wildcards are not allowed.

Examples:
    ServiceAdmin.pl -remove "New Services"
    ServiceAdmin.pl -remove -s machine -u user -p password "New Service"

EOF
exit(0);
}

# ######################################################################### #
#
# PURPOSE	: Prints usage for -start option to STDOUT and exits.
#
# ######################################################################### #
sub StartHelp
{
print <<EOF;

ServiceAdmin.pl -start [-s machine [-u user [-p password ] ] ]
                servicename...

Description:
    Start service(s) that are currently stopped.

Parameter List:
    -s         machine      Specifies the remote system to connect to.

    -u         user         Specifies the username (user context)
                            to run the scripts commands under.

    -p         password     Specifies the user password. 

    default                 Specifies the service(s) to start.
                            Can specify either display name or service name
                            of the service. Also Wildcard * may be used.

Examples:
    ServiceAdmin.pl -start Alerter M*
    ServiceAdmin.pl -start utilman
    ServiceAdmin.pl -start -s machine -u user -p password Dns*

EOF
exit(0);
}

# ######################################################################### #
#
# PURPOSE   : Prints usage for -stop option to STDOUT and exits.
#
# ######################################################################### #
sub StopHelp
{
print <<EOF;

ServiceAdmin.pl -stop [-s machine [-u user [-p password ] ] ]
                servicename...

Description:
    Stop service(s) that are currently running.

Parameter List:
    -s         machine      Specifies the remote system to connect to.

    -u         user         Specifies the username (user context)
                            to run the scripts commands under.

    -p         password     Specifies the user password.

    default                 Specifies the service(s) to Stop.
                            Can specify either display name or service name
                            of the service. Also Wildcard * may be used.

Examples:
    ServiceAdmin.pl -stop Alerter M*
    ServiceAdmin.pl -stop utilman
    ServiceAdmin.pl -stop -s machine -u user -p password Dns*

EOF
exit(0);
}

# ######################################################################### #
#
# PURPOSE   : Prints usage for -query option to STDOUT and exits.
#
# ######################################################################### #
sub QueryHelp
{
print <<EOF;

ServiceAdmin.pl -query [-s machine [-u user [-p password]]]
                [-filter filter]
                [-format format]
                [-verbose]
                [servicename...]

Description:
    Query and display the status and properties/settings for
    one or more services.

Parameter List:
    -s         machine      Specifies the remote system to connect to.

    -u         user         Specifies the username (user context)
                            to run the scripts commands under.

    -p         password     Specifies the user password.

    -filter    filter       Specifies the services to filter in or out
                            of the query based on the specified criteria.

    -format    format       Specifies the type of output to display.
                            Values to be passed with this switch are 
                            TABLE, LIST or CSV.

    -verbose                Display verbose service properties/settings.

    default                 Specifies the service(s) to query and
                            display properties and settings from.
                            Can specify either display name or service name
                            of the service. Also Wildcard * may be used.

Note:

1. Following are the filters that are applicable.

    FilterName        Valid Operators    Valid Value types
    ----------        ---------------    -----------------
    Name              '!' , '='           String
    DisplayName       '!' , '='           String
    State             '!' , '='           RUNNING | STOPPED | PAUSED
    ErrorControl      '!' , '='           IGNORE | NORMAL | SEVERE | CRITICAL
    AcceptPause       '!' , '='           YES | NO
    AcceptStop        '!' , '='           YES | NO
    StartMode         '!' , '='           AUTO | MANUAL | DISABLED | BOOT
    Interact          '!' , '='           ON | OFF
    RunAs             '!' , '='           String
    LoadOrderGroup    '!' , '='           String
    LoadOrderNum      '!' , '=', '<', '>' Number
    Dependencies      '!' , '='           String
    Path              '!' , '='           String
    Type              '!' , '='           OWNPROC | SHAREDPROC

Examples:
    ServiceAdmin.pl -query *
    ServiceAdmin.pl -query Remote* -format List -verbose
    ServiceAdmin.pl -query -s machine -u user -p password -format csv
                     Alerter Messenger Event*
    ServiceAdmin.pl -query -filter STARTMODE=DISABLED -v *
    ServiceAdmin.pl -query -filter "Name!Dns Server" -v Dns*
    ServiceAdmin.pl -query -filter "Acceptpause=NO" -filter "Interact=OFF"
                    -format List Event*
    ServiceAdmin.pl -query -filter "runas=domain\\user" -v Net*
    ServiceAdmin.pl -query -s machine -u user -p password 
                    -filter "LoadOrderNum>10" Net*
    ServiceAdmin.pl -query -filter "Dependencies=Tcpip" -v Dns*
    ServiceAdmin.pl -query -filter "Path=c:\\Winnt\\system32\\accwiz.exe"
                    -filter "type=SharedProc" util*

EOF
exit(0);
}

#####################################################################################
#
#                              End of Script
#
#####################################################################################