qlog.c 19 KB

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