Logo Search packages:      
Sourcecode: firebird1.5 version File versions

burp.cpp

/*
 *    PROGRAM:    JRD Backup and Restore Program
 *    MODULE:           burp.c
 *    DESCRIPTION:      Command line interpreter for backup/restore
 *
 * 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): ______________________________________.
 * 2001.07.06 Sean Leyne - Code Cleanup, removed "#ifdef READONLY_DATABASE"
 *                         conditionals, as the engine now fully supports
 *                         readonly databases.
 *
 * 2002.02.15 Sean Leyne - Code Cleanup, removed obsolete "EPSON" defines
 *
 * 2002.10.29 Sean Leyne - Removed obsolete "Netware" port
 *
// 2002.10.30 Sean Leyne - Removed support for obsolete "PC_PLATFORM" define
//
 */

#include "firebird.h"
#include "../jrd/ib_stdio.h"
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include "../jrd/common.h"
#include "../jrd/y_ref.h"
#include "../jrd/ibase.h"
#include <stdarg.h>
#include "../jrd/ibsetjmp.h"
#include "../jrd/msg_encode.h"
#include "../jrd/thd_proto.h"
#include "../jrd/ods.h"             // to get MAX_PAGE_SIZE
#include "../burp/burp.h"
#include "../burp/burpswi.h"
#include "../burp/std_desc.h"
#ifdef WIN_NT
#include <windows.h>
#undef TEXT
#include <winnt.h>
#define TEXT char
#endif

#include "../jrd/license.h"

#include "../jrd/jrd_time.h"
#include "../burp/burp_proto.h"
#include "../burp/backu_proto.h"
#include "../burp/mvol_proto.h"
#include "../burp/resto_proto.h"
#include "../jrd/gds_proto.h"
#include "../jrd/why_proto.h"
#include "../jrd/gdsassert.h"

#ifdef HAVE_CTYPE_H
#include <ctype.h>
#endif

#ifdef SUPERSERVER
#include "../utilities/cmd_util_proto.h"
#endif


#ifdef UNIX
#include <unistd.h>
#endif

#ifdef VMS
#include <descrip.h>
#include <iodef.h>
#include <types.h>
#include <file.h>
#define SWITCH_CHAR     "/"
#endif

#ifndef VMS
#include <fcntl.h>
#endif

#if (defined WIN_NT)
#include <io.h>
#endif

#ifndef O_CREAT
#include <sys/types.h>
#include <sys/file.h>
#endif

// The following structure in only needed if we are building a local exe
// I've commented it out to make it clear since this global variable is
// defined in burp.cpp as well, and is not relevant for SUPERSERVER
// MOD 23-July-2002

#ifndef SUPERSERVER
struct tgbl *gdgbl;
#endif

#ifndef FOPEN_WRITE_TYPE
#define FOPEN_WRITE_TYPE      "w"
#endif

#ifndef FOPEN_READ_TYPE
#define FOPEN_READ_TYPE       "r"
#endif

#ifndef OPEN_MASK
#define OPEN_MASK       0666
#endif

#ifndef SWITCH_CHAR
#define SWITCH_CHAR     "-"
#endif


#define OUTPUT_SUPPRESS "SUPPRESS"
#define BURP_MSG_FAC    12

static void close_out_transaction(volatile SSHORT, isc_tr_handle *);
//static void enable_signals(void);
//static void excp_handler(void);
static SLONG get_number(SCHAR *);
static ULONG get_size(SCHAR *, FIL);
static SSHORT open_files(SCHAR *, SCHAR **, USHORT, USHORT, USHORT);
#ifdef SUPERSERVER
static int output_netware(SLONG, UCHAR *);
#else
static int output_main(SLONG, UCHAR *);
#endif
static int output_svc(SLONG, UCHAR *);
static void burp_output(const SCHAR *, ...) ATTRIBUTE_FORMAT(1,2);

#ifndef     SUPERSERVER
static int api_gbak(int, char**, USHORT, TEXT*, TEXT*, TEXT *, BOOLEAN, BOOLEAN);
#endif

#define QUIT            0
#define BACKUP          1
#define RESTORE         2
#define FDESC           3

#define     DB          tdgbl->db_handle

#define KBYTE     1024
#define     MBYTE KBYTE * KBYTE
#define GBYTE     MBYTE * KBYTE

#if defined (WIN95)
static BOOL fAnsiCP = FALSE;
#define TRANSLATE_CP(a) if (!fAnsiCP) AnsiToOem(a, a)
#else
#define TRANSLATE_CP(a)
#endif


#ifdef SUPERSERVER
int main_gbak(SVC service)
{
/**************************************
 *
 *    m a i n _ g b a k
 *
 **************************************
 *
 * Functional description
 *    Netware entry point for GBAK.
 *
 **************************************/
      int exit_code;

      exit_code = BURP_gbak(service->svc_argc, service->svc_argv,
                                      output_netware, (SLONG) service);

/* Mark service thread as finished. */
/* If service is detached, cleanup memory being used by service. */
      SVC_finish(service, SVC_finished);

      return exit_code;
}


static int output_netware(SLONG output_data, UCHAR* output_buf)
{
/**************************************
 *
 *    o u t p u t _ n e t w a r e
 *
 **************************************
 *
 * Functional description
 *    Routine which is passed to GBAK for calling back when there is output.
 *
 **************************************/
      SVC_fprintf((SVC) output_data, "%s", output_buf);

      return 0;
}


void BURP_svc_error(USHORT errcode,
                              USHORT arg1_t,
                              void *arg1,
                              USHORT arg2_t,
                              void *arg2,
                              USHORT arg3_t,
                              void *arg3,
                              USHORT arg4_t,
                              void *arg4,
                              USHORT arg5_t,
                              void *arg5)
{
/**************************************
 *
 *      B U R P _ s v c _ e r r o r
 *
 **************************************
 *
 * Functional description
 *
 **************************************/
      TGBL tdgbl;
      ISC_STATUS *status;

      tdgbl = GET_THREAD_DATA;

      status = tdgbl->service_blk->svc_status;

      CMD_UTIL_put_svc_status(status, BURP_MSG_FAC, errcode,
                                          arg1_t, arg1, arg2_t, arg2, arg3_t, arg3,
                                          arg4_t, arg4, arg5_t, arg5);

      SVC_STARTED(tdgbl->service_blk);
      BURP_msg_partial(256, 0, 0, 0, 0, 0);     /* msg 256: gbak: ERROR: */
      BURP_msg_put(errcode,
                        reinterpret_cast<TEXT*>(arg1),
                        reinterpret_cast<TEXT*>(arg2),
                        reinterpret_cast<TEXT*>(arg3),
                        reinterpret_cast<TEXT*>(arg4),
                        reinterpret_cast<TEXT*>(arg5));
      BURP_abort();
}


#else // SUPERSERVER

int CLIB_ROUTINE main(int argc, char* argv[])
{
/**************************************
 *
 *    m a i n
 *
 **************************************
 *
 * Functional description
 *    Parse and interpret command line, then "do the right thing."
 *
 **************************************/
      int exit_code;
      USHORT total;
      TEXT **end, **argvp, *string, *p, *q, c;
      IN_SW_TAB in_sw_tab;
      BOOLEAN flag_restore, flag_verbose, err;
      TEXT *sw_user, *sw_password, *sw_service;
      TEXT *d_user, *d_password, *d_service;

/* If a "-service" switch is specified then use Admin APIs */
      argvp = argv;
      end = argvp + argc;
      argvp++;

/* Initialize data */
      total = 0;
      flag_restore = flag_verbose = err = FALSE;
      sw_user = sw_password = sw_service = d_user = d_password = d_service = NULL;

/* Parse the command line for the -USER, -PASSWORD, -SERVICE,
   and -VERBOSE options. Calculate a length for the new command line to be
   passed to a server using services APIs */

      while (argvp < end && !err)
      {
            string = *argvp++;
            if (*string != '-') {
                  total += strlen(string) + 1;
                  continue;
            }
            if (!string[1])
                  string = "-*NONE*";
            for (in_sw_tab = burp_in_sw_table;
                  q = in_sw_tab->in_sw_name;
                  in_sw_tab++)
            {
                  for (p = string + 1; c = *p++;)
                        if (UPPER(c) != *q++)
                              break;
                  if (!c)
                        break;
            }
            switch (in_sw_tab->in_sw)
            {
            case IN_SW_BURP_C:            /* create database */
            case IN_SW_BURP_R:            /* replace database */
                  total += strlen(string) + 1;
                  flag_restore = TRUE;
                  break;
            case IN_SW_BURP_USER:   /* default user name */
                  if (argvp >= end)
                        err = TRUE;
                  else {
                        sw_user = string;
                        d_user = *argvp++;
                  }
                  break;
            case IN_SW_BURP_PASS:   /* default password */
                  if (argvp >= end)
                        err = TRUE;
                  else {
                        sw_password = string;
                        d_password = *argvp++;
                  }
                  break;
            case IN_SW_BURP_SE:     /* service name */
                  if (argvp >= end) {
                        err = TRUE;
                  } else {
                        sw_service = string;
                        d_service = *argvp++;
                  }
                  break;
            case IN_SW_BURP_V:            /* verify actions */
                  total += strlen(string) + 1;
                  flag_verbose = TRUE;
                  break;
            default:
                  total += strlen(string) + 1;
                  break;
            }
      }

      if (sw_service && !err)
      {
            /* Backup/restore operations will be running as a service thread.
             * To make this more efficiently the isc_spb_options is used.
             * This allows us to skip a conversion from the gbak command line
             * switches to service parameter block in here as well as vice versa
             * conversion within svc.c
             *
             * If -USER and -PASSWORD switches are used by the user within
             * the gbak command line then we have to eliminate them from there. The
             * password will be encrypted and added along with the user name
             * within SVC_start function later on. We shall also eliminate
             * the -SERVER switch because the switch has already been processed.
             */

            if (sw_user)
                  *sw_user = '\0';
            if (sw_password)
                  *sw_password = '\0';
            if (sw_service)
                  *sw_service = '\0';

            exit_code = api_gbak(argc, argv, total, d_password,
                                           d_user, d_service, flag_restore, flag_verbose);
      }
      else
            exit_code = BURP_gbak(argc, argv, output_main, (SLONG) NULL);

      return exit_code;
}


