typo
[strongswan.git] / src / dumm / mconsole.c
index 466a68a..25cb846 100644 (file)
@@ -3,7 +3,7 @@
  * Hochschule fuer Technik Rapperswil
  * Copyright (C) 2001-2004 Jeff Dike
  *
  * Hochschule fuer Technik Rapperswil
  * Copyright (C) 2001-2004 Jeff Dike
  *
- * Based on the "uml_mconsole" utilty from Jeff Dike.
+ * Based on the "uml_mconsole" utility from Jeff Dike.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
  * for more details.
  */
 
  * for more details.
  */
 
+#define _GNU_SOURCE
+
 #include <sys/types.h>
 #include <unistd.h>
 #include <stdio.h>
 #include <sys/socket.h>
 #include <sys/types.h>
 #include <unistd.h>
 #include <stdio.h>
 #include <sys/socket.h>
+#include <errno.h>
 #include <sys/un.h>
 
 #include <debug.h>
 #include <sys/un.h>
 
 #include <debug.h>
@@ -36,60 +39,92 @@ struct private_mconsole_t {
        /** public interface */
        mconsole_t public;
        /** mconsole socket */
        /** public interface */
        mconsole_t public;
        /** mconsole socket */
-       int socket;
+       int console;
+       /** notify socket */
+       int notify;
        /** address of uml socket */
        struct sockaddr_un uml;
 };
 
 /**
        /** address of uml socket */
        struct sockaddr_un uml;
 };
 
 /**
+ * mconsole message format from "arch/um/include/mconsole.h"
+ */
+typedef struct mconsole_request mconsole_request;
+/** mconsole request message */
+struct mconsole_request {
+       u_int32_t magic;
+       u_int32_t version;
+       u_int32_t len;
+       char data[MCONSOLE_MAX_DATA];
+};
+
+
+typedef struct mconsole_reply mconsole_reply;
+/** mconsole reply message */
+struct mconsole_reply {
+       u_int32_t err;
+       u_int32_t more;
+       u_int32_t len;
+       char data[MCONSOLE_MAX_DATA];
+};
+
+typedef struct mconsole_notify mconsole_notify;
+/** mconsole notify message */
+struct mconsole_notify {
+    u_int32_t magic;
+    u_int32_t version;
+    enum {
+               MCONSOLE_SOCKET,
+               MCONSOLE_PANIC,
+               MCONSOLE_HANG,
+               MCONSOLE_USER_NOTIFY,
+    } type;
+    u_int32_t len;
+    char data[MCONSOLE_MAX_DATA];
+};
+
+/**
  * send a request to UML using mconsole
  */
  * 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)
 {
 {
-       struct {
-               u_int32_t magic;
-               u_int32_t version;
-               u_int32_t len;
-               char data[MCONSOLE_MAX_DATA];
-       } request;
-       struct {
-               u_int32_t err;
-               u_int32_t more;
-               u_int32_t len;
-               char data[MCONSOLE_MAX_DATA];
-       } reply;
-       bool first = TRUE, good = TRUE;
-       int len;
+       mconsole_request request;
+       mconsole_reply reply;
+       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);
        
        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->socket, &request, sizeof(request), 0,
+       if (sendto(this->console, &request, sizeof(request), 0,
                (struct sockaddr*)&this->uml, sizeof(this->uml)) < 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 
        {
        }
        do 
        {
-               len = recvfrom(this->socket, &reply, sizeof(reply), 0, NULL, 0);
+               len = recv(this->console, &reply, sizeof(reply), 0);
                if (len < 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);
        }
        while (reply.more);
-       return good;
+       
+       *size = total;
+       return reply.err;
 }
 
 /**
 }
 
 /**
@@ -105,7 +140,13 @@ static bool add_iface(private_mconsole_t *this, char *guest, char *host)
        {
                return FALSE;
        }
        {
                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;
 }
 
 /**
 }
 
 /**
@@ -121,7 +162,66 @@ static bool del_iface(private_mconsole_t *this, char *guest)
        {
                return FALSE;
        }
        {
                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));
+}
+
+/**
+ * Poll until guest is ready
+ */
+static bool wait_bootup(private_mconsole_t *this)
+{
+       char *cmd, buf[128];
+       int len, res;
+       
+       cmd = "config con0";
+       while (TRUE)
+       {
+               len = sizeof(buf);
+               res = request(this, cmd, buf, &len);
+               if (res < 0)
+               {
+                       return FALSE;
+               }
+               if (res == 0)
+               {
+                       return TRUE;
+               }
+               usleep(50000);
+       }
 }
 
 /**
 }
 
 /**
@@ -129,41 +229,120 @@ static bool del_iface(private_mconsole_t *this, char *guest)
  */
 static void destroy(private_mconsole_t *this)
 {
  */
 static void destroy(private_mconsole_t *this)
 {
-       close(this->socket);
+       close(this->console);
+       close(this->notify);
        free(this);
 }
 
 /**
        free(this);
 }
 
 /**
- * create the mconsole instance
+ * setup the mconsole notify connection and wait for its readyness
+ */
+static bool wait_for_notify(private_mconsole_t *this, char *nsock)
+{
+       struct sockaddr_un addr;
+       mconsole_notify notify;
+       int len;
+
+       this->notify = socket(AF_UNIX, SOCK_DGRAM, 0);
+       if (this->notify < 0)
+       {
+               DBG1("opening mconsole notify socket failed: %m");
+               return FALSE;
+       }
+       memset(&addr, 0, sizeof(addr));
+       addr.sun_family = AF_UNIX;
+       strncpy(addr.sun_path, nsock, sizeof(addr));
+       if (bind(this->notify, (struct sockaddr*)&addr, sizeof(addr)) < 0)
+       {
+               DBG1("binding mconsole notify socket to '%s' failed: %m", nsock);
+               close(this->notify);
+               return FALSE;
+       }
+       do
+       {
+               len = recvfrom(this->notify, &notify, sizeof(notify), 0, NULL, 0);
+       } while (len < 0 && errno == EINTR);
+       if (len < 0 || len >= sizeof(notify))
+       {
+               DBG1("reading from mconsole notify socket failed: %m");
+               close(this->notify);
+               unlink(nsock);
+               return FALSE;
+       }
+       if (notify.magic != MCONSOLE_MAGIC ||
+               notify.version != MCONSOLE_VERSION ||
+               notify.type != MCONSOLE_SOCKET)
+       {
+               DBG1("received unexpected message from mconsole notify socket: %b",
+                        &notify, sizeof(notify));
+               close(this->notify);
+               unlink(nsock);
+               return FALSE;
+       }
+       memset(&this->uml, 0, sizeof(this->uml));
+       this->uml.sun_family = AF_UNIX;
+       strncpy(this->uml.sun_path, (char*)&notify.data, sizeof(this->uml.sun_path));
+       return TRUE;
+}
+
+/**
+ * setup the mconsole console connection
  */
  */
-mconsole_t *mconsole_create(char *sock)
+static bool setup_console(private_mconsole_t *this)
 {
        struct sockaddr_un addr;
 {
        struct sockaddr_un addr;
+       
+       this->console = socket(AF_UNIX, SOCK_DGRAM, 0);
+       if (this->console < 0)
+       {
+               DBG1("opening mconsole socket failed: %m");
+               return FALSE;
+       }
+       memset(&addr, 0, sizeof(addr));
+       addr.sun_family = AF_UNIX;
+       snprintf(&addr.sun_path[1], sizeof(addr.sun_path), "%5d-%d",
+                        getpid(), this->console);
+       if (bind(this->console, (struct sockaddr*)&addr, sizeof(addr)) < 0)
+       {
+               DBG1("binding mconsole socket to '%s' failed: %m", &addr.sun_path[1]);
+               close(this->console);
+               return FALSE;
+       }
+       return TRUE;
+}
+
+/**
+ * create the mconsole instance
+ */
+mconsole_t *mconsole_create(char *notify)
+{
        private_mconsole_t *this = malloc_thing(private_mconsole_t);
        
        this->public.add_iface = (bool(*)(mconsole_t*, char *guest, char *host))add_iface;
        this->public.del_iface = (bool(*)(mconsole_t*, char *guest))del_iface;
        private_mconsole_t *this = malloc_thing(private_mconsole_t);
        
        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;
        
        this->public.destroy = (void*)destroy;
        
-       this->socket = socket(AF_UNIX, SOCK_DGRAM, 0);
-       if (this->socket < 0)
+       if (!wait_for_notify(this, notify))
        {
        {
-               DBG1("opening mconsole socket failed: %m");
                free(this);
                return NULL;
        }
                free(this);
                return NULL;
        }
-       memset(&addr, 0, sizeof(addr));
-       addr.sun_family = AF_UNIX;
-       snprintf(&addr.sun_path[1], sizeof(addr.sun_path), "%5d-%s", getpid(), sock);
-       if (bind(this->socket, (struct sockaddr*)&addr, sizeof(addr)) < 0)
+       
+       if (!setup_console(this))
+       {
+               close(this->notify);
+               unlink(notify);
+               free(this);
+               return NULL;
+       }
+       unlink(notify);
+       
+       if (!wait_bootup(this))
        {
        {
-               DBG1("binding mconsole socket failed: %m");
                destroy(this);
                return NULL;
        }
                destroy(this);
                return NULL;
        }
-       memset(&this->uml, 0, sizeof(this->uml));
-       this->uml.sun_family = AF_UNIX;
-       strncpy(this->uml.sun_path, sock, sizeof(this->uml.sun_path));
        
        return &this->public;
 }
        
        return &this->public;
 }