32af9333c5c26d6da82bdc7e666683d79a35d620
[strongswan.git] / src / dumm / guest.c
1 /*
2 * Copyright (C) 2007 Martin Willi
3 * Hochschule fuer Technik Rapperswil
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 */
15
16 #define _GNU_SOURCE
17
18 #include <sys/types.h>
19 #include <sys/stat.h>
20 #include <unistd.h>
21 #include <stdio.h>
22 #include <sys/uio.h>
23 #include <sys/types.h>
24 #include <fcntl.h>
25 #include <signal.h>
26
27 #include <debug.h>
28 #include <utils/linked_list.h>
29
30 #include "dumm.h"
31 #include "guest.h"
32 #include "mconsole.h"
33
34 typedef struct private_guest_t private_guest_t;
35
36 struct private_guest_t {
37 /** implemented public interface */
38 guest_t public;
39 /** name of the guest */
40 char *name;
41 /** kernel to boot for guest */
42 char *kernel;
43 /** read only master filesystem guest uses */
44 char *master;
45 /** amount of memory for guest, in MB */
46 int mem;
47 /** pid of guest child process */
48 int pid;
49 /** log file for console 0 */
50 int bootlog;
51 /** mconsole to control running UML */
52 mconsole_t *mconsole;
53 /** list of interfaces attached to the guest */
54 linked_list_t *ifaces;
55 };
56
57 /**
58 * Implementation of guest_t.get_name.
59 */
60 static char* get_name(private_guest_t *this)
61 {
62 return this->name;
63 }
64
65 /**
66 * Implementation of guest_t.create_iface.
67 */
68 static iface_t* create_iface(private_guest_t *this, char *name)
69 {
70 iterator_t *iterator;
71 iface_t *iface;
72
73 if (this->pid == 0)
74 {
75 DBG1("guest '%s' not running, unable to add interface", this->name);
76 return NULL;
77 }
78
79 iterator = this->ifaces->create_iterator(this->ifaces, TRUE);
80 while (iterator->iterate(iterator, (void**)&iface))
81 {
82 if (streq(name, iface->get_guestif(iface)))
83 {
84 DBG1("guest '%s' already has an interface '%s'", this->name, name);
85 iterator->destroy(iterator);
86 return NULL;
87 }
88 }
89 iterator->destroy(iterator);
90
91 iface = iface_create(this->name, name, this->mconsole);
92 if (iface)
93 {
94 this->ifaces->insert_last(this->ifaces, iface);
95 }
96 return iface;
97 }
98
99 /**
100 * Implementation of guest_t.create_iface_iterator.
101 */
102 static iterator_t* create_iface_iterator(private_guest_t *this)
103 {
104 return this->ifaces->create_iterator(this->ifaces, TRUE);
105 }
106
107 /**
108 * write format string to a buffer, and advance buffer position
109 */
110 static char* write_arg(char **pos, size_t *left, char *format, ...)
111 {
112 size_t len;
113 char *res = NULL;
114 va_list args;
115
116 va_start(args, format);
117 len = vsnprintf(*pos, *left, format, args);
118 va_end(args);
119 if (len < *left)
120 {
121 res = *pos;
122 len++;
123 *pos += len + 1;
124 *left -= len + 1;
125 }
126 return res;
127 }
128
129 /**
130 * Implementation of guest_t.start.
131 */
132 static bool start(private_guest_t *this)
133 {
134 char buf[1024];
135 char cwd[512];
136 char *pos = buf;
137 char *args[16];
138 int i = 0;
139 size_t left = sizeof(buf);
140
141 args[i++] = this->kernel;
142 args[i++] = write_arg(&pos, &left, "root=/dev/root");
143 args[i++] = write_arg(&pos, &left, "rootfstype=hostfs");
144 args[i++] = write_arg(&pos, &left, "rootflags=%s/%s/%s",
145 getcwd(cwd, sizeof(cwd)), MOUNT_DIR, this->name);
146 args[i++] = write_arg(&pos, &left, "uml_dir=%s/%s", RUN_DIR, this->name);
147 args[i++] = write_arg(&pos, &left, "umid=%s", this->name);
148 args[i++] = write_arg(&pos, &left, "mem=%dM", this->mem);
149 /*args[i++] = write_arg(&pos, &left, "con=pts");*/
150 args[i++] = write_arg(&pos, &left, "con0=null,fd:%d", this->bootlog);
151 args[i++] = write_arg(&pos, &left, "con1=fd:0,fd:1");
152 args[i++] = write_arg(&pos, &left, "con3=null,null");
153 args[i++] = write_arg(&pos, &left, "con4=null,null");
154 args[i++] = write_arg(&pos, &left, "con5=null,null");
155 args[i++] = write_arg(&pos, &left, "con6=null,null");
156 args[i++] = NULL;
157
158 this->pid = fork();
159 switch (this->pid)
160 {
161 case 0: /* child, */
162 dup2(open("/dev/null", 0), 0);
163 dup2(open("/dev/null", 0), 1);
164 dup2(open("/dev/null", 0), 2);
165 execvp(args[0], args);
166 DBG1("starting UML kernel '%s' failed", args[0]);
167 exit(1);
168 case -1:
169 this->pid = 0;
170 return FALSE;
171 default:
172 break;
173 }
174 /* open mconsole */
175 snprintf(buf, sizeof(buf), "%s/%s/%s/mconsole", RUN_DIR, this->name, this->name);
176 this->mconsole = mconsole_create(buf);
177 if (this->mconsole == NULL)
178 {
179 DBG1("opening mconsole at '%s' failed, stopping guest", buf);
180 kill(this->pid, SIGINT);
181 this->pid = 0;
182 return FALSE;
183 }
184 return TRUE;
185 }
186
187 /**
188 * Implementation of guest_t.stop.
189 */
190 static void stop(private_guest_t *this)
191 {
192 if (this->pid)
193 {
194 kill(this->pid, SIGINT);
195 this->pid = 0;
196 }
197 }
198
199 /**
200 * Check if directory exists, create otherwise
201 */
202 static bool makedir(char *dir, char *name)
203 {
204 struct stat st;
205 char buf[256];
206 size_t len;
207
208 len = snprintf(buf, sizeof(buf), "%s/%s", dir, name);
209 if (len < 0 || len >= sizeof(buf))
210 {
211 return FALSE;
212 }
213 if (stat(buf, &st) != 0)
214 {
215 return mkdir(buf, S_IRWXU) == 0;
216 }
217 return S_ISDIR(st.st_mode);
218 }
219
220 /**
221 * umount the union filesystem
222 */
223 static bool umount_unionfs(char *name)
224 {
225 char cmd[128];
226 size_t len;
227
228 len = snprintf(cmd, sizeof(cmd), "fusermount -u %s/%s", MOUNT_DIR, name);
229 if (len < 0 || len >= sizeof(cmd))
230 {
231 return FALSE;
232 }
233 if (system(cmd) != 0)
234 {
235 DBG1("unmounting guest unionfs for %s failed", name);
236 return FALSE;
237 }
238 return TRUE;
239 }
240
241 /**
242 * mount the union filesystem
243 */
244 static bool mount_unionfs(char *name, char *master)
245 {
246 char cmd[256];
247 size_t len;
248
249 len = snprintf(cmd, sizeof(cmd), "unionfs %s/%s:%s %s/%s",
250 HOST_DIR, name, master, MOUNT_DIR, name);
251 if (len < 0 || len >= sizeof(cmd))
252 {
253 return FALSE;
254 }
255 if (system(cmd) != 0)
256 {
257 DBG1("mounting guest unionfs for %s using '%s' failed", name, cmd);
258 return FALSE;
259 }
260 return TRUE;
261 }
262
263 /**
264 * open logfile for boot messages
265 */
266 static int open_bootlog(char *name)
267 {
268 char blg[256];
269 size_t len;
270 int fd;
271
272 len = snprintf(blg, sizeof(blg), "%s/%s/boot.log", RUN_DIR, name);
273 if (len < 0 || len >= sizeof(blg))
274 {
275 return 1;
276 }
277 fd = open(blg, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
278 if (fd == -1)
279 {
280 DBG1("opening bootlog '%s' for %s failed, using stdout", blg, name);
281 return 1;
282 }
283 return fd;
284 }
285
286 /**
287 * Implementation of guest_t.destroy.
288 */
289 static void destroy(private_guest_t *this)
290 {
291 stop(this);
292 umount_unionfs(this->name);
293 this->ifaces->destroy_offset(this->ifaces, offsetof(iface_t, destroy));
294 DESTROY_IF(this->mconsole);
295 free(this->name);
296 free(this->kernel);
297 free(this->master);
298 free(this);
299 }
300
301 /**
302 * create the guest instance, including required dirs and mounts
303 */
304 guest_t *guest_create(char *name, char *kernel, char *master, int mem)
305 {
306 private_guest_t *this = malloc_thing(private_guest_t);
307
308 this->public.get_name = (void*)get_name;
309 this->public.create_iface = (iface_t*(*)(guest_t*,char*))create_iface;
310 this->public.create_iface_iterator = (iterator_t*(*)(guest_t*))create_iface_iterator;
311 this->public.start = (void*)start;
312 this->public.stop = (void*)stop;
313 this->public.destroy = (void*)destroy;
314
315 if (!makedir(HOST_DIR, name) || !makedir(MOUNT_DIR, name) ||
316 !makedir(RUN_DIR, name) || !mount_unionfs(name, master))
317 {
318 free(this);
319 return NULL;
320 }
321
322 this->name = strdup(name);
323 this->kernel = strdup(kernel);
324 this->master = strdup(master);
325 this->mem = mem;
326 this->pid = 0;
327 this->bootlog = open_bootlog(name);
328 this->mconsole = NULL;
329 this->ifaces = linked_list_create();
330
331 return &this->public;
332 }
333