Update: Thanks to uksza for the full set of state codes. Originally I only knew about 1 and 4.

use Getopt::Long; use Win32::Service; use warnings; my $hostname = ''; # this means local host. my %services; Win32::Service::GetServices($hostname,\%services); my %svc = reverse %services; # so that it's keyed by service ID my %state_code = ( 1 => 'not running', 2 => 'start pending', 3 => 'stop pending', 4 => 'running', 5 => 'resume pending', 6 => 'pause pending', 7 => 'paused' ); sub status { my $func = shift; # 'status' for my $svc ( map split(/,/), @_ ) { my %info; Win32::Service::GetStatus($hostname,$svc,\%info) or print("Err +or - No service named '$svc'\n"), next; print "Status of $svc:\n"; $svc{$svc} ne $svc and print "\tDescription: $svc{$svc}\n"; #printf "\tType: 0x%02X\n", $info{'ServiceType'}; print "\tState: ", $state_code{$info{'CurrentState'}} || $info +{'CurrentState'} , "\n"; $info{'ProcessId'} and print "\tPID: $info{'ProcessId'}\n"; #$info{'ControlsAccepted'} $info{'Win32ExitCode'} and print "\tExited with code $info{'Win32ExitCode'}.\n"; $info{'ServiceSpecificExitCode'} and print "\tExited with error; code $info{'ServiceSpecificExitCode' +} was logged.\n"; $info{'WaitHint'} and print "\tApprox. $info{'WaitHint'} milli +seconds until complete.\n"; $info{'CheckPoint'} and print "\tCheckpoint: $info{'CheckPoint +'}\n"; print "\n"; } } sub list { for my $svc ( sort keys %svc ) { my $descr = $svc{$svc}; { my %info; Win32::Service::GetStatus($hostname,$svc,\%info); print "(@info{qw( CurrentState CheckPoint WaitHint )}) "; } print $svc; $svc ne $descr and print qq( "$descr"); print "\n"; } } my %dispatch = ( start => \&Win32::Service::StartService, stop => \&Win32::Service::StopService, pause => \&Win32::Service::PauseService, resume => \&Win32::Service::ResumeService, ); sub simple { my $func = shift; for my $svc ( map split(/,/), @_ ) { print "Attempting to $func $svc...\n\t"; print $dispatch{$func}->($hostname, $svc) ? "Success!\n" : "Fa +ilure!\n"; } } sub prompt { my $func = shift; my @prompts = map split(/\//), @_; # Getopt::Long passes a single empty string if no argument is give +n. if ( @prompts < 1 or @prompts == 1 && $prompts[0] eq '' ) { print "Press <Enter>: "; } else { print join("\n",@prompts), " press <Enter>: "; } scalar <>; } sub usage { die <<EOF; Usage: $0 [options...] Where options are one or more of the following: --help : display this usage statement --list : list all known services --status LIST : display detailed status of the specified services --start LIST : start the specified services --stop LIST : stop the specified services --pause LIST : pause (suspend) the specified services --resume LIST : resume (unsuspend) the specified services --prompt TEXT : present a prompt and wait for the <enter> key You may give any option multiple times on the command line. They are executed in the order given. LIST is a comma-separated list of one or more service names. You can use the --list option to get a list of known services. Do NOT include whitespace; only commas! The --prompt option is useful for scripting situations. The prompt text is displayed and the program waits for the <Enter> key to be pressed. The text "press <Enter>: " is appended to any prompt string given as an argument. If no prompt string is given, "Press <Enter>: " is used as default. You may create a multi-line prompt by passing multiple slash-separated strings as an argument. Example: --stop SVC1 --prompt "SVC1 stopped."/"To start it," --start SVC1 This causes the following prompt to be displayed: SVC1 stopped. To start it, press <Enter>: Any text entered by the user at the prompt is ignored. EOF } sub extra { warn("\nWarning! Extra arguments passed and ignored!\n\t<@_>\nRun + with no options for a usage summary.\n"); } @ARGV or usage(); GetOptions( 'list!' => \&list, 'status|get_status=s' => \&status, 'start=s' => \&simple, 'stop=s' => \&simple, 'pause=s' => \&simple, 'resume=s' => \&simple, 'prompt:s' => \&prompt, 'help!' => \&usage, '<>' => \&extra, ) or usage();
We're building the house of the future together.