1#!/usr/bin/perl -w 2# 3# Copyright (C) 2010, 2012 Internet Systems Consortium, Inc. ("ISC") 4# 5# Permission to use, copy, modify, and/or distribute this software for any 6# purpose with or without fee is hereby granted, provided that the above 7# copyright notice and this permission notice appear in all copies. 8# 9# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 10# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 11# AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 12# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 13# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 14# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 15# PERFORMANCE OF THIS SOFTWARE. 16 17# Id: stop.pl,v 1.2 2010/06/17 05:38:05 marka Exp 18 19# Framework for stopping test servers 20# Based on the type of server specified, signal the server to stop, wait 21# briefly for it to die, and then kill it if it is still alive. 22# If a server is specified, stop it. Otherwise, stop all servers for test. 23 24use strict; 25use Cwd 'abs_path'; 26 27# Option handling 28# [--use-rndc] test [server] 29# 30# test - name of the test directory 31# server - name of the server directory 32 33my $usage = "usage: $0 [--use-rndc] test-directory [server-directory]"; 34my $use_rndc; 35 36while (@ARGV && $ARGV[0] =~ /^-/) { 37 my $opt = shift @ARGV; 38 if ($opt eq '--use-rndc') { 39 $use_rndc = 1; 40 } else { 41 die "$usage\n"; 42 } 43} 44 45my $test = $ARGV[0]; 46my $server = $ARGV[1]; 47 48my $errors = 0; 49 50die "$usage\n" unless defined($test); 51die "No test directory: \"$test\"\n" unless (-d $test); 52die "No server directory: \"$server\"\n" if (defined($server) && !-d "$test/$server"); 53 54# Global variables 55my $testdir = abs_path($test); 56my @servers; 57 58 59# Determine which servers need to be stopped. 60if (defined $server) { 61 @servers = ($server); 62} else { 63 local *DIR; 64 opendir DIR, $testdir or die "$testdir: $!\n"; 65 my @files = sort readdir DIR; 66 closedir DIR; 67 68 my @ns = grep /^ns[0-9]*$/, @files; 69 70 push @servers, @ns; 71} 72 73 74# Stop the server(s), pass 1: rndc. 75if ($use_rndc) { 76 foreach my $server (grep /^ns/, @servers) { 77 stop_rndc($server); 78 } 79 80 wait_for_servers(30, grep /^ns/, @servers); 81} 82 83 84# Pass 2: SIGTERM 85foreach my $server (@servers) { 86 stop_signal($server, "TERM"); 87} 88 89wait_for_servers(60, @servers); 90 91# Pass 3: SIGABRT 92foreach my $server (@servers) { 93 stop_signal($server, "ABRT"); 94} 95 96exit($errors ? 1 : 0); 97 98# Subroutines 99 100# Return the full path to a given server's PID file. 101sub server_pid_file { 102 my($server) = @_; 103 104 my $pid_file; 105 if ($server =~ /^ns/) { 106 $pid_file = "named.pid"; 107 } else { 108 print "I:Unknown server type $server\n"; 109 exit 1; 110 } 111 $pid_file = "$testdir/$server/$pid_file"; 112} 113 114# Read a PID. 115sub read_pid { 116 my($pid_file) = @_; 117 118 local *FH; 119 my $result = open FH, "< $pid_file"; 120 if (!$result) { 121 print "I:$pid_file: $!\n"; 122 unlink $pid_file; 123 return; 124 } 125 126 my $pid = <FH>; 127 chomp($pid); 128 return $pid; 129} 130 131# Stop a named process with rndc. 132sub stop_rndc { 133 my($server) = @_; 134 135 return unless ($server =~ /^ns(\d+)$/); 136 my $ip = "10.53.0.$1"; 137 138 # Ugly, but should work. 139 system("$ENV{RNDC} -c $testdir/../common/rndc.conf -s $ip -p 9953 stop | sed 's/^/I:$server /'"); 140 return; 141} 142 143# Stop a server by sending a signal to it. 144sub stop_signal { 145 my($server, $sig) = @_; 146 147 my $pid_file = server_pid_file($server); 148 return unless -f $pid_file; 149 150 my $pid = read_pid($pid_file); 151 return unless defined($pid); 152 153 if ($sig eq 'ABRT') { 154 print "I:$server didn't die when sent a SIGTERM\n"; 155 $errors++; 156 } 157 158 my $result = kill $sig, $pid; 159 if (!$result) { 160 print "I:$server died before a SIG$sig was sent\n"; 161 unlink $pid_file; 162 $errors++; 163 } 164 165 return; 166} 167 168sub wait_for_servers { 169 my($timeout, @servers) = @_; 170 171 my @pid_files = grep { defined($_) } 172 map { server_pid_file($_) } @servers; 173 174 while ($timeout > 0 && @pid_files > 0) { 175 @pid_files = grep { -f $_ } @pid_files; 176 sleep 1 if (@pid_files > 0); 177 $timeout--; 178 } 179 180 return; 181} 182