8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-26 07:23:08 +01:00
firebird-mirror/src/csv/csu.cpp
2001-12-24 02:51:06 +00:00

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];
}