main.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547
  1. // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
  2. /* Copyright (C) 2017-2018 Netronome Systems, Inc. */
  3. #include <ctype.h>
  4. #include <errno.h>
  5. #include <getopt.h>
  6. #include <linux/bpf.h>
  7. #include <stdio.h>
  8. #include <stdlib.h>
  9. #include <string.h>
  10. #include <bpf/bpf.h>
  11. #include <bpf/btf.h>
  12. #include <bpf/hashmap.h>
  13. #include <bpf/libbpf.h>
  14. #include "main.h"
  15. #define BATCH_LINE_LEN_MAX 65536
  16. #define BATCH_ARG_NB_MAX 4096
  17. const char *bin_name;
  18. static int last_argc;
  19. static char **last_argv;
  20. static int (*last_do_help)(int argc, char **argv);
  21. json_writer_t *json_wtr;
  22. bool pretty_output;
  23. bool json_output;
  24. bool show_pinned;
  25. bool block_mount;
  26. bool verifier_logs;
  27. bool relaxed_maps;
  28. bool use_loader;
  29. struct btf *base_btf;
  30. struct hashmap *refs_table;
  31. static void __noreturn clean_and_exit(int i)
  32. {
  33. if (json_output)
  34. jsonw_destroy(&json_wtr);
  35. exit(i);
  36. }
  37. void usage(void)
  38. {
  39. last_do_help(last_argc - 1, last_argv + 1);
  40. clean_and_exit(-1);
  41. }
  42. static int do_help(int argc, char **argv)
  43. {
  44. if (json_output) {
  45. jsonw_null(json_wtr);
  46. return 0;
  47. }
  48. fprintf(stderr,
  49. "Usage: %s [OPTIONS] OBJECT { COMMAND | help }\n"
  50. " %s batch file FILE\n"
  51. " %s version\n"
  52. "\n"
  53. " OBJECT := { prog | map | link | cgroup | perf | net | feature | btf | gen | struct_ops | iter }\n"
  54. " " HELP_SPEC_OPTIONS " |\n"
  55. " {-V|--version} }\n"
  56. "",
  57. bin_name, bin_name, bin_name);
  58. return 0;
  59. }
  60. static int do_batch(int argc, char **argv);
  61. static int do_version(int argc, char **argv);
  62. static const struct cmd commands[] = {
  63. { "help", do_help },
  64. { "batch", do_batch },
  65. { "prog", do_prog },
  66. { "map", do_map },
  67. { "link", do_link },
  68. { "cgroup", do_cgroup },
  69. { "perf", do_perf },
  70. { "net", do_net },
  71. { "feature", do_feature },
  72. { "btf", do_btf },
  73. { "gen", do_gen },
  74. { "struct_ops", do_struct_ops },
  75. { "iter", do_iter },
  76. { "version", do_version },
  77. { 0 }
  78. };
  79. #ifndef BPFTOOL_VERSION
  80. /* bpftool's major and minor version numbers are aligned on libbpf's. There is
  81. * an offset of 6 for the version number, because bpftool's version was higher
  82. * than libbpf's when we adopted this scheme. The patch number remains at 0
  83. * for now. Set BPFTOOL_VERSION to override.
  84. */
  85. #define BPFTOOL_MAJOR_VERSION (LIBBPF_MAJOR_VERSION + 6)
  86. #define BPFTOOL_MINOR_VERSION LIBBPF_MINOR_VERSION
  87. #define BPFTOOL_PATCH_VERSION 0
  88. #endif
  89. static void
  90. print_feature(const char *feature, bool state, unsigned int *nb_features)
  91. {
  92. if (state) {
  93. printf("%s %s", *nb_features ? "," : "", feature);
  94. *nb_features = *nb_features + 1;
  95. }
  96. }
  97. static int do_version(int argc, char **argv)
  98. {
  99. #ifdef HAVE_LIBBFD_SUPPORT
  100. const bool has_libbfd = true;
  101. #else
  102. const bool has_libbfd = false;
  103. #endif
  104. #ifdef HAVE_LLVM_SUPPORT
  105. const bool has_llvm = true;
  106. #else
  107. const bool has_llvm = false;
  108. #endif
  109. #ifdef BPFTOOL_WITHOUT_SKELETONS
  110. const bool has_skeletons = false;
  111. #else
  112. const bool has_skeletons = true;
  113. #endif
  114. bool bootstrap = false;
  115. int i;
  116. for (i = 0; commands[i].cmd; i++) {
  117. if (!strcmp(commands[i].cmd, "prog")) {
  118. /* Assume we run a bootstrap version if "bpftool prog"
  119. * is not available.
  120. */
  121. bootstrap = !commands[i].func;
  122. break;
  123. }
  124. }
  125. if (json_output) {
  126. jsonw_start_object(json_wtr); /* root object */
  127. jsonw_name(json_wtr, "version");
  128. #ifdef BPFTOOL_VERSION
  129. jsonw_printf(json_wtr, "\"%s\"", BPFTOOL_VERSION);
  130. #else
  131. jsonw_printf(json_wtr, "\"%d.%d.%d\"", BPFTOOL_MAJOR_VERSION,
  132. BPFTOOL_MINOR_VERSION, BPFTOOL_PATCH_VERSION);
  133. #endif
  134. jsonw_name(json_wtr, "libbpf_version");
  135. jsonw_printf(json_wtr, "\"%d.%d\"",
  136. libbpf_major_version(), libbpf_minor_version());
  137. jsonw_name(json_wtr, "features");
  138. jsonw_start_object(json_wtr); /* features */
  139. jsonw_bool_field(json_wtr, "libbfd", has_libbfd);
  140. jsonw_bool_field(json_wtr, "llvm", has_llvm);
  141. jsonw_bool_field(json_wtr, "skeletons", has_skeletons);
  142. jsonw_bool_field(json_wtr, "bootstrap", bootstrap);
  143. jsonw_end_object(json_wtr); /* features */
  144. jsonw_end_object(json_wtr); /* root object */
  145. } else {
  146. unsigned int nb_features = 0;
  147. #ifdef BPFTOOL_VERSION
  148. printf("%s v%s\n", bin_name, BPFTOOL_VERSION);
  149. #else
  150. printf("%s v%d.%d.%d\n", bin_name, BPFTOOL_MAJOR_VERSION,
  151. BPFTOOL_MINOR_VERSION, BPFTOOL_PATCH_VERSION);
  152. #endif
  153. printf("using libbpf %s\n", libbpf_version_string());
  154. printf("features:");
  155. print_feature("libbfd", has_libbfd, &nb_features);
  156. print_feature("llvm", has_llvm, &nb_features);
  157. print_feature("skeletons", has_skeletons, &nb_features);
  158. print_feature("bootstrap", bootstrap, &nb_features);
  159. printf("\n");
  160. }
  161. return 0;
  162. }
  163. int cmd_select(const struct cmd *cmds, int argc, char **argv,
  164. int (*help)(int argc, char **argv))
  165. {
  166. unsigned int i;
  167. last_argc = argc;
  168. last_argv = argv;
  169. last_do_help = help;
  170. if (argc < 1 && cmds[0].func)
  171. return cmds[0].func(argc, argv);
  172. for (i = 0; cmds[i].cmd; i++) {
  173. if (is_prefix(*argv, cmds[i].cmd)) {
  174. if (!cmds[i].func) {
  175. p_err("command '%s' is not supported in bootstrap mode",
  176. cmds[i].cmd);
  177. return -1;
  178. }
  179. return cmds[i].func(argc - 1, argv + 1);
  180. }
  181. }
  182. help(argc - 1, argv + 1);
  183. return -1;
  184. }
  185. bool is_prefix(const char *pfx, const char *str)
  186. {
  187. if (!pfx)
  188. return false;
  189. if (strlen(str) < strlen(pfx))
  190. return false;
  191. return !memcmp(str, pfx, strlen(pfx));
  192. }
  193. /* Last argument MUST be NULL pointer */
  194. int detect_common_prefix(const char *arg, ...)
  195. {
  196. unsigned int count = 0;
  197. const char *ref;
  198. char msg[256];
  199. va_list ap;
  200. snprintf(msg, sizeof(msg), "ambiguous prefix: '%s' could be '", arg);
  201. va_start(ap, arg);
  202. while ((ref = va_arg(ap, const char *))) {
  203. if (!is_prefix(arg, ref))
  204. continue;
  205. count++;
  206. if (count > 1)
  207. strncat(msg, "' or '", sizeof(msg) - strlen(msg) - 1);
  208. strncat(msg, ref, sizeof(msg) - strlen(msg) - 1);
  209. }
  210. va_end(ap);
  211. strncat(msg, "'", sizeof(msg) - strlen(msg) - 1);
  212. if (count >= 2) {
  213. p_err("%s", msg);
  214. return -1;
  215. }
  216. return 0;
  217. }
  218. void fprint_hex(FILE *f, void *arg, unsigned int n, const char *sep)
  219. {
  220. unsigned char *data = arg;
  221. unsigned int i;
  222. for (i = 0; i < n; i++) {
  223. const char *pfx = "";
  224. if (!i)
  225. /* nothing */;
  226. else if (!(i % 16))
  227. fprintf(f, "\n");
  228. else if (!(i % 8))
  229. fprintf(f, " ");
  230. else
  231. pfx = sep;
  232. fprintf(f, "%s%02hhx", i ? pfx : "", data[i]);
  233. }
  234. }
  235. /* Split command line into argument vector. */
  236. static int make_args(char *line, char *n_argv[], int maxargs, int cmd_nb)
  237. {
  238. static const char ws[] = " \t\r\n";
  239. char *cp = line;
  240. int n_argc = 0;
  241. while (*cp) {
  242. /* Skip leading whitespace. */
  243. cp += strspn(cp, ws);
  244. if (*cp == '\0')
  245. break;
  246. if (n_argc >= (maxargs - 1)) {
  247. p_err("too many arguments to command %d", cmd_nb);
  248. return -1;
  249. }
  250. /* Word begins with quote. */
  251. if (*cp == '\'' || *cp == '"') {
  252. char quote = *cp++;
  253. n_argv[n_argc++] = cp;
  254. /* Find ending quote. */
  255. cp = strchr(cp, quote);
  256. if (!cp) {
  257. p_err("unterminated quoted string in command %d",
  258. cmd_nb);
  259. return -1;
  260. }
  261. } else {
  262. n_argv[n_argc++] = cp;
  263. /* Find end of word. */
  264. cp += strcspn(cp, ws);
  265. if (*cp == '\0')
  266. break;
  267. }
  268. /* Separate words. */
  269. *cp++ = 0;
  270. }
  271. n_argv[n_argc] = NULL;
  272. return n_argc;
  273. }
  274. static int do_batch(int argc, char **argv)
  275. {
  276. char buf[BATCH_LINE_LEN_MAX], contline[BATCH_LINE_LEN_MAX];
  277. char *n_argv[BATCH_ARG_NB_MAX];
  278. unsigned int lines = 0;
  279. int n_argc;
  280. FILE *fp;
  281. char *cp;
  282. int err = 0;
  283. int i;
  284. if (argc < 2) {
  285. p_err("too few parameters for batch");
  286. return -1;
  287. } else if (argc > 2) {
  288. p_err("too many parameters for batch");
  289. return -1;
  290. } else if (!is_prefix(*argv, "file")) {
  291. p_err("expected 'file', got: %s", *argv);
  292. return -1;
  293. }
  294. NEXT_ARG();
  295. if (!strcmp(*argv, "-"))
  296. fp = stdin;
  297. else
  298. fp = fopen(*argv, "r");
  299. if (!fp) {
  300. p_err("Can't open file (%s): %s", *argv, strerror(errno));
  301. return -1;
  302. }
  303. if (json_output)
  304. jsonw_start_array(json_wtr);
  305. while (fgets(buf, sizeof(buf), fp)) {
  306. cp = strchr(buf, '#');
  307. if (cp)
  308. *cp = '\0';
  309. if (strlen(buf) == sizeof(buf) - 1) {
  310. errno = E2BIG;
  311. break;
  312. }
  313. /* Append continuation lines if any (coming after a line ending
  314. * with '\' in the batch file).
  315. */
  316. while ((cp = strstr(buf, "\\\n")) != NULL) {
  317. if (!fgets(contline, sizeof(contline), fp) ||
  318. strlen(contline) == 0) {
  319. p_err("missing continuation line on command %d",
  320. lines);
  321. err = -1;
  322. goto err_close;
  323. }
  324. cp = strchr(contline, '#');
  325. if (cp)
  326. *cp = '\0';
  327. if (strlen(buf) + strlen(contline) + 1 > sizeof(buf)) {
  328. p_err("command %d is too long", lines);
  329. err = -1;
  330. goto err_close;
  331. }
  332. buf[strlen(buf) - 2] = '\0';
  333. strcat(buf, contline);
  334. }
  335. n_argc = make_args(buf, n_argv, BATCH_ARG_NB_MAX, lines);
  336. if (!n_argc)
  337. continue;
  338. if (n_argc < 0) {
  339. err = n_argc;
  340. goto err_close;
  341. }
  342. if (json_output) {
  343. jsonw_start_object(json_wtr);
  344. jsonw_name(json_wtr, "command");
  345. jsonw_start_array(json_wtr);
  346. for (i = 0; i < n_argc; i++)
  347. jsonw_string(json_wtr, n_argv[i]);
  348. jsonw_end_array(json_wtr);
  349. jsonw_name(json_wtr, "output");
  350. }
  351. err = cmd_select(commands, n_argc, n_argv, do_help);
  352. if (json_output)
  353. jsonw_end_object(json_wtr);
  354. if (err)
  355. goto err_close;
  356. lines++;
  357. }
  358. if (errno && errno != ENOENT) {
  359. p_err("reading batch file failed: %s", strerror(errno));
  360. err = -1;
  361. } else {
  362. if (!json_output)
  363. printf("processed %d commands\n", lines);
  364. }
  365. err_close:
  366. if (fp != stdin)
  367. fclose(fp);
  368. if (json_output)
  369. jsonw_end_array(json_wtr);
  370. return err;
  371. }
  372. int main(int argc, char **argv)
  373. {
  374. static const struct option options[] = {
  375. { "json", no_argument, NULL, 'j' },
  376. { "help", no_argument, NULL, 'h' },
  377. { "pretty", no_argument, NULL, 'p' },
  378. { "version", no_argument, NULL, 'V' },
  379. { "bpffs", no_argument, NULL, 'f' },
  380. { "mapcompat", no_argument, NULL, 'm' },
  381. { "nomount", no_argument, NULL, 'n' },
  382. { "debug", no_argument, NULL, 'd' },
  383. { "use-loader", no_argument, NULL, 'L' },
  384. { "base-btf", required_argument, NULL, 'B' },
  385. { 0 }
  386. };
  387. bool version_requested = false;
  388. int opt, ret;
  389. setlinebuf(stdout);
  390. #ifdef USE_LIBCAP
  391. /* Libcap < 2.63 hooks before main() to compute the number of
  392. * capabilities of the running kernel, and doing so it calls prctl()
  393. * which may fail and set errno to non-zero.
  394. * Let's reset errno to make sure this does not interfere with the
  395. * batch mode.
  396. */
  397. errno = 0;
  398. #endif
  399. last_do_help = do_help;
  400. pretty_output = false;
  401. json_output = false;
  402. show_pinned = false;
  403. block_mount = false;
  404. bin_name = "bpftool";
  405. opterr = 0;
  406. while ((opt = getopt_long(argc, argv, "VhpjfLmndB:l",
  407. options, NULL)) >= 0) {
  408. switch (opt) {
  409. case 'V':
  410. version_requested = true;
  411. break;
  412. case 'h':
  413. return do_help(argc, argv);
  414. case 'p':
  415. pretty_output = true;
  416. /* fall through */
  417. case 'j':
  418. if (!json_output) {
  419. json_wtr = jsonw_new(stdout);
  420. if (!json_wtr) {
  421. p_err("failed to create JSON writer");
  422. return -1;
  423. }
  424. json_output = true;
  425. }
  426. jsonw_pretty(json_wtr, pretty_output);
  427. break;
  428. case 'f':
  429. show_pinned = true;
  430. break;
  431. case 'm':
  432. relaxed_maps = true;
  433. break;
  434. case 'n':
  435. block_mount = true;
  436. break;
  437. case 'd':
  438. libbpf_set_print(print_all_levels);
  439. verifier_logs = true;
  440. break;
  441. case 'B':
  442. base_btf = btf__parse(optarg, NULL);
  443. if (!base_btf) {
  444. p_err("failed to parse base BTF at '%s': %d\n",
  445. optarg, -errno);
  446. return -1;
  447. }
  448. break;
  449. case 'L':
  450. use_loader = true;
  451. break;
  452. default:
  453. p_err("unrecognized option '%s'", argv[optind - 1]);
  454. if (json_output)
  455. clean_and_exit(-1);
  456. else
  457. usage();
  458. }
  459. }
  460. argc -= optind;
  461. argv += optind;
  462. if (argc < 0)
  463. usage();
  464. if (version_requested)
  465. ret = do_version(argc, argv);
  466. else
  467. ret = cmd_select(commands, argc, argv, do_help);
  468. if (json_output)
  469. jsonw_destroy(&json_wtr);
  470. btf__free(base_btf);
  471. return ret;
  472. }