]> git.ipfire.org Git - thirdparty/open-vm-tools.git/commitdiff
[TimeInfo] Handle notifications and get updates
authorKaty Feng <fkaty@vmware.com>
Fri, 23 Dec 2022 00:25:50 +0000 (16:25 -0800)
committerKaty Feng <fkaty@vmware.com>
Fri, 23 Dec 2022 00:25:50 +0000 (16:25 -0800)
TimeInfo, which is part of TimeSync plugin, can be
used to query, set, subscribe, and receive updates for
time-related information from the host when guest is
using precisionclock to consume time from the host.
Previous changes laid foundation to subscribe and
unsubscribe for TimeInfo updates in open-vm-tools during
init/shutdown.

When open-vm-tools subscribes for TimeInfo updates, VMX
will send a notification GuestRPC to tools if and
when the timing properties change. This change
adds support to handle such GuestRPCs from VMX.
The handler for the GuestRPC is pretty straightforward
for now: it queues an async task that simply gets
all updates and logs them.

open-vm-tools/services/plugins/timeSync/Makefile.am
open-vm-tools/services/plugins/timeSync/timeInfo.c
open-vm-tools/services/plugins/timeSync/timeInfo.h
open-vm-tools/services/plugins/timeSync/timeSync.c

index 40b7127b099b1275307d849b025e2d7ba6030e49..bf3889beac5225197443146b6fc0a078119030b7 100644 (file)
@@ -26,6 +26,7 @@ libtimeSync_la_LDFLAGS += @PLUGIN_LDFLAGS@
 
 libtimeSync_la_LIBADD =
 libtimeSync_la_LIBADD += @VMTOOLS_LIBS@
+libtimeSync_la_LIBADD += @GOBJECT_LIBS@
 
 libtimeSync_la_SOURCES =
 libtimeSync_la_SOURCES += timeSync.c
index 98af375520e958f1536ec7c42faebcf12b1781eb..817d9742ec01e2638f218ab0bb14beeefd8012e3 100644 (file)
 #include "timeSync.h"
 #include "system.h"
 #include "strutil.h"
+#include "dynarray.h"
 #include "vmware/tools/log.h"
 #include "vmware/tools/plugin.h"
+#include "vmware/tools/threadPool.h"
+
+typedef struct TimeInfoData {
+   char *timestamp;
+   char *key;
+   char *value;
+} TimeInfoData;
+
+DEFINE_DYNARRAY_TYPE(TimeInfoData);
 
 typedef struct TimeInfoVmxRpcCtx {
    char *request;
    struct {
       char *reply;
       size_t replyLen;
+      TimeInfoDataArray data;
    } response;
 } TimeInfoVmxRpcCtx;
 
+// TODO: Move common definitions to a shared header with VMX.
 static const char *TIMEINFO_VMXRPC_CLOCKID         = "precisionclock0";
+static const char *TIMEINFO_VMXRPC_CMD_GETUPDATES  = "get-updates";
 static const char *TIMEINFO_VMXRPC_CMD_SUBSCRIBE   = "subscribe";
 static const char *TIMEINFO_VMXRPC_CMD_UNSUBSCRIBE = "unsubscribe";
 static const char *TIMEINFO_VMXRPC_STATUS_OK       = "OK";
@@ -56,6 +69,7 @@ TimeInfoVmxRpcDone(TimeInfoVmxRpcCtx *rpc)
 {
    free(rpc->request);
    RpcChannel_Free(rpc->response.reply);
+   TimeInfoDataArray_Destroy(&rpc->response.data);
    memset(rpc, 0, sizeof *rpc);
 }
 
