]>
Commit | Line | Data |
---|---|---|
6c9a5e8d AR |
1 | #!/bin/sh |
2 | # | |
b8ae064d | 3 | ## Copyright (C) 1996-2023 The Squid Software Foundation and contributors |
6c9a5e8d AR |
4 | ## |
5 | ## Squid software is distributed under GPLv2+ license and includes | |
6 | ## contributions from numerous individuals and organizations. | |
7 | ## Please see the COPYING and CONTRIBUTORS files for details. | |
8 | ## | |
9 | ||
10 | # Orchestrates a "squid -k parse ..." test of a single Squid configuration | |
11 | # file (with an optional .instructions file containing testing directions). | |
cd3dc147 | 12 | # Usage: test-squid-conf.sh <top_builddir> <sbindir> <squid.conf> |
6c9a5e8d AR |
13 | |
14 | top_builddir=$1 | |
cd3dc147 AR |
15 | sbindir=$2 |
16 | configFile=$3 | |
6c9a5e8d | 17 | |
2bbaef0e AR |
18 | # If set to yes, expect non-zero Squid exit code, |
19 | # with stderr output matching $messageRegex. | |
20 | expectFailure=no | |
21 | ||
22 | # If set, expect a matching stderr message | |
23 | messageRegex="" | |
24 | ||
0898d0f4 AR |
25 | # If set, expect stderr messages matching regexes in the named file. |
26 | # See expectMessages() and matchEachRegex(). | |
27 | messageRegexFilename="" | |
28 | ||
2bbaef0e AR |
29 | expectMessage() |
30 | { | |
31 | local p1="$1" | |
32 | local p2="$2" | |
33 | local where="$3" | |
34 | ||
35 | if test -n "$messageRegex" | |
36 | then | |
37 | echo "$where: ERROR: Repeated message-setting instruction"; | |
38 | exit 1 | |
39 | fi | |
40 | ||
41 | messageRegex="$p1" | |
42 | ||
43 | if test -n "$p2" | |
44 | then | |
45 | echo "$where: ERROR: Bad message-setting instruction: Unexpected second parameter: $p2" | |
46 | exit 1 | |
47 | fi | |
48 | } | |
12cac459 | 49 | |
0898d0f4 AR |
50 | # Starts expecting Squid stderr lines that match configured regular |
51 | # expressions. Expressions are read from standard input, one extended regex | |
52 | # per line, until a line matching here-document terminator in $p1 is read. | |
53 | # Empty lines are ignored. | |
54 | expectMessages() | |
55 | { | |
56 | local p1="$1" | |
57 | local p2="$2" | |
58 | local where="$3" | |
59 | ||
60 | if test -n "$messageRegexFilename" | |
61 | then | |
62 | echo "$where: ERROR: Repeated message-setting instruction" | |
63 | exit 1 | |
64 | fi | |
65 | ||
66 | if test -z "$p1" | |
67 | then | |
68 | echo "$where: ERROR: Missing here-doc terminator" | |
69 | exit 1 | |
70 | fi | |
71 | local heredocTerminator="$p1" | |
72 | ||
73 | if test -n "$p2" | |
74 | then | |
75 | echo "$where: ERROR: Bad here-doc: Unexpected input after '$terminator' terminator: $p2" | |
76 | exit 1 | |
77 | fi | |
78 | ||
79 | messageRegexFilename="squid-expected-messages" | |
80 | if ! :> $messageRegexFilename | |
81 | then | |
82 | echo "$where: ERROR: Cannot create a temporary file named $messageRegexFilename" | |
83 | exit 1 | |
84 | fi | |
85 | ||
86 | local foundTerminator=0; | |
87 | while read hereDocLine | |
88 | do | |
89 | lineNo=$(($lineNo+1)) | |
90 | where="$instructionsFile:$lineNo"; | |
91 | ||
92 | if test "<<$hereDocLine" = "$heredocTerminator" | |
93 | then | |
94 | foundTerminator=1 | |
95 | break; | |
96 | fi | |
97 | ||
98 | # skip empty lines; they cannot be used as regexes and they improve | |
99 | # here-document formatting | |
100 | if test -z "$hereDocLine" | |
101 | then | |
102 | continue; | |
103 | fi | |
104 | ||
105 | if ! printf '%s\n' "$hereDocLine" >> $messageRegexFilename | |
106 | then | |
107 | echo "$where: ERROR: Cannot write to a temporary file named $messageRegexFilename" | |
108 | exit 1 | |
109 | fi | |
110 | done | |
111 | ||
112 | if test $foundTerminator != 1 | |
113 | then | |
114 | echo "$where: ERROR: Input ended before here-doc terminator ($heredocTerminator)" | |
115 | exit 1 | |
116 | fi | |
117 | } | |
118 | ||
119 | # Checks that each of the extended regexes (in the given file) matches line(s) | |
120 | # in the given Squid log file. A log line can match at most once. | |
121 | matchEachRegex() | |
122 | { | |
123 | local regexFilename="$1" | |
124 | local errLog="$2" | |
125 | ||
126 | local errorLogRemaining="$errorLog.unmatched"; | |
127 | local errorLogNext="$errorLog.next"; | |
128 | ||
129 | if ! cp $errorLog $errorLogRemaining | |
130 | then | |
131 | echo "ERROR: Cannot create a temporary file named $errorLogRemaining" | |
132 | exit 1 | |
133 | fi | |
134 | ||
135 | local result=0 | |
136 | while read regex | |
137 | do | |
138 | if grep -q -E "$regex" $errorLogRemaining | |
139 | then | |
140 | # No good way to distinguish a possible lack of "grep -v" matches | |
141 | # from grep errors because both result in non-zero grep exit code. | |
142 | # For now, assume that error log always has some "extra" lines, | |
143 | # guaranteeing at least one "grep -v non-empty-regex" match. | |
144 | if ! grep -v -E "$regex" $errorLogRemaining > $errorLogNext || ! mv $errorLogNext $errorLogRemaining | |
145 | then | |
146 | echo "ERROR: Temporary file manipulation failure" | |
147 | exit 1 | |
148 | fi | |
149 | else | |
150 | echo "ERROR: Squid did not emit an expected message to stderr" | |
151 | echo " expected message regex: $regex" | |
152 | result=1 | |
153 | fi | |
154 | done < $regexFilename | |
155 | ||
156 | if test $result != 0 | |
157 | then | |
158 | echo "Unmatched Squid stderr lines (see $errorLogRemaining):" | |
159 | cat $errorLogRemaining | |
160 | fi | |
161 | ||
162 | return $result | |
163 | } | |
164 | ||
6c9a5e8d AR |
165 | instructionsFile="$configFile.instructions" |
166 | if test -e $instructionsFile | |
167 | then | |
168 | lineNo=0 | |
169 | while read instructionName p1 p2 | |
170 | do | |
171 | lineNo=$(($lineNo+1)) | |
172 | here="$instructionsFile:$lineNo"; | |
173 | ||
174 | if test -z "$instructionName" | |
175 | then | |
176 | continue; # skip empty lines | |
177 | fi | |
178 | ||
179 | if test "$instructionName" = "#" | |
180 | then | |
181 | continue; # skip comment lines | |
182 | fi | |
183 | ||
12cac459 AR |
184 | if test "$instructionName" = "expect-failure" |
185 | then | |
2bbaef0e AR |
186 | expectFailure=yes |
187 | expectMessage "$p1" "$p2" "$here" | |
188 | continue; | |
189 | fi | |
12cac459 | 190 | |
2bbaef0e AR |
191 | if test "$instructionName" = "expect-message" |
192 | then | |
193 | expectMessage "$p1" "$p2" "$here" | |
12cac459 AR |
194 | continue; |
195 | fi | |
196 | ||
0898d0f4 AR |
197 | if test "$instructionName" = "expect-messages" |
198 | then | |
199 | expectMessages "$p1" "$p2" "$here" | |
200 | continue; | |
201 | fi | |
202 | ||
6c9a5e8d AR |
203 | if test "$instructionName" = "skip-unless-autoconf-defines" |
204 | then | |
205 | # Skip test unless the given macro is #defined in autoconf.h | |
206 | defineName=$p1 | |
207 | ||
208 | if test -n "$p2" | |
209 | then | |
210 | echo "$here: ERROR: Bad $instructionName instruction: Unexpected second parameter: $p2"; | |
211 | exit 1; | |
212 | fi | |
213 | ||
214 | autoconfHeader="$top_builddir/include/autoconf.h" | |
215 | if ! grep -q -w "$defineName" $autoconfHeader | |
216 | then | |
217 | echo "$here: ERROR: Bad $instructionName instruction: Unknown macro $defineName"; | |
218 | exit 1; | |
219 | fi | |
220 | ||
221 | if grep -q "# *undef *\b$defineName\b" $autoconfHeader | |
222 | then | |
223 | echo "$here: WARNING: Skipping $configFile test because $defineName is not defined in $autoconfHeader"; | |
224 | exit 0; | |
225 | fi | |
226 | ||
227 | if ! grep -q "# *define *\b$defineName\b" $autoconfHeader | |
228 | then | |
229 | echo "$here: ERROR: Cannot determine status of $defineName macro"; | |
230 | exit 1; | |
231 | fi | |
232 | else | |
233 | echo "$here: ERROR: Unknown test-squid-conf.sh instruction name: $instructionName"; | |
234 | exit 1; | |
235 | fi | |
236 | done < $instructionsFile | |
12cac459 AR |
237 | fi |
238 | ||
239 | errorLog="squid-stderr.log" | |
6c9a5e8d | 240 | |
12cac459 AR |
241 | $sbindir/squid -k parse -f $configFile 2> $errorLog |
242 | result=$? | |
243 | ||
2bbaef0e AR |
244 | # this is the value we return to our caller; |
245 | # must be set by the code below using updateOutcome | |
246 | exitCode="" | |
247 | updateOutcome() | |
248 | { | |
249 | local newOutcome="$1" | |
250 | # never overwrite non-zero values (i.e. only overwrite null and zero) | |
251 | if test -z "$exitCode" -o "$exitCode" = 0 | |
252 | then | |
253 | exitCode="$newOutcome" | |
254 | fi | |
255 | } | |
256 | ||
257 | if test -n "$messageRegex" && ! grep -q -E "$messageRegex" $errorLog | |
258 | then | |
259 | echo "ERROR: Squid did not emit an expected message to stderr" | |
260 | echo " expected message regex: $messageRegex" | |
261 | updateOutcome 1 | |
262 | fi | |
263 | ||
0898d0f4 AR |
264 | if test -n "$messageRegexFilename" && ! matchEachRegex $messageRegexFilename $errorLog |
265 | then | |
266 | # matchEachRegex reports errors | |
267 | updateOutcome 1 | |
268 | fi | |
269 | ||
2bbaef0e | 270 | if test $expectFailure = no |
12cac459 | 271 | then |
2bbaef0e AR |
272 | if test "$result" -ne 0 |
273 | then | |
274 | echo "ERROR: Squid rejected valid $configFile; Squid exit code: $result" | |
275 | updateOutcome $result | |
276 | else | |
277 | # stay silent about ordinary success | |
278 | updateOutcome 0 | |
279 | fi | |
280 | else | |
281 | if test "$result" -eq 0 | |
282 | then | |
283 | echo "ERROR: Squid successfully parsed malformed $configFile instead of rejecting it" | |
284 | updateOutcome 1 | |
285 | else | |
286 | # stay silent about this expected failure (invisible in our output) | |
287 | #echo "Squid rejected malformed $configFile as expected; Squid exit code: $result" | |
288 | updateOutcome 0 | |
289 | fi | |
6c9a5e8d AR |
290 | fi |
291 | ||
2bbaef0e | 292 | if test -z "$exitCode" |
12cac459 | 293 | then |
2bbaef0e AR |
294 | echo "ERROR: BUG: Forgot to set \$exitCode: $0" |
295 | updateOutcome 1 | |
12cac459 AR |
296 | fi |
297 | ||
2bbaef0e AR |
298 | # after a bad outcome, share Squid output |
299 | if test $exitCode -ne 0 | |
12cac459 | 300 | then |
12cac459 AR |
301 | echo "Squid stderr output:" |
302 | cat $errorLog | |
12cac459 AR |
303 | fi |
304 | ||
2bbaef0e | 305 | exit $exitCode |
12cac459 | 306 |