g_source_attach(__src, g_main_loop_get_context((ctx)->mainLoop)); \
} while (0)
+/* Indentation leves for the state log function below. */
+#define TOOLS_STATE_LOG_ROOT 0
+#define TOOLS_STATE_LOG_CONTAINER 1
+#define TOOLS_STATE_LOG_PLUGIN 2
+
+/**
+ * Convenience function for printing state logs. This function makes sure
+ * all code logging state information uses the same log domain, level and
+ * use the same indentation.
+ *
+ * @param[in] level Indentation level (see constants above).
+ * @param[in] fmt Message format.
+ * @param[in] ... Message arguments.
+ */
+
+static inline void
+ToolsCore_LogState(guint level,
+ const char *fmt,
+ ...)
+{
+ gchar *indented = g_strdup_printf("%*s%s", 3 * level, "", fmt);
+
+ va_list args;
+ va_start(args, fmt);
+ g_logv("state", G_LOG_LEVEL_INFO, indented, args);
+ va_end(args);
+
+ g_free(indented);
+}
+
/**
* Signal sent when registering or unregistering capabilities.
} ToolsAppType;
+struct ToolsPluginData;
+
/**
* Defines the registration data for an "application provider". Application
* providers allow plugins to hook into new application frameworks that will
* Registration callback. This is called after "activate", to register an
* application provided by a plugin.
*
- * @param[in] ctx The application context.
- * @param[in] prov The provider instance.
- * @param[in] reg The application registration data.
+ * @param[in] ctx The application context.
+ * @param[in] prov The provider instance.
+ * @param[in] plugin The plugin that owns the registration.
+ * @param[in] reg The application registration data.
*
* @return Whether registration succeeded.
*/
- gboolean (*registerApp)(ToolsAppCtx *ctx, struct ToolsAppProvider *prov, gpointer reg);
+ gboolean (*registerApp)(ToolsAppCtx *ctx,
+ struct ToolsAppProvider *prov,
+ struct ToolsPluginData *plugin,
+ gpointer reg);
/**
* Shutdown callback (optional). Called when the service is being shut down.
* The provider is responsible for keeping track of registrations and
* cleaning them up during shutdown.
*
+ * This method is only called if the provider was successfully activated.
+ *
* @param[in] ctx The application context.
* @param[in] prov The provider instance.
*/
RpcDebugSendFn sendFn;
/** Shutdown function. */
RpcDebugShutdownFn shutdownFn;
+ /** Plugin data that debug plugins can also export. */
+ ToolsPluginData *plugin;
} RpcDebugPlugin;
# define VMTOOLS_RELEASE_FILENAME_LOCAL(path) g_free(path)
#endif
+/** Convenience macro around VMTools_WrapArray. */
+#define VMTOOLS_WRAP_ARRAY(a) VMTools_WrapArray((a), sizeof *(a), G_N_ELEMENTS(a))
+
+
G_BEGIN_DECLS
void
gpointer data)
{
if (gBackupState == NULL) {
- g_message("Backup is idle.\n");
+ ToolsCore_LogState(TOOLS_STATE_LOG_PLUGIN, "Backup is idle.\n");
} else {
- g_message("Backup is in state: %s\n",
- VmBackupGetStateName(gBackupState->machineState));
+ ToolsCore_LogState(TOOLS_STATE_LOG_PLUGIN,
+ "Backup is in state: %s\n",
+ VmBackupGetStateName(gBackupState->machineState));
}
}
ASSERT_ON_COMPILE(ARRAYSIZE(providerStates) == TOOLS_PROVIDER_MAX);
- g_message("VM Tools Service '%s':\n", state->name);
- g_message(" Plugin path: %s\n", state->pluginPath);
+ ToolsCore_LogState(TOOLS_STATE_LOG_ROOT,
+ "VM Tools Service '%s':\n",
+ state->name);
+ ToolsCore_LogState(TOOLS_STATE_LOG_CONTAINER,
+ "Plugin path: %s\n",
+ state->pluginPath);
for (i = 0; i < state->providers->len; i++) {
ToolsAppProviderReg *prov = &g_array_index(state->providers,
ToolsAppProviderReg,
i);
- g_message(" App provider: %s (%s)\n",
- prov->prov->name,
- providerStates[prov->state]);
+ ToolsCore_LogState(TOOLS_STATE_LOG_CONTAINER,
+ "App provider: %s (%s)\n",
+ prov->prov->name,
+ providerStates[prov->state]);
if (prov->prov->dumpState != NULL) {
prov->prov->dumpState(&state->ctx, prov->prov, NULL);
}
if (preg->prov->dumpState != NULL) {
preg->prov->dumpState(&state->ctx, preg->prov, reg);
} else {
- g_message(" App type %u (no provider info).\n", type);
+ ToolsCore_LogState(TOOLS_STATE_LOG_PLUGIN,
+ "App type %u (no provider info).\n",
+ type);
}
} else {
- g_message(" App type %u (no provider).\n", type);
+ ToolsCore_LogState(TOOLS_STATE_LOG_PLUGIN,
+ "App type %u (no provider).\n",
+ type);
}
return TRUE;
}
ToolsCoreDumpPluginInfo(ToolsServiceState *state,
ToolsPluginData *plugin)
{
- g_message(" Plugin: %s\n", plugin->name);
+ ToolsCore_LogState(TOOLS_STATE_LOG_CONTAINER, "Plugin: %s\n", plugin->name);
if (plugin->regs == NULL) {
- g_message(" No registrations.\n");
+ ToolsCore_LogState(TOOLS_STATE_LOG_PLUGIN, "No registrations.\n");
}
}
{
if (reg != NULL) {
RpcChannelCallback *cb = reg;
- g_message(" RPC callback: %s\n", cb->name);
+ ToolsCore_LogState(TOOLS_STATE_LOG_PLUGIN, "RPC callback: %s\n", cb->name);
}
}
{
if (reg != NULL) {
ToolsPluginSignalCb *sig = reg;
- g_message(" Signal callback: %s\n", sig->signame);
+ ToolsCore_LogState(TOOLS_STATE_LOG_PLUGIN, "Signal callback: %s\n", sig->signame);
}
}
preg->state = TOOLS_PROVIDER_ACTIVE;
}
- if (!preg->prov->registerApp(&state->ctx, preg->prov, reg)) {
+ if (!preg->prov->registerApp(&state->ctx, preg->prov, plugin, reg)) {
g_warning("Failed registration of app type %d (%s) from plugin %s.",
type, preg->prov->name, plugin->name);
goto exit;
/**
* Registration callback for GuestRPC applications.
*
- * @param[in] ctx The application context.
- * @param[in] prov Unused.
- * @param[in] reg The application registration data.
+ * @param[in] ctx The application context.
+ * @param[in] prov Unused.
+ * @param[in] plugin Unused.
+ * @param[in] reg The application registration data.
*
* @return TRUE.
*/
static gboolean
ToolsCoreRegisterRPC(ToolsAppCtx *ctx,
ToolsAppProvider *prov,
+ ToolsPluginData *plugin,
gpointer reg)
{
RpcChannel_RegisterCallback(ctx->rpc, reg);
/**
* Registration callback for signal connections.
*
- * @param[in] ctx The application context.
- * @param[in] prov Unused.
- * @param[in] reg The application registration data.
+ * @param[in] ctx The application context.
+ * @param[in] prov Unused.
+ * @param[in] plugin Unused.
+ * @param[in] reg The application registration data.
*
* @return TRUE if the signal exists.
*/
static gboolean
ToolsCoreRegisterSignal(ToolsAppCtx *ctx,
ToolsAppProvider *prov,
+ ToolsPluginData *plugin,
gpointer reg)
{
ToolsPluginSignalCb *sig = reg;
goto exit;
}
+ /*
+ * If there is a debug plugin, see if it exports standard plugin registration
+ * data too.
+ */
+ if (state->debugData != NULL && state->debugData->debugPlugin->plugin != NULL) {
+ ToolsPluginData *data = state->debugData->debugPlugin->plugin;
+ ToolsPlugin *plugin = g_malloc(sizeof *plugin);
+ plugin->module = NULL;
+ plugin->data = data;
+ VMTools_BindTextDomain(data->name, NULL, NULL);
+ g_ptr_array_add(state->plugins, plugin);
+ }
+
ret = TRUE;
exit:
g_array_free(pcaps, TRUE);
}
- g_signal_emit_by_name(state->ctx.serviceObj, TOOLS_CORE_SIG_SHUTDOWN, &state->ctx);
-
/*
* Stop all app providers, and free the memory we allocated for the two
* internal app providers.
ToolsAppProviderReg,
i);
- if (preg->prov->shutdown != NULL) {
+ if (preg->prov->shutdown != NULL && preg->state == TOOLS_PROVIDER_ACTIVE) {
preg->prov->shutdown(&state->ctx, preg->prov);
}
}
}
+ g_signal_emit_by_name(state->ctx.serviceObj, TOOLS_CORE_SIG_SHUTDOWN, &state->ctx);
+
while (state->plugins->len > 0) {
ToolsPlugin *plugin = g_ptr_array_index(state->plugins, state->plugins->len - 1);
GArray *regs = (plugin->data != NULL) ? plugin->data->regs : NULL;
}
g_ptr_array_remove_index(state->plugins, state->plugins->len - 1);
- g_module_close(plugin->module);
+ if (plugin->module != NULL) {
+ g_module_close(plugin->module);
+ }
g_free(plugin);
}
TestDebugValidateReset(RpcInData *data,
gboolean ret)
{
- ToolsAppCtx *ctx = data->appCtx;
RPCDEBUG_ASSERT(data->result != NULL, FALSE);
CU_ASSERT_STRING_EQUAL(data->result, "ATR debug");
-
- /*
- * If reset was successful, connect the "test-signal" signal so we
- * test custom registration of signals. The test plugin will emit
- * this signal after it sends an "test.rpcout.msg1" RPC as part of
- * handling a "test.rpcin.msg1" RPC.
- */
- g_signal_connect(ctx->serviceObj,
- "test-signal",
- G_CALLBACK(TestDebugHandleSignal),
- NULL);
-
return (gboolean) ret;
}
xdr_TestPluginData, sizeof (TestPluginData) },
{ NULL, NULL }
};
+ static ToolsPluginData pluginData = {
+ "testDebug",
+ NULL,
+ NULL,
+ NULL,
+ };
static RpcDebugPlugin regData = {
recvFns,
NULL,
TestDebugSendNext,
- NULL
+ NULL,
+ &pluginData,
};
- TestPluginData testdata;
- testdata.data = "rpc1test";
- testdata.f_int = 1357;
- testdata.f_bool = TRUE;
-
- /* Build the command for the "test.rpcin.msg1" RPC. */
- if (!RpcChannel_BuildXdrCommand("test.rpcin.msg1",
- xdr_TestPluginData,
- &testdata,
- &gRpcMessages[4].message,
- &gRpcMessages[4].messageLen)) {
- g_error("Failed to create test.rpcin.msg1 command.\n");
+ /* Standard plugin interface, used to listen for signals. */
+ {
+ ToolsPluginSignalCb sigs[] = {
+ { "test-signal", TestDebugHandleSignal, NULL },
+ };
+ ToolsAppReg regs[] = {
+ { TOOLS_APP_SIGNALS, VMTOOLS_WRAP_ARRAY(sigs) },
+ };
+
+ pluginData.regs = VMTOOLS_WRAP_ARRAY(regs);
+ }
+
+ /* Initialize the paylod of the "test.rpcin.msg1" RPC. */
+ {
+ TestPluginData testdata;
+ testdata.data = "rpc1test";
+ testdata.f_int = 1357;
+ testdata.f_bool = TRUE;
+
+ /* Build the command for the "test.rpcin.msg1" RPC. */
+ if (!RpcChannel_BuildXdrCommand("test.rpcin.msg1",
+ xdr_TestPluginData,
+ &testdata,
+ &gRpcMessages[4].message,
+ &gRpcMessages[4].messageLen)) {
+ g_error("Failed to create test.rpcin.msg1 command.\n");
+ }
}
gCtx = ctx;
*
* @param[in] ctx Unused.
* @param[in] prov Unused.
+ * @param[in] plugin Unused.
* @param[in] reg Registration data (should be a string).
*
* @retval FALSE if registration value is TEST_APP_ERROR.
static gboolean
TestProviderRegisterApp(ToolsAppCtx *ctx,
ToolsAppProvider *prov,
+ ToolsPluginData *plugin,
gpointer reg)
{
TestApp *app = reg;
{ "TestAppNoProvider" }
};
ToolsAppReg regs[] = {
- { TOOLS_APP_GUESTRPC, VMTools_WrapArray(rpcs, sizeof *rpcs, ARRAYSIZE(rpcs)) },
- { TOOLS_APP_PROVIDER, VMTools_WrapArray(provs, sizeof *provs, ARRAYSIZE(provs)) },
- { TOOLS_APP_SIGNALS, VMTools_WrapArray(sigs, sizeof *sigs, ARRAYSIZE(sigs)) },
- { 42, VMTools_WrapArray(tapp, sizeof *tapp, ARRAYSIZE(tapp)) },
- { 43, VMTools_WrapArray(tnoprov, sizeof *tnoprov, ARRAYSIZE(tnoprov)) },
+ { TOOLS_APP_GUESTRPC, VMTOOLS_WRAP_ARRAY(rpcs) },
+ { TOOLS_APP_PROVIDER, VMTOOLS_WRAP_ARRAY(provs) },
+ { TOOLS_APP_SIGNALS, VMTOOLS_WRAP_ARRAY(sigs) },
+ { 42, VMTOOLS_WRAP_ARRAY(tapp) },
+ { 43, VMTOOLS_WRAP_ARRAY(tnoprov) },
};
g_signal_new("test-signal",
G_TYPE_NONE,
0);
- regData.regs = VMTools_WrapArray(regs, sizeof *regs, ARRAYSIZE(regs));
+ regData.regs = VMTOOLS_WRAP_ARRAY(regs);
return ®Data;
}
memset(&data, 0, sizeof data);
memset(&rpcdata, 0, sizeof rpcdata);
- if (!plugin->sendFn(&rpcdata)) {
+ if (plugin->sendFn == NULL || !plugin->sendFn(&rpcdata)) {
RpcDebug_DecRef(cdata->ctx);
cdata->hasLibRef = FALSE;
return FALSE;
err = CU_basic_run_tests();
/* Clean up internal library / debug plugin state. */
- ASSERT(g_atomic_int_get(&gRefCount) == 0);
+ ASSERT(g_atomic_int_get(&gRefCount) >= 0);
ASSERT(ldata != NULL);
if (ldata->debugPlugin->shutdownFn != NULL) {