xref: /openbmc/linux/tools/bpf/bpftool/cgroup.c (revision 8cc8c635)
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>
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"       \
2805ee19c1SDaniel Borkmann 	"                        connect6 | getpeername4 | getpeername6 |\n"   \
2905ee19c1SDaniel Borkmann 	"                        getsockname4 | getsockname6 | sendmsg4 |\n"   \
3005ee19c1SDaniel Borkmann 	"                        sendmsg6 | recvmsg4 | recvmsg6 |\n"           \
31a8deba85SLiu Jian 	"                        sysctl | getsockopt | setsockopt |\n"	       \
32a8deba85SLiu Jian 	"                        sock_release }"
335ccda64dSRoman Gushchin 
34a98bf573SJakub Kicinski static unsigned int query_flags;
35a98bf573SJakub Kicinski 
365ccda64dSRoman Gushchin static enum bpf_attach_type parse_attach_type(const char *str)
375ccda64dSRoman Gushchin {
385ccda64dSRoman Gushchin 	enum bpf_attach_type type;
395ccda64dSRoman Gushchin 
405ccda64dSRoman Gushchin 	for (type = 0; type < __MAX_BPF_ATTACH_TYPE; type++) {
4150325b17SAndrii Nakryiko 		if (attach_type_name[type] &&
4250325b17SAndrii Nakryiko 		    is_prefix(str, attach_type_name[type]))
435ccda64dSRoman Gushchin 			return type;
445ccda64dSRoman Gushchin 	}
455ccda64dSRoman Gushchin 
465ccda64dSRoman Gushchin 	return __MAX_BPF_ATTACH_TYPE;
475ccda64dSRoman Gushchin }
485ccda64dSRoman Gushchin 
4950325b17SAndrii Nakryiko static int show_bpf_prog(int id, enum bpf_attach_type attach_type,
502058b383SRoman Gushchin 			 const char *attach_flags_str,
512058b383SRoman Gushchin 			 int level)
525ccda64dSRoman Gushchin {
535ccda64dSRoman Gushchin 	struct bpf_prog_info info = {};
545ccda64dSRoman Gushchin 	__u32 info_len = sizeof(info);
555ccda64dSRoman Gushchin 	int prog_fd;
565ccda64dSRoman Gushchin 
575ccda64dSRoman Gushchin 	prog_fd = bpf_prog_get_fd_by_id(id);
585ccda64dSRoman Gushchin 	if (prog_fd < 0)
595ccda64dSRoman Gushchin 		return -1;
605ccda64dSRoman Gushchin 
615ccda64dSRoman Gushchin 	if (bpf_obj_get_info_by_fd(prog_fd, &info, &info_len)) {
625ccda64dSRoman Gushchin 		close(prog_fd);
635ccda64dSRoman Gushchin 		return -1;
645ccda64dSRoman Gushchin 	}
655ccda64dSRoman Gushchin 
665ccda64dSRoman Gushchin 	if (json_output) {
675ccda64dSRoman Gushchin 		jsonw_start_object(json_wtr);
685ccda64dSRoman Gushchin 		jsonw_uint_field(json_wtr, "id", info.id);
6950325b17SAndrii Nakryiko 		if (attach_type < ARRAY_SIZE(attach_type_name))
705ccda64dSRoman Gushchin 			jsonw_string_field(json_wtr, "attach_type",
7150325b17SAndrii Nakryiko 					   attach_type_name[attach_type]);
7250325b17SAndrii Nakryiko 		else
7350325b17SAndrii Nakryiko 			jsonw_uint_field(json_wtr, "attach_type", attach_type);
745ccda64dSRoman Gushchin 		jsonw_string_field(json_wtr, "attach_flags",
755ccda64dSRoman Gushchin 				   attach_flags_str);
765ccda64dSRoman Gushchin 		jsonw_string_field(json_wtr, "name", info.name);
775ccda64dSRoman Gushchin 		jsonw_end_object(json_wtr);
785ccda64dSRoman Gushchin 	} else {
7950325b17SAndrii Nakryiko 		printf("%s%-8u ", level ? "    " : "", info.id);
8050325b17SAndrii Nakryiko 		if (attach_type < ARRAY_SIZE(attach_type_name))
8150325b17SAndrii Nakryiko 			printf("%-15s", attach_type_name[attach_type]);
8250325b17SAndrii Nakryiko 		else
8350325b17SAndrii Nakryiko 			printf("type %-10u", attach_type);
8450325b17SAndrii Nakryiko 		printf(" %-15s %-15s\n", attach_flags_str, info.name);
855ccda64dSRoman Gushchin 	}
865ccda64dSRoman Gushchin 
875ccda64dSRoman Gushchin 	close(prog_fd);
885ccda64dSRoman Gushchin 	return 0;
895ccda64dSRoman Gushchin }
905ccda64dSRoman Gushchin 
912058b383SRoman Gushchin static int count_attached_bpf_progs(int cgroup_fd, enum bpf_attach_type type)
922058b383SRoman Gushchin {
932058b383SRoman Gushchin 	__u32 prog_cnt = 0;
942058b383SRoman Gushchin 	int ret;
952058b383SRoman Gushchin 
96a98bf573SJakub Kicinski 	ret = bpf_prog_query(cgroup_fd, type, query_flags, NULL,
97a98bf573SJakub Kicinski 			     NULL, &prog_cnt);
982058b383SRoman Gushchin 	if (ret)
992058b383SRoman Gushchin 		return -1;
1002058b383SRoman Gushchin 
1012058b383SRoman Gushchin 	return prog_cnt;
1022058b383SRoman Gushchin }
1032058b383SRoman Gushchin 
1041162f844SHechao Li static int cgroup_has_attached_progs(int cgroup_fd)
1051162f844SHechao Li {
1061162f844SHechao Li 	enum bpf_attach_type type;
1071162f844SHechao Li 	bool no_prog = true;
1081162f844SHechao Li 
1091162f844SHechao Li 	for (type = 0; type < __MAX_BPF_ATTACH_TYPE; type++) {
1101162f844SHechao Li 		int count = count_attached_bpf_progs(cgroup_fd, type);
1111162f844SHechao Li 
1121162f844SHechao Li 		if (count < 0 && errno != EINVAL)
1131162f844SHechao Li 			return -1;
1141162f844SHechao Li 
1151162f844SHechao Li 		if (count > 0) {
1161162f844SHechao Li 			no_prog = false;
1171162f844SHechao Li 			break;
1181162f844SHechao Li 		}
1191162f844SHechao Li 	}
1201162f844SHechao Li 
1211162f844SHechao Li 	return no_prog ? 0 : 1;
1221162f844SHechao Li }
1232058b383SRoman Gushchin static int show_attached_bpf_progs(int cgroup_fd, enum bpf_attach_type type,
1242058b383SRoman Gushchin 				   int level)
1255ccda64dSRoman Gushchin {
126a9436dcaSQuentin Monnet 	const char *attach_flags_str;
1275ccda64dSRoman Gushchin 	__u32 prog_ids[1024] = {0};
1285ccda64dSRoman Gushchin 	__u32 prog_cnt, iter;
1295ccda64dSRoman Gushchin 	__u32 attach_flags;
1305ccda64dSRoman Gushchin 	char buf[32];
1315ccda64dSRoman Gushchin 	int ret;
1325ccda64dSRoman Gushchin 
1335ccda64dSRoman Gushchin 	prog_cnt = ARRAY_SIZE(prog_ids);
134a98bf573SJakub Kicinski 	ret = bpf_prog_query(cgroup_fd, type, query_flags, &attach_flags,
135a98bf573SJakub Kicinski 			     prog_ids, &prog_cnt);
1365ccda64dSRoman Gushchin 	if (ret)
1375ccda64dSRoman Gushchin 		return ret;
1385ccda64dSRoman Gushchin 
1395ccda64dSRoman Gushchin 	if (prog_cnt == 0)
1405ccda64dSRoman Gushchin 		return 0;
1415ccda64dSRoman Gushchin 
1425ccda64dSRoman Gushchin 	switch (attach_flags) {
1435ccda64dSRoman Gushchin 	case BPF_F_ALLOW_MULTI:
1445ccda64dSRoman Gushchin 		attach_flags_str = "multi";
1455ccda64dSRoman Gushchin 		break;
1465ccda64dSRoman Gushchin 	case BPF_F_ALLOW_OVERRIDE:
1475ccda64dSRoman Gushchin 		attach_flags_str = "override";
1485ccda64dSRoman Gushchin 		break;
1495ccda64dSRoman Gushchin 	case 0:
1505ccda64dSRoman Gushchin 		attach_flags_str = "";
1515ccda64dSRoman Gushchin 		break;
1525ccda64dSRoman Gushchin 	default:
1535ccda64dSRoman Gushchin 		snprintf(buf, sizeof(buf), "unknown(%x)", attach_flags);
1545ccda64dSRoman Gushchin 		attach_flags_str = buf;
1555ccda64dSRoman Gushchin 	}
1565ccda64dSRoman Gushchin 
1575ccda64dSRoman Gushchin 	for (iter = 0; iter < prog_cnt; iter++)
15850325b17SAndrii Nakryiko 		show_bpf_prog(prog_ids[iter], type,
1592058b383SRoman Gushchin 			      attach_flags_str, level);
1605ccda64dSRoman Gushchin 
1615ccda64dSRoman Gushchin 	return 0;
1625ccda64dSRoman Gushchin }
1635ccda64dSRoman Gushchin 
16465b875bcSJakub Kicinski static int do_show(int argc, char **argv)
1655ccda64dSRoman Gushchin {
1665ccda64dSRoman Gushchin 	enum bpf_attach_type type;
1671162f844SHechao Li 	int has_attached_progs;
168a98bf573SJakub Kicinski 	const char *path;
1695ccda64dSRoman Gushchin 	int cgroup_fd;
1705ccda64dSRoman Gushchin 	int ret = -1;
1715ccda64dSRoman Gushchin 
172a98bf573SJakub Kicinski 	query_flags = 0;
173a98bf573SJakub Kicinski 
174a98bf573SJakub Kicinski 	if (!REQ_ARGS(1))
175a98bf573SJakub Kicinski 		return -1;
176a98bf573SJakub Kicinski 	path = GET_ARG();
177a98bf573SJakub Kicinski 
178a98bf573SJakub Kicinski 	while (argc) {
179a98bf573SJakub Kicinski 		if (is_prefix(*argv, "effective")) {
180a98bf573SJakub Kicinski 			if (query_flags & BPF_F_QUERY_EFFECTIVE) {
181a98bf573SJakub Kicinski 				p_err("duplicated argument: %s", *argv);
182a98bf573SJakub Kicinski 				return -1;
183a98bf573SJakub Kicinski 			}
184a98bf573SJakub Kicinski 			query_flags |= BPF_F_QUERY_EFFECTIVE;
185a98bf573SJakub Kicinski 			NEXT_ARG();
186a98bf573SJakub Kicinski 		} else {
187a98bf573SJakub Kicinski 			p_err("expected no more arguments, 'effective', got: '%s'?",
188a98bf573SJakub Kicinski 			      *argv);
189a98bf573SJakub Kicinski 			return -1;
190a98bf573SJakub Kicinski 		}
1915ccda64dSRoman Gushchin 	}
1925ccda64dSRoman Gushchin 
193a98bf573SJakub Kicinski 	cgroup_fd = open(path, O_RDONLY);
1945ccda64dSRoman Gushchin 	if (cgroup_fd < 0) {
195a98bf573SJakub Kicinski 		p_err("can't open cgroup %s", path);
1965ccda64dSRoman Gushchin 		goto exit;
1975ccda64dSRoman Gushchin 	}
1985ccda64dSRoman Gushchin 
1991162f844SHechao Li 	has_attached_progs = cgroup_has_attached_progs(cgroup_fd);
2001162f844SHechao Li 	if (has_attached_progs < 0) {
2011162f844SHechao Li 		p_err("can't query bpf programs attached to %s: %s",
2021162f844SHechao Li 		      path, strerror(errno));
2031162f844SHechao Li 		goto exit_cgroup;
2041162f844SHechao Li 	} else if (!has_attached_progs) {
2051162f844SHechao Li 		ret = 0;
2061162f844SHechao Li 		goto exit_cgroup;
2071162f844SHechao Li 	}
2081162f844SHechao Li 
2095ccda64dSRoman Gushchin 	if (json_output)
2105ccda64dSRoman Gushchin 		jsonw_start_array(json_wtr);
2115ccda64dSRoman Gushchin 	else
2125ccda64dSRoman Gushchin 		printf("%-8s %-15s %-15s %-15s\n", "ID", "AttachType",
2135ccda64dSRoman Gushchin 		       "AttachFlags", "Name");
2145ccda64dSRoman Gushchin 
2155ccda64dSRoman Gushchin 	for (type = 0; type < __MAX_BPF_ATTACH_TYPE; type++) {
2165ccda64dSRoman Gushchin 		/*
2175ccda64dSRoman Gushchin 		 * Not all attach types may be supported, so it's expected,
2185ccda64dSRoman Gushchin 		 * that some requests will fail.
21965b875bcSJakub Kicinski 		 * If we were able to get the show for at least one
2205ccda64dSRoman Gushchin 		 * attach type, let's return 0.
2215ccda64dSRoman Gushchin 		 */
2222058b383SRoman Gushchin 		if (show_attached_bpf_progs(cgroup_fd, type, 0) == 0)
2235ccda64dSRoman Gushchin 			ret = 0;
2245ccda64dSRoman Gushchin 	}
2255ccda64dSRoman Gushchin 
2265ccda64dSRoman Gushchin 	if (json_output)
2275ccda64dSRoman Gushchin 		jsonw_end_array(json_wtr);
2285ccda64dSRoman Gushchin 
2291162f844SHechao Li exit_cgroup:
2305ccda64dSRoman Gushchin 	close(cgroup_fd);
2315ccda64dSRoman Gushchin exit:
2325ccda64dSRoman Gushchin 	return ret;
2335ccda64dSRoman Gushchin }
2345ccda64dSRoman Gushchin 
2352058b383SRoman Gushchin /*
2362058b383SRoman Gushchin  * To distinguish nftw() errors and do_show_tree_fn() errors
2372058b383SRoman Gushchin  * and avoid duplicating error messages, let's return -2
2382058b383SRoman Gushchin  * from do_show_tree_fn() in case of error.
2392058b383SRoman Gushchin  */
2402058b383SRoman Gushchin #define NFTW_ERR		-1
2412058b383SRoman Gushchin #define SHOW_TREE_FN_ERR	-2
2422058b383SRoman Gushchin static int do_show_tree_fn(const char *fpath, const struct stat *sb,
2432058b383SRoman Gushchin 			   int typeflag, struct FTW *ftw)
2442058b383SRoman Gushchin {
2452058b383SRoman Gushchin 	enum bpf_attach_type type;
2461162f844SHechao Li 	int has_attached_progs;
2472058b383SRoman Gushchin 	int cgroup_fd;
2482058b383SRoman Gushchin 
2492058b383SRoman Gushchin 	if (typeflag != FTW_D)
2502058b383SRoman Gushchin 		return 0;
2512058b383SRoman Gushchin 
2522058b383SRoman Gushchin 	cgroup_fd = open(fpath, O_RDONLY);
2532058b383SRoman Gushchin 	if (cgroup_fd < 0) {
2542058b383SRoman Gushchin 		p_err("can't open cgroup %s: %s", fpath, strerror(errno));
2552058b383SRoman Gushchin 		return SHOW_TREE_FN_ERR;
2562058b383SRoman Gushchin 	}
2572058b383SRoman Gushchin 
2581162f844SHechao Li 	has_attached_progs = cgroup_has_attached_progs(cgroup_fd);
2591162f844SHechao Li 	if (has_attached_progs < 0) {
2602058b383SRoman Gushchin 		p_err("can't query bpf programs attached to %s: %s",
2612058b383SRoman Gushchin 		      fpath, strerror(errno));
2622058b383SRoman Gushchin 		close(cgroup_fd);
2632058b383SRoman Gushchin 		return SHOW_TREE_FN_ERR;
2641162f844SHechao Li 	} else if (!has_attached_progs) {
2652058b383SRoman Gushchin 		close(cgroup_fd);
2662058b383SRoman Gushchin 		return 0;
2672058b383SRoman Gushchin 	}
2682058b383SRoman Gushchin 
2692058b383SRoman Gushchin 	if (json_output) {
2702058b383SRoman Gushchin 		jsonw_start_object(json_wtr);
2712058b383SRoman Gushchin 		jsonw_string_field(json_wtr, "cgroup", fpath);
2722058b383SRoman Gushchin 		jsonw_name(json_wtr, "programs");
2732058b383SRoman Gushchin 		jsonw_start_array(json_wtr);
2742058b383SRoman Gushchin 	} else {
2752058b383SRoman Gushchin 		printf("%s\n", fpath);
2762058b383SRoman Gushchin 	}
2772058b383SRoman Gushchin 
2782058b383SRoman Gushchin 	for (type = 0; type < __MAX_BPF_ATTACH_TYPE; type++)
2792058b383SRoman Gushchin 		show_attached_bpf_progs(cgroup_fd, type, ftw->level);
2802058b383SRoman Gushchin 
28139c9f106SQuentin Monnet 	if (errno == EINVAL)
28239c9f106SQuentin Monnet 		/* Last attach type does not support query.
28339c9f106SQuentin Monnet 		 * Do not report an error for this, especially because batch
28439c9f106SQuentin Monnet 		 * mode would stop processing commands.
28539c9f106SQuentin Monnet 		 */
28639c9f106SQuentin Monnet 		errno = 0;
28739c9f106SQuentin Monnet 
2882058b383SRoman Gushchin 	if (json_output) {
2892058b383SRoman Gushchin 		jsonw_end_array(json_wtr);
2902058b383SRoman Gushchin 		jsonw_end_object(json_wtr);
2912058b383SRoman Gushchin 	}
2922058b383SRoman Gushchin 
2932058b383SRoman Gushchin 	close(cgroup_fd);
2942058b383SRoman Gushchin 
2952058b383SRoman Gushchin 	return 0;
2962058b383SRoman Gushchin }
2972058b383SRoman Gushchin 
2982058b383SRoman Gushchin static char *find_cgroup_root(void)
2992058b383SRoman Gushchin {
3002058b383SRoman Gushchin 	struct mntent *mnt;
3012058b383SRoman Gushchin 	FILE *f;
3022058b383SRoman Gushchin 
3032058b383SRoman Gushchin 	f = fopen("/proc/mounts", "r");
3042058b383SRoman Gushchin 	if (f == NULL)
3052058b383SRoman Gushchin 		return NULL;
3062058b383SRoman Gushchin 
3072058b383SRoman Gushchin 	while ((mnt = getmntent(f))) {
3082058b383SRoman Gushchin 		if (strcmp(mnt->mnt_type, "cgroup2") == 0) {
3092058b383SRoman Gushchin 			fclose(f);
3102058b383SRoman Gushchin 			return strdup(mnt->mnt_dir);
3112058b383SRoman Gushchin 		}
3122058b383SRoman Gushchin 	}
3132058b383SRoman Gushchin 
3142058b383SRoman Gushchin 	fclose(f);
3152058b383SRoman Gushchin 	return NULL;
3162058b383SRoman Gushchin }
3172058b383SRoman Gushchin 
3182058b383SRoman Gushchin static int do_show_tree(int argc, char **argv)
3192058b383SRoman Gushchin {
320a98bf573SJakub Kicinski 	char *cgroup_root, *cgroup_alloced = NULL;
3212058b383SRoman Gushchin 	int ret;
3222058b383SRoman Gushchin 
323a98bf573SJakub Kicinski 	query_flags = 0;
324a98bf573SJakub Kicinski 
325a98bf573SJakub Kicinski 	if (!argc) {
326a98bf573SJakub Kicinski 		cgroup_alloced = find_cgroup_root();
327a98bf573SJakub Kicinski 		if (!cgroup_alloced) {
3282058b383SRoman Gushchin 			p_err("cgroup v2 isn't mounted");
3292058b383SRoman Gushchin 			return -1;
3302058b383SRoman Gushchin 		}
331a98bf573SJakub Kicinski 		cgroup_root = cgroup_alloced;
332a98bf573SJakub Kicinski 	} else {
333a98bf573SJakub Kicinski 		cgroup_root = GET_ARG();
334a98bf573SJakub Kicinski 
335a98bf573SJakub Kicinski 		while (argc) {
336a98bf573SJakub Kicinski 			if (is_prefix(*argv, "effective")) {
337a98bf573SJakub Kicinski 				if (query_flags & BPF_F_QUERY_EFFECTIVE) {
338a98bf573SJakub Kicinski 					p_err("duplicated argument: %s", *argv);
3392058b383SRoman Gushchin 					return -1;
3402058b383SRoman Gushchin 				}
341a98bf573SJakub Kicinski 				query_flags |= BPF_F_QUERY_EFFECTIVE;
342a98bf573SJakub Kicinski 				NEXT_ARG();
343a98bf573SJakub Kicinski 			} else {
344a98bf573SJakub Kicinski 				p_err("expected no more arguments, 'effective', got: '%s'?",
345a98bf573SJakub Kicinski 				      *argv);
346a98bf573SJakub Kicinski 				return -1;
347a98bf573SJakub Kicinski 			}
348a98bf573SJakub Kicinski 		}
349a98bf573SJakub Kicinski 	}
3502058b383SRoman Gushchin 
3512058b383SRoman Gushchin 	if (json_output)
3522058b383SRoman Gushchin 		jsonw_start_array(json_wtr);
3532058b383SRoman Gushchin 	else
3542058b383SRoman Gushchin 		printf("%s\n"
3552058b383SRoman Gushchin 		       "%-8s %-15s %-15s %-15s\n",
3562058b383SRoman Gushchin 		       "CgroupPath",
3572058b383SRoman Gushchin 		       "ID", "AttachType", "AttachFlags", "Name");
3582058b383SRoman Gushchin 
3592058b383SRoman Gushchin 	switch (nftw(cgroup_root, do_show_tree_fn, 1024, FTW_MOUNT)) {
3602058b383SRoman Gushchin 	case NFTW_ERR:
3612058b383SRoman Gushchin 		p_err("can't iterate over %s: %s", cgroup_root,
3622058b383SRoman Gushchin 		      strerror(errno));
3632058b383SRoman Gushchin 		ret = -1;
3642058b383SRoman Gushchin 		break;
3652058b383SRoman Gushchin 	case SHOW_TREE_FN_ERR:
3662058b383SRoman Gushchin 		ret = -1;
3672058b383SRoman Gushchin 		break;
3682058b383SRoman Gushchin 	default:
3692058b383SRoman Gushchin 		ret = 0;
3702058b383SRoman Gushchin 	}
3712058b383SRoman Gushchin 
3722058b383SRoman Gushchin 	if (json_output)
3732058b383SRoman Gushchin 		jsonw_end_array(json_wtr);
3742058b383SRoman Gushchin 
375a98bf573SJakub Kicinski 	free(cgroup_alloced);
3762058b383SRoman Gushchin 
3772058b383SRoman Gushchin 	return ret;
3782058b383SRoman Gushchin }
3792058b383SRoman Gushchin 
3805ccda64dSRoman Gushchin static int do_attach(int argc, char **argv)
3815ccda64dSRoman Gushchin {
3825ccda64dSRoman Gushchin 	enum bpf_attach_type attach_type;
3835ccda64dSRoman Gushchin 	int cgroup_fd, prog_fd;
3845ccda64dSRoman Gushchin 	int attach_flags = 0;
3855ccda64dSRoman Gushchin 	int ret = -1;
3865ccda64dSRoman Gushchin 	int i;
3875ccda64dSRoman Gushchin 
3885ccda64dSRoman Gushchin 	if (argc < 4) {
389b4fac96dSJakub Kicinski 		p_err("too few parameters for cgroup attach");
3905ccda64dSRoman Gushchin 		goto exit;
3915ccda64dSRoman Gushchin 	}
3925ccda64dSRoman Gushchin 
3935ccda64dSRoman Gushchin 	cgroup_fd = open(argv[0], O_RDONLY);
3945ccda64dSRoman Gushchin 	if (cgroup_fd < 0) {
3956c6874f4SJakub Kicinski 		p_err("can't open cgroup %s", argv[0]);
3965ccda64dSRoman Gushchin 		goto exit;
3975ccda64dSRoman Gushchin 	}
3985ccda64dSRoman Gushchin 
3995ccda64dSRoman Gushchin 	attach_type = parse_attach_type(argv[1]);
4005ccda64dSRoman Gushchin 	if (attach_type == __MAX_BPF_ATTACH_TYPE) {
401b4fac96dSJakub Kicinski 		p_err("invalid attach type");
4025ccda64dSRoman Gushchin 		goto exit_cgroup;
4035ccda64dSRoman Gushchin 	}
4045ccda64dSRoman Gushchin 
4055ccda64dSRoman Gushchin 	argc -= 2;
4065ccda64dSRoman Gushchin 	argv = &argv[2];
4075ccda64dSRoman Gushchin 	prog_fd = prog_parse_fd(&argc, &argv);
4085ccda64dSRoman Gushchin 	if (prog_fd < 0)
4095ccda64dSRoman Gushchin 		goto exit_cgroup;
4105ccda64dSRoman Gushchin 
4115ccda64dSRoman Gushchin 	for (i = 0; i < argc; i++) {
4125ccda64dSRoman Gushchin 		if (is_prefix(argv[i], "multi")) {
4135ccda64dSRoman Gushchin 			attach_flags |= BPF_F_ALLOW_MULTI;
4145ccda64dSRoman Gushchin 		} else if (is_prefix(argv[i], "override")) {
4155ccda64dSRoman Gushchin 			attach_flags |= BPF_F_ALLOW_OVERRIDE;
4165ccda64dSRoman Gushchin 		} else {
417b4fac96dSJakub Kicinski 			p_err("unknown option: %s", argv[i]);
4185ccda64dSRoman Gushchin 			goto exit_cgroup;
4195ccda64dSRoman Gushchin 		}
4205ccda64dSRoman Gushchin 	}
4215ccda64dSRoman Gushchin 
4225ccda64dSRoman Gushchin 	if (bpf_prog_attach(prog_fd, cgroup_fd, attach_type, attach_flags)) {
4235ccda64dSRoman Gushchin 		p_err("failed to attach program");
4245ccda64dSRoman Gushchin 		goto exit_prog;
4255ccda64dSRoman Gushchin 	}
4265ccda64dSRoman Gushchin 
4275ccda64dSRoman Gushchin 	if (json_output)
4285ccda64dSRoman Gushchin 		jsonw_null(json_wtr);
4295ccda64dSRoman Gushchin 
4305ccda64dSRoman Gushchin 	ret = 0;
4315ccda64dSRoman Gushchin 
4325ccda64dSRoman Gushchin exit_prog:
4335ccda64dSRoman Gushchin 	close(prog_fd);
4345ccda64dSRoman Gushchin exit_cgroup:
4355ccda64dSRoman Gushchin 	close(cgroup_fd);
4365ccda64dSRoman Gushchin exit:
4375ccda64dSRoman Gushchin 	return ret;
4385ccda64dSRoman Gushchin }
4395ccda64dSRoman Gushchin 
4405ccda64dSRoman Gushchin static int do_detach(int argc, char **argv)
4415ccda64dSRoman Gushchin {
4425ccda64dSRoman Gushchin 	enum bpf_attach_type attach_type;
4435ccda64dSRoman Gushchin 	int prog_fd, cgroup_fd;
4445ccda64dSRoman Gushchin 	int ret = -1;
4455ccda64dSRoman Gushchin 
4465ccda64dSRoman Gushchin 	if (argc < 4) {
447b4fac96dSJakub Kicinski 		p_err("too few parameters for cgroup detach");
4485ccda64dSRoman Gushchin 		goto exit;
4495ccda64dSRoman Gushchin 	}
4505ccda64dSRoman Gushchin 
4515ccda64dSRoman Gushchin 	cgroup_fd = open(argv[0], O_RDONLY);
4525ccda64dSRoman Gushchin 	if (cgroup_fd < 0) {
4536c6874f4SJakub Kicinski 		p_err("can't open cgroup %s", argv[0]);
4545ccda64dSRoman Gushchin 		goto exit;
4555ccda64dSRoman Gushchin 	}
4565ccda64dSRoman Gushchin 
4575ccda64dSRoman Gushchin 	attach_type = parse_attach_type(argv[1]);
4585ccda64dSRoman Gushchin 	if (attach_type == __MAX_BPF_ATTACH_TYPE) {
4595ccda64dSRoman Gushchin 		p_err("invalid attach type");
4605ccda64dSRoman Gushchin 		goto exit_cgroup;
4615ccda64dSRoman Gushchin 	}
4625ccda64dSRoman Gushchin 
4635ccda64dSRoman Gushchin 	argc -= 2;
4645ccda64dSRoman Gushchin 	argv = &argv[2];
4655ccda64dSRoman Gushchin 	prog_fd = prog_parse_fd(&argc, &argv);
4665ccda64dSRoman Gushchin 	if (prog_fd < 0)
4675ccda64dSRoman Gushchin 		goto exit_cgroup;
4685ccda64dSRoman Gushchin 
4695ccda64dSRoman Gushchin 	if (bpf_prog_detach2(prog_fd, cgroup_fd, attach_type)) {
4705ccda64dSRoman Gushchin 		p_err("failed to detach program");
4715ccda64dSRoman Gushchin 		goto exit_prog;
4725ccda64dSRoman Gushchin 	}
4735ccda64dSRoman Gushchin 
4745ccda64dSRoman Gushchin 	if (json_output)
4755ccda64dSRoman Gushchin 		jsonw_null(json_wtr);
4765ccda64dSRoman Gushchin 
4775ccda64dSRoman Gushchin 	ret = 0;
4785ccda64dSRoman Gushchin 
4795ccda64dSRoman Gushchin exit_prog:
4805ccda64dSRoman Gushchin 	close(prog_fd);
4815ccda64dSRoman Gushchin exit_cgroup:
4825ccda64dSRoman Gushchin 	close(cgroup_fd);
4835ccda64dSRoman Gushchin exit:
4845ccda64dSRoman Gushchin 	return ret;
4855ccda64dSRoman Gushchin }
4865ccda64dSRoman Gushchin 
4875ccda64dSRoman Gushchin static int do_help(int argc, char **argv)
4885ccda64dSRoman Gushchin {
4895ccda64dSRoman Gushchin 	if (json_output) {
4905ccda64dSRoman Gushchin 		jsonw_null(json_wtr);
4915ccda64dSRoman Gushchin 		return 0;
4925ccda64dSRoman Gushchin 	}
4935ccda64dSRoman Gushchin 
4945ccda64dSRoman Gushchin 	fprintf(stderr,
49590040351SQuentin Monnet 		"Usage: %1$s %2$s { show | list } CGROUP [**effective**]\n"
49690040351SQuentin Monnet 		"       %1$s %2$s tree [CGROUP_ROOT] [**effective**]\n"
49790040351SQuentin Monnet 		"       %1$s %2$s attach CGROUP ATTACH_TYPE PROG [ATTACH_FLAGS]\n"
49890040351SQuentin Monnet 		"       %1$s %2$s detach CGROUP ATTACH_TYPE PROG\n"
49990040351SQuentin Monnet 		"       %1$s %2$s help\n"
5005ccda64dSRoman Gushchin 		"\n"
501393de512SAndrey Ignatov 		HELP_SPEC_ATTACH_TYPES "\n"
5025ccda64dSRoman Gushchin 		"       " HELP_SPEC_ATTACH_FLAGS "\n"
5035ccda64dSRoman Gushchin 		"       " HELP_SPEC_PROGRAM "\n"
504c07ba629SQuentin Monnet 		"       " HELP_SPEC_OPTIONS " |\n"
505*8cc8c635SQuentin Monnet 		"                    {-f|--bpffs} }\n"
5065ccda64dSRoman Gushchin 		"",
50790040351SQuentin Monnet 		bin_name, argv[-2]);
5085ccda64dSRoman Gushchin 
5095ccda64dSRoman Gushchin 	return 0;
5105ccda64dSRoman Gushchin }
5115ccda64dSRoman Gushchin 
5125ccda64dSRoman Gushchin static const struct cmd cmds[] = {
5136ebe6dbdSJakub Kicinski 	{ "show",	do_show },
51465b875bcSJakub Kicinski 	{ "list",	do_show },
5152058b383SRoman Gushchin 	{ "tree",       do_show_tree },
5165ccda64dSRoman Gushchin 	{ "attach",	do_attach },
5175ccda64dSRoman Gushchin 	{ "detach",	do_detach },
5185ccda64dSRoman Gushchin 	{ "help",	do_help },
5195ccda64dSRoman Gushchin 	{ 0 }
5205ccda64dSRoman Gushchin };
5215ccda64dSRoman Gushchin 
5225ccda64dSRoman Gushchin int do_cgroup(int argc, char **argv)
5235ccda64dSRoman Gushchin {
5245ccda64dSRoman Gushchin 	return cmd_select(cmds, argc, argv, do_help);
5255ccda64dSRoman Gushchin }
526