xref: /openbmc/linux/tools/bpf/bpftool/cgroup.c (revision 39c9f106)
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"       \
2813a370b9SAndrey Ignatov 	"                        connect6 | sendmsg4 | sendmsg6 }"
295ccda64dSRoman Gushchin 
305ccda64dSRoman Gushchin static const char * const attach_type_strings[] = {
315ccda64dSRoman Gushchin 	[BPF_CGROUP_INET_INGRESS] = "ingress",
325ccda64dSRoman Gushchin 	[BPF_CGROUP_INET_EGRESS] = "egress",
335ccda64dSRoman Gushchin 	[BPF_CGROUP_INET_SOCK_CREATE] = "sock_create",
345ccda64dSRoman Gushchin 	[BPF_CGROUP_SOCK_OPS] = "sock_ops",
355ccda64dSRoman Gushchin 	[BPF_CGROUP_DEVICE] = "device",
36393de512SAndrey Ignatov 	[BPF_CGROUP_INET4_BIND] = "bind4",
37393de512SAndrey Ignatov 	[BPF_CGROUP_INET6_BIND] = "bind6",
38393de512SAndrey Ignatov 	[BPF_CGROUP_INET4_CONNECT] = "connect4",
39393de512SAndrey Ignatov 	[BPF_CGROUP_INET6_CONNECT] = "connect6",
40393de512SAndrey Ignatov 	[BPF_CGROUP_INET4_POST_BIND] = "post_bind4",
41393de512SAndrey Ignatov 	[BPF_CGROUP_INET6_POST_BIND] = "post_bind6",
4213a370b9SAndrey Ignatov 	[BPF_CGROUP_UDP4_SENDMSG] = "sendmsg4",
4313a370b9SAndrey Ignatov 	[BPF_CGROUP_UDP6_SENDMSG] = "sendmsg6",
445ccda64dSRoman Gushchin 	[__MAX_BPF_ATTACH_TYPE] = NULL,
455ccda64dSRoman Gushchin };
465ccda64dSRoman Gushchin 
475ccda64dSRoman Gushchin static enum bpf_attach_type parse_attach_type(const char *str)
485ccda64dSRoman Gushchin {
495ccda64dSRoman Gushchin 	enum bpf_attach_type type;
505ccda64dSRoman Gushchin 
515ccda64dSRoman Gushchin 	for (type = 0; type < __MAX_BPF_ATTACH_TYPE; type++) {
525ccda64dSRoman Gushchin 		if (attach_type_strings[type] &&
535ccda64dSRoman Gushchin 		    is_prefix(str, attach_type_strings[type]))
545ccda64dSRoman Gushchin 			return type;
555ccda64dSRoman Gushchin 	}
565ccda64dSRoman Gushchin 
575ccda64dSRoman Gushchin 	return __MAX_BPF_ATTACH_TYPE;
585ccda64dSRoman Gushchin }
595ccda64dSRoman Gushchin 
6065b875bcSJakub Kicinski static int show_bpf_prog(int id, const char *attach_type_str,
612058b383SRoman Gushchin 			 const char *attach_flags_str,
622058b383SRoman Gushchin 			 int level)
635ccda64dSRoman Gushchin {
645ccda64dSRoman Gushchin 	struct bpf_prog_info info = {};
655ccda64dSRoman Gushchin 	__u32 info_len = sizeof(info);
665ccda64dSRoman Gushchin 	int prog_fd;
675ccda64dSRoman Gushchin 
685ccda64dSRoman Gushchin 	prog_fd = bpf_prog_get_fd_by_id(id);
695ccda64dSRoman Gushchin 	if (prog_fd < 0)
705ccda64dSRoman Gushchin 		return -1;
715ccda64dSRoman Gushchin 
725ccda64dSRoman Gushchin 	if (bpf_obj_get_info_by_fd(prog_fd, &info, &info_len)) {
735ccda64dSRoman Gushchin 		close(prog_fd);
745ccda64dSRoman Gushchin 		return -1;
755ccda64dSRoman Gushchin 	}
765ccda64dSRoman Gushchin 
775ccda64dSRoman Gushchin 	if (json_output) {
785ccda64dSRoman Gushchin 		jsonw_start_object(json_wtr);
795ccda64dSRoman Gushchin 		jsonw_uint_field(json_wtr, "id", info.id);
805ccda64dSRoman Gushchin 		jsonw_string_field(json_wtr, "attach_type",
815ccda64dSRoman Gushchin 				   attach_type_str);
825ccda64dSRoman Gushchin 		jsonw_string_field(json_wtr, "attach_flags",
835ccda64dSRoman Gushchin 				   attach_flags_str);
845ccda64dSRoman Gushchin 		jsonw_string_field(json_wtr, "name", info.name);
855ccda64dSRoman Gushchin 		jsonw_end_object(json_wtr);
865ccda64dSRoman Gushchin 	} else {
872058b383SRoman Gushchin 		printf("%s%-8u %-15s %-15s %-15s\n", level ? "    " : "",
882058b383SRoman Gushchin 		       info.id,
895ccda64dSRoman Gushchin 		       attach_type_str,
905ccda64dSRoman Gushchin 		       attach_flags_str,
915ccda64dSRoman Gushchin 		       info.name);
925ccda64dSRoman Gushchin 	}
935ccda64dSRoman Gushchin 
945ccda64dSRoman Gushchin 	close(prog_fd);
955ccda64dSRoman Gushchin 	return 0;
965ccda64dSRoman Gushchin }
975ccda64dSRoman Gushchin 
982058b383SRoman Gushchin static int count_attached_bpf_progs(int cgroup_fd, enum bpf_attach_type type)
992058b383SRoman Gushchin {
1002058b383SRoman Gushchin 	__u32 prog_cnt = 0;
1012058b383SRoman Gushchin 	int ret;
1022058b383SRoman Gushchin 
1032058b383SRoman Gushchin 	ret = bpf_prog_query(cgroup_fd, type, 0, NULL, NULL, &prog_cnt);
1042058b383SRoman Gushchin 	if (ret)
1052058b383SRoman Gushchin 		return -1;
1062058b383SRoman Gushchin 
1072058b383SRoman Gushchin 	return prog_cnt;
1082058b383SRoman Gushchin }
1092058b383SRoman Gushchin 
1102058b383SRoman Gushchin static int show_attached_bpf_progs(int cgroup_fd, enum bpf_attach_type type,
1112058b383SRoman Gushchin 				   int level)
1125ccda64dSRoman Gushchin {
1135ccda64dSRoman Gushchin 	__u32 prog_ids[1024] = {0};
1145ccda64dSRoman Gushchin 	char *attach_flags_str;
1155ccda64dSRoman Gushchin 	__u32 prog_cnt, iter;
1165ccda64dSRoman Gushchin 	__u32 attach_flags;
1175ccda64dSRoman Gushchin 	char buf[32];
1185ccda64dSRoman Gushchin 	int ret;
1195ccda64dSRoman Gushchin 
1205ccda64dSRoman Gushchin 	prog_cnt = ARRAY_SIZE(prog_ids);
1215ccda64dSRoman Gushchin 	ret = bpf_prog_query(cgroup_fd, type, 0, &attach_flags, prog_ids,
1225ccda64dSRoman Gushchin 			     &prog_cnt);
1235ccda64dSRoman Gushchin 	if (ret)
1245ccda64dSRoman Gushchin 		return ret;
1255ccda64dSRoman Gushchin 
1265ccda64dSRoman Gushchin 	if (prog_cnt == 0)
1275ccda64dSRoman Gushchin 		return 0;
1285ccda64dSRoman Gushchin 
1295ccda64dSRoman Gushchin 	switch (attach_flags) {
1305ccda64dSRoman Gushchin 	case BPF_F_ALLOW_MULTI:
1315ccda64dSRoman Gushchin 		attach_flags_str = "multi";
1325ccda64dSRoman Gushchin 		break;
1335ccda64dSRoman Gushchin 	case BPF_F_ALLOW_OVERRIDE:
1345ccda64dSRoman Gushchin 		attach_flags_str = "override";
1355ccda64dSRoman Gushchin 		break;
1365ccda64dSRoman Gushchin 	case 0:
1375ccda64dSRoman Gushchin 		attach_flags_str = "";
1385ccda64dSRoman Gushchin 		break;
1395ccda64dSRoman Gushchin 	default:
1405ccda64dSRoman Gushchin 		snprintf(buf, sizeof(buf), "unknown(%x)", attach_flags);
1415ccda64dSRoman Gushchin 		attach_flags_str = buf;
1425ccda64dSRoman Gushchin 	}
1435ccda64dSRoman Gushchin 
1445ccda64dSRoman Gushchin 	for (iter = 0; iter < prog_cnt; iter++)
14565b875bcSJakub Kicinski 		show_bpf_prog(prog_ids[iter], attach_type_strings[type],
1462058b383SRoman Gushchin 			      attach_flags_str, level);
1475ccda64dSRoman Gushchin 
1485ccda64dSRoman Gushchin 	return 0;
1495ccda64dSRoman Gushchin }
1505ccda64dSRoman Gushchin 
15165b875bcSJakub Kicinski static int do_show(int argc, char **argv)
1525ccda64dSRoman Gushchin {
1535ccda64dSRoman Gushchin 	enum bpf_attach_type type;
1545ccda64dSRoman Gushchin 	int cgroup_fd;
1555ccda64dSRoman Gushchin 	int ret = -1;
1565ccda64dSRoman Gushchin 
1575ccda64dSRoman Gushchin 	if (argc < 1) {
158b4fac96dSJakub Kicinski 		p_err("too few parameters for cgroup show");
1595ccda64dSRoman Gushchin 		goto exit;
1605ccda64dSRoman Gushchin 	} else if (argc > 1) {
161b4fac96dSJakub Kicinski 		p_err("too many parameters for cgroup show");
1625ccda64dSRoman Gushchin 		goto exit;
1635ccda64dSRoman Gushchin 	}
1645ccda64dSRoman Gushchin 
1655ccda64dSRoman Gushchin 	cgroup_fd = open(argv[0], O_RDONLY);
1665ccda64dSRoman Gushchin 	if (cgroup_fd < 0) {
167b4fac96dSJakub Kicinski 		p_err("can't open cgroup %s", argv[1]);
1685ccda64dSRoman Gushchin 		goto exit;
1695ccda64dSRoman Gushchin 	}
1705ccda64dSRoman Gushchin 
1715ccda64dSRoman Gushchin 	if (json_output)
1725ccda64dSRoman Gushchin 		jsonw_start_array(json_wtr);
1735ccda64dSRoman Gushchin 	else
1745ccda64dSRoman Gushchin 		printf("%-8s %-15s %-15s %-15s\n", "ID", "AttachType",
1755ccda64dSRoman Gushchin 		       "AttachFlags", "Name");
1765ccda64dSRoman Gushchin 
1775ccda64dSRoman Gushchin 	for (type = 0; type < __MAX_BPF_ATTACH_TYPE; type++) {
1785ccda64dSRoman Gushchin 		/*
1795ccda64dSRoman Gushchin 		 * Not all attach types may be supported, so it's expected,
1805ccda64dSRoman Gushchin 		 * that some requests will fail.
18165b875bcSJakub Kicinski 		 * If we were able to get the show for at least one
1825ccda64dSRoman Gushchin 		 * attach type, let's return 0.
1835ccda64dSRoman Gushchin 		 */
1842058b383SRoman Gushchin 		if (show_attached_bpf_progs(cgroup_fd, type, 0) == 0)
1855ccda64dSRoman Gushchin 			ret = 0;
1865ccda64dSRoman Gushchin 	}
1875ccda64dSRoman Gushchin 
1885ccda64dSRoman Gushchin 	if (json_output)
1895ccda64dSRoman Gushchin 		jsonw_end_array(json_wtr);
1905ccda64dSRoman Gushchin 
1915ccda64dSRoman Gushchin 	close(cgroup_fd);
1925ccda64dSRoman Gushchin exit:
1935ccda64dSRoman Gushchin 	return ret;
1945ccda64dSRoman Gushchin }
1955ccda64dSRoman Gushchin 
1962058b383SRoman Gushchin /*
1972058b383SRoman Gushchin  * To distinguish nftw() errors and do_show_tree_fn() errors
1982058b383SRoman Gushchin  * and avoid duplicating error messages, let's return -2
1992058b383SRoman Gushchin  * from do_show_tree_fn() in case of error.
2002058b383SRoman Gushchin  */
2012058b383SRoman Gushchin #define NFTW_ERR		-1
2022058b383SRoman Gushchin #define SHOW_TREE_FN_ERR	-2
2032058b383SRoman Gushchin static int do_show_tree_fn(const char *fpath, const struct stat *sb,
2042058b383SRoman Gushchin 			   int typeflag, struct FTW *ftw)
2052058b383SRoman Gushchin {
2062058b383SRoman Gushchin 	enum bpf_attach_type type;
2072058b383SRoman Gushchin 	bool skip = true;
2082058b383SRoman Gushchin 	int cgroup_fd;
2092058b383SRoman Gushchin 
2102058b383SRoman Gushchin 	if (typeflag != FTW_D)
2112058b383SRoman Gushchin 		return 0;
2122058b383SRoman Gushchin 
2132058b383SRoman Gushchin 	cgroup_fd = open(fpath, O_RDONLY);
2142058b383SRoman Gushchin 	if (cgroup_fd < 0) {
2152058b383SRoman Gushchin 		p_err("can't open cgroup %s: %s", fpath, strerror(errno));
2162058b383SRoman Gushchin 		return SHOW_TREE_FN_ERR;
2172058b383SRoman Gushchin 	}
2182058b383SRoman Gushchin 
2192058b383SRoman Gushchin 	for (type = 0; type < __MAX_BPF_ATTACH_TYPE; type++) {
2202058b383SRoman Gushchin 		int count = count_attached_bpf_progs(cgroup_fd, type);
2212058b383SRoman Gushchin 
2222058b383SRoman Gushchin 		if (count < 0 && errno != EINVAL) {
2232058b383SRoman Gushchin 			p_err("can't query bpf programs attached to %s: %s",
2242058b383SRoman Gushchin 			      fpath, strerror(errno));
2252058b383SRoman Gushchin 			close(cgroup_fd);
2262058b383SRoman Gushchin 			return SHOW_TREE_FN_ERR;
2272058b383SRoman Gushchin 		}
2282058b383SRoman Gushchin 		if (count > 0) {
2292058b383SRoman Gushchin 			skip = false;
2302058b383SRoman Gushchin 			break;
2312058b383SRoman Gushchin 		}
2322058b383SRoman Gushchin 	}
2332058b383SRoman Gushchin 
2342058b383SRoman Gushchin 	if (skip) {
2352058b383SRoman Gushchin 		close(cgroup_fd);
2362058b383SRoman Gushchin 		return 0;
2372058b383SRoman Gushchin 	}
2382058b383SRoman Gushchin 
2392058b383SRoman Gushchin 	if (json_output) {
2402058b383SRoman Gushchin 		jsonw_start_object(json_wtr);
2412058b383SRoman Gushchin 		jsonw_string_field(json_wtr, "cgroup", fpath);
2422058b383SRoman Gushchin 		jsonw_name(json_wtr, "programs");
2432058b383SRoman Gushchin 		jsonw_start_array(json_wtr);
2442058b383SRoman Gushchin 	} else {
2452058b383SRoman Gushchin 		printf("%s\n", fpath);
2462058b383SRoman Gushchin 	}
2472058b383SRoman Gushchin 
2482058b383SRoman Gushchin 	for (type = 0; type < __MAX_BPF_ATTACH_TYPE; type++)
2492058b383SRoman Gushchin 		show_attached_bpf_progs(cgroup_fd, type, ftw->level);
2502058b383SRoman Gushchin 
25139c9f106SQuentin Monnet 	if (errno == EINVAL)
25239c9f106SQuentin Monnet 		/* Last attach type does not support query.
25339c9f106SQuentin Monnet 		 * Do not report an error for this, especially because batch
25439c9f106SQuentin Monnet 		 * mode would stop processing commands.
25539c9f106SQuentin Monnet 		 */
25639c9f106SQuentin Monnet 		errno = 0;
25739c9f106SQuentin Monnet 
2582058b383SRoman Gushchin 	if (json_output) {
2592058b383SRoman Gushchin 		jsonw_end_array(json_wtr);
2602058b383SRoman Gushchin 		jsonw_end_object(json_wtr);
2612058b383SRoman Gushchin 	}
2622058b383SRoman Gushchin 
2632058b383SRoman Gushchin 	close(cgroup_fd);
2642058b383SRoman Gushchin 
2652058b383SRoman Gushchin 	return 0;
2662058b383SRoman Gushchin }
2672058b383SRoman Gushchin 
2682058b383SRoman Gushchin static char *find_cgroup_root(void)
2692058b383SRoman Gushchin {
2702058b383SRoman Gushchin 	struct mntent *mnt;
2712058b383SRoman Gushchin 	FILE *f;
2722058b383SRoman Gushchin 
2732058b383SRoman Gushchin 	f = fopen("/proc/mounts", "r");
2742058b383SRoman Gushchin 	if (f == NULL)
2752058b383SRoman Gushchin 		return NULL;
2762058b383SRoman Gushchin 
2772058b383SRoman Gushchin 	while ((mnt = getmntent(f))) {
2782058b383SRoman Gushchin 		if (strcmp(mnt->mnt_type, "cgroup2") == 0) {
2792058b383SRoman Gushchin 			fclose(f);
2802058b383SRoman Gushchin 			return strdup(mnt->mnt_dir);
2812058b383SRoman Gushchin 		}
2822058b383SRoman Gushchin 	}
2832058b383SRoman Gushchin 
2842058b383SRoman Gushchin 	fclose(f);
2852058b383SRoman Gushchin 	return NULL;
2862058b383SRoman Gushchin }
2872058b383SRoman Gushchin 
2882058b383SRoman Gushchin static int do_show_tree(int argc, char **argv)
2892058b383SRoman Gushchin {
2902058b383SRoman Gushchin 	char *cgroup_root;
2912058b383SRoman Gushchin 	int ret;
2922058b383SRoman Gushchin 
2932058b383SRoman Gushchin 	switch (argc) {
2942058b383SRoman Gushchin 	case 0:
2952058b383SRoman Gushchin 		cgroup_root = find_cgroup_root();
2962058b383SRoman Gushchin 		if (!cgroup_root) {
2972058b383SRoman Gushchin 			p_err("cgroup v2 isn't mounted");
2982058b383SRoman Gushchin 			return -1;
2992058b383SRoman Gushchin 		}
3002058b383SRoman Gushchin 		break;
3012058b383SRoman Gushchin 	case 1:
3022058b383SRoman Gushchin 		cgroup_root = argv[0];
3032058b383SRoman Gushchin 		break;
3042058b383SRoman Gushchin 	default:
3052058b383SRoman Gushchin 		p_err("too many parameters for cgroup tree");
3062058b383SRoman Gushchin 		return -1;
3072058b383SRoman Gushchin 	}
3082058b383SRoman Gushchin 
3092058b383SRoman Gushchin 
3102058b383SRoman Gushchin 	if (json_output)
3112058b383SRoman Gushchin 		jsonw_start_array(json_wtr);
3122058b383SRoman Gushchin 	else
3132058b383SRoman Gushchin 		printf("%s\n"
3142058b383SRoman Gushchin 		       "%-8s %-15s %-15s %-15s\n",
3152058b383SRoman Gushchin 		       "CgroupPath",
3162058b383SRoman Gushchin 		       "ID", "AttachType", "AttachFlags", "Name");
3172058b383SRoman Gushchin 
3182058b383SRoman Gushchin 	switch (nftw(cgroup_root, do_show_tree_fn, 1024, FTW_MOUNT)) {
3192058b383SRoman Gushchin 	case NFTW_ERR:
3202058b383SRoman Gushchin 		p_err("can't iterate over %s: %s", cgroup_root,
3212058b383SRoman Gushchin 		      strerror(errno));
3222058b383SRoman Gushchin 		ret = -1;
3232058b383SRoman Gushchin 		break;
3242058b383SRoman Gushchin 	case SHOW_TREE_FN_ERR:
3252058b383SRoman Gushchin 		ret = -1;
3262058b383SRoman Gushchin 		break;
3272058b383SRoman Gushchin 	default:
3282058b383SRoman Gushchin 		ret = 0;
3292058b383SRoman Gushchin 	}
3302058b383SRoman Gushchin 
3312058b383SRoman Gushchin 	if (json_output)
3322058b383SRoman Gushchin 		jsonw_end_array(json_wtr);
3332058b383SRoman Gushchin 
3342058b383SRoman Gushchin 	if (argc == 0)
3352058b383SRoman Gushchin 		free(cgroup_root);
3362058b383SRoman Gushchin 
3372058b383SRoman Gushchin 	return ret;
3382058b383SRoman Gushchin }
3392058b383SRoman Gushchin 
3405ccda64dSRoman Gushchin static int do_attach(int argc, char **argv)
3415ccda64dSRoman Gushchin {
3425ccda64dSRoman Gushchin 	enum bpf_attach_type attach_type;
3435ccda64dSRoman Gushchin 	int cgroup_fd, prog_fd;
3445ccda64dSRoman Gushchin 	int attach_flags = 0;
3455ccda64dSRoman Gushchin 	int ret = -1;
3465ccda64dSRoman Gushchin 	int i;
3475ccda64dSRoman Gushchin 
3485ccda64dSRoman Gushchin 	if (argc < 4) {
349b4fac96dSJakub Kicinski 		p_err("too few parameters for cgroup attach");
3505ccda64dSRoman Gushchin 		goto exit;
3515ccda64dSRoman Gushchin 	}
3525ccda64dSRoman Gushchin 
3535ccda64dSRoman Gushchin 	cgroup_fd = open(argv[0], O_RDONLY);
3545ccda64dSRoman Gushchin 	if (cgroup_fd < 0) {
355b4fac96dSJakub Kicinski 		p_err("can't open cgroup %s", argv[1]);
3565ccda64dSRoman Gushchin 		goto exit;
3575ccda64dSRoman Gushchin 	}
3585ccda64dSRoman Gushchin 
3595ccda64dSRoman Gushchin 	attach_type = parse_attach_type(argv[1]);
3605ccda64dSRoman Gushchin 	if (attach_type == __MAX_BPF_ATTACH_TYPE) {
361b4fac96dSJakub Kicinski 		p_err("invalid attach type");
3625ccda64dSRoman Gushchin 		goto exit_cgroup;
3635ccda64dSRoman Gushchin 	}
3645ccda64dSRoman Gushchin 
3655ccda64dSRoman Gushchin 	argc -= 2;
3665ccda64dSRoman Gushchin 	argv = &argv[2];
3675ccda64dSRoman Gushchin 	prog_fd = prog_parse_fd(&argc, &argv);
3685ccda64dSRoman Gushchin 	if (prog_fd < 0)
3695ccda64dSRoman Gushchin 		goto exit_cgroup;
3705ccda64dSRoman Gushchin 
3715ccda64dSRoman Gushchin 	for (i = 0; i < argc; i++) {
3725ccda64dSRoman Gushchin 		if (is_prefix(argv[i], "multi")) {
3735ccda64dSRoman Gushchin 			attach_flags |= BPF_F_ALLOW_MULTI;
3745ccda64dSRoman Gushchin 		} else if (is_prefix(argv[i], "override")) {
3755ccda64dSRoman Gushchin 			attach_flags |= BPF_F_ALLOW_OVERRIDE;
3765ccda64dSRoman Gushchin 		} else {
377b4fac96dSJakub Kicinski 			p_err("unknown option: %s", argv[i]);
3785ccda64dSRoman Gushchin 			goto exit_cgroup;
3795ccda64dSRoman Gushchin 		}
3805ccda64dSRoman Gushchin 	}
3815ccda64dSRoman Gushchin 
3825ccda64dSRoman Gushchin 	if (bpf_prog_attach(prog_fd, cgroup_fd, attach_type, attach_flags)) {
3835ccda64dSRoman Gushchin 		p_err("failed to attach program");
3845ccda64dSRoman Gushchin 		goto exit_prog;
3855ccda64dSRoman Gushchin 	}
3865ccda64dSRoman Gushchin 
3875ccda64dSRoman Gushchin 	if (json_output)
3885ccda64dSRoman Gushchin 		jsonw_null(json_wtr);
3895ccda64dSRoman Gushchin 
3905ccda64dSRoman Gushchin 	ret = 0;
3915ccda64dSRoman Gushchin 
3925ccda64dSRoman Gushchin exit_prog:
3935ccda64dSRoman Gushchin 	close(prog_fd);
3945ccda64dSRoman Gushchin exit_cgroup:
3955ccda64dSRoman Gushchin 	close(cgroup_fd);
3965ccda64dSRoman Gushchin exit:
3975ccda64dSRoman Gushchin 	return ret;
3985ccda64dSRoman Gushchin }
3995ccda64dSRoman Gushchin 
4005ccda64dSRoman Gushchin static int do_detach(int argc, char **argv)
4015ccda64dSRoman Gushchin {
4025ccda64dSRoman Gushchin 	enum bpf_attach_type attach_type;
4035ccda64dSRoman Gushchin 	int prog_fd, cgroup_fd;
4045ccda64dSRoman Gushchin 	int ret = -1;
4055ccda64dSRoman Gushchin 
4065ccda64dSRoman Gushchin 	if (argc < 4) {
407b4fac96dSJakub Kicinski 		p_err("too few parameters for cgroup detach");
4085ccda64dSRoman Gushchin 		goto exit;
4095ccda64dSRoman Gushchin 	}
4105ccda64dSRoman Gushchin 
4115ccda64dSRoman Gushchin 	cgroup_fd = open(argv[0], O_RDONLY);
4125ccda64dSRoman Gushchin 	if (cgroup_fd < 0) {
413b4fac96dSJakub Kicinski 		p_err("can't open cgroup %s", argv[1]);
4145ccda64dSRoman Gushchin 		goto exit;
4155ccda64dSRoman Gushchin 	}
4165ccda64dSRoman Gushchin 
4175ccda64dSRoman Gushchin 	attach_type = parse_attach_type(argv[1]);
4185ccda64dSRoman Gushchin 	if (attach_type == __MAX_BPF_ATTACH_TYPE) {
4195ccda64dSRoman Gushchin 		p_err("invalid attach type");
4205ccda64dSRoman Gushchin 		goto exit_cgroup;
4215ccda64dSRoman Gushchin 	}
4225ccda64dSRoman Gushchin 
4235ccda64dSRoman Gushchin 	argc -= 2;
4245ccda64dSRoman Gushchin 	argv = &argv[2];
4255ccda64dSRoman Gushchin 	prog_fd = prog_parse_fd(&argc, &argv);
4265ccda64dSRoman Gushchin 	if (prog_fd < 0)
4275ccda64dSRoman Gushchin 		goto exit_cgroup;
4285ccda64dSRoman Gushchin 
4295ccda64dSRoman Gushchin 	if (bpf_prog_detach2(prog_fd, cgroup_fd, attach_type)) {
4305ccda64dSRoman Gushchin 		p_err("failed to detach program");
4315ccda64dSRoman Gushchin 		goto exit_prog;
4325ccda64dSRoman Gushchin 	}
4335ccda64dSRoman Gushchin 
4345ccda64dSRoman Gushchin 	if (json_output)
4355ccda64dSRoman Gushchin 		jsonw_null(json_wtr);
4365ccda64dSRoman Gushchin 
4375ccda64dSRoman Gushchin 	ret = 0;
4385ccda64dSRoman Gushchin 
4395ccda64dSRoman Gushchin exit_prog:
4405ccda64dSRoman Gushchin 	close(prog_fd);
4415ccda64dSRoman Gushchin exit_cgroup:
4425ccda64dSRoman Gushchin 	close(cgroup_fd);
4435ccda64dSRoman Gushchin exit:
4445ccda64dSRoman Gushchin 	return ret;
4455ccda64dSRoman Gushchin }
4465ccda64dSRoman Gushchin 
4475ccda64dSRoman Gushchin static int do_help(int argc, char **argv)
4485ccda64dSRoman Gushchin {
4495ccda64dSRoman Gushchin 	if (json_output) {
4505ccda64dSRoman Gushchin 		jsonw_null(json_wtr);
4515ccda64dSRoman Gushchin 		return 0;
4525ccda64dSRoman Gushchin 	}
4535ccda64dSRoman Gushchin 
4545ccda64dSRoman Gushchin 	fprintf(stderr,
4556ebe6dbdSJakub Kicinski 		"Usage: %s %s { show | list } CGROUP\n"
4562058b383SRoman Gushchin 		"       %s %s tree [CGROUP_ROOT]\n"
4575ccda64dSRoman Gushchin 		"       %s %s attach CGROUP ATTACH_TYPE PROG [ATTACH_FLAGS]\n"
4585ccda64dSRoman Gushchin 		"       %s %s detach CGROUP ATTACH_TYPE PROG\n"
4595ccda64dSRoman Gushchin 		"       %s %s help\n"
4605ccda64dSRoman Gushchin 		"\n"
461393de512SAndrey Ignatov 		HELP_SPEC_ATTACH_TYPES "\n"
4625ccda64dSRoman Gushchin 		"       " HELP_SPEC_ATTACH_FLAGS "\n"
4635ccda64dSRoman Gushchin 		"       " HELP_SPEC_PROGRAM "\n"
4645ccda64dSRoman Gushchin 		"       " HELP_SPEC_OPTIONS "\n"
4655ccda64dSRoman Gushchin 		"",
4662058b383SRoman Gushchin 		bin_name, argv[-2],
4675ccda64dSRoman Gushchin 		bin_name, argv[-2], bin_name, argv[-2],
4685ccda64dSRoman Gushchin 		bin_name, argv[-2], bin_name, argv[-2]);
4695ccda64dSRoman Gushchin 
4705ccda64dSRoman Gushchin 	return 0;
4715ccda64dSRoman Gushchin }
4725ccda64dSRoman Gushchin 
4735ccda64dSRoman Gushchin static const struct cmd cmds[] = {
4746ebe6dbdSJakub Kicinski 	{ "show",	do_show },
47565b875bcSJakub Kicinski 	{ "list",	do_show },
4762058b383SRoman Gushchin 	{ "tree",       do_show_tree },
4775ccda64dSRoman Gushchin 	{ "attach",	do_attach },
4785ccda64dSRoman Gushchin 	{ "detach",	do_detach },
4795ccda64dSRoman Gushchin 	{ "help",	do_help },
4805ccda64dSRoman Gushchin 	{ 0 }
4815ccda64dSRoman Gushchin };
4825ccda64dSRoman Gushchin 
4835ccda64dSRoman Gushchin int do_cgroup(int argc, char **argv)
4845ccda64dSRoman Gushchin {
4855ccda64dSRoman Gushchin 	return cmd_select(cmds, argc, argv, do_help);
4865ccda64dSRoman Gushchin }
487