… | |
… | |
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 | /* 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 |
33 | |
40 | |
34 | #define QEMU "/usr/bin/qemu" |
41 | #define QEMU "/usr/bin/qemu" |
35 | #define USER "qemu" |
42 | #define USER "qemu" |
|
|
43 | |
|
|
44 | /* |
|
|
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 | |
36 | |
225 | |
37 | /* Tiny code to open tap/tun device, and hand the fd to qemu. |
226 | /* Tiny code to open tap/tun device, and hand the fd to qemu. |
38 | Run as root, drops to given user. */ |
227 | Run as root, drops to given user. */ |
39 | int main(int argc, char *argv[]) |
228 | int main(int argc, char *argv[]) |
40 | { |
229 | { |
41 | struct ifreq ifr; |
230 | struct ifreq ifr; |
42 | struct passwd *userdata; |
231 | struct passwd *userdata; |
43 | char *newargs[argc + 2]; |
232 | char *newargs[argc + 2]; |
44 | int i,fd,vlan,tapnr; |
233 | int i,fd,vlan,tapnr; |
45 | char *ptr; |
234 | char *ptr; |
46 | char *command; |
|
|
47 | char *display; |
235 | char *display; |
48 | char *xauthfile; |
|
|
49 | |
236 | |
50 | /* Check parameters */ |
237 | /* Check parameters */ |
51 | if (argc < 4) { |
238 | if (argc < 4) { |
52 | fprintf(stderr, "Usage: qemu-start TAPDEVNR VLAN SYSTEM <qemu options>...\n"); |
239 | fprintf(stderr, "Usage: qemu-start TAPDEVNR VLAN SYSTEM <qemu options>...\n"); |
53 | fprintf(stderr, " A device tapTAPDEVNR must have been configured.\n"); |
240 | fprintf(stderr, " A device tapTAPDEVNR must have been configured.\n"); |
… | |
… | |
58 | } |
245 | } |
59 | |
246 | |
60 | /* First parameter: TAPDEVNR */ |
247 | /* First parameter: TAPDEVNR */ |
61 | tapnr = strtol(argv[1],&ptr,0); |
248 | tapnr = strtol(argv[1],&ptr,0); |
62 | if (*ptr) { |
249 | if (*ptr) { |
63 | fprintf(stderr, "Invalid value for TAPDEVNR parameter (use a number!)\n"); |
250 | fprintf(stderr, "Invalid value `%s' for TAPDEVNR parameter (use a number!)\n",argv[1]); |
64 | exit(1); |
251 | exit(1); |
65 | } |
252 | } |
66 | |
253 | |
67 | /* Second parameter: VLAN */ |
254 | /* Second parameter: VLAN */ |
68 | vlan = strtol(argv[2],&ptr,0); |
255 | vlan = strtol(argv[2],&ptr,0); |
69 | if (*ptr) { |
256 | if (*ptr) { |
70 | fprintf(stderr, "Invalid value for VLAN parameter (use a number!)\n"); |
257 | fprintf(stderr, "Invalid value `%s' for VLAN parameter (use a number!)\n", argv[2]); |
71 | exit(1); |
258 | exit(1); |
72 | } |
259 | } |
|
|
260 | |
|
|
261 | /* 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(); |
73 | |
269 | |
74 | /* Open /dev/net/tun */ |
270 | /* Open /dev/net/tun */ |
75 | fd = open("/dev/net/tun", O_RDWR); |
271 | fd = open("/dev/net/tun", O_RDWR); |
76 | if (fd < 0) { |
272 | if (fd < 0) { |
77 | perror("Could not open /dev/net/tun"); |
273 | perror("Could not open /dev/net/tun"); |
… | |
… | |
86 | if (ioctl(fd, TUNSETIFF, (void *) &ifr) != 0) { |
282 | if (ioctl(fd, TUNSETIFF, (void *) &ifr) != 0) { |
87 | perror("Could not get tap device"); |
283 | perror("Could not get tap device"); |
88 | exit(1); |
284 | exit(1); |
89 | } |
285 | } |
90 | |
286 | |
91 | /* Fix X-display */ |
287 | /* Get userid data. */ |
92 | userdata = getpwuid(getuid()); |
|
|
93 | if (! (xauthfile = getenv("XAUTHORITY"))) |
|
|
94 | asprintf(&xauthfile,"~%s/.Xauthority",userdata->pw_name); |
|
|
95 | setuid(0); |
|
|
96 | setgid(0); |
|
|
97 | asprintf(&command,"xauth -f %s extract - $(echo $DISPLAY|sed \"s,^localhost,$(hostname)/unix,\") | xauth -f ~%s/.Xauthority merge -; chown %s: ~%s/.Xauthority",xauthfile,USER,USER,USER); |
|
|
98 | fprintf(stderr,"%s\n",command); |
|
|
99 | system(command); |
|
|
100 | |
|
|
101 | /* Set correct userid. */ |
|
|
102 | userdata = getpwnam(USER); |
288 | userdata = getpwnam(USER); |
103 | if (!userdata) { |
289 | if (!userdata) { |
104 | fprintf(stderr, "No user '%s'\n", USER); |
290 | fprintf(stderr, "No user '%s'\n", USER); |
105 | exit(1); |
291 | exit(1); |
106 | } |
292 | } |
|
|
293 | |
|
|
294 | /* Change to QEMU user */ |
107 | setgroups(0, NULL); |
295 | if (setgroups(0, NULL)) { |
|
|
296 | perror("setgroups failed"); |
|
|
297 | exit(1); |
|
|
298 | } |
108 | setgid(userdata->pw_gid); |
299 | if (setgid(userdata->pw_gid)) { |
|
|
300 | perror("setgid failed"); |
|
|
301 | exit(1); |
|
|
302 | } |
109 | if (setuid(userdata->pw_uid) != 0) { |
303 | if (setuid(userdata->pw_uid)) { |
110 | perror("setting uid"); |
304 | perror("setuid failed"); |
111 | exit(1); |
305 | exit(1); |
112 | } |
306 | } |
113 | |
307 | |
114 | /* Clean the environment */ |
308 | /* Clean the environment */ |
115 | display=getenv("DISPLAY"); |
309 | if(!(display=getenv("DISPLAY"))) { |
|
|
310 | perror("Getting DISPLAY failed"); |
|
|
311 | exit(1); |
|
|
312 | } |
116 | clearenv(); |
313 | if(clearenv()) { |
|
|
314 | perror("Clearing environment failed"); |
|
|
315 | exit(1); |
|
|
316 | } |
117 | setenv("HOME",userdata->pw_dir,1); |
317 | if(setenv("HOME",userdata->pw_dir,1) || |
118 | setenv("DISPLAY",display,1); |
318 | setenv("DISPLAY",display,1) || |
119 | setenv("PATH","/usr/bin:/bin:/usr/local/bin",1); |
319 | setenv("PATH","/usr/bin:/bin:/usr/local/bin",1) || |
120 | setenv("LOGNAME",userdata->pw_name,1); |
320 | setenv("LOGNAME",userdata->pw_name,1) || |
121 | setenv("USER",userdata->pw_name,1); |
321 | setenv("USER",userdata->pw_name,1)) { |
|
|
322 | perror("Setting environment failed"); |
|
|
323 | exit(1); |
|
|
324 | } |
122 | |
325 | |
123 | /* Create the right qemu invocation */ |
326 | /* Create the right qemu invocation */ |
124 | if (!strlen(argv[3])) |
327 | if (!strlen(argv[3])) |
125 | newargs[0] = QEMU; |
328 | newargs[0] = QEMU; |
126 | else |
|
|
127 | asprintf(&newargs[0],"%s-system-%s",QEMU,argv[3]); |
329 | else if (asprintf(&newargs[0],"%s-system-%s",QEMU,argv[3]) < 0) { |
|
|
330 | perror("Preparing qemu executable filename failed"); |
|
|
331 | exit(1); |
|
|
332 | } |
128 | newargs[1] = "-net"; |
333 | newargs[1] = "-net"; |
129 | asprintf(&newargs[2],"nic,vlan=%d",vlan); |
334 | if (asprintf(&newargs[2],"nic,vlan=%d",vlan) < 0) { |
|
|
335 | perror("Preparing nic argument failed"); |
|
|
336 | exit(1); |
|
|
337 | } |
130 | newargs[3] = "-net"; |
338 | newargs[3] = "-net"; |
131 | asprintf(&newargs[4],"tap,fd=%d,vlan=%d",fd,vlan); |
339 | if (asprintf(&newargs[4],"tap,fd=%d,vlan=%d",fd,vlan) < 0) { |
|
|
340 | perror("Preparing tap argument failed"); |
|
|
341 | exit(1); |
|
|
342 | } |
132 | for (i = 4; i <= argc; i++) |
343 | for (i = 4; i <= argc; i++) |
133 | newargs[i+1] = argv[i]; |
344 | newargs[i+1] = argv[i]; |
134 | |
345 | |
135 | execvp(newargs[0], newargs); |
346 | execvp(newargs[0], newargs); |
|
|
347 | perror('Execution of qemu failed'); |
136 | exit(1); |
348 | exit(1); |
137 | } |
349 | } |
|
|
350 | |