xref: /openbmc/linux/tools/bpf/bpftool/cgroup.c (revision a9436dca)
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 
175ccda64dSRoman Gushchin #include <bpf.h>
185ccda64dSRoman Gushchin 
195ccda64dSRoman Gushchin #include "main.h"
205ccda64dSRoman Gushchin 
215ccda64dSRoman Gushchin #define HELP_SPEC_ATTACH_FLAGS						\
225ccda64dSRoman Gushchin 	"ATTACH_FLAGS := { multi | override }"
235ccda64dSRoman Gushchin 
245ccda64dSRoman Gushchin #define HELP_SPEC_ATTACH_TYPES						       \
25393de512SAndrey Ignatov 	"       ATTACH_TYPE := { ingress | egress | sock_create |\n"	       \
26393de512SAndrey Ignatov 	"                        sock_ops | device | bind4 | bind6 |\n"	       \
27393de512SAndrey Ignatov 	"                        post_bind4 | post_bind6 | connect4 |\n"       \
28000aa125SDaniel Borkmann 	"                        connect6 | sendmsg4 | sendmsg6 |\n"           \
29f6d08d9dSStanislav Fomichev 	"                        recvmsg4 | recvmsg6 | sysctl |\n"	       \
30f6d08d9dSStanislav Fomichev 	"                        getsockopt | setsockopt }"
315ccda64dSRoman Gushchin 
32a98bf573SJakub Kicinski static unsigned int query_flags;
33a98bf573SJakub Kicinski 
345ccda64dSRoman Gushchin static const char * const attach_type_strings[] = {
355ccda64dSRoman Gushchin 	[BPF_CGROUP_INET_INGRESS] = "ingress",
365ccda64dSRoman Gushchin 	[BPF_CGROUP_INET_EGRESS] = "egress",
375ccda64dSRoman Gushchin 	[BPF_CGROUP_INET_SOCK_CREATE] = "sock_create",
385ccda64dSRoman Gushchin 	[BPF_CGROUP_SOCK_OPS] = "sock_ops",
395ccda64dSRoman Gushchin 	[BPF_CGROUP_DEVICE] = "device",
40393de512SAndrey Ignatov 	[BPF_CGROUP_INET4_BIND] = "bind4",
41393de512SAndrey Ignatov 	[BPF_CGROUP_INET6_BIND] = "bind6",
42393de512SAndrey Ignatov 	[BPF_CGROUP_INET4_CONNECT] = "connect4",
43393de512SAndrey Ignatov 	[BPF_CGROUP_INET6_CONNECT] = "connect6",
44393de512SAndrey Ignatov 	[BPF_CGROUP_INET4_POST_BIND] = "post_bind4",
45393de512SAndrey Ignatov 	[BPF_CGROUP_INET6_POST_BIND] = "post_bind6",
4613a370b9SAndrey Ignatov 	[BPF_CGROUP_UDP4_SENDMSG] = "sendmsg4",
4713a370b9SAndrey Ignatov 	[BPF_CGROUP_UDP6_SENDMSG] = "sendmsg6",
48f25377eeSAndrey Ignatov 	[BPF_CGROUP_SYSCTL] = "sysctl",
49000aa125SDaniel Borkmann 	[BPF_CGROUP_UDP4_RECVMSG] = "recvmsg4",
50000aa125SDaniel Borkmann 	[BPF_CGROUP_UDP6_RECVMSG] = "recvmsg6",
51f6d08d9dSStanislav Fomichev 	[BPF_CGROUP_GETSOCKOPT] = "getsockopt",
52f6d08d9dSStanislav Fomichev 	[BPF_CGROUP_SETSOCKOPT] = "setsockopt",
535ccda64dSRoman Gushchin 	[__MAX_BPF_ATTACH_TYPE] = NULL,
545ccda64dSRoman Gushchin };
555ccda64dSRoman Gushchin 
565ccda64dSRoman Gushchin static enum bpf_attach_type parse_attach_type(const char *str)
575ccda64dSRoman Gushchin {
585ccda64dSRoman Gushchin 	enum bpf_attach_type type;
595ccda64dSRoman Gushchin 
605ccda64dSRoman Gushchin 	for (type = 0; type < __MAX_BPF_ATTACH_TYPE; type++) {
615ccda64dSRoman Gushchin 		if (attach_type_strings[type] &&
625ccda64dSRoman Gushchin 		    is_prefix(str, attach_type_strings[type]))
635ccda64dSRoman Gushchin 			return type;
645ccda64dSRoman Gushchin 	}
655ccda64dSRoman Gushchin 
665ccda64dSRoman Gushchin 	return __MAX_BPF_ATTACH_TYPE;
675ccda64dSRoman Gushchin }
685ccda64dSRoman Gushchin 
6965b875bcSJakub Kicinski static int show_bpf_prog(int id, const char *attach_type_str,
702058b383SRoman Gushchin 			 const char *attach_flags_str,
712058b383SRoman Gushchin 			 int level)
725ccda64dSRoman Gushchin {
735ccda64dSRoman Gushchin 	struct bpf_prog_info info = {};
745ccda64dSRoman Gushchin 	__u32 info_len = sizeof(info);
755ccda64dSRoman Gushchin 	int prog_fd;
765ccda64dSRoman Gushchin 
775ccda64dSRoman Gushchin 	prog_fd = bpf_prog_get_fd_by_id(id);
785ccda64dSRoman Gushchin 	if (prog_fd < 0)
795ccda64dSRoman Gushchin 		return -1;
805ccda64dSRoman Gushchin 
815ccda64dSRoman Gushchin 	if (bpf_obj_get_info_by_fd(prog_fd, &info, &info_len)) {
825ccda64dSRoman Gushchin 		close(prog_fd);
835ccda64dSRoman Gushchin 		return -1;
845ccda64dSRoman Gushchin 	}
855ccda64dSRoman Gushchin 
865ccda64dSRoman Gushchin 	if (json_output) {
875ccda64dSRoman Gushchin 		jsonw_start_object(json_wtr);
885ccda64dSRoman Gushchin 		jsonw_uint_field(json_wtr, "id", info.id);
895ccda64dSRoman Gushchin 		jsonw_string_field(json_wtr, "attach_type",
905ccda64dSRoman Gushchin 				   attach_type_str);
915ccda64dSRoman Gushchin 		jsonw_string_field(json_wtr, "attach_flags",
925ccda64dSRoman Gushchin 				   attach_flags_str);
935ccda64dSRoman Gushchin 		jsonw_string_field(json_wtr, "name", info.name);
945ccda64dSRoman Gushchin 		jsonw_end_object(json_wtr);
955ccda64dSRoman Gushchin 	} else {
962058b383SRoman Gushchin 		printf("%s%-8u %-15s %-15s %-15s\n", level ? "    " : "",
972058b383SRoman Gushchin 		       info.id,
985ccda64dSRoman Gushchin 		       attach_type_str,
995ccda64dSRoman Gushchin 		       attach_flags_str,
1005ccda64dSRoman Gushchin 		       info.name);
1015ccda64dSRoman Gushchin 	}
1025ccda64dSRoman Gushchin 
1035ccda64dSRoman Gushchin 	close(prog_fd);
1045ccda64dSRoman Gushchin 	return 0;
1055ccda64dSRoman Gushchin }
1065ccda64dSRoman Gushchin 
1072058b383SRoman Gushchin static int count_attached_bpf_progs(int cgroup_fd, enum bpf_attach_type type)
1082058b383SRoman Gushchin {
1092058b383SRoman Gushchin 	__u32 prog_cnt = 0;
1102058b383SRoman Gushchin 	int ret;
1112058b383SRoman Gushchin 
112a98bf573SJakub Kicinski 	ret = bpf_prog_query(cgroup_fd, type, query_flags, NULL,
113a98bf573SJakub Kicinski 			     NULL, &prog_cnt);
1142058b383SRoman Gushchin 	if (ret)
1152058b383SRoman Gushchin 		return -1;
1162058b383SRoman Gushchin 
1172058b383SRoman Gushchin 	return prog_cnt;
1182058b383SRoman Gushchin }
1192058b383SRoman Gushchin 
1202058b383SRoman Gushchin static int show_attached_bpf_progs(int cgroup_fd, enum bpf_attach_type type,
1212058b383SRoman Gushchin 				   int level)
1225ccda64dSRoman Gushchin {
123a9436dcaSQuentin Monnet 	const char *attach_flags_str;
1245ccda64dSRoman Gushchin 	__u32 prog_ids[1024] = {0};
1255ccda64dSRoman Gushchin 	__u32 prog_cnt, iter;
1265ccda64dSRoman Gushchin 	__u32 attach_flags;
1275ccda64dSRoman Gushchin 	char buf[32];
1285ccda64dSRoman Gushchin 	int ret;
1295ccda64dSRoman Gushchin 
1305ccda64dSRoman Gushchin 	prog_cnt = ARRAY_SIZE(prog_ids);
131a98bf573SJakub Kicinski 	ret = bpf_prog_query(cgroup_fd, type, query_flags, &attach_flags,
132a98bf573SJakub Kicinski 			     prog_ids, &prog_cnt);
1335ccda64dSRoman Gushchin 	if (ret)
1345ccda64dSRoman Gushchin 		return ret;
1355ccda64dSRoman Gushchin 
1365ccda64dSRoman Gushchin 	if (prog_cnt == 0)
1375ccda64dSRoman Gushchin 		return 0;
1385ccda64dSRoman Gushchin 
1395ccda64dSRoman Gushchin 	switch (attach_flags) {
1405ccda64dSRoman Gushchin 	case BPF_F_ALLOW_MULTI:
1415ccda64dSRoman Gushchin 		attach_flags_str = "multi";
1425ccda64dSRoman Gushchin 		break;
1435ccda64dSRoman Gushchin 	case BPF_F_ALLOW_OVERRIDE:
1445ccda64dSRoman Gushchin 		attach_flags_str = "override";
1455ccda64dSRoman Gushchin 		break;
1465ccda64dSRoman Gushchin 	case 0:
1475ccda64dSRoman Gushchin 		attach_flags_str = "";
1485ccda64dSRoman Gushchin 		break;
1495ccda64dSRoman Gushchin 	default:
1505ccda64dSRoman Gushchin 		snprintf(buf, sizeof(buf), "unknown(%x)", attach_flags);
1515ccda64dSRoman Gushchin 		attach_flags_str = buf;
1525ccda64dSRoman Gushchin 	}
1535ccda64dSRoman Gushchin 
1545ccda64dSRoman Gushchin 	for (iter = 0; iter < prog_cnt; iter++)
15565b875bcSJakub Kicinski 		show_bpf_prog(prog_ids[iter], attach_type_strings[type],
1562058b383SRoman Gushchin 			      attach_flags_str, level);
1575ccda64dSRoman Gushchin 
1585ccda64dSRoman Gushchin 	return 0;
1595ccda64dSRoman Gushchin }
1605ccda64dSRoman Gushchin 
16165b875bcSJakub Kicinski static int do_show(int argc, char **argv)
1625ccda64dSRoman Gushchin {
1635ccda64dSRoman Gushchin 	enum bpf_attach_type type;
164a98bf573SJakub Kicinski 	const char *path;
1655ccda64dSRoman Gushchin 	int cgroup_fd;
1665ccda64dSRoman Gushchin 	int ret = -1;
1675ccda64dSRoman Gushchin 
168a98bf573SJakub Kicinski 	query_flags = 0;
169a98bf573SJakub Kicinski 
170a98bf573SJakub Kicinski 	if (!REQ_ARGS(1))
171a98bf573SJakub Kicinski 		return -1;
172a98bf573SJakub Kicinski 	path = GET_ARG();
173a98bf573SJakub Kicinski 
174a98bf573SJakub Kicinski 	while (argc) {
175a98bf573SJakub Kicinski 		if (is_prefix(*argv, "effective")) {
176a98bf573SJakub Kicinski 			if (query_flags & BPF_F_QUERY_EFFECTIVE) {
177a98bf573SJakub Kicinski 				p_err("duplicated argument: %s", *argv);
178a98bf573SJakub Kicinski 				return -1;
179a98bf573SJakub Kicinski 			}
180a98bf573SJakub Kicinski 			query_flags |= BPF_F_QUERY_EFFECTIVE;
181a98bf573SJakub Kicinski 			NEXT_ARG();
182a98bf573SJakub Kicinski 		} else {
183a98bf573SJakub Kicinski 			p_err("expected no more arguments, 'effective', got: '%s'?",
184a98bf573SJakub Kicinski 			      *argv);
185a98bf573SJakub Kicinski 			return -1;
186a98bf573SJakub Kicinski 		}
1875ccda64dSRoman Gushchin 	}
1885ccda64dSRoman Gushchin 
189a98bf573SJakub Kicinski 	cgroup_fd = open(path, O_RDONLY);
1905ccda64dSRoman Gushchin 	if (cgroup_fd < 0) {
191a98bf573SJakub Kicinski 		p_err("can't open cgroup %s", path);
1925ccda64dSRoman Gushchin 		goto exit;
1935ccda64dSRoman Gushchin 	}
1945ccda64dSRoman Gushchin 
1955ccda64dSRoman Gushchin 	if (json_output)
1965ccda64dSRoman Gushchin 		jsonw_start_array(json_wtr);
1975ccda64dSRoman Gushchin 	else
1985ccda64dSRoman Gushchin 		printf("%-8s %-15s %-15s %-15s\n", "ID", "AttachType",
1995ccda64dSRoman Gushchin 		       "AttachFlags", "Name");
2005ccda64dSRoman Gushchin 
2015ccda64dSRoman Gushchin 	for (type = 0; type < __MAX_BPF_ATTACH_TYPE; type++) {
2025ccda64dSRoman Gushchin 		/*
2035ccda64dSRoman Gushchin 		 * Not all attach types may be supported, so it's expected,
2045ccda64dSRoman Gushchin 		 * that some requests will fail.
20565b875bcSJakub Kicinski 		 * If we were able to get the show for at least one
2065ccda64dSRoman Gushchin 		 * attach type, let's return 0.
2075ccda64dSRoman Gushchin 		 */
2082058b383SRoman Gushchin 		if (show_attached_bpf_progs(cgroup_fd, type, 0) == 0)
2095ccda64dSRoman Gushchin 			ret = 0;
2105ccda64dSRoman Gushchin 	}
2115ccda64dSRoman Gushchin 
2125ccda64dSRoman Gushchin 	if (json_output)
2135ccda64dSRoman Gushchin 		jsonw_end_array(json_wtr);
2145ccda64dSRoman Gushchin 
2155ccda64dSRoman Gushchin 	close(cgroup_fd);
2165ccda64dSRoman Gushchin exit:
2175ccda64dSRoman Gushchin 	return ret;
2185ccda64dSRoman Gushchin }
2195ccda64dSRoman Gushchin 
2202058b383SRoman Gushchin /*
2212058b383SRoman Gushchin  * To distinguish nftw() errors and do_show_tree_fn() errors
2222058b383SRoman Gushchin  * and avoid duplicating error messages, let's return -2
2232058b383SRoman Gushchin  * from do_show_tree_fn() in case of error.
2242058b383SRoman Gushchin  */
2252058b383SRoman Gushchin #define NFTW_ERR		-1
2262058b383SRoman Gushchin #define SHOW_TREE_FN_ERR	-2
2272058b383SRoman Gushchin static int do_show_tree_fn(const char *fpath, const struct stat *sb,
2282058b383SRoman Gushchin 			   int typeflag, struct FTW *ftw)
2292058b383SRoman Gushchin {
2302058b383SRoman Gushchin 	enum bpf_attach_type type;
2312058b383SRoman Gushchin 	bool skip = true;
2322058b383SRoman Gushchin 	int cgroup_fd;
2332058b383SRoman Gushchin 
2342058b383SRoman Gushchin 	if (typeflag != FTW_D)
2352058b383SRoman Gushchin 		return 0;
2362058b383SRoman Gushchin 
2372058b383SRoman Gushchin 	cgroup_fd = open(fpath, O_RDONLY);
2382058b383SRoman Gushchin 	if (cgroup_fd < 0) {
2392058b383SRoman Gushchin 		p_err("can't open cgroup %s: %s", fpath, strerror(errno));
2402058b383SRoman Gushchin 		return SHOW_TREE_FN_ERR;
2412058b383SRoman Gushchin 	}
2422058b383SRoman Gushchin 
2432058b383SRoman Gushchin 	for (type = 0; type < __MAX_BPF_ATTACH_TYPE; type++) {
2442058b383SRoman Gushchin 		int count = count_attached_bpf_progs(cgroup_fd, type);
2452058b383SRoman Gushchin 
2462058b383SRoman Gushchin 		if (count < 0 && errno != EINVAL) {
2472058b383SRoman Gushchin 			p_err("can't query bpf programs attached to %s: %s",
2482058b383SRoman Gushchin 			      fpath, strerror(errno));
2492058b383SRoman Gushchin 			close(cgroup_fd);
2502058b383SRoman Gushchin 			return SHOW_TREE_FN_ERR;
2512058b383SRoman Gushchin 		}
2522058b383SRoman Gushchin 		if (count > 0) {
2532058b383SRoman Gushchin 			skip = false;
2542058b383SRoman Gushchin 			break;
2552058b383SRoman Gushchin 		}
2562058b383SRoman Gushchin 	}
2572058b383SRoman Gushchin 
2582058b383SRoman Gushchin 	if (skip) {
2592058b383SRoman Gushchin 		close(cgroup_fd);
2602058b383SRoman Gushchin 		return 0;
2612058b383SRoman Gushchin 	}
2622058b383SRoman Gushchin 
2632058b383SRoman Gushchin 	if (json_output) {
2642058b383SRoman Gushchin 		jsonw_start_object(json_wtr);
2652058b383SRoman Gushchin 		jsonw_string_field(json_wtr, "cgroup", fpath);
2662058b383SRoman Gushchin 		jsonw_name(json_wtr, "programs");
2672058b383SRoman Gushchin 		jsonw_start_array(json_wtr);
2682058b383SRoman Gushchin 	} else {
2692058b383SRoman Gushchin 		printf("%s\n", fpath);
2702058b383SRoman Gushchin 	}
2712058b383SRoman Gushchin 
2722058b383SRoman Gushchin 	for (type = 0; type < __MAX_BPF_ATTACH_TYPE; type++)
2732058b383SRoman Gushchin 		show_attached_bpf_progs(cgroup_fd, type, ftw->level);
2742058b383SRoman Gushchin 
27539c9f106SQuentin Monnet 	if (errno == EINVAL)
27639c9f106SQuentin Monnet 		/* Last attach type does not support query.
27739c9f106SQuentin Monnet 		 * Do not report an error for this, especially because batch
27839c9f106SQuentin Monnet 		 * mode would stop processing commands.
27939c9f106SQuentin Monnet 		 */
28039c9f106SQuentin Monnet 		errno = 0;
28139c9f106SQuentin Monnet 
2822058b383SRoman Gushchin 	if (json_output) {
2832058b383SRoman Gushchin 		jsonw_end_array(json_wtr);
2842058b383SRoman Gushchin 		jsonw_end_object(json_wtr);
2852058b383SRoman Gushchin 	}
2862058b383SRoman Gushchin 
2872058b383SRoman Gushchin 	close(cgroup_fd);
2882058b383SRoman Gushchin 
2892058b383SRoman Gushchin 	return 0;
2902058b383SRoman Gushchin }
2912058b383SRoman Gushchin 
2922058b383SRoman Gushchin static char *find_cgroup_root(void)
2932058b383SRoman Gushchin {
2942058b383SRoman Gushchin 	struct mntent *mnt;
2952058b383SRoman Gushchin 	FILE *f;
2962058b383SRoman Gushchin 
2972058b383SRoman Gushchin 	f = fopen("/proc/mounts", "r");
2982058b383SRoman Gushchin 	if (f == NULL)
2992058b383SRoman Gushchin 		return NULL;
3002058b383SRoman Gushchin 
3012058b383SRoman Gushchin 	while ((mnt = getmntent(f))) {
3022058b383SRoman Gushchin 		if (strcmp(mnt->mnt_type, "cgroup2") == 0) {
3032058b383SRoman Gushchin 			fclose(f);
3042058b383SRoman Gushchin 			return strdup(mnt->mnt_dir);
3052058b383SRoman Gushchin 		}
3062058b383SRoman Gushchin 	}
3072058b383SRoman Gushchin 
3082058b383SRoman Gushchin 	fclose(f);
3092058b383SRoman Gushchin 	return NULL;
3102058b383SRoman Gushchin }
3112058b383SRoman Gushchin 
3122058b383SRoman Gushchin static int do_show_tree(int argc, char **argv)
3132058b383SRoman Gushchin {
314a98bf573SJakub Kicinski 	char *cgroup_root, *cgroup_alloced = NULL;
3152058b383SRoman Gushchin 	int ret;
3162058b383SRoman Gushchin 
317a98bf573SJakub Kicinski 	query_flags = 0;
318a98bf573SJakub Kicinski 
319a98bf573SJakub Kicinski 	if (!argc) {
320a98bf573SJakub Kicinski 		cgroup_alloced = find_cgroup_root();
321a98bf573SJakub Kicinski 		if (!cgroup_alloced) {
3222058b383SRoman Gushchin 			p_err("cgroup v2 isn't mounted");
3232058b383SRoman Gushchin 			return -1;
3242058b383SRoman Gushchin 		}
325a98bf573SJakub Kicinski 		cgroup_root = cgroup_alloced;
326a98bf573SJakub Kicinski 	} else {
327a98bf573SJakub Kicinski 		cgroup_root = GET_ARG();
328a98bf573SJakub Kicinski 
329a98bf573SJakub Kicinski 		while (argc) {
330a98bf573SJakub Kicinski 			if (is_prefix(*argv, "effective")) {
331a98bf573SJakub Kicinski 				if (query_flags & BPF_F_QUERY_EFFECTIVE) {
332a98bf573SJakub Kicinski 					p_err("duplicated argument: %s", *argv);
3332058b383SRoman Gushchin 					return -1;
3342058b383SRoman Gushchin 				}
335a98bf573SJakub Kicinski 				query_flags |= BPF_F_QUERY_EFFECTIVE;
336a98bf573SJakub Kicinski 				NEXT_ARG();
337a98bf573SJakub Kicinski 			} else {
338a98bf573SJakub Kicinski 				p_err("expected no more arguments, 'effective', got: '%s'?",
339a98bf573SJakub Kicinski 				      *argv);
340a98bf573SJakub Kicinski 				return -1;
341a98bf573SJakub Kicinski 			}
342a98bf573SJakub Kicinski 		}
343a98bf573SJakub Kicinski 	}
3442058b383SRoman Gushchin 
3452058b383SRoman Gushchin 	if (json_output)
3462058b383SRoman Gushchin 		jsonw_start_array(json_wtr);
3472058b383SRoman Gushchin 	else
3482058b383SRoman Gushchin 		printf("%s\n"
3492058b383SRoman Gushchin 		       "%-8s %-15s %-15s %-15s\n",
3502058b383SRoman Gushchin 		       "CgroupPath",
3512058b383SRoman Gushchin 		       "ID", "AttachType", "AttachFlags", "Name");
3522058b383SRoman Gushchin 
3532058b383SRoman Gushchin 	switch (nftw(cgroup_root, do_show_tree_fn, 1024, FTW_MOUNT)) {
3542058b383SRoman Gushchin 	case NFTW_ERR:
3552058b383SRoman Gushchin 		p_err("can't iterate over %s: %s", cgroup_root,
3562058b383SRoman Gushchin 		      strerror(errno));
3572058b383SRoman Gushchin 		ret = -1;
3582058b383SRoman Gushchin 		break;
3592058b383SRoman Gushchin 	case SHOW_TREE_FN_ERR:
3602058b383SRoman Gushchin 		ret = -1;
3612058b383SRoman Gushchin 		break;
3622058b383SRoman Gushchin 	default:
3632058b383SRoman Gushchin 		ret = 0;
3642058b383SRoman Gushchin 	}
3652058b383SRoman Gushchin 
3662058b383SRoman Gushchin 	if (json_output)
3672058b383SRoman Gushchin 		jsonw_end_array(json_wtr);
3682058b383SRoman Gushchin 
369a98bf573SJakub Kicinski 	free(cgroup_alloced);
3702058b383SRoman Gushchin 
3712058b383SRoman Gushchin 	return ret;
3722058b383SRoman Gushchin }
3732058b383SRoman Gushchin 
3745ccda64dSRoman Gushchin static int do_attach(int argc, char **argv)
3755ccda64dSRoman Gushchin {
3765ccda64dSRoman Gushchin 	enum bpf_attach_type attach_type;
3775ccda64dSRoman Gushchin 	int cgroup_fd, prog_fd;
3785ccda64dSRoman Gushchin 	int attach_flags = 0;
3795ccda64dSRoman Gushchin 	int ret = -1;
3805ccda64dSRoman Gushchin 	int i;
3815ccda64dSRoman Gushchin 
3825ccda64dSRoman Gushchin 	if (argc < 4) {
383b4fac96dSJakub Kicinski 		p_err("too few parameters for cgroup attach");
3845ccda64dSRoman Gushchin 		goto exit;
3855ccda64dSRoman Gushchin 	}
3865ccda64dSRoman Gushchin 
3875ccda64dSRoman Gushchin 	cgroup_fd = open(argv[0], O_RDONLY);
3885ccda64dSRoman Gushchin 	if (cgroup_fd < 0) {
3896c6874f4SJakub Kicinski 		p_err("can't open cgroup %s", argv[0]);
3905ccda64dSRoman Gushchin 		goto exit;
3915ccda64dSRoman Gushchin 	}
3925ccda64dSRoman Gushchin 
3935ccda64dSRoman Gushchin 	attach_type = parse_attach_type(argv[1]);
3945ccda64dSRoman Gushchin 	if (attach_type == __MAX_BPF_ATTACH_TYPE) {
395b4fac96dSJakub Kicinski 		p_err("invalid attach type");
3965ccda64dSRoman Gushchin 		goto exit_cgroup;
3975ccda64dSRoman Gushchin 	}
3985ccda64dSRoman Gushchin 
3995ccda64dSRoman Gushchin 	argc -= 2;
4005ccda64dSRoman Gushchin 	argv = &argv[2];
4015ccda64dSRoman Gushchin 	prog_fd = prog_parse_fd(&argc, &argv);
4025ccda64dSRoman Gushchin 	if (prog_fd < 0)
4035ccda64dSRoman Gushchin 		goto exit_cgroup;
4045ccda64dSRoman Gushchin 
4055ccda64dSRoman Gushchin 	for (i = 0; i < argc; i++) {
4065ccda64dSRoman Gushchin 		if (is_prefix(argv[i], "multi")) {
4075ccda64dSRoman Gushchin 			attach_flags |= BPF_F_ALLOW_MULTI;
4085ccda64dSRoman Gushchin 		} else if (is_prefix(argv[i], "override")) {
4095ccda64dSRoman Gushchin 			attach_flags |= BPF_F_ALLOW_OVERRIDE;
4105ccda64dSRoman Gushchin 		} else {
411b4fac96dSJakub Kicinski 			p_err("unknown option: %s", argv[i]);
4125ccda64dSRoman Gushchin 			goto exit_cgroup;
4135ccda64dSRoman Gushchin 		}
4145ccda64dSRoman Gushchin 	}
4155ccda64dSRoman Gushchin 
4165ccda64dSRoman Gushchin 	if (bpf_prog_attach(prog_fd, cgroup_fd, attach_type, attach_flags)) {
4175ccda64dSRoman Gushchin 		p_err("failed to attach program");
4185ccda64dSRoman Gushchin 		goto exit_prog;
4195ccda64dSRoman Gushchin 	}
4205ccda64dSRoman Gushchin 
4215ccda64dSRoman Gushchin 	if (json_output)
4225ccda64dSRoman Gushchin 		jsonw_null(json_wtr);
4235ccda64dSRoman Gushchin 
4245ccda64dSRoman Gushchin 	ret = 0;
4255ccda64dSRoman Gushchin 
4265ccda64dSRoman Gushchin exit_prog:
4275ccda64dSRoman Gushchin 	close(prog_fd);
4285ccda64dSRoman Gushchin exit_cgroup:
4295ccda64dSRoman Gushchin 	close(cgroup_fd);
4305ccda64dSRoman Gushchin exit:
4315ccda64dSRoman Gushchin 	return ret;
4325ccda64dSRoman Gushchin }
4335ccda64dSRoman Gushchin 
4345ccda64dSRoman Gushchin static int do_detach(int argc, char **argv)
4355ccda64dSRoman Gushchin {
4365ccda64dSRoman Gushchin 	enum bpf_attach_type attach_type;
4375ccda64dSRoman Gushchin 	int prog_fd, cgroup_fd;
4385ccda64dSRoman Gushchin 	int ret = -1;
4395ccda64dSRoman Gushchin 
4405ccda64dSRoman Gushchin 	if (argc < 4) {
441b4fac96dSJakub Kicinski 		p_err("too few parameters for cgroup detach");
4425ccda64dSRoman Gushchin 		goto exit;
4435ccda64dSRoman Gushchin 	}
4445ccda64dSRoman Gushchin 
4455ccda64dSRoman Gushchin 	cgroup_fd = open(argv[0], O_RDONLY);
4465ccda64dSRoman Gushchin 	if (cgroup_fd < 0) {
4476c6874f4SJakub Kicinski 		p_err("can't open cgroup %s", argv[0]);
4485ccda64dSRoman Gushchin 		goto exit;
4495ccda64dSRoman Gushchin 	}
4505ccda64dSRoman Gushchin 
4515ccda64dSRoman Gushchin 	attach_type = parse_attach_type(argv[1]);
4525ccda64dSRoman Gushchin 	if (attach_type == __MAX_BPF_ATTACH_TYPE) {
4535ccda64dSRoman Gushchin 		p_err("invalid attach type");
4545ccda64dSRoman Gushchin 		goto exit_cgroup;
4555ccda64dSRoman Gushchin 	}
4565ccda64dSRoman Gushchin 
4575ccda64dSRoman Gushchin 	argc -= 2;
4585ccda64dSRoman Gushchin 	argv = &argv[2];
4595ccda64dSRoman Gushchin 	prog_fd = prog_parse_fd(&argc, &argv);
4605ccda64dSRoman Gushchin 	if (prog_fd < 0)
4615ccda64dSRoman Gushchin 		goto exit_cgroup;
4625ccda64dSRoman Gushchin 
4635ccda64dSRoman Gushchin 	if (bpf_prog_detach2(prog_fd, cgroup_fd, attach_type)) {
4645ccda64dSRoman Gushchin 		p_err("failed to detach program");
4655ccda64dSRoman Gushchin 		goto exit_prog;
4665ccda64dSRoman Gushchin 	}
4675ccda64dSRoman Gushchin 
4685ccda64dSRoman Gushchin 	if (json_output)
4695ccda64dSRoman Gushchin 		jsonw_null(json_wtr);
4705ccda64dSRoman Gushchin 
4715ccda64dSRoman Gushchin 	ret = 0;
4725ccda64dSRoman Gushchin 
4735ccda64dSRoman Gushchin exit_prog:
4745ccda64dSRoman Gushchin 	close(prog_fd);
4755ccda64dSRoman Gushchin exit_cgroup:
4765ccda64dSRoman Gushchin 	close(cgroup_fd);
4775ccda64dSRoman Gushchin exit:
4785ccda64dSRoman Gushchin 	return ret;
4795ccda64dSRoman Gushchin }
4805ccda64dSRoman Gushchin 
4815ccda64dSRoman Gushchin static int do_help(int argc, char **argv)
4825ccda64dSRoman Gushchin {
4835ccda64dSRoman Gushchin 	if (json_output) {
4845ccda64dSRoman Gushchin 		jsonw_null(json_wtr);
4855ccda64dSRoman Gushchin 		return 0;
4865ccda64dSRoman Gushchin 	}
4875ccda64dSRoman Gushchin 
4885ccda64dSRoman Gushchin 	fprintf(stderr,
489a98bf573SJakub Kicinski 		"Usage: %s %s { show | list } CGROUP [**effective**]\n"
490a98bf573SJakub Kicinski 		"       %s %s tree [CGROUP_ROOT] [**effective**]\n"
4915ccda64dSRoman Gushchin 		"       %s %s attach CGROUP ATTACH_TYPE PROG [ATTACH_FLAGS]\n"
4925ccda64dSRoman Gushchin 		"       %s %s detach CGROUP ATTACH_TYPE PROG\n"
4935ccda64dSRoman Gushchin 		"       %s %s help\n"
4945ccda64dSRoman Gushchin 		"\n"
495393de512SAndrey Ignatov 		HELP_SPEC_ATTACH_TYPES "\n"
4965ccda64dSRoman Gushchin 		"       " HELP_SPEC_ATTACH_FLAGS "\n"
4975ccda64dSRoman Gushchin 		"       " HELP_SPEC_PROGRAM "\n"
4985ccda64dSRoman Gushchin 		"       " HELP_SPEC_OPTIONS "\n"
4995ccda64dSRoman Gushchin 		"",
5002058b383SRoman Gushchin 		bin_name, argv[-2],
5015ccda64dSRoman Gushchin 		bin_name, argv[-2], bin_name, argv[-2],
5025ccda64dSRoman Gushchin 		bin_name, argv[-2], bin_name, argv[-2]);
5035ccda64dSRoman Gushchin 
5045ccda64dSRoman Gushchin 	return 0;
5055ccda64dSRoman Gushchin }
5065ccda64dSRoman Gushchin 
5075ccda64dSRoman Gushchin static const struct cmd cmds[] = {
5086ebe6dbdSJakub Kicinski 	{ "show",	do_show },
50965b875bcSJakub Kicinski 	{ "list",	do_show },
5102058b383SRoman Gushchin 	{ "tree",       do_show_tree },
5115ccda64dSRoman Gushchin 	{ "attach",	do_attach },
5125ccda64dSRoman Gushchin 	{ "detach",	do_detach },
5135ccda64dSRoman Gushchin 	{ "help",	do_help },
5145ccda64dSRoman Gushchin 	{ 0 }
5155ccda64dSRoman Gushchin };
5165ccda64dSRoman Gushchin 
5175ccda64dSRoman Gushchin int do_cgroup(int argc, char **argv)
5185ccda64dSRoman Gushchin {
5195ccda64dSRoman Gushchin 	return cmd_select(cmds, argc, argv, do_help);
5205ccda64dSRoman Gushchin }
521