varnish-cache/bin/varnishncsa/varnishncsa.c
0
/*-
1
 * Copyright (c) 2006 Verdens Gang AS
2
 * Copyright (c) 2006-2016 Varnish Software AS
3
 * All rights reserved.
4
 *
5
 * Author: Anders Berg <andersb@vgnett.no>
6
 * Author: Poul-Henning Kamp <phk@phk.freebsd.dk>
7
 * Author: Tollef Fog Heen <tfheen@varnish-software.com>
8
 * Author: Martin Blix Grydeland <mbgrydeland@varnish-software.com>
9
 *
10
 * SPDX-License-Identifier: BSD-2-Clause
11
 *
12
 * Redistribution and use in source and binary forms, with or without
13
 * modification, are permitted provided that the following conditions
14
 * are met:
15
 * 1. Redistributions of source code must retain the above copyright
16
 *    notice, this list of conditions and the following disclaimer.
17
 * 2. Redistributions in binary form must reproduce the above copyright
18
 *    notice, this list of conditions and the following disclaimer in the
19
 *    documentation and/or other materials provided with the distribution.
20
 *
21
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
22
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24
 * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
25
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31
 * SUCH DAMAGE.
32
 *
33
 * Obtain log data from the shared memory log, order it by session ID, and
34
 * display it in Apache / NCSA combined log format.
35
 *
36
 * See doc/sphinx/reference/varnishncsa.rst for the supported format
37
 * specifiers.
38
 *
39
 */
