1262743Sglebius/*-
2262743Sglebius * SPDX-License-Identifier: BSD-2-Clause
3262743Sglebius *
4262743Sglebius * Copyright (C) 2019 Netflix, Inc
5262743Sglebius *
6262743Sglebius * Redistribution and use in source and binary forms, with or without
7262743Sglebius * modification, are permitted provided that the following conditions
8262743Sglebius * are met:
9262743Sglebius * 1. Redistributions of source code must retain the above copyright
10262743Sglebius *    notice, this list of conditions and the following disclaimer.
11262743Sglebius * 2. Redistributions in binary form must reproduce the above copyright
12262743Sglebius *    notice, this list of conditions and the following disclaimer in the
13262743Sglebius *    documentation and/or other materials provided with the distribution.
14262743Sglebius *
15262743Sglebius * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16262743Sglebius * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17262743Sglebius * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18262743Sglebius * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19262743Sglebius * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20262743Sglebius * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21262743Sglebius * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22262743Sglebius * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23262743Sglebius * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24262743Sglebius * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25262743Sglebius * SUCH DAMAGE.
26262743Sglebius */
27262743Sglebius
28191255Skmacy#include <sys/param.h>
29191255Skmacy#include <sys/ioccom.h>
30191255Skmacy
31196368Skmacy#include <ctype.h>
32205066Skmacy#include <dirent.h>
33205066Skmacy#include <dlfcn.h>
34191255Skmacy#include <err.h>
35191255Skmacy#include <fcntl.h>
36191255Skmacy#include <getopt.h>
37191255Skmacy#include <libutil.h>
38262743Sglebius#include <stdbool.h>
39191255Skmacy#include <stddef.h>
40191255Skmacy#include <stdio.h>
41196368Skmacy#include <stdlib.h>
42191255Skmacy#include <string.h>
43240086Sglebius#include <sysexits.h>
44262743Sglebius#include <unistd.h>
45191255Skmacy
46191255Skmacy#include "comnd.h"
47191255Skmacy
48191255Skmacystatic struct cmd top;
49262743Sglebius
50191255Skmacystatic void
51262743Sglebiusprint_tree(const struct cmd *f)
52205066Skmacy{
53191255Skmacy
54191255Skmacy	if (f->parent != NULL)
55191255Skmacy		print_tree(f->parent);
56191255Skmacy	if (f->name != NULL)
57191255Skmacy		fprintf(stderr, " %s", f->name);
58262743Sglebius}
59191255Skmacy
60191255Skmacystatic void
61191255Skmacyprint_usage(const struct cmd *f)
62191255Skmacy{
63262743Sglebius
64191255Skmacy	fprintf(stderr, "    %s", getprogname());
65195837Srwatson	print_tree(f->parent);
66191255Skmacy	fprintf(stderr, " %-15s - %s\n", f->name, f->descr);
67191255Skmacy}
68191255Skmacy
69191255Skmacystatic void
70191255Skmacygen_usage(const struct cmd *t)
71191255Skmacy{
72205066Skmacy	struct cmd *walker;
73205066Skmacy
74205066Skmacy	fprintf(stderr, "usage:\n");
75262743Sglebius	SLIST_FOREACH(walker, &t->subcmd, link) {
76191255Skmacy		print_usage(walker);
77191255Skmacy	}
78191255Skmacy	exit(EX_USAGE);
79262743Sglebius}
80191255Skmacy
81196368Skmacyint
82191255Skmacycmd_dispatch(int argc, char *argv[], const struct cmd *t)
83262743Sglebius{
84262743Sglebius	struct cmd *walker;
85262743Sglebius
86262743Sglebius	if (t == NULL)
87262743Sglebius		t = &top;
88262743Sglebius
89262743Sglebius	if (argv[1] == NULL) {
90191255Skmacy		gen_usage(t);
91262743Sglebius		return (1);
92262743Sglebius	}
93262743Sglebius	SLIST_FOREACH(walker, &t->subcmd, link) {
94262743Sglebius		if (strcmp(argv[1], walker->name) == 0) {
95262743Sglebius			walker->fn(walker, argc-1, &argv[1]);
96191255Skmacy			return (0);
97262743Sglebius		}
98191255Skmacy	}
99191255Skmacy	fprintf(stderr, "Unknown command: %s\n", argv[1]);
100262743Sglebius	gen_usage(t);
101262743Sglebius	return (1);
102262743Sglebius}
103262743Sglebius
104262743Sglebiusstatic void
105191255Skmacyarg_suffix(char *buf, size_t len, arg_type at)
106262743Sglebius{
107262743Sglebius	switch (at) {
108262743Sglebius	case arg_none:
109262743Sglebius		break;
110262743Sglebius	case arg_string:
111262743Sglebius		strlcat(buf, "=<STRING>", len);
112191255Skmacy		break;
113262743Sglebius	case arg_path:
114191255Skmacy		strlcat(buf, "=<FILE>", len);
115262743Sglebius		break;
116262743Sglebius	default:
117262743Sglebius		strlcat(buf, "=<NUM>", len);
118191255Skmacy		break;
119191255Skmacy	}
120262743Sglebius}
121191255Skmacy
122205488Skmacyvoid
123262743Sglebiusarg_help(int argc __unused, char * const *argv, const struct cmd *f)
124262743Sglebius{
125262743Sglebius	int i;
126262743Sglebius	char buf[31];
127262743Sglebius	const struct opts *opts = f->opts;
128262743Sglebius	const struct args *args = f->args;
129262743Sglebius
130191324Skmacy	// XXX walk up the cmd list...
131262743Sglebius	if (argv[optind])
132191255Skmacy		fprintf(stderr, "Unknown argument: %s\n", argv[optind]);
133262743Sglebius	fprintf(stderr, "Usage:\n    %s", getprogname());
134262743Sglebius	print_tree(f);
135262743Sglebius	if (opts)
136205488Skmacy		fprintf(stderr, " <args>");
137191255Skmacy	if (args) {
138262743Sglebius		while (args->descr != NULL) {
139191255Skmacy			fprintf(stderr, " %s", args->descr);
140216855Sbz			args++;
141216855Sbz		}
142196368Skmacy	}
143196368Skmacy	fprintf(stderr, "\n\n%s\n", f->descr);
144196368Skmacy	if (opts != NULL) {
145191255Skmacy		fprintf(stderr, "Options:\n");
146191255Skmacy		for (i = 0; opts[i].long_arg != NULL; i++) {
147262743Sglebius			*buf = '\0';
148191255Skmacy			if (isprint(opts[i].short_arg)) {
149191255Skmacy				snprintf(buf, sizeof(buf), " -%c, ", opts[i].short_arg);
150191255Skmacy			} else {
151191255Skmacy				strlcpy(buf, "    ", sizeof(buf));
152191255Skmacy			}
153191255Skmacy			strlcat(buf, "--", sizeof(buf));
154191255Skmacy			strlcat(buf, opts[i].long_arg, sizeof(buf));
155191255Skmacy			arg_suffix(buf, sizeof(buf), opts[i].at);
156194660Szec			fprintf(stderr, "%-30.30s - %s\n", buf, opts[i].descr);
157191255Skmacy		}
158262743Sglebius	}
159262743Sglebius	exit(EX_USAGE);
160262743Sglebius}
161262743Sglebius
162262743Sglebiusstatic int
163262743Sglebiusfind_long(struct option *lopts, int ch)
164262743Sglebius{
165262743Sglebius	int i;
166195699Srwatson
167262743Sglebius	for (i = 0; lopts[i].val != ch && lopts[i].name != NULL; i++)
168262743Sglebius		continue;
169262743Sglebius	return (i);
170195727Srwatson}
171195699Srwatson
172262743Sglebiusint
173227309Sedarg_parse(int argc, char * const * argv, const struct cmd *f)
174262743Sglebius{
175195699Srwatson	int i, n, idx, ch;
176262743Sglebius	uint64_t v;
177262743Sglebius	struct option *lopts;
178191255Skmacy	char *shortopts, *p;
179262743Sglebius	const struct opts *opts = f->opts;
180191255Skmacy	const struct args *args = f->args;
181262743Sglebius
182262743Sglebius	if (opts == NULL)
183191255Skmacy		n = 0;
184205066Skmacy	else
185262743Sglebius		for (n = 0; opts[n].long_arg != NULL;)
186262743Sglebius			n++;
187205066Skmacy	lopts = malloc((n + 2) * sizeof(struct option));
188262743Sglebius	if (lopts == NULL)
189262743Sglebius		err(EX_OSERR, "option memory");
190205066Skmacy	p = shortopts = malloc((2 * n + 3) * sizeof(char));
191262743Sglebius	if (shortopts == NULL)
192262743Sglebius		err(EX_OSERR, "shortopts memory");
193262743Sglebius	idx = 0;
194191255Skmacy	for (i = 0; i < n; i++) {
195205066Skmacy		lopts[i].name = opts[i].long_arg;
196262743Sglebius		lopts[i].has_arg = opts[i].at == arg_none ? no_argument : required_argument;
197262743Sglebius		lopts[i].flag = NULL;
198191255Skmacy		lopts[i].val = opts[i].short_arg;
199205066Skmacy		if (isprint(opts[i].short_arg)) {
200191255Skmacy			*p++ = opts[i].short_arg;
201262743Sglebius			if (lopts[i].has_arg)
202262743Sglebius				*p++ = ':';
203262743Sglebius		}
204262743Sglebius	}
205262743Sglebius	lopts[n].name = "help";
206262743Sglebius	lopts[n].has_arg = no_argument;
207262743Sglebius	lopts[n].flag = NULL;
208262743Sglebius	lopts[n].val = '?';
209262743Sglebius	*p++ = '?';
210205066Skmacy	*p++ = '\0';
211191255Skmacy	memset(lopts + n + 1, 0, sizeof(struct option));
212262743Sglebius	while ((ch = getopt_long(argc, argv, shortopts, lopts, &idx)) != -1) {
213262743Sglebius		/*
214262743Sglebius		 * If ch != 0, we've found a short option, and we have to
215205066Skmacy		 * look it up lopts table. Otherwise idx is valid.
216262743Sglebius		 */
217205066Skmacy		if (ch != 0)
218205066Skmacy			idx = find_long(lopts, ch);
219262743Sglebius		if (idx == n)
220262743Sglebius			arg_help(argc, argv, f);
221262743Sglebius		switch (opts[idx].at) {
222262743Sglebius		case arg_none:
223262743Sglebius			*(bool *)opts[idx].ptr = true;
224262743Sglebius			break;
225262743Sglebius		case arg_string:
226262743Sglebius		case arg_path:
227191255Skmacy			*(const char **)opts[idx].ptr = optarg;
228191255Skmacy			break;
229262743Sglebius		case arg_uint8:
230262743Sglebius			v = strtoul(optarg, NULL, 0);
231262743Sglebius			if (v > 0xff)
232262743Sglebius				goto bad_arg;
233262743Sglebius			*(uint8_t *)opts[idx].ptr = v;
234262743Sglebius			break;
235191255Skmacy		case arg_uint16:
236191255Skmacy			v = strtoul(optarg, NULL, 0);
237262743Sglebius			if (v > 0xffff)
238262743Sglebius				goto bad_arg;
239262743Sglebius			*(uint16_t *)opts[idx].ptr = v;
240191255Skmacy			break;
241262743Sglebius		case arg_uint32:
242201758Smbr			v = strtoul(optarg, NULL, 0);
243191255Skmacy			if (v > 0xffffffffu)
244191255Skmacy				goto bad_arg;
245262743Sglebius			*(uint32_t *)opts[idx].ptr = v;
246262743Sglebius			break;
247262743Sglebius		case arg_uint64:
248262743Sglebius			v = strtoul(optarg, NULL, 0);
249191255Skmacy			if (v > 0xffffffffffffffffull)
250262743Sglebius				goto bad_arg;
251262743Sglebius			*(uint64_t *)opts[idx].ptr = v;
252191255Skmacy			break;
253262743Sglebius		case arg_size:
254191255Skmacy			if (expand_number(optarg, &v) < 0)
255262743Sglebius				goto bad_arg;
256262743Sglebius			*(uint64_t *)opts[idx].ptr = v;
257205066Skmacy			break;
258262743Sglebius		}
259205066Skmacy	}
260262743Sglebius	if (args) {
261205066Skmacy		while (args->descr) {
262205066Skmacy			if (optind >= argc) {
263205066Skmacy				fprintf(stderr, "Missing arg %s\n", args->descr);
264205066Skmacy				arg_help(argc, argv, f);
265205066Skmacy				free(lopts);
266262743Sglebius				free(shortopts);
267262743Sglebius				return (1);
268262743Sglebius			}
269205066Skmacy			*(char **)args->ptr = argv[optind++];
270205066Skmacy			args++;
271205066Skmacy		}
272205066Skmacy	}
273205066Skmacy	free(lopts);
274205066Skmacy	free(shortopts);
275205066Skmacy	return (0);
276205066Skmacybad_arg:
277205066Skmacy	fprintf(stderr, "Bad value to --%s: %s\n", opts[idx].long_arg, optarg);
278205066Skmacy	free(lopts);
279205066Skmacy	free(shortopts);
280205066Skmacy	exit(EX_USAGE);
281205066Skmacy}
282262743Sglebius
283262743Sglebius/*
284205066Skmacy * Loads all the .so's from the specified directory.
285205066Skmacy */
286205066Skmacyvoid
287205066Skmacycmd_load_dir(const char *dir, cmd_load_cb_t cb, void *argp)
288205066Skmacy{
289205066Skmacy	DIR *d;
290205066Skmacy	struct dirent *dent;
291262743Sglebius	char *path = NULL;
292262743Sglebius	void *h;
293205066Skmacy
294262743Sglebius	d = opendir(dir);
295262743Sglebius	if (d == NULL)
296205066Skmacy		return;
297262743Sglebius	for (dent = readdir(d); dent != NULL; dent = readdir(d)) {
298262743Sglebius		if (strcmp(".so", dent->d_name + dent->d_namlen - 3) != 0)
299262743Sglebius			continue;
300262743Sglebius		asprintf(&path, "%s/%s", dir, dent->d_name);
301205066Skmacy		if (path == NULL)
302262743Sglebius			err(EX_OSERR, "Can't malloc for path, giving up.");
303205066Skmacy		if ((h = dlopen(path, RTLD_NOW | RTLD_GLOBAL)) == NULL)
304262743Sglebius			warnx("Can't load %s: %s", path, dlerror());
305262743Sglebius		else {
306262743Sglebius			if (cb != NULL)
307262743Sglebius				cb(argp, h);
308205066Skmacy		}
309205066Skmacy		free(path);
310262743Sglebius		path = NULL;
311262743Sglebius	}
312262743Sglebius	closedir(d);
313262743Sglebius}
314262743Sglebius
315262743Sglebiusvoid
316205066Skmacycmd_register(struct cmd *up, struct cmd *cmd)
317205066Skmacy{
318262743Sglebius	struct cmd *walker, *last;
319262743Sglebius
320205066Skmacy	if (up == NULL)
321205066Skmacy		up = &top;
322205066Skmacy	SLIST_INIT(&cmd->subcmd);
323205066Skmacy	cmd->parent = up;
324205066Skmacy	last = NULL;
325205066Skmacy	SLIST_FOREACH(walker, &up->subcmd, link) {
326205066Skmacy		if (strcmp(walker->name, cmd->name) > 0)
327205066Skmacy			break;
328205066Skmacy		last = walker;
329205066Skmacy	}
330205066Skmacy	if (last == NULL) {
331205066Skmacy		SLIST_INSERT_HEAD(&up->subcmd, cmd, link);
332262743Sglebius	} else {
333262743Sglebius		SLIST_INSERT_AFTER(last, cmd, link);
334262743Sglebius	}
335262743Sglebius}
336205066Skmacy
337205066Skmacyvoid
338205066Skmacycmd_init(void)
339262743Sglebius{
340262743Sglebius
341262743Sglebius}
342205066Skmacy