diff --git a/ChangeLog b/ChangeLog
index 29f1818d6d..f5c5af06a0 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+Sat Aug 26 16:36:15 CEST 2006 Daniel Veillard <veillard@redhat.com>
+
+	* src/sexpr.c src/sexpr.h src/xend_internal.c src/xml.c: applied
+	  patch from Jeremy Katz to add graphical console for PV Xen guests
+
 Sat Aug 26 00:17:24 CEST 2006 Daniel Veillard <veillard@redhat.com>
 
 	* docs/site.xsl docs/*.html: add links to virt-manager
diff --git a/src/sexpr.c b/src/sexpr.c
index baf034142c..f3966da579 100644
--- a/src/sexpr.c
+++ b/src/sexpr.c
@@ -486,3 +486,27 @@ sexpr_node(struct sexpr *sexpr, const char *node)
 
     return (n && n->car->kind == SEXPR_VALUE) ? n->car->value : NULL;
 }
+
+/**
+ * sexpr_fmt_node:
+ * @sexpr: a pointer to a parsed S-Expression
+ * @fmt: a path for the node to lookup in the S-Expression
+ * @... extra data to build the path
+ *
+ * Search a node value in the S-Expression based on its path
+ * NOTE: path are limited to 4096 bytes.
+ *
+ * Returns the value of the node or NULL if not found.
+ */
+const char *
+sexpr_fmt_node(struct sexpr *sexpr, const char *fmt, ...)
+{
+    va_list ap;
+    char node[4096];
+
+    va_start(ap, fmt);
+    vsnprintf(node, sizeof(node), fmt, ap);
+    va_end(ap);
+
+    return sexpr_node(sexpr, node);
+}
diff --git a/src/sexpr.h b/src/sexpr.h
index 3026ee54fb..6c9048715c 100644
--- a/src/sexpr.h
+++ b/src/sexpr.h
@@ -45,5 +45,6 @@ void sexpr_free(struct sexpr *sexpr);
 
 /* lookup in S-Expressions */
 const char *sexpr_node(struct sexpr *sexpr, const char *node);
+const char *sexpr_fmt_node(struct sexpr *sexpr, const char *fmt, ...);
 struct sexpr *sexpr_lookup(struct sexpr *sexpr, const char *node);
 #endif
diff --git a/src/xend_internal.c b/src/xend_internal.c
index c398797a9c..48a5c5bde3 100644
--- a/src/xend_internal.c
+++ b/src/xend_internal.c
@@ -1618,23 +1618,23 @@ xend_parse_sexp_desc(virConnectPtr conn, struct sexpr *root)
             virBufferAdd(&buf, "      <readonly/>\n", 18);
             virBufferAdd(&buf, "    </disk>\n", 12);
         }
+    }
         
