b44a8e5e25ff5c805fb6c6dca2674836fd87bc68
[strongswan.git] / src / dumm / mconsole.c
1 /*
2 * Copyright (C) 2007 Martin Willi
3 * Hochschule fuer Technik Rapperswil
4 * Copyright (C) 2001-2004 Jeff Dike
5 *
6 * Based on the "uml_mconsole" utility from Jeff Dike.
7 *
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the
10 * Free Software Foundation; either version 2 of the License, or (at your
11 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
12 *
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16 * for more details.
17 */
18
19 #define _GNU_SOURCE
20
21 #include <sys/types.h>
22 #include <unistd.h>
23 #include <stdio.h>
24 #include <sys/socket.h>
25 #include <errno.h>
26 #include <sys/un.h>
27
28 #include <debug.h>
29
30 #include "mconsole.h"
31
32 #define MCONSOLE_MAGIC 0xcafebabe
33 #define MCONSOLE_VERSION 2
34 #define MCONSOLE_MAX_DATA 512
35
36 typedef struct private_mconsole_t private_mconsole_t;
37
38 struct private_mconsole_t {
39 /** public interface */
40 mconsole_t public;
41 /** mconsole socket */
42 int console;
43 /** notify socket */
44 int notify;
45 /** address of uml socket */
46 struct sockaddr_un uml;
47 /** idle function */
48 void (*idle)(void);
49 };
50
51 /**
52 * mconsole message format from "arch/um/include/mconsole.h"
53 */
54 typedef struct mconsole_request mconsole_request;
55 /** mconsole request message */
56 struct mconsole_request {
57 u_int32_t magic;
58 u_int32_t version;
59 u_int32_t len;
60 char data[MCONSOLE_MAX_DATA];
61 };
62
63
64 typedef struct mconsole_reply mconsole_reply;
65 /** mconsole reply message */
66 struct mconsole_reply {
67 u_int32_t err;
68 u_int32_t more;
69 u_int32_t len;
70 char data[MCONSOLE_MAX_DATA];
71 };
72
73 typedef struct mconsole_notify mconsole_notify;
74 /** mconsole notify message */
75 struct mconsole_notify {
76 u_int32_t magic;
77 u_int32_t version;
78 enum {
79 MCONSOLE_SOCKET,
80 MCONSOLE_PANIC,
81 MCONSOLE_HANG,
82 MCONSOLE_USER_NOTIFY,
83 } type;
84 u_int32_t len;
85 char data[MCONSOLE_MAX_DATA];
86 };
87
88 /**
89 * send a request to UML using mconsole
90 */
91 static int request(private_mconsole_t *this, void(*cb)(void*,char*,size_t),
92 void *data, char *command, ...)
93 {
94 mconsole_request request;
95 mconsole_reply reply;
96 int len, flags = 0;
97 va_list args;
98
99 memset(&request, 0, sizeof(request));
100 request.magic = MCONSOLE_MAGIC;
101 request.version = MCONSOLE_VERSION;
102 va_start(args, command);
103 request.len = vsnprintf(request.data, sizeof(request.data), command, args);
104 va_end(args);
105
106 if (this->idle)
107 {
108 flags = MSG_DONTWAIT;
109 }
110 do
111 {
112 if (this->idle)
113 {
114 this->idle();
115 }
116 len = sendto(this->console, &request, sizeof(request), flags,
117 (struct sockaddr*)&this->uml, sizeof(this->uml));
118 }
119 while (len < 0 && (errno == EINTR || errno == EAGAIN));
120
121 if (len < 0)
122 {
123 DBG1("sending mconsole command to UML failed: %m");
124 return -1;
125 }
126 do
127 {
128 len = recv(this->console, &reply, sizeof(reply), flags);
129 if (len < 0 && (errno == EINTR || errno == EAGAIN))
130 {
131 if (this->idle)
132 {
133 this->idle();
134 }
135 continue;
136 }
137 if (len < 0)
138 {
139 DBG1("receiving from mconsole failed: %m");
140 return -1;
141 }
142 if (len > 0)
143 {
144 if (cb)
145 {
146 cb(data, reply.data, reply.len);
147 }
148 else if (reply.err)
149 {
150 DBG1("received mconsole error %d: %*.s",
151 reply.err, reply.len, reply.data);
152 break;
153 }
154 }
155 }
156 while (reply.more);
157
158 return reply.err;
159 }
160
161 /**
162 * ignore error message
163 */
164 static void ignore(void *data, char *buf, size_t len)
165 {
166 }
167
168 /**
169 * Implementation of mconsole_t.add_iface.
170 */
171 static bool add_iface(private_mconsole_t *this, char *guest, char *host)
172 {
173 int tries = 0;
174
175 while (tries++ < 5)
176 {
177 if (request(this, ignore, NULL, "config %s=tuntap,%s", guest, host) == 0)
178 {
179 return TRUE;
180 }
181 usleep(10000 * tries * tries);
182 }
183 return FALSE;
184 }
185
186 /**
187 * Implementation of mconsole_t.del_iface.
188 */
189 static bool del_iface(private_mconsole_t *this, char *guest)
190 {
191 if (request(this, NULL, NULL, "remove %s", guest) != 0)
192 {
193 return FALSE;
194 }
195 return TRUE;
196 }
197
198 /**
199 * Implementation of mconsole_t.exec
200 */
201 static int exec(private_mconsole_t *this, void(*cb)(void*,char*,size_t),
202 void *data, char *cmd)
203 {
204 if (request(this, cb, data, "exec %s", cmd) != 0)
205 {
206 return -1;
207 }
208 return 0;
209 }
210
211 /**
212 * Poll until guest is ready
213 */
214 static bool wait_bootup(private_mconsole_t *this)
215 {
216 int res;
217
218 while (TRUE)
219 {
220 res = request(this, ignore, NULL, "config eth9=mcast");
221 if (res < 0)
222 {
223 return FALSE;
224 }
225 if (res == 0)
226 {
227 while (request(this, ignore, NULL, "remove eth9") != 0)
228 {
229 usleep(50000);
230 }
231 return TRUE;
232 }
233 if (this->idle)
234 {
235 this->idle();
236 }
237 else
238 {
239 usleep(50000);
240 }
241 }
242 }
243
244 /**
245 * Implementation of mconsole_t.destroy.
246 */
247 static void destroy(private_mconsole_t *this)
248 {
249 close(this->console);
250 close(this->notify);
251 free(this);
252 }
253
254 /**
255 * setup the mconsole notify connection and wait for its readyness
256 */
257 static bool wait_for_notify(private_mconsole_t *this, char *nsock)
258 {
259 struct sockaddr_un addr;
260 mconsole_notify notify;
261 int len, flags = 0;
262
263 this->notify = socket(AF_UNIX, SOCK_DGRAM, 0);
264 if (this->notify < 0)
265 {
266 DBG1("opening mconsole notify socket failed: %m");
267 return FALSE;
268 }
269 memset(&addr, 0, sizeof(addr));
270 addr.sun_family = AF_UNIX;
271 strncpy(addr.sun_path, nsock, sizeof(addr));
272 if (bind(this->notify, (struct sockaddr*)&addr, sizeof(addr)) < 0)
273 {
274 DBG1("binding mconsole notify socket to '%s' failed: %m", nsock);
275 close(this->notify);
276 return FALSE;
277 }
278 if (this->idle)
279 {
280 flags = MSG_DONTWAIT;
281 }
282 do
283 {
284 if (this->idle)
285 {
286 this->idle();
287 }
288 len = recvfrom(this->notify, &notify, sizeof(notify), flags, NULL, 0);
289 }
290 while (len < 0 && (errno == EINTR || errno == EAGAIN));
291
292 if (len < 0 || len >= sizeof(notify))
293 {
294 DBG1("reading from mconsole notify socket failed: %m");
295 close(this->notify);
296 unlink(nsock);
297 return FALSE;
298 }
299 if (notify.magic != MCONSOLE_MAGIC ||
300 notify.version != MCONSOLE_VERSION ||
301 notify.type != MCONSOLE_SOCKET)
302 {
303 DBG1("received unexpected message from mconsole notify socket: %b",
304 &notify, sizeof(notify));
305 close(this->notify);
306 unlink(nsock);
307 return FALSE;
308 }
309 memset(&this->uml, 0, sizeof(this->uml));
310 this->uml.sun_family = AF_UNIX;
311 strncpy(this->uml.sun_path, (char*)&notify.data, sizeof(this->uml.sun_path));
312 return TRUE;
313 }
314
315 /**
316 * setup the mconsole console connection
317 */
318 static bool setup_console(private_mconsole_t *this)
319 {
320 struct sockaddr_un addr;
321
322 this->console = socket(AF_UNIX, SOCK_DGRAM, 0);
323 if (this->console < 0)
324 {
325 DBG1("opening mconsole socket failed: %m");
326 return FALSE;
327 }
328 memset(&addr, 0, sizeof(addr));
329 addr.sun_family = AF_UNIX;
330 snprintf(&addr.sun_path[1], sizeof(addr.sun_path), "%5d-%d",
331 getpid(), this->console);
332 if (bind(this->console, (struct sockaddr*)&addr, sizeof(addr)) < 0)
333 {
334 DBG1("binding mconsole socket to '%s' failed: %m", &addr.sun_path[1]);
335 close(this->console);
336 return FALSE;
337 }
338 return TRUE;
339 }
340
341 /**
342 * create the mconsole instance
343 */
344 mconsole_t *mconsole_create(char *notify, void(*idle)(void))
345 {
346 private_mconsole_t *this = malloc_thing(private_mconsole_t);
347
348 this->public.add_iface = (bool(*)(mconsole_t*, char *guest, char *host))add_iface;
349 this->public.del_iface = (bool(*)(mconsole_t*, char *guest))del_iface;
350 this->public.exec = (int(*)(mconsole_t*, void(*cb)(void*,char*,size_t), void *data, char *cmd))exec;
351 this->public.destroy = (void*)destroy;
352
353 this->idle = idle;
354
355 if (!wait_for_notify(this, notify))
356 {
357 free(this);
358 return NULL;
359 }
360
361 if (!setup_console(this))
362 {
363 close(this->notify);
364 unlink(notify);
365 free(this);
366 return NULL;
367 }
368 unlink(notify);
369
370 if (!wait_bootup(this))
371 {
372 destroy(this);
373 return NULL;
374 }
375
376 return &this->public;
377 }
378