Logo Search packages:      
Sourcecode: firebird1.5 version File versions

svc.cpp

/*
 *    PROGRAM:    JRD Access Method
 *    MODULE:           svc.c
 *    DESCRIPTION:      Service manager functions
 *
 * The contents of this file are subject to the Interbase Public
 * License Version 1.0 (the "License"); you may not use this file
 * except in compliance with the License. You may obtain a copy
 * of the License at http://www.Inprise.com/IPL.html
 *
 * Software distributed under the License is distributed on an
 * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express
 * or implied. See the License for the specific language governing
 * rights and limitations under the License.
 *
 * The Original Code was created by Inprise Corporation
 * and its predecessors. Portions created by Inprise Corporation are
 * Copyright (C) Inprise Corporation.
 *
 * All Rights Reserved.
 * Contributor(s): ______________________________________.
 *
 * 2002.02.15 Sean Leyne - Code Cleanup, removed obsolete "EPSON" define
 * 2002.02.15 Sean Leyne - Code Cleanup, removed obsolete "IMP" port
 *
 * 2002.10.27 Sean Leyne - Completed removal of obsolete "DELTA" port
 * 2002.10.27 Sean Leyne - Completed removal of obsolete "IMP" port
 *
 * 2002.10.29 Sean Leyne - Removed obsolete "Netware" port
 *
 */

#include "firebird.h"
#include "../jrd/y_ref.h"
#include "../jrd/ib_stdio.h"
#include <string.h>
#include "../jrd/ibsetjmp.h"
#include "../jrd/jrd_time.h"
#include "../jrd/common.h"
#include "../jrd/file_params.h"
#include <stdarg.h>
#include "../jrd/jrd.h"
#include "../jrd/svc.h"
#include "../jrd/jrd_pwd.h"
#include "../alice/aliceswi.h"
#include "../burp/burpswi.h"
#include "../jrd/ibase.h"
#include "gen/codes.h"
#include "../jrd/license.h"
#include "../jrd/err_proto.h"
#include "../jrd/gds_proto.h"
#include "../jrd/inf_proto.h"
#include "../jrd/isc_proto.h"
#include "../jrd/jrd_proto.h"
#include "../jrd/mov_proto.h"
#include "../jrd/sch_proto.h"
#include "../jrd/svc_proto.h"
#include "../jrd/thd_proto.h"
#include "../jrd/why_proto.h"
#include "../jrd/utl_proto.h"
#include "../jrd/jrd_proto.h"
#include "../jrd/enc_proto.h"
#include "../utilities/gsecswi.h"
#include "../utilities/dbaswi.h"
#include "../common/classes/alloc.h"
#ifdef SERVER_SHUTDOWN
#include "../jrd/jrd_proto.h"
#endif

#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif

#if HAVE_SYS_WAIT_H
# include <sys/wait.h>
#endif
#ifndef WEXITSTATUS
# define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
#endif
#ifndef WIFEXITED
# define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
#endif

#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif

#ifdef HAVE_VFORK_H
#include <vfork.h>
#endif

#ifdef sparc
#ifdef SOLARIS
#include <fcntl.h>
#endif
#endif

#ifdef SCO_UNIX
#include <fcntl.h>
#endif

/* This is defined in JRD/SCL.H, but including it causes
 * a linker warning.
 */
#define SYSDBA_USER_NAME      "SYSDBA"
#define SVC_user_dba          2
#define SVC_user_any          1
#define SVC_user_none         0

#if !defined(WIN_NT)
#  include <signal.h>
#  ifndef VMS
#    include <sys/param.h>
#    include <sys/stat.h>
#  else
#    include <stat.h>
#  endif
#  include <errno.h>
#endif

#ifdef WIN_NT
#  include <windows.h>
#  include <io.h>
#  include <stdlib.h>
#  include <fcntl.h>
#  include <sys/stat.h>
#  define SYS_ERR       isc_arg_win32
#endif


#ifndef SYS_ERR
#define SYS_ERR         isc_arg_unix
#endif

#ifdef VMS
#define waitpid(x,y,z)  wait (y)
#endif

#define statistics      stat

#define GET_LINE  1
#define GET_EOF         2
#define GET_BINARY      4

#define     SVC_TRMNTR  '\377'

/* This checks if the service has forked a process.  If not,
   it will post the isc_svcnoexe error. */

#define IS_SERVICE_RUNNING(service) \
                        if (!(service->svc_flags & SVC_forked)) {\
                            THREAD_ENTER; \
                            ERR_post (isc_svcnoexe, isc_arg_string, \
                            service->svc_service->serv_name, 0); }

#define NEED_ADMIN_PRIVS(svc) {*status++ = isc_insufficient_svc_privileges; \
                                 *status++ = isc_arg_string; \
                         *status++ = (ISC_STATUS) ERR_string(svc,strlen(svc)); \
                         *status++ = isc_arg_end; }

#define ERR_FILE_IN_USE       { TEXT buffer[MAXPATHLEN]; \
                              gds__prefix (buffer, LOCK_HEADER); \
                              *status++ = isc_file_in_use; \
                              *status++ = isc_arg_string; \
                              *status++ = (ISC_STATUS) ERR_string (buffer, strlen(buffer)); \
                                  *status++ = isc_arg_end; }


/* Option block for service parameter block */

typedef struct spb {
      TEXT *spb_sys_user_name;
      TEXT *spb_user_name;
      TEXT *spb_password;
      TEXT *spb_password_enc;
      TEXT *spb_command_line;
      USHORT spb_version;
} SPB;

static void conv_switches(USHORT, USHORT, SCHAR *, TEXT **);
static TEXT *find_switch(int, IN_SW_TAB);
static USHORT process_switches(USHORT, SCHAR *, TEXT *);
static void get_options(UCHAR *, USHORT, TEXT *, SPB *);
static TEXT *get_string_parameter(UCHAR **, TEXT **);
#ifndef SUPERSERVER
static void io_error(TEXT *, SLONG, TEXT *, ISC_STATUS, BOOLEAN);
static void service_close(SVC);
#endif
static BOOLEAN get_action_svc_bitmask(TEXT **, IN_SW_TAB, TEXT **, USHORT *,
                                                        USHORT *);
static void get_action_svc_string(TEXT **, TEXT **, USHORT *, USHORT *);
static void get_action_svc_data(TEXT **, TEXT **, USHORT *, USHORT *);
static BOOLEAN get_action_svc_parameter(TEXT **, IN_SW_TAB, TEXT **, USHORT *,
                                                            USHORT *);

#ifdef SUPERSERVER
static UCHAR service_dequeue_byte(SVC);
static void service_enqueue_byte(UCHAR, SVC);
static USHORT service_add_one(USHORT i);
static USHORT service_empty(SVC service);
static USHORT service_full(SVC service);
static void service_fork(void (*)(), SVC);
#else
static void service_fork(TEXT *, SVC);
#endif
static void service_get(SVC, SCHAR *, USHORT, USHORT, USHORT, USHORT *);
static void service_put(SVC, SCHAR *, USHORT);
#ifndef SUPERSERVER
static void timeout_handler(SVC);
#endif
#if defined(WIN_NT) && !defined(SUPERSERVER)
static USHORT service_read(SVC, SCHAR *, USHORT, USHORT);
#endif

#ifdef DEBUG
void test_thread(SVC);
void test_cmd(USHORT, SCHAR *, TEXT **);
#define TEST_THREAD test_thread
#define TEST_CMD test_cmd
#else
#define TEST_THREAD NULL
#define TEST_CMD NULL
#endif

#ifdef SERVER_SHUTDOWN
static shutdown_fct_t shutdown_fct = 0;
static ULONG shutdown_param = 0L;
#endif

#define SPB_SEC_USERNAME      "isc_spb_sec_username"

static MUTX_T svc_mutex[1], thd_mutex[1];
static BOOLEAN svc_initialized = FALSE, thd_initialized = FALSE;

/* Service Functions */
#ifdef SUPERSERVER
extern int main_gbak(SVC service);
extern int main_gfix(SVC service);
extern int main_wal_print();
extern int main_lock_print();
extern int main_gstat(SVC service);
extern int main_gsec(SVC service);

#define MAIN_GBAK       main_gbak
#define MAIN_GFIX       main_gfix
#define MAIN_WAL_PRINT  main_wal_print
#define MAIN_LOCK_PRINT main_lock_print
#define MAIN_GSTAT            main_gstat
#define MAIN_GSEC       main_gsec
#else
#define MAIN_GBAK       NULL
#define MAIN_GFIX       NULL
#define MAIN_WAL_PRINT  NULL
#define MAIN_LOCK_PRINT NULL
#define MAIN_GSTAT            NULL
#define MAIN_GSEC       NULL
#endif

void SVC_STATUS_ARG(ISC_STATUS*& status, USHORT type, const void* value)
{
      if (value)
      {
            switch (type)
            {
            case isc_arg_number:
                  *status++ = type;
                  *status++ = reinterpret_cast<ISC_STATUS>(value);
                  break;
            case isc_arg_string:
                  *status++ = type;
                  *status++ = (ISC_STATUS)
                  SVC_err_string(static_cast<const char*>(value),
                                       strlen(static_cast<const char*>(value)));
                  break;
            default:
                  break;
            }
      }
}

/* Entries which have a NULL serv_executable field will not fork
   a process on the server, but will establish a valid connection
   which can be used for isc_info_svc calls.

  The format of the structure is:

  isc_action_svc call,
  old service name (for compatibility),
  old cmd-line switches (for compatibility),
  executable to fork (for compatibility),
  thread to execute,
  in use flag (for compatibility)
*/

typedef const void (*PFN_SERV_t) ();

static const serv services[] =
{

      { isc_action_max, "print_cache", "-svc", "bin/fb_cache_print", NULL, 0 },
      { isc_action_max, "print_locks", "-svc", "bin/fb_lock_print", NULL, 0 },
      { isc_action_max, "start_cache", "-svc", "bin/fb_cache_manager", NULL, 0 },
      { isc_action_max, "analyze_database", "-svc", "bin/gstat", NULL, 0 },
      { isc_action_max, "backup", "-svc -b", "bin/gbak",    reinterpret_cast<PFN_SERV_t>(MAIN_GBAK), 0 },
      { isc_action_max, "create", "-svc -c", "bin/gbak",    reinterpret_cast<PFN_SERV_t>(MAIN_GBAK), 0 },
      { isc_action_max, "restore", "-svc -r", "bin/gbak",   reinterpret_cast<PFN_SERV_t>(MAIN_GBAK), 0 },
      { isc_action_max, "gdef", "-svc", "bin/gdef", NULL, 0 },
      { isc_action_max, "gsec", "-svc", "bin/gsec", NULL, 0 },
      { isc_action_max, "disable_journal", "-svc -disable", "bin/gjrn", NULL, 0 },
      { isc_action_max, "dump_journal", "-svc -online_dump", "bin/gjrn", NULL, 0 },
      { isc_action_max, "enable_journal", "-svc -enable", "bin/gjrn", NULL, 0 },
      { isc_action_max, "monitor_journal", "-svc -console", "bin/gjrn", NULL, 0 },
      { isc_action_max, "query_server", NULL, NULL, NULL, 0 },
      { isc_action_max, "start_journal", "-svc -server", "bin/gjrn", NULL, 0 },
      { isc_action_max, "stop_cache", "-svc -shut -cache", "bin/gfix", NULL, 0 },
      { isc_action_max, "stop_journal", "-svc -console", "bin/gjrn", NULL, 0 },
      { isc_action_max, "anonymous", NULL, NULL, NULL, 0 },

/* NEW VERSION 2 calls, the name field MUST be different from those names above
 */
      { isc_action_max, "service_mgr", NULL, NULL, NULL, 0 },
      { isc_action_svc_backup, "Backup Database", NULL, "bin/gbak",     reinterpret_cast<PFN_SERV_t>(MAIN_GBAK), 0 },
      { isc_action_svc_restore, "Restore Database", NULL, "bin/gbak",   reinterpret_cast<PFN_SERV_t>(MAIN_GBAK), 0 },
      { isc_action_svc_repair, "Repair Database", NULL, "bin/gfix",     reinterpret_cast<PFN_SERV_t>(MAIN_GFIX), 0 },
      { isc_action_svc_add_user, "Add User", NULL, "bin/gsec",    reinterpret_cast<PFN_SERV_t>(MAIN_GSEC), 0 },
      { isc_action_svc_delete_user, "Delete User", NULL, "bin/gsec",    reinterpret_cast<PFN_SERV_t>(MAIN_GSEC), 0 },
      { isc_action_svc_modify_user, "Modify User", NULL, "bin/gsec",    reinterpret_cast<PFN_SERV_t>(MAIN_GSEC), 0 },
      { isc_action_svc_display_user, "Display User", NULL, "bin/gsec",  reinterpret_cast<PFN_SERV_t>(MAIN_GSEC), 0 },
      { isc_action_svc_properties, "Database Properties", NULL, "bin/gfix",   reinterpret_cast<PFN_SERV_t>(MAIN_GFIX), 0 },
      { isc_action_svc_lock_stats, "Lock Stats", NULL, "bin/fb_lock_print",   reinterpret_cast<PFN_SERV_t>(TEST_THREAD), 0 },
      { isc_action_svc_db_stats, "Database Stats", NULL, "bin/gstat",   reinterpret_cast<PFN_SERV_t>(MAIN_GSTAT), 0 },
      { isc_action_svc_get_ib_log, "Get Log File", NULL, NULL,    reinterpret_cast<PFN_SERV_t>(SVC_read_ib_log), 0 },
/* actions with no names are undocumented */
      { isc_action_svc_set_config, NULL, NULL, NULL,  reinterpret_cast<PFN_SERV_t>(TEST_THREAD), 0 },
      { isc_action_svc_default_config, NULL, NULL, NULL,    reinterpret_cast<PFN_SERV_t>(TEST_THREAD), 0 },
      { isc_action_svc_set_env, NULL, NULL, NULL,     reinterpret_cast<PFN_SERV_t>(TEST_THREAD), 0 },
      { isc_action_svc_set_env_lock, NULL, NULL, NULL,      reinterpret_cast<PFN_SERV_t>(TEST_THREAD), 0 },
      { isc_action_svc_set_env_msg, NULL, NULL, NULL, reinterpret_cast<PFN_SERV_t>(TEST_THREAD), 0 },
      { 0, NULL, NULL, NULL, NULL, 0 }
};

