prototype of dumm GUI
[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, char *command,
92 char buf[], size_t *size)
93 {
94 mconsole_request request;
95 mconsole_reply reply;
96 int len, total = 0, flags = 0;
97
98 memset(&request, 0, sizeof(request));
99 request.magic = MCONSOLE_MAGIC;
100 request.version = MCONSOLE_VERSION;
101 request.len = min(strlen(command), sizeof(reply.data) - 1);
102 strncpy(request.data, command, request.len);
103 *buf = '\0';
104 (*size)--;
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 snprintf(buf, *size, "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 snprintf(buf, *size, "receiving from mconsole failed: %m");
140 return -1;
141 }
142 if (len > 0)
143 {
144 strncat(buf, reply.data, min(reply.len, *size - total));
145 total += reply.len;
146 }
147 }
148 while (reply.more);
149
150 *size = total;
151 return reply.err;
152 }
153
154 /**
155 * Implementation of mconsole_t.add_iface.
156 */
157 static bool add_iface(private_mconsole_t *this, char *guest, char *host)
158 {
159 char buf[128];
160 int len;
161
162 len = snprintf(buf, sizeof(buf), "config %s=tuntap,%s", guest, host);
163 if (len < 0 || len >= sizeof(buf))
164 {
165 return FALSE;
166 }
167 len = sizeof(buf);
168 if (request(this, buf, buf, &len) != 0)
169 {
170 DBG1("adding interface failed: %.*s", len, buf);
171 return FALSE;
172 }
173 return TRUE;
174 }
175
176 /**
177 * Implementation of mconsole_t.del_iface.
178 */
179 static bool del_iface(private_mconsole_t *this, char *guest)
180 {
181 char buf[128];
182 int len;
183
184 len = snprintf(buf, sizeof(buf), "remove %s", guest);
185 if (len < 0 || len >= sizeof(buf))
186 {
187 return FALSE;
188 }
189 if (request(this, buf, buf, &len) != 0)
190 {
191 DBG1("removing interface failed: %.*s", len, buf);
192 return FALSE;
193 }
194 return TRUE;
195 }
196
197 /**
198 * Poll until guest is ready
199 */
200 static bool wait_bootup(private_mconsole_t *this)
201 {
202 char *cmd, buf[128];
203 int len, res;
204
205 cmd = "config con0";
206 while (TRUE)
207 {
208 len = sizeof(buf);
209 res = request(this, cmd, buf, &len);
210 if (res < 0)
211 {
212 return FALSE;
213 }
214 if (res == 0)
215 {
216 return TRUE;
217 }
218 if (this->idle)
219 {
220 this->idle();
221 }
222 else
223 {
224 usleep(50000);
225 }
226 }
227 }
228
229 /**
230 * Implementation of mconsole_t.destroy.
231 */
232 static void destroy(private_mconsole_t *this)
233 {
234 close(this->console);
235 close(this->notify);
236 free(this);
237 }
238
239 /**
240 * setup the mconsole notify connection and wait for its readyness
241 */
242 static bool wait_for_notify(private_mconsole_t *this, char *nsock)
243 {
244 struct sockaddr_un addr;
245 mconsole_notify notify;
246 int len, flags = 0;
247
248 this->notify = socket(AF_UNIX, SOCK_DGRAM, 0);
249 if (this->notify < 0)
250 {
251 DBG1("opening mconsole notify socket failed: %m");
252 return FALSE;
253 }
254 memset(&addr, 0, sizeof(addr));
255 addr.sun_family = AF_UNIX;
256 strncpy(addr.sun_path, nsock, sizeof(addr));
257 if (bind(this->notify, (struct sockaddr*)&addr, sizeof(addr)) < 0)
258 {
259 DBG1("binding mconsole notify socket to '%s' failed: %m", nsock);
260 close(this->notify);
261 return FALSE;
262 }
263 if (this->idle)
264 {
265 flags = MSG_DONTWAIT;
266 }
267 do
268 {
269 if (this->idle)
270 {
271 this->idle();
272 }
273 len = recvfrom(this->notify, &notify, sizeof(notify), flags, NULL, 0);
274 }
275 while (len < 0 && (errno == EINTR || errno == EAGAIN));
276
277 if (len < 0 || len >= sizeof(notify))
278 {
279 DBG1("reading from mconsole notify socket failed: %m");
280 close(this->notify);
281 unlink(nsock);
282 return FALSE;
283 }
284 if (notify.magic != MCONSOLE_MAGIC ||
285 notify.version != MCONSOLE_VERSION ||
286 notify.type != MCONSOLE_SOCKET)
287 {
288 DBG1("received unexpected message from mconsole notify socket: %b",
289 &notify, sizeof(notify));
290 close(this->notify);
291 unlink(nsock);
292 return FALSE;
293 }
294 memset(&this->uml, 0, sizeof(this->uml));
295 this->uml.sun_family = AF_UNIX;
296 strncpy(this->uml.sun_path, (char*)&notify.data, sizeof(this->uml.sun_path));
297 return TRUE;
298 }
299
300 /**
301 * setup the mconsole console connection
302 */
303 static bool setup_console(private_mconsole_t *this)
304 {
305 struct sockaddr_un addr;
306
307 this->console = socket(AF_UNIX, SOCK_DGRAM, 0);
308 if (this->console < 0)
309 {
310 DBG1("opening mconsole socket failed: %m");
311 return FALSE;
312 }
313 memset(&addr, 0, sizeof(addr));
314 addr.sun_family = AF_UNIX;
315 snprintf(&addr.sun_path[1], sizeof(addr.sun_path), "%5d-%d",
316 getpid(), this->console);
317 if (bind(this->console, (struct sockaddr*)&addr, sizeof(addr)) < 0)
318 {
319 DBG1("binding mconsole socket to '%s' failed: %m", &addr.sun_path[1]);
320 close(this->console);
321 return FALSE;
322 }
323 return TRUE;
324 }
325
326 /**
327 * create the mconsole instance
328 */
329 mconsole_t *mconsole_create(char *notify, void(*idle)(void))
330 {
331 private_mconsole_t *this = malloc_thing(private_mconsole_t);
332
333 this->public.add_iface = (bool(*)(mconsole_t*, char *guest, char *host))add_iface;
334 this->public.del_iface = (bool(*)(mconsole_t*, char *guest))del_iface;
335 this->public.destroy = (void*)destroy;
336
337 this->idle = idle;
338
339 if (!wait_for_notify(this, notify))
340 {
341 free(this);
342 return NULL;
343 }
344
345 if (!setup_console(this))
346 {
347 close(this->notify);
348 unlink(notify);
349 free(this);
350 return NULL;
351 }
352 unlink(notify);
353
354 if (!wait_bootup(this))
355 {
356 destroy(this);
357 return NULL;
358 }
359
360 return &this->public;
361 }
362