8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-22 20:43:02 +01:00

Implemented CORE-2666: Make it possible to use API to do remote backups/restores

This commit is contained in:
alexpeshkoff 2012-06-20 11:47:30 +00:00
parent 4d1651a327
commit 477e14a820
17 changed files with 699 additions and 260 deletions

View File

@ -150,3 +150,44 @@ List of existing trace sessions
with isc_info_svc_line or isc_info_svc_to_eof information items.
See also README.trace_services
4) Services API extension - running gbak at server side with .fbk at the client.
(Alex Peshkov, peshkoff@mail.ru, 2011-2012)
This way of doing backups is specially efficient when one needs to perform
backup/restore operation for database, located on ther server accessed using
internet, due to serious performance instrease.
The simplest way to use this feature is fbsvcmgr. To backup database run
approximately the following:
fbsvcmgr remotehost:service_mgr -user sysdba -password XXX \
action_backup -dbname some.fdb -bkp_file stdout >some.fbk
and to restore it:
fbsvcmgr remotehost:service_mgr -user sysdba -password XXX \
action_restore -dbname some.fdb -bkp_file stdin <some.fbk
Please notice - you can't use "verbose" switch when performing backup because
data channel from server to client is used to deliver blocks of fbk files. You
will get appropriate error message if you try to do it. When restoring database
verbose mode may be used without limitations.
If you want to perform backup/restore from your own program, you should use
services API for it. Backup is very simple - just pass "stdout" as backup file
name to server and use isc_info_svc_to_eof in isc_service_query() call. Data,
returned by repeating calls to isc_service_query() (certainly with
isc_info_svc_to_eof tag) is a stream, representing image of backup file. Restore
is a bit more tricky. Client sends new spb parameter isc_info_svc_stdin to server
in isc_service_query(). If service needs some data in stdin, it returns
isc_info_svc_stdin in query results, followed by 4-bytes value - number of bytes
server is ready to accept from client. (0 value means no more data is needed right
now.) The main trick is that client should NOT send more data than requested by
server - this causes an error "Size of data is more than requested". The data is
sent in next isc_service_query() call in the send_items block, using
isc_info_svc_line tag in tradition form: isc_info_svc_line, 2 bytes length, data.
When server needs next portion, it once more returns non-zero isc_info_svc_stdin
value from isc_service_query().
A sample of how services API should be used for remote backup and restore can be
found in source code of fbsvcmgr.

View File

