From: Katy Feng Date: Fri, 23 Dec 2022 00:25:50 +0000 (-0800) Subject: [TimeInfo] Handle notifications and get updates X-Git-Tag: stable-12.2.0~32 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=a7da31373c5bb21d647a22c240a204645fa5f9ef;p=thirdparty%2Fopen-vm-tools.git [TimeInfo] Handle notifications and get updates 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. --- diff --git a/open-vm-tools/services/plugins/timeSync/Makefile.am b/open-vm-tools/services/plugins/timeSync/Makefile.am index 40b7127b0..bf3889bea 100644 --- a/open-vm-tools/services/plugins/timeSync/Makefile.am +++ b/open-vm-tools/services/plugins/timeSync/Makefile.am @@ -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 diff --git a/open-vm-tools/services/plugins/timeSync/timeInfo.c b/open-vm-tools/services/plugins/timeSync/timeInfo.c index 98af37552..817d9742e 100644 --- a/open-vm-tools/services/plugins/timeSync/timeInfo.c +++ b/open-vm-tools/services/plugins/timeSync/timeInfo.c @@ -27,18 +27,31 @@ #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(); } } diff --git a/open-vm-tools/services/plugins/timeSync/timeInfo.h b/open-vm-tools/services/plugins/timeSync/timeInfo.h index 8772de24f..fa625afac 100644 --- a/open-vm-tools/services/plugins/timeSync/timeInfo.h +++ b/open-vm-tools/services/plugins/timeSync/timeInfo.h @@ -27,6 +27,7 @@ void TimeInfo_Init(ToolsAppCtx *ctx); void TimeInfo_Shutdown(void); +gboolean TimeInfo_TcloHandler(RpcInData *data); #endif /* _TIMEINFO_H_ */ diff --git a/open-vm-tools/services/plugins/timeSync/timeSync.c b/open-vm-tools/services/plugins/timeSync/timeSync.c index 6ec952066..b97accff5 100644 --- a/open-vm-tools/services/plugins/timeSync/timeSync.c +++ b/open-vm-tools/services/plugins/timeSync/timeSync.c @@ -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, ®Data },