1: /*
2: * Copyright (C) 2009 Martin Willi
3: * HSR Hochschule fuer Technik Rapperswil
4: *
5: * This program is free software; you can redistribute it and/or modify it
6: * under the terms of the GNU General Public License as published by the
7: * Free Software Foundation; either version 2 of the License, or (at your
8: * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9: *
10: * This program is distributed in the hope that it will be useful, but
11: * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12: * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13: * for more details.
14: */
15:
16: #include "command.h"
17:
18: #define _GNU_SOURCE
19: #include <getopt.h>
20: #include <stdlib.h>
21: #include <string.h>
22: #include <stdio.h>
23: #include <errno.h>
24:
25: #include <library.h>
26: #include <utils/debug.h>
27: #include <utils/optionsfrom.h>
28:
29: /**
30: * Registered commands.
31: */
32: static command_t cmds[MAX_COMMANDS];
33:
34: /**
35: * active command.
36: */
37: static int active = 0;
38:
39: /**
40: * number of registered commands
41: */
42: static int registered = 0;
43:
44: /**
45: * help command index
46: */
47: static int help_idx;
48:
49: /**
50: * Uri to connect to
51: */
52: static char *uri = NULL;
53:
54: static int argc;
55:
56: static char **argv;
57:
58: static options_t *options;
59:
60: /**
61: * Global options used by all subcommands
62: */
63: static struct option command_opts[MAX_COMMANDS > MAX_OPTIONS ?
64: MAX_COMMANDS : MAX_OPTIONS];
65:
66: /**
67: * Global optstring used by all subcommands
68: */
69: static char command_optstring[(MAX_COMMANDS > MAX_OPTIONS ?
70: MAX_COMMANDS : MAX_OPTIONS) * 3];
71:
72: /**
73: * Build command_opts/command_optstr for the active command
74: */
75: static void build_opts()
76: {
77: int i, pos = 0;
78:
79: memset(command_opts, 0, sizeof(command_opts));
80: memset(command_optstring, 0, sizeof(command_optstring));
81: if (active == help_idx)
82: {
83: for (i = 0; i < MAX_COMMANDS && cmds[i].cmd; i++)
84: {
85: command_opts[i].name = cmds[i].cmd;
86: command_opts[i].val = cmds[i].op;
87: command_optstring[i] = cmds[i].op;
88: }
89: }
90: else
91: {
92: for (i = 0; cmds[active].options[i].name; i++)
93: {
94: command_opts[i].name = cmds[active].options[i].name;
95: command_opts[i].has_arg = cmds[active].options[i].arg;
96: command_opts[i].val = cmds[active].options[i].op;
97: command_optstring[pos++] = cmds[active].options[i].op;
98: switch (cmds[active].options[i].arg)
99: {
100: case optional_argument:
101: command_optstring[pos++] = ':';
102: /* FALL */
103: case required_argument:
104: command_optstring[pos++] = ':';
105: /* FALL */
106: case no_argument:
107: default:
108: break;
109: }
110: }
111: }
112: }
113:
114: /**
115: * getopt_long wrapper
116: */
117: int command_getopt(char **arg)
118: {
119: int op;
120:
121: while (TRUE)
122: {
123: op = getopt_long(argc, argv, command_optstring, command_opts, NULL);
124: switch (op)
125: {
126: case '+':
127: case 'v':
128: case 'u':
129: continue;
130: default:
131: *arg = optarg;
132: return op;
133: }
134: }
135: }
136:
137: /**
138: * Register a command
139: */
140: void command_register(command_t command)
141: {
142: int i;
143:
144: if (registered == MAX_COMMANDS)
145: {
146: fprintf(stderr, "unable to register command, please increase "
147: "MAX_COMMANDS\n");
148: return;
149: }
150: for (i = 0; i < MAX_COMMANDS && cmds[i].cmd; i++)
151: {
152: if (cmds[i].op == command.op)
153: {
154: fprintf(stderr, "unable to register command --%s, short option "
155: "conflicts with --%s\n", command.cmd, cmds[i].cmd);
156: return;
157: }
158: }
159:
160: cmds[registered] = command;
161: /* append default options, but not to --help */
162: if (!active)
163: {
164: for (i = 0; i < countof(cmds[registered].options) - 1; i++)
165: {
166: if (!cmds[registered].options[i].name)
167: {
168: break;
169: }
170: }
171: if (i > countof(cmds[registered].options) - 3)
172: {
173: fprintf(stderr, "command '%s' registered too many options, please "
174: "increase MAX_OPTIONS\n", command.cmd);
175: }
176: else
177: {
178: cmds[registered].options[i++] = (command_option_t) {
179: "debug", 'v', 1, "set debug level, default: 1"
180: };
181: cmds[registered].options[i++] = (command_option_t) {
182: "options", '+', 1, "read command line options from file"
183: };
184: cmds[registered].options[i++] = (command_option_t) {
185: "uri", 'u', 1, "service URI to connect to"
186: };
187: }
188: for (i = 0; cmds[registered].line[i]; i++)
189: {
190: if (i == MAX_LINES - 1)
191: {
192: fprintf(stderr, "command '%s' specifies too many usage summary "
193: "lines, please increase MAX_LINES\n", command.cmd);
194: break;
195: }
196: }
197: }
198: registered++;
199: }
200:
201: /**
202: * Print usage text, with an optional error
203: */
204: int command_usage(char *error, ...)
205: {
206: va_list args;
207: FILE *out = stdout;
208: int i;
209:
210: if (error)
211: {
212: out = stderr;
213: fprintf(out, "Error: ");
214: va_start(args, error);
215: vfprintf(out, error, args);
216: va_end(args);
217: fprintf(out, "\n");
218: }
219: fprintf(out, "strongSwan %s swanctl\n", VERSION);
220:
221: if (active == help_idx)
222: {
223: fprintf(out, "loaded plugins: %s\n",
224: lib->plugins->loaded_plugins(lib->plugins));
225: }
226:
227: fprintf(out, "usage:\n");
228: if (active == help_idx)
229: {
230: for (i = 0; i < MAX_COMMANDS && cmds[i].cmd; i++)
231: {
232: fprintf(out, " swanctl --%-16s (-%c) %s\n",
233: cmds[i].cmd, cmds[i].op, cmds[i].description);
234: }
235: }
236: else
237: {
238: for (i = 0; i < MAX_LINES && cmds[active].line[i]; i++)
239: {
240: if (i == 0)
241: {
242: fprintf(out, " swanctl --%s %s\n",
243: cmds[active].cmd, cmds[active].line[i]);
244: }
245: else
246: {
247: fprintf(out, " %s\n", cmds[active].line[i]);
248: }
249: }
250: for (i = 0; cmds[active].options[i].name; i++)
251: {
252: fprintf(out, " --%-15s (-%c) %s\n",
253: cmds[active].options[i].name, cmds[active].options[i].op,
254: cmds[active].options[i].desc);
255: }
256: }
257: return error != NULL;
258: }
259:
260: /**
261: * Dispatch cleanup hook
262: */
263: static void cleanup()
264: {
265: options->destroy(options);
266: }
267:
268: /**
269: * Process options common for all commands
270: */
271: static bool process_common_opts()
272: {
273: while (TRUE)
274: {
275: switch (getopt_long(argc, argv, command_optstring, command_opts, NULL))
276: {
277: case '+':
278: if (!options->from(options, optarg, &argc, &argv, optind))
279: {
280: return FALSE;
281: }
282: continue;
283: case 'v':
284: dbg_default_set_level(atoi(optarg));
285: continue;
286: case 'u':
287: uri = optarg;
288: continue;
289: default:
290: continue;
291: case '?':
292: return FALSE;
293: case EOF:
294: return TRUE;
295: }
296: }
297: }
298:
299: /**
300: * Open vici connection, call a command
301: */
302: static int call_command(command_t *cmd)
303: {
304: vici_conn_t *conn;
305: int ret;
306:
307: conn = vici_connect(uri);
308: if (!conn)
309: {
310: ret = errno;
311: command_usage("connecting to '%s' URI failed: %s",
312: uri ?: "default", strerror(errno));
313: return ret;
314: }
315: ret = cmd->call(conn);
316: vici_disconnect(conn);
317: return ret;
318: }
319:
320: /**
321: * Dispatch commands.
322: */
323: int command_dispatch(int c, char *v[])
324: {
325: int op, i;
326:
327: uri = lib->settings->get_str(lib->settings, "%s.socket",
328: lib->settings->get_str(lib->settings, "%s.plugins.vici.socket",
329: NULL, lib->ns), lib->ns);
330:
331: options = options_create();
332: atexit(cleanup);
333: active = help_idx = registered;
334: argc = c;
335: argv = v;
336: command_register((command_t){NULL, 'h', "help", "show usage information"});
337:
338: build_opts();
339: op = getopt_long(c, v, command_optstring, command_opts, NULL);
340: for (i = 0; i < MAX_COMMANDS && cmds[i].cmd; i++)
341: {
342: if (cmds[i].op == op)
343: {
344: active = i;
345: build_opts();
346: if (help_idx == i)
347: {
348: return command_usage(NULL);
349: }
350: if (!process_common_opts())
351: {
352: return command_usage("invalid options");
353: }
354: optind = 2;
355: return call_command(&cmds[i]);
356: }
357: }
358: return command_usage(c > 1 ? "invalid operation" : NULL);
359: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>