swanctl: Add a --reload-settings command
[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 if (!options->from(options, optarg, &argc, &argv, optind))
128 {
129 /* a error value */
130 return 255;
131 }
132 continue;
133 case 'v':
134 dbg_default_set_level(atoi(optarg));
135 continue;
136 case 'u':
137 uri = optarg;
138 continue;
139 default:
140 *arg = optarg;
141 return op;
142 }
143 }
144 }
145
146 /**
147 * Register a command
148 */
149 void command_register(command_t command)
150 {
151 int i;
152
153 if (registered == MAX_COMMANDS)
154 {
155 fprintf(stderr, "unable to register command, please increase "
156 "MAX_COMMANDS\n");
157 return;
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 }
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 --%-15s (-%c) %s\n",
224 cmds[i].cmd, cmds[i].op, cmds[i].description);
225 }
226 }
227 else
228 {
229 for (i = 0; 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 * Open vici connection, call a command
261 */
262 static int call_command(command_t *cmd)
263 {
264 vici_conn_t *conn;
265 int ret;
266
267 conn = vici_connect(uri);
268 if (!conn)
269 {
270 command_usage("connecting to '%s' URI failed: %s",
271 uri ?: "default", strerror(errno));
272 return errno;
273 }
274 ret = cmd->call(conn);
275 vici_disconnect(conn);
276 return ret;
277 }
278
279 /**
280 * Dispatch commands.
281 */
282 int command_dispatch(int c, char *v[])
283 {
284 int op, i;
285
286 options = options_create();
287 atexit(cleanup);
288 active = help_idx = registered;
289 argc = c;
290 argv = v;
291 command_register((command_t){NULL, 'h', "help", "show usage information"});
292
293 build_opts();
294 op = getopt_long(c, v, command_optstring, command_opts, NULL);
295 for (i = 0; i < MAX_COMMANDS && cmds[i].cmd; i++)
296 {
297 if (cmds[i].op == op)
298 {
299 active = i;
300 build_opts();
301 if (help_idx == i)
302 {
303 return command_usage(NULL);
304 }
305 return call_command(&cmds[i]);
306 }
307 }
308 return command_usage(c > 1 ? "invalid operation" : NULL);
309 }