varnish-cache/bin/varnishtest/vtc_misc.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/wait.h>
33
#include <sys/socket.h>
34
35
#include <grp.h>
36
#include <math.h>
37
#include <pwd.h>
38
#include <stdio.h>
39
#include <stdlib.h>
40
#include <string.h>
41
#include <sys/un.h>
42
#include <unistd.h>
43
44
#ifdef HAVE_SYS_PERSONALITY_H
45
#  include <sys/personality.h>
46
#endif
47
48
#include "vtc.h"
49
50
#include "vfil.h"
51
#include "vnum.h"
52
#include "vre.h"
53
#include "vtcp.h"
54
#include "vsa.h"
55
#include "vss.h"
56
#include "vtim.h"
57
#include "vus.h"
58
59
/* SECTION: vtest vtest
60
 *
61
 * This should be the first command in your vtc as it will identify the test
62
 * case with a short yet descriptive sentence. It takes exactly one argument, a
63
 * string, eg::
64
 *
65
 *         vtest "Check that vtest is actually a valid command"
66
 *
67
 * It will also print that string in the log.
68
 */
69
70
void v_matchproto_(cmd_f)
71 39720
cmd_vtest(CMD_ARGS)
72
{
73
74 39720
        (void)priv;
75 39720
        (void)vl;
76
77 39720
        if (av == NULL)
78 39640
                return;
79 80
        AZ(strcmp(av[0], "vtest"));
80
81 80
        vtc_log(vl, 1, "VTEST %s", av[1]);
82 80
        AZ(av[2]);
83 39720
}
84
85
/* SECTION: varnishtest varnishtest
86
 *
87
 * Alternate name for 'vtest', see above.
88
 *
89
 */
90
91
void v_matchproto_(cmd_f)
92 79200
cmd_varnishtest(CMD_ARGS)
93
{
94
95 79200
        (void)priv;
96 79200
        (void)vl;
97
98 79200
        if (av == NULL)
99 39640
                return;
100 39560
        AZ(strcmp(av[0], "varnishtest"));
101
102 39560
        vtc_log(vl, 1, "VTEST %s", av[1]);
103 39560
        AZ(av[2]);
104 79200
}
105
106
/* SECTION: shell shell
107
 *
108
 * NOTE: This command is available everywhere commands are given.
109
 *
110
 * Pass the string given as argument to a shell. If you have multiple
111
 * commands to run, you can use curly brackets to describe a multi-lines
112
 * script, eg::
113
 *
114
 *         shell {
115
 *                 echo begin
116
 *                 cat /etc/fstab
117
 *                 echo end
118
 *         }
119
 *
120
 * By default a zero exit code is expected, otherwise the vtc will fail.
121
 *
122
 * Notice that the commandstring is prefixed with "exec 2>&1;" to combine
123
 * stderr and stdout back to the test process.
124
 *
125
 * Optional arguments:
126
 *
127
 * \-err
128
 *      Expect non-zero exit code.
129
 *
130
 * \-exit N
131
 *      Expect exit code N instead of zero.
132
 *
133
 * \-expect STRING
134
 *      Expect string to be found in stdout+err.
135
 *
136
 * \-match REGEXP
137
 *      Expect regexp to match the stdout+err output.
138
 */
139
/* SECTION: client-server.spec.shell
140
 *
141
 * shell
142
 *      Same as for the top-level shell.
143
 */
144
145
static void
146 18600
cmd_shell_engine(struct vtclog *vl, int ok, const char *cmd,
147
    const char *expect, const char *re)
