xref: /openbmc/linux/tools/bpf/bpftool/cgroup.c (revision 000aa125)
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"           \
29000aa125SDaniel Borkmann 	"                        recvmsg4 | recvmsg6 | sysctl }"
305ccda64dSRoman Gushchin 
315ccda64dSRoman Gushchin static const char * const attach_type_strings[] = {
325ccda64dSRoman Gushchin 	[BPF_CGROUP_INET_INGRESS] = "ingress",
335ccda64dSRoman Gushchin 	[BPF_CGROUP_INET_EGRESS] = "egress",
345ccda64dSRoman Gushchin 	[BPF_CGROUP_INET_SOCK_CREATE] = "sock_create",
355ccda64dSRoman Gushchin 	[BPF_CGROUP_SOCK_OPS] = "sock_ops",
365ccda64dSRoman Gushchin 	[BPF_CGROUP_DEVICE] = "device",
37393de512SAndrey Ignatov 	[BPF_CGROUP_INET4_BIND] = "bind4",
38393de512SAndrey Ignatov 	[BPF_CGROUP_INET6_BIND] = "bind6",
39393de512SAndrey Ignatov 	[BPF_CGROUP_INET4_CONNECT] = "connect4",
40393de512SAndrey Ignatov 	[BPF_CGROUP_INET6_CONNECT] = "connect6",
41393de512SAndrey Ignatov 	[BPF_CGROUP_INET4_POST_BIND] = "post_bind4",
42393de512SAndrey Ignatov 	[BPF_CGROUP_INET6_POST_BIND] = "post_bind6",
4313a370b9SAndrey Ignatov 	[BPF_CGROUP_UDP4_SENDMSG] = "sendmsg4",
4413a370b9SAndrey Ignatov 	[BPF_CGROUP_UDP6_SENDMSG] = "sendmsg6",
45f25377eeSAndrey Ignatov 	[BPF_CGROUP_SYSCTL] = "sysctl",
46000aa125SDaniel Borkmann 	[BPF_CGROUP_UDP4_RECVMSG] = "recvmsg4",
47000aa125SDaniel Borkmann 	[BPF_CGROUP_UDP6_RECVMSG] = "recvmsg6",
485ccda64dSRoman Gushchin 	[__MAX_BPF_ATTACH_TYPE] = NULL,
495ccda64dSRoman Gushchin };
505ccda64dSRoman Gushchin 
515ccda64dSRoman Gushchin static enum bpf_attach_type parse_attach_type(const char *str)
525ccda64dSRoman Gushchin {
535ccda64dSRoman Gushchin 	enum bpf_attach_type type;
545ccda64dSRoman Gushchin 
555ccda64dSRoman Gushchin 	for (type = 0; type < __MAX_BPF_ATTACH_TYPE; type++) {
565ccda64dSRoman Gushchin 		if (attach_type_strings[type] &&
575ccda64dSRoman Gushchin 		    is_prefix(str, attach_type_strings[type]))
585ccda64dSRoman Gushchin 			return type;
595ccda64dSRoman Gushchin 	}
605ccda64dSRoman Gushchin 
615ccda64dSRoman Gushchin 	return __MAX_BPF_ATTACH_TYPE;
625ccda64dSRoman Gushchin }
635ccda64dSRoman Gushchin 
6465b875bcSJakub Kicinski static int show_bpf_prog(int id, const char *attach_type_str,
652058b383SRoman Gushchin 			 const char *attach_flags_str,
662058b383SRoman Gushchin 			 int level)
675ccda64dSRoman Gushchin {
685ccda64dSRoman Gushchin 	struct bpf_prog_info info = {};
695ccda64dSRoman Gushchin 	__u32 info_len = sizeof(info);
705ccda64dSRoman Gushchin 	int prog_fd;
715ccda64dSRoman Gushchin 
725ccda64dSRoman Gushchin 	prog_fd = bpf_prog_get_fd_by_id(id);
735ccda64dSRoman Gushchin 	if (prog_fd < 0)
745ccda64dSRoman Gushchin 		return -1;
755ccda64dSRoman Gushchin 
765ccda64dSRoman Gushchin 	if (bpf_obj_get_info_by_fd(prog_fd, &info, &info_len)) {
775ccda64dSRoman Gushchin 		close(prog_fd);
785ccda64dSRoman Gushchin 		return -1;
795ccda64dSRoman Gushchin 	}
805ccda64dSRoman Gushchin 
815ccda64dSRoman Gushchin 	if (json_output) {
825ccda64dSRoman Gushchin 		jsonw_start_object(json_wtr);
835ccda64dSRoman Gushchin 		jsonw_uint_field(json_wtr, "id", info.id);
845ccda64dSRoman Gushchin 		jsonw_string_field(json_wtr, "attach_type",
855ccda64dSRoman Gushchin 				   attach_type_str);
865ccda64dSRoman Gushchin 		jsonw_string_field(json_wtr, "attach_flags",
875ccda64dSRoman Gushchin 				   attach_flags_str);
885ccda64dSRoman Gushchin 		jsonw_string_field(json_wtr, "name", info.name);
895ccda64dSRoman Gushchin 		jsonw_end_object(json_wtr);
905ccda64dSRoman Gushchin 	} else {
912058b383SRoman Gushchin 		printf("%s%-8u %-15s %-15s %-15s\n", level ? "    " : "",
922058b383SRoman Gushchin 		       info.id,
935ccda64dSRoman Gushchin 		       attach_type_str,
945ccda64dSRoman Gushchin 		       attach_flags_str,
955ccda64dSRoman Gushchin 		       info.name);
965ccda64dSRoman Gushchin 	}
975ccda64dSRoman Gushchin 
985ccda64dSRoman Gushchin 	close(prog_fd);
995ccda64dSRoman Gushchin 	return 0;
1005ccda64dSRoman Gushchin }
1015ccda64dSRoman Gushchin 
1022058b383SRoman Gushchin static int count_attached_bpf_progs(int cgroup_fd, enum bpf_attach_type type)
1032058b383SRoman Gushchin {
1042058b383SRoman Gushchin 	__u32 prog_cnt = 0;
1052058b383SRoman Gushchin 	int ret;
1062058b383SRoman Gushchin 
1072058b383SRoman Gushchin 	ret = bpf_prog_query(cgroup_fd, type, 0, NULL, NULL, &prog_cnt);
1082058b383SRoman Gushchin 	if (ret)
1092058b383SRoman Gushchin 		return -1;
1102058b383SRoman Gushchin 
1112058b383SRoman Gushchin 	return prog_cnt;
1122058b383SRoman Gushchin }
1132058b383SRoman Gushchin 
1142058b383SRoman Gushchin static int show_attached_bpf_progs(int cgroup_fd, enum bpf_attach_type type,
1152058b383SRoman Gushchin 				   int level)
1165ccda64dSRoman Gushchin {
1175ccda64dSRoman Gushchin 	__u32 prog_ids[1024] = {0};
1185ccda64dSRoman Gushchin 	char *attach_flags_str;
1195ccda64dSRoman Gushchin 	__u32 prog_cnt, iter;
1205ccda64dSRoman Gushchin 	__u32 attach_flags;
1215ccda64dSRoman Gushchin 	char buf[32];
1225ccda64dSRoman Gushchin 	int ret;
1235ccda64dSRoman Gushchin 
1245ccda64dSRoman Gushchin 	prog_cnt = ARRAY_SIZE(prog_ids);
1255ccda64dSRoman Gushchin 	ret = bpf_prog_query(cgroup_fd, type, 0, &attach_flags, prog_ids,
1265ccda64dSRoman Gushchin 			     &prog_cnt);
1275ccda64dSRoman Gushchin 	if (ret)
1285ccda64dSRoman Gushchin 		return ret;
1295ccda64dSRoman Gushchin 
1305ccda64dSRoman Gushchin 	if (prog_cnt == 0)
1315ccda64dSRoman Gushchin 		return 0;
1325ccda64dSRoman Gushchin 
1335ccda64dSRoman Gushchin 	switch (attach_flags) {
1345ccda64dSRoman Gushchin 	case BPF_F_ALLOW_MULTI:
1355ccda64dSRoman Gushchin 		attach_flags_str = "multi";
1365ccda64dSRoman Gushchin 		break;
1375ccda64dSRoman Gushchin 	case BPF_F_ALLOW_OVERRIDE:
1385ccda64dSRoman Gushchin 		attach_flags_str = "override";
1395ccda64dSRoman Gushchin 		break;
1405ccda64dSRoman Gushchin 	case 0:
1415ccda64dSRoman Gushchin 		attach_flags_str = "";
1425ccda64dSRoman Gushchin 		break;
1435ccda64dSRoman Gushchin 	default:
1445ccda64dSRoman Gushchin 		snprintf(buf, sizeof(buf), "unknown(%x)", attach_flags);
1455ccda64dSRoman Gushchin 		attach_flags_str = buf;
1465ccda64dSRoman Gushchin 	}
1475ccda64dSRoman Gushchin 
1485ccda64dSRoman Gushchin 	for (iter = 0; iter < prog_cnt; iter++)
14965b875bcSJakub Kicinski 		show_bpf_prog(prog_ids[iter], attach_type_strings[type],
1502058b383SRoman Gushchin 			      attach_flags_str, level);
1515ccda64dSRoman Gushchin 
1525ccda64dSRoman Gushchin 	return 0;
1535ccda64dSRoman Gushchin }
1545ccda64dSRoman Gushchin 
15565b875bcSJakub Kicinski static int do_show(int argc, char **argv)
1565ccda64dSRoman Gushchin {
1575ccda64dSRoman Gushchin 	enum bpf_attach_type type;
1585ccda64dSRoman Gushchin 	int cgroup_fd;
1595ccda64dSRoman Gushchin 	int ret = -1;
1605ccda64dSRoman Gushchin 
1615ccda64dSRoman Gushchin 	if (argc < 1) {
162b4fac96dSJakub Kicinski 		p_err("too few parameters for cgroup show");
1635ccda64dSRoman Gushchin 		goto exit;
1645ccda64dSRoman Gushchin 	} else if (argc > 1) {
165b4fac96dSJakub Kicinski 		p_err("too many parameters for cgroup show");
1665ccda64dSRoman Gushchin 		goto exit;
1675ccda64dSRoman Gushchin 	}
1685ccda64dSRoman Gushchin 
1695ccda64dSRoman Gushchin 	cgroup_fd = open(argv[0], O_RDONLY);
1705ccda64dSRoman Gushchin 	if (cgroup_fd < 0) {
171b4fac96dSJakub Kicinski 		p_err("can't open cgroup %s", argv[1]);
1725ccda64dSRoman Gushchin 		goto exit;
1735ccda64dSRoman Gushchin 	}
1745ccda64dSRoman Gushchin 
1755ccda64dSRoman Gushchin 	if (json_output)
1765ccda64dSRoman Gushchin 		jsonw_start_array(json_wtr);
1775ccda64dSRoman Gushchin 	else
1785ccda64dSRoman Gushchin 		printf("%-8s %-15s %-15s %-15s\n", "ID", "AttachType",
1795ccda64dSRoman Gushchin 		       "AttachFlags", "Name");
1805ccda64dSRoman Gushchin 
1815ccda64dSRoman Gushchin 	for (type = 0; type < __MAX_BPF_ATTACH_TYPE; type++) {
1825ccda64dSRoman Gushchin 		/*
1835ccda64dSRoman Gushchin 		 * Not all attach types may be supported, so it's expected,
1845ccda64dSRoman Gushchin 		 * that some requests will fail.
18565b875bcSJakub Kicinski 		 * If we were able to get the show for at least one
1865ccda64dSRoman Gushchin 		 * attach type, let's return 0.
1875ccda64dSRoman Gushchin 		 */
1882058b383SRoman Gushchin 		if (show_attached_bpf_progs(cgroup_fd, type, 0) == 0)
1895ccda64dSRoman Gushchin 			ret = 0;
1905ccda64dSRoman Gushchin 	}
1915ccda64dSRoman Gushchin 
1925ccda64dSRoman Gushchin 	if (json_output)
1935ccda64dSRoman Gushchin 		jsonw_end_array(json_wtr);
1945ccda64dSRoman Gushchin 
1955ccda64dSRoman Gushchin 	close(cgroup_fd);
1965ccda64dSRoman Gushchin exit:
1975ccda64dSRoman Gushchin 	return ret;
1985ccda64dSRoman Gushchin }
1995ccda64dSRoman Gushchin 
2002058b383SRoman Gushchin /*
2012058b383SRoman Gushchin  * To distinguish nftw() errors and do_show_tree_fn() errors
2022058b383SRoman Gushchin  * and avoid duplicating error messages, let's return -2
2032058b383SRoman Gushchin  * from do_show_tree_fn() in case of error.
2042058b383SRoman Gushchin  */
2052058b383SRoman Gushchin #define NFTW_ERR		-1
2062058b383SRoman Gushchin #define SHOW_TREE_FN_ERR	-2
2072058b383SRoman Gushchin static int do_show_tree_fn(const char *fpath, const struct stat *sb,
2082058b383SRoman Gushchin 			   int typeflag, struct FTW *ftw)
2092058b383SRoman Gushchin {
2102058b383SRoman Gushchin 	enum bpf_attach_type type;
2112058b383SRoman Gushchin 	bool skip = true;
2122058b383SRoman Gushchin 	int cgroup_fd;
2132058b383SRoman Gushchin 
2142058b383SRoman Gushchin 	if (typeflag != FTW_D)
2152058b383SRoman Gushchin 		return 0;
2162058b383SRoman Gushchin 
2172058b383SRoman Gushchin 	cgroup_fd = open(fpath, O_RDONLY);
2182058b383SRoman Gushchin 	if (cgroup_fd < 0) {
2192058b383SRoman Gushchin 		p_err("can't open cgroup %s: %s", fpath, strerror(errno));
2202058b383SRoman Gushchin 		return SHOW_TREE_FN_ERR;
2212058b383SRoman Gushchin 	}
2222058b383SRoman Gushchin 
2232058b383SRoman Gushchin 	for (type = 0; type < __MAX_BPF_ATTACH_TYPE; type++) {
2242058b383SRoman Gushchin 		int count = count_attached_bpf_progs(cgroup_fd, type);
2252058b383SRoman Gushchin 
2262058b383SRoman Gushchin 		if (count < 0 && errno != EINVAL) {
2272058b383SRoman Gushchin 			p_err("can't query bpf programs attached to %s: %s",
2282058b383SRoman Gushchin 			      fpath, strerror(errno));
2292058b383SRoman Gushchin 			close(cgroup_fd);
2302058b383SRoman Gushchin 			return SHOW_TREE_FN_ERR;
2312058b383SRoman Gushchin 		}
2322058b383SRoman Gushchin 		if (count > 0) {
2332058b383SRoman Gushchin 			skip = false;
2342058b383SRoman Gushchin 			break;
2352058b383SRoman Gushchin 		}
2362058b383SRoman Gushchin 	}
2372058b383SRoman Gushchin 
2382058b383SRoman Gushchin 	if (skip) {
2392058b383SRoman Gushchin 		close(cgroup_fd);
2402058b383SRoman Gushchin 		return 0;
2412058b383SRoman Gushchin 	}
2422058b383SRoman Gushchin 
2432058b383SRoman Gushchin 	if (json_output) {
2442058b383SRoman Gushchin 		jsonw_start_object(json_wtr);
2452058b383SRoman Gushchin 		jsonw_string_field(json_wtr, "cgroup", fpath);
2462058b383SRoman Gushchin 		jsonw_name(json_wtr, "programs");
2472058b383SRoman Gushchin 		jsonw_start_array(json_wtr);
2482058b383SRoman Gushchin 	} else {
2492058b383SRoman Gushchin 		printf("%s\n", fpath);
2502058b383SRoman Gushchin 	}
2512058b383SRoman Gushchin 
2522058b383SRoman Gushchin 	for (type = 0; type < __MAX_BPF_ATTACH_TYPE; type++)
2532058b383SRoman Gushchin 		show_attached_bpf_progs(cgroup_fd, type, ftw->level);
2542058b383SRoman Gushchin 
25539c9f106SQuentin Monnet 	if (errno == EINVAL)
25639c9f106SQuentin Monnet 		/* Last attach type does not support query.
25739c9f106SQuentin Monnet 		 * Do not report an error for this, especially because batch
25839c9f106SQuentin Monnet 		 * mode would stop processing commands.
25939c9f106SQuentin Monnet 		 */
26039c9f106SQuentin Monnet 		errno = 0;
26139c9f106SQuentin Monnet 
2622058b383SRoman Gushchin 	if (json_output) {
2632058b383SRoman Gushchin 		jsonw_end_array(json_wtr);
2642058b383SRoman Gushchin 		jsonw_end_object(json_wtr);
2652058b383SRoman Gushchin 	}
2662058b383SRoman Gushchin 
2672058b383SRoman Gushchin 	close(cgroup_fd);
2682058b383SRoman Gushchin 
2692058b383SRoman Gushchin 	return 0;
2702058b383SRoman Gushchin }
2712058b383SRoman Gushchin 
2722058b383SRoman Gushchin static char *find_cgroup_root(void)
2732058b383SRoman Gushchin {
2742058b383SRoman Gushchin 	struct mntent *mnt;
2752058b383SRoman Gushchin 	FILE *f;
2762058b383SRoman Gushchin 
2772058b383SRoman Gushchin 	f = fopen("/proc/mounts", "r");
2782058b383SRoman Gushchin 	if (f == NULL)
2792058b383SRoman Gushchin 		return NULL;
2802058b383SRoman Gushchin 
2812058b383SRoman Gushchin 	while ((mnt = getmntent(f))) {
2822058b383SRoman Gushchin 		if (strcmp(mnt->mnt_type, "cgroup2") == 0) {
2832058b383SRoman Gushchin 			fclose(f);
2842058b383SRoman Gushchin 			return strdup(mnt->mnt_dir);
2852058b383SRoman Gushchin 		}
2862058b383SRoman Gushchin 	}
2872058b383SRoman Gushchin 
2882058b383SRoman Gushchin 	fclose(f);
2892058b383SRoman Gushchin 	return NULL;
2902058b383SRoman Gushchin }
2912058b383SRoman Gushchin 
2922058b383SRoman Gushchin static int do_show_tree(int argc, char **argv)
2932058b383SRoman Gushchin {
2942058b383SRoman Gushchin 	char *cgroup_root;
2952058b383SRoman Gushchin 	int ret;
2962058b383SRoman Gushchin 
2972058b383SRoman Gushchin 	switch (argc) {
2982058b383SRoman Gushchin 	case 0:
2992058b383SRoman Gushchin 		cgroup_root = find_cgroup_root();
3002058b383SRoman Gushchin 		if (!cgroup_root) {
3012058b383SRoman Gushchin 			p_err("cgroup v2 isn't mounted");
3022058b383SRoman Gushchin 			return -1;
3032058b383SRoman Gushchin 		}
3042058b383SRoman Gushchin 		break;
3052058b383SRoman Gushchin 	case 1:
3062058b383SRoman Gushchin 		cgroup_root = argv[0];
3072058b383SRoman Gushchin 		break;
3082058b383SRoman Gushchin 	default:
3092058b383SRoman Gushchin 		p_err("too many parameters for cgroup tree");
3102058b383SRoman Gushchin 		return -1;
3112058b383SRoman Gushchin 	}
3122058b383SRoman Gushchin 
3132058b383SRoman Gushchin 
3142058b383SRoman Gushchin 	if (json_output)
3152058b383SRoman Gushchin 		jsonw_start_array(json_wtr);
3162058b383SRoman Gushchin 	else
3172058b383SRoman Gushchin 		printf("%s\n"
3182058b383SRoman Gushchin 		       "%-8s %-15s %-15s %-15s\n",
3192058b383SRoman Gushchin 		       "CgroupPath",
3202058b383SRoman Gushchin 		       "ID", "AttachType", "AttachFlags", "Name");
3212058b383SRoman Gushchin 
3222058b383SRoman Gushchin 	switch (nftw(cgroup_root, do_show_tree_fn, 1024, FTW_MOUNT)) {
3232058b383SRoman Gushchin 	case NFTW_ERR:
3242058b383SRoman Gushchin 		p_err("can't iterate over %s: %s", cgroup_root,
3252058b383SRoman Gushchin 		      strerror(errno));
3262058b383SRoman Gushchin 		ret = -1;
3272058b383SRoman Gushchin 		break;
3282058b383SRoman Gushchin 	case SHOW_TREE_FN_ERR:
3292058b383SRoman Gushchin 		ret = -1;
3302058b383SRoman Gushchin 		break;
3312058b383SRoman Gushchin 	default:
3322058b383SRoman Gushchin 		ret = 0;
3332058b383SRoman Gushchin 	}
3342058b383SRoman Gushchin 
3352058b383SRoman Gushchin 	if (json_output)
3362058b383SRoman Gushchin 		jsonw_end_array(json_wtr);
3372058b383SRoman Gushchin 
3382058b383SRoman Gushchin 	if (argc == 0)
3392058b383SRoman Gushchin 		free(cgroup_root);
3402058b383SRoman Gushchin 
3412058b383SRoman Gushchin 	return ret;
3422058b383SRoman Gushchin }
3432058b383SRoman Gushchin 
3445ccda64dSRoman Gushchin static int do_attach(int argc, char **argv)
3455ccda64dSRoman Gushchin {
3465ccda64dSRoman Gushchin 	enum bpf_attach_type attach_type;
3475ccda64dSRoman Gushchin 	int cgroup_fd, prog_fd;
3485ccda64dSRoman Gushchin 	int attach_flags = 0;
3495ccda64dSRoman Gushchin 	int ret = -1;
3505ccda64dSRoman Gushchin 	int i;
3515ccda64dSRoman Gushchin 
3525ccda64dSRoman Gushchin 	if (argc < 4) {
353b4fac96dSJakub Kicinski 		p_err("too few parameters for cgroup attach");
3545ccda64dSRoman Gushchin 		goto exit;
3555ccda64dSRoman Gushchin 	}
3565ccda64dSRoman Gushchin 
3575ccda64dSRoman Gushchin 	cgroup_fd = open(argv[0], O_RDONLY);
3585ccda64dSRoman Gushchin 	if (cgroup_fd < 0) {
359b4fac96dSJakub Kicinski 		p_err("can't open cgroup %s", argv[1]);
3605ccda64dSRoman Gushchin 		goto exit;
3615ccda64dSRoman Gushchin 	}
3625ccda64dSRoman Gushchin 
3635ccda64dSRoman Gushchin 	attach_type = parse_attach_type(argv[1]);
3645ccda64dSRoman Gushchin 	if (attach_type == __MAX_BPF_ATTACH_TYPE) {
365b4fac96dSJakub Kicinski 		p_err("invalid attach type");
3665ccda64dSRoman Gushchin 		goto exit_cgroup;
3675ccda64dSRoman Gushchin 	}
3685ccda64dSRoman Gushchin 
3695ccda64dSRoman Gushchin 	argc -= 2;
3705ccda64dSRoman Gushchin 	argv = &argv[2];
3715ccda64dSRoman Gushchin 	prog_fd = prog_parse_fd(&argc, &argv);
3725ccda64dSRoman Gushchin 	if (prog_fd < 0)
3735ccda64dSRoman Gushchin 		goto exit_cgroup;
3745ccda64dSRoman Gushchin 
3755ccda64dSRoman Gushchin 	for (i = 0; i < argc; i++) {
3765ccda64dSRoman Gushchin 		if (is_prefix(argv[i], "multi")) {
3775ccda64dSRoman Gushchin 			attach_flags |= BPF_F_ALLOW_MULTI;
3785ccda64dSRoman Gushchin 		} else if (is_prefix(argv[i], "override")) {
3795ccda64dSRoman Gushchin 			attach_flags |= BPF_F_ALLOW_OVERRIDE;
3805ccda64dSRoman Gushchin 		} else {
381b4fac96dSJakub Kicinski 			p_err("unknown option: %s", argv[i]);
3825ccda64dSRoman Gushchin 			goto exit_cgroup;
3835ccda64dSRoman Gushchin 		}
3845ccda64dSRoman Gushchin 	}
3855ccda64dSRoman Gushchin 
3865ccda64dSRoman Gushchin 	if (bpf_prog_attach(prog_fd, cgroup_fd, attach_type, attach_flags)) {
3875ccda64dSRoman Gushchin 		p_err("failed to attach program");
3885ccda64dSRoman Gushchin 		goto exit_prog;
3895ccda64dSRoman Gushchin 	}
3905ccda64dSRoman Gushchin 
3915ccda64dSRoman Gushchin 	if (json_output)
3925ccda64dSRoman Gushchin 		jsonw_null(json_wtr);
3935ccda64dSRoman Gushchin 
3945ccda64dSRoman Gushchin 	ret = 0;
3955ccda64dSRoman Gushchin 
3965ccda64dSRoman Gushchin exit_prog:
3975ccda64dSRoman Gushchin 	close(prog_fd);
3985ccda64dSRoman Gushchin exit_cgroup:
3995ccda64dSRoman Gushchin 	close(cgroup_fd);
4005ccda64dSRoman Gushchin exit:
4015ccda64dSRoman Gushchin 	return ret;
4025ccda64dSRoman Gushchin }
4035ccda64dSRoman Gushchin 
4045ccda64dSRoman Gushchin static int do_detach(int argc, char **argv)
4055ccda64dSRoman Gushchin {
4065ccda64dSRoman Gushchin 	enum bpf_attach_type attach_type;
4075ccda64dSRoman Gushchin 	int prog_fd, cgroup_fd;
4085ccda64dSRoman Gushchin 	int ret = -1;
4095ccda64dSRoman Gushchin 
4105ccda64dSRoman Gushchin 	if (argc < 4) {
411b4fac96dSJakub Kicinski 		p_err("too few parameters for cgroup detach");
4125ccda64dSRoman Gushchin 		goto exit;
4135ccda64dSRoman Gushchin 	}
4145ccda64dSRoman Gushchin 
4155ccda64dSRoman Gushchin 	cgroup_fd = open(argv[0], O_RDONLY);
4165ccda64dSRoman Gushchin 	if (cgroup_fd < 0) {
417b4fac96dSJakub Kicinski 		p_err("can't open cgroup %s", argv[1]);
4185ccda64dSRoman Gushchin 		goto exit;
4195ccda64dSRoman Gushchin 	}
4205ccda64dSRoman Gushchin 
4215ccda64dSRoman Gushchin 	attach_type = parse_attach_type(argv[1]);
4225ccda64dSRoman Gushchin 	if (attach_type == __MAX_BPF_ATTACH_TYPE) {
4235ccda64dSRoman Gushchin 		p_err("invalid attach type");
4245ccda64dSRoman Gushchin 		goto exit_cgroup;
4255ccda64dSRoman Gushchin 	}
4265ccda64dSRoman Gushchin 
4275ccda64dSRoman Gushchin 	argc -= 2;
4285ccda64dSRoman Gushchin 	argv = &argv[2];
4295ccda64dSRoman Gushchin 	prog_fd = prog_parse_fd(&argc, &argv);
4305ccda64dSRoman Gushchin 	if (prog_fd < 0)
4315ccda64dSRoman Gushchin 		goto exit_cgroup;
4325ccda64dSRoman Gushchin 
4335ccda64dSRoman Gushchin 	if (bpf_prog_detach2(prog_fd, cgroup_fd, attach_type)) {
4345ccda64dSRoman Gushchin 		p_err("failed to detach program");
4355ccda64dSRoman Gushchin 		goto exit_prog;
4365ccda64dSRoman Gushchin 	}
4375ccda64dSRoman Gushchin 
4385ccda64dSRoman Gushchin 	if (json_output)
4395ccda64dSRoman Gushchin 		jsonw_null(json_wtr);
4405ccda64dSRoman Gushchin 
4415ccda64dSRoman Gushchin 	ret = 0;
4425ccda64dSRoman Gushchin 
4435ccda64dSRoman Gushchin exit_prog:
4445ccda64dSRoman Gushchin 	close(prog_fd);
4455ccda64dSRoman Gushchin exit_cgroup:
4465ccda64dSRoman Gushchin 	close(cgroup_fd);
4475ccda64dSRoman Gushchin exit:
4485ccda64dSRoman Gushchin 	return ret;
4495ccda64dSRoman Gushchin }
4505ccda64dSRoman Gushchin 
4515ccda64dSRoman Gushchin static int do_help(int argc, char **argv)
4525ccda64dSRoman Gushchin {
4535ccda64dSRoman Gushchin 	if (json_output) {
4545ccda64dSRoman Gushchin 		jsonw_null(json_wtr);
4555ccda64dSRoman Gushchin 		return 0;
4565ccda64dSRoman Gushchin 	}
4575ccda64dSRoman Gushchin 
4585ccda64dSRoman Gushchin 	fprintf(stderr,
4596ebe6dbdSJakub Kicinski 		"Usage: %s %s { show | list } CGROUP\n"
4602058b383SRoman Gushchin 		"       %s %s tree [CGROUP_ROOT]\n"
4615ccda64dSRoman Gushchin 		"       %s %s attach CGROUP ATTACH_TYPE PROG [ATTACH_FLAGS]\n"
4625ccda64dSRoman Gushchin 		"       %s %s detach CGROUP ATTACH_TYPE PROG\n"
4635ccda64dSRoman Gushchin 		"       %s %s help\n"
4645ccda64dSRoman Gushchin 		"\n"
465393de512SAndrey Ignatov 		HELP_SPEC_ATTACH_TYPES "\n"
4665ccda64dSRoman Gushchin 		"       " HELP_SPEC_ATTACH_FLAGS "\n"
4675ccda64dSRoman Gushchin 		"       " HELP_SPEC_PROGRAM "\n"
4685ccda64dSRoman Gushchin 		"       " HELP_SPEC_OPTIONS "\n"
4695ccda64dSRoman Gushchin 		"",
4702058b383SRoman Gushchin 		bin_name, argv[-2],
4715ccda64dSRoman Gushchin 		bin_name, argv[-2], bin_name, argv[-2],
4725ccda64dSRoman Gushchin 		bin_name, argv[-2], bin_name, argv[-2]);
4735ccda64dSRoman Gushchin 
4745ccda64dSRoman Gushchin 	return 0;
4755ccda64dSRoman Gushchin }
4765ccda64dSRoman Gushchin 
4775ccda64dSRoman Gushchin static const struct cmd cmds[] = {
4786ebe6dbdSJakub Kicinski 	{ "show",	do_show },
47965b875bcSJakub Kicinski 	{ "list",	do_show },
4802058b383SRoman Gushchin 	{ "tree",       do_show_tree },
4815ccda64dSRoman Gushchin 	{ "attach",	do_attach },
4825ccda64dSRoman Gushchin 	{ "detach",	do_detach },
4835ccda64dSRoman Gushchin 	{ "help",	do_help },
4845ccda64dSRoman Gushchin 	{ 0 }
4855ccda64dSRoman Gushchin };
4865ccda64dSRoman Gushchin 
4875ccda64dSRoman Gushchin int do_cgroup(int argc, char **argv)
4885ccda64dSRoman Gushchin {
4895ccda64dSRoman Gushchin 	return cmd_select(cmds, argc, argv, do_help);
4905ccda64dSRoman Gushchin }
491