/* * Do a verification of the specified files against the Catlaog * * Returns: false on failure * true on success */ bool do_verify(JCR *jcr) { const char *level; BSOCK *fd, *sd; int stat; char ed1[100]; JOB_DBR jr; JobId_t verify_jobid = 0; char *store_address; uint32_t store_port; const char *Name; free_wstorage(jcr); /* we don't write */ memset(&jcr->previous_jr, 0, sizeof(jcr->previous_jr)); /* * Find JobId of last job that ran. Note, we do this when * the job actually starts running, not at schedule time, * so that we find the last job that terminated before * this job runs rather than before it is scheduled. This * permits scheduling a Backup and Verify at the same time, * but with the Verify at a lower priority. * * For VERIFY_CATALOG we want the JobId of the last INIT. * For VERIFY_VOLUME_TO_CATALOG, we want the JobId of the * last backup Job. */ if (jcr->getJobLevel() == L_VERIFY_CATALOG || jcr->getJobLevel() == L_VERIFY_VOLUME_TO_CATALOG || jcr->getJobLevel() == L_VERIFY_DISK_TO_CATALOG || jcr->getJobLevel() == L_VERIFY_DATA) { memcpy(&jr, &jcr->jr, sizeof(jr)); if (jcr->verify_job && (jcr->getJobLevel() == L_VERIFY_VOLUME_TO_CATALOG || jcr->getJobLevel() == L_VERIFY_DISK_TO_CATALOG || jcr->getJobLevel() == L_VERIFY_DATA)) { Name = jcr->verify_job->name(); } else { Name = NULL; } Dmsg1(100, "find last jobid for: %s\n", NPRT(Name)); /* see if user supplied a jobid= as run argument or from menu */ if (jcr->RestoreJobId) { verify_jobid = jcr->RestoreJobId; Dmsg1(100, "Supplied jobid=%d\n", verify_jobid); } else { if (!db_find_last_jobid(jcr, jcr->db, Name, &jr)) { if (jcr->getJobLevel() == L_VERIFY_CATALOG) { Jmsg(jcr, M_FATAL, 0, _( "Unable to find JobId of previous InitCatalog Job.\n" "Please run a Verify with Level=InitCatalog before\n" "running the current Job.\n")); } else { Jmsg(jcr, M_FATAL, 0, _( "Unable to find JobId of previous Job for this client.\n")); } return false; } verify_jobid = jr.JobId; } Dmsg1(100, "Last full jobid=%d\n", verify_jobid); } /* * Now get the job record for the previous backup that interests * us. We use the verify_jobid that we found above. */ if (jcr->getJobLevel() == L_VERIFY_CATALOG || jcr->getJobLevel() == L_VERIFY_VOLUME_TO_CATALOG || jcr->getJobLevel() == L_VERIFY_DISK_TO_CATALOG || jcr->getJobLevel() == L_VERIFY_DATA) { jcr->previous_jr.JobId = verify_jobid; if (!db_get_job_record(jcr, jcr->db, &jcr->previous_jr)) { Jmsg(jcr, M_FATAL, 0, _("Could not get job record for previous Job. ERR=%s"), db_strerror(jcr->db)); return false; } if (!(jcr->previous_jr.JobStatus == JS_Terminated || jcr->previous_jr.JobStatus == JS_Warnings)) { Jmsg(jcr, M_FATAL, 0, _("Last Job %d did not terminate normally. JobStatus=%c\n"), verify_jobid, jcr->previous_jr.JobStatus); return false; } Jmsg(jcr, M_INFO, 0, _("Verifying against JobId=%d Job=%s\n"), jcr->previous_jr.JobId, jcr->previous_jr.Job); } /* * If we are verifying a Volume, we need the Storage * daemon, so open a connection, otherwise, just * create a dummy authorization key (passed to * File daemon but not used). */ if (jcr->getJobLevel() == L_VERIFY_VOLUME_TO_CATALOG || jcr->getJobLevel() == L_VERIFY_DATA) { int stat; /* * Note: negative status is an error, zero status, means * no files were backed up, so skip calling SD and * client. */ stat = create_restore_bootstrap_file(jcr); if (stat < 0) { /* error */ return false; } else if (stat == 0) { /* No files, nothing to do */ verify_cleanup(jcr, JS_Terminated); /* clean up */ return true; /* get out */ } } else { jcr->sd_auth_key = bstrdup("dummy"); /* dummy Storage daemon key */ } /* Pass the original fileset to the client */ if (jcr->getJobLevel() == L_VERIFY_DATA) { FILESET_DBR fdbr; memset(&fdbr, 0, sizeof(fdbr)); fdbr.FileSetId = jcr->previous_jr.FileSetId; if (!db_get_fileset_record(jcr, jcr->db, &fdbr)) { Jmsg(jcr, M_FATAL, 0, _("Could not get fileset record from previous Job. ERR=%s"), db_strerror(jcr->db)); return false; } jcr->fileset = (FILESET *)GetResWithName(R_FILESET, fdbr.FileSet); if (!jcr->fileset) { if (jcr->verify_job) { jcr->fileset = jcr->verify_job->fileset; Jmsg(jcr, M_WARNING, 0, _("Could not find FileSet resource \"%s\" from previous Job\n"), fdbr.FileSet); Jmsg(jcr, M_INFO, 0, _("Using FileSet \"%\"\n"), jcr->fileset->name()); } else { Jmsg(jcr, M_FATAL, 0, _("Could not get FileSet resource for verify Job.")); return false; } } Dmsg1(50, "FileSet = %s\n", jcr->fileset->name()); } /* Pass the current fileset to the client */ if (jcr->getJobLevel() == L_VERIFY_DISK_TO_CATALOG && jcr->verify_job) { jcr->fileset = jcr->verify_job->fileset; } Dmsg2(100, "ClientId=%u JobLevel=%c\n", jcr->previous_jr.ClientId, jcr->getJobLevel()); if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) { Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db)); return false; } /* Print Job Start message */ Jmsg(jcr, M_INFO, 0, _("Start Verify JobId=%s Level=%s Job=%s\n"), edit_uint64(jcr->JobId, ed1), level_to_str(jcr->getJobLevel()), jcr->Job); if (jcr->getJobLevel() == L_VERIFY_VOLUME_TO_CATALOG || jcr->getJobLevel() == L_VERIFY_DATA) { /* * Start conversation with Storage daemon */ jcr->setJobStatus(JS_Blocked); if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) { return false; } /* * Now start a job with the Storage daemon */ if (!start_storage_daemon_job(jcr, jcr->rstorage, NULL)) { return false; } sd = jcr->store_bsock; jcr->sd_calls_client = jcr->client->sd_calls_client; /* * Send the bootstrap file -- what Volumes/files to restore */ if (!send_bootstrap_file(jcr, sd) || !response(jcr, sd, OKbootstrap, "Bootstrap", DISPLAY_ERROR)) { goto bail_out; } if (!jcr->sd_calls_client) { if (!run_storage_and_start_message_thread(jcr, sd)) { return false; } } } /* * OK, now connect to the File daemon * and ask him for the files. */ jcr->setJobStatus(JS_Blocked); if (!connect_to_file_daemon(jcr, 10, FDConnectTimeout, 1)) { goto bail_out; } jcr->setJobStatus(JS_Running); fd = jcr->file_bsock; Dmsg0(30, ">filed: Send include list\n"); if (!send_include_list(jcr)) { goto bail_out; } Dmsg0(30, ">filed: Send exclude list\n"); if (!send_exclude_list(jcr)) { goto bail_out; } /* * Send Level command to File daemon, as well * as the Storage address if appropriate. */ switch (jcr->getJobLevel()) { case L_VERIFY_INIT: level = "init"; break; case L_VERIFY_CATALOG: level = "catalog"; break; case L_VERIFY_DATA: case L_VERIFY_VOLUME_TO_CATALOG: if (jcr->sd_calls_client) { if (jcr->FDVersion < 10) { Jmsg(jcr, M_FATAL, 0, _("The File daemon does not support SDCallsClient.\n")); goto bail_out; } if (!send_client_addr_to_sd(jcr)) { goto bail_out; } if (!run_storage_and_start_message_thread(jcr, jcr->store_bsock)) { return false; } store_address = jcr->rstore->address; /* dummy */ store_port = 0; /* flag that SD calls FD */ } else { /* * send Storage daemon address to the File daemon */ if (jcr->rstore->SDDport == 0) { jcr->rstore->SDDport = jcr->rstore->SDport; } store_address = get_storage_address(jcr->client, jcr->rstore); store_port = jcr->rstore->SDDport; } if (!send_store_addr_to_fd(jcr, jcr->rstore, store_address, store_port)) { goto bail_out; } if (!jcr->RestoreBootstrap) { Jmsg0(jcr, M_FATAL, 0, _("Deprecated feature ... use bootstrap.\n")); goto bail_out; } if (jcr->getJobLevel() == L_VERIFY_VOLUME_TO_CATALOG) { level = "volume"; } else { level = "data"; } break; case L_VERIFY_DISK_TO_CATALOG: level="disk_to_catalog"; break; default: Jmsg2(jcr, M_FATAL, 0, _("Unimplemented Verify level %d(%c)\n"), jcr->getJobLevel(), jcr->getJobLevel()); goto bail_out; } if (!send_runscripts_commands(jcr)) { goto bail_out; } /* * Send verify command/level to File daemon */ fd->fsend(verifycmd, level); if (!response(jcr, fd, OKverify, "Verify", DISPLAY_ERROR)) { goto bail_out; } /* * Now get data back from File daemon and * compare it to the catalog or store it in the * catalog depending on the run type. */ /* Compare to catalog */ switch (jcr->getJobLevel()) { case L_VERIFY_CATALOG: Dmsg0(10, "Verify level=catalog\n"); jcr->sd_msg_thread_done = true; /* no SD msg thread, so it is done */ jcr->SDJobStatus = JS_Terminated; get_attributes_and_compare_to_catalog(jcr, jcr->previous_jr.JobId); break; case L_VERIFY_VOLUME_TO_CATALOG: Dmsg0(10, "Verify level=volume\n"); get_attributes_and_compare_to_catalog(jcr, jcr->previous_jr.JobId); break; case L_VERIFY_DISK_TO_CATALOG: Dmsg0(10, "Verify level=disk_to_catalog\n"); jcr->sd_msg_thread_done = true; /* no SD msg thread, so it is done */ jcr->SDJobStatus = JS_Terminated; get_attributes_and_compare_to_catalog(jcr, jcr->previous_jr.JobId); break; case L_VERIFY_INIT: /* Build catalog */ Dmsg0(10, "Verify level=init\n"); jcr->sd_msg_thread_done = true; /* no SD msg thread, so it is done */ jcr->SDJobStatus = JS_Terminated; get_attributes_and_put_in_catalog(jcr); db_end_transaction(jcr, jcr->db); /* terminate any open transaction */ db_write_batch_file_records(jcr); break; case L_VERIFY_DATA: /* Nothing special to do */ bget_dirmsg(fd); /* eat EOD */ break; default: Jmsg1(jcr, M_FATAL, 0, _("Unimplemented verify level %d\n"), jcr->getJobLevel()); goto bail_out; } stat = wait_for_job_termination(jcr); verify_cleanup(jcr, stat); return true; bail_out: return false; }
bool setup_job(JCR *jcr) { int errstat; jcr->lock(); Dsm_check(100); init_msg(jcr, jcr->res.messages, job_code_callback_director); /* * Initialize termination condition variable */ if ((errstat = pthread_cond_init(&jcr->term_wait, NULL)) != 0) { berrno be; Jmsg1(jcr, M_FATAL, 0, _("Unable to init job cond variable: ERR=%s\n"), be.bstrerror(errstat)); jcr->unlock(); goto bail_out; } jcr->term_wait_inited = true; /* * Initialize nextrun ready condition variable */ if ((errstat = pthread_cond_init(&jcr->nextrun_ready, NULL)) != 0) { berrno be; Jmsg1(jcr, M_FATAL, 0, _("Unable to init job nextrun cond variable: ERR=%s\n"), be.bstrerror(errstat)); jcr->unlock(); goto bail_out; } jcr->nextrun_ready_inited = true; create_unique_job_name(jcr, jcr->res.job->name()); jcr->setJobStatus(JS_Created); jcr->unlock(); /* * Open database */ Dmsg0(100, "Open database\n"); jcr->db = db_sql_get_pooled_connection(jcr, jcr->res.catalog->db_driver, jcr->res.catalog->db_name, jcr->res.catalog->db_user, jcr->res.catalog->db_password, jcr->res.catalog->db_address, jcr->res.catalog->db_port, jcr->res.catalog->db_socket, jcr->res.catalog->mult_db_connections, jcr->res.catalog->disable_batch_insert); if (jcr->db == NULL) { Jmsg(jcr, M_FATAL, 0, _("Could not open database \"%s\".\n"), jcr->res.catalog->db_name); goto bail_out; } Dmsg0(150, "DB opened\n"); if (!jcr->fname) { jcr->fname = get_pool_memory(PM_FNAME); } if (!jcr->res.pool_source) { jcr->res.pool_source = get_pool_memory(PM_MESSAGE); pm_strcpy(jcr->res.pool_source, _("unknown source")); } if (!jcr->res.npool_source) { jcr->res.npool_source = get_pool_memory(PM_MESSAGE); pm_strcpy(jcr->res.npool_source, _("unknown source")); } if (jcr->JobReads()) { if (!jcr->res.rpool_source) { jcr->res.rpool_source = get_pool_memory(PM_MESSAGE); pm_strcpy(jcr->res.rpool_source, _("unknown source")); } } /* * Create Job record */ init_jcr_job_record(jcr); if (!get_or_create_client_record(jcr)) { goto bail_out; } if (!db_create_job_record(jcr, jcr->db, &jcr->jr)) { Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db)); goto bail_out; } jcr->JobId = jcr->jr.JobId; Dmsg4(100, "Created job record JobId=%d Name=%s Type=%c Level=%c\n", jcr->JobId, jcr->Job, jcr->jr.JobType, jcr->jr.JobLevel); new_plugins(jcr); /* instantiate plugins for this jcr */ generate_plugin_event(jcr, bDirEventJobStart); if (job_canceled(jcr)) { goto bail_out; } if (jcr->JobReads() && !jcr->rstorage) { if (jcr->res.job->storage) { copy_rwstorage(jcr, jcr->res.job->storage, _("Job resource")); } else { copy_rwstorage(jcr, jcr->res.job->pool->storage, _("Pool resource")); } } if (!jcr->JobReads()) { free_rstorage(jcr); } /* * Now, do pre-run stuff, like setting job level (Inc/diff, ...) * this allows us to setup a proper job start record for restarting * in case of later errors. */ switch (jcr->getJobType()) { case JT_BACKUP: switch (jcr->getJobProtocol()) { case PT_NDMP: if (!do_ndmp_backup_init(jcr)) { ndmp_backup_cleanup(jcr, JS_ErrorTerminated); goto bail_out; } break; default: if (jcr->is_JobLevel(L_VIRTUAL_FULL)) { if (!do_native_vbackup_init(jcr)) { native_vbackup_cleanup(jcr, JS_ErrorTerminated); goto bail_out; } } else { if (!do_native_backup_init(jcr)) { native_backup_cleanup(jcr, JS_ErrorTerminated); goto bail_out; } } break; } break; case JT_VERIFY: if (!do_verify_init(jcr)) { verify_cleanup(jcr, JS_ErrorTerminated); goto bail_out; } break; case JT_RESTORE: switch (jcr->getJobProtocol()) { case PT_NDMP: if (!do_ndmp_restore_init(jcr)) { ndmp_restore_cleanup(jcr, JS_ErrorTerminated); goto bail_out; } break; default: /* * Any non NDMP restore is not interested at the items * that were selected for restore so drop them now. */ if (jcr->restore_tree_root) { free_tree(jcr->restore_tree_root); jcr->restore_tree_root = NULL; } if (!do_native_restore_init(jcr)) { native_restore_cleanup(jcr, JS_ErrorTerminated); goto bail_out; } break; } break; case JT_ADMIN: if (!do_admin_init(jcr)) { admin_cleanup(jcr, JS_ErrorTerminated); goto bail_out; } break; case JT_COPY: case JT_MIGRATE: if (!do_migration_init(jcr)) { migration_cleanup(jcr, JS_ErrorTerminated); goto bail_out; } break; default: Pmsg1(0, _("Unimplemented job type: %d\n"), jcr->getJobType()); jcr->setJobStatus(JS_ErrorTerminated); goto bail_out; } generate_plugin_event(jcr, bDirEventJobInit); Dsm_check(100); return true; bail_out: return false; }
/* * Do a verification of the specified files against the Catlaog * * Returns: false on failure * true on success */ bool do_verify(JCR *jcr) { const char *level; BSOCK *fd; int stat; char ed1[100]; JOB_DBR jr; JobId_t verify_jobid = 0; const char *Name; free_wstorage(jcr); /* we don't write */ memset(&jcr->previous_jr, 0, sizeof(jcr->previous_jr)); /* * Find JobId of last job that ran. Note, we do this when * the job actually starts running, not at schedule time, * so that we find the last job that terminated before * this job runs rather than before it is scheduled. This * permits scheduling a Backup and Verify at the same time, * but with the Verify at a lower priority. * * For VERIFY_CATALOG we want the JobId of the last INIT. * For VERIFY_VOLUME_TO_CATALOG, we want the JobId of the * last backup Job. */ if (jcr->get_JobLevel() == L_VERIFY_CATALOG || jcr->get_JobLevel() == L_VERIFY_VOLUME_TO_CATALOG || jcr->get_JobLevel() == L_VERIFY_DISK_TO_CATALOG) { memcpy(&jr, &jcr->jr, sizeof(jr)); if (jcr->verify_job && (jcr->get_JobLevel() == L_VERIFY_VOLUME_TO_CATALOG || jcr->get_JobLevel() == L_VERIFY_DISK_TO_CATALOG)) { Name = jcr->verify_job->name(); } else { Name = NULL; } Dmsg1(100, "find last jobid for: %s\n", NPRT(Name)); if (!db_find_last_jobid(jcr, jcr->db, Name, &jr)) { if (jcr->get_JobLevel() == L_VERIFY_CATALOG) { Jmsg(jcr, M_FATAL, 0, _( "Unable to find JobId of previous InitCatalog Job.\n" "Please run a Verify with Level=InitCatalog before\n" "running the current Job.\n")); } else { Jmsg(jcr, M_FATAL, 0, _( "Unable to find JobId of previous Job for this client.\n")); } return false; } verify_jobid = jr.JobId; Dmsg1(100, "Last full jobid=%d\n", verify_jobid); } /* * Now get the job record for the previous backup that interests * us. We use the verify_jobid that we found above. */ if (jcr->get_JobLevel() == L_VERIFY_CATALOG || jcr->get_JobLevel() == L_VERIFY_VOLUME_TO_CATALOG || jcr->get_JobLevel() == L_VERIFY_DISK_TO_CATALOG) { jcr->previous_jr.JobId = verify_jobid; if (!db_get_job_record(jcr, jcr->db, &jcr->previous_jr)) { Jmsg(jcr, M_FATAL, 0, _("Could not get job record for previous Job. ERR=%s"), db_strerror(jcr->db)); return false; } if (!(jcr->previous_jr.JobStatus == JS_Terminated || jcr->previous_jr.JobStatus == JS_Warnings)) { Jmsg(jcr, M_FATAL, 0, _("Last Job %d did not terminate normally. JobStatus=%c\n"), verify_jobid, jcr->previous_jr.JobStatus); return false; } Jmsg(jcr, M_INFO, 0, _("Verifying against JobId=%d Job=%s\n"), jcr->previous_jr.JobId, jcr->previous_jr.Job); } /* * If we are verifying a Volume, we need the Storage * daemon, so open a connection, otherwise, just * create a dummy authorization key (passed to * File daemon but not used). */ if (jcr->get_JobLevel() == L_VERIFY_VOLUME_TO_CATALOG) { if (!create_restore_bootstrap_file(jcr)) { return false; } } else { jcr->sd_auth_key = bstrdup("dummy"); /* dummy Storage daemon key */ } if (jcr->get_JobLevel() == L_VERIFY_DISK_TO_CATALOG && jcr->verify_job) { jcr->fileset = jcr->verify_job->fileset; } Dmsg2(100, "ClientId=%u JobLevel=%c\n", jcr->previous_jr.ClientId, jcr->get_JobLevel()); if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) { Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db)); return false; } /* Print Job Start message */ Jmsg(jcr, M_INFO, 0, _("Start Verify JobId=%s Level=%s Job=%s\n"), edit_uint64(jcr->JobId, ed1), level_to_str(jcr->get_JobLevel()), jcr->Job); if (jcr->get_JobLevel() == L_VERIFY_VOLUME_TO_CATALOG) { /* * Start conversation with Storage daemon */ set_jcr_job_status(jcr, JS_Blocked); if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) { return false; } /* * Now start a job with the Storage daemon */ if (!start_storage_daemon_job(jcr, jcr->rstorage, NULL)) { return false; } if (!jcr->store_bsock->fsend("run")) { return false; } /* * Now start a Storage daemon message thread */ if (!start_storage_daemon_message_thread(jcr)) { return false; } Dmsg0(50, "Storage daemon connection OK\n"); } /* * OK, now connect to the File daemon * and ask him for the files. */ set_jcr_job_status(jcr, JS_Blocked); if (!connect_to_file_daemon(jcr, 10, FDConnectTimeout, 1)) { goto bail_out; } set_jcr_job_status(jcr, JS_Running); fd = jcr->file_bsock; Dmsg0(30, ">filed: Send include list\n"); if (!send_include_list(jcr)) { goto bail_out; } Dmsg0(30, ">filed: Send exclude list\n"); if (!send_exclude_list(jcr)) { goto bail_out; } /* * Send Level command to File daemon, as well * as the Storage address if appropriate. */ switch (jcr->get_JobLevel()) { case L_VERIFY_INIT: level = "init"; break; case L_VERIFY_CATALOG: level = "catalog"; break; case L_VERIFY_VOLUME_TO_CATALOG: /* * send Storage daemon address to the File daemon */ if (jcr->rstore->SDDport == 0) { jcr->rstore->SDDport = jcr->rstore->SDport; } bnet_fsend(fd, storaddr, jcr->rstore->address, jcr->rstore->SDDport); if (!response(jcr, fd, OKstore, "Storage", DISPLAY_ERROR)) { goto bail_out; } /* * Send the bootstrap file -- what Volumes/files to restore */ if (!send_bootstrap_file(jcr, fd) || !response(jcr, fd, OKbootstrap, "Bootstrap", DISPLAY_ERROR)) { goto bail_out; } if (!jcr->RestoreBootstrap) { Jmsg0(jcr, M_FATAL, 0, _("Deprecated feature ... use bootstrap.\n")); goto bail_out; } level = "volume"; break; case L_VERIFY_DATA: level = "data"; break; case L_VERIFY_DISK_TO_CATALOG: level="disk_to_catalog"; break; default: Jmsg2(jcr, M_FATAL, 0, _("Unimplemented Verify level %d(%c)\n"), jcr->get_JobLevel(), jcr->get_JobLevel()); goto bail_out; } if (!send_runscripts_commands(jcr)) { goto bail_out; } /* * Send verify command/level to File daemon */ fd->fsend(verifycmd, level); if (!response(jcr, fd, OKverify, "Verify", DISPLAY_ERROR)) { goto bail_out; } /* * Now get data back from File daemon and * compare it to the catalog or store it in the * catalog depending on the run type. */ /* Compare to catalog */ switch (jcr->get_JobLevel()) { case L_VERIFY_CATALOG: Dmsg0(10, "Verify level=catalog\n"); jcr->sd_msg_thread_done = true; /* no SD msg thread, so it is done */ jcr->SDJobStatus = JS_Terminated; get_attributes_and_compare_to_catalog(jcr, jcr->previous_jr.JobId); break; case L_VERIFY_VOLUME_TO_CATALOG: Dmsg0(10, "Verify level=volume\n"); get_attributes_and_compare_to_catalog(jcr, jcr->previous_jr.JobId); break; case L_VERIFY_DISK_TO_CATALOG: Dmsg0(10, "Verify level=disk_to_catalog\n"); jcr->sd_msg_thread_done = true; /* no SD msg thread, so it is done */ jcr->SDJobStatus = JS_Terminated; get_attributes_and_compare_to_catalog(jcr, jcr->previous_jr.JobId); break; case L_VERIFY_INIT: /* Build catalog */ Dmsg0(10, "Verify level=init\n"); jcr->sd_msg_thread_done = true; /* no SD msg thread, so it is done */ jcr->SDJobStatus = JS_Terminated; get_attributes_and_put_in_catalog(jcr); db_end_transaction(jcr, jcr->db); /* terminate any open transaction */ db_write_batch_file_records(jcr); break; default: Jmsg1(jcr, M_FATAL, 0, _("Unimplemented verify level %d\n"), jcr->get_JobLevel()); goto bail_out; } stat = wait_for_job_termination(jcr); verify_cleanup(jcr, stat); return true; bail_out: return false; }
/* * This is the engine called by jobq.c:jobq_add() when we were pulled * from the work queue. * At this point, we are running in our own thread and all * necessary resources are allocated -- see jobq.c */ static void *job_thread(void *arg) { JCR *jcr = (JCR *)arg; pthread_detach(pthread_self()); Dsm_check(100); Dmsg0(200, "=====Start Job=========\n"); jcr->setJobStatus(JS_Running); /* this will be set only if no error */ jcr->start_time = time(NULL); /* set the real start time */ jcr->jr.StartTime = jcr->start_time; if (jcr->res.job->MaxStartDelay != 0 && jcr->res.job->MaxStartDelay < (utime_t)(jcr->start_time - jcr->sched_time)) { jcr->setJobStatus(JS_Canceled); Jmsg(jcr, M_FATAL, 0, _("Job canceled because max start delay time exceeded.\n")); } if (job_check_maxrunschedtime(jcr)) { jcr->setJobStatus(JS_Canceled); Jmsg(jcr, M_FATAL, 0, _("Job canceled because max run sched time exceeded.\n")); } /* TODO : check if it is used somewhere */ if (jcr->res.job->RunScripts == NULL) { Dmsg0(200, "Warning, job->RunScripts is empty\n"); jcr->res.job->RunScripts = New(alist(10, not_owned_by_alist)); } if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) { Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db)); } /* Run any script BeforeJob on dird */ run_scripts(jcr, jcr->res.job->RunScripts, "BeforeJob"); /* * We re-update the job start record so that the start * time is set after the run before job. This avoids * that any files created by the run before job will * be saved twice. They will be backed up in the current * job, but not in the next one unless they are changed. * Without this, they will be backed up in this job and * in the next job run because in that case, their date * is after the start of this run. */ jcr->start_time = time(NULL); jcr->jr.StartTime = jcr->start_time; if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) { Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db)); } generate_plugin_event(jcr, bDirEventJobRun); switch (jcr->getJobType()) { case JT_BACKUP: switch (jcr->getJobProtocol()) { case PT_NDMP: if (!job_canceled(jcr)) { if (do_ndmp_backup(jcr)) { do_autoprune(jcr); } else { ndmp_backup_cleanup(jcr, JS_ErrorTerminated); } } else { ndmp_backup_cleanup(jcr, JS_Canceled); } break; default: if (!job_canceled(jcr)) { if (jcr->is_JobLevel(L_VIRTUAL_FULL)) { if (do_native_vbackup(jcr)) { do_autoprune(jcr); } else { native_vbackup_cleanup(jcr, JS_ErrorTerminated); } } else { if (do_native_backup(jcr)) { do_autoprune(jcr); } else { native_backup_cleanup(jcr, JS_ErrorTerminated); } } } else { if (jcr->is_JobLevel(L_VIRTUAL_FULL)) { native_vbackup_cleanup(jcr, JS_Canceled); } else { native_backup_cleanup(jcr, JS_Canceled); } } break; } break; case JT_VERIFY: if (!job_canceled(jcr)) { if (do_verify(jcr)) { do_autoprune(jcr); } else { verify_cleanup(jcr, JS_ErrorTerminated); } } else { verify_cleanup(jcr, JS_Canceled); } break; case JT_RESTORE: switch (jcr->getJobProtocol()) { case PT_NDMP: if (!job_canceled(jcr)) { if (do_ndmp_restore(jcr)) { do_autoprune(jcr); } else { ndmp_restore_cleanup(jcr, JS_ErrorTerminated); } } else { ndmp_restore_cleanup(jcr, JS_Canceled); } break; default: if (!job_canceled(jcr)) { if (do_native_restore(jcr)) { do_autoprune(jcr); } else { native_restore_cleanup(jcr, JS_ErrorTerminated); } } else { native_restore_cleanup(jcr, JS_Canceled); } break; } break; case JT_ADMIN: if (!job_canceled(jcr)) { if (do_admin(jcr)) { do_autoprune(jcr); } else { admin_cleanup(jcr, JS_ErrorTerminated); } } else { admin_cleanup(jcr, JS_Canceled); } break; case JT_COPY: case JT_MIGRATE: if (!job_canceled(jcr)) { if (do_migration(jcr)) { do_autoprune(jcr); } else { migration_cleanup(jcr, JS_ErrorTerminated); } } else { migration_cleanup(jcr, JS_Canceled); } break; default: Pmsg1(0, _("Unimplemented job type: %d\n"), jcr->getJobType()); break; } run_scripts(jcr, jcr->res.job->RunScripts, "AfterJob"); /* Send off any queued messages */ if (jcr->msg_queue && jcr->msg_queue->size() > 0) { dequeue_messages(jcr); } generate_plugin_event(jcr, bDirEventJobEnd); Dmsg1(50, "======== End Job stat=%c ==========\n", jcr->JobStatus); Dsm_check(100); return NULL; }
/* * Do a verification of the specified files against the Catlaog * * Returns: false on failure * true on success */ bool do_verify(JCR *jcr) { int JobLevel; const char *level; BSOCK *fd = NULL; BSOCK *sd = NULL; int status; char ed1[100]; JOB_DBR jr; JobId_t verify_jobid = 0; const char *Name; free_wstorage(jcr); /* we don't write */ memset(&jcr->previous_jr, 0, sizeof(jcr->previous_jr)); /* * Find JobId of last job that ran. Note, we do this when * the job actually starts running, not at schedule time, * so that we find the last job that terminated before * this job runs rather than before it is scheduled. This * permits scheduling a Backup and Verify at the same time, * but with the Verify at a lower priority. * * For VERIFY_CATALOG we want the JobId of the last INIT. * For VERIFY_VOLUME_TO_CATALOG, we want the JobId of the * last backup Job. */ JobLevel = jcr->getJobLevel(); switch (JobLevel) { case L_VERIFY_CATALOG: case L_VERIFY_VOLUME_TO_CATALOG: case L_VERIFY_DISK_TO_CATALOG: memcpy(&jr, &jcr->jr, sizeof(jr)); if (jcr->res.verify_job && (JobLevel == L_VERIFY_VOLUME_TO_CATALOG || JobLevel == L_VERIFY_DISK_TO_CATALOG)) { Name = jcr->res.verify_job->name(); } else { Name = NULL; } Dmsg1(100, "find last jobid for: %s\n", NPRT(Name)); /* * See if user supplied a jobid= as run argument or from menu */ if (jcr->VerifyJobId) { verify_jobid = jcr->VerifyJobId; Dmsg1(100, "Supplied jobid=%d\n", verify_jobid); } else { if (!db_find_last_jobid(jcr, jcr->db, Name, &jr)) { if (JobLevel == L_VERIFY_CATALOG) { Jmsg(jcr, M_FATAL, 0, _( "Unable to find JobId of previous InitCatalog Job.\n" "Please run a Verify with Level=InitCatalog before\n" "running the current Job.\n")); } else { Jmsg(jcr, M_FATAL, 0, _( "Unable to find JobId of previous Job for this client.\n")); } return false; } verify_jobid = jr.JobId; } Dmsg1(100, "Last full jobid=%d\n", verify_jobid); /* * Now get the job record for the previous backup that interests * us. We use the verify_jobid that we found above. */ jcr->previous_jr.JobId = verify_jobid; if (!db_get_job_record(jcr, jcr->db, &jcr->previous_jr)) { Jmsg(jcr, M_FATAL, 0, _("Could not get job record for previous Job. ERR=%s"), db_strerror(jcr->db)); return false; } if (!(jcr->previous_jr.JobStatus == JS_Terminated || jcr->previous_jr.JobStatus == JS_Warnings)) { Jmsg(jcr, M_FATAL, 0, _("Last Job %d did not terminate normally. JobStatus=%c\n"), verify_jobid, jcr->previous_jr.JobStatus); return false; } Jmsg(jcr, M_INFO, 0, _("Verifying against JobId=%d Job=%s\n"), jcr->previous_jr.JobId, jcr->previous_jr.Job); } /* * If we are verifying a Volume, we need the Storage * daemon, so open a connection, otherwise, just * create a dummy authorization key (passed to * File daemon but not used). */ switch (JobLevel) { case L_VERIFY_VOLUME_TO_CATALOG: /* * Note: negative status is an error, zero status, means * no files were backed up, so skip calling SD and * client. */ status = create_restore_bootstrap_file(jcr); if (status < 0) { /* error */ return false; } else if (status == 0) { /* No files, nothing to do */ verify_cleanup(jcr, JS_Terminated); /* clean up */ return true; /* get out */ } if (jcr->res.verify_job) { jcr->res.fileset = jcr->res.verify_job->fileset; } break; default: jcr->sd_auth_key = bstrdup("dummy"); /* dummy Storage daemon key */ break; } Dmsg2(100, "ClientId=%u JobLevel=%c\n", jcr->previous_jr.ClientId, JobLevel); if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) { Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db)); return false; } /* * Print Job Start message */ Jmsg(jcr, M_INFO, 0, _("Start Verify JobId=%s Level=%s Job=%s\n"), edit_uint64(jcr->JobId, ed1), level_to_str(JobLevel), jcr->Job); switch (JobLevel) { case L_VERIFY_VOLUME_TO_CATALOG: /* * Start conversation with Storage daemon */ jcr->setJobStatus(JS_Blocked); if (!connect_to_storage_daemon(jcr, 10, me->SDConnectTimeout, true)) { return false; } sd = jcr->store_bsock; /* * Now start a job with the Storage daemon */ if (!start_storage_daemon_job(jcr, jcr->res.rstorage, NULL, /* send_bsr */ true)) { return false; } jcr->passive_client = jcr->res.client->passive; if (!jcr->passive_client) { /* * Start the Job in the SD. */ if (!sd->fsend("run")) { return false; } /* * Now start a Storage daemon message thread */ if (!start_storage_daemon_message_thread(jcr)) { return false; } Dmsg0(50, "Storage daemon connection OK\n"); } /* * OK, now connect to the File daemon and ask him for the files. */ jcr->setJobStatus(JS_Blocked); if (!connect_to_file_daemon(jcr, 10, me->FDConnectTimeout, true)) { goto bail_out; } send_job_info(jcr); fd = jcr->file_bsock; /* * Check if the file daemon supports passive client mode. */ if (jcr->passive_client && jcr->FDVersion < FD_VERSION_51) { Jmsg(jcr, M_FATAL, 0, _("Client \"%s\" doesn't support passive client mode. " "Please upgrade your client or disable compat mode.\n"), jcr->res.client->name()); goto bail_out; } break; default: /* * OK, now connect to the File daemon and ask him for the files. */ jcr->setJobStatus(JS_Blocked); if (!connect_to_file_daemon(jcr, 10, me->FDConnectTimeout, true)) { goto bail_out; } send_job_info(jcr); fd = jcr->file_bsock; break; } jcr->setJobStatus(JS_Running); Dmsg0(30, ">filed: Send include list\n"); if (!send_include_list(jcr)) { goto bail_out; } Dmsg0(30, ">filed: Send exclude list\n"); if (!send_exclude_list(jcr)) { goto bail_out; } /* * Send Level command to File daemon, as well as the Storage address if appropriate. */ switch (JobLevel) { case L_VERIFY_INIT: level = "init"; break; case L_VERIFY_CATALOG: level = "catalog"; break; case L_VERIFY_VOLUME_TO_CATALOG: if (!jcr->RestoreBootstrap) { Jmsg0(jcr, M_FATAL, 0, _("Deprecated feature ... use bootstrap.\n")); goto bail_out; } if (!jcr->passive_client) { int tls_need = BNET_TLS_NONE; STORERES *store = jcr->res.rstore; /* * Send Storage daemon address to the File daemon */ if (store->SDDport == 0) { store->SDDport = store->SDport; } /* * TLS Requirement */ if (store->tls.enable) { if (store->tls.require) { tls_need = BNET_TLS_REQUIRED; } else { tls_need = BNET_TLS_OK; } } fd->fsend(storaddrcmd, store->address, store->SDDport, tls_need, jcr->sd_auth_key); if (!response(jcr, fd, OKstore, "Storage", DISPLAY_ERROR)) { goto bail_out; } } else { int tls_need = BNET_TLS_NONE; CLIENTRES *client = jcr->res.client; /* * TLS Requirement */ if (client->tls.enable) { if (client->tls.require) { tls_need = BNET_TLS_REQUIRED; } else { tls_need = BNET_TLS_OK; } } /* * Tell the SD to connect to the FD. */ sd->fsend(passiveclientcmd, client->address, client->FDport, tls_need); if (!response(jcr, sd, OKpassiveclient, "Passive client", DISPLAY_ERROR)) { goto bail_out; } /* * Start the Job in the SD. */ if (!sd->fsend("run")) { goto bail_out; } /* * Now start a Storage daemon message thread */ if (!start_storage_daemon_message_thread(jcr)) { goto bail_out; } Dmsg0(50, "Storage daemon connection OK\n"); } level = "volume"; break; case L_VERIFY_DATA: level = "data"; break; case L_VERIFY_DISK_TO_CATALOG: level="disk_to_catalog"; break; default: Jmsg2(jcr, M_FATAL, 0, _("Unimplemented Verify level %d(%c)\n"), JobLevel, JobLevel); goto bail_out; } if (!send_runscripts_commands(jcr)) { goto bail_out; } /* * Send verify command/level to File daemon */ fd->fsend(verifycmd, level); if (!response(jcr, fd, OKverify, "Verify", DISPLAY_ERROR)) { goto bail_out; } /* * Now get data back from File daemon and * compare it to the catalog or store it in the * catalog depending on the run type. */ switch (JobLevel) { case L_VERIFY_CATALOG: /* * Verify from catalog */ Dmsg0(10, "Verify level=catalog\n"); jcr->sd_msg_thread_done = true; /* no SD msg thread, so it is done */ jcr->SDJobStatus = JS_Terminated; get_attributes_and_compare_to_catalog(jcr, jcr->previous_jr.JobId); break; case L_VERIFY_VOLUME_TO_CATALOG: /* * Verify Volume to catalog entries */ Dmsg0(10, "Verify level=volume\n"); get_attributes_and_compare_to_catalog(jcr, jcr->previous_jr.JobId); break; case L_VERIFY_DISK_TO_CATALOG: /* * Verify Disk attributes to catalog */ Dmsg0(10, "Verify level=disk_to_catalog\n"); jcr->sd_msg_thread_done = true; /* no SD msg thread, so it is done */ jcr->SDJobStatus = JS_Terminated; get_attributes_and_compare_to_catalog(jcr, jcr->previous_jr.JobId); break; case L_VERIFY_INIT: /* * Build catalog */ Dmsg0(10, "Verify level=init\n"); jcr->sd_msg_thread_done = true; /* no SD msg thread, so it is done */ jcr->SDJobStatus = JS_Terminated; get_attributes_and_put_in_catalog(jcr); db_end_transaction(jcr, jcr->db); /* terminate any open transaction */ db_write_batch_file_records(jcr); break; default: Jmsg1(jcr, M_FATAL, 0, _("Unimplemented verify level %d\n"), JobLevel); goto bail_out; } status = wait_for_job_termination(jcr); verify_cleanup(jcr, status); return true; bail_out: if (jcr->file_bsock) { jcr->file_bsock->signal(BNET_TERMINATE); jcr->file_bsock->close(); delete jcr->file_bsock; jcr->file_bsock = NULL; } return false; }