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

4038 lines
99 KiB
C++
Raw Normal View History

2001-05-23 15:26:42 +02:00
/*
* 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-16 03:21:35 +01:00
*
* 2002.02.15 Sean Leyne - Code Cleanup, removed obsolete "EPSON" define
2002-02-16 04:27:33 +01:00
* 2002.02.15 Sean Leyne - Code Cleanup, removed obsolete "IMP" port
2002-02-16 03:21:35 +01:00
*
* 2002.10.27 Sean Leyne - Completed removal of obsolete "DELTA" port
* 2002.10.27 Sean Leyne - Completed removal of obsolete "IMP" port
*
2002-10-30 07:40:58 +01:00
* 2002.10.29 Sean Leyne - Removed obsolete "Netware" port
*
2001-05-23 15:26:42 +02:00
*/
#include "firebird.h"
#include "../jrd/y_ref.h"
2001-05-23 15:26:42 +02:00
#include "../jrd/ib_stdio.h"
#include <string.h>
#include "../jrd/ibsetjmp.h"
#include "../jrd/jrd_time.h"
2001-05-23 15:26:42 +02:00
#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"
2001-05-23 15:26:42 +02:00
#include "../alice/aliceswi.h"
#include "../burp/burpswi.h"
#include "../jrd/ibase.h"
#include "gen/codes.h"
2001-05-23 15:26:42 +02:00
#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"
2001-05-23 15:26:42 +02:00
#ifdef SERVER_SHUTDOWN
#include "../jrd/jrd_proto.h"
#endif
2001-07-12 07:46:06 +02:00
#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)
2001-07-12 07:46:06 +02:00
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_VFORK_H
#include <vfork.h>
#endif
2001-05-23 15:26:42 +02:00
#ifdef sparc
#ifdef SOLARIS
#include <fcntl.h>
#endif
#endif
2002-02-16 03:21:35 +01:00
#ifdef SCO_UNIX
2001-05-23 15:26:42 +02:00
#include <fcntl.h>
#endif
/* This is defined in JRD/SCL.H, but including it causes
2002-02-16 03:21:35 +01:00
* a linker warning.
2001-05-23 15:26:42 +02:00
*/
#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
2001-05-23 15:26:42 +02:00
#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]; \
2001-05-23 15:26:42 +02:00
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
2003-01-20 14:51:35 +01:00
#ifdef SERVER_SHUTDOWN
2001-05-23 15:26:42 +02:00
static shutdown_fct_t shutdown_fct = 0;
static ULONG shutdown_param = 0L;
2003-01-20 14:51:35 +01:00
#endif
2001-05-23 15:26:42 +02:00
#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}
2001-05-23 15:26:42 +02:00
};
#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[] = {
2001-12-24 03:51:06 +01:00
{ 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 }
2001-05-23 15:26:42 +02:00
};
#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
2001-12-24 03:51:06 +01:00
extern int main_gbak(SVC service);
extern int main_gfix(SVC service);
2001-05-23 15:26:42 +02:00
extern int main_wal_print();
extern int main_lock_print();
2001-12-24 03:51:06 +01:00
extern int main_gstat(SVC service);
extern int main_gsec(SVC service);
2001-05-23 15:26:42 +02:00
#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;
}
}
}
2001-05-23 15:26:42 +02:00
/* 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)
*/
2003-02-13 11:11:35 +01:00
typedef const void (*PFN_SERV_t) ();
2001-05-23 15:26:42 +02:00
static const serv services[] =
{
2003-03-09 22:13:24 +01:00
#ifndef NOT_USED_OR_REPLACED
2001-05-23 15:26:42 +02:00
#ifdef WIN_NT
2002-10-29 14:46:48 +01:00
{ 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 },
2001-05-23 15:26:42 +02:00
#else
2001-12-24 03:51:06 +01:00
{ 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 },
2001-05-23 15:26:42 +02:00
#endif /* WIN_NT */
2001-12-24 03:51:06 +01:00
{ 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 },
2001-05-23 15:26:42 +02:00
/* NEW VERSION 2 calls, the name field MUST be different from those names above
*/
2001-12-24 03:51:06 +01:00
{ 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 },
2001-05-23 15:26:42 +02:00
/* actions with no names are undocumented */
2001-12-24 03:51:06 +01:00
{ 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 }
2001-05-23 15:26:42 +02:00
};
#else /* LINUX: disallow services API for 6.0 Linux Classic */
{isc_action_max, "anonymous", NULL, NULL, NULL, 0},
2001-05-23 15:26:42 +02:00
#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},
2001-05-23 15:26:42 +02:00
/* 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},
2001-05-23 15:26:42 +02:00
#endif
{0, NULL, NULL, NULL, NULL, 0}
2001-05-23 15:26:42 +02:00
};
#endif /* LINUX */
/* The SERVER_CAPABILITIES_FLAG is used to mark architectural
** differences across servers. This allows applications like server
2002-02-16 04:27:33 +01:00
** manager to disable features as necessary.
2001-05-23 15:26:42 +02:00
*/
#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
2001-12-24 03:51:06 +01:00
extern "C" {
SVC SVC_attach(USHORT service_length,
TEXT* service_name,
USHORT spb_length,
SCHAR* spb)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* S V C _ a t t a c h
*
**************************************
*
* Functional description
* Connect to a Firebird service.
2001-05-23 15:26:42 +02:00
*
**************************************/
2001-12-24 03:51:06 +01:00
2003-02-13 11:11:35 +01:00
const struct serv *serv;
2001-05-23 15:26:42 +02:00
TEXT misc_buf[512];
#ifndef SUPERSERVER
TEXT service_path[MAXPATHLEN];
#endif
SPB options;
TEXT name[129] /*, project[33] */ ;
2001-12-24 03:51:06 +01:00
int id = 0;
int group;
int node_id;
USHORT user_flag;
2001-05-23 15:26:42 +02:00
/* 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;
}
2001-12-24 03:51:06 +01:00
else {
2001-05-23 15:26:42 +02:00
strcpy(misc_buf, service_name);
2001-12-24 03:51:06 +01:00
}
2001-05-23 15:26:42 +02:00
/* Find the service by looking for an exact match. */
2001-07-12 07:46:06 +02:00
for (serv = (struct serv*)services; serv->serv_name; serv++)
2001-05-23 15:26:42 +02:00
if (!strcmp(misc_buf, serv->serv_name))
break;
if (!serv->serv_name)
2003-03-09 22:08:33 +01:00
#ifdef NOT_USED_OR_REPLACED
2001-05-23 15:26:42 +02:00
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
2001-12-24 03:51:06 +01:00
TDBB tdbb = GET_THREAD_DATA;
2001-05-23 15:26:42 +02:00
/* 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;
2001-05-23 15:26:42 +02:00
2001-12-24 03:51:06 +01:00
try {
2001-05-23 15:26:42 +02:00
/* 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;
}
2001-05-23 15:26:42 +02:00
/* Process the service parameter block. */
2001-12-24 03:51:06 +01:00
if (spb_length > sizeof(misc_buf)) {
2001-05-23 15:26:42 +02:00
misc = (TEXT *) gds__alloc((SLONG) spb_length);
2001-12-24 03:51:06 +01:00
if (!misc) {
ERR_post(isc_virmemexh, 0);
}
} else {
2001-05-23 15:26:42 +02:00
misc = misc_buf;
2001-12-24 03:51:06 +01:00
}
get_options(reinterpret_cast<UCHAR*>(spb), spb_length, misc, &options);
2001-05-23 15:26:42 +02:00
/* Perhaps checkout the user in the security database. */
2001-12-24 03:51:06 +01:00
if (!strcmp(serv->serv_name, "anonymous")) {
2001-05-23 15:26:42 +02:00
user_flag = SVC_user_none;
2001-12-24 03:51:06 +01:00
} else {
2001-05-23 15:26:42 +02:00
if (!options.spb_user_name)
2001-12-24 03:51:06 +01:00
{
// user name and password are required while
// attaching to the services manager
ERR_post(isc_service_att_err, isc_arg_gds, isc_svcnouser, 0);
}
2001-05-23 15:26:42 +02:00
if (options.spb_user_name || id == -1)
2001-12-24 03:51:06 +01:00
{
SecurityDatabase::verifyUser(name, options.spb_user_name,
options.spb_password, options.spb_password_enc,
&id, &group, &node_id);
2001-12-24 03:51:06 +01:00
}
2001-05-23 15:26:42 +02:00
/* 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 */
{
2001-05-23 15:26:42 +02:00
user_flag = SVC_user_any;
2001-12-24 03:51:06 +01:00
} else {
2001-05-23 15:26:42 +02:00
user_flag = SVC_user_dba | SVC_user_any;
2001-12-24 03:51:06 +01:00
}
2001-05-23 15:26:42 +02:00
}
/* Allocate a buffer for the service switches, if any. Then move them in. */
2001-12-24 03:51:06 +01:00
USHORT len = ((serv->serv_std_switches) ? strlen(serv->serv_std_switches) : 0) +
2001-05-23 15:26:42 +02:00
((options.spb_command_line) ? strlen(options.spb_command_line) : 0) +
1;
if (len > 1)
2001-12-24 03:51:06 +01:00
{
2001-05-23 15:26:42 +02:00
if ((switches = (TEXT *) gds__alloc((SLONG) len)) == NULL)
2001-12-24 03:51:06 +01:00
{
/* FREE: by exception handler */
2001-05-23 15:26:42 +02:00
ERR_post(isc_virmemexh, 0);
2001-12-24 03:51:06 +01:00
}
}
2001-05-23 15:26:42 +02:00
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. */
2001-12-24 03:51:06 +01:00
// service = (SVC) gds__alloc((SLONG) (sizeof(struct svc)));
service = FB_NEW(*getDefaultMemoryPool()) svc;
/* FREE: by exception handler */
2001-05-23 15:26:42 +02:00
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 */
2001-05-23 15:26:42 +02:00
if (!service->svc_status)
ERR_post(isc_virmemexh, 0);
memset((void *) service->svc_status, 0,
ISC_STATUS_LENGTH * sizeof(STATUS));
2001-12-24 03:51:06 +01:00
//service->blk_type = type_svc;
//service->blk_pool_id = 0;
//service->blk_length = 0;
2001-05-23 15:26:42 +02:00
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);
2002-02-16 04:27:33 +01:00
/* The password will be issued to the service threads on NT since
* there is no OS authentication. If the password is not yet
2001-05-23 15:26:42 +02:00
* encrypted, then encrypt it before saving it (since there is no
* decrypt function).
*/
if (options.spb_password) {
2001-12-24 03:51:06 +01:00
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);
2001-05-23 15:26:42 +02:00
MOVE_FASTER(p, s, l);
service->svc_enc_password[l] = 0;
}
2001-12-24 03:51:06 +01:00
if (options.spb_password_enc) {
2001-05-23 15:26:42 +02:00
strcpy(service->svc_enc_password, options.spb_password_enc);
2001-12-24 03:51:06 +01:00
}
2001-05-23 15:26:42 +02:00
2002-02-16 04:27:33 +01:00
/* If an executable is defined for the service, try to fork a new process.
2001-05-23 15:26:42 +02:00
* 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));
2001-05-23 15:26:42 +02:00
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);
}
2001-12-24 03:51:06 +01:00
if (misc != misc_buf) {
2001-05-23 15:26:42 +02:00
gds__free((SLONG *) misc);
2001-12-24 03:51:06 +01:00
}
} // try
2003-02-13 14:33:57 +01:00
catch (const std::exception&) {
if (spb_buf) {
gds__free((SLONG *) spb_buf);
}
2001-12-24 03:51:06 +01:00
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();
}
2001-05-23 15:26:42 +02:00
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
}
2002-10-29 17:27:47 +01:00
const TEXT* SVC_err_string(const TEXT* data, USHORT length)
2001-05-23 15:26:42 +02:00
{
/********************************************
*
* 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);
}
2003-01-20 14:51:35 +01:00
#ifdef SERVER_SHUTDOWN
void SVC_shutdown_init(shutdown_fct_t fptr,
ULONG param)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* 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.
*
**************************************/
2003-01-20 14:51:35 +01:00
shutdown_fct = fptr;
2001-05-23 15:26:42 +02:00
shutdown_param = param;
}
2003-01-20 14:51:35 +01:00
#endif // SERVER_SHUTDOWN
2001-05-23 15:26:42 +02:00
#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];
2001-05-23 15:26:42 +02:00
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:
{
2001-12-24 03:51:06 +01:00
UCHAR dbbuf[1024];
2001-05-23 15:26:42 +02:00
USHORT num_dbs = 0, num = 0;
USHORT num_att = 0;
TEXT *ptr, *ptr2;
*info++ = item;
ptr =
2001-12-24 03:51:06 +01:00
JRD_num_attachments(reinterpret_cast<char*>(dbbuf),
2001-05-23 15:26:42 +02:00
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
2002-02-16 04:27:33 +01:00
buffer 'info' with values of environment variable INTERBASE,
2001-05-23 15:26:42 +02:00
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);
2001-05-23 15:26:42 +02:00
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:
2001-12-24 03:51:06 +01:00
if ( (l = length = service->svc_resp_len) )
2001-05-23 15:26:42 +02:00
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;
2001-05-23 15:26:42 +02:00
else
get_flags = GET_BINARY;
2001-05-23 15:26:42 +02:00
service_get(service, info + 3, end - (info + 4), get_flags,
timeout, &length);
2002-02-16 04:27:33 +01:00
/* If the read timed out, return the data, if any, & a timeout
2001-05-23 15:26:42 +02:00
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;
}
2001-05-23 15:26:42 +02:00
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:
2001-12-24 03:51:06 +01:00
{
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;
}
2001-05-23 15:26:42 +02:00
}
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
2002-02-16 04:27:33 +01:00
buffer 'info' with values of environment variable INTERBASE,
2001-05-23 15:26:42 +02:00
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));
2001-05-23 15:26:42 +02:00
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:
2001-12-24 03:51:06 +01:00
if ( (l = length = service->svc_resp_len) )
2001-05-23 15:26:42 +02:00
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);
2002-02-16 04:27:33 +01:00
/* If the read timed out, return the data, if any, & a timeout
2001-05-23 15:26:42 +02:00
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 a Firebird service
2001-05-23 15:26:42 +02:00
*
**************************************/
TDBB tdbb;
2003-02-13 11:11:35 +01:00
const struct serv *serv;
2001-05-23 15:26:42 +02:00
USHORT svc_id;
2001-12-24 03:51:06 +01:00
TEXT* tmp_ptr = NULL;
2001-05-23 15:26:42 +02:00
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
2002-02-16 04:27:33 +01:00
* network). This parameter will be implemented at
2001-05-23 15:26:42 +02:00
* a later date.
*/
2002-09-26 11:26:40 +02:00
isc_resv_handle reserved = (isc_resv_handle)0; /* Reserved for future functionality */
2001-05-23 15:26:42 +02:00
/* The name of the service is the first element of the buffer */
svc_id = *spb;
2001-07-12 07:46:06 +02:00
for (serv = (struct serv*)services; serv->serv_action; serv++)
2001-05-23 15:26:42 +02:00
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;
2001-12-24 03:51:06 +01:00
try {
2001-05-23 15:26:42 +02:00
2002-02-16 04:27:33 +01:00
/* Only need to add username and password information to those calls which need
2001-05-23 15:26:42 +02:00
* 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) {
2001-05-23 15:26:42 +02:00
/* 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) {
2001-05-23 15:26:42 +02:00
conv_switches(spb_length, opt_switch_len, spb,
&service->svc_switches);
}
2001-05-23 15:26:42 +02:00
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);
}
2001-05-23 15:26:42 +02:00
}
/* 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));
2001-05-23 15:26:42 +02:00
service->svc_flags = SVC_forked;
service_fork(service_path, service);
}
#else
/* Break up the command line into individual arguments. */
2001-12-24 03:51:06 +01:00
if (service->svc_switches)
{
USHORT argc;
TEXT* p;
TEXT* q;
TEXT** arg;
2001-05-23 15:26:42 +02:00
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);
2001-12-24 03:51:06 +01:00
*arg++ = (TEXT *)(serv->serv_thd);
2001-05-23 15:26:42 +02:00
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);
2001-05-24 16:54:26 +02:00
service->svc_stdout = (UCHAR*)gds__alloc((SLONG) SVC_STDOUT_BUFFER_SIZE + 1);
2001-05-23 15:26:42 +02:00
/* FREE: at SVC_detach() */
if (!service->svc_stdout) /* NOMEM: */
2002-02-16 04:27:33 +01:00
{
2001-05-23 15:26:42 +02:00
ERR_post(isc_virmemexh, 0);
2001-05-24 16:54:26 +02:00
}
2001-05-23 15:26:42 +02:00
if (serv->serv_thd) {
SLONG count;
#pragma FB_COMPILER_MESSAGE("Fix! Probable bug!")
EVENT evnt_ptr =
2001-05-24 16:54:26 +02:00
reinterpret_cast<EVENT> (&(service->svc_start_event));
2001-05-23 15:26:42 +02:00
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)) {
2001-12-24 03:51:06 +01:00
if (ISC_event_wait(1,
&evnt_ptr,
&count,
60 * 1000,
(FPTR_VOID) 0,
0))
{
continue;
}
2001-05-23 15:26:42 +02:00
else; /* the event was posted */
break;
}
ISC_event_fini(evnt_ptr);
THREAD_ENTER;
}
else
2001-12-24 03:51:06 +01:00
{
ERR_post(isc_svcnotdef,
isc_arg_string,
SVC_err_string(const_cast<char*>(serv->serv_name),
strlen(serv->serv_name)),
0);
}
2001-05-23 15:26:42 +02:00
#endif /* SUPERSERVER */
2001-12-24 03:51:06 +01:00
} // try
2003-02-13 14:33:57 +01:00
catch (const std::exception&) {
2001-12-24 03:51:06 +01:00
if (service->svc_switches) {
gds__free((SLONG *) service->svc_switches);
}
if (service) {
// gds__free((SLONG *) service);
delete service;
}
ERR_punt();
}
2001-05-23 15:26:42 +02:00
2001-12-24 03:51:06 +01:00
return reinterpret_cast<void*>(reserved);
2001-05-23 15:26:42 +02:00
}
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
2001-05-23 15:26:42 +02:00
* 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;
2001-05-23 15:26:42 +02:00
*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) {
2001-12-24 03:51:06 +01:00
SVC_STATUS_ARG(status, isc_arg_string, (void*)"ib_fopen");
2001-05-23 15:26:42 +02:00
}
else {
2001-12-24 03:51:06 +01:00
SVC_STATUS_ARG(status, isc_arg_string, (void*)"ib_fgets");
2001-05-23 15:26:42 +02:00
}
*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
}
2001-12-24 03:51:06 +01:00
} // extern "C"
static void get_options(UCHAR* spb,
USHORT spb_length,
TEXT* scratch,
SPB* options)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* 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;
2001-12-24 03:51:06 +01:00
if ( (l = *spb++) )
2001-05-23 15:26:42 +02:00
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];
2001-05-23 15:26:42 +02:00
GetTempPath(MAXPATHLEN, tmpPath);
2001-05-23 15:26:42 +02:00
fname = _tempnam(tmpPath, "ibsvc");
tmp =
_open(fname, _O_RDWR | _O_CREAT | _O_TEMPORARY,
_S_IREAD | _S_IWRITE);
my_input = (HANDLE) _get_osfhandle(tmp);
2001-05-23 15:26:42 +02:00
fname = _tempnam(tmpPath, "ibsvc");
tmp =
_open(fname, _O_RDWR | _O_CREAT | _O_TEMPORARY,
_S_IREAD | _S_IWRITE);
my_output = (HANDLE) _get_osfhandle(tmp);
2001-05-23 15:26:42 +02:00
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);
2001-05-23 15:26:42 +02:00
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 = ' ';
}
2001-05-23 15:26:42 +02:00
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;
2001-05-23 15:26:42 +02:00
if (!(ret = CreateProcess(NULL,
argv_data,
NULL,
NULL,
TRUE,
NORMAL_PRIORITY_CLASS,
2001-05-23 15:26:42 +02:00
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;
}
2002-12-11 10:41:49 +01:00
static void service_get(SVC service,
2001-05-23 15:26:42 +02:00
SCHAR * buffer,
USHORT length,
USHORT flags,
USHORT timeout,
USHORT * return_length)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* 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
2002-02-16 04:27:33 +01:00
what's available until all data has been read, or timeout occurs.
2001-05-23 15:26:42 +02:00
Otherwise, set the timeout flag and return.
2002-02-16 04:27:33 +01:00
Fall out of the loop if a BROKEN_PIPE error occurs.
2001-05-23 15:26:42 +02:00
*/
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)) {
2001-05-23 15:26:42 +02:00
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))
2001-05-23 15:26:42 +02:00
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)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* 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;
2001-05-23 15:26:42 +02:00
while (length) {
n = 0;
len = (flags & GET_BINARY) ? length : 1;
if (ReadFile((HANDLE) service->svc_input, buf, len, (ULONG*) &n, NULL) ||
2001-05-23 15:26:42 +02:00
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] = ' ';
2001-05-23 15:26:42 +02:00
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)
2001-05-24 16:54:26 +02:00
return 1;
return 0;
2001-05-23 15:26:42 +02:00
}
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);
2001-12-24 03:51:06 +01:00
*arg++ = (TEXT *)(service_executable);
2001-05-23 15:26:42 +02:00
/* 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;
2001-05-24 16:54:26 +02:00
service->svc_stdout = (UCHAR*)gds__alloc((SLONG) SVC_STDOUT_BUFFER_SIZE + 1);
2001-05-23 15:26:42 +02:00
/* FREE: at SVC_detach() */
if (!service->svc_stdout) /* NOMEM: */
ERR_post(isc_virmemexh, 0);
THREAD_EXIT;
2001-05-24 16:54:26 +02:00
gds__thread_start(reinterpret_cast<FPTR_INT_VOID_PTR>(service_executable),
service,
0,
0,
(void*)&service->svc_handle);
2001-05-23 15:26:42 +02:00
THREAD_ENTER;
}
2001-05-24 16:54:26 +02:00
static void service_get(SVC service,
SCHAR* buffer,
USHORT length,
USHORT flags,
USHORT timeout,
USHORT* return_length)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* s e r v i c e _ g e t
*
**************************************
*
* Functional description
*
**************************************/
int ch = 'Z';
ULONG elapsed_time;
#ifdef HAVE_GETTIMEOFDAY
2001-05-23 15:26:42 +02:00
struct timeval start_time, end_time;
#else
time_t start_time, end_time;
2001-05-23 15:26:42 +02:00
#endif
#ifdef HAVE_GETTIMEOFDAY
#ifdef GETTIMEOFDAY_RETURNS_TIMEZONE
(void)gettimeofday(&start_time, (struct timezone *)0);
2001-05-23 15:26:42 +02:00
#else
(void)gettimeofday(&start_time);
2001-05-23 15:26:42 +02:00
#endif
#else
time(&start_time);
2002-08-24 11:40:38 +02:00
#endif
2001-05-23 15:26:42 +02:00
*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);
2001-05-23 15:26:42 +02:00
#else
(void)gettimeofday(&end_time);
#endif
2001-05-23 15:26:42 +02:00
elapsed_time = end_time.tv_sec - start_time.tv_sec;
#else
time(&end_time);
elapsed_time = end_time - start_time;
2001-05-23 15:26:42 +02:00
#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 == ' ')
{
2001-05-23 15:26:42 +02:00
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))
2001-05-23 15:26:42 +02:00
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 == ' ')
{
2001-05-23 15:26:42 +02:00
*p = ' ';
q++;
}
p++;
}
arg++;
2001-05-23 15:26:42 +02:00
if (!*p)
break;
*p++ = '\0';
2001-05-23 15:26:42 +02:00
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
2001-05-23 15:26:42 +02:00
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
2001-05-23 15:26:42 +02:00
struct sigvec sv_hndlr;
#else
struct sigaction sv_hndlr;
#endif
#endif
int c;
2001-12-24 03:51:06 +01:00
//USHORT timed_out;
2001-05-23 15:26:42 +02:00
SCHAR *buf;
SSHORT iter = 0;
int errno_save;
IS_SERVICE_RUNNING(service)
errno = 0;
service->svc_flags &= ~SVC_timeout;
buf = buffer;
if (timeout) {
2001-07-12 07:46:06 +02:00
ISC_set_timer((SLONG) (timeout * 100000), (void(*)())timeout_handler, service,
(SLONG*)&sv_timr, (void**)&sv_hndlr);
2001-05-23 15:26:42 +02:00
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)
2001-07-12 07:46:06 +02:00
ISC_reset_timer((void(*)())timeout_handler, service, (SLONG*)&sv_timr,
(void**)&sv_hndlr);
2001-05-23 15:26:42 +02:00
io_error("ib_getc", errno_save, "service pipe", isc_io_read_err,
TRUE);
}
}
if (timeout) {
2001-07-12 07:46:06 +02:00
ISC_reset_timer((void(*)())timeout_handler, service, (SLONG*)&sv_timr, (void**)&sv_hndlr);
2001-05-23 15:26:42 +02:00
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);
2001-12-24 03:51:06 +01:00
// gds__free((SLONG *) service);
delete service;
2001-05-23 15:26:42 +02:00
}
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;
2001-12-24 03:51:06 +01:00
BOOLEAN found = FALSE;
2001-05-23 15:26:42 +02:00
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;
2002-02-16 04:27:33 +01:00
/* in case of "display all users" the spb buffer contains
2001-05-23 15:26:42 +02:00
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, l2;
2001-05-23 15:26:42 +02:00
l = gds__vax_integer(reinterpret_cast < UCHAR * >(*spb),
2001-05-23 15:26:42 +02:00
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
2001-05-23 15:26:42 +02:00
/* Do not go beyond the bounds of the spb buffer */
if (l > *len)
ERR_post(isc_bad_spb_form, 0);
*spb += sizeof(ISC_USHORT);
2001-05-24 16:54:26 +02:00
if (*cmd)
{
2001-05-23 15:26:42 +02:00
**cmd = SVC_TRMNTR;
*cmd += 1;
MOVE_FASTER(local_server, *cmd, l2);
*cmd += l2;
2001-05-23 15:26:42 +02:00
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 */
2001-05-23 15:26:42 +02:00
*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
2002-02-16 04:27:33 +01:00
* removed as the services API takes shape. They are used to
2001-05-23 15:26:42 +02:00
* 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