… | |
… | |
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 | */ |
|
|
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 | |
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. */ |
39 | int main(int argc, char *argv[]) |
231 | int 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 | } |