mirror of
https://github.com/FirebirdSQL/firebird.git
synced 2025-01-26 07:23:08 +01:00
572 lines
15 KiB
C++
572 lines
15 KiB
C++
/*
|
|
* PROGRAM: Central Server
|
|
* MODULE: csu.c
|
|
* DESCRIPTION: Central Server Utility
|
|
*
|
|
* 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): ______________________________________.
|
|
*/
|
|
/*
|
|
$Id: csu.cpp,v 1.3 2001-12-24 02:50:48 tamlin Exp $
|
|
*/
|
|
|
|
#include "firebird.h"
|
|
#include "../csv/csi.h"
|
|
#include "../jrd/license.h"
|
|
#include "gen/codes.h"
|
|
#include "../jrd/thd.h"
|
|
#include "../csv/css_proto.h"
|
|
#include "../jrd/gds_proto.h"
|
|
|
|
#define ALLOC(type, length) CSS_alloc_local (type, length)
|
|
#define FREE(block) CSS_free_local (block)
|
|
|
|
static void move(UCHAR *, UCHAR *, USHORT);
|
|
static void parse_command_line(int, TEXT **, USHORT *, CSN *, CSN *);
|
|
static void process_list_cmd(PTR, USHORT *);
|
|
static void process_nonlist_cmd(PTR, CSN);
|
|
static STATUS process_response(MSG_RESP, STATUS *, USHORT *);
|
|
|
|
static TEXT error_buffer[1024];
|
|
static USHORT return_buffer_length;
|
|
static UCHAR *return_buffer;
|
|
|
|
|
|
int CLIB_ROUTINE main( int argc, char **argv)
|
|
{
|
|
/**************************************
|
|
*
|
|
* m a i n
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Run the central server utility.
|
|
*
|
|
**************************************/
|
|
USHORT sw_list, listed_flag, flags;
|
|
CSN csv_enable_names, csv_other_names, cs_name;
|
|
STATUS status_vector[20];
|
|
SRQ *que;
|
|
PRB process;
|
|
PTR connection, server, client;
|
|
CSH CSS_header;
|
|
SLONG process_number, process_numbers[256], *ptr, *end;
|
|
|
|
#ifdef VMS
|
|
argc = VMS_parse(&argv, argc);
|
|
#endif
|
|
|
|
csv_enable_names = csv_other_names = NULL;
|
|
sw_list = FALSE;
|
|
|
|
THREAD_ENTER;
|
|
parse_command_line(argc, argv, &sw_list, &csv_enable_names,
|
|
&csv_other_names);
|
|
|
|
if (!CSS_init(status_vector, FALSE)) {
|
|
printf("It's likely that no central servers are operating.\n");
|
|
gds__print_status(status_vector);
|
|
exit(FINI_ERROR);
|
|
}
|
|
|
|
client = CSS_create_process(PRB_client);
|
|
|
|
/* Start by finding all server processes */
|
|
|
|
CSS_header = ACQUIRE;
|
|
CSS_probe_processes();
|
|
|
|
end = process_numbers;
|
|
|
|
QUE_LOOP(CSS_header->csh_processes, que) {
|
|
process = (PRB) ((UCHAR *) que - OFFSET(PRB, prb_processes));
|
|
server = REL_PTR(process);
|
|
if (client != server &&
|
|
(process->prb_flags & PRB_server) &&
|
|
process->prb_protocol_version == CSI_PROTOCOL_VERSION)
|
|
*end++ = process->prb_process_number;
|
|
}
|
|
|
|
RELEASE;
|
|
|
|
/* Go back and talk to servers */
|
|
|
|
/* Initialize a flag that indicates if anything has been listed */
|
|
|
|
listed_flag = FALSE;
|
|
|
|
for (ptr = process_numbers; ptr < end; ptr++) {
|
|
process_number = *ptr;
|
|
if (server = CSS_find_process(process_number)) {
|
|
connection = CSS_connect(server);
|
|
|
|
/* For each server name specified by the user, ask this
|
|
server to process it. */
|
|
|
|
for (cs_name = csv_enable_names;
|
|
cs_name && CSS_find_process(process_number);
|
|
cs_name =
|
|
cs_name->csn_next) process_nonlist_cmd(connection, cs_name);
|
|
|
|
/* For each database name specified by the user, ask this
|
|
server to process it. */
|
|
|
|
for (cs_name = csv_other_names;
|
|
cs_name && CSS_find_process(process_number);
|
|
cs_name = cs_name->csn_next) if (cs_name->csn_flags)
|
|
process_nonlist_cmd(connection, cs_name);
|
|
|
|
/* We may also need to do a list of this server's databases */
|
|
|
|
if (sw_list && CSS_find_process(process_number))
|
|
process_list_cmd(connection, &listed_flag);
|
|
|
|
CSS_disconnect(connection);
|
|
}
|
|
}
|
|
|
|
if (sw_list && !listed_flag)
|
|
printf("\nNo central servers are operating.\n\n");
|
|
|
|
for (cs_name = csv_other_names; cs_name; cs_name = cs_name->csn_next)
|
|
if ((flags = cs_name->csn_flags) && !(flags & DBN_cmd_process)) {
|
|
printf("Database \"%s\" ", cs_name->csn_db_name);
|
|
if (cs_name->csn_cs_len)
|
|
printf("running on server\n \"%s\" could not be ",
|
|
cs_name->csn_cs_name);
|
|
else
|
|
printf("could not be\n ");
|
|
printf("found and so it was not %sed.\n",
|
|
(flags & DBN_reenable) ? "re-enabl" : (flags & DBN_kill) ?
|
|
"kill" : "disabl");
|
|
}
|
|
|
|
for (cs_name = csv_enable_names; cs_name; cs_name = cs_name->csn_next)
|
|
if (!(cs_name->csn_flags & DBN_cmd_process))
|
|
printf
|
|
("Server \"%s\" could not be found and so database\n \"%s\" was not enabled.\n",
|
|
cs_name->csn_cs_name, cs_name->csn_db_name);
|
|
|
|
exit(FINI_OK);
|
|
}
|
|
|
|
|
|
static void move( UCHAR * from_ptr, UCHAR * to_ptr, USHORT length)
|
|
{
|
|
/**************************************
|
|
*
|
|
* m o v e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Move some bytes.
|
|
*
|
|
**************************************/
|
|
USHORT l;
|
|
UCHAR *from, *to;
|
|
|
|
from = from_ptr;
|
|
to = to_ptr;
|
|
|
|
if (length)
|
|
do
|
|
*to++ = *from++;
|
|
while (--length);
|
|
}
|
|
|
|
|
|
static void parse_command_line(
|
|
int argc,
|
|
TEXT ** argv,
|
|
USHORT * sw_list,
|
|
CSN * csv_enable_names, CSN * csv_other_names)
|
|
{
|
|
/**************************************
|
|
*
|
|
* p a r s e _ c o m m a n d _ l i n e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Parse a Unix style command line.
|
|
*
|
|
**************************************/
|
|
USHORT sw_enable, sw_name, sw_all, dbn_flag, all_flag;
|
|
USHORT length, listed_flag, csv_name_len;
|
|
TEXT **end, *p, c, expanded_name[256], *q, csv_name[256];
|
|
CSN other_names, enable_names, name, *head;
|
|
|
|
other_names = enable_names = NULL;
|
|
dbn_flag = all_flag = sw_enable = 0;
|
|
sw_name = FALSE;
|
|
|
|
for (end = argv++ + argc; argv < end;) {
|
|
p = *argv++;
|
|
if (*p++ == '-') {
|
|
/* Clear -a, -e, and -n indicators for each new switch. Clear -d,
|
|
-k, and -r indicators except when the switch is "-a" or "-n". */
|
|
|
|
sw_name = sw_all = FALSE;
|
|
if (((c = UPPER(*p)) != 'A' && c != 'N') || *(p + 1) || sw_enable)
|
|
dbn_flag = 0;
|
|
sw_enable = csv_name_len = 0;
|
|
|
|
while (c = *p++)
|
|
switch (UPPER(c)) {
|
|
case 'L':
|
|
*sw_list = TRUE;
|
|
break;
|
|
|
|
case 'D':
|
|
dbn_flag |= DBN_disable;
|
|
break;
|
|
|
|
case 'K':
|
|
dbn_flag |= DBN_kill;
|
|
break;
|
|
|
|
case 'R':
|
|
dbn_flag |= DBN_reenable;
|
|
break;
|
|
|
|
case 'E':
|
|
sw_enable = 1;
|
|
break;
|
|
|
|
case 'A':
|
|
sw_all = TRUE;
|
|
break;
|
|
|
|
case 'N':
|
|
sw_name = TRUE;
|
|
break;
|
|
|
|
case 'Z':
|
|
printf("csu version %s\n", GDS_VERSION);
|
|
break;
|
|
}
|
|
|
|
if (sw_enable && (dbn_flag || sw_all || sw_name)) {
|
|
printf
|
|
("The -e switch may only be used in conjunction with\nthe -l and -z switches.\n");
|
|
exit(FINI_ERROR);
|
|
}
|
|
else if (sw_all)
|
|
if (!dbn_flag)
|
|
printf
|
|
("No command indicated with the -a switch. It will be ignored.\n");
|
|
else {
|
|
all_flag |= dbn_flag;
|
|
dbn_flag = 0;
|
|
}
|
|
}
|
|
else if (!dbn_flag && !sw_enable && !sw_name)
|
|
printf
|
|
("No command indicated for database \"%s\".\nThe name will be ignored.\n",
|
|
p - 1);
|
|
else {
|
|
if (sw_enable == 1 || sw_name) {
|
|
if ((length = strlen(--p)) >= sizeof(csv_name))
|
|
length = sizeof(csv_name) - 1;
|
|
csv_name_len = length;
|
|
for (q = csv_name; length--;) {
|
|
c = *p++;
|
|
*q++ = UPPER(c);
|
|
}
|
|
if (sw_name)
|
|
sw_name = FALSE;
|
|
else
|
|
sw_enable++;
|
|
continue;
|
|
}
|
|
|
|
length = ISC_expand_filename(p - 1, 0, expanded_name);
|
|
|
|
#ifdef VMS
|
|
/* The next expansion is Gateway specific but we'll do it
|
|
for everyone. Since '@' isn't valid in a VMS filename,
|
|
this is ok. */
|
|
|
|
/* Look for an '@' after the first SCHARacter in expanded_name.
|
|
If one is found, try to expand the remainder of the string. */
|
|
|
|
if (*(p = expanded_name) != '@')
|
|
while (*p)
|
|
if (*p++ == '@' && *p) {
|
|
length =
|
|
ISC_expand_logical(p,
|
|
length - (p - expanded_name),
|
|
p) + (p - expanded_name);
|
|
break;
|
|
}
|
|
#endif
|
|
csv_name[csv_name_len] = 0;
|
|
|
|
head = (sw_enable) ? &enable_names : &other_names;
|
|
for (name = *head; name; name = name->csn_next)
|
|
if (!strcmp(name->csn_db_name, expanded_name) &&
|
|
!strcmp(name->csn_cs_name, csv_name))
|
|
break;
|
|
|
|
if (!name) {
|
|
name =
|
|
(CSN) ALLOC(type_csn,
|
|
sizeof(struct csn) + length + csv_name_len);
|
|
name->csn_next = *head;
|
|
*head = name;
|
|
name->csn_cs_name = name->csn_data;
|
|
name->csn_db_name = name->csn_data + csv_name_len + 1;
|
|
move(csv_name, name->csn_cs_name, csv_name_len + 1);
|
|
move(expanded_name, name->csn_db_name, length + 1);
|
|
name->csn_cs_len = csv_name_len;
|
|
name->csn_db_len = length;
|
|
}
|
|
|
|
name->csn_flags |= (sw_enable) ? DBN_enable : dbn_flag;
|
|
}
|
|
}
|
|
|
|
if (all_flag) {
|
|
/* We're to do something to all central server databases.
|
|
But first, make sure we won't try to do the same thing
|
|
or something similar to a specific database. Reenable
|
|
takes precedence over kill and disable. Kill takes
|
|
precedence over disable. */
|
|
|
|
if (all_flag & DBN_reenable)
|
|
all_flag |= DBN_kill | DBN_disable;
|
|
else if (all_flag & DBN_kill)
|
|
all_flag |= DBN_disable;
|
|
|
|
for (name = other_names; name; name = name->csn_next)
|
|
name->csn_flags &= ~all_flag;
|
|
|
|
/* The -a switch gets saved as a name with a zero length */
|
|
|
|
name = (CSN) ALLOC(type_csn, sizeof(struct csn));
|
|
name->csn_next = other_names;
|
|
other_names = name;
|
|
name->csn_cs_name = name->csn_db_name = name->csn_data;
|
|
name->csn_cs_len = name->csn_db_len = 0;
|
|
name->csn_flags = all_flag;
|
|
}
|
|
|
|
*csv_other_names = other_names;
|
|
*csv_enable_names = enable_names;
|
|
}
|
|
|
|
|
|
static void process_list_cmd( PTR connection, USHORT * listed_flag)
|
|
{
|
|
/**************************************
|
|
*
|
|
* p r o c e s s _ l i s t _ c m d
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Process a list command for a server.
|
|
*
|
|
**************************************/
|
|
MSG_UTIL message;
|
|
MSG_RESP response;
|
|
STATUS status_vector[20];
|
|
USHORT l;
|
|
CSU_LIST list_msg;
|
|
|
|
message = (MSG_UTIL) CSS_alloc_message(type_msg, sizeof(struct msg_util));
|
|
message->msg_util_header.msg_type = MSG_util_cmd;
|
|
message->msg_util_cmd = UTIL_list;
|
|
message->msg_util_csn_len = message->msg_util_dbn_len = 0;
|
|
|
|
if (!CSS_put_message(connection, message, 0) ||
|
|
!(response = CSS_get_message(connection, 0, 0)))
|
|
return;
|
|
|
|
if (!process_response(response, status_vector, 0)) {
|
|
list_msg = (CSU_LIST) return_buffer;
|
|
|
|
/* Print a header before the first name listed */
|
|
|
|
if (!*listed_flag) {
|
|
printf("\nServer\n\tAttaches Flags Database Name\n\n");
|
|
*listed_flag = TRUE;
|
|
}
|
|
|
|
/* Print the server name */
|
|
|
|
printf("%s\n", list_msg->csu_list_name);
|
|
list_msg =
|
|
(CSU_LIST) (list_msg->csu_list_name +
|
|
FB_ALIGN(list_msg->csu_list_length + 1, 2));
|
|
|
|
/* Loop over returned database name info */
|
|
|
|
while (l = list_msg->csu_list_length) {
|
|
printf("\t%7d%s %c %c %s\n", list_msg->csu_list_attaches,
|
|
(list_msg->csu_list_flags & DBN_server_att) ? "+s" : " ",
|
|
(list_msg->csu_list_flags & DBN_disable) ? 'D' : ' ',
|
|
(list_msg->csu_list_flags & DBN_kill) ? 'K' : ' ',
|
|
list_msg->csu_list_name);
|
|
list_msg =
|
|
(CSU_LIST) (list_msg->csu_list_name + FB_ALIGN(l + 1, 2));
|
|
}
|
|
}
|
|
else if (status_vector[1] != gds__unavailable) {
|
|
/**** DO SOMETHING MORE INTELLIGENT ****/
|
|
}
|
|
}
|
|
|
|
|
|
static void process_nonlist_cmd( PTR connection, CSN cs_name)
|
|
{
|
|
/**************************************
|
|
*
|
|
* p r o c e s s _ n o n l i s t _ c m d
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Process a disable, enable, kill, or re-enable command
|
|
* for a server.
|
|
*
|
|
**************************************/
|
|
SLONG length;
|
|
MSG_UTIL message;
|
|
MSG_RESP response;
|
|
USHORT l, cmd;
|
|
STATUS status_vector[20];
|
|
|
|
l = cs_name->csn_cs_len + cs_name->csn_db_len;
|
|
length = sizeof(struct msg_util) + l;
|
|
message = (MSG_UTIL) CSS_alloc_message(type_msg, (int) length);
|
|
message->msg_util_header.msg_type = MSG_util_cmd;
|
|
if (cs_name->csn_flags & DBN_enable)
|
|
cmd = UTIL_enable;
|
|
else if (cs_name->csn_flags & DBN_reenable)
|
|
cmd = UTIL_reenable;
|
|
else if (cs_name->csn_flags & DBN_kill)
|
|
cmd = UTIL_kill;
|
|
else
|
|
cmd = UTIL_disable;
|
|
message->msg_util_cmd = cmd;
|
|
message->msg_util_csn_len = cs_name->csn_cs_len;
|
|
message->msg_util_dbn_len = cs_name->csn_db_len;
|
|
move(cs_name->csn_data, message->msg_util_data, l + 2);
|
|
|
|
if (!CSS_put_message(connection, message, 0) ||
|
|
!(response = CSS_get_message(connection, 0, 0)))
|
|
return;
|
|
|
|
if (!process_response(response, status_vector, 0)) {
|
|
/* Set a flag to indicate that a central server has processed
|
|
the command relating to this server and database name */
|
|
|
|
cs_name->csn_flags |= DBN_cmd_process;
|
|
}
|
|
else if (status_vector[1] != gds__unavailable)
|
|
if (cs_name->csn_flags & DBN_enable) {
|
|
printf
|
|
("Server \"%s\" could not enable database \"%s\" because:\n",
|
|
cs_name->csn_cs_name, cs_name->csn_db_name);
|
|
gds__print_status(status_vector);
|
|
cs_name->csn_flags |= DBN_cmd_process;
|
|
}
|
|
else {
|
|
/**** DO SOMETHING MORE INTELLIGENT ****/
|
|
}
|
|
}
|
|
|
|
|
|
static STATUS process_response(
|
|
MSG_RESP message,
|
|
STATUS * user_status, USHORT * return_length)
|
|
{
|
|
/**************************************
|
|
*
|
|
* p r o c e s s _ r e s p o n s e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Process a response packet from server.
|
|
*
|
|
**************************************/
|
|
TEXT *p, *q;
|
|
STATUS *status, *stuff, *end;
|
|
USHORT l;
|
|
|
|
/* If there isn't a message, the server has disappeared */
|
|
|
|
if (!message || message->msg_resp_header.msg_type != MSG_response) {
|
|
status = user_status;
|
|
*status++ = gds_arg_gds;
|
|
*status++ = gds__random;
|
|
*status++ = gds_arg_string;
|
|
*status++ = (STATUS) "connection lost to central server";
|
|
*status = gds_arg_end;
|
|
return user_status[1];
|
|
}
|
|
|
|
/* Well, at least we got something -- eat it up */
|
|
|
|
p = error_buffer;
|
|
|
|
for (status = user_status, end = status + 20, stuff =
|
|
message->msg_resp_status; (*status = *stuff++) && status < end;)
|
|
switch (*status++) {
|
|
case gds_arg_interpreted:
|
|
case gds_arg_string:
|
|
q = (TEXT *) message + *stuff++;
|
|
*status++ = (STATUS) p;
|
|
while (*p++ = *q++);
|
|
break;
|
|
|
|
case gds_arg_cstring:
|
|
l = *status++ = *stuff++;
|
|
q = (TEXT *) message + *stuff++;
|
|
*status++ = (STATUS) p;
|
|
if (l)
|
|
do
|
|
*p++ = *q++;
|
|
while (--l);
|
|
break;
|
|
|
|
default:
|
|
*status++ = *stuff++;
|
|
}
|
|
|
|
if (!user_status[1] || user_status[1] == gds__segment) {
|
|
if (return_buffer_length < (l = message->msg_resp_length)) {
|
|
if (return_buffer)
|
|
FREE(return_buffer);
|
|
return_buffer = (UCHAR *) ALLOC(0, l);
|
|
return_buffer_length = l;
|
|
}
|
|
if (l)
|
|
move(message->msg_resp_data, return_buffer, l);
|
|
if (return_length)
|
|
*return_length = l;
|
|
}
|
|
|
|
CSS_free_global(message);
|
|
|
|
return user_status[1];
|
|
}
|