static int output_main( SLONG output_data, UCHAR * output_buf)
{
/**************************************
 *
 *    o u t p u t _ m a i n
 *
 **************************************
 *
 * Functional description
 *    Routine which is passed to GBAK for calling back when there is output.
 *
 **************************************/
      ib_fprintf(ib_stderr, "%s", output_buf);
      return 0;
}

#endif      // SUPERSERVER


static int output_svc( SLONG output_data, UCHAR * output_buf)
{
/**************************************
 *
 *    o u t p u t _ s v c
 *
 **************************************
 *
 * Functional description
 *    Routine which is passed to GBAK for calling back when there is output
 *      if gbak is run as a service
 *
 **************************************/
      ib_fprintf(ib_stdout, "%s", output_buf);
      return 0;
}

extern "C" {

int DLL_EXPORT BURP_gbak(int        argc,
                                     char*            argv[],
                                     OUTPUTPROC output_proc,
                                     SLONG            output_data)
{
/**************************************
 *
 *    B U R P _ g b a k
 *
 **************************************
 *
 * Functional description
 *    Routine called by command line utility, services API, and server manager.
 *
 **************************************/
      TEXT *file1, *file2, **end, *string, *p, *q, c, *device, *redirect;
      UCHAR *dpb;

      IN_SW_TAB in_sw_tab;
      FIL file, file_list, next_file;
      int temp, result;
      time_t clock;
      SLONG redir_in, redir_out, redir_err;
      volatile SSHORT action = QUIT;
      USHORT                              sw_replace;
      USHORT                              sw_tape;
      volatile struct tgbl*   tdgbl;
      JMP_BUF                             env;
      IB_FILE*                      tmp_outfile;

/* TMN: This variable should probably be removed, but I left it in */
/* in case some platform should redefine the BURP SET_THREAD_DATA. */
/*struct tgbl     thd_context;*/

      tdgbl = (struct tgbl *) gds__alloc(sizeof(*tdgbl));
/* NOMEM: return error, FREE: during function exit in the SETJMP */
      if (tdgbl == NULL)
      {
            SVC service;
            service = (SVC) output_data;
            SVC_STARTED(service);
            return FINI_ERROR;
      }

      SET_THREAD_DATA;
      SVC_PUTSPECIFIC_DATA;
      memset((void *) tdgbl, 0, sizeof(*tdgbl));
      tdgbl->burp_env = reinterpret_cast<UCHAR*>(env);
      tdgbl->file_desc = INVALID_HANDLE_VALUE;
      tdgbl->output_proc = output_proc;
      tdgbl->output_data = output_data;

/* Initialize static data. */
      for (in_sw_tab = burp_in_sw_table; in_sw_tab->in_sw_name; in_sw_tab++) {
            in_sw_tab->in_sw_state = FALSE;
      }

      try {

#ifdef VMS
      argc = VMS_parse(&argv, argc);
#endif

/* Perform some special handling when run as an Interbase service.  The
   first switch can be "-svc" (lower case!) or it can be "-svc_re" followed
   by 3 file descriptors to use in re-directing stdin, stdout, and stderr.

   If this utility is started as a thread in the engine, then the first switch
   will be "-svc_thd".
*/
      tdgbl->gbl_sw_service_gbak = FALSE;
      tdgbl->gbl_sw_service_thd = FALSE;
      tdgbl->service_blk = NULL;
      tdgbl->status = const_cast<long* volatile>(tdgbl->status_vector);

      if (argc > 1 && !strcmp(argv[1], "-svc")) {
            tdgbl->gbl_sw_service_gbak = TRUE;
            argv++;
            argc--;
      }
      else if (argc > 1 && !strcmp(argv[1], "-svc_thd")) {
            tdgbl->gbl_sw_service_gbak = TRUE;
            tdgbl->gbl_sw_service_thd = TRUE;
            tdgbl->service_blk = (SVC) output_data;
            tdgbl->status = tdgbl->service_blk->svc_status;
            argv++;
            argc--;
      }
      else if (argc > 4 && !strcmp(argv[1], "-svc_re")) {
            tdgbl->gbl_sw_service_gbak = TRUE;
            tdgbl->output_proc = output_svc;
            redir_in = atol(argv[2]);
            redir_out = atol(argv[3]);
            redir_err = atol(argv[4]);
#ifdef WIN_NT
#if defined (WIN95)
            fAnsiCP = TRUE;
#endif
            redir_in = _open_osfhandle(redir_in, 0);
            redir_out = _open_osfhandle(redir_out, 0);
            redir_err = _open_osfhandle(redir_err, 0);
#endif
            if (redir_in != 0)
                  if (dup2((int) redir_in, 0))
                        close((int) redir_in);
            if (redir_out != 1)
                  if (dup2((int) redir_out, 1))
                        close((int) redir_out);
            if (redir_err != 2)
                  if (dup2((int) redir_err, 2))
                        close((int) redir_err);
            argv += 4;
            argc -= 4;
      }

#if defined (WIN95)
      if (!fAnsiCP)
            fAnsiCP = (GetConsoleCP() == GetACP());
#endif


      sw_replace = sw_tape = FALSE;

      tdgbl->gbl_sw_compress = TRUE;
      tdgbl->gbl_sw_convert_ext_tables = FALSE;
      tdgbl->gbl_sw_transportable = TRUE;
      tdgbl->gbl_sw_ignore_limbo = FALSE;
      tdgbl->gbl_sw_blk_factor = 0;
      tdgbl->gbl_sw_no_reserve = FALSE;
      tdgbl->gbl_sw_mode = FALSE;
      tdgbl->gbl_sw_skip_count = 0;
      tdgbl->action = NULL;
      dpb = const_cast<UCHAR*>(tdgbl->dpb_string);
      tdgbl->dpb_length = 0;
      file1 = file2 = NULL;
      file = file_list = NULL;
      tdgbl->io_buffer_size = GBAK_IO_BUFFER_SIZE;
      end = argv + argc;
      ++argv;

      while (argv < end) {
            string = *argv;
            temp = strlen(string) - 1;
            if (string[temp] == ',')
                  string[temp] = '\0';

            if (*string != '-') {
                  if (!file || file->fil_length || !get_size(*argv, file)) {
                        /*  Miserable thing must be a filename
                           (dummy in a length for the backup file */

                        file = (FIL) BURP_ALLOC_ZERO(FIL_LEN);
                        file->fil_name = string;
                        file->fil_fd = INVALID_HANDLE_VALUE;
                        if (!file_list)
                              file->fil_length = MAX_LENGTH;
                        else
                              file->fil_length = 0;
                        file->fil_next = file_list;
                        file_list = file;
                  }
                  argv++;
            }
            else {
                  ++argv;
                  if (!string[1])
                        string = "-*NONE*";
                  for (in_sw_tab = burp_in_sw_table; q = in_sw_tab->in_sw_name;
                         in_sw_tab++) {
                        for (p = string + 1; c = *p++;)
                              if (UPPER(c) != *q++)
                                    break;
                        if (!c)
                              break;
                  }
                  in_sw_tab->in_sw_state = TRUE;
                  if (!in_sw_tab->in_sw) {
                        BURP_print(137, string + 1, 0, 0, 0, 0);  /* msg 137  unknown switch %s */
                        BURP_print(95, 0, 0, 0, 0, 0);      /* msg 95  legal switches are */
                        for (in_sw_tab = burp_in_sw_table; in_sw_tab->in_sw;
                               in_sw_tab++)
                              if (in_sw_tab->in_sw_msg) {
                                    BURP_msg_put(in_sw_tab->in_sw_msg, (void*)SWITCH_CHAR, 0, 0,
                                                       0, 0);
                              }

                        BURP_print(132, 0, 0, 0, 0, 0);     /* msg 132 switches can be abbreviated to one character */
                        BURP_error(1, 0, 0, 0, 0, 0); /* msg 1: found unknown switch */
                  }
                  else if (in_sw_tab->in_sw == IN_SW_BURP_S) {
                        if (argv >= end)
                              BURP_error(200, 0, 0, 0, 0, 0);
                        /* msg 200: missing parameter for the number of bytes to be skipped */
                        tdgbl->gbl_sw_skip_count = get_number(*argv);
                        if (!tdgbl->gbl_sw_skip_count)
                              BURP_error(201, *argv, 0, 0, 0, 0);
                        /* msg 201: expected number of bytes to be skipped, encountered "%s" */
                        argv++;
                  }
                  else if (in_sw_tab->in_sw == IN_SW_BURP_P) {
                        if (argv >= end)
                              BURP_error(2, 0, 0, 0, 0, 0); /* msg 2 page size parameter missing */
                        tdgbl->gbl_sw_page_size = (USHORT) get_number(*argv);
                        if (!tdgbl->gbl_sw_page_size)
                              BURP_error(12, *argv, 0, 0, 0, 0);  /* msg 12 expected page size, encountered "%s" */
                        argv++;
                  }
                  else if (in_sw_tab->in_sw == IN_SW_BURP_BU) {
                        if (argv >= end)
                              BURP_error(258, 0, 0, 0, 0, 0);     /* msg 258 page buffers parameter missing */
                        tdgbl->gbl_sw_page_buffers = get_number(*argv);
                        if (!tdgbl->gbl_sw_page_buffers)
                              BURP_error(259, *argv, 0, 0, 0, 0); /* msg 259 expected page buffers, encountered "%s" */
                        argv++;
                  }
                  else if (in_sw_tab->in_sw == IN_SW_BURP_MODE) {
                        if (argv >= end)
                              BURP_error(279, 0, 0, 0, 0, 0);     /* msg 279: "read_only" or "read_write" required */
                        string = *argv++;
                        if (!strcmp(string, BURP_SW_MODE_RO))
                              tdgbl->gbl_sw_mode_val = TRUE;
                        else if (!strcmp(string, BURP_SW_MODE_RW))
                              tdgbl->gbl_sw_mode_val = FALSE;
                        else
                              BURP_error(279, 0, 0, 0, 0, 0);     /* msg 279: "read_only" or "read_write" required */
                        tdgbl->gbl_sw_mode = TRUE;
                  }
                  else if (in_sw_tab->in_sw == IN_SW_BURP_PASS) {
                        if (argv >= end)
                              BURP_error(189, 0, 0, 0, 0, 0);     /* password parameter missing */
                        tdgbl->gbl_sw_password = *argv++;
                  }
                  else if (in_sw_tab->in_sw == IN_SW_BURP_USER) {
                        if (argv >= end)
                              BURP_error(188, 0, 0, 0, 0, 0);     /* user name parameter missing */
                        tdgbl->gbl_sw_user = *argv++;
                  }
                  else if (in_sw_tab->in_sw == IN_SW_BURP_ROLE) {
                        if (argv >= end)
                              BURP_error(253, 0, 0, 0, 0, 0);     /* SQL role parameter missing */
                        tdgbl->gbl_sw_sql_role = *argv++;
                  }
                  else if (in_sw_tab->in_sw == IN_SW_BURP_FA) {
                        if (argv >= end)
                              BURP_error(182, 0, 0, 0, 0, 0);     /* msg 182 blocking factor parameter missing */
                        tdgbl->gbl_sw_blk_factor =
                              (volatile USHORT) get_number(*argv);
                        if (!tdgbl->gbl_sw_blk_factor)
                              BURP_error(183, *argv, 0, 0, 0, 0); /* msg 183 expected blocking factor, encountered "%s"  */
                        argv++;
                  }
                  else if (in_sw_tab->in_sw == IN_SW_BURP_SE) {
                        if (argv >= end) {
                              BURP_error(273, 0, 0, 0, 0, 0);     /* msg 273: service name parameter missing */
                        }
                        in_sw_tab->in_sw_state = FALSE;
                        ++argv;                 /* skip a service specification */
                  }
                  else if (in_sw_tab->in_sw == IN_SW_BURP_D) {
                        device = *argv;
                        if (argv >= end)  /* device may equal NULL */
                              device = NULL;
                        else if (*device == '-')
                              device = NULL;
                        else
                              ++argv;
                  }
                  /* want to do output redirect handling now instead of waiting */
                  else if (in_sw_tab->in_sw == IN_SW_BURP_Y) {
                        redirect = *argv;
                        if (argv >= end)  /* redirect may equal NULL */
                              redirect = NULL;
                        else if (*redirect == '-')
                              redirect = NULL;
                        else
                              ++argv;
                        if (!redirect)
                              BURP_error(4, 0, 0, 0, 0, 0); /* msg 4 redirect location for output is not specified */

                        p = redirect;
                        string = OUTPUT_SUPPRESS;
                        tdgbl->sw_redirect = NOOUTPUT;
                        while (c = *p++) {
                              if (UPPER(c) != *string++) {
                                    tdgbl->sw_redirect = TRUE;
                                    break;
                              }
                        }
                        if (tdgbl->sw_redirect == TRUE) {   /* not FALSE, and not NOOUTPUT */

                              /* Make sure the status file doesn't already exist */
                              if (tmp_outfile = ib_fopen(redirect, FOPEN_READ_TYPE)) {
                                    BURP_print(66, redirect, 0, 0, 0, 0);
                                    /* msg 66 can't open status and error output file %s */
                                    ib_fclose(tmp_outfile);
                                    EXIT(FINI_ERROR);
                              }
                              if (!
                                    (tdgbl->output_file =
                                     ib_fopen(redirect, FOPEN_WRITE_TYPE))) {
                                    BURP_print(66, redirect, 0, 0, 0, 0);
                                    /* msg 66 can't open status and error output file %s */
                                    EXIT(FINI_ERROR);
                              }
                        }
                  }                             /*else if (in_sw_tab->in_sw == IN_SW_BURP_Y) */
            }                                   /* else */
      }                                         /* while (argv < end) */

/* reverse the linked list of file blocks */

      tdgbl->gbl_sw_files = NULL;

      for (file = file_list; file; file = next_file) {
            next_file = file->fil_next;
            file->fil_next = tdgbl->gbl_sw_files;
            tdgbl->gbl_sw_files = file;
      }

/* pop off the obviously boring ones, plus do some checking */

      for (file = tdgbl->gbl_sw_files; file; file = file->fil_next) {
            if (!file1)
                  file1 = file->fil_name;
            else if (!file2)
                  file2 = file->fil_name;
            for (file_list = file->fil_next; file_list;
                   file_list = file_list->fil_next) {
                  if (!strcmp(file->fil_name, file_list->fil_name))
                        BURP_error(9, 0, 0, 0, 0, 0); /* msg 9 mutiple sources or destinations specified */
            }

      }

/* Initialize 'dpb' and 'dpb_length' */
      *dpb++ = gds_dpb_version1;
      *dpb++ = isc_dpb_gbak_attach;
      *dpb++ = strlen(GDS_VERSION);
      for (q = GDS_VERSION; *q;)
            *dpb++ = *q++;
      tdgbl->dpb_length = dpb - tdgbl->dpb_string;

      for (in_sw_tab = burp_in_sw_table; in_sw_tab->in_sw_name; in_sw_tab++) {
            if (in_sw_tab->in_sw_state) {
                  switch (in_sw_tab->in_sw) {
                  case (IN_SW_BURP_B):
                        if (sw_replace)
                              BURP_error(5, 0, 0, 0, 0, 0); /* msg 5 conflicting switches for backup/restore */
                        sw_replace = IN_SW_BURP_B;
                        break;

                  case (IN_SW_BURP_C):
                        if (sw_replace == IN_SW_BURP_B)
                              BURP_error(5, 0, 0, 0, 0, 0); /* msg 5 conflicting switches for backup/restore */
                        if (sw_replace != IN_SW_BURP_R)
                              sw_replace = IN_SW_BURP_C;
                        break;

                  case (IN_SW_BURP_CO):
                        tdgbl->gbl_sw_convert_ext_tables = TRUE;
                        break;

                  case (IN_SW_BURP_E):
                        tdgbl->gbl_sw_compress = FALSE;
                        break;

                  case (IN_SW_BURP_G):
                        if (!tdgbl->dpb_length)
                              *dpb++ = gds_dpb_version1;
                        *dpb++ = gds_dpb_no_garbage_collect;
                        *dpb++ = 0;
                        tdgbl->dpb_length = dpb - tdgbl->dpb_string;
                        break;

                  case (IN_SW_BURP_I):
                        tdgbl->gbl_sw_deactivate_indexes = TRUE;
                        break;

                  case (IN_SW_BURP_IG):
                        if (!tdgbl->dpb_length)
                              *dpb++ = gds_dpb_version1;
                        *dpb++ = gds_dpb_damaged;
                        *dpb++ = 1;
                        *dpb++ = 1;
                        tdgbl->dpb_length = dpb - tdgbl->dpb_string;
                        break;

                  case (IN_SW_BURP_K):
                        tdgbl->gbl_sw_kill = TRUE;
                        break;

                  case (IN_SW_BURP_L):
                        tdgbl->gbl_sw_ignore_limbo = TRUE;
                        break;

                  case (IN_SW_BURP_M):
                        tdgbl->gbl_sw_meta = TRUE;
                        break;

                  case (IN_SW_BURP_MODE):
                        tdgbl->gbl_sw_mode = TRUE;
                        break;

                  case (IN_SW_BURP_N):
                        tdgbl->gbl_sw_novalidity = TRUE;
                        break;

                  case (IN_SW_BURP_NT):   /* Backup non-transportable format */
                        tdgbl->gbl_sw_transportable = FALSE;
                        break;

                  case (IN_SW_BURP_O):
                        tdgbl->gbl_sw_incremental = TRUE;
                        break;

                  case (IN_SW_BURP_OL):
                        tdgbl->gbl_sw_old_descriptions = TRUE;
                        break;

                  case (IN_SW_BURP_PASS):
                        if (!tdgbl->dpb_length)
                              *dpb++ = gds_dpb_version1;
                        if (!tdgbl->gbl_sw_service_gbak)
                              *dpb++ = gds_dpb_password;
                        else
                              *dpb++ = gds_dpb_password_enc;
                        *dpb++ = strlen(tdgbl->gbl_sw_password);
                        for (q = tdgbl->gbl_sw_password; *q;)
                              *dpb++ = *q++;
                        tdgbl->dpb_length = dpb - tdgbl->dpb_string;
                        break;

                  case (IN_SW_BURP_R):
                        if (sw_replace == IN_SW_BURP_B)
                              BURP_error(5, 0, 0, 0, 0, 0); /* msg 5 conflicting switches for backup/restore */
                        sw_replace = IN_SW_BURP_R;
                        break;

                  case (IN_SW_BURP_T):
                        tdgbl->gbl_sw_transportable = TRUE;
                        break;

                  case (IN_SW_BURP_U):
                        BURP_error(7, 0, 0, 0, 0, 0); /* msg 7 protection isn't there yet */
                        break;

                  case (IN_SW_BURP_US):
                        tdgbl->gbl_sw_no_reserve = TRUE;
                        break;

                  case (IN_SW_BURP_ROLE):
                        if (!tdgbl->dpb_length)
                              *dpb++ = isc_dpb_version1;
                        *dpb++ = isc_dpb_sql_role_name;
                        *dpb++ = strlen(tdgbl->gbl_sw_sql_role);
                        for (q = tdgbl->gbl_sw_sql_role; *q;)
                              *dpb++ = *q++;
                        tdgbl->dpb_length = dpb - tdgbl->dpb_string;
                        break;

                  case (IN_SW_BURP_USER):
                        if (!tdgbl->dpb_length)
                              *dpb++ = gds_dpb_version1;
                        *dpb++ = gds_dpb_user_name;
                        *dpb++ = strlen(tdgbl->gbl_sw_user);
                        for (q = tdgbl->gbl_sw_user; *q;)
                              *dpb++ = *q++;
                        tdgbl->dpb_length = dpb - tdgbl->dpb_string;
                        break;

                  case (IN_SW_BURP_V):
                        tdgbl->gbl_sw_verbose = TRUE;
                        break;

                  case (IN_SW_BURP_Z):
                        BURP_print(91, (void*) GDS_VERSION, 0, 0, 0, 0);      /* msg 91 gbak version %s */
                        tdgbl->gbl_sw_version = TRUE;
                        break;

                  default:
                        break;
                  }
            }
      }

      if (!sw_replace)
            sw_replace = IN_SW_BURP_B;

      if (tdgbl->gbl_sw_page_size)
      {
            if (sw_replace == IN_SW_BURP_B)
                  BURP_error(8, 0, 0, 0, 0, 0); /* msg 8 page size is allowed only on restore or create */
            temp = tdgbl->gbl_sw_page_size;
            {
                  int curr_pg_size = 1024;
                  while (curr_pg_size <= MAX_PAGE_SIZE) {
                        if (temp <= curr_pg_size) {
                              temp = curr_pg_size;
                              break;
                        }
                        curr_pg_size <<= 1;
                  }
            }
            if (temp > MAX_PAGE_SIZE)
            {
#ifdef SUPERSERVER
                  BURP_svc_error(   3,
                                          isc_arg_number,
                                          reinterpret_cast<void*>(tdgbl->gbl_sw_page_size),
                                          0, NULL, 0, NULL,
                                          0, NULL, 0, NULL);
#else
                  /* msg 3 Page size specified (%ld) greater than limit (MAX_PAGE_SIZE bytes) */
                  BURP_error(3, (void*) (ULONG) tdgbl->gbl_sw_page_size, 0, 0, 0, 0);
#endif
            }
            if (temp != tdgbl->gbl_sw_page_size) {
                  BURP_print(103, (TEXT *) (ULONG) tdgbl->gbl_sw_page_size
                                 , (TEXT *) (SLONG) temp,
                                 0, 0, 0);      /* msg 103 page size specified (%ld bytes) rounded up to %ld bytes */
                  tdgbl->gbl_sw_page_size = temp;
            }
      }

      if (tdgbl->gbl_sw_page_buffers) {
            if (sw_replace == IN_SW_BURP_B)
                  BURP_error(260, 0, 0, 0, 0, 0);     /* msg 260 page buffers is allowed only on restore or create */
      }

      if (!tdgbl->gbl_sw_blk_factor || sw_replace != IN_SW_BURP_B)
            tdgbl->gbl_sw_blk_factor = 1;

      if (!file2)
            BURP_error(10, 0, 0, 0, 0, 0);      /* msg 10 requires both input and output filenames */

      if (!strcmp(file1, file2))
            BURP_error(11, 0, 0, 0, 0, 0);      /* msg 11 input and output have the same name.  Disallowed. */

      clock = time(NULL);
      strcpy(const_cast<char*>(tdgbl->gbl_backup_start_time), ctime(&clock));
      p =
            const_cast<char*>(tdgbl->gbl_backup_start_time +
                        strlen(const_cast<const char*>(tdgbl->gbl_backup_start_time)) - 1);
      if (*p == '\n')
            *p = 0;

      tdgbl->action = (ACT) BURP_ALLOC_ZERO(ACT_LEN);
      tdgbl->action->act_total = 0;
      tdgbl->action->act_file = NULL;
      tdgbl->action->act_action = ACT_unknown;

      action =
            open_files(file1, &file2, tdgbl->gbl_sw_verbose, sw_replace, sw_tape);

      MVOL_init(tdgbl->io_buffer_size);

      switch (action) {
      case (RESTORE):
            SVC_STARTED(tdgbl->service_blk);
            result = RESTORE_restore(file1, file2);
            break;

      case (BACKUP):
            SVC_STARTED(tdgbl->service_blk);
            result = BACKUP_backup(file1, file2);
            break;

      case (QUIT):
            SVC_STARTED(tdgbl->service_blk);
            BURP_abort();
            break;
      }
      if (result != FINI_OK && result != FINI_DB_NOT_ONLINE)
            BURP_abort();

      EXIT(result);
      return result;
      }     // try

      catch (const std::exception&)
      {
            int exit_code;
            UCHAR *mem;

            /* All calls to EXIT(), normal and error exits, wind up here */

            tdgbl->burp_env = NULL;
            exit_code = tdgbl->exit_code;

            /* Close the gbak file handles if they still open */
            for (file = tdgbl->gbl_sw_backup_files; file; file = file->fil_next) {
                  if (file->fil_fd != INVALID_HANDLE_VALUE)
                        CLOSE(file->fil_fd);
                  if (exit_code != 0
                        && (tdgbl->action->act_action == ACT_backup_split
                              || tdgbl->action->act_action == ACT_backup))
                        UNLINK(file->fil_name);
            }

            /* Detach from database to release system resources */
            if (tdgbl->db_handle != 0) {
                  close_out_transaction(action,
                                                  const_cast<isc_tr_handle*>(&tdgbl->tr_handle));
                  close_out_transaction(action,
                                                  const_cast<isc_tr_handle*>(&tdgbl->global_trans));
                  if (isc_detach_database(const_cast<ISC_STATUS*>(tdgbl->status_vector),
                                                      const_cast<isc_db_handle*>(&tdgbl->db_handle)))
                  {
                        BURP_print_status(const_cast<ISC_STATUS*>(tdgbl->status_vector));
                  }
            }

            /* Close the status output file */
            if (tdgbl->sw_redirect == TRUE && tdgbl->output_file != NULL) {
                  ib_fclose(tdgbl->output_file);
                  tdgbl->output_file = NULL;
            }

            /* Free all unfreed memory used by Gbak itself */
            while (tdgbl->head_of_mem_list != NULL) {
                  mem = tdgbl->head_of_mem_list;
                  tdgbl->head_of_mem_list = *((UCHAR **) tdgbl->head_of_mem_list);
                  gds__free(mem);
            }

            RESTORE_THREAD_DATA;
            if (tdgbl != NULL) {
                  gds__free((SLONG *) tdgbl);
            }

#if defined(DEBUG_GDS_ALLOC) && !defined(SUPERSERVER)
            gds_alloc_report(0, __FILE__, __LINE__);
#endif

            /* All returns occur from this point - even normal returns */
            return exit_code;
      }
}



void BURP_abort(void)
{
/**************************************
 *
 *    B U R P _ a b o r t
 *
 **************************************
 *
 * Functional description
 *    Abandon a failed operation.
 *
 **************************************/
      TGBL tdgbl;

      tdgbl = GET_THREAD_DATA;

      BURP_print(83, 0, 0, 0, 0, 0);      /* msg 83 Exiting before completion due to errors */

#ifdef SUPERSERVER
      SVC_STARTED(tdgbl->service_blk);
#endif

      EXIT(FINI_ERROR);
}


void BURP_error(USHORT errcode,
                        const void* arg1,
                        const void* arg2,
                        const void* arg3,
                        const void* arg4,
                        const void* arg5)
{
/**************************************
 *
 *     B U R P _ e r r o r
 *
 **************************************
 *
 * Functional description
 *    Format and print an error message, then punt.
 *
 **************************************/
#ifdef SUPERSERVER
      TGBL tdgbl;
      ISC_STATUS *status;

      tdgbl = GET_THREAD_DATA;
      status = tdgbl->service_blk->svc_status;

      CMD_UTIL_put_svc_status(status, BURP_MSG_FAC, errcode,
                                          isc_arg_string, arg1,
                                          isc_arg_string, arg2,
                                          isc_arg_string, arg3,
                                          isc_arg_string, arg4, isc_arg_string, arg5);
      SVC_STARTED(tdgbl->service_blk);
#endif

      BURP_msg_partial(256, 0, 0, 0, 0, 0);     /* msg 256: gbak: ERROR: */
      BURP_msg_put(errcode, arg1, arg2, arg3, arg4, arg5);
      BURP_abort();
}


void BURP_print_status( ISC_STATUS * status_vector)
{
/**************************************
 *
 *    B U R P _ p r i n t _ s t a t u s
 *
 **************************************
 *
 * Functional description
 *    Print error message. Use isc_interprete
 *    to allow redirecting output.
 *
 **************************************/
#ifdef SUPERSERVER
      TGBL tdgbl;
      ISC_STATUS *status;
      int i = 0, j;
#endif
      ISC_STATUS *vector;
      SCHAR s[1024];

      if (status_vector) {
            vector = status_vector;
#ifdef SUPERSERVER
            tdgbl = GET_THREAD_DATA;
            status = tdgbl->service_blk->svc_status;
            if (status != status_vector) {
                  while (*status && (++i < ISC_STATUS_LENGTH))
                        status++;
                  for (j = 0; status_vector[j] && (i < ISC_STATUS_LENGTH); j++, i++)
                        *status++ = status_vector[j];
            }
#endif

            if (isc_interprete(s, &vector)) {
                  TRANSLATE_CP(s);
                  BURP_msg_partial(256, 0, 0, 0, 0, 0);     /* msg 256: gbak: ERROR: */
                  burp_output("%s\n", s);
                  while (isc_interprete(s, &vector)) {
                        TRANSLATE_CP(s);
                        BURP_msg_partial(256, 0, 0, 0, 0, 0);     /* msg 256: gbak: ERROR: */
                        burp_output("    %s\n", s);
                  }
            }
      }
}


void BURP_print_warning( ISC_STATUS * status_vector)
{
/**************************************
 *
 *    B U R P _ p r i n t _ w a r n i n g
 *
 **************************************
 *
 * Functional description
 *    Print warning message. Use isc_interprete
 *    to allow redirecting output.
 *
 **************************************/
      ISC_STATUS *vector;
      SCHAR s[1024];

      if (status_vector) {
            /* skip the error, assert that one does not exist */
            assert(status_vector[0] == gds_arg_gds);
            assert(status_vector[1] == 0);
            /* print the warning message */
            vector = &status_vector[2];
            if (isc_interprete(s, &vector)) {
                  TRANSLATE_CP(s);
                  BURP_msg_partial(255, 0, 0, 0, 0, 0);     /* msg 255: gbak: WARNING: */
                  burp_output("%s\n", s);
                  while (isc_interprete(s, &vector)) {
                        TRANSLATE_CP(s);
                        BURP_msg_partial(255, 0, 0, 0, 0, 0);     /* msg 255: gbak: WARNING: */
                        burp_output("    %s\n", s);
                  }
            }
      }
}


void BURP_error_redirect(     ISC_STATUS* status_vector,
                                          USHORT errcode,
                                          void* arg1,
                                          void* arg2)
{
/**************************************
 *
 *    B U R P _ e r r o r _ r e d i r e c t
 *
 **************************************
 *
 * Functional description
 *    Issue error message. Output messages then abort.
 *
 **************************************/

      BURP_print_status(status_vector);
      BURP_error(errcode, arg1, arg2, NULL, NULL, NULL);
}


void BURP_msg_partial(  USHORT number,
                                    const void* arg1,
                                    const void* arg2,
                                    const void* arg3,
                                    const void* arg4,
                                    const void* arg5)
{
/**************************************
 *
 *    B U R P _ m s g _ p a r t i a l
 *
 **************************************
 *
 * Functional description
 *    Retrieve a message from the error file,
 *    format it, and print it without a newline.
 *
 **************************************/
      TEXT buffer[256];

      gds__msg_format(NULL,
                              BURP_MSG_FAC,
                              number,
                              sizeof(buffer),
                              buffer,
                              static_cast<const char*>(arg1),
                              static_cast<const char*>(arg2),
                              static_cast<const char*>(arg3),
                              static_cast<const char*>(arg4),
                              static_cast<const char*>(arg5));
      burp_output("%s", buffer);
}


void BURP_msg_put(      USHORT number,
                              const void* arg1,
                              const void* arg2,
                              const void* arg3,
                              const void* arg4,
                              const void* arg5)
{
/**************************************
 *
 *    B U R P _ m s g _ p u t
 *
 **************************************
 *
 * Functional description
 *    Retrieve a message from the error file, format it, and print it.
 *
 **************************************/
      TEXT buffer[256];

      gds__msg_format(NULL,
                              BURP_MSG_FAC,
                              number,
                              sizeof(buffer),
                              buffer,
                              static_cast<const char*>(arg1),
                              static_cast<const char*>(arg2),
                              static_cast<const char*>(arg3),
                              static_cast<const char*>(arg4),
                              static_cast<const char*>(arg5));
      TRANSLATE_CP(buffer);
      burp_output("%s\n", buffer);
}


void BURP_msg_get(      USHORT number,
                              void* msg,
                              void* arg1,
                              void* arg2,
                              void* arg3,
                              void* arg4,
                              void* arg5)
{
/**************************************
 *
 *    B U R P _ m s g _ g e t
 *
 **************************************
 *
 * Functional description
 *    Retrieve a message from the error file, format it and copy it to the buffer
 *
 **************************************/
      TEXT buffer[128];

      gds__msg_format(NULL,
                              BURP_MSG_FAC,
                              number,
                              sizeof(buffer),
                              buffer,
                              reinterpret_cast<char*>(arg1),
                              reinterpret_cast<char*>(arg2),
                              reinterpret_cast<char*>(arg3),
                              reinterpret_cast<char*>(arg4),
                              reinterpret_cast<char*>(arg5));
      strcpy(reinterpret_cast<char*>(msg), buffer);
}


void BURP_output_version( TEXT * arg1, TEXT * arg2)
{
/**************************************
 *
 *    B U R P _ o u t p u t _ v e r s i o n
 *
 **************************************
 *
 * Functional description
 *    Callback routine for access method
 *    printing (specifically show version);
 *    will accept.
 *
 **************************************/

      burp_output(arg1, arg2);
}


void BURP_print(USHORT number,
                        const void* arg1,
                        const void* arg2,
                        const void* arg3,
                        const void* arg4,
                        const void* arg5)
{
/**************************************
 *
 *    B U R P _ p r i n t
 *
 **************************************
 *
 * Functional description
 *    Display a formatted error message
 *    in a way that VMS or civilized systems
 *    will accept.
 *
 **************************************/

      BURP_msg_partial(169, 0, 0, 0, 0, 0);     /* msg 169: gbak: */
      BURP_msg_put(number, arg1, arg2, arg3, arg4, arg5);
}


void BURP_verbose(USHORT number,
                          const void* arg1,
                          const void* arg2,
                          const void* arg3,
                          const void* arg4,
                          const void* arg5)
{
/**************************************
 *
 *    B U R P _ v e r b o s e
 *
 **************************************
 *
 * Functional description
 *    Calls BURP_print for displaying a formatted error message
 *    but only for verbose output.  If not verbose then calls
 *    user defined yieding function.
 *
 **************************************/

      TGBL tdgbl = GET_THREAD_DATA;

      if (tdgbl->gbl_sw_verbose)
            BURP_print(number, arg1, arg2, arg3, arg4, arg5);
      else
            burp_output("%s","");
}

}     // extern "C"