148
{
149
        struct vsb *vsb, re_vsb[1];
150
        FILE *fp;
151 18600
        vre_t *vre = NULL;
152
        int r, c;
153
        int err, erroff;
154
        char errbuf[VRE_ERROR_LEN];
155
156 18600
        AN(vl);
157 18600
        AN(cmd);
158 18600
        vsb = VSB_new_auto();
159 18600
        AN(vsb);
160 18600
        if (re != NULL) {
161 2840
                vre = VRE_compile(re, 0, &err, &erroff, 1);
162 2840
                if (vre == NULL) {
163 0
                        AN(VSB_init(re_vsb, errbuf, sizeof errbuf));
164 0
                        AZ(VRE_error(re_vsb, err));
165 0
                        AZ(VSB_finish(re_vsb));
166 0
                        VSB_fini(re_vsb);
167 0
                        vtc_fatal(vl,
168
                            "shell_match invalid regexp (\"%s\" at %d)",
169 0
                            errbuf, erroff);
170
                }
171 2840
        }
172 18600
        VSB_printf(vsb, "exec 2>&1 ; %s", cmd);
173 18600
        AZ(VSB_finish(vsb));
174 18600
        vtc_dump(vl, 4, "shell_cmd", VSB_data(vsb), -1);
175 18600
        fp = popen(VSB_data(vsb), "r");
176 18600
        if (fp == NULL)
177 0
                vtc_fatal(vl, "popen fails: %s", strerror(errno));
178 18600
        VSB_clear(vsb);
179 18600
        do {
180 23481132
                c = getc(fp);
181 23481132
                if (c != EOF)
182 23462532
                        VSB_putc(vsb, c);
183 23481132
        } while (c != EOF);
184 18600
        r = pclose(fp);
185 18600
        AZ(VSB_finish(vsb));
186 18600
        vtc_dump(vl, 4, "shell_out", VSB_data(vsb), VSB_len(vsb));
187 18600
        vtc_log(vl, 4, "shell_status = 0x%04x", WEXITSTATUS(r));
188 18600
        if (WIFSIGNALED(r))
189 0
                vtc_log(vl, 4, "shell_signal = %d", WTERMSIG(r));
190
191 18600
        if (ok < 0 && !WEXITSTATUS(r) && !WIFSIGNALED(r))
192 0
                vtc_fatal(vl, "shell did not fail as expected");
193 18600
        else if (ok >= 0 && WEXITSTATUS(r) != ok)
194 160
                vtc_fatal(vl, "shell_exit not as expected: "
195 80
                    "got 0x%04x wanted 0x%04x", WEXITSTATUS(r), ok);
196
197 18520
        if (expect != NULL) {
198 9200
                if (strstr(VSB_data(vsb), expect) == NULL)
199 0
                        vtc_fatal(vl,
200 0
                            "shell_expect not found: (\"%s\")", expect);
201
                else
202 9200
                        vtc_log(vl, 4, "shell_expect found");
203 18520
        } else if (vre != NULL) {
204 2840
                if (VRE_match(vre, VSB_data(vsb), VSB_len(vsb), 0, NULL) < 1)
205 0
                        vtc_fatal(vl, "shell_match failed: (\"%s\")", re);
206
                else
207 2840
                        vtc_log(vl, 4, "shell_match succeeded");
208 2840
                VRE_free(&vre);
209 2840
        }
210 18520
        VSB_destroy(&vsb);
211 18520
}
212
213
214
void
215 58240
cmd_shell(CMD_ARGS)
216
{
217 58240
        const char *expect = NULL;
218 58240
        const char *re = NULL;
219
        int n;
220 58240
        int ok = 0;
221
222 58240
        (void)priv;
223
224 58240
        if (av == NULL)
225 39640
                return;
226 39480
        for (n = 1; av[n] != NULL; n++) {
227 39480
                if (!strcmp(av[n], "-err")) {
228 8200
                        ok = -1;
229 39480
                } else if (!strcmp(av[n], "-exit")) {
230 640
                        n += 1;
231 640
                        ok = atoi(av[n]);
232 31280
                } else if (!strcmp(av[n], "-expect")) {
233 9200
                        if (re != NULL)
234 0
                                vtc_fatal(vl,
235
                                    "Cannot use -expect with -match");
236 9200
                        n += 1;
237 9200
                        expect = av[n];
238 30640
                } else if (!strcmp(av[n], "-match")) {
239 2840
                        if (expect != NULL)
240 0
                                vtc_fatal(vl,
241
                                    "Cannot use -match with -expect");
242 2840
                        n += 1;
243 2840
                        re = av[n];
244 2840
                } else {
245 18600
                        break;
246
                }
247 20880
        }
248 18600
        AN(av[n]);
249 18600
        cmd_shell_engine(vl, ok, av[n], expect, re);
250 58240
}
251
252
/* SECTION: filewrite filewrite
253
 *
254
 * Write strings to file
255
 *
256
 *         filewrite [-a] /somefile "Hello" " " "World\n"
257
 *
258
 * The -a flag opens the file in append mode.
259
 *
260
 */