/* The SERVER_CAPABILITIES_FLAG is used to mark architectural
** differences across servers.  This allows applications like server
** manager to disable features as necessary.
*/

#ifdef SUPERSERVER
#define SERVER_CAPABILITIES         REMOTE_HOP_SUPPORT | \
                                    MULTI_CLIENT_SUPPORT | \
                                          SERVER_CONFIG_SUPPORT
#endif /* SUPERSERVER */


#ifndef SERVER_CAPABILITIES
#  define SERVER_CAPABILITIES_FLAG  REMOTE_HOP_SUPPORT | NO_SERVER_SHUTDOWN_SUPPORT
#else

#  ifdef WIN_NT
#    define SERVER_CAPABILITIES_FLAG    SERVER_CAPABILITIES | QUOTED_FILENAME_SUPPORT
#  else

#    define SERVER_CAPABILITIES_FLAG    SERVER_CAPABILITIES | NO_SERVER_SHUTDOWN_SUPPORT
#  endif /* WIN_NT */

#endif /* SERVER_CAPABILITIES */

#ifdef SHLIB_DEFS
#define pipe            (*_libgds_pipe)
#define waitpid         (*_libgds_waitpid)
#define _exit           (*_libgds__exit)
#define dup       (*_libgds_dup)
#define ib_fdopen       (*_libgds_fdopen)
#define execvp          (*_libgds_execvp)
#define statistics      (*_libgds_stat)

extern int pipe();
extern pid_t waitpid();
extern void _exit();
extern int dup();
extern IB_FILE *ib_fdopen();
extern int execvp();
extern int statistics();
#endif


