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

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

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

Revision 290 Revision 315
28#include <fcntl.h> 28#include <fcntl.h>
29#include <unistd.h> 29#include <unistd.h>
30#include <stdlib.h> 30#include <stdlib.h>
31#include <net/if.h> 31#include <net/if.h>
32#include <linux/if_tun.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
33 43
34#define QEMU "/usr/bin/qemu" 44#define QEMU "/usr/bin/qemu"
35#define USER "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 */
51void 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
36 228
37/* Tiny code to open tap/tun device, and hand the fd to qemu. 229/* Tiny code to open tap/tun device, and hand the fd to qemu.
38 Run as root, drops to given user. */ 230 Run as root, drops to given user. */
39int main(int argc, char *argv[]) 231int main(int argc, char *argv[])
40{ 232{
41 struct ifreq ifr; 233 struct ifreq ifr;
42 struct passwd *userdata; 234 struct passwd *userdata;
43 char *newargs[argc + 2]; 235 char *newargs[argc + 2];
44 int i,fd,vlan,tapnr; 236 int fd,vlan,tapnr;
45 char *ptr; 237 unsigned int i;
46 char *command; 238 unsigned new_arg_count = 5;
239 struct gengetopt_args_info args_info;
47 char *display; 240 char *display;
241 char *system;
242 char *topleft;
48 243
49 /* Check parameters */ 244 /* Ugly fix because gengetopt 2.18 does not have the `usage' option */
50 if (argc < 4) { 245 gengetopt_args_info_usage = "qemu-start [OPTIONS] -- [OPTIONS for QEMU]";
51 fprintf(stderr, "Usage: qemu-start TAPDEVNR VLAN SYSTEM <qemu options>...\n"); 246
52 fprintf(stderr, " A device tapTAPDEVNR must have been configured.\n"); 247 if (cmdline_parser(argc, argv, &args_info))
53 fprintf(stderr, " VLAN is the Virtual LAN number (just use 0 if unsure).\n"); 248 exit(1);
54 fprintf(stderr, " SYSTEM is the system to emulate. Use \"\" for the current architecture\n"); 249
55 fprintf(stderr, " (uses /usr/bin/qemm); else a binary /usr/bin/qemu-system-SYSTEM is used.\n"); 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);
56 exit(1); 260 exit(1);
57 } 261 }
262 asprintf(&system, "%s", args_info.system_arg);
263 }
58 264
59 /* First parameter: TAPDEVNR */ 265 /* parameters: Window location */
60 tapnr = strtol(argv[1],&ptr,0); 266 if (args_info.window_left_given || args_info.window_top_given) {
61 if (*ptr) { 267 if (!(args_info.window_left_given && args_info.window_top_given)) {
62 fprintf(stderr, "Invalid value for TAPDEVNR parameter (use a number!)\n"); 268 fprintf(stderr,("If either window-left (x) or window-top (y) are given, then both must be given\n"));
63 exit(1); 269 exit(1);
64 } 270 }
65 271 if (asprintf(&topleft, "%d,%d",args_info.window_left_arg,args_info.window_top_arg) < 0) {
66 /* Second parameter: VLAN */ 272 perror("Preparing top-left environment value string failed");
67 vlan = strtol(argv[2],&ptr,0);
68 if (*ptr) {
69 fprintf(stderr, "Invalid value for VLAN parameter (use a number!)\n");
70 exit(1); 273 exit(1);
71 } 274 }
275 }
276
277 /* Export the xauth data */
278 fix_xauthority();
72 279
73 /* Open /dev/net/tun */ 280 /* Open /dev/net/tun */
74 fd = open("/dev/net/tun", O_RDWR); 281 fd = open("/dev/net/tun", O_RDWR);
75 if (fd < 0) { 282 if (fd < 0) {
76 perror("Could not open /dev/net/tun"); 283 perror("Could not open /dev/net/tun");
85 if (ioctl(fd, TUNSETIFF, (void *) &ifr) != 0) { 292 if (ioctl(fd, TUNSETIFF, (void *) &ifr) != 0) {
86 perror("Could not get tap device"); 293 perror("Could not get tap device");
87 exit(1); 294 exit(1);
88 } 295 }
89 296
90 /* Fix X-display */ 297 /* Get userid data. */
91 userdata = getpwuid(getuid());
92 setuid(0);
93 setgid(0);
94 asprintf(&command,"xauth -f ~%s/.Xauthority extract - $(echo $DISPLAY|sed \"s,^localhost,$(hostname)/unix,\") | xauth -f ~%s/.Xauthority merge -; chown %s: ~%s/.Xauthority",userdata->pw_name,USER,USER,USER);
95 system(command);
96
97 /* Set correct userid. */
98 userdata = getpwnam(USER); 298 userdata = getpwnam(USER);
99 if (!userdata) { 299 if (!userdata) {
100 fprintf(stderr, "No user '%s'\n", USER); 300 fprintf(stderr, "No user '%s'\n", USER);
101 exit(1); 301 exit(1);
102 } 302 }
303
304 /* Change to QEMU user */
103 setgroups(0, NULL); 305 if (setgroups(0, NULL)) {
306 perror("setgroups failed");
307 exit(1);
308 }
104 setgid(userdata->pw_gid); 309 if (setgid(userdata->pw_gid)) {
310 perror("setgid failed");
311 exit(1);
312 }
105 if (setuid(userdata->pw_uid) != 0) { 313 if (setuid(userdata->pw_uid)) {
106 perror("setting uid"); 314 perror("setuid failed");
107 exit(1); 315 exit(1);
108 } 316 }
109 317
110 /* Clean the environment */ 318 /* Clean the environment */
111 display=getenv("DISPLAY"); 319 if(!(display=getenv("DISPLAY"))) {
320 perror("Getting DISPLAY failed");
321 exit(1);
322 }
112 clearenv(); 323 if(clearenv()) {
324 perror("Clearing environment failed");
325 exit(1);
326 }
113 setenv("HOME",userdata->pw_dir,1); 327 if(setenv("HOME",userdata->pw_dir,1) ||
114 setenv("DISPLAY",display,1); 328 setenv("DISPLAY",display,1) ||
115 setenv("PATH","/usr/bin:/bin:/usr/local/bin",1); 329 setenv("PATH","/usr/bin:/bin:/usr/local/bin",1) ||
116 setenv("LOGNAME",userdata->pw_name,1); 330 setenv("LOGNAME",userdata->pw_name,1) ||
117 setenv("USER",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 }
118 341
119 /* Create the right qemu invocation */ 342 /* Create the right qemu invocation */
120 if (!strlen(argv[3])) 343 if (!args_info.system_given)
121 newargs[0] = QEMU; 344 newargs[0] = QEMU;
122 else 345 else if (asprintf(&newargs[0],"%s-system-%s",QEMU,args_info.system_arg) < 0) {
123 asprintf(&newargs[0],"%s-system-%s",QEMU,argv[3]); 346 perror("Preparing qemu executable filename failed");
347 exit(1);
348 }
124 newargs[1] = "-net"; 349 newargs[1] = "-net";
125 asprintf(&newargs[2],"nic,vlan=%d",vlan); 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 }
126 newargs[3] = "-net"; 364 newargs[3] = "-net";
127 asprintf(&newargs[4],"tap,fd=%d,vlan=%d",fd,vlan); 365 if (asprintf(&newargs[4],"tap,fd=%d,vlan=%d",fd,vlan) < 0) {
128 for (i = 4; i <= argc; i++) 366 perror("Preparing tap argument failed");
129 newargs[i+1] = argv[i]; 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;
130 374
131 execvp(newargs[0], newargs); 375 execvp(newargs[0], newargs);
376 perror("Execution of qemu failed");
132 exit(1); 377 exit(1);
133} 378}

Legend:
Removed from v.290  
changed lines
  Added in v.315

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