18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * builtin-config.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2015, Taeung Song <treeze.taeung@gmail.com> 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci#include "builtin.h" 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include "util/cache.h" 118c2ecf20Sopenharmony_ci#include <subcmd/parse-options.h> 128c2ecf20Sopenharmony_ci#include "util/debug.h" 138c2ecf20Sopenharmony_ci#include "util/config.h" 148c2ecf20Sopenharmony_ci#include <linux/string.h> 158c2ecf20Sopenharmony_ci#include <stdio.h> 168c2ecf20Sopenharmony_ci#include <stdlib.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_cistatic bool use_system_config, use_user_config; 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_cistatic const char * const config_usage[] = { 218c2ecf20Sopenharmony_ci "perf config [<file-option>] [options] [section.name[=value] ...]", 228c2ecf20Sopenharmony_ci NULL 238c2ecf20Sopenharmony_ci}; 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_cienum actions { 268c2ecf20Sopenharmony_ci ACTION_LIST = 1 278c2ecf20Sopenharmony_ci} actions; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cistatic struct option config_options[] = { 308c2ecf20Sopenharmony_ci OPT_SET_UINT('l', "list", &actions, 318c2ecf20Sopenharmony_ci "show current config variables", ACTION_LIST), 328c2ecf20Sopenharmony_ci OPT_BOOLEAN(0, "system", &use_system_config, "use system config file"), 338c2ecf20Sopenharmony_ci OPT_BOOLEAN(0, "user", &use_user_config, "use user config file"), 348c2ecf20Sopenharmony_ci OPT_END() 358c2ecf20Sopenharmony_ci}; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cistatic int set_config(struct perf_config_set *set, const char *file_name) 388c2ecf20Sopenharmony_ci{ 398c2ecf20Sopenharmony_ci struct perf_config_section *section = NULL; 408c2ecf20Sopenharmony_ci struct perf_config_item *item = NULL; 418c2ecf20Sopenharmony_ci const char *first_line = "# this file is auto-generated."; 428c2ecf20Sopenharmony_ci FILE *fp; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci if (set == NULL) 458c2ecf20Sopenharmony_ci return -1; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci fp = fopen(file_name, "w"); 488c2ecf20Sopenharmony_ci if (!fp) 498c2ecf20Sopenharmony_ci return -1; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci fprintf(fp, "%s\n", first_line); 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci /* overwrite configvariables */ 548c2ecf20Sopenharmony_ci perf_config_items__for_each_entry(&set->sections, section) { 558c2ecf20Sopenharmony_ci if (!use_system_config && section->from_system_config) 568c2ecf20Sopenharmony_ci continue; 578c2ecf20Sopenharmony_ci fprintf(fp, "[%s]\n", section->name); 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci perf_config_items__for_each_entry(§ion->items, item) { 608c2ecf20Sopenharmony_ci if (!use_system_config && item->from_system_config) 618c2ecf20Sopenharmony_ci continue; 628c2ecf20Sopenharmony_ci if (item->value) 638c2ecf20Sopenharmony_ci fprintf(fp, "\t%s = %s\n", 648c2ecf20Sopenharmony_ci item->name, item->value); 658c2ecf20Sopenharmony_ci } 668c2ecf20Sopenharmony_ci } 678c2ecf20Sopenharmony_ci fclose(fp); 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci return 0; 708c2ecf20Sopenharmony_ci} 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_cistatic int show_spec_config(struct perf_config_set *set, const char *var) 738c2ecf20Sopenharmony_ci{ 748c2ecf20Sopenharmony_ci struct perf_config_section *section; 758c2ecf20Sopenharmony_ci struct perf_config_item *item; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci if (set == NULL) 788c2ecf20Sopenharmony_ci return -1; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci perf_config_items__for_each_entry(&set->sections, section) { 818c2ecf20Sopenharmony_ci if (!strstarts(var, section->name)) 828c2ecf20Sopenharmony_ci continue; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci perf_config_items__for_each_entry(§ion->items, item) { 858c2ecf20Sopenharmony_ci const char *name = var + strlen(section->name) + 1; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci if (strcmp(name, item->name) == 0) { 888c2ecf20Sopenharmony_ci char *value = item->value; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci if (value) { 918c2ecf20Sopenharmony_ci printf("%s=%s\n", var, value); 928c2ecf20Sopenharmony_ci return 0; 938c2ecf20Sopenharmony_ci } 948c2ecf20Sopenharmony_ci } 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci } 978c2ecf20Sopenharmony_ci } 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci return 0; 1008c2ecf20Sopenharmony_ci} 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_cistatic int show_config(struct perf_config_set *set) 1038c2ecf20Sopenharmony_ci{ 1048c2ecf20Sopenharmony_ci struct perf_config_section *section; 1058c2ecf20Sopenharmony_ci struct perf_config_item *item; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci if (set == NULL) 1088c2ecf20Sopenharmony_ci return -1; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci perf_config_set__for_each_entry(set, section, item) { 1118c2ecf20Sopenharmony_ci char *value = item->value; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci if (value) 1148c2ecf20Sopenharmony_ci printf("%s.%s=%s\n", section->name, 1158c2ecf20Sopenharmony_ci item->name, value); 1168c2ecf20Sopenharmony_ci } 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci return 0; 1198c2ecf20Sopenharmony_ci} 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_cistatic int parse_config_arg(char *arg, char **var, char **value) 1228c2ecf20Sopenharmony_ci{ 1238c2ecf20Sopenharmony_ci const char *last_dot = strchr(arg, '.'); 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci /* 1268c2ecf20Sopenharmony_ci * Since "var" actually contains the section name and the real 1278c2ecf20Sopenharmony_ci * config variable name separated by a dot, we have to know where the dot is. 1288c2ecf20Sopenharmony_ci */ 1298c2ecf20Sopenharmony_ci if (last_dot == NULL || last_dot == arg) { 1308c2ecf20Sopenharmony_ci pr_err("The config variable does not contain a section name: %s\n", arg); 1318c2ecf20Sopenharmony_ci return -1; 1328c2ecf20Sopenharmony_ci } 1338c2ecf20Sopenharmony_ci if (!last_dot[1]) { 1348c2ecf20Sopenharmony_ci pr_err("The config variable does not contain a variable name: %s\n", arg); 1358c2ecf20Sopenharmony_ci return -1; 1368c2ecf20Sopenharmony_ci } 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci *value = strchr(arg, '='); 1398c2ecf20Sopenharmony_ci if (*value == NULL) 1408c2ecf20Sopenharmony_ci *var = arg; 1418c2ecf20Sopenharmony_ci else if (!strcmp(*value, "=")) { 1428c2ecf20Sopenharmony_ci pr_err("The config variable does not contain a value: %s\n", arg); 1438c2ecf20Sopenharmony_ci return -1; 1448c2ecf20Sopenharmony_ci } else { 1458c2ecf20Sopenharmony_ci *value = *value + 1; /* excluding a first character '=' */ 1468c2ecf20Sopenharmony_ci *var = strsep(&arg, "="); 1478c2ecf20Sopenharmony_ci if (*var[0] == '\0') { 1488c2ecf20Sopenharmony_ci pr_err("invalid config variable: %s\n", arg); 1498c2ecf20Sopenharmony_ci return -1; 1508c2ecf20Sopenharmony_ci } 1518c2ecf20Sopenharmony_ci } 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci return 0; 1548c2ecf20Sopenharmony_ci} 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ciint cmd_config(int argc, const char **argv) 1578c2ecf20Sopenharmony_ci{ 1588c2ecf20Sopenharmony_ci int i, ret = -1; 1598c2ecf20Sopenharmony_ci struct perf_config_set *set; 1608c2ecf20Sopenharmony_ci char *user_config = mkpath("%s/.perfconfig", getenv("HOME")); 1618c2ecf20Sopenharmony_ci const char *config_filename; 1628c2ecf20Sopenharmony_ci bool changed = false; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci argc = parse_options(argc, argv, config_options, config_usage, 1658c2ecf20Sopenharmony_ci PARSE_OPT_STOP_AT_NON_OPTION); 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci if (use_system_config && use_user_config) { 1688c2ecf20Sopenharmony_ci pr_err("Error: only one config file at a time\n"); 1698c2ecf20Sopenharmony_ci parse_options_usage(config_usage, config_options, "user", 0); 1708c2ecf20Sopenharmony_ci parse_options_usage(NULL, config_options, "system", 0); 1718c2ecf20Sopenharmony_ci return -1; 1728c2ecf20Sopenharmony_ci } 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci if (use_system_config) 1758c2ecf20Sopenharmony_ci config_exclusive_filename = perf_etc_perfconfig(); 1768c2ecf20Sopenharmony_ci else if (use_user_config) 1778c2ecf20Sopenharmony_ci config_exclusive_filename = user_config; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci if (!config_exclusive_filename) 1808c2ecf20Sopenharmony_ci config_filename = user_config; 1818c2ecf20Sopenharmony_ci else 1828c2ecf20Sopenharmony_ci config_filename = config_exclusive_filename; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci /* 1858c2ecf20Sopenharmony_ci * At only 'config' sub-command, individually use the config set 1868c2ecf20Sopenharmony_ci * because of reinitializing with options config file location. 1878c2ecf20Sopenharmony_ci */ 1888c2ecf20Sopenharmony_ci set = perf_config_set__new(); 1898c2ecf20Sopenharmony_ci if (!set) 1908c2ecf20Sopenharmony_ci goto out_err; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci switch (actions) { 1938c2ecf20Sopenharmony_ci case ACTION_LIST: 1948c2ecf20Sopenharmony_ci if (argc) { 1958c2ecf20Sopenharmony_ci pr_err("Error: takes no arguments\n"); 1968c2ecf20Sopenharmony_ci parse_options_usage(config_usage, config_options, "l", 1); 1978c2ecf20Sopenharmony_ci } else { 1988c2ecf20Sopenharmony_cido_action_list: 1998c2ecf20Sopenharmony_ci if (show_config(set) < 0) { 2008c2ecf20Sopenharmony_ci pr_err("Nothing configured, " 2018c2ecf20Sopenharmony_ci "please check your %s \n", config_filename); 2028c2ecf20Sopenharmony_ci goto out_err; 2038c2ecf20Sopenharmony_ci } 2048c2ecf20Sopenharmony_ci } 2058c2ecf20Sopenharmony_ci break; 2068c2ecf20Sopenharmony_ci default: 2078c2ecf20Sopenharmony_ci if (!argc) 2088c2ecf20Sopenharmony_ci goto do_action_list; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci for (i = 0; argv[i]; i++) { 2118c2ecf20Sopenharmony_ci char *var, *value; 2128c2ecf20Sopenharmony_ci char *arg = strdup(argv[i]); 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci if (!arg) { 2158c2ecf20Sopenharmony_ci pr_err("%s: strdup failed\n", __func__); 2168c2ecf20Sopenharmony_ci goto out_err; 2178c2ecf20Sopenharmony_ci } 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci if (parse_config_arg(arg, &var, &value) < 0) { 2208c2ecf20Sopenharmony_ci free(arg); 2218c2ecf20Sopenharmony_ci goto out_err; 2228c2ecf20Sopenharmony_ci } 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci if (value == NULL) { 2258c2ecf20Sopenharmony_ci if (show_spec_config(set, var) < 0) { 2268c2ecf20Sopenharmony_ci pr_err("%s is not configured: %s\n", 2278c2ecf20Sopenharmony_ci var, config_filename); 2288c2ecf20Sopenharmony_ci free(arg); 2298c2ecf20Sopenharmony_ci goto out_err; 2308c2ecf20Sopenharmony_ci } 2318c2ecf20Sopenharmony_ci } else { 2328c2ecf20Sopenharmony_ci if (perf_config_set__collect(set, config_filename, 2338c2ecf20Sopenharmony_ci var, value) < 0) { 2348c2ecf20Sopenharmony_ci pr_err("Failed to add '%s=%s'\n", 2358c2ecf20Sopenharmony_ci var, value); 2368c2ecf20Sopenharmony_ci free(arg); 2378c2ecf20Sopenharmony_ci goto out_err; 2388c2ecf20Sopenharmony_ci } 2398c2ecf20Sopenharmony_ci changed = true; 2408c2ecf20Sopenharmony_ci } 2418c2ecf20Sopenharmony_ci free(arg); 2428c2ecf20Sopenharmony_ci } 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci if (!changed) 2458c2ecf20Sopenharmony_ci break; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci if (set_config(set, config_filename) < 0) { 2488c2ecf20Sopenharmony_ci pr_err("Failed to set the configs on %s\n", 2498c2ecf20Sopenharmony_ci config_filename); 2508c2ecf20Sopenharmony_ci goto out_err; 2518c2ecf20Sopenharmony_ci } 2528c2ecf20Sopenharmony_ci } 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci ret = 0; 2558c2ecf20Sopenharmony_ciout_err: 2568c2ecf20Sopenharmony_ci perf_config_set__delete(set); 2578c2ecf20Sopenharmony_ci return ret; 2588c2ecf20Sopenharmony_ci} 259