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