Logo Search packages:      
Sourcecode: vde2 version File versions  Download package

vdeq.c

/* Copyright 2003 Renzo Davoli 
 * TNX: 2005.11.18 new syntax mgmt patch by Iain McFarlane <imcfarla@tiscali.co.uk>
 * Licensed under the GPL
 */

#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <stdint.h>
#include <libgen.h>
#include <signal.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/uio.h>
#include <sys/poll.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <pwd.h>

#include <vde.h>
#include <libvdeplug/libvdeplug.h>

#define SWITCH_MAGIC 0xfeedface
#define BUFSIZE 2048
#define ETH_ALEN 6
#define MAXDESCR 128

static int nb_nics;
VDECONN **conn;

unsigned char bufin[BUFSIZE];

struct pollfd *pollv;

char *filename;
char *vdeqname;
#define NUMW 10

static int countnics(const char *s)
{
      register int nics=1;
      while (*s) {
            if (*s==',') nics++;
            s++;
      }
      return nics;
}

static int countnewnics(int argc,char *argv[])
{
      register int nics=0;
      register int netflag=0;
      while (argc > 0) {
            if (strcmp(argv[0],"-net")==0)
                  netflag=1;
            else {
                  if (netflag && (strncmp(argv[0],"vde",3)==0))
                        nics++;
                  netflag=0;
            }
            argv++;
            argc--;
      }
      return nics;
}

static void usage(void) 
{
      if (strcmp(vdeqname,"vdeq") != 0 && strncmp(vdeqname,"vde",3)==0) {
            fprintf(stderr,"Usage: %s [-h]\n"
                        "\t %s ...qemu options... -net vde[,vlan=n][,sock=sock] ... \n"
                        "Old syntax:\n"
                        "\t %s  [-sock sock1 [,sock2...]] qemu_options\n"
                        "\t (%s executes a qemu machine named %s, \n\t  output of \"%s -h\" follows)\n\n", vdeqname,vdeqname,vdeqname,vdeqname,filename,filename);
            execlp(filename,filename,"-h",(char *) 0);
      } else {
            fprintf(stderr,"Usage: %s [-h]\n"
                        "\t %s qemu_executable ...qemu options... -net vde[,vlan=n][,sock=sock] ... \n"
                        "Old syntax:\n"
                        "\t %s qemu_executable [-sock sock1 [,sock2...]] qemu_options\n", vdeqname,vdeqname, vdeqname);
            exit(0);
      }
}

static void cleanup()
{
      register int i;
      for (i=0; i<nb_nics; i++) {
            if (conn[i] != NULL)
                  vde_close(conn[i]);
      }
}

static void sig_handler(int sig)
{
      fprintf(stderr,"%s: Caught signal %d, cleaning up and exiting\n", vdeqname, sig);
      cleanup();
      signal(sig, SIG_DFL);
      kill(getpid(), sig);
}

static void setsighandlers()
{
      /* setting signal handlers.
       * sets clean termination for SIGHUP, SIGINT and SIGTERM, and simply
       * ignores all the others signals which could cause termination. */
      struct { int sig; const char *name; int ignore; } signals[] = {
            { SIGHUP, "SIGHUP", 0 },
            { SIGINT, "SIGINT", 0 },
            { SIGPIPE, "SIGPIPE", 1 },
            { SIGALRM, "SIGALRM", 1 },
            { SIGTERM, "SIGTERM", 0 },
            { SIGUSR1, "SIGUSR1", 1 },
            { SIGUSR2, "SIGUSR2", 1 },
            { SIGPROF, "SIGPROF", 1 },
            { SIGVTALRM, "SIGVTALRM", 1 },
#ifdef VDE_LINUX
            { SIGPOLL, "SIGPOLL", 1 },
#ifdef SIGSTKFLT
            { SIGSTKFLT, "SIGSTKFLT", 1 },
#endif
            { SIGIO, "SIGIO", 1 },
            { SIGPWR, "SIGPWR", 1 },
#ifdef SIGUNUSED
            { SIGUNUSED, "SIGUNUSED", 1 },
#endif
#endif
#ifdef VDE_DARWIN
            { SIGXCPU, "SIGXCPU", 1 },
            { SIGXFSZ, "SIGXFSZ", 1 },
#endif
            { 0, NULL, 0 }
      };

      int i;
      for(i = 0; signals[i].sig != 0; i++)
            if(signal(signals[i].sig,
                              signals[i].ignore ? SIG_IGN : sig_handler) < 0)
                  fprintf(stderr,"Setting handler for %s: %s\n", signals[i].name,
                              strerror(errno));
}

static void leave(int sig)
{
      fprintf(stderr,"qemu exited: %s quits\n", vdeqname);
      cleanup();
      exit(0);
}

