xref: /openbmc/linux/tools/bpf/bpftool/cgroup.c (revision 38f0408e)
1907b2236SJakub Kicinski // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
25ccda64dSRoman Gushchin // Copyright (C) 2017 Facebook
35ccda64dSRoman Gushchin // Author: Roman Gushchin <guro@fb.com>
45ccda64dSRoman Gushchin 
52058b383SRoman Gushchin #define _XOPEN_SOURCE 500
62058b383SRoman Gushchin #include <errno.h>
75ccda64dSRoman Gushchin #include <fcntl.h>
82058b383SRoman Gushchin #include <ftw.h>
92058b383SRoman Gushchin #include <mntent.h>
102058b383SRoman Gushchin #include <stdio.h>
115ccda64dSRoman Gushchin #include <stdlib.h>
125ccda64dSRoman Gushchin #include <string.h>
135ccda64dSRoman Gushchin #include <sys/stat.h>
145ccda64dSRoman Gushchin #include <sys/types.h>
155ccda64dSRoman Gushchin #include <unistd.h>
165ccda64dSRoman Gushchin 
17229c3b47SToke Høiland-Jørgensen #include <bpf/bpf.h>
18596f5fb2SStanislav Fomichev #include <bpf/btf.h>
195ccda64dSRoman Gushchin 
205ccda64dSRoman Gushchin #include "main.h"
215ccda64dSRoman Gushchin 
225ccda64dSRoman Gushchin #define HELP_SPEC_ATTACH_FLAGS						\
235ccda64dSRoman Gushchin 	"ATTACH_FLAGS := { multi | override }"
245ccda64dSRoman Gushchin 
255ccda64dSRoman Gushchin #define HELP_SPEC_ATTACH_TYPES						\
261ba5ad36SDaniel Müller 	"       ATTACH_TYPE := { cgroup_inet_ingress | cgroup_inet_egress |\n" \
271ba5ad36SDaniel Müller 	"                        cgroup_inet_sock_create | cgroup_sock_ops |\n" \
281ba5ad36SDaniel Müller 	"                        cgroup_device | cgroup_inet4_bind |\n" \
291ba5ad36SDaniel Müller 	"                        cgroup_inet6_bind | cgroup_inet4_post_bind |\n" \
301ba5ad36SDaniel Müller 	"                        cgroup_inet6_post_bind | cgroup_inet4_connect |\n" \
311ba5ad36SDaniel Müller 	"                        cgroup_inet6_connect | cgroup_inet4_getpeername |\n" \
321ba5ad36SDaniel Müller 	"                        cgroup_inet6_getpeername | cgroup_inet4_getsockname |\n" \
331ba5ad36SDaniel Müller 	"                        cgroup_inet6_getsockname | cgroup_udp4_sendmsg |\n" \
341ba5ad36SDaniel Müller 	"                        cgroup_udp6_sendmsg | cgroup_udp4_recvmsg |\n" \
351ba5ad36SDaniel Müller 	"                        cgroup_udp6_recvmsg | cgroup_sysctl |\n" \
361ba5ad36SDaniel Müller 	"                        cgroup_getsockopt | cgroup_setsockopt |\n" \
371ba5ad36SDaniel Müller 	"                        cgroup_inet_sock_release }"
385ccda64dSRoman Gushchin 
39a98bf573SJakub Kicinski static unsigned int query_flags;
40596f5fb2SStanislav Fomichev static struct btf *btf_vmlinux;
41596f5fb2SStanislav Fomichev static __u32 btf_vmlinux_id;
42a98bf573SJakub Kicinski 
parse_attach_type(const char * str)435ccda64dSRoman Gushchin static enum bpf_attach_type parse_attach_type(const char *str)
445ccda64dSRoman Gushchin {
451ba5ad36SDaniel Müller 	const char *attach_type_str;
465ccda64dSRoman Gushchin 	enum bpf_attach_type type;
475ccda64dSRoman Gushchin 
481ba5ad36SDaniel Müller 	for (type = 0; ; type++) {
491ba5ad36SDaniel Müller 		attach_type_str = libbpf_bpf_attach_type_str(type);
501ba5ad36SDaniel Müller 		if (!attach_type_str)
511ba5ad36SDaniel Müller 			break;
521ba5ad36SDaniel Müller 		if (!strcmp(str, attach_type_str))
531ba5ad36SDaniel Müller 			return type;
541ba5ad36SDaniel Müller 	}
551ba5ad36SDaniel Müller 
561ba5ad36SDaniel Müller 	/* Also check traditionally used attach type strings. For these we keep
571ba5ad36SDaniel Müller 	 * allowing prefixed usage.
581ba5ad36SDaniel Müller 	 */
591ba5ad36SDaniel Müller 	for (type = 0; ; type++) {
601ba5ad36SDaniel Müller 		attach_type_str = bpf_attach_type_input_str(type);
611ba5ad36SDaniel Müller 		if (!attach_type_str)
621ba5ad36SDaniel Müller 			break;
631ba5ad36SDaniel Müller 		if (is_prefix(str, attach_type_str))
645ccda64dSRoman Gushchin 			return type;
655ccda64dSRoman Gushchin 	}
665ccda64dSRoman Gushchin 
675ccda64dSRoman Gushchin 	return __MAX_BPF_ATTACH_TYPE;
685ccda64dSRoman Gushchin }
695ccda64dSRoman Gushchin 
guess_vmlinux_btf_id(__u32 attach_btf_obj_id)70596f5fb2SStanislav Fomichev static void guess_vmlinux_btf_id(__u32 attach_btf_obj_id)
71596f5fb2SStanislav Fomichev {
72596f5fb2SStanislav Fomichev 	struct bpf_btf_info btf_info = {};
73596f5fb2SStanislav Fomichev 	__u32 btf_len = sizeof(btf_info);
74596f5fb2SStanislav Fomichev 	char name[16] = {};
75596f5fb2SStanislav Fomichev 	int err;
76596f5fb2SStanislav Fomichev 	int fd;
77596f5fb2SStanislav Fomichev 
78596f5fb2SStanislav Fomichev 	btf_info.name = ptr_to_u64(name);
79596f5fb2SStanislav Fomichev 	btf_info.name_len = sizeof(name);
80596f5fb2SStanislav Fomichev 
81596f5fb2SStanislav Fomichev 	fd = bpf_btf_get_fd_by_id(attach_btf_obj_id);
82596f5fb2SStanislav Fomichev 	if (fd < 0)
83596f5fb2SStanislav Fomichev 		return;
84596f5fb2SStanislav Fomichev 
85*38f0408eSIlya Leoshkevich 	err = bpf_btf_get_info_by_fd(fd, &btf_info, &btf_len);
86596f5fb2SStanislav Fomichev 	if (err)
87596f5fb2SStanislav Fomichev 		goto out;
88596f5fb2SStanislav Fomichev 
89596f5fb2SStanislav Fomichev 	if (btf_info.kernel_btf && strncmp(name, "vmlinux", sizeof(name)) == 0)
90596f5fb2SStanislav Fomichev 		btf_vmlinux_id = btf_info.id;
91596f5fb2SStanislav Fomichev 
92596f5fb2SStanislav Fomichev out:
93596f5fb2SStanislav Fomichev 	close(fd);
94596f5fb2SStanislav Fomichev }
95596f5fb2SStanislav Fomichev 
show_bpf_prog(int id,enum bpf_attach_type attach_type,const char * attach_flags_str,int level)9650325b17SAndrii Nakryiko static int show_bpf_prog(int id, enum bpf_attach_type attach_type,
972058b383SRoman Gushchin 			 const char *attach_flags_str,
982058b383SRoman Gushchin 			 int level)
995ccda64dSRoman Gushchin {
100b662000aSRaman Shukhau 	char prog_name[MAX_PROG_FULL_NAME];
101596f5fb2SStanislav Fomichev 	const char *attach_btf_name = NULL;
1025ccda64dSRoman Gushchin 	struct bpf_prog_info info = {};
1031ba5ad36SDaniel Müller 	const char *attach_type_str;
1045ccda64dSRoman Gushchin 	__u32 info_len = sizeof(info);
1055ccda64dSRoman Gushchin 	int prog_fd;
1065ccda64dSRoman Gushchin 
1075ccda64dSRoman Gushchin 	prog_fd = bpf_prog_get_fd_by_id(id);
1085ccda64dSRoman Gushchin 	if (prog_fd < 0)
1095ccda64dSRoman Gushchin 		return -1;
1105ccda64dSRoman Gushchin 
111*38f0408eSIlya Leoshkevich 	if (bpf_prog_get_info_by_fd(prog_fd, &info, &info_len)) {
1125ccda64dSRoman Gushchin 		close(prog_fd);
1135ccda64dSRoman Gushchin 		return -1;
1145ccda64dSRoman Gushchin 	}
1155ccda64dSRoman Gushchin 
1161ba5ad36SDaniel Müller 	attach_type_str = libbpf_bpf_attach_type_str(attach_type);
117596f5fb2SStanislav Fomichev 
118596f5fb2SStanislav Fomichev 	if (btf_vmlinux) {
119596f5fb2SStanislav Fomichev 		if (!btf_vmlinux_id)
120596f5fb2SStanislav Fomichev 			guess_vmlinux_btf_id(info.attach_btf_obj_id);
121596f5fb2SStanislav Fomichev 
122596f5fb2SStanislav Fomichev 		if (btf_vmlinux_id == info.attach_btf_obj_id &&
123596f5fb2SStanislav Fomichev 		    info.attach_btf_id < btf__type_cnt(btf_vmlinux)) {
124596f5fb2SStanislav Fomichev 			const struct btf_type *t =
125596f5fb2SStanislav Fomichev 				btf__type_by_id(btf_vmlinux, info.attach_btf_id);
126596f5fb2SStanislav Fomichev 			attach_btf_name =
127596f5fb2SStanislav Fomichev 				btf__name_by_offset(btf_vmlinux, t->name_off);
128596f5fb2SStanislav Fomichev 		}
129596f5fb2SStanislav Fomichev 	}
130596f5fb2SStanislav Fomichev 
131b662000aSRaman Shukhau 	get_prog_full_name(&info, prog_fd, prog_name, sizeof(prog_name));
1325ccda64dSRoman Gushchin 	if (json_output) {
1335ccda64dSRoman Gushchin 		jsonw_start_object(json_wtr);
1345ccda64dSRoman Gushchin 		jsonw_uint_field(json_wtr, "id", info.id);
1351ba5ad36SDaniel Müller 		if (attach_type_str)
1361ba5ad36SDaniel Müller 			jsonw_string_field(json_wtr, "attach_type", attach_type_str);
13750325b17SAndrii Nakryiko 		else
13850325b17SAndrii Nakryiko 			jsonw_uint_field(json_wtr, "attach_type", attach_type);
139bdcee1b0SPu Lehui 		if (!(query_flags & BPF_F_QUERY_EFFECTIVE))
140bdcee1b0SPu Lehui 			jsonw_string_field(json_wtr, "attach_flags", attach_flags_str);
141b662000aSRaman Shukhau 		jsonw_string_field(json_wtr, "name", prog_name);
142596f5fb2SStanislav Fomichev 		if (attach_btf_name)
143596f5fb2SStanislav Fomichev 			jsonw_string_field(json_wtr, "attach_btf_name", attach_btf_name);
144596f5fb2SStanislav Fomichev 		jsonw_uint_field(json_wtr, "attach_btf_obj_id", info.attach_btf_obj_id);
145596f5fb2SStanislav Fomichev 		jsonw_uint_field(json_wtr, "attach_btf_id", info.attach_btf_id);
1465ccda64dSRoman Gushchin 		jsonw_end_object(json_wtr);
1475ccda64dSRoman Gushchin 	} else {
14850325b17SAndrii Nakryiko 		printf("%s%-8u ", level ? "    " : "", info.id);
1491ba5ad36SDaniel Müller 		if (attach_type_str)
1501ba5ad36SDaniel Müller 			printf("%-15s", attach_type_str);
15150325b17SAndrii Nakryiko 		else
15250325b17SAndrii Nakryiko 			printf("type %-10u", attach_type);
153bdcee1b0SPu Lehui 		if (query_flags & BPF_F_QUERY_EFFECTIVE)
154bdcee1b0SPu Lehui 			printf(" %-15s", prog_name);
155bdcee1b0SPu Lehui 		else
156596f5fb2SStanislav Fomichev 			printf(" %-15s %-15s", attach_flags_str, prog_name);
157596f5fb2SStanislav Fomichev 		if (attach_btf_name)
158596f5fb2SStanislav Fomichev 			printf(" %-15s", attach_btf_name);
159596f5fb2SStanislav Fomichev 		else if (info.attach_btf_id)
160596f5fb2SStanislav Fomichev 			printf(" attach_btf_obj_id=%d attach_btf_id=%d",
161596f5fb2SStanislav Fomichev 			       info.attach_btf_obj_id, info.attach_btf_id);
162596f5fb2SStanislav Fomichev 		printf("\n");
1635ccda64dSRoman Gushchin 	}
1645ccda64dSRoman Gushchin 
1655ccda64dSRoman Gushchin 	close(prog_fd);
1665ccda64dSRoman Gushchin 	return 0;
1675ccda64dSRoman Gushchin }
1685ccda64dSRoman Gushchin 
count_attached_bpf_progs(int cgroup_fd,enum bpf_attach_type type)1692058b383SRoman Gushchin static int count_attached_bpf_progs(int cgroup_fd, enum bpf_attach_type type)
1702058b383SRoman Gushchin {
1712058b383SRoman Gushchin 	__u32 prog_cnt = 0;
1722058b383SRoman Gushchin 	int ret;
1732058b383SRoman Gushchin 
174a98bf573SJakub Kicinski 	ret = bpf_prog_query(cgroup_fd, type, query_flags, NULL,
175a98bf573SJakub Kicinski 			     NULL, &prog_cnt);
1762058b383SRoman Gushchin 	if (ret)
1772058b383SRoman Gushchin 		return -1;
1782058b383SRoman Gushchin 
1792058b383SRoman Gushchin 	return prog_cnt;
1802058b383SRoman Gushchin }
1812058b383SRoman Gushchin 
cgroup_has_attached_progs(int cgroup_fd)1821162f844SHechao Li static int cgroup_has_attached_progs(int cgroup_fd)
1831162f844SHechao Li {
1841162f844SHechao Li 	enum bpf_attach_type type;
1851162f844SHechao Li 	bool no_prog = true;
1861162f844SHechao Li 
1871162f844SHechao Li 	for (type = 0; type < __MAX_BPF_ATTACH_TYPE; type++) {
1881162f844SHechao Li 		int count = count_attached_bpf_progs(cgroup_fd, type);
1891162f844SHechao Li 
1901162f844SHechao Li 		if (count < 0 && errno != EINVAL)
1911162f844SHechao Li 			return -1;
1921162f844SHechao Li 
1931162f844SHechao Li 		if (count > 0) {
1941162f844SHechao Li 			no_prog = false;
1951162f844SHechao Li 			break;
1961162f844SHechao Li 		}
1971162f844SHechao Li 	}
1981162f844SHechao Li 
1991162f844SHechao Li 	return no_prog ? 0 : 1;
2001162f844SHechao Li }
201bdcee1b0SPu Lehui 
show_effective_bpf_progs(int cgroup_fd,enum bpf_attach_type type,int level)202bdcee1b0SPu Lehui static int show_effective_bpf_progs(int cgroup_fd, enum bpf_attach_type type,
203bdcee1b0SPu Lehui 				    int level)
204bdcee1b0SPu Lehui {
205bdcee1b0SPu Lehui 	LIBBPF_OPTS(bpf_prog_query_opts, p);
206bdcee1b0SPu Lehui 	__u32 prog_ids[1024] = {0};
207bdcee1b0SPu Lehui 	__u32 iter;
208bdcee1b0SPu Lehui 	int ret;
209bdcee1b0SPu Lehui 
210bdcee1b0SPu Lehui 	p.query_flags = query_flags;
211bdcee1b0SPu Lehui 	p.prog_cnt = ARRAY_SIZE(prog_ids);
212bdcee1b0SPu Lehui 	p.prog_ids = prog_ids;
213bdcee1b0SPu Lehui 
214bdcee1b0SPu Lehui 	ret = bpf_prog_query_opts(cgroup_fd, type, &p);
215bdcee1b0SPu Lehui 	if (ret)
216bdcee1b0SPu Lehui 		return ret;
217bdcee1b0SPu Lehui 
218bdcee1b0SPu Lehui 	if (p.prog_cnt == 0)
219bdcee1b0SPu Lehui 		return 0;
220bdcee1b0SPu Lehui 
221bdcee1b0SPu Lehui 	for (iter = 0; iter < p.prog_cnt; iter++)
222bdcee1b0SPu Lehui 		show_bpf_prog(prog_ids[iter], type, NULL, level);
223bdcee1b0SPu Lehui 
224bdcee1b0SPu Lehui 	return 0;
225bdcee1b0SPu Lehui }
226bdcee1b0SPu Lehui 
show_attached_bpf_progs(int cgroup_fd,enum bpf_attach_type type,int level)2272058b383SRoman Gushchin static int show_attached_bpf_progs(int cgroup_fd, enum bpf_attach_type type,
2282058b383SRoman Gushchin 				   int level)
2295ccda64dSRoman Gushchin {
230596f5fb2SStanislav Fomichev 	LIBBPF_OPTS(bpf_prog_query_opts, p);
231596f5fb2SStanislav Fomichev 	__u32 prog_attach_flags[1024] = {0};
232a9436dcaSQuentin Monnet 	const char *attach_flags_str;
2335ccda64dSRoman Gushchin 	__u32 prog_ids[1024] = {0};
2345ccda64dSRoman Gushchin 	char buf[32];
235596f5fb2SStanislav Fomichev 	__u32 iter;
2365ccda64dSRoman Gushchin 	int ret;
2375ccda64dSRoman Gushchin 
238596f5fb2SStanislav Fomichev 	p.query_flags = query_flags;
239596f5fb2SStanislav Fomichev 	p.prog_cnt = ARRAY_SIZE(prog_ids);
240596f5fb2SStanislav Fomichev 	p.prog_ids = prog_ids;
241596f5fb2SStanislav Fomichev 	p.prog_attach_flags = prog_attach_flags;
242596f5fb2SStanislav Fomichev 
243596f5fb2SStanislav Fomichev 	ret = bpf_prog_query_opts(cgroup_fd, type, &p);
2445ccda64dSRoman Gushchin 	if (ret)
2455ccda64dSRoman Gushchin 		return ret;
2465ccda64dSRoman Gushchin 
247596f5fb2SStanislav Fomichev 	if (p.prog_cnt == 0)
2485ccda64dSRoman Gushchin 		return 0;
2495ccda64dSRoman Gushchin 
250596f5fb2SStanislav Fomichev 	for (iter = 0; iter < p.prog_cnt; iter++) {
251596f5fb2SStanislav Fomichev 		__u32 attach_flags;
252596f5fb2SStanislav Fomichev 
253596f5fb2SStanislav Fomichev 		attach_flags = prog_attach_flags[iter] ?: p.attach_flags;
254596f5fb2SStanislav Fomichev 
2555ccda64dSRoman Gushchin 		switch (attach_flags) {
2565ccda64dSRoman Gushchin 		case BPF_F_ALLOW_MULTI:
2575ccda64dSRoman Gushchin 			attach_flags_str = "multi";
2585ccda64dSRoman Gushchin 			break;
2595ccda64dSRoman Gushchin 		case BPF_F_ALLOW_OVERRIDE:
2605ccda64dSRoman Gushchin 			attach_flags_str = "override";
2615ccda64dSRoman Gushchin 			break;
2625ccda64dSRoman Gushchin 		case 0:
2635ccda64dSRoman Gushchin 			attach_flags_str = "";
2645ccda64dSRoman Gushchin 			break;
2655ccda64dSRoman Gushchin 		default:
2665ccda64dSRoman Gushchin 			snprintf(buf, sizeof(buf), "unknown(%x)", attach_flags);
2675ccda64dSRoman Gushchin 			attach_flags_str = buf;
2685ccda64dSRoman Gushchin 		}
2695ccda64dSRoman Gushchin 
27050325b17SAndrii Nakryiko 		show_bpf_prog(prog_ids[iter], type,
2712058b383SRoman Gushchin 			      attach_flags_str, level);
272596f5fb2SStanislav Fomichev 	}
2735ccda64dSRoman Gushchin 
2745ccda64dSRoman Gushchin 	return 0;
2755ccda64dSRoman Gushchin }
2765ccda64dSRoman Gushchin 
show_bpf_progs(int cgroup_fd,enum bpf_attach_type type,int level)277bdcee1b0SPu Lehui static int show_bpf_progs(int cgroup_fd, enum bpf_attach_type type,
278bdcee1b0SPu Lehui 			  int level)
279bdcee1b0SPu Lehui {
280bdcee1b0SPu Lehui 	return query_flags & BPF_F_QUERY_EFFECTIVE ?
281bdcee1b0SPu Lehui 	       show_effective_bpf_progs(cgroup_fd, type, level) :
282bdcee1b0SPu Lehui 	       show_attached_bpf_progs(cgroup_fd, type, level);
283bdcee1b0SPu Lehui }
284bdcee1b0SPu Lehui 
do_show(int argc,char ** argv)28565b875bcSJakub Kicinski static int do_show(int argc, char **argv)
2865ccda64dSRoman Gushchin {
2875ccda64dSRoman Gushchin 	enum bpf_attach_type type;
2881162f844SHechao Li 	int has_attached_progs;
289a98bf573SJakub Kicinski 	const char *path;
2905ccda64dSRoman Gushchin 	int cgroup_fd;
2915ccda64dSRoman Gushchin 	int ret = -1;
2925ccda64dSRoman Gushchin 
293a98bf573SJakub Kicinski 	query_flags = 0;
294a98bf573SJakub Kicinski 
295a98bf573SJakub Kicinski 	if (!REQ_ARGS(1))
296a98bf573SJakub Kicinski 		return -1;
297a98bf573SJakub Kicinski 	path = GET_ARG();
298a98bf573SJakub Kicinski 
299a98bf573SJakub Kicinski 	while (argc) {
300a98bf573SJakub Kicinski 		if (is_prefix(*argv, "effective")) {
301a98bf573SJakub Kicinski 			if (query_flags & BPF_F_QUERY_EFFECTIVE) {
302a98bf573SJakub Kicinski 				p_err("duplicated argument: %s", *argv);
303a98bf573SJakub Kicinski 				return -1;
304a98bf573SJakub Kicinski 			}
305a98bf573SJakub Kicinski 			query_flags |= BPF_F_QUERY_EFFECTIVE;
306a98bf573SJakub Kicinski 			NEXT_ARG();
307a98bf573SJakub Kicinski 		} else {
308a98bf573SJakub Kicinski 			p_err("expected no more arguments, 'effective', got: '%s'?",
309a98bf573SJakub Kicinski 			      *argv);
310a98bf573SJakub Kicinski 			return -1;
311a98bf573SJakub Kicinski 		}
3125ccda64dSRoman Gushchin 	}
3135ccda64dSRoman Gushchin 
314a98bf573SJakub Kicinski 	cgroup_fd = open(path, O_RDONLY);
3155ccda64dSRoman Gushchin 	if (cgroup_fd < 0) {
316a98bf573SJakub Kicinski 		p_err("can't open cgroup %s", path);
3175ccda64dSRoman Gushchin 		goto exit;
3185ccda64dSRoman Gushchin 	}
3195ccda64dSRoman Gushchin 
3201162f844SHechao Li 	has_attached_progs = cgroup_has_attached_progs(cgroup_fd);
3211162f844SHechao Li 	if (has_attached_progs < 0) {
3221162f844SHechao Li 		p_err("can't query bpf programs attached to %s: %s",
3231162f844SHechao Li 		      path, strerror(errno));
3241162f844SHechao Li 		goto exit_cgroup;
3251162f844SHechao Li 	} else if (!has_attached_progs) {
3261162f844SHechao Li 		ret = 0;
3271162f844SHechao Li 		goto exit_cgroup;
3281162f844SHechao Li 	}
3291162f844SHechao Li 
3305ccda64dSRoman Gushchin 	if (json_output)
3315ccda64dSRoman Gushchin 		jsonw_start_array(json_wtr);
332bdcee1b0SPu Lehui 	else if (query_flags & BPF_F_QUERY_EFFECTIVE)
333bdcee1b0SPu Lehui 		printf("%-8s %-15s %-15s\n", "ID", "AttachType", "Name");
3345ccda64dSRoman Gushchin 	else
3355ccda64dSRoman Gushchin 		printf("%-8s %-15s %-15s %-15s\n", "ID", "AttachType",
3365ccda64dSRoman Gushchin 		       "AttachFlags", "Name");
3375ccda64dSRoman Gushchin 
338596f5fb2SStanislav Fomichev 	btf_vmlinux = libbpf_find_kernel_btf();
3395ccda64dSRoman Gushchin 	for (type = 0; type < __MAX_BPF_ATTACH_TYPE; type++) {
3405ccda64dSRoman Gushchin 		/*
3415ccda64dSRoman Gushchin 		 * Not all attach types may be supported, so it's expected,
3425ccda64dSRoman Gushchin 		 * that some requests will fail.
34365b875bcSJakub Kicinski 		 * If we were able to get the show for at least one
3445ccda64dSRoman Gushchin 		 * attach type, let's return 0.
3455ccda64dSRoman Gushchin 		 */
346bdcee1b0SPu Lehui 		if (show_bpf_progs(cgroup_fd, type, 0) == 0)
3475ccda64dSRoman Gushchin 			ret = 0;
3485ccda64dSRoman Gushchin 	}
3495ccda64dSRoman Gushchin 
3505ccda64dSRoman Gushchin 	if (json_output)
3515ccda64dSRoman Gushchin 		jsonw_end_array(json_wtr);
3525ccda64dSRoman Gushchin 
3531162f844SHechao Li exit_cgroup:
3545ccda64dSRoman Gushchin 	close(cgroup_fd);
3555ccda64dSRoman Gushchin exit:
3565ccda64dSRoman Gushchin 	return ret;
3575ccda64dSRoman Gushchin }
3585ccda64dSRoman Gushchin 
3592058b383SRoman Gushchin /*
3602058b383SRoman Gushchin  * To distinguish nftw() errors and do_show_tree_fn() errors
3612058b383SRoman Gushchin  * and avoid duplicating error messages, let's return -2
3622058b383SRoman Gushchin  * from do_show_tree_fn() in case of error.
3632058b383SRoman Gushchin  */
3642058b383SRoman Gushchin #define NFTW_ERR		-1
3652058b383SRoman Gushchin #define SHOW_TREE_FN_ERR	-2
do_show_tree_fn(const char * fpath,const struct stat * sb,int typeflag,struct FTW * ftw)3662058b383SRoman Gushchin static int do_show_tree_fn(const char *fpath, const struct stat *sb,
3672058b383SRoman Gushchin 			   int typeflag, struct FTW *ftw)
3682058b383SRoman Gushchin {
3692058b383SRoman Gushchin 	enum bpf_attach_type type;
3701162f844SHechao Li 	int has_attached_progs;
3712058b383SRoman Gushchin 	int cgroup_fd;
3722058b383SRoman Gushchin 
3732058b383SRoman Gushchin 	if (typeflag != FTW_D)
3742058b383SRoman Gushchin 		return 0;
3752058b383SRoman Gushchin 
3762058b383SRoman Gushchin 	cgroup_fd = open(fpath, O_RDONLY);
3772058b383SRoman Gushchin 	if (cgroup_fd < 0) {
3782058b383SRoman Gushchin 		p_err("can't open cgroup %s: %s", fpath, strerror(errno));
3792058b383SRoman Gushchin 		return SHOW_TREE_FN_ERR;
3802058b383SRoman Gushchin 	}
3812058b383SRoman Gushchin 
3821162f844SHechao Li 	has_attached_progs = cgroup_has_attached_progs(cgroup_fd);
3831162f844SHechao Li 	if (has_attached_progs < 0) {
3842058b383SRoman Gushchin 		p_err("can't query bpf programs attached to %s: %s",
3852058b383SRoman Gushchin 		      fpath, strerror(errno));
3862058b383SRoman Gushchin 		close(cgroup_fd);
3872058b383SRoman Gushchin 		return SHOW_TREE_FN_ERR;
3881162f844SHechao Li 	} else if (!has_attached_progs) {
3892058b383SRoman Gushchin 		close(cgroup_fd);
3902058b383SRoman Gushchin 		return 0;
3912058b383SRoman Gushchin 	}
3922058b383SRoman Gushchin 
3932058b383SRoman Gushchin 	if (json_output) {
3942058b383SRoman Gushchin 		jsonw_start_object(json_wtr);
3952058b383SRoman Gushchin 		jsonw_string_field(json_wtr, "cgroup", fpath);
3962058b383SRoman Gushchin 		jsonw_name(json_wtr, "programs");
3972058b383SRoman Gushchin 		jsonw_start_array(json_wtr);
3982058b383SRoman Gushchin 	} else {
3992058b383SRoman Gushchin 		printf("%s\n", fpath);
4002058b383SRoman Gushchin 	}
4012058b383SRoman Gushchin 
402596f5fb2SStanislav Fomichev 	btf_vmlinux = libbpf_find_kernel_btf();
4032058b383SRoman Gushchin 	for (type = 0; type < __MAX_BPF_ATTACH_TYPE; type++)
404bdcee1b0SPu Lehui 		show_bpf_progs(cgroup_fd, type, ftw->level);
4052058b383SRoman Gushchin 
40639c9f106SQuentin Monnet 	if (errno == EINVAL)
40739c9f106SQuentin Monnet 		/* Last attach type does not support query.
40839c9f106SQuentin Monnet 		 * Do not report an error for this, especially because batch
40939c9f106SQuentin Monnet 		 * mode would stop processing commands.
41039c9f106SQuentin Monnet 		 */
41139c9f106SQuentin Monnet 		errno = 0;
41239c9f106SQuentin Monnet 
4132058b383SRoman Gushchin 	if (json_output) {
4142058b383SRoman Gushchin 		jsonw_end_array(json_wtr);
4152058b383SRoman Gushchin 		jsonw_end_object(json_wtr);
4162058b383SRoman Gushchin 	}
4172058b383SRoman Gushchin 
4182058b383SRoman Gushchin 	close(cgroup_fd);
4192058b383SRoman Gushchin 
4202058b383SRoman Gushchin 	return 0;
4212058b383SRoman Gushchin }
4222058b383SRoman Gushchin 
find_cgroup_root(void)4232058b383SRoman Gushchin static char *find_cgroup_root(void)
4242058b383SRoman Gushchin {
4252058b383SRoman Gushchin 	struct mntent *mnt;
4262058b383SRoman Gushchin 	FILE *f;
4272058b383SRoman Gushchin 
4282058b383SRoman Gushchin 	f = fopen("/proc/mounts", "r");
4292058b383SRoman Gushchin 	if (f == NULL)
4302058b383SRoman Gushchin 		return NULL;
4312058b383SRoman Gushchin 
4322058b383SRoman Gushchin 	while ((mnt = getmntent(f))) {
4332058b383SRoman Gushchin 		if (strcmp(mnt->mnt_type, "cgroup2") == 0) {
4342058b383SRoman Gushchin 			fclose(f);
4352058b383SRoman Gushchin 			return strdup(mnt->mnt_dir);
4362058b383SRoman Gushchin 		}
4372058b383SRoman Gushchin 	}
4382058b383SRoman Gushchin 
4392058b383SRoman Gushchin 	fclose(f);
4402058b383SRoman Gushchin 	return NULL;
4412058b383SRoman Gushchin }
4422058b383SRoman Gushchin 
do_show_tree(int argc,char ** argv)4432058b383SRoman Gushchin static int do_show_tree(int argc, char **argv)
4442058b383SRoman Gushchin {
445a98bf573SJakub Kicinski 	char *cgroup_root, *cgroup_alloced = NULL;
4462058b383SRoman Gushchin 	int ret;
4472058b383SRoman Gushchin 
448a98bf573SJakub Kicinski 	query_flags = 0;
449a98bf573SJakub Kicinski 
450a98bf573SJakub Kicinski 	if (!argc) {
451a98bf573SJakub Kicinski 		cgroup_alloced = find_cgroup_root();
452a98bf573SJakub Kicinski 		if (!cgroup_alloced) {
4532058b383SRoman Gushchin 			p_err("cgroup v2 isn't mounted");
4542058b383SRoman Gushchin 			return -1;
4552058b383SRoman Gushchin 		}
456a98bf573SJakub Kicinski 		cgroup_root = cgroup_alloced;
457a98bf573SJakub Kicinski 	} else {
458a98bf573SJakub Kicinski 		cgroup_root = GET_ARG();
459a98bf573SJakub Kicinski 
460a98bf573SJakub Kicinski 		while (argc) {
461a98bf573SJakub Kicinski 			if (is_prefix(*argv, "effective")) {
462a98bf573SJakub Kicinski 				if (query_flags & BPF_F_QUERY_EFFECTIVE) {
463a98bf573SJakub Kicinski 					p_err("duplicated argument: %s", *argv);
4642058b383SRoman Gushchin 					return -1;
4652058b383SRoman Gushchin 				}
466a98bf573SJakub Kicinski 				query_flags |= BPF_F_QUERY_EFFECTIVE;
467a98bf573SJakub Kicinski 				NEXT_ARG();
468a98bf573SJakub Kicinski 			} else {
469a98bf573SJakub Kicinski 				p_err("expected no more arguments, 'effective', got: '%s'?",
470a98bf573SJakub Kicinski 				      *argv);
471a98bf573SJakub Kicinski 				return -1;
472a98bf573SJakub Kicinski 			}
473a98bf573SJakub Kicinski 		}
474a98bf573SJakub Kicinski 	}
4752058b383SRoman Gushchin 
4762058b383SRoman Gushchin 	if (json_output)
4772058b383SRoman Gushchin 		jsonw_start_array(json_wtr);
478bdcee1b0SPu Lehui 	else if (query_flags & BPF_F_QUERY_EFFECTIVE)
479bdcee1b0SPu Lehui 		printf("%s\n"
480bdcee1b0SPu Lehui 		       "%-8s %-15s %-15s\n",
481bdcee1b0SPu Lehui 		       "CgroupPath",
482bdcee1b0SPu Lehui 		       "ID", "AttachType", "Name");
4832058b383SRoman Gushchin 	else
4842058b383SRoman Gushchin 		printf("%s\n"
4852058b383SRoman Gushchin 		       "%-8s %-15s %-15s %-15s\n",
4862058b383SRoman Gushchin 		       "CgroupPath",
4872058b383SRoman Gushchin 		       "ID", "AttachType", "AttachFlags", "Name");
4882058b383SRoman Gushchin 
4892058b383SRoman Gushchin 	switch (nftw(cgroup_root, do_show_tree_fn, 1024, FTW_MOUNT)) {
4902058b383SRoman Gushchin 	case NFTW_ERR:
4912058b383SRoman Gushchin 		p_err("can't iterate over %s: %s", cgroup_root,
4922058b383SRoman Gushchin 		      strerror(errno));
4932058b383SRoman Gushchin 		ret = -1;
4942058b383SRoman Gushchin 		break;
4952058b383SRoman Gushchin 	case SHOW_TREE_FN_ERR:
4962058b383SRoman Gushchin 		ret = -1;
4972058b383SRoman Gushchin 		break;
4982058b383SRoman Gushchin 	default:
4992058b383SRoman Gushchin 		ret = 0;
5002058b383SRoman Gushchin 	}
5012058b383SRoman Gushchin 
5022058b383SRoman Gushchin 	if (json_output)
5032058b383SRoman Gushchin 		jsonw_end_array(json_wtr);
5042058b383SRoman Gushchin 
505a98bf573SJakub Kicinski 	free(cgroup_alloced);
5062058b383SRoman Gushchin 
5072058b383SRoman Gushchin 	return ret;
5082058b383SRoman Gushchin }
5092058b383SRoman Gushchin 
do_attach(int argc,char ** argv)5105ccda64dSRoman Gushchin static int do_attach(int argc, char **argv)
5115ccda64dSRoman Gushchin {
5125ccda64dSRoman Gushchin 	enum bpf_attach_type attach_type;
5135ccda64dSRoman Gushchin 	int cgroup_fd, prog_fd;
5145ccda64dSRoman Gushchin 	int attach_flags = 0;
5155ccda64dSRoman Gushchin 	int ret = -1;
5165ccda64dSRoman Gushchin 	int i;
5175ccda64dSRoman Gushchin 
5185ccda64dSRoman Gushchin 	if (argc < 4) {
519b4fac96dSJakub Kicinski 		p_err("too few parameters for cgroup attach");
5205ccda64dSRoman Gushchin 		goto exit;
5215ccda64dSRoman Gushchin 	}
5225ccda64dSRoman Gushchin 
5235ccda64dSRoman Gushchin 	cgroup_fd = open(argv[0], O_RDONLY);
5245ccda64dSRoman Gushchin 	if (cgroup_fd < 0) {
5256c6874f4SJakub Kicinski 		p_err("can't open cgroup %s", argv[0]);
5265ccda64dSRoman Gushchin 		goto exit;
5275ccda64dSRoman Gushchin 	}
5285ccda64dSRoman Gushchin 
5295ccda64dSRoman Gushchin 	attach_type = parse_attach_type(argv[1]);
5305ccda64dSRoman Gushchin 	if (attach_type == __MAX_BPF_ATTACH_TYPE) {
531b4fac96dSJakub Kicinski 		p_err("invalid attach type");
5325ccda64dSRoman Gushchin 		goto exit_cgroup;
5335ccda64dSRoman Gushchin 	}
5345ccda64dSRoman Gushchin 
5355ccda64dSRoman Gushchin 	argc -= 2;
5365ccda64dSRoman Gushchin 	argv = &argv[2];
5375ccda64dSRoman Gushchin 	prog_fd = prog_parse_fd(&argc, &argv);
5385ccda64dSRoman Gushchin 	if (prog_fd < 0)
5395ccda64dSRoman Gushchin 		goto exit_cgroup;
5405ccda64dSRoman Gushchin 
5415ccda64dSRoman Gushchin 	for (i = 0; i < argc; i++) {
5425ccda64dSRoman Gushchin 		if (is_prefix(argv[i], "multi")) {
5435ccda64dSRoman Gushchin 			attach_flags |= BPF_F_ALLOW_MULTI;
5445ccda64dSRoman Gushchin 		} else if (is_prefix(argv[i], "override")) {
5455ccda64dSRoman Gushchin 			attach_flags |= BPF_F_ALLOW_OVERRIDE;
5465ccda64dSRoman Gushchin 		} else {
547b4fac96dSJakub Kicinski 			p_err("unknown option: %s", argv[i]);
5485ccda64dSRoman Gushchin 			goto exit_cgroup;
5495ccda64dSRoman Gushchin 		}
5505ccda64dSRoman Gushchin 	}
5515ccda64dSRoman Gushchin 
5525ccda64dSRoman Gushchin 	if (bpf_prog_attach(prog_fd, cgroup_fd, attach_type, attach_flags)) {
5535ccda64dSRoman Gushchin 		p_err("failed to attach program");
5545ccda64dSRoman Gushchin 		goto exit_prog;
5555ccda64dSRoman Gushchin 	}
5565ccda64dSRoman Gushchin 
5575ccda64dSRoman Gushchin 	if (json_output)
5585ccda64dSRoman Gushchin 		jsonw_null(json_wtr);
5595ccda64dSRoman Gushchin 
5605ccda64dSRoman Gushchin 	ret = 0;
5615ccda64dSRoman Gushchin 
5625ccda64dSRoman Gushchin exit_prog:
5635ccda64dSRoman Gushchin 	close(prog_fd);
5645ccda64dSRoman Gushchin exit_cgroup:
5655ccda64dSRoman Gushchin 	close(cgroup_fd);
5665ccda64dSRoman Gushchin exit:
5675ccda64dSRoman Gushchin 	return ret;
5685ccda64dSRoman Gushchin }
5695ccda64dSRoman Gushchin 
do_detach(int argc,char ** argv)5705ccda64dSRoman Gushchin static int do_detach(int argc, char **argv)
5715ccda64dSRoman Gushchin {
5725ccda64dSRoman Gushchin 	enum bpf_attach_type attach_type;
5735ccda64dSRoman Gushchin 	int prog_fd, cgroup_fd;
5745ccda64dSRoman Gushchin 	int ret = -1;
5755ccda64dSRoman Gushchin 
5765ccda64dSRoman Gushchin 	if (argc < 4) {
577b4fac96dSJakub Kicinski 		p_err("too few parameters for cgroup detach");
5785ccda64dSRoman Gushchin 		goto exit;
5795ccda64dSRoman Gushchin 	}
5805ccda64dSRoman Gushchin 
5815ccda64dSRoman Gushchin 	cgroup_fd = open(argv[0], O_RDONLY);
5825ccda64dSRoman Gushchin 	if (cgroup_fd < 0) {
5836c6874f4SJakub Kicinski 		p_err("can't open cgroup %s", argv[0]);
5845ccda64dSRoman Gushchin 		goto exit;
5855ccda64dSRoman Gushchin 	}
5865ccda64dSRoman Gushchin 
5875ccda64dSRoman Gushchin 	attach_type = parse_attach_type(argv[1]);
5885ccda64dSRoman Gushchin 	if (attach_type == __MAX_BPF_ATTACH_TYPE) {
5895ccda64dSRoman Gushchin 		p_err("invalid attach type");
5905ccda64dSRoman Gushchin 		goto exit_cgroup;
5915ccda64dSRoman Gushchin 	}
5925ccda64dSRoman Gushchin 
5935ccda64dSRoman Gushchin 	argc -= 2;
5945ccda64dSRoman Gushchin 	argv = &argv[2];
5955ccda64dSRoman Gushchin 	prog_fd = prog_parse_fd(&argc, &argv);
5965ccda64dSRoman Gushchin 	if (prog_fd < 0)
5975ccda64dSRoman Gushchin 		goto exit_cgroup;
5985ccda64dSRoman Gushchin 
5995ccda64dSRoman Gushchin 	if (bpf_prog_detach2(prog_fd, cgroup_fd, attach_type)) {
6005ccda64dSRoman Gushchin 		p_err("failed to detach program");
6015ccda64dSRoman Gushchin 		goto exit_prog;
6025ccda64dSRoman Gushchin 	}
6035ccda64dSRoman Gushchin 
6045ccda64dSRoman Gushchin 	if (json_output)
6055ccda64dSRoman Gushchin 		jsonw_null(json_wtr);
6065ccda64dSRoman Gushchin 
6075ccda64dSRoman Gushchin 	ret = 0;
6085ccda64dSRoman Gushchin 
6095ccda64dSRoman Gushchin exit_prog:
6105ccda64dSRoman Gushchin 	close(prog_fd);
6115ccda64dSRoman Gushchin exit_cgroup:
6125ccda64dSRoman Gushchin 	close(cgroup_fd);
6135ccda64dSRoman Gushchin exit:
6145ccda64dSRoman Gushchin 	return ret;
6155ccda64dSRoman Gushchin }
6165ccda64dSRoman Gushchin 
do_help(int argc,char ** argv)6175ccda64dSRoman Gushchin static int do_help(int argc, char **argv)
6185ccda64dSRoman Gushchin {
6195ccda64dSRoman Gushchin 	if (json_output) {
6205ccda64dSRoman Gushchin 		jsonw_null(json_wtr);
6215ccda64dSRoman Gushchin 		return 0;
6225ccda64dSRoman Gushchin 	}
6235ccda64dSRoman Gushchin 
6245ccda64dSRoman Gushchin 	fprintf(stderr,
62590040351SQuentin Monnet 		"Usage: %1$s %2$s { show | list } CGROUP [**effective**]\n"
62690040351SQuentin Monnet 		"       %1$s %2$s tree [CGROUP_ROOT] [**effective**]\n"
62790040351SQuentin Monnet 		"       %1$s %2$s attach CGROUP ATTACH_TYPE PROG [ATTACH_FLAGS]\n"
62890040351SQuentin Monnet 		"       %1$s %2$s detach CGROUP ATTACH_TYPE PROG\n"
62990040351SQuentin Monnet 		"       %1$s %2$s help\n"
6305ccda64dSRoman Gushchin 		"\n"
631393de512SAndrey Ignatov 		HELP_SPEC_ATTACH_TYPES "\n"
6325ccda64dSRoman Gushchin 		"       " HELP_SPEC_ATTACH_FLAGS "\n"
6335ccda64dSRoman Gushchin 		"       " HELP_SPEC_PROGRAM "\n"
634c07ba629SQuentin Monnet 		"       " HELP_SPEC_OPTIONS " |\n"
6358cc8c635SQuentin Monnet 		"                    {-f|--bpffs} }\n"
6365ccda64dSRoman Gushchin 		"",
63790040351SQuentin Monnet 		bin_name, argv[-2]);
6385ccda64dSRoman Gushchin 
6395ccda64dSRoman Gushchin 	return 0;
6405ccda64dSRoman Gushchin }
6415ccda64dSRoman Gushchin 
6425ccda64dSRoman Gushchin static const struct cmd cmds[] = {
6436ebe6dbdSJakub Kicinski 	{ "show",	do_show },
64465b875bcSJakub Kicinski 	{ "list",	do_show },
6452058b383SRoman Gushchin 	{ "tree",       do_show_tree },
6465ccda64dSRoman Gushchin 	{ "attach",	do_attach },
6475ccda64dSRoman Gushchin 	{ "detach",	do_detach },
6485ccda64dSRoman Gushchin 	{ "help",	do_help },
6495ccda64dSRoman Gushchin 	{ 0 }
6505ccda64dSRoman Gushchin };
6515ccda64dSRoman Gushchin 
do_cgroup(int argc,char ** argv)6525ccda64dSRoman Gushchin int do_cgroup(int argc, char **argv)
6535ccda64dSRoman Gushchin {
6545ccda64dSRoman Gushchin 	return cmd_select(cmds, argc, argv, do_help);
6555ccda64dSRoman Gushchin }
656