261
262
void v_matchproto_(cmd_f)
263 40880
cmd_filewrite(CMD_ARGS)
264
{
265
        FILE *fo;
266
        int n;
267 40880
        const char *mode = "w";
268
269 40880
        (void)priv;
270
271 40880
        if (av == NULL)
272 39640
                return;
273 1240
        if (av[1] != NULL && !strcmp(av[1], "-a")) {
274 560
                av++;
275 560
                mode = "a";
276 560
        }
277 1240
        if (av[1] == NULL)
278 0
                vtc_fatal(vl, "Need filename");
279 1240
        fo = fopen(av[1], mode);
280 1240
        if (fo == NULL)
281 0
                vtc_fatal(vl, "Cannot open %s: %s", av[1], strerror(errno));
282 2960
        for (n = 2; av[n] != NULL; n++)
283 1720
                (void)fputs(av[n], fo);
284 1240
        AZ(fclose(fo));
285 40880
}
286
287
/* SECTION: setenv setenv
288
 *
289
 * Set or change an environment variable::
290
 *
291
 *         setenv FOO "bar baz"
292
 *
293
 * The above will set the environment variable $FOO to the value
294
 * provided. There is also an ``-ifunset`` argument which will only
295
 * set the value if the the environment variable does not already
296
 * exist::
297
 *
298
 *        setenv -ifunset FOO quux
299
 */
300
301
void v_matchproto_(cmd_f)
302 39800
cmd_setenv(CMD_ARGS)
303
{
304
        int r;
305
        int force;
306
307 39800
        (void)priv;
308
309 39800
        if (av == NULL)
310 39640
                return;
311 160
        AN(av[1]);
312 160
        AN(av[2]);
313
314 160
        force = 1;
315 160
        if (strcmp("-ifunset", av[1]) == 0) {
316 40
                force = 0;
317 40
                av++;
318 40
                AN(av[2]);
319 40
        }
320 160
        if (av[3] != NULL)
321 0
                vtc_fatal(vl, "CMD setenv: Unexpected argument '%s'", av[3]);
322 160
        r = setenv(av[1], av[2], force);
323 160
        if (r != 0)
324 0
                vtc_log(vl, 0, "CMD setenv %s=\"%s\" failed: %s",
325 0
                    av[1], av[2], strerror(errno));
326 39800
}
327
328
/* SECTION: delay delay
329
 *
330
 * NOTE: This command is available everywhere commands are given.
331
 *
332
 * Sleep for the number of seconds specified in the argument. The number
333
 * can include a fractional part, e.g. 1.5.
334
 */
335
void
336 63191
cmd_delay(CMD_ARGS)
337
{
338
        double f;
339
340 63191
        (void)priv;
341 63191
        if (av == NULL)
342 39640
                return;
343 23551
        AN(av[1]);
344 23551
        AZ(av[2]);
345 23551
        f = VNUM(av[1]);
346 23551
        if (isnan(f))
347 0
                vtc_fatal(vl, "Syntax error in number (%s)", av[1]);
348 23551
        vtc_log(vl, 3, "delaying %g second(s)", f);
349 23551
        VTIM_sleep(f);
350 63191
}
351
352
/* SECTION: include include
353
 *
354
 * Executes a vtc fragment::
355
 *
356
 *         include FILE [...]
357
 *
358
 * Open a file and execute it as a VTC fragment. This command is available
359
 * everywhere commands are given.
360
 *
361
 */
362
void
363 39640
cmd_include(CMD_ARGS)
364
{
365
        char *spec;
366
        unsigned i;
367
368 39640
        if (av == NULL)
369 39640
                return;
370
371 0
        if (av[1] == NULL)
372 0
                vtc_fatal(vl, "CMD include: At least 1 argument required");
373
374 0
        for (i = 1; av[i] != NULL; i++) {
375 0
                spec = VFIL_readfile(NULL, av[i], NULL);
376 0
                if (spec == NULL)
377 0
                        vtc_fatal(vl, "CMD include: Unable to read file '%s' "
378 0
                            "(%s)", av[i], strerror(errno));
379 0
                vtc_log(vl, 2, "Begin include '%s'", av[i]);
380 0
                parse_string(vl, priv, spec);
381 0
                vtc_log(vl, 2, "End include '%s'", av[i]);
382 0
                free(spec);
383 0
        }
384 39640
}
385
386
/**********************************************************************
387
 * Most test-cases use only numeric IP#'s but a few requires non-demented
388
 * DNS services.  This is a basic sanity check for those.
389
 */