static void close_out_transaction(volatile  SSHORT action,
                                                  isc_tr_handle* handle)
{
/**************************************
 *
 *    c l o s e _ o u t _ t r a n s a c t i o n
 *
 **************************************
 *
 * Functional description
 *    Make a transaction go away.  This is
 *    important as we need to detach from the
 *    database so all engine allocated memory is
 *    returned to the system.
 *
 **************************************/
      ISC_STATUS_ARRAY status_vector;

      if (*handle != 0) {
            if (action == RESTORE) {
                  /* Even if the restore failed, commit the transaction so that
                   * a partial database is at least recovered.
                   */
                  isc_commit_transaction(status_vector, handle);
                  if (status_vector[1]) {
                        /* If we can't commit - have to roll it back, as
                         * we need to close all outstanding transactions before
                         * we can detach from the database.
                         */
                        isc_rollback_transaction(status_vector, handle);
                        if (status_vector[1])
                              BURP_print_status(status_vector);
                  }
            }
            else
                  /* A backup shouldn't touch any data - we ensure that
                   * by never writing data during a backup, but let's double
                   * ensure it by doing a rollback
                   */
            if (isc_rollback_transaction(status_vector, handle))
                  BURP_print_status(status_vector);
      }
}


static SLONG get_number( SCHAR * string)
{
/**************************************
 *
 *    g e t _ n u m b e r
 *
 **************************************
 *
 * Functional description
 *    Convert a string to binary, complaining bitterly if
 *    the string is bum.
 *
 **************************************/
      SCHAR c, *p;
      SLONG value;

      for (value = 0, p = string; c = *p++;) {
            if (c < '0' || c > '9')
                  return 0;
            value *= 10;
            value += c - '0';
      }

      return value;
}


