qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Qemu-devel] [PATCH] Add script hook for VNC server to support mDNS/


From: Anthony Liguori
Subject: Re: [Qemu-devel] [PATCH] Add script hook for VNC server to support mDNS/DNS-SD
Date: Thu, 07 Feb 2008 17:00:03 -0600
User-agent: Thunderbird 2.0.0.9 (X11/20071229)

This version is still a bit chatty when -name isn't specified so attached is an updated version of the patch which is less chatty when -name isn't specified.

Regards,

Anthony Liguori

Anthony Liguori wrote:
Modern DNS clients (like vinagre) support mDNS/DNS-SD discovery of VNC servers.
VNC servers like Vino link directly against libavahi to publish themselves.
Fitting libavahi into QEMU though would be pretty hairy and not as flexible as
simply calling out to a helper script and using the avahi utilities.

This patch adds support for a ',script=/patch/to/script' option to the existing
 -vnc option.  It also includes an example script which uses avahi-publish the
presence of the QEMU instance.  Care has been taken to ensure that errors are
silenced if either bash or avahi aren't present and the user hasn't explicitly
specified ',script='.

Subject: [PATCH] Add script hook for VNC server to support mDNS/DNS-SD

Modern DNS clients (like vinagre) support mDNS/DNS-SD discovery of VNC servers.
VNC servers like Vino link directly against libavahi to publish themselves.
Fitting libavahi into QEMU though would be pretty hairy and not as flexible as
simply calling out to a helper script and using the avahi utilities.

This patch adds support for a ',script=/patch/to/script' option to the existing
 -vnc option.  It also includes an example script which uses avahi-publish the
presence of the QEMU instance.  Care has been taken to ensure that errors are
silenced if either bash or avahi aren't present and the user hasn't explicitly
specified ',script='.

diff --git a/qemu-doc.texi b/qemu-doc.texi
index f9924d2..cd59aab 100644
--- a/qemu-doc.texi
+++ b/qemu-doc.texi
@@ -486,6 +486,21 @@ path following this option specifies where the x509 
certificates are to
 be loaded from. See the @ref{vnc_security} section for details on generating
 certificates.
 
address@hidden address@hidden/path/to/script}
+
+Launch a script when the VNC server is started.  The script is expected to
+continue running as long as the VNC server is running.  When the VNC server
+closes, the script will receive either a SIGTERM or standard input will be
+shutdown.  The script will be passed arguments in the order of @var{NAME}
address@hidden @var{PORT} where @var{NAME} is the value passed to the
address@hidden option, @var{ADDRESS} is the address the server is bound to,
+and @var{PORT} is the port the server is listening on.
+
+This script will not be executed if @var{reverse} is being used or if the
+server is listening on a Unix domain socket.  If the @option{-name} option is
+not specified, the script will not be executed.  The default location of the
+script is @file{/etc/qemu-vncup}.
+
 @end table
 
 @item -k @var{language}
diff --git a/qemu-vncup b/qemu-vncup
new file mode 100755
index 0000000..78b9d2d
--- /dev/null
+++ b/qemu-vncup
@@ -0,0 +1,27 @@
+#!/bin/bash
+
+function dokill() {
+    kill -SIGTERM $1 >& /dev/null
+}
+
+if [ -z "$1" -o -z "$3" ]; then
+    echo "Usage: $0 NAME ADDRESS PORT"
+    exit 1
+fi
+
+which avahi-publish >& /dev/null
+if [ $? != 0 ] ; then
+    exit 0
+fi
+
+avahi-publish -s "$1 Virtual Console" '_rfb._tcp' $3 > /dev/null 2>&1 < 
/dev/null &
+
+pid=`jobs -p %1`
+
+trap "dokill -SIGTERM $pid; exit" SIGINT SIGTERM
+
+while read LINE ; do
+    echo $LINE
+done
+
+dokill -SIGTERM $pid
diff --git a/vnc.c b/vnc.c
index e7f3255..8d68770 100644
--- a/vnc.c
+++ b/vnc.c
@@ -31,6 +31,8 @@
 
 #define VNC_REFRESH_INTERVAL (1000 / 30)
 
+#define VNC_DEFAULT_SCRIPT "/etc/qemu-vncup"
+
 #include "vnc_keysym.h"
 #include "keymaps.c"
 #include "d3des.h"
@@ -40,6 +42,11 @@
 #include <gnutls/x509.h>
 #endif /* CONFIG_VNC_TLS */
 
+#ifndef _WIN32
+#include <signal.h>
+#include <sys/wait.h>
+#endif
+
 // #define _VNC_DEBUG 1
 
 #if _VNC_DEBUG
@@ -174,6 +181,11 @@ struct VncState
     size_t read_handler_expect;
     /* input */
     uint8_t modifiers_state[256];
+
+#ifndef _WIN32
+    pid_t script_pid;
+    int script_fd;
+#endif
 };
 
 static VncState *vnc_state; /* needed for info vnc */
@@ -2056,6 +2068,13 @@ void vnc_display_close(DisplayState *ds)
        vs->wiremode = VNC_WIREMODE_CLEAR;
 #endif /* CONFIG_VNC_TLS */
     }
