--- /dev/null
+// SPDX-License-Identifier: GPL-2.0-only
+#include <bpftool_helpers.h>
+#include <test_progs.h>
+#include <linux/bpf.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <stdbool.h>
+
+#define BPFFS_DIR "/sys/fs/bpf/test_metadata"
+#define BPFFS_USED BPFFS_DIR "/used"
+#define BPFFS_UNUSED BPFFS_DIR "/unused"
+
+#define BPF_FILE_USED "metadata_used.bpf.o"
+#define BPF_FILE_UNUSED "metadata_unused.bpf.o"
+#define METADATA_MAP_NAME "metadata.rodata"
+
+#define MAX_BPFTOOL_OUTPUT_LEN (64*1024)
+
+#define MAX_TOKENS_TO_CHECK 3
+static char output[MAX_BPFTOOL_OUTPUT_LEN];
+
+struct test_desc {
+ char *name;
+ char *bpf_prog;
+ char *bpffs_path;
+ char *expected_output[MAX_TOKENS_TO_CHECK];
+ char *expected_output_json[MAX_TOKENS_TO_CHECK];
+ char *metadata_map_name;
+};
+
+static int setup(struct test_desc *test)
+{
+ return mkdir(BPFFS_DIR, 0700);
+}
+
+static void cleanup(struct test_desc *test)
+{
+ unlink(test->bpffs_path);
+ rmdir(BPFFS_DIR);
+}
+
+static int check_metadata(char *buf, char * const *tokens, int count)
+{
+ int i;
+
+ for (i = 0; i < count && tokens[i]; i++)
+ if (!strstr(buf, tokens[i]))
+ return 1;
+
+ return 0;
+}
+
+static void run_test(struct test_desc *test)
+{
+ int ret;
+ char cmd[MAX_BPFTOOL_CMD_LEN];
+
+ ret = snprintf(cmd, MAX_BPFTOOL_CMD_LEN, "prog load %s %s",
+ test->bpf_prog, test->bpffs_path);
+ if (!ASSERT_GT(ret, 0, "format prog insert command"))
+ return;
+ ret = run_bpftool_command(cmd);
+ if (!ASSERT_OK(ret, "load program"))
+ return;
+
+ /* Check output with default format */
+ ret = snprintf(cmd, MAX_BPFTOOL_CMD_LEN, "prog show pinned %s",
+ test->bpffs_path);
+ if (!ASSERT_GT(ret, 0, "format pinned prog check command"))
+ return;
+ ret = get_bpftool_command_output(cmd, output,
+ MAX_BPFTOOL_OUTPUT_LEN);
+ if (ASSERT_OK(ret, "get program info")) {
+ ret = check_metadata(output, test->expected_output,
+ ARRAY_SIZE(test->expected_output));
+ ASSERT_OK(ret, "find metadata");
+ }
+
+ /* Check output with json format */
+ ret = snprintf(cmd, MAX_BPFTOOL_CMD_LEN, "prog -j show pinned %s",
+ test->bpffs_path);
+ if (!ASSERT_GT(ret, 0, "format pinned prog check command in json"))
+ return;
+ ret = get_bpftool_command_output(cmd, output,
+ MAX_BPFTOOL_OUTPUT_LEN);
+ if (ASSERT_OK(ret, "get program info in json")) {
+ ret = check_metadata(output, test->expected_output_json,
+ ARRAY_SIZE(test->expected_output_json));
+ ASSERT_OK(ret, "find metadata in json");
+ }
+
+ /* Check that the corresponding map can be found and accessed */
+ ret = snprintf(cmd, MAX_BPFTOOL_CMD_LEN, "map show name %s",
+ test->metadata_map_name);
+ if (!ASSERT_GT(ret, 0, "format map check command"))
+ return;
+ ASSERT_OK(run_bpftool_command(cmd), "access metadata map");
+}
+
+static struct test_desc tests[] = {
+ {
+ .name = "metadata_unused",
+ .bpf_prog = BPF_FILE_UNUSED,
+ .bpffs_path = BPFFS_UNUSED,
+ .expected_output = {
+ "a = \"foo\"",
+ "b = 1"
+ },
+ .expected_output_json = {
+ "\"metadata\":{\"a\":\"foo\",\"b\":1}"
+ },
+ .metadata_map_name = METADATA_MAP_NAME
+ },
+ {
+ .name = "metadata_used",
+ .bpf_prog = BPF_FILE_USED,
+ .bpffs_path = BPFFS_USED,
+ .expected_output = {
+ "a = \"bar\"",
+ "b = 2"
+ },
+ .expected_output_json = {
+ "\"metadata\":{\"a\":\"bar\",\"b\":2}"
+ },
+ .metadata_map_name = METADATA_MAP_NAME
+ }
+};
+static const int tests_count = ARRAY_SIZE(tests);
+
+void test_bpftool_metadata(void)
+{
+ int i;
+
+ for (i = 0; i < tests_count; i++) {
+ if (!test__start_subtest(tests[i].name))
+ continue;
+ if (ASSERT_OK(setup(&tests[i]), "setup bpffs pin dir")) {
+ run_test(&tests[i]);
+ cleanup(&tests[i]);
+ }
+ }
+}
+++ /dev/null
-#!/bin/sh
-# SPDX-License-Identifier: GPL-2.0
-
-# Kselftest framework requirement - SKIP code is 4.
-ksft_skip=4
-
-BPF_FILE_USED="metadata_used.bpf.o"
-BPF_FILE_UNUSED="metadata_unused.bpf.o"
-
-TESTNAME=bpftool_metadata
-BPF_FS=$(awk '$3 == "bpf" {print $2; exit}' /proc/mounts)
-BPF_DIR=$BPF_FS/test_$TESTNAME
-
-_cleanup()
-{
- set +e
- rm -rf $BPF_DIR 2> /dev/null
-}
-
-cleanup_skip()
-{
- echo "selftests: $TESTNAME [SKIP]"
- _cleanup
-
- exit $ksft_skip
-}
-
-cleanup()
-{
- if [ "$?" = 0 ]; then
- echo "selftests: $TESTNAME [PASS]"
- else
- echo "selftests: $TESTNAME [FAILED]"
- fi
- _cleanup
-}
-
-if [ $(id -u) -ne 0 ]; then
- echo "selftests: $TESTNAME [SKIP] Need root privileges"
- exit $ksft_skip
-fi
-
-if [ -z "$BPF_FS" ]; then
- echo "selftests: $TESTNAME [SKIP] Could not run test without bpffs mounted"
- exit $ksft_skip
-fi
-
-if ! bpftool version > /dev/null 2>&1; then
- echo "selftests: $TESTNAME [SKIP] Could not run test without bpftool"
- exit $ksft_skip
-fi
-
-set -e
-
-trap cleanup_skip EXIT
-
-mkdir $BPF_DIR
-
-trap cleanup EXIT
-
-bpftool prog load $BPF_FILE_UNUSED $BPF_DIR/unused
-
-METADATA_PLAIN="$(bpftool prog)"
-echo "$METADATA_PLAIN" | grep 'a = "foo"' > /dev/null
-echo "$METADATA_PLAIN" | grep 'b = 1' > /dev/null
-
-bpftool prog --json | grep '"metadata":{"a":"foo","b":1}' > /dev/null
-
-bpftool map | grep 'metadata.rodata' > /dev/null
-
-rm $BPF_DIR/unused
-
-bpftool prog load $BPF_FILE_USED $BPF_DIR/used
-
-METADATA_PLAIN="$(bpftool prog)"
-echo "$METADATA_PLAIN" | grep 'a = "bar"' > /dev/null
-echo "$METADATA_PLAIN" | grep 'b = 2' > /dev/null
-
-bpftool prog --json | grep '"metadata":{"a":"bar","b":2}' > /dev/null
-
-bpftool map | grep 'metadata.rodata' > /dev/null
-
-rm $BPF_DIR/used
-
-exit 0