8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-24 23:23:03 +01:00
firebird-mirror/src/jrd/svc.cpp

4021 lines
98 KiB
C++

/*
* 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/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/y_ref.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++ = (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++ = (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 **);
static void io_error(TEXT *, SLONG, TEXT *, STATUS, BOOLEAN);
static void service_close(SVC);
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);
static void timeout_handler(SVC);
#ifdef WIN_NT
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
#ifdef WIN_NT
static SLONG SVC_cache_default;
static SLONG SVC_priority_class;
static SLONG SVC_client_map;
static SLONG SVC_working_set_min;
static SLONG SVC_working_set_max;
static struct ipccfg SVC_hdrtbl[] = {
ISCCFG_DBCACHE, 0, &SVC_cache_default, 0, 0,
ISCCFG_PRIORITY, 0, &SVC_priority_class, 0, 0,
ISCCFG_IPCMAP, 0, &SVC_client_map, 0, 0,
ISCCFG_MEMMIN, 0, &SVC_working_set_min, 0, 0,
ISCCFG_MEMMAX, 0, &SVC_working_set_max, 0, 0,
NULL, 0, NULL, 0, 0
};
#else
static SLONG SVC_conn_timeout;
static SLONG SVC_dbcache;
static SLONG SVC_deadlock;
static SLONG SVC_dummy_intrvl;
static SLONG SVC_lockspin;
static SLONG SVC_lockhash;
static SLONG SVC_evntmem;
static SLONG SVC_lockorder;
static SLONG SVC_anylockmem;
static SLONG SVC_locksem;
static SLONG SVC_locksig;
static struct ipccfg SVC_hdrtbl[] = {
{ ISCCFG_CONN_TIMEOUT, 0, &SVC_conn_timeout, 0, 0 },
{ ISCCFG_DBCACHE, 0, &SVC_dbcache, 0, 0 },
{ ISCCFG_DEADLOCK, 0, &SVC_deadlock, 0, 0 },
{ ISCCFG_DUMMY_INTRVL, 0, &SVC_dummy_intrvl, 0, 0 },
{ ISCCFG_LOCKSPIN, 0, &SVC_lockspin, 0, 0 },
{ ISCCFG_LOCKHASH, 0, &SVC_lockhash, 0, 0 },
{ ISCCFG_EVNTMEM, 0, &SVC_evntmem, 0, 0 },
{ ISCCFG_LOCKORDER, 0, &SVC_lockorder, 0, 0 },
{ ISCCFG_ANYLOCKMEM, 0, &SVC_anylockmem, 0, 0 },
{ ISCCFG_ANYLOCKSEM, 0, &SVC_locksem, 0, 0 },
{ ISCCFG_ANYLOCKSIG, 0, &SVC_locksig, 0, 0 },
{ NULL, 0, NULL, 0, 0 }
};
#endif /* WIN_NT */
#define SPB_SEC_USERNAME "isc_spb_sec_username"
#define SPB_LIC_KEY "isc_spb_lic_key"
#define SPB_LIC_ID "isc_spb_lic_id"
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(STATUS*& status, USHORT type, const void* value)
{
if (value)
{
switch (type)
{
case isc_arg_number:
*status++ = type;
*status++ = reinterpret_cast<STATUS>(value);
break;
case isc_arg_string:
*status++ = type;
*status++ = (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[] =
{
#if !(defined LINUX || defined FREEBSD || defined NETBSD)
#ifdef WIN_NT
{ 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 },
#else
{ isc_action_max, "print_cache", "-svc", "bin/gds_cache_print", NULL, 0 },
{ isc_action_max, "print_locks", "-svc", "bin/gds_lock_print", NULL, 0 },
{ isc_action_max, "start_cache", "-svc", "bin/gds_cache_manager", NULL, 0 },
#endif /* WIN_NT */
{ 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, NULL, reinterpret_cast<PFN_SERV_t>(TEST_THREAD), 0 },
{ isc_action_svc_db_stats, "Database Stats", NULL, NULL, 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 }
};
#else /* LINUX: disallow services API for 6.0 Linux Classic */
{isc_action_max, "anonymous", NULL, NULL, NULL, 0},
#ifdef SUPERSERVER
{isc_action_max, "query_server", NULL, NULL, NULL, 0},
{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, NULL, reinterpret_cast<PFN_SERV_t>(TEST_THREAD), 0},
{isc_action_svc_db_stats, "Database Stats", NULL, NULL, 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},
#endif
{0, NULL, NULL, NULL, NULL, 0}
};
#endif /* LINUX */
/* 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 an Interbase 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)
#if ((defined LINUX || defined FREEBSD || defined NETBSD) && !defined SUPERSERVER)
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;
while (p < end) {
if (*p++ == isc_spb_command_line)
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 (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 =
(STATUS *) gds__alloc(ISC_STATUS_LENGTH * sizeof(STATUS));
/* FREE: by exception handler */
if (!service->svc_status)
ERR_post(isc_virmemexh, 0);
memset((void *) service->svc_status, 0,
ISC_STATUS_LENGTH * sizeof(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;
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, const_cast<TEXT*>(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;
}
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) {
JRD_shutdown_all();
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*/
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;
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:
if (SVC_hdrtbl) {
IPCCFG h;
*info++ = item;
ISC_get_config(LOCK_HEADER, SVC_hdrtbl);
for (h = SVC_hdrtbl; h->ipccfg_keyword; h++) {
CK_SPACE_FOR_NUMERIC;
*info++ = h->ipccfg_key;
ADD_SPB_NUMERIC(info, *h->ipccfg_variable);
}
if (info < end)
*info++ = isc_info_flag_end;
}
break;
case isc_info_svc_default_config:
*info++ = item;
if (service->svc_user_flag & SVC_user_dba) {
THREAD_ENTER;
if (ISC_set_config(LOCK_HEADER, NULL))
ERR_FILE_IN_USE;
THREAD_EXIT;
}
else
NEED_ADMIN_PRIVS("isc_info_svc_default_config");
break;
case isc_info_svc_set_config:
*info++ = item;
length = (USHORT) isc_vax_integer(items, sizeof(USHORT));
items += sizeof(USHORT);
/* Check for proper user authority */
if (service->svc_user_flag & SVC_user_dba) {
int n;
UCHAR *p, *end;
end = reinterpret_cast < UCHAR * >(items + length);
/* count the number of parameters being set */
p = reinterpret_cast < UCHAR * >(items);
for (n = 0; p < end; n++)
p += p[1] + 2;
/* if there is at least one then do the configuration */
if (n++) {
IPCCFG tmpcfg;
/* allocate a buffer big enough to n struct ipccfg and
* n ipccfg variables.
*/
tmpcfg =
(IPCCFG) gds__alloc(n * (sizeof(struct ipccfg) +
ALIGNMENT) + length);
if (tmpcfg) {
p = (UCHAR *) tmpcfg + n * sizeof(struct ipccfg);
for (n = 0; (UCHAR *) items < end; n++) {
int nBytes;
tmpcfg[n].ipccfg_keyword = NULL;
tmpcfg[n].ipccfg_key = *items++;
tmpcfg[n].ipccfg_variable = (SLONG *) p;
nBytes = *items++;
*(ULONG *) p = isc_vax_integer(items, nBytes);
p += ROUNDUP(nBytes, ALIGNMENT);
items += nBytes;
}
tmpcfg[n].ipccfg_keyword = NULL;
tmpcfg[n].ipccfg_key = 0;
tmpcfg[n].ipccfg_variable = NULL;
THREAD_ENTER;
if (ISC_set_config(LOCK_HEADER, tmpcfg))
ERR_FILE_IN_USE;
THREAD_EXIT;
gds__free((ULONG *) tmpcfg);
}
else {
/* Return an error if there is no more memory to
* access the ibconfig file. Do not continue
* processing the query request.
*/
THREAD_ENTER;
*status++ = isc_virmemexh;
*status++ = isc_arg_end;
return tdbb->tdbb_status_vector[1];
}
}
}
else {
items += length;
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 + 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 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 + 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 (service->svc_user_flag == SVC_user_none)
break;
}
if (info < end)
*info = isc_info_end;
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];
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(reinterpret_cast < char *>(buffer), "");
break;
case isc_info_svc_get_env_lock:
gds__prefix_lock(reinterpret_cast < char *>(buffer), "");
break;
case isc_info_svc_get_env_msg:
gds__prefix_msg(reinterpret_cast < char *>(buffer), "");
}
/* 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(reinterpret_cast <
char *>(buffer)),
reinterpret_cast < char *>(buffer),
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:
if (SVC_hdrtbl) {
IPCCFG h;
UCHAR *p;
ISC_get_config(LOCK_HEADER, SVC_hdrtbl);
p = buffer;
for (h = SVC_hdrtbl; h->ipccfg_keyword; h++) {
*p++ = h->ipccfg_key;
length =
INF_convert(*h->ipccfg_variable,
reinterpret_cast < char *>(p + 1));
*p++ = (UCHAR) length;
p += length;
}
if (!
(info =
INF_put_item(item, p - buffer,
reinterpret_cast < char *>(buffer), info,
end))) {
THREAD_ENTER;
return;
}
}
break;
case isc_info_svc_default_config:
*info++ = item;
if (service->svc_user_flag & SVC_user_dba) {
THREAD_ENTER;
if (ISC_set_config(LOCK_HEADER, NULL))
*info++ = 3; /* File in use */
else
*info++ = 0; /* Success */
THREAD_EXIT;
}
else
*info++ = 2;
break;
case isc_info_svc_set_config:
*info++ = item;
length = (USHORT) isc_vax_integer(items, sizeof(USHORT));
items += sizeof(USHORT);
/* Check for proper user authority */
if (service->svc_user_flag & SVC_user_dba) {
int n;
UCHAR *p, *end;
end = reinterpret_cast < UCHAR * >(items + length);
/* count the number of parameters being set */
p = reinterpret_cast < UCHAR * >(items);
for (n = 0; p < end; n++)
p += p[1] + 2;
/* if there is at least one then do the configuration */
if (n++) {
IPCCFG tmpcfg;
/* allocate a buffer big enough to n struct ipccfg and
* n ipccfg variables.
*/
tmpcfg =
(IPCCFG) gds__alloc(n *
(sizeof(struct ipccfg) +
ALIGNMENT) + length);
if (tmpcfg) {
p = (UCHAR *) tmpcfg + n * sizeof(struct ipccfg);
for (n = 0; (UCHAR *) items < end; n++) {
int nBytes;
tmpcfg[n].ipccfg_keyword = NULL;
tmpcfg[n].ipccfg_key = *items++;
tmpcfg[n].ipccfg_variable = (SLONG *) p;
nBytes = *items++;
*(ULONG *) p = isc_vax_integer(items, nBytes);
p += ROUNDUP(nBytes, ALIGNMENT);
items += nBytes;
}
tmpcfg[n].ipccfg_keyword = NULL;
tmpcfg[n].ipccfg_key = 0;
tmpcfg[n].ipccfg_variable = NULL;
THREAD_ENTER;
if (ISC_set_config(LOCK_HEADER, tmpcfg))
*info++ = 3; /* File in use */
else
*info++ = 0; /* Success */
THREAD_EXIT;
gds__free((ULONG *) tmpcfg);
}
else {
*info++ = 1; /* No Memory */
items = reinterpret_cast < char *>(end);
}
}
*info++ = 0; /* Success */
}
else {
items += length;
*info++ = 2; /* No user authority */
}
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;
}
THREAD_ENTER;
}
void *SVC_start(SVC service, USHORT spb_length, SCHAR * spb)
{
/**************************************
*
* S V C _ s t a r t
*
**************************************
*
* Functional description
* Start an InterBase 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_svcnotdef, isc_arg_string,
SVC_err_string(const_cast < char *>(serv->serv_name),
strlen(serv->serv_name)), 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_flags & SVC_detached))
service->svc_flags = 0;
service->svc_flags |= SVC_thd_running;
if (service->svc_argc && service->svc_switches) {
gds__free(service->svc_switches);
service->svc_switches = NULL;
}
}
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, const_cast<TEXT*>(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(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, 0, 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 InterBase
* log file into the service buffers.
*
**************************************/
IB_FILE *file;
TEXT name[MAXPATHLEN], buffer[100];
BOOLEAN svc_started = FALSE;
#ifdef SUPERSERVER
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);
service->svc_handle = 0;
if (service->svc_service->in_use != NULL)
*(service->svc_service->in_use) = FALSE;
#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;
}
static void io_error(
TEXT * string,
SLONG status,
TEXT * filename, 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);
}
#ifdef WIN_NT
#ifndef SUPERSERVER
#define PIPE_OPERATIONS
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);
CloseHandle((HANDLE) service->svc_handle);
}
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++;
}
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_USESTDHANDLES;
start_crud.hStdInput = my_output;
start_crud.hStdOutput = my_input;
start_crud.hStdError = my_input;
if (!(ret = CreateProcess(NULL,
argv_data,
NULL,
NULL,
TRUE,
NORMAL_PRIORITY_CLASS | DETACHED_PROCESS,
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 = 0L;
SCHAR *buf;
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);
}
return buf - buffer;
}
#endif /* ifndef SUPERSERVER */
#endif /* WIN_NT */
#ifdef SUPERSERVER
#define PIPE_OPERATIONS
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_YIELD;
/* 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_YIELD;
#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 */
#ifndef SUPERSERVER
#ifndef PIPE_OPERATIONS
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);
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);
}
#endif /* ifndef PIPE_OPERATIONS */
#endif /* ifndef SUPERSERVER */
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.
*
**************************************/
}
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);
// 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) {
service->svc_flags &= ~SVC_thd_running;
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;
//BOOLEAN lic_key = FALSE, lic_id = 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;
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;
l =
gds__vax_integer(reinterpret_cast < UCHAR * >(*spb),
sizeof(ISC_USHORT));
/* 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(*spb, *cmd, l);
*cmd += l;
**cmd = SVC_TRMNTR;
*cmd += 1;
**cmd = ' ';
*cmd += 1;
}
*spb += l;
*total += l + 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