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