linux/tools/perf/util/config.c
<<
>>
Prefs
   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
  20char buildid_dir[MAXPATHLEN]; /* root dir for buildid, binary cache */
  21
  22static FILE *config_file;
  23static const char *config_file_name;
  24static int config_linenr;
  25static int config_file_eof;
  26
  27static const char *config_exclusive_filename;
  28
  29static 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
  55static 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
 121static inline int iskeychar(int c)
 122{
 123        return isalnum(c) || c == '-' || c == '_';
 124}
 125
 126static 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
 157static 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
 193static 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
 213static 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
 269static 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
 288static 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
 302static 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
 309int 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
 317static 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
 332int 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
 338const char *perf_config_dirname(const char *name, const char *value)
 339{
 340        if (!name)
 341                return NULL;
 342        return value;
 343}
 344
 345static 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
 352int 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
 362static 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
 380static 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
 388static 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
 394static int perf_config_system(void)
 395{
 396        return !perf_env_bool("PERF_CONFIG_NOSYSTEM", 0);
 397}
 398
 399static int perf_config_global(void)
 400{
 401        return !perf_env_bool("PERF_CONFIG_NOGLOBAL", 0);
 402}
 403
 404int 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;
 443out_free:
 444                free(user_config);
 445        }
 446out:
 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 */
 456int config_error_nonbool(const char *var)
 457{
 458        return error("Missing value for '%s'", var);
 459}
 460
 461struct buildid_dir_config {
 462        char *dir;
 463};
 464
 465static 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
 482static 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
 489void 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