extern "C" {


SVC SVC_attach(USHORT   service_length,
                     TEXT*    service_name,
                     USHORT   spb_length,
                     SCHAR*   spb)
{
/**************************************
 *
 *    S V C _ a t t a c h
 *
 **************************************
 *
 * Functional description
 *    Connect to a Firebird service.
 *
 **************************************/

      const struct serv *serv;
      TEXT misc_buf[512];
#ifndef SUPERSERVER
      TEXT service_path[MAXPATHLEN];
#endif
      SPB options;
      TEXT name[129] /*, project[33] */ ;
      int id = 0;
      int group;
      int node_id;
      USHORT user_flag;

/* If the service name begins with a slash, ignore it. */

      if (*service_name == '/' || *service_name == '\\') {
            service_name++;
            if (service_length)
                  service_length--;
      }
      if (service_length) {
            strncpy(misc_buf, service_name, (int) service_length);
            misc_buf[service_length] = 0;
      }
      else {
            strcpy(misc_buf, service_name);
      }

/* Find the service by looking for an exact match. */

      for (serv = (struct serv*)services; serv->serv_name; serv++)
            if (!strcmp(misc_buf, serv->serv_name))
                  break;

      if (!serv->serv_name)
#ifdef NOT_USED_OR_REPLACED
            ERR_post(isc_service_att_err, isc_arg_gds, isc_service_not_supported,
                         0);
#else
            ERR_post(isc_service_att_err, isc_arg_gds, isc_svcnotdef,
                         isc_arg_string, SVC_err_string(misc_buf, strlen(misc_buf)),
                         0);
#endif

      TDBB tdbb = GET_THREAD_DATA;

/* If anything goes wrong, we want to be able to free any memory
   that may have been allocated. */

      SCHAR *spb_buf = 0;
      TEXT *switches = 0;
      TEXT *misc = 0;
      SVC service = 0;

      try {

/* Insert internal switch SERVICE_THD_PARAM in the service parameter block. */

      SCHAR *p = spb, *end = spb + spb_length;
      bool cmd_line_found = false;
      while (!cmd_line_found && p < end) {
            switch (*p++) {
            case isc_spb_version1:
            case isc_spb_version:
                  p++;
                  break;

            case isc_spb_sys_user_name:
            case isc_spb_user_name:
            case isc_spb_password:
            case isc_spb_password_enc:
                  {
                  USHORT length = *p++;
                  p += length;
                  }
                  break;

            case isc_spb_command_line:
                  cmd_line_found = true;
                  break;
            }
      }

      /* dimitr: it looks that we shouldn't execute the below code
                 if the first switch of the command line is "-svc_re",
                     but I couldn't find where such a switch is specified
                     by any of the client tools, so it seems that in fact
                     it's not used at all. Hence I ignore this situation. */
      if (cmd_line_found && p++ < end) {
            USHORT ignored_length = 0;
            if (!strncmp(p, "-svc ", 5))
                  ignored_length = 5;
            else if (!strncmp(p, "-svc_thd ", 9))
                  ignored_length = 9;
            USHORT param_length = sizeof(SERVICE_THD_PARAM) - 1;
            USHORT spb_buf_length = spb_length + param_length - ignored_length + 1;
            SCHAR *q = spb_buf = (TEXT*) gds__alloc(spb_buf_length + 1);
            memcpy(q, spb, p - spb);
            q += p - spb - 1;
            *q++ += param_length - ignored_length + 1;
            memcpy(q, SERVICE_THD_PARAM, param_length);
            q += param_length;
            *q++ = ' ';
            p += ignored_length;
            memcpy(q, p, end - p);
            spb = spb_buf;
            spb_length = spb_buf_length;
      }

/* Process the service parameter block. */

      if (spb_length > sizeof(misc_buf)) {
            misc = (TEXT *) gds__alloc((SLONG) spb_length);
            if (!misc) {
                  ERR_post(isc_virmemexh, 0);
            }
      } else {
            misc = misc_buf;
      }

      get_options(reinterpret_cast<UCHAR*>(spb), spb_length, misc, &options);

/* Perhaps checkout the user in the security database. */

      if (!strcmp(serv->serv_name, "anonymous")) {
            user_flag = SVC_user_none;
      } else {
            if (!options.spb_user_name)
            {
                  // user name and password are required while
                  // attaching to the services manager
                  ERR_post(isc_service_att_err, isc_arg_gds, isc_svcnouser, 0);
            }
            if (options.spb_user_name || id == -1)
            {
                  SecurityDatabase::verifyUser(name, options.spb_user_name,
                                                   options.spb_password, options.spb_password_enc,
                                                             &id, &group, &node_id);
            }

/* Check that the validated user has the authority to access this service */

#ifdef HAVE_STRCASECMP
            if (strcasecmp(options.spb_user_name, SYSDBA_USER_NAME))
#else
#ifdef HAVE_STRICMP
            if (stricmp(options.spb_user_name, SYSDBA_USER_NAME))
#else
#error dont know how to compare strings case insensitive on this system
#endif /* HAVE_STRICMP */
#endif /* HAVE_STRCASECMP */
            {
                  user_flag = SVC_user_any;
            } else {
                  user_flag = SVC_user_dba | SVC_user_any;
            }
      }

/* Allocate a buffer for the service switches, if any.  Then move them in. */

      USHORT len = ((serv->serv_std_switches) ? strlen(serv->serv_std_switches) : 0) +
            ((options.spb_command_line) ? strlen(options.spb_command_line) : 0) +
            1;

      if (len > 1)
      {
            if ((switches = (TEXT *) gds__alloc((SLONG) len)) == NULL)
            {
                  /* FREE: by exception handler */
                  ERR_post(isc_virmemexh, 0);
            }
      }

      if (switches)
            *switches = 0;
      if (serv->serv_std_switches)
            strcpy(switches, serv->serv_std_switches);
      if (options.spb_command_line && serv->serv_std_switches)
            strcat(switches, " ");
      if (options.spb_command_line)
            strcat(switches, options.spb_command_line);

/* Services operate outside of the context of databases.  Therefore
   we cannot use the JRD allocator. */

//    service = (SVC) gds__alloc((SLONG) (sizeof(struct svc)));
      service = FB_NEW(*getDefaultMemoryPool()) svc;
/* FREE: by exception handler */
      if (!service)
            ERR_post(isc_virmemexh, 0);

      memset((void *) service, 0, sizeof(struct svc));

      service->svc_status =
            (ISC_STATUS *) gds__alloc(ISC_STATUS_LENGTH * sizeof(ISC_STATUS));
/* FREE: by exception handler */
      if (!service->svc_status)
            ERR_post(isc_virmemexh, 0);

      memset((void *) service->svc_status, 0,
               ISC_STATUS_LENGTH * sizeof(ISC_STATUS));

      //service->blk_type = type_svc;
      //service->blk_pool_id = 0;
      //service->blk_length = 0;
      service->svc_service = serv;
      service->svc_resp_buf = service->svc_resp_ptr = NULL;
      service->svc_resp_buf_len = service->svc_resp_len = 0;
      service->svc_flags = (serv->serv_executable ? SVC_forked : 0) | (switches ? SVC_cmd_line : 0);
      service->svc_switches = switches;
      service->svc_handle = 0;
      service->svc_user_flag = user_flag;
      service->svc_do_shutdown = FALSE;
#ifdef SUPERSERVER
      service->svc_stdout_head = 1;
      service->svc_stdout_tail = SVC_STDOUT_BUFFER_SIZE;
      service->svc_stdout = NULL;
      service->svc_argv = NULL;
#endif
      service->svc_spb_version = options.spb_version;
      if (options.spb_user_name)
            strcpy(service->svc_username, options.spb_user_name);

/* The password will be issued to the service threads on NT since
 * there is no OS authentication.  If the password is not yet
 * encrypted, then encrypt it before saving it (since there is no
 * decrypt function).
 */
      if (options.spb_password) {
            UCHAR* s = reinterpret_cast<UCHAR*>(service->svc_enc_password);
            UCHAR* p = (UCHAR *) ENC_crypt(options.spb_password, PASSWORD_SALT) + 2;
            int l = strlen((char *) p);
            MOVE_FASTER(p, s, l);
            service->svc_enc_password[l] = 0;
      }

      if (options.spb_password_enc) {
            strcpy(service->svc_enc_password, options.spb_password_enc);
      }

/* If an executable is defined for the service, try to fork a new process.
 * Only do this if we are working with a version 1 service */

#ifndef SUPERSERVER
      if (serv->serv_executable && options.spb_version == isc_spb_version1)
#else
      if (serv->serv_thd && options.spb_version == isc_spb_version1)
#endif
      {
#ifndef SUPERSERVER
            gds__prefix(service_path, serv->serv_executable);
            service_fork(service_path, service);
#else
            /* if service is single threaded, only call if not currently running */
            if (serv->in_use == NULL) {   /* No worry for multi-threading */
                  service_fork(reinterpret_cast < void (*)() > (serv->serv_thd),
                                     service);
            }
            else if (!*(serv->in_use)) {
                  *(serv->in_use) = TRUE;
                  service_fork(reinterpret_cast < void (*)() > (serv->serv_thd),
                                     service);
            }
            else {
                  ERR_post(isc_service_att_err, isc_arg_gds,
                               isc_svc_in_use, isc_arg_string, serv->serv_name, 0);
            }
#endif
      }

      if (spb_buf) {
            gds__free((SLONG *) spb_buf);
      }
      if (misc != misc_buf) {
            gds__free((SLONG *) misc);
      }

      }     // try
      catch (const std::exception&) {
            if (spb_buf) {
                  gds__free((SLONG *) spb_buf);
            }
            if (misc && misc != misc_buf) {
                  gds__free((SLONG *) misc);
            }
            if (switches) {
                  gds__free((SLONG *) switches);
            }
            if (service) {
                  if (service->svc_status) {
                        gds__free((SLONG *) service->svc_status);
                  }
//                gds__free((SLONG *) service);
                  delete service;
            }
            ERR_punt();
      }

      return service;
}

#if defined(SUPERSERVER) && defined(SERVER_SHUTDOWN)
static int shutdown_thread(void *arg) {
/**************************************
 *
 *    s h u t d o w n _ t h r e a d
 *
 **************************************
 *
 * Functional description
 *    Shutdown SuperServer. If hangs, server 
 *  is forcely & dirty closed after timeout.
 *
 **************************************/

      THREAD_ENTER;
      JRD_shutdown_all();
      *reinterpret_cast<int*>(arg) = 1;
      return 0;
}
#endif // defined(SUPERSERVER) && defined(SERVER_SHUTDOWN)

void SVC_detach(SVC service)
{
/**************************************
 *
 *    S V C _ d e t a c h
 *
 **************************************
 *
 * Functional description
 *    Close down a service.
 *
 **************************************/

#ifdef SERVER_SHUTDOWN
      if (service->svc_do_shutdown) {
#ifdef SUPERSERVER
            int flShutdownComplete = 0;
            gds__thread_start(shutdown_thread, &flShutdownComplete, 
                  THREAD_medium, 0, 0);
            THREAD_EXIT;
            int timeout = 10; // seconds
            while (timeout--) {
                  if (flShutdownComplete)
                        break;
                  THREAD_SLEEP(1 * 1000);
            }
            if (!flShutdownComplete) {
                  gds__log("Forced server shutdown - not all databases closed");
            }
#else // SUPERSERVER
            JRD_shutdown_all();
#endif // SUPERSERVER
            if (shutdown_fct)
                        (shutdown_fct) (shutdown_param);
            else
                  exit(0);

            /* The shutdown operation is complete, just wait to die */
            while (1);
      }
#endif /* SERVER_SHUTDOWN */

#ifdef SUPERSERVER

/* Mark service as detached. */
/* If service thread is finished, cleanup memory being used by service. */

      SVC_finish(service, SVC_detached);

#else

/* Go ahead and cleanup memory being used by service */
      SVC_cleanup(service);

#endif
}

const TEXT* SVC_err_string(const TEXT* data, USHORT length)
{
/********************************************
 *
 *   S V C _ e r r _ s t r i n g
 *
 ********************************************
 *
 * Functional Description:
 *     Uses ERR_string to save string data for the
 *     status vector
 ********************************************/
      return ERR_string(data, length);
}


#ifdef SERVER_SHUTDOWN
void SVC_shutdown_init(shutdown_fct_t fptr,
                  ULONG param)
{
/**************************************
 *
 *    S V C _ s h u t d o w n _ i n i t
 *
 **************************************
 *
 * Functional description
 *    Set the global function pointer to the shutdown function.
 *
 **************************************/

      shutdown_fct = fptr;
      shutdown_param = param;
}
#endif // SERVER_SHUTDOWN


#ifdef SUPERSERVER
void SVC_fprintf(SVC service, const SCHAR * format, ...)
{
/**************************************
 *
 *    S V C _ f p r i n t f
 *
 **************************************/
/* Ensure that service is not detached. */
      if (!(service->svc_flags & SVC_detached)) {
            va_list arglist;
            char buf[1000];
            ULONG i = 0;
            ULONG n;

            va_start(arglist, format);
            n = vsprintf(buf, format, arglist);
            va_end(arglist);

            while (n > 0 && !(service->svc_flags & SVC_detached)) {
                  service_enqueue_byte(buf[i], service);
                  n--;
                  i++;
            }
      }
}


void SVC_putc(SVC service, UCHAR ch)
{
/**************************************
 *
 *    S V C _ p u t c
 *
 **************************************/
/* Ensure that service is not detached. */
      if (!(service->svc_flags & SVC_detached))
            service_enqueue_byte(ch, service);
}
#endif /*SUPERSERVER*/


ISC_STATUS SVC_query2(SVC service,
                                TDBB tdbb,
                                USHORT send_item_length,
                                SCHAR * send_items,
                                USHORT recv_item_length,
                                SCHAR * recv_items, USHORT buffer_length, SCHAR * info)
{
/**************************************
 *
 *    S V C _ q u e r y 2
 *
 **************************************
 *
 * Functional description
 *    Provide information on service object
 *
 **************************************/
      SCHAR item, *items, *end_items, *end;
      char buffer[MAXPATHLEN];
      USHORT l, length, version, get_flags;
      ISC_STATUS *status;
      USHORT timeout;

      THREAD_EXIT;

/* Setup the status vector */
      status = tdbb->tdbb_status_vector;
      *status++ = isc_arg_gds;

/* Process the send portion of the query first. */

      timeout = 0;
      items = send_items;
      end_items = items + send_item_length;
      while (items < end_items && *items != isc_info_end)
      {
            switch ((item = *items++))
            {
            case isc_info_end:
                  break;

            default:
                  if (items + 2 <= end_items) {
                        l =
                              (USHORT) gds__vax_integer(reinterpret_cast <
                                                                    UCHAR * >(items), 2);
                        items += 2;
                        if (items + l <= end_items) {
                              switch (item) {
                              case isc_info_svc_line:
                                    service_put(service, items, l);
                                    break;
                              case isc_info_svc_message:
                                    service_put(service, items - 3, l + 3);
                                    break;
                              case isc_info_svc_timeout:
                                    timeout =
                                          (USHORT) gds__vax_integer(reinterpret_cast <
                                                                                UCHAR * >(items), l);
                                    break;
                              case isc_info_svc_version:
                                    version =
                                          (USHORT) gds__vax_integer(reinterpret_cast <
                                                                                UCHAR * >(items), l);
                                    break;
                              }
                        }
                        items += l;
                  }
                  else
                        items += 2;
                  break;
            }
      }

/* Process the receive portion of the query now. */

      end = info + buffer_length;

      items = recv_items;
      end_items = items + recv_item_length;
      while (items < end_items && *items != isc_info_end)
      {
            /*
               if we attached to the "anonymous" service we allow only following queries:

               isc_info_svc_get_config     - called from within remote/ibconfig.c
               isc_info_svc_dump_pool_info - called from within utilities/print_pool.c
               isc_info_svc_user_dbpath    - called from within utilities/security.c
             */
            if (service->svc_user_flag == SVC_user_none)
            {
                  switch (*items)
                  {
                  case isc_info_svc_get_config:
                  case isc_info_svc_dump_pool_info:
                  case isc_info_svc_user_dbpath:
                        break;
                  default:
                        ERR_post(isc_bad_spb_form, 0);
                        break;
                  }
            }

            switch ((item = *items++))
            {
            case isc_info_end:
                  break;

#ifdef SERVER_SHUTDOWN
            case isc_info_svc_svr_db_info:
                  {
                        UCHAR dbbuf[1024];
                        USHORT num_dbs = 0, num = 0;
                        USHORT num_att = 0;
                        TEXT *ptr, *ptr2;

                        *info++ = item;
                        ptr =
                              JRD_num_attachments(reinterpret_cast<char*>(dbbuf),
                                                            sizeof(dbbuf), JRD_info_dbnames,
                                                            &num_att, &num_dbs);
                        /* Move the number of attachments into the info buffer */
                        CK_SPACE_FOR_NUMERIC;
                        *info++ = isc_spb_num_att;
                        ADD_SPB_NUMERIC(info, num_att);

                        /* Move the number of databases in use into the info buffer */
                        CK_SPACE_FOR_NUMERIC;
                        *info++ = isc_spb_num_db;
                        ADD_SPB_NUMERIC(info, num_dbs);

                        /* Move db names into the info buffer */
                        if (ptr2 = ptr) {
                              num = (USHORT) isc_vax_integer(ptr2, sizeof(USHORT));
                              assert(num == num_dbs);
                              ptr2 += sizeof(USHORT);
                              for (; num; num--) {
                                    length =
                                          (USHORT) isc_vax_integer(ptr2, sizeof(USHORT));
                                    ptr2 += sizeof(USHORT);
                                    if (!
                                          (info =
                                           INF_put_item(isc_spb_dbname, length, ptr2, info,
                                                              end))) {
                                          THREAD_ENTER;
                                          return 0;
                                    }
                                    ptr2 += length;
                              }

                              if (ptr != reinterpret_cast < TEXT * >(dbbuf))
                                    gds__free(ptr);   /* memory has been allocated by
                                                               JRD_num_attachments() */
                        }

                        if (info < end)
                              *info++ = isc_info_flag_end;
                  }

                  break;

            case isc_info_svc_svr_online:
                  *info++ = item;
                  if (service->svc_user_flag & SVC_user_dba) {
                        service->svc_do_shutdown = FALSE;
                        WHY_set_shutdown(FALSE);
                  }
                  else
                        NEED_ADMIN_PRIVS("isc_info_svc_svr_online");
                  break;

            case isc_info_svc_svr_offline:
                  *info++ = item;
                  if (service->svc_user_flag & SVC_user_dba) {
                        service->svc_do_shutdown = TRUE;
                        WHY_set_shutdown(TRUE);
                  }
                  else
                        NEED_ADMIN_PRIVS("isc_info_svc_svr_offline");
                  break;
#endif /* SERVER_SHUTDOWN */

                  /* The following 3 service commands (or items) stuff the response
                     buffer 'info' with values of environment variable INTERBASE,
                     INTERBASE_LOCK or INTERBASE_MSG. If the environment variable
                     is not set then default value is returned.
                   */
            case isc_info_svc_get_env:
            case isc_info_svc_get_env_lock:
            case isc_info_svc_get_env_msg:
                  switch (item) {
                  case isc_info_svc_get_env:
                        gds__prefix(buffer, "");
                        break;
                  case isc_info_svc_get_env_lock:
                        gds__prefix_lock(buffer, "");
                        break;
                  case isc_info_svc_get_env_msg:
                        gds__prefix_msg(buffer, "");
                  }

                  /* Note: it is safe to use strlen to get a length of "buffer"
                     because gds_prefix[_lock|_msg] return a zero-terminated
                     string
                   */
                  info = INF_put_item(item, strlen(buffer), buffer, info, end);
                  if (!info) {
                        THREAD_ENTER;
                        return 0;
                  }
                  break;

#ifdef SUPERSERVER
            case isc_info_svc_dump_pool_info:
                  {
                        int length;
                        char fname[MAXPATHLEN];
                        length = isc_vax_integer(items, sizeof(USHORT));
                        items += sizeof(USHORT);
                        strncpy(fname, items, length);
                        fname[length] = 0;
                        JRD_print_all_counters(fname);
                        break;
                  }
#endif
/*
            case isc_info_svc_get_config:
                  // TODO: iterate through all integer-based config values
                  //           and return them to the client
                  break;

            case isc_info_svc_default_config:
                  *info++ = item;
                  if (service->svc_user_flag & SVC_user_dba) {
                        THREAD_ENTER;
                        // TODO: reset the config values to defaults
                        THREAD_EXIT;
                  }
                  else
                        NEED_ADMIN_PRIVS("isc_info_svc_default_config");
                  break;

            case isc_info_svc_set_config:
                  *info++ = item;
                  if (service->svc_user_flag & SVC_user_dba) {
                        THREAD_ENTER;
                        // TODO: set the config values
                        THREAD_EXIT;
                  }
                  else {
                        NEED_ADMIN_PRIVS("isc_info_svc_set_config");
                  }
                  break;
*/
            case isc_info_svc_version:
                  /* The version of the service manager */
                  CK_SPACE_FOR_NUMERIC;
                  *info++ = item;
                  ADD_SPB_NUMERIC(info, SERVICE_VERSION);
                  break;

            case isc_info_svc_capabilities:
                  /* bitmask defining any specific architectural differences */
                  CK_SPACE_FOR_NUMERIC;
                  *info++ = item;
                  ADD_SPB_NUMERIC(info, SERVER_CAPABILITIES_FLAG);
                  break;

            case isc_info_svc_running:
                  /* Returns the status of the flag SVC_thd_running */
                  CK_SPACE_FOR_NUMERIC;
                  *info++ = item;
                  if (service->svc_flags & SVC_thd_running)
                        ADD_SPB_NUMERIC(info, TRUE)
                  else
                        ADD_SPB_NUMERIC(info, FALSE)

                  break;

            case isc_info_svc_server_version:
                  /* The version of the server engine */
                  info = INF_put_item(item, strlen(GDS_VERSION), GDS_VERSION, info, end);
                  if (!info) {
                        THREAD_ENTER;
                        return 0;
                  }
                  break;

            case isc_info_svc_implementation:
                  /* The server implementation - e.g. Interbase/sun4 */
                  isc_format_implementation(IMPLEMENTATION, sizeof(buffer), buffer,
                                                        0, 0, NULL);
                  info = INF_put_item(item, strlen(buffer), buffer, info, end);
                  if (!info) {
                        THREAD_ENTER;
                        return 0;
                  }

                  break;


            case isc_info_svc_user_dbpath:
                  /* The path to the user security database (security.fdb) */
                  SecurityDatabase::getPath(buffer);

                  if (!(info = INF_put_item(item, strlen(buffer), buffer,
                                                        info, end))) {
                        THREAD_ENTER;
                        return 0;
                  }
                  break;

            case isc_info_svc_response:
                  service->svc_resp_len = 0;
                  if (info + 4 >= end) {
                        *info++ = isc_info_truncated;
                        break;
                  }
                  service_put(service, &item, 1);
                  service_get(service, &item, 1, GET_BINARY, 0, &length);
                  service_get(service, buffer, 2, GET_BINARY, 0, &length);
                  l =
                        (USHORT) gds__vax_integer(reinterpret_cast <
                                                              UCHAR * >(buffer), 2);
                  length = MIN(end - (info + 5), l);
                  service_get(service, info + 3, length, GET_BINARY, 0, &length);
                  info = INF_put_item(item, length, info + 3, info, end);
                  if (length != l) {
                        *info++ = isc_info_truncated;
                        l -= length;
                        if (l > service->svc_resp_buf_len) {
                              THREAD_ENTER;
                              if (service->svc_resp_buf)
                                    gds__free((SLONG *) service->svc_resp_buf);
                              service->svc_resp_buf = (UCHAR *) gds__alloc((SLONG) l);
                              /* FREE: in SVC_detach() */
                              if (!service->svc_resp_buf) { /* NOMEM: */
                                    DEV_REPORT("SVC_query: out of memory");
                                    /* NOMEM: not really handled well */
                                    l = 0;      /* set the length to zero */
                              }
                              service->svc_resp_buf_len = l;
                              THREAD_EXIT;
                        }
                        service_get(service,
                                          reinterpret_cast < char *>(service->svc_resp_buf),
                                          l, GET_BINARY, 0, &length);
                        service->svc_resp_ptr = service->svc_resp_buf;
                        service->svc_resp_len = l;
                  }
                  break;

            case isc_info_svc_response_more:
                  if ( (l = length = service->svc_resp_len) )
                        length = MIN(end - (info + 5), l);
                  if (!
                        (info =
                         INF_put_item(item, length,
                                            reinterpret_cast <
                                            char *>(service->svc_resp_ptr), info, end))) {
                        THREAD_ENTER;
                        return 0;
                  }
                  service->svc_resp_ptr += length;
                  service->svc_resp_len -= length;
                  if (length != l)
                        *info++ = isc_info_truncated;
                  break;

            case isc_info_svc_total_length:
                  service_put(service, &item, 1);
                  service_get(service, &item, 1, GET_BINARY, 0, &length);
                  service_get(service, buffer, 2, GET_BINARY, 0, &length);
                  l =
                        (USHORT) gds__vax_integer(reinterpret_cast <
                                                              UCHAR * >(buffer), 2);
                  service_get(service, buffer, l, GET_BINARY, 0, &length);
                  if (!(info = INF_put_item(item, length, buffer, info, end))) {
                        THREAD_ENTER;
                        return 0;
                  }
                  break;

            case isc_info_svc_line:
            case isc_info_svc_to_eof:
            case isc_info_svc_limbo_trans:
            case isc_info_svc_get_users:
                  if (info + 4 >= end) {
                        *info++ = isc_info_truncated;
                        break;
                  }

                  if (item == isc_info_svc_line)
                        get_flags = GET_LINE;
                  else if (item == isc_info_svc_to_eof)
                        get_flags = GET_EOF;
                  else
                        get_flags = GET_BINARY;

                  service_get(service, info + 3, end - (info + 5), get_flags,
                                    timeout, &length);

                  /* If the read timed out, return the data, if any, & a timeout
                     item.  If the input buffer was not large enough
                     to store a read to eof, return the data that was read along
                     with an indication that more is available. */

                  if (!(info = INF_put_item(item, length, info + 3, info, end))) {
                        THREAD_ENTER;
                        return 0;
                  }

                  if (service->svc_flags & SVC_timeout)
                  {
                        *info++ = isc_info_svc_timeout;
                  }
                  else
                  {
                        if (!length && !(service->svc_flags & SVC_finished))
                        {
                              *info++ = isc_info_data_not_ready;
                        }
                        else
                        {
                              if (item == isc_info_svc_to_eof &&
                                    !(service->svc_flags & SVC_finished))
                              {
                                    *info++ = isc_info_truncated;
                              }
                        }
                  }
                  break;
            }

            if (service->svc_user_flag == SVC_user_none)
                  break;
      }

      if (info < end)
            *info = isc_info_end;

      if (!(service->svc_flags & SVC_thd_running))
      {
            SVC_finish(service, SVC_finished);
      }

      THREAD_ENTER;
      return tdbb->tdbb_status_vector[1];
}

void SVC_query(SVC            service,
                     USHORT   send_item_length,
                     SCHAR*   send_items,
                     USHORT   recv_item_length,
                     SCHAR*   recv_items,
                     USHORT   buffer_length,
                     SCHAR*   info)
{
/**************************************
 *
 *    S V C _ q u e r y
 *
 **************************************
 *
 * Functional description
 *    Provide information on service object.
 *
 **************************************/
      SCHAR item, *items, *end_items, *end, *p, *q;
      UCHAR buffer[256];
      TEXT PathBuffer[MAXPATHLEN];
      USHORT l, length, version, get_flags;
      USHORT timeout;

      THREAD_EXIT;

/* Process the send portion of the query first. */

      timeout = 0;
      items = send_items;
      end_items = items + send_item_length;
      while (items < end_items && *items != isc_info_end)
      {
            switch ((item = *items++))
            {
            case isc_info_end:
                  break;

            default:
                  if (items + 2 <= end_items)
                  {
                        l = (USHORT) gds__vax_integer(reinterpret_cast<UCHAR*>(items), 2);
                        items += 2;
                        if (items + l <= end_items)
                        {
                              switch (item) {
                              case isc_info_svc_line:
                                    service_put(service, items, l);
                                    break;
                              case isc_info_svc_message:
                                    service_put(service, items - 3, l + 3);
                                    break;
                              case isc_info_svc_timeout:
                                    timeout =
                                          (USHORT) gds__vax_integer(reinterpret_cast<UCHAR*>(items), l);
                                    break;
                              case isc_info_svc_version:
                                    version =
                                          (USHORT) gds__vax_integer(reinterpret_cast<UCHAR*>(items), l);
                                    break;
                              }
                        }
                        items += l;
                  }
                  else
                        items += 2;
                  break;
            }
      }

/* Process the receive portion of the query now. */

      end = info + buffer_length;

      items = recv_items;
      end_items = items + recv_item_length;
      while (items < end_items && *items != isc_info_end)
      {
            switch ((item = *items++))
            {
            case isc_info_end:
                  break;

#ifdef SERVER_SHUTDOWN
            case isc_info_svc_svr_db_info:
                  {
                        USHORT num_att = 0;
                        USHORT num_dbs = 0;
                        JRD_num_attachments(NULL, 0, 0, &num_att, &num_dbs);
                        length = INF_convert(num_att, reinterpret_cast < char *>(buffer));
                        info = INF_put_item(item,
                                                      length,
                                                      reinterpret_cast<char*>(buffer),
                                                      info,
                                                      end);
                        if (!info) {
                              THREAD_ENTER;
                              return;
                        }
                        length = INF_convert(num_dbs, reinterpret_cast < char *>(buffer));
                        info = INF_put_item(item,
                                                      length,
                                                      reinterpret_cast<char*>(buffer),
                                                      info,
                                                      end);
                        if (!info) {
                              THREAD_ENTER;
                              return;
                        }
                  }
                  break;

            case isc_info_svc_svr_online:
                  *info++ = item;
                  if (service->svc_user_flag & SVC_user_dba) {
                        service->svc_do_shutdown = FALSE;
                        WHY_set_shutdown(FALSE);
                        *info++ = 0;      /* Success */
                  }
                  else
                        *info++ = 2;      /* No user authority */
                  break;

            case isc_info_svc_svr_offline:
                  *info++ = item;
                  if (service->svc_user_flag & SVC_user_dba) {
                        service->svc_do_shutdown = TRUE;
                        WHY_set_shutdown(TRUE);
                        *info++ = 0;      /* Success */
                  }
                  else
                        *info++ = 2;      /* No user authority */
                  break;
#endif /* SERVER_SHUTDOWN */

                  /* The following 3 service commands (or items) stuff the response
                     buffer 'info' with values of environment variable INTERBASE,
                     INTERBASE_LOCK or INTERBASE_MSG. If the environment variable
                     is not set then default value is returned.
                   */
            case isc_info_svc_get_env:
            case isc_info_svc_get_env_lock:
            case isc_info_svc_get_env_msg:
                  switch (item) {
                  case isc_info_svc_get_env:
                        gds__prefix(PathBuffer, "");
                        break;
                  case isc_info_svc_get_env_lock:
                        gds__prefix_lock(PathBuffer, "");
                        break;
                  case isc_info_svc_get_env_msg:
                        gds__prefix_msg(PathBuffer, "");
                  }

                  /* Note: it is safe to use strlen to get a length of "buffer"
                     because gds_prefix[_lock|_msg] return a zero-terminated
                     string
                   */
                  if (!(info = INF_put_item(item, strlen(PathBuffer),
                                                        PathBuffer, info, end))) {
                        THREAD_ENTER;
                        return;
                  }
                  break;

#ifdef SUPERSERVER
            case isc_info_svc_dump_pool_info:
                  {
                        int length;
                        char fname[MAXPATHLEN];
                        length = isc_vax_integer(items, sizeof(USHORT));
                        items += sizeof(USHORT);
                        strncpy(fname, items, length);
                        fname[length] = 0;
                        JRD_print_all_counters(fname);
                        break;
                  }
#endif
/*
            case isc_info_svc_get_config:
                  // TODO: iterate through all integer-based config values
                  //           and return them to the client
                  break;

            case isc_info_svc_default_config:
                  *info++ = item;
                  if (service->svc_user_flag & SVC_user_dba) {
                        THREAD_ENTER;
                        // TODO: reset the config values to defaults
                        THREAD_EXIT;
                  }
                  else
                        NEED_ADMIN_PRIVS("isc_info_svc_default_config");
                  break;

            case isc_info_svc_set_config:
                  *info++ = item;
                  if (service->svc_user_flag & SVC_user_dba) {
                        THREAD_ENTER;
                        // TODO: set the config values
                        THREAD_EXIT;
                  }
                  else {
                        NEED_ADMIN_PRIVS("isc_info_svc_set_config");
                  }
                  break;
*/
            case isc_info_svc_version:
                  /* The version of the service manager */

                  length =
                        INF_convert(SERVICE_VERSION,
                                          reinterpret_cast < char *>(buffer));
                  if (!
                        (info =
                         INF_put_item(item, length,
                                            reinterpret_cast < char *>(buffer), info,
                                            end))) {
                        THREAD_ENTER;
                        return;
                  }
                  break;

            case isc_info_svc_capabilities:
                  /* bitmask defining any specific architectural differences */

                  length =
                        INF_convert(SERVER_CAPABILITIES_FLAG,
                                          reinterpret_cast < char *>(buffer));
                  if (!
                        (info =
                         INF_put_item(item, length,
                                            reinterpret_cast < char *>(buffer), info,
                                            end))) {
                        THREAD_ENTER;
                        return;
                  }
                  break;

            case isc_info_svc_server_version:
                  /* The version of the server engine */

                  p = reinterpret_cast < char *>(buffer);
                  *p++ = 1;               /* Count */
                  *p++ = sizeof(GDS_VERSION) - 1;
                  for (q = GDS_VERSION; *q; p++, q++)
                        *p = *q;
                  if (!(info = INF_put_item(item,
                                                      p - (SCHAR *) buffer,
                                                      reinterpret_cast<char*>(buffer),
                                                      info,
                                                      end)))
                  {
                        THREAD_ENTER;
                        return;
                  }
                  break;

            case isc_info_svc_implementation:
                  /* The server implementation - e.g. Interbase/sun4 */

                  p = reinterpret_cast < char *>(buffer);
                  *p++ = 1;               /* Count */
                  *p++ = IMPLEMENTATION;
                  if (!(info = INF_put_item(item,
                                                      p - (SCHAR *) buffer,
                                                      reinterpret_cast<char*>(buffer),
                                                      info,
                                                      end)))
                  {
                        THREAD_ENTER;
                        return;
                  }
                  break;


            case isc_info_svc_user_dbpath:
                  /* The path to the user security database (security.fdb) */
                  SecurityDatabase::getPath(reinterpret_cast<char*>(buffer));

                  if (!(info = INF_put_item(item,
                                                        strlen(reinterpret_cast<char*>(buffer)),
                                                        reinterpret_cast<char*>(buffer),
                                                        info,
                                                        end)))
                  {
                        THREAD_ENTER;
                        return;
                  }
                  break;

            case isc_info_svc_response:
                  service->svc_resp_len = 0;
                  if (info + 4 > end)
                  {
                        *info++ = isc_info_truncated;
                        break;
                  }
                  service_put(service, &item, 1);
                  service_get(service, &item, 1, GET_BINARY, 0, &length);
                  service_get(service, reinterpret_cast < char *>(buffer), 2,
                                    GET_BINARY, 0, &length);
                  l = (USHORT) gds__vax_integer(buffer, 2);
                  length = MIN(end - (info + 4), l);
                  service_get(service, info + 3, length, GET_BINARY, 0, &length);
                  info = INF_put_item(item, length, info + 3, info, end);
                  if (length != l)
                  {
                        *info++ = isc_info_truncated;
                        l -= length;
                        if (l > service->svc_resp_buf_len)
                        {
                              THREAD_ENTER;
                              if (service->svc_resp_buf)
                                    gds__free((SLONG *) service->svc_resp_buf);
                              service->svc_resp_buf = (UCHAR *) gds__alloc((SLONG) l);
                              /* FREE: in SVC_detach() */
                              if (!service->svc_resp_buf)
                              {     /* NOMEM: */
                                    DEV_REPORT("SVC_query: out of memory");
                                    /* NOMEM: not really handled well */
                                    l = 0;      /* set the length to zero */
                              }
                              service->svc_resp_buf_len = l;
                              THREAD_EXIT;
                        }
                        service_get(service,
                                          reinterpret_cast<char*>(service->svc_resp_buf),
                                          l,
                                          GET_BINARY,
                                          0,
                                          &length);
                        service->svc_resp_ptr = service->svc_resp_buf;
                        service->svc_resp_len = l;
                  }
                  break;

            case isc_info_svc_response_more:
                  if ( (l = length = service->svc_resp_len) )
                        length = MIN(end - (info + 4), l);
                  if (!(info = INF_put_item(item,
                                                      length,
                                                      reinterpret_cast<char*>(service->svc_resp_ptr),
                                                      info,
                                                      end)))
                  {
                        THREAD_ENTER;
                        return;
                  }
                  service->svc_resp_ptr += length;
                  service->svc_resp_len -= length;
                  if (length != l)
                        *info++ = isc_info_truncated;
                  break;

            case isc_info_svc_total_length:
                  service_put(service, &item, 1);
                  service_get(service, &item, 1, GET_BINARY, 0, &length);
                  service_get(service, reinterpret_cast < char *>(buffer), 2,
                                    GET_BINARY, 0, &length);
                  l = (USHORT) gds__vax_integer(buffer, 2);
                  service_get(service, reinterpret_cast < char *>(buffer), l,
                                    GET_BINARY, 0, &length);
                  if (!(info = INF_put_item(item,
                                                            length,
                                                            reinterpret_cast<char*>(buffer),
                                                            info,
                                                            end)))
                  {
                        THREAD_ENTER;
                        return;
                  }
                  break;

            case isc_info_svc_line:
            case isc_info_svc_to_eof:
                  if (info + 4 > end)
                  {
                        *info++ = isc_info_truncated;
                        break;
                  }
                  get_flags = (item == isc_info_svc_line) ? GET_LINE : GET_EOF;
                  service_get(service, info + 3, end - (info + 4), get_flags,
                                    timeout, &length);

                  /* If the read timed out, return the data, if any, & a timeout
                     item.  If the input buffer was not large enough
                     to store a read to eof, return the data that was read along
                     with an indication that more is available. */

                  info = INF_put_item(item, length, info + 3, info, end);

                  if (service->svc_flags & SVC_timeout)
                        *info++ = isc_info_svc_timeout;
                  else
                  {
                        if (!length && !(service->svc_flags & SVC_finished))
                              *info++ = isc_info_data_not_ready;
                        else
                              if (item == isc_info_svc_to_eof &&
                                    !(service->svc_flags & SVC_finished))
                                    *info++ = isc_info_truncated;
                  }
                  break;
            }
      }

      if (info < end)
      {
            *info = isc_info_end;
      }

      if (!(service->svc_flags & SVC_thd_running))
      {
            SVC_finish(service, SVC_finished);
      }

      THREAD_ENTER;
}


void *SVC_start(SVC service, USHORT spb_length, SCHAR * spb)
{
/**************************************
 *
 *    S V C _ s t a r t
 *
 **************************************
 *
 * Functional description
 *      Start a Firebird service
 *
 **************************************/
      TDBB tdbb;
      const struct serv *serv;
      USHORT svc_id;
      TEXT* tmp_ptr = NULL;
      USHORT opt_switch_len = 0;
      BOOLEAN flag_spb_options = FALSE;
#ifndef SUPERSERVER
      TEXT service_path[MAXPATHLEN];
#endif

/*    NOTE: The parameter RESERVED must not be used
 *    for any purpose as there are networking issues
 *    involved (as with any handle that goes over the
 *    network).  This parameter will be implemented at
 *    a later date.
 */

      isc_resv_handle reserved = (isc_resv_handle)0;  /* Reserved for future functionality */

/* The name of the service is the first element of the buffer */
      svc_id = *spb;

      for (serv = (struct serv*)services; serv->serv_action; serv++)
            if (serv->serv_action == svc_id)
                  break;

      if (!serv->serv_name)
            ERR_post(isc_service_att_err, isc_arg_gds, isc_service_not_supported, 0);

/* currently we do not use "anonymous" service for any purposes but
   isc_service_query() */
      if (service->svc_user_flag == SVC_user_none)
            ERR_post(isc_bad_spb_form, 0);

      if (!thd_initialized) {
            THD_MUTEX_INIT(thd_mutex);
            thd_initialized = TRUE;
      }

      THD_MUTEX_LOCK(thd_mutex);
      if (service->svc_flags & SVC_thd_running) {
            THD_MUTEX_UNLOCK(thd_mutex);
            ERR_post(isc_svc_in_use, isc_arg_string,
                         SVC_err_string(const_cast < char *>(serv->serv_name),
                                                strlen(serv->serv_name)), 0);
      }
      else {
            /* Another service may have been started with this service block.  If so,
             * we must reset the service flags.
             */
            if (service->svc_switches && !(service->svc_flags & SVC_cmd_line)) {
                  gds__free(service->svc_switches);
                  service->svc_switches = NULL;
            }
            if (!(service->svc_flags & SVC_detached))
                  service->svc_flags = 0;
            service->svc_flags |= SVC_thd_running;
      }
      THD_MUTEX_UNLOCK(thd_mutex);

      tdbb = GET_THREAD_DATA;

      try {

/* Only need to add username and password information to those calls which need
 * to make a database connection
 */
      if (*spb == isc_action_svc_backup ||
            *spb == isc_action_svc_restore ||
            *spb == isc_action_svc_repair ||
            *spb == isc_action_svc_add_user ||
            *spb == isc_action_svc_delete_user ||
            *spb == isc_action_svc_modify_user ||
            *spb == isc_action_svc_display_user ||
            *spb == isc_action_svc_db_stats ||
            *spb == isc_action_svc_properties) {
            /* the user issued a username when connecting to the service so
             * add the length of the username and switch to new_spb_length
             */

            if (*service->svc_username)
                  opt_switch_len +=
                        (strlen(service->svc_username) + 1 + sizeof(USERNAME_SWITCH));

            /* the user issued a password when connecting to the service so add
             * the length of the password and switch to new_spb_length
             */
            if (*service->svc_enc_password)
                  opt_switch_len +=
                        (strlen(service->svc_enc_password) + 1 +
                         sizeof(PASSWORD_SWITCH));

            /* If svc_switches is not used -- call a command-line parsing utility */
            if (!service->svc_switches) {
                  conv_switches(spb_length, opt_switch_len, spb,
                                      &service->svc_switches);
            }
            else {
                  /* Command line options (isc_spb_options) is used.
                   * Currently the only case in which it might happen is -- gbak utility
                   * is called with a "-server" switch.
                   */
                  flag_spb_options = TRUE;

                  tmp_ptr = (TEXT *)
                        gds__alloc((SLONG)
                                       (strlen(service->svc_switches) + +1 +
                                          opt_switch_len + 1));
                  if (!tmp_ptr)           /* NOMEM: */
                        ERR_post(isc_virmemexh, 0);
            }

            /* add the username and password to the end of svc_switches if needed */
            if (service->svc_switches) {
                  if (flag_spb_options)
                        strcpy(tmp_ptr, service->svc_switches);

                  if (*service->svc_username) {
                        if (!flag_spb_options)
                              sprintf(service->svc_switches, "%s %s %s",
                                          service->svc_switches, USERNAME_SWITCH,
                                          service->svc_username);
                        else
                              sprintf(tmp_ptr, "%s %s %s", tmp_ptr, USERNAME_SWITCH,
                                          service->svc_username);
                  }

                  if (*service->svc_enc_password) {
                        if (!flag_spb_options)
                              sprintf(service->svc_switches, "%s %s %s",
                                          service->svc_switches, PASSWORD_SWITCH,
                                          service->svc_enc_password);
                        else
                              sprintf(tmp_ptr, "%s %s %s", tmp_ptr, PASSWORD_SWITCH,
                                          service->svc_enc_password);
                  }

                  if (flag_spb_options) {
                        gds__free(service->svc_switches);
                        service->svc_switches = tmp_ptr;
                  }
            }
      }
      else {
            /* If svc_switches is not used -- call a command-line parsing utility */
            if (!service->svc_switches) {
                  conv_switches(spb_length, opt_switch_len, spb,
                                      &service->svc_switches);
            }
            else {
                  assert(service->svc_switches == NULL);
            }
      }
/* All services except for get_ib_log require switches */
      if (service->svc_switches == NULL && *spb != isc_action_svc_get_ib_log)
            ERR_post(isc_bad_spb_form, 0);

#ifndef SUPERSERVER
      if (serv->serv_executable) {
            gds__prefix(service_path, serv->serv_executable);
            service->svc_flags = SVC_forked;
            service_fork(service_path, service);
      }

#else

/* Break up the command line into individual arguments. */
      if (service->svc_switches)
      {
            USHORT argc;
            TEXT* p;
            TEXT* q;
            TEXT** arg;
            for (argc = 2, p = service->svc_switches; *p;) {
                  if (*p == ' ') {
                        argc++;
                        while (*p == ' ')
                              p++;
                  }
                  else {
                        if (*p == SVC_TRMNTR) {
                              while (*p++ && *p != SVC_TRMNTR);
                              assert(*p == SVC_TRMNTR);
                        }
                        p++;
                  }
            }

            service->svc_argc = argc;

            arg = (TEXT **) gds__alloc((SLONG) ((argc + 1) * sizeof(TEXT *)));
            /*
             * the service block can be reused hence free a memory from the
             * previous usage if any.
             */
            if (service->svc_argv)
                  gds__free(service->svc_argv);
            service->svc_argv = arg;
            /* FREE: at SVC_detach() - Possible memory leak if ERR_post() occurs */
            if (!arg)                     /* NOMEM: */
                  ERR_post(isc_virmemexh, 0);

            *arg++ = (TEXT *)(serv->serv_thd);
            p = q = service->svc_switches;

            while (*q == ' ')
                  q++;

            while (*q) {
                  *arg = p;
                  while (*p = *q++) {
                        if (*p == ' ')
                              break;

                        if (*p == SVC_TRMNTR) {
                              *arg = ++p; /* skip terminator */
                              while (*p = *q++)
                                    /* If *q points to the last argument, then terminate the argument */
                                    if ((*q == 0 || *q == ' ') && *p == SVC_TRMNTR) {
                                          *p = '\0';
                                          break;
                                    }
                                    else
                                          p++;
                        }

                        if (*p == '\\' && *q == ' ') {
                              *p = ' ';
                              q++;
                        }
                        p++;
                  }
                  arg++;
                  if (!*p)
                        break;
                  *p++ = '\0';
                  while (*q == ' ')
                        q++;
            }
            *arg = NULL;
      }

/*
 * the service block can be reused hence free a memory from the
 * previous usage as well as init a status vector.
 */

      memset((void *) service->svc_status, 0,
               ISC_STATUS_LENGTH * sizeof(ISC_STATUS));

      if (service->svc_stdout)
            gds__free(service->svc_stdout);

      service->svc_stdout = (UCHAR*)gds__alloc((SLONG) SVC_STDOUT_BUFFER_SIZE + 1);
/* FREE: at SVC_detach() */
      if (!service->svc_stdout)     /* NOMEM: */
      {
            ERR_post(isc_virmemexh, 0);
      }

      if (serv->serv_thd) {
            SLONG count;
#pragma FB_COMPILER_MESSAGE("Fix! Probable bug!")
            EVENT evnt_ptr =
                  reinterpret_cast<EVENT> (&(service->svc_start_event));

            THREAD_EXIT;
            /* create an event for the service.  The event will be signaled once the
             * particular service has reached a point in which it can start to return
             * information to the client.  This will allow isc_service_start to
             * include in its status vector information about the service's ability to
             * start.  This is needed since gds__thread_start will almost always
             * succeed.
             */
            ISC_event_init(evnt_ptr, 0, 0);
            count = ISC_event_clear(evnt_ptr);

            gds__thread_start(reinterpret_cast < FPTR_INT_VOID_PTR >
                                      (serv->serv_thd), service, THREAD_medium, 0,
                                      (void *) &service->svc_handle);

            /* check for the service being detached.  This will prevent the thread
             * from waiting infinitely if the client goes away
             */
            while (!(service->svc_flags & SVC_detached)) {
                  if (ISC_event_wait(1,
                                                &evnt_ptr,
                                                &count,
                                                60 * 1000,
                                                (FPTR_VOID) 0,
                                                0))
                  {
                        continue;
                  }
                  else {                        /* the event was posted */
                        break;
                  }
            }

            ISC_event_fini(evnt_ptr);
            THREAD_ENTER;
      }
      else
      {
            ERR_post(isc_svcnotdef,
                        isc_arg_string,
                        SVC_err_string(const_cast<char*>(serv->serv_name),
                        strlen(serv->serv_name)),
                        0);
      }

#endif /* SUPERSERVER */

      }     // try
      catch (const std::exception&) {
            if (service->svc_switches) {
                  gds__free((SLONG *) service->svc_switches);
            }
            if (service) {
//                gds__free((SLONG *) service);
                  delete service;
            }
            ERR_punt();
      }

      return reinterpret_cast<void*>(reserved);
}


void SVC_read_ib_log(SVC service)
{
/**************************************
 *
 *      S V C _ r e a d _ i b _ l o g
 *
 **************************************
 *
 * Functional description
 *   Service function which reads the Firebird
 *   log file into the service buffers.
 *
 **************************************/
      IB_FILE *file;
      TEXT name[MAXPATHLEN], buffer[100];
      BOOLEAN svc_started = FALSE;
#ifdef SUPERSERVER
      ISC_STATUS *status;

      status = service->svc_status;
      *status++ = isc_arg_gds;
#endif

      gds__prefix(name, LOGFILE);
      if ((file = ib_fopen(name, "r")) != NULL) {
#ifdef SUPERSERVER
            *status++ = FB_SUCCESS;
            *status++ = isc_arg_end;
#endif
            SVC_STARTED(service);
            svc_started = TRUE;
            while (!ib_feof(file) && !ib_ferror(file)) {
                  ib_fgets(buffer, sizeof(buffer), file);
#ifdef SUPERSERVER
                  SVC_fprintf(service, "%s", buffer);
#else
                  service_put(service, buffer, sizeof(buffer));
#endif
            }
      }

      if (!file || file && ib_ferror(file)) {
#ifdef SUPERSERVER
            *status++ = isc_sys_request;
            if (!file) {
                  SVC_STATUS_ARG(status, isc_arg_string, (void*)"ib_fopen");
            }
            else {
                  SVC_STATUS_ARG(status, isc_arg_string, (void*)"ib_fgets");
            }
            *status++ = SYS_ARG;
            *status++ = errno;
            *status++ = isc_arg_end;
#endif
            if (!svc_started)
                  SVC_STARTED(service);
      }

      if (file)
            ib_fclose(file);

#ifdef SUPERSERVER
      SVC_finish(service, SVC_finished);
#else
      SVC_cleanup(service);
#endif
}


}     // extern "C"

