Coverage Report

Created: 2025-12-31 06:58

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/openssl36/ssl/quic/qlog.c
Line
Count
Source
1
/*
2
 * Copyright 2023-2025 The OpenSSL Project Authors. All Rights Reserved.
3
 *
4
 * Licensed under the Apache License 2.0 (the "License").  You may not use
5
 * this file except in compliance with the License.  You can obtain a copy
6
 * in the file LICENSE in the source distribution or at
7
 * https://www.openssl.org/source/license.html
8
 */
9
10
#include <stdbool.h>
11
#include "internal/qlog.h"
12
#include "internal/json_enc.h"
13
#include "internal/common.h"
14
#include "internal/cryptlib.h"
15
#include "crypto/ctype.h"
16
17
0
#define BITS_PER_WORD (sizeof(size_t) * 8)
18
#define NUM_ENABLED_W ((QLOG_EVENT_TYPE_NUM + BITS_PER_WORD - 1) / BITS_PER_WORD)
19
20
static ossl_unused ossl_inline int bit_get(const size_t *p, uint32_t bit_no)
21
0
{
22
0
    return p[bit_no / BITS_PER_WORD] & (((size_t)1) << (bit_no % BITS_PER_WORD));
23
0
}
24
25
static ossl_unused ossl_inline void bit_set(size_t *p, uint32_t bit_no, int enable)
26
0
{
27
0
    size_t mask = (((size_t)1) << (bit_no % BITS_PER_WORD));
28
29
0
    if (enable)
30
0
        p[bit_no / BITS_PER_WORD] |= mask;
31
0
    else
32
0
        p[bit_no / BITS_PER_WORD] &= ~mask;
33
0
}
34
35
struct qlog_st {
36
    QLOG_TRACE_INFO info;
37
38
    BIO *bio;
39
    size_t enabled[NUM_ENABLED_W];
40
    uint32_t event_type;
41
    const char *event_cat, *event_name, *event_combined_name;
42
    OSSL_TIME event_time, prev_event_time;
43
    OSSL_JSON_ENC json;
44
    int header_done, first_event_done;
45
};
46
47
static OSSL_TIME default_now(void *arg)
48
0
{
49
0
    return ossl_time_now();
50
0
}
51
52
/*
53
 * Construction
54
 * ============
55
 */
