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