static int checkver(char *prog)
{
      char *newargv[3];
      int fd[2];
      int f,len,version=0;
      char buf[257];
      newargv[0]=prog;
      newargv[1]="-h";
      newargv[2]=0;
      buf[256]=0;
      if (pipe(fd) < 0) {
        perror("pipe");
        exit(1);
      }
      if ((f=fork()) > 0) {
            int status;
            close(fd[1]);
            len=read(fd[0],buf,256);
            if (len>0) {
                  int i;
                  for(i=0;i<len && version==0;i++) {
                        if(strncmp(buf+i,"version ",8)==0) {
                              int v1,v2,v3;
                              sscanf(buf+i+8,"%d.%d.%d",&v1,&v2,&v3);
                              version=(v1 << 16) + (v2 << 8) + v3;
                        }
                  }
            }
            waitpid(f,&status,0);
            close(fd[0]);
      }
      else if (f==0) {
            close(fd[0]);
            dup2(fd[1],1);
            dup2(fd[1],2);
            close(fd[1]);
            if (execvp(prog,newargv) < 0) {
                  exit(1);
            }
      }
      return version;
}

static char *parsevdearg(char *arg,char **sock,int *pport, int fd)
{
      char newarg[128];
      int vlan=0;
      *sock=VDESTDSOCK;
      *pport=0;
      while (*arg==',') arg++;
      if (strncmp(arg,"vlan=",5)==0) {
            vlan=atoi(arg+5);
            while (*arg != 0 && *arg != ',')
                  arg++;
      }
      while (*arg==',') arg++;
      if (strncmp(arg,"sock=",5)==0) {
            arg+=5;
            if (*arg=='\"') {
                  arg++;
                  *sock=arg;
                  while (*arg != 0 && *arg != '\"')
                        arg++;
            } else {
                  *sock=arg;
                  while (*arg != 0 && *arg != ',')
                        arg++;
            }
            if (*arg != 0) {
                  *arg=0; arg++;
            }
      }
      while (*arg==',') arg++;
      if (strncmp(arg,"port=",5)==0) {
            *pport=atoi(arg+5);
            while (*arg != 0 && *arg != ',')
                  arg++;
      }
      while (*arg==',') arg++;

      snprintf(newarg,128,"tap,vlan=%d,fd=%d%s%s",vlan,fd,(*arg == 0)?"":",",arg);
      return strdup(newarg);
}

