varnish-cache/lib/libvarnishapi/vut.c
1
/*-
2
 * Copyright (c) 2006 Verdens Gang AS
3
 * Copyright (c) 2006-2015 Varnish Software AS
4
 * All rights reserved.
5
 *
6
 * Author: Martin Blix Grydeland <martin@varnish-software.com>
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
 * Common functions for the utilities
30
 */
31
32
#include "config.h"
33
34
#include <ctype.h>
35
#include <stdint.h>
36
#include <stdarg.h>
37
#include <stdlib.h>
38
#include <unistd.h>
39
#include <stdio.h>
40
#include <errno.h>
41
#include <string.h>
42
#include <signal.h>
43
#include <sys/stat.h> /* for MUSL */
44
45
#include "compat/daemon.h"
46
#include "vdef.h"
47
#include "vpf.h"
48
#include "vapi/vsm.h"
49
#include "vapi/vsl.h"
50
#include "vtim.h"
51
#include "vas.h"
52
#include "miniobj.h"
53
#include "vcs.h"
54
55
#include "vut.h"
56
57
#include "vapi/voptget.h"
58
59
static int vut_synopsis(const struct vopt_spec *);
60
static int vut_options(const struct vopt_spec *);
61
62
static struct vpf_fh    *pfh;
63
static unsigned         daemonized;
64
65
static struct VUT pfh_vut;
66
67
static int
68 6
vut_daemon(struct VUT *vut)
69
{
70 6
        if (daemonized)
71 0
                VUT_Error(vut, 1, "Already running as a daemon");
72 6
        daemonized = 1;
73 6
        return (varnish_daemon(0, 0));
74
}
75
76
static void
77 12
vut_vpf_remove(void)
78
{
79
80 12
        CHECK_OBJ(&pfh_vut, VUT_MAGIC);
81 12
        AN(pfh);
82 12
        AN(pfh_vut.P_arg);
83
84 12
        if (VPF_Remove(pfh) != 0)
85 3
                VUT_Error(&pfh_vut, 1, "Cannot remove pid file %s: %s",
86 3
                    pfh_vut.P_arg, strerror(errno));
87
88 9
        free(pfh_vut.P_arg);
89 9
        ZERO_OBJ(&pfh_vut, sizeof pfh_vut);
90 9
        pfh = NULL;
91 9
}
92
93
static int v_matchproto_(VSLQ_dispatch_f)
94 835
vut_dispatch(struct VSL_data *vsl, struct VSL_transaction * const trans[],
95
    void *priv)
96
{
97
        struct VUT *vut;
98
        int i;
99
100 835
        CAST_OBJ_NOTNULL(vut, priv, VUT_MAGIC);
101
102 835
        if (vut->k_arg == 0)
103 0
                return (-1);    /* End of file */
104 835
        AN(vut->dispatch_f);
105 835
        i = vut->dispatch_f(vsl, trans, vut->dispatch_priv);
106 835
        if (vut->k_arg > 0)
107 9
                vut->k_arg--;
108 835
        if (i >= 0 && vut->k_arg == 0)
109 3
                return (-1);    /* End of file */
110 832
        return (i);
111
}
112
113
//lint -sem(vut_error, r_no)
114
static void v_noreturn_ v_matchproto_(VUT_error_f)
115 168
vut_error(struct VUT *vut, int status, const char *fmt, va_list ap)
116
{
117
118 168
        CHECK_OBJ_NOTNULL(vut, VUT_MAGIC);
119 168
        AN(fmt);
120 168
        vfprintf(stderr, fmt, ap);
121 168
        fprintf(stderr, "\n");
122
123 168
        exit(status);
124
}
125
126
void
127 168
VUT_Error(struct VUT *vut, int status, const char *fmt, ...)
128
{
129
        va_list ap;
130
131 168
        CHECK_OBJ_NOTNULL(vut, VUT_MAGIC);
132 168
        AN(vut->error_f);
133 168
        AN(status);
134
135 168
        va_start(ap, fmt);
136 168
        vut->error_f(vut, status, fmt, ap);
137 0
        va_end(ap);
138 0
        exit(2);
139
}
140
141
int
142 393
VUT_Arg(struct VUT *vut, int opt, const char *arg)
143
{
144
        int i;
145
        char *p;
146
147 393
        switch (opt) {
148
        case 'd':
149
                /* Head */
150 87
                vut->d_opt = 1;
151 87
                return (1);
152
        case 'D':
153
                /* Daemon mode */
154 12
                vut->D_opt = 1;
155 12
                return (1);
156
        case 'g':
157
                /* Grouping */
158 27
                AN(arg);
159 27
                vut->g_arg = VSLQ_Name2Grouping(arg, -1);
160 27
                if (vut->g_arg == -2)
161 3
                        VUT_Error(vut, 1, "Ambiguous grouping type: %s", arg);
162 24
                else if (vut->g_arg < 0)
163 3
                        VUT_Error(vut, 1, "Unknown grouping type: %s", arg);
164 21
                return (1);
165
        case 'k':
166
                /* Log transaction limit */
167 6
                AN(arg);
168 6
                vut->k_arg = (int)strtol(arg, &p, 10);
169 6
                if (*p != '\0' || vut->k_arg <= 0)
170 3
                        VUT_Error(vut, 1, "-k: Invalid number '%s'", arg);
171 3
                return (1);
172
        case 'n':
173
                /* Varnish instance name */
174 114
                AN(arg);
175 114
                REPLACE(vut->n_arg, arg);
176 114
                return (1);
177
        case 'P':
178
                /* PID file */
179 12
                AN(arg);
180 12
                REPLACE(vut->P_arg, arg);
181 12
                return (1);
182
        case 'q':
183
                /* Query to use */
184 30
                AN(arg);
185 30
                REPLACE(vut->q_arg, arg);
186 30
                return (1);
187
        case 'r':
188
                /* Binary file input */
189 12
                AN(arg);
190 12
                REPLACE(vut->r_arg, arg);
191 12
                return (1);
192
        case 't':
193
                /* VSM connect timeout */
194 9
                REPLACE(vut->t_arg, arg);
195 9
                return (1);
196
        case 'V':
197
                /* Print version number and exit */
198 15
                VCS_Message(vut->progname);
199 15
                exit(0);
200
        default:
201 69
                AN(vut->vsl);
202 69
                i = VSL_Arg(vut->vsl, opt, arg);
203 69
                if (i < 0)
204 27
                        VUT_Error(vut, 1, "%s", VSL_Error(vut->vsl));
205 42
                return (i);
206
        }
207
}
208
209
struct VUT *
210 366
VUT_Init(const char *progname, int argc, char * const *argv,
211
    const struct vopt_spec *voc)
