vinyl-cache/bin/vinyltop/vinyltop.c
0
/*-
1
 * Copyright (c) 2006 Verdens Gang AS
2
 * Copyright (c) 2006-2015 Varnish Software AS
3
 * All rights reserved.
4
 *
5
 * Author: Poul-Henning Kamp <phk@phk.freebsd.dk>
6
 * Author: Dag-Erling Smørgrav <des@des.no>
7
 * Author: Guillaume Quintard <guillaume.quintard@gmail.com>
8
 *
9
 * SPDX-License-Identifier: BSD-2-Clause
10
 *
11
 * Redistribution and use in source and binary forms, with or without
12
 * modification, are permitted provided that the following conditions
13
 * are met:
14
 * 1. Redistributions of source code must retain the above copyright
15
 *    notice, this list of conditions and the following disclaimer.
16
 * 2. Redistributions in binary form must reproduce the above copyright
17
 *    notice, this list of conditions and the following disclaimer in the
18
 *    documentation and/or other materials provided with the distribution.
19
 *
20
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23
 * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
24
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30
 * SUCH DAMAGE.
31
 *
32
 * Log tailer for Vinyl
33
 */
34
35
#include "config.h"
36
37
#include <ctype.h>
38
#include <pthread.h>
39
#include <signal.h>
40
#include <stdarg.h>
41
#include <stdint.h>
42
#include <stdlib.h>
43
#include <string.h>
44
#include <unistd.h>
45
46
#define VOPT_DEFINITION
47
#define VOPT_INC "vinyltop_options.h"
48
49
#include "vdef.h"
50
51
#include "vcurses.h"
52
#include "vapi/vsl.h"
53
#include "vapi/vsm.h"
54
#include "vapi/voptget.h"
55
#include "vas.h"
56
#include "vtree.h"
57
#include "vut.h"
58
#include "vapi/vsig.h"
59
60
static struct VUT *vut;
61
62
struct top {
63
        uint8_t                 tag;
64
        const char              *rec_data;
65
        char                    *rec_buf;
66
        int                     clen;
67
        unsigned                hash;
68
        VRBT_ENTRY(top)         e_order;
69
        VRBT_ENTRY(top)         e_key;
70
        double                  count;
71
};
72
73
static unsigned period = 60; /* seconds */
74
static int end_of_file = 0;
75
static unsigned ntop;
76
static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
77
static int f_flag = 0;
78
static unsigned maxfieldlen = 0;
79
static const char *ident;
80
81
static VRBT_HEAD(t_order, top) h_order = VRBT_INITIALIZER(&h_order);
82
static VRBT_HEAD(t_key, top) h_key = VRBT_INITIALIZER(&h_key);
83
84
static inline int
85 26640
cmp_key(const struct top *a, const struct top *b)
86
{
87 26640
        if (a->hash != b->hash)
88 25212
                return (a->hash - b->hash);
89 1428
        if (a->tag != b->tag)
90 1320
                return (a->tag - b->tag);
91 108
        if (a->clen != b->clen)
92 0
                return (a->clen - b->clen);
93 108
        return (memcmp(a->rec_data, b->rec_data, a->clen));
94 26640
}
95
96
static inline int
97 9216
cmp_order(const struct top *a, const struct top *b)
98
{
99 9216
        if (a->count > b->count)
100 408
                return (-1);
101 8808
        else if (a->count < b->count)
102 24
                return (1);
103 8784
        return (cmp_key(a, b));
104 9216
}
105
106 5557
VRBT_GENERATE_INSERT_COLOR(t_order, top, e_order, static)
107 1884
VRBT_GENERATE_INSERT_FINISH(t_order, top, e_order, static)
108 11100
VRBT_GENERATE_INSERT(t_order, top, e_order, cmp_order, static)
109 120
VRBT_GENERATE_REMOVE_COLOR(t_order, top, e_order, static)
110 468
VRBT_GENERATE_MINMAX(t_order, top, e_order, static)
111 7925
VRBT_GENERATE_NEXT(t_order, top, e_order, static)
112 240
VRBT_GENERATE_REMOVE(t_order, top, e_order, static)
113
114 5428
VRBT_GENERATE_INSERT_COLOR(t_key, top, e_key, static)
115 0
VRBT_GENERATE_REMOVE_COLOR(t_key, top, e_key, static)
116 1812
VRBT_GENERATE_INSERT_FINISH(t_key, top, e_key, static)
117 10608
VRBT_GENERATE_INSERT(t_key, top, e_key, cmp_key, static)
118 0
VRBT_GENERATE_REMOVE(t_key, top, e_key, static)
119 10872
VRBT_GENERATE_FIND(t_key, top, e_key, cmp_key, static)
120
121
static int v_matchproto_(VSLQ_dispatch_f)
122 120
accumulate(struct VSL_data *vsl, struct VSL_transaction * const pt[],
123
        void *priv)
