690 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			690 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
 | |
| // Copyright (C) 2017 Facebook
 | |
| // Author: Roman Gushchin <guro@fb.com>
 | |
| 
 | |
| #define _XOPEN_SOURCE 500
 | |
| #include <errno.h>
 | |
| #include <fcntl.h>
 | |
| #include <ftw.h>
 | |
| #include <mntent.h>
 | |
| #include <stdio.h>
 | |
| #include <stdlib.h>
 | |
| #include <string.h>
 | |
| #include <sys/stat.h>
 | |
| #include <sys/types.h>
 | |
| #include <unistd.h>
 | |
| 
 | |
| #include <bpf/bpf.h>
 | |
| #include <bpf/btf.h>
 | |
| 
 | |
| #include "main.h"
 | |
| 
 | |
| static const int cgroup_attach_types[] = {
 | |
| 	BPF_CGROUP_INET_INGRESS,
 | |
| 	BPF_CGROUP_INET_EGRESS,
 | |
| 	BPF_CGROUP_INET_SOCK_CREATE,
 | |
| 	BPF_CGROUP_INET_SOCK_RELEASE,
 | |
| 	BPF_CGROUP_INET4_BIND,
 | |
| 	BPF_CGROUP_INET6_BIND,
 | |
| 	BPF_CGROUP_INET4_POST_BIND,
 | |
| 	BPF_CGROUP_INET6_POST_BIND,
 | |
| 	BPF_CGROUP_INET4_CONNECT,
 | |
| 	BPF_CGROUP_INET6_CONNECT,
 | |
| 	BPF_CGROUP_UNIX_CONNECT,
 | |
| 	BPF_CGROUP_INET4_GETPEERNAME,
 | |
| 	BPF_CGROUP_INET6_GETPEERNAME,
 | |
| 	BPF_CGROUP_UNIX_GETPEERNAME,
 | |
| 	BPF_CGROUP_INET4_GETSOCKNAME,
 | |
| 	BPF_CGROUP_INET6_GETSOCKNAME,
 | |
| 	BPF_CGROUP_UNIX_GETSOCKNAME,
 | |
| 	BPF_CGROUP_UDP4_SENDMSG,
 | |
| 	BPF_CGROUP_UDP6_SENDMSG,
 | |
| 	BPF_CGROUP_UNIX_SENDMSG,
 | |
| 	BPF_CGROUP_UDP4_RECVMSG,
 | |
| 	BPF_CGROUP_UDP6_RECVMSG,
 | |
| 	BPF_CGROUP_UNIX_RECVMSG,
 | |
| 	BPF_CGROUP_SOCK_OPS,
 | |
| 	BPF_CGROUP_DEVICE,
 | |
| 	BPF_CGROUP_SYSCTL,
 | |
| 	BPF_CGROUP_GETSOCKOPT,
 | |
| 	BPF_CGROUP_SETSOCKOPT,
 | |
| 	BPF_LSM_CGROUP
 | |
| };
 | |
| 
 | |
| #define HELP_SPEC_ATTACH_FLAGS						\
 | |
| 	"ATTACH_FLAGS := { multi | override }"
 | |
| 
 | |
| #define HELP_SPEC_ATTACH_TYPES						\
 | |
| 	"       ATTACH_TYPE := { cgroup_inet_ingress | cgroup_inet_egress |\n" \
 | |
| 	"                        cgroup_inet_sock_create | cgroup_sock_ops |\n" \
 | |
| 	"                        cgroup_device | cgroup_inet4_bind |\n" \
 | |
| 	"                        cgroup_inet6_bind | cgroup_inet4_post_bind |\n" \
 | |
| 	"                        cgroup_inet6_post_bind | cgroup_inet4_connect |\n" \
 | |
| 	"                        cgroup_inet6_connect | cgroup_unix_connect |\n" \
 | |
| 	"                        cgroup_inet4_getpeername | cgroup_inet6_getpeername |\n" \
 | |
