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