commit 92a9e5df6369b243f8db99fa229c445796ad83d3
Author: Laine Stump <laine@laine.org>
Date:   Tue Mar 30 13:44:22 2010 +0200

    Implement XML parser/formatter for "timer" subelement of domain clock
    
    This extension is described in
    
    http://www.redhat.com/archives/libvir-list/2010-March/msg00304.html
    
    Currently all attributes are optional, except name.
    
    * src/conf/domain_conf.h: add data definition for virDomainTimerDef
      and add a list of them to virDomainClockDef
    * src/conf/domain_conf.c: XML parser and formatter for a timer inside a clock
    * src/libvirt_private.syms: add new Timer enum helper functions to symbols

Index: libvirt-0.7.6/src/conf/domain_conf.c
===================================================================
--- libvirt-0.7.6.orig/src/conf/domain_conf.c
+++ libvirt-0.7.6/src/conf/domain_conf.c
@@ -237,6 +237,29 @@ VIR_ENUM_IMPL(virDomainClockOffset, VIR_
               "variable",
               "timezone");
 
+VIR_ENUM_IMPL(virDomainTimerName, VIR_DOMAIN_TIMER_NAME_LAST,
+              "platform",
+              "pit",
+              "rtc",
+              "hpet",
+              "tsc");
+
+VIR_ENUM_IMPL(virDomainTimerWallclock, VIR_DOMAIN_TIMER_WALLCLOCK_LAST,
+              "host",
+              "guest");
+
+VIR_ENUM_IMPL(virDomainTimerTickpolicy, VIR_DOMAIN_TIMER_TICKPOLICY_LAST,
+              "delay",
+              "catchup",
+              "merge",
+              "discard");
+
+VIR_ENUM_IMPL(virDomainTimerMode, VIR_DOMAIN_TIMER_MODE_LAST,
+              "auto",
+              "native",
+              "emulate",
+              "paravirt");
+
 #define virDomainReportError(conn, code, fmt...)                           \
         virReportErrorHelper(conn, VIR_FROM_DOMAIN, code, __FILE__,        \
                                __FUNCTION__, __LINE__, fmt)
@@ -580,6 +603,18 @@ void virSecurityLabelDefFree(virDomainDe
     VIR_FREE(def->seclabel.imagelabel);
 }
 