40
41
#include "config.h"
42
43
#include <stdlib.h>
44
#include <stdio.h>
45
#include <unistd.h>
46
#include <string.h>
47
#include <signal.h>
48
#include <stdarg.h>
49
#include <inttypes.h>
50
#include <limits.h>
51
#include <ctype.h>
52
#include <time.h>
53
#include <math.h>
54
55
#define VOPT_DEFINITION
56
#define VOPT_INC "varnishncsa_options.h"
57
58
#include "vdef.h"
59
60
#include "vapi/vsl.h"
61
#include "vapi/voptget.h"
62
#include "vas.h"
63
#include "venc.h"
64
#include "vsb.h"
65
#include "vut.h"
66
#include "vqueue.h"
67
#include "miniobj.h"
68
69
#define TIME_FMT "[%d/%b/%Y:%T %z]"
70
#define FORMAT "%h %l %u %t \"%r\" %s %b \"%{Referer}i\" \"%{User-agent}i\""
71
72
static struct VUT *vut;
73
74
struct format;
75
76
enum e_frag {
77
        F_H,                    /* %H Proto */
78
        F_U,                    /* %U URL path */
79
        F_q,                    /* %q Query string */
80
        F_b,                    /* %b Body bytes sent */
81
        F_h,                    /* %h Host name / IP Address */
82
        F_m,                    /* %m Method */
83
        F_s,                    /* %s Status */
84
        F_I,                    /* %I Bytes received */
85
        F_O,                    /* %O Bytes sent */
86
        F_tstart,               /* Time start */
87
        F_tend,                 /* Time end */
88
        F_ttfb,                 /* %{Varnish:time_firstbyte}x */
89
        F_host,                 /* Host header */
90
        F_auth,                 /* Authorization header */
91
        F__MAX,
92
};
93
94
struct fragment {
95
        uint64_t                gen;
96
        const char              *b, *e;
97
};
98
99
typedef int format_f(const struct format *format);
100
101
struct format {
102
        unsigned                magic;
103
#define FORMAT_MAGIC            0xC3119CDA
104
105
        char                    time_type;
106
        VTAILQ_ENTRY(format)    list;
107
        format_f                *func;
108
        struct fragment         *frag;
109
        char                    *string;
110
        const char *const       *strptr;
111
        char                    *time_fmt;
112
        int64_t                 *int64;
113
};
114
115
struct watch {
116
        unsigned                magic;
117
#define WATCH_MAGIC             0xA7D4005C
118
119
        VTAILQ_ENTRY(watch)     list;
120
        char                    *key;
121
        int                     keylen;
122
        struct fragment         frag;
123
};
124
VTAILQ_HEAD(watch_head, watch);
125
126
struct vsl_watch {
127
        unsigned                magic;
128
#define VSL_WATCH_MAGIC         0xE3E27D23
129
130
        VTAILQ_ENTRY(vsl_watch) list;
131
        enum VSL_tag_e          tag;
132
        int                     idx;
133
        char                    *prefix;
134
        int                     prefixlen;
135
        struct fragment         frag;
136
};
137
VTAILQ_HEAD(vsl_watch_head, vsl_watch);
138
139
static struct ctx {
140
        /* Options */
141
        int                     a_opt;
142
        char                    *w_arg;
143
144
        FILE                    *fo;
145
        struct vsb              *vsb;
146
        uint64_t                gen;
147
        VTAILQ_HEAD(,format)    format;
148
        int                     quote_how;
149
        char                    *missing_string;
150
        char                    *missing_int;
151
152
        /* State */
153
        struct watch_head       watch_vcl_log;
154
        struct watch_head       watch_reqhdr; /* also bereqhdr */
155
        struct watch_head       watch_resphdr; /* also beresphdr */
156
        struct vsl_watch_head   watch_vsl;
157
        struct fragment         frag[F__MAX];
158
        const char              *hitmiss;
159
        const char              *handling;
160
        const char              *side;
161
        int64_t                 vxid;
162
        int                     recv_compl;
163
} CTX;
164
165
enum format_policy {
166
        FMTPOL_INTERNAL,
167
        FMTPOL_REQ,
168
        FMTPOL_RESP,
169
};
170
171
static void parse_format(const char *format);
172
173
static unsigned
174 1386
frag_needed(const struct fragment *frag, enum format_policy fp)
175
{
176
        unsigned is_first, want_first, want_frag;
177
178 1386
        is_first = CTX.gen != frag->gen;
179
180 1386
        switch (fp) {
181
        case FMTPOL_INTERNAL:
182 709
                want_first = 1;
183 709
                want_frag = 1;
184 709
                break;
185
        case FMTPOL_REQ:
186 575
                want_first = *CTX.side == 'c';
187 575
                want_frag = !CTX.recv_compl;
188 575
                break;
189
        case FMTPOL_RESP:
190 102
                want_first = *CTX.side == 'b';
191 102
                want_frag = (*CTX.side == 'c') || !CTX.recv_compl;
192 102
                break;
193
        default:
194 0
                WRONG("Invalid format policy");
195 0
        }
196
197 1386
        if (!want_frag)
198 44
                return (0);
199 1342
        if (want_first && !is_first)
200 36
                return (0);
201 1306
        return (1);
202 1386
}
203
204
static void
205 5
openout(int append)
206
{
207
208 5
        AN(CTX.w_arg);
209 5
        if (!strcmp(CTX.w_arg, "-"))
210 0
                CTX.fo = stdout;
211
        else
212 5
                CTX.fo = fopen(CTX.w_arg, append ? "a" : "w");
213 5
        if (CTX.fo == NULL)
214 2
                VUT_Error(vut, 1, "Can't open output file (%s)",
215 1
                    strerror(errno));
216 4
}
217
218
static int v_matchproto_(VUT_cb_f)
219 1
rotateout(struct VUT *v)
220
{
221
222 1
        assert(v == vut);
223 1
        AN(CTX.w_arg);
224 1
        AN(CTX.fo);
225 1
        (void)fclose(CTX.fo);
226 1
        openout(1);
227 1
        AN(CTX.fo);
228 1
        return (0);
229
}
230
231
static int v_matchproto_(VUT_cb_f)
232 857
flushout(struct VUT *v)
233
{
234
235 857
        assert(v == vut);
236 857
        AN(CTX.fo);
237 857
        if (fflush(CTX.fo))
238 0
                return (-5);
239 857
        return (0);
240 857
}
241
242
static inline int
243 238
vsb_fcat(struct vsb *vsb, const struct fragment *f, const char *dflt)
244
{
245 238
        if (f->gen == CTX.gen) {
246 234
                assert(f->b <= f->e);
247 234
                VSB_quote(vsb, f->b, f->e - f->b, CTX.quote_how);
248 238
        } else if (dflt)
249 4
                VSB_quote(vsb, dflt, -1, CTX.quote_how);
250
        else
251 0
                return (-1);
252 238
        return (0);
253 238
}
254
255
static int v_matchproto_(format_f)
256 305
format_string(const struct format *format)
257
{
258
259 305
        CHECK_OBJ_NOTNULL(format, FORMAT_MAGIC);
260 305
        AN(format->string);
261 305
        AZ(VSB_cat(CTX.vsb, format->string));
262 305
        return (1);
263
}
264
265
static int v_matchproto_(format_f)
266 37
format_strptr(const struct format *format)
267
{
268
269 37
        CHECK_OBJ_NOTNULL(format, FORMAT_MAGIC);
270 37
        AN(format->strptr);
271 37
        AN(*format->strptr);
272 37
        AZ(VSB_cat(CTX.vsb, *format->strptr));
273 37
        return (1);
274
}
275
276
static int v_matchproto_(format_f)
277 21
format_int64(const struct format *format)
278
{
279
280 21
        CHECK_OBJ_NOTNULL(format, FORMAT_MAGIC);
281 21
        VSB_printf(CTX.vsb, "%jd", (intmax_t)*format->int64);
282 21
        return (1);
283
}
284
285
static int v_matchproto_(format_f)
286 213
format_fragment(const struct format *format)
287
{
288
289 213
        CHECK_OBJ_NOTNULL(format, FORMAT_MAGIC);
290 213
        AN(format->frag);
291
292 213
        if (format->frag->gen != CTX.gen) {
293 48
                if (format->string == NULL)
294 0
                        return (-1);
295 48
                VSB_quote(CTX.vsb, format->string, -1, CTX.quote_how);
296 48
                return (0);
297
        }
298 165
        AZ(vsb_fcat(CTX.vsb, format->frag, NULL));
299 165
        return (1);
300 213
}
301
302
static int v_matchproto_(format_f)
303 60
format_time(const struct format *format)
304
{
305
        double t_start, t_end, d;
306
        char *p;
307
        char buf[64];
308
        time_t t;
309
        intmax_t l;
310
        struct tm tm;
311
312 60
        CHECK_OBJ_NOTNULL(format, FORMAT_MAGIC);
313 60
        if (CTX.frag[F_tstart].gen == CTX.gen) {
314 60
                t_start = strtod(CTX.frag[F_tstart].b, &p);
315 60
                if (p != CTX.frag[F_tstart].e)
316 0
                        t_start = NAN;
317 60
        } else
318 0
                t_start = NAN;
319 60
        if (isnan(t_start)) {
320
                /* Missing t_start is a no go */
321 0
                if (format->string == NULL)
322 0
                        return (-1);
323 0
                AZ(VSB_cat(CTX.vsb, format->string));
324 0
                return (0);
325
        }
326
327
        /* Missing t_end defaults to t_start */
328 60
        if (CTX.frag[F_tend].gen == CTX.gen) {
329 59
                t_end = strtod(CTX.frag[F_tend].b, &p);
330 59
                if (p != CTX.frag[F_tend].e)
331 0
                        t_end = t_start;
332 59
        } else
333 1
                t_end = t_start;
334
335 60
        AN(format->time_fmt);
336
337 60
        switch (format->time_type) {
338
        case 't':
339 30
                t = (intmax_t)floor(t_start);
340 30
                (void)localtime_r(&t, &tm);
341 30
                AN(strftime(buf, sizeof buf, format->time_fmt, &tm));
342 30
                AZ(VSB_cat(CTX.vsb, buf));
343 30
                return (1);
344
        case '3':
345 3
                l = (intmax_t)(modf(t_start, &d) * 1e3);
346 3
                break;
347
        case '6':
348 3
                l = (intmax_t)(modf(t_start, &d) * 1e6);
349 3
                break;
350
        case 'S':
351 3
                l = (intmax_t)t_start;
352 3
                break;
353
        case 'M':
354 3
                l = (intmax_t)(t_start * 1e3);
355 3
                break;
356
        case 'U':
357 3
                l = (intmax_t)(t_start * 1e6);
358 3
                break;
359
        case 's':
360 6
                l = (intmax_t)(t_end - t_start);
361 6
                break;
362
        case 'm':
363 3
                l = (intmax_t)((t_end - t_start) * 1e3);
364 3
                break;
365
        case 'u':
366 6
                l = (intmax_t)((t_end - t_start) * 1e6);
367 6
                break;
368
        default:
369 0
                WRONG("Time format specifier");
370 0
        }
371
372
#ifdef __FreeBSD__
373 30
        assert(fmtcheck(format->time_fmt, "%jd") == format->time_fmt);
374
#endif
375 30
        AZ(VSB_printf(CTX.vsb, format->time_fmt, l));
376
377 30
        return (1);
378 60
}
379
380
static int v_matchproto_(format_f)
381 15
format_requestline(const struct format *format)
382
{
383
384 15
        (void)format;
385 15
        AZ(vsb_fcat(CTX.vsb, &CTX.frag[F_m], "-"));
386 15
        AZ(VSB_putc(CTX.vsb, ' '));
387 15
        if (CTX.frag[F_host].gen == CTX.gen) {
388 13
                if (strncmp(CTX.frag[F_host].b, "http://", 7))
389 13
                        AZ(VSB_cat(CTX.vsb, "http://"));
390 13
                AZ(vsb_fcat(CTX.vsb, &CTX.frag[F_host], NULL));
391 13
        } else
392 2
                AZ(VSB_cat(CTX.vsb, "http://localhost"));
393 15
        AZ(vsb_fcat(CTX.vsb, &CTX.frag[F_U], ""));
394 15
        AZ(vsb_fcat(CTX.vsb, &CTX.frag[F_q], ""));
395 15
        AZ(VSB_putc(CTX.vsb, ' '));
396 15
        AZ(vsb_fcat(CTX.vsb, &CTX.frag[F_H], "HTTP/1.0"));
397 15
        return (1);
398
}
399
400
static int v_matchproto_(format_f)
401 18
format_auth(const struct format *format)
402
{
403 18
        struct vsb *vsb = VSB_new_auto();
404 18
        AN(vsb);
405
        char *q;
406
407 18
        if (CTX.frag[F_auth].gen != CTX.gen ||
408 8
            VENC_Decode_Base64(vsb, CTX.frag[F_auth].b, CTX.frag[F_auth].e)) {
409 10
                VSB_destroy(&vsb);
410 10
                if (format->string == NULL)
411 0
                        return (-1);
412 10
                VSB_quote(CTX.vsb, format->string, -1, CTX.quote_how);
413 10
                return (0);
414
        }
415 8
        AZ(VSB_finish(vsb));
416 8
        q = strchr(VSB_data(vsb), ':');
417 8
        if (q != NULL)
418 8
                *q = '\0';
419 8
        VSB_quote(CTX.vsb, VSB_data(vsb), -1, CTX.quote_how);
420 8
        VSB_destroy(&vsb);
421 8
        return (1);
422 18
}
423
424
static int
425 96
print(void)
426
{
427
        const struct format *f;
428 96
        int i, r = 1;
429
430 96
        VSB_clear(CTX.vsb);
431 765
        VTAILQ_FOREACH(f, &CTX.format, list) {
432 669
                CHECK_OBJ_NOTNULL(f, FORMAT_MAGIC);
433 669
                i = (f->func)(f);
434 669
                AZ(VSB_error(CTX.vsb));
435 669
                if (r > i)
436 26
                        r = i;
437 669
        }
438 96
        AZ(VSB_putc(CTX.vsb, '\n'));
439 96
        AZ(VSB_finish(CTX.vsb));
440 96
        if (r >= 0) {
441 96
                i = fwrite(VSB_data(CTX.vsb), 1, VSB_len(CTX.vsb), CTX.fo);
442 96
                if (i != VSB_len(CTX.vsb))
443 0
                        return (-5);
444 96
        }
445 96
        return (0);
446 96
}
447
448
static void
449 226
addf_string(const char *str)
450
{
451
        struct format *f;
452
453 226
        AN(str);
454 226
        ALLOC_OBJ(f, FORMAT_MAGIC);
455 226
        AN(f);
456 226
        f->func = format_string;
457 226
        f->string = strdup(str);
458 226
        AN(f->string);
459 226
        VTAILQ_INSERT_TAIL(&CTX.format, f, list);
460 226
}
461
462
static void
463 8
addf_strptr(const char *const *strptr)
464
{
465
        struct format *f;
466
467 8
        AN(strptr);
468 8
        ALLOC_OBJ(f, FORMAT_MAGIC);
469 8
        AN(f);
470 8
        f->func = format_strptr;
471 8
        f->strptr = strptr;
472 8
        VTAILQ_INSERT_TAIL(&CTX.format, f, list);
473 8
}
474
475
static void
476 99
addf_fragment(struct fragment *frag, const char *str)
477
{
478
        struct format *f;
479
480 99
        AN(frag);
481 99
        ALLOC_OBJ(f, FORMAT_MAGIC);
482 99
        AN(f);
483 99
        f->func = format_fragment;
484 99
        f->frag = frag;
485 99
        if (str != NULL) {
486 99
                f->string = strdup(str);
487 99
                AN(f->string);
488 99
        }
489 99
        VTAILQ_INSERT_TAIL(&CTX.format, f, list);
490 99
}
491
492
static void
493 5
addf_int64(int64_t *i)
494
{
495
        struct format *f;
496
497 5
        AN(i);
498 5
        ALLOC_OBJ(f, FORMAT_MAGIC);
499 5
        AN(f);
500 5
        f->func = format_int64;
501 5
        f->int64 = i;
502 5
        VTAILQ_INSERT_TAIL(&CTX.format, f, list);
503 5
}
504
505
static void
506 32
addf_time(char type, const char *fmt)
507
{
508
        struct format *f;
509
510 32
        ALLOC_OBJ(f, FORMAT_MAGIC);
511 32
        AN(f);
512 32
        AN(fmt);
513 32
        f->func = format_time;
514 32
        f->time_type = type;
515 32
        f->time_fmt = strdup(fmt);
516
517 32
        if (f->time_type == 'T') {
518 5
                if (!strcmp(fmt, "s"))
519 2
                        f->time_type = 's';
520 3
                else if (!strcmp(fmt, "ms"))
521 1
                        f->time_type = 'm';
522 2
                else if (!strcmp(fmt, "us"))
523 2
                        f->time_type = 'u';
524
                else
525 0
                        VUT_Error(vut, 1, "Unknown specifier: %%{%s}T",
526 0
                            fmt);
527 5
                REPLACE(f->time_fmt, "%jd");
528 32
        } else if (f->time_type == 't') {
529 27
                if (!strcmp(fmt, "sec")) {
530 1
                        f->time_type = 'S';
531 1
                        REPLACE(f->time_fmt, "%jd");
532 27
                } else if (!strncmp(fmt, "msec", 4)) {
533 3
                        fmt += 4;
534 3
                        if (!strcmp(fmt, "_frac")) {
535 1
                                f->time_type = '3';
536 1
                                REPLACE(f->time_fmt, "%03jd");
537 3
                        } else if (*fmt == '\0') {
538 1
                                f->time_type = 'M';
539 1
                                REPLACE(f->time_fmt, "%jd");
540 1
                        }
541 26
                } else if (!strncmp(fmt, "usec", 4)) {
542 3
                        fmt += 4;
543 3
                        if (!strcmp(fmt, "_frac")) {
544 1
                                f->time_type = '6';
545 1
                                REPLACE(f->time_fmt, "%06jd");
546 3
                        } else if (*fmt == '\0') {
547 1
                                f->time_type = 'U';
548 1
                                REPLACE(f->time_fmt, "%jd");
549 1
                        }
550 3
                }
551 27
        }
552
553 32
        AN(f->time_fmt);
554 32
        VTAILQ_INSERT_TAIL(&CTX.format, f, list);
555 32
}
556
557
static void
558 19
addf_requestline(void)
559
{
560
        struct format *f;
561
562 19
        ALLOC_OBJ(f, FORMAT_MAGIC);
563 19
        AN(f);
564 19
        f->func = format_requestline;
565 19
        VTAILQ_INSERT_TAIL(&CTX.format, f, list);
566 19
}
567
568
static void
569 2
addf_vcl_log(const char *key)
570
{
571
        struct watch *w;
572
        struct format *f;
573
574 2
        AN(key);
575 2
        ALLOC_OBJ(w, WATCH_MAGIC);
576 2
        AN(w);
577 2
        w->keylen = asprintf(&w->key, "%s:", key);
578 2
        assert(w->keylen > 0);
579 2
        VTAILQ_INSERT_TAIL(&CTX.watch_vcl_log, w, list);
580
581 2
        ALLOC_OBJ(f, FORMAT_MAGIC);
582 2
        AN(f);
583 2
        f->func = format_fragment;
584 2
        f->frag = &w->frag;
585 2
        f->string = strdup("");
586 2
        AN(f->string);
587 2
        VTAILQ_INSERT_TAIL(&CTX.format, f, list);
588 2
}
589
590
static void
591 55
addf_hdr(struct watch_head *head, const char *key)
592
{
593
        struct watch *w;
594
        struct format *f;
595
596 55
        AN(head);
597 55
        AN(key);
598 55
        ALLOC_OBJ(w, WATCH_MAGIC);
599 55
        AN(w);
600 55
        w->keylen = asprintf(&w->key, "%s:", key);
601 55
        assert(w->keylen > 0);
602 55
        VTAILQ_INSERT_TAIL(head, w, list);
603
604 55
        ALLOC_OBJ(f, FORMAT_MAGIC);
605 55
        AN(f);
606 55
        f->func = format_fragment;
607 55
        f->frag = &w->frag;
608 55
        f->string = strdup(CTX.missing_string);
609 55
        AN(f->string);
610 55
        VTAILQ_INSERT_TAIL(&CTX.format, f, list);
611 55
}
612
613
static void
614 13
addf_vsl(enum VSL_tag_e tag, long i, const char *prefix)
615
{
616
        struct vsl_watch *w;
617
618 13
        ALLOC_OBJ(w, VSL_WATCH_MAGIC);
619 13
        AN(w);
620 13
        if (VSL_tagflags[tag] && CTX.quote_how != VSB_QUOTE_JSON)
621 2
                VUT_Error(vut, 1, "Tag %s can contain control characters",
622 1
                    VSL_tags[tag]);
623 12
        w->tag = tag;
624 12
        assert(i <= INT_MAX);
625 12
        w->idx = i;
626 12
        if (prefix != NULL) {
627 3
                w->prefixlen = asprintf(&w->prefix, "%s:", prefix);
628 3
                assert(w->prefixlen > 0);
629 3
        }
630 12
        VTAILQ_INSERT_TAIL(&CTX.watch_vsl, w, list);
631 12
        addf_fragment(&w->frag, CTX.missing_string);
632 12
}
633
634
static void
635 20
addf_auth(void)
636
{
637
        struct format *f;
638
639 20
        ALLOC_OBJ(f, FORMAT_MAGIC);
640 20
        AN(f);
641 20
        f->func = format_auth;
642 20
        f->string = strdup("-");
643 20
        AN(f->string);
644 20
        VTAILQ_INSERT_TAIL(&CTX.format, f, list);
645 20
}
646
647
static void
648 40
parse_x_format(char *buf)
649
{
650
        char *e, *r, *s;
651
        long lval;
652
        int slt;
653
654 40
        if (!strcmp(buf, "Varnish:time_firstbyte")) {
655 1
                addf_fragment(&CTX.frag[F_ttfb], CTX.missing_int);
656 1
                return;
657
        }
658 39
        if (!strcmp(buf, "Varnish:hitmiss")) {
659 1
                addf_strptr(&CTX.hitmiss);
660 1
                return;
661
        }
662 38
        if (!strcmp(buf, "Varnish:handling")) {
663 2
                addf_strptr(&CTX.handling);
664 2
                return;
665
        }
666 36
        if (!strcmp(buf, "Varnish:side")) {
667 5
                addf_strptr(&CTX.side);
668 5
                return;
669
        }
670 31
        if (!strcmp(buf, "Varnish:vxid")) {
671 5
                addf_int64(&CTX.vxid);
672 5
                return;
673
        }
674 26
        if (!strncmp(buf, "VCL_Log:", 8)) {
675 2
                addf_vcl_log(buf + 8);
676 2
                return;
677
        }
678 24
        if (!strncmp(buf, "VSL:", 4)) {
679 22
                buf += 4;
680 22
                e = buf;
681 206
                while (*e != '\0')
682 184
                        e++;
683 22
                if (e == buf)
684 1
                        VUT_Error(vut, 1, "Missing tag in VSL:");
685 21
                if (e[-1] == ']') {
686 12
                        r = e - 1;
687 50
                        while (r > buf && *r != '[')
688 38
                                r--;
689 12
                        if (r == buf || r[1] == ']')
690 1
                                VUT_Error(vut, 1, "Syntax error: VSL:%s", buf);
691 11
                        e[-1] = '\0';
692 11
                        lval = strtol(r + 1, &s, 10);
693 11
                        if (s != e - 1)
694 1
                                VUT_Error(vut, 1, "Syntax error: VSL:%s]", buf);
695 10
                        if (lval <= 0 || lval > 255) {
696 4
                                VUT_Error(vut, 1,
697
                                    "Syntax error. Field specifier must be"
698
                                    " between 1 and 255: %s]",
699 2
                                    buf);
700
                        }
701 8
                        *r = '\0';
702 8
                } else
703 9
                        lval = 0;
704 17
                r = buf;
705 143
                while (r < e && *r != ':')
706 126
                        r++;
707 17
                if (r != e) {
708 3
                        slt = VSL_Name2Tag(buf, r - buf);
709 3
                        r++;
710 3
                } else {
711 14
                        slt = VSL_Name2Tag(buf, -1);
712 14
                        r = NULL;
713
                }
714 17
                if (slt == -2)
715 1
                        VUT_Error(vut, 1, "Tag not unique: %s", buf);
716 16
                if (slt == -1)
717 3
                        VUT_Error(vut, 1, "Unknown log tag: %s", buf);
718 13
                assert(slt >= 0);
719
720 13
                addf_vsl((enum VSL_tag_e)slt, lval, r);
721 13
                return;
722
        }
723 2
        if (!strcmp(buf, "Varnish:default_format")) {
724 1
                parse_format(FORMAT);
725 1
                return;
726
        }
727 1
        VUT_Error(vut, 1, "Unknown formatting extension: %s", buf);
728 30
}
729
730
static void
731 72
parse_format(const char *format)
732
{
733
        const char *p, *q;
734
        struct vsb *vsb;
735
        char buf[256];
736
        int b;
737
738 72
        if (format == NULL)
739 15
                format = FORMAT;
740
741 72
        vsb = VSB_new_auto();
742 72
        AN(vsb);
743
744 697
        for (p = format; *p != '\0'; p++) {
745
746
                /* Allow the most essential escape sequences in format */
747 628
                if (*p == '\\' && p[1] != '\0') {
748 1
                        if (*++p == 't')
749 0
                                AZ(VSB_putc(vsb, '\t'));
750 1
                        else if (*p == 'n')
751 1
                                AZ(VSB_putc(vsb, '\n'));
752
                        else
753 0
                                AZ(VSB_putc(vsb, *p));
754 1
                        continue;
755
                }
756
757 627
                if (*p != '%') {
758 355
                        AZ(VSB_putc(vsb, *p));
759 355
                        continue;
760
                }
761
762 272
                if (VSB_len(vsb) > 0) {
763 205
                        AZ(VSB_finish(vsb));
764 205
                        addf_string(VSB_data(vsb));
765 205
                        VSB_clear(vsb);
766 205
                }
767
768 272
                p++;
769 272
                switch (*p) {
770
                case 'b':       /* Body bytes sent */
771 20
                        addf_fragment(&CTX.frag[F_b], CTX.missing_int);
772 20
                        break;
773
                case 'D':       /* Float request time */
774 1
                        addf_time('T', "us");
775 1
                        break;
776
                case 'h':       /* Client host name / IP Address */
777 20
                        addf_fragment(&CTX.frag[F_h], CTX.missing_string);
778 20
                        break;
779
                case 'H':       /* Protocol */
780 3
                        addf_fragment(&CTX.frag[F_H], "HTTP/1.0");
781 3
                        break;
782
                case 'I':       /* Bytes received */
783 1
                        addf_fragment(&CTX.frag[F_I], CTX.missing_int);
784 1
                        break;
785
                case 'l':       /* Client user ID (identd) always '-' */
786 17
                        AZ(VSB_putc(vsb, '-'));
787 17
                        break;
788
                case 'm':       /* Method */
789 3
                        addf_fragment(&CTX.frag[F_m], CTX.missing_string);
790 3
                        break;
791
                case 'O':       /* Bytes sent */
792 1
                        addf_fragment(&CTX.frag[F_O], CTX.missing_int);
793 1
                        break;
794
                case 'q':       /* Query string */
795 3
                        addf_fragment(&CTX.frag[F_q], "");
796 3
                        break;
797
                case 'r':       /* Request line */
798 19
                        addf_requestline();
799 19
                        break;
800
                case 's':       /* Status code */
801 31
                        addf_fragment(&CTX.frag[F_s], CTX.missing_int);
802 31
                        break;
803
                case 't':       /* strftime */
804 17
                        addf_time(*p, TIME_FMT);
805 17
                        break;
806
                case 'T':       /* Int request time */
807 1
                        addf_time(*p, "s");
808 1
                        break;
809
                case 'u':       /* Remote user from auth */
810 20
                        addf_auth();
811 20
                        break;
812
                case 'U':       /* URL */
813 4
                        addf_fragment(&CTX.frag[F_U], CTX.missing_string);
814 4
                        break;
815
                case '{':
816 110
                        p++;
817 110
                        q = p;
818 110
                        b = 1;
819 1126
                        while (*q) {
820 1125
                                if (*q == '{')
821 4
                                        b++;
822 1121
                                else if (*q == '}')
823 113
                                        if (--b == 0)
824 109
                                                break;
825 1016
                                q++;
826
                        }
827 110
                        if (b > 0)
828 2
                                VUT_Error(vut, 1, "Unmatched bracket at: %s",
829 1
                                    p - 2);
830 109
                        assert((unsigned)(q - p) < sizeof buf - 1);
831 109
                        strncpy(buf, p, q - p);
832 109
                        buf[q - p] = '\0';
833 109
                        q++;
834 109
                        switch (*q) {
835
                        case 'i':
836 49
                                addf_hdr(&CTX.watch_reqhdr, buf);
837 49
                                break;
838
                        case 'o':
839 6
                                addf_hdr(&CTX.watch_resphdr, buf);
840 6
                                break;
841
                        case 't':
842 10
                                addf_time(*q, buf);
843 10
                                break;
844
                        case 'T':
845 3
                                addf_time(*q, buf);
846 3
                                break;
847
                        case 'x':
848 40
                                parse_x_format(buf);
849 40
                                break;
850
                        default:
851 2
                                VUT_Error(vut, 1,
852
                                    "Unknown format specifier at: %s",
853 1
                                    p - 2);
854
                        }
855 108
                        p = q;
856 108
                        break;
857
                default:
858 2
                        VUT_Error(vut, 1, "Unknown format specifier at: %s",
859 1
                            p - 1);
860
                }
861 269
        }
862
863 69
        if (VSB_len(vsb) > 0) {
864
                /* Add any remaining static */
865 21
                AZ(VSB_finish(vsb));
866 21
                addf_string(VSB_data(vsb));
867 21
                VSB_clear(vsb);
868 21
        }
869
870 69
        VSB_destroy(&vsb);
871 69
}
872
873
static int
874 3299
isprefix(const char *prefix, size_t len, const char *b,
875
    const char *e, const char **next)
