simple console support through pts devices
authorMartin Willi <martin@strongswan.org>
Tue, 28 Aug 2007 07:53:46 +0000 (07:53 -0000)
committerMartin Willi <martin@strongswan.org>
Tue, 28 Aug 2007 07:53:46 +0000 (07:53 -0000)
src/dumm/guest.c
src/dumm/guest.h
src/dumm/main.c
src/dumm/mconsole.c
src/dumm/mconsole.h

index abed806..b74449c 100644 (file)
@@ -24,6 +24,7 @@
 #include <fcntl.h>
 #include <signal.h>
 #include <dirent.h>
+#include <termios.h>
 
 #include <debug.h>
 #include <utils/linked_list.h>
@@ -43,6 +44,7 @@
 #define KERNEL_FILE "linux"
 #define LOG_FILE "boot.log"
 #define NOTIFY_FILE "notify"
+#define PTYS 0
 
 typedef struct private_guest_t private_guest_t;
 
@@ -67,6 +69,19 @@ struct private_guest_t {
        cowfs_t *cowfs;
        /** mconsole to control running UML */
        mconsole_t *mconsole;
+       /** pty consoles */
+       struct {
+               /** pty master fd */
+               int master;
+               /** pty slave fd */
+               int slave;
+               /** name of the pty */
+               char name[16];
+               /** currently in use */
+               bool occupied;
+               /** is valid */
+               bool valid;
+       } pty[PTYS];
        /** list of interfaces attached to the guest */
        linked_list_t *ifaces;
 };
@@ -168,6 +183,18 @@ static char* write_arg(char **pos, size_t *left, char *format, ...)
 }
 
 /**
+ * Implementation of get_t.close_console.
+ */
+static char* get_console(private_guest_t *this, int console)
+{
+       if (this->state == GUEST_RUNNING)
+       {
+               return this->mconsole->get_console_pts(this->mconsole, console);
+       }
+       return NULL;
+}
+
+/**
  * Implementation of guest_t.stop.
  */
 static void stop(private_guest_t *this)
@@ -212,15 +239,8 @@ static bool start(private_guest_t *this)
        args[i++] = write_arg(&pos, &left, "umid=%s", this->name);
        args[i++] = write_arg(&pos, &left, "mem=%dM", this->mem);
        args[i++] = write_arg(&pos, &left, "mconsole=notify:%s", notify);
-       /*args[i++] = write_arg(&pos, &left, "con=pts");*/
-       args[i++] = write_arg(&pos, &left, "con0=null,fd:%d", this->bootlog);
-       //args[i++] = write_arg(&pos, &left, "con0=fd:0,fd:1");
-       //args[i++] = write_arg(&pos, &left, "con1=null,null");
-       args[i++] = write_arg(&pos, &left, "con2=null,null");
-       args[i++] = write_arg(&pos, &left, "con3=null,null");
-       args[i++] = write_arg(&pos, &left, "con4=null,null");
-       args[i++] = write_arg(&pos, &left, "con5=null,null");
-       args[i++] = write_arg(&pos, &left, "con6=null,null");
+       args[i++] = write_arg(&pos, &left, "con=pts");
+       args[i++] = write_arg(&pos, &left, "con0=none,fd:%d", this->bootlog);
        args[i++] = NULL;
          
        this->pid = fork();
@@ -247,6 +267,7 @@ static bool start(private_guest_t *this)
                stop(this);
                return FALSE;
        }
+       
        this->state = GUEST_RUNNING;
        return TRUE;
 }      
@@ -285,12 +306,22 @@ static bool set_scenario(private_guest_t *this, char *path)
  */
 static void sigchild(private_guest_t *this)
 {
+       int i;
+
        if (this->state != GUEST_STOPPING)
        {       /* collect zombie if uml crashed */
                waitpid(this->pid, NULL, WNOHANG);
        }
        DESTROY_IF(this->mconsole);
        this->mconsole = NULL;
+       for (i = 0; i < PTYS; i++)
+       {
+               if (this->pty[i].valid)
+               {
+                       close(this->pty[i].master);
+                       close(this->pty[i].slave);
+               }
+       }
        this->state = GUEST_STOPPED;
 }
 
