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