1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * config.c 4 * 5 * Helper functions for parsing config items. 6 * Originally copied from GIT source. 7 * 8 * Copyright (C) Linus Torvalds, 2005 9 * Copyright (C) Johannes Schindelin, 2005 10 * 11 */ 12 #include <errno.h> 13 #include <sys/param.h> 14 #include "cache.h" 15 #include "callchain.h" 16 #include "header.h" 17 #include <subcmd/exec-cmd.h> 18 #include "util/event.h" /* proc_map_timeout */ 19 #include "util/hist.h" /* perf_hist_config */ 20 #include "util/stat.h" /* perf_stat__set_big_num */ 21 #include "util/evsel.h" /* evsel__hw_names, evsel__use_bpf_counters */ 22 #include "srcline.h" 23 #include "build-id.h" 24 #include "debug.h" 25 #include "config.h" 26 #include <sys/types.h> 27 #include <sys/stat.h> 28 #include <stdlib.h> 29 #include <unistd.h> 30 #include <linux/string.h> 31 #include <linux/zalloc.h> 32 #include <linux/ctype.h> 33 34 #define MAXNAME (256) 35 36 #define DEBUG_CACHE_DIR ".debug" 37 38 #define METRIC_ONLY_LEN 20 39 40 static struct stats walltime_nsecs_stats; 41 42 struct perf_stat_config stat_config = { 43 .aggr_mode = AGGR_GLOBAL, 44 .aggr_level = MAX_CACHE_LVL + 1, 45 .scale = true, 46 .unit_width = 4, /* strlen("unit") */ 47 .run_count = 1, 48 .metric_only_len = METRIC_ONLY_LEN, 49 .walltime_nsecs_stats = &walltime_nsecs_stats, 50 .big_num = true, 51 .ctl_fd = -1, 52 .ctl_fd_ack = -1, 53 .iostat_run = false, 54 }; 55 56 char buildid_dir[MAXPATHLEN]; /* root dir for buildid, binary cache */ 57 58 static FILE *config_file; 59 static const char *config_file_name; 60 static int config_linenr; 61 static int config_file_eof; 62 static struct perf_config_set *config_set; 63 64 const char *config_exclusive_filename; 65 66 static int get_next_char(void) 67 { 68 int c; 69 FILE *f; 70 71 c = '\n'; 72 if ((f = config_file) != NULL) { 73 c = fgetc(f); 74 if (c == '\r') { 75 /* DOS like systems */ 76 c = fgetc(f); 77 if (c != '\n') { 78 ungetc(c, f); 79 c = '\r'; 80 } 81 } 82 if (c == '\n') 83 config_linenr++; 84 if (c == EOF) { 85 config_file_eof = 1; 86 c = '\n'; 87 } 88 } 89 return c; 90 } 91 92 static char *parse_value(void) 93 { 94 static char value[1024]; 95 int quote = 0, comment = 0, space = 0; 96 size_t len = 0; 97 98 for (;;) { 99 int c = get_next_char(); 100 101 if (len >= sizeof(value) - 1) 102 return NULL; 103 if (c == '\n') { 104 if (quote) 105 return NULL; 106 value[len] = 0; 107 return value; 108 } 109 if (comment) 110 continue; 111 if (isspace(c) && !quote) { 112 space = 1; 113 continue; 114 } 115 if (!quote) { 116 if (c == ';' || c == '#') { 117 comment = 1; 118 continue; 119 } 120 } 121 if (space) { 122 if (len) 123 value[len++] = ' '; 124 space = 0; 125 } 126 if (c == '\\') { 127 c = get_next_char(); 128 switch (c) { 129 case '\n': 130 continue; 131 case 't': 132 c = '\t'; 133 break; 134 case 'b': 135 c = '\b'; 136 break; 137 case 'n': 138 c = '\n'; 139 break; 140 /* Some characters escape as themselves */ 141 case '\\': case '"': 142 break; 143 /* Reject unknown escape sequences */ 144 default: 145 return NULL; 146 } 147 value[len++] = c; 148 continue; 149 } 150 if (c == '"') { 151 quote = 1-quote; 152 continue; 153 } 154 value[len++] = c; 155 } 156 } 157 158 static inline int iskeychar(int c) 159 { 160 return isalnum(c) || c == '-' || c == '_'; 161 } 162 163 static int get_value(config_fn_t fn, void *data, char *name, unsigned int len) 164 { 165 int c; 166 char *value; 167 168 /* Get the full name */ 169 for (;;) { 170 c = get_next_char(); 171 if (config_file_eof) 172 break; 173 if (!iskeychar(c)) 174 break; 175 name[len++] = c; 176 if (len >= MAXNAME) 177 return -1; 178 } 179 name[len] = 0; 180 while (c == ' ' || c == '\t') 181 c = get_next_char(); 182 183 value = NULL; 184 if (c != '\n') { 185 if (c != '=') 186 return -1; 187 value = parse_value(); 188 if (!value) 189 return -1; 190 } 191 return fn(name, value, data); 192 } 193 194 static int get_extended_base_var(char *name, int baselen, int c) 195 { 196 do { 197 if (c == '\n') 198 return -1; 199 c = get_next_char(); 200 } while (isspace(c)); 201 202 /* We require the format to be '[base "extension"]' */ 203 if (c != '"') 204 return -1; 205 name[baselen++] = '.'; 206 207 for (;;) { 208 int ch = get_next_char(); 209 210 if (ch == '\n') 211 return -1; 212 if (ch == '"') 213 break; 214 if (ch == '\\') { 215 ch = get_next_char(); 216 if (ch == '\n') 217 return -1; 218 } 219 name[baselen++] = ch; 220 if (baselen > MAXNAME / 2) 221 return -1; 222 } 223 224 /* Final ']' */ 225 if (get_next_char() != ']') 226 return -1; 227 return baselen; 228 } 229 230 static int get_base_var(char *name) 231 { 232 int baselen = 0; 233 234 for (;;) { 235 int c = get_next_char(); 236 if (config_file_eof) 237 return -1; 238 if (c == ']') 239 return baselen; 240 if (isspace(c)) 241 return get_extended_base_var(name, baselen, c); 242 if (!iskeychar(c) && c != '.') 243 return -1; 244 if (baselen > MAXNAME / 2) 245 return -1; 246 name[baselen++] = tolower(c); 247 } 248 } 249 250 static int perf_parse_file(config_fn_t fn, void *data) 251 { 252 int comment = 0; 253 int baselen = 0; 254 static char var[MAXNAME]; 255 256 /* U+FEFF Byte Order Mark in UTF8 */ 257 static const unsigned char *utf8_bom = (unsigned char *) "\xef\xbb\xbf"; 258 const unsigned char *bomptr = utf8_bom; 259 260 for (;;) { 261 int line, c = get_next_char(); 262 263 if (bomptr && *bomptr) { 264 /* We are at the file beginning; skip UTF8-encoded BOM 265 * if present. Sane editors won't put this in on their 266 * own, but e.g. Windows Notepad will do it happily. */ 267 if ((unsigned char) c == *bomptr) { 268 bomptr++; 269 continue; 270 } else { 271 /* Do not tolerate partial BOM. */ 272 if (bomptr != utf8_bom) 273 break; 274 /* No BOM at file beginning. Cool. */ 275 bomptr = NULL; 276 } 277 } 278 if (c == '\n') { 279 if (config_file_eof) 280 return 0; 281 comment = 0; 282 continue; 283 } 284 if (comment || isspace(c)) 285 continue; 286 if (c == '#' || c == ';') { 287 comment = 1; 288 continue; 289 } 290 if (c == '[') { 291 baselen = get_base_var(var); 292 if (baselen <= 0) 293 break; 294 var[baselen++] = '.'; 295 var[baselen] = 0; 296 continue; 297 } 298 if (!isalpha(c)) 299 break; 300 var[baselen] = tolower(c); 301 302 /* 303 * The get_value function might or might not reach the '\n', 304 * so saving the current line number for error reporting. 305 */ 306 line = config_linenr; 307 if (get_value(fn, data, var, baselen+1) < 0) { 308 config_linenr = line; 309 break; 310 } 311 } 312 pr_err("bad config file line %d in %s\n", config_linenr, config_file_name); 313 return -1; 314 } 315 316 static int parse_unit_factor(const char *end, unsigned long *val) 317 { 318 if (!*end) 319 return 1; 320 else if (!strcasecmp(end, "k")) { 321 *val *= 1024; 322 return 1; 323 } 324 else if (!strcasecmp(end, "m")) { 325 *val *= 1024 * 1024; 326 return 1; 327 } 328 else if (!strcasecmp(end, "g")) { 329 *val *= 1024 * 1024 * 1024; 330 return 1; 331 } 332 return 0; 333 } 334 335 static int perf_parse_llong(const char *value, long long *ret) 336 { 337 if (value && *value) { 338 char *end; 339 long long val = strtoll(value, &end, 0); 340 unsigned long factor = 1; 341 342 if (!parse_unit_factor(end, &factor)) 343 return 0; 344 *ret = val * factor; 345 return 1; 346 } 347 return 0; 348 } 349 350 static int perf_parse_long(const char *value, long *ret) 351 { 352 if (value && *value) { 353 char *end; 354 long val = strtol(value, &end, 0); 355 unsigned long factor = 1; 356 if (!parse_unit_factor(end, &factor)) 357 return 0; 358 *ret = val * factor; 359 return 1; 360 } 361 return 0; 362 } 363 364 static void bad_config(const char *name) 365 { 366 if (config_file_name) 367 pr_warning("bad config value for '%s' in %s, ignoring...\n", name, config_file_name); 368 else 369 pr_warning("bad config value for '%s', ignoring...\n", name); 370 } 371 372 int perf_config_u64(u64 *dest, const char *name, const char *value) 373 { 374 long long ret = 0; 375 376 if (!perf_parse_llong(value, &ret)) { 377 bad_config(name); 378 return -1; 379 } 380 381 *dest = ret; 382 return 0; 383 } 384 385 int perf_config_int(int *dest, const char *name, const char *value) 386 { 387 long ret = 0; 388 if (!perf_parse_long(value, &ret)) { 389 bad_config(name); 390 return -1; 391 } 392 *dest = ret; 393 return 0; 394 } 395 396 int perf_config_u8(u8 *dest, const char *name, const char *value) 397 { 398 long ret = 0; 399 400 if (!perf_parse_long(value, &ret)) { 401 bad_config(name); 402 return -1; 403 } 404 *dest = ret; 405 return 0; 406 } 407 408 static int perf_config_bool_or_int(const char *name, const char *value, int *is_bool) 409 { 410 int ret; 411 412 *is_bool = 1; 413 if (!value) 414 return 1; 415 if (!*value) 416 return 0; 417 if (!strcasecmp(value, "true") || !strcasecmp(value, "yes") || !strcasecmp(value, "on")) 418 return 1; 419 if (!strcasecmp(value, "false") || !strcasecmp(value, "no") || !strcasecmp(value, "off")) 420 return 0; 421 *is_bool = 0; 422 return perf_config_int(&ret, name, value) < 0 ? -1 : ret; 423 } 424 425 int perf_config_bool(const char *name, const char *value) 426 { 427 int discard; 428 return !!perf_config_bool_or_int(name, value, &discard); 429 } 430 431 static const char *perf_config_dirname(const char *name, const char *value) 432 { 433 if (!name) 434 return NULL; 435 return value; 436 } 437 438 static int perf_buildid_config(const char *var, const char *value) 439 { 440 /* same dir for all commands */ 441 if (!strcmp(var, "buildid.dir")) { 442 const char *dir = perf_config_dirname(var, value); 443 444 if (!dir) { 445 pr_err("Invalid buildid directory!\n"); 446 return -1; 447 } 448 strncpy(buildid_dir, dir, MAXPATHLEN-1); 449 buildid_dir[MAXPATHLEN-1] = '\0'; 450 } 451 452 return 0; 453 } 454 455 static int perf_default_core_config(const char *var, const char *value) 456 { 457 if (!strcmp(var, "core.proc-map-timeout")) 458 proc_map_timeout = strtoul(value, NULL, 10); 459 460 if (!strcmp(var, "core.addr2line-timeout")) 461 symbol_conf.addr2line_timeout_ms = strtoul(value, NULL, 10); 462 463 if (!strcmp(var, "core.addr2line-disable-warn")) 464 symbol_conf.addr2line_disable_warn = perf_config_bool(var, value); 465 466 /* Add other config variables here. */ 467 return 0; 468 } 469 470 static int perf_ui_config(const char *var, const char *value) 471 { 472 /* Add other config variables here. */ 473 if (!strcmp(var, "ui.show-headers")) 474 symbol_conf.show_hist_headers = perf_config_bool(var, value); 475 476 return 0; 477 } 478 479 void perf_stat__set_big_num(int set) 480 { 481 stat_config.big_num = (set != 0); 482 } 483 484 static void perf_stat__set_no_csv_summary(int set) 485 { 486 stat_config.no_csv_summary = (set != 0); 487 } 488 489 static int perf_stat_config(const char *var, const char *value) 490 { 491 if (!strcmp(var, "stat.big-num")) 492 perf_stat__set_big_num(perf_config_bool(var, value)); 493 494 if (!strcmp(var, "stat.no-csv-summary")) 495 perf_stat__set_no_csv_summary(perf_config_bool(var, value)); 496 497 if (!strcmp(var, "stat.bpf-counter-events")) 498 evsel__bpf_counter_events = strdup(value); 499 500 /* Add other config variables here. */ 501 return 0; 502 } 503 504 int perf_default_config(const char *var, const char *value, 505 void *dummy __maybe_unused) 506 { 507 if (strstarts(var, "core.")) 508 return perf_default_core_config(var, value); 509 510 if (strstarts(var, "hist.")) 511 return perf_hist_config(var, value); 512 513 if (strstarts(var, "ui.")) 514 return perf_ui_config(var, value); 515 516 if (strstarts(var, "call-graph.")) 517 return perf_callchain_config(var, value); 518 519 if (strstarts(var, "buildid.")) 520 return perf_buildid_config(var, value); 521 522 if (strstarts(var, "stat.")) 523 return perf_stat_config(var, value); 524 525 if (strstarts(var, "addr2line.")) 526 return addr2line_configure(var, value, dummy); 527 528 /* Add other config variables here. */ 529 return 0; 530 } 531 532 static int perf_config_from_file(config_fn_t fn, const char *filename, void *data) 533 { 534 int ret; 535 FILE *f = fopen(filename, "r"); 536 537 ret = -1; 538 if (f) { 539 config_file = f; 540 config_file_name = filename; 541 config_linenr = 1; 542 config_file_eof = 0; 543 ret = perf_parse_file(fn, data); 544 fclose(f); 545 config_file_name = NULL; 546 } 547 return ret; 548 } 549 550 const char *perf_etc_perfconfig(void) 551 { 552 static const char *system_wide; 553 if (!system_wide) 554 system_wide = system_path(ETC_PERFCONFIG); 555 return system_wide; 556 } 557 558 static int perf_env_bool(const char *k, int def) 559 { 560 const char *v = getenv(k); 561 return v ? perf_config_bool(k, v) : def; 562 } 563 564 int perf_config_system(void) 565 { 566 return !perf_env_bool("PERF_CONFIG_NOSYSTEM", 0); 567 } 568 569 int perf_config_global(void) 570 { 571 return !perf_env_bool("PERF_CONFIG_NOGLOBAL", 0); 572 } 573 574 static char *home_perfconfig(void) 575 { 576 const char *home = NULL; 577 char *config; 578 struct stat st; 579 char path[PATH_MAX]; 580 581 home = getenv("HOME"); 582 583 /* 584 * Skip reading user config if: 585 * - there is no place to read it from (HOME) 586 * - we are asked not to (PERF_CONFIG_NOGLOBAL=1) 587 */ 588 if (!home || !*home || !perf_config_global()) 589 return NULL; 590 591 config = strdup(mkpath(path, sizeof(path), "%s/.perfconfig", home)); 592 if (config == NULL) { 593 pr_warning("Not enough memory to process %s/.perfconfig, ignoring it.\n", home); 594 return NULL; 595 } 596 597 if (stat(config, &st) < 0) 598 goto out_free; 599 600 if (st.st_uid && (st.st_uid != geteuid())) { 601 pr_warning("File %s not owned by current user or root, ignoring it.\n", config); 602 goto out_free; 603 } 604 605 if (st.st_size) 606 return config; 607 608 out_free: 609 free(config); 610 return NULL; 611 } 612 613 const char *perf_home_perfconfig(void) 614 { 615 static const char *config; 616 static bool failed; 617 618 if (failed || config) 619 return config; 620 621 config = home_perfconfig(); 622 if (!config) 623 failed = true; 624 625 return config; 626 } 627 628 static struct perf_config_section *find_section(struct list_head *sections, 629 const char *section_name) 630 { 631 struct perf_config_section *section; 632 633 list_for_each_entry(section, sections, node) 634 if (!strcmp(section->name, section_name)) 635 return section; 636 637 return NULL; 638 } 639 640 static struct perf_config_item *find_config_item(const char *name, 641 struct perf_config_section *section) 642 { 643 struct perf_config_item *item; 644 645 list_for_each_entry(item, §ion->items, node) 646 if (!strcmp(item->name, name)) 647 return item; 648 649 return NULL; 650 } 651 652 static struct perf_config_section *add_section(struct list_head *sections, 653 const char *section_name) 654 { 655 struct perf_config_section *section = zalloc(sizeof(*section)); 656 657 if (!section) 658 return NULL; 659 660 INIT_LIST_HEAD(§ion->items); 661 section->name = strdup(section_name); 662 if (!section->name) { 663 pr_debug("%s: strdup failed\n", __func__); 664 free(section); 665 return NULL; 666 } 667 668 list_add_tail(§ion->node, sections); 669 return section; 670 } 671 672 static struct perf_config_item *add_config_item(struct perf_config_section *section, 673 const char *name) 674 { 675 struct perf_config_item *item = zalloc(sizeof(*item)); 676 677 if (!item) 678 return NULL; 679 680 item->name = strdup(name); 681 if (!item->name) { 682 pr_debug("%s: strdup failed\n", __func__); 683 free(item); 684 return NULL; 685 } 686 687 list_add_tail(&item->node, §ion->items); 688 return item; 689 } 690 691 static int set_value(struct perf_config_item *item, const char *value) 692 { 693 char *val = strdup(value); 694 695 if (!val) 696 return -1; 697 698 zfree(&item->value); 699 item->value = val; 700 return 0; 701 } 702 703 static int collect_config(const char *var, const char *value, 704 void *perf_config_set) 705 { 706 int ret = -1; 707 char *ptr, *key; 708 char *section_name, *name; 709 struct perf_config_section *section = NULL; 710 struct perf_config_item *item = NULL; 711 struct perf_config_set *set = perf_config_set; 712 struct list_head *sections; 713 714 if (set == NULL) 715 return -1; 716 717 sections = &set->sections; 718 key = ptr = strdup(var); 719 if (!key) { 720 pr_debug("%s: strdup failed\n", __func__); 721 return -1; 722 } 723 724 section_name = strsep(&ptr, "."); 725 name = ptr; 726 if (name == NULL || value == NULL) 727 goto out_free; 728 729 section = find_section(sections, section_name); 730 if (!section) { 731 section = add_section(sections, section_name); 732 if (!section) 733 goto out_free; 734 } 735 736 item = find_config_item(name, section); 737 if (!item) { 738 item = add_config_item(section, name); 739 if (!item) 740 goto out_free; 741 } 742 743 /* perf_config_set can contain both user and system config items. 744 * So we should know where each value is from. 745 * The classification would be needed when a particular config file 746 * is overwritten by setting feature i.e. set_config(). 747 */ 748 if (strcmp(config_file_name, perf_etc_perfconfig()) == 0) { 749 section->from_system_config = true; 750 item->from_system_config = true; 751 } else { 752 section->from_system_config = false; 753 item->from_system_config = false; 754 } 755 756 ret = set_value(item, value); 757 758 out_free: 759 free(key); 760 return ret; 761 } 762 763 int perf_config_set__collect(struct perf_config_set *set, const char *file_name, 764 const char *var, const char *value) 765 { 766 config_file_name = file_name; 767 return collect_config(var, value, set); 768 } 769 770 static int perf_config_set__init(struct perf_config_set *set) 771 { 772 int ret = -1; 773 774 /* Setting $PERF_CONFIG makes perf read _only_ the given config file. */ 775 if (config_exclusive_filename) 776 return perf_config_from_file(collect_config, config_exclusive_filename, set); 777 if (perf_config_system() && !access(perf_etc_perfconfig(), R_OK)) { 778 if (perf_config_from_file(collect_config, perf_etc_perfconfig(), set) < 0) 779 goto out; 780 } 781 if (perf_config_global() && perf_home_perfconfig()) { 782 if (perf_config_from_file(collect_config, perf_home_perfconfig(), set) < 0) 783 goto out; 784 } 785 786 out: 787 return ret; 788 } 789 790 struct perf_config_set *perf_config_set__new(void) 791 { 792 struct perf_config_set *set = zalloc(sizeof(*set)); 793 794 if (set) { 795 INIT_LIST_HEAD(&set->sections); 796 perf_config_set__init(set); 797 } 798 799 return set; 800 } 801 802 struct perf_config_set *perf_config_set__load_file(const char *file) 803 { 804 struct perf_config_set *set = zalloc(sizeof(*set)); 805 806 if (set) { 807 INIT_LIST_HEAD(&set->sections); 808 perf_config_from_file(collect_config, file, set); 809 } 810 811 return set; 812 } 813 814 static int perf_config__init(void) 815 { 816 if (config_set == NULL) 817 config_set = perf_config_set__new(); 818 819 return config_set == NULL; 820 } 821 822 int perf_config_set(struct perf_config_set *set, 823 config_fn_t fn, void *data) 824 { 825 int ret = 0; 826 char key[BUFSIZ]; 827 struct perf_config_section *section; 828 struct perf_config_item *item; 829 830 perf_config_set__for_each_entry(set, section, item) { 831 char *value = item->value; 832 833 if (value) { 834 scnprintf(key, sizeof(key), "%s.%s", 835 section->name, item->name); 836 ret = fn(key, value, data); 837 if (ret < 0) { 838 pr_err("Error in the given config file: wrong config key-value pair %s=%s\n", 839 key, value); 840 /* 841 * Can't be just a 'break', as perf_config_set__for_each_entry() 842 * expands to two nested for() loops. 843 */ 844 goto out; 845 } 846 } 847 } 848 out: 849 return ret; 850 } 851 852 int perf_config(config_fn_t fn, void *data) 853 { 854 if (config_set == NULL && perf_config__init()) 855 return -1; 856 857 return perf_config_set(config_set, fn, data); 858 } 859 860 void perf_config__exit(void) 861 { 862 perf_config_set__delete(config_set); 863 config_set = NULL; 864 } 865 866 static void perf_config_item__delete(struct perf_config_item *item) 867 { 868 zfree(&item->name); 869 zfree(&item->value); 870 free(item); 871 } 872 873 static void perf_config_section__purge(struct perf_config_section *section) 874 { 875 struct perf_config_item *item, *tmp; 876 877 list_for_each_entry_safe(item, tmp, §ion->items, node) { 878 list_del_init(&item->node); 879 perf_config_item__delete(item); 880 } 881 } 882 883 static void perf_config_section__delete(struct perf_config_section *section) 884 { 885 perf_config_section__purge(section); 886 zfree(§ion->name); 887 free(section); 888 } 889 890 static void perf_config_set__purge(struct perf_config_set *set) 891 { 892 struct perf_config_section *section, *tmp; 893 894 list_for_each_entry_safe(section, tmp, &set->sections, node) { 895 list_del_init(§ion->node); 896 perf_config_section__delete(section); 897 } 898 } 899 900 void perf_config_set__delete(struct perf_config_set *set) 901 { 902 if (set == NULL) 903 return; 904 905 perf_config_set__purge(set); 906 free(set); 907 } 908 909 /* 910 * Call this to report error for your variable that should not 911 * get a boolean value (i.e. "[my] var" means "true"). 912 */ 913 int config_error_nonbool(const char *var) 914 { 915 pr_err("Missing value for '%s'", var); 916 return -1; 917 } 918 919 void set_buildid_dir(const char *dir) 920 { 921 if (dir) 922 scnprintf(buildid_dir, MAXPATHLEN, "%s", dir); 923 924 /* default to $HOME/.debug */ 925 if (buildid_dir[0] == '\0') { 926 char *home = getenv("HOME"); 927 928 if (home) { 929 snprintf(buildid_dir, MAXPATHLEN, "%s/%s", 930 home, DEBUG_CACHE_DIR); 931 } else { 932 strncpy(buildid_dir, DEBUG_CACHE_DIR, MAXPATHLEN-1); 933 } 934 buildid_dir[MAXPATHLEN-1] = '\0'; 935 } 936 /* for communicating with external commands */ 937 setenv("PERF_BUILDID_DIR", buildid_dir, 1); 938 } 939 940 struct perf_config_scan_data { 941 const char *name; 942 const char *fmt; 943 const char *value; 944 va_list args; 945 int ret; 946 }; 947 948 static int perf_config_scan_cb(const char *var, const char *value, void *data) 949 { 950 struct perf_config_scan_data *d = data; 951 952 if (!strcmp(var, d->name)) 953 d->ret = vsscanf(value, d->fmt, d->args); 954 955 return 0; 956 } 957 958 int perf_config_scan(const char *name, const char *fmt, ...) 959 { 960 struct perf_config_scan_data d = { 961 .name = name, 962 .fmt = fmt, 963 }; 964 965 va_start(d.args, fmt); 966 perf_config(perf_config_scan_cb, &d); 967 va_end(d.args); 968 969 return d.ret; 970 } 971 972 static int perf_config_get_cb(const char *var, const char *value, void *data) 973 { 974 struct perf_config_scan_data *d = data; 975 976 if (!strcmp(var, d->name)) 977 d->value = value; 978 979 return 0; 980 } 981 982 const char *perf_config_get(const char *name) 983 { 984 struct perf_config_scan_data d = { 985 .name = name, 986 .value = NULL, 987 }; 988 989 perf_config(perf_config_get_cb, &d); 990 return d.value; 991 } 992