static void get_options(UCHAR*      spb,
                                    USHORT      spb_length,
                                    TEXT* scratch,
                                    SPB*  options)
{
/**************************************
 *
 *    g e t _ o p t i o n s
 *
 **************************************
 *
 * Functional description
 *    Parse service parameter block picking up options and things.
 *
 **************************************/
      UCHAR *p, *end_spb;
      USHORT l;

      MOVE_CLEAR(options, (SLONG) sizeof(struct spb));
      p = spb;
      end_spb = p + spb_length;

      if (p < end_spb && (*p != isc_spb_version1 && *p != isc_spb_version))
            ERR_post(isc_bad_spb_form, isc_arg_gds, isc_wrospbver, 0);

      while (p < end_spb)
            switch (*p++) {
            case isc_spb_version1:
                  options->spb_version = isc_spb_version1;
                  break;

            case isc_spb_version:
                  options->spb_version = *p++;
                  break;

            case isc_spb_sys_user_name:
                  options->spb_sys_user_name = get_string_parameter(&p, &scratch);
                  break;

            case isc_spb_user_name:
                  options->spb_user_name = get_string_parameter(&p, &scratch);
                  break;

            case isc_spb_password:
                  options->spb_password = get_string_parameter(&p, &scratch);
                  break;

            case isc_spb_password_enc:
                  options->spb_password_enc = get_string_parameter(&p, &scratch);
                  break;

            case isc_spb_command_line:
                  options->spb_command_line = get_string_parameter(&p, &scratch);
                  break;

            default:
                  l = *p++;
                  p += l;
            }
}


