implemented mconsole notification to check if guest came up
[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 #include <sys/types.h>
20 #include <unistd.h>
21 #include <stdio.h>
22 #include <sys/socket.h>
23 #include <sys/un.h>
24
25 #include <debug.h>
26
27 #include "mconsole.h"
28
29 #define MCONSOLE_MAGIC 0xcafebabe
30 #define MCONSOLE_VERSION 2
31 #define MCONSOLE_MAX_DATA 512
32
33 typedef struct private_mconsole_t private_mconsole_t;
34
35 struct private_mconsole_t {
36 /** public interface */
37 mconsole_t public;
38 /** mconsole socket */
39 int console;
40 /** notify socket */
41 int notify;
42 /** address of uml socket */
43 struct sockaddr_un uml;
44 };
45
46 /**
47 * mconsole message format from "arch/um/include/mconsole.h"
48 */
49 typedef struct mconsole_request mconsole_request;
50 /** mconsole request message */
51 struct mconsole_request {
52 u_int32_t magic;
53 u_int32_t version;
54 u_int32_t len;
55 char data[MCONSOLE_MAX_DATA];
56 };
57
58
59 typedef struct mconsole_reply mconsole_reply;
60 /** mconsole reply message */
61 struct mconsole_reply {
62 u_int32_t err;
63 u_int32_t more;
64 u_int32_t len;
65 char data[MCONSOLE_MAX_DATA];
66 };
67
68 typedef struct mconsole_notify mconsole_notify;
69 /** mconsole notify message */
70 struct mconsole_notify {
71 u_int32_t magic;
72 u_int32_t version;
73 enum {
74 MCONSOLE_SOCKET,
75 MCONSOLE_PANIC,
76 MCONSOLE_HANG,
77 MCONSOLE_USER_NOTIFY,
78 } type;
79 u_int32_t len;
80 char data[MCONSOLE_MAX_DATA];
81 };
82
83 /**
84 * send a request to UML using mconsole
85 */
86 static bool request(private_mconsole_t *this, char *command)
87 {
88 mconsole_request request;
89 mconsole_reply reply;
90 bool first = TRUE, good = TRUE;
91 int len;
92
93 memset(&request, 0, sizeof(request));
94 request.magic = MCONSOLE_MAGIC;
95 request.version = MCONSOLE_VERSION;
96 request.len = min(strlen(command), sizeof(reply.data) - 1);
97 strncpy(request.data, command, request.len);
98
99 if (sendto(this->console, &request, sizeof(request), 0,
100 (struct sockaddr*)&this->uml, sizeof(this->uml)) < 0)
101 {
102 DBG1("sending mconsole command to UML failed: %m");
103 return FALSE;
104 }
105 do
106 {
107 len = recvfrom(this->console, &reply, sizeof(reply), 0, NULL, 0);
108 if (len < 0)
109 {
110 DBG1("receiving from mconsole failed: %m");
111 return FALSE;
112 }
113 if (first && reply.err)
114 {
115 good = FALSE;
116 DBG1("received error from UML mconsole: %s", reply.data);
117 }
118 first = FALSE;
119 }
120 while (reply.more);
121 return good;
122 }
123
124 /**
125 * Implementation of mconsole_t.add_iface.
126 */
127 static bool add_iface(private_mconsole_t *this, char *guest, char *host)
128 {
129 char buf[128];
130 int len;
131
132 len = snprintf(buf, sizeof(buf), "config %s=tuntap,%s", guest, host);
133 if (len < 0 || len >= sizeof(buf))
134 {
135 return FALSE;
136 }
137 return request(this, buf);
138 }
139
140 /**
141 * Implementation of mconsole_t.del_iface.
142 */
143 static bool del_iface(private_mconsole_t *this, char *guest)
144 {
145 char buf[128];
146 int len;
147
148 len = snprintf(buf, sizeof(buf), "remove %s", guest);
149 if (len < 0 || len >= sizeof(buf))
150 {
151 return FALSE;
152 }
153 return request(this, buf);
154 }
155
156 /**
157 * Implementation of mconsole_t.destroy.
158 */
159 static void destroy(private_mconsole_t *this)
160 {
161 close(this->console);
162 close(this->notify);
163 free(this);
164 }
165
166 /**
167 * setup the mconsole notify connection and wait for its readyness
168 */
169 static bool wait_for_notify(private_mconsole_t *this, char *nsock)
170 {
171 struct sockaddr_un addr;
172 mconsole_notify notify;
173 int len;
174
175 this->notify = socket(AF_UNIX, SOCK_DGRAM, 0);
176 if (this->notify < 0)
177 {
178 DBG1("opening mconsole notify socket failed: %m");
179 return FALSE;
180 }
181 memset(&addr, 0, sizeof(addr));
182 addr.sun_family = AF_UNIX;
183 strncpy(addr.sun_path, nsock, sizeof(addr));
184 if (bind(this->notify, (struct sockaddr*)&addr, sizeof(addr)) < 0)
185 {
186 DBG1("binding mconsole notify socket to '%s' failed: %m", nsock);
187 close(this->notify);
188 return FALSE;
189 }
190 len = recvfrom(this->notify, &notify, sizeof(notify), 0, NULL, 0);
191 if (len < 0 || len >= sizeof(notify))
192 {
193 DBG1("reading from mconsole notify socket failed: %m");
194 close(this->notify);
195 unlink(nsock);
196 return FALSE;
197 }
198 if (notify.magic != MCONSOLE_MAGIC ||
199 notify.version != MCONSOLE_VERSION ||
200 notify.type != MCONSOLE_SOCKET)
201 {
202 DBG1("received unexpected message from mconsole notify socket: %b",
203 &notify, sizeof(notify));
204 close(this->notify);
205 unlink(nsock);
206 return FALSE;
207 }
208 memset(&this->uml, 0, sizeof(this->uml));
209 this->uml.sun_family = AF_UNIX;
210 strncpy(this->uml.sun_path, (char*)&notify.data, sizeof(this->uml.sun_path));
211 return TRUE;
212 }
213
214 /**
215 * setup the mconsole console connection
216 */
217 static bool setup_console(private_mconsole_t *this)
218 {
219 struct sockaddr_un addr;
220
221 this->console = socket(AF_UNIX, SOCK_DGRAM, 0);
222 if (this->console < 0)
223 {
224 DBG1("opening mconsole socket failed: %m");
225 return FALSE;
226 }
227 memset(&addr, 0, sizeof(addr));
228 addr.sun_family = AF_UNIX;
229 snprintf(&addr.sun_path[1], sizeof(addr.sun_path), "%5d-%d",
230 getpid(), this->console);
231 if (bind(this->console, (struct sockaddr*)&addr, sizeof(addr)) < 0)
232 {
233 DBG1("binding mconsole socket to '%s' failed: %m", &addr.sun_path[1]);
234 close(this->console);
235 return FALSE;
236 }
237 return TRUE;
238 }
239
240 /**
241 * create the mconsole instance
242 */
243 mconsole_t *mconsole_create(char *notify)
244 {
245 private_mconsole_t *this = malloc_thing(private_mconsole_t);
246
247 this->public.add_iface = (bool(*)(mconsole_t*, char *guest, char *host))add_iface;
248 this->public.del_iface = (bool(*)(mconsole_t*, char *guest))del_iface;
249 this->public.destroy = (void*)destroy;
250
251 if (!wait_for_notify(this, notify))
252 {
253 free(this);
254 return NULL;
255 }
256
257 if (!setup_console(this))
258 {
259 close(this->notify);
260 unlink(notify);
261 free(this);
262 return NULL;
263 }
264 unlink(notify);
265
266 return &this->public;
267 }
268