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 c = get_next_char(); 226 if (bomptr && *bomptr) { 227 /* We are at the file beginning; skip UTF8-encoded BOM 228 * if present. Sane editors won't put this in on their 229 * own, but e.g. Windows Notepad will do it happily. */ 230 if ((unsigned char) c == *bomptr) { 231 bomptr++; 232 continue; 233 } else { 234 /* Do not tolerate partial BOM. */ 235 if (bomptr != utf8_bom) 236 break; 237 /* No BOM at file beginning. Cool. */ 238 bomptr = NULL; 239 } 240 } 241 if (c == '\n') { 242 if (config_file_eof) 243 return 0; 244 comment = 0; 245 continue; 246 } 247 if (comment || isspace(c)) 248 continue; 249 if (c == '#' || c == ';') { 250 comment = 1; 251 continue; 252 } 253 if (c == '[') { 254 baselen = get_base_var(var); 255 if (baselen <= 0) 256 break; 257 var[baselen++] = '.'; 258 var[baselen] = 0; 259 continue; 260 } 261 if (!isalpha(c)) 262 break; 263 var[baselen] = tolower(c); 264 if (get_value(fn, data, var, baselen+1) < 0) 265 break; 266 } 267 die("bad config file line %d in %s", config_linenr, config_file_name); 268 } 269 270 static int parse_unit_factor(const char *end, unsigned long *val) 271 { 272 if (!*end) 273 return 1; 274 else if (!strcasecmp(end, "k")) { 275 *val *= 1024; 276 return 1; 277 } 278 else if (!strcasecmp(end, "m")) { 279 *val *= 1024 * 1024; 280 return 1; 281 } 282 else if (!strcasecmp(end, "g")) { 283 *val *= 1024 * 1024 * 1024; 284 return 1; 285 } 286 return 0; 287 } 288 289 static int perf_parse_long(const char *value, long *ret) 290 { 291 if (value && *value) { 292 char *end; 293 long val = strtol(value, &end, 0); 294 unsigned long factor = 1; 295 if (!parse_unit_factor(end, &factor)) 296 return 0; 297 *ret = val * factor; 298 return 1; 299 } 300 return 0; 301 } 302 303 static void die_bad_config(const char *name) 304 { 305 if (config_file_name) 306 die("bad config value for '%s' in %s", name, config_file_name); 307 die("bad config value for '%s'", name); 308 } 309 310 int perf_config_int(const char *name, const char *value) 311 { 312 long ret = 0; 313 if (!perf_parse_long(value, &ret)) 314 die_bad_config(name); 315 return ret; 316 } 317 318 static int perf_config_bool_or_int(const char *name, const char *value, int *is_bool) 319 { 320 *is_bool = 1; 321 if (!value) 322 return 1; 323 if (!*value) 324 return 0; 325 if (!strcasecmp(value, "true") || !strcasecmp(value, "yes") || !strcasecmp(value, "on")) 326 return 1; 327 if (!strcasecmp(value, "false") || !strcasecmp(value, "no") || !strcasecmp(value, "off")) 328 return 0; 329 *is_bool = 0; 330 return perf_config_int(name, value); 331 } 332 333 int perf_config_bool(const char *name, const char *value) 334 { 335 int discard; 336 return !!perf_config_bool_or_int(name, value, &discard); 337 } 338 339 const char *perf_config_dirname(const char *name, const char *value) 340 { 341 if (!name) 342 return NULL; 343 return value; 344 } 345 346 static int perf_default_core_config(const char *var __maybe_unused, 347 const char *value __maybe_unused) 348 { 349 /* Add other config variables here. */ 350 return 0; 351 } 352 353 static int perf_ui_config(const char *var, const char *value) 354 { 355 /* Add other config variables here. */ 356 if (!strcmp(var, "ui.show-headers")) { 357 symbol_conf.show_hist_headers = perf_config_bool(var, value); 358 return 0; 359 } 360 return 0; 361 } 362 363 int perf_default_config(const char *var, const char *value, 364 void *dummy __maybe_unused) 365 { 366 if (!prefixcmp(var, "core.")) 367 return perf_default_core_config(var, value); 368 369 if (!prefixcmp(var, "hist.")) 370 return perf_hist_config(var, value); 371 372 if (!prefixcmp(var, "ui.")) 373 return perf_ui_config(var, value); 374 375 /* Add other config variables here. */ 376 return 0; 377 } 378 379 static int perf_config_from_file(config_fn_t fn, const char *filename, void *data) 380 { 381 int ret; 382 FILE *f = fopen(filename, "r"); 383 384 ret = -1; 385 if (f) { 386 config_file = f; 387 config_file_name = filename; 388 config_linenr = 1; 389 config_file_eof = 0; 390 ret = perf_parse_file(fn, data); 391 fclose(f); 392 config_file_name = NULL; 393 } 394 return ret; 395 } 396 397 static const char *perf_etc_perfconfig(void) 398 { 399 static const char *system_wide; 400 if (!system_wide) 401 system_wide = system_path(ETC_PERFCONFIG); 402 return system_wide; 403 } 404 405 static int perf_env_bool(const char *k, int def) 406 { 407 const char *v = getenv(k); 408 return v ? perf_config_bool(k, v) : def; 409 } 410 411 static int perf_config_system(void) 412 { 413 return !perf_env_bool("PERF_CONFIG_NOSYSTEM", 0); 414 } 415 416 static int perf_config_global(void) 417 { 418 return !perf_env_bool("PERF_CONFIG_NOGLOBAL", 0); 419 } 420 421 int perf_config(config_fn_t fn, void *data) 422 { 423 int ret = 0, found = 0; 424 const char *home = NULL; 425 426 /* Setting $PERF_CONFIG makes perf read _only_ the given config file. */ 427 if (config_exclusive_filename) 428 return perf_config_from_file(fn, config_exclusive_filename, data); 429 if (perf_config_system() && !access(perf_etc_perfconfig(), R_OK)) { 430 ret += perf_config_from_file(fn, perf_etc_perfconfig(), 431 data); 432 found += 1; 433 } 434 435 home = getenv("HOME"); 436 if (perf_config_global() && home) { 437 char *user_config = strdup(mkpath("%s/.perfconfig", home)); 438 struct stat st; 439 440 if (user_config == NULL) { 441 warning("Not enough memory to process %s/.perfconfig, " 442 "ignoring it.", home); 443 goto out; 444 } 445 446 if (stat(user_config, &st) < 0) 447 goto out_free; 448 449 if (st.st_uid && (st.st_uid != geteuid())) { 450 warning("File %s not owned by current user or root, " 451 "ignoring it.", user_config); 452 goto out_free; 453 } 454 455 if (!st.st_size) 456 goto out_free; 457 458 ret += perf_config_from_file(fn, user_config, data); 459 found += 1; 460 out_free: 461 free(user_config); 462 } 463 out: 464 if (found == 0) 465 return -1; 466 return ret; 467 } 468 469 /* 470 * Call this to report error for your variable that should not 471 * get a boolean value (i.e. "[my] var" means "true"). 472 */ 473 int config_error_nonbool(const char *var) 474 { 475 return error("Missing value for '%s'", var); 476 } 477 478 struct buildid_dir_config { 479 char *dir; 480 }; 481 482 static int buildid_dir_command_config(const char *var, const char *value, 483 void *data) 484 { 485 struct buildid_dir_config *c = data; 486 const char *v; 487 488 /* same dir for all commands */ 489 if (!prefixcmp(var, "buildid.") && !strcmp(var + 8, "dir")) { 490 v = perf_config_dirname(var, value); 491 if (!v) 492 return -1; 493 strncpy(c->dir, v, MAXPATHLEN-1); 494 c->dir[MAXPATHLEN-1] = '\0'; 495 } 496 return 0; 497 } 498 499 static void check_buildid_dir_config(void) 500 { 501 struct buildid_dir_config c; 502 c.dir = buildid_dir; 503 perf_config(buildid_dir_command_config, &c); 504 } 505 506 void set_buildid_dir(void) 507 { 508 buildid_dir[0] = '\0'; 509 510 /* try config file */ 511 check_buildid_dir_config(); 512 513 /* default to $HOME/.debug */ 514 if (buildid_dir[0] == '\0') { 515 char *v = getenv("HOME"); 516 if (v) { 517 snprintf(buildid_dir, MAXPATHLEN-1, "%s/%s", 518 v, DEBUG_CACHE_DIR); 519 } else { 520 strncpy(buildid_dir, DEBUG_CACHE_DIR, MAXPATHLEN-1); 521 } 522 buildid_dir[MAXPATHLEN-1] = '\0'; 523 } 524 /* for communicating with external commands */ 525 setenv("PERF_BUILDID_DIR", buildid_dir, 1); 526 } 527