1b02d2290SJakub Kicinski // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) 2b02d2290SJakub Kicinski /* Copyright (C) 2017-2018 Netronome Systems, Inc. */ 3b02d2290SJakub Kicinski /* Copyright Meta Platforms, Inc. and affiliates */ 4b02d2290SJakub Kicinski 5b02d2290SJakub Kicinski #include <ctype.h> 6b02d2290SJakub Kicinski #include <errno.h> 7b02d2290SJakub Kicinski #include <getopt.h> 8b02d2290SJakub Kicinski #include <stdio.h> 9b02d2290SJakub Kicinski #include <stdlib.h> 10b02d2290SJakub Kicinski #include <string.h> 11b02d2290SJakub Kicinski #include <stdarg.h> 12b02d2290SJakub Kicinski 13b02d2290SJakub Kicinski #include "main.h" 14b02d2290SJakub Kicinski 15b02d2290SJakub Kicinski const char *bin_name; 16b02d2290SJakub Kicinski static int last_argc; 17b02d2290SJakub Kicinski static char **last_argv; 18b02d2290SJakub Kicinski static int (*last_do_help)(int argc, char **argv); 19b02d2290SJakub Kicinski json_writer_t *json_wtr; 20b02d2290SJakub Kicinski bool pretty_output; 21b02d2290SJakub Kicinski bool json_output; 22b02d2290SJakub Kicinski 23b02d2290SJakub Kicinski static void __attribute__((noreturn)) clean_and_exit(int i) 24b02d2290SJakub Kicinski { 25b02d2290SJakub Kicinski if (json_output) 26b02d2290SJakub Kicinski jsonw_destroy(&json_wtr); 27b02d2290SJakub Kicinski 28b02d2290SJakub Kicinski exit(i); 29b02d2290SJakub Kicinski } 30b02d2290SJakub Kicinski 31b02d2290SJakub Kicinski void usage(void) 32b02d2290SJakub Kicinski { 33b02d2290SJakub Kicinski last_do_help(last_argc - 1, last_argv + 1); 34b02d2290SJakub Kicinski 35b02d2290SJakub Kicinski clean_and_exit(-1); 36b02d2290SJakub Kicinski } 37b02d2290SJakub Kicinski 38b02d2290SJakub Kicinski static int do_help(int argc __attribute__((unused)), 39b02d2290SJakub Kicinski char **argv __attribute__((unused))) 40b02d2290SJakub Kicinski { 41b02d2290SJakub Kicinski if (json_output) { 42b02d2290SJakub Kicinski jsonw_null(json_wtr); 43b02d2290SJakub Kicinski return 0; 44b02d2290SJakub Kicinski } 45b02d2290SJakub Kicinski 46b02d2290SJakub Kicinski fprintf(stderr, 47b02d2290SJakub Kicinski "Usage: %s [OPTIONS] OBJECT { COMMAND | help }\n" 48b02d2290SJakub Kicinski " %s version\n" 49b02d2290SJakub Kicinski "\n" 50*3f0a638dSJakub Kicinski " OBJECT := { page-pool | qstats }\n" 51b02d2290SJakub Kicinski " " HELP_SPEC_OPTIONS "\n" 52b02d2290SJakub Kicinski "", 53b02d2290SJakub Kicinski bin_name, bin_name); 54b02d2290SJakub Kicinski 55b02d2290SJakub Kicinski return 0; 56b02d2290SJakub Kicinski } 57b02d2290SJakub Kicinski 58b02d2290SJakub Kicinski static int do_version(int argc __attribute__((unused)), 59b02d2290SJakub Kicinski char **argv __attribute__((unused))) 60b02d2290SJakub Kicinski { 61b02d2290SJakub Kicinski if (json_output) { 62b02d2290SJakub Kicinski jsonw_start_object(json_wtr); 63b02d2290SJakub Kicinski jsonw_name(json_wtr, "version"); 64b02d2290SJakub Kicinski jsonw_printf(json_wtr, SRC_VERSION); 65b02d2290SJakub Kicinski jsonw_end_object(json_wtr); 66b02d2290SJakub Kicinski } else { 67b02d2290SJakub Kicinski printf("%s " SRC_VERSION "\n", bin_name); 68b02d2290SJakub Kicinski } 69b02d2290SJakub Kicinski return 0; 70b02d2290SJakub Kicinski } 71b02d2290SJakub Kicinski 72b02d2290SJakub Kicinski static const struct cmd commands[] = { 73b02d2290SJakub Kicinski { "help", do_help }, 74124dac9bSJakub Kicinski { "page-pool", do_page_pool }, 75*3f0a638dSJakub Kicinski { "qstats", do_qstats }, 76b02d2290SJakub Kicinski { "version", do_version }, 77b02d2290SJakub Kicinski { 0 } 78b02d2290SJakub Kicinski }; 79b02d2290SJakub Kicinski 80b02d2290SJakub Kicinski int cmd_select(const struct cmd *cmds, int argc, char **argv, 81b02d2290SJakub Kicinski int (*help)(int argc, char **argv)) 82b02d2290SJakub Kicinski { 83b02d2290SJakub Kicinski unsigned int i; 84b02d2290SJakub Kicinski 85b02d2290SJakub Kicinski last_argc = argc; 86b02d2290SJakub Kicinski last_argv = argv; 87b02d2290SJakub Kicinski last_do_help = help; 88b02d2290SJakub Kicinski 89b02d2290SJakub Kicinski if (argc < 1 && cmds[0].func) 90b02d2290SJakub Kicinski return cmds[0].func(argc, argv); 91b02d2290SJakub Kicinski 92b02d2290SJakub Kicinski for (i = 0; cmds[i].cmd; i++) { 93b02d2290SJakub Kicinski if (is_prefix(*argv, cmds[i].cmd)) { 94b02d2290SJakub Kicinski if (!cmds[i].func) { 95b02d2290SJakub Kicinski p_err("command '%s' is not available", cmds[i].cmd); 96b02d2290SJakub Kicinski return -1; 97b02d2290SJakub Kicinski } 98b02d2290SJakub Kicinski return cmds[i].func(argc - 1, argv + 1); 99b02d2290SJakub Kicinski } 100b02d2290SJakub Kicinski } 101b02d2290SJakub Kicinski 102b02d2290SJakub Kicinski help(argc - 1, argv + 1); 103b02d2290SJakub Kicinski 104b02d2290SJakub Kicinski return -1; 105b02d2290SJakub Kicinski } 106b02d2290SJakub Kicinski 107b02d2290SJakub Kicinski bool is_prefix(const char *pfx, const char *str) 108b02d2290SJakub Kicinski { 109b02d2290SJakub Kicinski if (!pfx) 110b02d2290SJakub Kicinski return false; 111b02d2290SJakub Kicinski if (strlen(str) < strlen(pfx)) 112b02d2290SJakub Kicinski return false; 113b02d2290SJakub Kicinski 114b02d2290SJakub Kicinski return !memcmp(str, pfx, strlen(pfx)); 115b02d2290SJakub Kicinski } 116b02d2290SJakub Kicinski 117b02d2290SJakub Kicinski /* Last argument MUST be NULL pointer */ 118b02d2290SJakub Kicinski int detect_common_prefix(const char *arg, ...) 119b02d2290SJakub Kicinski { 120b02d2290SJakub Kicinski unsigned int count = 0; 121b02d2290SJakub Kicinski const char *ref; 122b02d2290SJakub Kicinski char msg[256]; 123b02d2290SJakub Kicinski va_list ap; 124b02d2290SJakub Kicinski 125b02d2290SJakub Kicinski snprintf(msg, sizeof(msg), "ambiguous prefix: '%s' could be '", arg); 126b02d2290SJakub Kicinski va_start(ap, arg); 127b02d2290SJakub Kicinski while ((ref = va_arg(ap, const char *))) { 128b02d2290SJakub Kicinski if (!is_prefix(arg, ref)) 129b02d2290SJakub Kicinski continue; 130b02d2290SJakub Kicinski count++; 131b02d2290SJakub Kicinski if (count > 1) 132b02d2290SJakub Kicinski strncat(msg, "' or '", sizeof(msg) - strlen(msg) - 1); 133b02d2290SJakub Kicinski strncat(msg, ref, sizeof(msg) - strlen(msg) - 1); 134b02d2290SJakub Kicinski } 135b02d2290SJakub Kicinski va_end(ap); 136b02d2290SJakub Kicinski strncat(msg, "'", sizeof(msg) - strlen(msg) - 1); 137b02d2290SJakub Kicinski 138b02d2290SJakub Kicinski if (count >= 2) { 139b02d2290SJakub Kicinski p_err("%s", msg); 140b02d2290SJakub Kicinski return -1; 141b02d2290SJakub Kicinski } 142b02d2290SJakub Kicinski 143b02d2290SJakub Kicinski return 0; 144b02d2290SJakub Kicinski } 145b02d2290SJakub Kicinski 146b02d2290SJakub Kicinski void p_err(const char *fmt, ...) 147b02d2290SJakub Kicinski { 148b02d2290SJakub Kicinski va_list ap; 149b02d2290SJakub Kicinski 150b02d2290SJakub Kicinski va_start(ap, fmt); 151b02d2290SJakub Kicinski if (json_output) { 152b02d2290SJakub Kicinski jsonw_start_object(json_wtr); 153b02d2290SJakub Kicinski jsonw_name(json_wtr, "error"); 154b02d2290SJakub Kicinski jsonw_vprintf_enquote(json_wtr, fmt, ap); 155b02d2290SJakub Kicinski jsonw_end_object(json_wtr); 156b02d2290SJakub Kicinski } else { 157b02d2290SJakub Kicinski fprintf(stderr, "Error: "); 158b02d2290SJakub Kicinski vfprintf(stderr, fmt, ap); 159b02d2290SJakub Kicinski fprintf(stderr, "\n"); 160b02d2290SJakub Kicinski } 161b02d2290SJakub Kicinski va_end(ap); 162b02d2290SJakub Kicinski } 163b02d2290SJakub Kicinski 164b02d2290SJakub Kicinski void p_info(const char *fmt, ...) 165b02d2290SJakub Kicinski { 166b02d2290SJakub Kicinski va_list ap; 167b02d2290SJakub Kicinski 168b02d2290SJakub Kicinski if (json_output) 169b02d2290SJakub Kicinski return; 170b02d2290SJakub Kicinski 171b02d2290SJakub Kicinski va_start(ap, fmt); 172b02d2290SJakub Kicinski vfprintf(stderr, fmt, ap); 173b02d2290SJakub Kicinski fprintf(stderr, "\n"); 174b02d2290SJakub Kicinski va_end(ap); 175b02d2290SJakub Kicinski } 176b02d2290SJakub Kicinski 177b02d2290SJakub Kicinski int main(int argc, char **argv) 178b02d2290SJakub Kicinski { 179b02d2290SJakub Kicinski static const struct option options[] = { 180b02d2290SJakub Kicinski { "json", no_argument, NULL, 'j' }, 181b02d2290SJakub Kicinski { "help", no_argument, NULL, 'h' }, 182b02d2290SJakub Kicinski { "pretty", no_argument, NULL, 'p' }, 183b02d2290SJakub Kicinski { "version", no_argument, NULL, 'V' }, 184b02d2290SJakub Kicinski { 0 } 185b02d2290SJakub Kicinski }; 186b02d2290SJakub Kicinski bool version_requested = false; 187b02d2290SJakub Kicinski int opt, ret; 188b02d2290SJakub Kicinski 189b02d2290SJakub Kicinski setlinebuf(stdout); 190b02d2290SJakub Kicinski 191b02d2290SJakub Kicinski last_do_help = do_help; 192b02d2290SJakub Kicinski pretty_output = false; 193b02d2290SJakub Kicinski json_output = false; 194b02d2290SJakub Kicinski bin_name = "ynltool"; 195b02d2290SJakub Kicinski 196b02d2290SJakub Kicinski opterr = 0; 197b02d2290SJakub Kicinski while ((opt = getopt_long(argc, argv, "Vhjp", 198b02d2290SJakub Kicinski options, NULL)) >= 0) { 199b02d2290SJakub Kicinski switch (opt) { 200b02d2290SJakub Kicinski case 'V': 201b02d2290SJakub Kicinski version_requested = true; 202b02d2290SJakub Kicinski break; 203b02d2290SJakub Kicinski case 'h': 204b02d2290SJakub Kicinski return do_help(argc, argv); 205b02d2290SJakub Kicinski case 'p': 206b02d2290SJakub Kicinski pretty_output = true; 207b02d2290SJakub Kicinski /* fall through */ 208b02d2290SJakub Kicinski case 'j': 209b02d2290SJakub Kicinski if (!json_output) { 210b02d2290SJakub Kicinski json_wtr = jsonw_new(stdout); 211b02d2290SJakub Kicinski if (!json_wtr) { 212b02d2290SJakub Kicinski p_err("failed to create JSON writer"); 213b02d2290SJakub Kicinski return -1; 214b02d2290SJakub Kicinski } 215b02d2290SJakub Kicinski json_output = true; 216b02d2290SJakub Kicinski } 217b02d2290SJakub Kicinski jsonw_pretty(json_wtr, pretty_output); 218b02d2290SJakub Kicinski break; 219b02d2290SJakub Kicinski default: 220b02d2290SJakub Kicinski p_err("unrecognized option '%s'", argv[optind - 1]); 221b02d2290SJakub Kicinski if (json_output) 222b02d2290SJakub Kicinski clean_and_exit(-1); 223b02d2290SJakub Kicinski else 224b02d2290SJakub Kicinski usage(); 225b02d2290SJakub Kicinski } 226b02d2290SJakub Kicinski } 227b02d2290SJakub Kicinski 228b02d2290SJakub Kicinski argc -= optind; 229b02d2290SJakub Kicinski argv += optind; 230b02d2290SJakub Kicinski if (argc < 0) 231b02d2290SJakub Kicinski usage(); 232b02d2290SJakub Kicinski 233b02d2290SJakub Kicinski if (version_requested) 234b02d2290SJakub Kicinski ret = do_version(argc, argv); 235b02d2290SJakub Kicinski else 236b02d2290SJakub Kicinski ret = cmd_select(commands, argc, argv, do_help); 237b02d2290SJakub Kicinski 238b02d2290SJakub Kicinski if (json_output) 239b02d2290SJakub Kicinski jsonw_destroy(&json_wtr); 240b02d2290SJakub Kicinski 241b02d2290SJakub Kicinski return ret; 242b02d2290SJakub Kicinski } 243