56
QLOG *ossl_qlog_new(const QLOG_TRACE_INFO *info)
57
0
{
58
0
    QLOG *qlog = OPENSSL_zalloc(sizeof(QLOG));
59
60
0
    if (qlog == NULL)
61
0
        return NULL;
62
63
0
    qlog->info.odcid = info->odcid;
64
0
    qlog->info.is_server = info->is_server;
65
0
    qlog->info.now_cb = info->now_cb;
66
0
    qlog->info.now_cb_arg = info->now_cb_arg;
67
0
    qlog->info.override_process_id = info->override_process_id;
68
69
0
    if (info->title != NULL
70
0
        && (qlog->info.title = OPENSSL_strdup(info->title)) == NULL)
71
0
        goto err;
72
73
0
    if (info->description != NULL
74
0
        && (qlog->info.description = OPENSSL_strdup(info->description)) == NULL)
75
0
        goto err;
76
77
0
    if (info->group_id != NULL
78
0
        && (qlog->info.group_id = OPENSSL_strdup(info->group_id)) == NULL)
79
0
        goto err;
80
81
0
    if (info->override_impl_name != NULL
82
0
        && (qlog->info.override_impl_name
83
0
               = OPENSSL_strdup(info->override_impl_name))
84
0
            == NULL)
85
0
        goto err;
86
87
0
    if (!ossl_json_init(&qlog->json, NULL,
88
0
            OSSL_JSON_FLAG_IJSON | OSSL_JSON_FLAG_SEQ))
89
0
        goto err;
90
91
0
    if (qlog->info.now_cb == NULL)
92
0
        qlog->info.now_cb = default_now;
93
94
0
    return qlog;
95
96
0
err:
97
0
    if (qlog != NULL) {
98
0
        OPENSSL_free((char *)qlog->info.title);
99
0
        OPENSSL_free((char *)qlog->info.description);
100
0
        OPENSSL_free((char *)qlog->info.group_id);
101
0
        OPENSSL_free((char *)qlog->info.override_impl_name);
102
0
        OPENSSL_free(qlog);
103
0
    }
104
0
    return NULL;
105
0
}
106
107
QLOG *ossl_qlog_new_from_env(const QLOG_TRACE_INFO *info)
108
50.4k
{
109
50.4k
    QLOG *qlog = NULL;
110
50.4k
    const char *qlogdir = ossl_safe_getenv("QLOGDIR");
111
50.4k
    const char *qfilter = ossl_safe_getenv("OSSL_QFILTER");
112
50.4k
    char qlogdir_sep, *filename = NULL;
113
50.4k
    size_t i, l, strl;
114
115
50.4k
    if (info == NULL || qlogdir == NULL)
116
50.4k
        return NULL;
117
118
0
    l = strlen(qlogdir);
119
0
    if (l == 0)
120
0
        return NULL;
121
122
0
    qlogdir_sep = ossl_determine_dirsep(qlogdir);
123
124
    /* dir; [sep]; ODCID; _; strlen("client" / "server"); strlen(".sqlog"); NUL */
125
0
    strl = l + 1 + info->odcid.id_len * 2 + 1 + 6 + 6 + 1;
126
0
    filename = OPENSSL_malloc(strl);
127
0
    if (filename == NULL)
128
0
        return NULL;
129
130
0
    memcpy(filename, qlogdir, l);
131
0
    if (qlogdir_sep != '\0')
132
0
        filename[l++] = qlogdir_sep;
133
134
0
    for (i = 0; i < info->odcid.id_len; ++i)
135
0
        l += BIO_snprintf(filename + l, strl - l, "%02x", info->odcid.id[i]);
136
137
0
    l += BIO_snprintf(filename + l, strl - l, "_%s.sqlog",
138
0
        info->is_server ? "server" : "client");
139
140
0
    qlog = ossl_qlog_new(info);
141
0
    if (qlog == NULL)
142
0
        goto err;
143
144
0
    if (!ossl_qlog_set_sink_filename(qlog, filename))
145
0
        goto err;
146
147
0
    if (qfilter == NULL || qfilter[0] == '\0')
148
0
        qfilter = "*";
149
150
0
    if (!ossl_qlog_set_filter(qlog, qfilter))
151
0
        goto err;
152
153
0
    OPENSSL_free(filename);
154
0
    return qlog;
155
156
0
err:
157
0
    OPENSSL_free(filename);
158
0
    ossl_qlog_free(qlog);
159
0
    return NULL;
160
0
}
161
162
void ossl_qlog_free(QLOG *qlog)
163
50.4k
{
164
50.4k
    if (qlog == NULL)
165
50.4k
        return;
166
167
0
    ossl_json_flush_cleanup(&qlog->json);
168
0
    BIO_free_all(qlog->bio);
169
0
    OPENSSL_free((char *)qlog->info.title);
170
0
    OPENSSL_free((char *)qlog->info.description);
171
0
    OPENSSL_free((char *)qlog->info.group_id);
172
0
    OPENSSL_free((char *)qlog->info.override_impl_name);
173
0
    OPENSSL_free(qlog);
174
0
}
175
176
/*
177
 * Configuration
178
 * =============
179
 */
