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