Added a non-blocking, skipping variant of IKE_SA enumerator
[strongswan.git] / src / libcharon / 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
17 #define _GNU_SOURCE
18 #include <string.h>
19 #include <stdio.h>
20 #include <unistd.h>
21 #include <sys/types.h>
22 #include <sys/stat.h>
23 #include <errno.h>
24
25 #include "uci_control.h"
26
27 #include <daemon.h>
28 #include <threading/thread.h>
29 #include <processing/jobs/callback_job.h>
30
31 #define FIFO_FILE "/var/run/charon.fifo"
32
33
34 typedef struct private_uci_control_t private_uci_control_t;
35
36 /**
37 * private data of uci_control_t
38 */
39 struct private_uci_control_t {
40
41 /**
42 * Public part
43 */
44 uci_control_t public;
45
46 /**
47 * Job
48 */
49 callback_job_t *job;
50 };
51
52 /**
53 * write answer to fifo
54 */
55 static void write_fifo(private_uci_control_t *this, char *format, ...)
56 {
57 va_list args;
58 FILE *out;
59
60 out = fopen(FIFO_FILE, "w");
61 if (out)
62 {
63 va_start(args, format);
64 vfprintf(out, format, args);
65 va_end(args);
66 fclose(out);
67 }
68 else
69 {
70 DBG1(DBG_CFG, "writing to UCI fifo failed: %s", strerror(errno));
71 }
72 }
73
74 /**
75 * print IKE_SA status information
76 */
77 static void status(private_uci_control_t *this, char *name)
78 {
79 enumerator_t *configs, *sas;
80 iterator_t *children;
81 ike_sa_t *ike_sa;
82 child_sa_t *child_sa;
83 peer_cfg_t *peer_cfg;
84 char buf[2048];
85 FILE *out = NULL;
86
87 configs = charon->backends->create_peer_cfg_enumerator(charon->backends,
88 NULL, NULL, NULL, NULL);
89 while (configs->enumerate(configs, &peer_cfg))
90 {
91 if (name && !streq(name, peer_cfg->get_name(peer_cfg)))
92 {
93 continue;
94 }
95 sas = charon->controller->create_ike_sa_enumerator(
96 charon->controller, TRUE);
97 while (sas->enumerate(sas, &ike_sa))
98 {
99 if (!streq(ike_sa->get_name(ike_sa), peer_cfg->get_name(peer_cfg)))
100 {
101 continue;
102 }
103 if (!out)
104 {
105 out = fmemopen(buf, sizeof(buf), "w");
106 if (!out)
107 {
108 continue;
109 }
110 }
111 fprintf(out, "%-8s %-20D %-16H ", ike_sa->get_name(ike_sa),
112 ike_sa->get_other_id(ike_sa), ike_sa->get_other_host(ike_sa));
113
114 children = ike_sa->create_child_sa_iterator(ike_sa);
115 while (children->iterate(children, (void**)&child_sa))
116 {
117 fprintf(out, "%#R",
118 child_sa->get_traffic_selectors(child_sa, FALSE));
119 }
120 children->destroy(children);
121 fprintf(out, "\n");
122 }
123 sas->destroy(sas);
124 }
125 configs->destroy(configs);
126 if (out)
127 {
128 fclose(out);
129 write_fifo(this, "%s", buf);
130 }
131 else
132 {
133 write_fifo(this, "");
134 }
135 }
136
137 /**
138 * Initiate an IKE_SA
139 */
140 static void initiate(private_uci_control_t *this, char *name)
141 {
142 peer_cfg_t *peer_cfg;
143 child_cfg_t *child_cfg;
144 enumerator_t *enumerator;
145
146 peer_cfg = charon->backends->get_peer_cfg_by_name(charon->backends, name);
147 if (peer_cfg)
148 {
149 enumerator = peer_cfg->create_child_cfg_enumerator(peer_cfg);
150 if (enumerator->enumerate(enumerator, &child_cfg) &&
151 charon->controller->initiate(charon->controller, peer_cfg,
152 child_cfg->get_ref(child_cfg),
153 controller_cb_empty, NULL) == SUCCESS)
154 {
155 write_fifo(this, "connection '%s' established\n", name);
156 }
157 else
158 {
159 write_fifo(this, "establishing connection '%s' failed\n", name);
160 }
161 enumerator->destroy(enumerator);
162 }
163 else
164 {
165 write_fifo(this, "no connection named '%s' found\n", name);
166 }
167 }
168
169 /**
170 * terminate an IKE_SA
171 */
172 static void terminate(private_uci_control_t *this, char *name)
173 {
174 enumerator_t *enumerator;
175 ike_sa_t *ike_sa;
176 u_int id;
177
178 enumerator = charon->controller->create_ike_sa_enumerator(
179 charon->controller, TRUE);
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 len;
243 bool oldstate;
244 FILE *in;
245
246 memset(message, 0, sizeof(message));
247 oldstate = thread_cancelability(TRUE);
248 in = fopen(FIFO_FILE, "r");
249 thread_cancelability(oldstate);
250 if (in)
251 {
252 len = fread(message, 1, sizeof(message) - 1, in);
253 fclose(in);
254 if (len > 0)
255 {
256 process(this, message);
257 }
258 else
259 {
260 DBG1(DBG_DMN, "reading from UCI fifo failed: %s", strerror(errno));
261 }
262 }
263 else
264 {
265 DBG1(DBG_DMN, "opening UCI fifo failed: %s", strerror(errno));
266 }
267 return JOB_REQUEUE_FAIR;
268 }
269
270 /**
271 * Implementation of uci_control_t.destroy
272 */
273 static void destroy(private_uci_control_t *this)
274 {
275 this->job->cancel(this->job);
276 unlink(FIFO_FILE);
277 free(this);
278 }
279
280 /**
281 * Described in header.
282 */
283 uci_control_t *uci_control_create()
284 {
285 private_uci_control_t *this = malloc_thing(private_uci_control_t);
286
287 this->public.destroy = (void(*)(uci_control_t*))destroy;
288
289 unlink(FIFO_FILE);
290 if (mkfifo(FIFO_FILE, S_IRUSR|S_IWUSR) != 0)
291 {
292 DBG1(DBG_CFG, "creating UCI control fifo '%s' failed: %s",
293 FIFO_FILE, strerror(errno));
294 }
295 else
296 {
297 this->job = callback_job_create((callback_job_cb_t)receive,
298 this, NULL, NULL);
299 lib->processor->queue_job(lib->processor, (job_t*)this->job);
300 }
301 return &this->public;
302 }
303