int main(int argc, char **argv)
{
  char *argsock,**sockname;
      int *ports;
  int result;
  register ssize_t nx;
  int newargc;
  char **newargv;
  typedef int pair[2];
  pair *sp;
  register int i,j;
      int oldsyntax=0;
      int newsyntax=0;
      int ver;
  mode_t mode;

  vdeqname=basename(argv[0]);
      //callerpwd=getpwuid(getuid());
      /* OLD SYNTAX MGMT */
      if (strncmp(vdeqname,"vdeo",4) == 0) {
            oldsyntax=1;
            if (strcmp(vdeqname,"vdeoq") != 0) {
                  filename=vdeqname+4;
            }
      }
      else if (strcmp(vdeqname,"vdeq") != 0 && strncmp(vdeqname,"vde",3)==0) {
            filename=vdeqname+3;
      }
      else if (argc > 1) {
        filename=argv[1];
            argc--;
            argv++;
  } else {
        usage();
  }
      if ((ver=checkver(filename)) < 0x800) 
            oldsyntax=1;
      if (!oldsyntax) {
            nb_nics=countnewnics(argc-1,argv+1);
            if (nb_nics > 0)
                  newsyntax=1;
      }
  if ((argc > 1 && (
                    strcmp(argv[1],"-h")==0 ||
                    strcmp(argv[1],"-help")==0 ||
                    strcmp(argv[1],"--help")==0
                    )) || (
                    strcmp(filename,"-h")==0 ||
                    strcmp(filename,"-help")==0 ||
                    strcmp(filename,"--help")==0
              )) {
        usage();
  } else if (argc > 2 && (
              (strcmp(argv[1],"-vdesock")==0) ||
              (strcmp(argv[1],"-sock")==0) ||
              (strcmp(argv[1],"-unix")==0) ||
              (strcmp(argv[1],"-s")==0))
          ){
        argsock=argv[2];
        argv+=2;
        argc-=2;
  } else
        argsock=NULL;

    if (argc > 2 && (
              (strcmp(argv[1],"--mod")==0) ||
              (strcmp(argv[1],"-m")==0))
          ){
      sscanf(argv[2],"%o",&mode);
      argv+=2;
      argc-=2;
    }

      if (!newsyntax) {
            if (argsock == NULL)
                  nb_nics=1;
            else
                  nb_nics=countnics(argsock);
            if (!oldsyntax && nb_nics > 1)
                  fprintf(stderr,
                              "Warning: all the vde connections will be connected to one net interface\n"
                              "         to configure several interface use the new syntax -net vde\n");
      }

      if ((sp= (pair *) malloc(nb_nics * 2 * sizeof (int)))<0) {
            perror("malloc nics");
            exit(1);
      }

      if ((conn=(VDECONN **) calloc (nb_nics,sizeof(VDECONN *))) <0) {
        perror("calloc conn");
        exit(1);
  }

  for (i=0; i<nb_nics; i++) {
      if (socketpair(AF_UNIX, SOCK_DGRAM, 0, sp[i]) < 0){
            perror("socketpair");
            exit(1);
            }
  }

      if ((sockname= (char **) malloc(sizeof(char *) * nb_nics))<0) {
            perror("malloc sockname");
            exit(1);
      }
  if ((ports= (int *) calloc(nb_nics, sizeof(int)))<0) {
        perror("malloc ports");
        exit(1);
  }

      if (newsyntax)
      {
            int netflag;
            int vdeint;
            newargv=argv;
            newargc=argc;
            for (i=1,netflag=0,vdeint=0;i<argc;i++) {
                  if (strcmp(argv[i],"-net")==0)
                        netflag=1;
                  else {
                        if (netflag && strncmp(argv[i],"vde",3) == 0)
                        {
                              argv[i]=parsevdearg(argv[i]+3,&sockname[vdeint],&ports[vdeint],sp[vdeint][0]);
                              vdeint++;
                        }
                        netflag=0;
                  }
            }
      } else
  {
            if (argsock==NULL)
                  sockname[0]=VDESTDSOCK;
            else
            {
                  register char *s=argsock;
                  register char oldch;
                  i=0;
                  do {
                        sockname[i++]=s;
                        while (*s != ',' && *s != '\0')
                              s++;
                        oldch=*s;
                        *s=0;
                        s++;
                  } while (oldch != 0);
            }

            /*  printf("-- %s --\n",numfd);
                        printf("as %s\n",argsock);
                        for (i=0; i<nb_nics; i++)
                        printf("%d -> %s\n",i,sockname[i]); */
            newargc=argc+2+(2*nb_nics);
            if ((newargv=(char **) malloc ((newargc+1)* sizeof(char *))) <0) {
                  perror("malloc");
                  exit(1);
            }

            newargv[0]=filename;
            if (oldsyntax) {
                  for (i=0; i<nb_nics; i++) {
                        char numfd[10];
                        sprintf(numfd,"%d",sp[i][0]);
                        newargv[2*i+1]="-tun-fd";
                        newargv[2*i+2]=strdup(numfd);
                  }
                  {
                        char nnics[10];
                        sprintf(nnics,"%d",nb_nics);
                        newargv[2*nb_nics+1]="-nics";
                        newargv[2*nb_nics+2]=strdup(nnics);
                  }
            } else {
                  for (i=0; i<nb_nics; i++) {
                        char numfd[30];
                        sprintf(numfd,"tap,vlan=0,fd=%d",sp[i][0]);
                        newargv[2*i+1]="-net";
                        newargv[2*i+2]=strdup(numfd);
                  }
                  newargv[2*nb_nics+1]="-net";
                  newargv[2*nb_nics+2]="nic";
            }
            for (i=(2*nb_nics)+3,j=1;j<argc;i++,j++) newargv[i]=argv[j];

            newargv[i]=0;
      }

  if ((pollv= (struct pollfd *) malloc(sizeof(struct pollfd) * 2 * nb_nics))<0) {
            perror("malloc pollfd");
        exit(1);
  }
      setsighandlers();
  for (i=0; i<nb_nics; i++) {
            struct vde_open_args vdearg={ports[i],NULL,mode};
            conn[i]=vde_open(sockname[i],"vdeqemu",&vdearg);
        pollv[2*i+1].fd=vde_datafd(conn[i]);
        pollv[2*i].fd=sp[i][1];
        pollv[2*i].events= pollv[2*i+1].events=POLLIN|POLLHUP;
  }

  if (fork()) {
        close(0); 
        signal(SIGCHLD, leave);
        for (i=0; i<nb_nics; i++) 
              close(sp[i][0]);
        for(;;) {
          if ((result=poll(pollv,2*nb_nics,-1)) < 0) {
            perror("poll");
            cleanup();
            exit(1);
          }
          for (i=0; i<nb_nics; i++) {
            if (pollv[2*i].revents & POLLHUP || pollv[2*i+1].revents & POLLHUP)
            break;
            if (pollv[2*i].revents & POLLIN) {
            if ((nx=read(sp[i][1],bufin,sizeof(bufin))) < 0) {
              perror("read");
              cleanup();
              exit(1);
            }
            //fprintf(stderr,"RX from qemu %d\n",nx);
            if (vde_send(conn[i],bufin,nx,0) < 0) {
              perror("sendto");
              cleanup();
              exit(1);
            }
            }
            if (pollv[2*i+1].revents & POLLIN) {
            if ((nx=vde_recv(conn[i],bufin,BUFSIZE,0)) < 0) {
              perror("recvfrom");
              cleanup();
              exit(1);
            }
            //fprintf(stderr,"TX to qemu %d\n",nx);
            if (write(sp[i][1],bufin,nx) < 0) {
              perror("write");
              cleanup();
              exit(1);
            }
            }
          }
        }
  } else {
        for (i=0; i<nb_nics; i++) {
              close(sp[i][1]);
              close(vde_datafd(conn[i]));
              close(vde_ctlfd(conn[i]));
        }
        execvp(filename,newargv);
  }  
      cleanup();
  return(0);
}

Generated by  Doxygen 1.6.0   Back to index