static SSHORT open_files(TEXT * file1,
                                     TEXT ** file2,
                                     USHORT sw_verbose, USHORT sw_replace, USHORT sw_tape)
{
/**************************************
 *
 *    o p e n _ f i l e s
 *
 **************************************
 *
 * Functional description
 *    from the input file names and
 *    positions, guess what the users
 *    intention was.  Return the type
 *    of the first file, plus open file
 *    and db handle.
 *
 **************************************/
      ISC_STATUS *status_vector;
      TGBL tdgbl;
      FIL fil;
      int seq, total;
      SSHORT flag;


      tdgbl = GET_THREAD_DATA;
      status_vector = tdgbl->status;

/* try to attach the database using the first file_name */

      if (sw_replace != IN_SW_BURP_C && sw_replace != IN_SW_BURP_R)
            if (!(isc_attach_database(status_vector,
                                                  (SSHORT) 0,
                                                  file1,
                                                  &tdgbl->db_handle,
                                                  tdgbl->dpb_length,
                                                  reinterpret_cast<char*>(tdgbl->dpb_string))))
            {
                  if (sw_replace != IN_SW_BURP_B) {
                        /* msg 13 REPLACE specified, but the first file %s is a database */
                        BURP_error(13, file1, 0, 0, 0, 0);
                        if (isc_detach_database(status_vector, &tdgbl->db_handle)) {
                              BURP_print_status(status_vector);
                        }
                        return QUIT;
                  }
                  if (tdgbl->gbl_sw_version) {
                        /* msg 139 Version(s) for database "%s" */
                        BURP_print(139, file1, 0, 0, 0, 0);
                        isc_version(&tdgbl->db_handle,
                                          reinterpret_cast<void (*)()>(BURP_output_version),
                                          (void*) "\t%s\n");
                  }
                  if (sw_verbose)
                        BURP_print(166, file1, 0, 0, 0, 0); /* msg 166: readied database %s for backup */
            }
            else if (sw_replace == IN_SW_BURP_B ||
                         (status_vector[1] != gds_io_error
                          && status_vector[1] != gds_bad_db_format)) {
                  BURP_print_status(status_vector);
                  return QUIT;
            }

      if (sw_replace == IN_SW_BURP_B) {

            /* Now it is safe to skip a db file */
            tdgbl->gbl_sw_backup_files = tdgbl->gbl_sw_files->fil_next;
            tdgbl->gbl_sw_files = tdgbl->gbl_sw_files->fil_next;
            assert(strcmp(tdgbl->gbl_sw_files->fil_name, *file2) == 0);

            flag = BACKUP;
            tdgbl->action->act_action = ACT_backup;
            for (fil = tdgbl->gbl_sw_files; fil; fil = fil->fil_next)
            {
                  /* adjust the file size first */
                  switch (fil->fil_size_code)
                  {
                  case size_n:
                        break;
                  case size_k:
                        fil->fil_length *= KBYTE;
                        break;
                  case size_m:
                        fil->fil_length *= MBYTE;
                        break;
                  case size_g:
                        fil->fil_length *= GBYTE;
                        break;
                  case size_e:
                        BURP_error(262, fil->fil_name, 0, 0, 0, 0);     /* msg 262 size specification either missing or incorrect for file %s  */
                        break;
                  default:
                        assert(FALSE);
                        break;
                  }

                  if ((fil->fil_seq = ++(tdgbl->action->act_total)) >= 2)
                  {
                        tdgbl->action->act_action = ACT_backup_split;
                  }
                  if (sw_verbose)
                  {
                        BURP_print(75, fil->fil_name, 0, 0, 0, 0);      /* msg 75  creating file %s */
                  }
                  if (!strcmp(fil->fil_name, "stdout"))
                  {
                        if (tdgbl->action->act_total >= 2 || fil->fil_next)
                        {
                              BURP_error(266, 0, 0, 0, 0, 0);     /* msg 266 standard output is not supported when using split operation */
                              flag = QUIT;
                              break;
                        }
                        /* We ignore SIGPIPE so that we can report an IO error when we
                         * try to write to the broken pipe.
                         */
#ifndef WIN_NT
                        signal(SIGPIPE, SIG_IGN);
#endif
                        fil->fil_fd = reinterpret_cast<DESC>(GBAK_STDOUT_DESC);
                        break;
                  }
                  else
                  {

#ifdef WIN_NT
                        if ((fil->fil_fd = MVOL_open(fil->fil_name, MODE_WRITE,
                                                                   CREATE_ALWAYS)) == INVALID_HANDLE_VALUE)
#else
                        if ((fil->fil_fd = open(fil->fil_name, MODE_WRITE, OPEN_MASK)) == -1)
#endif /* WIN_NT */

                        {

#ifdef SUPERSERVER
                              BURP_svc_error(65, isc_arg_string, fil->fil_name,
                                                   0, NULL, 0, NULL, 0, NULL, 0, NULL);
#endif
                              BURP_print(65, fil->fil_name, 0, 0, 0, 0);      /* msg 65 can't
                                                                                                   open backup file %s */
                              flag = QUIT;
                              break;
                        }
                  }

                  if (fil->fil_length == 0)
                  {
                        if (fil->fil_next)
                        {
                              BURP_error(262, fil->fil_name, 0, 0, 0, 0);     /* msg 262 size specification either missing or incorrect for file %s  */
                              flag = QUIT;
                              break;
                        }
                        else
                        {
                              fil->fil_length = MAX_LENGTH; /* Write as much as possible to
                                                                                 the last file */
                        }
                  }
                  if (fil->fil_length < MIN_SPLIT_SIZE)
                  {
#ifdef SUPERSERVER
                        BURP_svc_error(   271,
                                                isc_arg_number,
                                                reinterpret_cast<void*>(fil->fil_length),
                                                isc_arg_number,
                                                reinterpret_cast<void*>(MIN_SPLIT_SIZE),
                                                0, NULL, 0, NULL, 0, NULL);
#else
                        BURP_error(271, (TEXT *) fil->fil_length,
                                       (TEXT *) (SLONG) MIN_SPLIT_SIZE, 0, 0, 0);
                        /* msg file size given (%d) is less than minimum allowed (%d) */
#endif
                        flag = QUIT;
                        break;
                  }
            }

            if (flag == BACKUP)
            {
                  tdgbl->action->act_file = tdgbl->gbl_sw_files;
                  tdgbl->file_desc = tdgbl->gbl_sw_files->fil_fd;
            }
            else
            {
                  if (isc_detach_database(status_vector, &tdgbl->db_handle))
                  {
                        BURP_print_status(status_vector);
                  }
            }

            return flag;
      }


/*
 * If we got to here, then we're really not backing up a database,
 * so open a backup file.
 */

/* There are four possible cases such as:
 *
 *   1. restore single backup file to single db file
 *   2. restore single backup file to multiple db files
 *   3. restore multiple backup files (join operation) to single db file
 *   4. restore multiple backup files (join operation) to multiple db files
 *
 * Just looking at the command line, we can't say for sure whether it is a
 * specification of the last file to be join or it is a specification of the
 * primary db file (case 4), for example:
 *
 *     gbak -c gbk1 gbk2 gbk3 db1 200 db2 500 db3 -v
 *                            ^^^
 *     db1 could be either the last file to be join or primary db file
 *
 * Since 'gbk' and 'gsplit' formats are different (gsplit file has its own
 * header record) hence we can use it as follows:
 *
 *     - open first file
 *     - read & check a header record
 *
 *    If a header is identified as a 'gsplit' one then we know exactly how
 * many files need to be join and in which order. We keep opening a file by
 * file till we reach the last one to be join. During this step we check
 * that the files are accessible and are in proper order. It gives us
 * possibility to let silly customer know about an error as soon as possible.
 * Besides we have to find out which file is going to be a db file.
 *
 *    If header is not identified as a 'gsplit' record then we assume that
 * we got a single backup file.
 */

      fil = tdgbl->gbl_sw_files;
      tdgbl->gbl_sw_backup_files = tdgbl->gbl_sw_files;

      tdgbl->action->act_action = ACT_restore;
      if (!strcmp(fil->fil_name, "stdin")) {
            fil->fil_fd = reinterpret_cast<DESC>(GBAK_STDIN_DESC);
            tdgbl->file_desc = fil->fil_fd;
            tdgbl->gbl_sw_files = fil->fil_next;
      }
      else {
            /* open first file */
#ifdef WIN_NT
            if ((fil->fil_fd = MVOL_open(fil->fil_name, MODE_READ, OPEN_EXISTING))
                  == INVALID_HANDLE_VALUE)
#else
            if ((fil->fil_fd = open(fil->fil_name, MODE_READ)) ==
                  INVALID_HANDLE_VALUE)
#endif
            {
                  BURP_error(65, fil->fil_name, 0, 0, 0, 0);      /* msg 65 can't open backup file %s */
                  return QUIT;
            }

            if (sw_verbose)
                  BURP_print(100, fil->fil_name, 0, 0, 0, 0);     /* msg 100 opened file
                                                                                       %s */
            /* read and check a header record */
            tdgbl->action->act_file = fil;
            seq = 1;
            if (MVOL_split_hdr_read() == TRUE) {
                  tdgbl->action->act_action = ACT_restore_join;
                  total = tdgbl->action->act_total;   /* number of files to be join */
                  if (fil->fil_seq != seq || seq > total) {
                        BURP_error(263, fil->fil_name, 0, 0, 0, 0);     /* msg 263 file %s out of sequence */
                        return QUIT;
                  }

                  for (++seq, fil = fil->fil_next; seq <= total;
                         fil = fil->fil_next, seq++) {
                        if (!fil) {
                              BURP_error(264, 0, 0, 0, 0, 0);     /* msg 264 can't join -- one of the files missing */
                              return QUIT;
                        }
                        if (!strcmp(fil->fil_name, "stdin")) {
                              BURP_error(265, 0, 0, 0, 0, 0);     /* msg 265 standard input is not supported when using join operation */
                              return QUIT;
                        }
                        tdgbl->action->act_file = fil;
#ifdef WIN_NT
                        if ((fil->fil_fd = MVOL_open(fil->fil_name, MODE_READ,
                                                                   OPEN_EXISTING)) ==
                              INVALID_HANDLE_VALUE)
#else
                        if ((fil->fil_fd = open(fil->fil_name, MODE_READ))
                              == INVALID_HANDLE_VALUE)
#endif
                        {
#ifdef SUPERSERVER
                              BURP_svc_error(65, isc_arg_string, fil->fil_name,
                                                   0, NULL, 0, NULL, 0, NULL, 0, NULL);
#endif
                              BURP_print(65, fil->fil_name, 0, 0, 0, 0);      /* msg 65 can't open
                                                                                                   backup file %s */
                              return QUIT;
                        }

                        if (sw_verbose)
                              BURP_print(100, fil->fil_name, 0, 0, 0, 0);     /* msg 100
                                                                                                   opened file
                                                                                                   %s */
                        if (MVOL_split_hdr_read() == TRUE) {
                              if ((total != tdgbl->action->act_total) ||
                                    (seq != fil->fil_seq) || (seq > total)) {
                                    BURP_error(263, fil->fil_name, 0, 0, 0, 0);     /* msg 263 file %s out of sequence */
                                    return QUIT;
                              }
                        }
                        else {
                              BURP_error(267, fil->fil_name, 0, 0, 0, 0);     /* msg 267 backup file %s might be corrupt */
                              return QUIT;
                        }
                  }
                  tdgbl->action->act_file = tdgbl->gbl_sw_files;
                  tdgbl->file_desc = tdgbl->action->act_file->fil_fd;
                  if ((tdgbl->gbl_sw_files = fil) == NULL) {
                        BURP_error(268, 0, 0, 0, 0, 0);     /* msg 268 database file specification missing */
                        return QUIT;
                  }
            }
            else {
                  /* Move pointer to the begining of the file. At this point we
                     assume -- this is a single backup file because we were
                     not able to read a split header record.
                   */
#ifdef WIN_NT
                  if (strnicmp(fil->fil_name, "\\\\.\\tape", 8))
                        SetFilePointer(fil->fil_fd, 0, NULL, FILE_BEGIN);
                  else
                        SetTapePosition(fil->fil_fd, TAPE_REWIND, 0, 0, 0, FALSE);
#else
                  lseek(fil->fil_fd, 0, SEEK_SET);
#endif
                  tdgbl->file_desc = fil->fil_fd;
                  tdgbl->gbl_sw_files = fil->fil_next;
            }
      }


/* If we got here, we've opened a backup file, and we're
   thinking about creating or replacing a database. */

      *file2 = tdgbl->gbl_sw_files->fil_name;
      if (tdgbl->gbl_sw_files->fil_size_code != size_n)
            BURP_error(262, *file2, 0, 0, 0, 0);      /* msg 262 size specificati on either missing or incorrect for file %s  */

      if ((sw_replace == IN_SW_BURP_C || sw_replace == IN_SW_BURP_R) &&
            !isc_attach_database(status_vector,
                                           (SSHORT) 0,
                                           *file2,
                                           &tdgbl->db_handle,
                                           tdgbl->dpb_length,
                                           reinterpret_cast<char*>(tdgbl->dpb_string)))
      {
            if (sw_replace == IN_SW_BURP_C) {
                  if (isc_detach_database(status_vector, &tdgbl->db_handle)) {
                        BURP_print_status(status_vector);
                  }
                  BURP_error(14, *file2, 0, 0, 0, 0);
                  /* msg 14 database %s already exists.  To replace it, use the -R switch */
            }
            else {
                  isc_drop_database(status_vector, &tdgbl->db_handle);
                  if (tdgbl->db_handle) {
                        ISC_STATUS_ARRAY status_vector2;
                        if (isc_detach_database(status_vector2, &tdgbl->db_handle)) {
                              BURP_print_status(status_vector2);
                        }

                        /* Complain only if the drop database entrypoint is available.
                           If it isn't, the database will simply be overwritten. */

                        if (status_vector[1] != gds_unavailable)
                              BURP_error(233, *file2, 0, 0, 0, 0);
                        /* msg 233 Cannot drop database %s, might be in use */
                  }
            }
      }
      if (sw_replace == IN_SW_BURP_R && status_vector[1] == isc_adm_task_denied) {
            /* if we got an error from attach database and we have replace switch set
             * then look for error from attach returned due to not owner, if we are
             * not owner then return the error status back up
             */
            BURP_error(274, 0, 0, 0, 0, 0);
            /* msg # 274 : Cannot restore over current database, must be sysdba
               * or owner of the existing database.
             */
      }
/* if we got here, then all is well, remove any error condition from the
 * status vector when running as a service thread.  If we don't then the
 * service will think that there is an error if isc_attach_database failed
 * like it should have (if creating a database).
 */
      if (tdgbl->gbl_sw_service_thd)
            memset(tdgbl->status, 0, ISC_STATUS_LENGTH * sizeof(ISC_STATUS));

/* check the file size specification */
      for (fil = tdgbl->gbl_sw_files; fil; fil = fil->fil_next) {
            if (fil->fil_size_code != size_n)
                  BURP_error(262, fil->fil_name, 0, 0, 0, 0);     /* msg 262 size specification either missing or incorrect for file %s  */
      }

      return RESTORE;
}