+static void
+virDomainClockDefClear(virDomainClockDefPtr def)
+{
+    if (def->offset == VIR_DOMAIN_CLOCK_OFFSET_TIMEZONE)
+        VIR_FREE(def->data.timezone);
+
+    int i;
+    for (i = 0; i < def->ntimers; i++)
+        VIR_FREE(def->timers[i]);
+    VIR_FREE(def->timers);
+}
+
 void virDomainDefFree(virDomainDefPtr def)
 {
     unsigned int i;
@@ -649,8 +684,7 @@ void virDomainDefFree(virDomainDefPtr de
     VIR_FREE(def->os.bootloader);
     VIR_FREE(def->os.bootloaderArgs);
 
-    if (def->clock.offset == VIR_DOMAIN_CLOCK_OFFSET_TIMEZONE)
-        VIR_FREE(def->clock.data.timezone);
+    virDomainClockDefClear(&def->clock);
 
     VIR_FREE(def->name);
     VIR_FREE(def->cpumask);
@@ -2354,6 +2388,108 @@ error:
 }
 
 
+/* Parse the XML definition for a clock timer */
+static virDomainTimerDefPtr
+virDomainTimerDefParseXML(virConnectPtr conn,
+                          const xmlNodePtr node,
+                          xmlXPathContextPtr ctxt,
+                          int flags ATTRIBUTE_UNUSED)
+{
+    char *name = NULL;
+    char *present = NULL;
+    char *tickpolicy = NULL;
+    char *wallclock = NULL;
+    char *mode = NULL;
+
+    virDomainTimerDefPtr def;
+    xmlNodePtr oldnode = ctxt->node;
+    
+    if (VIR_ALLOC(def) < 0) {
+        virReportOOMError(NULL);
+        return NULL;
+    }
+    
+    ctxt->node = node;
+
+    name = virXMLPropString(node, "name");
+    if (name == NULL) {
+        virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                             "%s", _("missing timer name"));
+        goto error;
+    }
+    if ((def->name = virDomainTimerNameTypeFromString(name)) < 0) {
+        virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                             _("unknown timer name '%s'"), name);
+        goto error;
+    }
+
+    def->present = -1; /* unspecified */
+    if ((present = virXMLPropString(node, "present")) != NULL) {
+        if (STREQ(present, "yes")) {
+            def->present = 1;
+        } else if (STREQ(present, "no")) {
+            def->present = 0;
+        } else {
+            virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                                 _("unknown timer present value '%s'"), present);
+            goto error;
+        }
+    }
+
+    def->tickpolicy = -1;
+    tickpolicy = virXMLPropString(node, "tickpolicy");
+    if (tickpolicy != NULL) {
+        if ((def->tickpolicy = virDomainTimerTickpolicyTypeFromString(tickpolicy)) < 0) {
+            virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                                 _("unknown timer tickpolicy '%s'"), tickpolicy);
+            goto error;
+        }
+    }
+
+    def->wallclock = -1;
+    wallclock = virXMLPropString(node, "wallclock");
+    if (wallclock != NULL) {
+        if ((def->wallclock = virDomainTimerWallclockTypeFromString(wallclock)) < 0) {
+            virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                                 _("unknown timer wallclock '%s'"), wallclock);
+            goto error;
+        }
+    }
+
+    int ret = virXPathULong(conn, "string(./frequency)", ctxt, &def->frequency);
+    if (ret == -1) {
+        def->frequency = 0;
+    } else if (ret <= 0) {
+        virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                             "%s", _("invalid timer frequency"));
+        goto error;
+    }
+
+    def->mode = -1;
+    mode = virXMLPropString(node, "mode");
+    if (mode != NULL) {
+        if ((def->mode = virDomainTimerModeTypeFromString(mode)) < 0) {
+            virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                                 _("unknown timer mode '%s'"), mode);
+            goto error;
+        }
+    }
+
+cleanup:
+    VIR_FREE(name);
+    VIR_FREE(present);
+    VIR_FREE(tickpolicy);
+    VIR_FREE(wallclock);
+    VIR_FREE(mode);
+    ctxt->node = oldnode;
+
+    return def;
+
+error:
+    VIR_FREE(def);
+    goto cleanup;
+}
+
 /* Parse the XML definition for a graphics device */
 static virDomainGraphicsDefPtr
 virDomainGraphicsDefParseXML(virConnectPtr conn,
@@ -3499,7 +3635,6 @@ static virDomainDefPtr virDomainDefParse
                                    virDomainLifecycleCrashTypeFromString) < 0)
         goto error;
 
-
     tmp = virXPathString(conn, "string(./clock/@offset)", ctxt);
     if (tmp) {
         if ((def->clock.offset = virDomainClockOffsetTypeFromString(tmp)) < 0) {
@@ -3528,6 +3663,25 @@ static virDomainDefPtr virDomainDefParse
         break;
     }
 
+    if ((n = virXPathNodeSet(conn, "./clock/timer", ctxt, &nodes)) < 0) {
+        virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                             "%s", _("failed to parse timers"));
+        goto error;
+    }
+    if (n && VIR_ALLOC_N(def->clock.timers, n) < 0)
+        goto no_memory;
+    for (i = 0 ; i < n ; i++) {
+        virDomainTimerDefPtr timer = virDomainTimerDefParseXML(conn,
+                                                               nodes[i],
+                                                               ctxt,
+                                                               flags);
+        if (!timer)
+            goto error;
+
+        def->clock.timers[def->clock.ntimers++] = timer;
+    }
+    VIR_FREE(nodes);
+
     def->os.bootloader = virXPathString(conn, "string(./bootloader)", ctxt);
     def->os.bootloaderArgs = virXPathString(conn, "string(./bootloader_args)", ctxt);
 
