summaryrefslogtreecommitdiffstats
path: root/bin/tests/system/stop.pl
diff options
context:
space:
mode:
Diffstat (limited to 'bin/tests/system/stop.pl')
-rw-r--r--bin/tests/system/stop.pl292
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;
+}