cgroup.c 16 KB

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