home *** CD-ROM | disk | FTP | other *** search
/ io Programmo 32 / IOPROG_32.ISO / SOFT / SqlEval7 / devtools / samples / BACKUP / mprocess.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1998-08-30  |  17.4 KB  |  638 lines

  1. //
  2. // This file is part of Microsoft SQL Server online documentation.
  3. // Copyright (C) 1992-1997 Microsoft Corporation. All rights reserved.
  4. //
  5. // This source code is an intended supplement to the Microsoft SQL
  6. // Server online references and related electronic documentation.
  7. //
  8. // This sample is for instructional purposes only.
  9. // Code contained herein is not intended to be used "as is" in real applications.
  10. // 
  11. // mprocess.cpp :
  12. //  
  13. //  Test & demonstrate the use of multiple streams where each stream is
  14. //  handled by a secondary process.
  15. //
  16. // This is a sample program used to demonstrate the Virtual Device Interface
  17. // feature of Microsoft SQL Server.
  18. //
  19. // The program will backup or restore the 'pubs' sample database.
  20. //
  21. // The program requires two command line parameters.  
  22. // 1)
  23. // One of:
  24. //  b   perform a backup
  25. //  r   perform a restore
  26. //
  27. //  s   Act as a secondary client  (used internally only)
  28. //
  29. // 2)
  30. // If b or r is given, then a second parm gives the number of streams to use,
  31. // (1-32).
  32. // The secondary processes are invoked automatically, and the second parm is the
  33. // stream id (0..31)
  34. //  
  35.  
  36. #define _WIN32_DCOM
  37.  
  38. #include <objbase.h>    // for 'CoInitialize()'
  39. #include <stdio.h>      // for file operations
  40. #include <ctype.h>      // for toupper ()
  41. #include <windows.h>
  42.  
  43. #include "vdi.h"        // interface declaration
  44. #include "vdierror.h"   // error constants
  45. #include "vdiguid.h"    // define the GUIDs 
  46.  
  47. void LogError (
  48.     LPSTR           location,    // must always be provided
  49.     LPSTR           description, // NULL is acceptable
  50.     DWORD           errCode);    // windows status code
  51.  
  52. int performTransfer (
  53.     IClientVirtualDevice*   vd,
  54.     int                     backup,
  55.     int                     streamId);
  56.  
  57. HANDLE execSQL (int doBackup, int nStreams);
  58.  
  59. int
  60. runSecondary (int streamId, IClientVirtualDeviceSet *vds);
  61.  
  62. int
  63. startSecondaries(
  64.     IClientVirtualDeviceSet *vds,
  65.     HANDLE  hSQLProcess,  // handle to process dealing with the SQL
  66.     int     nStreams,     // number of i/o streams
  67.     char*   pgmName);    // the name of this program
  68.  
  69.  
  70.  
  71. //
  72. // main function
  73. //
  74. int main(int argc, char *argv[])
  75. {
  76.     HRESULT                     hr;
  77.     IClientVirtualDeviceSet*    vds = NULL ; 
  78.     VDConfig                    config;
  79.     int                         badParm=TRUE;
  80.     int                         doBackup;
  81.     HANDLE                      hProcess;
  82.     int                         termCode = -1;
  83.     int                         nStreams=1;
  84.     int                         isSecondary = FALSE;
  85.  
  86.     // Check the input parm
  87.     //
  88.     if (argc == 3)
  89.     {
  90.         sscanf (argv[2], "%d", &nStreams);
  91.         switch (toupper(argv[1][0]))
  92.         {
  93.             case 'B':
  94.                 doBackup = TRUE;
  95.                 badParm = FALSE;
  96.                 break;
  97.         
  98.             case 'R':
  99.                 doBackup = FALSE;
  100.                 badParm = FALSE;
  101.                 break;
  102.  
  103.             case 'S':
  104.                 doBackup = FALSE; // we don't know or care
  105.                 badParm = FALSE;
  106.                 isSecondary = TRUE;
  107.                 // nStreams is the streamid!
  108.                 break;
  109.         }
  110.     }
  111.  
  112.     if (badParm)
  113.     {
  114.         printf ("useage: mprocess {B|R} <nStreams>\n"
  115.             "Demonstrate a multistream Backup or Restore using the Virtual Device Interface\n");
  116.         exit (1);
  117.     }
  118.  
  119.     if (isSecondary)
  120.     {
  121.         printf("Secondary pid %d working on stream %d\n", GetCurrentProcessId (), nStreams);
  122.     }
  123.     else
  124.     {
  125.         // 1..32 streams.
  126.         //
  127.         if (nStreams < 1)
  128.             nStreams = 1;
  129.         else if (nStreams > 32)
  130.             nStreams = 32;
  131.  
  132.         printf ("Performing a %s using %d virtual device(s).\n", 
  133.             (doBackup) ? "BACKUP" : "RESTORE", nStreams);
  134.     }
  135.  
  136.     // Initialize COM Library
  137.     // Note: _WIN32_DCOM must be defined during the compile.
  138.     //
  139.     hr = CoInitializeEx (NULL, COINIT_MULTITHREADED);
  140.  
  141.     if (!SUCCEEDED (hr))
  142.     {
  143.         printf ("Coinit fails: x%X\n", hr);
  144.         exit (1);
  145.     }
  146.  
  147.  
  148.     // Get an interface to the device set.
  149.     // Notice how we use a single IID for both the class and interface
  150.     // identifiers.
  151.     //
  152.     hr = CoCreateInstance ( 
  153.         IID_IClientVirtualDeviceSet,
  154.         NULL, 
  155.         CLSCTX_INPROC_SERVER,
  156.         IID_IClientVirtualDeviceSet,
  157.         (void**)&vds);
  158.  
  159.     if (!SUCCEEDED (hr))
  160.     {
  161.         // This failure might happen if the DLL was not registered.
  162.         //
  163.         printf ("Could not create component: x%X\n", hr);
  164.         printf ("Check registration of SQLVDI.DLL and value of IID\n");
  165.         goto exit;
  166.     }
  167.  
  168.     // Perform secondary processing, if this is a 
  169.     // secondary process.
  170.     //
  171.     if (isSecondary)
  172.     {
  173.         termCode = runSecondary (nStreams, vds);
  174.  
  175.         goto exit;
  176.     }
  177.  
  178.     // The following logic is executed by the primary process.
  179.     //
  180.  
  181.     // Setup the VDI configuration we want to use.
  182.     // This program doesn't use any fancy features, so the
  183.     // only field to setup is the deviceCount.
  184.     //
  185.     // The server will treat the virtual device just like a pipe:
  186.     // I/O will be strictly sequential with only the basic commands.
  187.     //
  188.     memset (&config, 0, sizeof(config));
  189.  
  190.     config.deviceCount = nStreams;
  191.  
  192.     // Create the virtual device set
  193.     //
  194.     hr = vds->Create (L"SUPERBAK", &config);
  195.     if (!SUCCEEDED (hr))
  196.     {
  197.         printf ("VDS::Create fails: x%X", hr);
  198.         goto exit;
  199.     }
  200.  
  201.     // Send the SQL command, via isql in a subprocess.
  202.     //
  203.     printf("\nSending the SQL...\n");
  204.  
  205.     hProcess = execSQL (doBackup, nStreams);
  206.     if (hProcess == NULL)
  207.     {
  208.         printf ("execSQL failed.\n");
  209.         goto shutdown;
  210.     }
  211.  
  212.  
  213.     // Wait for the server to connect, completing the configuration.
  214.     // Notice that we wait a maximum of 15 seconds.
  215.     //
  216.     printf("\nWaiting for SQL to complete configuration...\n");
  217.  
  218.     hr = vds->GetConfiguration (15000, &config);
  219.     if (!SUCCEEDED (hr))
  220.     {
  221.         printf ("VDS::Getconfig fails: x%X\n", hr);
  222.         goto shutdown;
  223.     }
  224.  
  225.     // Handle the virtual devices in secondary processes.
  226.     //
  227.     printf ("\nSpawning secondary processes...\n");
  228.     termCode = startSecondaries (vds, hProcess, nStreams, argv[0]);
  229.     
  230. shutdown:
  231.  
  232.     // Close the set
  233.     //
  234.     vds->Close ();
  235.  
  236.     // COM reference counting: Release the interface.
  237.     //
  238.     vds->Release () ;
  239.  
  240. exit:
  241.  
  242.     // Uninitialize COM Library
  243.     //
  244.     CoUninitialize () ;
  245.  
  246.     return termCode;
  247. }
  248.  
  249. //
  250. // Execute a basic backup/restore, by starting 'isql' in a subprocess.
  251. //
  252. // Returns:
  253. //  NULL    : failed to start isql
  254. //  else    : process handle
  255. //
  256. HANDLE execSQL (int doBackup, int nStreams)
  257. {
  258.     char                    cmd[5000];
  259.     char                    extend[100];
  260.     PROCESS_INFORMATION     pi;
  261.     STARTUPINFO             si;
  262.     int                     ix;
  263.  
  264.     // Build the SQL, submitting it via 'isql'
  265.     //
  266.     sprintf (cmd, 
  267.         "isql -Usa -P -b -Q\"%s DATABASE PUBS %s VIRTUAL_DEVICE='SUPERBAK'",
  268.         (doBackup) ? "BACKUP" : "RESTORE",
  269.         (doBackup) ? "TO"     : "FROM");
  270.     
  271.     for (ix=1; ix<nStreams; ix++)
  272.     {
  273.         sprintf (extend, ", VIRTUAL_DEVICE='SUPERBAK%d'", ix);
  274.         strcat (cmd, extend);
  275.     }
  276.  
  277.     strcat (cmd, "\"");
  278.  
  279.     printf ("Submitting SQL:\n%s\n\n", cmd);
  280.  
  281.     // use my process for startup info
  282.     //
  283.     GetStartupInfo (&si);
  284.  
  285.     if (!CreateProcess (NULL, cmd, NULL, NULL,
  286.             TRUE,   // inherit handles (stdin/stdout)
  287.             0,      // creation flags,
  288.             NULL, NULL,
  289.             &si,    // startup info
  290.             &pi))   // out: process info
  291.     {
  292.         LogError ("startSecondary", "CreateProcess", GetLastError ());
  293.         return NULL;
  294.     }
  295.     
  296.     // Return the process handle
  297.     //
  298.     return (pi.hProcess);
  299. }
  300.  
  301. //-----------------------------------------------------------
  302. // Invoke the secondary processes, and wait for all children
  303. // to complete.
  304. //
  305. // Returns: 0 if no errors were detected.
  306. //
  307. //
  308. int
  309. startSecondaries(
  310.     IClientVirtualDeviceSet *vds,
  311.     HANDLE  hSQLProcess,  // handle to process dealing with the SQL
  312.     int     nStreams,     // number of i/o streams
  313.     char*   pgmName)      // the name of this program
  314. {
  315.     int ix,nActive;
  316.     HANDLE      children[33];  // 32 is maximum number of streams.
  317.                                // plus one for the isql process.
  318.     DWORD   waitStatus, exitCode;
  319.     char                    cmd[200];
  320.     PROCESS_INFORMATION     pi;
  321.     STARTUPINFO             si;
  322.  
  323.     // use my process for startup info
  324.     //
  325.     GetStartupInfo (&si);
  326.  
  327.     for (ix=0; ix<nStreams; ix++)
  328.     {
  329.         sprintf (cmd, "%s s %d", pgmName, ix);
  330.  
  331.         if (!CreateProcess (NULL, cmd, NULL, NULL,
  332.             TRUE,   // inherit handles (just stdin/stdout I hope!)
  333.             0,      // creation flags,
  334.             NULL, NULL,
  335.             &si,    // startup info
  336.             &pi))   // out: process info
  337.         {
  338.             printf ("Error starting %s\n", cmd);
  339.             LogError ("startSecondary", "CreateProcess", GetLastError ());
  340.             goto errorExit;
  341.         }
  342.         // keep the process handle
  343.         children[ix] = pi.hProcess;
  344.     }
  345.  
  346.     // Add the isql process into the array
  347.     //
  348.     children[nStreams] = hSQLProcess;
  349.     nActive = nStreams+1;
  350.  
  351.     // Wait for all to finish.
  352.     // Max wait is one minute for this tiny test.
  353.     //
  354.     printf ("All children are now running.\n"
  355.         "Waiting for their completion...\n");
  356.  
  357.     // Notice how this differs from the threaded model in mthread.cpp.
  358.     // In the multiprocess model, the primary client (running this code)
  359.     // is responsible for detecting abnormal termination of the
  360.     // secondary clients.
  361.     // A simple "wait-for-all" approach may wait indefinitely if only
  362.     // one of the secondaries was to abnormally terminate.
  363.     //
  364.     do
  365.     {
  366.         // Wait for any completion
  367.         //
  368.         waitStatus = WaitForMultipleObjects (nActive, children, 
  369.             FALSE, INFINITE);
  370.  
  371.         if (waitStatus >= WAIT_OBJECT_0 &&
  372.             waitStatus < WAIT_OBJECT_0+nActive)
  373.         {
  374.             // One of the children completed.
  375.             // Determine which one.
  376.             //
  377.             ix = waitStatus - WAIT_OBJECT_0;
  378.  
  379.             // Check its completion code
  380.             //
  381.             if (!GetExitCodeProcess (children[ix], &exitCode))
  382.             {
  383.                 LogError ("startSecondary", "GetExitCode", GetLastError ());
  384.                 goto errorExit;
  385.             }
  386.  
  387.             if (exitCode != 0)
  388.             {
  389.                 printf ("A child exitted with code %d\n", exitCode);
  390.                 goto errorExit;
  391.             }
  392.  
  393.             // It is good programming practice to close handles when
  394.             // finished with them.
  395.             // Since this sample simply terminates the process for error
  396.             // handling, we don't need to do it, as handles are automatically
  397.             // closed as part of process termination.
  398.             //
  399.             CloseHandle (children[ix]);
  400.  
  401.             // Remove the handle for this child
  402.             //
  403.             memmove (&children[ix], &children[ix+1], 
  404.                 sizeof (HANDLE) * (nActive-ix-1));
  405.  
  406.             nActive--;
  407.  
  408.         }
  409.         else
  410.         {
  411.             printf("Unexpected wait code: %d\n", waitStatus);
  412.             goto errorExit;
  413.         }
  414.  
  415.     } while (nActive > 0);
  416.  
  417.     printf ("All children completed successfully\n");
  418.  
  419.     return 0;
  420.  
  421. errorExit:
  422.     // Handle all problems in a trivial fashion:
  423.     //  SignalAbort() will cause all processes using the virtual device set
  424.     //  to terminate processing.
  425.     //  Thus, we don't bother waiting for any children to terminate.
  426.     //
  427.     vds->SignalAbort ();
  428.     return -1;
  429.  
  430. }
  431.  
  432. //------------------------------------------------------------------
  433. // Perform secondary client processing
  434. // Return 0 if no errors detected, else nonzero.
  435. //
  436. int
  437. runSecondary (int streamId, IClientVirtualDeviceSet *vds)
  438. {
  439.     HRESULT                         hr;
  440.     WCHAR                           devName[20];
  441.     IClientVirtualDevice*           vd;
  442.     VDConfig                        config;
  443.     int                             termCode;
  444.  
  445.     // Open the device
  446.     //
  447.     if (streamId == 0)
  448.     {
  449.         // The first device has the same name as the set.
  450.         //
  451.         wcscpy (devName, L"SUPERBAK");
  452.     }
  453.     else
  454.     {
  455.         // For this example, we've simply appended a number
  456.         // for additional devices.  You are free to name them
  457.         // as you wish.
  458.         //
  459.         swprintf (devName, L"SUPERBAK%d", streamId);
  460.     }
  461.  
  462.     // Open the virtual device set in this secondary process.
  463.     //
  464.     hr = vds->OpenInSecondary (L"SUPERBAK");
  465.     if (!SUCCEEDED (hr))
  466.     {
  467.         printf ("VD::Open(%ls) fails: x%X", devName, hr);
  468.         return -1;
  469.     }
  470.  
  471.     // Open the device assigned to this process.
  472.     //
  473.     hr = vds->OpenDevice (devName, &vd);
  474.     if (!SUCCEEDED (hr))
  475.     {
  476.         printf ("OpenDevice fails on %ls: x%X", devName, hr);
  477.         return -1;
  478.     }
  479.  
  480.     // Grab the config to figure out data direction
  481.     //
  482.     hr = vds->GetConfiguration (INFINITE, &config);
  483.     if (!SUCCEEDED (hr))
  484.     {
  485.         printf ("VDS::Getconfig fails: x%X\n", hr);
  486.         termCode = -1;
  487.         goto errExit;
  488.     }
  489.  
  490.     printf ("\nPerforming data transfer...\n");
  491.         
  492.     termCode = performTransfer (vd, 
  493.         (config.features&VDF_WriteMedia), streamId);
  494.  
  495. errExit:
  496.  
  497.     // If errors were detected, force an abort.
  498.     //
  499.     if (termCode != 0)
  500.     {
  501.         vds->SignalAbort ();
  502.     }
  503.  
  504.     vds->Close ();
  505.  
  506.     return termCode;
  507. }
  508.  
  509.  
  510. // This routine reads commands from the server until a 'Close' status is received.
  511. // It simply synchronously reads or writes a file on the root of the current drive.
  512. //
  513. // Returns 0, if no errors are detected, else non-zero.
  514. //
  515. int performTransfer (
  516.     IClientVirtualDevice*   vd,
  517.     int                     backup,
  518.     int                     streamId)
  519. {
  520.     FILE *          fh;
  521.     char            fname[80];
  522.     VDC_Command *   cmd;
  523.     DWORD           completionCode;
  524.     DWORD           bytesTransferred;
  525.     HRESULT         hr;
  526.     int             termCode = -1;
  527.  
  528.     sprintf (fname, "multi.%d.dmp", streamId);
  529.  
  530.     fh = fopen (fname, (backup)? "wb" : "rb");
  531.     if (fh == NULL )
  532.     {
  533.         printf ("Failed to open: %s\n", fname);
  534.         return -1;
  535.     }
  536.  
  537.     while (SUCCEEDED (hr=vd->GetCommand (INFINITE, &cmd)))
  538.     {
  539.         bytesTransferred = 0;
  540.         switch (cmd->commandCode)
  541.         {
  542.             case VDC_Read:
  543.                 bytesTransferred = fread (cmd->buffer, 1, cmd->size, fh);
  544.                 if (bytesTransferred == cmd->size)
  545.                     completionCode = ERROR_SUCCESS;
  546.                 else
  547.                     // assume failure is eof
  548.                     completionCode = ERROR_HANDLE_EOF;
  549.  
  550.                 break;
  551.  
  552.             case VDC_Write:
  553.                 bytesTransferred = fwrite (cmd->buffer, 1, cmd->size, fh);
  554.                 if (bytesTransferred == cmd->size )
  555.                 {
  556.                     completionCode = ERROR_SUCCESS;
  557.                 }
  558.                 else
  559.                     // assume failure is disk full
  560.                     completionCode = ERROR_DISK_FULL;
  561.                 break;
  562.  
  563.             case VDC_Flush:
  564.                 fflush (fh);
  565.                 completionCode = ERROR_SUCCESS;
  566.                 break;
  567.     
  568.             case VDC_ClearError:
  569.                 completionCode = ERROR_SUCCESS;
  570.                 break;
  571.  
  572.             default:
  573.                 // If command is unknown...
  574.                 completionCode = ERROR_NOT_SUPPORTED;
  575.         }
  576.  
  577.  
  578.         hr = vd->CompleteCommand (cmd, completionCode, bytesTransferred, 0);
  579.         if (!SUCCEEDED (hr))
  580.         {
  581.             printf ("Completion Failed: x%X\n", hr);
  582.             break;
  583.         }
  584.     }
  585.  
  586.     if (hr != VD_E_CLOSE)
  587.     {
  588.         printf ("Unexpected termination: x%X\n", hr);
  589.     }
  590.     else
  591.     {
  592.         // As far as the data transfer is concerned, no
  593.         // errors occurred.  The code which issues the SQL
  594.         // must determine if the backup/restore was
  595.         // really successful.
  596.         //
  597.         printf ("Successfully completed data transfer.\n");
  598.         termCode = 0;
  599.     }
  600.  
  601.     fclose (fh);
  602.  
  603.     return termCode;
  604. }
  605.  
  606. //--------------------------------------------------------------------
  607. // 
  608. // A simple error logger.
  609. //
  610. void LogError (
  611.     LPSTR           location,    // must always be provided
  612.     LPSTR           description, // NULL is acceptable
  613.     DWORD           errCode)     // windows status code
  614. {
  615.     LPVOID lpMsgBuf;
  616.  
  617.     printf (
  618.         "Error at %s: %s StatusCode: %X\n",
  619.         location, 
  620.         (description==NULL)?"":description,
  621.         errCode);
  622.  
  623.     // Attempt to explain the code
  624.     //
  625.     if (errCode != 0 && FormatMessage(
  626.         FORMAT_MESSAGE_ALLOCATE_BUFFER | 
  627.             FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,    
  628.         NULL,
  629.         errCode,
  630.         MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
  631.         (LPTSTR) &lpMsgBuf,    0,    NULL ) )// Process any inserts in lpMsgBuf.
  632.     {
  633.         printf ("Explanation: %s\n", lpMsgBuf);
  634.         LocalFree( lpMsgBuf );
  635.     }
  636. }
  637.  
  638.