| 	"                        cgroup_unix_getpeername | cgroup_inet4_getsockname |\n" \
 | |
| 	"                        cgroup_inet6_getsockname | cgroup_unix_getsockname |\n" \
 | |
| 	"                        cgroup_udp4_sendmsg | cgroup_udp6_sendmsg |\n" \
 | |
| 	"                        cgroup_unix_sendmsg | cgroup_udp4_recvmsg |\n" \
 | |
| 	"                        cgroup_udp6_recvmsg | cgroup_unix_recvmsg |\n" \
 | |
| 	"                        cgroup_sysctl | cgroup_getsockopt |\n" \
 | |
| 	"                        cgroup_setsockopt | cgroup_inet_sock_release }"
 | |
| 
 | |
| static unsigned int query_flags;
 | |
| static struct btf *btf_vmlinux;
 | |
| static __u32 btf_vmlinux_id;
 | |
| 
 | |
| static enum bpf_attach_type parse_attach_type(const char *str)
 | |
| {
 | |
| 	const char *attach_type_str;
 | |
| 	enum bpf_attach_type type;
 | |
| 
 | |
| 	for (type = 0; ; type++) {
 | |
| 		attach_type_str = libbpf_bpf_attach_type_str(type);
 | |
| 		if (!attach_type_str)
 | |
| 			break;
 | |
| 		if (!strcmp(str, attach_type_str))
 | |
| 			return type;
 | |
| 	}
 | |
| 
 | |
| 	/* Also check traditionally used attach type strings. For these we keep
 | |
| 	 * allowing prefixed usage.
 | |
| 	 */
 | |
| 	for (type = 0; ; type++) {
 | |
| 		attach_type_str = bpf_attach_type_input_str(type);
 | |
| 		if (!attach_type_str)
 | |
| 			break;
 | |
| 		if (is_prefix(str, attach_type_str))
 | |
| 			return type;
 | |
| 	}
 | |
| 
 | |
| 	return __MAX_BPF_ATTACH_TYPE;
 | |
| }
 | |
| 
 | |
| static void guess_vmlinux_btf_id(__u32 attach_btf_obj_id)
 | |
| {
 | |
| 	struct bpf_btf_info btf_info = {};
 | |
| 	__u32 btf_len = sizeof(btf_info);
 | |
| 	char name[16] = {};
 | |
| 	int err;
 | |
| 	int fd;
 | |
| 
 | |
| 	btf_info.name = ptr_to_u64(name);
 | |
| 	btf_info.name_len = sizeof(name);
 | |
| 
 | |
| 	fd = bpf_btf_get_fd_by_id(attach_btf_obj_id);
 | |
| 	if (fd < 0)
 | |
| 		return;
 | |
| 
 | |
| 	err = bpf_btf_get_info_by_fd(fd, &btf_info, &btf_len);
 | |
| 	if (err)
 | |
| 		goto out;
 | |
| 
 | |
| 	if (btf_info.kernel_btf && strncmp(name, "vmlinux", sizeof(name)) == 0)
 | |
| 		btf_vmlinux_id = btf_info.id;
 | |
| 
 | |
| out:
 | |
| 	close(fd);
 | |
| }
 | |
| 
 | |
| static int show_bpf_prog(int id, enum bpf_attach_type attach_type,
 | |
| 			 const char *attach_flags_str,
 | |
| 			 int level)
 | |
