162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  (C) 2010,2011       Thomas Renninger <trenn@suse.de>, Novell Inc.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *  Ideas taken over from the perf userspace tool (included in the Linus
662306a36Sopenharmony_ci *  kernel git repo): subcommand builtins and param parsing.
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <stdio.h>
1062306a36Sopenharmony_ci#include <stdlib.h>
1162306a36Sopenharmony_ci#include <string.h>
1262306a36Sopenharmony_ci#include <unistd.h>
1362306a36Sopenharmony_ci#include <errno.h>
1462306a36Sopenharmony_ci#include <sched.h>
1562306a36Sopenharmony_ci#include <sys/types.h>
1662306a36Sopenharmony_ci#include <sys/stat.h>
1762306a36Sopenharmony_ci#include <sys/utsname.h>
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#include "builtin.h"
2062306a36Sopenharmony_ci#include "helpers/helpers.h"
2162306a36Sopenharmony_ci#include "helpers/bitmask.h"
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_cistatic int cmd_help(int argc, const char **argv);
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci/* Global cpu_info object available for all binaries
2862306a36Sopenharmony_ci * Info only retrieved from CPU 0
2962306a36Sopenharmony_ci *
3062306a36Sopenharmony_ci * Values will be zero/unknown on non X86 archs
3162306a36Sopenharmony_ci */
3262306a36Sopenharmony_cistruct cpupower_cpu_info cpupower_cpu_info;
3362306a36Sopenharmony_ciint run_as_root;
3462306a36Sopenharmony_ciint base_cpu;
3562306a36Sopenharmony_ci/* Affected cpus chosen by -c/--cpu param */
3662306a36Sopenharmony_cistruct bitmask *cpus_chosen;
3762306a36Sopenharmony_cistruct bitmask *online_cpus;
3862306a36Sopenharmony_cistruct bitmask *offline_cpus;
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci#ifdef DEBUG
4162306a36Sopenharmony_ciint be_verbose;
4262306a36Sopenharmony_ci#endif
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_cistatic void print_help(void);
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_cistruct cmd_struct {
4762306a36Sopenharmony_ci	const char *cmd;
4862306a36Sopenharmony_ci	int (*main)(int, const char **);
4962306a36Sopenharmony_ci	int needs_root;
5062306a36Sopenharmony_ci};
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_cistatic struct cmd_struct commands[] = {
5362306a36Sopenharmony_ci	{ "frequency-info",	cmd_freq_info,	0	},
5462306a36Sopenharmony_ci	{ "frequency-set",	cmd_freq_set,	1	},
5562306a36Sopenharmony_ci	{ "idle-info",		cmd_idle_info,	0	},
5662306a36Sopenharmony_ci	{ "idle-set",		cmd_idle_set,	1	},
5762306a36Sopenharmony_ci	{ "powercap-info",	cmd_cap_info,	0	},
5862306a36Sopenharmony_ci	{ "set",		cmd_set,	1	},
5962306a36Sopenharmony_ci	{ "info",		cmd_info,	0	},
6062306a36Sopenharmony_ci	{ "monitor",		cmd_monitor,	0	},
6162306a36Sopenharmony_ci	{ "help",		cmd_help,	0	},
6262306a36Sopenharmony_ci	/*	{ "bench",	cmd_bench,	1	}, */
6362306a36Sopenharmony_ci};
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_cistatic void print_help(void)
6662306a36Sopenharmony_ci{
6762306a36Sopenharmony_ci	unsigned int i;
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci#ifdef DEBUG
7062306a36Sopenharmony_ci	printf(_("Usage:\tcpupower [-d|--debug] [-c|--cpu cpulist ] <command> [<args>]\n"));
7162306a36Sopenharmony_ci#else
7262306a36Sopenharmony_ci	printf(_("Usage:\tcpupower [-c|--cpu cpulist ] <command> [<args>]\n"));
7362306a36Sopenharmony_ci#endif
7462306a36Sopenharmony_ci	printf(_("Supported commands are:\n"));
7562306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(commands); i++)
7662306a36Sopenharmony_ci		printf("\t%s\n", commands[i].cmd);
7762306a36Sopenharmony_ci	printf(_("\nNot all commands can make use of the -c cpulist option.\n"));
7862306a36Sopenharmony_ci	printf(_("\nUse 'cpupower help <command>' for getting help for above commands.\n"));
7962306a36Sopenharmony_ci}
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_cistatic int print_man_page(const char *subpage)
8262306a36Sopenharmony_ci{
8362306a36Sopenharmony_ci	int len;
8462306a36Sopenharmony_ci	char *page;
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	len = 10; /* enough for "cpupower-" */
8762306a36Sopenharmony_ci	if (subpage != NULL)
8862306a36Sopenharmony_ci		len += strlen(subpage);
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	page = malloc(len);
9162306a36Sopenharmony_ci	if (!page)
9262306a36Sopenharmony_ci		return -ENOMEM;
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	sprintf(page, "cpupower");
9562306a36Sopenharmony_ci	if ((subpage != NULL) && strcmp(subpage, "help")) {
9662306a36Sopenharmony_ci		strcat(page, "-");
9762306a36Sopenharmony_ci		strcat(page, subpage);
9862306a36Sopenharmony_ci	}
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	execlp("man", "man", page, NULL);
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	/* should not be reached */
10362306a36Sopenharmony_ci	return -EINVAL;
10462306a36Sopenharmony_ci}
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_cistatic int cmd_help(int argc, const char **argv)
10762306a36Sopenharmony_ci{
10862306a36Sopenharmony_ci	if (argc > 1) {
10962306a36Sopenharmony_ci		print_man_page(argv[1]); /* exits within execlp() */
11062306a36Sopenharmony_ci		return EXIT_FAILURE;
11162306a36Sopenharmony_ci	}
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	print_help();
11462306a36Sopenharmony_ci	return EXIT_SUCCESS;
11562306a36Sopenharmony_ci}
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_cistatic void print_version(void)
11862306a36Sopenharmony_ci{
11962306a36Sopenharmony_ci	printf(PACKAGE " " VERSION "\n");
12062306a36Sopenharmony_ci	printf(_("Report errors and bugs to %s, please.\n"), PACKAGE_BUGREPORT);
12162306a36Sopenharmony_ci}
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_cistatic void handle_options(int *argc, const char ***argv)
12462306a36Sopenharmony_ci{
12562306a36Sopenharmony_ci	int ret, x, new_argc = 0;
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	if (*argc < 1)
12862306a36Sopenharmony_ci		return;
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	for (x = 0;  x < *argc && ((*argv)[x])[0] == '-'; x++) {
13162306a36Sopenharmony_ci		const char *param = (*argv)[x];
13262306a36Sopenharmony_ci		if (!strcmp(param, "-h") || !strcmp(param, "--help")) {
13362306a36Sopenharmony_ci			print_help();
13462306a36Sopenharmony_ci			exit(EXIT_SUCCESS);
13562306a36Sopenharmony_ci		} else if (!strcmp(param, "-c") || !strcmp(param, "--cpu")) {
13662306a36Sopenharmony_ci			if (*argc < 2) {
13762306a36Sopenharmony_ci				print_help();
13862306a36Sopenharmony_ci				exit(EXIT_FAILURE);
13962306a36Sopenharmony_ci			}
14062306a36Sopenharmony_ci			if (!strcmp((*argv)[x+1], "all"))
14162306a36Sopenharmony_ci				bitmask_setall(cpus_chosen);
14262306a36Sopenharmony_ci			else {
14362306a36Sopenharmony_ci				ret = bitmask_parselist(
14462306a36Sopenharmony_ci						(*argv)[x+1], cpus_chosen);
14562306a36Sopenharmony_ci				if (ret < 0) {
14662306a36Sopenharmony_ci					fprintf(stderr, _("Error parsing cpu "
14762306a36Sopenharmony_ci							  "list\n"));
14862306a36Sopenharmony_ci					exit(EXIT_FAILURE);
14962306a36Sopenharmony_ci				}
15062306a36Sopenharmony_ci			}
15162306a36Sopenharmony_ci			x += 1;
15262306a36Sopenharmony_ci			/* Cut out param: cpupower -c 1 info -> cpupower info */
15362306a36Sopenharmony_ci			new_argc += 2;
15462306a36Sopenharmony_ci			continue;
15562306a36Sopenharmony_ci		} else if (!strcmp(param, "-v") ||
15662306a36Sopenharmony_ci			!strcmp(param, "--version")) {
15762306a36Sopenharmony_ci			print_version();
15862306a36Sopenharmony_ci			exit(EXIT_SUCCESS);
15962306a36Sopenharmony_ci#ifdef DEBUG
16062306a36Sopenharmony_ci		} else if (!strcmp(param, "-d") || !strcmp(param, "--debug")) {
16162306a36Sopenharmony_ci			be_verbose = 1;
16262306a36Sopenharmony_ci			new_argc++;
16362306a36Sopenharmony_ci			continue;
16462306a36Sopenharmony_ci#endif
16562306a36Sopenharmony_ci		} else {
16662306a36Sopenharmony_ci			fprintf(stderr, "Unknown option: %s\n", param);
16762306a36Sopenharmony_ci			print_help();
16862306a36Sopenharmony_ci			exit(EXIT_FAILURE);
16962306a36Sopenharmony_ci		}
17062306a36Sopenharmony_ci	}
17162306a36Sopenharmony_ci	*argc -= new_argc;
17262306a36Sopenharmony_ci	*argv += new_argc;
17362306a36Sopenharmony_ci}
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ciint main(int argc, const char *argv[])
17662306a36Sopenharmony_ci{
17762306a36Sopenharmony_ci	const char *cmd;
17862306a36Sopenharmony_ci	unsigned int i, ret;
17962306a36Sopenharmony_ci	struct stat statbuf;
18062306a36Sopenharmony_ci	struct utsname uts;
18162306a36Sopenharmony_ci	char pathname[32];
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	cpus_chosen = bitmask_alloc(sysconf(_SC_NPROCESSORS_CONF));
18462306a36Sopenharmony_ci	online_cpus = bitmask_alloc(sysconf(_SC_NPROCESSORS_CONF));
18562306a36Sopenharmony_ci	offline_cpus = bitmask_alloc(sysconf(_SC_NPROCESSORS_CONF));
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	argc--;
18862306a36Sopenharmony_ci	argv += 1;
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	handle_options(&argc, &argv);
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	cmd = argv[0];
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	if (argc < 1) {
19562306a36Sopenharmony_ci		print_help();
19662306a36Sopenharmony_ci		return EXIT_FAILURE;
19762306a36Sopenharmony_ci	}
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	setlocale(LC_ALL, "");
20062306a36Sopenharmony_ci	textdomain(PACKAGE);
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	/* Turn "perf cmd --help" into "perf help cmd" */
20362306a36Sopenharmony_ci	if (argc > 1 && !strcmp(argv[1], "--help")) {
20462306a36Sopenharmony_ci		argv[1] = argv[0];
20562306a36Sopenharmony_ci		argv[0] = cmd = "help";
20662306a36Sopenharmony_ci	}
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	base_cpu = sched_getcpu();
20962306a36Sopenharmony_ci	if (base_cpu < 0) {
21062306a36Sopenharmony_ci		fprintf(stderr, _("No valid cpus found.\n"));
21162306a36Sopenharmony_ci		return EXIT_FAILURE;
21262306a36Sopenharmony_ci	}
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	get_cpu_info(&cpupower_cpu_info);
21562306a36Sopenharmony_ci	run_as_root = !geteuid();
21662306a36Sopenharmony_ci	if (run_as_root) {
21762306a36Sopenharmony_ci		ret = uname(&uts);
21862306a36Sopenharmony_ci		sprintf(pathname, "/dev/cpu/%d/msr", base_cpu);
21962306a36Sopenharmony_ci		if (!ret && !strcmp(uts.machine, "x86_64") &&
22062306a36Sopenharmony_ci		    stat(pathname, &statbuf) != 0) {
22162306a36Sopenharmony_ci			if (system("modprobe msr") == -1)
22262306a36Sopenharmony_ci	fprintf(stderr, _("MSR access not available.\n"));
22362306a36Sopenharmony_ci		}
22462306a36Sopenharmony_ci	}
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(commands); i++) {
22762306a36Sopenharmony_ci		struct cmd_struct *p = commands + i;
22862306a36Sopenharmony_ci		if (strcmp(p->cmd, cmd))
22962306a36Sopenharmony_ci			continue;
23062306a36Sopenharmony_ci		if (!run_as_root && p->needs_root) {
23162306a36Sopenharmony_ci			fprintf(stderr, _("Subcommand %s needs root "
23262306a36Sopenharmony_ci					  "privileges\n"), cmd);
23362306a36Sopenharmony_ci			return EXIT_FAILURE;
23462306a36Sopenharmony_ci		}
23562306a36Sopenharmony_ci		ret = p->main(argc, argv);
23662306a36Sopenharmony_ci		if (cpus_chosen)
23762306a36Sopenharmony_ci			bitmask_free(cpus_chosen);
23862306a36Sopenharmony_ci		if (online_cpus)
23962306a36Sopenharmony_ci			bitmask_free(online_cpus);
24062306a36Sopenharmony_ci		if (offline_cpus)
24162306a36Sopenharmony_ci			bitmask_free(offline_cpus);
24262306a36Sopenharmony_ci		return ret;
24362306a36Sopenharmony_ci	}
24462306a36Sopenharmony_ci	print_help();
24562306a36Sopenharmony_ci	return EXIT_FAILURE;
24662306a36Sopenharmony_ci}
247