Merge branch 'ikev1-clean' into ikev1-master
[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, *children;
80 ike_sa_t *ike_sa;
81 child_sa_t *child_sa;
82 peer_cfg_t *peer_cfg;
83 char buf[2048];
84 FILE *out = NULL;
85
86 configs = charon->backends->create_peer_cfg_enumerator(charon->backends,
87 NULL, NULL, NULL, NULL, IKE_ANY);
88 while (configs->enumerate(configs, &peer_cfg))
89 {
90 if (name && !streq(name, peer_cfg->get_name(peer_cfg)))
91 {
92 continue;
93 }
94 sas = charon->controller->create_ike_sa_enumerator(
95 charon->controller, TRUE);
96 while (sas->enumerate(sas, &ike_sa))
97 {
98 if (!streq(ike_sa->get_name(ike_sa), peer_cfg->get_name(peer_cfg)))
99 {
100 continue;
101 }
102 if (!out)
103 {
104 out = fmemopen(buf, sizeof(buf), "w");
105 if (!out)
106 {
107 continue;
108 }
109 }
110 fprintf(out, "%-8s %-20D %-16H ", ike_sa->get_name(ike_sa),
111 ike_sa->get_other_id(ike_sa), ike_sa->get_other_host(ike_sa));
112
113 children = ike_sa->create_child_sa_enumerator(ike_sa);
114 while (children->enumerate(children, (void**)&child_sa))
115 {
116 fprintf(out, "%#R",
117 child_sa->get_traffic_selectors(child_sa, FALSE));
118 }
119 children->destroy(children);
120 fprintf(out, "\n");
121 }
122 sas->destroy(sas);
123 }
124 configs->destroy(configs);
125 if (out)
126 {
127 fclose(out);
128 write_fifo(this, "%s", buf);
129 }
130 else
131 {
132 write_fifo(this, "");
133 }
134 }
135
136 /**
137 * Initiate an IKE_SA
138 */
139 static void initiate(private_uci_control_t *this, char *name)
140 {
141 peer_cfg_t *peer_cfg;
142 child_cfg_t *child_cfg;
143 enumerator_t *enumerator;
144
145 peer_cfg = charon->backends->get_peer_cfg_by_name(charon->backends, name);
146 if (peer_cfg)
147 {
148 enumerator = peer_cfg->create_child_cfg_enumerator(peer_cfg);
149 if (enumerator->enumerate(enumerator, &child_cfg) &&
150 charon->controller->initiate(charon->controller, peer_cfg,
151 child_cfg->get_ref(child_cfg),
152 controller_cb_empty, NULL, 0) == SUCCESS)
153 {
154 write_fifo(this, "connection '%s' established\n", name);
155 }
156 else
157 {
158 write_fifo(this, "establishing connection '%s' failed\n", name);
159 }
160 enumerator->destroy(enumerator);
161 }
162 else
163 {
164 write_fifo(this, "no connection named '%s' found\n", name);
165 }
166 }
167
168 /**
169 * terminate an IKE_SA
170 */
171 static void terminate(private_uci_control_t *this, char *name)
172 {
173 enumerator_t *enumerator;
174 ike_sa_t *ike_sa;
175 u_int id;
176
177 enumerator = charon->controller->create_ike_sa_enumerator(
178 charon->controller, TRUE);
179 while (enumerator->enumerate(enumerator, &ike_sa))
180 {
181 if (streq(name, ike_sa->get_name(ike_sa)))
182 {
183 id = ike_sa->get_unique_id(ike_sa);
184 enumerator->destroy(enumerator);
185 charon->controller->terminate_ike(charon->controller, id,
186 controller_cb_empty, NULL, 0);
187 write_fifo(this, "connection '%s' terminated\n", name);
188 return;
189 }
190 }
191 enumerator->destroy(enumerator);
192 write_fifo(this, "no active connection named '%s'\n", name);
193 }
194
195 /**
196 * dispatch control request
197 */
198 static void process(private_uci_control_t *this, char *message)
199 {
200 enumerator_t* enumerator;
201
202 enumerator = enumerator_create_token(message, " \n", "");
203 if (enumerator->enumerate(enumerator, &message))
204 {
205 if (streq(message, "status"))
206 {
207 if (enumerator->enumerate(enumerator, &message))
208 {
209 status(this, message);
210 }
211 else
212 {
213 status(this, NULL);
214 }
215 }
216 else if (streq(message, "up") &&
217 enumerator->enumerate(enumerator, &message))
218 {
219 initiate(this, message);
220 }
221 else if (streq(message, "down") &&
222 enumerator->enumerate(enumerator, &message))
223 {
224 terminate(this, message);
225 }
226 else
227 {
228 write_fifo(this, "usage: status [<name>] | up <name> | down <name>\n"
229 " status format: name peer-id peer-addr tunnel(s)\n");
230 }
231 }
232 enumerator->destroy(enumerator);
233 }
234
235 /**
236 * read from fifo
237 */
238 static job_requeue_t receive(private_uci_control_t *this)
239 {
240 char message[128];
241 int len;
242 bool oldstate;
243 FILE *in;
244
245 memset(message, 0, sizeof(message));
246 oldstate = thread_cancelability(TRUE);
247 in = fopen(FIFO_FILE, "r");
248 thread_cancelability(oldstate);
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 METHOD(uci_control_t, destroy, void,
270 private_uci_control_t *this)
271 {
272 this->job->cancel(this->job);
273 unlink(FIFO_FILE);
274 free(this);
275 }
276
277 /**
278 * Described in header.
279 */
280 uci_control_t *uci_control_create()
281 {
282 private_uci_control_t *this;
283
284 INIT(this,
285 .public = {
286 .destroy = _destroy,
287 },
288 );
289
290 unlink(FIFO_FILE);
291 if (mkfifo(FIFO_FILE, S_IRUSR|S_IWUSR) != 0)
292 {
293 DBG1(DBG_CFG, "creating UCI control fifo '%s' failed: %s",
294 FIFO_FILE, strerror(errno));
295 }
296 else
297 {
298 this->job = callback_job_create_with_prio((callback_job_cb_t)receive,
299 this, NULL, NULL, JOB_PRIO_CRITICAL);
300 lib->processor->queue_job(lib->processor, (job_t*)this->job);
301 }
302 return &this->public;
303 }
304