1 // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) 2 /* Copyright (C) 2017-2018 Netronome Systems, Inc. */ 3 4 #include <assert.h> 5 #include <errno.h> 6 #include <fcntl.h> 7 #include <linux/err.h> 8 #include <linux/kernel.h> 9 #include <net/if.h> 10 #include <stdbool.h> 11 #include <stdio.h> 12 #include <stdlib.h> 13 #include <string.h> 14 #include <unistd.h> 15 #include <sys/types.h> 16 #include <sys/stat.h> 17 18 #include <bpf/bpf.h> 19 #include <bpf/btf.h> 20 21 #include "json_writer.h" 22 #include "main.h" 23 24 const char * const map_type_name[] = { 25 [BPF_MAP_TYPE_UNSPEC] = "unspec", 26 [BPF_MAP_TYPE_HASH] = "hash", 27 [BPF_MAP_TYPE_ARRAY] = "array", 28 [BPF_MAP_TYPE_PROG_ARRAY] = "prog_array", 29 [BPF_MAP_TYPE_PERF_EVENT_ARRAY] = "perf_event_array", 30 [BPF_MAP_TYPE_PERCPU_HASH] = "percpu_hash", 31 [BPF_MAP_TYPE_PERCPU_ARRAY] = "percpu_array", 32 [BPF_MAP_TYPE_STACK_TRACE] = "stack_trace", 33 [BPF_MAP_TYPE_CGROUP_ARRAY] = "cgroup_array", 34 [BPF_MAP_TYPE_LRU_HASH] = "lru_hash", 35 [BPF_MAP_TYPE_LRU_PERCPU_HASH] = "lru_percpu_hash", 36 [BPF_MAP_TYPE_LPM_TRIE] = "lpm_trie", 37 [BPF_MAP_TYPE_ARRAY_OF_MAPS] = "array_of_maps", 38 [BPF_MAP_TYPE_HASH_OF_MAPS] = "hash_of_maps", 39 [BPF_MAP_TYPE_DEVMAP] = "devmap", 40 [BPF_MAP_TYPE_DEVMAP_HASH] = "devmap_hash", 41 [BPF_MAP_TYPE_SOCKMAP] = "sockmap", 42 [BPF_MAP_TYPE_CPUMAP] = "cpumap", 43 [BPF_MAP_TYPE_XSKMAP] = "xskmap", 44 [BPF_MAP_TYPE_SOCKHASH] = "sockhash", 45 [BPF_MAP_TYPE_CGROUP_STORAGE] = "cgroup_storage", 46 [BPF_MAP_TYPE_REUSEPORT_SOCKARRAY] = "reuseport_sockarray", 47 [BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE] = "percpu_cgroup_storage", 48 [BPF_MAP_TYPE_QUEUE] = "queue", 49 [BPF_MAP_TYPE_STACK] = "stack", 50 [BPF_MAP_TYPE_SK_STORAGE] = "sk_storage", 51 [BPF_MAP_TYPE_STRUCT_OPS] = "struct_ops", 52 }; 53 54 const size_t map_type_name_size = ARRAY_SIZE(map_type_name); 55 56 static bool map_is_per_cpu(__u32 type) 57 { 58 return type == BPF_MAP_TYPE_PERCPU_HASH || 59 type == BPF_MAP_TYPE_PERCPU_ARRAY || 60 type == BPF_MAP_TYPE_LRU_PERCPU_HASH || 61 type == BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE; 62 } 63 64 static bool map_is_map_of_maps(__u32 type) 65 { 66 return type == BPF_MAP_TYPE_ARRAY_OF_MAPS || 67 type == BPF_MAP_TYPE_HASH_OF_MAPS; 68 } 69 70 static bool map_is_map_of_progs(__u32 type) 71 { 72 return type == BPF_MAP_TYPE_PROG_ARRAY; 73 } 74 75 static int map_type_from_str(const char *type) 76 { 77 unsigned int i; 78 79 for (i = 0; i < ARRAY_SIZE(map_type_name); i++) 80 /* Don't allow prefixing in case of possible future shadowing */ 81 if (map_type_name[i] && !strcmp(map_type_name[i], type)) 82 return i; 83 return -1; 84 } 85 86 static void *alloc_value(struct bpf_map_info *info) 87 { 88 if (map_is_per_cpu(info->type)) 89 return malloc(round_up(info->value_size, 8) * 90 get_possible_cpus()); 91 else 92 return malloc(info->value_size); 93 } 94 95 static int map_fd_by_name(char *name, int **fds) 96 { 97 unsigned int id = 0; 98 int fd, nb_fds = 0; 99 void *tmp; 100 int err; 101 102 while (true) { 103 struct bpf_map_info info = {}; 104 __u32 len = sizeof(info); 105 106 err = bpf_map_get_next_id(id, &id); 107 if (err) { 108 if (errno != ENOENT) { 109 p_err("%s", strerror(errno)); 110 goto err_close_fds; 111 } 112 return nb_fds; 113 } 114 115 fd = bpf_map_get_fd_by_id(id); 116 if (fd < 0) { 117 p_err("can't get map by id (%u): %s", 118 id, strerror(errno)); 119 goto err_close_fds; 120 } 121 122 err = bpf_obj_get_info_by_fd(fd, &info, &len); 123 if (err) { 124 p_err("can't get map info (%u): %s", 125 id, strerror(errno)); 126 goto err_close_fd; 127 } 128 129 if (strncmp(name, info.name, BPF_OBJ_NAME_LEN)) { 130 close(fd); 131 continue; 132 } 133 134 if (nb_fds > 0) { 135 tmp = realloc(*fds, (nb_fds + 1) * sizeof(int)); 136 if (!tmp) { 137 p_err("failed to realloc"); 138 goto err_close_fd; 139 } 140 *fds = tmp; 141 } 142 (*fds)[nb_fds++] = fd; 143 } 144 145 err_close_fd: 146 close(fd); 147 err_close_fds: 148 while (--nb_fds >= 0) 149 close((*fds)[nb_fds]); 150 return -1; 151 } 152 153 static int map_parse_fds(int *argc, char ***argv, int **fds) 154 { 155 if (is_prefix(**argv, "id")) { 156 unsigned int id; 157 char *endptr; 158 159 NEXT_ARGP(); 160 161 id = strtoul(**argv, &endptr, 0); 162 if (*endptr) { 163 p_err("can't parse %s as ID", **argv); 164 return -1; 165 } 166 NEXT_ARGP(); 167 168 (*fds)[0] = bpf_map_get_fd_by_id(id); 169 if ((*fds)[0] < 0) { 170 p_err("get map by id (%u): %s", id, strerror(errno)); 171 return -1; 172 } 173 return 1; 174 } else if (is_prefix(**argv, "name")) { 175 char *name; 176 177 NEXT_ARGP(); 178 179 name = **argv; 180 if (strlen(name) > BPF_OBJ_NAME_LEN - 1) { 181 p_err("can't parse name"); 182 return -1; 183 } 184 NEXT_ARGP(); 185 186 return map_fd_by_name(name, fds); 187 } else if (is_prefix(**argv, "pinned")) { 188 char *path; 189 190 NEXT_ARGP(); 191 192 path = **argv; 193 NEXT_ARGP(); 194 195 (*fds)[0] = open_obj_pinned_any(path, BPF_OBJ_MAP); 196 if ((*fds)[0] < 0) 197 return -1; 198 return 1; 199 } 200 201 p_err("expected 'id', 'name' or 'pinned', got: '%s'?", **argv); 202 return -1; 203 } 204 205 int map_parse_fd(int *argc, char ***argv) 206 { 207 int *fds = NULL; 208 int nb_fds, fd; 209 210 fds = malloc(sizeof(int)); 211 if (!fds) { 212 p_err("mem alloc failed"); 213 return -1; 214 } 215 nb_fds = map_parse_fds(argc, argv, &fds); 216 if (nb_fds != 1) { 217 if (nb_fds > 1) { 218 p_err("several maps match this handle"); 219 while (nb_fds--) 220 close(fds[nb_fds]); 221 } 222 fd = -1; 223 goto exit_free; 224 } 225 226 fd = fds[0]; 227 exit_free: 228 free(fds); 229 return fd; 230 } 231 232 int map_parse_fd_and_info(int *argc, char ***argv, void *info, __u32 *info_len) 233 { 234 int err; 235 int fd; 236 237 fd = map_parse_fd(argc, argv); 238 if (fd < 0) 239 return -1; 240 241 err = bpf_obj_get_info_by_fd(fd, info, info_len); 242 if (err) { 243 p_err("can't get map info: %s", strerror(errno)); 244 close(fd); 245 return err; 246 } 247 248 return fd; 249 } 250 251 static int do_dump_btf(const struct btf_dumper *d, 252 struct bpf_map_info *map_info, void *key, 253 void *value) 254 { 255 __u32 value_id; 256 int ret; 257 258 /* start of key-value pair */ 259 jsonw_start_object(d->jw); 260 261 if (map_info->btf_key_type_id) { 262 jsonw_name(d->jw, "key"); 263 264 ret = btf_dumper_type(d, map_info->btf_key_type_id, key); 265 if (ret) 266 goto err_end_obj; 267 } 268 269 value_id = map_info->btf_vmlinux_value_type_id ? 270 : map_info->btf_value_type_id; 271 272 if (!map_is_per_cpu(map_info->type)) { 273 jsonw_name(d->jw, "value"); 274 ret = btf_dumper_type(d, value_id, value); 275 } else { 276 unsigned int i, n, step; 277 278 jsonw_name(d->jw, "values"); 279 jsonw_start_array(d->jw); 280 n = get_possible_cpus(); 281 step = round_up(map_info->value_size, 8); 282 for (i = 0; i < n; i++) { 283 jsonw_start_object(d->jw); 284 jsonw_int_field(d->jw, "cpu", i); 285 jsonw_name(d->jw, "value"); 286 ret = btf_dumper_type(d, value_id, value + i * step); 287 jsonw_end_object(d->jw); 288 if (ret) 289 break; 290 } 291 jsonw_end_array(d->jw); 292 } 293 294 err_end_obj: 295 /* end of key-value pair */ 296 jsonw_end_object(d->jw); 297 298 return ret; 299 } 300 301 static json_writer_t *get_btf_writer(void) 302 { 303 json_writer_t *jw = jsonw_new(stdout); 304 305 if (!jw) 306 return NULL; 307 jsonw_pretty(jw, true); 308 309 return jw; 310 } 311 312 static void print_entry_json(struct bpf_map_info *info, unsigned char *key, 313 unsigned char *value, struct btf *btf) 314 { 315 jsonw_start_object(json_wtr); 316 317 if (!map_is_per_cpu(info->type)) { 318 jsonw_name(json_wtr, "key"); 319 print_hex_data_json(key, info->key_size); 320 jsonw_name(json_wtr, "value"); 321 print_hex_data_json(value, info->value_size); 322 if (btf) { 323 struct btf_dumper d = { 324 .btf = btf, 325 .jw = json_wtr, 326 .is_plain_text = false, 327 }; 328 329 jsonw_name(json_wtr, "formatted"); 330 do_dump_btf(&d, info, key, value); 331 } 332 } else { 333 unsigned int i, n, step; 334 335 n = get_possible_cpus(); 336 step = round_up(info->value_size, 8); 337 338 jsonw_name(json_wtr, "key"); 339 print_hex_data_json(key, info->key_size); 340 341 jsonw_name(json_wtr, "values"); 342 jsonw_start_array(json_wtr); 343 for (i = 0; i < n; i++) { 344 jsonw_start_object(json_wtr); 345 346 jsonw_int_field(json_wtr, "cpu", i); 347 348 jsonw_name(json_wtr, "value"); 349 print_hex_data_json(value + i * step, 350 info->value_size); 351 352 jsonw_end_object(json_wtr); 353 } 354 jsonw_end_array(json_wtr); 355 if (btf) { 356 struct btf_dumper d = { 357 .btf = btf, 358 .jw = json_wtr, 359 .is_plain_text = false, 360 }; 361 362 jsonw_name(json_wtr, "formatted"); 363 do_dump_btf(&d, info, key, value); 364 } 365 } 366 367 jsonw_end_object(json_wtr); 368 } 369 370 static void print_entry_error(struct bpf_map_info *info, unsigned char *key, 371 const char *error_msg) 372 { 373 int msg_size = strlen(error_msg); 374 bool single_line, break_names; 375 376 break_names = info->key_size > 16 || msg_size > 16; 377 single_line = info->key_size + msg_size <= 24 && !break_names; 378 379 printf("key:%c", break_names ? '\n' : ' '); 380 fprint_hex(stdout, key, info->key_size, " "); 381 382 printf(single_line ? " " : "\n"); 383 384 printf("value:%c%s", break_names ? '\n' : ' ', error_msg); 385 386 printf("\n"); 387 } 388 389 static void print_entry_plain(struct bpf_map_info *info, unsigned char *key, 390 unsigned char *value) 391 { 392 if (!map_is_per_cpu(info->type)) { 393 bool single_line, break_names; 394 395 break_names = info->key_size > 16 || info->value_size > 16; 396 single_line = info->key_size + info->value_size <= 24 && 397 !break_names; 398 399 if (info->key_size) { 400 printf("key:%c", break_names ? '\n' : ' '); 401 fprint_hex(stdout, key, info->key_size, " "); 402 403 printf(single_line ? " " : "\n"); 404 } 405 406 if (info->value_size) { 407 printf("value:%c", break_names ? '\n' : ' '); 408 fprint_hex(stdout, value, info->value_size, " "); 409 } 410 411 printf("\n"); 412 } else { 413 unsigned int i, n, step; 414 415 n = get_possible_cpus(); 416 step = round_up(info->value_size, 8); 417 418 if (info->key_size) { 419 printf("key:\n"); 420 fprint_hex(stdout, key, info->key_size, " "); 421 printf("\n"); 422 } 423 if (info->value_size) { 424 for (i = 0; i < n; i++) { 425 printf("value (CPU %02d):%c", 426 i, info->value_size > 16 ? '\n' : ' '); 427 fprint_hex(stdout, value + i * step, 428 info->value_size, " "); 429 printf("\n"); 430 } 431 } 432 } 433 } 434 435 static char **parse_bytes(char **argv, const char *name, unsigned char *val, 436 unsigned int n) 437 { 438 unsigned int i = 0, base = 0; 439 char *endptr; 440 441 if (is_prefix(*argv, "hex")) { 442 base = 16; 443 argv++; 444 } 445 446 while (i < n && argv[i]) { 447 val[i] = strtoul(argv[i], &endptr, base); 448 if (*endptr) { 449 p_err("error parsing byte: %s", argv[i]); 450 return NULL; 451 } 452 i++; 453 } 454 455 if (i != n) { 456 p_err("%s expected %d bytes got %d", name, n, i); 457 return NULL; 458 } 459 460 return argv + i; 461 } 462 463 /* on per cpu maps we must copy the provided value on all value instances */ 464 static void fill_per_cpu_value(struct bpf_map_info *info, void *value) 465 { 466 unsigned int i, n, step; 467 468 if (!map_is_per_cpu(info->type)) 469 return; 470 471 n = get_possible_cpus(); 472 step = round_up(info->value_size, 8); 473 for (i = 1; i < n; i++) 474 memcpy(value + i * step, value, info->value_size); 475 } 476 477 static int parse_elem(char **argv, struct bpf_map_info *info, 478 void *key, void *value, __u32 key_size, __u32 value_size, 479 __u32 *flags, __u32 **value_fd) 480 { 481 if (!*argv) { 482 if (!key && !value) 483 return 0; 484 p_err("did not find %s", key ? "key" : "value"); 485 return -1; 486 } 487 488 if (is_prefix(*argv, "key")) { 489 if (!key) { 490 if (key_size) 491 p_err("duplicate key"); 492 else 493 p_err("unnecessary key"); 494 return -1; 495 } 496 497 argv = parse_bytes(argv + 1, "key", key, key_size); 498 if (!argv) 499 return -1; 500 501 return parse_elem(argv, info, NULL, value, key_size, value_size, 502 flags, value_fd); 503 } else if (is_prefix(*argv, "value")) { 504 int fd; 505 506 if (!value) { 507 if (value_size) 508 p_err("duplicate value"); 509 else 510 p_err("unnecessary value"); 511 return -1; 512 } 513 514 argv++; 515 516 if (map_is_map_of_maps(info->type)) { 517 int argc = 2; 518 519 if (value_size != 4) { 520 p_err("value smaller than 4B for map in map?"); 521 return -1; 522 } 523 if (!argv[0] || !argv[1]) { 524 p_err("not enough value arguments for map in map"); 525 return -1; 526 } 527 528 fd = map_parse_fd(&argc, &argv); 529 if (fd < 0) 530 return -1; 531 532 *value_fd = value; 533 **value_fd = fd; 534 } else if (map_is_map_of_progs(info->type)) { 535 int argc = 2; 536 537 if (value_size != 4) { 538 p_err("value smaller than 4B for map of progs?"); 539 return -1; 540 } 541 if (!argv[0] || !argv[1]) { 542 p_err("not enough value arguments for map of progs"); 543 return -1; 544 } 545 if (is_prefix(*argv, "id")) 546 p_info("Warning: updating program array via MAP_ID, make sure this map is kept open\n" 547 " by some process or pinned otherwise update will be lost"); 548 549 fd = prog_parse_fd(&argc, &argv); 550 if (fd < 0) 551 return -1; 552 553 *value_fd = value; 554 **value_fd = fd; 555 } else { 556 argv = parse_bytes(argv, "value", value, value_size); 557 if (!argv) 558 return -1; 559 560 fill_per_cpu_value(info, value); 561 } 562 563 return parse_elem(argv, info, key, NULL, key_size, value_size, 564 flags, NULL); 565 } else if (is_prefix(*argv, "any") || is_prefix(*argv, "noexist") || 566 is_prefix(*argv, "exist")) { 567 if (!flags) { 568 p_err("flags specified multiple times: %s", *argv); 569 return -1; 570 } 571 572 if (is_prefix(*argv, "any")) 573 *flags = BPF_ANY; 574 else if (is_prefix(*argv, "noexist")) 575 *flags = BPF_NOEXIST; 576 else if (is_prefix(*argv, "exist")) 577 *flags = BPF_EXIST; 578 579 return parse_elem(argv + 1, info, key, value, key_size, 580 value_size, NULL, value_fd); 581 } 582 583 p_err("expected key or value, got: %s", *argv); 584 return -1; 585 } 586 587 static void show_map_header_json(struct bpf_map_info *info, json_writer_t *wtr) 588 { 589 jsonw_uint_field(wtr, "id", info->id); 590 if (info->type < ARRAY_SIZE(map_type_name)) 591 jsonw_string_field(wtr, "type", map_type_name[info->type]); 592 else 593 jsonw_uint_field(wtr, "type", info->type); 594 595 if (*info->name) 596 jsonw_string_field(wtr, "name", info->name); 597 598 jsonw_name(wtr, "flags"); 599 jsonw_printf(wtr, "%d", info->map_flags); 600 } 601 602 static int show_map_close_json(int fd, struct bpf_map_info *info) 603 { 604 char *memlock, *frozen_str; 605 int frozen = 0; 606 607 memlock = get_fdinfo(fd, "memlock"); 608 frozen_str = get_fdinfo(fd, "frozen"); 609 610 jsonw_start_object(json_wtr); 611 612 show_map_header_json(info, json_wtr); 613 614 print_dev_json(info->ifindex, info->netns_dev, info->netns_ino); 615 616 jsonw_uint_field(json_wtr, "bytes_key", info->key_size); 617 jsonw_uint_field(json_wtr, "bytes_value", info->value_size); 618 jsonw_uint_field(json_wtr, "max_entries", info->max_entries); 619 620 if (memlock) 621 jsonw_int_field(json_wtr, "bytes_memlock", atoi(memlock)); 622 free(memlock); 623 624 if (info->type == BPF_MAP_TYPE_PROG_ARRAY) { 625 char *owner_prog_type = get_fdinfo(fd, "owner_prog_type"); 626 char *owner_jited = get_fdinfo(fd, "owner_jited"); 627 628 if (owner_prog_type) { 629 unsigned int prog_type = atoi(owner_prog_type); 630 631 if (prog_type < ARRAY_SIZE(prog_type_name)) 632 jsonw_string_field(json_wtr, "owner_prog_type", 633 prog_type_name[prog_type]); 634 else 635 jsonw_uint_field(json_wtr, "owner_prog_type", 636 prog_type); 637 } 638 if (owner_jited) 639 jsonw_bool_field(json_wtr, "owner_jited", 640 !!atoi(owner_jited)); 641 642 free(owner_prog_type); 643 free(owner_jited); 644 } 645 close(fd); 646 647 if (frozen_str) { 648 frozen = atoi(frozen_str); 649 free(frozen_str); 650 } 651 jsonw_int_field(json_wtr, "frozen", frozen); 652 653 if (info->btf_id) 654 jsonw_int_field(json_wtr, "btf_id", info->btf_id); 655 656 if (!hash_empty(map_table.table)) { 657 struct pinned_obj *obj; 658 659 jsonw_name(json_wtr, "pinned"); 660 jsonw_start_array(json_wtr); 661 hash_for_each_possible(map_table.table, obj, hash, info->id) { 662 if (obj->id == info->id) 663 jsonw_string(json_wtr, obj->path); 664 } 665 jsonw_end_array(json_wtr); 666 } 667 668 jsonw_end_object(json_wtr); 669 670 return 0; 671 } 672 673 static void show_map_header_plain(struct bpf_map_info *info) 674 { 675 printf("%u: ", info->id); 676 if (info->type < ARRAY_SIZE(map_type_name)) 677 printf("%s ", map_type_name[info->type]); 678 else 679 printf("type %u ", info->type); 680 681 if (*info->name) 682 printf("name %s ", info->name); 683 684 printf("flags 0x%x", info->map_flags); 685 print_dev_plain(info->ifindex, info->netns_dev, info->netns_ino); 686 printf("\n"); 687 } 688 689 static int show_map_close_plain(int fd, struct bpf_map_info *info) 690 { 691 char *memlock, *frozen_str; 692 int frozen = 0; 693 694 memlock = get_fdinfo(fd, "memlock"); 695 frozen_str = get_fdinfo(fd, "frozen"); 696 697 show_map_header_plain(info); 698 printf("\tkey %uB value %uB max_entries %u", 699 info->key_size, info->value_size, info->max_entries); 700 701 if (memlock) 702 printf(" memlock %sB", memlock); 703 free(memlock); 704 705 if (info->type == BPF_MAP_TYPE_PROG_ARRAY) { 706 char *owner_prog_type = get_fdinfo(fd, "owner_prog_type"); 707 char *owner_jited = get_fdinfo(fd, "owner_jited"); 708 709 if (owner_prog_type || owner_jited) 710 printf("\n\t"); 711 if (owner_prog_type) { 712 unsigned int prog_type = atoi(owner_prog_type); 713 714 if (prog_type < ARRAY_SIZE(prog_type_name)) 715 printf("owner_prog_type %s ", 716 prog_type_name[prog_type]); 717 else 718 printf("owner_prog_type %d ", prog_type); 719 } 720 if (owner_jited) 721 printf("owner%s jited", 722 atoi(owner_jited) ? "" : " not"); 723 724 free(owner_prog_type); 725 free(owner_jited); 726 } 727 close(fd); 728 729 if (!hash_empty(map_table.table)) { 730 struct pinned_obj *obj; 731 732 hash_for_each_possible(map_table.table, obj, hash, info->id) { 733 if (obj->id == info->id) 734 printf("\n\tpinned %s", obj->path); 735 } 736 } 737 printf("\n"); 738 739 if (frozen_str) { 740 frozen = atoi(frozen_str); 741 free(frozen_str); 742 } 743 744 if (!info->btf_id && !frozen) 745 return 0; 746 747 printf("\t"); 748 749 if (info->btf_id) 750 printf("btf_id %d", info->btf_id); 751 752 if (frozen) 753 printf("%sfrozen", info->btf_id ? " " : ""); 754 755 printf("\n"); 756 return 0; 757 } 758 759 static int do_show_subset(int argc, char **argv) 760 { 761 struct bpf_map_info info = {}; 762 __u32 len = sizeof(info); 763 int *fds = NULL; 764 int nb_fds, i; 765 int err = -1; 766 767 fds = malloc(sizeof(int)); 768 if (!fds) { 769 p_err("mem alloc failed"); 770 return -1; 771 } 772 nb_fds = map_parse_fds(&argc, &argv, &fds); 773 if (nb_fds < 1) 774 goto exit_free; 775 776 if (json_output && nb_fds > 1) 777 jsonw_start_array(json_wtr); /* root array */ 778 for (i = 0; i < nb_fds; i++) { 779 err = bpf_obj_get_info_by_fd(fds[i], &info, &len); 780 if (err) { 781 p_err("can't get map info: %s", 782 strerror(errno)); 783 for (; i < nb_fds; i++) 784 close(fds[i]); 785 break; 786 } 787 788 if (json_output) 789 show_map_close_json(fds[i], &info); 790 else 791 show_map_close_plain(fds[i], &info); 792 793 close(fds[i]); 794 } 795 if (json_output && nb_fds > 1) 796 jsonw_end_array(json_wtr); /* root array */ 797 798 exit_free: 799 free(fds); 800 return err; 801 } 802 803 static int do_show(int argc, char **argv) 804 { 805 struct bpf_map_info info = {}; 806 __u32 len = sizeof(info); 807 __u32 id = 0; 808 int err; 809 int fd; 810 811 if (show_pinned) 812 build_pinned_obj_table(&map_table, BPF_OBJ_MAP); 813 814 if (argc == 2) 815 return do_show_subset(argc, argv); 816 817 if (argc) 818 return BAD_ARG(); 819 820 if (json_output) 821 jsonw_start_array(json_wtr); 822 while (true) { 823 err = bpf_map_get_next_id(id, &id); 824 if (err) { 825 if (errno == ENOENT) 826 break; 827 p_err("can't get next map: %s%s", strerror(errno), 828 errno == EINVAL ? " -- kernel too old?" : ""); 829 break; 830 } 831 832 fd = bpf_map_get_fd_by_id(id); 833 if (fd < 0) { 834 if (errno == ENOENT) 835 continue; 836 p_err("can't get map by id (%u): %s", 837 id, strerror(errno)); 838 break; 839 } 840 841 err = bpf_obj_get_info_by_fd(fd, &info, &len); 842 if (err) { 843 p_err("can't get map info: %s", strerror(errno)); 844 close(fd); 845 break; 846 } 847 848 if (json_output) 849 show_map_close_json(fd, &info); 850 else 851 show_map_close_plain(fd, &info); 852 } 853 if (json_output) 854 jsonw_end_array(json_wtr); 855 856 return errno == ENOENT ? 0 : -1; 857 } 858 859 static int dump_map_elem(int fd, void *key, void *value, 860 struct bpf_map_info *map_info, struct btf *btf, 861 json_writer_t *btf_wtr) 862 { 863 int num_elems = 0; 864 int lookup_errno; 865 866 if (!bpf_map_lookup_elem(fd, key, value)) { 867 if (json_output) { 868 print_entry_json(map_info, key, value, btf); 869 } else { 870 if (btf) { 871 struct btf_dumper d = { 872 .btf = btf, 873 .jw = btf_wtr, 874 .is_plain_text = true, 875 }; 876 877 do_dump_btf(&d, map_info, key, value); 878 } else { 879 print_entry_plain(map_info, key, value); 880 } 881 num_elems++; 882 } 883 return num_elems; 884 } 885 886 /* lookup error handling */ 887 lookup_errno = errno; 888 889 if (map_is_map_of_maps(map_info->type) || 890 map_is_map_of_progs(map_info->type)) 891 return 0; 892 893 if (json_output) { 894 jsonw_start_object(json_wtr); 895 jsonw_name(json_wtr, "key"); 896 print_hex_data_json(key, map_info->key_size); 897 jsonw_name(json_wtr, "value"); 898 jsonw_start_object(json_wtr); 899 jsonw_string_field(json_wtr, "error", strerror(lookup_errno)); 900 jsonw_end_object(json_wtr); 901 jsonw_end_object(json_wtr); 902 } else { 903 const char *msg = NULL; 904 905 if (lookup_errno == ENOENT) 906 msg = "<no entry>"; 907 else if (lookup_errno == ENOSPC && 908 map_info->type == BPF_MAP_TYPE_REUSEPORT_SOCKARRAY) 909 msg = "<cannot read>"; 910 911 print_entry_error(map_info, key, 912 msg ? : strerror(lookup_errno)); 913 } 914 915 return 0; 916 } 917 918 static int maps_have_btf(int *fds, int nb_fds) 919 { 920 struct bpf_map_info info = {}; 921 __u32 len = sizeof(info); 922 int err, i; 923 924 for (i = 0; i < nb_fds; i++) { 925 err = bpf_obj_get_info_by_fd(fds[i], &info, &len); 926 if (err) { 927 p_err("can't get map info: %s", strerror(errno)); 928 return -1; 929 } 930 931 if (!info.btf_id) 932 return 0; 933 } 934 935 return 1; 936 } 937 938 static struct btf *btf_vmlinux; 939 940 static struct btf *get_map_kv_btf(const struct bpf_map_info *info) 941 { 942 struct btf *btf = NULL; 943 944 if (info->btf_vmlinux_value_type_id) { 945 if (!btf_vmlinux) { 946 btf_vmlinux = libbpf_find_kernel_btf(); 947 if (IS_ERR(btf_vmlinux)) 948 p_err("failed to get kernel btf"); 949 } 950 return btf_vmlinux; 951 } else if (info->btf_value_type_id) { 952 int err; 953 954 err = btf__get_from_id(info->btf_id, &btf); 955 if (err || !btf) { 956 p_err("failed to get btf"); 957 btf = err ? ERR_PTR(err) : ERR_PTR(-ESRCH); 958 } 959 } 960 961 return btf; 962 } 963 964 static void free_map_kv_btf(struct btf *btf) 965 { 966 if (!IS_ERR(btf) && btf != btf_vmlinux) 967 btf__free(btf); 968 } 969 970 static void free_btf_vmlinux(void) 971 { 972 if (!IS_ERR(btf_vmlinux)) 973 btf__free(btf_vmlinux); 974 } 975 976 static int 977 map_dump(int fd, struct bpf_map_info *info, json_writer_t *wtr, 978 bool show_header) 979 { 980 void *key, *value, *prev_key; 981 unsigned int num_elems = 0; 982 struct btf *btf = NULL; 983 int err; 984 985 key = malloc(info->key_size); 986 value = alloc_value(info); 987 if (!key || !value) { 988 p_err("mem alloc failed"); 989 err = -1; 990 goto exit_free; 991 } 992 993 prev_key = NULL; 994 995 if (wtr) { 996 btf = get_map_kv_btf(info); 997 if (IS_ERR(btf)) { 998 err = PTR_ERR(btf); 999 goto exit_free; 1000 } 1001 1002 if (show_header) { 1003 jsonw_start_object(wtr); /* map object */ 1004 show_map_header_json(info, wtr); 1005 jsonw_name(wtr, "elements"); 1006 } 1007 jsonw_start_array(wtr); /* elements */ 1008 } else if (show_header) { 1009 show_map_header_plain(info); 1010 } 1011 1012 if (info->type == BPF_MAP_TYPE_REUSEPORT_SOCKARRAY && 1013 info->value_size != 8) 1014 p_info("Warning: cannot read values from %s map with value_size != 8", 1015 map_type_name[info->type]); 1016 while (true) { 1017 err = bpf_map_get_next_key(fd, prev_key, key); 1018 if (err) { 1019 if (errno == ENOENT) 1020 err = 0; 1021 break; 1022 } 1023 num_elems += dump_map_elem(fd, key, value, info, btf, wtr); 1024 prev_key = key; 1025 } 1026 1027 if (wtr) { 1028 jsonw_end_array(wtr); /* elements */ 1029 if (show_header) 1030 jsonw_end_object(wtr); /* map object */ 1031 } else { 1032 printf("Found %u element%s\n", num_elems, 1033 num_elems != 1 ? "s" : ""); 1034 } 1035 1036 exit_free: 1037 free(key); 1038 free(value); 1039 close(fd); 1040 free_map_kv_btf(btf); 1041 1042 return err; 1043 } 1044 1045 static int do_dump(int argc, char **argv) 1046 { 1047 json_writer_t *wtr = NULL, *btf_wtr = NULL; 1048 struct bpf_map_info info = {}; 1049 int nb_fds, i = 0; 1050 __u32 len = sizeof(info); 1051 int *fds = NULL; 1052 int err = -1; 1053 1054 if (argc != 2) 1055 usage(); 1056 1057 fds = malloc(sizeof(int)); 1058 if (!fds) { 1059 p_err("mem alloc failed"); 1060 return -1; 1061 } 1062 nb_fds = map_parse_fds(&argc, &argv, &fds); 1063 if (nb_fds < 1) 1064 goto exit_free; 1065 1066 if (json_output) { 1067 wtr = json_wtr; 1068 } else { 1069 int do_plain_btf; 1070 1071 do_plain_btf = maps_have_btf(fds, nb_fds); 1072 if (do_plain_btf < 0) 1073 goto exit_close; 1074 1075 if (do_plain_btf) { 1076 btf_wtr = get_btf_writer(); 1077 wtr = btf_wtr; 1078 if (!btf_wtr) 1079 p_info("failed to create json writer for btf. falling back to plain output"); 1080 } 1081 } 1082 1083 if (wtr && nb_fds > 1) 1084 jsonw_start_array(wtr); /* root array */ 1085 for (i = 0; i < nb_fds; i++) { 1086 if (bpf_obj_get_info_by_fd(fds[i], &info, &len)) { 1087 p_err("can't get map info: %s", strerror(errno)); 1088 break; 1089 } 1090 err = map_dump(fds[i], &info, wtr, nb_fds > 1); 1091 if (!wtr && i != nb_fds - 1) 1092 printf("\n"); 1093 1094 if (err) 1095 break; 1096 close(fds[i]); 1097 } 1098 if (wtr && nb_fds > 1) 1099 jsonw_end_array(wtr); /* root array */ 1100 1101 if (btf_wtr) 1102 jsonw_destroy(&btf_wtr); 1103 exit_close: 1104 for (; i < nb_fds; i++) 1105 close(fds[i]); 1106 exit_free: 1107 free(fds); 1108 free_btf_vmlinux(); 1109 return err; 1110 } 1111 1112 static int alloc_key_value(struct bpf_map_info *info, void **key, void **value) 1113 { 1114 *key = NULL; 1115 *value = NULL; 1116 1117 if (info->key_size) { 1118 *key = malloc(info->key_size); 1119 if (!*key) { 1120 p_err("key mem alloc failed"); 1121 return -1; 1122 } 1123 } 1124 1125 if (info->value_size) { 1126 *value = alloc_value(info); 1127 if (!*value) { 1128 p_err("value mem alloc failed"); 1129 free(*key); 1130 *key = NULL; 1131 return -1; 1132 } 1133 } 1134 1135 return 0; 1136 } 1137 1138 static int do_update(int argc, char **argv) 1139 { 1140 struct bpf_map_info info = {}; 1141 __u32 len = sizeof(info); 1142 __u32 *value_fd = NULL; 1143 __u32 flags = BPF_ANY; 1144 void *key, *value; 1145 int fd, err; 1146 1147 if (argc < 2) 1148 usage(); 1149 1150 fd = map_parse_fd_and_info(&argc, &argv, &info, &len); 1151 if (fd < 0) 1152 return -1; 1153 1154 err = alloc_key_value(&info, &key, &value); 1155 if (err) 1156 goto exit_free; 1157 1158 err = parse_elem(argv, &info, key, value, info.key_size, 1159 info.value_size, &flags, &value_fd); 1160 if (err) 1161 goto exit_free; 1162 1163 err = bpf_map_update_elem(fd, key, value, flags); 1164 if (err) { 1165 p_err("update failed: %s", strerror(errno)); 1166 goto exit_free; 1167 } 1168 1169 exit_free: 1170 if (value_fd) 1171 close(*value_fd); 1172 free(key); 1173 free(value); 1174 close(fd); 1175 1176 if (!err && json_output) 1177 jsonw_null(json_wtr); 1178 return err; 1179 } 1180 1181 static void print_key_value(struct bpf_map_info *info, void *key, 1182 void *value) 1183 { 1184 json_writer_t *btf_wtr; 1185 struct btf *btf = NULL; 1186 int err; 1187 1188 err = btf__get_from_id(info->btf_id, &btf); 1189 if (err) { 1190 p_err("failed to get btf"); 1191 return; 1192 } 1193 1194 if (json_output) { 1195 print_entry_json(info, key, value, btf); 1196 } else if (btf) { 1197 /* if here json_wtr wouldn't have been initialised, 1198 * so let's create separate writer for btf 1199 */ 1200 btf_wtr = get_btf_writer(); 1201 if (!btf_wtr) { 1202 p_info("failed to create json writer for btf. falling back to plain output"); 1203 btf__free(btf); 1204 btf = NULL; 1205 print_entry_plain(info, key, value); 1206 } else { 1207 struct btf_dumper d = { 1208 .btf = btf, 1209 .jw = btf_wtr, 1210 .is_plain_text = true, 1211 }; 1212 1213 do_dump_btf(&d, info, key, value); 1214 jsonw_destroy(&btf_wtr); 1215 } 1216 } else { 1217 print_entry_plain(info, key, value); 1218 } 1219 btf__free(btf); 1220 } 1221 1222 static int do_lookup(int argc, char **argv) 1223 { 1224 struct bpf_map_info info = {}; 1225 __u32 len = sizeof(info); 1226 void *key, *value; 1227 int err; 1228 int fd; 1229 1230 if (argc < 2) 1231 usage(); 1232 1233 fd = map_parse_fd_and_info(&argc, &argv, &info, &len); 1234 if (fd < 0) 1235 return -1; 1236 1237 err = alloc_key_value(&info, &key, &value); 1238 if (err) 1239 goto exit_free; 1240 1241 err = parse_elem(argv, &info, key, NULL, info.key_size, 0, NULL, NULL); 1242 if (err) 1243 goto exit_free; 1244 1245 err = bpf_map_lookup_elem(fd, key, value); 1246 if (err) { 1247 if (errno == ENOENT) { 1248 if (json_output) { 1249 jsonw_null(json_wtr); 1250 } else { 1251 printf("key:\n"); 1252 fprint_hex(stdout, key, info.key_size, " "); 1253 printf("\n\nNot found\n"); 1254 } 1255 } else { 1256 p_err("lookup failed: %s", strerror(errno)); 1257 } 1258 1259 goto exit_free; 1260 } 1261 1262 /* here means bpf_map_lookup_elem() succeeded */ 1263 print_key_value(&info, key, value); 1264 1265 exit_free: 1266 free(key); 1267 free(value); 1268 close(fd); 1269 1270 return err; 1271 } 1272 1273 static int do_getnext(int argc, char **argv) 1274 { 1275 struct bpf_map_info info = {}; 1276 __u32 len = sizeof(info); 1277 void *key, *nextkey; 1278 int err; 1279 int fd; 1280 1281 if (argc < 2) 1282 usage(); 1283 1284 fd = map_parse_fd_and_info(&argc, &argv, &info, &len); 1285 if (fd < 0) 1286 return -1; 1287 1288 key = malloc(info.key_size); 1289 nextkey = malloc(info.key_size); 1290 if (!key || !nextkey) { 1291 p_err("mem alloc failed"); 1292 err = -1; 1293 goto exit_free; 1294 } 1295 1296 if (argc) { 1297 err = parse_elem(argv, &info, key, NULL, info.key_size, 0, 1298 NULL, NULL); 1299 if (err) 1300 goto exit_free; 1301 } else { 1302 free(key); 1303 key = NULL; 1304 } 1305 1306 err = bpf_map_get_next_key(fd, key, nextkey); 1307 if (err) { 1308 p_err("can't get next key: %s", strerror(errno)); 1309 goto exit_free; 1310 } 1311 1312 if (json_output) { 1313 jsonw_start_object(json_wtr); 1314 if (key) { 1315 jsonw_name(json_wtr, "key"); 1316 print_hex_data_json(key, info.key_size); 1317 } else { 1318 jsonw_null_field(json_wtr, "key"); 1319 } 1320 jsonw_name(json_wtr, "next_key"); 1321 print_hex_data_json(nextkey, info.key_size); 1322 jsonw_end_object(json_wtr); 1323 } else { 1324 if (key) { 1325 printf("key:\n"); 1326 fprint_hex(stdout, key, info.key_size, " "); 1327 printf("\n"); 1328 } else { 1329 printf("key: None\n"); 1330 } 1331 printf("next key:\n"); 1332 fprint_hex(stdout, nextkey, info.key_size, " "); 1333 printf("\n"); 1334 } 1335 1336 exit_free: 1337 free(nextkey); 1338 free(key); 1339 close(fd); 1340 1341 return err; 1342 } 1343 1344 static int do_delete(int argc, char **argv) 1345 { 1346 struct bpf_map_info info = {}; 1347 __u32 len = sizeof(info); 1348 void *key; 1349 int err; 1350 int fd; 1351 1352 if (argc < 2) 1353 usage(); 1354 1355 fd = map_parse_fd_and_info(&argc, &argv, &info, &len); 1356 if (fd < 0) 1357 return -1; 1358 1359 key = malloc(info.key_size); 1360 if (!key) { 1361 p_err("mem alloc failed"); 1362 err = -1; 1363 goto exit_free; 1364 } 1365 1366 err = parse_elem(argv, &info, key, NULL, info.key_size, 0, NULL, NULL); 1367 if (err) 1368 goto exit_free; 1369 1370 err = bpf_map_delete_elem(fd, key); 1371 if (err) 1372 p_err("delete failed: %s", strerror(errno)); 1373 1374 exit_free: 1375 free(key); 1376 close(fd); 1377 1378 if (!err && json_output) 1379 jsonw_null(json_wtr); 1380 return err; 1381 } 1382 1383 static int do_pin(int argc, char **argv) 1384 { 1385 int err; 1386 1387 err = do_pin_any(argc, argv, map_parse_fd); 1388 if (!err && json_output) 1389 jsonw_null(json_wtr); 1390 return err; 1391 } 1392 1393 static int do_create(int argc, char **argv) 1394 { 1395 struct bpf_create_map_attr attr = { NULL, }; 1396 const char *pinfile; 1397 int err, fd; 1398 1399 if (!REQ_ARGS(7)) 1400 return -1; 1401 pinfile = GET_ARG(); 1402 1403 while (argc) { 1404 if (!REQ_ARGS(2)) 1405 return -1; 1406 1407 if (is_prefix(*argv, "type")) { 1408 NEXT_ARG(); 1409 1410 if (attr.map_type) { 1411 p_err("map type already specified"); 1412 return -1; 1413 } 1414 1415 attr.map_type = map_type_from_str(*argv); 1416 if ((int)attr.map_type < 0) { 1417 p_err("unrecognized map type: %s", *argv); 1418 return -1; 1419 } 1420 NEXT_ARG(); 1421 } else if (is_prefix(*argv, "name")) { 1422 NEXT_ARG(); 1423 attr.name = GET_ARG(); 1424 } else if (is_prefix(*argv, "key")) { 1425 if (parse_u32_arg(&argc, &argv, &attr.key_size, 1426 "key size")) 1427 return -1; 1428 } else if (is_prefix(*argv, "value")) { 1429 if (parse_u32_arg(&argc, &argv, &attr.value_size, 1430 "value size")) 1431 return -1; 1432 } else if (is_prefix(*argv, "entries")) { 1433 if (parse_u32_arg(&argc, &argv, &attr.max_entries, 1434 "max entries")) 1435 return -1; 1436 } else if (is_prefix(*argv, "flags")) { 1437 if (parse_u32_arg(&argc, &argv, &attr.map_flags, 1438 "flags")) 1439 return -1; 1440 } else if (is_prefix(*argv, "dev")) { 1441 NEXT_ARG(); 1442 1443 if (attr.map_ifindex) { 1444 p_err("offload device already specified"); 1445 return -1; 1446 } 1447 1448 attr.map_ifindex = if_nametoindex(*argv); 1449 if (!attr.map_ifindex) { 1450 p_err("unrecognized netdevice '%s': %s", 1451 *argv, strerror(errno)); 1452 return -1; 1453 } 1454 NEXT_ARG(); 1455 } else { 1456 p_err("unknown arg %s", *argv); 1457 return -1; 1458 } 1459 } 1460 1461 if (!attr.name) { 1462 p_err("map name not specified"); 1463 return -1; 1464 } 1465 1466 set_max_rlimit(); 1467 1468 fd = bpf_create_map_xattr(&attr); 1469 if (fd < 0) { 1470 p_err("map create failed: %s", strerror(errno)); 1471 return -1; 1472 } 1473 1474 err = do_pin_fd(fd, pinfile); 1475 close(fd); 1476 if (err) 1477 return err; 1478 1479 if (json_output) 1480 jsonw_null(json_wtr); 1481 return 0; 1482 } 1483 1484 static int do_pop_dequeue(int argc, char **argv) 1485 { 1486 struct bpf_map_info info = {}; 1487 __u32 len = sizeof(info); 1488 void *key, *value; 1489 int err; 1490 int fd; 1491 1492 if (argc < 2) 1493 usage(); 1494 1495 fd = map_parse_fd_and_info(&argc, &argv, &info, &len); 1496 if (fd < 0) 1497 return -1; 1498 1499 err = alloc_key_value(&info, &key, &value); 1500 if (err) 1501 goto exit_free; 1502 1503 err = bpf_map_lookup_and_delete_elem(fd, key, value); 1504 if (err) { 1505 if (errno == ENOENT) { 1506 if (json_output) 1507 jsonw_null(json_wtr); 1508 else 1509 printf("Error: empty map\n"); 1510 } else { 1511 p_err("pop failed: %s", strerror(errno)); 1512 } 1513 1514 goto exit_free; 1515 } 1516 1517 print_key_value(&info, key, value); 1518 1519 exit_free: 1520 free(key); 1521 free(value); 1522 close(fd); 1523 1524 return err; 1525 } 1526 1527 static int do_freeze(int argc, char **argv) 1528 { 1529 int err, fd; 1530 1531 if (!REQ_ARGS(2)) 1532 return -1; 1533 1534 fd = map_parse_fd(&argc, &argv); 1535 if (fd < 0) 1536 return -1; 1537 1538 if (argc) { 1539 close(fd); 1540 return BAD_ARG(); 1541 } 1542 1543 err = bpf_map_freeze(fd); 1544 close(fd); 1545 if (err) { 1546 p_err("failed to freeze map: %s", strerror(errno)); 1547 return err; 1548 } 1549 1550 if (json_output) 1551 jsonw_null(json_wtr); 1552 1553 return 0; 1554 } 1555 1556 static int do_help(int argc, char **argv) 1557 { 1558 if (json_output) { 1559 jsonw_null(json_wtr); 1560 return 0; 1561 } 1562 1563 fprintf(stderr, 1564 "Usage: %1$s %2$s { show | list } [MAP]\n" 1565 " %1$s %2$s create FILE type TYPE key KEY_SIZE value VALUE_SIZE \\\n" 1566 " entries MAX_ENTRIES name NAME [flags FLAGS] \\\n" 1567 " [dev NAME]\n" 1568 " %1$s %2$s dump MAP\n" 1569 " %1$s %2$s update MAP [key DATA] [value VALUE] [UPDATE_FLAGS]\n" 1570 " %1$s %2$s lookup MAP [key DATA]\n" 1571 " %1$s %2$s getnext MAP [key DATA]\n" 1572 " %1$s %2$s delete MAP key DATA\n" 1573 " %1$s %2$s pin MAP FILE\n" 1574 " %1$s %2$s event_pipe MAP [cpu N index M]\n" 1575 " %1$s %2$s peek MAP\n" 1576 " %1$s %2$s push MAP value VALUE\n" 1577 " %1$s %2$s pop MAP\n" 1578 " %1$s %2$s enqueue MAP value VALUE\n" 1579 " %1$s %2$s dequeue MAP\n" 1580 " %1$s %2$s freeze MAP\n" 1581 " %1$s %2$s help\n" 1582 "\n" 1583 " " HELP_SPEC_MAP "\n" 1584 " DATA := { [hex] BYTES }\n" 1585 " " HELP_SPEC_PROGRAM "\n" 1586 " VALUE := { DATA | MAP | PROG }\n" 1587 " UPDATE_FLAGS := { any | exist | noexist }\n" 1588 " TYPE := { hash | array | prog_array | perf_event_array | percpu_hash |\n" 1589 " percpu_array | stack_trace | cgroup_array | lru_hash |\n" 1590 " lru_percpu_hash | lpm_trie | array_of_maps | hash_of_maps |\n" 1591 " devmap | devmap_hash | sockmap | cpumap | xskmap | sockhash |\n" 1592 " cgroup_storage | reuseport_sockarray | percpu_cgroup_storage |\n" 1593 " queue | stack | sk_storage | struct_ops }\n" 1594 " " HELP_SPEC_OPTIONS "\n" 1595 "", 1596 bin_name, argv[-2]); 1597 1598 return 0; 1599 } 1600 1601 static const struct cmd cmds[] = { 1602 { "show", do_show }, 1603 { "list", do_show }, 1604 { "help", do_help }, 1605 { "dump", do_dump }, 1606 { "update", do_update }, 1607 { "lookup", do_lookup }, 1608 { "getnext", do_getnext }, 1609 { "delete", do_delete }, 1610 { "pin", do_pin }, 1611 { "event_pipe", do_event_pipe }, 1612 { "create", do_create }, 1613 { "peek", do_lookup }, 1614 { "push", do_update }, 1615 { "enqueue", do_update }, 1616 { "pop", do_pop_dequeue }, 1617 { "dequeue", do_pop_dequeue }, 1618 { "freeze", do_freeze }, 1619 { 0 } 1620 }; 1621 1622 int do_map(int argc, char **argv) 1623 { 1624 return cmd_select(cmds, argc, argv, do_help); 1625 } 1626