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