root/tools/perf/builtin-help.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. parse_help_format
  2. get_man_viewer_info
  3. check_emacsclient_version
  4. exec_failed
  5. exec_woman_emacs
  6. exec_man_konqueror
  7. exec_man_man
  8. exec_man_cmd
  9. add_man_viewer
  10. supported_man_viewer
  11. do_add_man_viewer_info
  12. unsupported_man_viewer
  13. add_man_viewer_path
  14. add_man_viewer_cmd
  15. add_man_viewer_info
  16. perf_help_config
  17. list_common_cmds_help
  18. cmd_to_page
  19. setup_man_path
  20. exec_viewer
  21. show_man_page
  22. show_info_page
  23. get_html_page_path
  24. open_html
  25. show_html_page
  26. cmd_help

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

/* [<][>][^][v][top][bottom][index][help] */