home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / msdn_vcb / samples / vc98 / sdk / dbmsg / sql / ods / procsrv / procsrv.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-04-03  |  64.1 KB  |  2,088 lines

  1. //
  2. // This program is an example of an Open Data Services application. It accepts
  3. // requests from clients to execute stored procedures either as language
  4. // events or as remote stored procedure calls.
  5. // It may also be invoked using the Service Control Manager.
  6. //
  7.  
  8. #include <stdlib.h>
  9. #include <stdio.h>
  10. #include <conio.h>
  11. #include <string.h>
  12. #include <ctype.h>
  13. #include <process.h>
  14. #include <srv.h>
  15.  
  16. #define VERSION "6.00.01"
  17.  
  18. // Globals
  19. //
  20. BOOL         SrvCtrMan;
  21. HANDLE       hServerDone;
  22. SRV_SERVER * gblServer = NULL;
  23. DBCHAR     * szRegistryName = "PROCSRV";  // Default registry name
  24.  
  25. SERVICE_STATUS_HANDLE   NTBServiceHandle;
  26. CRITICAL_SECTION        SCMCrtSec;
  27. SERVICE_STATUS          NTBServiceStatus;
  28.  
  29.  
  30. // Syscharsets query from DBLIB 4.2 NT clients
  31. //
  32. #define SERVER_INFO_QUERY   "exec sp_server_info 18"
  33.  
  34. // Define some user message codes.
  35. //
  36. #define     SRV_MAXERROR           20000
  37. #define     SP_UNKNOWN             SRV_MAXERROR + 1
  38. #define     INVALID_SP_SYNTAX      SRV_MAXERROR + 2
  39. #define     BAD_SP_PARAMETER       SRV_MAXERROR + 3
  40. #define     BROADCAST              SRV_MAXERROR + 4
  41. #define     EXEC                   SRV_MAXERROR + 5
  42.  
  43. // Miscellaneous defines used by sp-handling routines.
  44. //
  45. #define EXEC_CMD            "exec"
  46. #define MAXNAME             31
  47. #define MAXLEN              80
  48. #define MAXPARAMS           4
  49. #define VALUE               0x0000
  50. #define REFERENCE           0x0001
  51. #define CMDSTR              7
  52. #define BUF_SIZE            2048
  53. #define XBASE_HDR_SIZE      32
  54. #define XBASE_MAX_COLUMNS   128
  55.  
  56. // Standard error macro for reporting API errors
  57. //
  58. #define SETERROR( api, retstring )                          \
  59.     sprintf(retstring,"%s: Error %d from %s on line %d\n",  \
  60.             __FILE__, GetLastError(), api, __LINE__);
  61.  
  62. // Event handlers for SP requests.
  63. //
  64. SRVRETCODE proclist();         // List SP associated with Server
  65. SRVRETCODE sp_exec();          // Execute the specified command string
  66. SRVRETCODE diskfree();         // Return the space on a given drive
  67. SRVRETCODE disklist();         // List available drives and their space
  68. SRVRETCODE scan_xbase();       // Open and read an xBase file
  69.  
  70. // Stored Procedure parameter information structure.
  71. //
  72. typedef struct sp_params {
  73.     DBCHAR name[MAXNAME];           // Parameter name
  74.     int    type;                    // Parameter data type
  75.     DBINT  length;                  // Parameter type length
  76.     int    status;                  // Parameter return status
  77.     DBCHAR defaultvalue[MAXLEN *10];// Optional default value
  78. } SP_PARAMS;
  79.  
  80. // Stored Procedure information structure.
  81. //
  82. typedef struct sp_info {
  83.     DBCHAR      name[MAXNAME];          // Procedure name
  84.     DBCHAR      usage[MAXLEN];          // Usage string
  85.     int         numparams;              // Number of parameters
  86.     SP_PARAMS   params[MAXPARAMS];      // Parameter array
  87.     SRVRETCODE   (*handler)(VOID *, ...);  // Pointer to function with variable arguments
  88. } SP_INFO;
  89.  
  90. // Array of Stored Procedure handlers.
  91. //
  92. SP_INFO Sps[] =
  93. {
  94.     "proclist",
  95.     "usage: proclist",
  96.     0,
  97.        {
  98.         0
  99.        },
  100.     proclist,
  101.  
  102.     "sp_exec",              // Procedure name
  103.     "usage: sp_exec <[@command =] command string>", // Procedure usage
  104.     1,                      // Number of parameters
  105.    {    // Parameter definitions
  106.         "command",          // Parameter name
  107.         SRVCHAR,            // Parameter type
  108.         MAXLEN,             // Parameter length (0 if fixed)
  109.         VALUE,              // Pass by value
  110.         "dir *.*",          // Default parameter value
  111.     },
  112.     sp_exec,                // Procedure function pointer
  113.  
  114.     "disklist",
  115.     "usage: disklist ",
  116.     0,
  117.    {
  118.         0
  119.     },
  120.     disklist,
  121.  
  122.     "diskfree",
  123.     "usage: diskfree <[@drive =] drive letter> [,] <[@space =] free space>",
  124.     2,
  125.    {
  126.         "drive",
  127.         SRVCHAR,
  128.         1,
  129.         VALUE,              // pass by value
  130.         "c",
  131.  
  132.         "space",
  133.         SRVINT4,
  134.         8,
  135.         REFERENCE,          // pass by reference
  136.         "0",
  137.     },
  138.     diskfree,
  139.  
  140.     "scan_xbase",               // rpc name
  141.     "usage: scan_xbase <[@file_name =] xbase file name>",   // rpc usage
  142.     1,                          // number of parameters
  143.    {   // parameter definitions
  144.         "file_name",            // parameter name
  145.         SRVCHAR,                // parameter type
  146.         MAXLEN,                 // parameter length (0 if fixed)
  147.         VALUE,                  // pass by value
  148.         "\\sql\\opends\\samples\\procsrv\\build\\sales.dbf", // default parameter value
  149.     },
  150.     scan_xbase,                 // rpc function pointer
  151.  
  152. };
  153.  
  154. #define Rpcnumber sizeof(Sps) / sizeof(SP_INFO)
  155.  
  156. // Other function prototypes
  157. //
  158. void main( int argc, char **argv );
  159. void WINAPI ProcSrvMain( DWORD argc, char *argv[] );
  160. void initsignal( SRV_SERVER *, char * );
  161. void ctrlc_hndl( ULONG );
  162. void WINAPI NTBServiceCtrlHandler( DWORD );
  163. void completesignal( SRV_SERVER * );
  164. void NTBShutdown( LPVOID );
  165. char * get_last_error_str();
  166.  
  167. SRVRETCODE chk_err( SRV_SERVER * server,
  168.                  SRV_PROC   * srvproc,
  169.                  int          srverror,
  170.                  BYTE         severity,
  171.                  BYTE         state,
  172.                  int          oserrnum,
  173.                  DBCHAR     * errtext,
  174.                  int          errtextlen,
  175.                  DBCHAR     * oserrtext,
  176.                  int          oserrtextlen );
  177.  
  178. SRVRETCODE init_remote(SRV_PROC * srvproc);
  179.  
  180. SRVRETCODE init_server(SRV_SERVER * server);
  181.  
  182. SRVRETCODE sp_execute(SRV_PROC * srvproc);
  183.  
  184. SRVRETCODE exit_remote(SRV_PROC *srvproc);
  185.  
  186. SRVRETCODE lang_execute(SRV_PROC *srvproc);
  187.  
  188. // Miscellaneous prototypes for sp language events
  189. //
  190. SRVRETCODE lang_parser(SRV_PROC *srvproc, SP_INFO **sp,
  191.                     DBCHAR paramvalues[MAXPARAMS][MAXLEN]);
  192.  
  193. DBCHAR *scan_next(DBCHAR *string, DBCHAR *word);
  194.  
  195.  
  196. // =================================== main ==================================
  197. //
  198. void main( int argc, char *argv[] )
  199. {
  200.     SERVICE_TABLE_ENTRY DispatchTable[] =
  201.     {
  202.         "ProcSrv", ProcSrvMain,
  203.         NULL,      NULL
  204.     };
  205.  
  206.     // Assume ProcSrv.exe was started from the Service Control Manager or
  207.     // SQL Service Manager.
  208.     //
  209.     SrvCtrMan = TRUE;
  210.  
  211.     // Check for command line arguments.  The following command line arguments
  212.     // are supported:
  213.     //
  214.     //    -c  Procsrv was started from the command line.  Avoids the timeout
  215.     //        delay when an attempt is made to start procsrv as a service.
  216.     //
  217.     //    -r<registry key name>
  218.     //        Look in Registry under the <registry key name> for the ListenOn
  219.     //        values.  Also assumes procsrv.exe was started from command line.
  220.     //
  221.     //    -?  Displays the command usage information.
  222.     //
  223.     if( argc > 2 )
  224.         goto Usage;
  225.  
  226.     if( argc == 2 )
  227.     {
  228.         if( !strcmp(argv[1], "-?") )
  229.             goto Usage;
  230.  
  231.         if( !stricmp(argv[1], "-c") )
  232.             SrvCtrMan = FALSE;
  233.         else if( strlen(argv[1]) < 3 )
  234.             goto Usage;
  235.         else if( !strnicmp(argv[1], "-r", 2) )
  236.         {
  237.             szRegistryName = argv[1] + 2;
  238.             SrvCtrMan = FALSE;
  239.         }
  240.         else
  241.             goto Usage;
  242.     }
  243.  
  244.     if( SrvCtrMan )
  245.     {
  246.         // Now we will attempt to start the ProcSrv.exe as a service.  The attempt
  247.         // will time out if this process was started from the command line.
  248.         // StartServiceCtrlDispatcher does not return until after ProcSrv has stopped.
  249.         // The ProcSrvMain is called from Service Control Manager within
  250.         // the context of the same process.
  251.         //
  252.         if( StartServiceCtrlDispatcher(DispatchTable) )
  253.         {
  254.             _flushall();
  255.             return;
  256.         }
  257.     }
  258.  
  259.     // The service was started from the command line or the attempt to
  260.     // start the service failed.  We can assume that this process
  261.     // was started from the command line.
  262.     //
  263.     SrvCtrMan = FALSE;
  264.     ProcSrvMain( argc, argv );
  265.     return;
  266.  
  267. Usage:
  268.     printf( "Usage: procsrv [-c] | [-r<registry key name>] | [-?]\n"
  269.             "    -c  Procsrv was started from the command line\n"
  270.             "    -r  Look in Registry under <registry key name> for ListenOn values\n"
  271.             "    -?  Displays this help message\n" );
  272. }
  273.  
  274.  
  275. // ================================== ProcSrvMain ============================
  276. //
  277. void WINAPI ProcSrvMain( DWORD argc, char *argv[] )
  278. {
  279.     SRV_CONFIG * config;        // The configuration structure
  280.     DWORD        dwPathLength;
  281.     char         szPath[1024];
  282.     char         szLogBuffer[1024];
  283.  
  284.     if( !SrvCtrMan )
  285.     {
  286.         printf( "\nProcedure Server, Copyright 1994, Microsoft\n" );
  287.         printf( "              version: %s\n\n", VERSION );
  288.     }
  289.  
  290.     // Allocate a configuration structure that is used to initialize
  291.     // the Open Data Services application
  292.     //
  293.     config = srv_config_alloc();
  294.  
  295.     // Allow 20 connections at a time.
  296.     //
  297.     srv_config(config, (DBINT)SRV_CONNECTIONS, "20", SRV_NULLTERM);
  298.  
  299.     // Set the log file.
  300.     //
  301.     // Get the path of this process.  We'll use it to constuct the path of the
  302.     // log file.
  303.     //
  304.     szPath[0] = '\0';
  305.     dwPathLength = GetModuleFileName( GetModuleHandle(NULL),
  306.                                       szPath,
  307.                                       sizeof(szPath) );
  308.  
  309.     // Stip off process name (i.e. "ProcSrv.exe")
  310.     //
  311.     while( dwPathLength > 1 )
  312.     {
  313.         --dwPathLength;
  314.         if( szPath[dwPathLength] == '\\' || szPath[dwPathLength] == ':' )
  315.         {
  316.             dwPathLength++;
  317.             szPath[dwPathLength] = '\0';    // Null terminate after back slash
  318.             break;
  319.         }
  320.     }
  321.  
  322.     // Append "<registryname>.log" to path
  323.     //
  324.     strcat( szPath, szRegistryName );
  325.     strcat( szPath, ".log" );
  326.  
  327.     srv_config(config, (DBINT)SRV_LOGFILE, szPath, SRV_NULLTERM);
  328.  
  329.     // All data source strings will be converted from ANSI to the OEM codepage
  330.     // in order to make this application behave like SQL Server.
  331.     //
  332.     srv_config(config, (DBINT)SRV_ANSI_CODEPAGE, "FALSE", SRV_NULLTERM);
  333.  
  334.     // Install the error handler.
  335.     //
  336.     srv_errhandle(chk_err);
  337.  
  338.     //  Initialize Procedure Server and save the server handle
  339.     //  so it can be used in later functions.
  340.     //
  341.     gblServer = srv_init(config, szRegistryName, SRV_NULLTERM);
  342.  
  343.     if( gblServer == NULL )
  344.     {
  345.         printf( "\nUnable to initialize Procedure Server.  "
  346.                 "Check Event Log.\n" );
  347.         goto Exit;
  348.     }
  349.  
  350.     // Create an event flag that will tell us when ProcSrv is completely
  351.     // shut down (srv_run() has returned)
  352.     //
  353.     hServerDone = CreateEvent( NULL, TRUE, FALSE, NULL );
  354.     if( hServerDone == NULL )
  355.     {
  356.         sprintf( szLogBuffer,
  357.                  "Procedure Server Service Manager Failer: %s "
  358.                  "(ProcSrvMain(), line = %d), message = %s",
  359.                  szRegistryName,
  360.                  __LINE__,
  361.                  get_last_error_str() );
  362.         srv_log( gblServer, TRUE, szLogBuffer, SRV_NULLTERM );
  363.         printf( "\n%s\n", szLogBuffer );
  364.         goto Exit;
  365.     }
  366.  
  367.     // When starting Procedure Server, initialize the remote server structure.
  368.     // This is done in the init_server() function.
  369.     // All the other event handlers are also defined in the init_server()
  370.     // function.
  371.     //
  372.     srv_handle( gblServer, (DBINT)SRV_START, init_server );
  373.  
  374.     sprintf( szLogBuffer,
  375.              "Procedure Server Starting, name = %s",
  376.              szRegistryName );
  377.  
  378.     //  Now everything's ready to go with Procedure Server, so we
  379.     //  start it and keep it going until we get a stop request.
  380.     //
  381.     srv_log( gblServer, FALSE, " ", SRV_NULLTERM );    // insert blank line
  382.     srv_log( gblServer, TRUE, szLogBuffer, SRV_NULLTERM );
  383.  
  384.     // initsignal() notifies the Service Control Manager that the
  385.     // service has been started and sets up the signal handlers.
  386.     //
  387.     initsignal( gblServer, argv[0] );
  388.  
  389.     // completesignal() notifies the Service Control Manager that the
  390.     // service has completed its startup process.
  391.     //
  392.     completesignal( gblServer );
  393.  
  394.     // srv_run() does not return until either a FAILure occurs or a SRV_EXIT
  395.     // event has been issued.
  396.     //
  397.     if( srv_run(gblServer) == FAIL )
  398.     {
  399.         printf( "\nProcedure Server Failer, Check logs.\n" );
  400.         goto Exit;
  401.     }
  402.  
  403.     // Set flag indicating all processing completed
  404.     //
  405.     SetEvent( hServerDone );
  406.     return;
  407.  
  408. Exit:
  409.     // initsignal() notifies the Service Control Manager that the
  410.     // service has been started and sets up the signal handlers.
  411.     // This must be done even though we have an "error exit" condition.
  412.     //
  413.     initsignal( gblServer, argv[0] );
  414.  
  415.     // completesignal() notifies the Service Control Manager that the
  416.     // service has completed its startup process.
  417.     //
  418.     completesignal( gblServer );
  419.  
  420.     // NTBShutdown() sets the SQL Service Manager to "Stop" and terminates
  421.     // the service.
  422.     //
  423.     SetThreadPriority( (HANDLE)_beginthread(NTBShutdown, 0, NULL),
  424.                        THREAD_PRIORITY_HIGHEST );
  425.  
  426.     // Set flag indicating all processing completed
  427.     //
  428.     SetEvent( hServerDone );
  429. }
  430.  
  431.  
  432. // This section defines all the Open Data Services event handler functions for
  433. // the Procedure Server application. The procedures implemented are:
  434. //
  435. //
  436. //  PROCLIST        Returns all the supported procedures and their usuage.
  437. //
  438. //  SP_EXEC         Executes a command string and returns output as a rows
  439. //                  of text.
  440. //
  441. //  DISKFREE        Returns the amount the amount of available space for a given
  442. //                  drive.
  443. //
  444. //  DISKLIST        Returns a row for each defined drive containing its name
  445. //                  and the amount of disk space available.
  446. //
  447. // SCAN_XBASE       Reads an xBase file and sends it to the client as if it
  448. //                  were a SQL Server query result set (the equivalent of a
  449. //                  'SELECT * FROM tablename' SQL statement).
  450. //
  451. //
  452. // ================================== init_server ============================
  453. //
  454. // INIT_SERVER
  455. //    Initialize the server on a SRV_START event.
  456. //    Event handlers for the server are installed.
  457. //
  458. // Parameters:
  459. //    server - Pointer to SRV_SERVER structure
  460. //
  461. // Returns:
  462. //    SRV_CONTINUE
  463. //
  464. SRVRETCODE init_server( SRV_SERVER *server )
  465. {
  466.     char log_buffer[256];
  467.  
  468.     //  When we get a connection request from a client, we want to
  469.     //  call "init_remote()" to make a connection to the remote
  470.     //  server.
  471.     //
  472.     srv_handle(server, (DBINT)SRV_CONNECT, init_remote);
  473.  
  474.     // When the client issues a language request, call
  475.     // "lang_execute()" to send the SQL statement to the remote DBMS.
  476.     //
  477.     srv_handle(server, (DBINT)SRV_LANGUAGE, lang_execute);
  478.  
  479.     // When the client issues an RSP, call "sp_execute()"
  480.     // to send the RSP to the remote DBMS (the SQL Server).
  481.     //
  482.     srv_handle(server, (DBINT)SRV_RPC, sp_execute);
  483.  
  484.     // When a disconnect request is issued, call "exit_remote()"
  485.     // to close the connection to the remote DBMS.
  486.     //
  487.     srv_handle(server, (DBINT)SRV_DISCONNECT, exit_remote);
  488.  
  489.     // Log Server information to log file
  490.     //
  491.     sprintf(log_buffer, "Client connections allowed = %s",
  492.             srv_sfield(server, SRV_CONNECTIONS, (int *)NULL));
  493.  
  494.     srv_log(server, FALSE, log_buffer, SRV_NULLTERM);
  495.     printf("%s\n", log_buffer);
  496.  
  497.     return SRV_CONTINUE;
  498. }
  499.  
  500.  
  501. // ================================== init_remote ============================
  502. //
  503. // INIT_REMOTE
  504. //    Event handler for a SRV_CONNECT event.
  505. //    A connection is made to the procedure server.
  506. //
  507. // Parameters:
  508. //    srvproc - the handle to the client connection that got the SRV_CONNECT.
  509. //
  510. // Returns:
  511. //    SRV_CONTINUE
  512. //
  513. // Side Effects:
  514. //    If the connection to the remote dbms cannot be made, then issue
  515. //    a SRV_DISCONNECT request.
  516. //
  517. //
  518. SRVRETCODE init_remote( SRV_PROC *srvproc )
  519. {
  520.     char *string;
  521.     int   len;
  522.  
  523.     // Set server name
  524.     //
  525.     srvproc->serverlen = (BYTE)strlen(szRegistryName);
  526.     srvproc->servername = srv_alloc((DBINT)srvproc->serverlen);
  527.     strcpy(srvproc->servername, szRegistryName);
  528.  
  529.     // Display info on console
  530.     //
  531.     string = srv_pfield(srvproc, SRV_CPID, &len);
  532.     string[len] = '\0';
  533.     printf("\nClient process ID: %s\n", string);
  534.  
  535.     string = srv_pfield(srvproc,    SRV_USER, &len);
  536.     string[len] = '\0';
  537.     printf("User name: %s\n", string);
  538.  
  539.     string = srv_pfield(srvproc, SRV_APPLNAME, &len);
  540.     string[len] = '\0';
  541.     if (len > 0)
  542.         printf("Application program name: %s\n", string);
  543.  
  544.     string = srv_pfield(srvproc, SRV_RMTSERVER, &len);
  545.     string[len] = '\0';
  546.     if (len > 0)
  547.         printf("Remote Server: %s\n", string);
  548.  
  549.     return SRV_CONTINUE;
  550. }
  551.  
  552.  
  553. // ================================ lang_execute =============================
  554. //
  555. // LANG_EXECUTE
  556. //    Execute a client language request on the procedure server.
  557. //
  558. // Parameters:
  559. //    srvproc - process handle to the current client connection.
  560. //
  561. // Returns:
  562. //    SRV_CONTINUE
  563. //
  564. SRVRETCODE lang_execute( SRV_PROC *srvproc )
  565. {
  566.     int      i;
  567.     DBCHAR   paramvalues[MAXPARAMS][MAXLEN];
  568.     BYTE     convertvalues[MAXPARAMS][MAXLEN];
  569.     SP_INFO *sp = NULL;
  570.  
  571.     // Initialize parameter storage
  572.     //
  573.     for (i = 0; i < MAXPARAMS; i++) {
  574.         memset(paramvalues[i], 0, MAXLEN);
  575.         memset(convertvalues[i], 0, MAXLEN);
  576.     }
  577.     if (lang_parser(srvproc, &sp, paramvalues) == SUCCEED) {
  578.         for (i = 0; i < sp->numparams; i++) {
  579.             if (sp->params[i].status == REFERENCE) {
  580.                 srv_sendstatus(srvproc, 1);
  581.                 srv_sendmsg(srvproc, SRV_MSG_ERROR, INVALID_SP_SYNTAX,
  582.                             SRV_INFO, (DBTINYINT)0, NULL, 0, 0,
  583.                                     "Procedure contains a return parameter.\
  584.                             Unable to execute as a language event.",
  585.                              SRV_NULLTERM);
  586.                 srv_senddone(srvproc, (SRV_DONE_ERROR | SRV_DONE_FINAL), 0,
  587.                              0);
  588.                 return SRV_CONTINUE;
  589.  
  590.             }
  591.             if (strlen(paramvalues[i]) == 0 &&
  592.                 strlen(sp->params[i].defaultvalue) == 0) {
  593.                 srv_sendstatus(srvproc, 1);
  594.                 srv_sendmsg(srvproc, SRV_MSG_ERROR, INVALID_SP_SYNTAX,
  595.                             SRV_INFO, (DBTINYINT)0, NULL, 0, 0, sp->usage,
  596.                             SRV_NULLTERM);
  597.                 srv_senddone(srvproc, (SRV_DONE_ERROR | SRV_DONE_FINAL), 0,
  598.                              0);
  599.                 return SRV_CONTINUE;
  600.             }
  601.             if (strlen(paramvalues[i]) == 0 &&
  602.                 strlen(sp->params[i].defaultvalue) != 0)
  603.                 strcpy(paramvalues[i], sp->params[i].defaultvalue);
  604.  
  605.             // convert parameters from character string to parmeter type
  606.             //
  607.             srv_convert(srvproc, SRVCHAR, paramvalues[i], -1,
  608.                         sp->params[i].type, &convertvalues[i],
  609.                         sp->params[i].length);
  610.         }
  611.  
  612.         // Execute the procedure
  613.         //
  614.         (sp->handler)((VOID *)srvproc, &convertvalues[0], &convertvalues[1],
  615.                       &convertvalues[2], &convertvalues[3]);
  616.     }
  617.     return SRV_CONTINUE;
  618. }
  619.  
  620.  
  621. // ================================= lang_parser =============================
  622. //
  623. // LANG_PARSER
  624. //    A procedure server specific language event parser.
  625. //
  626. // Parameters:
  627. //    srvproc - process handle to the current client connection.
  628. //    sp - Pointer to the stored procedure structure
  629. //    paramvalues - An array of the values of the parameters.
  630. //
  631. // Returns:
  632. //    SUCCEED
  633. //
  634. SRVRETCODE lang_parser( SRV_PROC *srvproc,
  635.                      SP_INFO **sp,
  636.                      DBCHAR    paramvalues[MAXPARAMS][MAXLEN] )
  637. {
  638.     DBCHAR *query;      // pointer to language buffer
  639.     int     i;
  640.     int     numparams;
  641.     DBCHAR  msg[MAXLEN *5];
  642.     DBINT   msgnum;
  643.     DBCHAR  spname[MAXLEN];
  644.     BOOL    paramsyntax = FALSE;
  645.     DBCHAR  paramname[MAXLEN];
  646.     DBCHAR  equalstring[2];
  647.     DBCHAR *paramvalue = NULL;
  648.  
  649.     query = srv_langptr(srvproc);
  650.  
  651.     // Ignore the syscharsets query from DBLIB 4.2 NT clients
  652.     //
  653.     if (!strncmp(query, SERVER_INFO_QUERY, strlen(SERVER_INFO_QUERY))){
  654.         srv_senddone(srvproc, SRV_DONE_FINAL, 0, 0);
  655.         return FAIL;
  656.     }
  657.  
  658.     query = scan_next(query, spname);
  659.     if (strlen(spname) == 0) {
  660.         srv_senddone(srvproc, SRV_DONE_FINAL, 0, 0);
  661.         return FAIL;
  662.     }
  663.     if (strnicmp(spname, EXEC_CMD, (sizeof(EXEC_CMD) - 1)) == 0) {
  664.         // stored procedure name
  665.         //
  666.         query = scan_next(query, spname);
  667.         if (strlen(spname) == 0)
  668.             goto syntax_error;
  669.     }
  670.  
  671.     // Check for existence
  672.     //
  673.     for (i = 0; i < Rpcnumber; i++)
  674.         if (strcmp(Sps[i].name, spname) == 0) {
  675.             *sp = &Sps[i];
  676.             break;
  677.         }
  678.     if (*sp == NULL) {
  679.         sprintf(msg, "Procedure \'%s \' not found.", spname);
  680.         msgnum = SP_UNKNOWN;
  681.         goto error;
  682.     }
  683.  
  684.     // Parameters
  685.     //
  686.     numparams = 0;
  687.     while (*query != '\0') {
  688.         if (++numparams > (*sp)->numparams) {
  689.             sprintf(msg, (*sp)->usage);
  690.             msgnum = INVALID_SP_SYNTAX;
  691.             goto error;
  692.         }
  693.         if (!paramsyntax && *query == '@')
  694.             paramsyntax = TRUE; // parameter name mode
  695.  
  696.         if (!paramsyntax)
  697.             if (paramvalue == NULL)
  698.                 paramvalue = paramvalues[0];
  699.             else
  700.                 paramvalue += MAXLEN;
  701.  
  702.         if (paramsyntax) {
  703.             if (*query != '@') {
  704.                 sprintf( msg,
  705.                          "Once the form '@name = value' has been used, "
  706.                          "all subsequent parameters must be passed in "
  707.                          "the form '@name = value'." );
  708.                 msgnum = INVALID_SP_SYNTAX;
  709.                 goto error;
  710.             } else
  711.                 query++;
  712.  
  713.             query = scan_next(query,
  714.             paramname);
  715.             if (strlen(paramname) == 0)
  716.                 goto syntax_error;
  717.  
  718.             // Get parameter index
  719.             //
  720.             paramvalue = NULL;
  721.             for (i = 0; i < (*sp)->numparams; i++)
  722.                 if (strcmp((*sp)->params[i].name, paramname) == 0) {
  723.                     paramvalue = paramvalues[i];
  724.                     break;
  725.                 }
  726.             if (paramvalue == NULL) {
  727.                 sprintf( msg,
  728.                          "Procedure '%s' does not recognize parameter name: %s",
  729.                          spname, paramname );
  730.                 msgnum = BAD_SP_PARAMETER;
  731.                 goto error;
  732.             }
  733.  
  734.             // Already assigned value
  735.             //
  736.             if (strlen(paramvalue) > 0)
  737.                 goto syntax_error;
  738.  
  739.             // Check for '='
  740.             //
  741.             query = scan_next(query,
  742.             equalstring);
  743.             if (*equalstring != '=')
  744.                 goto syntax_error;
  745.  
  746.         }
  747.         query = scan_next(query,
  748.         paramvalue);
  749.         if (strlen(paramvalue) == 0)
  750.             goto syntax_error;
  751.  
  752.         if (*query == ',') {
  753.             query++;
  754.             while (*query == ' ' || *query == '\t')
  755.                 query++;
  756.         }
  757.     }
  758.     return SUCCEED;
  759.  
  760. syntax_error:
  761.     sprintf(msg, "Incorrect syntax found near '%s'.", query);
  762.     msgnum = INVALID_SP_SYNTAX;
  763.  
  764. error:
  765.     srv_sendstatus(srvproc, 1);
  766.     srv_sendmsg(srvproc, SRV_MSG_ERROR, msgnum, SRV_INFO, (DBTINYINT)0, NULL,
  767.                 0, 0, msg, SRV_NULLTERM);
  768.     srv_senddone(srvproc, (SRV_DONE_ERROR | SRV_DONE_FINAL), 0, 0);
  769.     return FAIL;
  770. }
  771.  
  772.  
  773. // ================================= scan_next ===============================
  774. //
  775. // SCAN_NEXT
  776. //    Reads the next token in a string, ignoring whitespace.
  777. //
  778. // Parameters:
  779. //    string - The language event string
  780. //    word - The next token in the string
  781. //
  782. // Returns:
  783. //    The string incremented passed the token.
  784. //
  785. DBCHAR *scan_next( DBCHAR *string, DBCHAR *word )
  786. {
  787.     DBCHAR *p;
  788.  
  789.     word[0] = '\0';
  790.     if (*string == '\"' || *string == '\'') {
  791.         // check for unclosed quote
  792.         //
  793.         p = strchr(string + 1,
  794.         *string);
  795.         if (p == NULL)
  796.             return string;
  797.         strncpy(word, string + 1, p - (string + 1));
  798.         word[p - (string + 1)] = '\0';
  799.         string += 2;
  800.     } else {
  801.         // clear proceeding white space
  802.         //
  803.         while (*string == ' ' || *string == '\t' || *string == '\n' ||
  804.                *string == '\r')
  805.             string++;
  806.         sscanf(string, "%s", word);
  807.  
  808.         // ignore comments
  809.         //
  810.         while (strncmp(word, "/*", 2) == 0) {
  811.             string = strstr(string,
  812.             "*/");
  813.             if (string != NULL) {
  814.                 string += 2;
  815.                 word[0] = '\0';
  816.                 while (*string == ' ' || *string == '\t' || *string == '\n' ||
  817.                        *string == '\r')
  818.                     string++;
  819.                 sscanf(string, "%s", word);
  820.             } else
  821.                 return string;
  822.         }
  823.     }
  824.     if (strlen(word) > 0)
  825.         string += strlen(word);
  826.  
  827.     // clear trailing white space
  828.     //
  829.     while (*string == ' ' || *string == '\t' || *string == '\n' ||
  830.            *string == '\r')
  831.         string++;
  832.  
  833.     return string;
  834. }
  835.  
  836.  
  837. // ================================== sp_execute =============================
  838. //
  839. // SP_EXECUTE
  840. //    Execute a client stored procedure.
  841. //
  842. //    Scans the list of defined stored procedures, checks the parameters and
  843. //    executes the procedure. If results are returned it is the responsiblity
  844. //    of the underlying proedure.
  845. //
  846. // Parameters:
  847. //    srvproc - The process handle to use to send results to the client.
  848. //
  849. // Returns:
  850. //    SRV_CONTINUE
  851. //
  852. SRVRETCODE sp_execute( SRV_PROC  *srvproc )
  853. {
  854.     int      i;
  855.     int      x;
  856.     int      y;
  857.     int      len;
  858.     int      numparams;
  859.     SP_INFO *sp = NULL;
  860.     DBCHAR   msg[MAXLEN];
  861.     DBINT    msgnum;
  862.     DBCHAR   paramvalues[MAXPARAMS][MAXLEN];
  863.     BOOL     paramnamemode = FALSE;
  864.     DBCHAR  *paramname;
  865.     DBINT    paramtype;
  866.     DBCHAR  *value;
  867.     DBINT    type;
  868.     DBINT    status;
  869.  
  870.     // Initialize parameter storage
  871.     //
  872.     for( i = 0; i < MAXPARAMS; i++ )
  873.         memset( paramvalues[i], 0, MAXLEN );
  874.  
  875.     for( i = 0; i < Rpcnumber; i++ )
  876.     {
  877.         // Create name generated by calling server
  878.         //
  879.         if( strcmp(Sps[i].name, srv_rpcname(srvproc, (int *)NULL)) == 0 )
  880.         {
  881.             sp = &Sps[i];
  882.             break;
  883.         }
  884.     }
  885.  
  886.     if( sp == NULL )
  887.     {
  888.         sprintf( msg,
  889.                  "Procedure \'%s \' not found.",
  890.                  srv_rpcname(srvproc, (int *)NULL) );
  891.  
  892.         msgnum = SP_UNKNOWN;
  893.         goto error;
  894.     }
  895.  
  896.     numparams = srv_rpcparams( srvproc );
  897.  
  898.     if( srv_paramname(srvproc, 1, &len) && len > 0 )
  899.         paramnamemode = TRUE;
  900.  
  901.     for( y = 1; y <= numparams; y++ )
  902.     {
  903.         // Find parameter number
  904.         //
  905.         if( paramnamemode )
  906.         {
  907.             paramname = srv_paramname( srvproc,
  908.                                        y,
  909.                                        &len );
  910.  
  911.             if( strlen(paramname) == 0 )
  912.                 goto parameter_error;
  913.  
  914.             if( *paramname == '@' )
  915.                 paramname++;
  916.             else
  917.                 goto parameter_error;
  918.  
  919.             value = NULL
  920.             ;
  921.             for( x = 0; x < sp->numparams; x++ )
  922.             {
  923.                 if( strcmp(sp->params[x].name, paramname) == 0 )
  924.                 {
  925.                     value  = paramvalues[x];
  926.                     type   = sp->params[x].type;
  927.                     status = sp->params[x].status;
  928.                     break;
  929.                 }
  930.             }
  931.  
  932.             if( value == NULL )
  933.                 goto parameter_error;
  934.         }
  935.         else  // if( paramnamemode )
  936.         {
  937.             value  = paramvalues[y - 1];
  938.             type   = sp->params[y - 1].type;
  939.             status = sp->params[y - 1].status;
  940.         }
  941.  
  942.         // Check parameters for correct type
  943.         //
  944.         paramtype = srv_paramtype( srvproc, y );
  945.  
  946.         switch( paramtype )
  947.         {
  948.         case SRVVARCHAR:    // Type sent by Servers instead of SRVCHAR
  949.             paramtype = SRVCHAR;
  950.             break;
  951.         case SRVINTN:       // Type sent by Servers instead of SRVINT
  952.             paramtype = SRVINT4;
  953.             break;
  954.         default:
  955.             break;
  956.         }
  957.  
  958.         if( type != paramtype )
  959.         {
  960.             if( paramnamemode )
  961.                 sprintf( msg, "Parameter \'%s \' is incorrect type.",
  962.                          paramname );
  963.             else
  964.                 sprintf( msg, "Parameter \'%d \' is incorrect type.", y );
  965.  
  966.             msgnum = BAD_SP_PARAMETER;
  967.             goto error;
  968.         }
  969.  
  970.         // Check parameters for correct status
  971.         //
  972.         if( (DBINT)srv_paramstatus(srvproc, y) != status )
  973.         {
  974.             if( paramnamemode )
  975.                 sprintf( msg, "Parameter \'%s \' has incorrect status.",
  976.                          paramname);
  977.             else
  978.                 sprintf( msg, "Parameter \'%d \' had incorrect status.", y );
  979.  
  980.             msgnum = BAD_SP_PARAMETER;
  981.             goto error;
  982.         }
  983.  
  984.         // Move SP parameters to local variables
  985.         //
  986.         srv_bmove( srv_paramdata(srvproc, y), value, srv_paramlen(srvproc, y) );
  987.         value[srv_paramlen(srvproc, y)] = '\0';
  988.  
  989.     }
  990.  
  991.     // If unspecified, use default value
  992.     //
  993.     for( i = 0; i < sp->numparams; i++ )
  994.     {
  995.         if( strlen(paramvalues[i]) == 0
  996.         &&  strlen(sp->params[i].defaultvalue) == 0 )
  997.         {
  998.             strcpy( msg, sp->usage );
  999.             msgnum = INVALID_SP_SYNTAX;
  1000.             goto error;
  1001.         }
  1002.  
  1003.         if( strlen(paramvalues[i]) == 0
  1004.         &&  strlen(sp->params[i].defaultvalue) != 0 )
  1005.             strcpy(paramvalues[i], sp->params[i].defaultvalue);
  1006.     }
  1007.  
  1008.     // Execute procedure
  1009.     //
  1010.     (*sp->handler)( (VOID *)srvproc,
  1011.                     paramvalues[0],
  1012.                     paramvalues[1],
  1013.                     paramvalues[2],
  1014.                     paramvalues[3] );
  1015.  
  1016.     return SRV_CONTINUE;
  1017.  
  1018. parameter_error:
  1019.     sprintf( msg, "Procedure '%s' does not recognize parameter name: %s",
  1020.              sp->name, paramname );
  1021.     msgnum = BAD_SP_PARAMETER;
  1022.  
  1023. error:
  1024.     srv_sendstatus( srvproc, 1 );
  1025.     srv_sendmsg( srvproc, SRV_MSG_ERROR, msgnum, SRV_INFO, (DBTINYINT)0, NULL,
  1026.                  0, 0, msg, SRV_NULLTERM );
  1027.     srv_senddone( srvproc, (SRV_DONE_ERROR | SRV_DONE_FINAL), 0, 0 );
  1028.     return SRV_CONTINUE;
  1029. }
  1030.  
  1031.  
  1032. // ================================= exit_remote =============================
  1033. //
  1034. // EXIT_REMOTE
  1035. //    Handler for SRV_DISCONNECT events.
  1036. //
  1037. //    The code to disconnect from the procedure server.
  1038. //
  1039. // Parameters:
  1040. //    srvproc - the handle to the client connection
  1041. //
  1042. // Returns:
  1043. //    SRV_DISCONNECT
  1044. ///
  1045. SRVRETCODE exit_remote( SRV_PROC  *srvproc )
  1046. {
  1047.     char *string;
  1048.     int len;
  1049.  
  1050.     // Display info on console
  1051.     //
  1052.     string = srv_pfield(srvproc, SRV_CPID, &len);
  1053.     string[len] = '\0';
  1054.     printf("\nClient connection closed, process ID: %s\n", string);
  1055.  
  1056.     return SRV_CONTINUE;
  1057. }
  1058.  
  1059.  
  1060. // ================================== chk_err =================================================
  1061. //
  1062. // CHK_ERR
  1063. //    Print out errors.
  1064. //
  1065. // Parameters:
  1066. //    server        - pointer to procedure server server structure.
  1067. //    srvproc      - pointer to client connection structure
  1068. //    errornum     - error number.
  1069. //    severity     - error severity.
  1070. //    state        - error state.
  1071. //    oserrnum     - operating system error number, if any.
  1072. //    errtext      - the text of the error message.
  1073. //    errtextlen   - length of the errtext message
  1074. //    oserrtext    - the text of the operating system error message.
  1075. //    oserrtextlen - length of the errtext message
  1076. //
  1077. // Returns:
  1078. //    SRV_CONTINUE, SRV_CANCEL, or SRV_EXIT_PROGRAM
  1079. //
  1080.  
  1081. SRVRETCODE chk_err( SRV_SERVER *server,
  1082.                  SRV_PROC   *srvproc,
  1083.                  int         errornum,
  1084.                  BYTE        severity,
  1085.                  BYTE        state,
  1086.                  int         oserrnum,
  1087.                  DBCHAR     *errtext,
  1088.                  int         errtextlen,
  1089.                  DBCHAR     *oserrtext,
  1090.                  int         oserrtextlen )
  1091. {
  1092.     char log_buffer[256];
  1093.     char error[256];
  1094.     char oserror[256];
  1095.  
  1096.     memcpy(error, errtext, errtextlen);
  1097.     error[errtextlen] = '\0';
  1098.     memcpy(oserror, oserrtext, oserrtextlen);
  1099.     oserror[oserrtextlen] = '\0';
  1100.  
  1101.     // Strip out resource information. Get the actual error number.
  1102.     errornum = (errornum & 0x0000FFFF);
  1103.  
  1104.     // Operating system error?
  1105.     //
  1106.     if (oserrnum != SRV_ENO_OS_ERR) {
  1107.         sprintf(log_buffer, "SERVER OS ERROR: %d: %s.", oserrnum, oserror);
  1108.         if (server)
  1109.             srv_log(server, TRUE, log_buffer, SRV_NULLTERM);
  1110.         else    // If application not initialized log to screen
  1111.             printf ("%s\n", log_buffer);
  1112.     }
  1113.  
  1114.     // Is this a fatal error for the server?
  1115.     //
  1116.     if (severity >= SRV_FATAL_SERVER) {
  1117.         sprintf(log_buffer,
  1118.                     "SERVER: FATAL SERVER ERROR: errornum = %d, "
  1119.                     "severity = %d, state = %d: %s.",
  1120.                 errornum, severity, state, error);
  1121.  
  1122.         if (server)
  1123.             srv_log(server, TRUE, log_buffer, SRV_NULLTERM);
  1124.         else    // If application not initialized log to screen
  1125.             printf ("%s\n", log_buffer);
  1126.         return SRV_EXIT;
  1127.     } else {
  1128.         //
  1129.         // Did the "srvproc" get a fatal error?
  1130.         //
  1131.         if (severity >= SRV_FATAL_PROCESS) {
  1132.             sprintf(log_buffer,
  1133.                           "SERVER: FATAL CONNECT ERROR: errornum = %d, "
  1134.                           "severity = %d, state = %d: %s.",
  1135.                      errornum, severity, state, error);
  1136.  
  1137.             if (server)
  1138.                 srv_log(server, TRUE, log_buffer, SRV_NULLTERM);
  1139.             else    // If application not initialized log to screen
  1140.                 printf ("%s\n", log_buffer);
  1141.  
  1142.             return SRV_CANCEL;
  1143.         }
  1144.     }
  1145.  
  1146.     // A non-fatal error or an information message received.
  1147.     // We'll pass it through to the client.
  1148.     //
  1149.     if (srvproc != (SRV_PROC *)NULL && (server != NULL))
  1150.         if (severity < 10) {    // if informational message
  1151.             srv_sendmsg(srvproc, SRV_MSG_INFO, (DBINT)errornum, severity, 0,
  1152.                         NULL, 0, 0, error, SRV_NULLTERM);
  1153.         } else {            // must be an error message
  1154.                srv_sendmsg(srvproc, SRV_MSG_ERROR, (DBINT)errornum, severity, 0,
  1155.                            NULL, 0, 0, error, SRV_NULLTERM);
  1156.     } else {
  1157.         sprintf(log_buffer, "ODS ERROR: errornum = %d, severity = %d: %s",
  1158.                 errornum, severity, error);
  1159.         if (server)
  1160.             srv_log(server, TRUE, log_buffer, SRV_NULLTERM);
  1161.         else    // If application not initialized log to screen
  1162.             printf ("%s\n", log_buffer);
  1163.     }
  1164.     return SRV_CONTINUE;
  1165. }
  1166.  
  1167.  
  1168. // The following are the supported store procedure functions
  1169. //
  1170. // ================================== proclist ===============================
  1171. //
  1172. // PROCLIST
  1173. //    Returns the usage for all defined stored procedures
  1174. //
  1175. // Parameters:
  1176. //    srvproc - the handle to the client connection that got the SRV_CONNECT.
  1177. //
  1178. // Returns:
  1179. //    SUCCEED
  1180. //
  1181. // Side Effects:
  1182. //    Returns a result set to client
  1183. //
  1184. SRVRETCODE proclist( SRV_PROC *srvproc )
  1185. {
  1186.     DBCHAR colname1[MAXNAME];
  1187.     DBCHAR colname2[MAXNAME];
  1188.     int i;
  1189.  
  1190.     sprintf(colname1, "spname");
  1191.     srv_describe(srvproc, 1, colname1, SRV_NULLTERM, SRVCHAR, MAXNAME,
  1192.                  SRVCHAR, 0, NULL);
  1193.  
  1194.     sprintf(colname2, "spusage");
  1195.     srv_describe(srvproc, 2, colname2, SRV_NULLTERM, SRVCHAR, MAXLEN, SRVCHAR,
  1196.                  0, NULL);
  1197.  
  1198.     // Return each SP handler as a row
  1199.     //
  1200.     for (i = 0; i < Rpcnumber; i++) {
  1201.         srv_setcoldata(srvproc, 1, Sps[i].name);
  1202.         srv_setcollen(srvproc, 1, strlen(Sps[i].name));
  1203.  
  1204.         srv_setcoldata(srvproc, 2, Sps[i].usage);
  1205.         srv_setcollen(srvproc, 2, strlen(Sps[i].usage));
  1206.  
  1207.         srv_sendrow(srvproc);
  1208.     }
  1209.     srv_senddone(srvproc, (SRV_DONE_COUNT | SRV_DONE_FINAL), 0, i);
  1210.  
  1211.     return SUCCEED;
  1212. }
  1213.  
  1214.  
  1215. // ================================== sp_exec ================================
  1216. //
  1217. // SP_EXEC
  1218. //      Execute a given command string and returns any output as rows of
  1219. //      text.
  1220. //
  1221. // Parameters:
  1222. //      srvproc - the handle to the client connection that got the SRV_CONNECT.
  1223. //      command - the command string to execute
  1224. //
  1225. // Returns:
  1226. //      SUCCEED or FAIL
  1227. //
  1228. // Side Effects:
  1229. //      Returns messages and/or a result set to client
  1230. //
  1231. SRVRETCODE sp_exec( SRV_PROC *srvproc, DBCHAR *command )
  1232. {
  1233.  
  1234.     DBCHAR bReadBuffer[MAXLEN];
  1235.     DBCHAR bErrorMsg[80];
  1236.     int    cbReadBuffer;
  1237.  
  1238.     DBINT   cnt;
  1239.     DBINT   rows = 0;
  1240.     DBCHAR *paramvalue;
  1241.     DBINT   paramlength;
  1242.     DBINT   cmdlength;
  1243.     BOOL    fSuccess;
  1244.  
  1245.     STARTUPINFO         si;
  1246.     PROCESS_INFORMATION pi;
  1247.  
  1248.     SECURITY_ATTRIBUTES saPipe;
  1249.     HANDLE              hReadPipe;
  1250.     HANDLE              hWritePipe;
  1251.  
  1252.     // Allocation local storage for command string.
  1253.     //
  1254.     paramlength = strlen( command );
  1255.     cmdlength   = paramlength + CMDSTR + 1;
  1256.     paramvalue  = (DBCHAR *)malloc( cmdlength );
  1257.  
  1258.     if( !paramvalue )
  1259.     {
  1260.         SETERROR( "Malloc", bErrorMsg );
  1261.         srv_sendstatus( srvproc, 1 );
  1262.         srv_sendmsg( srvproc, SRV_MSG_ERROR, EXEC, SRV_INFO, (DBTINYINT)0,
  1263.                      NULL, 0, 0, bErrorMsg, SRV_NULLTERM );
  1264.         srv_senddone( srvproc, (SRV_DONE_ERROR | SRV_DONE_FINAL), 0, 0 );
  1265.         return FAIL;
  1266.     }
  1267.  
  1268.     // Cancatenate "cmd /c " to command string so child process will
  1269.     // execute the given command and exit.  Move command string to
  1270.     // local variable.
  1271.     //
  1272.     memset( paramvalue, 0, cmdlength );
  1273.     srv_bmove( "cmd /c ", paramvalue, CMDSTR );
  1274.     srv_bmove( command, ¶mvalue[CMDSTR], paramlength );
  1275.  
  1276.     // Create child process to execute the command string.  Use an
  1277.     // anonymous pipe to read the output from the command and send
  1278.     // any results to the client.
  1279.  
  1280.     // In order for the child process to be able to write
  1281.     // to the anonymous pipe, the handle must be marked as
  1282.     // inheritable by child processes by setting the
  1283.     // SECURITY_ATTRIBUTES.bInheritHandle flag to TRUE.
  1284.     //
  1285.     saPipe.nLength              = sizeof( SECURITY_ATTRIBUTES );
  1286.     saPipe.lpSecurityDescriptor = NULL;
  1287.     saPipe.bInheritHandle       = TRUE;
  1288.  
  1289.     fSuccess = CreatePipe( &hReadPipe,      // read handle
  1290.                            &hWritePipe,     // write handle
  1291.                            &saPipe,         // security descriptor
  1292.                            0 );             // use default pipe buffer size
  1293.     if( !fSuccess )
  1294.     {
  1295.         SETERROR( "CreatePipe", bErrorMsg );
  1296.         srv_sendstatus( srvproc, 1 );
  1297.         srv_sendmsg( srvproc, SRV_MSG_ERROR, EXEC, SRV_INFO, (DBTINYINT)0,
  1298.                      NULL, 0, 0, bErrorMsg, SRV_NULLTERM );
  1299.         srv_senddone( srvproc, (SRV_DONE_ERROR | SRV_DONE_FINAL), 0, 0 );
  1300.         free( paramvalue );
  1301.         return FAIL;
  1302.     }
  1303.  
  1304.     // Now we must set standard out and standard error to the
  1305.     // write end of the pipe.  Once standard out and standard
  1306.     // error are set to the pipe handle, we must close the pipe
  1307.     // handle so that when the child process dies, the write end
  1308.     // of the pipe will close, setting an EOF condition on the pipe.
  1309.     //
  1310.     memset( &si, 0, sizeof(si) );
  1311.  
  1312.     si.cb          = sizeof(si);
  1313.     si.dwFlags     = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
  1314.     si.wShowWindow = SW_HIDE;
  1315.     si.hStdOutput  = hWritePipe;
  1316.     si.hStdError   = hWritePipe;
  1317.  
  1318.     // Set the fInheritHandles parameter to TRUE so that open
  1319.     // file handles will be inheritied. We can close the child
  1320.     // process and thread handles as we won't be needing them.
  1321.     // The child process will not die until these handles are
  1322.     // closed.
  1323.     //
  1324.     fSuccess = CreateProcess( NULL,         // filename
  1325.                               paramvalue,   // command line for child
  1326.                               NULL,         // process security descriptor
  1327.                               NULL,         // thread security descriptor
  1328.                               TRUE,         // inherit handles?
  1329.                               0,            // creation flags
  1330.                               NULL,         // inherited environment address
  1331.                               NULL,         // startup dir; NULL = start in current
  1332.                               &si,          // pointer to startup info (input)
  1333.                               &pi );        // pointer to process info (output)
  1334.     if( !fSuccess )
  1335.     {
  1336.         SETERROR( "CreateProcess", bErrorMsg );
  1337.         srv_sendstatus( srvproc, 1 );
  1338.         srv_sendmsg( srvproc, SRV_MSG_ERROR, EXEC, SRV_INFO, (DBTINYINT)0,
  1339.                      NULL, 0, 0, bErrorMsg, SRV_NULLTERM );
  1340.         srv_senddone( srvproc, (SRV_DONE_ERROR | SRV_DONE_FINAL), 0, 0 );
  1341.         free( paramvalue );
  1342.         return FAIL;
  1343.     }
  1344.  
  1345.     CloseHandle( pi.hThread );
  1346.     CloseHandle( pi.hProcess );
  1347.  
  1348.     // We need to close our instance of the inherited pipe write
  1349.     // handle now that it's been inherited so that it will actually
  1350.     // close when the child process ends. This will put an EOF
  1351.     // condition on the pipe which we can then detect.
  1352.     //
  1353.     fSuccess = CloseHandle( hWritePipe );
  1354.  
  1355.     if( !fSuccess )
  1356.     {
  1357.         SETERROR( "CloseHandle", bErrorMsg );
  1358.  
  1359.         srv_sendstatus( srvproc, 1 );
  1360.         srv_sendmsg( srvproc, SRV_MSG_ERROR, EXEC, SRV_INFO, (DBTINYINT)0,
  1361.                      NULL, 0, 0, bErrorMsg, SRV_NULLTERM );
  1362.         srv_senddone( srvproc, (SRV_DONE_ERROR | SRV_DONE_FINAL), 0, 0 );
  1363.  
  1364.         free( paramvalue );
  1365.         return FAIL;
  1366.     }
  1367.  
  1368.     // Now read from the pipe until EOF condition reached.
  1369.     //
  1370.     do
  1371.     {
  1372.         cnt = 0;
  1373.  
  1374.         while( fSuccess = ReadFile( hReadPipe,          // read handle
  1375.                                     &bReadBuffer[cnt],  // buffer for incoming data
  1376.                                     1,                  // number of bytes to read
  1377.                                     &cbReadBuffer,      // number of bytes actually read
  1378.                                     NULL))
  1379.         {
  1380.             if( !fSuccess )
  1381.             {
  1382.                 if( GetLastError() == ERROR_BROKEN_PIPE )
  1383.                     break;  // child has died
  1384.                 else
  1385.                 {
  1386.                     SETERROR( "CloseHandle", bErrorMsg );
  1387.  
  1388.                     srv_sendstatus( srvproc, 1 );
  1389.                     srv_sendmsg( srvproc, SRV_MSG_ERROR, EXEC, SRV_INFO,
  1390.                                  (DBTINYINT)0, NULL, 0, 0, bErrorMsg,
  1391.                                  SRV_NULLTERM );
  1392.                     srv_senddone( srvproc, (SRV_DONE_ERROR | SRV_DONE_FINAL),
  1393.                                   0, 0 );
  1394.  
  1395.                     free( paramvalue );
  1396.                     return FAIL;
  1397.                 }
  1398.             }
  1399.  
  1400.             if (bReadBuffer[cnt] == '\n')
  1401.                 break;
  1402.             else
  1403.                 cnt++;
  1404.         }
  1405.  
  1406.         if( fSuccess && cbReadBuffer )
  1407.         {
  1408.             if( rows == 0 )
  1409.             {
  1410.                 // Describe result row: it will be one column of size
  1411.                 // sizeof(buf)  We do this in the retrieval loop to
  1412.                 // ensure that the row description will occur only if
  1413.                 // there are rows to be outputted.
  1414.                 //
  1415.                 srv_describe( srvproc,
  1416.                               1,
  1417.                               ¶mvalue[CMDSTR],
  1418.                               SRV_NULLTERM,
  1419.                               SRVCHAR,
  1420.                               sizeof(bReadBuffer),
  1421.                               SRVCHAR,
  1422.                               sizeof(bReadBuffer),
  1423.                               bReadBuffer );
  1424.             }
  1425.  
  1426.             // Make sure we have at least one data
  1427.             //
  1428.             if( !cnt )
  1429.             {
  1430.                 bReadBuffer[0] = ' ';
  1431.                 cnt = 1;
  1432.             }
  1433.  
  1434.             // Remove carriage return if it exists
  1435.             //
  1436.             if( bReadBuffer[cnt-1] == 0x0D )
  1437.                 cnt--;
  1438.  
  1439.             // Send result rows back to client.
  1440.             //
  1441.             srv_setcollen( srvproc, 1, cnt );
  1442.             srv_sendrow( srvproc );
  1443.             rows++;
  1444.         }
  1445.     } while( fSuccess && cbReadBuffer );
  1446.  
  1447.     // close the trace file, pipe handles
  1448.     //
  1449.     CloseHandle( hReadPipe );
  1450.  
  1451.     if( rows == 0 )
  1452.     {
  1453.         srv_sendstatus( srvproc, 0 );
  1454.         srv_sendmsg( srvproc, SRV_MSG_INFO, EXEC, SRV_INFO, (DBTINYINT)0, NULL,
  1455.                      0, 0, "Command executed successfully", SRV_NULLTERM );
  1456.     }
  1457.  
  1458.     srv_senddone( srvproc, (SRV_DONE_COUNT | SRV_DONE_FINAL), 0, rows );
  1459.     free( paramvalue );
  1460.     return SUCCEED;
  1461. }
  1462.  
  1463.  
  1464. // =================================== disklist ==============================
  1465. //
  1466. // DISKLIST
  1467. //     Returns a row for each defined drive containing its name and the
  1468. //     amount of disk space available.
  1469. //
  1470. // Parameters:
  1471. //    srvproc - the handle to the client connection that got the SRV_CONNECT.
  1472. //
  1473. // Returns:
  1474. //    SUCCEED
  1475. //
  1476. // Side Effects:
  1477. //     Returns a result set to client
  1478. //
  1479. SRVRETCODE disklist( SRV_PROC  *srvproc )
  1480. {
  1481.     DBCHAR colname1[MAXNAME];
  1482.     DBCHAR colname2[MAXNAME];
  1483.     DBCHAR drivename;
  1484.     DBCHAR rootname[16];
  1485.     int    drivenum;
  1486.     unsigned    secPerCluster;
  1487.     unsigned    bytesPerSector;
  1488.     unsigned    freeClusters;
  1489.     unsigned    totalClusters;
  1490.     int    drivenums;
  1491.     int    space_remaining;
  1492.     int    i = 0;
  1493.  
  1494.     sprintf( colname1, "drive" );
  1495.     srv_describe( srvproc, 1, colname1, SRV_NULLTERM, SRVCHAR, 1, SRVCHAR, 1,
  1496.                   (BYTE *)&drivename );
  1497.  
  1498.     sprintf( colname2, "Kbytes free" );
  1499.     srv_describe( srvproc, 2, colname2, SRV_NULLTERM, SRVINT4, 4, SRVINT4, 4,
  1500.                   (BYTE *)&space_remaining );
  1501.  
  1502.     drivenums = GetLogicalDrives();
  1503.  
  1504.     drivenums >>= 2;        // Ignore drives A and B
  1505.     for( drivename = 'C', drivenum = 3; drivename <= 'Z';
  1506.          drivename++, drivenum++ )
  1507.     {
  1508.         if( drivenums & 1 )
  1509.         {
  1510.             i++;
  1511.  
  1512.             sprintf( rootname, "%c:\\", drivename );
  1513.             GetDiskFreeSpace( rootname, &secPerCluster, &bytesPerSector,
  1514.                               &freeClusters, &totalClusters );
  1515.  
  1516.             space_remaining = secPerCluster * bytesPerSector * (freeClusters/1000) ;
  1517.  
  1518.             srv_sendrow( srvproc );
  1519.         }
  1520.  
  1521.         drivenums >>= 1;
  1522.     }
  1523.  
  1524.     srv_senddone( srvproc, (SRV_DONE_COUNT | SRV_DONE_FINAL), 0, i );
  1525.     return SUCCEED;
  1526. }
  1527.  
  1528.  
  1529. // ================================== diskfree ===============================
  1530. //
  1531. // DISKFREE
  1532. //    Returns the amount of space available on a given drive. The value
  1533. //     is placed into the defined return parameter of the stored procedure.
  1534. //
  1535. //     NOTE: This routine can not be called via a language event.
  1536. //
  1537. // Parameters:
  1538. //    srvproc - the handle to the client connection that got the SRV_CONNECT.
  1539. //     drive - the drive letter to check
  1540. //
  1541. // Returns:
  1542. //    SUCCEED
  1543. //
  1544. // Side Effects:
  1545. //    Returns messages and/or a result set to client. Returns a value in the
  1546. //     defined return parameter.
  1547. //
  1548. SRVRETCODE diskfree( SRV_PROC *srvproc, DBCHAR *drive )
  1549. {
  1550.     DBCHAR colname1[MAXNAME];
  1551.     int    drivenum;
  1552.     DBCHAR rootname[16];
  1553.     int    drivenums;
  1554.     int    secPerCluster;
  1555.     int    bytesPerSector;
  1556.     int    freeClusters;
  1557.     int    totalClusters;
  1558.     int    space_remaining = -1;
  1559.     int    i = 0;
  1560.  
  1561.     drive = strupr( drive );
  1562.  
  1563.     sprintf( colname1, "drive" );
  1564.     srv_describe( srvproc, 1, colname1, SRV_NULLTERM, SRVCHAR, 1, SRVCHAR, 1,
  1565.                   (BYTE *)drive );
  1566.     srv_sendrow( srvproc );
  1567.     srv_senddone( srvproc, (SRV_DONE_COUNT | SRV_DONE_MORE), 0, 1 );
  1568.  
  1569.     drivenums = GetLogicalDrives();
  1570.  
  1571.     drivenum = drive[0] - 'A' + 1;
  1572.  
  1573.     drivenums >>= drivenum - 1; //Ignore drives A and B
  1574.     if( drivenums & 0x01 )
  1575.     {
  1576.         sprintf( rootname, "%c:\\", drive[0] );
  1577.         GetDiskFreeSpace( rootname, &secPerCluster, &bytesPerSector,
  1578.                           &freeClusters, &totalClusters );
  1579.  
  1580.         space_remaining = secPerCluster * freeClusters * bytesPerSector;
  1581.  
  1582.     }
  1583.  
  1584.     // Process return parameter
  1585.     //
  1586.     if( srv_paramstatus(srvproc, 2) & 0x0001 )
  1587.         srv_paramset( srvproc, 2, (BYTE *)&space_remaining, 4 );
  1588.  
  1589.     srv_senddone( srvproc, SRV_DONE_FINAL, 0, 0 );
  1590.     return SUCCEED;
  1591. }
  1592.  
  1593.  
  1594. // ================================== scan_xbase =============================
  1595. //
  1596. // SCAN_XBASE
  1597. //    Reads an xBase file and sends it to the client as if it were a SQL
  1598. //    Server query result set (the equivalent of a 'SELECT * FROM
  1599. //    tablename' SQL statement).
  1600. //
  1601. // Parameters:
  1602. //    srvproc - the handle to the client connection that got the SRV_CONNECT.
  1603. //    szFileName - dbase file path name
  1604. //
  1605. // Returns:
  1606. //    SUCCEED or FAIL
  1607. //
  1608. // Side Effects:
  1609. //    Returns messages and/or a result set to client
  1610. //
  1611. SRVRETCODE scan_xbase( SRV_PROC *srvproc, char *filename )
  1612. {
  1613.     FILE  *xbasefile;
  1614.     size_t count;
  1615.     char   buffer[BUF_SIZE];
  1616.     short  numrecords;
  1617.     short  headerlength;
  1618.     short  recordlength;
  1619.     short  lengthlist[XBASE_MAX_COLUMNS];
  1620.     int    i;
  1621.     short  j;
  1622.     short  position;
  1623.     short  numcolumns;
  1624.  
  1625.     // now read the database header info
  1626.     //
  1627.     if ((xbasefile = fopen(filename, "r")) == NULL) {
  1628.         srv_sendstatus(srvproc, 1);
  1629.         srv_sendmsg(srvproc, SRV_MSG_ERROR, EXEC, SRV_INFO, (DBTINYINT)0,
  1630.                     NULL, 0, 0, "Error reading xBase file", SRV_NULLTERM);
  1631.         srv_senddone(srvproc, (SRV_DONE_ERROR | SRV_DONE_FINAL), 0, 0);
  1632.         return FAIL;
  1633.     }
  1634.     count = fread(buffer,
  1635.     XBASE_HDR_SIZE,
  1636.     1,
  1637.     xbasefile);
  1638.  
  1639.     if (count == 0) {
  1640.         srv_sendstatus(srvproc, 1);
  1641.         srv_sendmsg(srvproc, SRV_MSG_ERROR, EXEC, SRV_INFO, (DBTINYINT)0,
  1642.                     NULL, 0, 0, "Error reading xBase file", SRV_NULLTERM);
  1643.         srv_senddone(srvproc, (SRV_DONE_ERROR | SRV_DONE_FINAL), 0, 0);
  1644.         fclose(xbasefile);
  1645.         return FAIL;
  1646.     }
  1647.     numrecords   = *((short *)&buffer[4]);
  1648.     headerlength = *((short *)&buffer[8]);
  1649.     recordlength = *((short *)&buffer[10]);
  1650.     numcolumns   = (headerlength - 32 - 1) / 32;
  1651.  
  1652.     // now get the column header information
  1653.     //
  1654.     for (j = 0; j < numcolumns; j++) {
  1655.         count = fread(buffer,   XBASE_HDR_SIZE, 1, xbasefile);
  1656.         if (count == 0) {
  1657.             srv_sendstatus(srvproc, 1);
  1658.             srv_sendmsg(srvproc, SRV_MSG_ERROR, EXEC, SRV_INFO, (DBTINYINT)0,
  1659.                         NULL, 0, 0, "Error reading xBase file", SRV_NULLTERM);
  1660.             srv_senddone(srvproc, (SRV_DONE_ERROR | SRV_DONE_FINAL), 0, 0);
  1661.             fclose(xbasefile);
  1662.             return FAIL;
  1663.         }
  1664.  
  1665.         // we need to NULL terminate the column name (if it is a
  1666.         // full 11 characters int)
  1667.         //
  1668.         buffer[11] = '\0';
  1669.  
  1670.         // now find our the column length for this data buffer
  1671.         //
  1672.         lengthlist[j] = (short)buffer[16];
  1673.  
  1674.         // now 'describe' this column
  1675.         //
  1676.         srv_describe( srvproc, j + 1,   // column number
  1677.                       buffer,           // pointer to column name
  1678.                       SRV_NULLTERM,     // column name is NULL terminated
  1679.                       SRVCHAR,          // datatype is char (xBase numbers are ASCII)
  1680.                       lengthlist[j],    // column length
  1681.                       SRVCHAR,          // destination datatype is also char
  1682.                       lengthlist[j],    // destination column length
  1683.                       NULL);            // pointer to where the data will be
  1684.  
  1685.     }
  1686.  
  1687.     // now read the one byte 'column header seperator'
  1688.     //
  1689.     count = fread(buffer, 1, 1,  xbasefile);
  1690.     if (count == 0) {
  1691.         srv_sendstatus(srvproc, 1);
  1692.         srv_sendmsg(srvproc, SRV_MSG_ERROR, EXEC, SRV_INFO, (DBTINYINT)0,
  1693.                     NULL, 0, 0, "Error reading xBase file", SRV_NULLTERM);
  1694.         srv_senddone(srvproc, (SRV_DONE_ERROR | SRV_DONE_FINAL), 0, 0);
  1695.         fclose(xbasefile);
  1696.         return FAIL;
  1697.     }
  1698.     for (i = 0; i < numrecords; i++) {
  1699.         count = fread(buffer, recordlength, 1, xbasefile);
  1700.         if (count == 0 && !feof(xbasefile)) {
  1701.             srv_sendstatus(srvproc, 1);
  1702.             srv_sendmsg(srvproc, SRV_MSG_ERROR, EXEC, SRV_INFO, (DBTINYINT)0,
  1703.                         NULL, 0, 0, "Error reading xBase file", SRV_NULLTERM);
  1704.             srv_senddone(srvproc, (SRV_DONE_ERROR | SRV_DONE_FINAL), 0, 0);
  1705.             fclose(xbasefile);
  1706.             return FAIL;
  1707.         }
  1708.  
  1709.         // check to see if this is a deleted row
  1710.         //
  1711.         if (buffer[0] == '*')
  1712.             break;
  1713.  
  1714.         // Now set the length and data pointers for each column
  1715.         //
  1716.         for (j = 0, position = 1; j < numcolumns; j++) {
  1717.             srv_setcollen(srvproc, j + 1, lengthlist[j]);
  1718.             srv_setcoldata(srvproc, j + 1, &buffer[position]);
  1719.             position += lengthlist[j];
  1720.         }
  1721.  
  1722.         // send the row to the client.
  1723.         //
  1724.         srv_sendrow(srvproc);
  1725.     }
  1726.     srv_senddone(srvproc, SRV_DONE_COUNT | SRV_DONE_FINAL, 0, i);
  1727.     fclose(xbasefile);
  1728.     return SUCCEED;
  1729. }
  1730.  
  1731.  
  1732. // The following section defines the Service Control Manager support functions.
  1733. //
  1734. // ================================== initsignal =============================
  1735. //
  1736. //  initsignal -- Install signal handlers for NTB Server.
  1737. //
  1738. void initsignal( SRV_SERVER * server, char * szServiceName )
  1739. {
  1740.     char szLogBuffer[1024];
  1741.  
  1742.     if( SrvCtrMan ) // if started from Service Control Manager
  1743.     {
  1744.         // Use RegisterServiceCtrlHandler() to communicate with
  1745.         // the Service Control Manager.
  1746.         //
  1747.         NTBServiceHandle = RegisterServiceCtrlHandler( "ProcSrv",
  1748.                                                        NTBServiceCtrlHandler );
  1749.         InitializeCriticalSection( &SCMCrtSec );
  1750.  
  1751.         // Now send a START_PENDING message
  1752.         //
  1753.         NTBServiceStatus.dwServiceType  = SERVICE_WIN32_OWN_PROCESS;
  1754.         NTBServiceStatus.dwCurrentState = SERVICE_START_PENDING;
  1755.  
  1756.         NTBServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP
  1757.                                             | SERVICE_ACCEPT_PAUSE_CONTINUE
  1758.                                             | SERVICE_ACCEPT_SHUTDOWN;
  1759.  
  1760.         NTBServiceStatus.dwWin32ExitCode           = NO_ERROR;
  1761.         NTBServiceStatus.dwServiceSpecificExitCode = 0;
  1762.         NTBServiceStatus.dwCheckPoint              = 1;
  1763.         NTBServiceStatus.dwWaitHint                = 20000L;   // 20 seconds
  1764.  
  1765.         if( !SetServiceStatus(NTBServiceHandle, &NTBServiceStatus) )
  1766.         {
  1767.             sprintf( szLogBuffer,
  1768.                      "Procedure Server Service Manager Failer: %s "
  1769.                      "(initsignal(), line = %d), message = %s",
  1770.                      szServiceName,
  1771.                      __LINE__,
  1772.                      get_last_error_str() );
  1773.             srv_log( server, TRUE, szLogBuffer, SRV_NULLTERM );
  1774.  
  1775.             // Flush all file buffers
  1776.             //
  1777.             _flushall();
  1778.             ExitProcess( 1 );
  1779.         }
  1780.  
  1781.         // We need to increment checkpoint field in the above structure
  1782.         // regularly in order to notify Service Control Manager that
  1783.         // we aren't hung.
  1784.         //
  1785.         ++(NTBServiceStatus.dwCheckPoint);
  1786.  
  1787.     } // if( SrvCtrMan )
  1788.  
  1789.     // Don't display a message box for hard errors, return the error back
  1790.     // to the application instead.
  1791.     //
  1792.     SetErrorMode( SEM_FAILCRITICALERRORS );
  1793.  
  1794.     // Install Ctrl-C handler
  1795.     //
  1796.     if( !SrvCtrMan )
  1797.     {
  1798.         if( SetConsoleCtrlHandler((PHANDLER_ROUTINE)ctrlc_hndl, TRUE)
  1799.         != TRUE )
  1800.         {
  1801.             sprintf( szLogBuffer,
  1802.                      "Procedure Server Service Manager Failer: %s "
  1803.                      "(SetConsoleCtrlHandler(), line = %d), message = %s",
  1804.                      szServiceName,
  1805.                      __LINE__,
  1806.                      get_last_error_str() );
  1807.             srv_log( server, TRUE, szLogBuffer, SRV_NULLTERM );
  1808.         }
  1809.     }
  1810.     return;
  1811. }
  1812.  
  1813.  
  1814. // ================================== ctrlc_hndl =============================
  1815. //
  1816. //  ctrlc_hndl(ulong) -- Handles Ctrl-C and Ctrl-Break events received
  1817. //  by NTB Server.
  1818. //
  1819. void ctrlc_hndl( ULONG CtrlTyp )
  1820. {
  1821.     char c;
  1822.     char szLogBuffer[1024];
  1823.  
  1824.     switch( CtrlTyp )
  1825.     {
  1826.     case CTRL_C_EVENT:
  1827.     case CTRL_BREAK_EVENT:
  1828.  
  1829.         printf( "Terminate Procedure Server? (y/n): ");
  1830.  
  1831.         do
  1832.         {
  1833.             c = getch();
  1834.         } while( c != 'y' && c != 'Y' && c != 'n' && c != 'N' );
  1835.  
  1836.         printf( "%c\n", c );
  1837.  
  1838.         if( c == 'y' || c == 'Y' )
  1839.         {
  1840.             sprintf( szLogBuffer,
  1841.                      "Procedure Server terminated by Ctrl-C or Ctrl-Break, name = %s",
  1842.                      szRegistryName );
  1843.  
  1844.             if( gblServer ) {
  1845.                 srv_log( gblServer, TRUE, szLogBuffer, SRV_NULLTERM );
  1846.                 srv_setevent( gblServer, SRV_EXIT );
  1847.             }
  1848.             WaitForSingleObject(hServerDone, INFINITE );
  1849.  
  1850.             _flushall();
  1851.             ExitProcess( 0 );
  1852.         }
  1853.  
  1854.         break;
  1855.  
  1856.     default:
  1857.         break;
  1858.     }
  1859.     return;
  1860. }
  1861.  
  1862.  
  1863. // ============================== NTBServiceCtrlHandler ======================
  1864. //
  1865. //  NTBServiceCtrlHandler(DWORD) -- Responds to START, STOP, etc..
  1866. //  requests of Service Control Manager.
  1867. //
  1868. void WINAPI NTBServiceCtrlHandler( DWORD dwCtrl )
  1869. {
  1870.     char szLogBuffer[1024];
  1871.  
  1872.     switch( dwCtrl )
  1873.     {
  1874.     case SERVICE_CONTROL_SHUTDOWN:
  1875.         //
  1876.         // NT is shutting down.
  1877.         //
  1878.         // Fall through
  1879.  
  1880.     case SERVICE_CONTROL_STOP:
  1881.  
  1882.         EnterCriticalSection( &SCMCrtSec );
  1883.  
  1884.         NTBServiceStatus.dwCurrentState            = SERVICE_STOP_PENDING;
  1885.         NTBServiceStatus.dwWin32ExitCode           = NO_ERROR;
  1886.         NTBServiceStatus.dwServiceSpecificExitCode = 0;
  1887.         NTBServiceStatus.dwCheckPoint              = 1;
  1888.         NTBServiceStatus.dwWaitHint                = 60000L;   // 60 seconds
  1889.  
  1890.         SetServiceStatus( NTBServiceHandle, &NTBServiceStatus );
  1891.         LeaveCriticalSection( &SCMCrtSec );
  1892.  
  1893.         // NTBShutdown() sets the SQL Service Manager to "Stop" and terminates
  1894.         // the service.
  1895.         //
  1896.         SetThreadPriority( (HANDLE)_beginthread(NTBShutdown, 0, NULL),
  1897.                            THREAD_PRIORITY_HIGHEST );
  1898.         break;
  1899.  
  1900.     case SERVICE_CONTROL_INTERROGATE:
  1901.  
  1902.         // Serialize with increment signal thread
  1903.         //
  1904.         EnterCriticalSection( &SCMCrtSec );
  1905.  
  1906.         SetServiceStatus( NTBServiceHandle, &NTBServiceStatus );
  1907.         LeaveCriticalSection( &SCMCrtSec );
  1908.         break;
  1909.  
  1910.     case SERVICE_CONTROL_PAUSE:
  1911.  
  1912.         // Serialize with increment signal thread
  1913.         //
  1914.         EnterCriticalSection( &SCMCrtSec );
  1915.  
  1916.         srv_setevent( gblServer, SRV_SLEEP );
  1917.  
  1918.         NTBServiceStatus.dwCurrentState            = SERVICE_PAUSED;
  1919.         NTBServiceStatus.dwWin32ExitCode           = NO_ERROR;
  1920.         NTBServiceStatus.dwServiceSpecificExitCode = 0;
  1921.         NTBServiceStatus.dwCheckPoint              = 1;
  1922.         NTBServiceStatus.dwWaitHint                = 60000L;   // 60 seconds
  1923.  
  1924.         SetServiceStatus( NTBServiceHandle, &NTBServiceStatus );
  1925.         LeaveCriticalSection( &SCMCrtSec );
  1926.  
  1927.         sprintf( szLogBuffer,
  1928.                  "Procedure Server PAUSED, name = %s",
  1929.                  szRegistryName );
  1930.  
  1931.         if( gblServer )
  1932.             srv_log( gblServer, TRUE, szLogBuffer, SRV_NULLTERM );
  1933.         break;
  1934.  
  1935.     case SERVICE_CONTROL_CONTINUE:
  1936.  
  1937.         EnterCriticalSection( &SCMCrtSec );
  1938.  
  1939.         srv_setevent( gblServer, SRV_RESTART );
  1940.  
  1941.         NTBServiceStatus.dwCurrentState            = SERVICE_RUNNING;
  1942.         NTBServiceStatus.dwWin32ExitCode           = NO_ERROR;
  1943.         NTBServiceStatus.dwServiceSpecificExitCode = 0;
  1944.         NTBServiceStatus.dwCheckPoint              = 1;
  1945.         NTBServiceStatus.dwWaitHint                = 60000L;   // 60 seconds
  1946.  
  1947.         SetServiceStatus(NTBServiceHandle, &NTBServiceStatus);
  1948.         LeaveCriticalSection( &SCMCrtSec );
  1949.  
  1950.         sprintf( szLogBuffer,
  1951.                  "Procedure Server CONTINUED, name = %s",
  1952.                  szRegistryName );
  1953.  
  1954.         if( gblServer )
  1955.             srv_log( gblServer, TRUE, szLogBuffer, SRV_NULLTERM );
  1956.         break;
  1957.  
  1958.     default:
  1959.         // Values 128-255 can be user app defined
  1960.         //
  1961.         ;
  1962.     } // switch( dwCtrl )
  1963.  
  1964.     return;
  1965. }
  1966.  
  1967.  
  1968. // ================================= completesignal ==========================
  1969. //
  1970. // completesignal() -- Notifies Service Control Manager that NTB Server
  1971. //  has started.
  1972. //
  1973. void completesignal( SRV_SERVER * server )
  1974. {
  1975.     char szLogBuffer[1024];
  1976.  
  1977.     if( !SrvCtrMan )
  1978.         return;
  1979.  
  1980.     EnterCriticalSection( &SCMCrtSec );
  1981.  
  1982.     NTBServiceStatus.dwCurrentState = SERVICE_RUNNING;
  1983.     NTBServiceStatus.dwCheckPoint   = 0;
  1984.     NTBServiceStatus.dwWaitHint     = 0;
  1985.  
  1986.     if( !SetServiceStatus(NTBServiceHandle, &NTBServiceStatus) )
  1987.     {
  1988.         sprintf( szLogBuffer,
  1989.                  "Procedure Server Service Manager Failer: %s, (completesignal(), line = %d), message = %s",
  1990.                      szRegistryName,
  1991.                      __LINE__,
  1992.                  get_last_error_str() );
  1993.  
  1994.         srv_log( server, TRUE, szLogBuffer, SRV_NULLTERM );
  1995.     }
  1996.  
  1997.     LeaveCriticalSection( &SCMCrtSec );
  1998.     return;
  1999. }
  2000.  
  2001.  
  2002. // ==================================== NTBShutdown ==========================
  2003. //
  2004. //  NTBShutdown() -- This routine notifies ODS to terminate.  After ODS has terminate,
  2005. //                   the Service Control Manager is notified that everything has shut down.
  2006. //
  2007. void NTBShutdown( LPVOID notused )
  2008. {
  2009.     char szLogBuffer[1024];
  2010.  
  2011.     // Flush all file buffers
  2012.     //
  2013.     _flushall();
  2014.  
  2015.     // Tell ODS to terminate...
  2016.     //
  2017.     srv_setevent( gblServer, SRV_EXIT );
  2018.  
  2019.     do
  2020.     {
  2021.         // Start the checkpoint incrementer
  2022.         //
  2023.         ++(NTBServiceStatus.dwCheckPoint);
  2024.  
  2025.     } while( WaitForSingleObject(hServerDone, 1000) == WAIT_TIMEOUT );
  2026.  
  2027.     sprintf( szLogBuffer,
  2028.              "Procedure Server STOPPED, name = %s",
  2029.              szRegistryName );
  2030.  
  2031.     if( gblServer )
  2032.         srv_log( gblServer, TRUE, szLogBuffer, SRV_NULLTERM );
  2033.  
  2034.     _flushall();
  2035.  
  2036.     EnterCriticalSection( &SCMCrtSec );
  2037.  
  2038.     NTBServiceStatus.dwCurrentState            = SERVICE_STOPPED;
  2039.     NTBServiceStatus.dwWin32ExitCode           = NO_ERROR;
  2040.     NTBServiceStatus.dwServiceSpecificExitCode = 0;
  2041.     NTBServiceStatus.dwCheckPoint              = 0;
  2042.     NTBServiceStatus.dwWaitHint                = 0;
  2043.  
  2044.     SetServiceStatus( NTBServiceHandle, &NTBServiceStatus );
  2045.     LeaveCriticalSection( &SCMCrtSec );
  2046.  
  2047.     // Flush all file buffers
  2048.     //
  2049.     _flushall();
  2050.  
  2051.     ExitProcess( 0 );
  2052. }
  2053.  
  2054.  
  2055. // ============================== get_last_error_str =========================
  2056. //
  2057. // This function returns the Operating System message text and length
  2058. // associated with the error value sent to it.
  2059. //
  2060. //      Inputs:
  2061. //              iOSerror = value of message to be returned
  2062. //
  2063. //      Outputs:
  2064. //              pointer to message string (NULL if message not found)
  2065. //
  2066. char * get_last_error_str()
  2067. {
  2068.     static char * szBuffer = NULL;
  2069.     DWORD dwLastError = GetLastError();
  2070.  
  2071.     if( szBuffer )
  2072.         LocalFree( szBuffer );
  2073.  
  2074.     szBuffer = NULL;
  2075.  
  2076.     //  Attempt retrieving the message from system resource table
  2077.     //
  2078.     FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM    |
  2079.                    FORMAT_MESSAGE_ALLOCATE_BUFFER,
  2080.                    NULL,
  2081.                    dwLastError,
  2082.                    (DWORD)GetSystemDefaultLangID(),
  2083.                    (LPSTR)&szBuffer,
  2084.                    255,            // maximum message length allowed
  2085.                    (LPVOID)NULL );
  2086.     return szBuffer;
  2087. }
  2088.