main.c 12 KB

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