static TEXT *get_string_parameter(UCHAR ** spb_ptr, TEXT ** opt_ptr)
{
/**************************************
 *
 *    g e t _ s t r i n g _ p a r a m e t e r
 *
 **************************************
 *
 * Functional description
 *    Pick up a string valued parameter, copy it to a running temp,
 *    and return pointer to copied string.
 *
 **************************************/
      UCHAR *spb;
      TEXT *opt;
      USHORT l;

      opt = *opt_ptr;
      spb = *spb_ptr;

      if ( (l = *spb++) )
            do
                  *opt++ = *spb++;
            while (--l);

      *opt++ = 0;
      *spb_ptr = spb;
      spb = (UCHAR *) * opt_ptr;
      *opt_ptr = opt;

      return (TEXT *) spb;
}


#ifndef SUPERSERVER
static void io_error(
                               TEXT * string,
                               SLONG status,
                               TEXT * filename, ISC_STATUS operation, BOOLEAN reenter_flag)
{
/**************************************
 *
 *    i o _ e r r o r
 *
 **************************************
 *
 * Functional description
 *    Report an I/O error.  If the reenter_flag
 *    is TRUE, re-enter the scheduler.
 *
 **************************************/

#ifdef MULTI_THREAD
      if (reenter_flag)
            THREAD_ENTER;
#endif

      ERR_post(isc_io_error, isc_arg_string, string, isc_arg_string, filename,
                   isc_arg_gds, operation, SYS_ERR, status, 0);
}
#endif

#ifdef WIN_NT
#ifndef SUPERSERVER
static void service_close(SVC service)
{
/**************************************
 *
 *    s e r v i c e _ c l o s e           ( W I N _ N T )
 *
 **************************************
 *
 * Functional description
 *    Shutdown the connection to a service.
 *    Simply close the input and output pipes.
 *
 **************************************/
      CloseHandle((HANDLE) service->svc_input);
      CloseHandle((HANDLE) service->svc_output);
}


