diff options
Diffstat (limited to '')
-rw-r--r-- | bin/tests/system/stop.pl | 292 |
1 files changed, 292 insertions, 0 deletions
diff --git a/bin/tests/system/stop.pl b/bin/tests/system/stop.pl new file mode 100644 index 0000000..6783b85 --- /dev/null +++ b/bin/tests/system/stop.pl @@ -0,0 +1,292 @@ +#!/usr/bin/perl -w + +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# SPDX-License-Identifier: MPL-2.0 +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, you can obtain one at https://mozilla.org/MPL/2.0/. +# +# See the COPYRIGHT file distributed with this work for additional +# information regarding copyright ownership. + +# Framework for stopping test servers +# Based on the type of server specified, signal the server to stop, wait +# briefly for it to die, and then kill it if it is still alive. +# If a server is specified, stop it. Otherwise, stop all servers for test. + +use strict; +use warnings; + +use Cwd ':DEFAULT', 'abs_path'; +use English '-no_match_vars'; +use Getopt::Long; + +# Usage: +# perl stop.pl [--use-rndc [--port port]] test [server] +# +# --use-rndc Attempt to stop the server via the "rndc stop" command. +# +# --port port Only relevant if --use-rndc is specified, this sets the +# command port over which the attempt should be made. If +# not specified, port 9953 is used. +# +# test Name of the test directory. +# +# server Name of the server directory. + +my $usage = "usage: $0 [--use-rndc [--halt] [--port port]] test-directory [server-directory]"; + +my $use_rndc = 0; +my $halt = 0; +my $rndc_port = 9953; +my $errors = 0; + +GetOptions( + 'use-rndc!' => \$use_rndc, + 'halt!' => \$halt, + 'port=i' => \$rndc_port + ) or die "$usage\n"; + +my ( $test, $server_arg ) = @ARGV; + +if (!$test) { + die "$usage\n"; +} + +# Global variables +my $topdir = abs_path($ENV{'SYSTEMTESTTOP'}); +my $testdir = abs_path($topdir . "/" . $test); + +if (! -d $testdir) { + die "No test directory: \"$testdir\"\n"; +} + +if ($server_arg && ! -d "$testdir/$server_arg") { + die "No server directory: \"$testdir/$server_arg\"\n"; +} + +my $RNDC = $ENV{RNDC}; + +my @ns; +my @ans; + +if ($server_arg) { + if ($server_arg =~ /^ns/) { + push(@ns, $server_arg); + } elsif ($server_arg =~ /^ans/) { + push(@ans, $server_arg); + } else { + print "$0: ns or ans directory expected"; + print "I:$test:failed"; + } +} else { + # Determine which servers need to be stopped for this test. + opendir DIR, $testdir or die "unable to read test directory: \"$test\" ($OS_ERROR)\n"; + my @files = sort readdir DIR; + closedir DIR; + + @ns = grep /^ns[0-9]*$/, @files; + @ans = grep /^ans[0-9]*$/, @files; +} + +# Stop the server(s), pass 1: rndc. +if ($use_rndc) { + foreach my $name(@ns) { + stop_rndc($name, $rndc_port); + } + + @ns = wait_for_servers(30, @ns); +} + +# Pass 2: SIGTERM +foreach my $name (@ns) { + stop_signal($name, "TERM"); +} + +@ns = wait_for_servers(60, @ns); + +foreach my $name(@ans) { + stop_signal($name, "TERM", 1); +} + +@ans = wait_for_servers(1200, @ans); + +# Pass 3: SIGABRT +foreach my $name (@ns) { + print "I:$test:$name didn't die when sent a SIGTERM\n"; + stop_signal($name, "ABRT"); + $errors = 1; +} +foreach my $name (@ans) { + print "I:$test:$name didn't die when sent a SIGTERM\n"; + stop_signal($name, "ABRT", 1); + $errors = 1; +} + +exit($errors); + +# Subroutines + +# Return the full path to a given server's lock file. +sub server_lock_file { + my ( $server ) = @_; + + return if (defined($ENV{'CYGWIN'}) && $ENV{'CYGWIN'}); + + return $testdir . "/" . $server . "/named.lock" if ($server =~ /^ns/); + return if ($server =~ /^ans/); + + die "Unknown server type $server\n"; +} + +# Return the full path to a given server's PID file. +sub server_pid_file { + my ( $server ) = @_; + + return $testdir . "/" . $server . "/named.pid" if ($server =~ /^ns/); + return $testdir . "/" . $server . "/ans.pid" if ($server =~ /^ans/); + + die "Unknown server type $server\n"; +} + +# Read a PID. +sub read_pid { + my ( $pid_file ) = @_; + + return unless -f $pid_file; + # we don't really care about the race condition here + my $result = open(my $fh, "<", $pid_file); + if (!defined($result)) { + print "I:$test:$pid_file: $!\n"; + unlink $pid_file; + return; + } + + my $pid = <$fh>; + return unless defined($pid); + + chomp($pid); + return $pid; +} + +# Stop a named process with rndc. +sub stop_rndc { + my ( $server, $port ) = @_; + my $n; + + if ($server =~ /^ns(\d+)/) { + $n = $1; + } else { + die "unable to parse server number from name \"$server\"\n"; + } + + my $ip = "10.53.0.$n"; + if (-e "$testdir/$server/named.ipv6-only") { + $ip = "fd92:7065:b8e:ffff::$n"; + } + + my $how = $halt ? "halt" : "stop"; + + # Ugly, but should work. + system("$RNDC -c ../common/rndc.conf -s $ip -p $port $how | sed 's/^/I:$test:$server /'"); + return; +} + +sub server_died { + my ( $server, $signal ) = @_; + + print "I:$test:$server died before a SIG$signal was sent\n"; + $errors = 1; + + my $pid_file = server_pid_file($server); + unlink($pid_file); + + return; +} + +sub send_signal { + my ( $signal, $pid, $ans ) = @_; + + if (! defined $ans) { + $ans = 0; + } + + my $result = 0; + + if (!$ans && ($^O eq 'cygwin' || $^O eq 'msys')) { + my $killout = `/bin/kill -f -$signal $pid 2>&1`; + chomp($killout); + $result = 1 if ($killout eq ''); + } else { + $result = kill $signal, $pid; + } + return $result; +} + +# Stop a server by sending a signal to it. +sub stop_signal { + my ( $server, $signal, $ans ) = @_; + if (! defined $ans) { + $ans = 0; + } + + my $pid_file = server_pid_file($server); + my $pid = read_pid($pid_file); + + return unless defined($pid); + + # Send signal to the server, and bail out if signal can't be sent + if (send_signal($signal, $pid, $ans) != 1) { + server_died($server, $signal); + return; + } + + return; +} + +sub pid_file_exists { + my ( $server ) = @_; + + my $pid_file = server_pid_file($server); + my $pid = read_pid($pid_file); + + return unless defined($pid); + + # If we're here, the PID file hasn't been cleaned up yet + if (send_signal(0, $pid) == 0) { + # XXX: on windows this is likely to result in a + # false positive, so don't bother reporting the error. + if (!defined($ENV{'CYGWIN'}) || !$ENV{'CYGWIN'}) { + print "I:$test:$server crashed on shutdown\n"; + $errors = 1; + } + return; + } + + return $server; +} + +sub lock_file_exists { + my ( $server ) = @_; + my $lock_file = server_lock_file($server); + + return unless defined($lock_file) && -f $lock_file; + + return $server; +} + +sub wait_for_servers { + my ( $timeout, @servers ) = @_; + + while ($timeout > 0 && @servers > 0) { + sleep 1 if (@servers > 0); + @servers = + grep { defined($_) } + map { pid_file_exists($_) || lock_file_exists($_) } @servers; + $timeout--; + } + + return @servers; +} |