mirror of
https://github.com/Microsoft/sql-server-samples.git
synced 2025-12-08 14:58:54 +00:00
Fix sample to work on SLES. . The issue is in access to the shared memory that VDI uses to communicate between SQL Server and the client application. When opening the file, the mode is modified by the current umask setting and group sharing is removed.
350 lines
8.8 KiB
C++
350 lines
8.8 KiB
C++
/***********************************************************************
|
|
Copyright (c) Microsoft Corporation
|
|
All Rights Reserved.
|
|
***********************************************************************/
|
|
// This source code is an intended supplement to the Microsoft SQL
|
|
// Server online references and related electronic documentation.
|
|
//
|
|
// This sample is for instructional purposes only.
|
|
// Code contained herein is not intended to be used "as is" in real applications.
|
|
//
|
|
// vdipipesample.cpp
|
|
//
|
|
// This is a sample program used to demonstrate the Virtual Device Interface
|
|
// feature of Microsoft SQL Server. This code implements the client side of
|
|
// the Virtual Device Interface and uses sqlcmd to issue a T-SQL statement
|
|
// to start the server side of the backup or restore.
|
|
//
|
|
// The program will backup or restore a database.
|
|
//
|
|
// The program accepts 6 command line parameters.
|
|
//
|
|
// One of:
|
|
// b perform a backup
|
|
// r perform a restore
|
|
// One of:
|
|
// d perform a backup/restore on a database
|
|
// l perform a backup/restore on a log
|
|
// The DB Name
|
|
// databaseName
|
|
// The SQL username
|
|
// sa
|
|
// The SQL Password
|
|
// MyPassword1!
|
|
// And the filename:
|
|
// filename.bak
|
|
//
|
|
|
|
#include <cstdio> // for file operations
|
|
#include <ctype.h> // for toupper ()
|
|
#include <cstdio>
|
|
#include <iostream>
|
|
#include <memory>
|
|
#include <cstring> // for memset
|
|
#include <stdexcept>
|
|
#include <string>
|
|
#include <unistd.h>
|
|
#include <uuid/uuid.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
|
|
#include "vdi.h" // interface declaration
|
|
#include "vdierror.h" // error constants
|
|
|
|
using namespace std;
|
|
|
|
void performTransfer(
|
|
ClientVirtualDevice* vd,
|
|
int backup,
|
|
char* fname);
|
|
|
|
shared_ptr<FILE> sendSQL(bool doBackup,
|
|
bool dataBackup,
|
|
char* databaseName,
|
|
char* userName,
|
|
char* password);
|
|
|
|
// Using a GUID for the VDS Name is a good way to assure uniqueness.
|
|
//
|
|
static char wVdsName [50];
|
|
|
|
//
|
|
// main function
|
|
//
|
|
int main(int argc, char* argv[])
|
|
{
|
|
ClientVirtualDeviceSet* vds = NULL;
|
|
ClientVirtualDevice* vd = NULL;
|
|
int status;
|
|
|
|
VDConfig config;
|
|
bool badParm = false;
|
|
bool doBackup = true;
|
|
bool dataBackup = true;
|
|
char* databaseName = nullptr;
|
|
char* userName = nullptr;
|
|
char* password = nullptr;
|
|
char* backupFile = nullptr;
|
|
shared_ptr<FILE> processPipe;
|
|
|
|
// Check the input parm
|
|
//
|
|
if (argc == 7)
|
|
{
|
|
if (toupper(argv[1][0]) == 'B')
|
|
{
|
|
doBackup = true;
|
|
}
|
|
else if (toupper(argv[1][0]) == 'R')
|
|
{
|
|
doBackup = false;
|
|
}
|
|
else
|
|
{
|
|
badParm = true;
|
|
}
|
|
|
|
if (toupper(argv[2][0]) == 'D')
|
|
{
|
|
dataBackup = true;
|
|
}
|
|
else if (toupper(argv[2][0]) == 'L')
|
|
{
|
|
dataBackup = false;
|
|
}
|
|
else
|
|
{
|
|
badParm = true;
|
|
}
|
|
|
|
databaseName = &argv[3][0];
|
|
userName = &argv[4][0];
|
|
password = &argv[5][0];
|
|
backupFile = &argv[6][0];
|
|
}
|
|
else
|
|
{
|
|
badParm = true;
|
|
}
|
|
|
|
if (badParm)
|
|
{
|
|
printf("usage: vdipipesample {B|R} {D|L} <databaseName> <userName> <password> <filename>\n"
|
|
"Demonstrate a Backup or Restore using the Virtual Device Interface\n");
|
|
return 1;
|
|
}
|
|
|
|
umask(0);
|
|
vds = new ClientVirtualDeviceSet();
|
|
|
|
// Setup the VDI configuration we want to use.
|
|
// This program doesn't use any fancy features, so the
|
|
// only field to setup is the deviceCount.
|
|
//
|
|
// The server will treat the virtual device just like a pipe:
|
|
// I/O will be strictly sequential with only the basic commands.
|
|
//
|
|
memset(&config, 0, sizeof(config));
|
|
config.deviceCount = 1;
|
|
|
|
// Create a GUID to use for a unique virtual device name
|
|
//
|
|
uuid_t vdsId;
|
|
uuid_generate(vdsId);
|
|
uuid_unparse(vdsId, wVdsName);
|
|
|
|
// Create the virtual device set
|
|
//
|
|
status = vds->Create(wVdsName, &config);
|
|
if (status != 0)
|
|
{
|
|
printf("VDS::Create fails: x%X", status);
|
|
goto exit;
|
|
}
|
|
|
|
// Send the SQL command, by starting 'sqlcmd' in a new process.
|
|
//
|
|
printf("\nSending the SQL...\n");
|
|
|
|
processPipe = sendSQL(doBackup, dataBackup, databaseName, userName, password);
|
|
if (!processPipe)
|
|
{
|
|
printf("sendSQL failed.\n");
|
|
goto shutdown;
|
|
}
|
|
|
|
printf("\nGetting configuration.\n");
|
|
// Wait for the server to connect, completing the configuration.
|
|
//
|
|
status = vds->GetConfiguration(10000, &config);
|
|
if (status != 0)
|
|
{
|
|
printf("VDS::Getconfig fails: x%X\n", status);
|
|
if (status == VD_E_TIMEOUT)
|
|
{
|
|
printf("Timed out. Was Microsoft SQLServer running?\n");
|
|
}
|
|
goto shutdown;
|
|
}
|
|
|
|
printf("Features returned by SQL Server: 0x%x\n", config.features);
|
|
|
|
printf("\nOpening the device.\n");
|
|
// Open the single device in the set.
|
|
//
|
|
status = vds->OpenDevice(wVdsName, &vd);
|
|
if (status != 0)
|
|
{
|
|
printf("VDS::OpenDevice fails: x%X\n", status);
|
|
goto shutdown;
|
|
}
|
|
|
|
printf("\nPerforming data transfer...\n");
|
|
|
|
performTransfer(vd, doBackup, backupFile);
|
|
|
|
shutdown:
|
|
|
|
// Close the set
|
|
//
|
|
vds->Close();
|
|
|
|
exit:
|
|
|
|
// Print out any messages from SQLCMD
|
|
//
|
|
if (processPipe)
|
|
{
|
|
char line[1024];
|
|
while (fgets(line, 1024, processPipe.get()))
|
|
{
|
|
printf("%s", line);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
// Execute a basic backup/restore, by spawning a process to execute 'sqlcmd'.
|
|
//
|
|
shared_ptr<FILE> sendSQL(bool doBackup,
|
|
bool dataBackup,
|
|
char* databaseName,
|
|
char* userName,
|
|
char* password)
|
|
{
|
|
printf("Connecting to SQL Server.");
|
|
char sqlCommand [1024]; // plenty of space for our purpose
|
|
|
|
sprintf(sqlCommand,
|
|
"sqlcmd -U %s -P %s -S . -Q \"%s %s %s %s VIRTUAL_DEVICE='%s' WITH %s, MAXTRANSFERSIZE=1048576 \"",
|
|
userName,
|
|
password,
|
|
(doBackup) ? "BACKUP" : "RESTORE",
|
|
(dataBackup) ? "DATABASE" : "LOG",
|
|
databaseName,
|
|
(doBackup) ? "TO" : "FROM",
|
|
wVdsName,
|
|
(doBackup) ? "FORMAT" : "REPLACE");
|
|
|
|
shared_ptr<FILE> pipe(popen(sqlCommand, "r"), pclose);
|
|
|
|
return pipe;
|
|
}
|
|
|
|
// This routine reads commands from the server until a 'Close' status is received.
|
|
//
|
|
void performTransfer(
|
|
ClientVirtualDevice* vd,
|
|
int backup,
|
|
char* fname)
|
|
{
|
|
FILE* fh;
|
|
VDC_Command* cmd;
|
|
int completionCode;
|
|
size_t bytesTransferred;
|
|
int status;
|
|
|
|
fh = fopen(fname, (backup) ? "wb" : "rb");
|
|
if (fh == NULL)
|
|
{
|
|
printf("Failed to open: %s\n", fname);
|
|
return;
|
|
}
|
|
|
|
// Timeout in seconds
|
|
//
|
|
int timeout = 90;
|
|
while ((status = vd->GetCommand(timeout, &cmd)) == 0)
|
|
{
|
|
bytesTransferred = 0;
|
|
switch (cmd->commandCode)
|
|
{
|
|
case VDC_Read:
|
|
bytesTransferred = fread(cmd->buffer, 1, cmd->size, fh);
|
|
if (bytesTransferred == (size_t)cmd->size)
|
|
{
|
|
completionCode = ERROR_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
// assume failure is eof
|
|
completionCode = ERROR_HANDLE_EOF;
|
|
}
|
|
break;
|
|
|
|
case VDC_Write:
|
|
bytesTransferred = fwrite(cmd->buffer, 1, cmd->size, fh);
|
|
if (bytesTransferred == (size_t)cmd->size)
|
|
{
|
|
completionCode = ERROR_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
// assume failure is disk full
|
|
completionCode = ERROR_DISK_FULL;
|
|
}
|
|
break;
|
|
|
|
case VDC_Flush:
|
|
fflush(fh);
|
|
completionCode = ERROR_SUCCESS;
|
|
break;
|
|
|
|
case VDC_ClearError:
|
|
completionCode = ERROR_SUCCESS;
|
|
break;
|
|
|
|
default:
|
|
// If command is unknown...
|
|
completionCode = ERROR_NOT_SUPPORTED;
|
|
}
|
|
|
|
status = vd->CompleteCommand(cmd, completionCode, bytesTransferred, 0);
|
|
printf("Completed command code: %i, completionCode: %i, bytes; %li \n",
|
|
cmd->commandCode, completionCode, bytesTransferred);
|
|
if (status != 0)
|
|
{
|
|
printf("Completion Failed: x%X\n", status);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (status != VD_E_CLOSE)
|
|
{
|
|
printf("Unexpected termination: x%X\n", status);
|
|
}
|
|
else
|
|
{
|
|
// As far as the data transfer is concerned, no
|
|
// errors occurred. The code which issues the SQL
|
|
// must determine if the backup/restore was
|
|
// really successful.
|
|
//
|
|
printf("Successfully completed data transfer.\n");
|
|
}
|
|
|
|
fclose(fh);
|
|
}
|
|
|