| {
 | |
| 	char prog_name[MAX_PROG_FULL_NAME];
 | |
| 	const char *attach_btf_name = NULL;
 | |
| 	struct bpf_prog_info info = {};
 | |
| 	const char *attach_type_str;
 | |
| 	__u32 info_len = sizeof(info);
 | |
| 	int prog_fd;
 | |
| 
 | |
| 	prog_fd = bpf_prog_get_fd_by_id(id);
 | |
| 	if (prog_fd < 0)
 | |
| 		return -1;
 | |
| 
 | |
| 	if (bpf_prog_get_info_by_fd(prog_fd, &info, &info_len)) {
 | |
| 		close(prog_fd);
 | |
| 		return -1;
 | |
| 	}
 | |
| 
 | |
| 	attach_type_str = libbpf_bpf_attach_type_str(attach_type);
 | |
| 
 | |
| 	if (btf_vmlinux) {
 | |
| 		if (!btf_vmlinux_id)
 | |
| 			guess_vmlinux_btf_id(info.attach_btf_obj_id);
 | |
| 
 | |
| 		if (btf_vmlinux_id == info.attach_btf_obj_id &&
 | |
| 		    info.attach_btf_id < btf__type_cnt(btf_vmlinux)) {
 | |
| 			const struct btf_type *t =
 | |
| 				btf__type_by_id(btf_vmlinux, info.attach_btf_id);
 | |
| 			attach_btf_name =
 | |
| 				btf__name_by_offset(btf_vmlinux, t->name_off);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	get_prog_full_name(&info, prog_fd, prog_name, sizeof(prog_name));
 | |
| 	if (json_output) {
 | |
| 		jsonw_start_object(json_wtr);
 | |
| 		jsonw_uint_field(json_wtr, "id", info.id);
 | |
| 		if (attach_type_str)
 | |
| 			jsonw_string_field(json_wtr, "attach_type", attach_type_str);
 | |
| 		else
 | |
| 			jsonw_uint_field(json_wtr, "attach_type", attach_type);
 | |
| 		if (!(query_flags & BPF_F_QUERY_EFFECTIVE))
 | |
| 			jsonw_string_field(json_wtr, "attach_flags", attach_flags_str);
 | |
| 		jsonw_string_field(json_wtr, "name", prog_name);
 | |
| 		if (attach_btf_name)
 | |
| 			jsonw_string_field(json_wtr, "attach_btf_name", attach_btf_name);
 | |
| 		jsonw_uint_field(json_wtr, "attach_btf_obj_id", info.attach_btf_obj_id);
 | |
| 		jsonw_uint_field(json_wtr, "attach_btf_id", info.attach_btf_id);
 | |
| 		jsonw_end_object(json_wtr);
 | |
| 	} else {
 | |
| 		printf("%s%-8u ", level ? "    " : "", info.id);
 | |
| 		if (attach_type_str)
 | |
| 			printf("%-15s", attach_type_str);
 | |
| 		else
 | |
| 			printf("type %-10u", attach_type);
 | |
| 		if (query_flags & BPF_F_QUERY_EFFECTIVE)
 | |
| 			printf(" %-15s", prog_name);
 | |
| 		else
 | |
| 			printf(" %-15s %-15s", attach_flags_str, prog_name);
 | |
| 		if (attach_btf_name)
 | |
| 			printf(" %-15s", attach_btf_name);
 | |
| 		else if (info.attach_btf_id)
 | |
| 			printf(" attach_btf_obj_id=%d attach_btf_id=%d",
 | |
| 			       info.attach_btf_obj_id, info.attach_btf_id);
 | |
| 		printf("\n");
 | |
| 	}
 | |
| 
 | |
| 	close(prog_fd);
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int count_attached_bpf_progs(int cgroup_fd, enum bpf_attach_type type)
 | |
| {
 | |
| 	__u32 prog_cnt = 0;
 | |
| 	int ret;
 | |
| 
 | |
| 	ret = bpf_prog_query(cgroup_fd, type, query_flags, NULL,
 | |
| 			     NULL, &prog_cnt);
 | |
| 	if (ret)
 | |
| 		return -1;
 | |
| 
 | |
| 	return prog_cnt;
 | |
| }
 | |
| 
 | |
| static int cgroup_has_attached_progs(int cgroup_fd)
 | |
| {
 | |
| 	unsigned int i = 0;
 | |
| 	bool no_prog = true;
 | |
| 
 | |
| 	for (i = 0; i < ARRAY_SIZE(cgroup_attach_types); i++) {
 | |
| 		int count = count_attached_bpf_progs(cgroup_fd, cgroup_attach_types[i]);
 | |
| 
 | |
| 		if (count < 0)
 | |
| 			return -1;
 | |
| 
 | |
| 		if (count > 0) {
 | |
| 			no_prog = false;
 | |
| 			break;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return no_prog ? 0 : 1;
 | |
| }
 | |
| 
 | |
| static int show_effective_bpf_progs(int cgroup_fd, enum bpf_attach_type type,
 | |
| 				    int level)
 | |
| {
 | |
| 	LIBBPF_OPTS(bpf_prog_query_opts, p);
 | |
| 	__u32 prog_ids[1024] = {0};
 | |
| 	__u32 iter;
 | |
| 	int ret;
 | |
| 
 | |
| 	p.query_flags = query_flags;
 | |
| 	p.prog_cnt = ARRAY_SIZE(prog_ids);
 | |
| 	p.prog_ids = prog_ids;
 | |
| 
 | |
| 	ret = bpf_prog_query_opts(cgroup_fd, type, &p);
 | |
| 	if (ret)
 | |
| 		return ret;
 | |
| 
 | |
| 	if (p.prog_cnt == 0)
 | |
| 		return 0;
 | |
| 
 | |
| 	for (iter = 0; iter < p.prog_cnt; iter++)
 | |
| 		show_bpf_prog(prog_ids[iter], type, NULL, level);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int show_attached_bpf_progs(int cgroup_fd, enum bpf_attach_type type,
 | |
| 				   int level)
 | |
| {
 | |
| 	LIBBPF_OPTS(bpf_prog_query_opts, p);
 | |
| 	__u32 prog_attach_flags[1024] = {0};
 | |
| 	const char *attach_flags_str;
 | |
| 	__u32 prog_ids[1024] = {0};
 | |
| 	char buf[32];
 | |
| 	__u32 iter;
 | |
| 	int ret;
 | |
| 
 | |
| 	p.query_flags = query_flags;
 | |
| 	p.prog_cnt = ARRAY_SIZE(prog_ids);
 | |
| 	p.prog_ids = prog_ids;
 | |
| 	p.prog_attach_flags = prog_attach_flags;
 | |
| 
 | |
| 	ret = bpf_prog_query_opts(cgroup_fd, type, &p);
 | |
| 	if (ret)
 | |
| 		return ret;
 | |
| 
 | |
| 	if (p.prog_cnt == 0)
 | |
| 		return 0;
 | |
| 
 | |
| 	for (iter = 0; iter < p.prog_cnt; iter++) {
 | |
| 		__u32 attach_flags;
 | |
| 
 | |
| 		attach_flags = prog_attach_flags[iter] ?: p.attach_flags;
 | |
| 
 | |
| 		switch (attach_flags) {
 | |
| 		case BPF_F_ALLOW_MULTI:
 | |
| 			attach_flags_str = "multi";
 | |
| 			break;
 | |
| 		case BPF_F_ALLOW_OVERRIDE:
 | |
| 			attach_flags_str = "override";
 | |
| 			break;
 | |
| 		case 0:
 | |
| 			attach_flags_str = "";
 | |
| 			break;
 | |
| 		default:
 | |
| 			snprintf(buf, sizeof(buf), "unknown(%x)", attach_flags);
 | |
| 			attach_flags_str = buf;
 | |
| 		}
 | |
| 
 | |
| 		show_bpf_prog(prog_ids[iter], type,
 | |
| 			      attach_flags_str, level);
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int show_bpf_progs(int cgroup_fd, enum bpf_attach_type type,
 | |
| 			  int level)
 | |
| {
 | |
| 	return query_flags & BPF_F_QUERY_EFFECTIVE ?
 | |
| 	       show_effective_bpf_progs(cgroup_fd, type, level) :
 | |
| 	       show_attached_bpf_progs(cgroup_fd, type, level);
 | |
| }
 | |
| 
 | |
| static int do_show(int argc, char **argv)
 | |
| {
 | |
| 	enum bpf_attach_type type;
 | |
| 	int has_attached_progs;
 | |
| 	const char *path;
 | |
| 	int cgroup_fd;
 | |
| 	int ret = -1;
 | |
| 
 | |
| 	query_flags = 0;
 | |
| 
 | |
| 	if (!REQ_ARGS(1))
 | |
| 		return -1;
 | |
| 	path = GET_ARG();
 | |
| 
 | |
| 	while (argc) {
 | |
| 		if (is_prefix(*argv, "effective")) {
 | |
| 			if (query_flags & BPF_F_QUERY_EFFECTIVE) {
 | |
| 				p_err("duplicated argument: %s", *argv);
 | |
| 				return -1;
 | |
| 			}
 | |
| 			query_flags |= BPF_F_QUERY_EFFECTIVE;
 | |
| 			NEXT_ARG();
 | |
| 		} else {
 | |
| 			p_err("expected no more arguments, 'effective', got: '%s'?",
 | |
| 			      *argv);
 | |
| 			return -1;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	cgroup_fd = open(path, O_RDONLY);
 | |
| 	if (cgroup_fd < 0) {
 | |
| 		p_err("can't open cgroup %s", path);
 | |
| 		goto exit;
 | |
| 	}
 | |
| 
 | |
| 	has_attached_progs = cgroup_has_attached_progs(cgroup_fd);
 | |
| 	if (has_attached_progs < 0) {
 | |
| 		p_err("can't query bpf programs attached to %s: %s",
 | |
| 		      path, strerror(errno));
 | |
| 		goto exit_cgroup;
 | |
| 	} else if (!has_attached_progs) {
 | |
| 		ret = 0;
 | |
| 		goto exit_cgroup;
 | |
| 	}
 | |
| 
 | |
| 	if (json_output)
 | |
| 		jsonw_start_array(json_wtr);
 | |
| 	else if (query_flags & BPF_F_QUERY_EFFECTIVE)
 | |
| 		printf("%-8s %-15s %-15s\n", "ID", "AttachType", "Name");
 | |
| 	else
 | |
| 		printf("%-8s %-15s %-15s %-15s\n", "ID", "AttachType",
 | |
| 		       "AttachFlags", "Name");
 | |
| 
 | |
| 	btf_vmlinux = libbpf_find_kernel_btf();
 | |
| 	for (type = 0; type < __MAX_BPF_ATTACH_TYPE; type++) {
 | |
| 		/*
 | |
| 		 * Not all attach types may be supported, so it's expected,
 | |
| 		 * that some requests will fail.
 | |
| 		 * If we were able to get the show for at least one
 | |
| 		 * attach type, let's return 0.
 | |
| 		 */
 | |
| 		if (show_bpf_progs(cgroup_fd, type, 0) == 0)
 | |
| 			ret = 0;
 | |
| 	}
 | |
| 
 | |
| 	if (json_output)
 | |
| 		jsonw_end_array(json_wtr);
 | |
| 
 | |
| exit_cgroup:
 | |
| 	close(cgroup_fd);
 | |
| exit:
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * To distinguish nftw() errors and do_show_tree_fn() errors
 | |
|  * and avoid duplicating error messages, let's return -2
 | |
|  * from do_show_tree_fn() in case of error.
 | |
|  */
 | |
| #define NFTW_ERR		-1
 | |
| #define SHOW_TREE_FN_ERR	-2
 | |
| static int do_show_tree_fn(const char *fpath, const struct stat *sb,
 | |
| 			   int typeflag, struct FTW *ftw)
 | |
| {
 | |
| 	enum bpf_attach_type type;
 | |
| 	int has_attached_progs;
 | |
| 	int cgroup_fd;
 | |
| 
 | |
| 	if (typeflag != FTW_D)
 | |
| 		return 0;
 | |
| 
 | |
| 	cgroup_fd = open(fpath, O_RDONLY);
 | |
| 	if (cgroup_fd < 0) {
 | |
| 		p_err("can't open cgroup %s: %s", fpath, strerror(errno));
 | |
| 		return SHOW_TREE_FN_ERR;
 | |
| 	}
 | |
| 
 | |
| 	has_attached_progs = cgroup_has_attached_progs(cgroup_fd);
 | |
| 	if (has_attached_progs < 0) {
 | |
| 		p_err("can't query bpf programs attached to %s: %s",
 | |
| 		      fpath, strerror(errno));
 | |
| 		close(cgroup_fd);
 | |
| 		return SHOW_TREE_FN_ERR;
 | |
| 	} else if (!has_attached_progs) {
 | |
| 		close(cgroup_fd);
 | |
| 		return 0;
 | |
| 	}
 | |
| 
 | |
| 	if (json_output) {
 | |
| 		jsonw_start_object(json_wtr);
 | |
| 		jsonw_string_field(json_wtr, "cgroup", fpath);
 | |
| 		jsonw_name(json_wtr, "programs");
 | |
| 		jsonw_start_array(json_wtr);
 | |
| 	} else {
 | |
| 		printf("%s\n", fpath);
 | |
| 	}
 | |
| 
 | |
| 	btf_vmlinux = libbpf_find_kernel_btf();
 | |
| 	for (type = 0; type < __MAX_BPF_ATTACH_TYPE; type++)
 | |
| 		show_bpf_progs(cgroup_fd, type, ftw->level);
 | |
| 
 | |
| 	if (errno == EINVAL)
 | |
| 		/* Last attach type does not support query.
 | |
| 		 * Do not report an error for this, especially because batch
 | |
| 		 * mode would stop processing commands.
 | |
| 		 */
 | |
| 		errno = 0;
 | |
| 
 | |
| 	if (json_output) {
 | |
| 		jsonw_end_array(json_wtr);
 | |
| 		jsonw_end_object(json_wtr);
 | |
| 	}
 | |
| 
 | |
| 	close(cgroup_fd);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static char *find_cgroup_root(void)
 | |
| {
 | |
| 	struct mntent *mnt;
 | |
| 	FILE *f;
 | |
| 
 | |
| 	f = fopen("/proc/mounts", "r");
 | |
| 	if (f == NULL)
 | |
| 		return NULL;
 | |
| 
 | |
| 	while ((mnt = getmntent(f))) {
 | |
| 		if (strcmp(mnt->mnt_type, "cgroup2") == 0) {
 | |
| 			fclose(f);
 | |
| 			return strdup(mnt->mnt_dir);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	fclose(f);
 | |
| 	return NULL;
 | |
| }
 | |
| 
 | |
| static int do_show_tree(int argc, char **argv)
 | |
| {
 | |
| 	char *cgroup_root, *cgroup_alloced = NULL;
 | |
| 	int ret;
 | |
| 
 | |
| 	query_flags = 0;
 | |
| 
 | |
| 	if (!argc) {
 | |
| 		cgroup_alloced = find_cgroup_root();
 | |
| 		if (!cgroup_alloced) {
 | |
| 			p_err("cgroup v2 isn't mounted");
 | |
| 			return -1;
 | |
| 		}
 | |
| 		cgroup_root = cgroup_alloced;
 | |
| 	} else {
 | |
| 		cgroup_root = GET_ARG();
 | |
| 
 | |
| 		while (argc) {
 | |
| 			if (is_prefix(*argv, "effective")) {
 | |
| 				if (query_flags & BPF_F_QUERY_EFFECTIVE) {
 | |
| 					p_err("duplicated argument: %s", *argv);
 | |
| 					return -1;
 | |
| 				}
 | |
| 				query_flags |= BPF_F_QUERY_EFFECTIVE;
 | |
| 				NEXT_ARG();
 | |
| 			} else {
 | |
| 				p_err("expected no more arguments, 'effective', got: '%s'?",
 | |
| 				      *argv);
 | |
| 				return -1;
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if (json_output)
 | |
| 		jsonw_start_array(json_wtr);
 | |
| 	else if (query_flags & BPF_F_QUERY_EFFECTIVE)
 | |
| 		printf("%s\n"
 | |
| 		       "%-8s %-15s %-15s\n",
 | |
| 		       "CgroupPath",
 | |
| 		       "ID", "AttachType", "Name");
 | |
| 	else
 | |
| 		printf("%s\n"
 | |
| 		       "%-8s %-15s %-15s %-15s\n",
 | |
| 		       "CgroupPath",
 | |
| 		       "ID", "AttachType", "AttachFlags", "Name");
 | |
| 
 | |
| 	switch (nftw(cgroup_root, do_show_tree_fn, 1024, FTW_MOUNT)) {
 | |
| 	case NFTW_ERR:
 | |
| 		p_err("can't iterate over %s: %s", cgroup_root,
 | |
| 		      strerror(errno));
 | |
| 		ret = -1;
 | |
| 		break;
 | |
| 	case SHOW_TREE_FN_ERR:
 | |
| 		ret = -1;
 | |
| 		break;
 | |
| 	default:
 | |
| 		ret = 0;
 | |
| 	}
 | |
| 
 | |
| 	if (json_output)
 | |
| 		jsonw_end_array(json_wtr);
 | |
| 
 | |
| 	free(cgroup_alloced);
 | |
| 
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static int do_attach(int argc, char **argv)
 | |
| {
 | |
| 	enum bpf_attach_type attach_type;
 | |
| 	int cgroup_fd, prog_fd;
 | |
| 	int attach_flags = 0;
 | |
| 	int ret = -1;
 | |
| 	int i;
 | |
| 
 | |
| 	if (argc < 4) {
 | |
| 		p_err("too few parameters for cgroup attach");
 | |
| 		goto exit;
 | |
| 	}
 | |
| 
 | |
| 	cgroup_fd = open(argv[0], O_RDONLY);
 | |
| 	if (cgroup_fd < 0) {
 | |
| 		p_err("can't open cgroup %s", argv[0]);
 | |
| 		goto exit;
 | |
| 	}
 | |
| 
 | |
| 	attach_type = parse_attach_type(argv[1]);
 | |
| 	if (attach_type == __MAX_BPF_ATTACH_TYPE) {
 | |
| 		p_err("invalid attach type");
 | |
| 		goto exit_cgroup;
 | |
| 	}
 | |
| 
 | |
| 	argc -= 2;
 | |
| 	argv = &argv[2];
 | |
| 	prog_fd = prog_parse_fd(&argc, &argv);
 | |
| 	if (prog_fd < 0)
 | |
| 		goto exit_cgroup;
 | |
| 
 | |
| 	for (i = 0; i < argc; i++) {
 | |
| 		if (is_prefix(argv[i], "multi")) {
 | |
| 			attach_flags |= BPF_F_ALLOW_MULTI;
 | |
| 		} else if (is_prefix(argv[i], "override")) {
 | |
| 			attach_flags |= BPF_F_ALLOW_OVERRIDE;
 | |
| 		} else {
 | |
| 			p_err("unknown option: %s", argv[i]);
 | |
| 			goto exit_cgroup;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if (bpf_prog_attach(prog_fd, cgroup_fd, attach_type, attach_flags)) {
 | |
| 		p_err("failed to attach program");
 | |
| 		goto exit_prog;
 | |
| 	}
 | |
| 
 | |
| 	if (json_output)
 | |
| 		jsonw_null(json_wtr);
 | |
| 
 | |
| 	ret = 0;
 | |
| 
 | |
| exit_prog:
 | |
| 	close(prog_fd);
 | |
| exit_cgroup:
 | |
| 	close(cgroup_fd);
 | |
| exit:
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static int do_detach(int argc, char **argv)
 | |
| {
 | |
| 	enum bpf_attach_type attach_type;
 | |
| 	int prog_fd, cgroup_fd;
 | |
| 	int ret = -1;
 | |
| 
 | |
| 	if (argc < 4) {
 | |
| 		p_err("too few parameters for cgroup detach");
 | |
| 		goto exit;
 | |
| 	}
 | |
| 
 | |
| 	cgroup_fd = open(argv[0], O_RDONLY);
 | |
| 	if (cgroup_fd < 0) {
 | |
| 		p_err("can't open cgroup %s", argv[0]);
 | |
| 		goto exit;
 | |
| 	}
 | |
| 
 | |
| 	attach_type = parse_attach_type(argv[1]);
 | |
| 	if (attach_type == __MAX_BPF_ATTACH_TYPE) {
 | |
| 		p_err("invalid attach type");
 | |
| 		goto exit_cgroup;
 | |
| 	}
 | |
| 
 | |
| 	argc -= 2;
 | |
| 	argv = &argv[2];
 | |
| 	prog_fd = prog_parse_fd(&argc, &argv);
 | |
| 	if (prog_fd < 0)
 | |
| 		goto exit_cgroup;
 | |
| 
 | |
| 	if (bpf_prog_detach2(prog_fd, cgroup_fd, attach_type)) {
 | |
| 		p_err("failed to detach program");
 | |
| 		goto exit_prog;
 | |
| 	}
 | |
| 
 | |
| 	if (json_output)
 | |
| 		jsonw_null(json_wtr);
 | |
| 
 | |
| 	ret = 0;
 | |
| 
 | |
| exit_prog:
 | |
| 	close(prog_fd);
 | |
| exit_cgroup:
 | |
| 	close(cgroup_fd);
 | |
| exit:
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static int do_help(int argc, char **argv)
 | |
| {
 | |
| 	if (json_output) {
 | |
| 		jsonw_null(json_wtr);
 | |
| 		return 0;
 | |
| 	}
 | |
| 
 | |
| 	fprintf(stderr,
 | |
| 		"Usage: %1$s %2$s { show | list } CGROUP [**effective**]\n"
 | |
| 		"       %1$s %2$s tree [CGROUP_ROOT] [**effective**]\n"
 | |
| 		"       %1$s %2$s attach CGROUP ATTACH_TYPE PROG [ATTACH_FLAGS]\n"
 | |
| 		"       %1$s %2$s detach CGROUP ATTACH_TYPE PROG\n"
 | |
| 		"       %1$s %2$s help\n"
 | |
| 		"\n"
 | |
| 		HELP_SPEC_ATTACH_TYPES "\n"
 | |
| 		"       " HELP_SPEC_ATTACH_FLAGS "\n"
 | |
| 		"       " HELP_SPEC_PROGRAM "\n"
 | |
| 		"       " HELP_SPEC_OPTIONS " |\n"
 | |
| 		"                    {-f|--bpffs} }\n"
 | |
| 		"",
 | |
| 		bin_name, argv[-2]);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static const struct cmd cmds[] = {
 | |
| 	{ "show",	do_show },
 | |
| 	{ "list",	do_show },
 | |
| 	{ "tree",       do_show_tree },
 | |
| 	{ "attach",	do_attach },
 | |
| 	{ "detach",	do_detach },
 | |
| 	{ "help",	do_help },
 | |
| 	{ 0 }
 | |
| };
 | |
| 
 | |
| int do_cgroup(int argc, char **argv)
 | |
| {
 | |
| 	return cmd_select(cmds, argc, argv, do_help);
 | |
| }
 |