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 8
vut_daemon(struct VUT *vut)
69
{
70 8
        if (daemonized)
71 0
                VUT_Error(vut, 1, "Already running as a daemon");
72 8
        daemonized = 1;
73 8
        return (varnish_daemon(0, 0));
74
}
75
76
static void
77 16
vut_vpf_remove(void)
78
{
79
80 16
        CHECK_OBJ(&pfh_vut, VUT_MAGIC);
81 16
        AN(pfh);
82 16
        AN(pfh_vut.P_arg);
83
84 16
        if (VPF_Remove(pfh) != 0)
85 4
                VUT_Error(&pfh_vut, 1, "Cannot remove pid file %s: %s",
86 4
                    pfh_vut.P_arg, strerror(errno));
87
88 12
        free(pfh_vut.P_arg);
89 12
        ZERO_OBJ(&pfh_vut, sizeof pfh_vut);
90 12
        pfh = NULL;
91 12
}
92
93
static int v_matchproto_(VSLQ_dispatch_f)
94 1112
vut_dispatch(struct VSL_data *vsl, struct VSL_transaction * const trans[],
95
    void *priv)
96
{
97
        struct VUT *vut;
98
        int i;
99
100 1112
        CAST_OBJ_NOTNULL(vut, priv, VUT_MAGIC);
101
102 1112
        if (vut->k_arg == 0)
103 0
                return (-1);    /* End of file */
104 1112
        AN(vut->dispatch_f);
105 1112
        i = vut->dispatch_f(vsl, trans, vut->dispatch_priv);
106 1112
        if (vut->k_arg > 0)
107 12
                vut->k_arg--;
108 1112
        if (i >= 0 && vut->k_arg == 0)
109 4
                return (-1);    /* End of file */
110 1108
        return (i);
111
}
112
113
//lint -sem(vut_error, r_no)
114
static void v_noreturn_ v_matchproto_(VUT_error_f)
115 232
vut_error(struct VUT *vut, int status, const char *fmt, va_list ap)
116
{
117
118 232
        CHECK_OBJ_NOTNULL(vut, VUT_MAGIC);
119 232
        AN(fmt);
120 232
        vfprintf(stderr, fmt, ap);
121 232
        fprintf(stderr, "\n");
122
123 232
        exit(status);
124
}
125
126
void
127 232
VUT_Error(struct VUT *vut, int status, const char *fmt, ...)
128
{
129
        va_list ap;
130
131 232
        CHECK_OBJ_NOTNULL(vut, VUT_MAGIC);
132 232
        AN(vut->error_f);
133 232
        AN(status);
134
135 232
        va_start(ap, fmt);
136 232
        vut->error_f(vut, status, fmt, ap);
137 0
        va_end(ap);
138 0
        exit(2);
139
}
140
141
int
142 548
VUT_Arg(struct VUT *vut, int opt, const char *arg)
143
{
144
        int i;
145
        char *p;
146
147 548
        switch (opt) {
148
        case 'd':
149
                /* Head */
150 116
                vut->d_opt = 1;
151 116
                return (1);
152
        case 'D':
153
                /* Daemon mode */
154 16
                vut->D_opt = 1;
155 16
                return (1);
156
        case 'g':
157
                /* Grouping */
158 36
                AN(arg);
159 36
                vut->g_arg = VSLQ_Name2Grouping(arg, -1);
160 36
                if (vut->g_arg == -2)
161 4
                        VUT_Error(vut, 1, "Ambiguous grouping type: %s", arg);
162 32
                else if (vut->g_arg < 0)
163 4
                        VUT_Error(vut, 1, "Unknown grouping type: %s", arg);
164 28
                return (1);
165
        case 'k':
166
                /* Log transaction limit */
167 8
                AN(arg);
168 8
                vut->k_arg = (int)strtol(arg, &p, 10);
169 8
                if (*p != '\0' || vut->k_arg <= 0)
170 4
                        VUT_Error(vut, 1, "-k: Invalid number '%s'", arg);
171 4
                return (1);
172
        case 'n':
173
                /* Varnish instance name */
174 160
                AN(arg);
175 160
                REPLACE(vut->n_arg, arg);
176 160
                return (1);
177
        case 'P':
178
                /* PID file */
179 16
                AN(arg);
180 16
                REPLACE(vut->P_arg, arg);
181 16
                return (1);
182
        case 'q':
183
                /* Query to use */
184 40
                AN(arg);
185 40
                REPLACE(vut->q_arg, arg);
186 40
                return (1);
187
        case 'r':
188
                /* Binary file input */
189 16
                AN(arg);
190 16
                REPLACE(vut->r_arg, arg);
191 16
                return (1);
192
        case 't':
193
                /* VSM connect timeout */
194 12
                REPLACE(vut->t_arg, arg);
195 12
                return (1);
196
        case 'V':
197
                /* Print version number and exit */
198 20
                VCS_Message(vut->progname);
199 20
                exit(0);
200
        default:
201 108
                AN(vut->vsl);
202 108
                i = VSL_Arg(vut->vsl, opt, arg);
203 108
                if (i < 0)
204 36
                        VUT_Error(vut, 1, "%s", VSL_Error(vut->vsl));
205 72
                return (i);
206
        }
207
}
208
209
struct VUT *
210 532
VUT_Init(const char *progname, int argc, char * const *argv,
211
    const struct vopt_spec *voc)
