1// SPDX-License-Identifier: GPL-2.0-only
2/*
3 *  (C) 2010,2011       Thomas Renninger <trenn@suse.de>, Novell Inc.
4 *
5 *  Ideas taken over from the perf userspace tool (included in the Linus
6 *  kernel git repo): subcommand builtins and param parsing.
7 */
8
9#include <stdio.h>
10#include <stdlib.h>
11#include <string.h>
12#include <unistd.h>
13#include <errno.h>
14#include <sched.h>
15#include <sys/types.h>
16#include <sys/stat.h>
17#include <sys/utsname.h>
18
19#include "builtin.h"
20#include "helpers/helpers.h"
21#include "helpers/bitmask.h"
22
23#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
24
25static int cmd_help(int argc, const char **argv);
26
27/* Global cpu_info object available for all binaries
28 * Info only retrieved from CPU 0
29 *
30 * Values will be zero/unknown on non X86 archs
31 */
32struct cpupower_cpu_info cpupower_cpu_info;
33int run_as_root;
34int base_cpu;
35/* Affected cpus chosen by -c/--cpu param */
36struct bitmask *cpus_chosen;
37struct bitmask *online_cpus;
38struct bitmask *offline_cpus;
39
40#ifdef DEBUG
41int be_verbose;
42#endif
43
44static void print_help(void);
45
46struct cmd_struct {
47	const char *cmd;
48	int (*main)(int, const char **);
49	int needs_root;
50};
51
52static struct cmd_struct commands[] = {
53	{ "frequency-info",	cmd_freq_info,	0	},
54	{ "frequency-set",	cmd_freq_set,	1	},
55	{ "idle-info",		cmd_idle_info,	0	},
56	{ "idle-set",		cmd_idle_set,	1	},
57	{ "powercap-info",	cmd_cap_info,	0	},
58	{ "set",		cmd_set,	1	},
59	{ "info",		cmd_info,	0	},
60	{ "monitor",		cmd_monitor,	0	},
61	{ "help",		cmd_help,	0	},
62	/*	{ "bench",	cmd_bench,	1	}, */
63};
64
65static void print_help(void)
66{
67	unsigned int i;
68
69#ifdef DEBUG
70	printf(_("Usage:\tcpupower [-d|--debug] [-c|--cpu cpulist ] <command> [<args>]\n"));
71#else
72	printf(_("Usage:\tcpupower [-c|--cpu cpulist ] <command> [<args>]\n"));
73#endif
74	printf(_("Supported commands are:\n"));
75	for (i = 0; i < ARRAY_SIZE(commands); i++)
76		printf("\t%s\n", commands[i].cmd);
77	printf(_("\nNot all commands can make use of the -c cpulist option.\n"));
78	printf(_("\nUse 'cpupower help <command>' for getting help for above commands.\n"));
79}
80
81static int print_man_page(const char *subpage)
82{
83	int len;
84	char *page;
85
86	len = 10; /* enough for "cpupower-" */
87	if (subpage != NULL)
88		len += strlen(subpage);
89
90	page = malloc(len);
91	if (!page)
92		return -ENOMEM;
93
94	sprintf(page, "cpupower");
95	if ((subpage != NULL) && strcmp(subpage, "help")) {
96		strcat(page, "-");
97		strcat(page, subpage);
98	}
99
100	execlp("man", "man", page, NULL);
101
102	/* should not be reached */
103	return -EINVAL;
104}
105
106static int cmd_help(int argc, const char **argv)
107{
108	if (argc > 1) {
109		print_man_page(argv[1]); /* exits within execlp() */
110		return EXIT_FAILURE;
111	}
112
113	print_help();
114	return EXIT_SUCCESS;
115}
116
117static void print_version(void)
118{
119	printf(PACKAGE " " VERSION "\n");
120	printf(_("Report errors and bugs to %s, please.\n"), PACKAGE_BUGREPORT);
121}
122
123static void handle_options(int *argc, const char ***argv)
124{
125	int ret, x, new_argc = 0;
126
127	if (*argc < 1)
128		return;
129
130	for (x = 0;  x < *argc && ((*argv)[x])[0] == '-'; x++) {
131		const char *param = (*argv)[x];
132		if (!strcmp(param, "-h") || !strcmp(param, "--help")) {
133			print_help();
134			exit(EXIT_SUCCESS);
135		} else if (!strcmp(param, "-c") || !strcmp(param, "--cpu")) {
136			if (*argc < 2) {
137				print_help();
138				exit(EXIT_FAILURE);
139			}
140			if (!strcmp((*argv)[x+1], "all"))
141				bitmask_setall(cpus_chosen);
142			else {
143				ret = bitmask_parselist(
144						(*argv)[x+1], cpus_chosen);
145				if (ret < 0) {
146					fprintf(stderr, _("Error parsing cpu "
147							  "list\n"));
148					exit(EXIT_FAILURE);
149				}
150			}
151			x += 1;
152			/* Cut out param: cpupower -c 1 info -> cpupower info */
153			new_argc += 2;
154			continue;
155		} else if (!strcmp(param, "-v") ||
156			!strcmp(param, "--version")) {
157			print_version();
158			exit(EXIT_SUCCESS);
159#ifdef DEBUG
160		} else if (!strcmp(param, "-d") || !strcmp(param, "--debug")) {
161			be_verbose = 1;
162			new_argc++;
163			continue;
164#endif
165		} else {
166			fprintf(stderr, "Unknown option: %s\n", param);
167			print_help();
168			exit(EXIT_FAILURE);
169		}
170	}
171	*argc -= new_argc;
172	*argv += new_argc;
173}
174
175int main(int argc, const char *argv[])
176{
177	const char *cmd;
178	unsigned int i, ret;
179	struct stat statbuf;
180	struct utsname uts;
181	char pathname[32];
182
183	cpus_chosen = bitmask_alloc(sysconf(_SC_NPROCESSORS_CONF));
184	online_cpus = bitmask_alloc(sysconf(_SC_NPROCESSORS_CONF));
185	offline_cpus = bitmask_alloc(sysconf(_SC_NPROCESSORS_CONF));
186
187	argc--;
188	argv += 1;
189
190	handle_options(&argc, &argv);
191
192	cmd = argv[0];
193
194	if (argc < 1) {
195		print_help();
196		return EXIT_FAILURE;
197	}
198
199	setlocale(LC_ALL, "");
200	textdomain(PACKAGE);
201
202	/* Turn "perf cmd --help" into "perf help cmd" */
203	if (argc > 1 && !strcmp(argv[1], "--help")) {
204		argv[1] = argv[0];
205		argv[0] = cmd = "help";
206	}
207
208	base_cpu = sched_getcpu();
209	if (base_cpu < 0) {
210		fprintf(stderr, _("No valid cpus found.\n"));
211		return EXIT_FAILURE;
212	}
213
214	get_cpu_info(&cpupower_cpu_info);
215	run_as_root = !geteuid();
216	if (run_as_root) {
217		ret = uname(&uts);
218		sprintf(pathname, "/dev/cpu/%d/msr", base_cpu);
219		if (!ret && !strcmp(uts.machine, "x86_64") &&
220		    stat(pathname, &statbuf) != 0) {
221			if (system("modprobe msr") == -1)
222	fprintf(stderr, _("MSR access not available.\n"));
223		}
224	}
225
226	for (i = 0; i < ARRAY_SIZE(commands); i++) {
227		struct cmd_struct *p = commands + i;
228		if (strcmp(p->cmd, cmd))
229			continue;
230		if (!run_as_root && p->needs_root) {
231			fprintf(stderr, _("Subcommand %s needs root "
232					  "privileges\n"), cmd);
233			return EXIT_FAILURE;
234		}
235		ret = p->main(argc, argv);
236		if (cpus_chosen)
237			bitmask_free(cpus_chosen);
238		if (online_cpus)
239			bitmask_free(online_cpus);
240		if (offline_cpus)
241			bitmask_free(offline_cpus);
242		return ret;
243	}
244	print_help();
245	return EXIT_FAILURE;
246}
247