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