--- qemu-start/trunk/qemu-start.c 2006/02/11 16:36:13 294 +++ qemu-start/trunk/qemu-start.c 2007/08/10 12:12:28 295 @@ -30,10 +30,199 @@ #include #include #include +#include +#include + +/* This define is present in recent glibc, it is not available on sarge */ +#ifndef HOST_NAME_MAX +#define HOST_NAME_MAX 64 +#endif #define QEMU "/usr/bin/qemu" #define USER "qemu" +/* + * This function merges the xauthority data of the current X context + * into the QEMU user's .Xauthority file. + */ +void fix_xauthority(void) { + struct passwd *userdata; + int pfd[2]; + char *xauthfile; + char *display; + char *newdisplay; + char *hostname; +// int status1, status2; + int status,pid; + pid_t cpid1, cpid2; + + if (pipe(pfd)) { + perror("pipe failed"); + exit(1); + } + + cpid1 = fork(); + if (cpid1 < 0) { + perror("first fork failed"); + exit(1); + } + + /* Child: will execute the xauth extract */ + if (cpid1 == 0) { + /* Child may run as original process starter */ + if (setgroups(0, NULL)) { + perror("setgroups failed"); + exit(1); + } + if (setgid(getgid())) { + perror("setgid failed"); + exit(1); + } + if (setuid(getuid())) { + perror("setuid failed"); + exit(1); + } + + /* Write side of pipe corresponds to stdout */ + if (dup2(pfd[1],1) < 0) { + perror("dup2 failed"); + exit(1); + } + if (close(pfd[0])) { + perror("close of pfd[0] failed"); + exit(1); + } + if (close(pfd[1])) { + perror("close of pfd[1] failed"); + exit(1); + } + + userdata = getpwuid(getuid()); + if (!userdata) { + fprintf(stderr, "No user '%s'\n", USER); + exit(1); + } + + + /* Get xauthority file to use */ + if (! (xauthfile = getenv("XAUTHORITY"))) { + if (asprintf(&xauthfile,"%s/.Xauthority",userdata->pw_dir) < 1) { + perror("Constructing xauthority filename failed"); + exit(1); + } + } + + /* Get display */ + if (! (display = getenv("DISPLAY"))) { + perror("No DISPLAY variable found\n"); + exit(1); + } + + /* Substitution: if display starts with `localhost', substitute + `localhost' by `HOSTNAME/unix'. */ + if (strstr(display,"localhost") == display) { + hostname = malloc(HOST_NAME_MAX+1); + if (gethostname(hostname,HOST_NAME_MAX+1)) { + perror("Getting hostname failed"); + exit(1); + } + + if (!(newdisplay = malloc(strlen(hostname) + 5 + strlen(display)-9 + 1))) { + perror("Allocating new display failed"); + exit(1); + } + strcpy(newdisplay,hostname); + strcpy(newdisplay + strlen(hostname),"/unix"); + strcpy(newdisplay + strlen(hostname) + 5, display + 9); + display = newdisplay; + } else if (! (display = strdup(display))) { + perror("strdup DISPLAY failed"); + exit(1); + } + + execl("/usr/bin/X11/xauth","/usr/bin/X11/xauth","-f",xauthfile,"extract","-",display,NULL); + exit(1); + } + + + cpid2 = fork(); + if (cpid2 < 0) { + perror("second fork failed"); + exit(1); + } + + /* Child: will execute the xauth merge */ + if (cpid2 == 0) { + /* Get userid data. */ + userdata = getpwnam(USER); + if (!userdata) { + fprintf(stderr, "No user '%s'\n", USER); + exit(1); + } + + if (setgroups(0, NULL)) { + perror("setgroups failed"); + exit(1); + } + + /* Child may run as qemu user */ + if (setgid(userdata->pw_gid)) { + perror("setgid failed"); + exit(1); + } + if (setuid(userdata->pw_uid)) { + perror("setuid failed"); + exit(1); + } + /* Read side of pipe is stdin */ + if (dup2(pfd[0],0) < 0) { + perror("dup2 failed"); + exit(1); + } + if (close(pfd[0])) { + perror("close of pfd[0] failed"); + exit(1); + } + if (close(pfd[1])) { + perror("close of pfd[1] failed"); + exit(1); + } + + if (asprintf(&xauthfile,"%s/.Xauthority",userdata->pw_dir) < 1) { + perror("Constructing xauthority filename failed"); + exit(1); + } + + execl("/usr/bin/X11/xauth","/usr/bin/X11/xauth","-f",xauthfile,"merge","-",NULL); + exit(1); + } + + if (close(pfd[0])) { + perror("close of pfd[0] failed"); + exit(1); + } + if (close(pfd[1])) { + perror("close of pfd[1] failed"); + exit(1); + } + + while(1) { + + pid = wait(&status); + if ((pid < 1) && (errno == ECHILD)) + break; + if (pid < 1) { + perror("wait failed"); + exit(1); + } + if (! WIFEXITED(status) || WEXITSTATUS(status)) { + fprintf(stderr,"Child returned error"); + exit(1); + } + } +} + + /* Tiny code to open tap/tun device, and hand the fd to qemu. Run as root, drops to given user. */ int main(int argc, char *argv[]) @@ -43,9 +232,7 @@ char *newargs[argc + 2]; int i,fd,vlan,tapnr; char *ptr; - char *command; char *display; - char *xauthfile; /* Check parameters */ if (argc < 4) { @@ -60,17 +247,26 @@ /* First parameter: TAPDEVNR */ tapnr = strtol(argv[1],&ptr,0); if (*ptr) { - fprintf(stderr, "Invalid value for TAPDEVNR parameter (use a number!)\n"); + fprintf(stderr, "Invalid value `%s' for TAPDEVNR parameter (use a number!)\n",argv[1]); exit(1); } /* Second parameter: VLAN */ vlan = strtol(argv[2],&ptr,0); if (*ptr) { - fprintf(stderr, "Invalid value for VLAN parameter (use a number!)\n"); + fprintf(stderr, "Invalid value `%s' for VLAN parameter (use a number!)\n", argv[2]); + exit(1); + } + + /* Third parameter: System type */ + if (index(argv[3],'/')) { + fprintf(stderr, "Invalid value `%s' for system (may not contain a slash character)\n", argv[3]); exit(1); } + /* Export the xauth data */ + fix_xauthority(); + /* Open /dev/net/tun */ fd = open("/dev/net/tun", O_RDWR); if (fd < 0) { @@ -88,50 +284,67 @@ exit(1); } - /* Fix X-display */ - userdata = getpwuid(getuid()); - if (! (xauthfile = getenv("XAUTHORITY"))) - asprintf(&xauthfile,"~%s/.Xauthority",userdata->pw_name); - setuid(0); - setgid(0); - 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); -fprintf(stderr,"%s\n",command); - system(command); - - /* Set correct userid. */ + /* Get userid data. */ userdata = getpwnam(USER); if (!userdata) { fprintf(stderr, "No user '%s'\n", USER); exit(1); } - setgroups(0, NULL); - setgid(userdata->pw_gid); - if (setuid(userdata->pw_uid) != 0) { - perror("setting uid"); + + /* Change to QEMU user */ + if (setgroups(0, NULL)) { + perror("setgroups failed"); + exit(1); + } + if (setgid(userdata->pw_gid)) { + perror("setgid failed"); + exit(1); + } + if (setuid(userdata->pw_uid)) { + perror("setuid failed"); exit(1); } /* Clean the environment */ - display=getenv("DISPLAY"); - clearenv(); - setenv("HOME",userdata->pw_dir,1); - setenv("DISPLAY",display,1); - setenv("PATH","/usr/bin:/bin:/usr/local/bin",1); - setenv("LOGNAME",userdata->pw_name,1); - setenv("USER",userdata->pw_name,1); + if(!(display=getenv("DISPLAY"))) { + perror("Getting DISPLAY failed"); + exit(1); + } + if(clearenv()) { + perror("Clearing environment failed"); + exit(1); + } + if(setenv("HOME",userdata->pw_dir,1) || + setenv("DISPLAY",display,1) || + setenv("PATH","/usr/bin:/bin:/usr/local/bin",1) || + setenv("LOGNAME",userdata->pw_name,1) || + setenv("USER",userdata->pw_name,1)) { + perror("Setting environment failed"); + exit(1); + } /* Create the right qemu invocation */ if (!strlen(argv[3])) newargs[0] = QEMU; - else - asprintf(&newargs[0],"%s-system-%s",QEMU,argv[3]); + else if (asprintf(&newargs[0],"%s-system-%s",QEMU,argv[3]) < 0) { + perror("Preparing qemu executable filename failed"); + exit(1); + } newargs[1] = "-net"; - asprintf(&newargs[2],"nic,vlan=%d",vlan); + if (asprintf(&newargs[2],"nic,vlan=%d",vlan) < 0) { + perror("Preparing nic argument failed"); + exit(1); + } newargs[3] = "-net"; - asprintf(&newargs[4],"tap,fd=%d,vlan=%d",fd,vlan); + if (asprintf(&newargs[4],"tap,fd=%d,vlan=%d",fd,vlan) < 0) { + perror("Preparing tap argument failed"); + exit(1); + } for (i = 4; i <= argc; i++) newargs[i+1] = argv[i]; execvp(newargs[0], newargs); + perror('Execution of qemu failed'); exit(1); } +