handle default key sizes in openssl_crypter
[strongswan.git] / src / dumm / mconsole.c
index b782ebe..d9864f6 100644 (file)
@@ -3,7 +3,7 @@
  * 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
  * for more details.
  */
 
+#define _GNU_SOURCE
+
 #include <sys/types.h>
 #include <unistd.h>
 #include <stdio.h>
 #include <sys/socket.h>
+#include <errno.h>
 #include <sys/un.h>
 
 #include <debug.h>
@@ -36,60 +39,116 @@ struct private_mconsole_t {
        /** public interface */
        mconsole_t public;
        /** mconsole socket */
-       int socket;
+       int console;
+       /** notify socket */
+       int notify;
        /** address of uml socket */
        struct sockaddr_un uml;
+       /** idle function */
+       void (*idle)(void);
+};
+
+/**
+ * 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
  */
-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, flags = 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->socket, &request, sizeof(request), 0,
-               (struct sockaddr*)&this->uml, sizeof(this->uml)) < 0)
+       if (this->idle)
        {
-               DBG1("sending mconsole command to UML failed: %m");
-               return FALSE;
+               flags = MSG_DONTWAIT;
+       }
+       do
+       {
+               if (this->idle)
+               {
+                       this->idle();
+               }
+               len = sendto(this->console, &request, sizeof(request), flags,
+                                        (struct sockaddr*)&this->uml, sizeof(this->uml));
+       }
+       while (len < 0 && (errno == EINTR || errno == EAGAIN));
+       
+       if (len < 0)
+       {
+               snprintf(buf, *size, "sending mconsole command to UML failed: %m");
+               return -1;
        }
        do 
        {
-               len = recvfrom(this->socket, &reply, sizeof(reply), 0, NULL, 0);
+               len = recv(this->console, &reply, sizeof(reply), flags);
+               if (len < 0 && (errno == EINTR || errno == EAGAIN))
+               {
+                       if (this->idle)
+                       {
+                               this->idle();
+                       }
+                       continue;
+               }
                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;
 }
 
 /**
@@ -105,7 +164,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;
 }
 
 /**
@@ -121,7 +186,47 @@ 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;
+}
+
+/**
+ * Poll until guest is ready
+ */
+static bool wait_bootup(private_mconsole_t *this)
+{
+       char buf[128];
+       int len, res;
+       
+       while (TRUE)
+       {
+               len = sizeof(buf);
+               res = request(this, "config eth9=mcast", buf, &len);
+               if (res < 0)
+               {
+                       return FALSE;
+               }
+               if (res == 0)
+               {
+                       while (request(this, "remove eth9", buf, &len) != 0)
+                       {
+                               usleep(50000);
+                       }
+                       return TRUE;
+               }
+               if (this->idle)
+               {
+                       this->idle();
+               }
+               else
+               {
+                       usleep(50000);
+               }
+       }
 }
 
 /**
@@ -129,41 +234,131 @@ static bool del_iface(private_mconsole_t *this, char *guest)
  */
 static void destroy(private_mconsole_t *this)
 {
-       close(this->socket);
+       close(this->console);
+       close(this->notify);
        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, flags = 0;
+
+       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;
+       }
+       if (this->idle)
+       {
+               flags = MSG_DONTWAIT;
+       }
+       do
+       {
+               if (this->idle)
+               {
+                       this->idle();
+               }
+               len = recvfrom(this->notify, &notify, sizeof(notify), flags, NULL, 0);
+       }
+       while (len < 0 && (errno == EINTR || errno == EAGAIN));
+       
+       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;
+       
+       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, void(*idle)(void))
+{
        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.destroy = (void*)destroy;
        
-       this->socket = socket(AF_UNIX, SOCK_DGRAM, 0);
-       if (this->socket < 0)
+       this->idle = idle;
+       
+       if (!wait_for_notify(this, notify))
        {
-               DBG1("opening mconsole socket failed: %m");
                free(this);
                return NULL;
        }
-       memset(&addr, 0, sizeof(addr));
-       addr.sun_family = AF_UNIX;
-       sprintf(&addr.sun_path[1], "%5d", getpid());
-       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;
        }
-       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;
 }