xref: /linux/tools/net/ynl/ynltool/main.c (revision 24f171c7e145f43b9f187578e89b0982ce87e54c)
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