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 21 char buildid_dir[MAXPATHLEN]; /* root dir for buildid, binary cache */ 22 23 static FILE *config_file; 24 static const char *config_file_name; 25 static int config_linenr; 26 static int config_file_eof; 27 28 static const char *config_exclusive_filename; 29 30 static 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 56 static 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 122 static inline int iskeychar(int c) 123 { 124 return isalnum(c) || c == '-' || c == '_'; 125 } 126 127 static 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 158 static 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 194 static 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 214 static 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 279 static 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 298 static 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 313 static 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 327 static 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 334 u64 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 343 int 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 351 static 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 366 int 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 372 const char *perf_config_dirname(const char *name, const char *value) 373 { 374 if (!name) 375 return NULL; 376 return value; 377 } 378 379 static 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 386 static 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 396 int 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 415 static 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 433 static 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 441 static 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 447 static int perf_config_system(void) 448 { 449 return !perf_env_bool("PERF_CONFIG_NOSYSTEM", 0); 450 } 451 452 static int perf_config_global(void) 453 { 454 return !perf_env_bool("PERF_CONFIG_NOGLOBAL", 0); 455 } 456 457 int 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; 496 out_free: 497 free(user_config); 498 } 499 out: 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 */ 509 int config_error_nonbool(const char *var) 510 { 511 return error("Missing value for '%s'", var); 512 } 513 514 struct buildid_dir_config { 515 char *dir; 516 }; 517 518 static 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 (!prefixcmp(var, "buildid.") && !strcmp(var + 8, "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 535 static 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 542 void set_buildid_dir(void) 543 { 544 buildid_dir[0] = '\0'; 545 546 /* try config file */ 547 check_buildid_dir_config(); 548 549 /* default to $HOME/.debug */ 550 if (buildid_dir[0] == '\0') { 551 char *v = getenv("HOME"); 552 if (v) { 553 snprintf(buildid_dir, MAXPATHLEN-1, "%s/%s", 554 v, DEBUG_CACHE_DIR); 555 } else { 556 strncpy(buildid_dir, DEBUG_CACHE_DIR, MAXPATHLEN-1); 557 } 558 buildid_dir[MAXPATHLEN-1] = '\0'; 559 } 560 /* for communicating with external commands */ 561 setenv("PERF_BUILDID_DIR", buildid_dir, 1); 562 } 563