1/* 2 * config.c 3 * 4 * Helper functions for parsing config items. 5 * Originally copied from GIT source. 6 * 7 * Copyright (C) Linus Torvalds, 2005 8 * Copyright (C) Johannes Schindelin, 2005 9 * 10 */ 11#include "util.h" 12#include "cache.h" 13#include "exec_cmd.h" 14#include "util/hist.h" /* perf_hist_config */ 15 16#define MAXNAME (256) 17 18#define DEBUG_CACHE_DIR ".debug" 19 20 21char buildid_dir[MAXPATHLEN]; /* root dir for buildid, binary cache */ 22 23static FILE *config_file; 24static const char *config_file_name; 25static int config_linenr; 26static int config_file_eof; 27 28static const char *config_exclusive_filename; 29 30static int get_next_char(void) 31{ 32 int c; 33 FILE *f; 34 35 c = '\n'; 36 if ((f = config_file) != NULL) { 37 c = fgetc(f); 38 if (c == '\r') { 39 /* DOS like systems */ 40 c = fgetc(f); 41 if (c != '\n') { 42 ungetc(c, f); 43 c = '\r'; 44 } 45 } 46 if (c == '\n') 47 config_linenr++; 48 if (c == EOF) { 49 config_file_eof = 1; 50 c = '\n'; 51 } 52 } 53 return c; 54} 55 56static char *parse_value(void) 57{ 58 static char value[1024]; 59 int quote = 0, comment = 0, space = 0; 60 size_t len = 0; 61 62 for (;;) { 63 int c = get_next_char(); 64 65 if (len >= sizeof(value) - 1) 66 return NULL; 67 if (c == '\n') { 68 if (quote) 69 return NULL; 70 value[len] = 0; 71 return value; 72 } 73 if (comment) 74 continue; 75 if (isspace(c) && !quote) { 76 space = 1; 77 continue; 78 } 79 if (!quote) { 80 if (c == ';' || c == '#') { 81 comment = 1; 82 continue; 83 } 84 } 85 if (space) { 86 if (len) 87 value[len++] = ' '; 88 space = 0; 89 } 90 if (c == '\\') { 91 c = get_next_char(); 92 switch (c) { 93 case '\n': 94 continue; 95 case 't': 96 c = '\t'; 97 break; 98 case 'b': 99 c = '\b'; 100 break; 101 case 'n': 102 c = '\n'; 103 break; 104 /* Some characters escape as themselves */ 105 case '\\': case '"': 106 break; 107 /* Reject unknown escape sequences */ 108 default: 109 return NULL; 110 } 111 value[len++] = c; 112 continue; 113 } 114 if (c == '"') { 115 quote = 1-quote; 116 continue; 117 } 118 value[len++] = c; 119 } 120} 121 122static inline int iskeychar(int c) 123{ 124 return isalnum(c) || c == '-' || c == '_'; 125} 126 127static int get_value(config_fn_t fn, void *data, char *name, unsigned int len) 128{ 129 int c; 130 char *value; 131 132 /* Get the full name */ 133 for (;;) { 134 c = get_next_char(); 135 if (config_file_eof) 136 break; 137 if (!iskeychar(c)) 138 break; 139 name[len++] = c; 140 if (len >= MAXNAME) 141 return -1; 142 } 143 name[len] = 0; 144 while (c == ' ' || c == '\t') 145 c = get_next_char(); 146 147 value = NULL; 148 if (c != '\n') { 149 if (c != '=') 150 return -1; 151 value = parse_value(); 152 if (!value) 153 return -1; 154 } 155 return fn(name, value, data); 156} 157 158static int get_extended_base_var(char *name, int baselen, int c) 159{ 160 do { 161 if (c == '\n') 162 return -1; 163 c = get_next_char(); 164 } while (isspace(c)); 165 166 /* We require the format to be '[base "extension"]' */ 167 if (c != '"') 168 return -1; 169 name[baselen++] = '.'; 170 171 for (;;) { 172 int ch = get_next_char(); 173 174 if (ch == '\n') 175 return -1; 176 if (ch == '"') 177 break; 178 if (ch == '\\') { 179 ch = get_next_char(); 180 if (ch == '\n') 181 return -1; 182 } 183 name[baselen++] = ch; 184 if (baselen > MAXNAME / 2) 185 return -1; 186 } 187 188 /* Final ']' */ 189 if (get_next_char() != ']') 190 return -1; 191 return baselen; 192} 193 194static int get_base_var(char *name) 195{ 196 int baselen = 0; 197 198 for (;;) { 199 int c = get_next_char(); 200 if (config_file_eof) 201 return -1; 202 if (c == ']') 203 return baselen; 204 if (isspace(c)) 205 return get_extended_base_var(name, baselen, c); 206 if (!iskeychar(c) && c != '.') 207 return -1; 208 if (baselen > MAXNAME / 2) 209 return -1; 210 name[baselen++] = tolower(c); 211 } 212} 213 214static int perf_parse_file(config_fn_t fn, void *data) 215{ 216 int comment = 0; 217 int baselen = 0; 218 static char var[MAXNAME]; 219 220 /* U+FEFF Byte Order Mark in UTF8 */ 221 static const unsigned char *utf8_bom = (unsigned char *) "\xef\xbb\xbf"; 222 const unsigned char *bomptr = utf8_bom; 223 224 for (;;) { 225 int line, c = get_next_char(); 226 227 if (bomptr && *bomptr) { 228 /* We are at the file beginning; skip UTF8-encoded BOM 229 * if present. Sane editors won't put this in on their 230 * own, but e.g. Windows Notepad will do it happily. */ 231 if ((unsigned char) c == *bomptr) { 232 bomptr++; 233 continue; 234 } else { 235 /* Do not tolerate partial BOM. */ 236 if (bomptr != utf8_bom) 237 break; 238 /* No BOM at file beginning. Cool. */ 239 bomptr = NULL; 240 } 241 } 242 if (c == '\n') { 243 if (config_file_eof) 244 return 0; 245 comment = 0; 246 continue; 247 } 248 if (comment || isspace(c)) 249 continue; 250 if (c == '#' || c == ';') { 251 comment = 1; 252 continue; 253 } 254 if (c == '[') { 255 baselen = get_base_var(var); 256 if (baselen <= 0) 257 break; 258 var[baselen++] = '.'; 259 var[baselen] = 0; 260 continue; 261 } 262 if (!isalpha(c)) 263 break; 264 var[baselen] = tolower(c); 265 266 /* 267 * The get_value function might or might not reach the '\n', 268 * so saving the current line number for error reporting. 269 */ 270 line = config_linenr; 271 if (get_value(fn, data, var, baselen+1) < 0) { 272 config_linenr = line; 273 break; 274 } 275 } 276 die("bad config file line %d in %s", config_linenr, config_file_name); 277} 278 279static int parse_unit_factor(const char *end, unsigned long *val) 280{ 281 if (!*end) 282 return 1; 283 else if (!strcasecmp(end, "k")) { 284 *val *= 1024; 285 return 1; 286 } 287 else if (!strcasecmp(end, "m")) { 288 *val *= 1024 * 1024; 289 return 1; 290 } 291 else if (!strcasecmp(end, "g")) { 292 *val *= 1024 * 1024 * 1024; 293 return 1; 294 } 295 return 0; 296} 297 298static int perf_parse_llong(const char *value, long long *ret) 299{ 300 if (value && *value) { 301 char *end; 302 long long val = strtoll(value, &end, 0); 303 unsigned long factor = 1; 304 305 if (!parse_unit_factor(end, &factor)) 306 return 0; 307 *ret = val * factor; 308 return 1; 309 } 310 return 0; 311} 312 313static int perf_parse_long(const char *value, long *ret) 314{ 315 if (value && *value) { 316 char *end; 317 long val = strtol(value, &end, 0); 318 unsigned long factor = 1; 319 if (!parse_unit_factor(end, &factor)) 320 return 0; 321 *ret = val * factor; 322 return 1; 323 } 324 return 0; 325} 326 327static void die_bad_config(const char *name) 328{ 329 if (config_file_name) 330 die("bad config value for '%s' in %s", name, config_file_name); 331 die("bad config value for '%s'", name); 332} 333 334u64 perf_config_u64(const char *name, const char *value) 335{ 336 long long ret = 0; 337 338 if (!perf_parse_llong(value, &ret)) 339 die_bad_config(name); 340 return (u64) ret; 341} 342 343int perf_config_int(const char *name, const char *value) 344{ 345 long ret = 0; 346 if (!perf_parse_long(value, &ret)) 347 die_bad_config(name); 348 return ret; 349} 350 351static int perf_config_bool_or_int(const char *name, const char *value, int *is_bool) 352{ 353 *is_bool = 1; 354 if (!value) 355 return 1; 356 if (!*value) 357 return 0; 358 if (!strcasecmp(value, "true") || !strcasecmp(value, "yes") || !strcasecmp(value, "on")) 359 return 1; 360 if (!strcasecmp(value, "false") || !strcasecmp(value, "no") || !strcasecmp(value, "off")) 361 return 0; 362 *is_bool = 0; 363 return perf_config_int(name, value); 364} 365 366int perf_config_bool(const char *name, const char *value) 367{ 368 int discard; 369 return !!perf_config_bool_or_int(name, value, &discard); 370} 371 372const char *perf_config_dirname(const char *name, const char *value) 373{ 374 if (!name) 375 return NULL; 376 return value; 377} 378 379static int perf_default_core_config(const char *var __maybe_unused, 380 const char *value __maybe_unused) 381{ 382 /* Add other config variables here. */ 383 return 0; 384} 385 386static int perf_ui_config(const char *var, const char *value) 387{ 388 /* Add other config variables here. */ 389 if (!strcmp(var, "ui.show-headers")) { 390 symbol_conf.show_hist_headers = perf_config_bool(var, value); 391 return 0; 392 } 393 return 0; 394} 395 396int perf_default_config(const char *var, const char *value, 397 void *dummy __maybe_unused) 398{ 399 if (!prefixcmp(var, "core.")) 400 return perf_default_core_config(var, value); 401 402 if (!prefixcmp(var, "hist.")) 403 return perf_hist_config(var, value); 404 405 if (!prefixcmp(var, "ui.")) 406 return perf_ui_config(var, value); 407 408 if (!prefixcmp(var, "call-graph.")) 409 return perf_callchain_config(var, value); 410 411 /* Add other config variables here. */ 412 return 0; 413} 414 415static int perf_config_from_file(config_fn_t fn, const char *filename, void *data) 416{ 417 int ret; 418 FILE *f = fopen(filename, "r"); 419 420 ret = -1; 421 if (f) { 422 config_file = f; 423 config_file_name = filename; 424 config_linenr = 1; 425 config_file_eof = 0; 426 ret = perf_parse_file(fn, data); 427 fclose(f); 428 config_file_name = NULL; 429 } 430 return ret; 431} 432 433static const char *perf_etc_perfconfig(void) 434{ 435 static const char *system_wide; 436 if (!system_wide) 437 system_wide = system_path(ETC_PERFCONFIG); 438 return system_wide; 439} 440 441static int perf_env_bool(const char *k, int def) 442{ 443 const char *v = getenv(k); 444 return v ? perf_config_bool(k, v) : def; 445} 446 447static int perf_config_system(void) 448{ 449 return !perf_env_bool("PERF_CONFIG_NOSYSTEM", 0); 450} 451 452static int perf_config_global(void) 453{ 454 return !perf_env_bool("PERF_CONFIG_NOGLOBAL", 0); 455} 456 457int perf_config(config_fn_t fn, void *data) 458{ 459 int ret = 0, found = 0; 460 const char *home = NULL; 461 462 /* Setting $PERF_CONFIG makes perf read _only_ the given config file. */ 463 if (config_exclusive_filename) 464 return perf_config_from_file(fn, config_exclusive_filename, data); 465 if (perf_config_system() && !access(perf_etc_perfconfig(), R_OK)) { 466 ret += perf_config_from_file(fn, perf_etc_perfconfig(), 467 data); 468 found += 1; 469 } 470 471 home = getenv("HOME"); 472 if (perf_config_global() && home) { 473 char *user_config = strdup(mkpath("%s/.perfconfig", home)); 474 struct stat st; 475 476 if (user_config == NULL) { 477 warning("Not enough memory to process %s/.perfconfig, " 478 "ignoring it.", home); 479 goto out; 480 } 481 482 if (stat(user_config, &st) < 0) 483 goto out_free; 484 485 if (st.st_uid && (st.st_uid != geteuid())) { 486 warning("File %s not owned by current user or root, " 487 "ignoring it.", user_config); 488 goto out_free; 489 } 490 491 if (!st.st_size) 492 goto out_free; 493 494 ret += perf_config_from_file(fn, user_config, data); 495 found += 1; 496out_free: 497 free(user_config); 498 } 499out: 500 if (found == 0) 501 return -1; 502 return ret; 503} 504 505/* 506 * Call this to report error for your variable that should not 507 * get a boolean value (i.e. "[my] var" means "true"). 508 */ 509int config_error_nonbool(const char *var) 510{ 511 return error("Missing value for '%s'", var); 512} 513 514struct buildid_dir_config { 515 char *dir; 516}; 517 518static int buildid_dir_command_config(const char *var, const char *value, 519 void *data) 520{ 521 struct buildid_dir_config *c = data; 522 const char *v; 523 524 /* same dir for all commands */ 525 if (!strcmp(var, "buildid.dir")) { 526 v = perf_config_dirname(var, value); 527 if (!v) 528 return -1; 529 strncpy(c->dir, v, MAXPATHLEN-1); 530 c->dir[MAXPATHLEN-1] = '\0'; 531 } 532 return 0; 533} 534 535static void check_buildid_dir_config(void) 536{ 537 struct buildid_dir_config c; 538 c.dir = buildid_dir; 539 perf_config(buildid_dir_command_config, &c); 540} 541 542void set_buildid_dir(const char *dir) 543{ 544 if (dir) 545 scnprintf(buildid_dir, MAXPATHLEN-1, "%s", dir); 546 547 /* try config file */ 548 if (buildid_dir[0] == '\0') 549 check_buildid_dir_config(); 550 551 /* default to $HOME/.debug */ 552 if (buildid_dir[0] == '\0') { 553 char *v = getenv("HOME"); 554 if (v) { 555 snprintf(buildid_dir, MAXPATHLEN-1, "%s/%s", 556 v, DEBUG_CACHE_DIR); 557 } else { 558 strncpy(buildid_dir, DEBUG_CACHE_DIR, MAXPATHLEN-1); 559 } 560 buildid_dir[MAXPATHLEN-1] = '\0'; 561 } 562 /* for communicating with external commands */ 563 setenv("PERF_BUILDID_DIR", buildid_dir, 1); 564} 565