26c41346c013b346ac5b932317f504a5bfecec22
[strongswan.git] / src / swanctl / command.c
1 /*
2 * Copyright (C) 2009 Martin Willi
3 * 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
151 cmds[registered] = command;
152 /* append default options, but not to --help */
153 if (!active)
154 {
155 for (i = 0; i < countof(cmds[registered].options) - 1; i++)
156 {
157 if (!cmds[registered].options[i].name)
158 {
159 break;
160 }
161 }
162 if (i > countof(cmds[registered].options) - 3)
163 {
164 fprintf(stderr, "command '%s' registered too many options, please "
165 "increase MAX_OPTIONS\n", command.cmd);
166 }
167 else
168 {
169 cmds[registered].options[i++] = (command_option_t) {
170 "debug", 'v', 1, "set debug level, default: 1"
171 };
172 cmds[registered].options[i++] = (command_option_t) {
173 "options", '+', 1, "read command line options from file"
174 };
175 cmds[registered].options[i++] = (command_option_t) {
176 "uri", 'u', 1, "service URI to connect to"
177 };
178 }
179 }
180 registered++;
181 }
182
183 /**
184 * Print usage text, with an optional error
185 */
186 int command_usage(char *error, ...)
187 {
188 va_list args;
189 FILE *out = stdout;
190 int i;
191
192 if (error)
193 {
194 out = stderr;
195 fprintf(out, "Error: ");
196 va_start(args, error);
197 vfprintf(out, error, args);
198 va_end(args);
199 fprintf(out, "\n");
200 }
201 fprintf(out, "strongSwan %s swanctl\n", VERSION);
202
203 if (active == help_idx)
204 {
205 fprintf(out, "loaded plugins: %s\n",
206 lib->plugins->loaded_plugins(lib->plugins));
207 }
208
209 fprintf(out, "usage:\n");
210 if (active == help_idx)
211 {
212 for (i = 0; i < MAX_COMMANDS && cmds[i].cmd; i++)
213 {
214 fprintf(out, " swanctl --%-16s (-%c) %s\n",
215 cmds[i].cmd, cmds[i].op, cmds[i].description);
216 }
217 }
218 else
219 {
220 for (i = 0; cmds[active].line[i]; i++)
221 {
222 if (i == 0)
223 {
224 fprintf(out, " swanctl --%s %s\n",
225 cmds[active].cmd, cmds[active].line[i]);
226 }
227 else
228 {
229 fprintf(out, " %s\n", cmds[active].line[i]);
230 }
231 }
232 for (i = 0; cmds[active].options[i].name; i++)
233 {
234 fprintf(out, " --%-15s (-%c) %s\n",
235 cmds[active].options[i].name, cmds[active].options[i].op,
236 cmds[active].options[i].desc);
237 }
238 }
239 return error != NULL;
240 }
241
242 /**
243 * Dispatch cleanup hook
244 */
245 static void cleanup()
246 {
247 options->destroy(options);
248 }
249
250 /**
251 * Process options common for all commands
252 */
253 static bool process_common_opts()
254 {
255 while (TRUE)
256 {
257 switch (getopt_long(argc, argv, command_optstring, command_opts, NULL))
258 {
259 case '+':
260 if (!options->from(options, optarg, &argc, &argv, optind))
261 {
262 return FALSE;
263 }
264 continue;
265 case 'v':
266 dbg_default_set_level(atoi(optarg));
267 continue;
268 case 'u':
269 uri = optarg;
270 continue;
271 default:
272 continue;
273 case '?':
274 return FALSE;
275 case EOF:
276 return TRUE;
277 }
278 }
279 }
280
281 /**
282 * Open vici connection, call a command
283 */
284 static int call_command(command_t *cmd)
285 {
286 vici_conn_t *conn;
287 int ret;
288
289 conn = vici_connect(uri);
290 if (!conn)
291 {
292 ret = errno;
293 command_usage("connecting to '%s' URI failed: %s",
294 uri ?: "default", strerror(errno));
295 return ret;
296 }
297 ret = cmd->call(conn);
298 vici_disconnect(conn);
299 return ret;
300 }
301
302 /**
303 * Dispatch commands.
304 */
305 int command_dispatch(int c, char *v[])
306 {
307 int op, i;
308
309 options = options_create();
310 atexit(cleanup);
311 active = help_idx = registered;
312 argc = c;
313 argv = v;
314 command_register((command_t){NULL, 'h', "help", "show usage information"});
315
316 build_opts();
317 op = getopt_long(c, v, command_optstring, command_opts, NULL);
318 for (i = 0; i < MAX_COMMANDS && cmds[i].cmd; i++)
319 {
320 if (cmds[i].op == op)
321 {
322 active = i;
323 build_opts();
324 if (help_idx == i)
325 {
326 return command_usage(NULL);
327 }
328 if (!process_common_opts())
329 {
330 return command_usage("invalid options");
331 }
332 optind = 2;
333 return call_command(&cmds[i]);
334 }
335 }
336 return command_usage(c > 1 ? "invalid operation" : NULL);
337 }