Merge tag 'hwmon-for-v5.13-rc4' of git://git.kernel.org/pub/scm/linux/kernel/git...
[linux-2.6-microblaze.git] / tools / bpf / bpftool / cgroup.c
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
19 #include "main.h"
20
21 #define HELP_SPEC_ATTACH_FLAGS                                          \
22         "ATTACH_FLAGS := { multi | override }"
23
24 #define HELP_SPEC_ATTACH_TYPES                                                 \
25         "       ATTACH_TYPE := { ingress | egress | sock_create |\n"           \
26         "                        sock_ops | device | bind4 | bind6 |\n"        \
27         "                        post_bind4 | post_bind6 | connect4 |\n"       \
28         "                        connect6 | getpeername4 | getpeername6 |\n"   \
29         "                        getsockname4 | getsockname6 | sendmsg4 |\n"   \
30         "                        sendmsg6 | recvmsg4 | recvmsg6 |\n"           \
31         "                        sysctl | getsockopt | setsockopt |\n"         \
32         "                        sock_release }"
33
34 static unsigned int query_flags;
35
36 static enum bpf_attach_type parse_attach_type(const char *str)
37 {
38         enum bpf_attach_type type;
39
40         for (type = 0; type < __MAX_BPF_ATTACH_TYPE; type++) {
41                 if (attach_type_name[type] &&
42                     is_prefix(str, attach_type_name[type]))
43                         return type;
44         }
45
46         return __MAX_BPF_ATTACH_TYPE;
47 }
48
49 static int show_bpf_prog(int id, enum bpf_attach_type attach_type,
50                          const char *attach_flags_str,
51                          int level)
52 {
53         struct bpf_prog_info info = {};
54         __u32 info_len = sizeof(info);
55         int prog_fd;
56
57         prog_fd = bpf_prog_get_fd_by_id(id);
58         if (prog_fd < 0)
59                 return -1;
60
61         if (bpf_obj_get_info_by_fd(prog_fd, &info, &info_len)) {
62                 close(prog_fd);
63                 return -1;
64         }
65
66         if (json_output) {
67                 jsonw_start_object(json_wtr);
68                 jsonw_uint_field(json_wtr, "id", info.id);
69                 if (attach_type < ARRAY_SIZE(attach_type_name))
70                         jsonw_string_field(json_wtr, "attach_type",
71                                            attach_type_name[attach_type]);
72                 else
73                         jsonw_uint_field(json_wtr, "attach_type", attach_type);
74                 jsonw_string_field(json_wtr, "attach_flags",
75                                    attach_flags_str);
76                 jsonw_string_field(json_wtr, "name", info.name);
77                 jsonw_end_object(json_wtr);
78         } else {
79                 printf("%s%-8u ", level ? "    " : "", info.id);
80                 if (attach_type < ARRAY_SIZE(attach_type_name))
81                         printf("%-15s", attach_type_name[attach_type]);
82                 else
83                         printf("type %-10u", attach_type);
84                 printf(" %-15s %-15s\n", attach_flags_str, info.name);
85         }
86
87         close(prog_fd);
88         return 0;
89 }
90
91 static int count_attached_bpf_progs(int cgroup_fd, enum bpf_attach_type type)
92 {
93         __u32 prog_cnt = 0;
94         int ret;
95
96         ret = bpf_prog_query(cgroup_fd, type, query_flags, NULL,
97                              NULL, &prog_cnt);
98         if (ret)
99                 return -1;
100
101         return prog_cnt;
102 }
103
104 static int cgroup_has_attached_progs(int cgroup_fd)
105 {
106         enum bpf_attach_type type;
107         bool no_prog = true;
108
109         for (type = 0; type < __MAX_BPF_ATTACH_TYPE; type++) {
110                 int count = count_attached_bpf_progs(cgroup_fd, type);
111
112                 if (count < 0 && errno != EINVAL)
113                         return -1;
114
115                 if (count > 0) {
116                         no_prog = false;
117                         break;
118                 }
119         }
120
121         return no_prog ? 0 : 1;
122 }
123 static int show_attached_bpf_progs(int cgroup_fd, enum bpf_attach_type type,
124                                    int level)
125 {
126         const char *attach_flags_str;
127         __u32 prog_ids[1024] = {0};
128         __u32 prog_cnt, iter;
129         __u32 attach_flags;
130         char buf[32];
131         int ret;
132
133         prog_cnt = ARRAY_SIZE(prog_ids);
134         ret = bpf_prog_query(cgroup_fd, type, query_flags, &attach_flags,
135                              prog_ids, &prog_cnt);
136         if (ret)
137                 return ret;
138
139         if (prog_cnt == 0)
140                 return 0;
141
142         switch (attach_flags) {
143         case BPF_F_ALLOW_MULTI:
144                 attach_flags_str = "multi";
145                 break;
146         case BPF_F_ALLOW_OVERRIDE:
147                 attach_flags_str = "override";
148                 break;
149         case 0:
150                 attach_flags_str = "";
151                 break;
152         default:
153                 snprintf(buf, sizeof(buf), "unknown(%x)", attach_flags);
154                 attach_flags_str = buf;
155         }
156
157         for (iter = 0; iter < prog_cnt; iter++)
158                 show_bpf_prog(prog_ids[iter], type,
159                               attach_flags_str, level);
160
161         return 0;
162 }
163
164 static int do_show(int argc, char **argv)
165 {
166         enum bpf_attach_type type;
167         int has_attached_progs;
168         const char *path;
169         int cgroup_fd;
170         int ret = -1;
171
172         query_flags = 0;
173
174         if (!REQ_ARGS(1))
175                 return -1;
176         path = GET_ARG();
177
178         while (argc) {
179                 if (is_prefix(*argv, "effective")) {
180                         if (query_flags & BPF_F_QUERY_EFFECTIVE) {
181                                 p_err("duplicated argument: %s", *argv);
182                                 return -1;
183                         }
184                         query_flags |= BPF_F_QUERY_EFFECTIVE;
185                         NEXT_ARG();
186                 } else {
187                         p_err("expected no more arguments, 'effective', got: '%s'?",
188                               *argv);
189                         return -1;
190                 }
191         }
192
193         cgroup_fd = open(path, O_RDONLY);
194         if (cgroup_fd < 0) {
195                 p_err("can't open cgroup %s", path);
196                 goto exit;
197         }
198
199         has_attached_progs = cgroup_has_attached_progs(cgroup_fd);
200         if (has_attached_progs < 0) {
201                 p_err("can't query bpf programs attached to %s: %s",
202                       path, strerror(errno));
203                 goto exit_cgroup;
204         } else if (!has_attached_progs) {
205                 ret = 0;
206                 goto exit_cgroup;
207         }
208
209         if (json_output)
210                 jsonw_start_array(json_wtr);
211         else
212                 printf("%-8s %-15s %-15s %-15s\n", "ID", "AttachType",
213                        "AttachFlags", "Name");
214
215         for (type = 0; type < __MAX_BPF_ATTACH_TYPE; type++) {
216                 /*
217                  * Not all attach types may be supported, so it's expected,
218                  * that some requests will fail.
219                  * If we were able to get the show for at least one
220                  * attach type, let's return 0.
221                  */
222                 if (show_attached_bpf_progs(cgroup_fd, type, 0) == 0)
223                         ret = 0;
224         }
225
226         if (json_output)
227                 jsonw_end_array(json_wtr);
228
229 exit_cgroup:
230         close(cgroup_fd);
231 exit:
232         return ret;
233 }
234
235 /*
236  * To distinguish nftw() errors and do_show_tree_fn() errors
237  * and avoid duplicating error messages, let's return -2
238  * from do_show_tree_fn() in case of error.
239  */
240 #define NFTW_ERR                -1
241 #define SHOW_TREE_FN_ERR        -2
242 static int do_show_tree_fn(const char *fpath, const struct stat *sb,
243                            int typeflag, struct FTW *ftw)
244 {
245         enum bpf_attach_type type;
246         int has_attached_progs;
247         int cgroup_fd;
248
249         if (typeflag != FTW_D)
250                 return 0;
251
252         cgroup_fd = open(fpath, O_RDONLY);
253         if (cgroup_fd < 0) {
254                 p_err("can't open cgroup %s: %s", fpath, strerror(errno));
255                 return SHOW_TREE_FN_ERR;
256         }
257
258         has_attached_progs = cgroup_has_attached_progs(cgroup_fd);
259         if (has_attached_progs < 0) {
260                 p_err("can't query bpf programs attached to %s: %s",
261                       fpath, strerror(errno));
262                 close(cgroup_fd);
263                 return SHOW_TREE_FN_ERR;
264         } else if (!has_attached_progs) {
265                 close(cgroup_fd);
266                 return 0;
267         }
268
269         if (json_output) {
270                 jsonw_start_object(json_wtr);
271                 jsonw_string_field(json_wtr, "cgroup", fpath);
272                 jsonw_name(json_wtr, "programs");
273                 jsonw_start_array(json_wtr);
274         } else {
275                 printf("%s\n", fpath);
276         }
277
278         for (type = 0; type < __MAX_BPF_ATTACH_TYPE; type++)
279                 show_attached_bpf_progs(cgroup_fd, type, ftw->level);
280
281         if (errno == EINVAL)
282                 /* Last attach type does not support query.
283                  * Do not report an error for this, especially because batch
284                  * mode would stop processing commands.
285                  */
286                 errno = 0;
287
288         if (json_output) {
289                 jsonw_end_array(json_wtr);
290                 jsonw_end_object(json_wtr);
291         }
292
293         close(cgroup_fd);
294
295         return 0;
296 }
297
298 static char *find_cgroup_root(void)
299 {
300         struct mntent *mnt;
301         FILE *f;
302
303         f = fopen("/proc/mounts", "r");
304         if (f == NULL)
305                 return NULL;
306
307         while ((mnt = getmntent(f))) {
308                 if (strcmp(mnt->mnt_type, "cgroup2") == 0) {
309                         fclose(f);
310                         return strdup(mnt->mnt_dir);
311                 }
312         }
313
314         fclose(f);
315         return NULL;
316 }
317
318 static int do_show_tree(int argc, char **argv)
319 {
320         char *cgroup_root, *cgroup_alloced = NULL;
321         int ret;
322
323         query_flags = 0;
324
325         if (!argc) {
326                 cgroup_alloced = find_cgroup_root();
327                 if (!cgroup_alloced) {
328                         p_err("cgroup v2 isn't mounted");
329                         return -1;
330                 }
331                 cgroup_root = cgroup_alloced;
332         } else {
333                 cgroup_root = GET_ARG();
334
335                 while (argc) {
336                         if (is_prefix(*argv, "effective")) {
337                                 if (query_flags & BPF_F_QUERY_EFFECTIVE) {
338                                         p_err("duplicated argument: %s", *argv);
339                                         return -1;
340                                 }
341                                 query_flags |= BPF_F_QUERY_EFFECTIVE;
342                                 NEXT_ARG();
343                         } else {
344                                 p_err("expected no more arguments, 'effective', got: '%s'?",
345                                       *argv);
346                                 return -1;
347                         }
348                 }
349         }
350
351         if (json_output)
352                 jsonw_start_array(json_wtr);
353         else
354                 printf("%s\n"
355                        "%-8s %-15s %-15s %-15s\n",
356                        "CgroupPath",
357                        "ID", "AttachType", "AttachFlags", "Name");
358
359         switch (nftw(cgroup_root, do_show_tree_fn, 1024, FTW_MOUNT)) {
360         case NFTW_ERR:
361                 p_err("can't iterate over %s: %s", cgroup_root,
362                       strerror(errno));
363                 ret = -1;
364                 break;
365         case SHOW_TREE_FN_ERR:
366                 ret = -1;
367                 break;
368         default:
369                 ret = 0;
370         }
371
372         if (json_output)
373                 jsonw_end_array(json_wtr);
374
375         free(cgroup_alloced);
376
377         return ret;
378 }
379
380 static int do_attach(int argc, char **argv)
381 {
382         enum bpf_attach_type attach_type;
383         int cgroup_fd, prog_fd;
384         int attach_flags = 0;
385         int ret = -1;
386         int i;
387
388         if (argc < 4) {
389                 p_err("too few parameters for cgroup attach");
390                 goto exit;
391         }
392
393         cgroup_fd = open(argv[0], O_RDONLY);
394         if (cgroup_fd < 0) {
395                 p_err("can't open cgroup %s", argv[0]);
396                 goto exit;
397         }
398
399         attach_type = parse_attach_type(argv[1]);
400         if (attach_type == __MAX_BPF_ATTACH_TYPE) {
401                 p_err("invalid attach type");
402                 goto exit_cgroup;
403         }
404
405         argc -= 2;
406         argv = &argv[2];
407         prog_fd = prog_parse_fd(&argc, &argv);
408         if (prog_fd < 0)
409                 goto exit_cgroup;
410
411         for (i = 0; i < argc; i++) {
412                 if (is_prefix(argv[i], "multi")) {
413                         attach_flags |= BPF_F_ALLOW_MULTI;
414                 } else if (is_prefix(argv[i], "override")) {
415                         attach_flags |= BPF_F_ALLOW_OVERRIDE;
416                 } else {
417                         p_err("unknown option: %s", argv[i]);
418                         goto exit_cgroup;
419                 }
420         }
421
422         if (bpf_prog_attach(prog_fd, cgroup_fd, attach_type, attach_flags)) {
423                 p_err("failed to attach program");
424                 goto exit_prog;
425         }
426
427         if (json_output)
428                 jsonw_null(json_wtr);
429
430         ret = 0;
431
432 exit_prog:
433         close(prog_fd);
434 exit_cgroup:
435         close(cgroup_fd);
436 exit:
437         return ret;
438 }
439
440 static int do_detach(int argc, char **argv)
441 {
442         enum bpf_attach_type attach_type;
443         int prog_fd, cgroup_fd;
444         int ret = -1;
445
446         if (argc < 4) {
447                 p_err("too few parameters for cgroup detach");
448                 goto exit;
449         }
450
451         cgroup_fd = open(argv[0], O_RDONLY);
452         if (cgroup_fd < 0) {
453                 p_err("can't open cgroup %s", argv[0]);
454                 goto exit;
455         }
456
457         attach_type = parse_attach_type(argv[1]);
458         if (attach_type == __MAX_BPF_ATTACH_TYPE) {
459                 p_err("invalid attach type");
460                 goto exit_cgroup;
461         }
462
463         argc -= 2;
464         argv = &argv[2];
465         prog_fd = prog_parse_fd(&argc, &argv);
466         if (prog_fd < 0)
467                 goto exit_cgroup;
468
469         if (bpf_prog_detach2(prog_fd, cgroup_fd, attach_type)) {
470                 p_err("failed to detach program");
471                 goto exit_prog;
472         }
473
474         if (json_output)
475                 jsonw_null(json_wtr);
476
477         ret = 0;
478
479 exit_prog:
480         close(prog_fd);
481 exit_cgroup:
482         close(cgroup_fd);
483 exit:
484         return ret;
485 }
486
487 static int do_help(int argc, char **argv)
488 {
489         if (json_output) {
490                 jsonw_null(json_wtr);
491                 return 0;
492         }
493
494         fprintf(stderr,
495                 "Usage: %1$s %2$s { show | list } CGROUP [**effective**]\n"
496                 "       %1$s %2$s tree [CGROUP_ROOT] [**effective**]\n"
497                 "       %1$s %2$s attach CGROUP ATTACH_TYPE PROG [ATTACH_FLAGS]\n"
498                 "       %1$s %2$s detach CGROUP ATTACH_TYPE PROG\n"
499                 "       %1$s %2$s help\n"
500                 "\n"
501                 HELP_SPEC_ATTACH_TYPES "\n"
502                 "       " HELP_SPEC_ATTACH_FLAGS "\n"
503                 "       " HELP_SPEC_PROGRAM "\n"
504                 "       " HELP_SPEC_OPTIONS "\n"
505                 "",
506                 bin_name, argv[-2]);
507
508         return 0;
509 }
510
511 static const struct cmd cmds[] = {
512         { "show",       do_show },
513         { "list",       do_show },
514         { "tree",       do_show_tree },
515         { "attach",     do_attach },
516         { "detach",     do_detach },
517         { "help",       do_help },
518         { 0 }
519 };
520
521 int do_cgroup(int argc, char **argv)
522 {
523         return cmd_select(cmds, argc, argv, do_help);
524 }