static void service_fork(TEXT * service_path, SVC service)
{
/**************************************
 *
 *    s e r v i c e _ f o r k       ( W I N _ N T )
 *
 **************************************
 *
 * Functional description
 *    Startup a service.
 *
 **************************************/
      TEXT *argv_data, argv_data_buf[512], *p, *q, *arg;
      USHORT len, quote_flag, user_quote;
      HANDLE my_input, my_output, pipe_input, pipe_output, pipe_error;
      SECURITY_ATTRIBUTES attr;
      STARTUPINFO start_crud;
      PROCESS_INFORMATION pi;
      USHORT ret;
      BOOLEAN svc_flag;
      SLONG status;
      BOOLEAN windows_nt = ISC_is_WinNT();

/* Only Create the pipes on Windows NT.  There is a bug on Windows
   95 that prohibits these handles from being converted by the
   child process
*/
      my_input = pipe_output = my_output = pipe_input = INVALID_HANDLE_VALUE;
      if (windows_nt) {
            /* Set up input and output pipes and make them the ib_stdin, ib_stdout,
               and ib_stderr of the forked process. */

            attr.nLength = sizeof(SECURITY_ATTRIBUTES);
            attr.bInheritHandle = TRUE;
            attr.lpSecurityDescriptor = NULL;

            if (!CreatePipe(&my_input, &pipe_output, &attr, 0) ||
                  !CreatePipe(&pipe_input, &my_output, &attr, 0) ||
                  !DuplicateHandle(GetCurrentProcess(), pipe_output,
                                           GetCurrentProcess(), &pipe_error, 0, TRUE,
                                           DUPLICATE_SAME_ACCESS)) {
                  status = GetLastError();
                  CloseHandle(my_input);
                  CloseHandle(pipe_output);
                  CloseHandle(my_output);
                  CloseHandle(pipe_input);
                  ERR_post(isc_sys_request, isc_arg_string,
                               (my_output !=
                                INVALID_HANDLE_VALUE) ? "CreatePipe" :
                               "DuplicateHandle", SYS_ERR, status, 0);
            }
      }
      else {
            /* Create a temporary file and get the OS handle to
               the file created.  This handle will be used in subsequent
               calls to the windows API functions for working with the files
             */
            int tmp;
            char *fname;
            char tmpPath[MAXPATHLEN];

            GetTempPath(MAXPATHLEN, tmpPath);
            fname = _tempnam(tmpPath, "ibsvc");
            tmp =
                  _open(fname, _O_RDWR | _O_CREAT | _O_TEMPORARY,
                          _S_IREAD | _S_IWRITE);
            my_input = (HANDLE) _get_osfhandle(tmp);

            fname = _tempnam(tmpPath, "ibsvc");
            tmp =
                  _open(fname, _O_RDWR | _O_CREAT | _O_TEMPORARY,
                          _S_IREAD | _S_IWRITE);
            my_output = (HANDLE) _get_osfhandle(tmp);

            if (my_input == INVALID_HANDLE_VALUE ||
                  my_output == INVALID_HANDLE_VALUE) {
                  CloseHandle(my_input);
                  CloseHandle(my_output);
                  ERR_post(isc_sys_request, isc_arg_string, "CreateFile", SYS_ERR,
                               errno, 0);
            }
      }

/* Make sure we have buffers that are large enough to hold the number
   and size of the command line arguments.  Add some extra space for
   the pipe's file handles. */

      len = strlen(service_path) + strlen(service->svc_switches) + 16;
      for (p = service->svc_switches; *p;)
            if (*p++ == ' ')
                  len += 2;
      if (len > sizeof(argv_data_buf))
            argv_data = (TEXT*) gds__alloc((SLONG) len);
      else
            argv_data = argv_data_buf;
/* FREE: at procedure return */
      if (!argv_data)                     /* NOMEM: */
            ERR_post(isc_virmemexh, 0);

/* Create a command line. */

      svc_flag = FALSE;

      for (p = argv_data, q = service_path; *p = *q++; p++);

      q = service->svc_switches;
      if (*q)
            *p++ = ' ';

      while (*q == ' ')
            q++;
      user_quote = FALSE;
      while (*q) {
            arg = p;
            *p++ = '\"';
            quote_flag = FALSE;
            while (((*p = *q++) && *p != ' ') || user_quote) {
                  if (*p == '\\' && *q == ' ' && !user_quote) {
                        *p = ' ';
                        q++;
                        quote_flag = TRUE;
                  }
                  if (*p == '"') {
                        user_quote = !user_quote;
                        p++;
                        continue;
                  }
                  p++;
            }
            if (!quote_flag) {
                  *arg = ' ';
                  if (!strncmp(arg, " -svc", p - arg)) {
                        if (windows_nt)
                              sprintf(p, "_re %d %d %d", pipe_input, pipe_output,
                                          pipe_error);
                        else
                              sprintf(p, "_re %d %d %d", my_output, my_input,
                                          my_output);
                        p += strlen(p);
                        *p = q[-1];
                        svc_flag = TRUE;
                  }
            }
            else {
                  p[1] = p[0];
                  *p++ = '\"';
            }
            if (!*p)
                  break;
            *p++ = ' ';
            while (*q == ' ')
                  q++;
      }

      for (q = argv_data; *q; q++) {
            if (*q == SVC_TRMNTR)
                  *q = ' ';
      }

      THREAD_EXIT;

      start_crud.cb = sizeof(STARTUPINFO);
      start_crud.lpReserved = NULL;
      start_crud.lpReserved2 = NULL;
      start_crud.cbReserved2 = 0;
      start_crud.lpDesktop = NULL;
      start_crud.lpTitle = NULL;
      start_crud.dwFlags = STARTF_USESHOWWINDOW;
      start_crud.wShowWindow = SW_HIDE;

      if (!(ret = CreateProcess(NULL,
                                            argv_data,
                                            NULL,
                                            NULL,
                                            TRUE,
                                            NORMAL_PRIORITY_CLASS,
                                            NULL,
                                            NULL,
                                            &start_crud, &pi))) status = GetLastError();

      if (windows_nt) {
            CloseHandle(pipe_input);
            CloseHandle(pipe_output);
            CloseHandle(pipe_error);
      }
      THREAD_ENTER;

      if (argv_data != argv_data_buf)
            gds__free((SLONG *) argv_data);

      if (!ret)
            ERR_post(isc_sys_request, isc_arg_string, "CreateProcess", SYS_ERR,
                         status, 0);

      DuplicateHandle(GetCurrentProcess(), pi.hProcess,
                              GetCurrentProcess(), (PHANDLE) &service->svc_handle,
                              0, TRUE, DUPLICATE_SAME_ACCESS);

      CloseHandle(pi.hThread);
      CloseHandle(pi.hProcess);

      service->svc_input = (void *) my_input;
      service->svc_output = (void *) my_output;
}


static void service_get(SVC service,
                                    SCHAR * buffer,
                                    USHORT length,
                                    USHORT flags,
                                    USHORT timeout,
                                    USHORT * return_length)
{
/**************************************
 *
 *      s e r v i c e _ g e t           ( W I N _ N T )
 *
 **************************************
 *
 * Functional description
 *      Get input from a service.  It is assumed
 *      that we have checked out of the scheduler.
 *
 **************************************/
      SHORT iter;
      SLONG n = 0L;
      SCHAR *buf = buffer;
      USHORT bytes_read;
/* Kludge for PeekNamedPipe to work on Win NT 3.5 */
      UCHAR temp_buf[600];
      SLONG temp_len;
      BOOLEAN windows_nt = ISC_is_WinNT();

      *return_length = 0;
      service->svc_flags &= ~SVC_timeout;
      IS_SERVICE_RUNNING(service)

            if (timeout) {
            /* If a timeout period was given, check every .1 seconds to see if
               input is available from the pipe.  When something shows up, read
               what's available until all data has been read, or timeout occurs.
               Otherwise, set the timeout flag and return.
               Fall out of the loop if a BROKEN_PIPE error occurs.
             */
            iter = timeout * 10;
            while ((iter--) && ((buf - buffer) < length)) {
                  /* PeekNamedPipe sometimes return wrong &n, need to get real
                     length from &temp_len until it works */
                  if (windows_nt
                        && !PeekNamedPipe((HANDLE) service->svc_input, temp_buf, 600,
                                                  (ULONG*) &temp_len, (ULONG*) &n, NULL)) {
                        if (GetLastError() == ERROR_BROKEN_PIPE) {
                              service->svc_flags |= SVC_finished;
                              break;
                        }
                        io_error("PeekNamedPipe", GetLastError(), "service pipe",
                                     isc_io_read_err, TRUE);
                  }
                  else {
                        DWORD dwCurrentFilePosition;
                        /* Set the file pointer to the beginning of the file if we are at the end of the
                           file. */
                        temp_len = GetFileSize(service->svc_input, NULL);
                        dwCurrentFilePosition =
                              SetFilePointer(service->svc_input, 0, NULL, FILE_CURRENT);

                        if (temp_len && temp_len == dwCurrentFilePosition)
                              SetFilePointer(service->svc_input, 0, NULL, FILE_BEGIN);
                  }

                  if (temp_len) {
                        /* If data is available, read as much as will fit in buffer */
                        temp_len = MIN(temp_len, length - (buf - buffer));
                        bytes_read =
                              service_read(service, buf, (USHORT) temp_len, flags);
                        buf += bytes_read;

                        if (bytes_read < temp_len
                              || service->svc_flags & SVC_finished) {
                              /* We stopped w/out reading full length, must have hit
                                 a newline or special character. */
                              break;
                        }
                  }
                  else {
                        /* PeekNamedPipe() is not returning ERROR_BROKEN_PIPE in WIN95. So,
                           we are going to use WaitForSingleObject() to check if the process
                           on the other end of the pipe is still active. */
                        if (!windows_nt &&
                              WaitForSingleObject((HANDLE) service->svc_handle,
                                                            1) != WAIT_TIMEOUT) {
                              service->svc_flags |= SVC_finished;
                              break;
                        }

                        /* No data, so wait a while */
                        Sleep(100);
                  }
            }
            /* If we timed out, set the appropriate flags */
            if (iter < 0 && !(service->svc_flags & SVC_finished))
                  service->svc_flags |= SVC_timeout;
      }
      else {
            buf += service_read(service, buf, length, flags);
      }

      *return_length = buf - buffer;
}


static void service_put(SVC service, SCHAR * buffer, USHORT length)
{
/**************************************
 *
 *    s e r v i c e _ p u t         ( W I N _ N T )
 *
 **************************************
 *
 * Functional description
 *    Send output to a service.  It is assumed
 *    that we have checked out of the scheduler.
 *
 **************************************/
      SLONG n;

      IS_SERVICE_RUNNING(service)

            while (length) {
            if (!WriteFile((HANDLE) service->svc_output, buffer, (SLONG) length,
                                 (ULONG*) &n, NULL))
                  io_error("WriteFile", GetLastError(), "service pipe",
                               isc_io_write_err, TRUE);
            length -= (USHORT) n;
            buffer += n;
      }

      if (!FlushFileBuffers((HANDLE) service->svc_output))
            io_error("FlushFileBuffers", GetLastError(), "service pipe",
                         isc_io_write_err, TRUE);
}


static USHORT service_read(SVC service, SCHAR * buffer, USHORT length, USHORT flags)
{
/**************************************
 *
 *      s e r v i c e _ r e a d         ( W I N _ N T )
 *
 **************************************
 *
 * Functional description
 *      Read data from the named pipe.
 *      Returns true if there is more data to be
 *      read, false if we found the newline or
 *      special character.
 *
 **************************************/
      SLONG len, n = 0;
      SCHAR *buf = buffer;

      while (length) {
            n = 0;
            len = (flags & GET_BINARY) ? length : 1;
            if (ReadFile((HANDLE) service->svc_input, buf, len, (ULONG*) &n, NULL) ||
                  GetLastError() == ERROR_BROKEN_PIPE) {
                  if (!n) {
                        service->svc_flags |= SVC_finished;
                        break;
                  }
                  length -= (USHORT) n;
                  buf += n;
                  if (((flags & GET_LINE) && buf[-1] == '\n') ||
                        (!(flags & GET_BINARY) && buf[-1] == '\001'))
                        break;
            }
            else
                  io_error("ReadFile", GetLastError(), "service pipe",
                               isc_io_read_err, TRUE);
      }

      if ((flags & GET_LINE) && buf[-1] == '\n')
            buf[-1] = ' ';

      return buf - buffer;
}
#endif /* ifndef SUPERSERVER */
#endif /* WIN_NT */


#ifdef SUPERSERVER
static USHORT service_add_one(USHORT i)
{
/**************************************
 *
 *    s e r v i c e _ a d d _ o n e
 *
 **************************************
 *
 * Functional description
 *
 **************************************/
      return ((i % SVC_STDOUT_BUFFER_SIZE) + 1);
}


static USHORT service_empty(SVC service)
{
/**************************************
 *
 *    s e r v i c e _ e m p t y
 *
 **************************************
 *
 * Functional description
 *
 **************************************/
      if (service_add_one(service->svc_stdout_tail) == service->svc_stdout_head)
            return 1;

      return 0;
}


static USHORT service_full(SVC service)
{
/**************************************
 *
 *    s e r v i c e _ f u l l
 *
 **************************************
 *
 * Functional description
 *
 **************************************/
      if (service_add_one(service_add_one(service->svc_stdout_tail)) ==
            service->svc_stdout_head) return (1);
      else
            return (0);
}


static UCHAR service_dequeue_byte(SVC service)
{
/**************************************
 *
 *    s e r v i c e _ d e q u e u e _ b y t e
 *
 **************************************
 *
 * Functional description
 *
 **************************************/
      UCHAR ch;

      ch = service->svc_stdout[service->svc_stdout_head];
      service->svc_stdout_head = service_add_one(service->svc_stdout_head);

      return (ch);
}


static void service_enqueue_byte(UCHAR ch, SVC service)
{
/**************************************
 *
 *    s e r v i c e _ e n q u e u e _ b y t e
 *
 **************************************
 *
 * Functional description
 *
 **************************************/
/* Wait for space in buffer while service is not detached. */
      while (service_full(service) && !(service->svc_flags & SVC_detached))
            THREAD_SLEEP(1);

/* Ensure that service is not detached. */
      if (!(service->svc_flags & SVC_detached)) {
            service->svc_stdout_tail = service_add_one(service->svc_stdout_tail);
            service->svc_stdout[service->svc_stdout_tail] = ch;
      }
}


