/* * Copyright (C) 2009 Martin Willi * Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. See . * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. */ #include "command.h" #define _GNU_SOURCE #include #include #include #include #include #include #include #include /** * Registered commands. */ static command_t cmds[MAX_COMMANDS]; /** * active command. */ static int active = 0; /** * number of registered commands */ static int registered = 0; /** * help command index */ static int help_idx; /** * Uri to connect to */ static char *uri = NULL; static int argc; static char **argv; static options_t *options; /** * Global options used by all subcommands */ static struct option command_opts[MAX_COMMANDS > MAX_OPTIONS ? MAX_COMMANDS : MAX_OPTIONS]; /** * Global optstring used by all subcommands */ static char command_optstring[(MAX_COMMANDS > MAX_OPTIONS ? MAX_COMMANDS : MAX_OPTIONS) * 3]; /** * Build command_opts/command_optstr for the active command */ static void build_opts() { int i, pos = 0; memset(command_opts, 0, sizeof(command_opts)); memset(command_optstring, 0, sizeof(command_optstring)); if (active == help_idx) { for (i = 0; i < MAX_COMMANDS && cmds[i].cmd; i++) { command_opts[i].name = cmds[i].cmd; command_opts[i].val = cmds[i].op; command_optstring[i] = cmds[i].op; } } else { for (i = 0; cmds[active].options[i].name; i++) { command_opts[i].name = cmds[active].options[i].name; command_opts[i].has_arg = cmds[active].options[i].arg; command_opts[i].val = cmds[active].options[i].op; command_optstring[pos++] = cmds[active].options[i].op; switch (cmds[active].options[i].arg) { case optional_argument: command_optstring[pos++] = ':'; /* FALL */ case required_argument: command_optstring[pos++] = ':'; /* FALL */ case no_argument: default: break; } } } } /** * getopt_long wrapper */ int command_getopt(char **arg) { int op; while (TRUE) { op = getopt_long(argc, argv, command_optstring, command_opts, NULL); switch (op) { case '+': case 'v': case 'u': continue; default: *arg = optarg; return op; } } } /** * Register a command */ void command_register(command_t command) { int i; if (registered == MAX_COMMANDS) { fprintf(stderr, "unable to register command, please increase " "MAX_COMMANDS\n"); return; } for (i = 0; i < MAX_COMMANDS && cmds[i].cmd; i++) { if (cmds[i].op == command.op) { fprintf(stderr, "unable to register command --%s, short option " "conflicts with --%s\n", command.cmd, cmds[i].cmd); return; } } cmds[registered] = command; /* append default options, but not to --help */ if (!active) { for (i = 0; i < countof(cmds[registered].options) - 1; i++) { if (!cmds[registered].options[i].name) { break; } } if (i > countof(cmds[registered].options) - 3) { fprintf(stderr, "command '%s' registered too many options, please " "increase MAX_OPTIONS\n", command.cmd); } else { cmds[registered].options[i++] = (command_option_t) { "debug", 'v', 1, "set debug level, default: 1" }; cmds[registered].options[i++] = (command_option_t) { "options", '+', 1, "read command line options from file" }; cmds[registered].options[i++] = (command_option_t) { "uri", 'u', 1, "service URI to connect to" }; } for (i = 0; cmds[registered].line[i]; i++) { if (i == MAX_LINES - 1) { fprintf(stderr, "command '%s' specifies too many usage summary " "lines, please increase MAX_LINES\n", command.cmd); break; } } } registered++; } /** * Print usage text, with an optional error */ int command_usage(char *error, ...) { va_list args; FILE *out = stdout; int i; if (error) { out = stderr; fprintf(out, "Error: "); va_start(args, error); vfprintf(out, error, args); va_end(args); fprintf(out, "\n"); } fprintf(out, "strongSwan %s swanctl\n", VERSION); if (active == help_idx) { fprintf(out, "loaded plugins: %s\n", lib->plugins->loaded_plugins(lib->plugins)); } fprintf(out, "usage:\n"); if (active == help_idx) { for (i = 0; i < MAX_COMMANDS && cmds[i].cmd; i++) { fprintf(out, " swanctl --%-16s (-%c) %s\n", cmds[i].cmd, cmds[i].op, cmds[i].description); } } else { for (i = 0; i < MAX_LINES && cmds[active].line[i]; i++) { if (i == 0) { fprintf(out, " swanctl --%s %s\n", cmds[active].cmd, cmds[active].line[i]); } else { fprintf(out, " %s\n", cmds[active].line[i]); } } for (i = 0; cmds[active].options[i].name; i++) { fprintf(out, " --%-15s (-%c) %s\n", cmds[active].options[i].name, cmds[active].options[i].op, cmds[active].options[i].desc); } } return error != NULL; } /** * Dispatch cleanup hook */ static void cleanup() { options->destroy(options); } /** * Process options common for all commands */ static bool process_common_opts() { while (TRUE) { switch (getopt_long(argc, argv, command_optstring, command_opts, NULL)) { case '+': if (!options->from(options, optarg, &argc, &argv, optind)) { return FALSE; } continue; case 'v': dbg_default_set_level(atoi(optarg)); continue; case 'u': uri = optarg; continue; default: continue; case '?': return FALSE; case EOF: return TRUE; } } } /** * Open vici connection, call a command */ static int call_command(command_t *cmd) { vici_conn_t *conn; int ret; conn = vici_connect(uri); if (!conn) { ret = errno; command_usage("connecting to '%s' URI failed: %s", uri ?: "default", strerror(errno)); return ret; } ret = cmd->call(conn); vici_disconnect(conn); return ret; } /** * Dispatch commands. */ int command_dispatch(int c, char *v[]) { int op, i; uri = lib->settings->get_str(lib->settings, "%s.socket", lib->settings->get_str(lib->settings, "%s.plugins.vici.socket", NULL, lib->ns), lib->ns); options = options_create(); atexit(cleanup); active = help_idx = registered; argc = c; argv = v; command_register((command_t){NULL, 'h', "help", "show usage information"}); build_opts(); op = getopt_long(c, v, command_optstring, command_opts, NULL); for (i = 0; i < MAX_COMMANDS && cmds[i].cmd; i++) { if (cmds[i].op == op) { active = i; build_opts(); if (help_idx == i) { return command_usage(NULL); } if (!process_common_opts()) { return command_usage("invalid options"); } optind = 2; return call_command(&cmds[i]); } } return command_usage(c > 1 ? "invalid operation" : NULL); }