/[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 295 - (hide annotations)
Fri Aug 10 12:12:28 2007 UTC (17 years, 3 months ago) by frodo
File MIME type: text/plain
File size: 8337 byte(s)
(Frodo) Commit to get from 1.0 to 1.1

Redid all the xauth code, updated the copyright file and the debian stuff for
compatibility with etch.

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 295 /* 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 frodo 290 #define QEMU "/usr/bin/qemu"
42     #define USER "qemu"
43    
44 frodo 295 /*
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 frodo 290 /* 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 frodo 295 fprintf(stderr, "Invalid value `%s' for TAPDEVNR parameter (use a number!)\n",argv[1]);
251 frodo 290 exit(1);
252     }
253    
254     /* Second parameter: VLAN */
255     vlan = strtol(argv[2],&ptr,0);
256     if (*ptr) {
257 frodo 295 fprintf(stderr, "Invalid value `%s' for VLAN parameter (use a number!)\n", argv[2]);
258 frodo 290 exit(1);
259     }
260    
261 frodo 295 /* 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 frodo 290 /* 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 frodo 295 /* Get userid data. */
288 frodo 290 userdata = getpwnam(USER);
289     if (!userdata) {
290     fprintf(stderr, "No user '%s'\n", USER);
291     exit(1);
292     }
293 frodo 295
294     /* Change to QEMU user */
295     if (setgroups(0, NULL)) {
296     perror("setgroups failed");
297 frodo 290 exit(1);
298     }
299 frodo 295 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 frodo 290
308     /* Clean the environment */
309 frodo 295 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 frodo 290
326     /* Create the right qemu invocation */
327     if (!strlen(argv[3]))
328     newargs[0] = QEMU;
329 frodo 295 else if (asprintf(&newargs[0],"%s-system-%s",QEMU,argv[3]) < 0) {
330     perror("Preparing qemu executable filename failed");
331     exit(1);
332     }
333 frodo 290 newargs[1] = "-net";
334 frodo 295 if (asprintf(&newargs[2],"nic,vlan=%d",vlan) < 0) {
335     perror("Preparing nic argument failed");
336     exit(1);
337     }
338 frodo 290 newargs[3] = "-net";
339 frodo 295 if (asprintf(&newargs[4],"tap,fd=%d,vlan=%d",fd,vlan) < 0) {
340     perror("Preparing tap argument failed");
341     exit(1);
342     }
343 frodo 290 for (i = 4; i <= argc; i++)
344     newargs[i+1] = argv[i];
345    
346     execvp(newargs[0], newargs);
347 frodo 295 perror('Execution of qemu failed');
348 frodo 290 exit(1);
349     }
350 frodo 295

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