180
int ossl_qlog_set_sink_bio(QLOG *qlog, BIO *bio)
181
0
{
182
0
    if (qlog == NULL)
183
0
        return 0;
184
185
0
    ossl_qlog_flush(qlog); /* best effort */
186
0
    BIO_free_all(qlog->bio);
187
0
    qlog->bio = bio;
188
0
    ossl_json_set0_sink(&qlog->json, bio);
189
0
    return 1;
190
0
}
191
192
#ifndef OPENSSL_NO_STDIO
193
194
int ossl_qlog_set_sink_file(QLOG *qlog, FILE *f, int close_flag)
195
0
{
196
0
    BIO *bio;
197
198
0
    if (qlog == NULL)
199
0
        return 0;
200
201
0
    bio = BIO_new_fp(f, BIO_CLOSE);
202
0
    if (bio == NULL)
203
0
        return 0;
204
205
0
    if (!ossl_qlog_set_sink_bio(qlog, bio)) {
206
0
        BIO_free_all(bio);
207
0
        return 0;
208
0
    }
209
210
0
    return 1;
211
0
}
212
213
#endif
214
215
int ossl_qlog_set_sink_filename(QLOG *qlog, const char *filename)
216
0
{
217
0
    BIO *bio;
218
219
0
    if (qlog == NULL)
220
0
        return 0;
221
222
    /*
223
     * We supply our own text encoding as JSON requires UTF-8, so disable any
224
     * OS-specific processing here.
225
     */
226
0
    bio = BIO_new_file(filename, "wb");
227
0
    if (bio == NULL)
228
0
        return 0;
229
230
0
    if (!ossl_qlog_set_sink_bio(qlog, bio)) {
231
0
        BIO_free_all(bio);
232
0
        return 0;
233
0
    }
234
235
0
    return 1;
236
0
}
237
238
int ossl_qlog_flush(QLOG *qlog)
239
0
{
240
0
    if (qlog == NULL)
241
0
        return 1;
242
243
0
    return ossl_json_flush(&qlog->json);
244
0
}
245
246
int ossl_qlog_set_event_type_enabled(QLOG *qlog, uint32_t event_type,
247
    int enabled)
248
0
{
249
0
    if (qlog == NULL || event_type >= QLOG_EVENT_TYPE_NUM)
250
0
        return 0;
251
252
0
    bit_set(qlog->enabled, event_type, enabled);
253
0
    return 1;
254
0
}
255
256
int ossl_qlog_enabled(QLOG *qlog, uint32_t event_type)
257
0
{
258
0
    if (qlog == NULL)
259
0
        return 0;
260
261
0
    return bit_get(qlog->enabled, event_type) != 0;
262
0
}
263
264
/*
265
 * Event Lifecycle
266
 * ===============
267
 */
