// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * command structure borrowed from udev
 * (git://git.kernel.org/pub/scm/linux/hotplug/udev.git)
 *
 * Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
 *               2005-2007 Takahiro Hirofuchi
 */

#include <stdio.h>
#include <stdlib.h>

#include <getopt.h>
#include <syslog.h>

#include "usbip_common.h"
#include "usbip_network.h"
#include "usbip.h"

static int usbip_help(int argc, char *argv[]);
static int usbip_version(int argc, char *argv[]);

static const char usbip_version_string[] = PACKAGE_STRING;

static const char usbip_usage_string[] =
	"usbip [--debug] [--log] [--tcp-port PORT] [version]\n"
	"             [help] <command> <args>\n";

static void usbip_usage(void)
{
	printf("usage: %s", usbip_usage_string);
}

struct command {
	const char *name;
	int (*fn)(int argc, char *argv[]);
	const char *help;
	void (*usage)(void);
};

static const struct command cmds[] = {
	{
		.name  = "help",
		.fn    = usbip_help,
		.help  = NULL,
		.usage = NULL
	},
	{
		.name  = "version",
		.fn    = usbip_version,
		.help  = NULL,
		.usage = NULL
	},
	{
		.name  = "attach",
		.fn    = usbip_attach,
		.help  = "Attach a remote USB device",
		.usage = usbip_attach_usage
	},
	{
		.name  = "detach",
		.fn    = usbip_detach,
		.help  = "Detach a remote USB device",
		.usage = usbip_detach_usage
	},
	{
		.name  = "list",
		.fn    = usbip_list,
		.help  = "List exportable or local USB devices",
		.usage = usbip_list_usage
	},
	{
		.name  = "bind",
		.fn    = usbip_bind,
		.help  = "Bind device to " USBIP_HOST_DRV_NAME ".ko",
		.usage = usbip_bind_usage
	},
	{
		.name  = "unbind",
		.fn    = usbip_unbind,
		.help  = "Unbind device from " USBIP_HOST_DRV_NAME ".ko",
		.usage = usbip_unbind_usage
	},
	{
		.name  = "port",
		.fn    = usbip_port_show,
		.help  = "Show imported USB devices",
		.usage = NULL
	},
	{ NULL, NULL, NULL, NULL }
};

static int usbip_help(int argc, char *argv[])
{
	const struct command *cmd;
	int i;
	int ret = 0;

	if (argc > 1 && argv++) {
		for (i = 0; cmds[i].name != NULL; i++)
			if (!strcmp(cmds[i].name, argv[0]) && cmds[i].usage) {
				cmds[i].usage();
				goto done;
			}
		ret = -1;
	}

	usbip_usage();
	printf("\n");
	for (cmd = cmds; cmd->name != NULL; cmd++)
		if (cmd->help != NULL)
			printf("  %-10s %s\n", cmd->name, cmd->help);
	printf("\n");
done:
	return ret;
}

static int usbip_version(int argc, char *argv[])
{
	(void) argc;
	(void) argv;

	printf(PROGNAME " (%s)\n", usbip_version_string);
	return 0;
}

static int run_command(const struct command *cmd, int argc, char *argv[])
{
	dbg("running command: `%s'", cmd->name);
	return cmd->fn(argc, argv);
}

int main(int argc, char *argv[])
{
	static const struct option opts[] = {
		{ "debug",    no_argument,       NULL, 'd' },
		{ "log",      no_argument,       NULL, 'l' },
		{ "tcp-port", required_argument, NULL, 't' },
		{ NULL,       0,                 NULL,  0  }
	};

	char *cmd;
	int opt;
	int i, rc = -1;

	usbip_use_stderr = 1;
	opterr = 0;
	for (;;) {
		opt = getopt_long(argc, argv, "+dlt:", opts, NULL);

		if (opt == -1)
			break;

		switch (opt) {
		case 'd':
			usbip_use_debug = 1;
			break;
		case 'l':
			usbip_use_syslog = 1;
			openlog("", LOG_PID, LOG_USER);
			break;
		case 't':
			usbip_setup_port_number(optarg);
			break;
		case '?':
			printf("usbip: invalid option\n");
			/* Terminate after printing error */
			/* FALLTHRU */
		default:
			usbip_usage();
			goto out;
		}
	}

	cmd = argv[optind];
	if (cmd) {
		for (i = 0; cmds[i].name != NULL; i++)
			if (!strcmp(cmds[i].name, cmd)) {
				argc -= optind;
				argv += optind;
				optind = 0;
				rc = run_command(&cmds[i], argc, argv);
				goto out;
			}
	}

	/* invalid command */
	usbip_help(0, NULL);
out:
	return (rc > -1 ? EXIT_SUCCESS : EXIT_FAILURE);
}
