1593 lines
60 KiB
C
1593 lines
60 KiB
C
/****************************************************************
|
|
* *
|
|
* Copyright 2001, 2012 Fidelity Information Services, Inc *
|
|
* *
|
|
* This source code contains the intellectual property *
|
|
* of its copyright holder(s), and is made available *
|
|
* under a license. If you do not know the terms of *
|
|
* the license, please stop and do not read further. *
|
|
* *
|
|
****************************************************************/
|
|
|
|
#include "mdef.h"
|
|
|
|
#ifdef UNIX
|
|
# include "gtm_fcntl.h"
|
|
# include "gtm_stat.h"
|
|
# include "gtm_unistd.h"
|
|
# include <sys/shm.h>
|
|
# include "gtm_permissions.h"
|
|
#elif defined(VMS)
|
|
# include <rms.h>
|
|
# include <iodef.h>
|
|
#else
|
|
# error Unsupported Platform
|
|
#endif
|
|
#include "gtm_string.h"
|
|
#include "gtm_stdio.h"
|
|
#include "gtm_stdlib.h"
|
|
#include "gtm_tempnam.h"
|
|
#include "gtm_time.h"
|
|
|
|
#ifdef __MVS__
|
|
#include "gtm_zos_io.h"
|
|
#endif
|
|
#include "gdsroot.h"
|
|
#include "gtm_facility.h"
|
|
#include "fileinfo.h"
|
|
#include "gdsbt.h"
|
|
#include "gdsblk.h"
|
|
#include "gdsfhead.h"
|
|
#include "gdsbgtr.h"
|
|
#include "filestruct.h"
|
|
#include "ast.h"
|
|
#include "cli.h"
|
|
#include "iosp.h"
|
|
#include "error.h"
|
|
#include "mupipbckup.h"
|
|
#include "stp_parms.h"
|
|
#include "gdscc.h"
|
|
#include "gdskill.h"
|
|
#include "jnl.h"
|
|
#include "buddy_list.h" /* needed for tp.h */
|
|
#include "hashtab_int4.h" /* needed for tp.h */
|
|
#include "tp.h"
|
|
#include "io.h"
|
|
#include "interlock.h"
|
|
#include "lockconst.h"
|
|
#include "sleep_cnt.h"
|
|
|
|
#ifdef UNIX
|
|
#include "eintr_wrappers.h"
|
|
#include "gtmio.h" /* for OPENFILE macro */
|
|
#include "repl_sp.h" /* for F_CLOSE macro */
|
|
#include "gtm_ipc.h"
|
|
#include "repl_instance.h"
|
|
#include "mu_gv_cur_reg_init.h"
|
|
#include "ftok_sems.h"
|
|
#include "repl_msg.h"
|
|
#include "gtmsource.h"
|
|
#include "do_shmat.h" /* for do_shmat() prototype */
|
|
#include "mutex.h"
|
|
#include "heartbeat_timer.h"
|
|
#endif
|
|
|
|
#include "gtm_file_stat.h"
|
|
#include "util.h"
|
|
#include "gtm_caseconv.h"
|
|
#include "gt_timer.h"
|
|
#include "is_proc_alive.h"
|
|
#include "is_file_identical.h"
|
|
#include "dbfilop.h"
|
|
#include "mupip_exit.h"
|
|
#include "mu_getlst.h"
|
|
#include "mu_outofband_setup.h"
|
|
#include "gtmmsg.h"
|
|
#include "wcs_sleep.h"
|
|
#include "wcs_flu.h"
|
|
#include "trans_log_name.h"
|
|
#include "shmpool.h"
|
|
#include "mupip_backup.h"
|
|
#include "gtm_rename.h" /* for cre_jnl_file_intrpt_rename() prototype */
|
|
#include "gvcst_protos.h" /* for gvcst_init prototype */
|
|
#include "add_inter.h"
|
|
#include "gtm_logicals.h"
|
|
#include "gtm_c_stack_trace.h"
|
|
#include "have_crit.h"
|
|
#ifdef UNIX
|
|
#include "repl_sem.h"
|
|
#include "gtm_sem.h"
|
|
#endif
|
|
|
|
#ifdef UNIX
|
|
# define PATH_DELIM '/'
|
|
#elif defined(VMS)
|
|
# define PATH_DELIM ']'
|
|
static const unsigned short zero_fid[3];
|
|
#else
|
|
# error Unsupported Platform
|
|
#endif
|
|
|
|
#define TMPDIR_ACCESS_MODE (R_OK | W_OK | X_OK)
|
|
|
|
GBLDEF boolean_t backup_started;
|
|
GBLDEF boolean_t backup_interrupted;
|
|
|
|
GBLREF bool record;
|
|
GBLREF bool error_mupip;
|
|
GBLREF bool file_backed_up;
|
|
GBLREF bool incremental;
|
|
GBLREF bool online;
|
|
GBLREF uchar_ptr_t mubbuf;
|
|
GBLREF int4 mubmaxblk;
|
|
GBLREF tp_region *grlist;
|
|
GBLREF tp_region *halt_ptr;
|
|
GBLREF bool in_backup;
|
|
GBLREF bool is_directory;
|
|
GBLREF bool mu_ctrly_occurred;
|
|
GBLREF bool mu_ctrlc_occurred;
|
|
GBLREF bool mubtomag;
|
|
GBLREF gd_region *gv_cur_region;
|
|
GBLREF sgmnt_addrs *cs_addrs;
|
|
GBLREF sgmnt_data_ptr_t cs_data;
|
|
GBLREF mstr directory;
|
|
GBLREF uint4 process_id;
|
|
GBLREF uint4 image_count;
|
|
GBLREF boolean_t debug_mupip;
|
|
GBLREF char *before_image_lit[];
|
|
GBLREF char *jnl_state_lit[];
|
|
GBLREF char *repl_state_lit[];
|
|
GBLREF jnl_gbls_t jgbl;
|
|
GBLREF void (*call_on_signal)();
|
|
GBLREF gd_addr *gd_header;
|
|
GBLREF boolean_t jnlpool_init_needed;
|
|
#ifdef DEBUG
|
|
GBLREF int process_exiting; /* Process is on it's way out */
|
|
#endif
|
|
|
|
#ifdef UNIX
|
|
GBLREF backup_reg_list *mu_repl_inst_reg_list;
|
|
GBLREF jnlpool_addrs jnlpool;
|
|
GBLREF jnlpool_ctl_ptr_t jnlpool_ctl;
|
|
GBLREF uint4 mutex_per_process_init_pid;
|
|
GBLREF boolean_t holds_sem[NUM_SEM_SETS][NUM_SRC_SEMS];
|
|
GBLREF boolean_t pool_init;
|
|
#endif
|
|
|
|
LITREF char gtm_release_name[];
|
|
LITREF int4 gtm_release_name_len;
|
|
|
|
error_def(ERR_BACKUPCTRL);
|
|
error_def(ERR_BACKUPKILLIP);
|
|
error_def(ERR_DBCCERR);
|
|
error_def(ERR_DBFILERR);
|
|
error_def(ERR_DBRDONLY);
|
|
error_def(ERR_DBROLLEDBACK);
|
|
error_def(ERR_ERRCALL);
|
|
error_def(ERR_FILEEXISTS);
|
|
error_def(ERR_FILEPARSE);
|
|
error_def(ERR_FREEZECTRL);
|
|
error_def(ERR_JNLCREATE);
|
|
error_def(ERR_JNLDISABLE);
|
|
error_def(ERR_JNLFNF);
|
|
error_def(ERR_JNLNOCREATE);
|
|
error_def(ERR_JNLPOOLSETUP);
|
|
error_def(ERR_JNLSTATE);
|
|
error_def(ERR_KILLABANDONED);
|
|
error_def(ERR_MUNOACTION);
|
|
error_def(ERR_MUNOFINISH);
|
|
error_def(ERR_MUNOSTRMBKUP);
|
|
error_def(ERR_MUPCLIERR);
|
|
error_def(ERR_MUSELFBKUP);
|
|
error_def(ERR_NOTRNDMACC);
|
|
error_def(ERR_PERMGENFAIL);
|
|
error_def(ERR_PREVJNLLINKCUT);
|
|
error_def(ERR_REPLJNLCNFLCT);
|
|
error_def(ERR_REPLPOOLINST);
|
|
error_def(ERR_REPLREQROLLBACK);
|
|
error_def(ERR_REPLSTATE);
|
|
error_def(ERR_REPLSTATEERR);
|
|
error_def(ERR_SYSCALL);
|
|
error_def(ERR_TEXT);
|
|
ZOS_ONLY(error_def(ERR_BADTAG);)
|
|
|
|
static char * const jnl_parms[] =
|
|
{
|
|
"DISABLE",
|
|
"NOPREVJNLFILE",
|
|
"OFF"
|
|
};
|
|
enum
|
|
{
|
|
jnl_disable,
|
|
jnl_noprevjnlfile,
|
|
jnl_off,
|
|
jnl_end_of_list
|
|
};
|
|
|
|
void mupip_backup_call_on_signal(void)
|
|
{ /* Called if mupip backup is terminated by a signal. Performs cleanup of temporary files and shutdown backup. */
|
|
assert(NULL == call_on_signal); /* Should have been reset by signal handling caller before invoking this function.
|
|
* This will ensure we do not recurse via call_on_signal if there is another error.
|
|
*/
|
|
assert(process_exiting); /* should have been set by caller */
|
|
if (backup_started)
|
|
{ /* Cleanup that which we have messed */
|
|
backup_interrupted = TRUE;
|
|
mubclnup(NULL, need_to_del_tempfile);
|
|
}
|
|
}
|
|
|
|
/* When we have crit, check if this region is actively journaled and if gbl_jrec_time needs to be
|
|
* adjusted (to ensure time ordering of journal records within this region's journal file).
|
|
* This needs to be done BEFORE writing any journal records for this region. The value of
|
|
* jgbl.gbl_jrec_time at the end of this loop will be used to write journal records for ALL
|
|
* regions so all regions will have same eov/bov timestamps.
|
|
*/
|
|
#define UPDATE_GBL_JREC_TIME \
|
|
{ \
|
|
if (JNL_ENABLED(cs_data) \
|
|
UNIX_ONLY( && (0 != cs_addrs->nl->jnl_file.u.inode)) \
|
|
VMS_ONLY( && (0 != memcmp(cs_addrs->nl->jnl_file.jnl_file_id.fid, zero_fid, SIZEOF(zero_fid))))) \
|
|
{ \
|
|
jpc = cs_addrs->jnl; \
|
|
jbp = jpc->jnl_buff; \
|
|
ADJUST_GBL_JREC_TIME(jgbl, jbp); \
|
|
} \
|
|
}
|
|
|
|
void mupip_backup(void)
|
|
{
|
|
bool journal;
|
|
char *tempfilename, *ptr;
|
|
uint4 status, ret, kip_count;
|
|
unsigned short s_len, length;
|
|
int4 size, crit_counter, save_errno, rv;
|
|
uint4 ustatus, reg_count;
|
|
trans_num tn;
|
|
shmpool_buff_hdr_ptr_t sbufh_p;
|
|
shmpool_blk_hdr_ptr_t sblkh_p, next_sblkh_p;
|
|
backup_reg_list *rptr, *rrptr, *nocritrptr;
|
|
boolean_t inc_since_inc , inc_since_rec, result, newjnlfiles, tn_specified,
|
|
replication_on, newjnlfiles_specified, keep_prev_link, bkdbjnl_disable_specified,
|
|
bkdbjnl_off_specified;
|
|
unsigned char since_buff[50];
|
|
jnl_create_info jnl_info;
|
|
file_control *fc;
|
|
char tempdir_trans_buffer[MAX_TRANS_NAME_LEN],
|
|
tempnam_prefix[MAX_FN_LEN], tempdir_full_buffer[MAX_FN_LEN + 1];
|
|
char *jnl_str_ptr, jnl_str[256], entry[256], prev_jnl_fn[JNL_NAME_SIZE];
|
|
int index, jnl_fstat;
|
|
mstr tempdir_log, tempdir_trans, *file, *rfile, *replinstfile, tempdir_full, filestr;
|
|
uint4 jnl_status, temp_file_name_len, tempdir_trans_len, trans_log_name_status;
|
|
boolean_t jnl_options[jnl_end_of_list] = {FALSE, FALSE, FALSE}, save_no_prev_link;
|
|
jnl_private_control *jpc;
|
|
jnl_buffer_ptr_t jbp;
|
|
jnl_tm_t save_gbl_jrec_time;
|
|
gd_region *r_save, *reg;
|
|
int sync_io_status;
|
|
boolean_t sync_io, sync_io_specified, wait_for_zero_kip, decr_cnt;
|
|
# ifdef UNIX
|
|
struct stat stat_buf;
|
|
int fstat_res, fclose_res, tmpfd;
|
|
gd_segment *seg;
|
|
char instfilename[MAX_FN_LEN + 1], *errptr;
|
|
unsigned int full_len;
|
|
unix_db_info *udi;
|
|
sgmnt_addrs *csa;
|
|
repl_inst_hdr repl_instance, *inst_hdr, *save_inst_hdr;
|
|
unsigned char *cmdptr, command[MAX_FN_LEN * 2 + 5]; /* 5 == SIZEOF("cp") + 2 (space) + 1 (NULL) */
|
|
struct shmid_ds shm_buf;
|
|
struct semid_ds semstat;
|
|
union semun semarg;
|
|
int4 shm_id, sem_id;
|
|
replpool_identifier replpool_id;
|
|
sm_uc_ptr_t start_addr;
|
|
int group_id;
|
|
int perm;
|
|
struct perm_diag_data pdd;
|
|
pid_t *kip_pids_arr_ptr;
|
|
# elif defined(VMS)
|
|
struct FAB temp_fab;
|
|
struct NAM temp_nam;
|
|
struct XABPRO temp_xabpro;
|
|
short iosb[4];
|
|
char def_jnl_fn[MAX_FN_LEN];
|
|
GDS_INFO *gds_info;
|
|
char exp_file_name[MAX_FN_LEN];
|
|
uint4 exp_file_name_len;
|
|
boolean_t gotit;
|
|
unsigned short ntries;
|
|
# else
|
|
# error UNSUPPORTED PLATFORM
|
|
# endif
|
|
seq_num jnl_seqno;
|
|
now_t now; /* for GET_CUR_TIME macro */
|
|
char *time_ptr, time_str[CTIME_BEFORE_NL + 2]; /* for GET_CUR_TIME macro */
|
|
ZOS_ONLY(int realfiletag;)
|
|
|
|
/* ==================================== STEP 1. Initialization ======================================= */
|
|
backup_started = backup_interrupted = FALSE;
|
|
ret = SS_NORMAL;
|
|
jnl_str_ptr = &jnl_str[0];
|
|
halt_ptr = grlist = NULL;
|
|
in_backup = TRUE;
|
|
inc_since_inc = inc_since_rec = file_backed_up = error_mupip = FALSE;
|
|
debug_mupip = (CLI_PRESENT == cli_present("DBG"));
|
|
call_on_signal = mupip_backup_call_on_signal;
|
|
mu_outofband_setup();
|
|
jnl_status = 0;
|
|
if (NULL == gd_header)
|
|
gvinit();
|
|
/* ============================ STEP 2. Parse and construct grlist ================================== */
|
|
tn_specified = FALSE;
|
|
if (incremental = (CLI_PRESENT == cli_present("INCREMENTAL") || CLI_PRESENT == cli_present("BYTESTREAM")))
|
|
{
|
|
trans_num temp_tn;
|
|
if (0 == cli_get_hex64("TRANSACTION", &temp_tn))
|
|
{
|
|
temp_tn = 0;
|
|
s_len = SIZEOF(since_buff);
|
|
if (cli_get_str("SINCE", (char *)since_buff, &s_len))
|
|
{
|
|
lower_to_upper(since_buff, since_buff, s_len);
|
|
if ((0 == memcmp(since_buff, "INCREMENTAL", s_len))
|
|
|| (0 == memcmp(since_buff, "BYTESTREAM", s_len)))
|
|
inc_since_inc = TRUE;
|
|
else if (0 == memcmp(since_buff, "RECORD", s_len))
|
|
inc_since_rec = TRUE;
|
|
}
|
|
} else
|
|
{
|
|
tn_specified = TRUE;
|
|
if (temp_tn < 1)
|
|
{
|
|
util_out_print("The minimum allowable transaction number is one.", TRUE);
|
|
mupip_exit(ERR_MUNOACTION);
|
|
}
|
|
}
|
|
tn = temp_tn;
|
|
}
|
|
online = (TRUE != cli_negated("ONLINE"));
|
|
record = (CLI_PRESENT == cli_present("RECORD"));
|
|
newjnlfiles_specified = FALSE;
|
|
newjnlfiles = TRUE; /* by default */
|
|
keep_prev_link = TRUE;
|
|
if (CLI_PRESENT == cli_present("NEWJNLFILES"))
|
|
{
|
|
newjnlfiles_specified = newjnlfiles = TRUE;
|
|
if (CLI_NEGATED == cli_present("NEWJNLFILES.PREVLINK"))
|
|
keep_prev_link = FALSE;
|
|
sync_io_status = cli_present(UNIX_ONLY("NEWJNLFILES.SYNC_IO") VMS_ONLY("NEWJNLFILES.CACHE"));
|
|
sync_io_specified = TRUE;
|
|
if (CLI_PRESENT == sync_io_status)
|
|
sync_io = UNIX_ONLY(TRUE) VMS_ONLY(FALSE);
|
|
else if (CLI_NEGATED == sync_io_status)
|
|
sync_io = UNIX_ONLY(FALSE) VMS_ONLY(TRUE);
|
|
else
|
|
sync_io_specified = FALSE;
|
|
} else if (CLI_NEGATED == cli_present("NEWJNLFILES"))
|
|
{
|
|
keep_prev_link = FALSE; /* for safety */
|
|
newjnlfiles_specified = TRUE;
|
|
newjnlfiles = FALSE;
|
|
}
|
|
replication_on = FALSE;
|
|
if (CLI_PRESENT == cli_present("REPLICATION.ON")) /* REPLICATION.OFF is disabled at the CLI layer */
|
|
replication_on = TRUE;
|
|
bkdbjnl_disable_specified = FALSE;
|
|
bkdbjnl_off_specified = FALSE;
|
|
if (CLI_PRESENT == cli_present("BKUPDBJNL"))
|
|
{
|
|
if (CLI_PRESENT == cli_present("BKUPDBJNL.DISABLE"))
|
|
bkdbjnl_disable_specified = TRUE;
|
|
if (CLI_PRESENT == cli_present("BKUPDBJNL.OFF"))
|
|
bkdbjnl_off_specified = TRUE;
|
|
}
|
|
journal = (CLI_PRESENT == cli_present("JOURNAL"));
|
|
if (TRUE == cli_negated("JOURNAL"))
|
|
jnl_options[jnl_disable] = TRUE;
|
|
else if (journal)
|
|
{
|
|
s_len = SIZEOF(jnl_str);
|
|
UNSUPPORTED_PLATFORM_CHECK;
|
|
if (!CLI_GET_STR_ALL("JOURNAL", jnl_str_ptr, &s_len))
|
|
mupip_exit(ERR_MUPCLIERR);
|
|
while (*jnl_str_ptr)
|
|
{
|
|
if (!cli_get_str_ele(jnl_str_ptr, entry, &length, TRUE))
|
|
mupip_exit(ERR_MUPCLIERR);
|
|
for (index = 0; index < jnl_end_of_list; ++index)
|
|
if (0 == strncmp(jnl_parms[index], entry, length))
|
|
{
|
|
jnl_options[index] = TRUE;
|
|
break;
|
|
}
|
|
if (jnl_end_of_list == index)
|
|
{
|
|
util_out_print("Qualifier JOURNAL: Unrecognized option: !AD", TRUE, length, entry);
|
|
mupip_exit(ERR_MUPCLIERR);
|
|
}
|
|
jnl_str_ptr += length;
|
|
assert(',' == *jnl_str_ptr || !(*jnl_str_ptr)); /* either comma separator or end of option list */
|
|
if (',' == *jnl_str_ptr)
|
|
jnl_str_ptr++; /* skip separator */
|
|
}
|
|
if (jnl_options[jnl_disable] && jnl_options[jnl_off])
|
|
{
|
|
util_out_print("Qualifier JOURNAL: DISABLE may not be specified with any other options", TRUE);
|
|
mupip_exit(ERR_MUPCLIERR);
|
|
}
|
|
if (jnl_options[jnl_noprevjnlfile] && !newjnlfiles)
|
|
{
|
|
util_out_print("Qualifier JOURNAL: NOPREVJNLFILE may not be specified with NONEWJNLFILES qualifier", TRUE);
|
|
mupip_exit(ERR_MUPCLIERR);
|
|
}
|
|
}
|
|
mu_getlst("REG_NAME", SIZEOF(backup_reg_list));
|
|
if (error_mupip)
|
|
{
|
|
mubclnup(NULL, need_to_free_space);
|
|
util_out_print("!/MUPIP cannot start backup with above errors!/", TRUE);
|
|
mupip_exit(ERR_MUNOACTION);
|
|
}
|
|
if (mu_ctrly_occurred || mu_ctrlc_occurred)
|
|
{
|
|
mubclnup(NULL, need_to_free_space);
|
|
gtm_putmsg(VARLSTCNT(1) ERR_BACKUPCTRL);
|
|
mupip_exit(ERR_MUNOFINISH);
|
|
}
|
|
# ifdef UNIX
|
|
assert((NULL != grlist) || (NULL != mu_repl_inst_reg_list));
|
|
if (NULL != mu_repl_inst_reg_list)
|
|
{ /* Check that backup destination file for replication instance is different from the backup
|
|
* destination file for other regions that were specified.
|
|
*/
|
|
replinstfile = &(mu_repl_inst_reg_list->backup_file);
|
|
replinstfile->addr[replinstfile->len] = '\0';
|
|
if (CLI_PRESENT != cli_present("REPLACE"))
|
|
{ /* make sure backup files do not already exist */
|
|
if (FILE_PRESENT == (fstat_res = gtm_file_stat(replinstfile, NULL, NULL, FALSE, &ustatus)))
|
|
{
|
|
gtm_putmsg(VARLSTCNT(4) ERR_FILEEXISTS, 2, LEN_AND_STR((char *)replinstfile->addr));
|
|
error_mupip = TRUE;
|
|
} else if (FILE_STAT_ERROR == fstat_res)
|
|
{ /* stat doesn't usually return with an error. Assert so we can analyze */
|
|
assert(FALSE);
|
|
gtm_putmsg(VARLSTCNT(8) ERR_SYSCALL, 5, LEN_AND_LIT("stat"), CALLFROM, ustatus);
|
|
error_mupip = TRUE;
|
|
}
|
|
}
|
|
for (rptr = (backup_reg_list *)(grlist); NULL != rptr; rptr = rptr->fPtr)
|
|
{
|
|
file = &(rptr->backup_file);
|
|
file->addr[file->len] = '\0';
|
|
if (!STRCMP(replinstfile->addr, file->addr))
|
|
{
|
|
util_out_print("Cannot backup replication instance file and database region !AD "
|
|
"to the same destination file !AZ", TRUE, REG_LEN_STR(rptr->reg),
|
|
replinstfile->addr);
|
|
error_mupip = TRUE;
|
|
}
|
|
}
|
|
}
|
|
# endif
|
|
for (rptr = (backup_reg_list *)(grlist); NULL != rptr; rptr = rptr->fPtr)
|
|
{
|
|
file = &(rptr->backup_file);
|
|
file->addr[file->len] = '\0';
|
|
if ((backup_to_file == rptr->backup_to))
|
|
{ /* make sure that backup won't be overwriting the database file itself */
|
|
if (TRUE == is_file_identical(file->addr, (char *)rptr->reg->dyn.addr->fname))
|
|
{
|
|
gtm_putmsg(VARLSTCNT(4) ERR_MUSELFBKUP, 2, DB_LEN_STR(rptr->reg));
|
|
error_mupip = TRUE;
|
|
}
|
|
# ifdef UNIX
|
|
if (CLI_PRESENT != cli_present("REPLACE"))
|
|
{ /* make sure backup files do not already exist */
|
|
if (FILE_PRESENT == (fstat_res = gtm_file_stat(file, NULL, NULL, FALSE, &ustatus)))
|
|
{
|
|
gtm_putmsg(VARLSTCNT(4) ERR_FILEEXISTS, 2, LEN_AND_STR((char *)file->addr));
|
|
error_mupip = TRUE;
|
|
} else if (FILE_STAT_ERROR == fstat_res)
|
|
{ /* stat doesn't usually return with an error. Assert so we can analyze */
|
|
assert(FALSE);
|
|
gtm_putmsg(VARLSTCNT(8) ERR_SYSCALL, 5, LEN_AND_LIT("stat"), CALLFROM, ustatus);
|
|
error_mupip = TRUE;
|
|
}
|
|
}
|
|
# endif
|
|
for (rrptr = (backup_reg_list *)(grlist); rrptr != rptr; rrptr = rrptr->fPtr)
|
|
{
|
|
rfile = &(rrptr->backup_file);
|
|
assert('\0' == rfile->addr[rfile->len]);
|
|
if (!STRCMP(file->addr, rfile->addr))
|
|
{
|
|
util_out_print("Cannot backup database regions !AD and !AD to the same "
|
|
"destination file !AZ",
|
|
TRUE, REG_LEN_STR(rrptr->reg), REG_LEN_STR(rptr->reg), file->addr);
|
|
error_mupip = TRUE;
|
|
}
|
|
}
|
|
} else if (!incremental)
|
|
{ /* non-incremental backups to "exec" and "tcp" are not supported*/
|
|
gtm_putmsg(VARLSTCNT(1) ERR_NOTRNDMACC);
|
|
error_mupip = TRUE;
|
|
}
|
|
}
|
|
if (TRUE == error_mupip)
|
|
{
|
|
mubclnup(NULL, need_to_free_space);
|
|
util_out_print("!/MUPIP cannot start backup with above errors!/", TRUE);
|
|
mupip_exit(ERR_MUNOACTION);
|
|
}
|
|
/* =========================== STEP 3. Verify the regions and grab_crit()/freeze them ============ */
|
|
mubmaxblk = 0;
|
|
halt_ptr = grlist;
|
|
size = ROUND_UP(SIZEOF_FILE_HDR_MAX, DISK_BLOCK_SIZE);
|
|
ESTABLISH(mu_freeze_ch);
|
|
tempfilename = tempdir_full.addr = tempdir_full_buffer;
|
|
if (TRUE == online)
|
|
{
|
|
tempdir_log.addr = GTM_BAK_TEMPDIR_LOG_NAME;
|
|
tempdir_log.len = STR_LIT_LEN(GTM_BAK_TEMPDIR_LOG_NAME);
|
|
trans_log_name_status =
|
|
TRANS_LOG_NAME(&tempdir_log, &tempdir_trans, tempdir_trans_buffer, SIZEOF(tempdir_trans_buffer),
|
|
do_sendmsg_on_log2long);
|
|
# ifdef UNIX /* UNIX has upper (deprecated) and lower case versions of this env var where as VMS only has upper */
|
|
if ((SS_NORMAL != trans_log_name_status)
|
|
|| (NULL == tempdir_trans.addr) || (0 == tempdir_trans.len))
|
|
{ /* GTM_BAK_TEMPDIR_LOG_NAME not found, attempt GTM_BAK_TEMPDIR_LOG_NAME_UC instead */
|
|
tempdir_log.addr = GTM_BAK_TEMPDIR_LOG_NAME_UC;
|
|
tempdir_log.len = STR_LIT_LEN(GTM_BAK_TEMPDIR_LOG_NAME_UC);
|
|
trans_log_name_status =
|
|
TRANS_LOG_NAME(&tempdir_log, &tempdir_trans, tempdir_trans_buffer, SIZEOF(tempdir_trans_buffer),
|
|
do_sendmsg_on_log2long);
|
|
}
|
|
# endif
|
|
/* save the length of the "base" so we can (restore it and) re-use the string in tempdir_trans.addr */
|
|
tempdir_trans_len = tempdir_trans.len;
|
|
} else
|
|
tempdir_trans_len = 0;
|
|
jnlpool_init_needed = TRUE;
|
|
for (rptr = (backup_reg_list *)(grlist); NULL != rptr; rptr = rptr->fPtr)
|
|
{ /* restore the original length since we are looping thru regions */
|
|
tempdir_trans.len = tempdir_trans_len;
|
|
file = &(rptr->backup_file);
|
|
file->addr[file->len] = '\0';
|
|
if (mu_ctrly_occurred || mu_ctrlc_occurred)
|
|
break;
|
|
if ((dba_bg != rptr->reg->dyn.addr->acc_meth) && (dba_mm != rptr->reg->dyn.addr->acc_meth))
|
|
{
|
|
util_out_print("Region !AD is not a BG or MM databases", TRUE, REG_LEN_STR(rptr->reg));
|
|
rptr->not_this_time = give_up_before_create_tempfile;
|
|
continue;
|
|
}
|
|
if (reg_cmcheck(rptr->reg))
|
|
{
|
|
util_out_print("!/Can't BACKUP region !AD across network", TRUE, REG_LEN_STR(rptr->reg));
|
|
rptr->not_this_time = give_up_before_create_tempfile;
|
|
continue;
|
|
}
|
|
gv_cur_region = rptr->reg;
|
|
gvcst_init(gv_cur_region);
|
|
if (gv_cur_region->was_open)
|
|
{
|
|
gv_cur_region->open = FALSE;
|
|
rptr->not_this_time = give_up_before_create_tempfile;
|
|
continue;
|
|
}
|
|
TP_CHANGE_REG(gv_cur_region);
|
|
if (gv_cur_region->read_only)
|
|
{
|
|
gtm_putmsg(VARLSTCNT(4) ERR_DBRDONLY, 2, DB_LEN_STR(gv_cur_region));
|
|
rptr->not_this_time = give_up_before_create_tempfile;
|
|
continue;
|
|
}
|
|
/* Used to have MAX_RMS_RECORDSIZE here (instead of 32 * 1024) but this def does not exist on
|
|
* UNIX where we are making the same restirction due to lack of testing more than anything else
|
|
* so the hard coded value will do for now. SE 5/2005
|
|
*/
|
|
if (incremental && ((32 * 1024) - SIZEOF(shmpool_blk_hdr)) < cs_data->blk_size)
|
|
{ /* Limitation: VMS RMS IO limited to 32K - 1 VMS blk so we likewise limit our IO. This can be
|
|
* overcome with more code to deal with the larger block sizes much like the regular
|
|
* backup does but this is not being done as part of this (64bittn) project. SE 2/2005
|
|
*/
|
|
gtm_putmsg(VARLSTCNT(5) MAKE_MSG_TYPE(ERR_MUNOSTRMBKUP, ERROR), 3, DB_LEN_STR(gv_cur_region),
|
|
32 * 1024 - DISK_BLOCK_SIZE);
|
|
rptr->not_this_time = give_up_before_create_tempfile;
|
|
continue;
|
|
}
|
|
rptr->backup_hdr = (sgmnt_data_ptr_t)malloc(size);
|
|
if (TRUE == online)
|
|
{ /* determine the directory name and prefix for the temp file */
|
|
memset(tempnam_prefix, 0, MAX_FN_LEN);
|
|
memcpy(tempnam_prefix, gv_cur_region->rname, gv_cur_region->rname_len);
|
|
SPRINTF(&tempnam_prefix[gv_cur_region->rname_len], "_%x", process_id);
|
|
if ((SS_NORMAL == trans_log_name_status)
|
|
&& (NULL != tempdir_trans.addr) && (0 != tempdir_trans.len))
|
|
*(tempdir_trans.addr + tempdir_trans.len) = 0;
|
|
else if (incremental && (backup_to_file != rptr->backup_to))
|
|
{
|
|
tempdir_trans.addr = tempdir_trans_buffer;
|
|
tempdir_trans.len = SIZEOF(SCRATCH_DIR) - 1;
|
|
memcpy(tempdir_trans_buffer, SCRATCH_DIR, tempdir_trans.len);
|
|
tempdir_trans_buffer[tempdir_trans.len]='\0';
|
|
} else
|
|
{
|
|
ptr = rptr->backup_file.addr + rptr->backup_file.len - 1;
|
|
tempdir_trans.addr = tempdir_trans_buffer;
|
|
while ((PATH_DELIM != *ptr) && (ptr > rptr->backup_file.addr))
|
|
ptr--;
|
|
if (ptr > rptr->backup_file.addr)
|
|
{
|
|
memcpy(tempdir_trans_buffer, rptr->backup_file.addr,
|
|
(tempdir_trans.len = INTCAST(ptr - rptr->backup_file.addr + 1)));
|
|
tempdir_trans_buffer[tempdir_trans.len] = '\0';
|
|
} else
|
|
# ifdef UNIX
|
|
{
|
|
tempdir_trans_buffer[0] = '.';
|
|
tempdir_trans_buffer[1] = '\0';
|
|
tempdir_trans.len = 1;
|
|
}
|
|
}
|
|
/* verify the accessibility of the tempdir */
|
|
if (FILE_STAT_ERROR == (fstat_res = gtm_file_stat(&tempdir_trans, NULL, &tempdir_full, FALSE, &ustatus)))
|
|
{
|
|
gtm_putmsg(VARLSTCNT(5) ERR_FILEPARSE, 2, tempdir_trans.len, tempdir_trans.addr, ustatus);
|
|
mubclnup(rptr, need_to_del_tempfile);
|
|
mupip_exit(ustatus);
|
|
}
|
|
SPRINTF(tempfilename + tempdir_full.len,"/%s_XXXXXX",tempnam_prefix);
|
|
MKSTEMP(tempfilename, rptr->backup_fd);
|
|
if (FD_INVALID == rptr->backup_fd)
|
|
{
|
|
status = errno;
|
|
if ((NULL != tempdir_full.addr) &&
|
|
(0 != ACCESS(tempdir_full.addr, TMPDIR_ACCESS_MODE)))
|
|
{
|
|
status = errno;
|
|
util_out_print("!/Do not have full access to directory for temporary files: !AD", TRUE,
|
|
tempdir_trans.len, tempdir_trans.addr);
|
|
} else
|
|
util_out_print("!/Cannot create the temporary file in directory !AD for online backup",
|
|
TRUE, tempdir_trans.len, tempdir_trans.addr);
|
|
gtm_putmsg(VARLSTCNT(1) status);
|
|
util_out_print("!/MUPIP cannot start backup with above errors!/", TRUE);
|
|
mubclnup(rptr, need_to_del_tempfile);
|
|
mupip_exit(status);
|
|
}
|
|
# ifdef __MVS__
|
|
if (-1 == gtm_zos_set_tag(rptr->backup_fd, TAG_BINARY, TAG_NOTTEXT, TAG_FORCE, &realfiletag))
|
|
TAG_POLICY_GTM_PUTMSG(tempfilename, realfiletag, TAG_BINARY, errno);
|
|
# endif
|
|
/* Temporary file for backup was created above using "mkstemp" which on AIX opens the file without
|
|
* large file support enabled. Work around that by closing the file descriptor returned and reopening
|
|
* the file with the "open" system call (which gets correctly translated to "open64"). We need to do
|
|
* this because the temporary file can get > 2GB. Since it is not clear if mkstemp on other Unix platforms
|
|
* will open the file for large file support, we use this solution for other Unix flavours as well.
|
|
*/
|
|
tmpfd = rptr->backup_fd;
|
|
OPENFILE(tempfilename, O_RDWR, rptr->backup_fd);
|
|
if (FD_INVALID == rptr->backup_fd)
|
|
{
|
|
status = errno;
|
|
util_out_print("!/Error re-opening temporary file created by mkstemp()!/", TRUE);
|
|
gtm_putmsg(VARLSTCNT(1) status);
|
|
util_out_print("!/MUPIP cannot start backup with above errors!/", TRUE);
|
|
mubclnup(rptr, need_to_del_tempfile);
|
|
mupip_exit(status);
|
|
}
|
|
/* Now that the temporary file has been opened successfully, close the fd returned by mkstemp */
|
|
F_CLOSE(tmpfd, fclose_res); /* resets "tmpfd" to FD_INVALID */
|
|
tempdir_full.len = STRLEN(tempdir_full.addr); /* update the length */
|
|
# ifdef __MVS__
|
|
if (-1 == gtm_zos_tag_to_policy(rptr->backup_fd, TAG_BINARY, &realfiletag))
|
|
TAG_POLICY_GTM_PUTMSG(tempfilename, realfiletag, TAG_BINARY, errno);
|
|
# endif
|
|
if (debug_mupip)
|
|
util_out_print("!/MUPIP INFO: Temp file name: !AD", TRUE,tempdir_full.len, tempdir_full.addr);
|
|
memcpy(&rptr->backup_tempfile[0], tempdir_full.addr, tempdir_full.len);
|
|
rptr->backup_tempfile[tempdir_full.len] = 0;
|
|
/* give temporary files the group and permissions as other shared resources - like journal files */
|
|
FSTAT_FILE(((unix_db_info *)(gv_cur_region->dyn.addr->file_cntl->file_info))->fd, &stat_buf, fstat_res);
|
|
if (-1 != fstat_res)
|
|
if (gtm_set_group_and_perm(&stat_buf, &group_id, &perm, PERM_FILE, &pdd) < 0)
|
|
{
|
|
send_msg(VARLSTCNT(6+PERMGENDIAG_ARG_COUNT)
|
|
ERR_PERMGENFAIL, 4, RTS_ERROR_STRING("backup file"),
|
|
RTS_ERROR_STRING(
|
|
((unix_db_info *)(gv_cur_region->dyn.addr->file_cntl->file_info))->fn),
|
|
PERMGENDIAG_ARGS(pdd));
|
|
gtm_putmsg(VARLSTCNT(6+PERMGENDIAG_ARG_COUNT)
|
|
ERR_PERMGENFAIL, 4, RTS_ERROR_STRING("backup file"),
|
|
RTS_ERROR_STRING(
|
|
((unix_db_info *)(gv_cur_region->dyn.addr->file_cntl->file_info))->fn),
|
|
PERMGENDIAG_ARGS(pdd));
|
|
mubclnup(rptr, need_to_del_tempfile);
|
|
mupip_exit(EPERM);
|
|
}
|
|
/* setup new group and permissions if indicated by the security rules. Use
|
|
* 0770 anded with current mode for the new mode if masked permission selected.
|
|
*/
|
|
if ((-1 == fstat_res) || (-1 == FCHMOD(rptr->backup_fd, perm))
|
|
|| ((-1 != group_id) && (-1 == fchown(rptr->backup_fd, -1, group_id))))
|
|
{
|
|
status = errno;
|
|
gtm_putmsg(VARLSTCNT(1) status);
|
|
util_out_print("!/MUPIP cannot start backup with above errors!/", TRUE);
|
|
mubclnup(rptr, need_to_del_tempfile);
|
|
mupip_exit(status);
|
|
}
|
|
# elif defined(VMS)
|
|
assert(FALSE);
|
|
}
|
|
temp_xabpro = cc$rms_xabpro;
|
|
temp_xabpro.xab$w_pro = ((vms_gds_info *)(gv_cur_region->dyn.addr->file_cntl->file_info))->xabpro->xab$w_pro
|
|
& (~((XAB$M_NODEL << XAB$V_SYS) | (XAB$M_NODEL << XAB$V_OWN)));
|
|
temp_nam = cc$rms_nam;
|
|
temp_nam.nam$l_rsa = rptr->backup_tempfile;
|
|
temp_nam.nam$b_rss = SIZEOF(rptr->backup_tempfile) - 1; /* temp solution, note it is a byte value */
|
|
temp_fab = cc$rms_fab;
|
|
temp_fab.fab$l_nam = &temp_nam;
|
|
temp_fab.fab$l_xab = &temp_xabpro;
|
|
temp_fab.fab$b_org = FAB$C_SEQ;
|
|
temp_fab.fab$l_fop = FAB$M_MXV | FAB$M_CBT | FAB$M_TEF | FAB$M_CIF;
|
|
temp_fab.fab$b_fac = FAB$M_GET | FAB$M_PUT | FAB$M_BIO | FAB$M_TRN;
|
|
gtm_tempnam(tempdir_trans.addr, tempnam_prefix, tempfilename);
|
|
temp_file_name_len = exp_file_name_len = strlen(tempfilename);
|
|
memcpy(exp_file_name, tempfilename, temp_file_name_len);
|
|
if (!get_full_path(tempfilename, temp_file_name_len, exp_file_name,
|
|
&exp_file_name_len, SIZEOF(exp_file_name), &ustatus))
|
|
{
|
|
util_out_print("!/Unable to resolve concealed definition for file !AD ", TRUE,
|
|
temp_file_name_len, tempfilename);
|
|
gtm_putmsg(VARLSTCNT(1) ustatus);
|
|
mubclnup(rptr, need_to_del_tempfile);
|
|
mupip_exit(ERR_MUNOACTION);
|
|
}
|
|
if (debug_mupip)
|
|
util_out_print("!/MUPIP INFO: Temp file name: !AD", TRUE, exp_file_name_len, exp_file_name);
|
|
temp_fab.fab$l_fna = exp_file_name;
|
|
temp_fab.fab$b_fns = exp_file_name_len;
|
|
ntries = 0;
|
|
gotit = FALSE;
|
|
while (TRUE != gotit)
|
|
{
|
|
switch (status = sys$create(&temp_fab))
|
|
{
|
|
case RMS$_CREATED:
|
|
gotit = TRUE;
|
|
break;
|
|
case RMS$_NORMAL:
|
|
case RMS$_SUPERSEDE:
|
|
case RMS$_FILEPURGED:
|
|
sys$close(&temp_fab);
|
|
ntries++;
|
|
gtm_tempnam(tempdir_trans.addr, tempnam_prefix, tempfilename);
|
|
temp_fab.fab$l_fna = tempfilename;
|
|
temp_fab.fab$b_fns = strlen(tempfilename);
|
|
break;
|
|
default:
|
|
error_mupip = TRUE;
|
|
}
|
|
if (error_mupip || (ntries > MAX_TEMP_OPEN_TRY))
|
|
{
|
|
util_out_print("!/Cannot create the temporary file !AD for online backup.", TRUE,
|
|
LEN_AND_STR(tempfilename));
|
|
gtm_putmsg(VARLSTCNT(1) status);
|
|
mubclnup(rptr, need_to_del_tempfile);
|
|
mupip_exit(ERR_MUNOACTION);
|
|
}
|
|
}
|
|
rptr->backup_tempfile[temp_nam.nam$b_rsl] = '\0';
|
|
sys$close(&temp_fab);
|
|
# else
|
|
# error Unsupported Platform
|
|
# endif
|
|
} else
|
|
{
|
|
while (REG_ALREADY_FROZEN == region_freeze(gv_cur_region, TRUE, FALSE, FALSE))
|
|
{
|
|
hiber_start(1000);
|
|
if ((TRUE == mu_ctrly_occurred) || (TRUE == mu_ctrlc_occurred))
|
|
{
|
|
mubclnup(rptr, need_to_del_tempfile);
|
|
gtm_putmsg(VARLSTCNT(1) ERR_FREEZECTRL);
|
|
mupip_exit(ERR_MUNOFINISH);
|
|
}
|
|
}
|
|
}
|
|
if (cs_addrs->hdr->blk_size > mubmaxblk)
|
|
mubmaxblk = cs_addrs->hdr->blk_size + 4;
|
|
halt_ptr = (tp_region *)(rptr->fPtr);
|
|
}
|
|
if ((TRUE == mu_ctrly_occurred) || (TRUE == mu_ctrlc_occurred))
|
|
{
|
|
mubclnup(rptr, need_to_del_tempfile);
|
|
gtm_putmsg(VARLSTCNT(1) ERR_BACKUPCTRL);
|
|
mupip_exit(ERR_MUNOFINISH);
|
|
}
|
|
# ifdef UNIX
|
|
udi = NULL;
|
|
if (NULL != mu_repl_inst_reg_list)
|
|
{ /* Replication instance file needs to be backed up as well. But, before doing so, we need to get the ftok and
|
|
* the access control semaphore on the instance file and the journal pool. By doing so, we shut off other concurrent
|
|
* writers to the instance file.
|
|
* Note: It is important that this is done BEFORE grabbing crit on any of the regions as otherwise we might be
|
|
* creating a possible chance of deadlock due to out-of-order access. To illustrate, lets say we went ahead and got
|
|
* crit on all regions and are about to get the access control semaphore. During this time, online rollback comes
|
|
* along and acquires the access control semaphore on the journal pool and proceeds to get the crit on all regions.
|
|
* Now, backup holds crit and needs access control semaphore whereas rollback holds the access control semaphore
|
|
* and needs crit. Classic deadlock.
|
|
*/
|
|
decr_cnt = FALSE;
|
|
if (!pool_init)
|
|
{
|
|
if (NULL == jnlpool.jnlpool_dummy_reg)
|
|
{
|
|
r_save = gv_cur_region;
|
|
mu_gv_cur_reg_init();
|
|
jnlpool.jnlpool_dummy_reg = reg = gv_cur_region;
|
|
gv_cur_region = r_save;
|
|
ASSERT_IN_RANGE(MIN_RN_LEN, SIZEOF(JNLPOOL_DUMMY_REG_NAME) - 1, MAX_RN_LEN);
|
|
MEMCPY_LIT(reg->rname, JNLPOOL_DUMMY_REG_NAME);
|
|
reg->rname_len = STR_LIT_LEN(JNLPOOL_DUMMY_REG_NAME);
|
|
reg->rname[reg->rname_len] = '\0';
|
|
if (!repl_inst_get_name(instfilename, &full_len, MAX_FN_LEN + 1, issue_rts_error))
|
|
GTMASSERT; /* rts_error should have been issued by repl_inst_get_name */
|
|
udi = FILE_INFO(reg);
|
|
seg = reg->dyn.addr;
|
|
memcpy((char *)seg->fname, instfilename, full_len);
|
|
udi->fn = (char *)seg->fname;
|
|
seg->fname_len = full_len;
|
|
seg->fname[full_len] = '\0';
|
|
udi->ftok_semid = INVALID_SEMID;
|
|
}
|
|
else
|
|
{ /* Possible if jnlpool_init did mu_gv_cur_reg_init but returned prematurely due to NOJNLPOOL */
|
|
assert(!jnlpool.jnlpool_dummy_reg->open);
|
|
reg = jnlpool.jnlpool_dummy_reg;
|
|
/* Since mu_gv_cur_reg_init is already done, ensure that the reg->rname is correct */
|
|
assert(0 == MEMCMP_LIT(reg->rname, JNLPOOL_DUMMY_REG_NAME));
|
|
assert(reg->rname_len == STR_LIT_LEN(JNLPOOL_DUMMY_REG_NAME));
|
|
assert('\0' == reg->rname[reg->rname_len]);
|
|
udi = FILE_INFO(reg);
|
|
}
|
|
if (INVALID_SEMID == udi->ftok_semid)
|
|
{
|
|
if (!ftok_sem_get(jnlpool.jnlpool_dummy_reg, TRUE, REPLPOOL_ID, FALSE))
|
|
{
|
|
gtm_putmsg(VARLSTCNT(1) ERR_JNLPOOLSETUP);
|
|
error_mupip = TRUE;
|
|
goto repl_inst_bkup_done1;
|
|
}
|
|
decr_cnt = TRUE;
|
|
} else if (!ftok_sem_lock(reg, FALSE, FALSE))
|
|
{
|
|
gtm_putmsg(VARLSTCNT(1) ERR_JNLPOOLSETUP);
|
|
error_mupip = TRUE;
|
|
goto repl_inst_bkup_done1;
|
|
}
|
|
} else
|
|
{
|
|
udi = FILE_INFO(jnlpool.jnlpool_dummy_reg);
|
|
assert(udi->ftok_semid && (INVALID_SEMID != udi->ftok_semid));
|
|
if (!ftok_sem_lock(jnlpool.jnlpool_dummy_reg, FALSE, FALSE))
|
|
{
|
|
gtm_putmsg(VARLSTCNT(1) ERR_JNLPOOLSETUP);
|
|
error_mupip = TRUE;
|
|
goto repl_inst_bkup_done1;
|
|
}
|
|
}
|
|
assert(NULL != udi);
|
|
repl_inst_read(udi->fn, (off_t)0, (sm_uc_ptr_t)&repl_instance, SIZEOF(repl_inst_hdr));
|
|
save_inst_hdr = pool_init ? jnlpool.repl_inst_filehdr : NULL;
|
|
inst_hdr = jnlpool.repl_inst_filehdr = &repl_instance;
|
|
assert(NULL != jnlpool.jnlpool_dummy_reg);
|
|
assert(!pool_init || (NULL != jnlpool_ctl));
|
|
shm_id = inst_hdr->jnlpool_shmid;
|
|
sem_id = inst_hdr->jnlpool_semid;
|
|
udi = FILE_INFO(jnlpool.jnlpool_dummy_reg);
|
|
if (INVALID_SEMID != sem_id)
|
|
{
|
|
assert(inst_hdr->crash);
|
|
semarg.buf = &semstat;
|
|
if (-1 == semctl(inst_hdr->jnlpool_semid, 0, IPC_STAT, semarg))
|
|
{
|
|
save_errno = errno;
|
|
gtm_putmsg(VARLSTCNT(5) ERR_REPLREQROLLBACK, 2, full_len, udi->fn, save_errno);
|
|
error_mupip = TRUE;
|
|
goto repl_inst_bkup_done1;
|
|
} else if (semarg.buf->sem_ctime != inst_hdr->jnlpool_semid_ctime)
|
|
{
|
|
save_errno = errno;
|
|
gtm_putmsg(VARLSTCNT(8) ERR_REPLREQROLLBACK, 2, full_len, udi->fn,
|
|
ERR_TEXT, 2, RTS_ERROR_TEXT("jnlpool sem_ctime does not match"));
|
|
error_mupip = TRUE;
|
|
goto repl_inst_bkup_done1;
|
|
}
|
|
set_sem_set_src(sem_id); /* repl_sem.c has some functions which needs semid in static variable */
|
|
status = grab_sem(SOURCE, JNL_POOL_ACCESS_SEM);
|
|
if (SS_NORMAL != status)
|
|
{
|
|
save_errno = errno;
|
|
gtm_putmsg(VARLSTCNT(7) ERR_JNLPOOLSETUP, 0, ERR_TEXT, 2,
|
|
RTS_ERROR_LITERAL("Error with journal pool access semaphore"), REPL_SEM_ERRNO);
|
|
error_mupip = TRUE;
|
|
goto repl_inst_bkup_done1;
|
|
}
|
|
udi->grabbed_access_sem = TRUE;
|
|
}
|
|
/* At this point, we either hold the access control lock on the journal pool OR the journal pool
|
|
* semaphore doesn't exist. In either case, we can proceed with the "cp" of the instance file
|
|
*/
|
|
assert(holds_sem[SOURCE][JNL_POOL_ACCESS_SEM] || (INVALID_SEMID == sem_id));
|
|
if (inst_hdr->file_corrupt)
|
|
{
|
|
save_errno = errno;
|
|
gtm_putmsg(VARLSTCNT(8) ERR_REPLREQROLLBACK, 2, full_len, udi->fn,
|
|
ERR_TEXT, 2, LEN_AND_LIT("file_corrupt field in instance file header is set to TRUE"));
|
|
error_mupip = TRUE;
|
|
}
|
|
}
|
|
repl_inst_bkup_done1:
|
|
# endif
|
|
kip_count = 0;
|
|
SET_GBL_JREC_TIME; /* routines that write jnl records (e.g. wcs_flu) require this to be initialized */
|
|
DEBUG_ONLY(reg_count = 0);
|
|
for (rptr = (backup_reg_list *)(grlist); NULL != rptr; rptr = rptr->fPtr)
|
|
{
|
|
if (rptr->not_this_time <= keep_going)
|
|
{
|
|
TP_CHANGE_REG(rptr->reg);
|
|
assert(!cs_addrs->hold_onto_crit); /* DSE/Rollback can never reach here */
|
|
if (online)
|
|
{
|
|
grab_crit(gv_cur_region);
|
|
DEBUG_ONLY(reg_count++);
|
|
INCR_INHIBIT_KILLS(cs_addrs->nl);
|
|
if (cs_data->kill_in_prog)
|
|
kip_count++;
|
|
} else if (JNL_ENABLED(cs_data))
|
|
{ /* For the non-online case, we want to get crit on ALL regions to ensure we write the
|
|
* same timestamp in journal records across ALL regions (in wcs_flu below). We will
|
|
* release crit as soon as the wcs_flu across all regions is done.
|
|
*/
|
|
grab_crit(gv_cur_region);
|
|
DEBUG_ONLY(reg_count++);
|
|
}
|
|
/* We will be releasing crit if KIP is set, so don't update jgbl.gbl_jrec_time now */
|
|
if (!kip_count)
|
|
{
|
|
UPDATE_GBL_JREC_TIME;
|
|
}
|
|
}
|
|
}
|
|
/* If we have KILLs in progress on any of the regions, wait a maximum of 1 minute(s) for those to finish. */
|
|
wait_for_zero_kip = (online && kip_count);
|
|
for (crit_counter = 1; wait_for_zero_kip; )
|
|
{ /* Release all the crits before going into the wait loop */
|
|
DEBUG_ONLY(nocritrptr = NULL;)
|
|
for (rptr = (backup_reg_list *)(grlist); NULL != rptr; rptr = rptr->fPtr)
|
|
{
|
|
if (rptr->not_this_time > keep_going)
|
|
continue;
|
|
TP_CHANGE_REG(rptr->reg);
|
|
/* It is possible to not hold crit on some regions if we are in the second or higher iteration
|
|
* of the outer for loop (the one with the loop variable wait_for_zero_kip).
|
|
*/
|
|
if (cs_addrs->now_crit)
|
|
{ /* once we encountered a region in the list that we did not hold crit on, we should also
|
|
* not hold crit on all later regions in the list. assert that.
|
|
*/
|
|
assert(NULL == nocritrptr);
|
|
rel_crit(gv_cur_region);
|
|
}
|
|
DEBUG_ONLY(
|
|
else
|
|
nocritrptr = rptr;
|
|
)
|
|
}
|
|
/* Wait for a maximum of 1 minute on all the regions for KIP to reset */
|
|
for (rptr = (backup_reg_list *)(grlist); NULL != rptr; rptr = rptr->fPtr)
|
|
{
|
|
if (rptr->not_this_time > keep_going)
|
|
continue;
|
|
TP_CHANGE_REG(rptr->reg);
|
|
if (debug_mupip)
|
|
{
|
|
GET_CUR_TIME;
|
|
util_out_print("!/MUPIP INFO: !AD : Start kill-in-prog wait for database !AD", TRUE,
|
|
CTIME_BEFORE_NL, time_ptr, DB_LEN_STR(gv_cur_region));
|
|
}
|
|
UNIX_ONLY(kip_pids_arr_ptr = cs_addrs->nl->kip_pid_array);
|
|
while (cs_data->kill_in_prog && (MAX_CRIT_TRY > crit_counter++))
|
|
{
|
|
UNIX_ONLY(GET_C_STACK_FOR_KIP(kip_pids_arr_ptr, crit_counter, MAX_CRIT_TRY, 1, MAX_KIP_PID_SLOTS));
|
|
wcs_sleep(crit_counter);
|
|
}
|
|
}
|
|
if (debug_mupip)
|
|
{
|
|
GET_CUR_TIME;
|
|
util_out_print("!/MUPIP INFO: !AD : Done with kill-in-prog wait on ALL databases", TRUE,
|
|
CTIME_BEFORE_NL, time_ptr);
|
|
}
|
|
/* Since we have waited a while for KIP to get reset, get current time again to make it more accurate */
|
|
SET_GBL_JREC_TIME;
|
|
/* Most code in this for-loop is similar to the previous for loop where we grab_crit;
|
|
* but most of the times the second for loop will never be executed (because KIP will be
|
|
* zero on all database files) and in some rare cases, it is okay to take the hit of an
|
|
* extra rel_crit/wait-for-kip/grab_crit sequence.
|
|
*/
|
|
wait_for_zero_kip = (MAX_CRIT_TRY > crit_counter);
|
|
kip_count = 0;
|
|
for (rptr = (backup_reg_list *)(grlist); NULL != rptr; rptr = rptr->fPtr)
|
|
{
|
|
if (rptr->not_this_time > keep_going)
|
|
continue;
|
|
TP_CHANGE_REG(rptr->reg);
|
|
grab_crit(gv_cur_region);
|
|
if (cs_data->kill_in_prog)
|
|
{ /* It is possible for this to happen in case a concurrent GT.M process is in its 4th retry.
|
|
* In that case, it will not honor the inhibit_kills flag since it holds crit and therefore
|
|
* could have set kill-in-prog to a non-zero value while we were outside of crit.
|
|
* Check if we have waited 1 minute until now. If not, release crit and continue the wait.
|
|
* If waited already, then proceed with the backup. The reasoning is that once the GT.M process
|
|
* that is in the final retry finishes off the second part of the M-kill, it will not start
|
|
* a new transaction in the first try which is outside of crit so will honor the inhibit-kills
|
|
* flag and therefore not increment the kill_in_prog counter any more until backup is done.
|
|
* So we could be waiting for at most 1 kip increment per concurrent process that is updating
|
|
* the database. We expect these kills to be complete within 1 minute.
|
|
*/
|
|
if (wait_for_zero_kip)
|
|
{
|
|
kip_count++;
|
|
break;
|
|
}
|
|
assert(!kip_count);
|
|
UNIX_ONLY(GET_C_STACK_FOR_KIP(kip_pids_arr_ptr, crit_counter, MAX_CRIT_TRY, 2, MAX_KIP_PID_SLOTS));
|
|
gtm_putmsg(VARLSTCNT(4) ERR_BACKUPKILLIP, 2, DB_LEN_STR(gv_cur_region));
|
|
}
|
|
/* Now that we have crit, check if this region is actively journaled and if gbl_jrec_time needs to be
|
|
* adjusted (to ensure time ordering of journal records within this region's journal file).
|
|
* This needs to be done BEFORE writing any journal records for this region. The value of
|
|
* jgbl.gbl_jrec_time at the end of this loop will be used to write journal records for ALL
|
|
* regions so all regions will have same eov/bov timestamps.
|
|
*/
|
|
UPDATE_GBL_JREC_TIME;
|
|
}
|
|
if (0 == kip_count)
|
|
break; /* all regions have zero kill-in-prog so we can break out of this loop unconditionally */
|
|
}
|
|
assert(reg_count == have_crit(CRIT_HAVE_ANY_REG | CRIT_ALL_REGIONS));
|
|
DEBUG_ONLY(save_gbl_jrec_time = jgbl.gbl_jrec_time;)
|
|
/* ========================== STEP 4. Flush cache, calculate tn for incremental, and do backup =========== */
|
|
if ((FALSE == mu_ctrly_occurred) && (FALSE == mu_ctrlc_occurred))
|
|
{
|
|
mup_bak_pause(); /* ? save some crit time? */
|
|
backup_started = TRUE;
|
|
DEBUG_ONLY(jnl_seqno = 0;)
|
|
# ifdef UNIX
|
|
if (NULL != mu_repl_inst_reg_list)
|
|
{
|
|
if (error_mupip)
|
|
{ /* Some error happened while getting the ftok or the access control. Release locks, ignore instance
|
|
* file and continue with the backup of database files.
|
|
*/
|
|
goto repl_inst_bkup_done2;
|
|
}
|
|
assert(udi == FILE_INFO(jnlpool.jnlpool_dummy_reg));
|
|
seg = jnlpool.jnlpool_dummy_reg->dyn.addr; /* re-initialize (for pool_init == TRUE case) */
|
|
cmdptr = &command[0];
|
|
memcpy(cmdptr, "cp ", 3);
|
|
cmdptr += 3;
|
|
memcpy(cmdptr, seg->fname, seg->fname_len);
|
|
cmdptr[seg->fname_len] = ' ';
|
|
cmdptr += seg->fname_len + 1;
|
|
memcpy(cmdptr, mu_repl_inst_reg_list->backup_file.addr, mu_repl_inst_reg_list->backup_file.len);
|
|
cmdptr += mu_repl_inst_reg_list->backup_file.len;
|
|
*cmdptr = '\0';
|
|
if (0 != (rv = SYSTEM((char *)command)))
|
|
{
|
|
if (-1 == rv)
|
|
{
|
|
save_errno = errno;
|
|
errptr = (char *)STRERROR(save_errno);
|
|
util_out_print("system : !AZ", TRUE, errptr);
|
|
}
|
|
util_out_print("Error doing !AD", TRUE, cmdptr - command, command);
|
|
error_mupip = TRUE;
|
|
goto repl_inst_bkup_done2;
|
|
}
|
|
assert(!pool_init || (INVALID_SHMID != shm_id));
|
|
if (INVALID_SHMID != shm_id)
|
|
{
|
|
if (INVALID_SEMID == sem_id)
|
|
GTMASSERT; /* out-of-design situation */
|
|
/* The journal pool exists. Note down the journal seqno from there and copy that onto the backed up
|
|
* instance file header. Also, clean up other fields in the backed up instance file header.
|
|
*/
|
|
if (!pool_init)
|
|
{
|
|
if (-1 == shmctl(shm_id, IPC_STAT, &shm_buf))
|
|
{
|
|
save_errno = errno;
|
|
gtm_putmsg(VARLSTCNT(5) ERR_REPLPOOLINST, 3, shm_id,
|
|
RTS_ERROR_STRING(udi->fn));
|
|
gtm_putmsg(VARLSTCNT(8) ERR_SYSCALL, 5,
|
|
RTS_ERROR_LITERAL("shmctl()"), CALLFROM, save_errno);
|
|
error_mupip = TRUE;
|
|
goto repl_inst_bkup_done2;
|
|
}
|
|
if (-1 == (sm_long_t)(start_addr = (sm_uc_ptr_t) do_shmat(shm_id, 0, 0)))
|
|
{
|
|
save_errno = errno;
|
|
gtm_putmsg(VARLSTCNT(5) ERR_REPLPOOLINST, 3, shm_id,
|
|
RTS_ERROR_STRING(udi->fn));
|
|
gtm_putmsg(VARLSTCNT(8) ERR_SYSCALL, 5, RTS_ERROR_LITERAL("shmat()"), CALLFROM,
|
|
save_errno);
|
|
error_mupip = TRUE;
|
|
goto repl_inst_bkup_done2;
|
|
}
|
|
memcpy((void *)&replpool_id, (void *)start_addr, SIZEOF(replpool_identifier));
|
|
if (memcmp(replpool_id.label, GDS_RPL_LABEL, GDS_LABEL_SZ - 1))
|
|
{
|
|
if (!memcmp(replpool_id.label, GDS_RPL_LABEL, GDS_LABEL_SZ - 3))
|
|
util_out_print("Incorrect version for the journal pool shared memory "
|
|
"segment (id = !UL) belonging to replication instance !AD",
|
|
TRUE, shm_id, LEN_AND_STR(udi->fn));
|
|
else
|
|
util_out_print("Incorrect format for the journal pool shared memory segment"
|
|
" (id = !UL) belonging to replication instance !AD",
|
|
TRUE, shm_id, LEN_AND_STR(udi->fn));
|
|
error_mupip = TRUE;
|
|
goto repl_inst_bkup_done2;
|
|
}
|
|
if (memcmp(replpool_id.now_running, gtm_release_name, gtm_release_name_len + 1))
|
|
{
|
|
util_out_print("Attempt to access with version !AD, while already using !AD for "
|
|
"journal pool shared memory segment (id = !UL) belonging to replication "
|
|
"instance file !AD.", TRUE, gtm_release_name_len, gtm_release_name,
|
|
LEN_AND_STR(replpool_id.now_running), shm_id, LEN_AND_STR(udi->fn));
|
|
error_mupip = TRUE;
|
|
goto repl_inst_bkup_done2;
|
|
}
|
|
csa = &udi->s_addrs;
|
|
assert(!csa->hold_onto_crit);
|
|
jnlpool.jnlpool_ctl = (jnlpool_ctl_ptr_t)start_addr;
|
|
csa->critical = (mutex_struct_ptr_t)((sm_uc_ptr_t)jnlpool.jnlpool_ctl + JNLPOOL_CTL_SIZE);
|
|
csa->nl = (node_local_ptr_t)((sm_uc_ptr_t)csa->critical + CRIT_SPACE
|
|
+ SIZEOF(mutex_spin_parms_struct));
|
|
/* Do the per process initialization of mutex stuff (needed before grab_lock is done) */
|
|
csa->onln_rlbk_cycle = jnlpool.jnlpool_ctl->onln_rlbk_cycle;
|
|
assert(!mutex_per_process_init_pid || mutex_per_process_init_pid == process_id);
|
|
if (!mutex_per_process_init_pid)
|
|
mutex_per_process_init();
|
|
UNIX_ONLY(START_HEARTBEAT_IF_NEEDED;)
|
|
} /* else journal pool already initialized in gvcst_init */
|
|
grab_lock(jnlpool.jnlpool_dummy_reg, ASSERT_NO_ONLINE_ROLLBACK);
|
|
jnl_seqno = jnlpool.jnlpool_ctl->jnl_seqno;
|
|
assert(0 != jnl_seqno);
|
|
/* All the cleanup we want is exactly done by the "repl_inst_histinfo_truncate" function. But
|
|
* we dont want to clean the instance file. We want to instead clean the backed up instance file.
|
|
* To that effect, temporarily change "udi->fn" to reflect the backed up file so all the changes
|
|
* get flushed there. Restore it after the function call.
|
|
*/
|
|
udi->fn = (char *)mu_repl_inst_reg_list->backup_file.addr;
|
|
/* Before invoking "repl_inst_histinfo_truncate", set "strm_seqno[]" appropriately in the instance
|
|
* file header. The truncate function call expects this to be set before invocation.
|
|
*/
|
|
COPY_JCTL_STRMSEQNO_TO_INSTHDR_IF_NEEDED;
|
|
repl_inst_histinfo_truncate(jnl_seqno); /* Flush updated file header to backed up instance file */
|
|
rel_lock(jnlpool.jnlpool_dummy_reg);
|
|
udi->fn = (char *)seg->fname; /* Restore */
|
|
if (!pool_init)
|
|
{
|
|
jnlpool.jnlpool_ctl = NULL; /* or else exit handling will try to rel_lock this as well */
|
|
if (-1 == shmdt((caddr_t)start_addr))
|
|
{
|
|
save_errno = errno;
|
|
gtm_putmsg(VARLSTCNT(5) ERR_REPLPOOLINST, 3, shm_id,
|
|
RTS_ERROR_STRING(udi->fn));
|
|
gtm_putmsg(VARLSTCNT(8) ERR_SYSCALL, 5, RTS_ERROR_LITERAL("shmdt()"), CALLFROM,
|
|
save_errno);
|
|
error_mupip = TRUE;
|
|
goto repl_inst_bkup_done2;
|
|
}
|
|
} else
|
|
{ /* Now that instance file truncate is done, restore jnlpool.repl_inst_filehdr to its
|
|
* original value
|
|
*/
|
|
assert(NULL != save_inst_hdr);
|
|
jnlpool.repl_inst_filehdr = save_inst_hdr;
|
|
}
|
|
} else
|
|
{ /* We are guaranteed that NO one is actively accessing the instance file. So, no need to
|
|
* truncate the backed up instance file.
|
|
*/
|
|
jnl_seqno = jnlpool.repl_inst_filehdr->jnl_seqno;
|
|
}
|
|
assert(0 != jnl_seqno);
|
|
repl_inst_bkup_done2:
|
|
if (udi)
|
|
{
|
|
if (udi->grabbed_access_sem)
|
|
{
|
|
assert(holds_sem[SOURCE][JNL_POOL_ACCESS_SEM] && (INVALID_SEMID != sem_id));
|
|
if (SS_NORMAL != rel_sem_immediate(SOURCE, JNL_POOL_ACCESS_SEM))
|
|
GTMASSERT;
|
|
}
|
|
if (udi->grabbed_ftok_sem)
|
|
ftok_sem_release(jnlpool.jnlpool_dummy_reg, decr_cnt, TRUE);
|
|
}
|
|
if (!error_mupip)
|
|
{
|
|
util_out_print("Replication Instance file !AD backed up in file !AD", TRUE,
|
|
LEN_AND_STR(udi->fn),
|
|
mu_repl_inst_reg_list->backup_file.len, mu_repl_inst_reg_list->backup_file.addr);
|
|
util_out_print("Journal Seqnos up to 0x!16@XQ are backed up.", TRUE, &jnl_seqno);
|
|
util_out_print("", TRUE);
|
|
} else
|
|
util_out_print("Error backing up replication instance file !AD. Moving on to other backups.",
|
|
TRUE, LEN_AND_STR(udi->fn));
|
|
}
|
|
# endif
|
|
jgbl.dont_reset_gbl_jrec_time = TRUE;
|
|
for (rptr = (backup_reg_list *)(grlist); NULL != rptr; rptr = rptr->fPtr)
|
|
{
|
|
if (rptr->not_this_time > keep_going)
|
|
continue;
|
|
TP_CHANGE_REG(rptr->reg);
|
|
wcs_flu(WCSFLU_FLUSH_HDR | WCSFLU_WRITE_EPOCH | WCSFLU_MSYNC_DB); /* For VMS, WCSFLU_FSYNC_DB and
|
|
* WCSFLU_MSYNC_DB are ignored */
|
|
if (incremental)
|
|
{
|
|
if (inc_since_inc)
|
|
{
|
|
rptr->tn = cs_data->last_inc_backup;
|
|
rptr->last_blk_at_last_bkup = cs_data->last_inc_bkup_last_blk;
|
|
} else if (inc_since_rec)
|
|
{
|
|
rptr->tn = cs_data->last_rec_backup;
|
|
rptr->last_blk_at_last_bkup = cs_data->last_rec_bkup_last_blk;
|
|
} else if (0 == tn)
|
|
{
|
|
rptr->tn = cs_data->last_com_backup;
|
|
rptr->last_blk_at_last_bkup = cs_data->last_com_bkup_last_blk;
|
|
} else
|
|
{ /* /TRANS was specified as arg so use it and all bitmaps are
|
|
subject to being backed up (since this is not a contiguous backup
|
|
to the last one)
|
|
*/
|
|
rptr->tn = tn;
|
|
rptr->last_blk_at_last_bkup = 0;
|
|
}
|
|
}
|
|
assert((0 == jnl_seqno) || !REPL_ENABLED(cs_data) || (cs_data->reg_seqno <= jnl_seqno));
|
|
memcpy(rptr->backup_hdr, cs_data, SIZEOF_FILE_HDR(cs_data));
|
|
if (online) /* save a copy of the fileheader, modify current fileheader and release crit */
|
|
{
|
|
if (0 != cs_addrs->hdr->abandoned_kills)
|
|
{
|
|
gtm_putmsg(VARLSTCNT(6) ERR_KILLABANDONED, 4, DB_LEN_STR(rptr->reg),
|
|
LEN_AND_LIT("backup database could have incorrectly marked busy integrity errors"));
|
|
}
|
|
sbufh_p = cs_addrs->shmpool_buffer;
|
|
if (BACKUP_NOT_IN_PROGRESS != cs_addrs->nl->nbb)
|
|
{
|
|
if (TRUE == is_proc_alive(sbufh_p->backup_pid, sbufh_p->backup_image_count))
|
|
{
|
|
/* someone else is doing the backup */
|
|
util_out_print("!/Process !UL is backing up region !AD now.", TRUE,
|
|
sbufh_p->backup_pid, REG_LEN_STR(rptr->reg));
|
|
rptr->not_this_time = give_up_after_create_tempfile;
|
|
/* Decerement counter so that inhibited KILLs can now proceed */
|
|
DECR_INHIBIT_KILLS(cs_addrs->nl);
|
|
rel_crit(rptr->reg);
|
|
continue;
|
|
}
|
|
}
|
|
if (!newjnlfiles_specified)
|
|
newjnlfiles = (JNL_ENABLED(cs_data)) ? TRUE : FALSE;
|
|
/* switch the journal file, if journaled */
|
|
if (newjnlfiles)
|
|
{
|
|
if (cs_data->jnl_file_len)
|
|
cre_jnl_file_intrpt_rename(((int)cs_data->jnl_file_len), cs_data->jnl_file_name);
|
|
if (JNL_ALLOWED(cs_data))
|
|
{
|
|
memset(&jnl_info, 0, SIZEOF(jnl_info));
|
|
jnl_info.prev_jnl = &prev_jnl_fn[0];
|
|
set_jnl_info(gv_cur_region, &jnl_info);
|
|
save_no_prev_link = jnl_info.no_prev_link = (jnl_options[jnl_noprevjnlfile] ||
|
|
!keep_prev_link || !JNL_ENABLED(cs_data)) ? TRUE : FALSE;
|
|
VMS_ONLY(
|
|
gds_info = FILE_INFO(gv_cur_region);
|
|
/* Is it possible for gds_info to be uninitialized? */
|
|
assert(jnl_info.fn_len == gds_info->fab->fab$b_fns);
|
|
assert(0 == memcmp(jnl_info.fn, gds_info->fab->fab$l_fna, jnl_info.fn_len));
|
|
)
|
|
if (JNL_ENABLED(cs_data) &&
|
|
UNIX_ONLY((0 != cs_addrs->nl->jnl_file.u.inode))
|
|
VMS_ONLY((0 != memcmp(cs_addrs->nl->jnl_file.jnl_file_id.fid,
|
|
zero_fid, SIZEOF(zero_fid)))))
|
|
{ /* Note: following will again call wcs_flu() */
|
|
if (SS_NORMAL != (status = set_jnl_file_close(SET_JNL_FILE_CLOSE_BACKUP)))
|
|
{
|
|
util_out_print("!/Journal file !AD not closed:",
|
|
TRUE, jnl_info.jnl_len, jnl_info.jnl);
|
|
gtm_putmsg(VARLSTCNT(1) status);
|
|
rptr->not_this_time = give_up_after_create_tempfile;
|
|
DECR_INHIBIT_KILLS(cs_addrs->nl);
|
|
rel_crit(rptr->reg);
|
|
error_mupip = TRUE;
|
|
continue;
|
|
}
|
|
jnl_info.no_rename = FALSE;
|
|
} else
|
|
{
|
|
filestr.addr = (char *)jnl_info.jnl;
|
|
filestr.len = jnl_info.jnl_len;
|
|
if (FILE_STAT_ERROR == (jnl_fstat =
|
|
gtm_file_stat(&filestr, NULL, NULL, FALSE, &ustatus)))
|
|
{
|
|
gtm_putmsg(VARLSTCNT(5) ERR_JNLFNF, 2,
|
|
filestr.len, filestr.addr, ustatus);
|
|
rptr->not_this_time = give_up_after_create_tempfile;
|
|
DECR_INHIBIT_KILLS(cs_addrs->nl);
|
|
rel_crit(rptr->reg);
|
|
error_mupip = TRUE;
|
|
continue;
|
|
}
|
|
jnl_info.no_rename = (FILE_NOT_FOUND == jnl_fstat);
|
|
}
|
|
wcs_flu(WCSFLU_FSYNC_DB | WCSFLU_FLUSH_HDR | WCSFLU_MSYNC_DB); /* For VMS
|
|
WCSFLU_FSYNC_DB is ignored */
|
|
if (!JNL_ENABLED(cs_data) && (NULL != cs_addrs->nl))
|
|
{ /* Cleanup the jnl file info in shared memory before switching
|
|
journal file. This case occurs if mupip backup -newjnl is
|
|
run after jnl_file_lost() closes journaling on a region */
|
|
NULLIFY_JNL_FILE_ID(cs_addrs);
|
|
}
|
|
if (replication_on)
|
|
{
|
|
if (REPL_WAS_ENABLED(cs_data))
|
|
{
|
|
jnl_info.jnl_state = jnl_open;
|
|
jnl_info.repl_state = repl_open;
|
|
jnl_info.no_prev_link = TRUE;
|
|
gtm_putmsg(VARLSTCNT(8) ERR_REPLSTATE, 6,
|
|
LEN_AND_LIT(FILE_STR), DB_LEN_STR(gv_cur_region),
|
|
LEN_AND_STR(repl_state_lit[repl_open]));
|
|
gtm_putmsg(VARLSTCNT(8) ERR_JNLSTATE, 6,
|
|
LEN_AND_LIT(FILE_STR), DB_LEN_STR(gv_cur_region),
|
|
LEN_AND_STR(jnl_state_lit[jnl_open]));
|
|
} else if (!REPL_ALLOWED(cs_data))
|
|
{
|
|
gtm_putmsg(VARLSTCNT(8) ERR_REPLSTATEERR, 2,
|
|
DB_LEN_STR(gv_cur_region), ERR_TEXT, 2,
|
|
LEN_AND_LIT("Standalone access required"));
|
|
rptr->not_this_time = give_up_after_create_tempfile;
|
|
DECR_INHIBIT_KILLS(cs_addrs->nl);
|
|
rel_crit(rptr->reg);
|
|
error_mupip = TRUE;
|
|
continue;
|
|
}
|
|
} else if (REPL_WAS_ENABLED(cs_data))
|
|
{ /* Do not switch journal file when replication was turned
|
|
OFF by jnl_file_lost() */
|
|
assert(cs_data->jnl_state == jnl_closed);
|
|
gtm_putmsg(VARLSTCNT(10) ERR_REPLJNLCNFLCT, 8,
|
|
LEN_AND_STR(jnl_state_lit[jnl_open]),
|
|
DB_LEN_STR(gv_cur_region),
|
|
LEN_AND_STR(repl_state_lit[repl_closed]),
|
|
LEN_AND_STR(jnl_state_lit[jnl_open]));
|
|
rptr->not_this_time = give_up_after_create_tempfile;
|
|
DECR_INHIBIT_KILLS(cs_addrs->nl);
|
|
rel_crit(rptr->reg);
|
|
error_mupip = TRUE;
|
|
continue;
|
|
} else
|
|
{ /* While switching journal file, perhaps we never intended
|
|
* to turn journaling ON from OFF (either with replication
|
|
* or with M journaling). However, to keep the existing
|
|
* behavior, we will turn journaling ON */
|
|
jnl_info.jnl_state = jnl_open;
|
|
}
|
|
if (EXIT_NRM == cre_jnl_file(&jnl_info))
|
|
{
|
|
if (jnl_info.no_prev_link && (save_no_prev_link != jnl_info.no_prev_link))
|
|
gtm_putmsg(VARLSTCNT(6) ERR_PREVJNLLINKCUT, 4,
|
|
JNL_LEN_STR(cs_data), DB_LEN_STR(rptr->reg));
|
|
memcpy(cs_data->jnl_file_name, jnl_info.jnl, jnl_info.jnl_len);
|
|
cs_data->jnl_file_name[jnl_info.jnl_len] = '\0';
|
|
cs_data->jnl_file_len = jnl_info.jnl_len;
|
|
cs_data->jnl_buffer_size = jnl_info.buffer;
|
|
cs_data->jnl_alq = jnl_info.alloc;
|
|
cs_data->jnl_deq = jnl_info.extend;
|
|
cs_data->jnl_before_image = jnl_info.before_images;
|
|
cs_data->jnl_state = jnl_info.jnl_state;
|
|
cs_data->repl_state = jnl_info.repl_state;
|
|
if (newjnlfiles_specified && sync_io_specified)
|
|
cs_data->jnl_sync_io = sync_io;
|
|
cs_data->jnl_checksum = jnl_info.checksum;
|
|
cs_data->jnl_eovtn = cs_data->trans_hist.curr_tn;
|
|
gtm_putmsg(VARLSTCNT(10) ERR_JNLCREATE, 8, jnl_info.jnl_len, jnl_info.jnl,
|
|
LEN_AND_LIT("region"), REG_LEN_STR(gv_cur_region),
|
|
LEN_AND_STR(before_image_lit[(jnl_info.before_images ? 1 : 0)]));
|
|
if (JNL_ENABLED(cs_data) &&
|
|
(jnl_options[jnl_noprevjnlfile] || !keep_prev_link))
|
|
gtm_putmsg(VARLSTCNT(6) ERR_PREVJNLLINKCUT,
|
|
4, JNL_LEN_STR(cs_data), DB_LEN_STR(gv_cur_region));
|
|
fc = gv_cur_region->dyn.addr->file_cntl;
|
|
fc->op = FC_WRITE;
|
|
fc->op_buff = (sm_uc_ptr_t)cs_data;
|
|
fc->op_len = SGMNT_HDR_LEN;
|
|
fc->op_pos = 1;
|
|
status = dbfilop(fc);
|
|
if (SS_NORMAL != status)
|
|
{
|
|
UNIX_ONLY(gtm_putmsg(VARLSTCNT(7) ERR_DBFILERR, 2,
|
|
DB_LEN_STR(gv_cur_region), 0, status, 0);)
|
|
VMS_ONLY(gtm_putmsg(VARLSTCNT(9) ERR_DBFILERR, 2,
|
|
DB_LEN_STR(gv_cur_region), 0, status, 0,
|
|
gds_info->fab->fab$l_stv, 0);)
|
|
rptr->not_this_time = give_up_after_create_tempfile;
|
|
DECR_INHIBIT_KILLS(cs_addrs->nl);
|
|
rel_crit(rptr->reg);
|
|
error_mupip = TRUE;
|
|
continue;
|
|
}
|
|
} else
|
|
gtm_putmsg(VARLSTCNT(4) ERR_JNLNOCREATE, 2, jnl_info.jnl_len, jnl_info.jnl);
|
|
} else
|
|
gtm_putmsg(VARLSTCNT(4) MAKE_MSG_WARNING(ERR_JNLDISABLE),
|
|
2, DB_LEN_STR(gv_cur_region));
|
|
} else if (replication_on && !REPL_ENABLED(cs_data))
|
|
{
|
|
gtm_putmsg(VARLSTCNT(8) ERR_REPLSTATEERR, 2, DB_LEN_STR(gv_cur_region),
|
|
ERR_TEXT, 2,
|
|
LEN_AND_LIT("Cannot turn replication ON without also switching journal file"));
|
|
rptr->not_this_time = give_up_after_create_tempfile;
|
|
DECR_INHIBIT_KILLS(cs_addrs->nl);
|
|
rel_crit(rptr->reg);
|
|
error_mupip = TRUE;
|
|
continue;
|
|
}
|
|
if (FALSE == shmpool_lock_hdr(gv_cur_region))
|
|
{
|
|
gtm_putmsg(VARLSTCNT(9) ERR_DBCCERR, 2, REG_LEN_STR(gv_cur_region),
|
|
ERR_ERRCALL, 3, CALLFROM);
|
|
rptr->not_this_time = give_up_after_create_tempfile;
|
|
DECR_INHIBIT_KILLS(cs_addrs->nl);
|
|
rel_crit(rptr->reg);
|
|
continue;
|
|
}
|
|
memcpy(&(sbufh_p->tempfilename[0]), &(rptr->backup_tempfile[0]),
|
|
strlen(rptr->backup_tempfile));
|
|
sbufh_p->tempfilename[strlen(rptr->backup_tempfile)] = 0;
|
|
sbufh_p->backup_tn = cs_addrs->ti->curr_tn;
|
|
sbufh_p->backup_pid = process_id;
|
|
sbufh_p->inc_backup_tn = (incremental ? rptr->tn : 0);
|
|
sbufh_p->dskaddr = 0;
|
|
sbufh_p->backup_errno = 0;
|
|
sbufh_p->failed = 0;
|
|
VMS_ONLY(sbufh_p->backup_image_count = image_count);
|
|
/* Make sure that the backup queue does not have any remnants on it. Note that we do not
|
|
depend on the queue count here as it is imperative that, in the event that the count
|
|
and queue get out of sync, that there ARE NO BLOCKS on this queue when we start or
|
|
the backup will have unknown potentially very stale blocks resulting in at best bad
|
|
data progressing to severe integrity errors.
|
|
*/
|
|
for (sblkh_p = SBLKP_REL2ABS(&sbufh_p->que_backup, fl);
|
|
sblkh_p != (shmpool_blk_hdr_ptr_t)&sbufh_p->que_backup;
|
|
sblkh_p = next_sblkh_p)
|
|
{ /* Loop through the queued backup blocks */
|
|
VERIFY_QUEUE((que_head_ptr_t)&sbufh_p->que_free);
|
|
VERIFY_QUEUE((que_head_ptr_t)&sbufh_p->que_backup);
|
|
next_sblkh_p = SBLKP_REL2ABS(sblkh_p, fl); /* Get next offset before remove entry */
|
|
shmpool_blk_free(rptr->reg, sblkh_p);
|
|
}
|
|
/* Unlock everything and let the backup proceed */
|
|
shmpool_unlock_hdr(gv_cur_region);
|
|
/* Signal to GT.M processes to start backing up before-images of any GDS block that gets changed
|
|
* starting now. To do that set "nbb" to -1 as that is smaller than every valid block number in db
|
|
* (starting from 0 upto a max. of 2^28-1 which is lesser than 2^31-1=BACKUP_NOT_IN_PROGRESS)
|
|
*/
|
|
cs_addrs->nl->nbb = -1; /* start */
|
|
/* Decerement counter so that inhibited KILLs can now proceed */
|
|
DECR_INHIBIT_KILLS(cs_addrs->nl);
|
|
rel_crit(rptr->reg);
|
|
} else
|
|
rel_crit(rptr->reg);
|
|
}
|
|
jgbl.dont_reset_gbl_jrec_time = FALSE;
|
|
/* Ensure jgbl.gbl_jrec_time did not get reset by any of the jnl writing functions */
|
|
assert(save_gbl_jrec_time == jgbl.gbl_jrec_time);
|
|
for (rptr = (backup_reg_list *)(grlist); NULL != rptr; rptr = rptr->fPtr)
|
|
{
|
|
if (rptr->not_this_time > keep_going)
|
|
continue;
|
|
/*
|
|
* For backed up database we want to change some file header fields.
|
|
*/
|
|
rptr->backup_hdr->freeze = 0;
|
|
rptr->backup_hdr->image_count = 0;
|
|
rptr->backup_hdr->kill_in_prog = 0;
|
|
VMS_ONLY(rptr->backup_hdr->owner_node = 0;)
|
|
memset(rptr->backup_hdr->machine_name, 0, MAX_MCNAMELEN);
|
|
rptr->backup_hdr->repl_state = repl_closed;
|
|
rptr->backup_hdr->semid = INVALID_SEMID;
|
|
rptr->backup_hdr->shmid = INVALID_SHMID;
|
|
rptr->backup_hdr->gt_sem_ctime.ctime = 0;
|
|
rptr->backup_hdr->gt_shm_ctime.ctime = 0;
|
|
if (jnl_options[jnl_off] || bkdbjnl_off_specified)
|
|
rptr->backup_hdr->jnl_state = jnl_closed;
|
|
if (jnl_options[jnl_disable] || bkdbjnl_disable_specified)
|
|
{
|
|
rptr->backup_hdr->jnl_state = jnl_notallowed;
|
|
rptr->backup_hdr->jnl_file_len = 0;
|
|
rptr->backup_hdr->jnl_file_name[0] = '\0';
|
|
}
|
|
if (jnl_options[jnl_off] || bkdbjnl_off_specified ||
|
|
jnl_options[jnl_disable] || bkdbjnl_disable_specified)
|
|
gtm_putmsg(VARLSTCNT(8) ERR_JNLSTATE, 6, LEN_AND_LIT(FILE_STR),
|
|
rptr->backup_file.len, rptr->backup_file.addr,
|
|
LEN_AND_STR(jnl_state_lit[rptr->backup_hdr->jnl_state]));
|
|
}
|
|
ENABLE_AST
|
|
mubbuf = (uchar_ptr_t)malloc(BACKUP_READ_SIZE);
|
|
for (rptr = (backup_reg_list *)(grlist); NULL != rptr; rptr = rptr->fPtr)
|
|
{
|
|
if (rptr->not_this_time > keep_going)
|
|
continue;
|
|
gv_cur_region = rptr->reg;
|
|
TP_CHANGE_REG(gv_cur_region); /* sets cs_addrs and cs_data which mubinccpy/mubfilcpy rely on */
|
|
# ifdef UNIX
|
|
if ((cs_addrs->onln_rlbk_cycle != cs_addrs->nl->onln_rlbk_cycle)
|
|
|| (0 != cs_addrs->nl->onln_rlbk_pid))
|
|
{ /* A concurrent online rollback happened since we did the gvcst_init or one is going on right now.
|
|
* In either case, the BACKUP is unreliable. Cleanup and exit
|
|
*/
|
|
error_mupip = TRUE;
|
|
gtm_putmsg(VARLSTCNT(1) ERR_DBROLLEDBACK);
|
|
break;
|
|
}
|
|
# endif
|
|
result = (incremental ? mubinccpy(rptr) : mubfilcpy(rptr));
|
|
if (FALSE == result)
|
|
{
|
|
if (file_backed_up)
|
|
util_out_print("Files backed up before above error are OK.", TRUE);
|
|
error_mupip = TRUE;
|
|
break;
|
|
}
|
|
if (mu_ctrly_occurred || mu_ctrlc_occurred)
|
|
break;
|
|
}
|
|
free(mubbuf);
|
|
} else
|
|
{
|
|
mubclnup((backup_reg_list *)halt_ptr, need_to_rel_crit);
|
|
gtm_putmsg(VARLSTCNT(1) ERR_BACKUPCTRL);
|
|
mupip_exit(ERR_MUNOFINISH);
|
|
}
|
|
/* =============================== STEP 5. clean up ============================================== */
|
|
mubclnup((backup_reg_list *)halt_ptr, need_to_del_tempfile);
|
|
REVERT;
|
|
if (mu_ctrly_occurred || mu_ctrlc_occurred)
|
|
{
|
|
gtm_putmsg(VARLSTCNT(1) ERR_BACKUPCTRL);
|
|
ret = ERR_MUNOFINISH;
|
|
} else if (TRUE == error_mupip)
|
|
ret = ERR_MUNOFINISH;
|
|
else
|
|
util_out_print("!/!/BACKUP COMPLETED.!/", TRUE);
|
|
gv_cur_region = NULL;
|
|
mupip_exit(ret);
|
|
}
|