mirror of
https://github.com/FirebirdSQL/firebird.git
synced 2025-01-31 09:23:03 +01:00
759 lines
15 KiB
C++
759 lines
15 KiB
C++
/*
|
|
* PROGRAM: JRD Journalling Subsystem
|
|
* MODULE: console.c
|
|
* DESCRIPTION: Journal server console program
|
|
*
|
|
* 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.10.27 Sean Leyne - Code Cleanup, removed obsolete "UNIXWARE" port
|
|
*
|
|
* 2002.10.29 Sean Leyne - Removed obsolete "Netware" port
|
|
*
|
|
*/
|
|
|
|
#include "firebird.h"
|
|
#include "../jrd/ib_stdio.h"
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include "../jrd/common.h"
|
|
#ifdef SCO_UNIX
|
|
#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
#endif
|
|
#include "../jrd/jrd.h"
|
|
#include "../jrd/jrn.h"
|
|
#include "../journal/journal.h"
|
|
#include "../journal/conso_proto.h"
|
|
#include "../journal/gjrn_proto.h"
|
|
#include "../journal/misc_proto.h"
|
|
#include "../jrd/isc_f_proto.h"
|
|
|
|
#ifdef UNIX
|
|
#include <errno.h>
|
|
#define UNIX_JOURNALLING
|
|
#define BSD_SOCKETS
|
|
#endif
|
|
|
|
#ifdef BSD_SOCKETS
|
|
#define BSD_INCLUDES
|
|
#ifndef FCNTL_INCLUDED
|
|
#include <fcntl.h>
|
|
#endif
|
|
#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
#include <netdb.h>
|
|
#include <netinet/in.h>
|
|
#define MAXHOSTNAMELEN 64 /* should be in <sys/param.h> */
|
|
#define HANDLE int
|
|
#endif
|
|
|
|
#ifdef WIN_NT
|
|
#include <windows.h>
|
|
#define TEXT SCHAR
|
|
#endif
|
|
|
|
#define START_COMMAND "status"
|
|
|
|
static void close_connection(SLONG);
|
|
static void error(STATUS, UCHAR *);
|
|
static void expand_dbname(TEXT *);
|
|
|
|
#ifdef BSD_SOCKETS
|
|
static SSHORT find_address(TEXT *, struct sockaddr_in *, SSHORT);
|
|
#endif
|
|
|
|
static ENUM jrnr_t get_reply(SLONG);
|
|
static SLONG open_connection(SCHAR *, SSHORT);
|
|
static void put_command(SLONG, UCHAR *, USHORT, UCHAR *, USHORT);
|
|
|
|
extern CMDS commands[];
|
|
|
|
|
|
int CONSOLE_start_console( int argc, SCHAR ** argv)
|
|
{
|
|
/**************************************
|
|
*
|
|
* C O N S O L E _ s t a r t _ c o n s o l e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Journal console function.
|
|
*
|
|
**************************************/
|
|
JRNH header;
|
|
TEXT **end, directory[MAXPATHLEN], buffer[2 * MAXPATHLEN];
|
|
SLONG channel;
|
|
USHORT cycle, len, length;
|
|
ENUM jrnr_t reply;
|
|
SSHORT single_msg = FALSE;
|
|
SCHAR *msg, *dir;
|
|
USHORT sw_d, sw_v, sw_i;
|
|
TEXT mssg[128];
|
|
|
|
dir = ".";
|
|
msg = NULL;
|
|
sw_i = sw_d = sw_v = FALSE;
|
|
|
|
for (end = argv + argc, argv++; argv < end;) {
|
|
if ((*argv)[0] != '-')
|
|
argv++;
|
|
else {
|
|
MISC_down_case(*argv++, buffer);
|
|
switch (buffer[1]) {
|
|
case 'i':
|
|
sw_i = TRUE;
|
|
break;
|
|
|
|
case 'd':
|
|
sw_d = TRUE;
|
|
break;
|
|
|
|
case 'v':
|
|
sw_v = TRUE;
|
|
break;
|
|
|
|
case 'j':
|
|
if (!(dir = *argv++))
|
|
MISC_print_journal_syntax();
|
|
break;
|
|
|
|
case 'c':
|
|
break;
|
|
|
|
case 's':
|
|
single_msg = TRUE;
|
|
if (!(msg = *argv++))
|
|
MISC_print_journal_syntax();
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
len = ISC_expand_filename(dir, 0, directory);
|
|
if (directory[len - 1] == '/')
|
|
len--;
|
|
#ifdef WIN_NT
|
|
else if (directory[len - 1] == '\\')
|
|
len--;
|
|
#endif
|
|
directory[len++] = '/';
|
|
directory[len] = 0;
|
|
|
|
GJRN_get_msg(CONSOLE_PROG, mssg, NULL, NULL, NULL); /* Msg 215 Journal server console program */
|
|
printf("%s\n", mssg);
|
|
channel = open_connection(directory, FALSE);
|
|
|
|
header.jrnh_type = JRN_CONSOLE;
|
|
header.jrnh_version = JOURNAL_VERSION;
|
|
|
|
put_command(channel, &header, sizeof(header), START_COMMAND,
|
|
sizeof(START_COMMAND) - 1);
|
|
get_reply(channel);
|
|
|
|
if (single_msg == TRUE) {
|
|
MISC_down_case(msg, buffer);
|
|
length = strlen(msg);
|
|
|
|
if (length > strlen(START_COMMAND))
|
|
length = strlen(START_COMMAND);
|
|
|
|
/* attaching puts out status already. So don't do anything */
|
|
|
|
if (strncmp(START_COMMAND, buffer, (int) length)) {
|
|
/* Expand any filename present in the console command */
|
|
|
|
expand_dbname(msg);
|
|
|
|
if (length = strlen(msg)) {
|
|
put_command(channel, &header, sizeof(header), msg, length);
|
|
reply = get_reply(channel);
|
|
}
|
|
}
|
|
close_connection(channel);
|
|
return 0;
|
|
}
|
|
|
|
for (cycle = TRUE; cycle;) {
|
|
printf("gjrn> ");
|
|
if (sw_service_gjrn)
|
|
putc('\001', stdout);
|
|
fflush(stdout);
|
|
if (!gets(buffer))
|
|
break;
|
|
|
|
/* Expand any filename present in the console command */
|
|
|
|
expand_dbname(buffer);
|
|
|
|
if (length = strlen(buffer)) {
|
|
put_command(channel, &header, sizeof(header), buffer, length);
|
|
switch (reply = get_reply(channel)) {
|
|
case jrnr_shutdown:
|
|
case jrnr_exit:
|
|
cycle = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
close_connection(channel);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int CONSOLE_test_server( SCHAR * journal_dir)
|
|
{
|
|
/**************************************
|
|
*
|
|
* C O N S O L E _ t e s t _ s e r v e r
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Checks if a server is running in the
|
|
* specified directory.
|
|
*
|
|
**************************************/
|
|
SLONG channel;
|
|
TEXT directory[MAXPATHLEN];
|
|
USHORT len;
|
|
|
|
len = ISC_expand_filename(journal_dir, 0, directory);
|
|
if (directory[len - 1] == '/')
|
|
len--;
|
|
#ifdef WIN_NT
|
|
else if (directory[len - 1] == '\\')
|
|
len--;
|
|
#endif
|
|
directory[len++] = '/';
|
|
directory[len] = 0;
|
|
|
|
if ((channel = open_connection(directory, TRUE)) < 0)
|
|
return FALSE;
|
|
|
|
close_connection(channel);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
#ifdef BSD_SOCKETS
|
|
static void close_connection( SLONG channel)
|
|
{
|
|
/**************************************
|
|
*
|
|
* c l o s e _ c o n n e c t i o n ( U N I X )
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Journal console program.
|
|
*
|
|
**************************************/
|
|
|
|
if (close(channel) < 0)
|
|
error((STATUS) errno, "close socket");
|
|
}
|
|
#endif
|
|
|
|
|
|
#ifdef WIN_NT
|
|
static void close_connection( SLONG channel)
|
|
{
|
|
/**************************************
|
|
*
|
|
* c l o s e _ c o n n e c t i o n ( W I N _ N T )
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Journal console program.
|
|
*
|
|
**************************************/
|
|
|
|
if (!CloseHandle((HANDLE) channel))
|
|
error(GetLastError(), "CloseHandle (pipe)");
|
|
}
|
|
#endif
|
|
|
|
|
|
#ifdef BSD_SOCKETS
|
|
static void error( STATUS status, UCHAR * string)
|
|
{
|
|
/**************************************
|
|
*
|
|
* e r r o r ( U N I X )
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Report an error and punt.
|
|
*
|
|
**************************************/
|
|
TEXT msg[128];
|
|
|
|
GJRN_get_msg(GEN_ERR, msg, string, NULL, NULL); /* Msg 111 Error occurred during \"%s\" */
|
|
fprintf(stderr, "%s\n", msg);
|
|
perror(string);
|
|
exit(FINI_ERROR);
|
|
}
|
|
#endif
|
|
|
|
|
|
#ifdef WIN_NT
|
|
static void error( STATUS status, UCHAR * string)
|
|
{
|
|
/**************************************
|
|
*
|
|
* e r r o r ( W I N _ N T )
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Report an error and punt.
|
|
*
|
|
**************************************/
|
|
TEXT msg[512];
|
|
SSHORT l;
|
|
|
|
GJRN_get_msg(GEN_ERR, msg, string, NULL, NULL); /* Msg 111 Error occurred during \"%s\" */
|
|
fprintf(stderr, "%s\n", msg);
|
|
|
|
if (!(l = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
|
|
NULL,
|
|
status,
|
|
GetUserDefaultLangID(),
|
|
msg, sizeof(msg), NULL))) {
|
|
GJRN_get_msg(NT_ERR, msg, string, NULL, NULL); /* Msg 211 Windows NT error %d */
|
|
fprintf(stderr, msg, status);
|
|
fprintf(stderr, "\n");
|
|
}
|
|
else
|
|
fprintf(stderr, "%s\n", msg);
|
|
|
|
exit(FINI_ERROR);
|
|
}
|
|
#endif
|
|
|
|
|
|
static void expand_dbname( TEXT * in)
|
|
{
|
|
/**************************************
|
|
*
|
|
* e x p a n d _ d b n a m e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Expand file name in console command. The input string is
|
|
* modified.
|
|
*
|
|
**************************************/
|
|
CMDS *command;
|
|
TEXT cmd[32], operand1[JOURNAL_PATH_LENGTH],
|
|
operand2[JOURNAL_PATH_LENGTH], *p, *q;
|
|
TEXT exp_op[JOURNAL_PATH_LENGTH];
|
|
USHORT n;
|
|
|
|
cmd[0] = operand1[0] = operand2[0] = 0;
|
|
|
|
n = sscanf(in, "%s%s%s", cmd, operand1, operand2);
|
|
|
|
for (command = commands; p = command->cmds_string; command++) {
|
|
q = cmd;
|
|
while (*q && UPPER(*q) == *p++)
|
|
q++;
|
|
if (!*q)
|
|
break;
|
|
}
|
|
|
|
switch (command->cmds_command) {
|
|
case cmd_shutdown:
|
|
case cmd_exit:
|
|
case cmd_status:
|
|
case cmd_help:
|
|
case cmd_version:
|
|
break;
|
|
|
|
case cmd_erase:
|
|
if (!strlen(operand1)) {
|
|
sprintf(in, "%s", "HELP");
|
|
return;
|
|
}
|
|
break;
|
|
|
|
case cmd_drop:
|
|
case cmd_disable:
|
|
case cmd_archive:
|
|
if (!strlen(operand1)) {
|
|
sprintf(in, "%s", "HELP");
|
|
return;
|
|
}
|
|
ISC_expand_filename(operand1, 0, exp_op);
|
|
sprintf(in, "%s %s", cmd, exp_op);
|
|
break;
|
|
|
|
case cmd_reset:
|
|
case cmd_set:
|
|
if ((!strlen(operand1)) || (!strlen(operand2))) {
|
|
sprintf(in, "%s", "HELP");
|
|
return;
|
|
}
|
|
ISC_expand_filename(operand2, 0, exp_op);
|
|
sprintf(in, "%s %s %s", cmd, operand1, exp_op);
|
|
break;
|
|
|
|
default:
|
|
sprintf(in, "%s", "HELP");
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
#ifdef BSD_SOCKETS
|
|
static SSHORT find_address(
|
|
TEXT * filename,
|
|
struct sockaddr_in *address, SSHORT test_only)
|
|
{
|
|
/**************************************
|
|
*
|
|
* f i n d _ a d d r e s s ( U N I X )
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Get the socket address established by the server by
|
|
* looking in the designated files.
|
|
*
|
|
**************************************/
|
|
FILE *file;
|
|
SLONG addr, family, port, version;
|
|
|
|
if (!(file = fopen(filename, "r"))) {
|
|
if (test_only)
|
|
return (-1);
|
|
|
|
error(errno, "journal socket file open");
|
|
}
|
|
|
|
if (fscanf(file, "%ld %ld %ld %ld", &version, &addr, &family, &port) != 4) {
|
|
if (test_only)
|
|
return (-1);
|
|
|
|
error(0, "journal socket file format");
|
|
}
|
|
fclose(file);
|
|
|
|
if (version != JOURNAL_VERSION) {
|
|
if (test_only)
|
|
return (-1);
|
|
|
|
error(0, " address version");
|
|
}
|
|
|
|
address->sin_addr.s_addr = addr;
|
|
address->sin_family = family;
|
|
address->sin_port = port;
|
|
|
|
return TRUE;
|
|
}
|
|
#endif
|
|
|
|
|
|
#ifdef BSD_SOCKETS
|
|
static ENUM jrnr_t get_reply( SLONG channel)
|
|
{
|
|
/**************************************
|
|
*
|
|
* g e t _ r e p l y ( U N I X )
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Format and send off a command.
|
|
*
|
|
**************************************/
|
|
UCHAR *p, buffer[MAXPATHLEN];
|
|
ULONG len, length;
|
|
JRNR *reply;
|
|
int status;
|
|
|
|
p = buffer + sizeof(JRNR);
|
|
|
|
for (;;) {
|
|
reply = (JRNR *) buffer;
|
|
|
|
/* first read enough to find out how much to read */
|
|
|
|
length = sizeof(reply->jrnr_header);
|
|
|
|
if ((len = read(channel, buffer, length)) != length)
|
|
error(errno, "socket read");
|
|
|
|
/* read the rest of the response */
|
|
|
|
length = reply->jrnr_header.jrnh_length - length;
|
|
|
|
if ((len = read(channel, &buffer[len], length)) != length)
|
|
error(errno, "socket read");
|
|
|
|
if (reply->jrnr_response != jrnr_msg)
|
|
return reply->jrnr_response;
|
|
|
|
p[reply->jrnr_page] = 0;
|
|
printf("%s\n", p);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
#ifdef WIN_NT
|
|
static ENUM jrnr_t get_reply( SLONG channel)
|
|
{
|
|
/**************************************
|
|
*
|
|
* g e t _ r e p l y ( W I N _ N T )
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Format and send off a command.
|
|
*
|
|
**************************************/
|
|
UCHAR *p, buffer[MAXPATHLEN];
|
|
ULONG length, len;
|
|
JRNR *reply;
|
|
|
|
p = buffer + sizeof(JRNR);
|
|
|
|
for (;;) {
|
|
reply = (JRNR *) buffer;
|
|
|
|
/* first read enough to find out how much to read */
|
|
|
|
length = sizeof(reply->jrnr_header);
|
|
|
|
if (!ReadFile((HANDLE) channel, buffer, length, &len, NULL) ||
|
|
len != length) error(GetLastError(), "ReadFile (pipe)");
|
|
|
|
/* read the rest of the response */
|
|
|
|
length = reply->jrnr_header.jrnh_length - length;
|
|
|
|
if (!ReadFile((HANDLE) channel, &buffer[len], length, &len, NULL) ||
|
|
len != length)
|
|
error(GetLastError(), "ReadFile (pipe)");
|
|
|
|
if (reply->jrnr_response != jrnr_msg)
|
|
return reply->jrnr_response;
|
|
|
|
p[reply->jrnr_page] = 0;
|
|
printf("%s\n", p);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
#ifdef BSD_SOCKETS
|
|
static SLONG open_connection( SCHAR * filename, SSHORT test_only)
|
|
{
|
|
/**************************************
|
|
*
|
|
* o p e n _ c o n n e c t i o n ( U N I X )
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Open connection to console program.
|
|
* If test_only, we are just testing to
|
|
* see if journal server is running.
|
|
*
|
|
**************************************/
|
|
struct sockaddr_in address;
|
|
HANDLE channel;
|
|
TEXT name[MAXPATHLEN];
|
|
|
|
strcpy(name, filename);
|
|
strcat(name, CONSOLE_ADDR);
|
|
|
|
/* Allocate a port block and initialize a socket for communications */
|
|
|
|
if ((channel = (SLONG) socket(AF_INET, SOCK_STREAM, 0)) < 0)
|
|
error(errno, "socket");
|
|
|
|
memset((SCHAR *) & address, 0, sizeof(address));
|
|
|
|
/* Will return -1 only if test_only is set - otherwise fatal error */
|
|
|
|
if (find_address(name, &address, test_only) < 0)
|
|
return -1;
|
|
|
|
if (connect(channel, &address, sizeof(address)) < 0) {
|
|
if (test_only)
|
|
return (-1);
|
|
|
|
error(errno, "connect console");
|
|
}
|
|
|
|
return channel;
|
|
}
|
|
#endif
|
|
|
|
|
|
#ifdef WIN_NT
|
|
static SLONG open_connection( SCHAR * filename, SSHORT test_only)
|
|
{
|
|
/**************************************
|
|
*
|
|
* o p e n _ c o n n e c t i o n ( W I N _ N T )
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Open connection to console program.
|
|
* If test_only, we are just testing to
|
|
* see if journal server is running.
|
|
*
|
|
**************************************/
|
|
HANDLE channel;
|
|
TEXT name[MAXPATHLEN], *p, *q, c;
|
|
|
|
p = name;
|
|
strcpy(p, "\\\\.\\pipe");
|
|
p += strlen(p);
|
|
|
|
if (*filename != '/' && *filename != '\\')
|
|
*p++ = '\\';
|
|
|
|
while (*filename)
|
|
*p++ = ((c = *filename++) == ':') ? '_' : ((c == '/') ? '\\' : c);
|
|
|
|
strcpy(p, CONSOLE_FILE);
|
|
|
|
while (TRUE) {
|
|
channel = CreateFile(name,
|
|
GENERIC_READ | GENERIC_WRITE,
|
|
0, NULL, OPEN_EXISTING, 0, NULL);
|
|
if (channel != INVALID_HANDLE_VALUE)
|
|
break;
|
|
|
|
if (GetLastError() != ERROR_PIPE_BUSY) {
|
|
if (test_only)
|
|
return (-1);
|
|
|
|
error(GetLastError(), "CreateFile (pipe)");
|
|
}
|
|
|
|
WaitNamedPipe(name, 2000L);
|
|
}
|
|
|
|
return channel;
|
|
}
|
|
#endif
|
|
|
|
|
|
#ifdef BSD_SOCKETS
|
|
static void put_command(
|
|
SLONG channel,
|
|
UCHAR * header,
|
|
USHORT header_length,
|
|
UCHAR * data, USHORT data_length)
|
|
{
|
|
/**************************************
|
|
*
|
|
* p u t _ c o m m a n d ( U N I X )
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Format and send off a command.
|
|
*
|
|
**************************************/
|
|
TEXT *p, buffer[MAXPATHLEN];
|
|
USHORT l;
|
|
JRNH *header_ptr;
|
|
|
|
p = buffer;
|
|
l = header_length + data_length;
|
|
|
|
header_ptr = (JRNH *) header;
|
|
header_ptr->jrnh_length = l;
|
|
|
|
if (header_length)
|
|
do
|
|
*p++ = *header++;
|
|
while (--header_length);
|
|
|
|
if (data_length)
|
|
do
|
|
*p++ = *data++;
|
|
while (--data_length);
|
|
|
|
if (write(channel, buffer, l) < 0)
|
|
error(errno, "write socket");
|
|
}
|
|
#endif
|
|
|
|
|
|
#ifdef WIN_NT
|
|
static void put_command(
|
|
SLONG channel,
|
|
UCHAR * header,
|
|
USHORT header_length,
|
|
UCHAR * data, USHORT data_length)
|
|
{
|
|
/**************************************
|
|
*
|
|
* p u t _ c o m m a n d ( W I N _ N T )
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Format and send off a command.
|
|
*
|
|
**************************************/
|
|
TEXT *p, buffer[MAXPATHLEN];
|
|
ULONG l, len;
|
|
JRNH *header_ptr;
|
|
|
|
p = buffer;
|
|
l = header_length + data_length;
|
|
|
|
header_ptr = (JRNH *) header;
|
|
header_ptr->jrnh_length = l;
|
|
|
|
if (header_length)
|
|
do
|
|
*p++ = *header++;
|
|
while (--header_length);
|
|
|
|
if (data_length)
|
|
do
|
|
*p++ = *data++;
|
|
while (--data_length);
|
|
|
|
if (!WriteFile((HANDLE) channel, buffer, l, &len, NULL) || l != len)
|
|
error(GetLastError(), "WriteFile (pipe)");
|
|
}
|
|
#endif
|