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 }\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 { "version", do_version }, 192 { 0 } 193 }; 194 195 static int do_batch(int argc, char **argv) 196 { 197 char buf[BATCH_LINE_LEN_MAX], contline[BATCH_LINE_LEN_MAX]; 198 char *n_argv[BATCH_ARG_NB_MAX]; 199 unsigned int lines = 0; 200 int n_argc; 201 FILE *fp; 202 char *cp; 203 int err; 204 int i; 205 206 if (argc < 2) { 207 p_err("too few parameters for batch"); 208 return -1; 209 } else if (!is_prefix(*argv, "file")) { 210 p_err("expected 'file', got: %s", *argv); 211 return -1; 212 } else if (argc > 2) { 213 p_err("too many parameters for batch"); 214 return -1; 215 } 216 NEXT_ARG(); 217 218 if (!strcmp(*argv, "-")) 219 fp = stdin; 220 else 221 fp = fopen(*argv, "r"); 222 if (!fp) { 223 p_err("Can't open file (%s): %s", *argv, strerror(errno)); 224 return -1; 225 } 226 227 if (json_output) 228 jsonw_start_array(json_wtr); 229 while (fgets(buf, sizeof(buf), fp)) { 230 cp = strchr(buf, '#'); 231 if (cp) 232 *cp = '\0'; 233 234 if (strlen(buf) == sizeof(buf) - 1) { 235 errno = E2BIG; 236 break; 237 } 238 239 /* Append continuation lines if any (coming after a line ending 240 * with '\' in the batch file). 241 */ 242 while ((cp = strstr(buf, "\\\n")) != NULL) { 243 if (!fgets(contline, sizeof(contline), fp) || 244 strlen(contline) == 0) { 245 p_err("missing continuation line on command %d", 246 lines); 247 err = -1; 248 goto err_close; 249 } 250 251 cp = strchr(contline, '#'); 252 if (cp) 253 *cp = '\0'; 254 255 if (strlen(buf) + strlen(contline) + 1 > sizeof(buf)) { 256 p_err("command %d is too long", lines); 257 err = -1; 258 goto err_close; 259 } 260 buf[strlen(buf) - 2] = '\0'; 261 strcat(buf, contline); 262 } 263 264 n_argc = make_args(buf, n_argv, BATCH_ARG_NB_MAX, lines); 265 if (!n_argc) 266 continue; 267 if (n_argc < 0) 268 goto err_close; 269 270 if (json_output) { 271 jsonw_start_object(json_wtr); 272 jsonw_name(json_wtr, "command"); 273 jsonw_start_array(json_wtr); 274 for (i = 0; i < n_argc; i++) 275 jsonw_string(json_wtr, n_argv[i]); 276 jsonw_end_array(json_wtr); 277 jsonw_name(json_wtr, "output"); 278 } 279 280 err = cmd_select(cmds, n_argc, n_argv, do_help); 281 282 if (json_output) 283 jsonw_end_object(json_wtr); 284 285 if (err) 286 goto err_close; 287 288 lines++; 289 } 290 291 if (errno && errno != ENOENT) { 292 p_err("reading batch file failed: %s", strerror(errno)); 293 err = -1; 294 } else { 295 if (!json_output) 296 printf("processed %d commands\n", lines); 297 err = 0; 298 } 299 err_close: 300 if (fp != stdin) 301 fclose(fp); 302 303 if (json_output) 304 jsonw_end_array(json_wtr); 305 306 return err; 307 } 308 309 int main(int argc, char **argv) 310 { 311 static const struct option options[] = { 312 { "json", no_argument, NULL, 'j' }, 313 { "help", no_argument, NULL, 'h' }, 314 { "pretty", no_argument, NULL, 'p' }, 315 { "version", no_argument, NULL, 'V' }, 316 { "bpffs", no_argument, NULL, 'f' }, 317 { "mapcompat", no_argument, NULL, 'm' }, 318 { "nomount", no_argument, NULL, 'n' }, 319 { 0 } 320 }; 321 int opt, ret; 322 323 last_do_help = do_help; 324 pretty_output = false; 325 json_output = false; 326 show_pinned = false; 327 block_mount = false; 328 bin_name = argv[0]; 329 330 hash_init(prog_table.table); 331 hash_init(map_table.table); 332 333 opterr = 0; 334 while ((opt = getopt_long(argc, argv, "Vhpjfmn", 335 options, NULL)) >= 0) { 336 switch (opt) { 337 case 'V': 338 return do_version(argc, argv); 339 case 'h': 340 return do_help(argc, argv); 341 case 'p': 342 pretty_output = true; 343 /* fall through */ 344 case 'j': 345 if (!json_output) { 346 json_wtr = jsonw_new(stdout); 347 if (!json_wtr) { 348 p_err("failed to create JSON writer"); 349 return -1; 350 } 351 json_output = true; 352 } 353 jsonw_pretty(json_wtr, pretty_output); 354 break; 355 case 'f': 356 show_pinned = true; 357 break; 358 case 'm': 359 bpf_flags = MAPS_RELAX_COMPAT; 360 break; 361 case 'n': 362 block_mount = true; 363 break; 364 default: 365 p_err("unrecognized option '%s'", argv[optind - 1]); 366 if (json_output) 367 clean_and_exit(-1); 368 else 369 usage(); 370 } 371 } 372 373 argc -= optind; 374 argv += optind; 375 if (argc < 0) 376 usage(); 377 378 ret = cmd_select(cmds, argc, argv, do_help); 379 380 if (json_output) 381 jsonw_destroy(&json_wtr); 382 383 if (show_pinned) { 384 delete_pinned_obj_table(&prog_table); 385 delete_pinned_obj_table(&map_table); 386 } 387 388 return ret; 389 } 390