268
static void write_str_once(QLOG *qlog, const char *key, char **p)
269
0
{
270
0
    if (*p == NULL)
271
0
        return;
272
273
0
    ossl_json_key(&qlog->json, key);
274
0
    ossl_json_str(&qlog->json, *p);
275
276
0
    OPENSSL_free(*p);
277
0
    *p = NULL;
278
0
}
279
280
static void qlog_event_seq_header(QLOG *qlog)
281
0
{
282
0
    if (qlog->header_done)
283
0
        return;
284
285
0
    ossl_json_object_begin(&qlog->json);
286
0
    {
287
0
        ossl_json_key(&qlog->json, "qlog_version");
288
0
        ossl_json_str(&qlog->json, "0.3");
289
290
0
        ossl_json_key(&qlog->json, "qlog_format");
291
0
        ossl_json_str(&qlog->json, "JSON-SEQ");
292
293
0
        write_str_once(qlog, "title", (char **)&qlog->info.title);
294
0
        write_str_once(qlog, "description", (char **)&qlog->info.description);
295
296
0
        ossl_json_key(&qlog->json, "trace");
297
0
        ossl_json_object_begin(&qlog->json);
298
0
        {
299
0
            ossl_json_key(&qlog->json, "common_fields");
300
0
            ossl_json_object_begin(&qlog->json);
301
0
            {
302
0
                ossl_json_key(&qlog->json, "time_format");
303
0
                ossl_json_str(&qlog->json, "delta");
304
305
0
                ossl_json_key(&qlog->json, "protocol_type");
306
0
                ossl_json_array_begin(&qlog->json);
307
0
                {
308
0
                    ossl_json_str(&qlog->json, "QUIC");
309
0
                } /* protocol_type */
310
0
                ossl_json_array_end(&qlog->json);
311
312
0
                write_str_once(qlog, "group_id", (char **)&qlog->info.group_id);
313
314
0
                ossl_json_key(&qlog->json, "system_info");
315
0
                ossl_json_object_begin(&qlog->json);
316
0
                {
317
0
                    if (qlog->info.override_process_id != 0) {
318
0
                        ossl_json_key(&qlog->json, "process_id");
319
0
                        ossl_json_u64(&qlog->json, qlog->info.override_process_id);
320
0
                    } else {
321
0
#if defined(OPENSSL_SYS_UNIX)
322
0
                        ossl_json_key(&qlog->json, "process_id");
323
0
                        ossl_json_u64(&qlog->json, (uint64_t)getpid());
324
#elif defined(OPENSSL_SYS_WINDOWS)
325
                        ossl_json_key(&qlog->json, "process_id");
326
                        ossl_json_u64(&qlog->json, (uint64_t)GetCurrentProcessId());
327
#endif
328
0
                    }
329
0
                } /* system_info */
330
0
                ossl_json_object_end(&qlog->json);
331
0
            } /* common_fields */
332
0
            ossl_json_object_end(&qlog->json);
333
334
0
            ossl_json_key(&qlog->json, "vantage_point");
335
0
            ossl_json_object_begin(&qlog->json);
336
0
            {
337
0
                char buf[128];
338
0
                const char *p = buf;
339
340
0
                if (qlog->info.override_impl_name != NULL) {
341
0
                    p = qlog->info.override_impl_name;
342
0
                } else {
343
0
                    BIO_snprintf(buf, sizeof(buf), "OpenSSL/%s (%s)",
344
0
                        OpenSSL_version(OPENSSL_FULL_VERSION_STRING),
345
0
                        OpenSSL_version(OPENSSL_PLATFORM) + 10);
346
0
                }
347
348
0
                ossl_json_key(&qlog->json, "type");
349
0
                ossl_json_str(&qlog->json,
350
0
                    qlog->info.is_server ? "server" : "client");
351
352
0
                ossl_json_key(&qlog->json, "name");
353
0
                ossl_json_str(&qlog->json, p);
354
0
            } /* vantage_point */
355
0
            ossl_json_object_end(&qlog->json);
356
0
        } /* trace */
357
0
        ossl_json_object_end(&qlog->json);
358
0
    }
359
0
    ossl_json_object_end(&qlog->json);
360
361
0
    qlog->header_done = 1;
362
0
}
363
364
static void qlog_event_prologue(QLOG *qlog)
365
0
{
366
0
    qlog_event_seq_header(qlog);
367
368
0
    ossl_json_object_begin(&qlog->json);
369
370
0
    ossl_json_key(&qlog->json, "name");
371
0
    ossl_json_str(&qlog->json, qlog->event_combined_name);
372
373
0
    ossl_json_key(&qlog->json, "data");
374
0
    ossl_json_object_begin(&qlog->json);
375
0
}
376
377
static void qlog_event_epilogue(QLOG *qlog)
378
0
{
379
0
    ossl_json_object_end(&qlog->json);
380
381
0
    ossl_json_key(&qlog->json, "time");
382
0
    if (!qlog->first_event_done) {
383
0
        ossl_json_u64(&qlog->json, ossl_time2ms(qlog->event_time));
384
0
        qlog->prev_event_time = qlog->event_time;
385
0
        qlog->first_event_done = 1;
386
0
    } else {
387
0
        OSSL_TIME delta = ossl_time_subtract(qlog->event_time,
388
0
            qlog->prev_event_time);
389
390
0
        ossl_json_u64(&qlog->json, ossl_time2ms(delta));
391
0
        qlog->prev_event_time = qlog->event_time;
392
0
    }
393
394
0
    ossl_json_object_end(&qlog->json);
395
0
}
396
397
int ossl_qlog_event_try_begin(QLOG *qlog,
398
    uint32_t event_type,
399
    const char *event_cat,
400
    const char *event_name,
401
    const char *event_combined_name)