124
{
125
        struct top *tp, t;
126
        unsigned int u;
127
        unsigned tag;
128
        const char *b, *e, *p;
129
        unsigned len;
130
        struct VSL_transaction *tr;
131
132 120
        (void)priv;
133
134 240
        for (tr = pt[0]; tr != NULL; tr = *++pt) {
135 3772
                while ((1 == VSL_Next(tr->c))) {
136 3652
                        tag = VSL_TAG(tr->c->rec.ptr);
137 3652
                        if (VSL_tagflags[tag])
138 440
                                continue;
139 3212
                        if (!VSL_Match(vsl, tr->c))
140 1208
                                continue;
141 2004
                        b = VSL_CDATA(tr->c->rec.ptr);
142 2004
                        e = b + VSL_LEN(tr->c->rec.ptr);
143 2004
                        u = 0;
144 36988
                        for (p = b; p <= e; p++) {
145 36988
                                if (*p == '\0')
146 1972
                                        break;
147 35016
                                if (f_flag && (*p == ':' || isspace(*p)))
148 32
                                        break;
149 34984
                                u += *p;
150 34984
                        }
151 2004
                        len = p - b;
152 2004
                        if (len == 0)
153 120
                                continue;
154
155 1884
                        t.hash = u;
156 1884
                        t.tag = tag;
157 1884
                        t.clen = len;
158 1884
                        t.rec_data = VSL_CDATA(tr->c->rec.ptr);
159
160 1884
                        PTOK(pthread_mutex_lock(&mtx));
161 1884
                        tp = VRBT_FIND(t_key, &h_key, &t);
162 1884
                        if (tp) {
163 72
                                VRBT_REMOVE(t_order, &h_order, tp);
164 72
                                tp->count += 1.0;
165
                                /* Reinsert to rebalance */
166 72
                                VRBT_INSERT(t_order, &h_order, tp);
167 72
                        } else {
168 1812
                                ntop++;
169 1812
                                tp = calloc(1, sizeof *tp);
170 1812
                                assert(tp != NULL);
171 1812
                                tp->hash = u;
172 1812
                                tp->count = 1.0;
173 1812
                                tp->clen = len;
174 1812
                                tp->tag = tag;
175 1812
                                tp->rec_buf = strdup(t.rec_data);
176 1812
                                tp->rec_data = tp->rec_buf;
177 1812
                                AN(tp->rec_data);
178 1812
                                VRBT_INSERT(t_key, &h_key, tp);
179 1812
                                VRBT_INSERT(t_order, &h_order, tp);
180
                        }
181 1884
                        PTOK(pthread_mutex_unlock(&mtx));
182
183
                }
184 120
        }
185
186 120
        return (0);
187
}
188
189
static void
190 80
update(unsigned p)
191
{
192
        struct top *tp, *tp2;
193
        int l, len;
194 80
        double t = 0;
195
        static time_t last = 0;
196
        static unsigned n = 0;
197
        const char *q;
198
        time_t now;
199
200 80
        now = time(NULL);
201 80
        if (now == last)
202 3
                return;
203 77
        last = now;
204
205 77
        l = 1;
206 77
        if (n < p)
207 77
                n++;
208 77
        AC(erase());
209 77
        q = ident;
210 77
        len = COLS - vstrlen(q);
211 77
        if (end_of_file)
212 8
                IC(mvprintw(0, len - (1 + 6), "%s (EOF)", q));
213
        else
214 69
                IC(mvprintw(0, len - 1, "%s", q));
215 77
        IC(mvprintw(0, 0, "list length %u", ntop));
216 3581
        for (tp = VRBT_MIN(t_order, &h_order); tp != NULL; tp = tp2) {
217 3504
                tp2 = VRBT_NEXT(t_order, &h_order, tp);
218
219 3504
                if (++l < LINES) {
220 1084
                        len = vmin(tp->clen, COLS - 20);
221 1084
                        IC(mvprintw(l, 0, "%9.2f %-*.*s %*.*s\n",
222
                                tp->count, maxfieldlen, maxfieldlen,
223
                                VSL_tags[tp->tag],
224
                                len, len, tp->rec_data));
225 1084
                        t = tp->count;
226 1084
                }
227 3504
                if (end_of_file)
228 292
                        continue;
229 3212
                tp->count += (1.0/3.0 - tp->count) / (double)n;
230 3212
                if (tp->count * 10 < t || l > LINES * 10) {
231 0
                        VRBT_REMOVE(t_key, &h_key, tp);
232 0
                        VRBT_REMOVE(t_order, &h_order, tp);
233 0
                        free(tp->rec_buf);
234 0
                        free(tp);
235 0
                        ntop--;
236 0
                }
237 3212
        }
238 77
        AC(refresh());
239 80
}
240
241
static void *
242 20
do_curses(void *arg)
243
{
244
        int i;
245
246 20
        (void)arg;
247 5140
        for (i = 0; i < 256; i++) {
248 5120
                if (VSL_tags[i] == NULL)
249 3240
                        continue;
250 1880
                if (maxfieldlen < vstrlen(VSL_tags[i]))
251 140
                        maxfieldlen = vstrlen(VSL_tags[i]);
252 1880
        }
253
254 20
        (void)initscr();
255 20
        AC(raw());
256 20
        AC(noecho());
257 20
        AC(nonl());
258 20
        AC(intrflush(stdscr, FALSE));
259 20
        IC(curs_set(0));
260 20
        AC(erase());
261 20
        timeout(1000);
262 100
        while (!VSIG_int && !VSIG_term && !VSIG_hup) {
263 80
                PTOK(pthread_mutex_lock(&mtx));
264 80
                update(period);
265 80
                PTOK(pthread_mutex_unlock(&mtx));
266
267 80
                switch (getch()) {
268
                case ERR:
269 56
                        break;
270
#ifdef KEY_RESIZE
271
                case KEY_RESIZE:
272 4
                        AC(erase());
273 4
                        break;
274
#endif
275
                case '\014': /* Ctrl-L */
276
                case '\024': /* Ctrl-T */
277 4
                        AC(redrawwin(stdscr));
278 4
                        AC(refresh());
279 4
                        break;
280
                case '\032': /* Ctrl-Z */
281 0
                        AC(endwin());
282 0
                        AZ(raise(SIGTSTP));
283 0
                        break;
284
                case '\003': /* Ctrl-C */
285
                case '\021': /* Ctrl-Q */
286
                case 'Q':
287
                case 'q':
288 12
                        AZ(raise(SIGINT));
289 12
                        break;
290
                default:
291 4
                        AC(beep());
292 4
                        break;
293
                }
294
        }
295 20
        AC(endwin());
296 20
        return (NULL);
297
}
298
299
static void
300 20
dump(void)
301
{
302
        struct top *tp, *tp2;
303 664
        for (tp = VRBT_MIN(t_order, &h_order); tp != NULL; tp = tp2) {
304 644
                tp2 = VRBT_NEXT(t_order, &h_order, tp);
305 644
                printf("%9.2f %s %*.*s\n",
306 644
                        tp->count, VSL_tags[tp->tag],
307 644
                        tp->clen, tp->clen, tp->rec_data);
308 644
        }
309 20
}
310
311
int
312 56
main(int argc, char **argv)
313
{
314 56
        int o, once = 0;
315
        pthread_t thr;
316 56
        char *e = NULL;
317
318 56
        vut = VUT_InitProg(argc, argv, &vopt_spec);
319 56
        AN(vut);
320
321 144
        while ((o = getopt(argc, argv, vopt_spec.vopt_optstring)) != -1) {
322 100
                switch (o) {
323
                case '1':
324 20
                        AN(VUT_Arg(vut, 'd', NULL));
325 20
                        once = 1;
326 20
                        break;
327
                case 'f':
328 4
                        f_flag = 1;
329 4
                        break;
330
                case 'h':
331
                        /* Usage help */
332 4
                        VUT_Usage(vut, &vopt_spec, 0);
333
                case 'p':
334 8
                        errno = 0;
335 8
                        e = NULL;
336 8
                        period = strtoul(optarg, &e, 0);
337 8
                        if (errno != 0 || e == NULL || *e != '\0') {
338 8
                                fprintf(stderr,
339 4
                                    "Syntax error, %s is not a number", optarg);
340 4
                                exit(1);
341
                        }
342 4
                        break;
343
                default:
344 64
                        if (!VUT_Arg(vut, o, optarg))
345 4
                                VUT_Usage(vut, &vopt_spec, 1);
346 60
                }
347
        }
348
349 44
        if (optind != argc)
350 4
                VUT_Usage(vut, &vopt_spec, 1);
351
352 40
        VUT_Setup(vut);
353 40
        if (vut->vsm)
354 40
                ident = VSM_Dup(vut->vsm, "Arg", "-i");
355
        else
356 0
                ident = strdup("");
357 40
        AN(ident);
358 40
        vut->dispatch_f = accumulate;
359 40
        vut->dispatch_priv = NULL;
360 40
        if (once) {
361 20
                (void)VUT_Main(vut);
362 20
                dump();
363 20
        } else {
364 20
                PTOK(pthread_create(&thr, NULL, do_curses, NULL));
365 20
                (void)VUT_Main(vut);
366 20
                end_of_file = 1;
367 20
                PTOK(pthread_join(thr, NULL));
368
        }
369 40
        VUT_Fini(&vut);
370 40
        return (0);
371
}