212
{
213
        struct VUT *vut;
214
215 366
        AN(progname);
216 366
        AN(argv);
217 366
        AN(voc);
218
219 366
        ALLOC_OBJ(vut, VUT_MAGIC);
220 366
        AN(vut);
221
222 366
        if (argc == 2 && !strcmp(argv[1], "--synopsis"))
223 15
                exit(vut_synopsis(voc));
224 351
        if (argc == 2 && !strcmp(argv[1], "--options"))
225 15
                exit(vut_options(voc));
226 336
        if (argc == 2 && !strcmp(argv[1], "--optstring")) {
227 0
                (void)printf("%s\n", voc->vopt_optstring);
228 0
                exit(0);
229
        }
230
231 336
        vut->progname = progname;
232 336
        vut->g_arg = VSL_g_vxid;
233 336
        vut->k_arg = -1;
234 336
        vut->error_f = vut_error;
235 336
        AZ(vut->vsl);
236 336
        vut->vsl = VSL_New();
237 336
        AN(vut->vsl);
238 336
        return (vut);
239
}
240
241
void
242 132
VUT_Signal(VUT_sighandler_f sig_cb)
243
{
244
245 132
        AN(sig_cb);
246 132
        (void)signal(SIGHUP, sig_cb);
247 132
        (void)signal(SIGINT, sig_cb);
248 132
        (void)signal(SIGTERM, sig_cb);
249 132
        (void)signal(SIGUSR1, sig_cb);
250 132
}
251
252
void
253 33
VUT_Signaled(struct VUT *vut, int sig)
254
{
255
256 33
        CHECK_OBJ_NOTNULL(vut, VUT_MAGIC);
257 33
        vut->sighup |= (int)(sig == SIGHUP);
258 33
        vut->sigint |= (int)(sig == SIGINT || sig == SIGTERM);
259 33
        vut->sigusr1 |= (int)(sig == SIGUSR1);
260 33
}
261
262
void
263 132
VUT_Setup(struct VUT *vut)
264
{
265
        struct VSL_cursor *c;
266
267 132
        CHECK_OBJ_NOTNULL(vut, VUT_MAGIC);
268 132
        AN(vut->vsl);
269 132
        AZ(vut->vsm);
270 132
        AZ(vut->vslq);
271
272
        /* Check input arguments (2 used for bug in FlexeLint) */
273 264
        if ((vut->n_arg == NULL ? 0 : 2) +
274 132
            (vut->r_arg == NULL ? 0 : 2) > 2)
275 3
                VUT_Error(vut, 1, "Only one of -n and -r options may be used");
276
277
        /* Create and validate the query expression */
278 129
        vut->vslq = VSLQ_New(vut->vsl, NULL,
279 129
            (enum VSL_grouping_e)vut->g_arg, vut->q_arg);
280 129
        if (vut->vslq == NULL)
281 27
                VUT_Error(vut, 1, "Query expression error:\n%s",
282 27
                    VSL_Error(vut->vsl));
283
284
        /* Setup input */
285 102
        if (vut->r_arg) {
286 9
                c = VSL_CursorFile(vut->vsl, vut->r_arg, 0);
287 9
                if (c == NULL)
288 0
                        VUT_Error(vut, 1, "%s", VSL_Error(vut->vsl));
289 9
                VSLQ_SetCursor(vut->vslq, &c);
290 9
                AZ(c);
291
        } else {
292 93
                vut->vsm = VSM_New();
293 93
                AN(vut->vsm);
294 93
                if (vut->n_arg && VSM_Arg(vut->vsm, 'n', vut->n_arg) <= 0)
295 0
                        VUT_Error(vut, 1, "%s", VSM_Error(vut->vsm));
296 93
                if (vut->t_arg && VSM_Arg(vut->vsm, 't', vut->t_arg) <= 0)
297 9
                        VUT_Error(vut, 1, "%s", VSM_Error(vut->vsm));
298 84
                if (VSM_Attach(vut->vsm, STDERR_FILENO))
299 0
                        VUT_Error(vut, 1, "VSM: %s", VSM_Error(vut->vsm));
300
                // Cursor is handled in VUT_Main()
301
        }
302
303
        /* Open PID file */
304 93
        if (vut->P_arg) {
305 12
                if (pfh != NULL)
306 0
                        VUT_Error(vut, 1, "PID file already created");
307 12
                pfh = VPF_Open(vut->P_arg, 0644, NULL);
308 12
                if (pfh == NULL)
309 0
                        VUT_Error(vut, 1, "%s: %s", vut->P_arg, strerror(errno));
310
        }
311
312
        /* Daemon mode */
313 93
        if (vut->D_opt && vut_daemon(vut) == -1)
314 0
                VUT_Error(vut, 1, "Daemon mode: %s", strerror(errno));
315
316
        /* Write PID and setup exit handler */
317 93
        if (vut->P_arg) {
318 12
                AN(pfh);
319 12
                AZ(VPF_Write(pfh));
320
321
                /* NB: move ownership to a global pseudo-VUT. */
322 12
                INIT_OBJ(&pfh_vut, VUT_MAGIC);
323 12
                pfh_vut.P_arg = vut->P_arg;
324 12
                pfh_vut.error_f = vut->error_f;
325 12
                vut->P_arg = NULL;
326
327 12
                AZ(atexit(vut_vpf_remove));
328
        }
329 93
}
330
331
void
332 93
VUT_Fini(struct VUT **vutp)
333
{
334
        struct VUT *vut;
335
336 93
        TAKE_OBJ_NOTNULL(vut, vutp, VUT_MAGIC);
337 93
        AN(vut->progname);
338
339 93
        free(vut->n_arg);
340 93
        free(vut->q_arg);
341 93
        free(vut->r_arg);
342 93
        free(vut->t_arg);
343 93
        AZ(vut->P_arg);
344
345 93
        if (vut->vslq)
346 93
                VSLQ_Delete(&vut->vslq);
347 93
        if (vut->vsl)
348 93
                VSL_Delete(vut->vsl);
349 93
        if (vut->vsm)
350 84
                VSM_Destroy(&vut->vsm);
351
352 93
        memset(vut, 0, sizeof *vut);
353 93
        FREE_OBJ(vut);
354 93
}
355
356
int
357 93
VUT_Main(struct VUT *vut)
358
{
359
        struct VSL_cursor *c;
360 93
        int i = -1;
361 93
        int hascursor = -1;
362
363 93
        CHECK_OBJ_NOTNULL(vut, VUT_MAGIC);
364 93
        AN(vut->vslq);
365
366 11684
        while (!vut->sigint) {
367 11570
                if (vut->sighup && vut->sighup_f) {
368
                        /* sighup callback */
369 6
                        vut->sighup = 0;
370 6
                        i = vut->sighup_f(vut);
371 6
                        if (i)
372 0
                                break;
373
                }
374
375 11570
                if (vut->sigusr1) {
376
                        /* Flush and report any incomplete records */
377 0
                        vut->sigusr1 = 0;
378 0
                        (void)VSLQ_Flush(vut->vslq, vut_dispatch, vut);
379
                }
380
381
                /* We must repeatedly call VSM_Status() when !hascursor
382
                 * to make VSM discover our segment.
383
                 *
384
                 * XXX consider moving the error handling to VSLQ_Dispatch.
385
                 * or some other VSL utility function
386
                 * Reasons:
387
                 *
388
                 * - it does not seem to make much sense to call VSM_StillValid
389
                 *   in vsl if that can only detect invalid segments after
390
                 *   VSM_Status has run, so it appears both should be
391
                 *   consolidated
392
                 *
393
                 * - not all VSL Clients will use VUT, yet the log abandoned/
394
                 *   overrun situation will be occur for all of them.
395
                 */
396
397 22510
                if (vut->vsm != NULL &&
398 10940
                    (VSM_Status(vut->vsm) & VSM_WRK_RESTARTED)) {
399 0
                        if (hascursor < 1) {
400 0
                                fprintf(stderr, "Log abandoned (vsm)\n");
401 0
                                VSLQ_SetCursor(vut->vslq, NULL);
402 0
                                hascursor = 0;
403
                        }
404
                }
405 11570
                if (vut->vsm != NULL && hascursor < 1) {
406
                        /* Reconnect VSM */
407 84
                        AZ(vut->r_arg);
408 84
                        VTIM_sleep(0.1);
409 84
                        c = VSL_CursorVSM(vut->vsl, vut->vsm,
410 84
                            (vut->d_opt ? VSL_COPT_TAILSTOP : VSL_COPT_TAIL)
411
                            | VSL_COPT_BATCH);
412 84
                        if (c == NULL) {
413 0
                                VSL_ResetError(vut->vsl);
414 0
                                continue;
415
                        }
416 84
                        if (hascursor >= 0)
417 0
                                fprintf(stderr, "Log reacquired\n");
418 84
                        hascursor = 1;
419 84
                        VSLQ_SetCursor(vut->vslq, &c);
420 84
                        AZ(c);
421
                }
422
423 11570
                i = VSLQ_Dispatch(vut->vslq, vut_dispatch, vut);
424 11570
                if (i == vsl_more)
425 2589
                        continue;
426 8981
                else if (i == vsl_end) {
427 8909
                        if (vut->idle_f) {
428 5922
                                i = vut->idle_f(vut);
429 5922
                                if (i)
430 0
                                        break;
431
                        }
432 8909
                        VTIM_sleep(0.01);
433 8909
                        continue;
434 72
                } else if (i == vsl_e_eof)
435 72
                        break;
436
437 0
                if (vut->vsm == NULL)
438 0
                        break;
439
440
                /* XXX: Make continuation optional */
441
442 0
                (void)VSLQ_Flush(vut->vslq, vut_dispatch, vut);
443
444 0
                if (i == vsl_e_abandon) {
445 0
                        fprintf(stderr, "Log abandoned (vsl)\n");
446 0
                        VSLQ_SetCursor(vut->vslq, NULL);
447 0
                        hascursor = 0;
448 0
                } else if (i == vsl_e_overrun) {
449 0
                        fprintf(stderr, "Log overrun\n");
450 0
                        VSLQ_SetCursor(vut->vslq, NULL);
451 0
                        hascursor = 0;
452
                } else
453 0
                        fprintf(stderr, "Error %d from VSLQ_Dispatch()", i);
454
        }
455
456 93
        return (i);
457
}
458
459
/**********************************************************************/
460
461
462
static void
463 276
print_nobrackets(const char *s)
464
{
465
        const char *e;
466
467
        /* Remove whitespace */
468 552
        while (isspace(*s))
469 0
                s++;
470 276
        e = s + strlen(s);
471 552
        while (e > s && isspace(e[-1]))
472 0
                e--;
473
474
        /* Remove outer layer brackets if present */
475 276
        if (e > s && *s == '[' && e[-1] == ']') {
476 276
                s++;
477 276
                e--;
478
        }
479
480 276
        printf("%.*s", (int)(e - s), s);
481 276
}
482
483
static void
484 276
print_tabbed(const char *string, int tabs)
485
{
486
        int i;
487
        const char *c;
488
489 36561
        for (c = string; *c; c++) {
490 36285
                if (c == string || *(c - 1) == '\n')
491 600
                        for (i = 0; i < tabs; i++)
492 300
                                printf("\t");
493 36285
                printf("%c", *c);
494
        }
495 276
}
496
497
static void
498 276
print_opt(const struct vopt_list *opt)
499
{
500 276
        print_nobrackets(opt->synopsis);
501 276
        printf("\n\n");
502 276
        print_tabbed(opt->ldesc, 1);
503 276
        printf("\n\n");
504 276
}
505
506
static int
507 15
vut_synopsis(const struct vopt_spec *voc)
508
{
509 15
        printf(".. |synopsis| replace:: %s\n", voc->vopt_synopsis);
510 15
        return (0);
511
}
512
513
static int
514 15
vut_options(const struct vopt_spec *voc)
515
{
516
        int i;
517
518 291
        for (i = 0; i < voc->vopt_list_n; i++)
519 276
                print_opt(&voc->vopt_list[i]);
520 15
        printf("--optstring\n"
521
            "\tPrint the optstring parameter to ``getopt(3)`` to help"
522
            " writing wrapper scripts.\n\n");
523 15
        return (0);
524
}