402
3.95M
{
403
3.95M
    if (qlog == NULL)
404
3.95M
        return 0;
405
406
0
    if (!ossl_assert(qlog->event_type == QLOG_EVENT_TYPE_NONE)
407
0
        || !ossl_qlog_enabled(qlog, event_type))
408
0
        return 0;
409
410
0
    qlog->event_type = event_type;
411
0
    qlog->event_cat = event_cat;
412
0
    qlog->event_name = event_name;
413
0
    qlog->event_combined_name = event_combined_name;
414
0
    qlog->event_time = qlog->info.now_cb(qlog->info.now_cb_arg);
415
416
0
    qlog_event_prologue(qlog);
417
0
    return 1;
418
0
}
419
420
void ossl_qlog_event_end(QLOG *qlog)
421
0
{
422
0
    if (!ossl_assert(qlog != NULL && qlog->event_type != QLOG_EVENT_TYPE_NONE))
423
0
        return;
424
425
0
    qlog_event_epilogue(qlog);
426
0
    qlog->event_type = QLOG_EVENT_TYPE_NONE;
427
0
}
428
429
/*
430
 * Field Generators
431
 * ================
432
 */
433
void ossl_qlog_group_begin(QLOG *qlog, const char *name)
434
0
{
435
0
    if (name != NULL)
436
0
        ossl_json_key(&qlog->json, name);
437
438
0
    ossl_json_object_begin(&qlog->json);
439
0
}
440
441
void ossl_qlog_group_end(QLOG *qlog)
442
0
{
443
0
    ossl_json_object_end(&qlog->json);
444
0
}
445
446
void ossl_qlog_array_begin(QLOG *qlog, const char *name)
447
0
{
448
0
    if (name != NULL)
449
0
        ossl_json_key(&qlog->json, name);
450
451
0
    ossl_json_array_begin(&qlog->json);
452
0
}
453
454
void ossl_qlog_array_end(QLOG *qlog)
455
0
{
456
0
    ossl_json_array_end(&qlog->json);
457
0
}
458
459
void ossl_qlog_override_time(QLOG *qlog, OSSL_TIME event_time)
460
0
{
461
0
    qlog->event_time = event_time;
462
0
}
463
464
void ossl_qlog_str(QLOG *qlog, const char *name, const char *value)
465
0
{
466
0
    if (name != NULL)
467
0
        ossl_json_key(&qlog->json, name);
468
469
0
    ossl_json_str(&qlog->json, value);
470
0
}
471
472
void ossl_qlog_str_len(QLOG *qlog, const char *name,
473
    const char *value, size_t value_len)
474
0
{
475
0
    if (name != NULL)
476
0
        ossl_json_key(&qlog->json, name);
477
478
0
    ossl_json_str_len(&qlog->json, value, value_len);
479
0
}
480
481
void ossl_qlog_u64(QLOG *qlog, const char *name, uint64_t value)
482
0
{
483
0
    if (name != NULL)
484
0
        ossl_json_key(&qlog->json, name);
485
486
0
    ossl_json_u64(&qlog->json, value);
487
0
}
488
489
void ossl_qlog_i64(QLOG *qlog, const char *name, int64_t value)
490
0
{
491
0
    if (name != NULL)
492
0
        ossl_json_key(&qlog->json, name);
493
494
0
    ossl_json_i64(&qlog->json, value);
495
0
}
496
497
void ossl_qlog_bool(QLOG *qlog, const char *name, bool value)
498
0
{
499
0
    if (name != NULL)
500
0
        ossl_json_key(&qlog->json, name);
501
502
0
    ossl_json_bool(&qlog->json, value);
503
0
}
504
505
void ossl_qlog_bin(QLOG *qlog, const char *name,
506
    const void *value, size_t value_len)
