| | varnish-cache/bin/varnishtest/vtc_main.c |
0 |
|
/*- |
1 |
|
* Copyright (c) 2008-2011 Varnish Software AS |
2 |
|
* All rights reserved. |
3 |
|
* |
4 |
|
* Author: Poul-Henning Kamp <phk@phk.freebsd.dk> |
5 |
|
* |
6 |
|
* SPDX-License-Identifier: BSD-2-Clause |
7 |
|
* |
8 |
|
* Redistribution and use in source and binary forms, with or without |
9 |
|
* modification, are permitted provided that the following conditions |
10 |
|
* are met: |
11 |
|
* 1. Redistributions of source code must retain the above copyright |
12 |
|
* notice, this list of conditions and the following disclaimer. |
13 |
|
* 2. Redistributions in binary form must reproduce the above copyright |
14 |
|
* notice, this list of conditions and the following disclaimer in the |
15 |
|
* documentation and/or other materials provided with the distribution. |
16 |
|
* |
17 |
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
18 |
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
19 |
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
20 |
|
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE |
21 |
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
22 |
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
23 |
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
24 |
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
25 |
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
26 |
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
27 |
|
* SUCH DAMAGE. |
28 |
|
*/ |
29 |
|
|
30 |
|
#include "config.h" |
31 |
|
|
32 |
|
#include <sys/mman.h> |
33 |
|
#include <sys/socket.h> |
34 |
|
#include <sys/stat.h> |
35 |
|
#include <sys/wait.h> |
36 |
|
|
37 |
|
#include <ctype.h> |
38 |
|
#include <dirent.h> |
39 |
|
#include <poll.h> |
40 |
|
#include <stdio.h> |
41 |
|
#include <stdlib.h> |
42 |
|
#include <string.h> |
43 |
|
#include <unistd.h> |
44 |
|
|
45 |
|
#include "vtc.h" |
46 |
|
|
47 |
|
#include "vev.h" |
48 |
|
#include "vfil.h" |
49 |
|
#include "vnum.h" |
50 |
|
#include "vrnd.h" |
51 |
|
#include "vsa.h" |
52 |
|
#include "vss.h" |
53 |
|
#include "vsub.h" |
54 |
|
#include "vtcp.h" |
55 |
|
#include "vtim.h" |
56 |
|
#include "vct.h" |
57 |
|
|
58 |
|
static const char *argv0; |
59 |
|
|
60 |
|
struct buf { |
61 |
|
unsigned magic; |
62 |
|
#define BUF_MAGIC 0x39d1258a |
63 |
|
VTAILQ_ENTRY(buf) list; |
64 |
|
char *buf; |
65 |
|
struct vsb *diag; |
66 |
|
size_t bufsiz; |
67 |
|
}; |
68 |
|
|
69 |
|
static VTAILQ_HEAD(, buf) free_bufs = VTAILQ_HEAD_INITIALIZER(free_bufs); |
70 |
|
|
71 |
|
struct vtc_tst { |
72 |
|
unsigned magic; |
73 |
|
#define TST_MAGIC 0x618d8b88 |
74 |
|
VTAILQ_ENTRY(vtc_tst) list; |
75 |
|
const char *filename; |
76 |
|
char *script; |
77 |
|
unsigned ntodo; |
78 |
|
unsigned nwait; |
79 |
|
}; |
80 |
|
|
81 |
|
struct vtc_job { |
82 |
|
unsigned magic; |
83 |
|
#define JOB_MAGIC 0x1b5fc419 |
84 |
|
struct vtc_tst *tst; |
85 |
|
pid_t child; |
86 |
|
struct vev *ev; |
87 |
|
struct vev *evt; |
88 |
|
struct buf *bp; |
89 |
|
char *tmpdir; |
90 |
|
double t0; |
91 |
|
int killed; |
92 |
|
}; |
93 |
|
|
94 |
|
|
95 |
|
int iflg = 0; |
96 |
|
vtim_dur vtc_maxdur = 60; |
97 |
|
static unsigned vtc_bufsiz = 1024 * 1024; |
98 |
|
|
99 |
|
static VTAILQ_HEAD(, vtc_tst) tst_head = VTAILQ_HEAD_INITIALIZER(tst_head); |
100 |
|
static struct vev_root *vb; |
101 |
|
static int njob = 0; |
102 |
|
static int npar = 1; /* Number of parallel tests */ |
103 |
|
static int vtc_continue; /* Continue on error */ |
104 |
|
static int vtc_verbosity = 1; /* Verbosity Level */ |
105 |
|
static int vtc_good; |
106 |
|
static int vtc_fail; |
107 |
|
static int vtc_skip; |
108 |
|
static char *tmppath; |
109 |
|
static char *cwd = NULL; |
110 |
|
char *vmod_path = NULL; |
111 |
|
struct vsb *params_vsb = NULL; |
112 |
|
int leave_temp; |
113 |
|
static struct vsb *cbvsb; |
114 |
|
static int bad_backend_fd; |
115 |
|
|
116 |
|
static int cleaner_fd = -1; |
117 |
|
static pid_t cleaner_pid; |
118 |
|
const char *default_listen_addr; |
119 |
|
|
120 |
|
static struct buf * |
121 |
40160 |
get_buf(void) |
122 |
|
{ |
123 |
|
struct buf *bp; |
124 |
|
|
125 |
40160 |
bp = VTAILQ_FIRST(&free_bufs); |
126 |
40160 |
CHECK_OBJ_ORNULL(bp, BUF_MAGIC); |
127 |
40160 |
if (bp != NULL) { |
128 |
1160 |
VTAILQ_REMOVE(&free_bufs, bp, list); |
129 |
1160 |
VSB_clear(bp->diag); |
130 |
1160 |
} else { |
131 |
39000 |
ALLOC_OBJ(bp, BUF_MAGIC); |
132 |
39000 |
AN(bp); |
133 |
39000 |
bp->bufsiz = vtc_bufsiz; |
134 |
39000 |
bp->buf = mmap(NULL, bp->bufsiz, PROT_READ|PROT_WRITE, |
135 |
|
MAP_ANON | MAP_SHARED, -1, 0); |
136 |
39000 |
assert(bp->buf != MAP_FAILED); |
137 |
39000 |
bp->diag = VSB_new_auto(); |
138 |
39000 |
AN(bp->diag); |
139 |
|
} |
140 |
40160 |
memset(bp->buf, 0, bp->bufsiz); |
141 |
40160 |
return (bp); |
142 |
|
} |
143 |
|
|
144 |
|
static void |
145 |
40160 |
rel_buf(struct buf **bp) |
146 |
|
{ |
147 |
40160 |
CHECK_OBJ_NOTNULL(*bp, BUF_MAGIC); |
148 |
|
|
149 |
40160 |
VTAILQ_INSERT_HEAD(&free_bufs, (*bp), list); |
150 |
40160 |
*bp = NULL; |
151 |
40160 |
} |
152 |
|
|
153 |
|
/********************************************************************** |
154 |
|
* Parse a -D option argument into a name/val pair, and insert |
155 |
|
* into extmacro list |
156 |
|
*/ |
157 |
|
|
158 |
|
static int |
159 |
80 |
parse_D_opt(char *arg) |
160 |
|
{ |
161 |
|
char *p, *q; |
162 |
|
|
163 |
80 |
p = arg; |
164 |
80 |
q = strchr(p, '='); |
165 |
80 |
if (!q) |
166 |
0 |
return (0); |
167 |
80 |
*q++ = '\0'; |
168 |
80 |
extmacro_def(p, NULL, "%s", q); |
169 |
|
|
170 |
80 |
return (1); |
171 |
80 |
} |
172 |
|
|
173 |
|
/********************************************************************** |
174 |
|
* Print usage |
175 |
|
*/ |
176 |
|
|
177 |
|
static void v_noreturn_ |
178 |
200 |
usage(void) |
179 |
|
{ |
180 |
200 |
fprintf(stderr, "usage: %s [options] file ...\n", argv0); |
181 |
|
#define FMT " %-28s # %s\n" |
182 |
200 |
fprintf(stderr, FMT, "-b size", |
183 |
|
"Set internal buffer size (default: 1M)"); |
184 |
200 |
fprintf(stderr, FMT, "-C", "Use cleaner subprocess"); |
185 |
200 |
fprintf(stderr, FMT, "-D name=val", "Define macro"); |
186 |
200 |
fprintf(stderr, FMT, "-i", "Find varnish binaries in build tree"); |
187 |
200 |
fprintf(stderr, FMT, "-j jobs", "Run this many tests in parallel"); |
188 |
200 |
fprintf(stderr, FMT, "-k", "Continue on test failure"); |
189 |
200 |
fprintf(stderr, FMT, "-L", "Always leave temporary vtc.*"); |
190 |
200 |
fprintf(stderr, FMT, "-l", "Leave temporary vtc.* if test fails"); |
191 |
200 |
fprintf(stderr, FMT, "-n iterations", "Run tests this many times"); |
192 |
200 |
fprintf(stderr, FMT, "-p name=val", "Pass a varnishd parameter"); |
193 |
200 |
fprintf(stderr, FMT, "-q", "Quiet mode: report only failures"); |
194 |
200 |
fprintf(stderr, FMT, "-t duration", "Time tests out after this long"); |
195 |
200 |
fprintf(stderr, FMT, "-v", "Verbose mode: always report test log"); |
196 |
200 |
exit(1); |
197 |
|
} |
198 |
|
|
199 |
|
/********************************************************************** |
200 |
|
* When running many tests, cleaning the tmpdir with "rm -rf" becomes |
201 |
|
* chore which limits our performance. |
202 |
|
* When the number of tests are above 100, we spawn a child-process |
203 |
|
* to do that for us. |
204 |
|
*/ |
205 |
|
|
206 |
|
static void |
207 |
40160 |
cleaner_do(const char *dirname) |
208 |
|
{ |
209 |
|
char buf[BUFSIZ]; |
210 |
|
|
211 |
40160 |
AZ(memcmp(dirname, tmppath, strlen(tmppath))); |
212 |
40160 |
if (cleaner_pid > 0) { |
213 |
1360 |
bprintf(buf, "%s\n", dirname); |
214 |
1360 |
assert(write(cleaner_fd, buf, strlen(buf)) == strlen(buf)); |
215 |
1360 |
return; |
216 |
|
} |
217 |
38800 |
bprintf(buf, "exec /bin/rm -rf %s\n", dirname); |
218 |
38800 |
AZ(system(buf)); |
219 |
40160 |
} |
220 |
|
|
221 |
|
static void |
222 |
80 |
cleaner_setup(void) |
223 |
|
{ |
224 |
|
int p[2], st; |
225 |
|
char buf[BUFSIZ]; |
226 |
|
char *q; |
227 |
|
pid_t pp; |
228 |
|
|
229 |
80 |
AZ(pipe(p)); |
230 |
80 |
assert(p[0] > STDERR_FILENO); |
231 |
80 |
assert(p[1] > STDERR_FILENO); |
232 |
80 |
cleaner_pid = fork(); |
233 |
80 |
assert(cleaner_pid >= 0); |
234 |
160 |
if (cleaner_pid == 0) { |
235 |
80 |
closefd(&p[1]); |
236 |
80 |
(void)nice(1); /* Not important */ |
237 |
80 |
setbuf(stdin, NULL); |
238 |
80 |
AZ(dup2(p[0], STDIN_FILENO)); |
239 |
1440 |
while (fgets(buf, sizeof buf, stdin)) { |
240 |
1360 |
AZ(memcmp(buf, tmppath, strlen(tmppath))); |
241 |
1360 |
q = buf + strlen(buf); |
242 |
1360 |
assert(q > buf); |
243 |
1360 |
assert(q[-1] == '\n'); |
244 |
1360 |
q[-1] = '\0'; |
245 |
|
|
246 |
|
/* Dont expend a shell on running /bin/rm */ |
247 |
1360 |
pp = fork(); |
248 |
1360 |
assert(pp >= 0); |
249 |
2720 |
if (pp == 0) |
250 |
1360 |
exit(execlp( |
251 |
1360 |
"rm", "rm", "-rf", buf, (char*)0)); |
252 |
1360 |
assert(waitpid(pp, &st, 0) == pp); |
253 |
1360 |
AZ(st); |
254 |
|
} |
255 |
80 |
exit(0); |
256 |
|
} |
257 |
80 |
closefd(&p[0]); |
258 |
80 |
cleaner_fd = p[1]; |
259 |
80 |
} |
260 |
|
|
261 |
|
static void |
262 |
40160 |
cleaner_neuter(void) |
263 |
|
{ |
264 |
40160 |
if (cleaner_pid > 0) |
265 |
1360 |
closefd(&cleaner_fd); |
266 |
40160 |
} |
267 |
|
|
268 |
|
static void |
269 |
38880 |
cleaner_finish(void) |
270 |
|
{ |
271 |
|
int st; |
272 |
|
|
273 |
38880 |
if (cleaner_pid > 0) { |
274 |
80 |
closefd(&cleaner_fd); |
275 |
80 |
assert(waitpid(cleaner_pid, &st, 0) == cleaner_pid); |
276 |
80 |
AZ(st); |
277 |
80 |
} |
278 |
38880 |
} |
279 |
|
|
280 |
|
/********************************************************************** |
281 |
|
* CallBack |
282 |
|
*/ |
283 |
|
|
284 |
|
static int |
285 |
40482 |
tst_cb(const struct vev *ve, int what) |
286 |
|
{ |
287 |
|
struct vtc_job *jp; |
288 |
|
char buf[BUFSIZ]; |
289 |
|
int ecode; |
290 |
|
int i, stx; |
291 |
|
pid_t px; |
292 |
|
double t; |
293 |
|
FILE *f; |
294 |
|
char *p; |
295 |
|
|
296 |
40482 |
CAST_OBJ_NOTNULL(jp, ve->priv, JOB_MAGIC); |
297 |
40482 |
CHECK_OBJ_NOTNULL(jp->tst, TST_MAGIC); |
298 |
|
|
299 |
|
// printf("CB %p %s %d\n", ve, jp->tst->filename, what); |
300 |
40482 |
if (what == 0) { |
301 |
0 |
jp->killed = 1; |
302 |
0 |
AZ(kill(-jp->child, SIGKILL)); /* XXX: Timeout */ |
303 |
0 |
} else { |
304 |
40482 |
assert(what & (VEV__RD | VEV__HUP)); |
305 |
|
} |
306 |
|
|
307 |
40482 |
*buf = '\0'; |
308 |
40482 |
i = read(ve->fd, buf, sizeof buf); |
309 |
40482 |
if (i > 0) |
310 |
322 |
VSB_bcat(jp->bp->diag, buf, i); |
311 |
40482 |
if (i == 0) { |
312 |
|
|
313 |
40160 |
njob--; |
314 |
40160 |
px = wait4(jp->child, &stx, 0, NULL); |
315 |
40160 |
assert(px == jp->child); |
316 |
40160 |
t = VTIM_mono() - jp->t0; |
317 |
40160 |
AZ(close(ve->fd)); |
318 |
|
|
319 |
40160 |
ecode = WTERMSIG(stx); |
320 |
40160 |
if (ecode == 0) |
321 |
40160 |
ecode = WEXITSTATUS(stx); |
322 |
|
|
323 |
40160 |
AZ(VSB_finish(jp->bp->diag)); |
324 |
|
|
325 |
40160 |
VSB_clear(cbvsb); |
326 |
40160 |
VSB_cat(cbvsb, jp->bp->buf); |
327 |
40160 |
p = strchr(jp->bp->buf, '\0'); |
328 |
40160 |
if (p > jp->bp->buf && p[-1] != '\n') |
329 |
0 |
VSB_putc(cbvsb, '\n'); |
330 |
80320 |
VSB_quote_pfx(cbvsb, "* diag 0.0 ", |
331 |
40160 |
VSB_data(jp->bp->diag), -1, VSB_QUOTE_NONL); |
332 |
40160 |
AZ(VSB_finish(cbvsb)); |
333 |
40160 |
rel_buf(&jp->bp); |
334 |
|
|
335 |
40160 |
if ((ecode > 1 && vtc_verbosity) || vtc_verbosity > 1) |
336 |
39120 |
printf("%s", VSB_data(cbvsb)); |
337 |
|
|
338 |
40160 |
if (!ecode) |
339 |
39480 |
vtc_good++; |
340 |
680 |
else if (ecode == 1) |
341 |
600 |
vtc_skip++; |
342 |
|
else |
343 |
80 |
vtc_fail++; |
344 |
|
|
345 |
40160 |
if (leave_temp == 0 || (leave_temp == 1 && ecode <= 1)) { |
346 |
40160 |
cleaner_do(jp->tmpdir); |
347 |
40160 |
} else { |
348 |
0 |
bprintf(buf, "%s/LOG", jp->tmpdir); |
349 |
0 |
f = fopen(buf, "w"); |
350 |
0 |
AN(f); |
351 |
0 |
(void)fprintf(f, "%s\n", VSB_data(cbvsb)); |
352 |
0 |
AZ(fclose(f)); |
353 |
|
} |
354 |
40160 |
free(jp->tmpdir); |
355 |
|
|
356 |
40160 |
if (jp->killed) |
357 |
0 |
printf("# top TEST %s TIMED OUT (kill -9)\n", |
358 |
0 |
jp->tst->filename); |
359 |
40160 |
if (ecode > 1) { |
360 |
80 |
printf("# top TEST %s FAILED (%.3f)", |
361 |
80 |
jp->tst->filename, t); |
362 |
80 |
if (WIFSIGNALED(stx)) |
363 |
0 |
printf(" signal=%d\n", WTERMSIG(stx)); |
364 |
80 |
else if (WIFEXITED(stx)) |
365 |
80 |
printf(" exit=%d\n", WEXITSTATUS(stx)); |
366 |
80 |
if (!vtc_continue) { |
367 |
|
/* XXX kill -9 other jobs ? */ |
368 |
80 |
exit(2); |
369 |
|
} |
370 |
40080 |
} else if (vtc_verbosity) { |
371 |
40000 |
printf("# top TEST %s %s (%.3f)\n", |
372 |
40000 |
jp->tst->filename, |
373 |
40000 |
ecode ? "skipped" : "passed", t); |
374 |
40000 |
} |
375 |
40080 |
if (jp->evt != NULL) { |
376 |
40080 |
VEV_Stop(vb, jp->evt); |
377 |
40080 |
free(jp->evt); |
378 |
40080 |
} |
379 |
40440 |
jp->tst->nwait--; |
380 |
40440 |
if (jp->tst->nwait == 0) { |
381 |
39720 |
free(jp->tst->script); |
382 |
39720 |
FREE_OBJ(jp->tst); |
383 |
39720 |
} |
384 |
40080 |
FREE_OBJ(jp); |
385 |
40080 |
return (1); |
386 |
|
} |
387 |
322 |
return (0); |
388 |
40402 |
} |
389 |
|
|
390 |
|
/********************************************************************** |
391 |
|
* Start Test |
392 |
|
*/ |
393 |
|
|
394 |
|
static void |
395 |
40160 |
start_test(void) |
396 |
|
{ |
397 |
|
struct vtc_tst *tp; |
398 |
|
int p[2], retval; |
399 |
|
struct vtc_job *jp; |
400 |
|
char tmpdir[PATH_MAX]; |
401 |
|
|
402 |
40160 |
ALLOC_OBJ(jp, JOB_MAGIC); |
403 |
40160 |
AN(jp); |
404 |
|
|
405 |
40160 |
jp->bp = get_buf(); |
406 |
|
|
407 |
40160 |
bprintf(tmpdir, "%s/vtc.%d.%08x", tmppath, (int)getpid(), |
408 |
|
(unsigned)random()); |
409 |
40160 |
AZ(mkdir(tmpdir, 0755)); |
410 |
|
|
411 |
40160 |
tp = VTAILQ_FIRST(&tst_head); |
412 |
40160 |
CHECK_OBJ_NOTNULL(tp, TST_MAGIC); |
413 |
40160 |
AN(tp->ntodo); |
414 |
40160 |
tp->ntodo--; |
415 |
40160 |
VTAILQ_REMOVE(&tst_head, tp, list); |
416 |
40160 |
if (tp->ntodo > 0) |
417 |
360 |
VTAILQ_INSERT_TAIL(&tst_head, tp, list); |
418 |
|
|
419 |
40160 |
jp->tst = tp; |
420 |
40160 |
REPLACE(jp->tmpdir, tmpdir); |
421 |
|
|
422 |
40160 |
AZ(pipe(p)); |
423 |
40160 |
assert(p[0] > STDERR_FILENO); |
424 |
40160 |
assert(p[1] > STDERR_FILENO); |
425 |
40160 |
jp->t0 = VTIM_mono(); |
426 |
40160 |
jp->child = fork(); |
427 |
40160 |
assert(jp->child >= 0); |
428 |
80320 |
if (jp->child == 0) { |
429 |
40160 |
cleaner_neuter(); // Too dangerous to have around |
430 |
40160 |
AZ(setpgid(getpid(), 0)); |
431 |
40160 |
VFIL_null_fd(STDIN_FILENO); |
432 |
40160 |
assert(dup2(p[1], STDOUT_FILENO) == STDOUT_FILENO); |
433 |
40160 |
assert(dup2(p[1], STDERR_FILENO) == STDERR_FILENO); |
434 |
40160 |
VSUB_closefrom(STDERR_FILENO + 1); |
435 |
80320 |
retval = exec_file(jp->tst->filename, jp->tst->script, |
436 |
40160 |
jp->tmpdir, jp->bp->buf, jp->bp->bufsiz); |
437 |
40160 |
exit(retval); |
438 |
|
} |
439 |
40160 |
closefd(&p[1]); |
440 |
|
|
441 |
40160 |
jp->ev = VEV_Alloc(); |
442 |
40160 |
AN(jp->ev); |
443 |
40160 |
jp->ev->fd_flags = VEV__RD | VEV__HUP | VEV__ERR; |
444 |
40160 |
jp->ev->fd = p[0]; |
445 |
40160 |
jp->ev->priv = jp; |
446 |
40160 |
jp->ev->callback = tst_cb; |
447 |
40160 |
AZ(VEV_Start(vb, jp->ev)); |
448 |
|
|
449 |
40160 |
jp->evt = VEV_Alloc(); |
450 |
40160 |
AN(jp->evt); |
451 |
40160 |
jp->evt->fd = -1; |
452 |
40160 |
jp->evt->timeout = vtc_maxdur; |
453 |
40160 |
jp->evt->priv = jp; |
454 |
40160 |
jp->evt->callback = tst_cb; |
455 |
40160 |
AZ(VEV_Start(vb, jp->evt)); |
456 |
40160 |
} |
457 |
|
|
458 |
|
/********************************************************************** |
459 |
|
* i-mode = "we're inside a src-tree" |
460 |
|
* |
461 |
|
* Find the abs path to top of source dir from Makefile, if that |
462 |
|
* fails, fall back on "../../" |
463 |
|
* |
464 |
|
* Set PATH to all programs build directories |
465 |
|
* Set vmod_path to all vmods build directories |
466 |
|
* |
467 |
|
*/ |
468 |
|
|
469 |
|
static char * |
470 |
77040 |
top_dir(const char *makefile, const char *top_var) |
471 |
|
{ |
472 |
|
const char *b, *e; |
473 |
|
char *var; |
474 |
|
|
475 |
77040 |
AN(makefile); |
476 |
77040 |
AN(top_var); |
477 |
77040 |
assert(*top_var == '\n'); |
478 |
|
|
479 |
77040 |
b = strstr(makefile, top_var); |
480 |
77040 |
top_var++; |
481 |
|
|
482 |
77040 |
if (b == NULL) { |
483 |
0 |
fprintf(stderr, "could not find '%s' in Makefile\n", top_var); |
484 |
0 |
return (NULL); |
485 |
|
} |
486 |
|
|
487 |
77040 |
e = strchr(b + 1, '\n'); |
488 |
77040 |
if (e == NULL) { |
489 |
0 |
fprintf(stderr, "No NL after '%s' in Makefile\n", top_var); |
490 |
0 |
return (NULL); |
491 |
|
} |
492 |
|
|
493 |
77040 |
b = memchr(b, '/', e - b); |
494 |
77040 |
if (b == NULL) { |
495 |
0 |
fprintf(stderr, "No '/' after '%s' in Makefile\n", top_var); |
496 |
0 |
return (NULL); |
497 |
|
} |
498 |
77040 |
var = strndup(b, e - b); |
499 |
77040 |
AN(var); |
500 |
77040 |
return (var); |
501 |
77040 |
} |
502 |
|
|
503 |
|
static void |
504 |
115560 |
build_path(const char *topdir, const char *subdir, |
505 |
|
const char *pfx, const char *sfx, struct vsb *vsb) |
506 |
|
{ |
507 |
|
char buf[PATH_MAX]; |
508 |
|
DIR *dir; |
509 |
|
struct dirent *de; |
510 |
|
struct stat st; |
511 |
115560 |
const char *topsep = "", *sep = ""; |
512 |
|
|
513 |
115560 |
if (*subdir != '\0') |
514 |
77040 |
topsep = "/"; |
515 |
115560 |
bprintf(buf, "%s%s%s/", topdir, topsep, subdir); |
516 |
115560 |
dir = opendir(buf); |
517 |
115560 |
XXXAN(dir); |
518 |
500760 |
while (1) { |
519 |
8821200 |
de = readdir(dir); |
520 |
8821200 |
if (de == NULL) |
521 |
115560 |
break; |
522 |
8705640 |
if (strncmp(de->d_name, pfx, strlen(pfx))) |
523 |
8320440 |
continue; |
524 |
385200 |
bprintf(buf, "%s%s%s/%s", topdir, topsep, subdir, de->d_name); |
525 |
385200 |
if (!stat(buf, &st) && S_ISDIR(st.st_mode)) { |
526 |
385200 |
VSB_cat(vsb, sep); |
527 |
385200 |
VSB_cat(vsb, buf); |
528 |
385200 |
VSB_cat(vsb, sfx); |
529 |
385200 |
sep = ":"; |
530 |
385200 |
} |
531 |
|
} |
532 |
115560 |
AZ(closedir(dir)); |
533 |
115560 |
} |
534 |
|
|
535 |
|
static void |
536 |
38520 |
i_mode(void) |
537 |
|
{ |
538 |
|
struct vsb *vsb; |
539 |
|
char *p, *topbuild, *topsrc; |
540 |
|
|
541 |
|
/* |
542 |
|
* This code has a rather intimate knowledge of auto* generated |
543 |
|
* makefiles. |
544 |
|
*/ |
545 |
|
|
546 |
38520 |
vsb = VSB_new_auto(); |
547 |
38520 |
AN(vsb); |
548 |
|
|
549 |
38520 |
p = VFIL_readfile(NULL, "Makefile", NULL); |
550 |
38520 |
if (p == NULL) { |
551 |
0 |
fprintf(stderr, "No Makefile to search for -i flag.\n"); |
552 |
0 |
exit(2); |
553 |
|
} |
554 |
|
|
555 |
38520 |
topbuild = top_dir(p, "\nabs_top_builddir"); |
556 |
38520 |
topsrc = top_dir(p, "\nabs_top_srcdir"); |
557 |
38520 |
free(p); |
558 |
38520 |
if (topbuild == NULL || topsrc == NULL) { |
559 |
0 |
free(topbuild); |
560 |
0 |
free(topsrc); |
561 |
0 |
exit(2); |
562 |
|
} |
563 |
38520 |
extmacro_def("topbuild", NULL, "%s", topbuild); |
564 |
38520 |
extmacro_def("topsrc", NULL, "%s", topsrc); |
565 |
|
|
566 |
|
/* |
567 |
|
* Build $PATH which can find all programs in the build tree |
568 |
|
*/ |
569 |
38520 |
VSB_clear(vsb); |
570 |
38520 |
VSB_cat(vsb, "PATH="); |
571 |
38520 |
build_path(topbuild, "bin", "varnish", "", vsb); |
572 |
|
#ifdef WITH_CONTRIB |
573 |
38520 |
VSB_putc(vsb, ':'); |
574 |
38520 |
build_path(topsrc, "", "contrib", "", vsb); |
575 |
|
#endif |
576 |
38520 |
VSB_printf(vsb, ":%s", getenv("PATH")); |
577 |
38520 |
AZ(VSB_finish(vsb)); |
578 |
38520 |
AZ(putenv(strdup(VSB_data(vsb)))); |
579 |
|
|
580 |
|
/* |
581 |
|
* Build vmod_path which can find all VMODs in the build tree |
582 |
|
*/ |
583 |
|
|
584 |
38520 |
VSB_clear(vsb); |
585 |
38520 |
build_path(topbuild, "vmod", ".libs", "", vsb); |
586 |
38520 |
AZ(VSB_finish(vsb)); |
587 |
38520 |
vmod_path = strdup(VSB_data(vsb)); |
588 |
38520 |
AN(vmod_path); |
589 |
|
|
590 |
38520 |
free(topbuild); |
591 |
38520 |
free(topsrc); |
592 |
38520 |
VSB_destroy(&vsb); |
593 |
|
|
594 |
|
/* |
595 |
|
* strict jemalloc checking |
596 |
|
*/ |
597 |
38520 |
AZ(putenv(strdup("MALLOC_CONF=abort:true,junk:true"))); |
598 |
38520 |
} |
599 |
|
|
600 |
|
/********************************************************************** |
601 |
|
* Figure out what IP related magic |
602 |
|
*/ |
603 |
|
|
604 |
|
static void |
605 |
38960 |
ip_magic(void) |
606 |
|
{ |
607 |
|
const struct suckaddr *sa; |
608 |
|
char abuf[VTCP_ADDRBUFSIZE]; |
609 |
|
char pbuf[VTCP_PORTBUFSIZE]; |
610 |
|
char *s; |
611 |
|
|
612 |
|
/* |
613 |
|
* In FreeBSD jails localhost/127.0.0.1 becomes the jails IP# |
614 |
|
* XXX: IPv6-only hosts would have similar issue, but it is not |
615 |
|
* XXX: obvious how to cope. Ideally "127.0.0.1" would be |
616 |
|
* XXX: "localhost", but that doesn't work out of the box. |
617 |
|
* XXX: Things like "prefer_ipv6" parameter complicates things. |
618 |
|
*/ |
619 |
38960 |
sa = VSS_ResolveOne(NULL, "127.0.0.1", "0", 0, SOCK_STREAM, 0); |
620 |
38960 |
AN(sa); |
621 |
38960 |
bad_backend_fd = VTCP_bind(sa, NULL); |
622 |
38960 |
if (bad_backend_fd < 0) { |
623 |
0 |
VSA_free(&sa); |
624 |
0 |
sa = VSS_ResolveFirst(NULL, "localhost", "0", 0, SOCK_STREAM, 0); |
625 |
0 |
AN(sa); |
626 |
0 |
bad_backend_fd = VTCP_bind(sa, NULL); |
627 |
0 |
} |
628 |
38960 |
assert(bad_backend_fd >= 0); |
629 |
38960 |
VTCP_myname(bad_backend_fd, abuf, sizeof abuf, pbuf, sizeof(pbuf)); |
630 |
38960 |
extmacro_def("localhost", NULL, "%s", abuf); |
631 |
38960 |
s = strdup(abuf); |
632 |
38960 |
AN(s); |
633 |
|
|
634 |
|
#if defined (__APPLE__) |
635 |
|
/* |
636 |
|
* In macOS a bound socket that is not listening will timeout |
637 |
|
* instead of refusing the connection so close it and hope |
638 |
|
* for the best. |
639 |
|
*/ |
640 |
|
VTCP_close(&bad_backend_fd); |
641 |
|
#endif |
642 |
|
|
643 |
|
/* Expose a backend that is forever down. */ |
644 |
38960 |
if (VSA_Get_Proto(sa) == AF_INET) |
645 |
38960 |
extmacro_def("bad_backend", NULL, "%s:%s", abuf, pbuf); |
646 |
|
else |
647 |
0 |
extmacro_def("bad_backend", NULL, "[%s]:%s", abuf, pbuf); |
648 |
|
|
649 |
|
/* our default bind/listen address */ |
650 |
38960 |
if (VSA_Get_Proto(sa) == AF_INET) |
651 |
38960 |
bprintf(abuf, "%s:0", s); |
652 |
|
else |
653 |
0 |
bprintf(abuf, "[%s]:0", s); |
654 |
38960 |
free(s); |
655 |
|
|
656 |
38960 |
extmacro_def("listen_addr", NULL, "%s", abuf); |
657 |
38960 |
default_listen_addr = strdup(abuf); |
658 |
38960 |
AN(default_listen_addr); |
659 |
38960 |
VSA_free(&sa); |
660 |
|
|
661 |
|
/* |
662 |
|
* We need an IP number which will not respond, ever, and that is a |
663 |
|
* lot harder than it sounds. This IP# is from RFC5737 and a |
664 |
|
* C-class broadcast at that. |
665 |
|
* If tests involving ${bad_ip} fails and you run linux, you should |
666 |
|
* check your /proc/sys/net/ipv4/ip_nonlocal_bind setting. |
667 |
|
*/ |
668 |
|
|
669 |
38960 |
extmacro_def("bad_ip", NULL, "%s", "192.0.2.255"); |
670 |
38960 |
} |
671 |
|
|
672 |
|
/********************************************************************** |
673 |
|
* Macros |
674 |
|
*/ |
675 |
|
|
676 |
|
static char * v_matchproto_(macro_f) |
677 |
85374 |
macro_func_date(int argc, char *const *argv, const char **err) |
678 |
|
{ |
679 |
|
double t; |
680 |
|
char *s; |
681 |
|
|
682 |
85374 |
assert(argc >= 2); |
683 |
85374 |
AN(argv); |
684 |
85374 |
AN(err); |
685 |
|
|
686 |
85374 |
if (argc > 2) { |
687 |
0 |
*err = "macro does not take arguments"; |
688 |
0 |
return (NULL); |
689 |
|
} |
690 |
|
|
691 |
85374 |
t = VTIM_real(); |
692 |
85374 |
s = malloc(VTIM_FORMAT_SIZE); |
693 |
85374 |
AN(s); |
694 |
85374 |
VTIM_format(t, s); |
695 |
85374 |
return (s); |
696 |
85374 |
} |
697 |
|
|
698 |
|
static char * |
699 |
1560 |
macro_func_string_repeat(int argc, char *const *argv, const char **err) |
700 |
|
{ |
701 |
|
struct vsb vsb[1]; |
702 |
|
const char *p; |
703 |
|
char *res; |
704 |
|
size_t l; |
705 |
|
int i; |
706 |
|
|
707 |
1560 |
if (argc != 4) { |
708 |
0 |
*err = "repeat takes 2 arguments"; |
709 |
0 |
return (NULL); |
710 |
|
} |
711 |
|
|
712 |
1560 |
p = argv[2]; |
713 |
1560 |
i = SF_Parse_Integer(&p, err); |
714 |
|
|
715 |
1560 |
if (*err != NULL) |
716 |
0 |
return (NULL); |
717 |
|
|
718 |
1560 |
if (*p != '\0' || i < 0) { |
719 |
0 |
*err = "invalid number of repetitions"; |
720 |
0 |
return (NULL); |
721 |
|
} |
722 |
|
|
723 |
1560 |
l = (strlen(argv[3]) * i) + 1; |
724 |
1560 |
res = malloc(l); |
725 |
1560 |
AN(res); |
726 |
1560 |
AN(VSB_init(vsb, res, l)); |
727 |
1301160 |
while (i > 0) { |
728 |
1299600 |
AZ(VSB_cat(vsb, argv[3])); |
729 |
1299600 |
i--; |
730 |
|
} |
731 |
1560 |
AZ(VSB_finish(vsb)); |
732 |
1560 |
VSB_fini(vsb); |
733 |
1560 |
return (res); |
734 |
1560 |
} |
735 |
|
|
736 |
|
static char * |
737 |
1560 |
macro_func_string(int argc, char *const *argv, const char **err) |
738 |
|
{ |
739 |
|
|
740 |
1560 |
assert(argc >= 2); |
741 |
1560 |
AN(argv); |
742 |
1560 |
AN(err); |
743 |
|
|
744 |
1560 |
if (argc == 2) { |
745 |
0 |
*err = "missing action"; |
746 |
0 |
return (NULL); |
747 |
|
} |
748 |
|
|
749 |
1560 |
if (!strcmp(argv[2], "repeat")) |
750 |
1560 |
return (macro_func_string_repeat(argc - 1, argv + 1, err)); |
751 |
|
|
752 |
0 |
*err = "unknown action"; |
753 |
0 |
return (NULL); |
754 |
1560 |
} |
755 |
|
|
756 |
|
/********************************************************************** |
757 |
|
* Main |
758 |
|
*/ |
759 |
|
|
760 |
|
static int |
761 |
40000 |
read_file(const char *fn, int ntest) |
762 |
|
{ |
763 |
|
struct vtc_tst *tp; |
764 |
|
char *p, *q; |
765 |
|
|
766 |
40000 |
p = VFIL_readfile(NULL, fn, NULL); |
767 |
40000 |
if (p == NULL) { |
768 |
80 |
fprintf(stderr, "Cannot stat file \"%s\": %s\n", |
769 |
40 |
fn, strerror(errno)); |
770 |
40 |
return (2); |
771 |
|
} |
772 |
40040 |
for (q = p ;q != NULL && *q != '\0'; q++) { |
773 |
40040 |
if (vct_islws(*q)) |
774 |
80 |
continue; |
775 |
39960 |
if (*q != '#') |
776 |
39960 |
break; |
777 |
0 |
q = strchr(q, '\n'); |
778 |
0 |
if (q == NULL) |
779 |
0 |
break; |
780 |
0 |
} |
781 |
|
|
782 |
39960 |
if (q == NULL || *q == '\0') { |
783 |
0 |
fprintf(stderr, "File \"%s\" has no content.\n", fn); |
784 |
0 |
free(p); |
785 |
0 |
return (2); |
786 |
|
} |
787 |
|
|
788 |
40040 |
if ((strncmp(q, "varnishtest", 11) || !isspace(q[11])) && |
789 |
80 |
(strncmp(q, "vtest", 5) || !isspace(q[5]))) { |
790 |
320 |
fprintf(stderr, |
791 |
|
"File \"%s\" doesn't start with" |
792 |
160 |
" 'vtest' or 'varnishtest'\n", fn); |
793 |
160 |
free(p); |
794 |
160 |
vtc_skip++; |
795 |
160 |
return (2); |
796 |
|
} |
797 |
39800 |
ALLOC_OBJ(tp, TST_MAGIC); |
798 |
39800 |
AN(tp); |
799 |
39800 |
tp->filename = fn; |
800 |
39800 |
tp->script = p; |
801 |
39800 |
tp->ntodo = ntest; |
802 |
39800 |
tp->nwait = ntest; |
803 |
39800 |
VTAILQ_INSERT_TAIL(&tst_head, tp, list); |
804 |
39800 |
return (0); |
805 |
40000 |
} |
806 |
|
|
807 |
|
/********************************************************************** |
808 |
|
* Main |
809 |
|
*/ |
810 |
|
|
811 |
|
int |
812 |
39280 |
main(int argc, char * const *argv) |
813 |
|
{ |
814 |
|
int ch, i; |
815 |
39280 |
int ntest = 1; /* Run tests this many times */ |
816 |
39280 |
int nstart = 0; |
817 |
39280 |
int use_cleaner = 0; |
818 |
|
uintmax_t bufsiz; |
819 |
|
const char *p; |
820 |
|
char buf[PATH_MAX]; |
821 |
|
|
822 |
39280 |
argv0 = strrchr(argv[0], '/'); |
823 |
39280 |
if (argv0 == NULL) |
824 |
0 |
argv0 = argv[0]; |
825 |
|
else |
826 |
39280 |
argv0++; |
827 |
|
|
828 |
39280 |
if (getenv("TMPDIR") != NULL) |
829 |
39200 |
tmppath = strdup(getenv("TMPDIR")); |
830 |
|
else |
831 |
80 |
tmppath = strdup("/tmp"); |
832 |
|
|
833 |
39280 |
extmacro_def("pkg_version", NULL, PACKAGE_VERSION); |
834 |
39280 |
extmacro_def("pkg_branch", NULL, PACKAGE_BRANCH); |
835 |
|
|
836 |
39280 |
cwd = getcwd(buf, sizeof buf); |
837 |
39280 |
extmacro_def("pwd", NULL, "%s", cwd); |
838 |
|
|
839 |
39280 |
extmacro_def("date", macro_func_date, NULL); |
840 |
39280 |
extmacro_def("string", macro_func_string, NULL); |
841 |
|
|
842 |
39280 |
vmod_path = NULL; |
843 |
|
|
844 |
39280 |
params_vsb = VSB_new_auto(); |
845 |
39280 |
AN(params_vsb); |
846 |
39280 |
p = getenv("VTEST_DURATION"); |
847 |
39280 |
if (p == NULL) |
848 |
39240 |
p = getenv("VARNISHTEST_DURATION"); |
849 |
78440 |
if (p != NULL) |
850 |
40 |
vtc_maxdur = atoi(p); |
851 |
|
|
852 |
39280 |
VRND_SeedAll(); |
853 |
39280 |
cbvsb = VSB_new_auto(); |
854 |
39280 |
AN(cbvsb); |
855 |
39280 |
setbuf(stdout, NULL); |
856 |
39280 |
setbuf(stderr, NULL); |
857 |
117600 |
while ((ch = getopt(argc, argv, "b:CD:hij:kLln:p:qt:v")) != -1) { |
858 |
78480 |
switch (ch) { |
859 |
|
case 'b': |
860 |
80 |
if (VNUM_2bytes(optarg, &bufsiz, 0)) { |
861 |
0 |
fprintf(stderr, "Cannot parse b opt '%s'\n", |
862 |
0 |
optarg); |
863 |
0 |
exit(2); |
864 |
|
} |
865 |
80 |
if (bufsiz > UINT_MAX) { |
866 |
0 |
fprintf(stderr, "Invalid b opt '%s'\n", |
867 |
0 |
optarg); |
868 |
0 |
exit(2); |
869 |
|
} |
870 |
80 |
vtc_bufsiz = (unsigned)bufsiz; |
871 |
80 |
break; |
872 |
|
case 'C': |
873 |
120 |
use_cleaner = !use_cleaner; |
874 |
120 |
break; |
875 |
|
case 'D': |
876 |
80 |
if (!parse_D_opt(optarg)) { |
877 |
0 |
fprintf(stderr, "Cannot parse D opt '%s'\n", |
878 |
0 |
optarg); |
879 |
0 |
exit(2); |
880 |
|
} |
881 |
80 |
break; |
882 |
|
case 'i': |
883 |
38520 |
iflg = 1; |
884 |
38520 |
break; |
885 |
|
case 'j': |
886 |
80 |
npar = strtoul(optarg, NULL, 0); |
887 |
80 |
break; |
888 |
|
case 'L': |
889 |
80 |
leave_temp = 2; |
890 |
80 |
break; |
891 |
|
case 'l': |
892 |
120 |
leave_temp = 1; |
893 |
120 |
break; |
894 |
|
case 'k': |
895 |
80 |
vtc_continue = !vtc_continue; |
896 |
80 |
break; |
897 |
|
case 'n': |
898 |
80 |
ntest = strtoul(optarg, NULL, 0); |
899 |
80 |
break; |
900 |
|
case 'p': |
901 |
40 |
VSB_cat(params_vsb, " -p "); |
902 |
40 |
VSB_quote(params_vsb, optarg, -1, 0); |
903 |
40 |
break; |
904 |
|
case 'q': |
905 |
80 |
if (vtc_verbosity > 0) |
906 |
80 |
vtc_verbosity--; |
907 |
80 |
break; |
908 |
|
case 't': |
909 |
40 |
vtc_maxdur = strtoul(optarg, NULL, 0); |
910 |
40 |
break; |
911 |
|
case 'v': |
912 |
38920 |
if (vtc_verbosity < 2) |
913 |
38920 |
vtc_verbosity++; |
914 |
38920 |
break; |
915 |
|
default: |
916 |
160 |
usage(); |
917 |
|
} |
918 |
|
} |
919 |
39120 |
argc -= optind; |
920 |
39120 |
argv += optind; |
921 |
|
|
922 |
39120 |
if (argc < 1) |
923 |
40 |
usage(); |
924 |
|
|
925 |
78960 |
for (; argc > 0; argc--, argv++) { |
926 |
40000 |
if (!read_file(*argv, ntest)) |
927 |
39800 |
continue; |
928 |
200 |
if (!vtc_continue) |
929 |
120 |
exit(2); |
930 |
80 |
} |
931 |
|
|
932 |
38960 |
AZ(VSB_finish(params_vsb)); |
933 |
|
|
934 |
38960 |
ip_magic(); |
935 |
|
|
936 |
38960 |
if (iflg) |
937 |
38520 |
i_mode(); |
938 |
|
|
939 |
76960 |
vb = VEV_New(); |
940 |
|
|
941 |
76960 |
if (use_cleaner) |
942 |
80 |
cleaner_setup(); |
943 |
|
|
944 |
38960 |
i = 0; |
945 |
158201 |
while (!VTAILQ_EMPTY(&tst_head) || i) { |
946 |
119241 |
if (!VTAILQ_EMPTY(&tst_head) && njob < npar) { |
947 |
40160 |
start_test(); |
948 |
40160 |
njob++; |
949 |
|
/* Stagger ramp-up */ |
950 |
40160 |
if (nstart++ < npar) |
951 |
39000 |
(void)usleep(random() % 100000L); |
952 |
40160 |
i = 1; |
953 |
40160 |
continue; |
954 |
|
} |
955 |
79081 |
i = VEV_Once(vb); |
956 |
|
} |
957 |
38880 |
cleaner_finish(); |
958 |
38880 |
(void)close(bad_backend_fd); |
959 |
38880 |
if (vtc_continue) |
960 |
160 |
fprintf(stderr, |
961 |
|
"%d tests failed, %d tests skipped, %d tests passed\n", |
962 |
80 |
vtc_fail, vtc_skip, vtc_good); |
963 |
38880 |
if (vtc_fail) |
964 |
0 |
return (1); |
965 |
38880 |
if (vtc_skip && !vtc_good) |
966 |
680 |
return (77); |
967 |
38200 |
return (0); |
968 |
38880 |
} |