@ -1255,12 +1255,17 @@ int gbak(Firebird::UtilSvc* uSvc)
// Close the gbak file handles if they still open
for (burp_fil* file = tdgbl->gbl_sw_backup_files; file; file = file->fil_next)
{
if (file->fil_fd != INVALID_HANDLE_VALUE)
close_platf(file->fil_fd);
if (exit_code != FINI_OK &&
(tdgbl->action->act_action == ACT_backup_split || tdgbl->action->act_action == ACT_backup))
if (file->fil_fd != GBAK_STDIN_DESC() && file->fil_fd != GBAK_STDOUT_DESC())
{
unlink_platf(file->fil_name.c_str());
if (file->fil_fd != INVALID_HANDLE_VALUE)
{
close_platf(file->fil_fd);
}
if (exit_code != FINI_OK &&
(tdgbl->action->act_action == ACT_backup_split || tdgbl->action->act_action == ACT_backup))
{
unlink_platf(file->fil_name.c_str());
}
}
}
@ -1840,10 +1845,10 @@ static gbak_action open_files(const TEXT* file1,
}
if (fil->fil_name == "stdout")
{
if (tdgbl->action->act_total >= 2 || fil->fil_next)
if (tdgbl->action->act_total >= 2 || fil->fil_next || sw_verbose)
{
BURP_error(266, true);
// msg 266 standard output is not supported when using split operation
// msg 266 standard output is not supported when using split operation or in verbose mode
flag = QUIT;
break;
}
@ -1855,6 +1860,7 @@ static gbak_action open_files(const TEXT* file1,
#endif
tdgbl->uSvc->setDataMode(true);
fil->fil_fd = GBAK_STDOUT_DESC();
tdgbl->stdIoMode = true;
break;
}
else
@ -1959,10 +1965,13 @@ static gbak_action open_files(const TEXT* file1,
{
fil->fil_fd = GBAK_STDIN_DESC();
tdgbl->file_desc = fil->fil_fd;
tdgbl->stdIoMode = true;
tdgbl->gbl_sw_files = fil->fil_next;
}
else
{
tdgbl->stdIoMode = false;
// open first file
#ifdef WIN_NT
if ((fil->fil_fd = MVOL_open(fil->fil_name.c_str(), MODE_READ, OPEN_EXISTING)) ==

View File

@ -871,10 +871,11 @@ public:
BurpGlobals(Firebird::UtilSvc* us)
: ThreadData(ThreadData::tddGBL),
defaultCollations(*getDefaultMemoryPool()),
flag_on_line(true),
uSvc(us),
verboseInterval(10000),
flag_on_line(true),
firstMap(true),
verboseInterval(10000)
stdIoMode(false)
{
// this is VERY dirty hack to keep current behaviour
memset (&gbl_database_file_name, 0,
@ -886,8 +887,6 @@ public:
// would be set to FINI_OK (==0) in exit_local
}
Firebird::Array<Firebird::Pair<Firebird::NonPooled<Firebird::MetaName, Firebird::MetaName> > >
defaultCollations;
const TEXT* gbl_database_file_name;
TEXT gbl_backup_start_time[30];
bool gbl_sw_verbose;
@ -1034,10 +1033,13 @@ public:
char veryEnd;
//starting after this members must be initialized in constructor explicitly
bool flag_on_line; // indicates whether we will bring the database on-line
Firebird::Array<Firebird::Pair<Firebird::NonPooled<Firebird::MetaName, Firebird::MetaName> > >
defaultCollations;
Firebird::UtilSvc* uSvc;
bool firstMap; // this is the first time we entered get_mapping()
ULONG verboseInterval; // How many records should be backed up or restored before we show this message
bool flag_on_line; // indicates whether we will bring the database on-line
bool firstMap; // this is the first time we entered get_mapping()
bool stdIoMode; // stdin or stdout is used as backup file
};
// CVC: This aux routine declared here to not force inclusion of burp.h with burp_proto.h
@ -1048,16 +1050,7 @@ void BURP_exit_local(int code, BurpGlobals* tdgbl);
const int FINI_DB_NOT_ONLINE = 2;
// I/O definitions
#ifndef IO_BUFFER_SIZE
#ifdef BUFSIZ
const int GBAK_IO_BUFFER_SIZE = (16 * (BUFSIZ));
#else
const int GBAK_IO_BUFFER_SIZE = (16 * (1024));
#endif
#else
const int GBAK_IO_BUFFER_SIZE = (16 * (IO_BUFFER_SIZE));
#endif
const int GBAK_IO_BUFFER_SIZE = SVC_IO_BUFFER_SIZE;
/* Burp will always write a backup in multiples of the following number
* of bytes. The initial value is the smallest which ensures that writes

View File

@ -100,6 +100,7 @@ static void put_numeric(SCHAR, int);
static bool read_header(DESC, ULONG*, USHORT*, bool);
static bool write_header(DESC, ULONG, bool);
static DESC next_volume(DESC, ULONG, bool);
static void mvol_read(int*, UCHAR**);
//____________________________________________________________
@ -109,15 +110,16 @@ FB_UINT64 MVOL_fini_read()
{
BurpGlobals* tdgbl = BurpGlobals::getSpecific();
if (strcmp(tdgbl->mvol_old_file, "stdin") != 0)
if (!tdgbl->stdIoMode)
{
close_platf(tdgbl->file_desc);
}
for (burp_fil* file = tdgbl->gbl_sw_backup_files; file; file = file->fil_next)
for (burp_fil* file = tdgbl->gbl_sw_backup_files; file; file = file->fil_next)
{
if (file->fil_fd == tdgbl->file_desc)
{
if (file->fil_fd == tdgbl->file_desc) {
file->fil_fd = INVALID_HANDLE_VALUE;
}
file->fil_fd = INVALID_HANDLE_VALUE;
}
}
@ -139,15 +141,19 @@ FB_UINT64 MVOL_fini_write(int* io_cnt, UCHAR** io_ptr)
MVOL_write(rec_end, io_cnt, io_ptr);
flush_platf(tdgbl->file_desc);
if (strcmp(tdgbl->mvol_old_file, "stdout") != 0)
if (!tdgbl->stdIoMode)
{
close_platf(tdgbl->file_desc);
for (burp_fil* file = tdgbl->gbl_sw_backup_files; file; file = file->fil_next)
}
for (burp_fil* file = tdgbl->gbl_sw_backup_files; file; file = file->fil_next)
{
if (file->fil_fd == tdgbl->file_desc)
{
if (file->fil_fd == tdgbl->file_desc)
file->fil_fd = INVALID_HANDLE_VALUE;
file->fil_fd = INVALID_HANDLE_VALUE;
}
}
tdgbl->file_desc = INVALID_HANDLE_VALUE;
BURP_free(tdgbl->mvol_io_header);
tdgbl->mvol_io_header = NULL;
@ -255,12 +261,48 @@ void MVOL_init_write(const char* file_name, int* cnt, UCHAR** ptr)
}
//____________________________________________________________
//
// Read a buffer's worth of data. (common)
//
int MVOL_read(int* cnt, UCHAR** ptr)
{
BurpGlobals* tdgbl = BurpGlobals::getSpecific();
if (tdgbl->stdIoMode && tdgbl->uSvc->isService())
{
tdgbl->uSvc->started();
tdgbl->mvol_io_cnt = tdgbl->uSvc->getBytes(tdgbl->mvol_io_buffer, tdgbl->mvol_io_buffer_size);
if (!tdgbl->mvol_io_cnt)
{
BURP_error_redirect(0, 220);
// msg 220 Unexpected I/O error while reading from backup file
}
tdgbl->mvol_io_ptr = tdgbl->mvol_io_buffer;
}
else
{
mvol_read(cnt, ptr);
}
tdgbl->mvol_cumul_count += tdgbl->mvol_io_cnt;
file_not_empty();
if (ptr)
*ptr = tdgbl->mvol_io_ptr + 1;
if (cnt)
*cnt = tdgbl->mvol_io_cnt - 1;
return *tdgbl->mvol_io_ptr;
}
#ifndef WIN_NT
//____________________________________________________________
//
// Read a buffer's worth of data. (non-WIN_NT)
//
int MVOL_read(int* cnt, UCHAR** ptr)
static void mvol_read(int* cnt, UCHAR** ptr)
{
BurpGlobals* tdgbl = BurpGlobals::getSpecific();
@ -295,14 +337,6 @@ int MVOL_read(int* cnt, UCHAR** ptr)
}
}
}
tdgbl->mvol_cumul_count += tdgbl->mvol_io_cnt;
file_not_empty();
*ptr = tdgbl->mvol_io_ptr + 1;
*cnt = tdgbl->mvol_io_cnt - 1;
return *tdgbl->mvol_io_ptr;
}
@ -311,7 +345,7 @@ int MVOL_read(int* cnt, UCHAR** ptr)
//
// Read a buffer's worth of data. (WIN_NT)
//
int MVOL_read(int* cnt, UCHAR** ptr)
static void mvol_read(int* cnt, UCHAR** ptr)
{
BurpGlobals* tdgbl = BurpGlobals::getSpecific();
@ -343,14 +377,6 @@ int MVOL_read(int* cnt, UCHAR** ptr)
// msg 50 unexpected end of file on backup file
}
}
tdgbl->mvol_cumul_count += tdgbl->mvol_io_cnt;
file_not_empty();
*ptr = tdgbl->mvol_io_ptr + 1;
*cnt = tdgbl->mvol_io_cnt - 1;
return *tdgbl->mvol_io_ptr;
}
#endif // !WIN_NT
@ -507,78 +533,22 @@ UCHAR MVOL_write(const UCHAR c, int* io_cnt, UCHAR** io_ptr)
const ULONG size_to_write = BURP_UP_TO_BLOCK(*io_ptr - tdgbl->mvol_io_buffer);
ULONG left = size_to_write;
for (ptr = tdgbl->mvol_io_buffer; left > 0; ptr += cnt, left -= cnt)
if (tdgbl->stdIoMode && tdgbl->uSvc->isService())
{
if (tdgbl->action->act_action == ACT_backup_split)
tdgbl->uSvc->started();
tdgbl->uSvc->putBytes(tdgbl->mvol_io_buffer, left);
left = 0;
}
else
{
for (ptr = tdgbl->mvol_io_buffer; left > 0; ptr += cnt, left -= cnt)
{
// Write to the current file till fil_lingth > 0, otherwise
// switch to the next one
if (tdgbl->action->act_file->fil_length == 0)
{
if (tdgbl->action->act_file->fil_next)
{
close_platf(tdgbl->file_desc);
for (burp_fil* file = tdgbl->gbl_sw_backup_files; file; file = file->fil_next)
{
if (file->fil_fd == tdgbl->file_desc)
file->fil_fd = INVALID_HANDLE_VALUE;
}
tdgbl->action->act_file->fil_fd = INVALID_HANDLE_VALUE;
tdgbl->action->act_file = tdgbl->action->act_file->fil_next;
tdgbl->file_desc = tdgbl->action->act_file->fil_fd;
}
else
{
// This is a last file. Keep writing in a hope that there is
// enough free disk space ...
tdgbl->action->act_file->fil_length = MAX_LENGTH;
}
}
}
const size_t nBytesToWrite =
(tdgbl->action->act_action == ACT_backup_split &&
tdgbl->action->act_file->fil_length < left) ?
tdgbl->action->act_file->fil_length : left;
#ifndef WIN_NT
cnt = write(tdgbl->file_desc, ptr, nBytesToWrite);
#else
DWORD ret = 0;
if (!WriteFile(tdgbl->file_desc, ptr, (DWORD) nBytesToWrite, &cnt, NULL))
{
ret = GetLastError();
}
#endif // !WIN_NT
tdgbl->mvol_io_buffer = tdgbl->mvol_io_data;
if (cnt > 0)
{
tdgbl->mvol_cumul_count += cnt;
file_not_empty();
if (tdgbl->action->act_action == ACT_backup_split)
{
if (tdgbl->action->act_file->fil_length < left)
tdgbl->action->act_file->fil_length = 0;
else
tdgbl->action->act_file->fil_length -= left;
}
}
else
{
if (!cnt ||
#ifndef WIN_NT
errno == ENOSPC || errno == EIO || errno == ENXIO ||
errno == EFBIG)
#else
ret == ERROR_DISK_FULL || ret == ERROR_HANDLE_DISK_FULL)
#endif // !WIN_NT
{
if (tdgbl->action->act_action == ACT_backup_split)
// Write to the current file till fil_lingth > 0, otherwise
// switch to the next one
if (tdgbl->action->act_file->fil_length == 0)
{
// Close the current file and switch to the next one.
// If there is no more specified files left then
// issue an error and give up
if (tdgbl->action->act_file->fil_next)
{
close_platf(tdgbl->file_desc);
@ -587,89 +557,154 @@ UCHAR MVOL_write(const UCHAR c, int* io_cnt, UCHAR** io_ptr)
if (file->fil_fd == tdgbl->file_desc)
file->fil_fd = INVALID_HANDLE_VALUE;
}
tdgbl->action->act_file->fil_fd = INVALID_HANDLE_VALUE;
BURP_print(true, 272, SafeArg() <<
tdgbl->action->act_file->fil_name.c_str() <<
tdgbl->action->act_file->fil_length <<
tdgbl->action->act_file->fil_next->fil_name.c_str());
// msg 272 Warning -- free disk space exhausted for file %s,
// the rest of the bytes (%d) will be written to file %s
tdgbl->action->act_file->fil_next->fil_length +=
tdgbl->action->act_file->fil_length;
tdgbl->action->act_file = tdgbl->action->act_file->fil_next;
tdgbl->file_desc = tdgbl->action->act_file->fil_fd;
}
else
{
// This is a last file. Keep writing in a hope that there is
// enough free disk space ...
tdgbl->action->act_file->fil_length = MAX_LENGTH;
}
}
}
const size_t nBytesToWrite =
(tdgbl->action->act_action == ACT_backup_split &&
tdgbl->action->act_file->fil_length < left) ?
tdgbl->action->act_file->fil_length : left;
#ifndef WIN_NT
cnt = write(tdgbl->file_desc, ptr, nBytesToWrite);
#else
DWORD ret = 0;
if (!WriteFile(tdgbl->file_desc, ptr, (DWORD) nBytesToWrite, &cnt, NULL))
{
ret = GetLastError();
}
#endif // !WIN_NT
tdgbl->mvol_io_buffer = tdgbl->mvol_io_data;
if (cnt > 0)
{
tdgbl->mvol_cumul_count += cnt;
file_not_empty();
if (tdgbl->action->act_action == ACT_backup_split)
{
if (tdgbl->action->act_file->fil_length < left)
tdgbl->action->act_file->fil_length = 0;
else
tdgbl->action->act_file->fil_length -= left;
}
}
else
{
if (!cnt ||
#ifndef WIN_NT
errno == ENOSPC || errno == EIO || errno == ENXIO ||
errno == EFBIG)
#else
ret == ERROR_DISK_FULL || ret == ERROR_HANDLE_DISK_FULL)
#endif // !WIN_NT
{
if (tdgbl->action->act_action == ACT_backup_split)
{
// Close the current file and switch to the next one.
// If there is no more specified files left then
// issue an error and give up
if (tdgbl->action->act_file->fil_next)
{
close_platf(tdgbl->file_desc);
for (burp_fil* file = tdgbl->gbl_sw_backup_files; file; file = file->fil_next)
{
if (file->fil_fd == tdgbl->file_desc)
file->fil_fd = INVALID_HANDLE_VALUE;
}
tdgbl->action->act_file->fil_fd = INVALID_HANDLE_VALUE;
BURP_print(true, 272, SafeArg() <<
tdgbl->action->act_file->fil_name.c_str() <<
tdgbl->action->act_file->fil_length <<
tdgbl->action->act_file->fil_next->fil_name.c_str());
// msg 272 Warning -- free disk space exhausted for file %s,
// the rest of the bytes (%d) will be written to file %s
tdgbl->action->act_file->fil_next->fil_length +=
tdgbl->action->act_file->fil_length;
tdgbl->action->act_file = tdgbl->action->act_file->fil_next;
tdgbl->file_desc = tdgbl->action->act_file->fil_fd;
}
else
{
BURP_error(270, true);
// msg 270 free disk space exhausted
}
cnt = 0;
continue;
}
if (tdgbl->uSvc->isService())
{
BURP_error(270, true);
// msg 270 free disk space exhausted
}
cnt = 0;
continue;
}
if (tdgbl->uSvc->isService())
// Note: there is an assumption here, that if header data is being
// written, it is really being rewritten, so at least all the
// header data will be written
if (left != size_to_write)
{
// Wrote some, move remainder up in buffer.
// NOTE: We should NOT use memcpy here. We're moving overlapped
// data and memcpy does not guanantee the order the data
// is moved in
memcpy(tdgbl->mvol_io_data, ptr, left);
}
left += tdgbl->mvol_io_data - tdgbl->mvol_io_header;
bool full_buffer;
if (left >= tdgbl->mvol_io_buffer_size)
full_buffer = true;
else
full_buffer = false;
tdgbl->file_desc = next_volume(tdgbl->file_desc, MODE_WRITE, full_buffer);
if (full_buffer)
{
left -= tdgbl->mvol_io_buffer_size;
memcpy(tdgbl->mvol_io_data,
tdgbl->mvol_io_header + tdgbl->mvol_io_buffer_size,
left);
tdgbl->mvol_cumul_count += tdgbl->mvol_io_buffer_size;
tdgbl->mvol_io_buffer = tdgbl->mvol_io_data;
}
else
tdgbl->mvol_io_buffer = tdgbl->mvol_io_header;
break;
}
else if (!SYSCALL_INTERRUPTED(errno))
{
BURP_error(270, true);
// msg 270 free disk space exhausted
BURP_error_redirect(0, 221);
// msg 221 Unexpected I/O error while writing to backup file
}
// Note: there is an assumption here, that if header data is being
// written, it is really being rewritten, so at least all the
// header data will be written
if (left != size_to_write)
{
// Wrote some, move remainder up in buffer.
// NOTE: We should NOT use memcpy here. We're moving overlapped
// data and memcpy does not guanantee the order the data
// is moved in
memcpy(tdgbl->mvol_io_data, ptr, left);
}
left += tdgbl->mvol_io_data - tdgbl->mvol_io_header;
bool full_buffer;
if (left >= tdgbl->mvol_io_buffer_size)
full_buffer = true;
else
full_buffer = false;
tdgbl->file_desc = next_volume(tdgbl->file_desc, MODE_WRITE, full_buffer);
if (full_buffer)
{
left -= tdgbl->mvol_io_buffer_size;
memcpy(tdgbl->mvol_io_data,
tdgbl->mvol_io_header + tdgbl->mvol_io_buffer_size,
left);
tdgbl->mvol_cumul_count += tdgbl->mvol_io_buffer_size;
tdgbl->mvol_io_buffer = tdgbl->mvol_io_data;
}
else
tdgbl->mvol_io_buffer = tdgbl->mvol_io_header;
break;
}
else if (!SYSCALL_INTERRUPTED(errno))
{
BURP_error_redirect(0, 221);
// msg 221 Unexpected I/O error while writing to backup file
if (left < cnt) { // this is impossible, but...
cnt = left;
}
}
if (left < cnt) { // this is impossible, but...
cnt = left;
}
} // for
} // for
#ifdef DEBUG
{
int dbg_cnt;
if (debug_on)
{
for (dbg_cnt = 0; dbg_cnt < cnt; dbg_cnt++)
printf("%d,\n", *(ptr + dbg_cnt));
int dbg_cnt;
if (debug_on)
{
for (dbg_cnt = 0; dbg_cnt < cnt; dbg_cnt++)
printf("%d,\n", *(ptr + dbg_cnt));
}
}
}
#endif
}
// After the first block of first volume is written (using a default block size)
// change the block size to one that reflects the user's blocking factor. By
@ -1069,13 +1104,21 @@ static bool read_header(DESC handle, ULONG* buffer_size, USHORT* format, bool in
// Headers are a version number, and a volume number
if (tdgbl->stdIoMode && tdgbl->uSvc->isService())
{
tdgbl->uSvc->started();
tdgbl->mvol_io_cnt = tdgbl->uSvc->getBytes(tdgbl->mvol_io_buffer, tdgbl->mvol_io_buffer_size);
}
else
{
#ifndef WIN_NT
tdgbl->mvol_io_cnt = read(handle, tdgbl->mvol_io_buffer, tdgbl->mvol_actual_buffer_size);
tdgbl->mvol_io_cnt = read(handle, tdgbl->mvol_io_buffer, tdgbl->mvol_actual_buffer_size);
#else
DWORD bytesRead = 0;
ReadFile(handle, tdgbl->mvol_io_buffer, tdgbl->mvol_actual_buffer_size, &bytesRead, NULL);
tdgbl->mvol_io_cnt = bytesRead;
DWORD bytesRead = 0;
ReadFile(handle, tdgbl->mvol_io_buffer, tdgbl->mvol_actual_buffer_size, &bytesRead, NULL);
tdgbl->mvol_io_cnt = bytesRead;
#endif
}
if (!tdgbl->mvol_io_cnt)
BURP_error_redirect(0, 45); // maybe there's a better message

View File

@ -47,6 +47,7 @@
#endif
#include "../burp/split/spit.h"
#include "../common/classes/Switches.h"
#include "../burp/std_desc.h"
#include "../burp/burpswi.h"
#ifdef HAVE_UNISTD_H
@ -61,6 +62,53 @@ static const int mode_read = O_RDONLY;
static const int mode_write = O_WRONLY | O_CREAT;
static const int mask = 0666;
static DESC open_platf(const char* name, int writeFlag)
{
#ifdef WIN_NT
return CreateFile(name, writeFlag ? GENERIC_WRITE : GENERIC_READ, 0, NULL,
writeFlag ? CREATE_ALWAYS : OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
#else
return open(name, writeFlag ? mode_write : mode_read, mask);
#endif
}
static int read_platf(DESC file, void* buf, int count)
{
#ifdef WIN_NT
DWORD act;
if (!ReadFile(file, buf, count, &act, NULL))
{
return -1;
}
return act;
#else
return read(file, buf, count);
#endif
}
static int write_platf(DESC file, const void* buf, int count)
{
#ifdef WIN_NT
DWORD act;
if (!WriteFile(file, buf, count, &act, NULL))
{
return -1;
}
return act;
#else
return write(file, buf, count);
#endif
}
static void close_platf(DESC file)
{
#ifdef WIN_NT
CloseHandle(file);
#else
close(file);
#endif
}
// Definitions for GSPLIT
enum gsplit_option
{
@ -291,7 +339,7 @@ int main( int argc, char* argv[])
switch (sw_replace)
{
case IN_SW_SPIT_SP:
input_file_desc = GBAK_STDIN_DESC;
input_file_desc = GBAK_STDIN_DESC();
ret_cd = gen_multy_bakup_files(file_list, input_file_desc, file_num);
if (ret_cd == FB_FAILURE)
{
@ -610,8 +658,8 @@ static int gen_multy_bakup_files(b_fil* file_list, FILE_DESC input_file_desc, SL
file_size = fl_ptr->b_fil_size - header_rec_len;
file_name = fl_ptr->b_fil_name;
output_fl_desc = open(file_name, mode_write, mask);
if (output_fl_desc == -1)
output_fl_desc = open_platf(file_name, 1);
if (output_fl_desc == INVALID_HANDLE_VALUE)
{
free(io_buffer);
fprintf(stderr, "can not open back up file %s\n", file_name);
@ -681,8 +729,8 @@ static int gen_multy_bakup_files(b_fil* file_list, FILE_DESC input_file_desc, SL
file_size = fl_ptr->b_fil_size - header_rec_len;
file_name = fl_ptr->b_fil_name;
output_fl_desc = open(file_name, mode_write, mask);
if (output_fl_desc == -1)
output_fl_desc = open_platf(file_name, 1);
if (output_fl_desc == INVALID_HANDLE_VALUE)
{
free(io_buffer);
fprintf(stderr, "can not open back up file %s\n", file_name);
@ -793,21 +841,21 @@ static int read_and_write(FILE_DESC input_file_desc,
if (*byte_read + io_size > file_size)
{
last_read_size = (SLONG) (file_size - *byte_read);
read_cnt = read(input_file_desc, *io_buffer, last_read_size);
read_cnt = read_platf(input_file_desc, *io_buffer, last_read_size);
}
else
read_cnt = read(input_file_desc, *io_buffer, io_size);
read_cnt = read_platf(input_file_desc, *io_buffer, io_size);
switch (read_cnt)
{
case 0: // no more data to be read
close(output_fl_desc);
close_platf(output_fl_desc);
*end_of_input = true;
*byte_read = *byte_read + read_cnt;
return FB_SUCCESS;
case -1: // read failed
close(output_fl_desc);
close_platf(output_fl_desc);
fprintf(stderr, "fail to read input from stdin, errno = %d\n", errno);
return FB_FAILURE;
@ -816,12 +864,12 @@ static int read_and_write(FILE_DESC input_file_desc,
break;
}
const SLONG write_cnt = write(output_fl_desc, *io_buffer, read_cnt);
const SLONG write_cnt = write_platf(output_fl_desc, *io_buffer, read_cnt);
switch (write_cnt)
{
case -1: // write failed
close(output_fl_desc);
close_platf(output_fl_desc);
return FB_FAILURE;
default:
@ -829,7 +877,7 @@ static int read_and_write(FILE_DESC input_file_desc,
return FB_SUCCESS;
// write less data than it reads in
close(output_fl_desc);
close_platf(output_fl_desc);
*byte_write = write_cnt;
return FILE_IS_FULL;
}
@ -858,17 +906,17 @@ static int final_read_and_write(FILE_DESC input_file_desc,
*********************************************************************
*/
const SLONG read_cnt = read(input_file_desc, *io_buffer, io_size);
const SLONG read_cnt = read_platf(input_file_desc, *io_buffer, io_size);
switch (read_cnt)
{
case 0: // no more data to be read
close(output_fl_desc);
close_platf(output_fl_desc);
*end_of_input = true;
return FB_SUCCESS;
case -1: // read failed
close(output_fl_desc);
close_platf(output_fl_desc);
fprintf(stderr, "problem when reading input file, errno = %d\n", errno);
return FB_FAILURE;
@ -876,12 +924,12 @@ static int final_read_and_write(FILE_DESC input_file_desc,
break;
}
const SLONG write_cnt = write(output_fl_desc, *io_buffer, read_cnt);
const SLONG write_cnt = write_platf(output_fl_desc, *io_buffer, read_cnt);
switch (write_cnt)
{
case -1: // write failed
close(output_fl_desc);
close_platf(output_fl_desc);
return FB_FAILURE;
default:
@ -889,7 +937,7 @@ static int final_read_and_write(FILE_DESC input_file_desc,
return FB_SUCCESS;
fprintf(stderr, "There is no enough space to write to back up file %s\n", file_name);
close(output_fl_desc);
close_platf(output_fl_desc);
return FB_FAILURE;
}
}
@ -914,7 +962,7 @@ static int join_multy_bakup_files( b_fil* file_list)
*********************************************************************
*/
FILE_DESC output_fl_desc = GBAK_STDOUT_DESC;
FILE_DESC output_fl_desc = GBAK_STDOUT_DESC();
// See comment near the beginning of gen_multy_bakup_files() as it
// also applies to read_and_write_for_join().
@ -974,18 +1022,18 @@ static int read_and_write_for_join(FILE_DESC output_fl_desc,
TEXT num_arr[5], total_arr[5];
header_rec hdr_rec;
FILE_DESC input_fl_desc = open(file_name, mode_read);
FILE_DESC input_fl_desc = open_platf(file_name, mode_read);
if (input_fl_desc == -1)
if (input_fl_desc == INVALID_HANDLE_VALUE)
{
fprintf(stderr, "can not open input file %s\n", file_name);
return FB_FAILURE;
}
int read_cnt = read(input_fl_desc, io_buffer, header_rec_len);
int read_cnt = read_platf(input_fl_desc, io_buffer, header_rec_len);
if (read_cnt != static_cast<int>(header_rec_len))
{
close(input_fl_desc);
close_platf(input_fl_desc);
fprintf(stderr, "progam fails to read gsplit header record in back-up file%s\n", file_name);
return FB_FAILURE;
}
@ -994,7 +1042,7 @@ static int read_and_write_for_join(FILE_DESC output_fl_desc,
SLONG ret_cd = strncmp(char_ptr1, header_rec_name, sizeof(hdr_rec.name) - 1);
if (ret_cd != 0)
{
close(input_fl_desc);
close_platf(input_fl_desc);
fprintf(stderr, "gsplit: expected GSPLIT description record\n");
fprintf(stderr, "gsplit: Exiting before completion due to errors\n");
return FB_FAILURE;
@ -1026,13 +1074,13 @@ static int read_and_write_for_join(FILE_DESC output_fl_desc,
if ((num_int != cnt) || (num_int > *total_int))
{
close(input_fl_desc);
close_platf(input_fl_desc);
fprintf(stderr, "gsplit: join backup file is out of sequence\n");
fprintf(stderr, "gsplit: Exiting before completion due to errors\n");
return FB_FAILURE;
}
read_cnt = read(input_fl_desc, io_buffer, IO_BUFFER_SIZE);
read_cnt = read_platf(input_fl_desc, io_buffer, IO_BUFFER_SIZE);
while (true)
@ -1040,23 +1088,23 @@ static int read_and_write_for_join(FILE_DESC output_fl_desc,
switch (read_cnt)
{
case 0: // no more data to be read
close(input_fl_desc);
close_platf(input_fl_desc);
return FB_SUCCESS;
case -1: // read failed
close(input_fl_desc);
close_platf(input_fl_desc);
return FB_FAILURE;
default: // this is the last read
break;
}
SLONG write_cnt = write(output_fl_desc, io_buffer, read_cnt);
SLONG write_cnt = write_platf(output_fl_desc, io_buffer, read_cnt);
switch (write_cnt)
{
case -1: // write failed
close(input_fl_desc);
close_platf(input_fl_desc);
return FB_FAILURE;
default:
@ -1064,7 +1112,7 @@ static int read_and_write_for_join(FILE_DESC output_fl_desc,
break;
}
read_cnt = read(input_fl_desc, io_buffer, IO_BUFFER_SIZE);
read_cnt = read_platf(input_fl_desc, io_buffer, IO_BUFFER_SIZE);
} // end of while (true) loop
}
@ -1150,11 +1198,11 @@ static int write_header(const b_fil* fl_ptr,
ret_cd = set_hdr_str(header_str, file_name, pos, strlen(file_name));
SLONG end, indx;
SLONG write_cnt = write(output_fl_desc, header_str, header_rec_len);
SLONG write_cnt = write_platf(output_fl_desc, header_str, header_rec_len);
switch (write_cnt)
{
case -1: // write failed
close(output_fl_desc);
close_platf(output_fl_desc);
return FB_FAILURE;
default:
@ -1202,14 +1250,14 @@ static int flush_io_buff(const UCHAR* remaining_io,
SLONG write_cnt;
if (file_size > remaining_io_len)
write_cnt = write(output_fl_desc, remaining_io, remaining_io_len);
write_cnt = write_platf(output_fl_desc, remaining_io, remaining_io_len);
else // file_size <= remaining_io_len
write_cnt = write(output_fl_desc, remaining_io, (unsigned int) file_size);
write_cnt = write_platf(output_fl_desc, remaining_io, (unsigned int) file_size);
switch (write_cnt)
{
case -1: // write failed
close(output_fl_desc);
close_platf(output_fl_desc);
*flush_done = false;
return FB_FAILURE;
@ -1219,7 +1267,7 @@ static int flush_io_buff(const UCHAR* remaining_io,
else
{
// could not write out all remaining data
close(output_fl_desc);
close_platf(output_fl_desc);
*flush_done = false;
}
*byte_write = write_cnt;
@ -1245,18 +1293,18 @@ static int final_flush_io_buff(const UCHAR* remaining_io,
*********************************************************************
*/
SLONG write_cnt = write(output_fl_desc, remaining_io, remaining_io_len);
SLONG write_cnt = write_platf(output_fl_desc, remaining_io, remaining_io_len);
switch (write_cnt)
{
case -1: // write failed
close(output_fl_desc);
close_platf(output_fl_desc);
return FB_FAILURE;
default:
if (write_cnt == remaining_io_len) // write ok
return FB_SUCCESS;
close(output_fl_desc);
close_platf(output_fl_desc);
return FB_FAILURE;
}
}

View File

@ -40,8 +40,3 @@ const int MAX_NUM_OF_FILES = 9999;
const int MIN_FILE_SIZE = M_BYTES;
const char NEW_LINE = '\n';
const char TERMINAL = '\0';
typedef int FILE_DESC;
const FILE_DESC GBAK_STDIN_DESC = 0; // standard input file descriptor
const FILE_DESC GBAK_STDOUT_DESC = 1; // standard output file descriptor

View File

@ -61,4 +61,6 @@ const int INVALID_HANDLE_VALUE = -1;
#endif //WIN_NT
typedef DESC FILE_DESC;
#endif //GBAK_STD_DESC_H

View File

@ -137,6 +137,7 @@ public:
virtual void putSLong(char, SLONG) { }
virtual void putChar(char, char) { }
virtual void putBytes(const UCHAR*, size_t) { }
virtual ULONG getBytes(UCHAR*, ULONG) { return 0; }
virtual void setServiceStatus(const ISC_STATUS*) { }
virtual void setServiceStatus(const USHORT, const USHORT, const MsgFormat::SafeArg&) { }
virtual const ISC_STATUS* getStatus() { return 0; }

View File

@ -63,6 +63,7 @@ public:
virtual void putSLong(char, SLONG) = 0;
virtual void putChar(char, char) = 0;
virtual void putBytes(const UCHAR*, size_t) = 0;
virtual ULONG getBytes(UCHAR*, ULONG) = 0;
virtual void setServiceStatus(const ISC_STATUS*) = 0;
virtual void setServiceStatus(const USHORT, const USHORT, const MsgFormat::SafeArg&) = 0;
virtual const ISC_STATUS* getStatus() = 0;

View File

@ -1143,6 +1143,7 @@ bool isRunningCheck(const UCHAR* items, unsigned int length)
case isc_info_svc_limbo_trans:
case isc_info_svc_running:
case isc_info_svc_get_users:
case isc_info_svc_stdin:
if (state == S_INF)
{
(Firebird::Arg::Gds(isc_random) << "Wrong info items combination").raise();

View File

@ -334,6 +334,7 @@
#define isc_info_svc_running 67 /* Checks to see if a service is running on an attachment */
#define isc_info_svc_get_users 68 /* Returns the user information from isc_action_svc_display_users */
#define isc_info_svc_auth_block 69 /* Sets authentication block for service query() call */
#define isc_info_svc_stdin 78 /* Returns maximum size of data, needed as stdin for service */
/******************************************************

View File

@ -939,7 +939,7 @@ Data source : @4"}, /* eds_statement */
{336331015, "file @1 out of sequence"}, /* gbak_file_outof_sequence */
{336331016, "can't join -- one of the files missing"}, /* gbak_join_file_missing */
{336331017, " standard input is not supported when using join operation"}, /* gbak_stdin_not_supptd */
{336331018, "standard output is not supported when using split operation"}, /* gbak_stdout_not_supptd */
{336331018, "standard output is not supported when using split operation or in verbose mode"}, /* gbak_stdout_not_supptd */
{336331019, "backup file @1 might be corrupt"}, /* gbak_bkup_corrupt */
{336331020, "database file specification missing"}, /* gbak_unk_db_file_spec */
{336331021, "can't write a header record to file @1"}, /* gbak_hdr_write_failed */

View File

@ -119,6 +119,7 @@ const int SVC_user_none = 0;
const int GET_LINE = 1;
const int GET_EOF = 2;
const int GET_BINARY = 4;
const int GET_ONCE = 8;
const char* const SPB_SEC_USERNAME = "isc_spb_sec_username";
@ -433,6 +434,7 @@ void Service::started()
void Service::finish()
{
svc_sem_full.release();
finish(SVC_finished);
}
@ -661,7 +663,7 @@ void Service::need_admin_privs(Arg::StatusVector& status, const char* message)
bool Service::ck_space_for_numeric(UCHAR*& info, const UCHAR* const end)
{
if ((info + 1 + sizeof(ULONG)) > end)
{
{
if (info < end)
*info++ = isc_info_truncated;
return false;
@ -706,7 +708,9 @@ Service::Service(const TEXT* service_name, USHORT spb_length, const UCHAR* spb_d
svc_command_line(getPool()),
svc_network_protocol(getPool()), svc_remote_address(getPool()), svc_remote_process(getPool()),
svc_remote_pid(0), svc_trace_manager(NULL), svc_crypt_callback(crypt_callback),
svc_current_guard(NULL)
svc_current_guard(NULL),
svc_stdin_size_requested(0), svc_stdin_buffer(NULL), svc_stdin_size_preload(0),
svc_stdin_preload_requested(0), svc_stdin_user_size(0)
{
initStatus();
ThreadIdHolder holdId(svc_thread_strings);
@ -1004,6 +1008,8 @@ void Service::shutdownServices()
{
if (all[pos]->svc_flags & SVC_thd_running)
all[pos]->svc_detach_sem.release();
if (all[pos]->svc_stdin_size_requested)
all[pos]->svc_stdin_semaphore.release();
}
for (pos = 0; pos < all.getCount(); )
@ -1035,18 +1041,22 @@ ISC_STATUS Service::query2(thread_db* /*tdbb*/,
UCHAR item;
UCHAR buffer[MAXPATHLEN];
USHORT l, length, version, get_flags;
UCHAR* stdin_request_notification = NULL;
ThreadIdHolder holdId(svc_thread_strings);
// Setup the status vector
Arg::StatusVector status;
ULONG requestFromPut = 0;
try
{
// Process the send portion of the query first.
USHORT timeout = 0;
const UCHAR* items = send_items;
const UCHAR* const end_items = items + send_item_length;
while (items < end_items && *items != isc_info_end)
{
switch ((item = *items++))
@ -1064,10 +1074,10 @@ ISC_STATUS Service::query2(thread_db* /*tdbb*/,
switch (item)
{
case isc_info_svc_line:
put(items, l);
requestFromPut = put(items, l);
break;
case isc_info_svc_message:
put(items - 3, l + 3);
//put(items - 3, l + 3);
break;
case isc_info_svc_timeout:
timeout = (USHORT) gds__vax_integer(items, l);
@ -1311,6 +1321,21 @@ ISC_STATUS Service::query2(thread_db* /*tdbb*/,
} // scope
break;
case isc_info_svc_stdin:
// Check - is stdin data required for server
if (!ck_space_for_numeric(info, end))
{
return 0;
}
*info++ = item;
if (!stdin_request_notification)
{
stdin_request_notification = info;
}
ADD_SPB_NUMERIC(info, 0);
break;
case isc_info_svc_user_dbpath:
if (svc_user_flag & SVC_user_dba)
{
@ -1335,7 +1360,7 @@ ISC_STATUS Service::query2(thread_db* /*tdbb*/,
*info++ = isc_info_truncated;
break;
}
put(&item, 1);
//put(&item, 1);
get(&item, 1, GET_BINARY, 0, &length);
get(buffer, 2, GET_BINARY, 0, &length);
l = (USHORT) gds__vax_integer(buffer, 2);
@ -1380,7 +1405,7 @@ ISC_STATUS Service::query2(thread_db* /*tdbb*/,
break;
case isc_info_svc_total_length:
put(&item, 1);
//put(&item, 1);
get(&item, 1, GET_BINARY, 0, &length);
get(buffer, 2, GET_BINARY, 0, &length);
l = (USHORT) gds__vax_integer(buffer, 2);
@ -1413,6 +1438,11 @@ ISC_STATUS Service::query2(thread_db* /*tdbb*/,
break;
}
if (requestFromPut)
{
get_flags |= GET_ONCE;
}
get(info + 3, end - (info + 5), get_flags, timeout, &length);
// If the read timed out, return the data, if any, & a timeout
@ -1428,7 +1458,7 @@ ISC_STATUS Service::query2(thread_db* /*tdbb*/,
{
*info++ = isc_info_svc_timeout;
}
else
else //if (!svc_stdin_size_requested)
{
if (!length && !(svc_flags & SVC_finished))
{
@ -1458,6 +1488,8 @@ ISC_STATUS Service::query2(thread_db* /*tdbb*/,
const SLONG number = info - start_info;
fb_assert(number > 0);
memmove(start_info + 7, start_info, number);
if (stdin_request_notification)
stdin_request_notification += 7;
USHORT length2 = INF_convert(number, buffer);
fb_assert(length2 == 4); // We only accept SLONG
INF_put_item(isc_info_length, length2, buffer, start_info, end, true);
@ -1470,6 +1502,23 @@ ISC_STATUS Service::query2(thread_db* /*tdbb*/,
recv_item_length, recv_items, res_successful);
}
if (!requestFromPut)
{
requestFromPut = svc_stdin_size_requested;
}
if (requestFromPut)
{
if (stdin_request_notification)
{
ADD_SPB_NUMERIC(stdin_request_notification, requestFromPut);
}
else
{
(Arg::Gds(isc_random) << "No request from user for stdin data").raise();
}
}
if (status.hasData())
{
status.raise();
@ -1538,10 +1587,10 @@ void Service::query(USHORT send_item_length,
switch (item)
{
case isc_info_svc_line:
put(items, l);
//put(items, l);
break;
case isc_info_svc_message:
put(items - 3, l + 3);
//put(items - 3, l + 3);
break;
case isc_info_svc_timeout:
timeout = (USHORT) gds__vax_integer(items, l);
@ -1764,7 +1813,7 @@ void Service::query(USHORT send_item_length,
*info++ = isc_info_truncated;
break;
}
put(&item, 1);
//put(&item, 1);
get(&item, 1, GET_BINARY, 0, &length);
get(buffer, 2, GET_BINARY, 0, &length);
l = (USHORT) gds__vax_integer(buffer, 2);
@ -1809,7 +1858,7 @@ void Service::query(USHORT send_item_length,
break;
case isc_info_svc_total_length:
put(&item, 1);
//put(&item, 1);
get(&item, 1, GET_BINARY, 0, &length);
get(buffer, 2, GET_BINARY, 0, &length);
l = (USHORT) gds__vax_integer(buffer, 2);
@ -2181,19 +2230,29 @@ bool Service::full() const
void Service::enqueue(const UCHAR* s, ULONG len)
{
static int transferCount = 0;
if (checkForShutdown() || (svc_flags & SVC_detached))
{
svc_sem_full.release();
return;
}
while (len)
{
// Wait for space in buffer
bool flagFirst = true;
while (full())
{
THREAD_SLEEP(ENQUEUE_DEQUEUE_DELAY);
if (flagFirst)
{
svc_sem_full.release();
flagFirst = false;
}
svc_sem_empty.tryEnter(1, 0);
if (checkForShutdown() || (svc_flags & SVC_detached))
{
svc_sem_full.release();
return;
}
}
@ -2211,10 +2270,12 @@ void Service::enqueue(const UCHAR* s, ULONG len)
}
memcpy(&svc_stdout[svc_stdout_tail], s, cnt);
transferCount += cnt;
svc_stdout_tail = add_val(svc_stdout_tail, cnt);
s += cnt;
len -= cnt;
}
svc_sem_full.release();
}
@ -2235,6 +2296,7 @@ void Service::get(UCHAR* buffer, USHORT length, USHORT flags, USHORT timeout, US
svc_flags &= ~SVC_timeout;
}
bool flagFirst = true;
while (length)
{
if ((empty() && (svc_flags & SVC_finished)) || checkForShutdown())
@ -2244,7 +2306,24 @@ void Service::get(UCHAR* buffer, USHORT length, USHORT flags, USHORT timeout, US
if (empty())
{
THREAD_SLEEP(ENQUEUE_DEQUEUE_DELAY);
if (svc_stdin_size_requested && (!(flags & GET_BINARY)))
{
// service needs data from user - notify him
break;
}
if (flagFirst)
{
svc_sem_empty.release();
flagFirst = false;
}
if (flags & GET_ONCE)
{
break;
}
svc_sem_full.tryEnter(1, 0);
}
#ifdef HAVE_GETTIMEOFDAY
GETTIMEOFDAY(&end_time);
@ -2264,6 +2343,7 @@ void Service::get(UCHAR* buffer, USHORT length, USHORT flags, USHORT timeout, US
while (head != svc_stdout_tail && length > 0)
{
flagFirst = true;
const UCHAR ch = svc_stdout[head];
head = add_one(head);
length--;
@ -2283,12 +2363,100 @@ void Service::get(UCHAR* buffer, USHORT length, USHORT flags, USHORT timeout, US
svc_stdout_head = head;
}
svc_sem_empty.release();
}
void Service::put(const UCHAR* /*buffer*/, USHORT /*length*/)
ULONG Service::put(const UCHAR* buffer, ULONG length)
{
// Nothing
MutexLockGuard guard(svc_stdin_mutex);
// check length correctness
if (length > svc_stdin_size_requested && length > svc_stdin_preload_requested)
{
(Arg::Gds(isc_random) << "Size of data is more than requested").raise();
}
if (svc_stdin_size_requested) // service waits for data from us
{
svc_stdin_user_size = MIN(length, svc_stdin_size_requested);
memcpy(svc_stdin_buffer, buffer, svc_stdin_user_size);
// reset satisfied request
ULONG blockSize = svc_stdin_size_requested;
svc_stdin_size_requested = 0;
// let data be used
svc_stdin_semaphore.release();
if (length == 0)
{
return 0;
}
// reset used block of data
length -= svc_stdin_user_size;
buffer += svc_stdin_user_size;
if (length == 0) // ask user to preload next block of data
{
if (!svc_stdin_preload)
{
svc_stdin_preload.reset(FB_NEW(getPool()) UCHAR[PRELOAD_BUFFER_SIZE]);
}
svc_stdin_preload_requested = MIN(blockSize, PRELOAD_BUFFER_SIZE);
return svc_stdin_preload_requested;
}
}
// Store data in preload buffer
fb_assert(length <= PRELOAD_BUFFER_SIZE);
fb_assert(length <= svc_stdin_preload_requested);
fb_assert(svc_stdin_size_preload == 0);
memcpy(svc_stdin_preload, buffer, length);
svc_stdin_size_preload = length;
return 0;
}
ULONG Service::getBytes(UCHAR* buffer, ULONG size)
{
{ // Guard scope
MutexLockGuard guard(svc_stdin_mutex);
if (svc_flags & SVC_detached) // no more data for this service please
{
return 0;
}
if (svc_stdin_size_preload != 0) // use data, preloaded by user
{
// Use data from preload buffer
size = MIN(size, svc_stdin_size_preload);
memcpy(buffer, svc_stdin_preload, size);
if (size < svc_stdin_size_preload)
{
// not good, client should not request so small block
svc_stdin_size_preload -= size;
memmove(svc_stdin_preload, svc_stdin_preload + size, svc_stdin_size_preload);
}
else
{
svc_stdin_size_preload = 0;
}
return size;
}
// Request new data portion
svc_stdin_size_requested = size;
svc_stdin_buffer = buffer;
// Wakeup Service::query() if it waits for data from service
svc_sem_full.release();
}
// Wait for data from client
svc_stdin_semaphore.enter();
return svc_stdin_user_size;
}
@ -2308,8 +2476,26 @@ void Service::finish(USHORT flag)
delete this;
return;
}
if (svc_flags & SVC_detached)
{
svc_sem_empty.release();
// if service waits for data from us - return EOF
{ // guard scope
MutexLockGuard guard(svc_stdin_mutex);
if (svc_stdin_size_requested)
{
svc_stdin_user_size = 0;
svc_stdin_semaphore.release();
}
}
}
if (svc_flags & SVC_finished)
{
svc_sem_full.release();
svc_flags &= ~SVC_thd_running;
}
else

View File

@ -38,6 +38,17 @@
#include "../common/UtilSvc.h"
#include "../common/classes/Switches.h"
#include "../common/classes/ClumpletReader.h"
#include "../burp/split/spit.h"
#ifndef IO_BUFFER_SIZE
#ifdef BUFSIZ
const int SVC_IO_BUFFER_SIZE = (16 * (BUFSIZ));
#else // BUFSIZ
const int SVC_IO_BUFFER_SIZE = (16 * (1024));
#endif // BUFSIZ
#else // IO_BUFFER_SIZE
const int SVC_IO_BUFFER_SIZE = (16 * (IO_BUFFER_SIZE));
#endif // IO_BUFFER_SIZE
// forward decl.
@ -121,6 +132,8 @@ public: // utilities interface with service
virtual void putChar(char tag, char val);
// put raw bytes to svc_stdout
virtual void putBytes(const UCHAR*, size_t);
// get raw bytes from svc_stdin
virtual ULONG getBytes(UCHAR*, ULONG);
// append status_vector to service's status
virtual void setServiceStatus(const ISC_STATUS* status_vector);
// append error message to service's status
@ -218,8 +231,9 @@ private:
bool checkForShutdown();
// Transfer data from svc_stdout into buffer
void get(UCHAR* buffer, USHORT length, USHORT flags, USHORT timeout, USHORT* return_length);
// Designed to send output to a service - does nothing.
void put(const UCHAR* buffer, USHORT length);
// Sends stdin for a service
// Returns number of bytes service wants more
ULONG put(const UCHAR* buffer, ULONG length);
// Increment circular buffer pointer
static ULONG add_one(ULONG i);
@ -301,6 +315,8 @@ public:
private:
StatusStringsHelper svc_thread_strings;
Firebird::Semaphore svc_sem_empty, svc_sem_full;
//Service existence guard
class ExistenceGuard
{
@ -318,6 +334,23 @@ private:
Firebird::Mutex svc_existence_lock;
ExistenceGuard* svc_current_guard;
// Data pipe from client to service
Firebird::Semaphore svc_stdin_semaphore;
Firebird::Mutex svc_stdin_mutex;
// Size of data, requested by service (set in getBytes, reset in put)
ULONG svc_stdin_size_requested;
// Buffer passed by service
UCHAR* svc_stdin_buffer;
// Size of data, preloaded by user (set in put, reset in getBytes)
ULONG svc_stdin_size_preload;
// Buffer for datam preloaded by user
Firebird::AutoPtr<UCHAR> svc_stdin_preload;
// Size of data, requested from user to preload (set in getBytes)
ULONG svc_stdin_preload_requested;
// Size of data, placed into svc_stdin_buffer (set in put)
ULONG svc_stdin_user_size;
static const ULONG PRELOAD_BUFFER_SIZE = SVC_IO_BUFFER_SIZE;
};
} //namespace Jrd

View File

@ -2144,7 +2144,7 @@ ERROR: Backup incomplete', NULL, NULL);
('gbak_file_outof_sequence', 'open_files', 'burp.c', NULL, 12, 263, NULL, 'file @1 out of sequence', NULL, NULL);
('gbak_join_file_missing', 'open_files', 'burp.c', NULL, 12, 264, NULL, 'can''t join -- one of the files missing', NULL, NULL);
('gbak_stdin_not_supptd', 'open_files', 'burp.c', NULL, 12, 265, NULL, ' standard input is not supported when using join operation', NULL, NULL);
('gbak_stdout_not_supptd', 'open_files', 'burp.c', NULL, 12, 266, NULL, 'standard output is not supported when using split operation', NULL, NULL);
('gbak_stdout_not_supptd', 'open_files', 'burp.c', NULL, 12, 266, NULL, 'standard output is not supported when using split operation or in verbose mode', NULL, NULL);
('gbak_bkup_corrupt', 'open_files', 'burp.c', NULL, 12, 267, NULL, 'backup file @1 might be corrupt', NULL, NULL);
('gbak_unk_db_file_spec', 'open_files', 'burp.c', NULL, 12, 268, NULL, 'database file specification missing', NULL, NULL);
('gbak_hdr_write_failed', 'MVOL_init_write', 'mvol.c', NULL, 12, 269, NULL, 'can''t write a header record to file @1', NULL, NULL);

View File

@ -38,6 +38,7 @@
#include "../common/utils_proto.h"
#include "../common/classes/MsgPrint.h"
#include "../jrd/license.h"
#include "../burp/std_desc.h"
using namespace Firebird;
@ -481,7 +482,7 @@ const SvcSwitches traceChgStateOptions[] =
const SvcSwitches actionSwitch[] =
{
{"action_backup", putSingleTag, backupOptions, isc_action_svc_backup, isc_info_svc_line},
{"action_backup", putSingleTag, backupOptions, isc_action_svc_backup, isc_info_svc_to_eof},
{"action_restore", putSingleTag, restoreOptions, isc_action_svc_restore, isc_info_svc_line},
{"action_properties", putSingleTag, propertiesOptions, isc_action_svc_properties, 0},
{"action_repair", putSingleTag, repairOptions, isc_action_svc_repair, 0},
@ -533,9 +534,23 @@ bool printLine(const char*& p)
bool printData(const char*& p)
{
static DESC binout = INVALID_HANDLE_VALUE;
if (binout == INVALID_HANDLE_VALUE)
{
binout = GBAK_STDOUT_DESC();
}
string s;
bool rc = getLine(s, p);
printf ("%s", s.c_str());
if (rc)
{
#ifdef WIN_NT
DWORD cnt;
WriteFile(binout, s.c_str(), s.length(), &cnt, NULL);
#else
write(binout, s.c_str(), s.length());
#endif
}
return rc;
}
@ -615,12 +630,14 @@ public:
}
};
bool printInfo(const char* p, UserPrint& up)
bool printInfo(const char* p, size_t pSize, UserPrint& up, ULONG& stdinRq)
{
bool ret = false;
bool ignoreTruncation = false;
stdinRq = 0;
const char* const end = p + pSize;
while (*p != isc_info_end)
while (p < end && *p != isc_info_end)
{
switch (*p++)
{
@ -775,9 +792,10 @@ bool printInfo(const char* p, UserPrint& up)
case isc_info_truncated:
if (!ignoreTruncation)
{
printf("%s\n", getMessage(18).c_str());
return false;
printf("\n%s\n", getMessage(18).c_str());
}
fflush(stdout);
ret = true;
break;
case isc_info_svc_timeout:
@ -785,12 +803,24 @@ bool printInfo(const char* p, UserPrint& up)
ret = true;
break;
case isc_info_svc_stdin:
stdinRq = getNumeric(p);
if (stdinRq > 0)
{
ret = true;
}
break;
default:
status_exception::raise(Arg::Gds(isc_fbsvcmgr_query_err) <<
Arg::Num(static_cast<unsigned char>(p[-1])));
#ifdef DEV_BUILD
abort();
#endif
}
}
fflush(stdout);
return ret;
}
@ -968,6 +998,8 @@ int main(int ac, char** av)
if (spbItems.getBufferLength() > 0)
{
spbItems.insertTag(isc_info_svc_stdin);
// use one second timeout to poll service
char send[16];
char* p = send;
@ -977,10 +1009,53 @@ int main(int ac, char** av)
*p++ = isc_info_end;
char results[maxbuf];
UserPrint up;
UserPrint uPrint;
ULONG stdinRequest = 0;
Array<char> stdinBuffer;
do
{
if (isc_service_query(status, &svc_handle, 0, p - send, send,
char *sendBlock = send;
USHORT sendSize = p - send;
if (stdinRequest)
{
--sendSize;
size_t len = sendSize;
len += (1 + 2 + stdinRequest);
if (len > MAX_USHORT - 1)
{
len = MAX_USHORT - 1;
stdinRequest = len - (1 + 2) - sendSize;
}
sendBlock = stdinBuffer.getBuffer(len + 1);
memcpy(sendBlock, send, sendSize);
static DESC binIn = INVALID_HANDLE_VALUE;
if (binIn == INVALID_HANDLE_VALUE)
{
binIn = GBAK_STDIN_DESC();
}
#ifdef WIN_NT
DWORD n;
if (!ReadFile(binIn, &sendBlock[sendSize + 1 + 2], stdinRequest, &n, NULL))
#else
int n = read(binIn, &sendBlock[sendSize + 1 + 2], stdinRequest);
if (n < 0)
#endif
{
perror("stdin");
break;
}
stdinRequest = n;
sendBlock[sendSize] = isc_info_svc_line;
sendBlock[sendSize + 1] = stdinRequest;
sendBlock[sendSize + 2] = stdinRequest >> 8;
sendBlock [sendSize + 1 + 2 + stdinRequest] = isc_info_end;
sendSize += (1 + 2 + stdinRequest + 1);
stdinRequest = 0;
}
if (isc_service_query(status, &svc_handle, 0, sendSize, sendBlock,
static_cast<USHORT>(spbItems.getBufferLength()),
reinterpret_cast<const char*>(spbItems.getBuffer()),
sizeof(results), results))
@ -989,10 +1064,14 @@ int main(int ac, char** av)
isc_service_detach(status, &svc_handle);
return 1;
}
} while (printInfo(results, up) && !terminated);
} while (printInfo(results, sizeof(results), uPrint, stdinRequest) && !terminated);
}
isc_service_detach(status, &svc_handle);
if (isc_service_detach(status, &svc_handle))
{
isc_print_status(status);
return 1;
}
return 0;
}
catch (const Exception& e)

View File

@ -903,12 +903,18 @@ void TracePluginImpl::appendServiceQueryParams(size_t send_item_length,
case isc_info_svc_to_eof:
recv_query.printf(NEWLINE "\t\t retrieve as much of the server output as will fit in the supplied buffer");
break;
case isc_info_svc_limbo_trans:
recv_query.printf(NEWLINE "\t\t retrieve the limbo transactions");
break;
case isc_info_svc_get_users:
recv_query.printf(NEWLINE "\t\t retrieve the user information");
break;
case isc_info_svc_stdin:
recv_query.printf(NEWLINE "\t\t retrieve the size of data to send to the server");
break;
}
}