+#ifndef _WIN32
+    if (vs->script_pid) {
+       close(vs->script_fd);
+       while (waitpid(vs->script_pid, NULL, 0) == -1 && errno == EINTR);
+       vs->script_pid = -1;
+    }
+#endif
     vs->auth = VNC_AUTH_INVALID;
 #if CONFIG_VNC_TLS
     vs->subauth = VNC_AUTH_INVALID;
@@ -2079,6 +2098,54 @@ int vnc_display_password(DisplayState *ds, const char 
*password)
     return 0;
 }
 
+#ifndef _WIN32
+static int launch_vnc_script(VncState *vs, const char *script,
+                            const char *name, const char *addr,
+                            int port)
+{
+    int fds[2];
+    int quiet = 0;
+
+    vs->script_pid = 0;
+
+    if (pipe(fds) == -1) {
+       fprintf(stderr, "qemu: pipe failed\n");
+       return -1;
+    }
+
+    if (script == NULL) {
+       quiet = 1;
+       script = strdup(VNC_DEFAULT_SCRIPT);
+    }
+
+    vs->script_pid = fork();
+    vs->script_fd = fds[1];
+    if (vs->script_pid == 0) {
+       int fd;
+       char port_str[32];
+
+       dup2(fds[0], STDIN_FILENO);
+       for (fd = 3; fd < sysconf(_SC_OPEN_MAX); fd++)
+           close(fd);
+
+       snprintf(port_str, sizeof(port_str), "%d", port);
+       execlp(script, script, name, addr, port_str, NULL);
+       if (!quiet)
+           fprintf(stderr, "qemu: failed to exec vnc script: %m\n");
+       exit(1);
+    } else if (vs->script_pid < 0) {
+       fprintf(stderr, "qemu: fork failed\n");
+       return -1;
+    } else
+       close(fds[0]);
+
+    if (quiet)
+       qemu_free((char *)script);
+
+    return 0;
+}
+#endif
+
 int vnc_display_open(DisplayState *ds, const char *display)
 {
     struct sockaddr *addr;
@@ -2096,6 +2163,8 @@ int vnc_display_open(DisplayState *ds, const char 
*display)
 #if CONFIG_VNC_TLS
     int tls = 0, x509 = 0;
 #endif
+    char *script = NULL;
+    char *bind_address = NULL;
 
     vnc_display_close(ds);
     if (strcmp(display, "none") == 0)
@@ -2111,6 +2180,17 @@ int vnc_display_open(DisplayState *ds, const char 
*display)
            password = 1; /* Require password auth */
        } else if (strncmp(options, "reverse", 7) == 0) {
            reverse = 1;
+       } else if (strncmp(options, "script=", 7) == 0) {
+           const char *ptr, *end;
+
+           ptr = options + 7;
+           end = strchr(ptr, ',');
+           if (end == NULL)
+               end = ptr + strlen(ptr);
+
+           script = qemu_malloc((end - ptr) + 1);
+           memcpy(script, ptr, end - ptr);
+           script[end - ptr] = 0;
 #if CONFIG_VNC_TLS
        } else if (strncmp(options, "tls", 3) == 0) {
            tls = 1; /* Require TLS */
@@ -2210,6 +2290,8 @@ int vnc_display_open(DisplayState *ds, const char 
*display)
     } else
 #endif
     {
+       char *ptr;
+
        addr = (struct sockaddr *)&iaddr;
        addrlen = sizeof(iaddr);
 
@@ -2222,6 +2304,18 @@ int vnc_display_open(DisplayState *ds, const char 
*display)
 
        iaddr.sin_port = htons(ntohs(iaddr.sin_port) + (reverse ? 0 : 5900));
 
+       bind_address = strdup(display);
+       if (bind_address == NULL) {
+           fprintf(stderr, "qemu: strdup failed\n");
+           return -1;
+       }
+
+       ptr = strchr(bind_address, ':');
+       if (ptr == NULL)
+           ptr = strchr(bind_address, ',');
+       if (ptr)
+           *ptr = 0;
+
        vs->lsock = socket(PF_INET, SOCK_STREAM, 0);
        if (vs->lsock == -1) {
            fprintf(stderr, "Could not create socket\n");
@@ -2277,5 +2371,27 @@ int vnc_display_open(DisplayState *ds, const char 
*display)
        return -1;
     }
 
+#ifndef _WIN32
+    if (addr->sa_family != AF_INET) {
+       if (script)
+           fprintf(stderr, "qemu: cannot call vnc script with unix socket\n");
+    } else if (reverse) {
+       if (script)
+           fprintf(stderr,
+                   "qemu: cannot call vnc script with reverse connect\n");
+    } else if (!qemu_name) {
+       if (script)
+           fprintf(stderr, "qemu: cannot call vnc script without -name\n");
+    } else {
+        ret = launch_vnc_script(vs, script, qemu_name, bind_address,
+                               ntohs(iaddr.sin_port));
+       if (ret == -1)
+           return -1;
+    }
+#endif
+
+    qemu_free(script);
+    qemu_free(bind_address);
+
     return qemu_set_fd_handler2(vs->lsock, vnc_listen_poll, vnc_listen_read, 
NULL, vs);
 }

reply via email to

[Prev in Thread] Current Thread [Next in Thread]