876
{
877 3299
        assert(len > 0);
878 3299
        if (e < b + len || strncasecmp(b, prefix, len))
879 2816
                return (0);
880 483
        b += len;
881 483
        if (next) {
882 939
                while (b < e && *b == ' ')
883 456
                        b++;
884 483
                *next = b;
885 483
        }
886 483
        return (1);
887 3299
}
888
889
static void
890 520
frag_fields(enum format_policy fp, const char *b, const char *e, ...)
891
{
892
        va_list ap;
893
        const char *p, *q;
894
        int n, field;
895
        struct fragment *frag;
896
897 520
        AN(b);
898 520
        AN(e);
899 520
        va_start(ap, e);
900
901 520
        n = 0;
902 1222
        while (1) {
903 1225
                field = va_arg(ap, int);
904 1225
                frag = va_arg(ap, struct fragment *);
905 1225
                if (field == 0) {
906 520
                        AZ(frag);
907 520
                        break;
908
                }
909 705
                p = q = NULL;
910 1872
                while (n < field) {
911 1811
                        while (b < e && isspace(*b))
912 644
                                b++;
913 1167
                        p = b;
914 9987
                        while (b < e && !isspace(*b))
915 8820
                                b++;
916 1167
                        q = b;
917 1167
                        n++;
918
                }
919 705
                assert(p != NULL && q != NULL);
920 705
                if (p >= e || q <= p)
921 3
                        continue;
922 702
                if (frag_needed(frag, fp)) {
923
                        /* We only grab the same matching field once */
924 685
                        frag->gen = CTX.gen;
925 685
                        frag->b = p;
926 685
                        frag->e = q;
927 685
                }
928
        }
929 520
        va_end(ap);
930 520
}
931
932
static void
933 684
frag_line(enum format_policy fp, const char *b, const char *e,
934
    struct fragment *f)