static void service_fork(void (*service_executable) (), SVC service)
{
/**************************************
 *
 *    s e r v i c e _ f o r k
 *
 **************************************
 *
 * Functional description
 *    Startup a service.
 *
 **************************************/
      USHORT argc;
      TEXT **arg, *p, *q;

      for (argc = 2, p = service->svc_switches; *p;)
            if (*p++ == ' ')
                  argc++;

      service->svc_argc = argc;

      arg = (TEXT **) gds__alloc((SLONG) ((argc + 1) * sizeof(TEXT *)));
      service->svc_argv = arg;
/* FREE: at SVC_detach() - Possible memory leak if ERR_post() occurs */
      if (!arg)                           /* NOMEM: */
            ERR_post(isc_virmemexh, 0);

      *arg++ = (TEXT *)(service_executable);

/* Break up the command line into individual arguments. */

      p = q = service->svc_switches;

      while (*q == ' ')
            q++;
      while (*q) {
            *arg++ = p;
            while ((*p = *q++) && *p != ' ') {
                  if (*p == '\\' && *q == ' ') {
                        *p = ' ';
                        q++;
                  }
                  p++;
            }
            if (!*p)
                  break;
            *p++ = 0;
            while (*q == ' ')
                  q++;
      }
      *arg = NULL;

      service->svc_stdout = (UCHAR*)gds__alloc((SLONG) SVC_STDOUT_BUFFER_SIZE + 1);
/* FREE: at SVC_detach() */
      if (!service->svc_stdout)     /* NOMEM: */
            ERR_post(isc_virmemexh, 0);

      THREAD_EXIT;
      gds__thread_start(reinterpret_cast<FPTR_INT_VOID_PTR>(service_executable),
                                    service,
                                    0,
                                    0,
                                    (void*)&service->svc_handle);
      THREAD_ENTER;
}


static void service_get(SVC         service,
                                    SCHAR*      buffer,
                                    USHORT      length,
                                    USHORT      flags,
                                    USHORT      timeout,
                                    USHORT*     return_length)
{
/**************************************
 *
 *    s e r v i c e _ g e t
 *
 **************************************
 *
 * Functional description
 *
 **************************************/
      int ch = 'Z';
      ULONG elapsed_time;
#ifdef HAVE_GETTIMEOFDAY
      struct timeval start_time, end_time;
#else
      time_t start_time, end_time;
#endif

#ifdef HAVE_GETTIMEOFDAY
#ifdef GETTIMEOFDAY_RETURNS_TIMEZONE
      (void)gettimeofday(&start_time, (struct timezone *)0);
#else
      (void)gettimeofday(&start_time);
#endif
#else
      time(&start_time);
#endif
      *return_length = 0;
      service->svc_flags &= ~SVC_timeout;

      while (length) {
            if (service_empty(service))
                  THREAD_SLEEP(1);
#ifdef HAVE_GETTIMEOFDAY
#ifdef GETTIMEOFDAY_RETURNS_TIMEZONE
            (void)gettimeofday(&end_time, (struct timezone *)0);
#else
            (void)gettimeofday(&end_time);
#endif
            elapsed_time = end_time.tv_sec - start_time.tv_sec;
#else
            time(&end_time);
            elapsed_time = end_time - start_time;
#endif
            if ((timeout) && (elapsed_time >= timeout)) {
                  service->svc_flags &= SVC_timeout;
                  return;
            }

            while (!service_empty(service) && length > 0) {
                  ch = service_dequeue_byte(service);
                  length--;

                  /* If returning a line of information, replace all new line
                   * characters with a space.  This will ensure that the output is
                   * consistent when returning a line or to eof
                   */
                  if ((flags & GET_LINE) && (ch == '\n')) {
                        buffer[(*return_length)++] = ' ';
                        return;
                  }
                  else
                        buffer[(*return_length)++] = ch;
            }

            if (service_empty(service) && (service->svc_flags & SVC_finished))
                  return;
      }
}


static void service_put(SVC service, SCHAR * buffer, USHORT length)
{
/**************************************
 *
 *    s e r v i c e _ p u t
 *
 **************************************
 *
 * Functional description
 *    Send output to a service.  It is assumed
 *    that we have checked out of the scheduler.
 *
 **************************************/

/* Nothing */
}
#endif /* SUPERSERVER */


#if !defined(WIN_NT) && !defined(SUPERSERVER)
static void service_close(SVC service)
{
/**************************************
 *
 *    s e r v i c e _ c l o s e           ( G E N E R I C )
 *
 **************************************
 *
 * Functional description
 *    Shutdown the connection to a service.
 *    Simply close the input and output pipes.
 *
 **************************************/

      ib_fclose((IB_FILE *) service->svc_input);
      ib_fclose((IB_FILE *) service->svc_output);
}


static void service_fork(TEXT * service_path, SVC service)
{
/**************************************
 *
 *    s e r v i c e _ f o r k       ( G E N E R I C )
 *
 **************************************
 *
 * Functional description
 *    Startup a service.
 *
 **************************************/
      int pair1[2], pair2[2], pid;
      struct stat stat_buf;
      TEXT **argv, *argv_buf[20], **arg, *argv_data, argv_data_buf[512], *p, *q;
      USHORT argc, len;

      if (pipe(pair1) < 0 || pipe(pair2) < 0)
            io_error("pipe", errno, "", isc_io_create_err, FALSE);

/* Probe service executable to see it if plausibly exists. */

      if (statistics(service_path, &stat_buf) == -1)
            io_error("stat", errno, service_path, isc_io_access_err, FALSE);

/* Make sure we have buffers that are large enough to hold the number
   and size of the command line arguments. */

      for (argc = 2, p = service->svc_switches; *p;)
      {
            if (*p == ' ')
            {
                  argc++;
                  while (*p == ' ')
                        p++;
            }
            else
            {
                  if (*p == SVC_TRMNTR)
                  {
                        while (*p++ && *p != SVC_TRMNTR);
                        assert (*p == SVC_TRMNTR);
                  }
                  p++;
            }
      }

      if (argc > FB_NELEM(argv_buf))
            argv = (TEXT **) gds__alloc((SLONG) (argc * sizeof(TEXT *)));
      else
            argv = argv_buf;
/* FREE: at procedure return */
      if (!argv)                          /* NOMEM: */
            ERR_post(isc_virmemexh, 0);
      service->svc_argc = argc;

      len = strlen(service->svc_switches) + 1;
      if (len > sizeof(argv_data_buf))
            argv_data = (TEXT *) gds__alloc((SLONG) len);
      else
            argv_data = argv_data_buf;
/* FREE: at procedure return */
      if (!argv_data) {             /* NOMEM: */
            if (argv != argv_buf)
                  gds__free(argv);
            ERR_post(isc_virmemexh, 0);
      }

/* Break up the command line into individual arguments. */

      arg = argv;
      *arg++ = service_path;

      p = argv_data;
      q = service->svc_switches;

      while (*q == ' ')
            q++;
      while (*q)
      {
            *arg = p;
            while (*p = *q++)
            {
                  if (*p == ' ') break;

                  if (*p == SVC_TRMNTR)
                  {
                        *arg = ++p; /* skip terminator */
                        while (*p = *q++)
                                    /* If *q points to the last argument, then terminate the argument */
                              if ((*q == 0 || *q == ' ') && *p == SVC_TRMNTR)
                              {
                                    *p = '\0';
                                    break;
                              }
                              else
                                    p++;
                  }

                  if (*p == '\\' && *q == ' ')
                  {
                        *p = ' ';
                        q++;
                  }
                  p++;
            }
            arg++;
            if (!*p)
                  break;
            *p++ = '\0';
            while (*q == ' ')
                  q++;
      }
      *arg = NULL;

/* At last we can fork the sub-process.  If the fork succeeds, repeat
   it so that we don't have defunct processes hanging around. */

      THREAD_EXIT;

      switch (pid = vfork()) {
      case -1:
            THREAD_ENTER;
            if (argv != argv_buf)
                  gds__free(argv);
            if (argv_data != argv_data_buf)
                  gds__free(argv_data);
            ERR_post(isc_sys_request, isc_arg_string, "vfork", SYS_ERR, errno, 0);
            break;

      case 0:
            if (vfork() != 0)
                  _exit(FINI_OK);

            close(pair1[0]);
            close(pair2[1]);
            if (pair2[0] != 0) {
                  close(0);
                  dup(pair2[0]);
                  close(pair2[0]);
            }
            if (pair1[1] != 1) {
                  close(1);
                  dup(pair1[1]);
                  close(pair1[1]);
            }
            close(2);
            dup(1);
#ifdef DEV_BUILD
            {
                  char buf[2 * MAXPATHLEN];
                  char **s = argv;

                  strcpy (buf, "service_fork:");
                  while (*s != (char *)0)
                  {
                        strcat (buf, " ");
                        strcat (buf, *s);
                        s++;
                  }
                  gds__log(buf);
            }
#endif
            execvp(argv[0], argv);
            _exit(FINI_ERROR);
      }

      close(pair1[1]);
      close(pair2[0]);

      waitpid(pid, NULL, 0);

      THREAD_ENTER;

      if (argv != argv_buf)
            gds__free(argv);
      if (argv_data != argv_data_buf)
            gds__free(argv_data);

      if (!(service->svc_input = (void *) ib_fdopen(pair1[0], "r")) ||
            !(service->svc_output = (void *) ib_fdopen(pair2[1], "w")))
            io_error("ib_fdopen", errno, "service path", isc_io_access_err,
                         FALSE);
}


static void service_get(
                                    SVC service,
                                    SCHAR * buffer,
                                    USHORT length,
                                    USHORT flags, USHORT timeout, USHORT * return_length)
{
/**************************************
 *
 *    s e r v i c e _ g e t         ( G E N E R I C )
 *
 **************************************
 *
 * Functional description
 *    Get input from a service.  It is assumed
 *    that we have checked out of the scheduler.
 *
 **************************************/
#ifdef SYSV_SIGNALS
      SLONG sv_timr;
      void *sv_hndlr;
#else
      struct itimerval sv_timr;
#ifndef HAVE_SIGACTION
      struct sigvec sv_hndlr;
#else
      struct sigaction sv_hndlr;
#endif
#endif
      int c;
      //USHORT timed_out;
      SCHAR *buf;
      SSHORT iter = 0;
      int errno_save;

      IS_SERVICE_RUNNING(service)

            errno = 0;
      service->svc_flags &= ~SVC_timeout;
      buf = buffer;

      if (timeout) {
            ISC_set_timer((SLONG) (timeout * 100000), (void(*)())timeout_handler, service,
                                (SLONG*)&sv_timr, (void**)&sv_hndlr);
            iter = timeout * 10;
      }

      while (!timeout || iter) {
            if ((c = ib_getc((IB_FILE *) service->svc_input)) != EOF) {
                  *buf++ = c;
                  if (!(--length))
                        break;
                  if (((flags & GET_LINE) && c == '\n') ||
                        (!(flags & GET_BINARY) && c == '\001'))
                        break;
            }
            else if (!errno) {
                  service->svc_flags |= SVC_finished;
                  break;
            }
            else if (SYSCALL_INTERRUPTED(errno)) {
                  if (timeout)
                        --iter;
            }
            else {
                  errno_save = errno;
                  if (timeout)
                        ISC_reset_timer((void(*)())timeout_handler, service, (SLONG*)&sv_timr,
                                                (void**)&sv_hndlr);
                  io_error("ib_getc", errno_save, "service pipe", isc_io_read_err,
                               TRUE);
            }
      }

      if (timeout) {
            ISC_reset_timer((void(*)())timeout_handler, service, (SLONG*)&sv_timr, (void**)&sv_hndlr);
            if (!iter)
                  service->svc_flags |= SVC_timeout;
      }

      *return_length = buf - buffer;
}


static void service_put(SVC service, SCHAR * buffer, USHORT length)
{
/**************************************
 *
 *    s e r v i c e _ p u t         ( G E N E R I C )
 *
 **************************************
 *
 * Functional description
 *    Send output to a service.  It is assumed
 *    that we have checked out of the scheduler.
 *
 **************************************/

      IS_SERVICE_RUNNING(service)

            while (length--) {
            if (ib_putc(*buffer, (IB_FILE *) service->svc_output) != EOF)
                  buffer++;
            else if (SYSCALL_INTERRUPTED(errno)) {
                  ib_rewind((IB_FILE *) service->svc_output);
                  length++;
            }
            else
                  io_error("ib_putc", errno, "service pipe", isc_io_write_err,
                               TRUE);
      }

      if (ib_fflush((IB_FILE *) service->svc_output) == EOF)
            io_error("ib_fflush", errno, "service pipe", isc_io_write_err, TRUE);
}


static void timeout_handler(SVC service)
{
/**************************************
 *
 *    t i m e o u t _ h a n d l e r
 *
 **************************************
 *
 * Functional description
 *    Called when no input has been received
 *    through a pipe for a specificed period
 *    of time.
 *
 **************************************/
}
#endif // !defined(WIN_NT) && !defined(SUPERSERVER)


void SVC_cleanup(SVC service)
{
/**************************************
 *
 *    S V C _ c l e a n u p
 *
 **************************************
 *
 * Functional description
 *    Cleanup memory used by service.
 *
 **************************************/

#ifndef SUPERSERVER
/* If we forked an executable, close down it's pipes */

      if (service->svc_flags & SVC_forked)
            service_close(service);
#else

      if (service->svc_stdout) {
            gds__free((SLONG *) service->svc_stdout);
            service->svc_stdout = NULL;
      }
      if (service->svc_argv) {
            gds__free((SLONG *) service->svc_argv);
            service->svc_argv = NULL;
      }
#endif

      if (service->svc_resp_buf)
            gds__free((SLONG *) service->svc_resp_buf);

      if (service->svc_switches != NULL)
            gds__free((SLONG *) service->svc_switches);

      if (service->svc_status != NULL)
            gds__free((SLONG *) service->svc_status);

#ifdef WIN_NT
      CloseHandle((HANDLE) service->svc_handle);
#endif

//    gds__free((SLONG *) service);
      delete service;
}


void SVC_finish(SVC service, USHORT flag)
{
/**************************************
 *
 *    S V C _ f i n i s h
 *
 **************************************
 *
 * Functional description
 *    o Set the flag (either SVC_finished for the main service thread or
 *      SVC_detached for the client thread).
 *    o If both main thread and client thread are completed that is main
 *      thread is finished and client is detached then free memory
 *      used by service.
 *
 **************************************/

      if (!svc_initialized)
      {
            THD_MUTEX_INIT(svc_mutex);
            svc_initialized = TRUE;
      }

      THD_MUTEX_LOCK(svc_mutex);
      if (service && ((flag == SVC_finished) || (flag == SVC_detached)))
      {
            service->svc_flags |= flag;
            if ((service->svc_flags & SVC_finished) &&
                  (service->svc_flags & SVC_detached))
            {
                  SVC_cleanup(service);
            }
            else if (service->svc_flags & SVC_finished)
            {
                  if (service->svc_service && service->svc_service->in_use)
                  {
                        *(service->svc_service->in_use) = FALSE;
                  }

                  service->svc_flags &= ~SVC_thd_running;

#ifdef WIN_NT
                  CloseHandle((HANDLE) service->svc_handle);
#endif
                  service->svc_handle = 0;
            }
      }
      THD_MUTEX_UNLOCK(svc_mutex);
}


