uml "exec" writes stdout/stderr back to mconsole
authorMartin Willi <martin@strongswan.org>
Tue, 8 Jul 2008 14:58:20 +0000 (14:58 -0000)
committerMartin Willi <martin@strongswan.org>
Tue, 8 Jul 2008 14:58:20 +0000 (14:58 -0000)
guest->exec() accepts a callback for output
sligtly refactored mconsole.c

src/dumm/guest.c
src/dumm/guest.h
src/dumm/iface.c
src/dumm/irdumm.c
src/dumm/mconsole.c
src/dumm/mconsole.h
src/dumm/patches/mconsole-exec-2.6.26.patch

index 6aad150..c4cc7f1 100644 (file)
@@ -328,9 +328,10 @@ static bool load_template(private_guest_t *this, char *path)
 /**
  * Implementation of gues_t.exec
  */
-static int exec(private_guest_t *this, char *cmd, ...)
+static int exec(private_guest_t *this, void(*cb)(void*,char*), void *data,
+                               char *cmd, ...)
 {
-       char buf[512];
+       char buf[1024];
        size_t len;
        va_list args;
 
@@ -342,10 +343,10 @@ static int exec(private_guest_t *this, char *cmd, ...)
        
                if (len > 0 && len < sizeof(buf))
                {
-                       return this->mconsole->exec(this->mconsole, buf);
+                       return this->mconsole->exec(this->mconsole, cb, data, buf);
                }
        }
-       return FALSE;
+       return -1;
 }
 
 /**
@@ -472,7 +473,7 @@ static private_guest_t *guest_create_generic(char *parent, char *name,
        this->public.start = (void*)start;
        this->public.stop = (void*)stop;
        this->public.load_template = (bool(*)(guest_t*, char *path))load_template;
-       this->public.exec = (bool(*)(guest_t*, char *cmd, ...))exec;
+       this->public.exec = (int(*)(guest_t*, void(*cb)(void*,char*,size_t), void *data, char *cmd, ...))exec;
        this->public.sigchild = (void(*)(guest_t*))sigchild;
        this->public.destroy = (void*)destroy;
                
index c9406e1..1109622 100644 (file)
@@ -144,11 +144,14 @@ struct guest_t {
        /**
         * Execute a command in the guest.
         *
+        * @param cb            callback to call for each read block
+        * @param data          data to pass to callback
         * @param cmd           command to execute
         * @param ...           printf style argument list for cmd
-        * @return                      TRUE if command executed
+        * @return                      return value
         */