static void burp_output( const SCHAR * format, ...)
{
/**************************************
 *
 *    b u r p _ o u t p u t
 *
 **************************************
 *
 * Functional description
 *    Platform independent output routine.
 *
 **************************************/
      va_list arglist;
      UCHAR buf[1000];
      int exit_code;
      TGBL tdgbl;

      tdgbl = GET_THREAD_DATA;

      if (tdgbl->sw_redirect == NOOUTPUT || format[0] == '\0') {
            exit_code =
                  tdgbl->output_proc(tdgbl->output_data,
                                             (UCHAR*)(""));
      }
      else if (tdgbl->sw_redirect == TRUE && tdgbl->output_file != NULL) {
            VA_START(arglist, format);
            ib_vfprintf(tdgbl->output_file, format, arglist);
            va_end(arglist);
            exit_code =
                  tdgbl->output_proc(tdgbl->output_data,
                                             (UCHAR*)(""));
      }
      else {
            VA_START(arglist, format);
            vsprintf((char *) buf, format, arglist);
            va_end(arglist);

            exit_code = tdgbl->output_proc(tdgbl->output_data, buf);
      }

      if (exit_code != 0)
            EXIT(exit_code);
}


static ULONG get_size( SCHAR * string, FIL file)
{
/**********************************************
 *
 *      g e t _ s i z e
 *
 **********************************************
 *
 * Functional description
 *    Get size specification for either splitting or
 *    restoring to multiple files
 *
 **********************************************/
      SCHAR *num, c;
      ULONG size;
      BOOLEAN digit;


      file->fil_size_code = size_n;
      for (size = 0, digit = FALSE, num = string; c = *num++;) {
            if (isdigit(c)) {
                  size = size * 10 + (c - '0');
                  digit = TRUE;
            }
            else {
                  if (isalpha(c)) {
                        if (!digit) {
                              file->fil_size_code = size_e;
                              size = 0;
                              break;
                        }
                        switch (UPPER(c)) {
                        case 'K':
                              file->fil_size_code = size_k;
                              break;
                        case 'M':
                              file->fil_size_code = size_m;
                              break;
                        case 'G':
                              file->fil_size_code = size_g;
                              break;
                        default:
                              file->fil_size_code = size_e;
                              size = 0;
                              break;
                        }
                        if (*num) {
                              file->fil_size_code = size_e;
                              size = 0;
                        }
                        break;
                  }
            }
      }

      return (file->fil_length = size);
}