935
{
936
937 684
        if (!frag_needed(f, fp))
938
                /* We only grab the same matching record once */
939 63
                return;
940
941 621
        if (e == NULL)
942 4
                e = b + strlen(b);
943
944
        /* Skip leading space */
945 621
        while (b < e && isspace(*b))
946 0
                ++b;
947
948
        /* Skip trailing space */
949 621
        while (e > b && isspace(e[-1]))
950 0
                --e;
951
952 621
        f->gen = CTX.gen;
953 621
        f->b = b;
954 621
        f->e = e;
955 684
}
956
957
static void
958 1064
process_hdr(enum format_policy fp, const struct watch_head *head, const char *b,
959
    const char *e, int unset)
960
{
961
        struct watch *w;
962
        const char *p;
963
964 1419
        VTAILQ_FOREACH(w, head, list) {
965 355
                CHECK_OBJ_NOTNULL(w, WATCH_MAGIC);
966 355
                if (!isprefix(w->key, w->keylen, b, e, &p))
967 309
                        continue;
968 46
                if (unset) {
969 22
                        frag_line(fp, CTX.missing_string,
970
                            NULL,
971 11
                            &w->frag);
972 11
                } else
973 35
                        frag_line(fp, p, e, &w->frag);
974 46
        }
975 1064
}
976
977
static void
978 3518
process_vsl(const struct vsl_watch_head *head, enum VSL_tag_e tag,
979
    const char *b, const char *e)