390
391
static int
392 40
dns_works(void)
393
{
394
        const struct suckaddr *sa;
395
        char abuf[VTCP_ADDRBUFSIZE];
396
        char pbuf[VTCP_PORTBUFSIZE];
397
398 40
        sa = VSS_ResolveOne(NULL, "dns-canary.varnish-cache.org", NULL,
399
            AF_INET, SOCK_STREAM, 0);
400 40
        if (sa == NULL)
401 0
                return (0);
402 40
        VTCP_name(sa, abuf, sizeof abuf, pbuf, sizeof pbuf);
403 40
        VSA_free(&sa);
404 40
        if (strcmp(abuf, "192.0.2.255"))
405 0
                return (0);
406
407 40
        sa = VSS_ResolveOne(NULL, "dns-canary.varnish-cache.org", NULL,
408
            AF_INET6, SOCK_STREAM, 0);
409 40
        if (sa == NULL)
410 40
                return (1); /* the canary is ipv4 only */
411 0
        VSA_free(&sa);
412 0
        return (0);
413 40
}
414
415
/**********************************************************************
416
 * Test if IPv4/IPv6 works
417
 */
418
419
static int
420 280
ipvx_works(const char *target)
421
{
422
        const struct suckaddr *sa;
423
        int fd;
424
425 280
        sa = VSS_ResolveOne(NULL, target, "0", 0, SOCK_STREAM, 0);
426 280
        if (sa == NULL)
427 0
                return (0);
428 280
        fd = VTCP_bind(sa, NULL);
429 280
        VSA_free(&sa);
430 280
        if (fd >= 0) {
431 280
                VTCP_close(&fd);
432 280
                return (1);
433
        }
434 0
        return (0);
435 280
}
436
437
/**********************************************************************/
438
439
static int
440 40
addr_no_randomize_works(void)
441
{
442 40
        int r = 0;
443
444
#ifdef HAVE_SYS_PERSONALITY_H
445
        r = personality(0xffffffff);
446
        r = personality(r | ADDR_NO_RANDOMIZE);
447
#endif
448 40
        return (r >= 0);
449
}
450
451
/**********************************************************************/
452
453
static int
454 40
uds_socket(void *priv, const struct sockaddr_un *uds)
455
{
456
457 40
        return (VUS_bind(uds, priv));
458
}
459
static int
460 40
abstract_uds_works(void)
461
{
462
        const char *err;
463
        int fd;
464
465 40
        fd = VUS_resolver("@vtc.feature.abstract_uds", uds_socket, NULL, &err);
466 40
        if (fd < 0)
467 40
                return (0);
468 0
        AZ(close(fd));
469 0
        return (1);
470 40
}
471
472
/* SECTION: feature feature
473
 *
474
 * Test that the required feature(s) for a test are available, and skip
475
 * the test otherwise; or change the interpretation of the test, as
476
 * documented below. feature takes any number of arguments from this list:
477
 *
478
 * 64bit
479
 *        The environment is 64 bits
480
 * ipv4
481
 *        127.0.0.1 works
482
 * ipv6
483
 *        [::1] works
484
 * dns
485
 *        DNS lookups are working
486
 * topbuild
487
 *        The test has been started with '-i'
488
 * root
489
 *        The test has been invoked by the root user
490
 * user_varnish
491
 *        The varnish user is present
492
 * user_vcache
493
 *        The vcache user is present
494
 * group_varnish
495
 *        The varnish group is present
496
 * cmd <command-line>
497
 *        A command line that should execute with a zero exit status
498
 * ignore_unknown_macro
499
 *        Do not fail the test if a string of the form ${...} is not
500
 *        recognized as a macro.
501
 * persistent_storage
502
 *        Varnish was built with the deprecated persistent storage.
503
 * coverage
504
 *        Varnish was built with code coverage enabled.
505
 * asan
506
 *        Varnish was built with the address sanitizer.
507
 * msan
508
 *        Varnish was built with the memory sanitizer.
509
 * tsan
510
 *        Varnish was built with the thread sanitizer.
511
 * ubsan
512
 *        Varnish was built with the undefined behavior sanitizer.
513
 * sanitizer
514
 *        Varnish was built with a sanitizer.
515
 * workspace_emulator
516
 *        Varnish was built with its workspace emulator.
517
 * abstract_uds
518
 *        Creation of an abstract unix domain socket succeeded.
519
 * disable_aslr
520
 *        ASLR can be disabled.
521
 *
522
 * A feature name can be prefixed with an exclamation mark (!) to skip a
523
 * test if the feature is present.
524
 *
525
 * Be careful with ignore_unknown_macro, because it may cause a test with a
526
 * misspelled macro to fail silently. You should only need it if you must
527
 * run a test with strings of the form "${...}".
528
 */