#ifndef SUPERSERVER
static int api_gbak(int argc,
                              char *argv[],
                              USHORT length,
                              TEXT * password,
                              TEXT * user,
                              TEXT * service,
                              BOOLEAN restore,
                              BOOLEAN verbose)
{
/**********************************************
 *
 *    a p i _ g b a k
 *
 **********************************************
 *
 * Functional description
 *    Run gbak using services APIs
 *
 **********************************************/
      ISC_STATUS_ARRAY status;
      TEXT **begin, **end, *x, *p, *usr, *pswd;
      USHORT spblen, thdlen;
      char sendbuf[] = { isc_info_svc_line };
      char respbuf[1024];
      FRBRD *svc_handle = NULL;
      char *spb_ptr, *spb, *svc_name, *thd_ptr, *thd;
      struct tgbl *tdgbl, ldgbl;

      tdgbl = &ldgbl;
      SET_THREAD_DATA;
      memset((void *) tdgbl, 0, sizeof(*tdgbl));
      tdgbl->output_proc = output_main;

      if (!user)
            usr = getenv("ISC_USER");
      else
            usr = user;

      if (!password)
            pswd = getenv("ISC_PASSWORD");
      else
            pswd = password;

      spb = (char *) gds__alloc((SLONG) (2 + 2 + ((usr) ? strlen(usr) : 0) +
                                                         2 + ((pswd) ? strlen(pswd) : 0)) +
                                                         2 + length);
      /* 'isc_spb_version'
         'isc_spb_current_version'
         'isc_spb_user_name'
         'length'
         "usr"
         'isc_spb_password'
         'length'
         "pswd"
         'isc_spb_options'
         'length'
         "options" */
      if (spb == NULL) {
            status[0] = isc_arg_gds;
            status[1] = isc_virmemexh;
            status[2] = isc_arg_end;
            BURP_print_status(status);
            BURP_print(83, 0, 0, 0, 0, 0);      /* msg 83 Exiting before completion due to errors */
            return FINI_ERROR;
      }

      spb_ptr = spb;
      *spb_ptr++ = isc_spb_version;
      *spb_ptr++ = isc_spb_current_version;

      if (usr) {
            *spb_ptr++ = isc_spb_user_name;
            *spb_ptr++ = strlen(usr);
            MEMMOVE(usr, spb_ptr, strlen(usr));
            spb_ptr += strlen(usr);
            if (user)
                  *user = '\0';
      }

      if (pswd) {
            *spb_ptr++ = isc_spb_password;
            *spb_ptr++ = strlen(pswd);
            MEMMOVE(pswd, spb_ptr, strlen(pswd));
            spb_ptr += strlen(pswd);
            if (password)
                  *password = '\0';
      }

      svc_name = (char *) gds__alloc((SLONG) (strlen(service) + 1));

      if (svc_name == NULL) {
            status[0] = isc_arg_gds;
            status[1] = isc_virmemexh;
            status[2] = isc_arg_end;
            BURP_print_status(status);
            gds__free(spb);
            BURP_print(83, 0, 0, 0, 0, 0);      /* msg 83 Exiting before completion due to errors */
            return FINI_ERROR;
      }

      if (service) {
            strcpy(svc_name, service);
            *service = '\0';
      }


/* Fill command line options */

      *spb_ptr++ = isc_spb_command_line;
      begin = argv, end = argv + argc;
      argv++;

      *spb_ptr++ = length;
      while (argv < end) {
            if (**argv && argv > begin + 1)
                  *spb_ptr++ = ' ';
            for (x = *argv++; *x;)
                  *spb_ptr++ = *x++;
      }

      spblen = spb_ptr - spb;

      if (isc_service_attach( status,
                                          0,
                                          svc_name,
                                          (&svc_handle),
                                          spblen, spb))
      {
            BURP_print_status(status);
            gds__free(spb);
            gds__free(svc_name);
            BURP_print(83, 0, 0, 0, 0, 0);      /* msg 83 Exiting before completion due to errors */
            return FINI_ERROR;
      }

      thd = (char *) gds__alloc((SLONG) (2));
      /* 'isc_action_svc_restore/isc_action_svc_backup'
         'isc_spb_verbose' */

      if (thd == NULL) {
            status[0] = isc_arg_gds;
            status[1] = isc_virmemexh;
            status[2] = isc_arg_end;
            BURP_print_status(status);
            isc_service_detach(status, (&svc_handle));
            gds__free(spb);
            gds__free(svc_name);
            BURP_print(83, 0, 0, 0, 0, 0);      /* msg 83 Exiting before completion due to errors */
            return FINI_ERROR;
      }

      thd_ptr = thd;
      if (restore)
            *thd_ptr++ = isc_action_svc_restore;
      else
            *thd_ptr++ = isc_action_svc_backup;

      if (verbose)
            *thd_ptr++ = isc_spb_verbose;

      thdlen = thd_ptr - thd;

      if (isc_service_start(  status,
                                          (&svc_handle),
                                          NULL,
                                          thdlen,
                                          thd))
      {
            BURP_print_status(status);
            gds__free(spb);
            gds__free(svc_name);
            gds__free(thd);
            isc_service_detach(status, (&svc_handle));
            BURP_print(83, 0, 0, 0, 0, 0);      /* msg 83 Exiting before completion due to errors */
            return FINI_ERROR;
      }

      do {
            if (isc_service_query(  status,
                                                (&svc_handle),
                                                NULL,
                                                0,
                                                NULL,
                                                sizeof(sendbuf),
                                                sendbuf,
                                                sizeof(respbuf),
                                                respbuf))
            {
                  BURP_print_status(status);
                  gds__free(spb);
                  gds__free(svc_name);
                  gds__free(thd);
                  isc_service_detach(status,
                                             (&svc_handle));
                  BURP_print(83, 0, 0, 0, 0, 0);      /* msg 83 Exiting before completion due to errors */
                  return FINI_ERROR;
            }

            x = p = respbuf;

            if (*p++ == isc_info_svc_line)
            {
                  ISC_USHORT len = 0;

                  len = (ISC_USHORT) isc_vax_integer(p, sizeof(ISC_USHORT));
                  p += sizeof(ISC_USHORT);
                  if (!len)
                  {
                        if (*p == isc_info_data_not_ready)
                              continue;
                        else if (*p == isc_info_end)
                              break;
                  }

                  p[len] = '\0';
                  burp_output("%s\n", p);

                  p += len;
            }
      }
      while (*x == isc_info_svc_line);

      isc_service_detach(status, (&svc_handle));
      return FINI_OK;
}

#endif      // !SUPERSERVER


Generated by  Doxygen 1.6.0   Back to index