]> asedeno.scripts.mit.edu Git - linux.git/blob - tools/perf/builtin-help.c
perf tools: Add missing headers, mostly stdlib.h
[linux.git] / tools / perf / builtin-help.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * builtin-help.c
4  *
5  * Builtin help command
6  */
7 #include "perf.h"
8 #include "util/config.h"
9 #include "builtin.h"
10 #include <subcmd/exec-cmd.h>
11 #include "common-cmds.h"
12 #include <subcmd/parse-options.h>
13 #include <subcmd/run-command.h>
14 #include <subcmd/help.h>
15 #include "util/debug.h"
16 #include <linux/kernel.h>
17 #include <errno.h>
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <sys/types.h>
21 #include <sys/stat.h>
22 #include <unistd.h>
23
24 static struct man_viewer_list {
25         struct man_viewer_list *next;
26         char name[0];
27 } *man_viewer_list;
28
29 static struct man_viewer_info_list {
30         struct man_viewer_info_list *next;
31         const char *info;
32         char name[0];
33 } *man_viewer_info_list;
34
35 enum help_format {
36         HELP_FORMAT_NONE,
37         HELP_FORMAT_MAN,
38         HELP_FORMAT_INFO,
39         HELP_FORMAT_WEB,
40 };
41
42 static enum help_format parse_help_format(const char *format)
43 {
44         if (!strcmp(format, "man"))
45                 return HELP_FORMAT_MAN;
46         if (!strcmp(format, "info"))
47                 return HELP_FORMAT_INFO;
48         if (!strcmp(format, "web") || !strcmp(format, "html"))
49                 return HELP_FORMAT_WEB;
50
51         pr_err("unrecognized help format '%s'", format);
52         return HELP_FORMAT_NONE;
53 }
54
55 static const char *get_man_viewer_info(const char *name)
56 {
57         struct man_viewer_info_list *viewer;
58
59         for (viewer = man_viewer_info_list; viewer; viewer = viewer->next) {
60                 if (!strcasecmp(name, viewer->name))
61                         return viewer->info;
62         }
63         return NULL;
64 }
65
66 static int check_emacsclient_version(void)
67 {
68         struct strbuf buffer = STRBUF_INIT;
69         struct child_process ec_process;
70         const char *argv_ec[] = { "emacsclient", "--version", NULL };
71         int version;
72         int ret = -1;
73
74         /* emacsclient prints its version number on stderr */
75         memset(&ec_process, 0, sizeof(ec_process));
76         ec_process.argv = argv_ec;
77         ec_process.err = -1;
78         ec_process.stdout_to_stderr = 1;
79         if (start_command(&ec_process)) {
80                 fprintf(stderr, "Failed to start emacsclient.\n");
81                 return -1;
82         }
83         if (strbuf_read(&buffer, ec_process.err, 20) < 0) {
84                 fprintf(stderr, "Failed to read emacsclient version\n");
85                 goto out;
86         }
87         close(ec_process.err);
88
89         /*
90          * Don't bother checking return value, because "emacsclient --version"
91          * seems to always exits with code 1.
92          */
93         finish_command(&ec_process);
94
95         if (!strstarts(buffer.buf, "emacsclient")) {
96                 fprintf(stderr, "Failed to parse emacsclient version.\n");
97                 goto out;
98         }
99
100         version = atoi(buffer.buf + strlen("emacsclient"));
101
102         if (version < 22) {
103                 fprintf(stderr,
104                         "emacsclient version '%d' too old (< 22).\n",
105                         version);
106         } else
107                 ret = 0;
108 out:
109         strbuf_release(&buffer);
110         return ret;
111 }
112
113 static void exec_failed(const char *cmd)
114 {
115         char sbuf[STRERR_BUFSIZE];
116         pr_warning("failed to exec '%s': %s", cmd, str_error_r(errno, sbuf, sizeof(sbuf)));
117 }
118
119 static void exec_woman_emacs(const char *path, const char *page)
120 {
121         if (!check_emacsclient_version()) {
122                 /* This works only with emacsclient version >= 22. */
123                 char *man_page;
124
125                 if (!path)
126                         path = "emacsclient";
127                 if (asprintf(&man_page, "(woman \"%s\")", page) > 0) {
128                         execlp(path, "emacsclient", "-e", man_page, NULL);
129                         free(man_page);
130                 }
131                 exec_failed(path);
132         }
133 }
134
135 static void exec_man_konqueror(const char *path, const char *page)
136 {
137         const char *display = getenv("DISPLAY");
138
139         if (display && *display) {
140                 char *man_page;
141                 const char *filename = "kfmclient";
142
143                 /* It's simpler to launch konqueror using kfmclient. */
144                 if (path) {
145                         const char *file = strrchr(path, '/');
146                         if (file && !strcmp(file + 1, "konqueror")) {
147                                 char *new = strdup(path);
148                                 char *dest = strrchr(new, '/');
149
150                                 /* strlen("konqueror") == strlen("kfmclient") */
151                                 strcpy(dest + 1, "kfmclient");
152                                 path = new;
153                         }
154                         if (file)
155                                 filename = file;
156                 } else
157                         path = "kfmclient";
158                 if (asprintf(&man_page, "man:%s(1)", page) > 0) {
159                         execlp(path, filename, "newTab", man_page, NULL);
160                         free(man_page);
161                 }
162                 exec_failed(path);
163         }
164 }
165
166 static void exec_man_man(const char *path, const char *page)
167 {
168         if (!path)
169                 path = "man";
170         execlp(path, "man", page, NULL);
171         exec_failed(path);
172 }
173
174 static void exec_man_cmd(const char *cmd, const char *page)
175 {
176         char *shell_cmd;
177
178         if (asprintf(&shell_cmd, "%s %s", cmd, page) > 0) {
179                 execl("/bin/sh", "sh", "-c", shell_cmd, NULL);
180                 free(shell_cmd);
181         }
182         exec_failed(cmd);
183 }
184
185 static void add_man_viewer(const char *name)
186 {
187         struct man_viewer_list **p = &man_viewer_list;
188         size_t len = strlen(name);
189
190         while (*p)
191                 p = &((*p)->next);
192         *p = zalloc(sizeof(**p) + len + 1);
193         strcpy((*p)->name, name);
194 }
195
196 static int supported_man_viewer(const char *name, size_t len)
197 {
198         return (!strncasecmp("man", name, len) ||
199                 !strncasecmp("woman", name, len) ||
200                 !strncasecmp("konqueror", name, len));
201 }
202
203 static void do_add_man_viewer_info(const char *name,
204                                    size_t len,
205                                    const char *value)
206 {
207         struct man_viewer_info_list *new = zalloc(sizeof(*new) + len + 1);
208
209         strncpy(new->name, name, len);
210         new->info = strdup(value);
211         new->next = man_viewer_info_list;
212         man_viewer_info_list = new;
213 }
214
215 static void unsupported_man_viewer(const char *name, const char *var)
216 {
217         pr_warning("'%s': path for unsupported man viewer.\n"
218                    "Please consider using 'man.<tool>.%s' instead.", name, var);
219 }
220
221 static int add_man_viewer_path(const char *name,
222                                size_t len,
223                                const char *value)
224 {
225         if (supported_man_viewer(name, len))
226                 do_add_man_viewer_info(name, len, value);
227         else
228                 unsupported_man_viewer(name, "cmd");
229
230         return 0;
231 }
232
233 static int add_man_viewer_cmd(const char *name,
234                               size_t len,
235                               const char *value)
236 {
237         if (supported_man_viewer(name, len))
238                 unsupported_man_viewer(name, "path");
239         else
240                 do_add_man_viewer_info(name, len, value);
241
242         return 0;
243 }
244
245 static int add_man_viewer_info(const char *var, const char *value)
246 {
247         const char *name = var + 4;
248         const char *subkey = strrchr(name, '.');
249
250         if (!subkey) {
251                 pr_err("Config with no key for man viewer: %s", name);
252                 return -1;
253         }
254
255         if (!strcmp(subkey, ".path")) {
256                 if (!value)
257                         return config_error_nonbool(var);
258                 return add_man_viewer_path(name, subkey - name, value);
259         }
260         if (!strcmp(subkey, ".cmd")) {
261                 if (!value)
262                         return config_error_nonbool(var);
263                 return add_man_viewer_cmd(name, subkey - name, value);
264         }
265
266         pr_warning("'%s': unsupported man viewer sub key.", subkey);
267         return 0;
268 }
269
270 static int perf_help_config(const char *var, const char *value, void *cb)
271 {
272         enum help_format *help_formatp = cb;
273
274         if (!strcmp(var, "help.format")) {
275                 if (!value)
276                         return config_error_nonbool(var);
277                 *help_formatp = parse_help_format(value);
278                 if (*help_formatp == HELP_FORMAT_NONE)
279                         return -1;
280                 return 0;
281         }
282         if (!strcmp(var, "man.viewer")) {
283                 if (!value)
284                         return config_error_nonbool(var);
285                 add_man_viewer(value);
286                 return 0;
287         }
288         if (strstarts(var, "man."))
289                 return add_man_viewer_info(var, value);
290
291         return 0;
292 }
293
294 static struct cmdnames main_cmds, other_cmds;
295
296 void list_common_cmds_help(void)
297 {
298         unsigned int i, longest = 0;
299
300         for (i = 0; i < ARRAY_SIZE(common_cmds); i++) {
301                 if (longest < strlen(common_cmds[i].name))
302                         longest = strlen(common_cmds[i].name);
303         }
304
305         puts(" The most commonly used perf commands are:");
306         for (i = 0; i < ARRAY_SIZE(common_cmds); i++) {
307                 printf("   %-*s   ", longest, common_cmds[i].name);
308                 puts(common_cmds[i].help);
309         }
310 }
311
312 static const char *cmd_to_page(const char *perf_cmd)
313 {
314         char *s;
315
316         if (!perf_cmd)
317                 return "perf";
318         else if (strstarts(perf_cmd, "perf"))
319                 return perf_cmd;
320
321         return asprintf(&s, "perf-%s", perf_cmd) < 0 ? NULL : s;
322 }
323
324 static void setup_man_path(void)
325 {
326         char *new_path;
327         const char *old_path = getenv("MANPATH");
328
329         /* We should always put ':' after our path. If there is no
330          * old_path, the ':' at the end will let 'man' to try
331          * system-wide paths after ours to find the manual page. If
332          * there is old_path, we need ':' as delimiter. */
333         if (asprintf(&new_path, "%s:%s", system_path(PERF_MAN_PATH), old_path ?: "") > 0) {
334                 setenv("MANPATH", new_path, 1);
335                 free(new_path);
336         } else {
337                 pr_err("Unable to setup man path");
338         }
339 }
340
341 static void exec_viewer(const char *name, const char *page)
342 {
343         const char *info = get_man_viewer_info(name);
344
345         if (!strcasecmp(name, "man"))
346                 exec_man_man(info, page);
347         else if (!strcasecmp(name, "woman"))
348                 exec_woman_emacs(info, page);
349         else if (!strcasecmp(name, "konqueror"))
350                 exec_man_konqueror(info, page);
351         else if (info)
352                 exec_man_cmd(info, page);
353         else
354                 pr_warning("'%s': unknown man viewer.", name);
355 }
356
357 static int show_man_page(const char *perf_cmd)
358 {
359         struct man_viewer_list *viewer;
360         const char *page = cmd_to_page(perf_cmd);
361         const char *fallback = getenv("PERF_MAN_VIEWER");
362
363         setup_man_path();
364         for (viewer = man_viewer_list; viewer; viewer = viewer->next)
365                 exec_viewer(viewer->name, page); /* will return when unable */
366
367         if (fallback)
368                 exec_viewer(fallback, page);
369         exec_viewer("man", page);
370
371         pr_err("no man viewer handled the request");
372         return -1;
373 }
374
375 static int show_info_page(const char *perf_cmd)
376 {
377         const char *page = cmd_to_page(perf_cmd);
378         setenv("INFOPATH", system_path(PERF_INFO_PATH), 1);
379         execlp("info", "info", "perfman", page, NULL);
380         return -1;
381 }
382
383 static int get_html_page_path(char **page_path, const char *page)
384 {
385         struct stat st;
386         const char *html_path = system_path(PERF_HTML_PATH);
387
388         /* Check that we have a perf documentation directory. */
389         if (stat(mkpath("%s/perf.html", html_path), &st)
390             || !S_ISREG(st.st_mode)) {
391                 pr_err("'%s': not a documentation directory.", html_path);
392                 return -1;
393         }
394
395         return asprintf(page_path, "%s/%s.html", html_path, page);
396 }
397
398 /*
399  * If open_html is not defined in a platform-specific way (see for
400  * example compat/mingw.h), we use the script web--browse to display
401  * HTML.
402  */
403 #ifndef open_html
404 static void open_html(const char *path)
405 {
406         execl_cmd("web--browse", "-c", "help.browser", path, NULL);
407 }
408 #endif
409
410 static int show_html_page(const char *perf_cmd)
411 {
412         const char *page = cmd_to_page(perf_cmd);
413         char *page_path; /* it leaks but we exec bellow */
414
415         if (get_html_page_path(&page_path, page) < 0)
416                 return -1;
417
418         open_html(page_path);
419
420         return 0;
421 }
422
423 int cmd_help(int argc, const char **argv)
424 {
425         bool show_all = false;
426         enum help_format help_format = HELP_FORMAT_MAN;
427         struct option builtin_help_options[] = {
428         OPT_BOOLEAN('a', "all", &show_all, "print all available commands"),
429         OPT_SET_UINT('m', "man", &help_format, "show man page", HELP_FORMAT_MAN),
430         OPT_SET_UINT('w', "web", &help_format, "show manual in web browser",
431                         HELP_FORMAT_WEB),
432         OPT_SET_UINT('i', "info", &help_format, "show info page",
433                         HELP_FORMAT_INFO),
434         OPT_END(),
435         };
436         const char * const builtin_help_subcommands[] = {
437                 "buildid-cache", "buildid-list", "diff", "evlist", "help", "list",
438                 "record", "report", "bench", "stat", "timechart", "top", "annotate",
439                 "script", "sched", "kallsyms", "kmem", "lock", "kvm", "test", "inject", "mem", "data",
440 #ifdef HAVE_LIBELF_SUPPORT
441                 "probe",
442 #endif
443 #if defined(HAVE_LIBAUDIT_SUPPORT) || defined(HAVE_SYSCALL_TABLE_SUPPORT)
444                 "trace",
445 #endif
446         NULL };
447         const char *builtin_help_usage[] = {
448                 "perf help [--all] [--man|--web|--info] [command]",
449                 NULL
450         };
451         int rc;
452
453         load_command_list("perf-", &main_cmds, &other_cmds);
454
455         rc = perf_config(perf_help_config, &help_format);
456         if (rc)
457                 return rc;
458
459         argc = parse_options_subcommand(argc, argv, builtin_help_options,
460                         builtin_help_subcommands, builtin_help_usage, 0);
461
462         if (show_all) {
463                 printf("\n Usage: %s\n\n", perf_usage_string);
464                 list_commands("perf commands", &main_cmds, &other_cmds);
465                 printf(" %s\n\n", perf_more_info_string);
466                 return 0;
467         }
468
469         if (!argv[0]) {
470                 printf("\n usage: %s\n\n", perf_usage_string);
471                 list_common_cmds_help();
472                 printf("\n %s\n\n", perf_more_info_string);
473                 return 0;
474         }
475
476         switch (help_format) {
477         case HELP_FORMAT_MAN:
478                 rc = show_man_page(argv[0]);
479                 break;
480         case HELP_FORMAT_INFO:
481                 rc = show_info_page(argv[0]);
482                 break;
483         case HELP_FORMAT_WEB:
484                 rc = show_html_page(argv[0]);
485                 break;
486         case HELP_FORMAT_NONE:
487                 /* fall-through */
488         default:
489                 rc = -1;
490                 break;
491         }
492
493         return rc;
494 }