529
530
#if ENABLE_COVERAGE
531
static const unsigned coverage = 1;
532
#else
533
static const unsigned coverage = 0;
534
#endif
535
536
#if ENABLE_ASAN
537
static const unsigned asan = 1;
538
#else
539
static const unsigned asan = 0;
540
#endif
541
542
#if ENABLE_MSAN
543
static const unsigned msan = 1;
544
#else
545
static const unsigned msan = 0;
546
#endif
547
548
#if ENABLE_TSAN
549
static const unsigned tsan = 1;
550
#else
551
static const unsigned tsan = 0;
552
#endif
553
554
#if ENABLE_UBSAN
555
static const unsigned ubsan = 1;
556
#else
557
static const unsigned ubsan = 0;
558
#endif
559
560
#if ENABLE_SANITIZER
561
static const unsigned sanitizer = 1;
562
#else
563
static const unsigned sanitizer = 0;
564
#endif
565
566
#if ENABLE_WORKSPACE_EMULATOR
567
static const unsigned workspace_emulator = 1;
568
#else
569
static const unsigned workspace_emulator = 0;
570
#endif
571
572
#if WITH_PERSISTENT_STORAGE
573
static const unsigned with_persistent_storage = 1;
574
#else
575
static const unsigned with_persistent_storage = 0;
576
#endif
577
578
void v_matchproto_(cmd_f)
579 43920
cmd_feature(CMD_ARGS)
580
{
581
        const char *feat;
582
        int r, good, skip, neg;
583
584 43920
        (void)priv;
585
586 43920
        if (av == NULL)
587 39640
                return;
588
589
#define FEATURE(nm, tst)                                \
590
        do {                                            \
591
                if (!strcmp(feat, nm)) {                \
592
                        good = 1;                       \
593
                        if (tst) {                      \
594
                                skip = neg;             \
595
                        } else {                        \
596
                                skip = !neg;            \
597
                        }                               \
598
                }                                       \
599
        } while (0)
600
601 4280
        skip = 0;
602
603 8120
        for (av++; *av != NULL; av++) {
604 4320
                good = 0;
605 4320
                neg = 0;
606 4320
                feat = *av;
607
608 4320
                if (feat[0] == '!') {
609 280
                        neg = 1;
610 280
                        feat++;
611 280
                }
612
613 4320
                FEATURE("ipv4", ipvx_works("127.0.0.1"));
614 4320
                FEATURE("ipv6", ipvx_works("[::1]"));
615 4320
                FEATURE("64bit", sizeof(void*) == 8);
616 4320
                FEATURE("disable_aslr", addr_no_randomize_works());
617 4320
                FEATURE("dns", dns_works());
618 4320
                FEATURE("topbuild", iflg);
619 4320
                FEATURE("root", !geteuid());
620 4320
                FEATURE("user_varnish", getpwnam("varnish") != NULL);
621 4320
                FEATURE("user_vcache", getpwnam("vcache") != NULL);
622 4320
                FEATURE("group_varnish", getgrnam("varnish") != NULL);
623 4320
                FEATURE("persistent_storage", with_persistent_storage);
624 4320
                FEATURE("coverage", coverage);
625 4320
                FEATURE("asan", asan);
626 4320
                FEATURE("msan", msan);
627 4320
                FEATURE("tsan", tsan);
628 4320
                FEATURE("ubsan", ubsan);
629 4320
                FEATURE("sanitizer", sanitizer);
630 4320
                FEATURE("workspace_emulator", workspace_emulator);
631 4320
                FEATURE("abstract_uds", abstract_uds_works());
632
633 4320
                if (!strcmp(feat, "cmd")) {
634 1360
                        good = 1;
635 1360
                        skip = neg;
636 1360
                        av++;
637 1360
                        if (*av == NULL)
638 0
                                vtc_fatal(vl, "Missing the command-line");
639 1360
                        r = system(*av);
640 1360
                        if (WEXITSTATUS(r) != 0)
641 200
                                skip = !neg;
642 4320
                } else if (!strcmp(feat, "ignore_unknown_macro")) {
643 400
                        ign_unknown_macro = 1;
644 400
                        good = 1;
645 400
                }
646 4320
                if (!good)
647 0
                        vtc_fatal(vl, "FAIL test, unknown feature: %s", feat);
648
649 4320
                if (!skip)
650 3840
                        continue;
651
652 480
                vtc_stop = 2;
653 480
                if (neg)
654 80
                        vtc_log(vl, 1,
655 40
                            "SKIPPING test, conflicting feature: %s", feat);
656
                else
657 880
                        vtc_log(vl, 1,
658 440
                            "SKIPPING test, lacking feature: %s", feat);
659 480
                return;
660
        }
661 43920
}