1#!/usr/bin/perl -w 2 3# Copyright (C) Internet Systems Consortium, Inc. ("ISC") 4# 5# SPDX-License-Identifier: MPL-2.0 6# 7# This Source Code Form is subject to the terms of the Mozilla Public 8# License, v. 2.0. If a copy of the MPL was not distributed with this 9# file, you can obtain one at https://mozilla.org/MPL/2.0/. 10# 11# See the COPYRIGHT file distributed with this work for additional 12# information regarding copyright ownership. 13 14# Framework for stopping test servers 15# Based on the type of server specified, signal the server to stop, wait 16# briefly for it to die, and then kill it if it is still alive. 17# If a server is specified, stop it. Otherwise, stop all servers for test. 18 19use strict; 20use warnings; 21 22use Cwd ':DEFAULT', 'abs_path'; 23use English '-no_match_vars'; 24use Getopt::Long; 25 26# Usage: 27# perl stop.pl [--use-rndc [--port port]] test [server] 28# 29# --use-rndc Attempt to stop the server via the "rndc stop" command. 30# 31# --port port Only relevant if --use-rndc is specified, this sets the 32# command port over which the attempt should be made. If 33# not specified, port 9953 is used. 34# 35# test Name of the test directory. 36# 37# server Name of the server directory. 38 39my $usage = "usage: $0 [--use-rndc [--halt] [--port port]] test-directory [server-directory]"; 40 41my $use_rndc = 0; 42my $halt = 0; 43my $rndc_port = 9953; 44my $errors = 0; 45 46GetOptions( 47 'use-rndc!' => \$use_rndc, 48 'halt!' => \$halt, 49 'port=i' => \$rndc_port 50 ) or die "$usage\n"; 51 52my ( $test, $server_arg ) = @ARGV; 53 54if (!$test) { 55 die "$usage\n"; 56} 57 58# Global variables 59my $builddir = $ENV{'builddir'}; 60my $srcdir = $ENV{'srcdir'}; 61my $testdir = "$builddir/$test"; 62 63if (! -d $testdir) { 64 die "No test directory: \"$testdir\"\n"; 65} 66 67if ($server_arg && ! -d "$testdir/$server_arg") { 68 die "No server directory: \"$testdir/$server_arg\"\n"; 69} 70 71my $RNDC = $ENV{RNDC}; 72 73my @ns; 74my @ans; 75 76if ($server_arg) { 77 if ($server_arg =~ /^ns/) { 78 push(@ns, $server_arg); 79 } elsif ($server_arg =~ /^ans/) { 80 push(@ans, $server_arg); 81 } else { 82 print "$0: ns or ans directory expected"; 83 print "I:$test:failed"; 84 } 85} else { 86 # Determine which servers need to be stopped for this test. 87 opendir DIR, $testdir or die "unable to read test directory: \"$test\" ($OS_ERROR)\n"; 88 my @files = sort readdir DIR; 89 closedir DIR; 90 91 @ns = grep /^ns[0-9]*$/, @files; 92 @ans = grep /^ans[0-9]*$/, @files; 93} 94 95# Stop the server(s), pass 1: rndc. 96if ($use_rndc) { 97 foreach my $name(@ns) { 98 stop_rndc($name, $rndc_port); 99 } 100 101 @ns = wait_for_servers(120, @ns); 102} 103 104# Pass 2: SIGTERM 105foreach my $name (@ns) { 106 stop_signal($name, "TERM"); 107} 108 109@ns = wait_for_servers(300, @ns); 110 111foreach my $name(@ans) { 112 stop_signal($name, "TERM", 1); 113} 114 115@ans = wait_for_servers(300, @ans); 116 117# Pass 3: SIGABRT 118foreach my $name (@ns) { 119 print "I:$test:$name didn't die when sent a SIGTERM\n"; 120 stop_signal($name, "ABRT"); 121 $errors = 1; 122} 123foreach my $name (@ans) { 124 print "I:$test:$name didn't die when sent a SIGTERM\n"; 125 stop_signal($name, "ABRT", 1); 126 $errors = 1; 127} 128 129exit($errors); 130 131# Subroutines 132 133# Return the full path to a given server's PID file. 134sub server_pid_file { 135 my ( $server ) = @_; 136 137 return $testdir . "/" . $server . "/named.pid" if ($server =~ /^ns/); 138 return $testdir . "/" . $server . "/ans.pid" if ($server =~ /^ans/); 139 140 die "Unknown server type $server\n"; 141} 142 143# Read a PID. 144sub read_pid { 145 my ( $pid_file ) = @_; 146 147 return unless -f $pid_file; 148 # we don't really care about the race condition here 149 my $result = open(my $fh, "<", $pid_file); 150 if (!defined($result)) { 151 print "I:$test:$pid_file: $!\n"; 152 unlink $pid_file; 153 return; 154 } 155 156 my $pid = <$fh>; 157 return unless defined($pid); 158 159 chomp($pid); 160 return $pid; 161} 162 163# Stop a named process with rndc. 164sub stop_rndc { 165 my ( $server, $port ) = @_; 166 my $n; 167 168 if ($server =~ /^ns(\d+)/) { 169 $n = $1; 170 } else { 171 die "unable to parse server number from name \"$server\"\n"; 172 } 173 174 my $ip = "10.53.0.$n"; 175 if (-e "$testdir/$server/named.ipv6-only") { 176 $ip = "fd92:7065:b8e:ffff::$n"; 177 } 178 179 my $how = $halt ? "halt" : "stop"; 180 181 # Ugly, but should work. 182 system("$RNDC -c ../_common/rndc.conf -s $ip -p $port $how | sed 's/^/I:$test:$server /'"); 183 return; 184} 185 186sub server_died { 187 my ( $server, $signal ) = @_; 188 189 print "I:$test:$server died before a SIG$signal was sent\n"; 190 $errors = 1; 191 192 my $pid_file = server_pid_file($server); 193 unlink($pid_file); 194 195 return; 196} 197 198sub send_signal { 199 my ( $signal, $pid, $ans ) = @_; 200 201 if (! defined $ans) { 202 $ans = 0; 203 } 204 205 my $result = 0; 206 207 $result = kill $signal, $pid; 208 return $result; 209} 210 211# Stop a server by sending a signal to it. 212sub stop_signal { 213 my ( $server, $signal, $ans ) = @_; 214 if (! defined $ans) { 215 $ans = 0; 216 } 217 218 my $pid_file = server_pid_file($server); 219 my $pid = read_pid($pid_file); 220 221 return unless defined($pid); 222 223 # Send signal to the server, and bail out if signal can't be sent 224 if (send_signal($signal, $pid, $ans) != 1) { 225 server_died($server, $signal); 226 return; 227 } 228 229 return; 230} 231 232sub pid_file_exists { 233 my ( $server ) = @_; 234 235 my $pid_file = server_pid_file($server); 236 my $pid = read_pid($pid_file); 237 238 return unless defined($pid); 239 240 # If we're here, the PID file hasn't been cleaned up yet 241 if (send_signal(0, $pid) == 0) { 242 print "I:$test:$server crashed on shutdown\n"; 243 $errors = 1; 244 return; 245 } 246 247 return $server; 248} 249 250sub wait_for_servers { 251 my ( $timeout, @servers ) = @_; 252 253 while ($timeout > 0 && @servers > 0) { 254 sleep 1 if (@servers > 0); 255 @servers = 256 grep { defined($_) } 257 map { pid_file_exists($_) } @servers; 258 $timeout--; 259 } 260 261 return @servers; 262} 263