@@ -5279,6 +5433,76 @@ virDomainInputDefFormat(virConnectPtr co
 
 
 static int
+virDomainTimerDefFormat(virConnectPtr conn,
+                        virBufferPtr buf,
+                        virDomainTimerDefPtr def)
+{
+    const char *name = virDomainTimerNameTypeToString(def->name);
+
+    if (!name) {
+        virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                             _("unexpected timer name %d"), def->name);
+        return -1;
+    }
+    virBufferVSprintf(buf, "    <timer name='%s'", name);
+
+    if (def->present == 0) {
+        virBufferAddLit(buf, " present='no'");
+    } else if (def->present == 1) {
+        virBufferAddLit(buf, " present='yes'");
+    }
+
+    if (def->tickpolicy != -1) {
+        const char *tickpolicy
+            = virDomainTimerTickpolicyTypeToString(def->tickpolicy);
+        if (!tickpolicy) {
+            virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                                 _("unexpected timer tickpolicy %d"),
+                                 def->tickpolicy);
+            return -1;
+        }
+        virBufferVSprintf(buf, " tickpolicy='%s'", tickpolicy);
+    }
+
+    if ((def->name == VIR_DOMAIN_TIMER_NAME_PLATFORM)
+        || (def->name == VIR_DOMAIN_TIMER_NAME_RTC)) {
+        if (def->wallclock != -1) {
+            const char *wallclock
+                = virDomainTimerWallclockTypeToString(def->wallclock);
+            if (!wallclock) {
+                virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                                     _("unexpected timer wallclock %d"),
+                                     def->wallclock);
+                return -1;
+            }
+            virBufferVSprintf(buf, " wallclock='%s'", wallclock);
+        }
+    }
+
+    if (def->name == VIR_DOMAIN_TIMER_NAME_TSC) {
+        if (def->frequency > 0) {
+            virBufferVSprintf(buf, " frequency='%lu'", def->frequency);
+        }
+
+        if (def->mode != -1) {
+            const char *mode
+                = virDomainTimerModeTypeToString(def->mode);
+            if (!mode) {
+                virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                                     _("unexpected timer mode %d"),
+                                     def->mode);
+                return -1;
+            }
+            virBufferVSprintf(buf, " mode='%s'", mode);
+        }
+    }
+
+    virBufferAddLit(buf, "/>\n");
+
+    return 0;
+}
+
+static int
 virDomainGraphicsDefFormat(virConnectPtr conn,
                            virBufferPtr buf,
                            virDomainGraphicsDefPtr def,
@@ -5579,7 +5803,16 @@ char *virDomainDefFormat(virConnectPtr c
         virBufferEscapeString(&buf, " timezone='%s'", def->clock.data.timezone);
         break;
     }
-    virBufferAddLit(&buf, "/>\n");
+    if (def->clock.ntimers == 0) {
+        virBufferAddLit(&buf, "/>\n");
+    } else {
+        virBufferAddLit(&buf, ">\n");
+        for (n = 0; n < def->clock.ntimers; n++) {
+            if (virDomainTimerDefFormat(conn, &buf, def->clock.timers[n]) < 0)
+                goto cleanup;
+        }
+        virBufferAddLit(&buf, "  </clock>\n");
+    }
 
     if (virDomainLifecycleDefFormat(conn, &buf, def->onPoweroff,
                                     "on_poweroff",
Index: libvirt-0.7.6/src/conf/domain_conf.h
===================================================================
--- libvirt-0.7.6.orig/src/conf/domain_conf.h
+++ libvirt-0.7.6/src/conf/domain_conf.h
@@ -605,6 +605,56 @@ struct _virSecurityLabelDef {
     int type;
 };
 
+enum virDomainTimerNameType {
+    VIR_DOMAIN_TIMER_NAME_PLATFORM = 0,
+    VIR_DOMAIN_TIMER_NAME_PIT,
+    VIR_DOMAIN_TIMER_NAME_RTC,
+    VIR_DOMAIN_TIMER_NAME_HPET,
+    VIR_DOMAIN_TIMER_NAME_TSC,
+
+    VIR_DOMAIN_TIMER_NAME_LAST,
+};
+
+enum virDomainTimerWallclockType {
+    VIR_DOMAIN_TIMER_WALLCLOCK_HOST = 0,
+    VIR_DOMAIN_TIMER_WALLCLOCK_GUEST,
+
+    VIR_DOMAIN_TIMER_WALLCLOCK_LAST,
+};
+
+enum virDomainTimerTickpolicyType {
+    VIR_DOMAIN_TIMER_TICKPOLICY_DELAY = 0,
+    VIR_DOMAIN_TIMER_TICKPOLICY_CATCHUP,
+    VIR_DOMAIN_TIMER_TICKPOLICY_MERGE,
+    VIR_DOMAIN_TIMER_TICKPOLICY_DISCARD,
+
+    VIR_DOMAIN_TIMER_TICKPOLICY_LAST,
+};
+
+enum virDomainTimerModeType {
+    VIR_DOMAIN_TIMER_MODE_AUTO = 0,
+    VIR_DOMAIN_TIMER_MODE_NATIVE,
+    VIR_DOMAIN_TIMER_MODE_EMULATE,
+    VIR_DOMAIN_TIMER_MODE_PARAVIRT,
+
+    VIR_DOMAIN_TIMER_MODE_LAST,
+};
+
+typedef struct _virDomainTimerDef virDomainTimerDef;
+typedef virDomainTimerDef *virDomainTimerDefPtr;
+struct _virDomainTimerDef {
+    int name;
+    int present;    /* unspecified = -1, no = 0, yes = 1 */
+    int tickpolicy; /* none|catchup|merge|discard */
+
+    /* wallclock is only valid for name='platform|rtc' */
+    int wallclock;  /* host|guest */
+
+    /* frequency & mode are only valid for name='tsc' */
+    unsigned long frequency; /* in Hz, unspecified = 0 */
+    int mode;       /* auto|native|emulate|paravirt */
+};
+
 enum virDomainClockOffsetType {
     VIR_DOMAIN_CLOCK_OFFSET_UTC = 0,
     VIR_DOMAIN_CLOCK_OFFSET_LOCALTIME = 1,
@@ -628,6 +678,9 @@ struct _virDomainClockDef {
          * offset == VIR_DOMAIN_CLOCK_OFFSET_LOCALTIME */
         char *timezone;
     } data;
+
+    int ntimers;
+    virDomainTimerDefPtr *timers;
 };
 
 #define VIR_DOMAIN_CPUMASK_LEN 1024
@@ -968,4 +1021,9 @@ VIR_ENUM_DECL(virDomainState)
 VIR_ENUM_DECL(virDomainSeclabel)
 VIR_ENUM_DECL(virDomainClockOffset)
 
+VIR_ENUM_DECL(virDomainTimerName)
+VIR_ENUM_DECL(virDomainTimerWallclock)
+VIR_ENUM_DECL(virDomainTimerTickpolicy)
+VIR_ENUM_DECL(virDomainTimerMode)
+
 #endif /* __DOMAIN_CONF_H */
Index: libvirt-0.7.6/src/libvirt_private.syms
===================================================================
--- libvirt-0.7.6.orig/src/libvirt_private.syms
+++ libvirt-0.7.6/src/libvirt_private.syms
@@ -199,6 +199,14 @@ virDomainDeviceInfoIterate;
 virDomainDiskDefForeachPath;
 virDomainClockOffsetTypeToString;
 virDomainClockOffsetTypeFromString;
+virDomainTimerNameTypeToString;
+virDomainTimerNameTypeFromString;
+virDomainTimerWallclockTypeToString;
+virDomainTimerWallclockTypeFromString;
+virDomainTimerTickpolicyTypeToString;
+virDomainTimerTickpolicyTypeFromString;
+virDomainTimerModeTypeToString;
+virDomainTimerModeTypeFromString;
 
 
 # domain_event.h
