xref: /netbsd-src/external/mpl/bind/dist/bin/tests/system/stop.pl (revision 32d1c65c71fbdb65a012e8392a62a757dd6853e9)
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(30, @ns);
102}
103
104# Pass 2: SIGTERM
105foreach my $name (@ns) {
106	stop_signal($name, "TERM");
107}
108
109@ns = wait_for_servers(60, @ns);
110
111foreach my $name(@ans) {
112	stop_signal($name, "TERM", 1);
113}
114
115@ans = wait_for_servers(1200, @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 lock file.
134sub server_lock_file {
135	my ( $server ) = @_;
136
137	return $testdir . "/" . $server . "/named.lock" if ($server =~ /^ns/);
138	return if ($server =~ /^ans/);
139
140	die "Unknown server type $server\n";
141}
142
143# Return the full path to a given server's PID file.
144sub server_pid_file {
145	my ( $server ) = @_;
146
147	return $testdir . "/" . $server . "/named.pid" if ($server =~ /^ns/);
148	return $testdir . "/" . $server . "/ans.pid" if ($server =~ /^ans/);
149
150	die "Unknown server type $server\n";
151}
152
153# Read a PID.
154sub read_pid {
155	my ( $pid_file ) = @_;
156
157	return unless -f $pid_file;
158	# we don't really care about the race condition here
159	my $result = open(my $fh, "<", $pid_file);
160	if (!defined($result)) {
161		print "I:$test:$pid_file: $!\n";
162		unlink $pid_file;
163		return;
164	}
165
166	my $pid = <$fh>;
167	return unless defined($pid);
168
169	chomp($pid);
170	return $pid;
171}
172
173# Stop a named process with rndc.
174sub stop_rndc {
175	my ( $server, $port ) = @_;
176	my $n;
177
178	if ($server =~ /^ns(\d+)/) {
179		$n = $1;
180	} else {
181		die "unable to parse server number from name \"$server\"\n";
182	}
183
184	my $ip = "10.53.0.$n";
185	if (-e "$testdir/$server/named.ipv6-only") {
186		$ip = "fd92:7065:b8e:ffff::$n";
187	}
188
189	my $how = $halt ? "halt" : "stop";
190
191	# Ugly, but should work.
192	system("$RNDC -c ../_common/rndc.conf -s $ip -p $port $how | sed 's/^/I:$test:$server /'");
193	return;
194}
195
196sub server_died {
197	my ( $server, $signal ) = @_;
198
199	print "I:$test:$server died before a SIG$signal was sent\n";
200	$errors = 1;
201
202	my $pid_file = server_pid_file($server);
203	unlink($pid_file);
204
205	return;
206}
207
208sub send_signal {
209	my ( $signal, $pid, $ans ) = @_;
210
211	if (! defined $ans) {
212		$ans = 0;
213	}
214
215	my $result = 0;
216
217	$result = kill $signal, $pid;
218	return $result;
219}
220
221# Stop a server by sending a signal to it.
222sub stop_signal {
223	my ( $server, $signal, $ans ) = @_;
224	if (! defined $ans) {
225		$ans = 0;
226	}
227
228	my $pid_file = server_pid_file($server);
229	my $pid = read_pid($pid_file);
230
231	return unless defined($pid);
232
233	# Send signal to the server, and bail out if signal can't be sent
234	if (send_signal($signal, $pid, $ans) != 1) {
235		server_died($server, $signal);
236		return;
237	}
238
239	return;
240}
241
242sub pid_file_exists {
243	my ( $server ) = @_;
244
245	my $pid_file = server_pid_file($server);
246	my $pid = read_pid($pid_file);
247
248	return unless defined($pid);
249
250	# If we're here, the PID file hasn't been cleaned up yet
251	if (send_signal(0, $pid) == 0) {
252		print "I:$test:$server crashed on shutdown\n";
253		$errors = 1;
254		return;
255	}
256
257	return $server;
258}
259
260sub lock_file_exists {
261	my ( $server ) = @_;
262	my $lock_file = server_lock_file($server);
263
264	return unless defined($lock_file) && -f $lock_file;
265
266	return $server;
267}
268
269sub wait_for_servers {
270	my ( $timeout, @servers ) = @_;
271
272	while ($timeout > 0 && @servers > 0) {
273		sleep 1 if (@servers > 0);
274		@servers =
275			grep { defined($_) }
276			map  { pid_file_exists($_) || lock_file_exists($_) } @servers;
277		$timeout--;
278	}
279
280	return @servers;
281}
282