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 = ⊤ 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 = ⊤ 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