/* SPDX-License-Identifier: GPL-2.0
 * Copyright (c) 2018 Jesper Dangaard Brouer, Red Hat Inc.
 */
static const char *__doc__ =
	"Libbpf test program for loading BPF ELF object files";

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <bpf/libbpf.h>
#include <getopt.h>

static const struct option long_options[] = {
	{"help",	no_argument,		NULL, 'h' },
	{"debug",	no_argument,		NULL, 'D' },
	{"quiet",	no_argument,		NULL, 'q' },
	{0, 0, NULL,  0 }
};

static void usage(char *argv[])
{
	int i;

	printf("\nDOCUMENTATION:\n%s\n\n", __doc__);
	printf(" Usage: %s (options-see-below) BPF_FILE\n", argv[0]);
	printf(" Listing options:\n");
	for (i = 0; long_options[i].name != 0; i++) {
		printf(" --%-12s", long_options[i].name);
		printf(" short-option: -%c",
		       long_options[i].val);
		printf("\n");
	}
	printf("\n");
}

#define DEFINE_PRINT_FN(name, enabled) \
static int libbpf_##name(const char *fmt, ...)  	\
{							\
        va_list args;					\
        int ret;					\
							\
        va_start(args, fmt);				\
	if (enabled) {					\
		fprintf(stderr, "[" #name "] ");	\
		ret = vfprintf(stderr, fmt, args);	\
	}						\
        va_end(args);					\
        return ret;					\
}
DEFINE_PRINT_FN(warning, 1)
DEFINE_PRINT_FN(info, 1)
DEFINE_PRINT_FN(debug, 1)

#define EXIT_FAIL_LIBBPF EXIT_FAILURE
#define EXIT_FAIL_OPTION 2

int test_walk_progs(struct bpf_object *obj, bool verbose)
{
	struct bpf_program *prog;
	int cnt = 0;

	bpf_object__for_each_program(prog, obj) {
		cnt++;
		if (verbose)
			printf("Prog (count:%d) section_name: %s\n", cnt,
			       bpf_program__title(prog, false));
	}
	return 0;
}

int test_walk_maps(struct bpf_object *obj, bool verbose)
{
	struct bpf_map *map;
	int cnt = 0;

	bpf_map__for_each(map, obj) {
		cnt++;
		if (verbose)
			printf("Map (count:%d) name: %s\n", cnt,
			       bpf_map__name(map));
	}
	return 0;
}

int test_open_file(char *filename, bool verbose)
{
	struct bpf_object *bpfobj = NULL;
	long err;

	if (verbose)
		printf("Open BPF ELF-file with libbpf: %s\n", filename);

	/* Load BPF ELF object file and check for errors */
	bpfobj = bpf_object__open(filename);
	err = libbpf_get_error(bpfobj);
	if (err) {
		char err_buf[128];
		libbpf_strerror(err, err_buf, sizeof(err_buf));
		if (verbose)
			printf("Unable to load eBPF objects in file '%s': %s\n",
			       filename, err_buf);
		return EXIT_FAIL_LIBBPF;
	}
	test_walk_progs(bpfobj, verbose);
	test_walk_maps(bpfobj, verbose);

	if (verbose)
		printf("Close BPF ELF-file with libbpf: %s\n",
		       bpf_object__name(bpfobj));
	bpf_object__close(bpfobj);

	return 0;
}

int main(int argc, char **argv)
{
	char filename[1024] = { 0 };
	bool verbose = 1;
	int longindex = 0;
	int opt;

	libbpf_set_print(libbpf_warning, libbpf_info, NULL);

	/* Parse commands line args */
	while ((opt = getopt_long(argc, argv, "hDq",
				  long_options, &longindex)) != -1) {
		switch (opt) {
		case 'D':
			libbpf_set_print(libbpf_warning, libbpf_info,
					 libbpf_debug);
			break;
		case 'q': /* Use in scripting mode */
			verbose = 0;
			break;
		case 'h':
		default:
			usage(argv);
			return EXIT_FAIL_OPTION;
		}
	}
	if (optind >= argc) {
		usage(argv);
		printf("ERROR: Expected BPF_FILE argument after options\n");
		return EXIT_FAIL_OPTION;
	}
	snprintf(filename, sizeof(filename), "%s", argv[optind]);

	return test_open_file(filename, verbose);
}
