Skittles
Skittles

Reputation: 2918

Perl system call throws strange error

I just wrote a perl script that is restarting a list of services on a linux server. It's intended to run as a cron job. when I execute the script though, I keep getting this error;

root@www:~/scripts# ./ws_restart.pl
  * Stopping web server apache2                                   [ OK ]
sh: Syntax error: "(" unexpected
  * Stopping MySQL database server mysqld                         [ OK ]
sh: Syntax error: "(" unexpected

The call that is being used to do this is;

system("/etc/init.d/apache2 stop");
system("/etc/init.d/mysql stop");

I can paste the entire script code if needed, but I figured that this is the source of the problem and just need to know how to stop it.

Any ideas?

Here's the entire script;

#!/usr/bin/perl

use strict;
use warnings;

use Data::Dumper;

my $old_pids = {};
my $post_stop_ids = {};
my @services = qw/apache2 mysql solr/;
my $app_dir  = '/home/grip/apps/eventfinder';


# collect existing pids then kill services
foreach my $service (@services) {
  # gather up existing pids
  $old_pids->{$service} = [ get_pids_by_process($service) ];

  # issue stop command to each service
  set_service_state($service, 'stop');

  # attempt to regather same ids
  $post_stop_ids->{$service} = [ get_pids_by_process($service) ];

  # kill any rogue ids left over
  kill_rogue_procs($post_stop_ids->{$service});

  # give each kill time to finish
  sleep(5);
}


# attempt to restart killed services
foreach my $service (@services) {
  # issue start command to each service
  set_service_state($service, 'start');

  # Let's give each service enough time to crawl outta bed.
  # I know how much I hate waking up
  sleep(5);
}

# wait for it!...wait for it! :P
# Pad an extra 5 seconds to give solr enough time to come up before we reindex
sleep(5);

# start the reindexing process of solr
system("cd $app_dir ; RAILS_ENV=production rake reindex_active");

# call it a day...phew!
exit 0;

sub kill_rogue_procs {
  my @ids = shift;
  # check if we still have any rogue processes that failed to die
  # if so, kill them now.
  if(scalar @ids) {
    foreach my $pid (@ids) {
      system("kill $pid");
    }
  }
}


sub set_service_state {
  my ($proc, $state) = @_;

  if($proc eq 'apache2') {
    system("/etc/init.d/apache2 $state");
  } elsif($proc eq 'mysql') {
    system("/etc/init.d/mysql $state");
  } elsif($proc eq 'solr') {
    system("cd $app_dir ; RAILS_ENV=production rake sunspot:solr:$state");
  }
}


sub get_pids_by_process {
  my $proc = shift;

  my @proc_ids = ();

  open(PSAE, "/bin/ps -ae | grep $proc |") || die("Couldn't run command");

  while(<PSAE>) {
    push @proc_ids, $_ =~ /(\d{1,5})/;
  }

  close PSAE;

  return @proc_ids;
}

Upvotes: 0

Views: 371

Answers (3)

runrig
runrig

Reputation: 6524

This is wrong:

sub kill_rogue_procs {
  my @ids = shift;
  # check if we still have any rogue processes that failed to die
  # if so, kill them now.
  if(scalar @ids) {

From what you're passing to this sub, @ids will always contain a single array reference, so (scalar @ids) will always be true. It also means you end up passing something like the following to sh:

kill ARRAY(0x91b0768)

You want something like (if the arrayref is empty, there's nothing to loop over anyway):

my $ids = shift;
...
for my $pid (@$ids) {
  kill SIGTERM => $pid;

Or instead of the loop:

kill SIGTERM => @$ids;

Also, there is no need to call system to kill a process.

To this, I'd add the last line, so you don't grep the grep process itself:

sub get_pids_by_process {
  my $proc = shift;
  $proc =~ s/^(.)/[$1]/;  

Upvotes: 3

Paul Tomblin
Paul Tomblin

Reputation: 182762

Actually, I'd be more suspicious of what's in @ids in kill_rogue_procs. It's the result of a ps followed by a grep, so might have bogus values if ps doesn't return any results or if the pid isn't 5 digits long.

Upvotes: 3

Eduardo Ivanec
Eduardo Ivanec

Reputation: 11852

As sh is raising the errors, I'm pretty sure one of the parameters to system is being expanded to something unexpected. I'd print all parameters just prior to passing them to system for a quick debug.

Upvotes: 1

Related Questions