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