@@ -425,10 +456,11 @@ static private_guest_t *guest_create_generic(char *parent, char *name,
        this->public.create_iface_iterator = (iterator_t*(*)(guest_t*))create_iface_iterator;
        this->public.start = (void*)start;
        this->public.stop = (void*)stop;
+       this->public.get_console = (char*(*)(guest_t*,int))get_console;
        this->public.set_scenario = (bool(*)(guest_t*, char *path))set_scenario;
        this->public.sigchild = (void(*)(guest_t*))sigchild;
        this->public.destroy = (void*)destroy;
-       
+               
        if (*parent == '/' || getcwd(cwd, sizeof(cwd)) == NULL)
        {
                asprintf(&this->dirname, "%s/%s", parent, name);
index eb85ccb..10d42cb 100644 (file)
@@ -87,6 +87,17 @@ struct guest_t {
        bool (*stop) (guest_t *this);
        
        /**
+        * @brief Get a console pts device.
+        *
+        * Every guest has 6 consoles, numbered from 1 to 6. These are associated
+        * to a unique pts device on the host. 
+        *
+        * @param console       console number to get (1-6)
+        * @return                      pts device file name, NULL if failed
+        */
+       char* (*get_console) (guest_t *this, int console);
+       
+       /**
         * @brief Create a new interface in the current scenario.
         *
         * @param name  name of the interface in the guest
index a4389bc..b4c4352 100644 (file)
@@ -57,7 +57,10 @@ static char* get_line(char *format, ...)
                line = readline(prompt);
                if (line == NULL)
                {
-                       continue;
+                       printf("quit\n");
+                       dumm->destroy(dumm);
+                       clear_history();
+                       exit(0);
                }
                if (*line == '\0')
                {
@@ -190,6 +193,21 @@ static void guest_delif_menu(guest_t *guest)
        free(name);
 }
 
+static void guest_console(guest_t *guest)
+{
+       int con;
+       
+       for (con = 1; con <= 6; con++)
+       {
+               char *pts = guest->get_console(guest, con);
+               if (pts)
+               {
+                       printf("%d: %s\n", con, pts);
+                       free(pts);
+               }
+       }
+}
+
 static void guest_menu(guest_t *guest)
 {
        while (TRUE)
@@ -205,7 +223,7 @@ static void guest_menu(guest_t *guest)
                {
                        if (guest->start(guest))
                        {
-                               printf("guest '%s' is booting\n", guest->get_name(guest));
+                               printf("guest '%s' is running\n", guest->get_name(guest));
                        }
                        else
                        {
@@ -226,9 +244,13 @@ static void guest_menu(guest_t *guest)
                {
                        guest_delif_menu(guest);
                }
+               else if (streq(line, "console"))
+               {
+                       guest_console(guest);
+               }
                else
                {
-                       printf("back|start|stop|addif|delif\n");
+                       printf("back|start|stop|addif|delif|console\n");
                }
                free(line);
        }
index 01547e1..7757823 100644 (file)
@@ -16,6 +16,8 @@
  * for more details.
  */
 
+#define _GNU_SOURCE
+
 #include <sys/types.h>
 #include <unistd.h>
 #include <stdio.h>
@@ -84,42 +86,45 @@ struct mconsole_notify {
 /**
  * send a request to UML using mconsole
  */
-static bool request(private_mconsole_t *this, char *command)
+static int request(private_mconsole_t *this, char *command,
+                                  char buf[], size_t *size)
 {
        mconsole_request request;
        mconsole_reply reply;
-       bool first = TRUE, good = TRUE;
-       int len;
+       int len, total = 0;
        
        memset(&request, 0, sizeof(request));
        request.magic = MCONSOLE_MAGIC;
        request.version = MCONSOLE_VERSION;
        request.len = min(strlen(command), sizeof(reply.data) - 1);
        strncpy(request.data, command, request.len);
+       *buf = '\0';
+       (*size)--;
 
        if (sendto(this->console, &request, sizeof(request), 0,
                (struct sockaddr*)&this->uml, sizeof(this->uml)) < 0)
        {
-               DBG1("sending mconsole command to UML failed: %m");
-               return FALSE;
+               snprintf(buf, *size, "sending mconsole command to UML failed: %m");
+               return -1;
        }
        do 
        {
-               len = recvfrom(this->console, &reply, sizeof(reply), 0, NULL, 0);
+               len = recv(this->console, &reply, sizeof(reply), 0);
                if (len < 0)
                {
-                       DBG1("receiving from mconsole failed: %m");
-               return FALSE;
+                       snprintf(buf, *size, "receiving from mconsole failed: %m");
+               return -1;
                }
-               if (first && reply.err)
+               if (len > 0)
                {
-                       good = FALSE;
-                       DBG1("received error from UML mconsole: %s", reply.data);
+                       strncat(buf, reply.data, min(reply.len, *size - total));
+                       total += reply.len;
                }
-               first = FALSE;
        }
        while (reply.more);
-       return good;
+       
+       *size = total;
+       return reply.err;
 }
 
 /**
@@ -135,7 +140,13 @@ static bool add_iface(private_mconsole_t *this, char *guest, char *host)
        {
                return FALSE;
        }
-       return request(this, buf);
+       len = sizeof(buf);
+       if (request(this, buf, buf, &len) != 0)
+       {
+               DBG1("adding interface failed: %.*s", len, buf);
+               return FALSE;
+       }
+       return TRUE;
 }
 
 /**
@@ -151,7 +162,41 @@ static bool del_iface(private_mconsole_t *this, char *guest)
        {
                return FALSE;
        }
-       return request(this, buf);
+       if (request(this, buf, buf, &len) != 0)
+       {
+               DBG1("removing interface failed: %.*s", len, buf);
+               return FALSE;
+       }
+       return TRUE;
+}
+       
+/**
+ * Implementation of mconsole_t.get_console_pts.
+ */
+static char* get_console_pts(private_mconsole_t *this, int con)
+{
+       char buf[128];
+       char *pos;
+       int len;
+       
+       len = snprintf(buf, sizeof(buf), "config con%d", con);
+       if (len < 0 || len >= sizeof(buf))
+       {
+               return NULL;
+       }
+       len = sizeof(buf);
+       if (request(this, buf, buf, &len) != 0)
+       {
+               DBG1("getting console pts failed: %.*s", len, buf);
+               return NULL;
+       }
+       pos = memchr(buf, ':', len);
+       if (pos == NULL)
+       {
+               return NULL;
+       }
+       pos++;
+       return strndup(pos, len - (pos - buf));
 }
 
 /**
@@ -250,6 +295,7 @@ mconsole_t *mconsole_create(char *notify)
        
        this->public.add_iface = (bool(*)(mconsole_t*, char *guest, char *host))add_iface;
        this->public.del_iface = (bool(*)(mconsole_t*, char *guest))del_iface;
+       this->public.get_console_pts = (char*(*)(mconsole_t*, int con))get_console_pts;
        this->public.destroy = (void*)destroy;
        
        if (!wait_for_notify(this, notify))
index cf4f699..53aaa1b 100644 (file)
@@ -43,6 +43,14 @@ struct mconsole_t {
        bool (*del_iface)(mconsole_t *this, char *guest);
        
        /**
+        * @brief Get the pts device file assigned to a console.
+        *
+        * @param con                   console number in guest
+        * @return                              allocated device string
+        */
+       char* (*get_console_pts)(mconsole_t *this, int con);
+       
+       /**
         * @brief Destroy the mconsole instance
         */
        void (*destroy) (mconsole_t *this);