507
0
{
508
0
    if (name != NULL)
509
0
        ossl_json_key(&qlog->json, name);
510
511
0
    ossl_json_str_hex(&qlog->json, value, value_len);
512
0
}
513
514
/*
515
 * Filter Parsing
516
 * ==============
517
 */
518
struct lexer {
519
    const char *p, *term_end, *end;
520
};
521
522
static ossl_inline int is_term_sep_ws(char c)
523
0
{
524
0
    return c == ' ' || c == '\r' || c == '\n' || c == '\t';
525
0
}
526
527
static ossl_inline int is_name_char(char c)
528
0
{
529
0
    return ossl_isalpha(c) || ossl_isdigit(c) || c == '_' || c == '-';
530
0
}
531
532
static int lex_init(struct lexer *lex, const char *in, size_t in_len)
533
0
{
534
0
    if (in == NULL)
535
0
        return 0;
536
537
0
    lex->p = in;
538
0
    lex->term_end = in;
539
0
    lex->end = in + in_len;
540
0
    return 1;
541
0
}
542
543
static int lex_do(struct lexer *lex)
544
0
{
545
0
    const char *p = lex->term_end, *end = lex->end, *term_end;
546
547
0
    for (; is_term_sep_ws(*p) && p < end; ++p)
548
0
        ;
549
550
0
    if (p == end) {
551
0
        lex->p = end;
552
0
        lex->term_end = end;
553
0
        return 0;
554
0
    }
555
556
0
    for (term_end = p; !is_term_sep_ws(*term_end) && term_end < end; ++term_end)
557
0
        ;
558
559
0
    lex->p = p;
560
0
    lex->term_end = term_end;
561
0
    return 1;
562
0
}
563
564
static int lex_eot(struct lexer *lex)
565
0
{
566
0
    return lex->p == lex->term_end;
567
0
}
568
569
static int lex_peek_char(struct lexer *lex)
570
0
{
571
0
    return lex_eot(lex) ? -1 : *lex->p;
572
0
}
573
574
static int lex_skip_char(struct lexer *lex)
575
0
{
576
0
    if (lex_eot(lex))
577
0
        return 0;
578
579
0
    ++lex->p;
580
0
    return 1;
581
0
}
582
583
static int lex_match(struct lexer *lex, const char *s, size_t s_len)
584
0
{
585
0
    if ((size_t)(lex->term_end - lex->p) != s_len)
586
0
        return 0;
587
588
0
    if (memcmp(lex->p, s, s_len))
589
0
        return 0;
590
591
0
    return 1;
592
0
}
593
594
static void lex_get_rest(struct lexer *lex, const char **str, size_t *str_l)
595
0
{
596
0
    *str = lex->p;
597
0
    *str_l = lex->term_end - lex->p;
598
0
}
599
600
static int lex_extract_to(struct lexer *lex, char c,
601
    const char **str, size_t *str_l)
602
0
{
603
0
    const char *p = lex->p, *term_end = lex->term_end, *s;
604
605
0
    for (s = p; s < term_end && *s != c; ++s)
606
0
        ;
607
0
    if (s == term_end)
608
0
        return 0;
609
610
0
    *str = p;
611
0
    *str_l = s - p;
612
0
    lex->p = ++s;
613
0
    return 1;
614
0
}
615
616
static int ossl_unused filter_match_event(const char *cat, size_t cat_l,
617
    const char *event, size_t event_l,
618
    const char *expect_cat,
619
    const char *expect_event)
620
0
{
621
0
    size_t expect_cat_l = strlen(expect_cat);
622
0
    size_t expect_event_l = strlen(expect_event);
623
624
0
    if ((cat != NULL && cat_l != expect_cat_l)
625
0
        || (event != NULL && event_l != expect_event_l)
626
0
        || (cat != NULL && memcmp(cat, expect_cat, expect_cat_l))
627
0
        || (event != NULL && memcmp(event, expect_event, expect_event_l)))
628
0
        return 0;
629
630
0
    return 1;
631
0
}
632
633
/*
634
 * enabled: event enablement bitmask Array of size NUM_ENABLED_W.
635
 * add: 1 to enable an event, 0 to disable.
636
 * cat: Category name/length. Not necessarily zero terminated.
637
 *      NULL to match any.
638
 * event: Event name/length. Not necessarily zero terminated.
639
 *        NULL to match any.
640
 */
