1 /* 2 * Copyright (C) 2017-2018 Netronome Systems, Inc. 3 * 4 * This software is dual licensed under the GNU General License Version 2, 5 * June 1991 as shown in the file COPYING in the top-level directory of this 6 * source tree or the BSD 2-Clause License provided below. You have the 7 * option to license this software under the complete terms of either license. 8 * 9 * The BSD 2-Clause License: 10 * 11 * Redistribution and use in source and binary forms, with or 12 * without modification, are permitted provided that the following 13 * conditions are met: 14 * 15 * 1. Redistributions of source code must retain the above 16 * copyright notice, this list of conditions and the following 17 * disclaimer. 18 * 19 * 2. Redistributions in binary form must reproduce the above 20 * copyright notice, this list of conditions and the following 21 * disclaimer in the documentation and/or other materials 22 * provided with the distribution. 23 * 24 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 25 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 26 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 27 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 28 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 29 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 30 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 31 * SOFTWARE. 32 */ 33 34 /* Author: Jakub Kicinski <kubakici@wp.pl> */ 35 36 #include <assert.h> 37 #include <errno.h> 38 #include <fcntl.h> 39 #include <linux/kernel.h> 40 #include <stdbool.h> 41 #include <stdio.h> 42 #include <stdlib.h> 43 #include <string.h> 44 #include <unistd.h> 45 #include <sys/types.h> 46 #include <sys/stat.h> 47 48 #include <bpf.h> 49 50 #include "main.h" 51 52 static const char * const map_type_name[] = { 53 [BPF_MAP_TYPE_UNSPEC] = "unspec", 54 [BPF_MAP_TYPE_HASH] = "hash", 55 [BPF_MAP_TYPE_ARRAY] = "array", 56 [BPF_MAP_TYPE_PROG_ARRAY] = "prog_array", 57 [BPF_MAP_TYPE_PERF_EVENT_ARRAY] = "perf_event_array", 58 [BPF_MAP_TYPE_PERCPU_HASH] = "percpu_hash", 59 [BPF_MAP_TYPE_PERCPU_ARRAY] = "percpu_array", 60 [BPF_MAP_TYPE_STACK_TRACE] = "stack_trace", 61 [BPF_MAP_TYPE_CGROUP_ARRAY] = "cgroup_array", 62 [BPF_MAP_TYPE_LRU_HASH] = "lru_hash", 63 [BPF_MAP_TYPE_LRU_PERCPU_HASH] = "lru_percpu_hash", 64 [BPF_MAP_TYPE_LPM_TRIE] = "lpm_trie", 65 [BPF_MAP_TYPE_ARRAY_OF_MAPS] = "array_of_maps", 66 [BPF_MAP_TYPE_HASH_OF_MAPS] = "hash_of_maps", 67 [BPF_MAP_TYPE_DEVMAP] = "devmap", 68 [BPF_MAP_TYPE_SOCKMAP] = "sockmap", 69 [BPF_MAP_TYPE_CPUMAP] = "cpumap", 70 [BPF_MAP_TYPE_SOCKHASH] = "sockhash", 71 }; 72 73 static bool map_is_per_cpu(__u32 type) 74 { 75 return type == BPF_MAP_TYPE_PERCPU_HASH || 76 type == BPF_MAP_TYPE_PERCPU_ARRAY || 77 type == BPF_MAP_TYPE_LRU_PERCPU_HASH; 78 } 79 80 static bool map_is_map_of_maps(__u32 type) 81 { 82 return type == BPF_MAP_TYPE_ARRAY_OF_MAPS || 83 type == BPF_MAP_TYPE_HASH_OF_MAPS; 84 } 85 86 static bool map_is_map_of_progs(__u32 type) 87 { 88 return type == BPF_MAP_TYPE_PROG_ARRAY; 89 } 90 91 static void *alloc_value(struct bpf_map_info *info) 92 { 93 if (map_is_per_cpu(info->type)) 94 return malloc(round_up(info->value_size, 8) * 95 get_possible_cpus()); 96 else 97 return malloc(info->value_size); 98 } 99 100 static int map_parse_fd(int *argc, char ***argv) 101 { 102 int fd; 103 104 if (is_prefix(**argv, "id")) { 105 unsigned int id; 106 char *endptr; 107 108 NEXT_ARGP(); 109 110 id = strtoul(**argv, &endptr, 0); 111 if (*endptr) { 112 p_err("can't parse %s as ID", **argv); 113 return -1; 114 } 115 NEXT_ARGP(); 116 117 fd = bpf_map_get_fd_by_id(id); 118 if (fd < 0) 119 p_err("get map by id (%u): %s", id, strerror(errno)); 120 return fd; 121 } else if (is_prefix(**argv, "pinned")) { 122 char *path; 123 124 NEXT_ARGP(); 125 126 path = **argv; 127 NEXT_ARGP(); 128 129 return open_obj_pinned_any(path, BPF_OBJ_MAP); 130 } 131 132 p_err("expected 'id' or 'pinned', got: '%s'?", **argv); 133 return -1; 134 } 135 136 int map_parse_fd_and_info(int *argc, char ***argv, void *info, __u32 *info_len) 137 { 138 int err; 139 int fd; 140 141 fd = map_parse_fd(argc, argv); 142 if (fd < 0) 143 return -1; 144 145 err = bpf_obj_get_info_by_fd(fd, info, info_len); 146 if (err) { 147 p_err("can't get map info: %s", strerror(errno)); 148 close(fd); 149 return err; 150 } 151 152 return fd; 153 } 154 155 static void print_entry_json(struct bpf_map_info *info, unsigned char *key, 156 unsigned char *value) 157 { 158 jsonw_start_object(json_wtr); 159 160 if (!map_is_per_cpu(info->type)) { 161 jsonw_name(json_wtr, "key"); 162 print_hex_data_json(key, info->key_size); 163 jsonw_name(json_wtr, "value"); 164 print_hex_data_json(value, info->value_size); 165 } else { 166 unsigned int i, n, step; 167 168 n = get_possible_cpus(); 169 step = round_up(info->value_size, 8); 170 171 jsonw_name(json_wtr, "key"); 172 print_hex_data_json(key, info->key_size); 173 174 jsonw_name(json_wtr, "values"); 175 jsonw_start_array(json_wtr); 176 for (i = 0; i < n; i++) { 177 jsonw_start_object(json_wtr); 178 179 jsonw_int_field(json_wtr, "cpu", i); 180 181 jsonw_name(json_wtr, "value"); 182 print_hex_data_json(value + i * step, 183 info->value_size); 184 185 jsonw_end_object(json_wtr); 186 } 187 jsonw_end_array(json_wtr); 188 } 189 190 jsonw_end_object(json_wtr); 191 } 192 193 static void print_entry_plain(struct bpf_map_info *info, unsigned char *key, 194 unsigned char *value) 195 { 196 if (!map_is_per_cpu(info->type)) { 197 bool single_line, break_names; 198 199 break_names = info->key_size > 16 || info->value_size > 16; 200 single_line = info->key_size + info->value_size <= 24 && 201 !break_names; 202 203 printf("key:%c", break_names ? '\n' : ' '); 204 fprint_hex(stdout, key, info->key_size, " "); 205 206 printf(single_line ? " " : "\n"); 207 208 printf("value:%c", break_names ? '\n' : ' '); 209 fprint_hex(stdout, value, info->value_size, " "); 210 211 printf("\n"); 212 } else { 213 unsigned int i, n, step; 214 215 n = get_possible_cpus(); 216 step = round_up(info->value_size, 8); 217 218 printf("key:\n"); 219 fprint_hex(stdout, key, info->key_size, " "); 220 printf("\n"); 221 for (i = 0; i < n; i++) { 222 printf("value (CPU %02d):%c", 223 i, info->value_size > 16 ? '\n' : ' '); 224 fprint_hex(stdout, value + i * step, 225 info->value_size, " "); 226 printf("\n"); 227 } 228 } 229 } 230 231 static char **parse_bytes(char **argv, const char *name, unsigned char *val, 232 unsigned int n) 233 { 234 unsigned int i = 0, base = 0; 235 char *endptr; 236 237 if (is_prefix(*argv, "hex")) { 238 base = 16; 239 argv++; 240 } 241 242 while (i < n && argv[i]) { 243 val[i] = strtoul(argv[i], &endptr, base); 244 if (*endptr) { 245 p_err("error parsing byte: %s", argv[i]); 246 return NULL; 247 } 248 i++; 249 } 250 251 if (i != n) { 252 p_err("%s expected %d bytes got %d", name, n, i); 253 return NULL; 254 } 255 256 return argv + i; 257 } 258 259 static int parse_elem(char **argv, struct bpf_map_info *info, 260 void *key, void *value, __u32 key_size, __u32 value_size, 261 __u32 *flags, __u32 **value_fd) 262 { 263 if (!*argv) { 264 if (!key && !value) 265 return 0; 266 p_err("did not find %s", key ? "key" : "value"); 267 return -1; 268 } 269 270 if (is_prefix(*argv, "key")) { 271 if (!key) { 272 if (key_size) 273 p_err("duplicate key"); 274 else 275 p_err("unnecessary key"); 276 return -1; 277 } 278 279 argv = parse_bytes(argv + 1, "key", key, key_size); 280 if (!argv) 281 return -1; 282 283 return parse_elem(argv, info, NULL, value, key_size, value_size, 284 flags, value_fd); 285 } else if (is_prefix(*argv, "value")) { 286 int fd; 287 288 if (!value) { 289 if (value_size) 290 p_err("duplicate value"); 291 else 292 p_err("unnecessary value"); 293 return -1; 294 } 295 296 argv++; 297 298 if (map_is_map_of_maps(info->type)) { 299 int argc = 2; 300 301 if (value_size != 4) { 302 p_err("value smaller than 4B for map in map?"); 303 return -1; 304 } 305 if (!argv[0] || !argv[1]) { 306 p_err("not enough value arguments for map in map"); 307 return -1; 308 } 309 310 fd = map_parse_fd(&argc, &argv); 311 if (fd < 0) 312 return -1; 313 314 *value_fd = value; 315 **value_fd = fd; 316 } else if (map_is_map_of_progs(info->type)) { 317 int argc = 2; 318 319 if (value_size != 4) { 320 p_err("value smaller than 4B for map of progs?"); 321 return -1; 322 } 323 if (!argv[0] || !argv[1]) { 324 p_err("not enough value arguments for map of progs"); 325 return -1; 326 } 327 328 fd = prog_parse_fd(&argc, &argv); 329 if (fd < 0) 330 return -1; 331 332 *value_fd = value; 333 **value_fd = fd; 334 } else { 335 argv = parse_bytes(argv, "value", value, value_size); 336 if (!argv) 337 return -1; 338 } 339 340 return parse_elem(argv, info, key, NULL, key_size, value_size, 341 flags, NULL); 342 } else if (is_prefix(*argv, "any") || is_prefix(*argv, "noexist") || 343 is_prefix(*argv, "exist")) { 344 if (!flags) { 345 p_err("flags specified multiple times: %s", *argv); 346 return -1; 347 } 348 349 if (is_prefix(*argv, "any")) 350 *flags = BPF_ANY; 351 else if (is_prefix(*argv, "noexist")) 352 *flags = BPF_NOEXIST; 353 else if (is_prefix(*argv, "exist")) 354 *flags = BPF_EXIST; 355 356 return parse_elem(argv + 1, info, key, value, key_size, 357 value_size, NULL, value_fd); 358 } 359 360 p_err("expected key or value, got: %s", *argv); 361 return -1; 362 } 363 364 static int show_map_close_json(int fd, struct bpf_map_info *info) 365 { 366 char *memlock; 367 368 memlock = get_fdinfo(fd, "memlock"); 369 close(fd); 370 371 jsonw_start_object(json_wtr); 372 373 jsonw_uint_field(json_wtr, "id", info->id); 374 if (info->type < ARRAY_SIZE(map_type_name)) 375 jsonw_string_field(json_wtr, "type", 376 map_type_name[info->type]); 377 else 378 jsonw_uint_field(json_wtr, "type", info->type); 379 380 if (*info->name) 381 jsonw_string_field(json_wtr, "name", info->name); 382 383 jsonw_name(json_wtr, "flags"); 384 jsonw_printf(json_wtr, "%d", info->map_flags); 385 386 print_dev_json(info->ifindex, info->netns_dev, info->netns_ino); 387 388 jsonw_uint_field(json_wtr, "bytes_key", info->key_size); 389 jsonw_uint_field(json_wtr, "bytes_value", info->value_size); 390 jsonw_uint_field(json_wtr, "max_entries", info->max_entries); 391 392 if (memlock) 393 jsonw_int_field(json_wtr, "bytes_memlock", atoi(memlock)); 394 free(memlock); 395 396 if (!hash_empty(map_table.table)) { 397 struct pinned_obj *obj; 398 399 jsonw_name(json_wtr, "pinned"); 400 jsonw_start_array(json_wtr); 401 hash_for_each_possible(map_table.table, obj, hash, info->id) { 402 if (obj->id == info->id) 403 jsonw_string(json_wtr, obj->path); 404 } 405 jsonw_end_array(json_wtr); 406 } 407 408 jsonw_end_object(json_wtr); 409 410 return 0; 411 } 412 413 static int show_map_close_plain(int fd, struct bpf_map_info *info) 414 { 415 char *memlock; 416 417 memlock = get_fdinfo(fd, "memlock"); 418 close(fd); 419 420 printf("%u: ", info->id); 421 if (info->type < ARRAY_SIZE(map_type_name)) 422 printf("%s ", map_type_name[info->type]); 423 else 424 printf("type %u ", info->type); 425 426 if (*info->name) 427 printf("name %s ", info->name); 428 429 printf("flags 0x%x", info->map_flags); 430 print_dev_plain(info->ifindex, info->netns_dev, info->netns_ino); 431 printf("\n"); 432 printf("\tkey %uB value %uB max_entries %u", 433 info->key_size, info->value_size, info->max_entries); 434 435 if (memlock) 436 printf(" memlock %sB", memlock); 437 free(memlock); 438 439 printf("\n"); 440 if (!hash_empty(map_table.table)) { 441 struct pinned_obj *obj; 442 443 hash_for_each_possible(map_table.table, obj, hash, info->id) { 444 if (obj->id == info->id) 445 printf("\tpinned %s\n", obj->path); 446 } 447 } 448 return 0; 449 } 450 451 static int do_show(int argc, char **argv) 452 { 453 struct bpf_map_info info = {}; 454 __u32 len = sizeof(info); 455 __u32 id = 0; 456 int err; 457 int fd; 458 459 if (show_pinned) 460 build_pinned_obj_table(&map_table, BPF_OBJ_MAP); 461 462 if (argc == 2) { 463 fd = map_parse_fd_and_info(&argc, &argv, &info, &len); 464 if (fd < 0) 465 return -1; 466 467 if (json_output) 468 return show_map_close_json(fd, &info); 469 else 470 return show_map_close_plain(fd, &info); 471 } 472 473 if (argc) 474 return BAD_ARG(); 475 476 if (json_output) 477 jsonw_start_array(json_wtr); 478 while (true) { 479 err = bpf_map_get_next_id(id, &id); 480 if (err) { 481 if (errno == ENOENT) 482 break; 483 p_err("can't get next map: %s%s", strerror(errno), 484 errno == EINVAL ? " -- kernel too old?" : ""); 485 break; 486 } 487 488 fd = bpf_map_get_fd_by_id(id); 489 if (fd < 0) { 490 if (errno == ENOENT) 491 continue; 492 p_err("can't get map by id (%u): %s", 493 id, strerror(errno)); 494 break; 495 } 496 497 err = bpf_obj_get_info_by_fd(fd, &info, &len); 498 if (err) { 499 p_err("can't get map info: %s", strerror(errno)); 500 close(fd); 501 break; 502 } 503 504 if (json_output) 505 show_map_close_json(fd, &info); 506 else 507 show_map_close_plain(fd, &info); 508 } 509 if (json_output) 510 jsonw_end_array(json_wtr); 511 512 return errno == ENOENT ? 0 : -1; 513 } 514 515 static int do_dump(int argc, char **argv) 516 { 517 void *key, *value, *prev_key; 518 unsigned int num_elems = 0; 519 struct bpf_map_info info = {}; 520 __u32 len = sizeof(info); 521 int err; 522 int fd; 523 524 if (argc != 2) 525 usage(); 526 527 fd = map_parse_fd_and_info(&argc, &argv, &info, &len); 528 if (fd < 0) 529 return -1; 530 531 if (map_is_map_of_maps(info.type) || map_is_map_of_progs(info.type)) { 532 p_err("Dumping maps of maps and program maps not supported"); 533 close(fd); 534 return -1; 535 } 536 537 key = malloc(info.key_size); 538 value = alloc_value(&info); 539 if (!key || !value) { 540 p_err("mem alloc failed"); 541 err = -1; 542 goto exit_free; 543 } 544 545 prev_key = NULL; 546 if (json_output) 547 jsonw_start_array(json_wtr); 548 while (true) { 549 err = bpf_map_get_next_key(fd, prev_key, key); 550 if (err) { 551 if (errno == ENOENT) 552 err = 0; 553 break; 554 } 555 556 if (!bpf_map_lookup_elem(fd, key, value)) { 557 if (json_output) 558 print_entry_json(&info, key, value); 559 else 560 print_entry_plain(&info, key, value); 561 } else { 562 if (json_output) { 563 jsonw_name(json_wtr, "key"); 564 print_hex_data_json(key, info.key_size); 565 jsonw_name(json_wtr, "value"); 566 jsonw_start_object(json_wtr); 567 jsonw_string_field(json_wtr, "error", 568 "can't lookup element"); 569 jsonw_end_object(json_wtr); 570 } else { 571 p_info("can't lookup element with key: "); 572 fprint_hex(stderr, key, info.key_size, " "); 573 fprintf(stderr, "\n"); 574 } 575 } 576 577 prev_key = key; 578 num_elems++; 579 } 580 581 if (json_output) 582 jsonw_end_array(json_wtr); 583 else 584 printf("Found %u element%s\n", num_elems, 585 num_elems != 1 ? "s" : ""); 586 587 exit_free: 588 free(key); 589 free(value); 590 close(fd); 591 592 return err; 593 } 594 595 static int do_update(int argc, char **argv) 596 { 597 struct bpf_map_info info = {}; 598 __u32 len = sizeof(info); 599 __u32 *value_fd = NULL; 600 __u32 flags = BPF_ANY; 601 void *key, *value; 602 int fd, err; 603 604 if (argc < 2) 605 usage(); 606 607 fd = map_parse_fd_and_info(&argc, &argv, &info, &len); 608 if (fd < 0) 609 return -1; 610 611 key = malloc(info.key_size); 612 value = alloc_value(&info); 613 if (!key || !value) { 614 p_err("mem alloc failed"); 615 err = -1; 616 goto exit_free; 617 } 618 619 err = parse_elem(argv, &info, key, value, info.key_size, 620 info.value_size, &flags, &value_fd); 621 if (err) 622 goto exit_free; 623 624 err = bpf_map_update_elem(fd, key, value, flags); 625 if (err) { 626 p_err("update failed: %s", strerror(errno)); 627 goto exit_free; 628 } 629 630 exit_free: 631 if (value_fd) 632 close(*value_fd); 633 free(key); 634 free(value); 635 close(fd); 636 637 if (!err && json_output) 638 jsonw_null(json_wtr); 639 return err; 640 } 641 642 static int do_lookup(int argc, char **argv) 643 { 644 struct bpf_map_info info = {}; 645 __u32 len = sizeof(info); 646 void *key, *value; 647 int err; 648 int fd; 649 650 if (argc < 2) 651 usage(); 652 653 fd = map_parse_fd_and_info(&argc, &argv, &info, &len); 654 if (fd < 0) 655 return -1; 656 657 key = malloc(info.key_size); 658 value = alloc_value(&info); 659 if (!key || !value) { 660 p_err("mem alloc failed"); 661 err = -1; 662 goto exit_free; 663 } 664 665 err = parse_elem(argv, &info, key, NULL, info.key_size, 0, NULL, NULL); 666 if (err) 667 goto exit_free; 668 669 err = bpf_map_lookup_elem(fd, key, value); 670 if (!err) { 671 if (json_output) 672 print_entry_json(&info, key, value); 673 else 674 print_entry_plain(&info, key, value); 675 } else if (errno == ENOENT) { 676 if (json_output) { 677 jsonw_null(json_wtr); 678 } else { 679 printf("key:\n"); 680 fprint_hex(stdout, key, info.key_size, " "); 681 printf("\n\nNot found\n"); 682 } 683 } else { 684 p_err("lookup failed: %s", strerror(errno)); 685 } 686 687 exit_free: 688 free(key); 689 free(value); 690 close(fd); 691 692 return err; 693 } 694 695 static int do_getnext(int argc, char **argv) 696 { 697 struct bpf_map_info info = {}; 698 __u32 len = sizeof(info); 699 void *key, *nextkey; 700 int err; 701 int fd; 702 703 if (argc < 2) 704 usage(); 705 706 fd = map_parse_fd_and_info(&argc, &argv, &info, &len); 707 if (fd < 0) 708 return -1; 709 710 key = malloc(info.key_size); 711 nextkey = malloc(info.key_size); 712 if (!key || !nextkey) { 713 p_err("mem alloc failed"); 714 err = -1; 715 goto exit_free; 716 } 717 718 if (argc) { 719 err = parse_elem(argv, &info, key, NULL, info.key_size, 0, 720 NULL, NULL); 721 if (err) 722 goto exit_free; 723 } else { 724 free(key); 725 key = NULL; 726 } 727 728 err = bpf_map_get_next_key(fd, key, nextkey); 729 if (err) { 730 p_err("can't get next key: %s", strerror(errno)); 731 goto exit_free; 732 } 733 734 if (json_output) { 735 jsonw_start_object(json_wtr); 736 if (key) { 737 jsonw_name(json_wtr, "key"); 738 print_hex_data_json(key, info.key_size); 739 } else { 740 jsonw_null_field(json_wtr, "key"); 741 } 742 jsonw_name(json_wtr, "next_key"); 743 print_hex_data_json(nextkey, info.key_size); 744 jsonw_end_object(json_wtr); 745 } else { 746 if (key) { 747 printf("key:\n"); 748 fprint_hex(stdout, key, info.key_size, " "); 749 printf("\n"); 750 } else { 751 printf("key: None\n"); 752 } 753 printf("next key:\n"); 754 fprint_hex(stdout, nextkey, info.key_size, " "); 755 printf("\n"); 756 } 757 758 exit_free: 759 free(nextkey); 760 free(key); 761 close(fd); 762 763 return err; 764 } 765 766 static int do_delete(int argc, char **argv) 767 { 768 struct bpf_map_info info = {}; 769 __u32 len = sizeof(info); 770 void *key; 771 int err; 772 int fd; 773 774 if (argc < 2) 775 usage(); 776 777 fd = map_parse_fd_and_info(&argc, &argv, &info, &len); 778 if (fd < 0) 779 return -1; 780 781 key = malloc(info.key_size); 782 if (!key) { 783 p_err("mem alloc failed"); 784 err = -1; 785 goto exit_free; 786 } 787 788 err = parse_elem(argv, &info, key, NULL, info.key_size, 0, NULL, NULL); 789 if (err) 790 goto exit_free; 791 792 err = bpf_map_delete_elem(fd, key); 793 if (err) 794 p_err("delete failed: %s", strerror(errno)); 795 796 exit_free: 797 free(key); 798 close(fd); 799 800 if (!err && json_output) 801 jsonw_null(json_wtr); 802 return err; 803 } 804 805 static int do_pin(int argc, char **argv) 806 { 807 int err; 808 809 err = do_pin_any(argc, argv, bpf_map_get_fd_by_id); 810 if (!err && json_output) 811 jsonw_null(json_wtr); 812 return err; 813 } 814 815 static int do_help(int argc, char **argv) 816 { 817 if (json_output) { 818 jsonw_null(json_wtr); 819 return 0; 820 } 821 822 fprintf(stderr, 823 "Usage: %s %s { show | list } [MAP]\n" 824 " %s %s dump MAP\n" 825 " %s %s update MAP key DATA value VALUE [UPDATE_FLAGS]\n" 826 " %s %s lookup MAP key DATA\n" 827 " %s %s getnext MAP [key DATA]\n" 828 " %s %s delete MAP key DATA\n" 829 " %s %s pin MAP FILE\n" 830 " %s %s event_pipe MAP [cpu N index M]\n" 831 " %s %s help\n" 832 "\n" 833 " MAP := { id MAP_ID | pinned FILE }\n" 834 " DATA := { [hex] BYTES }\n" 835 " " HELP_SPEC_PROGRAM "\n" 836 " VALUE := { DATA | MAP | PROG }\n" 837 " UPDATE_FLAGS := { any | exist | noexist }\n" 838 " " HELP_SPEC_OPTIONS "\n" 839 "", 840 bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2], 841 bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2], 842 bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2]); 843 844 return 0; 845 } 846 847 static const struct cmd cmds[] = { 848 { "show", do_show }, 849 { "list", do_show }, 850 { "help", do_help }, 851 { "dump", do_dump }, 852 { "update", do_update }, 853 { "lookup", do_lookup }, 854 { "getnext", do_getnext }, 855 { "delete", do_delete }, 856 { "pin", do_pin }, 857 { "event_pipe", do_event_pipe }, 858 { 0 } 859 }; 860 861 int do_map(int argc, char **argv) 862 { 863 return cmd_select(cmds, argc, argv, do_help); 864 } 865