--- /dev/null
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+
+/*
+ * Helpers for handling qevents.
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "tc_qevent.h"
+#include "utils.h"
+
+void qevents_init(struct qevent_util *qevents)
+{
+ if (!qevents)
+ return;
+
+ for (; qevents->id; qevents++)
+ memset(qevents->data, 0, qevents->data_size);
+}
+
+int qevent_parse(struct qevent_util *qevents, int *p_argc, char ***p_argv)
+{
+ char **argv = *p_argv;
+ int argc = *p_argc;
+ const char *name = *argv;
+ int err;
+
+ if (!qevents)
+ goto out;
+
+ for (; qevents->id; qevents++) {
+ if (strcmp(name, qevents->id) == 0) {
+ NEXT_ARG();
+ err = qevents->parse_qevent(qevents, &argc, &argv);
+ if (err)
+ return err;
+
+ *p_argc = argc;
+ *p_argv = argv;
+ return 0;
+ }
+ }
+
+out:
+ fprintf(stderr, "Unknown qevent `%s'\n", name);
+ return -1;
+}
+
+int qevents_read(struct qevent_util *qevents, struct rtattr **tb)
+{
+ int err;
+
+ if (!qevents)
+ return 0;
+
+ for (; qevents->id; qevents++) {
+ if (tb[qevents->attr]) {
+ err = qevents->read_qevent(qevents, tb);
+ if (err)
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+void qevents_print(struct qevent_util *qevents, FILE *f)
+{
+ int first = true;
+
+ if (!qevents)
+ return;
+
+ for (; qevents->id; qevents++) {
+ struct qevent_base *qeb = qevents->data;
+
+ if (qeb->block_idx) {
+ if (first) {
+ open_json_array(PRINT_JSON, "qevents");
+ first = false;
+ }
+
+ open_json_object(NULL);
+ print_string(PRINT_ANY, "kind", " qevent %s", qevents->id);
+ qevents->print_qevent(qevents, f);
+ close_json_object();
+ }
+ }
+
+ if (!first)
+ close_json_array(PRINT_ANY, "");
+}
+
+int qevents_dump(struct qevent_util *qevents, struct nlmsghdr *n)
+{
+ int err;
+
+ if (!qevents)
+ return 0;
+
+ for (; qevents->id; qevents++) {
+ struct qevent_base *qeb = qevents->data;
+
+ if (qeb->block_idx) {
+ err = qevents->dump_qevent(qevents, n);
+ if (err)
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+static int parse_block_idx(const char *arg, struct qevent_base *qeb)
+{
+ if (qeb->block_idx) {
+ fprintf(stderr, "Qevent block index already specified\n");
+ return -1;
+ }
+
+ if (get_unsigned(&qeb->block_idx, arg, 10) || !qeb->block_idx) {
+ fprintf(stderr, "Illegal qevent block index\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int read_block_idx(struct rtattr *attr, struct qevent_base *qeb)
+{
+ if (qeb->block_idx) {
+ fprintf(stderr, "Qevent block index already specified\n");
+ return -1;
+ }
+
+ qeb->block_idx = rta_getattr_u32(attr);
+ if (!qeb->block_idx) {
+ fprintf(stderr, "Illegal qevent block index\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static void print_block_idx(FILE *f, __u32 block_idx)
+{
+ print_uint(PRINT_ANY, "block", " block %u", block_idx);
+}
+
+int qevent_parse_plain(struct qevent_util *qu, int *p_argc, char ***p_argv)
+{
+ struct qevent_plain *qe = qu->data;
+ char **argv = *p_argv;
+ int argc = *p_argc;
+
+ if (qe->base.block_idx) {
+ fprintf(stderr, "Duplicate qevent\n");
+ return -1;
+ }
+
+ while (argc > 0) {
+ if (strcmp(*argv, "block") == 0) {
+ NEXT_ARG();
+ if (parse_block_idx(*argv, &qe->base))
+ return -1;
+ } else {
+ break;
+ }
+ NEXT_ARG_FWD();
+ }
+
+ if (!qe->base.block_idx) {
+ fprintf(stderr, "Unspecified qevent block index\n");
+ return -1;
+ }
+
+ *p_argc = argc;
+ *p_argv = argv;
+ return 0;
+}
+
+int qevent_read_plain(struct qevent_util *qu, struct rtattr **tb)
+{
+ struct qevent_plain *qe = qu->data;
+
+ return read_block_idx(tb[qu->attr], &qe->base);
+}
+
+void qevent_print_plain(struct qevent_util *qu, FILE *f)
+{
+ struct qevent_plain *qe = qu->data;
+
+ print_block_idx(f, qe->base.block_idx);
+}
+
+int qevent_dump_plain(struct qevent_util *qu, struct nlmsghdr *n)
+{
+ struct qevent_plain *qe = qu->data;
+
+ return addattr32(n, 1024, qu->attr, qe->base.block_idx);
+}
--- /dev/null
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _TC_QEVENT_H_
+#define _TC_QEVENT_H_
+
+#include <linux/types.h>
+#include <libnetlink.h>
+
+struct qevent_base {
+ __u32 block_idx;
+};
+
+struct qevent_util {
+ const char *id;
+ int (*parse_qevent)(struct qevent_util *qu, int *argc, char ***argv);
+ int (*read_qevent)(struct qevent_util *qu, struct rtattr **tb);
+ void (*print_qevent)(struct qevent_util *qu, FILE *f);
+ int (*dump_qevent)(struct qevent_util *qu, struct nlmsghdr *n);
+ size_t data_size;
+ void *data;
+ int attr;
+};
+
+#define QEVENT(_name, _form, _data, _attr) \
+ { \
+ .id = _name, \
+ .parse_qevent = qevent_parse_##_form, \
+ .read_qevent = qevent_read_##_form, \
+ .print_qevent = qevent_print_##_form, \
+ .dump_qevent = qevent_dump_##_form, \
+ .data_size = sizeof(struct qevent_##_form), \
+ .data = _data, \
+ .attr = _attr, \
+ }
+
+void qevents_init(struct qevent_util *qevents);
+int qevent_parse(struct qevent_util *qevents, int *p_argc, char ***p_argv);
+int qevents_read(struct qevent_util *qevents, struct rtattr **tb);
+int qevents_dump(struct qevent_util *qevents, struct nlmsghdr *n);
+void qevents_print(struct qevent_util *qevents, FILE *f);
+
+struct qevent_plain {
+ struct qevent_base base;
+};
+int qevent_parse_plain(struct qevent_util *qu, int *p_argc, char ***p_argv);
+int qevent_read_plain(struct qevent_util *qu, struct rtattr **tb);
+void qevent_print_plain(struct qevent_util *qu, FILE *f);
+int qevent_dump_plain(struct qevent_util *qu, struct nlmsghdr *n);
+
+#endif