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