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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 315 - (hide 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 frodo 290 /*
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 frodo 295 #include <sys/wait.h>
34     #include <errno.h>
35 frodo 290
36 frodo 314 /* Autogenerated by gengetopt */
37     #include "cmdline.h"
38    
39 frodo 295 /* 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 frodo 290 #define QEMU "/usr/bin/qemu"
45     #define USER "qemu"
46    
47 frodo 295 /*
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 frodo 314 perror("Constructing xauthority filename failed");
196 frodo 295 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 frodo 314 fprintf(stderr,"Child returned error\n");
223 frodo 295 exit(1);
224     }
225     }
226     }
227    
228    
229 frodo 290 /* 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 frodo 314 int fd,vlan,tapnr;
237     unsigned int i;
238     unsigned new_arg_count = 5;
239     struct gengetopt_args_info args_info;
240 frodo 290 char *display;
241 frodo 314 char *system;
242     char *topleft;
243 frodo 290
244 frodo 314 /* Ugly fix because gengetopt 2.18 does not have the `usage' option */
245     gengetopt_args_info_usage = "qemu-start [OPTIONS] -- [OPTIONS for QEMU]";
246 frodo 290
247 frodo 314 if (cmdline_parser(argc, argv, &args_info))
248 frodo 290 exit(1);
249    
250 frodo 314 /* 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 frodo 290 }
264    
265 frodo 314 /* 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 frodo 295 }
276    
277     /* Export the xauth data */
278     fix_xauthority();
279    
280 frodo 290 /* 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 frodo 295 /* Get userid data. */
298 frodo 290 userdata = getpwnam(USER);
299     if (!userdata) {
300     fprintf(stderr, "No user '%s'\n", USER);
301     exit(1);
302     }
303 frodo 295
304     /* Change to QEMU user */
305     if (setgroups(0, NULL)) {
306     perror("setgroups failed");
307 frodo 290 exit(1);
308     }
309 frodo 295 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 frodo 290
318     /* Clean the environment */
319 frodo 295 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 frodo 314 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 frodo 290
342     /* Create the right qemu invocation */
343 frodo 314 if (!args_info.system_given)
344 frodo 290 newargs[0] = QEMU;
345 frodo 314 else if (asprintf(&newargs[0],"%s-system-%s",QEMU,args_info.system_arg) < 0) {
346 frodo 295 perror("Preparing qemu executable filename failed");
347     exit(1);
348     }
349 frodo 290 newargs[1] = "-net";
350 frodo 295 if (asprintf(&newargs[2],"nic,vlan=%d",vlan) < 0) {
351     perror("Preparing nic argument failed");
352     exit(1);
353     }
354 frodo 314 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 frodo 290 newargs[3] = "-net";
365 frodo 295 if (asprintf(&newargs[4],"tap,fd=%d,vlan=%d",fd,vlan) < 0) {
366     perror("Preparing tap argument failed");
367     exit(1);
368     }
369 frodo 314 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 frodo 290
375     execvp(newargs[0], newargs);
376 frodo 301 perror("Execution of qemu failed");
377 frodo 290 exit(1);
378     }

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