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 int perf_default_config(const char *var, const char *value, 354 void *dummy __maybe_unused) 355 { 356 if (!prefixcmp(var, "core.")) 357 return perf_default_core_config(var, value); 358 359 if (!prefixcmp(var, "hist.")) 360 return perf_hist_config(var, value); 361 362 /* Add other config variables here. */ 363 return 0; 364 } 365 366 static int perf_config_from_file(config_fn_t fn, const char *filename, void *data) 367 { 368 int ret; 369 FILE *f = fopen(filename, "r"); 370 371 ret = -1; 372 if (f) { 373 config_file = f; 374 config_file_name = filename; 375 config_linenr = 1; 376 config_file_eof = 0; 377 ret = perf_parse_file(fn, data); 378 fclose(f); 379 config_file_name = NULL; 380 } 381 return ret; 382 } 383 384 static const char *perf_etc_perfconfig(void) 385 { 386 static const char *system_wide; 387 if (!system_wide) 388 system_wide = system_path(ETC_PERFCONFIG); 389 return system_wide; 390 } 391 392 static int perf_env_bool(const char *k, int def) 393 { 394 const char *v = getenv(k); 395 return v ? perf_config_bool(k, v) : def; 396 } 397 398 static int perf_config_system(void) 399 { 400 return !perf_env_bool("PERF_CONFIG_NOSYSTEM", 0); 401 } 402 403 static int perf_config_global(void) 404 { 405 return !perf_env_bool("PERF_CONFIG_NOGLOBAL", 0); 406 } 407 408 int perf_config(config_fn_t fn, void *data) 409 { 410 int ret = 0, found = 0; 411 const char *home = NULL; 412 413 /* Setting $PERF_CONFIG makes perf read _only_ the given config file. */ 414 if (config_exclusive_filename) 415 return perf_config_from_file(fn, config_exclusive_filename, data); 416 if (perf_config_system() && !access(perf_etc_perfconfig(), R_OK)) { 417 ret += perf_config_from_file(fn, perf_etc_perfconfig(), 418 data); 419 found += 1; 420 } 421 422 home = getenv("HOME"); 423 if (perf_config_global() && home) { 424 char *user_config = strdup(mkpath("%s/.perfconfig", home)); 425 struct stat st; 426 427 if (user_config == NULL) { 428 warning("Not enough memory to process %s/.perfconfig, " 429 "ignoring it.", home); 430 goto out; 431 } 432 433 if (stat(user_config, &st) < 0) 434 goto out_free; 435 436 if (st.st_uid && (st.st_uid != geteuid())) { 437 warning("File %s not owned by current user or root, " 438 "ignoring it.", user_config); 439 goto out_free; 440 } 441 442 if (!st.st_size) 443 goto out_free; 444 445 ret += perf_config_from_file(fn, user_config, data); 446 found += 1; 447 out_free: 448 free(user_config); 449 } 450 out: 451 if (found == 0) 452 return -1; 453 return ret; 454 } 455 456 /* 457 * Call this to report error for your variable that should not 458 * get a boolean value (i.e. "[my] var" means "true"). 459 */ 460 int config_error_nonbool(const char *var) 461 { 462 return error("Missing value for '%s'", var); 463 } 464 465 struct buildid_dir_config { 466 char *dir; 467 }; 468 469 static int buildid_dir_command_config(const char *var, const char *value, 470 void *data) 471 { 472 struct buildid_dir_config *c = data; 473 const char *v; 474 475 /* same dir for all commands */ 476 if (!prefixcmp(var, "buildid.") && !strcmp(var + 8, "dir")) { 477 v = perf_config_dirname(var, value); 478 if (!v) 479 return -1; 480 strncpy(c->dir, v, MAXPATHLEN-1); 481 c->dir[MAXPATHLEN-1] = '\0'; 482 } 483 return 0; 484 } 485 486 static void check_buildid_dir_config(void) 487 { 488 struct buildid_dir_config c; 489 c.dir = buildid_dir; 490 perf_config(buildid_dir_command_config, &c); 491 } 492 493 void set_buildid_dir(void) 494 { 495 buildid_dir[0] = '\0'; 496 497 /* try config file */ 498 check_buildid_dir_config(); 499 500 /* default to $HOME/.debug */ 501 if (buildid_dir[0] == '\0') { 502 char *v = getenv("HOME"); 503 if (v) { 504 snprintf(buildid_dir, MAXPATHLEN-1, "%s/%s", 505 v, DEBUG_CACHE_DIR); 506 } else { 507 strncpy(buildid_dir, DEBUG_CACHE_DIR, MAXPATHLEN-1); 508 } 509 buildid_dir[MAXPATHLEN-1] = '\0'; 510 } 511 /* for communicating with external commands */ 512 setenv("PERF_BUILDID_DIR", buildid_dir, 1); 513 } 514