fis-gtm/sr_port/mupip_backup.c

1425 lines
53 KiB
C

/****************************************************************
* *
* Copyright 2001, 2011 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"
#if defined(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"
#if defined(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"
#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"
#if defined(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)();
#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 uint4 mutex_per_process_init_pid;
#endif
LITREF char gtm_release_name[];
LITREF int4 gtm_release_name_len;
error_def(ERR_BACKUPCTRL);
error_def(ERR_DBCCERR);
error_def(ERR_DBFILERR);
error_def(ERR_DBRDONLY);
error_def(ERR_ERRCALL);
error_def(ERR_FILEEXISTS);
error_def(ERR_FILEPARSE);
error_def(ERR_FREEZECTRL);
error_def(ERR_JNLDISABLE);
error_def(ERR_JNLFNF);
error_def(ERR_JNLPOOLSETUP);
error_def(ERR_JNLSTATE);
error_def(ERR_MUNOACTION);
error_def(ERR_MUNOFINISH);
error_def(ERR_MUNOSTRMBKUP);
error_def(ERR_MUPCLIERR);
error_def(ERR_NOTRNDMACC);
error_def(ERR_PREVJNLLINKCUT);
error_def(ERR_REPLJNLCNFLCT);
error_def(ERR_REPLPOOLINST);
error_def(ERR_REPLSTATE);
error_def(ERR_REPLSTATEERR);
error_def(ERR_SYSCALL);
error_def(ERR_TEXT);
error_def(ERR_JNLCREATE);
error_def(ERR_JNLNOCREATE);
error_def(ERR_MUSELFBKUP);
error_def(ERR_KILLABANDONED);
error_def(ERR_BACKUPKILLIP);
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 *tempdirname, *tempfilename, *ptr;
uint4 level, blk, status, ret, kip_count;
unsigned short s_len, length, ntries;
int4 size, gds_ratio, buff_size, i, crit_counter, save_errno, rv;
uint4 ustatus;
size_t backup_buf_size;
trans_num tn;
shmpool_buff_hdr_ptr_t sbufh_p;
shmpool_blk_hdr_ptr_t sblkh_p, next_sblkh_p;
static boolean_t once = TRUE;
backup_reg_list *rptr, *rrptr, *clnup_ptr, *nocritrptr;
boolean_t inc_since_inc , inc_since_rec, result, newjnlfiles, gotit, 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], jnl_file_name[JNL_NAME_SIZE];
char *jnl_str_ptr, rep_str[256], jnl_str[256], entry[256],
full_jnl_fn[JNL_NAME_SIZE], prev_jnl_fn[JNL_NAME_SIZE];
int ccnt, index, comparison, num, 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;
#if 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;
#elif defined(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;
unsigned char *cmdptr, command[MAX_FN_LEN * 2 + 5]; /* 5 == SIZEOF("cp") + 2 (space) + 1 (NULL) */
struct shmid_ds shm_buf;
int4 shm_id;
replpool_identifier replpool_id;
sm_uc_ptr_t start_addr;
int group_id;
int perm;
#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;)
uint4 *kip_pids_arr_ptr;
ZOS_ONLY(error_def(ERR_BADTAG);)
/* ==================================== 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 (once)
{
gvinit();
once = FALSE;
}
/* ============================ 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);
}
UNIX_ONLY(
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)
gtm_putmsg(VARLSTCNT(1) ustatus);
}
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;
}
}
}
)
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;
}
UNIX_ONLY(
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)
gtm_putmsg(VARLSTCNT(1) ustatus);
}
)
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;
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
#if defined(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)
gtm_set_group_and_perm(&stat_buf, &group_id, &perm, (0770 & stat_buf.st_mode));
/* 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);
}
UNIX_ONLY(
if (NULL != mu_repl_inst_reg_list)
{ /* Grab ftok semaphore of the replication instance file before taking a backup. But before that
* initialize the region corresponding to the instance file.
*/
assert(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';
if (!ftok_sem_get(jnlpool.jnlpool_dummy_reg, TRUE, REPLPOOL_ID, FALSE))
rts_error(VARLSTCNT(1) ERR_JNLPOOLSETUP);
}
)
kip_count = 0;
SET_GBL_JREC_TIME; /* routines that write jnl records (e.g. wcs_flu) require this to be initialized */
for (rptr = (backup_reg_list *)(grlist); NULL != rptr; rptr = rptr->fPtr)
{
if (rptr->not_this_time <= keep_going)
{
TP_CHANGE_REG(rptr->reg);
if (online)
{
grab_crit(gv_cur_region);
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);
}
/* 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);
kip_pids_arr_ptr = cs_addrs->nl->kip_pid_array;
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));
}
while (cs_data->kill_in_prog && (MAX_CRIT_TRY > crit_counter++))
{
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);
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 */
}
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)
{ /* Backup the replication instance file. To do this all that is needed is a system "cp" command
* of the instance file to the destination file. After that we need to update a few fields in the
* file header to make it correspond to the databases and reflect the fact that this is a
* cleanly shutdown instance file.
*/
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_done;
}
repl_inst_read(udi->fn, (off_t)0, (sm_uc_ptr_t)&repl_instance, SIZEOF(repl_inst_hdr));
jnlpool.repl_inst_filehdr = &repl_instance;
shm_id = repl_instance.jnlpool_shmid;
/* If the journal pool does not exist, it means the instance file is not in use and hence to back it
* up, we only need to copy it to the destination file (already done). No other cleanup is needed.
*/
if (INVALID_SEMID != shm_id)
{ /* The journal pool exists. Note down the current 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 (-1 == shmctl(shm_id, IPC_STAT, &shm_buf))
{
save_errno = errno;
gtm_putmsg(VARLSTCNT(5) ERR_REPLPOOLINST, 3, shm_id, RTS_ERROR_STRING(instfilename));
gtm_putmsg(VARLSTCNT(8) ERR_SYSCALL, 5,
RTS_ERROR_LITERAL("shmctl()"), CALLFROM, save_errno);
error_mupip = TRUE;
goto repl_inst_bkup_done;
}
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(instfilename));
gtm_putmsg(VARLSTCNT(8) ERR_SYSCALL, 5, RTS_ERROR_LITERAL("shmat()"), CALLFROM, save_errno);
error_mupip = TRUE;
goto repl_inst_bkup_done;
}
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(instfilename));
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(instfilename));
error_mupip = TRUE;
goto repl_inst_bkup_done;
}
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(instfilename));
error_mupip = TRUE;
goto repl_inst_bkup_done;
}
csa = &udi->s_addrs;
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) */
assert(!mutex_per_process_init_pid || mutex_per_process_init_pid == process_id);
if (!mutex_per_process_init_pid)
mutex_per_process_init();
grab_lock(jnlpool.jnlpool_dummy_reg);
jnl_seqno = jnlpool.jnlpool_ctl->jnl_seqno;
assert(0 != jnl_seqno);
rel_lock(jnlpool.jnlpool_dummy_reg);
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(instfilename));
gtm_putmsg(VARLSTCNT(8) ERR_SYSCALL, 5, RTS_ERROR_LITERAL("shmdt()"), CALLFROM, save_errno);
error_mupip = TRUE;
goto repl_inst_bkup_done;
}
/* All the cleanup we want is exactly done by the "repl_inst_triple_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;
repl_inst_triple_truncate(jnl_seqno); /* Flush updated file header to backed up instance file */
udi->fn = (char *)seg->fname;
} else
jnl_seqno = jnlpool.repl_inst_filehdr->jnl_seqno;
assert(0 != jnl_seqno);
repl_inst_bkup_done:
ftok_sem_release(jnlpool.jnlpool_dummy_reg, TRUE, TRUE);
if (!error_mupip)
{
util_out_print("Replication Instance file !AD backed up in file !AD", TRUE,
LEN_AND_STR(instfilename),
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(instfilename));
}
#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;
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 */
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);
}