641
static void filter_apply(size_t *enabled, int add,
642
    const char *cat, size_t cat_l,
643
    const char *event, size_t event_l)
644
0
{
645
    /* Find events which match the given filters. */
646
0
#define QLOG_EVENT(e_cat, e_name)                      \
647
0
    if (filter_match_event(cat, cat_l, event, event_l, \
648
0
            #e_cat, #e_name))                          \
649
0
        bit_set(enabled, QLOG_EVENT_TYPE_##e_cat##_##e_name, add);
650
0
#include "internal/qlog_events.h"
651
0
#undef QLOG_EVENT
652
0
}
653
654
static int lex_fail(struct lexer *lex, const char *msg)
655
0
{
656
    /*
657
     * TODO(QLOG FUTURE): Determine how to print log messages about bad filter
658
     * strings
659
     */
660
0
    lex->p = lex->term_end = lex->end;
661
0
    return 0;
662
0
}
663
664
static int validate_name(const char **p, size_t *l)
665
0
{
666
0
    const char *p_ = *p;
667
0
    size_t i, l_ = *l;
668
669
0
    if (l_ == 1 && *p_ == '*') {
670
0
        *p = NULL;
671
0
        *l = 0;
672
0
        return 1;
673
0
    }
674
675
0
    if (l_ == 0)
676
0
        return 0;
677
678
0
    for (i = 0; i < l_; ++i)
679
0
        if (!is_name_char(p_[i]))
680
0
            return 0;
681
682
0
    return 1;
683
0
}
684
685
int ossl_qlog_set_filter(QLOG *qlog, const char *filter)
686
0
{
687
0
    struct lexer lex = { 0 };
688
0
    char c;
689
0
    const char *cat, *event;
690
0
    size_t cat_l, event_l, enabled[NUM_ENABLED_W];
691
0
    int add;
692
693
0
    memcpy(enabled, qlog->enabled, sizeof(enabled));
694
695
0
    if (!lex_init(&lex, filter, strlen(filter)))
696
0
        return 0;
697
698
0
    while (lex_do(&lex)) {
699
0
        c = lex_peek_char(&lex);
700
0
        if (c == '+' || c == '-') {
701
0
            add = (c == '+');
702
0
            lex_skip_char(&lex);
703
704
0
            c = lex_peek_char(&lex);
705
0
            if (!is_name_char(c) && c != '*')
706
0
                return lex_fail(&lex, "expected alphanumeric name or '*'"
707
0
                                      " after +/-");
708
0
        } else if (!is_name_char(c) && c != '*') {
709
0
            return lex_fail(&lex, "expected +/- or alphanumeric name or '*'");
710
0
        } else {
711
0
            add = 1;
712
0
        }
713
714
0
        if (lex_match(&lex, "*", 1)) {
715
0
            filter_apply(enabled, add, NULL, 0, NULL, 0);
716
0
            continue;
717
0
        }
718
719
0
        if (!lex_extract_to(&lex, ':', &cat, &cat_l))
720
0
            return lex_fail(&lex, "expected ':' after category name");
721
722
0
        lex_get_rest(&lex, &event, &event_l);
723
0
        if (!validate_name(&cat, &cat_l))
724
0
            return lex_fail(&lex, "expected alphanumeric category name or '*'");
725
0
        if (!validate_name(&event, &event_l))
726
0
            return lex_fail(&lex, "expected alphanumeric event name or '*'");
727
728
0
        filter_apply(enabled, add, cat, cat_l, event, event_l);
729
0
    }
730
731
0
    memcpy(qlog->enabled, enabled, sizeof(enabled));
732
0
    return 1;
733
0
}