980
{
981
        struct vsl_watch *w;
982
        const char *p;
983 4874
        VTAILQ_FOREACH(w, head, list) {
984 1356
                CHECK_OBJ_NOTNULL(w, VSL_WATCH_MAGIC);
985 1356
                if (tag != w->tag)
986 1285
                        continue;
987 71
                p = b;
988 71
                if (w->prefixlen > 0 &&
989 42
                    !isprefix(w->prefix, w->prefixlen, b, e, &p))
990 38
                        continue;
991 33
                if (w->idx == 0)
992 6
                        frag_line(FMTPOL_INTERNAL, p, e, &w->frag);
993
                else
994 27
                        frag_fields(FMTPOL_INTERNAL, p, e, w->idx, &w->frag, 0, NULL);
995 33
        }
996 3518
}
997
998
static int v_matchproto_(VSLQ_dispatch_f)
999 122
dispatch_f(struct VSL_data *vsl, struct VSL_transaction * const pt[],
1000
    void *priv)
1001
{
1002
        struct VSL_transaction *t;
1003
        enum VSL_tag_e tag;
1004
        const char *b, *e, *p;
1005
        struct watch *w;
1006
        int i, skip;
1007
1008 122
        (void)vsl;
1009 122
        (void)priv;
1010
1011 244
        for (t = pt[0]; t != NULL; t = *++pt) {
1012 122
                CTX.gen++;
1013
1014 122
                if (t->type == VSL_t_req) {
1015 84
                        CTX.side = "c";
1016 122
                } else if (t->type == VSL_t_bereq) {
1017 12
                        CTX.side = "b";
1018 12
                } else
1019 26
                        continue;
1020
1021 96
                CTX.recv_compl = 0;
1022 96
                CTX.hitmiss = "-";
1023 96
                CTX.handling = "-";
1024 96
                CTX.vxid = t->vxid;
1025 96
                skip = 0;
1026 3627
                while (skip == 0 && 1 == VSL_Next(t->c)) {
1027 3531
                        tag = VSL_TAG(t->c->rec.ptr);
1028 3531
                        if (VSL_tagflags[tag] &&
1029 14
                            CTX.quote_how != VSB_QUOTE_JSON)
1030 13
                                continue;
1031
1032 3518
                        b = VSL_CDATA(t->c->rec.ptr);
1033 3518
                        e = b + VSL_LEN(t->c->rec.ptr);
1034 3518
                        if (!VSL_tagflags[tag]) {
1035 7034
                                while (e > b && e[-1] == '\0')
1036 3517
                                        e--;
1037 3517
                        }
1038
1039 3518
                        switch (tag) {
1040
                        case SLT_HttpGarbage:
1041 0
                                skip = 1;
1042 0
                                break;
1043
                        case SLT_PipeAcct:
1044 7
                                frag_fields(FMTPOL_INTERNAL, b, e,
1045
                                    3, &CTX.frag[F_I],
1046
                                    4, &CTX.frag[F_O],
1047
                                    0, NULL);
1048 7
                                break;
1049
                        case SLT_BackendOpen:
1050 12
                                frag_fields(FMTPOL_INTERNAL, b, e,
1051
                                    3, &CTX.frag[F_h],
1052
                                    0, NULL);
1053 12
                                break;
1054
                        case SLT_ReqStart:
1055 84
                                frag_fields(FMTPOL_INTERNAL, b, e,
1056
                                    1, &CTX.frag[F_h],
1057
                                    0, NULL);
1058 84
                                break;
1059
                        case SLT_BereqMethod:
1060
                        case SLT_ReqMethod:
1061 99
                                frag_line(FMTPOL_REQ, b, e, &CTX.frag[F_m]);
1062 99
                                break;
1063
                        case SLT_BereqURL:
1064
                        case SLT_ReqURL:
1065 116
                                p = memchr(b, '?', e - b);
1066 116
                                if (p == NULL)
1067 91
                                        p = e;
1068 116
                                frag_line(FMTPOL_REQ, b, p, &CTX.frag[F_U]);
1069 116
                                frag_line(FMTPOL_REQ, p, e, &CTX.frag[F_q]);
1070 116
                                break;
1071
                        case SLT_BereqProtocol:
1072
                        case SLT_ReqProtocol:
1073 98
                                frag_line(FMTPOL_REQ, b, e, &CTX.frag[F_H]);
1074 98
                                break;
1075
                        case SLT_BerespStatus:
1076
                        case SLT_RespStatus:
1077 90
                                frag_line(FMTPOL_RESP, b, e, &CTX.frag[F_s]);
1078 90
                                break;
1079
                        case SLT_BereqAcct:
1080
                        case SLT_ReqAcct:
1081 89
                                frag_fields(FMTPOL_INTERNAL, b, e,
1082
                                    3, &CTX.frag[F_I],
1083
                                    5, &CTX.frag[F_b],
1084
                                    6, &CTX.frag[F_O],
1085
                                    0, NULL);
1086 89
                                break;
1087
                        case SLT_Timestamp:
1088
#define ISPREFIX(a, b, c, d)    isprefix(a, strlen(a), b, c, d)
1089 465
                                if (ISPREFIX("Start:", b, e, &p)) {
1090 96
                                        frag_fields(FMTPOL_INTERNAL, p, e, 1,
1091
                                            &CTX.frag[F_tstart], 0, NULL);
1092
1093 751
                                } else if (ISPREFIX("Resp:", b, e, &p) ||
1094 293
                                    ISPREFIX("PipeSess:", b, e, &p) ||
1095 286
                                    ISPREFIX("BerespBody:", b, e, &p)) {
1096 93
                                        frag_fields(FMTPOL_INTERNAL, p, e, 1,
1097
                                            &CTX.frag[F_tend], 0, NULL);
1098
1099 543
                                } else if (ISPREFIX("Process:", b, e, &p) ||
1100 181
                                    ISPREFIX("Pipe:", b, e, &p) ||
1101 174
                                    ISPREFIX("Beresp:", b, e, &p)) {
1102 112
                                        frag_fields(FMTPOL_INTERNAL, p, e, 2,
1103
                                            &CTX.frag[F_ttfb], 0, NULL);
1104 112
                                }
1105 465
                                break;
1106
                        case SLT_BereqHeader:
1107
                        case SLT_ReqHeader:
1108 429
                                process_hdr(FMTPOL_REQ, &CTX.watch_reqhdr, b, e, 0);
1109 429
                                if (ISPREFIX("Authorization:", b, e, &p) &&
1110 20
                                    ISPREFIX("basic ", p, e, &p))
1111 20
                                        frag_line(FMTPOL_REQ, p, e,
1112
                                            &CTX.frag[F_auth]);
1113 409
                                else if (ISPREFIX("Host:", b, e, &p))
1114 92
                                        frag_line(FMTPOL_REQ, p, e,
1115
                                            &CTX.frag[F_host]);
1116
#undef ISPREFIX
1117 429
                                break;
1118
                        case SLT_BerespHeader:
1119
                        case SLT_RespHeader:
1120 603
                                process_hdr(FMTPOL_RESP, &CTX.watch_resphdr, b, e, 0);
1121 603
                                break;
1122
                        case SLT_BereqUnset:
1123
                        case SLT_ReqUnset:
1124 11
                                process_hdr(FMTPOL_REQ, &CTX.watch_reqhdr, b, e, 1);
1125 11
                                break;
1126
                        case SLT_BerespUnset:
1127
                        case SLT_RespUnset:
1128 21
                                process_hdr(FMTPOL_RESP, &CTX.watch_resphdr, b, e, 1);
1129 21
                                break;
1130
                        case SLT_HitMiss:
1131 11
                                CTX.hitmiss = "miss";
1132 11
                                CTX.handling = "hitmiss";
1133 11
                                break;
1134
                        case SLT_HitPass:
1135 1
                                CTX.hitmiss = "miss";
1136 1
                                CTX.handling = "hitpass";
1137 1
                                break;
1138
                        case SLT_VCL_call:
1139 338
                                if (!strcasecmp(b, "recv")) {
1140 83
                                        CTX.recv_compl = 1;
1141 83
                                        CTX.hitmiss = "-";
1142 83
                                        CTX.handling = "-";
1143 338
                                } else if (!strcasecmp(b, "hit")) {
1144 17
                                        CTX.hitmiss = "hit";
1145 17
                                        CTX.handling = "hit";
1146 255
                                } else if (!strcasecmp(b, "miss") && strcmp(CTX.handling, "hitmiss")) {
1147 36
                                        CTX.hitmiss = "miss";
1148 36
                                        CTX.handling = "miss";
1149 238
                                } else if (!strcasecmp(b, "pass") && strcmp(CTX.handling, "hitpass")) {
1150 6
                                        CTX.hitmiss = "miss";
1151 6
                                        CTX.handling = "pass";
1152 202
                                } else if (!strcasecmp(b, "synth")) {
1153
                                        /* Arguably, synth isn't a hit or
1154
                                           a miss, but miss is less
1155
                                           wrong */
1156 12
                                        CTX.hitmiss = "miss";
1157 12
                                        CTX.handling = "synth";
1158 196
                                } else if (!strcasecmp(b, "backend_response")) {
1159 10
                                        CTX.recv_compl = 1;
1160 10
                                }
1161 338
                                break;
1162
                        case SLT_VCL_return:
1163 338
                                if (!strcasecmp(b, "pipe")) {
1164 14
                                        CTX.hitmiss = "miss";
1165 14
                                        CTX.handling = "pipe";
1166 338
                                } else if (!strcasecmp(b, "restart"))
1167 0
                                        skip = 1;
1168 338
                                break;
1169
                        case SLT_VCL_Log:
1170 11
                                VTAILQ_FOREACH(w, &CTX.watch_vcl_log, list) {
1171 1
                                        CHECK_OBJ_NOTNULL(w, WATCH_MAGIC);
1172 1
                                        if (e - b < w->keylen ||
1173 1
                                            strncmp(b, w->key, w->keylen))
1174 0
                                                continue;
1175 1
                                        p = b + w->keylen;
1176 1
                                        frag_line(FMTPOL_INTERNAL, p, e, &w->frag);
1177 1
                                }
1178 10
                                break;
1179
                        default:
1180 696
                                break;
1181
                        }
1182
1183 3518
                        process_vsl(&CTX.watch_vsl, tag, b, e);
1184
                }
1185 96
                if (skip)
1186 0
                        continue;
1187 96
                i = print();
1188 96
                if (i)
1189 0
                        return (i);
1190 96
        }
1191
1192 122
        return (0);
1193 122
}
1194
1195
static char *
1196 3
read_format(const char *formatfile)
1197
{
1198
        FILE *fmtfile;
1199 3
        size_t len = 0;
1200
        int fmtlen;
1201 3
        char *fmt = NULL;
1202
1203 3
        fmtfile = fopen(formatfile, "r");
1204 3
        if (fmtfile == NULL)
1205 2
                VUT_Error(vut, 1, "Can't open format file (%s)",
1206 1
                    strerror(errno));
1207 2
        AN(fmtfile);
1208 2
        fmtlen = getline(&fmt, &len, fmtfile);
1209 2
        if (fmtlen == -1) {
1210 1
                free(fmt);
1211 1
                if (feof(fmtfile))
1212 1
                        VUT_Error(vut, 1, "Empty format file");
1213 0
                VUT_Error(vut, 1, "Can't read format from file (%s)",
1214 0
                    strerror(errno));
1215
        }
1216 1
        AZ(fclose(fmtfile));
1217 1
        if (fmt[fmtlen - 1] == '\n')
1218 1
                fmt[fmtlen - 1] = '\0';
1219 1
        return (fmt);
1220
}
1221
1222
int
1223 98
main(int argc, char * const *argv)
1224
{
1225
        signed char opt;
1226 98
        char *format = NULL;
1227 98
        int mode_opt = 0;
1228
1229 98
        vut = VUT_InitProg(argc, argv, &vopt_spec);
1230 98
        AN(vut);
1231 98
        memset(&CTX, 0, sizeof CTX);
1232 98
        VTAILQ_INIT(&CTX.format);
1233 98
        VTAILQ_INIT(&CTX.watch_vcl_log);
1234 98
        VTAILQ_INIT(&CTX.watch_reqhdr);
1235 98
        VTAILQ_INIT(&CTX.watch_resphdr);
1236 98
        VTAILQ_INIT(&CTX.watch_vsl);
1237 98
        CTX.vsb = VSB_new_auto();
1238 98
        AN(CTX.vsb);
1239 98
        CTX.quote_how = VSB_QUOTE_ESCHEX;
1240 98
        REPLACE(CTX.missing_string, "-");
1241 98
        REPLACE(CTX.missing_int, "-");
1242
1243 60
        tzset();                // We use localtime_r(3)
1244
1245 289
        while ((opt = getopt(argc, argv, vopt_spec.vopt_optstring)) != -1) {
1246 230
                switch (opt) {
1247
                case 'a':
1248
                        /* Append to file */
1249 0
                        CTX.a_opt = 1;
1250 0
                        break;
1251
                case 'b': /* backend mode */
1252
                case 'c': /* client mode */
1253
                case 'E': /* show ESI */
1254 16
                        AN(VUT_Arg(vut, opt, NULL));
1255 16
                        mode_opt = 1;
1256 16
                        break;
1257
                case 'F':
1258 55
                        if (format != NULL)
1259 0
                                VUT_Error(vut, 1, "Format already set");
1260 55
                        REPLACE(format, optarg);
1261 55
                        break;
1262
                case 'f':
1263 1
                        if (format != NULL)
1264 0
                                VUT_Error(vut, 1, "Format already set");
1265
                        /* Format string from file */
1266 1
                        format = read_format(optarg);
1267 1
                        AN(format);
1268 1
                        break;
1269
                case 'h':
1270
                        /* Usage help */
1271 1
                        VUT_Usage(vut, &vopt_spec, 0);
1272
                        break;
1273
                case 'j':
1274 5
                        REPLACE(CTX.missing_string, "");
1275 5
                        REPLACE(CTX.missing_int, "0");
1276 5
                        CTX.quote_how = VSB_QUOTE_JSON;
1277 5
                        break;
1278
                case 'w':
1279
                        /* Write to file */
1280 4
                        REPLACE(CTX.w_arg, optarg);
1281 4
                        break;
1282
                default:
1283 148
                        if (!VUT_Arg(vut, opt, optarg))
1284 0
                                VUT_Usage(vut, &vopt_spec, 1);
1285 148
                }
1286
        }
1287
1288
        /* default is client mode: */
1289 59
        if (!mode_opt)
1290 59
                AN(VUT_Arg(vut, 'c', NULL));
1291
1292 59
        if (optind != argc)
1293 1
                VUT_Usage(vut, &vopt_spec, 1);
1294
1295 58
        if (vut->D_opt && !CTX.w_arg)
1296 1
                VUT_Error(vut, 1, "Missing -w option");
1297
1298 57
        if (vut->D_opt && !strcmp(CTX.w_arg, "-"))
1299 0
                VUT_Error(vut, 1, "Daemon cannot write to stdout");
1300
1301
        /* Check for valid grouping mode */
1302 57
        assert(vut->g_arg < VSL_g__MAX);
1303 57
        if (vut->g_arg != VSL_g_vxid && vut->g_arg != VSL_g_request)
1304 2
                VUT_Error(vut, 1, "Invalid grouping mode: %s",
1305 1
                    VSLQ_grouping[vut->g_arg]);
1306
1307
        /* Prepare output format */
1308 56
        parse_format(format);
1309 56
        REPLACE(format, NULL);
1310
1311
        /* Setup output */
1312 56
        vut->dispatch_f = dispatch_f;
1313 56
        vut->dispatch_priv = NULL;
1314 56
        if (CTX.w_arg) {
1315 3
                openout(CTX.a_opt);
1316 3
                AN(CTX.fo);
1317 3
                if (vut->D_opt)
1318 1
                        vut->sighup_f = rotateout;
1319 3
        } else
1320 53
                CTX.fo = stdout;
1321 56
        vut->idle_f = flushout;
1322
1323 56
        VUT_Setup(vut);
1324 56
        (void)VUT_Main(vut);
1325 56
        VUT_Fini(&vut);
1326
1327 56
        exit(0);
1328
}