1 | // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) |
2 | // Copyright (C) 2017 Facebook |
3 | // Author: Roman Gushchin <guro@fb.com> |
4 | |
5 | #define _XOPEN_SOURCE 500 |
6 | #include <errno.h> |
7 | #include <fcntl.h> |
8 | #include <ftw.h> |
9 | #include <mntent.h> |
10 | #include <stdio.h> |
11 | #include <stdlib.h> |
12 | #include <string.h> |
13 | #include <sys/stat.h> |
14 | #include <sys/types.h> |
15 | #include <unistd.h> |
16 | |
17 | #include <bpf/bpf.h> |
18 | #include <bpf/btf.h> |
19 | |
20 | #include "main.h" |
21 | |
22 | static const int cgroup_attach_types[] = { |
23 | BPF_CGROUP_INET_INGRESS, |
24 | BPF_CGROUP_INET_EGRESS, |
25 | BPF_CGROUP_INET_SOCK_CREATE, |
26 | BPF_CGROUP_INET_SOCK_RELEASE, |
27 | BPF_CGROUP_INET4_BIND, |
28 | BPF_CGROUP_INET6_BIND, |
29 | BPF_CGROUP_INET4_POST_BIND, |
30 | BPF_CGROUP_INET6_POST_BIND, |
31 | BPF_CGROUP_INET4_CONNECT, |
32 | BPF_CGROUP_INET6_CONNECT, |
33 | BPF_CGROUP_UNIX_CONNECT, |
34 | BPF_CGROUP_INET4_GETPEERNAME, |
35 | BPF_CGROUP_INET6_GETPEERNAME, |
36 | BPF_CGROUP_UNIX_GETPEERNAME, |
37 | BPF_CGROUP_INET4_GETSOCKNAME, |
38 | BPF_CGROUP_INET6_GETSOCKNAME, |
39 | BPF_CGROUP_UNIX_GETSOCKNAME, |
40 | BPF_CGROUP_UDP4_SENDMSG, |
41 | BPF_CGROUP_UDP6_SENDMSG, |
42 | BPF_CGROUP_UNIX_SENDMSG, |
43 | BPF_CGROUP_UDP4_RECVMSG, |
44 | BPF_CGROUP_UDP6_RECVMSG, |
45 | BPF_CGROUP_UNIX_RECVMSG, |
46 | BPF_CGROUP_SOCK_OPS, |
47 | BPF_CGROUP_DEVICE, |
48 | BPF_CGROUP_SYSCTL, |
49 | BPF_CGROUP_GETSOCKOPT, |
50 | BPF_CGROUP_SETSOCKOPT, |
51 | BPF_LSM_CGROUP |
52 | }; |
53 | |
54 | #define HELP_SPEC_ATTACH_FLAGS \ |
55 | "ATTACH_FLAGS := { multi | override }" |
56 | |
57 | #define HELP_SPEC_ATTACH_TYPES \ |
58 | " ATTACH_TYPE := { cgroup_inet_ingress | cgroup_inet_egress |\n" \ |
59 | " cgroup_inet_sock_create | cgroup_sock_ops |\n" \ |
60 | " cgroup_device | cgroup_inet4_bind |\n" \ |
61 | " cgroup_inet6_bind | cgroup_inet4_post_bind |\n" \ |
62 | " cgroup_inet6_post_bind | cgroup_inet4_connect |\n" \ |
63 | " cgroup_inet6_connect | cgroup_unix_connect |\n" \ |
64 | " cgroup_inet4_getpeername | cgroup_inet6_getpeername |\n" \ |
65 | " cgroup_unix_getpeername | cgroup_inet4_getsockname |\n" \ |
66 | " cgroup_inet6_getsockname | cgroup_unix_getsockname |\n" \ |
67 | " cgroup_udp4_sendmsg | cgroup_udp6_sendmsg |\n" \ |
68 | " cgroup_unix_sendmsg | cgroup_udp4_recvmsg |\n" \ |
69 | " cgroup_udp6_recvmsg | cgroup_unix_recvmsg |\n" \ |
70 | " cgroup_sysctl | cgroup_getsockopt |\n" \ |
71 | " cgroup_setsockopt | cgroup_inet_sock_release }" |
72 | |
73 | static unsigned int query_flags; |
74 | static struct btf *btf_vmlinux; |
75 | static __u32 btf_vmlinux_id; |
76 | |
77 | static enum bpf_attach_type parse_attach_type(const char *str) |
78 | { |
79 | const char *attach_type_str; |
80 | enum bpf_attach_type type; |
81 | |
82 | for (type = 0; ; type++) { |
83 | attach_type_str = libbpf_bpf_attach_type_str(type); |
84 | if (!attach_type_str) |
85 | break; |
86 | if (!strcmp(str, attach_type_str)) |
87 | return type; |
88 | } |
89 | |
90 | /* Also check traditionally used attach type strings. For these we keep |
91 | * allowing prefixed usage. |
92 | */ |
93 | for (type = 0; ; type++) { |
94 | attach_type_str = bpf_attach_type_input_str(t: type); |
95 | if (!attach_type_str) |
96 | break; |
97 | if (is_prefix(pfx: str, str: attach_type_str)) |
98 | return type; |
99 | } |
100 | |
101 | return __MAX_BPF_ATTACH_TYPE; |
102 | } |
103 | |
104 | static void guess_vmlinux_btf_id(__u32 attach_btf_obj_id) |
105 | { |
106 | struct bpf_btf_info btf_info = {}; |
107 | __u32 btf_len = sizeof(btf_info); |
108 | char name[16] = {}; |
109 | int err; |
110 | int fd; |
111 | |
112 | btf_info.name = ptr_to_u64(ptr: name); |
113 | btf_info.name_len = sizeof(name); |
114 | |
115 | fd = bpf_btf_get_fd_by_id(attach_btf_obj_id); |
116 | if (fd < 0) |
117 | return; |
118 | |
119 | err = bpf_btf_get_info_by_fd(fd, &btf_info, &btf_len); |
120 | if (err) |
121 | goto out; |
122 | |
123 | if (btf_info.kernel_btf && strncmp(name, "vmlinux" , sizeof(name)) == 0) |
124 | btf_vmlinux_id = btf_info.id; |
125 | |
126 | out: |
127 | close(fd); |
128 | } |
129 | |
130 | static int show_bpf_prog(int id, enum bpf_attach_type attach_type, |
131 | const char *attach_flags_str, |
132 | int level) |
133 | { |
134 | char prog_name[MAX_PROG_FULL_NAME]; |
135 | const char *attach_btf_name = NULL; |
136 | struct bpf_prog_info info = {}; |
137 | const char *attach_type_str; |
138 | __u32 info_len = sizeof(info); |
139 | int prog_fd; |
140 | |
141 | prog_fd = bpf_prog_get_fd_by_id(id); |
142 | if (prog_fd < 0) |
143 | return -1; |
144 | |
145 | if (bpf_prog_get_info_by_fd(prog_fd, &info, &info_len)) { |
146 | close(prog_fd); |
147 | return -1; |
148 | } |
149 | |
150 | attach_type_str = libbpf_bpf_attach_type_str(attach_type); |
151 | |
152 | if (btf_vmlinux) { |
153 | if (!btf_vmlinux_id) |
154 | guess_vmlinux_btf_id(attach_btf_obj_id: info.attach_btf_obj_id); |
155 | |
156 | if (btf_vmlinux_id == info.attach_btf_obj_id && |
157 | info.attach_btf_id < btf__type_cnt(btf_vmlinux)) { |
158 | const struct btf_type *t = |
159 | btf__type_by_id(btf_vmlinux, info.attach_btf_id); |
160 | attach_btf_name = |
161 | btf__name_by_offset(btf_vmlinux, t->name_off); |
162 | } |
163 | } |
164 | |
165 | get_prog_full_name(prog_info: &info, prog_fd, name_buff: prog_name, buff_len: sizeof(prog_name)); |
166 | if (json_output) { |
167 | jsonw_start_object(self: json_wtr); |
168 | jsonw_uint_field(self: json_wtr, prop: "id" , num: info.id); |
169 | if (attach_type_str) |
170 | jsonw_string_field(self: json_wtr, prop: "attach_type" , val: attach_type_str); |
171 | else |
172 | jsonw_uint_field(self: json_wtr, prop: "attach_type" , num: attach_type); |
173 | if (!(query_flags & BPF_F_QUERY_EFFECTIVE)) |
174 | jsonw_string_field(self: json_wtr, prop: "attach_flags" , val: attach_flags_str); |
175 | jsonw_string_field(self: json_wtr, prop: "name" , val: prog_name); |
176 | if (attach_btf_name) |
177 | jsonw_string_field(self: json_wtr, prop: "attach_btf_name" , val: attach_btf_name); |
178 | jsonw_uint_field(self: json_wtr, prop: "attach_btf_obj_id" , num: info.attach_btf_obj_id); |
179 | jsonw_uint_field(self: json_wtr, prop: "attach_btf_id" , num: info.attach_btf_id); |
180 | jsonw_end_object(self: json_wtr); |
181 | } else { |
182 | printf("%s%-8u " , level ? " " : "" , info.id); |
183 | if (attach_type_str) |
184 | printf("%-15s" , attach_type_str); |
185 | else |
186 | printf("type %-10u" , attach_type); |
187 | if (query_flags & BPF_F_QUERY_EFFECTIVE) |
188 | printf(" %-15s" , prog_name); |
189 | else |
190 | printf(" %-15s %-15s" , attach_flags_str, prog_name); |
191 | if (attach_btf_name) |
192 | printf(" %-15s" , attach_btf_name); |
193 | else if (info.attach_btf_id) |
194 | printf(" attach_btf_obj_id=%u attach_btf_id=%u" , |
195 | info.attach_btf_obj_id, info.attach_btf_id); |
196 | printf("\n" ); |
197 | } |
198 | |
199 | close(prog_fd); |
200 | return 0; |
201 | } |
202 | |
203 | static int count_attached_bpf_progs(int cgroup_fd, enum bpf_attach_type type) |
204 | { |
205 | __u32 prog_cnt = 0; |
206 | int ret; |
207 | |
208 | ret = bpf_prog_query(cgroup_fd, type, query_flags, NULL, |
209 | NULL, &prog_cnt); |
210 | if (ret) |
211 | return -1; |
212 | |
213 | return prog_cnt; |
214 | } |
215 | |
216 | static int cgroup_has_attached_progs(int cgroup_fd) |
217 | { |
218 | unsigned int i = 0; |
219 | bool no_prog = true; |
220 | |
221 | for (i = 0; i < ARRAY_SIZE(cgroup_attach_types); i++) { |
222 | int count = count_attached_bpf_progs(cgroup_fd, type: cgroup_attach_types[i]); |
223 | |
224 | if (count < 0 && errno != EINVAL) |
225 | return -1; |
226 | |
227 | if (count > 0) { |
228 | no_prog = false; |
229 | break; |
230 | } |
231 | } |
232 | |
233 | return no_prog ? 0 : 1; |
234 | } |
235 | |
236 | static int show_effective_bpf_progs(int cgroup_fd, enum bpf_attach_type type, |
237 | int level) |
238 | { |
239 | LIBBPF_OPTS(bpf_prog_query_opts, p); |
240 | __u32 prog_ids[1024] = {0}; |
241 | __u32 iter; |
242 | int ret; |
243 | |
244 | p.query_flags = query_flags; |
245 | p.prog_cnt = ARRAY_SIZE(prog_ids); |
246 | p.prog_ids = prog_ids; |
247 | |
248 | ret = bpf_prog_query_opts(cgroup_fd, type, &p); |
249 | if (ret) |
250 | return ret; |
251 | |
252 | if (p.prog_cnt == 0) |
253 | return 0; |
254 | |
255 | for (iter = 0; iter < p.prog_cnt; iter++) |
256 | show_bpf_prog(id: prog_ids[iter], attach_type: type, NULL, level); |
257 | |
258 | return 0; |
259 | } |
260 | |
261 | static int show_attached_bpf_progs(int cgroup_fd, enum bpf_attach_type type, |
262 | int level) |
263 | { |
264 | LIBBPF_OPTS(bpf_prog_query_opts, p); |
265 | __u32 prog_attach_flags[1024] = {0}; |
266 | const char *attach_flags_str; |
267 | __u32 prog_ids[1024] = {0}; |
268 | char buf[32]; |
269 | __u32 iter; |
270 | int ret; |
271 | |
272 | p.query_flags = query_flags; |
273 | p.prog_cnt = ARRAY_SIZE(prog_ids); |
274 | p.prog_ids = prog_ids; |
275 | p.prog_attach_flags = prog_attach_flags; |
276 | |
277 | ret = bpf_prog_query_opts(cgroup_fd, type, &p); |
278 | if (ret) |
279 | return ret; |
280 | |
281 | if (p.prog_cnt == 0) |
282 | return 0; |
283 | |
284 | for (iter = 0; iter < p.prog_cnt; iter++) { |
285 | __u32 attach_flags; |
286 | |
287 | attach_flags = prog_attach_flags[iter] ?: p.attach_flags; |
288 | |
289 | switch (attach_flags) { |
290 | case BPF_F_ALLOW_MULTI: |
291 | attach_flags_str = "multi" ; |
292 | break; |
293 | case BPF_F_ALLOW_OVERRIDE: |
294 | attach_flags_str = "override" ; |
295 | break; |
296 | case 0: |
297 | attach_flags_str = "" ; |
298 | break; |
299 | default: |
300 | snprintf(buf, size: sizeof(buf), fmt: "unknown(%x)" , attach_flags); |
301 | attach_flags_str = buf; |
302 | } |
303 | |
304 | show_bpf_prog(id: prog_ids[iter], attach_type: type, |
305 | attach_flags_str, level); |
306 | } |
307 | |
308 | return 0; |
309 | } |
310 | |
311 | static int show_bpf_progs(int cgroup_fd, enum bpf_attach_type type, |
312 | int level) |
313 | { |
314 | return query_flags & BPF_F_QUERY_EFFECTIVE ? |
315 | show_effective_bpf_progs(cgroup_fd, type, level) : |
316 | show_attached_bpf_progs(cgroup_fd, type, level); |
317 | } |
318 | |
319 | static int do_show(int argc, char **argv) |
320 | { |
321 | int has_attached_progs; |
322 | const char *path; |
323 | int cgroup_fd; |
324 | int ret = -1; |
325 | unsigned int i; |
326 | |
327 | query_flags = 0; |
328 | |
329 | if (!REQ_ARGS(1)) |
330 | return -1; |
331 | path = GET_ARG(); |
332 | |
333 | while (argc) { |
334 | if (is_prefix(pfx: *argv, str: "effective" )) { |
335 | if (query_flags & BPF_F_QUERY_EFFECTIVE) { |
336 | p_err(fmt: "duplicated argument: %s" , *argv); |
337 | return -1; |
338 | } |
339 | query_flags |= BPF_F_QUERY_EFFECTIVE; |
340 | NEXT_ARG(); |
341 | } else { |
342 | p_err(fmt: "expected no more arguments, 'effective', got: '%s'?" , |
343 | *argv); |
344 | return -1; |
345 | } |
346 | } |
347 | |
348 | cgroup_fd = open(path, O_RDONLY); |
349 | if (cgroup_fd < 0) { |
350 | p_err(fmt: "can't open cgroup %s" , path); |
351 | goto exit; |
352 | } |
353 | |
354 | has_attached_progs = cgroup_has_attached_progs(cgroup_fd); |
355 | if (has_attached_progs < 0) { |
356 | p_err(fmt: "can't query bpf programs attached to %s: %s" , |
357 | path, strerror(errno)); |
358 | goto exit_cgroup; |
359 | } else if (!has_attached_progs) { |
360 | ret = 0; |
361 | goto exit_cgroup; |
362 | } |
363 | |
364 | if (json_output) |
365 | jsonw_start_array(self: json_wtr); |
366 | else if (query_flags & BPF_F_QUERY_EFFECTIVE) |
367 | printf("%-8s %-15s %-15s\n" , "ID" , "AttachType" , "Name" ); |
368 | else |
369 | printf("%-8s %-15s %-15s %-15s\n" , "ID" , "AttachType" , |
370 | "AttachFlags" , "Name" ); |
371 | |
372 | btf_vmlinux = libbpf_find_kernel_btf(); |
373 | for (i = 0; i < ARRAY_SIZE(cgroup_attach_types); i++) { |
374 | /* |
375 | * Not all attach types may be supported, so it's expected, |
376 | * that some requests will fail. |
377 | * If we were able to get the show for at least one |
378 | * attach type, let's return 0. |
379 | */ |
380 | if (show_bpf_progs(cgroup_fd, type: cgroup_attach_types[i], level: 0) == 0) |
381 | ret = 0; |
382 | } |
383 | |
384 | if (json_output) |
385 | jsonw_end_array(self: json_wtr); |
386 | |
387 | exit_cgroup: |
388 | close(cgroup_fd); |
389 | exit: |
390 | return ret; |
391 | } |
392 | |
393 | /* |
394 | * To distinguish nftw() errors and do_show_tree_fn() errors |
395 | * and avoid duplicating error messages, let's return -2 |
396 | * from do_show_tree_fn() in case of error. |
397 | */ |
398 | #define NFTW_ERR -1 |
399 | #define SHOW_TREE_FN_ERR -2 |
400 | static int do_show_tree_fn(const char *fpath, const struct stat *sb, |
401 | int typeflag, struct FTW *ftw) |
402 | { |
403 | int has_attached_progs; |
404 | int cgroup_fd; |
405 | unsigned int i; |
406 | |
407 | if (typeflag != FTW_D) |
408 | return 0; |
409 | |
410 | cgroup_fd = open(fpath, O_RDONLY); |
411 | if (cgroup_fd < 0) { |
412 | p_err(fmt: "can't open cgroup %s: %s" , fpath, strerror(errno)); |
413 | return SHOW_TREE_FN_ERR; |
414 | } |
415 | |
416 | has_attached_progs = cgroup_has_attached_progs(cgroup_fd); |
417 | if (has_attached_progs < 0) { |
418 | p_err(fmt: "can't query bpf programs attached to %s: %s" , |
419 | fpath, strerror(errno)); |
420 | close(cgroup_fd); |
421 | return SHOW_TREE_FN_ERR; |
422 | } else if (!has_attached_progs) { |
423 | close(cgroup_fd); |
424 | return 0; |
425 | } |
426 | |
427 | if (json_output) { |
428 | jsonw_start_object(self: json_wtr); |
429 | jsonw_string_field(self: json_wtr, prop: "cgroup" , val: fpath); |
430 | jsonw_name(self: json_wtr, name: "programs" ); |
431 | jsonw_start_array(self: json_wtr); |
432 | } else { |
433 | printf("%s\n" , fpath); |
434 | } |
435 | |
436 | btf_vmlinux = libbpf_find_kernel_btf(); |
437 | for (i = 0; i < ARRAY_SIZE(cgroup_attach_types); i++) |
438 | show_bpf_progs(cgroup_fd, type: cgroup_attach_types[i], level: ftw->level); |
439 | |
440 | if (errno == EINVAL) |
441 | /* Last attach type does not support query. |
442 | * Do not report an error for this, especially because batch |
443 | * mode would stop processing commands. |
444 | */ |
445 | errno = 0; |
446 | |
447 | if (json_output) { |
448 | jsonw_end_array(self: json_wtr); |
449 | jsonw_end_object(self: json_wtr); |
450 | } |
451 | |
452 | close(cgroup_fd); |
453 | |
454 | return 0; |
455 | } |
456 | |
457 | static char *find_cgroup_root(void) |
458 | { |
459 | struct mntent *mnt; |
460 | FILE *f; |
461 | |
462 | f = fopen("/proc/mounts" , "r" ); |
463 | if (f == NULL) |
464 | return NULL; |
465 | |
466 | while ((mnt = getmntent(f))) { |
467 | if (strcmp(mnt->mnt_type, "cgroup2" ) == 0) { |
468 | fclose(f); |
469 | return strdup(mnt->mnt_dir); |
470 | } |
471 | } |
472 | |
473 | fclose(f); |
474 | return NULL; |
475 | } |
476 | |
477 | static int do_show_tree(int argc, char **argv) |
478 | { |
479 | char *cgroup_root, *cgroup_alloced = NULL; |
480 | int ret; |
481 | |
482 | query_flags = 0; |
483 | |
484 | if (!argc) { |
485 | cgroup_alloced = find_cgroup_root(); |
486 | if (!cgroup_alloced) { |
487 | p_err(fmt: "cgroup v2 isn't mounted" ); |
488 | return -1; |
489 | } |
490 | cgroup_root = cgroup_alloced; |
491 | } else { |
492 | cgroup_root = GET_ARG(); |
493 | |
494 | while (argc) { |
495 | if (is_prefix(pfx: *argv, str: "effective" )) { |
496 | if (query_flags & BPF_F_QUERY_EFFECTIVE) { |
497 | p_err(fmt: "duplicated argument: %s" , *argv); |
498 | return -1; |
499 | } |
500 | query_flags |= BPF_F_QUERY_EFFECTIVE; |
501 | NEXT_ARG(); |
502 | } else { |
503 | p_err(fmt: "expected no more arguments, 'effective', got: '%s'?" , |
504 | *argv); |
505 | return -1; |
506 | } |
507 | } |
508 | } |
509 | |
510 | if (json_output) |
511 | jsonw_start_array(self: json_wtr); |
512 | else if (query_flags & BPF_F_QUERY_EFFECTIVE) |
513 | printf("%s\n" |
514 | "%-8s %-15s %-15s\n" , |
515 | "CgroupPath" , |
516 | "ID" , "AttachType" , "Name" ); |
517 | else |
518 | printf("%s\n" |
519 | "%-8s %-15s %-15s %-15s\n" , |
520 | "CgroupPath" , |
521 | "ID" , "AttachType" , "AttachFlags" , "Name" ); |
522 | |
523 | switch (nftw(cgroup_root, do_show_tree_fn, 1024, FTW_MOUNT)) { |
524 | case NFTW_ERR: |
525 | p_err("can't iterate over %s: %s" , cgroup_root, |
526 | strerror(errno)); |
527 | ret = -1; |
528 | break; |
529 | case SHOW_TREE_FN_ERR: |
530 | ret = -1; |
531 | break; |
532 | default: |
533 | ret = 0; |
534 | } |
535 | |
536 | if (json_output) |
537 | jsonw_end_array(self: json_wtr); |
538 | |
539 | free(cgroup_alloced); |
540 | |
541 | return ret; |
542 | } |
543 | |
544 | static int do_attach(int argc, char **argv) |
545 | { |
546 | enum bpf_attach_type attach_type; |
547 | int cgroup_fd, prog_fd; |
548 | int attach_flags = 0; |
549 | int ret = -1; |
550 | int i; |
551 | |
552 | if (argc < 4) { |
553 | p_err(fmt: "too few parameters for cgroup attach" ); |
554 | goto exit; |
555 | } |
556 | |
557 | cgroup_fd = open(argv[0], O_RDONLY); |
558 | if (cgroup_fd < 0) { |
559 | p_err(fmt: "can't open cgroup %s" , argv[0]); |
560 | goto exit; |
561 | } |
562 | |
563 | attach_type = parse_attach_type(str: argv[1]); |
564 | if (attach_type == __MAX_BPF_ATTACH_TYPE) { |
565 | p_err(fmt: "invalid attach type" ); |
566 | goto exit_cgroup; |
567 | } |
568 | |
569 | argc -= 2; |
570 | argv = &argv[2]; |
571 | prog_fd = prog_parse_fd(argc: &argc, argv: &argv); |
572 | if (prog_fd < 0) |
573 | goto exit_cgroup; |
574 | |
575 | for (i = 0; i < argc; i++) { |
576 | if (is_prefix(pfx: argv[i], str: "multi" )) { |
577 | attach_flags |= BPF_F_ALLOW_MULTI; |
578 | } else if (is_prefix(pfx: argv[i], str: "override" )) { |
579 | attach_flags |= BPF_F_ALLOW_OVERRIDE; |
580 | } else { |
581 | p_err(fmt: "unknown option: %s" , argv[i]); |
582 | goto exit_cgroup; |
583 | } |
584 | } |
585 | |
586 | if (bpf_prog_attach(prog_fd, cgroup_fd, attach_type, attach_flags)) { |
587 | p_err(fmt: "failed to attach program" ); |
588 | goto exit_prog; |
589 | } |
590 | |
591 | if (json_output) |
592 | jsonw_null(self: json_wtr); |
593 | |
594 | ret = 0; |
595 | |
596 | exit_prog: |
597 | close(prog_fd); |
598 | exit_cgroup: |
599 | close(cgroup_fd); |
600 | exit: |
601 | return ret; |
602 | } |
603 | |
604 | static int do_detach(int argc, char **argv) |
605 | { |
606 | enum bpf_attach_type attach_type; |
607 | int prog_fd, cgroup_fd; |
608 | int ret = -1; |
609 | |
610 | if (argc < 4) { |
611 | p_err(fmt: "too few parameters for cgroup detach" ); |
612 | goto exit; |
613 | } |
614 | |
615 | cgroup_fd = open(argv[0], O_RDONLY); |
616 | if (cgroup_fd < 0) { |
617 | p_err(fmt: "can't open cgroup %s" , argv[0]); |
618 | goto exit; |
619 | } |
620 | |
621 | attach_type = parse_attach_type(str: argv[1]); |
622 | if (attach_type == __MAX_BPF_ATTACH_TYPE) { |
623 | p_err(fmt: "invalid attach type" ); |
624 | goto exit_cgroup; |
625 | } |
626 | |
627 | argc -= 2; |
628 | argv = &argv[2]; |
629 | prog_fd = prog_parse_fd(argc: &argc, argv: &argv); |
630 | if (prog_fd < 0) |
631 | goto exit_cgroup; |
632 | |
633 | if (bpf_prog_detach2(prog_fd, cgroup_fd, attach_type)) { |
634 | p_err(fmt: "failed to detach program" ); |
635 | goto exit_prog; |
636 | } |
637 | |
638 | if (json_output) |
639 | jsonw_null(self: json_wtr); |
640 | |
641 | ret = 0; |
642 | |
643 | exit_prog: |
644 | close(prog_fd); |
645 | exit_cgroup: |
646 | close(cgroup_fd); |
647 | exit: |
648 | return ret; |
649 | } |
650 | |
651 | static int do_help(int argc, char **argv) |
652 | { |
653 | if (json_output) { |
654 | jsonw_null(self: json_wtr); |
655 | return 0; |
656 | } |
657 | |
658 | fprintf(stderr, |
659 | "Usage: %1$s %2$s { show | list } CGROUP [**effective**]\n" |
660 | " %1$s %2$s tree [CGROUP_ROOT] [**effective**]\n" |
661 | " %1$s %2$s attach CGROUP ATTACH_TYPE PROG [ATTACH_FLAGS]\n" |
662 | " %1$s %2$s detach CGROUP ATTACH_TYPE PROG\n" |
663 | " %1$s %2$s help\n" |
664 | "\n" |
665 | HELP_SPEC_ATTACH_TYPES "\n" |
666 | " " HELP_SPEC_ATTACH_FLAGS "\n" |
667 | " " HELP_SPEC_PROGRAM "\n" |
668 | " " HELP_SPEC_OPTIONS " |\n" |
669 | " {-f|--bpffs} }\n" |
670 | "" , |
671 | bin_name, argv[-2]); |
672 | |
673 | return 0; |
674 | } |
675 | |
676 | static const struct cmd cmds[] = { |
677 | { "show" , do_show }, |
678 | { "list" , do_show }, |
679 | { "tree" , do_show_tree }, |
680 | { "attach" , do_attach }, |
681 | { "detach" , do_detach }, |
682 | { "help" , do_help }, |
683 | { 0 } |
684 | }; |
685 | |
686 | int do_cgroup(int argc, char **argv) |
687 | { |
688 | return cmd_select(cmds, argc, argv, help: do_help); |
689 | } |
690 | |