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