/[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 315 - (show annotations)
Wed Apr 9 19:00:45 2008 UTC (15 years, 11 months ago) by frodo
File MIME type: text/plain
File size: 9190 byte(s)
(Frodo) Removed some left-over debugging code

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 /* Autogenerated by gengetopt */
37 #include "cmdline.h"
38
39 /* This define is present in recent glibc, it is not available on sarge */
40 #ifndef HOST_NAME_MAX
41 #define HOST_NAME_MAX 64
42 #endif
43
44 #define QEMU "/usr/bin/qemu"
45 #define USER "qemu"
46
47 /*
48 * This function merges the xauthority data of the current X context
49 * into the QEMU user's .Xauthority file.
50 */
51 void fix_xauthority(void) {
52 struct passwd *userdata;
53 int pfd[2];
54 char *xauthfile;
55 char *display;
56 char *newdisplay;
57 char *hostname;
58 // int status1, status2;
59 int status,pid;
60 pid_t cpid1, cpid2;
61
62 if (pipe(pfd)) {
63 perror("pipe failed");
64 exit(1);
65 }
66
67 cpid1 = fork();
68 if (cpid1 < 0) {
69 perror("first fork failed");
70 exit(1);
71 }
72
73 /* Child: will execute the xauth extract */
74 if (cpid1 == 0) {
75 /* Child may run as original process starter */
76 if (setgroups(0, NULL)) {
77 perror("setgroups failed");
78 exit(1);
79 }
80 if (setgid(getgid())) {
81 perror("setgid failed");
82 exit(1);
83 }
84 if (setuid(getuid())) {
85 perror("setuid failed");
86 exit(1);
87 }
88
89 /* Write side of pipe corresponds to stdout */
90 if (dup2(pfd[1],1) < 0) {
91 perror("dup2 failed");
92 exit(1);
93 }
94 if (close(pfd[0])) {
95 perror("close of pfd[0] failed");
96 exit(1);
97 }
98 if (close(pfd[1])) {
99 perror("close of pfd[1] failed");
100 exit(1);
101 }
102
103 userdata = getpwuid(getuid());
104 if (!userdata) {
105 fprintf(stderr, "No user '%s'\n", USER);
106 exit(1);
107 }
108
109
110 /* Get xauthority file to use */
111 if (! (xauthfile = getenv("XAUTHORITY"))) {
112 if (asprintf(&xauthfile,"%s/.Xauthority",userdata->pw_dir) < 1) {
113 perror("Constructing xauthority filename failed");
114 exit(1);
115 }
116 }
117
118 /* Get display */
119 if (! (display = getenv("DISPLAY"))) {
120 perror("No DISPLAY variable found\n");
121 exit(1);
122 }
123
124 /* Substitution: if display starts with `localhost', substitute
125 `localhost' by `HOSTNAME/unix'. */
126 if (strstr(display,"localhost") == display) {
127 hostname = malloc(HOST_NAME_MAX+1);
128 if (gethostname(hostname,HOST_NAME_MAX+1)) {
129 perror("Getting hostname failed");
130 exit(1);
131 }
132
133 if (!(newdisplay = malloc(strlen(hostname) + 5 + strlen(display)-9 + 1))) {
134 perror("Allocating new display failed");
135 exit(1);
136 }
137 strcpy(newdisplay,hostname);
138 strcpy(newdisplay + strlen(hostname),"/unix");
139 strcpy(newdisplay + strlen(hostname) + 5, display + 9);
140 display = newdisplay;
141 } else if (! (display = strdup(display))) {
142 perror("strdup DISPLAY failed");
143 exit(1);
144 }
145
146 execl("/usr/bin/X11/xauth","/usr/bin/X11/xauth","-f",xauthfile,"extract","-",display,NULL);
147 exit(1);
148 }
149
150
151 cpid2 = fork();
152 if (cpid2 < 0) {
153 perror("second fork failed");
154 exit(1);
155 }
156
157 /* Child: will execute the xauth merge */
158 if (cpid2 == 0) {
159 /* Get userid data. */
160 userdata = getpwnam(USER);
161 if (!userdata) {
162 fprintf(stderr, "No user '%s'\n", USER);
163 exit(1);
164 }
165
166 if (setgroups(0, NULL)) {
167 perror("setgroups failed");
168 exit(1);
169 }
170
171 /* Child may run as qemu user */
172 if (setgid(userdata->pw_gid)) {
173 perror("setgid failed");
174 exit(1);
175 }
176 if (setuid(userdata->pw_uid)) {
177 perror("setuid failed");
178 exit(1);
179 }
180 /* Read side of pipe is stdin */
181 if (dup2(pfd[0],0) < 0) {
182 perror("dup2 failed");
183 exit(1);
184 }
185 if (close(pfd[0])) {
186 perror("close of pfd[0] failed");
187 exit(1);
188 }
189 if (close(pfd[1])) {
190 perror("close of pfd[1] failed");
191 exit(1);
192 }
193
194 if (asprintf(&xauthfile,"%s/.Xauthority",userdata->pw_dir) < 1) {
195 perror("Constructing xauthority filename failed");
196 exit(1);
197 }
198
199 execl("/usr/bin/X11/xauth","/usr/bin/X11/xauth","-f",xauthfile,"merge","-",NULL);
200 exit(1);
201 }
202
203 if (close(pfd[0])) {
204 perror("close of pfd[0] failed");
205 exit(1);
206 }
207 if (close(pfd[1])) {
208 perror("close of pfd[1] failed");
209 exit(1);
210 }
211
212 while(1) {
213
214 pid = wait(&status);
215 if ((pid < 1) && (errno == ECHILD))
216 break;
217 if (pid < 1) {
218 perror("wait failed");
219 exit(1);
220 }
221 if (! WIFEXITED(status) || WEXITSTATUS(status)) {
222 fprintf(stderr,"Child returned error\n");
223 exit(1);
224 }
225 }
226 }
227
228
229 /* Tiny code to open tap/tun device, and hand the fd to qemu.
230 Run as root, drops to given user. */
231 int main(int argc, char *argv[])
232 {
233 struct ifreq ifr;
234 struct passwd *userdata;
235 char *newargs[argc + 2];
236 int fd,vlan,tapnr;
237 unsigned int i;
238 unsigned new_arg_count = 5;
239 struct gengetopt_args_info args_info;
240 char *display;
241 char *system;
242 char *topleft;
243
244 /* Ugly fix because gengetopt 2.18 does not have the `usage' option */
245 gengetopt_args_info_usage = "qemu-start [OPTIONS] -- [OPTIONS for QEMU]";
246
247 if (cmdline_parser(argc, argv, &args_info))
248 exit(1);
249
250 /* parameter: TAPDEVNR */
251 tapnr = args_info.tapnr_arg;
252
253 /* parameter: VLAN */
254 vlan = args_info.vlan_arg;
255
256 /* parameter: System type */
257 if (args_info.system_given) {
258 if (index(args_info.system_arg,'/')) {
259 fprintf(stderr, "Invalid value `%s' for system (may not contain a slash character)\n", args_info.system_arg);
260 exit(1);
261 }
262 asprintf(&system, "%s", args_info.system_arg);
263 }
264
265 /* parameters: Window location */
266 if (args_info.window_left_given || args_info.window_top_given) {
267 if (!(args_info.window_left_given && args_info.window_top_given)) {
268 fprintf(stderr,("If either window-left (x) or window-top (y) are given, then both must be given\n"));
269 exit(1);
270 }
271 if (asprintf(&topleft, "%d,%d",args_info.window_left_arg,args_info.window_top_arg) < 0) {
272 perror("Preparing top-left environment value string failed");
273 exit(1);
274 }
275 }
276
277 /* Export the xauth data */
278 fix_xauthority();
279
280 /* Open /dev/net/tun */
281 fd = open("/dev/net/tun", O_RDWR);
282 if (fd < 0) {
283 perror("Could not open /dev/net/tun");
284 exit(1);
285 }
286
287 /* Bind to the right tap device */
288 memset(&ifr, 0, sizeof(ifr));
289 ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
290 snprintf(ifr.ifr_name,IFNAMSIZ,"tap%d",tapnr);
291
292 if (ioctl(fd, TUNSETIFF, (void *) &ifr) != 0) {
293 perror("Could not get tap device");
294 exit(1);
295 }
296
297 /* Get userid data. */
298 userdata = getpwnam(USER);
299 if (!userdata) {
300 fprintf(stderr, "No user '%s'\n", USER);
301 exit(1);
302 }
303
304 /* Change to QEMU user */
305 if (setgroups(0, NULL)) {
306 perror("setgroups failed");
307 exit(1);
308 }
309 if (setgid(userdata->pw_gid)) {
310 perror("setgid failed");
311 exit(1);
312 }
313 if (setuid(userdata->pw_uid)) {
314 perror("setuid failed");
315 exit(1);
316 }
317
318 /* Clean the environment */
319 if(!(display=getenv("DISPLAY"))) {
320 perror("Getting DISPLAY failed");
321 exit(1);
322 }
323 if(clearenv()) {
324 perror("Clearing environment failed");
325 exit(1);
326 }
327 if(setenv("HOME",userdata->pw_dir,1) ||
328 setenv("DISPLAY",display,1) ||
329 setenv("PATH","/usr/bin:/bin:/usr/local/bin",1) ||
330 setenv("LOGNAME",userdata->pw_name,1) ||
331 setenv("USER",userdata->pw_name,1)) {
332 perror("Setting environment failed");
333 exit(1);
334 }
335 if (args_info.window_left_given || args_info.window_top_given) {
336 if (setenv("SDL_VIDEO_WINDOW_POS", topleft, 1)) {
337 perror("Setting top-left environment value failed");
338 exit(1);
339 }
340 }
341
342 /* Create the right qemu invocation */
343 if (!args_info.system_given)
344 newargs[0] = QEMU;
345 else if (asprintf(&newargs[0],"%s-system-%s",QEMU,args_info.system_arg) < 0) {
346 perror("Preparing qemu executable filename failed");
347 exit(1);
348 }
349 newargs[1] = "-net";
350 if (asprintf(&newargs[2],"nic,vlan=%d",vlan) < 0) {
351 perror("Preparing nic argument failed");
352 exit(1);
353 }
354 if (args_info.macaddr_given) {
355 if (asprintf(&newargs[2], "%s,mac=%s", newargs[2], args_info.macaddr_arg) < 0) {
356 perror("Adding macaddress to nic argument failed");
357 }
358 }
359 if (args_info.nic_model_given) {
360 if (asprintf(&newargs[2], "%s,model=%s", newargs[2], args_info.nic_model_arg) < 0) {
361 perror("Adding nic model to nic argument failed");
362 }
363 }
364 newargs[3] = "-net";
365 if (asprintf(&newargs[4],"tap,fd=%d,vlan=%d",fd,vlan) < 0) {
366 perror("Preparing tap argument failed");
367 exit(1);
368 }
369 for (i = 0; i < args_info.inputs_num; i++) {
370 newargs[i+5] = args_info.inputs[i];
371 new_arg_count++;
372 }
373 newargs[new_arg_count] = NULL;
374
375 execvp(newargs[0], newargs);
376 perror("Execution of qemu failed");
377 exit(1);
378 }

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