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;
37
38#ifdef DEBUG
39int be_verbose;
40#endif
41
42static void print_help(void);
43
44struct cmd_struct {
45	const char *cmd;
46	int (*main)(int, const char **);
47	int needs_root;
48};
49
50static struct cmd_struct commands[] = {
51	{ "frequency-info",	cmd_freq_info,	0	},
52	{ "frequency-set",	cmd_freq_set,	1	},
53	{ "idle-info",		cmd_idle_info,	0	},
54	{ "idle-set",		cmd_idle_set,	1	},
55	{ "set",		cmd_set,	1	},
56	{ "info",		cmd_info,	0	},
57	{ "monitor",		cmd_monitor,	0	},
58	{ "help",		cmd_help,	0	},
59	/*	{ "bench",	cmd_bench,	1	}, */
60};
61
62static void print_help(void)
63{
64	unsigned int i;
65
66#ifdef DEBUG
67	printf(_("Usage:\tcpupower [-d|--debug] [-c|--cpu cpulist ] <command> [<args>]\n"));
68#else
69	printf(_("Usage:\tcpupower [-c|--cpu cpulist ] <command> [<args>]\n"));
70#endif
71	printf(_("Supported commands are:\n"));
72	for (i = 0; i < ARRAY_SIZE(commands); i++)
73		printf("\t%s\n", commands[i].cmd);
74	printf(_("\nNot all commands can make use of the -c cpulist option.\n"));
75	printf(_("\nUse 'cpupower help <command>' for getting help for above commands.\n"));
76}
77
78static int print_man_page(const char *subpage)
79{
80	int len;
81	char *page;
82
83	len = 10; /* enough for "cpupower-" */
84	if (subpage != NULL)
85		len += strlen(subpage);
86
87	page = malloc(len);
88	if (!page)
89		return -ENOMEM;
90
91	sprintf(page, "cpupower");
92	if ((subpage != NULL) && strcmp(subpage, "help")) {
93		strcat(page, "-");
94		strcat(page, subpage);
95	}
96
97	execlp("man", "man", page, NULL);
98
99	/* should not be reached */
100	return -EINVAL;
101}
102
103static int cmd_help(int argc, const char **argv)
104{
105	if (argc > 1) {
106		print_man_page(argv[1]); /* exits within execlp() */
107		return EXIT_FAILURE;
108	}
109
110	print_help();
111	return EXIT_SUCCESS;
112}
113
114static void print_version(void)
115{
116	printf(PACKAGE " " VERSION "\n");
117	printf(_("Report errors and bugs to %s, please.\n"), PACKAGE_BUGREPORT);
118}
119
120static void handle_options(int *argc, const char ***argv)
121{
122	int ret, x, new_argc = 0;
123
124	if (*argc < 1)
125		return;
126
127	for (x = 0;  x < *argc && ((*argv)[x])[0] == '-'; x++) {
128		const char *param = (*argv)[x];
129		if (!strcmp(param, "-h") || !strcmp(param, "--help")) {
130			print_help();
131			exit(EXIT_SUCCESS);
132		} else if (!strcmp(param, "-c") || !strcmp(param, "--cpu")) {
133			if (*argc < 2) {
134				print_help();
135				exit(EXIT_FAILURE);
136			}
137			if (!strcmp((*argv)[x+1], "all"))
138				bitmask_setall(cpus_chosen);
139			else {
140				ret = bitmask_parselist(
141						(*argv)[x+1], cpus_chosen);
142				if (ret < 0) {
143					fprintf(stderr, _("Error parsing cpu "
144							  "list\n"));
145					exit(EXIT_FAILURE);
146				}
147			}
148			x += 1;
149			/* Cut out param: cpupower -c 1 info -> cpupower info */
150			new_argc += 2;
151			continue;
152		} else if (!strcmp(param, "-v") ||
153			!strcmp(param, "--version")) {
154			print_version();
155			exit(EXIT_SUCCESS);
156#ifdef DEBUG
157		} else if (!strcmp(param, "-d") || !strcmp(param, "--debug")) {
158			be_verbose = 1;
159			new_argc++;
160			continue;
161#endif
162		} else {
163			fprintf(stderr, "Unknown option: %s\n", param);
164			print_help();
165			exit(EXIT_FAILURE);
166		}
167	}
168	*argc -= new_argc;
169	*argv += new_argc;
170}
171
172int main(int argc, const char *argv[])
173{
174	const char *cmd;
175	unsigned int i, ret;
176	struct stat statbuf;
177	struct utsname uts;
178	char pathname[32];
179
180	cpus_chosen = bitmask_alloc(sysconf(_SC_NPROCESSORS_CONF));
181
182	argc--;
183	argv += 1;
184
185	handle_options(&argc, &argv);
186
187	cmd = argv[0];
188
189	if (argc < 1) {
190		print_help();
191		return EXIT_FAILURE;
192	}
193
194	setlocale(LC_ALL, "");
195	textdomain(PACKAGE);
196
197	/* Turn "perf cmd --help" into "perf help cmd" */
198	if (argc > 1 && !strcmp(argv[1], "--help")) {
199		argv[1] = argv[0];
200		argv[0] = cmd = "help";
201	}
202
203	base_cpu = sched_getcpu();
204	if (base_cpu < 0) {
205		fprintf(stderr, _("No valid cpus found.\n"));
206		return EXIT_FAILURE;
207	}
208
209	get_cpu_info(&cpupower_cpu_info);
210	run_as_root = !geteuid();
211	if (run_as_root) {
212		ret = uname(&uts);
213		sprintf(pathname, "/dev/cpu/%d/msr", base_cpu);
214		if (!ret && !strcmp(uts.machine, "x86_64") &&
215		    stat(pathname, &statbuf) != 0) {
216			if (system("modprobe msr") == -1)
217	fprintf(stderr, _("MSR access not available.\n"));
218		}
219	}
220
221	for (i = 0; i < ARRAY_SIZE(commands); i++) {
222		struct cmd_struct *p = commands + i;
223		if (strcmp(p->cmd, cmd))
224			continue;
225		if (!run_as_root && p->needs_root) {
226			fprintf(stderr, _("Subcommand %s needs root "
227					  "privileges\n"), cmd);
228			return EXIT_FAILURE;
229		}
230		ret = p->main(argc, argv);
231		if (cpus_chosen)
232			bitmask_free(cpus_chosen);
233		return ret;
234	}
235	print_help();
236	return EXIT_FAILURE;
237}
238