-       bool (*exec)(guest_t *this, char *cmd, ...);
+       int (*exec)(guest_t *this, void(*cb)(void*,char*,size_t), void *data,
+                               char *cmd, ...);
        
        /**
         * @brief Called whenever a SIGCHILD for the guests PID is received.
index fdfe50d..20a71f4 100644 (file)
@@ -105,8 +105,8 @@ static char* get_hostif(private_iface_t *this)
  */
 static bool add_address(private_iface_t *this, host_t *addr)
 {
-       if (this->guest->exec(this->guest, "ip addr add %H dev %s",
-                                                 addr, this->guestif))
+       if (this->guest->exec(this->guest, NULL, NULL, "ip addr add %H dev %s",
+                                                 addr, this->guestif) == 0)
        {
                this->addresses->insert_last(this->addresses, addr);
                return TRUE;
@@ -137,8 +137,8 @@ static bool delete_address(private_iface_t *this, host_t *addr)
        {
                if (current->ip_equals(current, addr))
                {
-                       if (this->guest->exec(this->guest, "ip addr del %H dev %s",
-                                                                 current, this->guestif))
+                       if (this->guest->exec(this->guest, NULL, NULL,
+                                               "ip addr del %H dev %s", current, this->guestif) == 0)
                        {
                                this->addresses->remove_at(this->addresses, enumerator);
                                success = TRUE;
@@ -157,11 +157,13 @@ static void set_bridge(private_iface_t *this, bridge_t *bridge)
 {
        if (this->bridge == NULL && bridge)
        {
-               this->guest->exec(this->guest, "ip link set %s up", this->guestif);
+               this->guest->exec(this->guest, NULL, NULL,
+                                                 "ip link set %s up", this->guestif);
        }
        else if (this->bridge && bridge == NULL)
        {
-               this->guest->exec(this->guest, "ip link set %s down", this->guestif);
+               this->guest->exec(this->guest, NULL, NULL,
+                                                 "ip link set %s down", this->guestif);
        }
        this->bridge = bridge;
 }
index e8cc12d..53443bf 100644 (file)
@@ -123,7 +123,6 @@ static VALUE guest_each(int argc, VALUE *argv, VALUE class)
 
 static VALUE guest_new(VALUE class, VALUE name, VALUE kernel,
                                           VALUE master, VALUE mem)
-
 {
        guest_t *guest;
        
@@ -170,12 +169,17 @@ static VALUE guest_stop(VALUE self)
        return self;
 }
 
+static void cb(void *data, char *buf, size_t len)
+{
+       printf("%.*s", len, buf);
+}
+
 static VALUE guest_exec(VALUE self, VALUE cmd)
 {
        guest_t *guest;
        
        Data_Get_Struct(self, guest_t, guest);
-       if (!guest->exec(guest, StringValuePtr(cmd)))
+       if (guest->exec(guest, cb, NULL, "%s", StringValuePtr(cmd)) != 0)
        {
                rb_raise(rb_eRuntimeError, "executing command failed");
        }
index 63e15c7..b44a8e5 100644 (file)
@@ -88,21 +88,21 @@ struct mconsole_notify {
 /**
  * send a request to UML using mconsole
  */
-static int request(private_mconsole_t *this, char *command,
-                                  char buf[], size_t *size)
+static int request(private_mconsole_t *this, void(*cb)(void*,char*,size_t),
+                                  void *data, char *command, ...)
 {
        mconsole_request request;
        mconsole_reply reply;
-       int len, total = 0, flags = 0;
+       int len, flags = 0;
+       va_list args;
        
        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)--;
-
+       va_start(args, command);
+       request.len = vsnprintf(request.data, sizeof(request.data), command, args);
+       va_end(args);
+       
        if (this->idle)
        {
                flags = MSG_DONTWAIT;
@@ -120,7 +120,7 @@ static int request(private_mconsole_t *this, char *command,
        
        if (len < 0)
        {
-               snprintf(buf, *size, "sending mconsole command to UML failed: %m");
+               DBG1("sending mconsole command to UML failed: %m");
                return -1;
        }
        do 
@@ -136,44 +136,50 @@ static int request(private_mconsole_t *this, char *command,
                }
                if (len < 0)
                {
-                       snprintf(buf, *size, "receiving from mconsole failed: %m");
+                       DBG1("receiving from mconsole failed: %m");
                        return -1;
                }
                if (len > 0)
                {
-                       strncat(buf, reply.data, min(reply.len, *size - total));
-                       total += reply.len;
+                       if (cb)
+                       {
+                               cb(data, reply.data, reply.len);
+                       }
+                       else if (reply.err)
+                       {
+                               DBG1("received mconsole error %d: %*.s",
+                                        reply.err, reply.len, reply.data);
+                               break;
+                       }
                }
        }
        while (reply.more);
        
-       *size = total;
        return reply.err;
 }
 
 /**
+ * ignore error message
+ */
+static void ignore(void *data, char *buf, size_t len)
+{
+}
+
+/**
  * Implementation of mconsole_t.add_iface.
  */
 static bool add_iface(private_mconsole_t *this, char *guest, char *host)
 {
-       char in[128], out[128];
-       int len, tries = 0;
+       int tries = 0;
        
-       len = snprintf(in, sizeof(in), "config %s=tuntap,%s", guest, host);
-       if (len < 0 || len >= sizeof(in))
+       while (tries++ < 5)
        {
-               return FALSE;
-       }
-       while (tries++ < 10)
-       {
-               len = sizeof(in);
-               if (request(this, in, out, &len) == 0)
+               if (request(this, ignore, NULL, "config %s=tuntap,%s", guest, host) == 0)
                {
                        return TRUE;
                }
-               usleep(10000 * tries);
+               usleep(10000 * tries * tries);
        }
-       DBG1("adding interface failed: %.*s", len, out);
        return FALSE;
 }
 
@@ -181,16 +187,8 @@ static bool add_iface(private_mconsole_t *this, char *guest, char *host)
  * Implementation of mconsole_t.del_iface.
  */
 static bool del_iface(private_mconsole_t *this, char *guest)
-{
-       char buf[128];
-       int len;
-       
-       len = snprintf(buf, sizeof(buf), "remove %s", guest);
-       if (len < 0 || len >= sizeof(buf))
-       {
-               return FALSE;
-       }
-       if (request(this, buf, buf, &len) != 0)
+{      
+       if (request(this, NULL, NULL, "remove %s", guest) != 0)
        {
                return FALSE;
        }
@@ -200,22 +198,14 @@ static bool del_iface(private_mconsole_t *this, char *guest)
 /**
  * Implementation of mconsole_t.exec
  */
-static bool exec(private_mconsole_t *this, char *cmd)
+static int exec(private_mconsole_t *this, void(*cb)(void*,char*,size_t),
+                               void *data, char *cmd)
 {
-       char buf[512];
-       int len;
-       
-       len = snprintf(buf, sizeof(buf), "exec %s", cmd);
-       if (len < 0 || len >= sizeof(buf))
+       if (request(this, cb, data, "exec %s", cmd) != 0)
        {
                return -1;
        }
-       if (request(this, buf, buf, &len) != 0)
-       {
-               DBG1("exec failed: %.*s", len, buf);
-               return FALSE;
-       }
-       return TRUE;
+       return 0;
 }
 
 /**
@@ -223,20 +213,18 @@ static bool exec(private_mconsole_t *this, char *cmd)
  */
 static bool wait_bootup(private_mconsole_t *this)
 {
-       char buf[128];
-       int len, res;
+       int res;
        
        while (TRUE)
        {
-               len = sizeof(buf);
-               res = request(this, "config eth9=mcast", buf, &len);
+               res = request(this, ignore, NULL, "config eth9=mcast");
                if (res < 0)
                {
                        return FALSE;
                }
                if (res == 0)
                {
-                       while (request(this, "remove eth9", buf, &len) != 0)
+                       while (request(this, ignore, NULL, "remove eth9") != 0)
                        {
                                usleep(50000);
                        }
@@ -359,7 +347,7 @@ mconsole_t *mconsole_create(char *notify, void(*idle)(void))
        
        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.exec = (bool(*)(mconsole_t*, char *cmd))exec;
+       this->public.exec = (int(*)(mconsole_t*,  void(*cb)(void*,char*,size_t), void *data, char *cmd))exec;
        this->public.destroy = (void*)destroy;
        
        this->idle = idle;
index a26e094..329c40c 100644 (file)
@@ -44,8 +44,14 @@ struct mconsole_t {
        
        /**
         * Execute a command in the UML host.
+        *
+        * @param cb                    callback function to invoke for each line
+        * @param data                  data to pass to callback
+        * @param cmd                   command to invoke
+        * @return                              return value of command
         */
-       bool (*exec)(mconsole_t *this, char *cmd);
+       int (*exec)(mconsole_t *this, void(*cb)(void*,char*,size_t), void *data,
+                               char *cmd);
        
        /**
         * @brief Destroy the mconsole instance
index 0ab47aa..6f9dcbf 100644 (file)
@@ -1,5 +1,5 @@
---- a/arch/um/drivers/mconsole_kern.c  2008-04-17 04:49:44.000000000 +0200
-+++ b/arch/um/drivers/mconsole_kern.c  2008-07-07 13:55:48.000000000 +0200
+--- linux-2.6.26rc5-orig/arch/um/drivers/mconsole_kern.c       2008-04-17 04:49:44.000000000 +0200
++++ uml-2.6.26rc5/arch/um/drivers/mconsole_kern.c      2008-07-08 16:00:07.000000000 +0200
 @@ -4,6 +4,7 @@
   * Licensed under the GPL
   */
@@ -8,41 +8,61 @@
  #include <linux/console.h>
  #include <linux/ctype.h>
  #include <linux/interrupt.h>
-@@ -199,6 +200,24 @@
+@@ -18,6 +19,7 @@
+ #include <linux/utsname.h>
+ #include <linux/workqueue.h>
+ #include <linux/mutex.h>
++#include <linux/file.h>
+ #include <asm/uaccess.h>
+ #include "init.h"
+@@ -199,6 +201,36 @@
  }
  #endif
  
 +void mconsole_exec(struct mc_request *req)
 +{
-+      int res;
++      int res, len;
++      struct file *out;
++      char buf[MCONSOLE_MAX_DATA];
 +
 +      char *envp[] = { "HOME=/", "TERM=linux", "PATH=/sbin:/usr/sbin:/bin:/usr/bin", NULL };
 +      char *argv[] = { "/bin/sh", "-c", req->request.data + strlen("exec "), NULL };
-+      res = call_usermodehelper("/bin/sh", argv, envp, 0);
-+
++      res = call_usermodehelper_pipe("/bin/sh", argv, envp, NULL, &out);
++      
 +      if (res < 0) {
-+      char buf[60];
-+              snprintf(buf, 60, "call_usermodehelper failed in mconsole_exec with error code: %d", -res);
-+              mconsole_reply(req, buf, 1, 0);
++              mconsole_reply(req, "call_usermodehelper_pipe failed", 1, 0);
 +              return;
 +      }
-+
-+      mconsole_reply(req, "The command has been started successfully.", 0, 0);
++      
++      for (;;) {
++              len = out->f_op->read(out, buf, sizeof(buf), 0);
++              if (len < 0) {
++                      mconsole_reply(req, "reading output failed", 1, 0);
++                      break;
++              }
++              if (len == 0) {
++                      mconsole_reply_len(req, buf, len, 0, 0);
++                      break;
++              }
++              mconsole_reply_len(req, buf, len, 0, 1);
++      }
++      fput(out);
 +}
 +
  void mconsole_proc(struct mc_request *req)
  {
        char path[64];
-@@ -270,6 +289,7 @@
+@@ -270,6 +302,7 @@
      stop - pause the UML; it will do nothing until it receives a 'go' \n\
      go - continue the UML after a 'stop' \n\
      log <string> - make UML enter <string> into the kernel log\n\
-+    exec <string> - pass <string> to /bin/sh -c in guest\n\
++    exec <string> - pass <string> to /bin/sh -c synchronously\n\
      proc <file> - returns the contents of the UML's /proc/<file>\n\
      stack <pid> - returns the stack of the specified pid\n\
  "
---- a/arch/um/drivers/mconsole_user.c  2008-05-21 18:34:47.000000000 +0200
-+++ b/arch/um/drivers/mconsole_user.c  2008-07-07 13:47:13.000000000 +0200
+--- linux-2.6.26rc5-orig/arch/um/drivers/mconsole_user.c       2008-05-21 18:34:47.000000000 +0200
++++ uml-2.6.26rc5/arch/um/drivers/mconsole_user.c      2008-07-07 13:47:13.000000000 +0200
 @@ -32,6 +32,7 @@
        { "stop", mconsole_stop, MCONSOLE_PROC },
        { "go", mconsole_go, MCONSOLE_INTR },
@@ -51,8 +71,8 @@
        { "proc", mconsole_proc, MCONSOLE_PROC },
        { "stack", mconsole_stack, MCONSOLE_INTR },
  };
---- a/arch/um/include/mconsole.h       2008-04-17 04:49:44.000000000 +0200
-+++ b/arch/um/include/mconsole.h       2008-07-07 13:46:56.000000000 +0200
+--- linux-2.6.26rc5-orig/arch/um/include/mconsole.h    2008-04-17 04:49:44.000000000 +0200
++++ uml-2.6.26rc5/arch/um/include/mconsole.h   2008-07-07 13:46:56.000000000 +0200
 @@ -85,6 +85,7 @@
  extern void mconsole_stop(struct mc_request *req);
  extern void mconsole_go(struct mc_request *req);
  extern void mconsole_proc(struct mc_request *req);
  extern void mconsole_stack(struct mc_request *req);
  
+--- linux-2.6.26rc5-orig/kernel/kmod.c 2008-05-21 18:34:56.000000000 +0200
++++ uml-2.6.26rc5/kernel/kmod.c        2008-07-08 13:50:37.000000000 +0200
+@@ -125,6 +125,7 @@
+       enum umh_wait wait;
+       int retval;
+       struct file *stdin;
++      struct file *stdout;
+       void (*cleanup)(char **argv, char **envp);
+ };
+@@ -160,8 +161,26 @@
+               FD_SET(0, fdt->open_fds);
+               FD_CLR(0, fdt->close_on_exec);
+               spin_unlock(&f->file_lock);
+-
+-              /* and disallow core files too */
++      }
++      if (sub_info->stdout) {
++              struct files_struct *f = current->files;
++              struct fdtable *fdt;
++              
++              sys_close(1);
++              sys_close(2);
++              get_file(sub_info->stdout);
++              fd_install(1, sub_info->stdout);
++              fd_install(2, sub_info->stdout);
++              spin_lock(&f->file_lock);
++              fdt = files_fdtable(f);
++              FD_SET(1, fdt->open_fds);
++              FD_CLR(1, fdt->close_on_exec);
++              FD_SET(2, fdt->open_fds);
++              FD_CLR(2, fdt->close_on_exec);
++              spin_unlock(&f->file_lock);
++      }
++      if (sub_info->stdin || sub_info->stdout) {
++              /* disallow core files */
+               current->signal->rlim[RLIMIT_CORE] = (struct rlimit){0, 0};
+       }
+@@ -433,6 +452,29 @@
+ }
+ EXPORT_SYMBOL(call_usermodehelper_stdinpipe);
++int call_usermodehelper_stdoutpipe(struct subprocess_info *sub_info,
++                                struct file **filp)
++{
++      struct file *f;
++
++      f = create_write_pipe();
++      if (IS_ERR(f))
++              return PTR_ERR(f);
++      sub_info->stdout = f;
++
++      f = create_read_pipe(f);
++      if (IS_ERR(f)) {
++              free_write_pipe(sub_info->stdout);
++              sub_info->stdout = NULL;
++              return PTR_ERR(f);
++      }
++      *filp = f;
++
++      return 0;
++}
++EXPORT_SYMBOL(call_usermodehelper_stdoutpipe);
++
++
+ /**
+  * call_usermodehelper_exec - start a usermode application
+  * @sub_info: information about the subprocessa
+@@ -489,7 +531,7 @@
+  * lower-level call_usermodehelper_* functions.
+  */
+ int call_usermodehelper_pipe(char *path, char **argv, char **envp,
+-                           struct file **filp)
++                           struct file **in, struct file **out)
+ {
+       struct subprocess_info *sub_info;
+       int ret;
+@@ -498,9 +540,17 @@
+       if (sub_info == NULL)
+               return -ENOMEM;
+-      ret = call_usermodehelper_stdinpipe(sub_info, filp);
+-      if (ret < 0)
+-              goto out;
++      if (in) {
++              ret = call_usermodehelper_stdinpipe(sub_info, in);
++              if (ret < 0)
++                      goto out;
++      }
++
++      if (out) {
++              ret = call_usermodehelper_stdoutpipe(sub_info, out);
++              if (ret < 0)
++                      goto out;
++      }
+       return call_usermodehelper_exec(sub_info, UMH_WAIT_EXEC);
+--- linux-2.6.26rc5-orig/include/linux/kmod.h  2008-04-17 04:49:44.000000000 +0200
++++ uml-2.6.26rc5/include/linux/kmod.h 2008-07-08 10:29:29.000000000 +0200
+@@ -93,6 +93,6 @@
+ struct file;
+ extern int call_usermodehelper_pipe(char *path, char *argv[], char *envp[],
+-                                  struct file **filp);
++                                  struct file **in, struct file **out);
+ #endif /* __LINUX_KMOD_H__ */
+--- linux-2.6.26rc5-orig/fs/exec.c     2008-06-05 14:00:42.000000000 +0200
++++ uml-2.6.26rc5/fs/exec.c    2008-07-08 10:28:33.000000000 +0200
+@@ -1737,7 +1737,7 @@
+               /* SIGPIPE can happen, but it's just never processed */
+               if (call_usermodehelper_pipe(corename+1, helper_argv, NULL,
+-                              &file)) {
++                              &file, NULL)) {
+                       printk(KERN_INFO "Core dump to %s pipe failed\n",
+                              corename);
+                       goto fail_unlock;