212
{
213
        struct VUT *vut;
214
215 532
        AN(progname);
216 532
        AN(argv);
217 532
        AN(voc);
218
219 532
        ALLOC_OBJ(vut, VUT_MAGIC);
220 532
        AN(vut);
221
222 532
        if (argc == 2 && !strcmp(argv[1], "--synopsis"))
223 20
                exit(vut_synopsis(voc));
224 512
        if (argc == 2 && !strcmp(argv[1], "--options"))
225 20
                exit(vut_options(voc));
226 492
        if (argc == 2 && !strcmp(argv[1], "--optstring")) {
227 0
                (void)printf("%s\n", voc->vopt_optstring);
228 0
                exit(0);
229
        }
230
231 492
        vut->progname = progname;
232 492
        vut->g_arg = VSL_g_vxid;
233 492
        vut->k_arg = -1;
234 492
        vut->error_f = vut_error;
235 492
        AZ(vut->vsl);
236 492
        vut->vsl = VSL_New();
237 492
        AN(vut->vsl);
238 492
        return (vut);
239
}
240
241
void
242 184
VUT_Signal(VUT_sighandler_f sig_cb)
243
{
244
245 184
        AN(sig_cb);
246 184
        (void)signal(SIGHUP, sig_cb);
247 184
        (void)signal(SIGINT, sig_cb);
248 184
        (void)signal(SIGTERM, sig_cb);
249 184
        (void)signal(SIGUSR1, sig_cb);
250 184
}
251
252
void
253 52
VUT_Signaled(struct VUT *vut, int sig)
254
{
255
256 52
        CHECK_OBJ_NOTNULL(vut, VUT_MAGIC);
257 52
        vut->sighup |= (int)(sig == SIGHUP);
258 52
        vut->sigint |= (int)(sig == SIGINT || sig == SIGTERM);
259 52
        vut->sigusr1 |= (int)(sig == SIGUSR1);
260 52
}
261
262
void
263 184
VUT_Setup(struct VUT *vut)
264
{
265
        struct VSL_cursor *c;
266
267 184
        CHECK_OBJ_NOTNULL(vut, VUT_MAGIC);
268 184
        AN(vut->vsl);
269 184
        AZ(vut->vsm);
270 184
        AZ(vut->vslq);
271
272
        /* Check input arguments (2 used for bug in FlexeLint) */
273 368
        if ((vut->n_arg == NULL ? 0 : 2) +
274 184
            (vut->r_arg == NULL ? 0 : 2) > 2)
275 4
                VUT_Error(vut, 1, "Only one of -n and -r options may be used");
276
277
        /* Create and validate the query expression */
278 180
        vut->vslq = VSLQ_New(vut->vsl, NULL,
279 180
            (enum VSL_grouping_e)vut->g_arg, vut->q_arg);
280 180
        if (vut->vslq == NULL)
281 36
                VUT_Error(vut, 1, "Query expression error:\n%s",
282 36
                    VSL_Error(vut->vsl));
283
284
        /* Setup input */
285 144
        if (vut->r_arg) {
286 12
                c = VSL_CursorFile(vut->vsl, vut->r_arg, 0);
287 12
                if (c == NULL)
288 0
                        VUT_Error(vut, 1, "%s", VSL_Error(vut->vsl));
289 12
                VSLQ_SetCursor(vut->vslq, &c);
290 12
                AZ(c);
291
        } else {
292 132
                vut->vsm = VSM_New();
293 132
                AN(vut->vsm);
294 132
                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 132
                if (vut->t_arg && VSM_Arg(vut->vsm, 't', vut->t_arg) <= 0)
297 12
                        VUT_Error(vut, 1, "%s", VSM_Error(vut->vsm));
298 120
                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 132
        if (vut->P_arg) {
305 16
                if (pfh != NULL)
306 0
                        VUT_Error(vut, 1, "PID file already created");
307 16
                pfh = VPF_Open(vut->P_arg, 0644, NULL);
308 16
                if (pfh == NULL)
309 0
                        VUT_Error(vut, 1, "%s: %s", vut->P_arg, strerror(errno));
310
        }
311
312
        /* Daemon mode */
313 132
        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 132
        if (vut->P_arg) {
318 16
                AN(pfh);
319 16
                AZ(VPF_Write(pfh));
320
321
                /* NB: move ownership to a global pseudo-VUT. */
322 16
                INIT_OBJ(&pfh_vut, VUT_MAGIC);
323 16
                pfh_vut.P_arg = vut->P_arg;
324 16
                pfh_vut.error_f = vut->error_f;
325 16
                vut->P_arg = NULL;
326
327 16
                AZ(atexit(vut_vpf_remove));
328
        }
329 132
}
330
331
void
332 132
VUT_Fini(struct VUT **vutp)
333
{
334
        struct VUT *vut;
335
336 132
        TAKE_OBJ_NOTNULL(vut, vutp, VUT_MAGIC);
337 132
        AN(vut->progname);
338
339 132
        free(vut->n_arg);
340 132
        free(vut->q_arg);
341 132
        free(vut->r_arg);
342 132
        free(vut->t_arg);
343 132
        AZ(vut->P_arg);
344
345 132
        if (vut->vslq)
346 132
                VSLQ_Delete(&vut->vslq);
347 132
        if (vut->vsl)
348 132
                VSL_Delete(vut->vsl);
349 132
        if (vut->vsm)
350 120
                VSM_Destroy(&vut->vsm);
351
352 132
        memset(vut, 0, sizeof *vut);
353 132
        FREE_OBJ(vut);
354 132
}
355
356
int
357 132
VUT_Main(struct VUT *vut)
358
{
359
        struct VSL_cursor *c;
360 132
        int i = -1;
361 132
        int hascursor = -1;
362
363 132
        CHECK_OBJ_NOTNULL(vut, VUT_MAGIC);
364 132
        AN(vut->vslq);
365
366 18698
        while (!vut->sigint) {
367 18534
                if (vut->sighup && vut->sighup_f) {
368
                        /* sighup callback */
369 12
                        vut->sighup = 0;
370 12
                        i = vut->sighup_f(vut);
371 12
                        if (i)
372 4
                                break;
373
                }
374
375 18530
                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 37048
                if (vut->vsm != NULL &&
398 18518
                    (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 18530
                if (vut->vsm != NULL && hascursor < 1) {
406
                        /* Reconnect VSM */
407 120
                        AZ(vut->r_arg);
408 120
                        VTIM_sleep(0.1);
409 120
                        c = VSL_CursorVSM(vut->vsl, vut->vsm,
410 120
                            (vut->d_opt ? VSL_COPT_TAILSTOP : VSL_COPT_TAIL)
411
                            | VSL_COPT_BATCH);
412 120
                        if (c == NULL) {
413 0
                                VSL_ResetError(vut->vsl);
414 0
                                continue;
415
                        }
416 120
                        if (hascursor >= 0)
417 0
                                fprintf(stderr, "Log reacquired\n");
418 120
                        hascursor = 1;
419 120
                        VSLQ_SetCursor(vut->vslq, &c);
420 120
                        AZ(c);
421
                }
422
423
                do
424 22114
                        i = VSLQ_Dispatch(vut->vslq, vut_dispatch, vut);
425 22114
                while (i == vsl_more && !vut->sighup && !vut->sigusr1);
426
427 18530
                if (i == vsl_more)
428 0
                        continue;
429 18530
                else if (i == vsl_end) {
430 18434
                        if (vut->idle_f) {
431 7858
                                i = vut->idle_f(vut);
432 7858
                                if (i)
433 0
                                        break;
434
                        }
435 18434
                        VTIM_sleep(0.01);
436 18434
                        continue;
437 96
                } else if (i == vsl_e_eof)
438 96
                        break;
439
440 0
                if (vut->vsm == NULL)
441 0
                        break;
442
443
                /* XXX: Make continuation optional */
444
445 0
                (void)VSLQ_Flush(vut->vslq, vut_dispatch, vut);
446
447 0
                if (i == vsl_e_abandon) {
448 0
                        fprintf(stderr, "Log abandoned (vsl)\n");
449 0
                        VSLQ_SetCursor(vut->vslq, NULL);
450 0
                        hascursor = 0;
451 0
                } else if (i == vsl_e_overrun) {
452 0
                        fprintf(stderr, "Log overrun\n");
453 0
                        VSLQ_SetCursor(vut->vslq, NULL);
454 0
                        hascursor = 0;
455
                } else
456 0
                        fprintf(stderr, "Error %d from VSLQ_Dispatch()", i);
457
        }
458
459 132
        return (i);
460
}
461
462
/**********************************************************************/
463
464
465
static void
466 368
print_nobrackets(const char *s)
467
{
468
        const char *e;
469
470
        /* Remove whitespace */
471 736
        while (isspace(*s))
472 0
                s++;
473 368
        e = s + strlen(s);
474 736
        while (e > s && isspace(e[-1]))
475 0
                e--;
476
477
        /* Remove outer layer brackets if present */
478 368
        if (e > s && *s == '[' && e[-1] == ']') {
479 368
                s++;
480 368
                e--;
481
        }
482
483 368
        printf("%.*s", (int)(e - s), s);
484 368
}
485
486
static void
487 368
print_tabbed(const char *string, int tabs)
488
{
489
        int i;
490
        const char *c;
491
492 48748
        for (c = string; *c; c++) {
493 48380
                if (c == string || *(c - 1) == '\n')
494 800
                        for (i = 0; i < tabs; i++)
495 400
                                printf("\t");
496 48380
                printf("%c", *c);
497
        }
498 368
}
499
500
static void
501 368
print_opt(const struct vopt_list *opt)
502
{
503 368
        print_nobrackets(opt->synopsis);
504 368
        printf("\n\n");
505 368
        print_tabbed(opt->ldesc, 1);
506 368
        printf("\n\n");
507 368
}
508
509
static int
510 20
vut_synopsis(const struct vopt_spec *voc)
511
{
512 20
        printf(".. |synopsis| replace:: %s\n", voc->vopt_synopsis);
513 20
        return (0);
514
}
515
516
static int
517 20
vut_options(const struct vopt_spec *voc)
518
{
519
        int i;
520
521 388
        for (i = 0; i < voc->vopt_list_n; i++)
522 368
                print_opt(&voc->vopt_list[i]);
523 20
        printf("--optstring\n"
524
            "\tPrint the optstring parameter to ``getopt(3)`` to help"
525
            " writing wrapper scripts.\n\n");
526 20
        return (0);
527
}