static void conv_switches(
                                      USHORT spb_length,
                                      USHORT opt_switch_len,
                                      SCHAR * spb, TEXT ** switches)
{
/**************************************
 *
 *    c o n v _ s w i t c h e s
 *
 **************************************
 *
 * Functional description
 *    Convert spb flags to utility switches.
 *
 **************************************/
      USHORT total;
      TEXT *p;

      p = spb;

      if (*p < isc_action_min || *p > isc_action_max)
            return;                             /* error action not defined */

/* Calculate the total length */
      if ((total = process_switches(spb_length, p, (TEXT *) NULL)) == 0)
            return;

      *switches =
            (TEXT *) gds__alloc(total + opt_switch_len +
                                          sizeof(SERVICE_THD_PARAM) + 1);
/* NOMEM: return error */
      if (!*switches)
            ERR_post(isc_virmemexh, 0);

      strcpy(*switches, SERVICE_THD_PARAM);
      strcat(*switches, " ");
      process_switches(spb_length, p,
                               *switches + strlen(SERVICE_THD_PARAM) + 1);
      return;
}


static TEXT *find_switch(int in_spb_sw, IN_SW_TAB table)
{
/**************************************
 *
 *      f i n d _ s w i t c h
 *
 **************************************
 *
 * Functional description
 *
 **************************************/
      IN_SW_TAB in_sw_tab;

      for (in_sw_tab = table; in_sw_tab->in_sw_name; in_sw_tab++) {
            if (in_spb_sw == in_sw_tab->in_spb_sw)
                  return in_sw_tab->in_sw_name;
      }

      return ((TEXT *) NULL);
}


static USHORT process_switches(
                                             USHORT spb_length,
                                             SCHAR * spb, TEXT * switches)
{
/**************************************
 *
 *      p r o c e s s _ s w i t c h e s
 *
 **************************************
 *
 *Functional description
 *   Loop through the appropriate switch table
 *   looking for the text for the given command switch.
 *
 *   Calling this function with switches = NULL returns
 *   the number of bytes to allocate for the switches and
 *   parameters.
 *
 **************************************/
      USHORT len, total;
      TEXT *p, *sw;
      ISC_USHORT svc_action;
      BOOLEAN found = FALSE;

      if (spb_length == 0)
            return 0;

      p = spb;
      len = spb_length;
      sw = switches;

      svc_action = *p;

      if (len > 1) {
            ++p;
            --len;
      }

      total = 0;                          /* total length of the command line */

      while (len > 0) {
            switch (svc_action) {
            case isc_action_svc_delete_user:
            case isc_action_svc_display_user:
                  if (!found) {
                        if (spb != p) {
                              --p;
                              ++len;
                        }
                        assert(spb == p);
                        if (!get_action_svc_parameter(&p, gsec_action_in_sw_table,
                                                                    &sw, &total, &len))
                              return 0;
                        else {
                              found = TRUE;
                              /* in case of "display all users" the spb buffer contains
                                 nothing but isc_action_svc_display_user */
                              if (len == 0)
                                    break;
                        }
                  }

                  switch (*p) {
                  case isc_spb_sec_username:
                        ++p;
                        --len;
                        get_action_svc_string(&p, &sw, &total, &len);
                        break;

                  default:
                        return 0;
                        break;
                  }
                  break;

            case isc_action_svc_add_user:
            case isc_action_svc_modify_user:
                  if (!found) {
                        if (spb != p) {
                              --p;
                              ++len;
                        }
                        assert(spb == p);
                        if (!get_action_svc_parameter(&p, gsec_action_in_sw_table,
                                                                    &sw, &total, &len))
                              return 0;
                        else {
                              found = TRUE;
                              if (*p != isc_spb_sec_username) {
                                    /* unexpected service parameter block:
                                       expected %d, encountered %d */
                                    ERR_post(isc_unexp_spb_form, isc_arg_string,
                                                 SVC_err_string(SPB_SEC_USERNAME,
                                                                        strlen(SPB_SEC_USERNAME)), 0);
                              }
                        }
                  }

                  switch (*p) {
                  case isc_spb_sec_userid:
                  case isc_spb_sec_groupid:
                        if (!get_action_svc_parameter(&p, gsec_in_sw_table,
                                                                    &sw, &total, &len))
                              return 0;
                        get_action_svc_data(&p, &sw, &total, &len);
                        break;

                  case isc_spb_sec_username:
                        ++p;
                        --len;
                        get_action_svc_string(&p, &sw, &total, &len);
                        break;
                  case isc_spb_sql_role_name:
                  case isc_spb_sec_password:
                  case isc_spb_sec_groupname:
                  case isc_spb_sec_firstname:
                  case isc_spb_sec_middlename:
                  case isc_spb_sec_lastname:
                        if (!get_action_svc_parameter(&p, gsec_in_sw_table,
                                                                    &sw, &total, &len))
                              return 0;
                        get_action_svc_string(&p, &sw, &total, &len);
                        break;

                  default:
                        return 0;
                        break;
                  }
                  break;

            case isc_action_svc_db_stats:
                  switch (*p) {
                  case isc_spb_dbname:
                        ++p;
                        --len;
                        get_action_svc_string(&p, &sw, &total, &len);
                        break;

                  case isc_spb_options:
                        ++p;
                        --len;
                        if (!get_action_svc_bitmask(&p, dba_in_sw_table,
                                                                  &sw, &total, &len)) return 0;
                        break;

                  case isc_spb_command_line: 
                        ++p;
                        --len;
                        get_action_svc_string(&p, &sw, &total, &len);
                        break;

                  default:
                        return 0;
                        break;
                  }
                  break;

            case isc_action_svc_backup:
            case isc_action_svc_restore:
                  switch (*p) {
                  case isc_spb_bkp_file:
                  case isc_spb_dbname:
                  case isc_spb_sql_role_name:
                        ++p;
                        --len;
                        get_action_svc_string(&p, &sw, &total, &len);
                        break;
                  case isc_spb_options:
                        ++p;
                        --len;
                        if (!get_action_svc_bitmask(&p, burp_in_sw_table,
                                                                  &sw, &total, &len)) return 0;
                        break;
                  case isc_spb_bkp_length:
                  case isc_spb_res_length:
                        ++p;
                        --len;
                        get_action_svc_data(&p, &sw, &total, &len);
                        break;
                  case isc_spb_bkp_factor:
                  case isc_spb_res_buffers:
                  case isc_spb_res_page_size:
                        if (!get_action_svc_parameter(&p, burp_in_sw_table,
                                                                    &sw, &total, &len))
                              return 0;
                        get_action_svc_data(&p, &sw, &total, &len);
                        break;
                  case isc_spb_res_access_mode:
                        ++p;
                        --len;
                        if (!get_action_svc_parameter(&p, burp_in_sw_table,
                                                                    &sw, &total, &len))
                              return 0;
                        break;
                  case isc_spb_verbose:
                        if (!get_action_svc_parameter(&p, burp_in_sw_table,
                                                                    &sw, &total, &len))
                              return 0;
                        break;
                  default:
                        return 0;
                        break;
                  }
                  break;

            case isc_action_svc_repair:
            case isc_action_svc_properties:
                  switch (*p) {
                  case isc_spb_dbname:
                        ++p;
                        --len;
                        get_action_svc_string(&p, &sw, &total, &len);
                        break;
                  case isc_spb_options:
                        ++p;
                        --len;
                        if (!get_action_svc_bitmask(&p, alice_in_sw_table,
                                                                  &sw, &total, &len))
                              return 0;
                        break;
                  case isc_spb_prp_page_buffers:
                  case isc_spb_prp_sweep_interval:
                  case isc_spb_prp_shutdown_db:
                  case isc_spb_prp_deny_new_attachments:
                  case isc_spb_prp_deny_new_transactions:
                  case isc_spb_prp_set_sql_dialect:
                  case isc_spb_rpr_commit_trans:
                  case isc_spb_rpr_rollback_trans:
                  case isc_spb_rpr_recover_two_phase:
                        if (!get_action_svc_parameter(&p, alice_in_sw_table,
                                                                    &sw, &total, &len))
                              return 0;
                        get_action_svc_data(&p, &sw, &total, &len);
                        break;
                  case isc_spb_prp_write_mode:
                  case isc_spb_prp_access_mode:
                  case isc_spb_prp_reserve_space:
                        ++p;
                        --len;
                        if (!get_action_svc_parameter(&p, alice_in_sw_table,
                                                                    &sw, &total, &len))
                              return 0;
                        break;
                  default:
                        return 0;
                        break;
                  }
                  break;
            default:
                  return 0;
                  break;
            }
      }

      if (sw && *(sw - 1) == ' ')
            *--sw = '\0';

      return total;
}


static BOOLEAN get_action_svc_bitmask(
                                                        TEXT ** spb,
                                                        IN_SW_TAB table,
                                                        TEXT ** cmd,
                                                        USHORT * total, USHORT * len)
{
/**************************************
 *
 *    g e t _ a c t i o n _ s v c _ b i t m a s k
 *
 **************************************
 *
 * Functional description
 *    Get bitmask from within spb buffer,
 *    find corresponding switches within specified table,
 *    add them to the command line,
 *    adjust pointers.
 *
 **************************************/
      ISC_ULONG opt, mask;
      TEXT *s_ptr;
      ISC_USHORT count;

      opt =
            gds__vax_integer(reinterpret_cast < UCHAR * >(*spb),
                                     sizeof(ISC_ULONG));
      for (mask = 1, count = (sizeof(ISC_ULONG) * 8) - 1; count; --count) {
            if (opt & mask) {
                  if (!(s_ptr = find_switch((opt & mask), table)))
                        return FALSE;
                  else {
                        if (*cmd) {
                              sprintf(*cmd, "-%s ", s_ptr);
                              *cmd += 1 + strlen(s_ptr) + 1;
                        }
                        *total += 1 + strlen(s_ptr) + 1;
                  }
            }
            mask = mask << 1;
      }

      *spb += sizeof(ISC_ULONG);
      *len -= sizeof(ISC_ULONG);
      return TRUE;
}


static void get_action_svc_string(
                                                  TEXT ** spb,
                                                  TEXT ** cmd, USHORT * total, USHORT * len)
{
/**************************************
 *
 *      g e t _ a c t i o n _ s v c _ s t r i n g
 *
 **************************************
 *
 * Functional description
 *      Get string from within spb buffer,
 *      add it to the command line, adjust pointers.
 *
 *      All string parameters are delimited by SVC_TRMNTR.  This
 *      is done to ensure that paths with spaces are handled correctly
 *      when creating the argc / argv paramters for the service.
 *
 **************************************/
      ISC_USHORT l, l2;

      l = gds__vax_integer(reinterpret_cast < UCHAR * >(*spb),
                                     sizeof(ISC_USHORT));

      const char* local_server = "localhost:";
#if defined(WIN_NT) && !defined(SUPERSERVER)
      // dimitr: temporary hack to make Services API available
      // for the engine without support of local protocol.
      // Once XNET is implemented in win32 CS, remove this ugly code.
      TEXT * spb_item = *spb - 1;
      l2 = (*spb_item == isc_spb_dbname) ? strlen(local_server) : 0;
#else
      l2 = 0;
#endif

/* Do not go beyond the bounds of the spb buffer */
      if (l > *len)
            ERR_post(isc_bad_spb_form, 0);

      *spb += sizeof(ISC_USHORT);
      if (*cmd)
      {
            **cmd = SVC_TRMNTR;
            *cmd += 1;
            MOVE_FASTER(local_server, *cmd, l2);
            *cmd += l2;
            MOVE_FASTER(*spb, *cmd, l);
            *cmd += l;
            **cmd = SVC_TRMNTR;
            *cmd += 1;
            **cmd = ' ';
            *cmd += 1;
      }
      *spb += l;
      *total += l + l2 + 1 + 2;           /* Two SVC_TRMNTR for strings */
      *len -= sizeof(ISC_USHORT) + l;
}


static void get_action_svc_data(
                                                TEXT ** spb,
                                                TEXT ** cmd, USHORT * total, USHORT * len)
{
/**************************************
 *
 *      g e t _ a c t i o n _ s v c _ d a t a
 *
 **************************************
 *
 * Functional description
 *      Get data from within spb buffer,
 *      add it to the command line, adjust pointers.
 *
 **************************************/
      ISC_ULONG ll;
      TEXT buf[64];

      ll =
            gds__vax_integer(reinterpret_cast < UCHAR * >(*spb),
                                     sizeof(ISC_ULONG));
      sprintf(buf, "%lu ", ll);
      if (*cmd) {
            sprintf(*cmd, "%lu ", ll);
            *cmd += strlen(buf);
      }
      *spb += sizeof(ISC_ULONG);
      *total += strlen(buf) + 1;
      *len -= sizeof(ISC_ULONG);
}


static BOOLEAN get_action_svc_parameter(
                                                            TEXT ** spb,
                                                            IN_SW_TAB table,
                                                            TEXT ** cmd,
                                                            USHORT * total, USHORT * len)
{
/**************************************
 *
 *      g e t _ a c t i o n _ s v c _ p a r a m e t e r
 *
 **************************************
 *
 * Functional description
 *      Get parameter from within spb buffer,
 *      find corresponding switch within specified table,
 *    add it to the command line,
 *    adjust pointers.
 *
 **************************************/
      TEXT *s_ptr;

      if (!(s_ptr = find_switch(**spb, table)))
            return FALSE;

      if (*cmd) {
            sprintf(*cmd, "-%s ", s_ptr);
            *cmd += 1 + strlen(s_ptr) + 1;
      }

      *spb += 1;
      *total += 1 + strlen(s_ptr) + 1;
      *len -= 1;
      return TRUE;
}

#ifdef DEBUG
/* The following two functions are temporary stubs and will be
 * removed as the services API takes shape.  They are used to
 * test that the paths for starting services and parsing command-lines
 * are followed correctly.
 */
void test_thread(SVC service)
{
      gds__log("Starting service");
}

void test_cmd(USHORT spb_length, SCHAR * spb, TEXT ** switches)
{
      gds__log("test_cmd called");
}
#endif

Generated by  Doxygen 1.6.0   Back to index