merged multi-auth branch back into trunk
[strongswan.git] / src / charon / plugins / uci / uci_control.c
1 /*
2 * Copyright (C) 2008 Thomas Kallenberg
3 * Copyright (C) 2008 Martin Willi
4 * Hochschule fuer Technik Rapperswil
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2 of the License, or (at your
9 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * for more details.
15 *
16 * $Id$
17 */
18
19 #define _GNU_SOURCE
20 #include <string.h>
21 #include <stdio.h>
22 #include <unistd.h>
23 #include <sys/types.h>
24 #include <sys/stat.h>
25 #include <errno.h>
26 #include <pthread.h>
27
28 #include "uci_control.h"
29
30 #include <daemon.h>
31 #include <processing/jobs/callback_job.h>
32
33 #define FIFO_FILE "/var/run/charon.fifo"
34
35
36 typedef struct private_uci_control_t private_uci_control_t;
37
38 /**
39 * private data of uci_control_t
40 */
41 struct private_uci_control_t {
42
43 /**
44 * Public part
45 */
46 uci_control_t public;
47
48 /**
49 * Job
50 */
51 callback_job_t *job;
52 };
53
54 /**
55 * write answer to fifo
56 */
57 static void write_fifo(private_uci_control_t *this, char *format, ...)
58 {
59 va_list args;
60 FILE *out;
61
62 out = fopen(FIFO_FILE, "w");
63 if (out)
64 {
65 va_start(args, format);
66 vfprintf(out, format, args);
67 va_end(args);
68 fclose(out);
69 }
70 else
71 {
72 DBG1(DBG_CFG, "writing to UCI fifo failed: %s", strerror(errno));
73 }
74 }
75
76 /**
77 * print IKE_SA status information
78 */
79 static void status(private_uci_control_t *this, char *name)
80 {
81 enumerator_t *configs, *sas;
82 iterator_t *children;
83 ike_sa_t *ike_sa;
84 child_sa_t *child_sa;
85 peer_cfg_t *peer_cfg;
86 char buf[2048];
87 FILE *out = NULL;
88
89 configs = charon->backends->create_peer_cfg_enumerator(charon->backends,
90 NULL, NULL, NULL, NULL);
91 while (configs->enumerate(configs, &peer_cfg))
92 {
93 if (name && !streq(name, peer_cfg->get_name(peer_cfg)))
94 {
95 continue;
96 }
97 sas = charon->controller->create_ike_sa_enumerator(charon->controller);
98 while (sas->enumerate(sas, &ike_sa))
99 {
100 if (!streq(ike_sa->get_name(ike_sa), peer_cfg->get_name(peer_cfg)))
101 {
102 continue;
103 }
104 if (!out)
105 {
106 out = fmemopen(buf, sizeof(buf), "w");
107 if (!out)
108 {
109 continue;
110 }
111 }
112 fprintf(out, "%-8s %-20D %-16H ", ike_sa->get_name(ike_sa),
113 ike_sa->get_other_id(ike_sa), ike_sa->get_other_host(ike_sa));
114
115 children = ike_sa->create_child_sa_iterator(ike_sa);
116 while (children->iterate(children, (void**)&child_sa))
117 {
118 fprintf(out, "%#R",
119 child_sa->get_traffic_selectors(child_sa, FALSE));
120 }
121 children->destroy(children);
122 fprintf(out, "\n");
123 }
124 sas->destroy(sas);
125 }
126 configs->destroy(configs);
127 if (out)
128 {
129 fclose(out);
130 write_fifo(this, "%s", buf);
131 }
132 else
133 {
134 write_fifo(this, "");
135 }
136 }
137
138 /**
139 * Initiate an IKE_SA
140 */
141 static void initiate(private_uci_control_t *this, char *name)
142 {
143 peer_cfg_t *peer_cfg;
144 child_cfg_t *child_cfg;
145 enumerator_t *enumerator;
146
147 peer_cfg = charon->backends->get_peer_cfg_by_name(charon->backends, name);
148 if (peer_cfg)
149 {
150 enumerator = peer_cfg->create_child_cfg_enumerator(peer_cfg);
151 if (enumerator->enumerate(enumerator, &child_cfg) &&
152 charon->controller->initiate(charon->controller, peer_cfg,
153 child_cfg->get_ref(child_cfg),
154 controller_cb_empty, NULL) == SUCCESS)
155 {
156 write_fifo(this, "connection '%s' established\n", name);
157 }
158 else
159 {
160 write_fifo(this, "establishing connection '%s' failed\n", name);
161 }
162 enumerator->destroy(enumerator);
163 }
164 else
165 {
166 write_fifo(this, "no connection named '%s' found\n", name);
167 }
168 }
169
170 /**
171 * terminate an IKE_SA
172 */
173 static void terminate(private_uci_control_t *this, char *name)
174 {
175 enumerator_t *enumerator;
176 ike_sa_t *ike_sa;
177 u_int id;
178
179 enumerator = charon->controller->create_ike_sa_enumerator(charon->controller);
180 while (enumerator->enumerate(enumerator, &ike_sa))
181 {
182 if (streq(name, ike_sa->get_name(ike_sa)))
183 {
184 id = ike_sa->get_unique_id(ike_sa);
185 enumerator->destroy(enumerator);
186 charon->controller->terminate_ike(charon->controller, id,
187 controller_cb_empty, NULL);
188 write_fifo(this, "connection '%s' terminated\n", name);
189 return;
190 }
191 }
192 enumerator->destroy(enumerator);
193 write_fifo(this, "no active connection named '%s'\n", name);
194 }
195
196 /**
197 * dispatch control request
198 */
199 static void process(private_uci_control_t *this, char *message)
200 {
201 enumerator_t* enumerator;
202
203 enumerator = enumerator_create_token(message, " \n", "");
204 if (enumerator->enumerate(enumerator, &message))
205 {
206 if (streq(message, "status"))
207 {
208 if (enumerator->enumerate(enumerator, &message))
209 {
210 status(this, message);
211 }
212 else
213 {
214 status(this, NULL);
215 }
216 }
217 else if (streq(message, "up") &&
218 enumerator->enumerate(enumerator, &message))
219 {
220 initiate(this, message);
221 }
222 else if (streq(message, "down") &&
223 enumerator->enumerate(enumerator, &message))
224 {
225 terminate(this, message);
226 }
227 else
228 {
229 write_fifo(this, "usage: status [<name>] | up <name> | down <name>\n"
230 " status format: name peer-id peer-addr tunnel(s)\n");
231 }
232 }
233 enumerator->destroy(enumerator);
234 }
235
236 /**
237 * read from fifo
238 */
239 static job_requeue_t receive(private_uci_control_t *this)
240 {
241 char message[128];
242 int oldstate, len;
243 FILE *in;
244
245 memset(message, 0, sizeof(message));
246 pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldstate);
247 in = fopen(FIFO_FILE, "r");
248 pthread_setcancelstate(oldstate, NULL);
249 if (in)
250 {
251 len = fread(message, 1, sizeof(message) - 1, in);
252 fclose(in);
253 if (len > 0)
254 {
255 process(this, message);
256 }
257 else
258 {
259 DBG1(DBG_DMN, "reading from UCI fifo failed: %s", strerror(errno));
260 }
261 }
262 else
263 {
264 DBG1(DBG_DMN, "opening UCI fifo failed: %s", strerror(errno));
265 }
266 return JOB_REQUEUE_FAIR;
267 }
268
269 /**
270 * Implementation of uci_control_t.destroy
271 */
272 static void destroy(private_uci_control_t *this)
273 {
274 this->job->cancel(this->job);
275 unlink(FIFO_FILE);
276 free(this);
277 }
278
279 /**
280 * Described in header.
281 */
282 uci_control_t *uci_control_create()
283 {
284 private_uci_control_t *this = malloc_thing(private_uci_control_t);
285
286 this->public.destroy = (void(*)(uci_control_t*))destroy;
287
288 unlink(FIFO_FILE);
289 if (mkfifo(FIFO_FILE, S_IRUSR|S_IWUSR) != 0)
290 {
291 DBG1(DBG_CFG, "creating UCI control fifo '%s' failed: %s",
292 FIFO_FILE, strerror(errno));
293 }
294 else
295 {
296 this->job = callback_job_create((callback_job_cb_t)receive,
297 this, NULL, NULL);
298 charon->processor->queue_job(charon->processor, (job_t*)this->job);
299 }
300 return &this->public;
301 }
302