-        /* Graphics device */
-        tmp = sexpr_node(root, "domain/image/hvm/vnc");
-        if (tmp != NULL) {
-            if (tmp[0] == '1') {
-                int port = xenStoreDomainGetVNCPort(conn, domid);
-                if (port == -1) 
-                    port = 5900 + domid;
-                virBufferVSprintf(&buf, "    <graphics type='vnc' port='%d'/>\n", port);
-            }
+    /* Graphics device */
+    tmp = sexpr_fmt_node(root, "domain/image/%s/vnc", hvm ? "hvm" : "linux");
+    if (tmp != NULL) {
+        if (tmp[0] == '1') {
+            int port = xenStoreDomainGetVNCPort(conn, domid);
+            if (port == -1) 
+                port = 5900 + domid;
+            virBufferVSprintf(&buf, "    <graphics type='vnc' port='%d'/>\n", port);
         }
+    }
         
-        tmp = sexpr_node(root, "domain/image/hvm/sdl");
-        if (tmp != NULL) {
-           if (tmp[0] == '1')
-               virBufferAdd(&buf, "    <graphics type='sdl'/>\n", 27 );
-        }
+    tmp = sexpr_fmt_node(root, "domain/image/%s/sdl", hvm ? "hvm" : "linux");
+    if (tmp != NULL) {
+        if (tmp[0] == '1')
+            virBufferAdd(&buf, "    <graphics type='sdl'/>\n", 27 );
     }
     
     tty = xenStoreDomainGetConsolePath(conn, domid);
diff --git a/src/xml.c b/src/xml.c
index d724ad6fec..cc6d9dd887 100644
--- a/src/xml.c
+++ b/src/xml.c
@@ -570,6 +570,40 @@ virDomainGetXMLDesc(virDomainPtr domain, int flags)
 #endif /* 0 - UNUSED */
 
 #ifndef PROXY
+/**
+ * virtDomainParseXMLGraphicsDesc:
+ * @node: node containing graphics description
+ * @buf: a buffer for the result S-Expr
+ *
+ * Parse the graphics part of the XML description and add it to the S-Expr 
+ * in buf.  This is a temporary interface as the S-Expr interface will be 
+ * replaced by XML-RPC in the future. However the XML format should stay 
+ * valid over time.
+ *
+ * Returns 0 in case of success, -1 in case of error
+ */
+static int virDomainParseXMLGraphicsDesc(xmlNodePtr node, virBufferPtr buf)
+{
+    xmlChar *graphics_type = NULL;
+
+    graphics_type = xmlGetProp(node, BAD_CAST "type");
+    if (graphics_type != NULL) {
+        if (xmlStrEqual(graphics_type, BAD_CAST "sdl")) {
+            virBufferAdd(buf, "(sdl 1)", 7);
+            // TODO:
+            // Need to understand sdl options
+            //
+            //virBufferAdd(buf, "(display localhost:10.0)", 24);
+            //virBufferAdd(buf, "(xauthority /root/.Xauthority)", 30);
+        }
+        else if (xmlStrEqual(graphics_type, BAD_CAST "vnc"))
+            virBufferAdd(buf, "(vnc 1)", 7);
+        xmlFree(graphics_type);
+    }
+    return 0;
+}
+
+
 /**
  * virDomainParseXMLOSDescHVM:
  * @node: node containing HVM OS description
@@ -591,7 +625,7 @@ virDomainParseXMLOSDescHVM(xmlNodePtr node, virBufferPtr buf, xmlXPathContextPtr
     const xmlChar *type = NULL;
     const xmlChar *loader = NULL;
     const xmlChar *boot_dev = NULL;
-    xmlChar *graphics_type = NULL;
+    int res;
 
     cur = node->children;
     while (cur != NULL) {
@@ -733,24 +767,11 @@ virDomainParseXMLOSDescHVM(xmlNodePtr node, virBufferPtr buf, xmlXPathContextPtr
     /* Is a graphics device specified? */
     obj = xmlXPathEval(BAD_CAST "/domain/devices/graphics[1]", ctxt);
     if ((obj != NULL) && (obj->type == XPATH_NODESET) &&
-        (obj->nodesetval != NULL) && (obj->nodesetval->nodeNr = 0)) {
-        virXMLError(VIR_ERR_NO_OS, "", 0); /* TODO: error */
-        goto error;
-    }
-
-    graphics_type = xmlGetProp(obj->nodesetval->nodeTab[0], BAD_CAST "type");
-    if (graphics_type != NULL) {
-        if (xmlStrEqual(graphics_type, BAD_CAST "sdl")) {
-            virBufferAdd(buf, "(sdl 1)", 7);
-            // TODO:
-            // Need to understand sdl options
-            //
-            //virBufferAdd(buf, "(display localhost:10.0)", 24);
-            //virBufferAdd(buf, "(xauthority /root/.Xauthority)", 30);
+        (obj->nodesetval != NULL) && (obj->nodesetval->nodeNr > 0)) {
+        res = virDomainParseXMLGraphicsDesc(obj->nodesetval->nodeTab[0], buf);
+        if (res != 0) {
+            goto error;
         }
-        else if (xmlStrEqual(graphics_type, BAD_CAST "vnc"))
-            virBufferAdd(buf, "(vnc 1)", 7);
-        xmlFree(graphics_type);
     }
     xmlXPathFreeObject(obj);
 
@@ -767,6 +788,7 @@ error:
  * virDomainParseXMLOSDescPV:
  * @node: node containing PV OS description
  * @buf: a buffer for the result S-Expr
+ * @ctxt: a path context representing the XML description
  *
  * Parse the OS part of the XML description for a paravirtualized domain
  * and add it to the S-Expr in buf.  This is a temporary interface as the
@@ -776,14 +798,16 @@ error:
  * Returns 0 in case of success, -1 in case of error.
  */
 static int
-virDomainParseXMLOSDescPV(xmlNodePtr node, virBufferPtr buf)
+virDomainParseXMLOSDescPV(xmlNodePtr node, virBufferPtr buf, xmlXPathContextPtr ctxt)
 {
     xmlNodePtr cur, txt;
+    xmlXPathObjectPtr obj = NULL;
     const xmlChar *type = NULL;
     const xmlChar *root = NULL;
     const xmlChar *kernel = NULL;
     const xmlChar *initrd = NULL;
     const xmlChar *cmdline = NULL;
+    int res;
 
     cur = node->children;
     while (cur != NULL) {
@@ -840,6 +864,19 @@ virDomainParseXMLOSDescPV(xmlNodePtr node, virBufferPtr buf)
         virBufferVSprintf(buf, "(root '%s')", (const char *) root);
     if (cmdline != NULL)
         virBufferVSprintf(buf, "(args '%s')", (const char *) cmdline);
+
+    /* Is a graphics device specified? */
+    obj = xmlXPathEval(BAD_CAST "/domain/devices/graphics[1]", ctxt);
+    if ((obj != NULL) && (obj->type == XPATH_NODESET) &&
+        (obj->nodesetval != NULL) && (obj->nodesetval->nodeNr > 0)) {
+        res = virDomainParseXMLGraphicsDesc(obj->nodesetval->nodeTab[0], buf);
+        if (res != 0) {
+            goto error;
+        }
+    }
+    xmlXPathFreeObject(obj);
+
+ error:
     virBufferAdd(buf, "))", 2);
     return (0);
 }
@@ -1177,7 +1214,7 @@ virDomainParseXMLDesc(const char *xmldesc, char **name)
 	}
 
 	if ((tmpobj == NULL) || !xmlStrEqual(tmpobj->stringval, BAD_CAST "hvm")) {
-	    res = virDomainParseXMLOSDescPV(obj->nodesetval->nodeTab[0], &buf);
+	    res = virDomainParseXMLOSDescPV(obj->nodesetval->nodeTab[0], &buf, ctxt);
 	} else {
 	    hvm = 1;
 	    res = virDomainParseXMLOSDescHVM(obj->nodesetval->nodeTab[0], &buf, ctxt);