cgroup.c 16 KB

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