@@ -83,6 +97,8 @@ TimeInfoVmxRpcDo(TimeInfoVmxRpcCtx *rpc,
    char *status;
 
    memset(rpc, 0, sizeof *rpc);
+   TimeInfoDataArray_Init(&rpc->response.data, 0);
+
    StrUtil_SafeStrcatF(&rpc->request, "timeInfo.%s", method);
    for (i = 0; i < argc; ++i) {
       StrUtil_SafeStrcatF(&rpc->request, " %s", argv[i]);
@@ -106,12 +122,25 @@ TimeInfoVmxRpcDo(TimeInfoVmxRpcCtx *rpc,
    next = rpc->response.reply;
    status = StrUtil_GetNextItem(&next, '\n');
 
-   /* On success, extract payload. */
-   if (status == NULL ||
-       strcmp(status, TIMEINFO_VMXRPC_STATUS_OK) != 0 || next != NULL) {
-      g_warning("%s: Invalid result payload.", __FUNCTION__);
+   if (status == NULL || strcmp(status, TIMEINFO_VMXRPC_STATUS_OK) != 0) {
+      g_warning("%s: RPC was unsuccessful.", __FUNCTION__);
       return FALSE;
    }
+
+   /* On success, extract payload. */
+   while (next != NULL) {
+      TimeInfoData data;
+      char *line = StrUtil_GetNextItem(&next, '\n');
+      g_debug("%s: > Response: data: %s", __FUNCTION__, VM_SAFE_STR(line));
+      data.key = StrUtil_GetNextItem(&line, ' ');
+      data.value = StrUtil_GetNextItem(&line, ' ');
+      data.timestamp = StrUtil_GetNextItem(&line, '\n');
+      if (data.timestamp == NULL || data.key == NULL || data.value == NULL) {
+         g_warning("%s: Invalid result payload.", __FUNCTION__);
+         return FALSE;
+      }
+      TimeInfoDataArray_Push(&rpc->response.data, data);
+   }
    return TRUE;
 }
 
@@ -158,6 +187,92 @@ TimeInfoVmxUnsubscribe(void)
 }
 
 
+/**
+ * Fetch TimeInfo updates from the platform with GuestRPC.
+ *
+ * @param[in] vmxRpc TimeInfo VMX RPC context
+ *
+ * @return TRUE on successful invocation of GuestRPC, FALSE otherwise.
+ */
+
+static gboolean
+TimeInfoVmxGetUpdates(TimeInfoVmxRpcCtx *vmxRpc)
+{
+   const char *argv[1] = { TIMEINFO_VMXRPC_CLOCKID };
+
+   g_debug("%s: Fetching updates from VMX.", __FUNCTION__);
+   if (!TimeInfoVmxRpcDo(vmxRpc, TIMEINFO_VMXRPC_CMD_GETUPDATES,
+                             argv, ARRAYSIZE(argv))) {
+      g_warning("%s: Failed to fetch updates.", __FUNCTION__);
+      return FALSE;
+   }
+   return TRUE;
+}
+
+
+/**
+ * Fetch and log TimeInfo updates.
+ */
+
+static void
+TimeInfoGetAndLogUpdates(void)
+{
+   TimeInfoVmxRpcCtx vmxRpc;
+
+   if (TimeInfoVmxGetUpdates(&vmxRpc)) {
+      int i;
+      for (i = 0; i < TimeInfoDataArray_Count(&vmxRpc.response.data); ++i) {
+         const TimeInfoData *data =
+            TimeInfoDataArray_AddressOf(&vmxRpc.response.data, i);
+         g_info("update: key %s value %s time %s", data->key, data->value,
+                data->timestamp);
+      }
+   } else {
+      g_warning("%s: Failed to perform get-updates.", __FUNCTION__);
+   }
+   TimeInfoVmxRpcDone(&vmxRpc);
+}
+
+
+/**
+ * Handler for async task when a TimeInfo update is received. Fetch updates
+ * from the platform and log them.
+ *
+ * @param[in] ctx  The application context.
+ * @param[in] data data pointer.
+ */
+
+static void
+TimeInfoHandleNotificationTask(ToolsAppCtx *ctx, gpointer data)
+{
+   g_debug("%s: Notification received.", __FUNCTION__);
+   TimeInfoGetAndLogUpdates();
+}
+
+
+/**
+ * GuestRPC handler for TimeInfo_Update. Submits an async task to fetch
+ * and log updates.
+ *
+ * @param[in] data RPC request data.
+ *
+ * @return TRUE on success.
+ */
+
+gboolean
+TimeInfo_TcloHandler(RpcInData *data)
+{
+   if (gToolsAppCtx == NULL) {
+      return RPCIN_SETRETVALS(data, "TimeInfo not enabled", FALSE);
+   }
+   ToolsCorePool_SubmitTask(gToolsAppCtx,
+                            TimeInfoHandleNotificationTask,
+                            NULL,
+                            NULL);
+   return RPCIN_SETRETVALS(data, "", TRUE);
+}
+
+
 /**
  * Initialize TimeInfo in TimeSync.
  *
@@ -177,6 +292,8 @@ TimeInfo_Init(ToolsAppCtx *ctx)
            __FUNCTION__, !timeInfoEnabled ? "not " : "");
    if (timeInfoEnabled) {
       gToolsAppCtx = ctx;
+      /* Flush initial updates. */
+      TimeInfoGetAndLogUpdates();
       TimeInfoVmxSubscribe();
    }
 }
index 8772de24f60473f07353cce4722ad54b7acc2724..fa625afac910e9c4d13ff3e1806534c8c5519c85 100644 (file)
@@ -27,6 +27,7 @@
 
 void TimeInfo_Init(ToolsAppCtx *ctx);
 void TimeInfo_Shutdown(void);
+gboolean TimeInfo_TcloHandler(RpcInData *data);
 
 #endif /* _TIMEINFO_H_ */
 
index 6ec9520665e94bd86724b8a875f5dc58c564338a..b97accff556377cc681eb2477df407c3555cd715 100644 (file)
@@ -1049,7 +1049,10 @@ ToolsOnLoad(ToolsAppCtx *ctx)
 
    TimeSyncData *data = g_malloc(sizeof (TimeSyncData));
    RpcChannelCallback rpcs[] = {
-      { TIMESYNC_SYNCHRONIZE, TimeSyncTcloHandler, data, NULL, NULL, 0 }
+      { TIMESYNC_SYNCHRONIZE, TimeSyncTcloHandler, data, NULL, NULL, 0 },
+#if defined(__linux__) && !defined(USERWORLD)
+      { TIMEINFO_UPDATE, TimeInfo_TcloHandler, data, NULL, NULL, 0 }
+#endif
    };
    ToolsPluginSignalCb sigs[] = {
       { TOOLS_CORE_SIG_SET_OPTION, TimeSyncSetOption, &regData },