Clean and tiddy-up files.
[tomato.git] / release / src / router / xl2tpd / xl2tpd-control.c
blob038ab715766cfbd78b83db9b589ccdb3765a973b
1 /*
2 * Layer Two Tunnelling Protocol Daemon Control Utility
3 * Copyright (C) 2011 Alexander Dorokhov
5 * This software is distributed under the terms
6 * of the GPL, which you should have received
7 * along with this source.
9 * xl2tpd-control client main file
13 #include <stdio.h>
14 #include <string.h>
15 #include <stdarg.h>
16 #include <unistd.h>
17 #include <sys/stat.h>
18 #include <sys/types.h>
19 #include <fcntl.h>
20 #include <errno.h>
22 #include "l2tp.h"
24 /* Paul: Alex: can we change this to use stdout, and let applications using
25 * xl2tpd-control capture the output, instead of creating tmp files?
27 /* result filename format including absolute path and formatting %i for pid */
28 #define RESULT_FILENAME_FORMAT "/var/run/xl2tpd/xl2tpd-control-%i.out"
30 #define ERROR_LEVEL 1
31 #define DEBUG_LEVEL 2
33 int log_level = ERROR_LEVEL;
35 void print_error (int level, const char *fmt, ...);
37 int read_result(int result_fd, char* buf, ssize_t size);
39 /* Definition of a command */
40 struct command_t
42 char *name;
43 int (*handler) (FILE*, char* tunnel, int optc, char *optv[]);
46 int command_add (FILE*, char* tunnel, int optc, char *optv[]);
47 int command_connect (FILE*, char* tunnel, int optc, char *optv[]);
48 int command_disconnect (FILE*, char* tunnel, int optc, char *optv[]);
49 int command_remove (FILE*, char* tunnel, int optc, char *optv[]);
51 struct command_t commands[] = {
52 {"add", &command_add},
53 {"connect", &command_connect},
54 {"disconnect", &command_disconnect},
55 {"remove", &command_remove},
56 {NULL, NULL}
59 void usage()
61 printf ("\nxl2tpd server version %s\n", SERVER_VERSION);
62 printf ("Usage: xl2tpd-control [-c <PATH>] <command> <tunnel name> [<COMMAND OPTIONS>]\n"
63 "\n"
64 " -c\tspecifies xl2tpd control file\n"
65 " -d\tspecify xl2tpd-control to run in debug mode\n"
66 "--help\tshows extended help\n"
67 "Available commands: add, connect, disconnect, remove\n"
71 void help()
73 usage();
74 printf (
75 "\n"
76 "Commands help:\n"
77 "\tadd\tadds new or modify existing lac configuration.\n"
78 "\t\tConfiguration must be specified as command options in\n"
79 "\t\t<key>=<value> pairs format.\n"
80 "\t\tSee available options in xl2tpd.conf(5)\n"
81 "\tconnect\ttries to activate the tunnel.\n"
82 "\t\tUsername and secret for the tunnel can be passed as\n"
83 "\t\tcommand options.\n"
84 "\tdisconnect\tdisconnects the tunnel.\n"
85 "\tremove\tremoves lac configuration from xl2tpd.\n"
86 "\t\txl2tpd disconnects the tunnel before removing.\n"
87 "\n"
88 "See xl2tpd-control man page for more help\n"
92 int main (int argc, char *argv[])
94 char* control_filename = NULL;
95 char* tunnel_name = NULL;
96 struct command_t* command = NULL;
97 int i; /* argv iterator */
99 if (!strncmp (argv[1], "--help", 6))
101 help();
102 return 0;
104 /* parse global options */
105 for (i = 1; i < argc; i++)
108 if (!strncmp (argv[i], "-c", 2))
110 control_filename = argv[++i];
111 } else if (!strncmp (argv[i], "-d", 2)) {
112 log_level = DEBUG_LEVEL;
113 } else {
114 break;
117 if (i >= argc)
119 print_error (ERROR_LEVEL, "error: command not specified\n");
120 usage();
121 return -1;
123 if (!control_filename)
125 control_filename = strdup (CONTROL_PIPE);
127 print_error (DEBUG_LEVEL, "set control filename to %s\n", control_filename);
129 /* parse command name */
130 for (command = commands; command->name; command++)
132 if (!strcasecmp (argv[i], command->name))
134 i++;
135 break;
139 if (command->name)
141 print_error (DEBUG_LEVEL, "get command %s\n", command->name);
142 } else {
143 print_error (ERROR_LEVEL, "error: no such command %s\n", argv[i]);
144 return -1;
147 /* get tunnel name */
148 if (i >= argc)
150 print_error (ERROR_LEVEL, "error: tunnel name not specified\n");
151 usage();
152 return -1;
154 tunnel_name = argv[i++];
155 /* check tunnel name for whitespaces */
156 if (strstr (tunnel_name, " "))
158 print_error (ERROR_LEVEL,
159 "error: tunnel name shouldn't include spaces\n");
160 usage();
161 return -1;
164 char buf[CONTROL_PIPE_MESSAGE_SIZE] = "";
165 FILE* mesf = fmemopen (buf, CONTROL_PIPE_MESSAGE_SIZE, "w");
167 /* create result pipe for reading */
168 char result_filename[128];
169 snprintf (result_filename, 128, RESULT_FILENAME_FORMAT, getpid());
170 unlink (result_filename);
171 mkfifo (result_filename, 0600);
172 int result_fd = open (result_filename, O_RDONLY | O_NONBLOCK, 0600);
173 if (result_fd < 0)
175 print_error (ERROR_LEVEL,
176 "error: unable to open %s for reading.\n", result_filename);
177 return -2;
180 /* turn off O_NONBLOCK */
181 if (fcntl (result_fd, F_SETFL, O_RDONLY) == -1) {
182 print_error (ERROR_LEVEL,
183 "Can not turn off nonblocking mode for result_fd: %s\n",
184 strerror(errno));
185 return -2;
188 /* pass result filename to command */
189 fprintf (mesf, "@%s ", result_filename);
190 if (ferror (mesf))
192 print_error (ERROR_LEVEL, "internal error: message buffer to short");
193 return -2;
196 /* format command with remaining arguments */
197 int command_res = command->handler (
198 mesf, tunnel_name, argc - i, argv + i
200 if (command_res < 0)
202 print_error (ERROR_LEVEL, "error: command parse error\n");
203 return -1;
206 fflush (mesf);
208 if (ferror (mesf))
210 print_error (ERROR_LEVEL,
211 "error: message too long (max = %i ch.)\n",
212 CONTROL_PIPE_MESSAGE_SIZE - 1);
213 return -1;
216 print_error (DEBUG_LEVEL, "command to be passed:\n%s\n", buf);
218 /* try to open control file for writing */
219 int control_fd = open (control_filename, O_WRONLY, 0600);
220 if (control_fd < 0)
222 int errorno = errno;
223 switch (errorno)
225 case EACCES:
226 print_error (ERROR_LEVEL,
227 "Unable to open %s for writing."
228 " Is xl2tpd running and you have appropriate permissions?\n",
229 control_filename);
230 break;
231 default:
232 print_error (ERROR_LEVEL,
233 "Unable to open %s for writing: %s\n",
234 control_filename, strerror (errorno));
236 return -1;
239 /* pass command to control pipe */
240 write (control_fd, buf, ftell (mesf));
241 close (control_fd);
243 /* read result from pipe */
244 char rbuf[CONTROL_PIPE_MESSAGE_SIZE] = "";
245 int command_result_code = read_result (
246 result_fd, rbuf, CONTROL_PIPE_MESSAGE_SIZE
248 printf ("%s", rbuf);
250 /* cleaning up */
252 close (result_fd);
253 unlink (result_filename);
255 return command_result_code;
258 void print_error (int level, const char *fmt, ...)
260 if (level > log_level) return;
261 va_list args;
262 va_start (args, fmt);
263 vfprintf (stderr, fmt, args);
264 va_end (args);
267 int read_result(int result_fd, char* buf, ssize_t size)
269 /* read result from result_fd */
270 /*FIXME: there is a chance to hang up reading.
271 Should I create watching thread with timeout?
273 ssize_t readed;
276 readed = read (result_fd, buf, size);
277 if (readed < 0)
279 print_error (ERROR_LEVEL,
280 "error: can't read command result: %s\n", strerror (errno));
281 break;
283 } while (readed == 0);
284 buf[readed] = '\0';
286 /* scan result code */
287 int command_result_code = -3;
288 sscanf (buf, "%i", &command_result_code);
290 return command_result_code;
293 int command_add
294 (FILE* mesf, char* tunnel, int optc, char *optv[])
296 if (optc <= 0)
298 print_error (ERROR_LEVEL, "error: tunnel configuration expected\n");
299 return -1;
301 fprintf (mesf, "a %s ", tunnel);
302 int i;
303 int wait_key = 1;
304 for (i = 0; i < optc; i++)
306 fprintf (mesf, "%s", optv[i]);
307 if (wait_key)
309 /* try to find '=' */
310 char* eqv = strstr (optv[i], "=");
311 if (eqv)
313 /* check is it not last symbol */
314 if (eqv != (optv[i] + strlen(optv[i]) - 1))
316 fprintf (mesf, ";"); /* end up option */
317 } else {
318 wait_key = 0; /* now we waiting for value */
320 } else { /* two-word key */
321 fprintf (mesf, " "); /* restore space */
323 } else {
324 fprintf (mesf, ";"); /* end up option */
325 wait_key = 1; /* now we again waiting for key */
328 return 0;
331 int command_connect
332 (FILE* mesf, char* tunnel, int optc, char *optv[])
334 fprintf (mesf, "c %s", tunnel);
335 /* try to read authname and password from opts */
336 if (optc > 0) {
337 if (optc == 1)
338 fprintf (mesf, " %s", optv[0]);
339 else // optc >= 2
340 fprintf (mesf, " %s %s", optv[0], optv[1]);
342 return 0;
345 int command_disconnect
346 (FILE* mesf, char* tunnel, int optc, char *optv[])
348 fprintf (mesf, "d %s", tunnel);
349 return 0;
352 int command_remove
353 (FILE* mesf, char* tunnel, int optc, char *optv[])
355 fprintf (mesf, "r %s", tunnel);
356 return 0;