static Bool VmBackupNullStart(VmBackupState *state, void *clientData) { VmBackup_SetCurrentOp(state, NULL, NULL, __FUNCTION__); return VmBackup_SendEvent(VMBACKUP_EVENT_SNAPSHOT_COMMIT, 0, ""); }
static void VmBackupFinalize(void) { g_debug("*** %s\n", __FUNCTION__); ASSERT(gBackupState != NULL); if (gBackupState->abortTimer != NULL) { g_source_destroy(gBackupState->abortTimer); g_source_unref(gBackupState->abortTimer); } if (gBackupState->currentOp != NULL) { VmBackup_Cancel(gBackupState->currentOp); VmBackup_Release(gBackupState->currentOp); } VmBackup_SendEvent(VMBACKUP_EVENT_REQUESTOR_DONE, VMBACKUP_SUCCESS, ""); if (gBackupState->timerEvent != NULL) { g_source_destroy(gBackupState->timerEvent); g_source_unref(gBackupState->timerEvent); } if (gBackupState->keepAlive != NULL) { g_source_destroy(gBackupState->keepAlive); g_source_unref(gBackupState->keepAlive); } gBackupState->provider->release(gBackupState->provider); g_free(gBackupState->scriptArg); g_free(gBackupState->volumes); g_free(gBackupState->snapshots); g_free(gBackupState->errorMsg); g_free(gBackupState); gBackupState = NULL; }
static gboolean VmBackupSnapshotDone(RpcInData *data) { g_debug("*** %s\n", __FUNCTION__); if (gBackupState == NULL) { return RPCIN_SETRETVALS(data, "Error: no quiesce operation in progress", FALSE); } else if (gBackupState->machineState != VMBACKUP_MSTATE_SYNC_FREEZE) { g_warning("Error: unexpected state for snapshot done message: %s", VmBackupGetStateName(gBackupState->machineState)); return RPCIN_SETRETVALS(data, "Error: unexpected state for quiesce done message.", FALSE); } else { if (data->argsSize > 1) { gBackupState->snapshots = g_strndup(data->args + 1, data->argsSize - 1); } if (!gBackupState->provider->snapshotDone(gBackupState, gBackupState->provider->clientData)) { VmBackup_SendEvent(VMBACKUP_EVENT_REQUESTOR_ERROR, VMBACKUP_SYNC_ERROR, "Error when notifying the sync provider."); if (VmBackupOnError()) { VmBackupFinalize(); } } else { gBackupState->machineState = VMBACKUP_MSTATE_SYNC_THAW; } return RPCIN_SETRETVALS(data, "", TRUE); } }
static gboolean VmBackupKeepAliveCallback(void *clientData) { g_debug("*** %s\n", __FUNCTION__); ASSERT(gBackupState != NULL); g_source_unref(gBackupState->keepAlive); gBackupState->keepAlive = NULL; VmBackup_SendEvent(VMBACKUP_EVENT_KEEP_ALIVE, 0, ""); return FALSE; }
static void VmBackupNullStart(ToolsAppCtx *ctx, void *clientData) { VmBackupState *state = (VmBackupState*) clientData; /* * This is more of a "let's at least do something" than something that * will actually ensure data integrity... */ sync(); VmBackup_SetCurrentOp(state, NULL, NULL, __FUNCTION__); if (!VmBackup_SendEvent(VMBACKUP_EVENT_SNAPSHOT_COMMIT, 0, "")) { g_warning("Failed to send commit event to host"); state->freezeStatus = VMBACKUP_FREEZE_ERROR; } else { state->freezeStatus = VMBACKUP_FREEZE_FINISHED; } }
static Bool VmBackupStartScripts(VmBackupScriptType type) { const char *opName; VmBackupMState nextState; g_debug("*** %s\n", __FUNCTION__); switch (type) { case VMBACKUP_SCRIPT_FREEZE: opName = "VmBackupOnFreeze"; nextState = VMBACKUP_MSTATE_SCRIPT_FREEZE; break; case VMBACKUP_SCRIPT_FREEZE_FAIL: opName = "VmBackupOnFreezeFail"; nextState = VMBACKUP_MSTATE_SCRIPT_ERROR; break; case VMBACKUP_SCRIPT_THAW: opName = "VmBackupOnThaw"; nextState = VMBACKUP_MSTATE_SCRIPT_THAW; break; default: NOT_REACHED(); } if (gBackupState->execScripts && !VmBackup_SetCurrentOp(gBackupState, VmBackup_NewScriptOp(type, gBackupState), NULL, opName)) { VmBackup_SendEvent(VMBACKUP_EVENT_REQUESTOR_ERROR, VMBACKUP_SCRIPT_ERROR, "Error when starting custom quiesce scripts."); return FALSE; } gBackupState->machineState = nextState; return TRUE; }
static Bool VmBackupEnableSync(void) { g_debug("*** %s\n", __FUNCTION__); g_signal_emit_by_name(gBackupState->ctx->serviceObj, TOOLS_CORE_SIG_IO_FREEZE, gBackupState->ctx, TRUE); if (!gBackupState->provider->start(gBackupState, gBackupState->provider->clientData)) { g_signal_emit_by_name(gBackupState->ctx->serviceObj, TOOLS_CORE_SIG_IO_FREEZE, gBackupState->ctx, FALSE); VmBackup_SendEvent(VMBACKUP_EVENT_REQUESTOR_ERROR, VMBACKUP_SYNC_ERROR, "Error when enabling the sync provider."); return FALSE; } gBackupState->machineState = VMBACKUP_MSTATE_SYNC_FREEZE; return TRUE; }
static void VmBackupDoAbort(void) { g_debug("*** %s\n", __FUNCTION__); ASSERT(gBackupState != NULL); if (gBackupState->machineState != VMBACKUP_MSTATE_SCRIPT_ERROR && gBackupState->machineState != VMBACKUP_MSTATE_SYNC_ERROR) { /* Mark the current operation as cancelled. */ if (gBackupState->currentOp != NULL) { VmBackup_Cancel(gBackupState->currentOp); VmBackup_Release(gBackupState->currentOp); gBackupState->currentOp = NULL; } VmBackup_SendEvent(VMBACKUP_EVENT_REQUESTOR_ABORT, VMBACKUP_REMOTE_ABORT, "Quiesce aborted."); /* Transition to the error state. */ if (VmBackupOnError()) { VmBackupFinalize(); } } }
static gboolean VmBackupStartCommon(RpcInData *data, gboolean forceVss) { GError *err = NULL; ToolsAppCtx *ctx = data->appCtx; VmBackupSyncProvider *provider = NULL; size_t i; /* List of available providers, in order of preference for loading. */ struct SyncProvider { VmBackupSyncProvider *(*ctor)(void); const gchar *cfgEntry; } providers[] = { #if defined(_WIN32) { VmBackup_NewVssProvider, "enableVSS" }, #endif { VmBackup_NewSyncDriverProvider, "enableSyncDriver" }, { VmBackup_NewNullProvider, NULL }, }; if (forceVss) { if (gBackupState->quiesceApps || gBackupState->quiesceFS) { /* If quiescing is requested, only allow VSS provider */ #if defined(_WIN32) if (VmBackupConfigGetBoolean(ctx->config, "enableVSS", TRUE)) { provider = VmBackup_NewVssProvider(); } #endif } else { /* If no quiescing is requested only allow null provider */ provider = VmBackup_NewNullProvider(); } if (provider == NULL) { g_warning("Requested quiescing cannot be initialized."); goto error; } } else { /* Instantiate the sync provider. */ for (i = 0; i < ARRAYSIZE(providers); i++) { struct SyncProvider *sp = &providers[i]; if (VmBackupConfigGetBoolean(ctx->config, sp->cfgEntry, TRUE)) { provider = sp->ctor(); if (provider != NULL) { break; } } } } ASSERT(provider != NULL); /* Instantiate the backup state and start the operation. */ gBackupState->ctx = data->appCtx; gBackupState->pollPeriod = 1000; gBackupState->machineState = VMBACKUP_MSTATE_IDLE; gBackupState->provider = provider; g_debug("Using quiesceApps = %d, quiesceFS = %d, allowHWProvider = %d," "execScripts = %d, scriptArg = %s, timeout = %u\n", gBackupState->quiesceApps, gBackupState->quiesceFS, gBackupState->allowHWProvider, gBackupState->execScripts, (gBackupState->scriptArg != NULL) ? gBackupState->scriptArg : "", gBackupState->timeout); g_debug("Quiescing volumes: %s", (gBackupState->volumes) ? gBackupState->volumes : "(null)"); gBackupState->configDir = GuestApp_GetConfPath(); if (gBackupState->configDir == NULL) { g_warning("Error getting configuration directory."); goto error; } VmBackup_SendEvent(VMBACKUP_EVENT_RESET, VMBACKUP_SUCCESS, ""); if (!VmBackupStartScripts(VMBACKUP_SCRIPT_FREEZE)) { goto error; } /* * VC has a 15 minute timeout for quiesced snapshots. After that timeout, * it just discards the operation and sends an error to the caller. But * Tools can still keep running, blocking any new quiesced snapshot * requests. So we set up our own timer (which is configurable, in case * anyone wants to play with it), so that we abort any ongoing operation * if we also hit that timeout. * * First check if the timeout is specified by the RPC command, if not, * check the tools.conf file, otherwise use the default. * * See bug 506106. */ if (gBackupState->timeout == 0) { gBackupState->timeout = (guint) g_key_file_get_integer( gBackupState->ctx->config, "vmbackup", "timeout", &err); if (err != NULL) { g_clear_error(&err); gBackupState->timeout = 15 * 60; } } /* Treat "0" as no timeout. */ if (gBackupState->timeout != 0) { gBackupState->abortTimer = g_timeout_source_new_seconds(gBackupState->timeout); VMTOOLSAPP_ATTACH_SOURCE(gBackupState->ctx, gBackupState->abortTimer, VmBackupAbortTimer, NULL, NULL); } VMBACKUP_ENQUEUE_EVENT(); return RPCIN_SETRETVALS(data, "", TRUE); error: if (gBackupState->provider) { gBackupState->provider->release(gBackupState->provider); } g_free(gBackupState->scriptArg); g_free(gBackupState->volumes); g_free(gBackupState); gBackupState = NULL; return RPCIN_SETRETVALS(data, "Error initializing quiesce operation.", FALSE); }
static gboolean VmBackupAsyncCallback(void *clientData) { VmBackupOpStatus status = VMBACKUP_STATUS_FINISHED; g_debug("*** %s\n", __FUNCTION__); ASSERT(gBackupState != NULL); g_source_unref(gBackupState->timerEvent); gBackupState->timerEvent = NULL; if (gBackupState->currentOp != NULL) { g_debug("VmBackupAsyncCallback: checking %s\n", gBackupState->currentOpName); status = VmBackup_QueryStatus(gBackupState->currentOp); } switch (status) { case VMBACKUP_STATUS_PENDING: goto exit; case VMBACKUP_STATUS_FINISHED: if (gBackupState->currentOpName != NULL) { g_debug("Async request '%s' completed\n", gBackupState->currentOpName); VmBackup_Release(gBackupState->currentOp); gBackupState->currentOpName = NULL; } gBackupState->currentOp = NULL; break; default: { gchar *msg; if (gBackupState->errorMsg != NULL) { msg = g_strdup_printf("'%s' operation failed: %s", gBackupState->currentOpName, gBackupState->errorMsg); } else { msg = g_strdup_printf("'%s' operation failed.", gBackupState->currentOpName); } VmBackup_SendEvent(VMBACKUP_EVENT_REQUESTOR_ERROR, VMBACKUP_UNEXPECTED_ERROR, msg); g_free(msg); VmBackup_Release(gBackupState->currentOp); gBackupState->currentOp = NULL; VmBackupOnError(); goto exit; } } /* * Keep calling the registered callback until it's either NULL, or * an asynchronous operation is scheduled. */ while (gBackupState->callback != NULL) { VmBackupCallback cb = gBackupState->callback; gBackupState->callback = NULL; if (cb(gBackupState)) { if (gBackupState->currentOp != NULL || gBackupState->forceRequeue) { goto exit; } } else { VmBackupOnError(); goto exit; } } /* * At this point, the current operation can be declared finished, and the * state machine can move to the next state. */ switch (gBackupState->machineState) { case VMBACKUP_MSTATE_SCRIPT_FREEZE: /* Next state is "sync freeze". */ if (!VmBackupEnableSync()) { VmBackupOnError(); } break; case VMBACKUP_MSTATE_SYNC_FREEZE: /* * The SYNC_FREEZE -> SYNC_THAW transition is handled by the RPC callback, * so this case is a no-op. */ break; case VMBACKUP_MSTATE_SYNC_THAW: /* Next state is "script thaw". */ g_signal_emit_by_name(gBackupState->ctx->serviceObj, TOOLS_CORE_SIG_IO_FREEZE, gBackupState->ctx, FALSE); if (!VmBackupStartScripts(VMBACKUP_SCRIPT_THAW)) { VmBackupOnError(); } break; case VMBACKUP_MSTATE_SCRIPT_ERROR: case VMBACKUP_MSTATE_SCRIPT_THAW: /* Next state is "idle". */ gBackupState->machineState = VMBACKUP_MSTATE_IDLE; break; case VMBACKUP_MSTATE_SYNC_ERROR: /* Next state is "script error". */ if (!VmBackupStartScripts(VMBACKUP_SCRIPT_FREEZE_FAIL)) { VmBackupOnError(); } break; default: g_error("Unexpected machine state: %s\n", VmBackupGetStateName(gBackupState->machineState)); } exit: /* If the state machine is back in IDLE, it means the backup operation finished. */ if (gBackupState->machineState == VMBACKUP_MSTATE_IDLE) { VmBackupFinalize(); } else { gBackupState->forceRequeue = FALSE; VMBACKUP_ENQUEUE_EVENT(); } return FALSE; }
static VmBackupOpStatus VmBackupScriptOpQuery(VmBackupOp *_op) // IN { VmBackupOpStatus ret = VMBACKUP_STATUS_PENDING; VmBackupScriptOp *op = (VmBackupScriptOp *) _op; VmBackupScript *scripts = op->state->scripts; VmBackupScript *currScript = NULL; if (scripts != NULL && op->state->currentScript >= 0) { currScript = &scripts[op->state->currentScript]; } if (op->canceled) { ret = VMBACKUP_STATUS_CANCELED; goto exit; } else if (scripts == NULL || currScript == NULL || currScript->proc == NULL) { ret = VMBACKUP_STATUS_FINISHED; goto exit; } if (!ProcMgr_IsAsyncProcRunning(currScript->proc)) { int exitCode; Bool succeeded; succeeded = (ProcMgr_GetExitCode(currScript->proc, &exitCode) == 0 && exitCode == 0); ProcMgr_Free(currScript->proc); currScript->proc = NULL; /* * If thaw scripts fail, keep running and only notify the failure after * all others have run. */ if (!succeeded) { if (op->type == VMBACKUP_SCRIPT_FREEZE) { ret = VMBACKUP_STATUS_ERROR; goto exit; } else if (op->type == VMBACKUP_SCRIPT_THAW) { op->thawFailed = TRUE; } } switch (VmBackupRunNextScript(op)) { case -1: ret = VMBACKUP_STATUS_ERROR; break; case 0: ret = op->thawFailed ? VMBACKUP_STATUS_ERROR : VMBACKUP_STATUS_FINISHED; break; default: break; } } exit: if (ret == VMBACKUP_STATUS_ERROR) { /* Report the script error to the host */ VmBackup_SendEvent(VMBACKUP_EVENT_REQUESTOR_ERROR, VMBACKUP_SCRIPT_ERROR, "Custom quiesce script failed."); } return ret; }