/[public]/qemu-start/trunk/qemu-start.c
ViewVC logotype

Contents of /qemu-start/trunk/qemu-start.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 301 - (show annotations)
Sat Aug 11 11:47:20 2007 UTC (16 years, 7 months ago) by frodo
File MIME type: text/plain
File size: 8337 byte(s)
(Frodo) Version 1.1.1. Fixed stupid compilation bug. Updated Makefile.

1 /*
2 qemu-start.c - Part of qemu-start, a package to start qemu nicely.
3 Copyright (c) 2006 Frodo Looijaard <frodo@frodo.looijaard.name>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19
20 #define _GNU_SOURCE /* asprintf */
21 #include <stdio.h>
22 #include <string.h>
23 #include <sys/types.h>
24 #include <pwd.h>
25 #include <grp.h>
26 #include <sys/stat.h>
27 #include <sys/ioctl.h>
28 #include <fcntl.h>
29 #include <unistd.h>
30 #include <stdlib.h>
31 #include <net/if.h>
32 #include <linux/if_tun.h>
33 #include <sys/wait.h>
34 #include <errno.h>
35
36 /* This define is present in recent glibc, it is not available on sarge */
37 #ifndef HOST_NAME_MAX
38 #define HOST_NAME_MAX 64
39 #endif
40
41 #define QEMU "/usr/bin/qemu"
42 #define USER "qemu"
43
44 /*
45 * This function merges the xauthority data of the current X context
46 * into the QEMU user's .Xauthority file.
47 */
48 void fix_xauthority(void) {
49 struct passwd *userdata;
50 int pfd[2];
51 char *xauthfile;
52 char *display;
53 char *newdisplay;
54 char *hostname;
55 // int status1, status2;
56 int status,pid;
57 pid_t cpid1, cpid2;
58
59 if (pipe(pfd)) {
60 perror("pipe failed");
61 exit(1);
62 }
63
64 cpid1 = fork();
65 if (cpid1 < 0) {
66 perror("first fork failed");
67 exit(1);
68 }
69
70 /* Child: will execute the xauth extract */
71 if (cpid1 == 0) {
72 /* Child may run as original process starter */
73 if (setgroups(0, NULL)) {
74 perror("setgroups failed");
75 exit(1);
76 }
77 if (setgid(getgid())) {
78 perror("setgid failed");
79 exit(1);
80 }
81 if (setuid(getuid())) {
82 perror("setuid failed");
83 exit(1);
84 }
85
86 /* Write side of pipe corresponds to stdout */
87 if (dup2(pfd[1],1) < 0) {
88 perror("dup2 failed");
89 exit(1);
90 }
91 if (close(pfd[0])) {
92 perror("close of pfd[0] failed");
93 exit(1);
94 }
95 if (close(pfd[1])) {
96 perror("close of pfd[1] failed");
97 exit(1);
98 }
99
100 userdata = getpwuid(getuid());
101 if (!userdata) {
102 fprintf(stderr, "No user '%s'\n", USER);
103 exit(1);
104 }
105
106
107 /* Get xauthority file to use */
108 if (! (xauthfile = getenv("XAUTHORITY"))) {
109 if (asprintf(&xauthfile,"%s/.Xauthority",userdata->pw_dir) < 1) {
110 perror("Constructing xauthority filename failed");
111 exit(1);
112 }
113 }
114
115 /* Get display */
116 if (! (display = getenv("DISPLAY"))) {
117 perror("No DISPLAY variable found\n");
118 exit(1);
119 }
120
121 /* Substitution: if display starts with `localhost', substitute
122 `localhost' by `HOSTNAME/unix'. */
123 if (strstr(display,"localhost") == display) {
124 hostname = malloc(HOST_NAME_MAX+1);
125 if (gethostname(hostname,HOST_NAME_MAX+1)) {
126 perror("Getting hostname failed");
127 exit(1);
128 }
129
130 if (!(newdisplay = malloc(strlen(hostname) + 5 + strlen(display)-9 + 1))) {
131 perror("Allocating new display failed");
132 exit(1);
133 }
134 strcpy(newdisplay,hostname);
135 strcpy(newdisplay + strlen(hostname),"/unix");
136 strcpy(newdisplay + strlen(hostname) + 5, display + 9);
137 display = newdisplay;
138 } else if (! (display = strdup(display))) {
139 perror("strdup DISPLAY failed");
140 exit(1);
141 }
142
143 execl("/usr/bin/X11/xauth","/usr/bin/X11/xauth","-f",xauthfile,"extract","-",display,NULL);
144 exit(1);
145 }
146
147
148 cpid2 = fork();
149 if (cpid2 < 0) {
150 perror("second fork failed");
151 exit(1);
152 }
153
154 /* Child: will execute the xauth merge */
155 if (cpid2 == 0) {
156 /* Get userid data. */
157 userdata = getpwnam(USER);
158 if (!userdata) {
159 fprintf(stderr, "No user '%s'\n", USER);
160 exit(1);
161 }
162
163 if (setgroups(0, NULL)) {
164 perror("setgroups failed");
165 exit(1);
166 }
167
168 /* Child may run as qemu user */
169 if (setgid(userdata->pw_gid)) {
170 perror("setgid failed");
171 exit(1);
172 }
173 if (setuid(userdata->pw_uid)) {
174 perror("setuid failed");
175 exit(1);
176 }
177 /* Read side of pipe is stdin */
178 if (dup2(pfd[0],0) < 0) {
179 perror("dup2 failed");
180 exit(1);
181 }
182 if (close(pfd[0])) {
183 perror("close of pfd[0] failed");
184 exit(1);
185 }
186 if (close(pfd[1])) {
187 perror("close of pfd[1] failed");
188 exit(1);
189 }
190
191 if (asprintf(&xauthfile,"%s/.Xauthority",userdata->pw_dir) < 1) {
192 perror("Constructing xauthority filename failed");
193 exit(1);
194 }
195
196 execl("/usr/bin/X11/xauth","/usr/bin/X11/xauth","-f",xauthfile,"merge","-",NULL);
197 exit(1);
198 }
199
200 if (close(pfd[0])) {
201 perror("close of pfd[0] failed");
202 exit(1);
203 }
204 if (close(pfd[1])) {
205 perror("close of pfd[1] failed");
206 exit(1);
207 }
208
209 while(1) {
210
211 pid = wait(&status);
212 if ((pid < 1) && (errno == ECHILD))
213 break;
214 if (pid < 1) {
215 perror("wait failed");
216 exit(1);
217 }
218 if (! WIFEXITED(status) || WEXITSTATUS(status)) {
219 fprintf(stderr,"Child returned error");
220 exit(1);
221 }
222 }
223 }
224
225
226 /* Tiny code to open tap/tun device, and hand the fd to qemu.
227 Run as root, drops to given user. */
228 int main(int argc, char *argv[])
229 {
230 struct ifreq ifr;
231 struct passwd *userdata;
232 char *newargs[argc + 2];
233 int i,fd,vlan,tapnr;
234 char *ptr;
235 char *display;
236
237 /* Check parameters */
238 if (argc < 4) {
239 fprintf(stderr, "Usage: qemu-start TAPDEVNR VLAN SYSTEM <qemu options>...\n");
240 fprintf(stderr, " A device tapTAPDEVNR must have been configured.\n");
241 fprintf(stderr, " VLAN is the Virtual LAN number (just use 0 if unsure).\n");
242 fprintf(stderr, " SYSTEM is the system to emulate. Use \"\" for the current architecture\n");
243 fprintf(stderr, " (uses /usr/bin/qemm); else a binary /usr/bin/qemu-system-SYSTEM is used.\n");
244 exit(1);
245 }
246
247 /* First parameter: TAPDEVNR */
248 tapnr = strtol(argv[1],&ptr,0);
249 if (*ptr) {
250 fprintf(stderr, "Invalid value `%s' for TAPDEVNR parameter (use a number!)\n",argv[1]);
251 exit(1);
252 }
253
254 /* Second parameter: VLAN */
255 vlan = strtol(argv[2],&ptr,0);
256 if (*ptr) {
257 fprintf(stderr, "Invalid value `%s' for VLAN parameter (use a number!)\n", argv[2]);
258 exit(1);
259 }
260
261 /* Third parameter: System type */
262 if (index(argv[3],'/')) {
263 fprintf(stderr, "Invalid value `%s' for system (may not contain a slash character)\n", argv[3]);
264 exit(1);
265 }
266
267 /* Export the xauth data */
268 fix_xauthority();
269
270 /* Open /dev/net/tun */
271 fd = open("/dev/net/tun", O_RDWR);
272 if (fd < 0) {
273 perror("Could not open /dev/net/tun");
274 exit(1);
275 }
276
277 /* Bind to the right tap device */
278 memset(&ifr, 0, sizeof(ifr));
279 ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
280 snprintf(ifr.ifr_name,IFNAMSIZ,"tap%d",tapnr);
281
282 if (ioctl(fd, TUNSETIFF, (void *) &ifr) != 0) {
283 perror("Could not get tap device");
284 exit(1);
285 }
286
287 /* Get userid data. */
288 userdata = getpwnam(USER);
289 if (!userdata) {
290 fprintf(stderr, "No user '%s'\n", USER);
291 exit(1);
292 }
293
294 /* Change to QEMU user */
295 if (setgroups(0, NULL)) {
296 perror("setgroups failed");
297 exit(1);
298 }
299 if (setgid(userdata->pw_gid)) {
300 perror("setgid failed");
301 exit(1);
302 }
303 if (setuid(userdata->pw_uid)) {
304 perror("setuid failed");
305 exit(1);
306 }
307
308 /* Clean the environment */
309 if(!(display=getenv("DISPLAY"))) {
310 perror("Getting DISPLAY failed");
311 exit(1);
312 }
313 if(clearenv()) {
314 perror("Clearing environment failed");
315 exit(1);
316 }
317 if(setenv("HOME",userdata->pw_dir,1) ||
318 setenv("DISPLAY",display,1) ||
319 setenv("PATH","/usr/bin:/bin:/usr/local/bin",1) ||
320 setenv("LOGNAME",userdata->pw_name,1) ||
321 setenv("USER",userdata->pw_name,1)) {
322 perror("Setting environment failed");
323 exit(1);
324 }
325
326 /* Create the right qemu invocation */
327 if (!strlen(argv[3]))
328 newargs[0] = QEMU;
329 else if (asprintf(&newargs[0],"%s-system-%s",QEMU,argv[3]) < 0) {
330 perror("Preparing qemu executable filename failed");
331 exit(1);
332 }
333 newargs[1] = "-net";
334 if (asprintf(&newargs[2],"nic,vlan=%d",vlan) < 0) {
335 perror("Preparing nic argument failed");
336 exit(1);
337 }
338 newargs[3] = "-net";
339 if (asprintf(&newargs[4],"tap,fd=%d,vlan=%d",fd,vlan) < 0) {
340 perror("Preparing tap argument failed");
341 exit(1);
342 }
343 for (i = 4; i <= argc; i++)
344 newargs[i+1] = argv[i];
345
346 execvp(newargs[0], newargs);
347 perror("Execution of qemu failed");
348 exit(1);
349 }
350

frodo@frodo.looijaard.name
ViewVC Help
Powered by ViewVC 1.1.26