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 | |
25 | static 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 | */ |
32 | struct cpupower_cpu_info cpupower_cpu_info; |
33 | int run_as_root; |
34 | int base_cpu; |
35 | /* Affected cpus chosen by -c/--cpu param */ |
36 | struct bitmask *cpus_chosen; |
37 | struct bitmask *online_cpus; |
38 | struct bitmask *offline_cpus; |
39 | |
40 | #ifdef DEBUG |
41 | int be_verbose; |
42 | #endif |
43 | |
44 | static void print_help(void); |
45 | |
46 | struct cmd_struct { |
47 | const char *cmd; |
48 | int (*main)(int, const char **); |
49 | int needs_root; |
50 | }; |
51 | |
52 | static 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 | |
65 | static 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 | |
81 | static 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 | |
106 | static int cmd_help(int argc, const char **argv) |
107 | { |
108 | if (argc > 1) { |
109 | print_man_page(subpage: argv[1]); /* exits within execlp() */ |
110 | return EXIT_FAILURE; |
111 | } |
112 | |
113 | print_help(); |
114 | return EXIT_SUCCESS; |
115 | } |
116 | |
117 | static void print_version(void) |
118 | { |
119 | printf(PACKAGE " " VERSION "\n" ); |
120 | printf(_("Report errors and bugs to %s, please.\n" ), PACKAGE_BUGREPORT); |
121 | } |
122 | |
123 | static 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(bmp: cpus_chosen); |
142 | else { |
143 | ret = bitmask_parselist( |
144 | buf: (*argv)[x+1], bmp: 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 | |
175 | int 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(n: sysconf(_SC_NPROCESSORS_CONF)); |
184 | online_cpus = bitmask_alloc(n: sysconf(_SC_NPROCESSORS_CONF)); |
185 | offline_cpus = bitmask_alloc(n: sysconf(_SC_NPROCESSORS_CONF)); |
186 | |
187 | argc--; |
188 | argv += 1; |
189 | |
190 | handle_options(argc: &argc, argv: &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(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(bmp: cpus_chosen); |
238 | if (online_cpus) |
239 | bitmask_free(bmp: online_cpus); |
240 | if (offline_cpus) |
241 | bitmask_free(bmp: offline_cpus); |
242 | return ret; |
243 | } |
244 | print_help(); |
245 | return EXIT_FAILURE; |
246 | } |
247 | |